Skip to content

Commit

Permalink
Simplified filters and added exclusion filters
Browse files Browse the repository at this point in the history
  • Loading branch information
jschick04 authored and bill-long committed May 8, 2024
1 parent 99816cc commit 3419095
Show file tree
Hide file tree
Showing 35 changed files with 475 additions and 555 deletions.
8 changes: 4 additions & 4 deletions src/EventLogExpert.UI.Tests/Models/FilterDataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ public sealed class FilterDataTests
[Fact]
public void FilterValue_WhenFilterTypeChanged_IsNull()
{
FilterData model = new() { Type = FilterType.Id, Value = "100" };
FilterData model = new() { Category = FilterCategory.Id, Value = "100" };

model.Type = FilterType.Level;
model.Category = FilterCategory.Level;

Assert.Null(model.Value);
}

[Fact]
public void FilterValues_WhenFilterTypeChanged_IsEmpty()
{
FilterData model = new() { Type = FilterType.Id, Values = ["100", "1000"] };
FilterData model = new() { Category = FilterCategory.Id, Values = ["100", "1000"] };

model.Type = FilterType.Level;
model.Category = FilterCategory.Level;

Assert.Empty(model.Values);
}
Expand Down
23 changes: 15 additions & 8 deletions src/EventLogExpert.UI/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ public enum HighlightColor
LightGray
}

public enum FilterCategory
{
[EnumMember(Value = "Event ID")] Id,
[EnumMember(Value = "Activity ID")] ActivityId,
Level,
[EnumMember(Value = "Keywords")] KeywordsDisplayNames,
Source,
[EnumMember(Value = "Task Category")] TaskCategory,
Description,
Xml
}

public enum FilterEvaluator
{
Equals,
Expand All @@ -63,14 +75,9 @@ public enum FilterEvaluator

public enum FilterType
{
[EnumMember(Value = "Event ID")] Id,
[EnumMember(Value = "Activity ID")] ActivityId,
Level,
[EnumMember(Value = "Keywords")] KeywordsDisplayNames,
Source,
[EnumMember(Value = "Task Category")] TaskCategory,
Description,
Xml
Basic,
Advanced,
Cached
}

public enum LogType
Expand Down
174 changes: 79 additions & 95 deletions src/EventLogExpert.UI/FilterMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ public static Dictionary<string, FilterGroupData> AddFilterGroup(
return group;
}

public static bool Filter(this DisplayEventModel? @event, ImmutableList<FilterModel> filters)
{
if (@event is null) { return false; }

if (filters.IsEmpty) { return true; }

if (filters.Any(filter => filter.IsExcluded && filter.Comparison.Expression(@event))) { return false; }

return filters.All(filter => filter.IsExcluded) || filters.Any(filter => !filter.IsExcluded && filter.Comparison.Expression(@event));
}

public static IDictionary<EventLogId, IEnumerable<DisplayEventModel>> FilterActiveLogs(
IEnumerable<EventLogData> logData,
EventFilter eventFilter)
Expand All @@ -55,85 +66,58 @@ public static IDictionary<EventLogId, IEnumerable<DisplayEventModel>> FilterActi
return activeLogsFiltered;
}

public static IEnumerable<DisplayEventModel> GetFilteredEvents(IEnumerable<DisplayEventModel> events, EventFilter eventFilter)
public static DisplayEventModel? FilterByDate(this DisplayEventModel? @event, FilterDateModel? dateFilter)
{
if (!IsFilteringEnabled(eventFilter)) { return events; }

List<Func<DisplayEventModel, bool>> filters = [];

if (!eventFilter.AdvancedFilters.IsEmpty)
{
filters.Add(e => eventFilter.AdvancedFilters
.Any(filter => filter.Comparison.Expression(e)));
}
if (@event is null) { return null; }

if (!eventFilter.BasicFilters.IsEmpty)
{
filters.Add(e => eventFilter.BasicFilters
.Any(filter => filter.Comparison.Expression(e)));
}
if (dateFilter is null) { return @event; }

if (!eventFilter.CachedFilters.IsEmpty)
{
filters.Add(e => eventFilter.CachedFilters
.Any(filter => filter.Comparison.Expression(e)));
}
return @event.TimeCreated >= dateFilter.After && @event.TimeCreated <= dateFilter.Before ? @event : null;
}

if (eventFilter.DateFilter?.IsEnabled is true)
{
return filters.Count <= 0 ?
events.Where(e =>
e.TimeCreated >= eventFilter.DateFilter.After &&
e.TimeCreated <= eventFilter.DateFilter.Before) :
events.AsParallel()
.Where(e =>
e.TimeCreated >= eventFilter.DateFilter.After &&
e.TimeCreated <= eventFilter.DateFilter.Before &&
filters
.Any(filter => filter(e)));
}
public static IEnumerable<DisplayEventModel> GetFilteredEvents(
IEnumerable<DisplayEventModel> events,
EventFilter eventFilter)
{
if (!IsFilteringEnabled(eventFilter)) { return events; }

return events.AsParallel()
.Where(e => filters
.Any(filter => filter(e)));
.Where(e => e.FilterByDate(eventFilter.DateFilter)
.Filter(eventFilter.Filters));
}

public static bool HasFilteringChanged(EventFilter updated, EventFilter original) =>
updated.DateFilter?.Equals(original.DateFilter) is false ||
updated.AdvancedFilters.Equals(original.AdvancedFilters) is false ||
updated.BasicFilters.Equals(original.BasicFilters) is false ||
updated.CachedFilters.Equals(original.CachedFilters) is false;
updated.Filters.Equals(original.Filters) is false;

public static bool IsFilteringEnabled(EventFilter eventFilter) =>
eventFilter.DateFilter?.IsEnabled is true ||
eventFilter.AdvancedFilters.IsEmpty is false ||
eventFilter.BasicFilters.IsEmpty is false ||
eventFilter.CachedFilters.IsEmpty is false;
eventFilter.Filters.IsEmpty is false;

/// <summary>Sorts events by RecordId if no order is specified</summary>
public static IEnumerable<DisplayEventModel> SortEvents(
this IEnumerable<DisplayEventModel> events,
ColumnName? orderBy = null,
bool isDescending = false) => orderBy switch
{
ColumnName.Level => isDescending ? events.OrderByDescending(e => e.Level) : events.OrderBy(e => e.Level),
ColumnName.DateAndTime => isDescending ?
events.OrderByDescending(e => e.TimeCreated) :
events.OrderBy(e => e.TimeCreated),
ColumnName.ActivityId => isDescending ?
events.OrderByDescending(e => e.ActivityId) :
events.OrderBy(e => e.ActivityId),
ColumnName.LogName => isDescending ? events.OrderByDescending(e => e.LogName) : events.OrderBy(e => e.LogName),
ColumnName.ComputerName => isDescending ?
events.OrderByDescending(e => e.ComputerName) :
events.OrderBy(e => e.ComputerName),
ColumnName.Source => isDescending ? events.OrderByDescending(e => e.Source) : events.OrderBy(e => e.Source),
ColumnName.EventId => isDescending ? events.OrderByDescending(e => e.Id) : events.OrderBy(e => e.Id),
ColumnName.TaskCategory => isDescending ?
events.OrderByDescending(e => e.TaskCategory) :
events.OrderBy(e => e.TaskCategory),
_ => isDescending ? events.OrderByDescending(e => e.RecordId) : events.OrderBy(e => e.RecordId)
};
{
ColumnName.Level => isDescending ? events.OrderByDescending(e => e.Level) : events.OrderBy(e => e.Level),
ColumnName.DateAndTime => isDescending ?
events.OrderByDescending(e => e.TimeCreated) :
events.OrderBy(e => e.TimeCreated),
ColumnName.ActivityId => isDescending ?
events.OrderByDescending(e => e.ActivityId) :
events.OrderBy(e => e.ActivityId),
ColumnName.LogName => isDescending ? events.OrderByDescending(e => e.LogName) : events.OrderBy(e => e.LogName),
ColumnName.ComputerName => isDescending ?
events.OrderByDescending(e => e.ComputerName) :
events.OrderBy(e => e.ComputerName),
ColumnName.Source => isDescending ? events.OrderByDescending(e => e.Source) : events.OrderBy(e => e.Source),
ColumnName.EventId => isDescending ? events.OrderByDescending(e => e.Id) : events.OrderBy(e => e.Id),
ColumnName.TaskCategory => isDescending ?
events.OrderByDescending(e => e.TaskCategory) :
events.OrderBy(e => e.TaskCategory),
_ => isDescending ? events.OrderByDescending(e => e.RecordId) : events.OrderBy(e => e.RecordId)
};

public static bool TryParse(FilterModel filterModel, out string comparison)
{
Expand All @@ -148,16 +132,16 @@ public static bool TryParse(FilterModel filterModel, out string comparison)
StringBuilder stringBuilder = new();

if (filterModel.Data.Evaluator != FilterEvaluator.MultiSelect ||
filterModel.Data.Type is FilterType.KeywordsDisplayNames)
filterModel.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append(GetComparisonString(filterModel.Data.Type, filterModel.Data.Evaluator));
stringBuilder.Append(GetComparisonString(filterModel.Data.Category, filterModel.Data.Evaluator));
}

switch (filterModel.Data.Evaluator)
{
case FilterEvaluator.Equals :
case FilterEvaluator.NotEqual :
if (filterModel.Data.Type is FilterType.KeywordsDisplayNames)
case FilterEvaluator.Equals:
case FilterEvaluator.NotEqual:
if (filterModel.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append($"\"{filterModel.Data.Value}\", StringComparison.OrdinalIgnoreCase))");
}
Expand All @@ -167,9 +151,9 @@ public static bool TryParse(FilterModel filterModel, out string comparison)
}

break;
case FilterEvaluator.Contains :
case FilterEvaluator.NotContains :
if (filterModel.Data.Type is FilterType.KeywordsDisplayNames)
case FilterEvaluator.Contains:
case FilterEvaluator.NotContains:
if (filterModel.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append($"(\"{filterModel.Data.Value}\", StringComparison.OrdinalIgnoreCase))");
}
Expand All @@ -179,8 +163,8 @@ public static bool TryParse(FilterModel filterModel, out string comparison)
}

break;
case FilterEvaluator.MultiSelect :
if (filterModel.Data.Type is FilterType.KeywordsDisplayNames)
case FilterEvaluator.MultiSelect:
if (filterModel.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append($"(e => (new[] {{\"{string.Join("\", \"", filterModel.Data.Values)}\"}}).Contains(e))");
}
Expand All @@ -190,12 +174,12 @@ public static bool TryParse(FilterModel filterModel, out string comparison)
}

break;
default : return false;
default: return false;
}

if (filterModel.Data is { Evaluator: FilterEvaluator.MultiSelect, Type: not FilterType.KeywordsDisplayNames })
if (filterModel.Data is { Evaluator: FilterEvaluator.MultiSelect, Category: not FilterCategory.KeywordsDisplayNames })
{
stringBuilder.Append(GetComparisonString(filterModel.Data.Type, filterModel.Data.Evaluator));
stringBuilder.Append(GetComparisonString(filterModel.Data.Category, filterModel.Data.Evaluator));
}

if (filterModel.SubFilters.Count > 0)
Expand Down Expand Up @@ -233,30 +217,30 @@ public static bool TryParseExpression(string? expression, out string error)
}
}

private static string GetComparisonString(FilterType type, FilterEvaluator evaluator) => evaluator switch
private static string GetComparisonString(FilterCategory type, FilterEvaluator evaluator) => evaluator switch
{
FilterEvaluator.Equals => type is FilterType.KeywordsDisplayNames ?
FilterEvaluator.Equals => type is FilterCategory.KeywordsDisplayNames ?
$"{type}.Any(e => string.Equals(e, " :
$"{type} == ",
FilterEvaluator.Contains => type switch
{
FilterType.Id or FilterType.ActivityId => $"{type}.ToString().Contains",
FilterType.KeywordsDisplayNames => $"{type}.Any(e => e.Contains",
FilterCategory.Id or FilterCategory.ActivityId => $"{type}.ToString().Contains",
FilterCategory.KeywordsDisplayNames => $"{type}.Any(e => e.Contains",
_ => $"{type}.Contains"
},
FilterEvaluator.NotEqual => type is FilterType.KeywordsDisplayNames ?
FilterEvaluator.NotEqual => type is FilterCategory.KeywordsDisplayNames ?
$"!{type}.Any(e => string.Equals(e, " :
$"{type} != ",
FilterEvaluator.NotContains => type switch
{
FilterType.Id or FilterType.ActivityId => $"!{type}.ToString().Contains",
FilterType.KeywordsDisplayNames => $"!{type}.Any(e => e.Contains",
FilterCategory.Id or FilterCategory.ActivityId => $"!{type}.ToString().Contains",
FilterCategory.KeywordsDisplayNames => $"!{type}.Any(e => e.Contains",
_ => $"!{type}.Contains"
},
FilterEvaluator.MultiSelect => type switch
{
FilterType.Id or FilterType.Level => $"{type}.ToString())",
FilterType.KeywordsDisplayNames => $"{type}.Any",
FilterCategory.Id or FilterCategory.Level => $"{type}.ToString())",
FilterCategory.KeywordsDisplayNames => $"{type}.Any",
_ => $"{type})"
},
_ => string.Empty
Expand All @@ -273,16 +257,16 @@ public static bool TryParseExpression(string? expression, out string error)
StringBuilder stringBuilder = new(subFilter.ShouldCompareAny ? " || " : " && ");

if (subFilter.Data.Evaluator != FilterEvaluator.MultiSelect ||
subFilter.Data.Type is FilterType.KeywordsDisplayNames)
subFilter.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append(GetComparisonString(subFilter.Data.Type, subFilter.Data.Evaluator));
stringBuilder.Append(GetComparisonString(subFilter.Data.Category, subFilter.Data.Evaluator));
}

switch (subFilter.Data.Evaluator)
{
case FilterEvaluator.Equals :
case FilterEvaluator.NotEqual :
if (subFilter.Data.Type is FilterType.KeywordsDisplayNames)
case FilterEvaluator.Equals:
case FilterEvaluator.NotEqual:
if (subFilter.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append($"\"{subFilter.Data.Value}\", StringComparison.OrdinalIgnoreCase))");
}
Expand All @@ -292,9 +276,9 @@ public static bool TryParseExpression(string? expression, out string error)
}

break;
case FilterEvaluator.Contains :
case FilterEvaluator.NotContains :
if (subFilter.Data.Type is FilterType.KeywordsDisplayNames)
case FilterEvaluator.Contains:
case FilterEvaluator.NotContains:
if (subFilter.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append($"(\"{subFilter.Data.Value}\", StringComparison.OrdinalIgnoreCase))");
}
Expand All @@ -304,8 +288,8 @@ public static bool TryParseExpression(string? expression, out string error)
}

break;
case FilterEvaluator.MultiSelect :
if (subFilter.Data.Type is FilterType.KeywordsDisplayNames)
case FilterEvaluator.MultiSelect:
if (subFilter.Data.Category is FilterCategory.KeywordsDisplayNames)
{
stringBuilder.Append($"(e => (new[] {{\"{string.Join("\", \"", subFilter.Data.Values)}\"}}).Contains(e))");
}
Expand All @@ -315,12 +299,12 @@ public static bool TryParseExpression(string? expression, out string error)
}

break;
default : return null;
default: return null;
}

if (subFilter.Data is { Evaluator: FilterEvaluator.MultiSelect, Type: not FilterType.KeywordsDisplayNames })
if (subFilter.Data is { Evaluator: FilterEvaluator.MultiSelect, Category: not FilterCategory.KeywordsDisplayNames })
{
stringBuilder.Append(GetComparisonString(subFilter.Data.Type, subFilter.Data.Evaluator));
stringBuilder.Append(GetComparisonString(subFilter.Data.Category, subFilter.Data.Evaluator));
}

return stringBuilder.ToString();
Expand Down
6 changes: 1 addition & 5 deletions src/EventLogExpert.UI/Models/EventFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,4 @@

namespace EventLogExpert.UI.Models;

public readonly record struct EventFilter(
FilterDateModel? DateFilter,
ImmutableList<FilterModel> AdvancedFilters,
ImmutableList<FilterModel> BasicFilters,
ImmutableList<FilterModel> CachedFilters);
public readonly record struct EventFilter(FilterDateModel? DateFilter, ImmutableList<FilterModel> Filters);
8 changes: 4 additions & 4 deletions src/EventLogExpert.UI/Models/FilterData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ namespace EventLogExpert.UI.Models;

public sealed record FilterData
{
private FilterType _type;
private FilterCategory _category;

public FilterType Type
public FilterCategory Category
{
get => _type;
get => _category;
set
{
_type = value;
_category = value;
Value = null;
Values.Clear();
}
Expand Down
Loading

0 comments on commit 3419095

Please sign in to comment.