diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..3580d23c
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+[*.{cs,vb}]
+
+# IDE0305: Initialisierung der Sammlung vereinfachen
+dotnet_diagnostic.IDE0305.severity = none
diff --git a/iRLeagueApiCore.sln b/iRLeagueApiCore.sln
index c6dfef91..926619f2 100644
--- a/iRLeagueApiCore.sln
+++ b/iRLeagueApiCore.sln
@@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iRLeagueApiCore.UnitTests",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7F90E099-56DE-4FEF-8CA2-4F4610CDEF19}"
ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
.github\workflows\dotnet_develop.yml = .github\workflows\dotnet_develop.yml
.github\workflows\dotnet_main_pr.yml = .github\workflows\dotnet_main_pr.yml
.github\workflows\dotnet_main_push.yml = .github\workflows\dotnet_main_push.yml
diff --git a/src/iRLeagueApiCore.Client/iRLeagueApiCore.Client.csproj b/src/iRLeagueApiCore.Client/iRLeagueApiCore.Client.csproj
index 4fd59d84..d0cd4a42 100644
--- a/src/iRLeagueApiCore.Client/iRLeagueApiCore.Client.csproj
+++ b/src/iRLeagueApiCore.Client/iRLeagueApiCore.Client.csproj
@@ -12,7 +12,7 @@
Library
net6.0
iRLeagueApiCore.Client
- 0.12.1
+ 0.12.2
Simon Schulze
Simon Schulze
This package contains shared objects for all members of the iRLeagueDatabase-iRLeagueApi stack
diff --git a/src/iRLeagueApiCore.Common/Enums/ComparatorType.cs b/src/iRLeagueApiCore.Common/Enums/ComparatorType.cs
index c9d59652..030c13be 100644
--- a/src/iRLeagueApiCore.Common/Enums/ComparatorType.cs
+++ b/src/iRLeagueApiCore.Common/Enums/ComparatorType.cs
@@ -9,5 +9,7 @@ public enum ComparatorType
IsBigger,
NotEqual,
InList,
- ForEach, // special comparator that multiplies the configured bonus/penalty for each multiple of the provided value
+ ForEach, // special comparator that multiplies the configured bonus/penalty for each multiple of the provided value
+ Min,
+ Max,
}
diff --git a/src/iRLeagueApiCore.Common/Enums/FilterType.cs b/src/iRLeagueApiCore.Common/Enums/FilterType.cs
index 0347533a..840ee26c 100644
--- a/src/iRLeagueApiCore.Common/Enums/FilterType.cs
+++ b/src/iRLeagueApiCore.Common/Enums/FilterType.cs
@@ -5,4 +5,5 @@ public enum FilterType
ColumnProperty,
Member,
Team,
+ Count,
}
diff --git a/src/iRLeagueApiCore.Common/Models/ResultConfigurations/BonusPointModel.cs b/src/iRLeagueApiCore.Common/Models/ResultConfigurations/BonusPointModel.cs
index 9784f6f4..d6648fc4 100644
--- a/src/iRLeagueApiCore.Common/Models/ResultConfigurations/BonusPointModel.cs
+++ b/src/iRLeagueApiCore.Common/Models/ResultConfigurations/BonusPointModel.cs
@@ -3,6 +3,8 @@ namespace iRLeagueApiCore.Common.Models;
[DataContract]
public class BonusPointModel
{
+ [DataMember]
+ public string Name { get; set; } = string.Empty;
[DataMember]
public BonusPointType Type { get; set; }
[DataMember]
diff --git a/src/iRLeagueApiCore.Common/iRLeagueApiCore.Common.csproj b/src/iRLeagueApiCore.Common/iRLeagueApiCore.Common.csproj
index bfee5b11..04e17124 100644
--- a/src/iRLeagueApiCore.Common/iRLeagueApiCore.Common.csproj
+++ b/src/iRLeagueApiCore.Common/iRLeagueApiCore.Common.csproj
@@ -18,7 +18,7 @@
Library
net6.0
iRLeagueApiCore.Common
- 0.12.1
+ 0.12.2
Simon Schulze
Simon Schulze
enable
diff --git a/src/iRLeagueApiCore.Server/iRLeagueApiCore.Server.csproj b/src/iRLeagueApiCore.Server/iRLeagueApiCore.Server.csproj
index afcc27c1..85593521 100644
--- a/src/iRLeagueApiCore.Server/iRLeagueApiCore.Server.csproj
+++ b/src/iRLeagueApiCore.Server/iRLeagueApiCore.Server.csproj
@@ -71,7 +71,7 @@
true
- 0.12.1
+ 0.12.2
enable
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationPointRuleBase.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationPointRuleBase.cs
index 8ba51883..74a635be 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationPointRuleBase.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationPointRuleBase.cs
@@ -1,5 +1,6 @@
using iRLeagueApiCore.Common.Enums;
using iRLeagueApiCore.Common.Models;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
using iRLeagueApiCore.Services.ResultService.Extensions;
using iRLeagueApiCore.Services.ResultService.Models;
using MySqlX.XDevAPI.Relational;
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationServiceBase.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationServiceBase.cs
index cd9750e2..8c642b2e 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationServiceBase.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/CalculationServiceBase.cs
@@ -30,9 +30,10 @@ protected static IEnumerable ApplyPoints(IEnumerable
// Calculation
pointRule.ApplyPoints(data, pointRows.ToList());
// remove points from filtered rows and set points eligible
- pointRows.ForEach(x => x.PointsEligible = true);
+ pointRows.ForEach(x => x.PointsEligible = true);
rows.Except(pointRows)
- .ForEach(x => {
+ .ForEach(x =>
+ {
x.RacePoints = 0;
x.PointsEligible = false;
});
@@ -74,7 +75,7 @@ protected static (TId? id, TimeSpan lap) GetBestLapValue(IEnumerable
}
catch (Exception ex) when (ex is InvalidOperationException)
{
- return Array.Empty<(TId? id, TValue value)>();
+ return [];
}
}
@@ -144,7 +145,7 @@ protected static IEnumerable CombineResults(IEnum
return combined.ToList();
}
- private static AddPenaltyCalculationData CreateAddPenaltyFromAutoPenalty(ResultRowCalculationResult row, AutoPenaltyConfigurationData autoPenalty,
+ private static AddPenaltyCalculationData CreateAddPenaltyFromAutoPenalty(ResultRowCalculationResult row, AutoPenaltyConfigurationData autoPenalty,
int penaltyMultiplikator)
{
var penalty = new AddPenaltyCalculationData()
@@ -158,17 +159,17 @@ private static AddPenaltyCalculationData CreateAddPenaltyFromAutoPenalty(ResultR
return penalty;
}
- private static IEnumerable CalculateAutoPenalties(IEnumerable rows,
+ private static IEnumerable CalculateAutoPenalties(IEnumerable rows,
IEnumerable autoPenalties)
{
- foreach(var autoPenalty in autoPenalties)
+ foreach (var autoPenalty in autoPenalties)
{
var penaltyRows = autoPenalty.Conditions
.FilterRows(rows);
var grouped = penaltyRows.GroupBy(x => x);
- foreach(var row in grouped.Where(x => x.Any()))
+ foreach (var row in grouped.Where(x => x.Any()))
{
- row.Key.AddPenalties = row.Key.AddPenalties.Concat(new[] { CreateAddPenaltyFromAutoPenalty(row.Key, autoPenalty, row.Count()) });
+ row.Key.AddPenalties = [.. row.Key.AddPenalties, CreateAddPenaltyFromAutoPenalty(row.Key, autoPenalty, row.Count())];
}
}
return rows;
@@ -287,13 +288,12 @@ private static IEnumerable ApplyBonusPoints(IEnumera
}
var minIncs = rows.Any(x => x.PenaltyPoints == 0) ? rows.Where(x => x.PenaltyPoints == 0).Min(x => x.Incidents) : -1;
- var fastestLapRow = GetBestLapValue(rows, x => x, x => x.FastestLapTime);
foreach (var bonus in BonusPoints)
{
var bonusType = bonus.Type;
- var bonusKeyValue = (int)bonus.Value;
- var bonusPoints = (int)bonus.Points;
+ var bonusKeyValue = bonus.Value;
+ var bonusPoints = bonus.Points;
rows = bonusType switch
{
BonusPointType.Position => ApplyPositionBonusPoints(rows, bonusKeyValue, bonusPoints),
@@ -306,7 +306,7 @@ private static IEnumerable ApplyBonusPoints(IEnumera
BonusPointType.LeadMostLaps => ApplyLeadMostLapsBonusPoints(rows, bonusPoints),
BonusPointType.NoIncidents => ApplyNoIncidentsBonusPoints(rows, bonusPoints),
BonusPointType.FastestAverageLap => ApplyFastestAverageLapBonusPoints(rows, bonusPoints),
- BonusPointType.Custom => ApplyCustomBonusPoints(rows, bonus.Conditions, bonusPoints),
+ BonusPointType.Custom => ApplyCustomBonusPoints(rows, bonus, bonusPoints),
_ => rows,
};
}
@@ -314,10 +314,11 @@ private static IEnumerable ApplyBonusPoints(IEnumera
return rows;
}
- private static IEnumerable ApplyCustomBonusPoints(IEnumerable rows, FilterGroupRowFilter conditions,
+ private static IEnumerable ApplyCustomBonusPoints(IEnumerable rows, BonusPointConfiguration bonus,
int bonusPoints)
{
- var bonusRows = conditions.FilterRows(rows);
+ var bonusRows = bonus.Conditions.FilterRows(rows);
+
foreach (var row in bonusRows)
{
row.BonusPoints += bonusPoints;
@@ -337,12 +338,11 @@ private static IEnumerable ApplyFastestAverageLapBon
private static IEnumerable ApplyNoIncidentsBonusPoints(IEnumerable rows, int points)
{
- foreach(var row in rows)
+ var bonusRows = rows.Where(x => x.Incidents == 0);
+
+ foreach (var row in bonusRows)
{
- if (row.Incidents == 0)
- {
- row.BonusPoints += points;
- }
+ row.BonusPoints += points;
}
return rows;
}
@@ -350,24 +350,22 @@ private static IEnumerable ApplyNoIncidentsBonusPoin
private static IEnumerable ApplyLeadMostLapsBonusPoints(IEnumerable rows, int points)
{
var mostLapsLead = rows.Max(x => x.LeadLaps);
- foreach(var row in rows)
+ var bonusRows = rows.Where(x => x.LeadLaps == mostLapsLead);
+
+ foreach (var row in bonusRows)
{
- if (row.LeadLaps == mostLapsLead)
- {
- row.BonusPoints += points;
- }
+ row.BonusPoints += points;
}
return rows;
}
private static IEnumerable ApplyLeadOneLapBonusPoints(IEnumerable rows, int points)
{
- foreach(var row in rows)
+ var bonusRows = rows.Where(x => x.LeadLaps > 0);
+
+ foreach (var row in bonusRows)
{
- if (row.LeadLaps > 0)
- {
- row.BonusPoints += points;
- }
+ row.BonusPoints += points;
}
return rows;
}
@@ -375,12 +373,11 @@ private static IEnumerable ApplyLeadOneLapBonusPoint
private static IEnumerable ApplyMostPositionsLostBonusPoints(IEnumerable rows, int points)
{
var mostPositionsLost = rows.Max(x => x.PositionChange);
- foreach (var row in rows)
+ var bonusRows = rows.Where(x => x.PositionChange == mostPositionsLost);
+
+ foreach (var row in bonusRows)
{
- if (row.PositionChange == mostPositionsLost)
- {
- row.BonusPoints += points;
- }
+ row.BonusPoints += points;
}
return rows;
}
@@ -388,12 +385,11 @@ private static IEnumerable ApplyMostPositionsLostBon
private static IEnumerable ApplyMostPositionsGainedBonusPoints(IEnumerable rows, int points)
{
var mostPositionsGained = rows.Min(x => x.PositionChange);
- foreach(var row in rows)
+ var bonusRows = rows.Where(x => x.PositionChange == mostPositionsGained);
+
+ foreach (var row in bonusRows)
{
- if (row.PositionChange == mostPositionsGained)
- {
- row.BonusPoints += points;
- }
+ row.BonusPoints += points;
}
return rows;
}
@@ -416,6 +412,7 @@ private static IEnumerable ApplyCleanestDriverBonusP
var minIncRows = GetBestValues(rows.Where(condition), x => x.Incidents, x => x, x => x.Min())
.Select(x => x.id)
.NotNull();
+
foreach (var row in minIncRows)
{
row.BonusPoints += points;
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/ColumnValueRowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/ColumnValueRowFilter.cs
deleted file mode 100644
index 9e3c89df..00000000
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/ColumnValueRowFilter.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using iRLeagueApiCore.Common.Enums;
-using iRLeagueApiCore.Services.ResultService.Models;
-using System.Globalization;
-using System.Reflection;
-
-namespace iRLeagueApiCore.Services.ResultService.Calculation;
-
-internal sealed class ColumnValueRowFilter : RowFilter
-{
- public bool AllowForEach { get; private set; }
-
- ///
- /// Create an instance of a
- ///
- /// Name of the property matching the desired column
- /// Type of comparison to execute
- /// Values corresponding to comparator
- /// Action to perform: Keep / Remove
- /// Enable multiplication of rows using "ForEach" comparator
- ///
- public ColumnValueRowFilter(string propertyName, ComparatorType comparator, IEnumerable values, MatchedValueAction action, bool allowForEach = false)
- {
- Action = action;
- AllowForEach = allowForEach;
- try
- {
- ColumnProperty = GetColumnPropertyInfo(propertyName);
- }
- catch (InvalidOperationException ex)
- {
- throw new ArgumentException($"Parameter value {propertyName} did not target a valid column property on type {typeof(ResultRowCalculationResult)}",
- nameof(propertyName), ex);
- }
- Comparator = comparator;
- CompareFunc = GetCompareFunction(comparator);
- try
- {
- FilterValues = GetFilterValuesOfType(ColumnProperty.PropertyType, values).ToList();
- }
- catch (Exception ex) when (ex is InvalidCastException ||
- ex is FormatException ||
- ex is OverflowException ||
- ex is ArgumentNullException)
- {
- throw new ArgumentException($"Parameter was not of type {ColumnProperty.PropertyType} of column property {ColumnProperty.Name}", nameof(values), ex);
- }
- }
-
- public PropertyInfo ColumnProperty { get; }
- public ComparatorType Comparator { get; }
- private Func, bool> CompareFunc { get; }
- public IEnumerable FilterValues { get; }
- public MatchedValueAction Action { get; }
-
- public override IEnumerable FilterRows(IEnumerable rows)
- {
- var match = rows.Where(x => MatchFilterValues(x, ColumnProperty, FilterValues, CompareFunc));
- if (Comparator == ComparatorType.ForEach && AllowForEach)
- {
- // special handling for ForEach --> duplicate rows by multiple of values
- match = MultiplyRows(match, ColumnProperty, FilterValues);
- }
- return Action switch
- {
- MatchedValueAction.Keep => match,
- MatchedValueAction.Remove => rows.Except(match),
- _ => rows,
- };
- }
-
- private static bool MatchFilterValues(ResultRowCalculationResult row, PropertyInfo property, IEnumerable filterValues, Func, bool> compare)
- {
- var value = (IComparable?)property.GetValue(row);
- return compare(value, filterValues);
- }
-
- private static PropertyInfo GetColumnPropertyInfo(string propertyName)
- {
- var sourceType = typeof(ResultRowCalculationResult);
- var propertyInfo = sourceType.GetProperty(propertyName)
- ?? throw new InvalidOperationException($"{typeof(ResultRowCalculationResult)} does not have a property with name {propertyName}");
- if (typeof(IComparable).IsAssignableFrom(propertyInfo.PropertyType) == false)
- {
- throw new InvalidOperationException($"Column {propertyName} of type {typeof(ResultRowCalculationResult)} does not implement IComparable");
- }
- return propertyInfo;
- }
-
- private static IEnumerable GetFilterValuesOfType(Type type, IEnumerable values)
- {
- if (type.Equals(typeof(TimeSpan)))
- {
- return values.Select(x => TimeSpan.Parse(x)).Cast();
- }
- return values.Select(x => Convert.ChangeType(x, type, CultureInfo.InvariantCulture)).Cast();
- }
-
- private static IEnumerable MultiplyRows(IEnumerable rows, PropertyInfo property,
- IEnumerable filterValues)
- {
- if (filterValues.Any() == false)
- {
- return rows;
- }
- var compareValue = Convert.ToDouble(filterValues.First());
- List multipliedRows = new();
- foreach (var row in rows)
- {
- var value = Convert.ToDouble(property.GetValue(row));
- var count = (int)(value / compareValue);
- for (int i=0; i, bool> GetCompareFunction(ComparatorType comparatorType)
- => comparatorType switch
- {
- ComparatorType.IsBigger => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1; },
- ComparatorType.IsBiggerOrEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1 || c == 0; },
- ComparatorType.IsEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 0; },
- ComparatorType.IsSmallerOrEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == -1 || c == 0; },
- ComparatorType.IsSmaller => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == -1; },
- ComparatorType.NotEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1 || c == -1; },
- ComparatorType.InList => (x, y) => { var c = y.Any(z => x?.CompareTo(z) == 0); return c; },
- ComparatorType.ForEach => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1 || c == 0; },
- _ => (x, y) => true,
- };
-}
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultPointRule.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultPointRule.cs
index 786807de..d8f417ba 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultPointRule.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultPointRule.cs
@@ -1,5 +1,6 @@
using iRLeagueApiCore.Common.Enums;
using iRLeagueApiCore.Common.Models;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
using iRLeagueApiCore.Services.ResultService.Models;
namespace iRLeagueApiCore.Services.ResultService.Calculation;
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/ColumnValueRowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/ColumnValueRowFilter.cs
new file mode 100644
index 00000000..66abad7d
--- /dev/null
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/ColumnValueRowFilter.cs
@@ -0,0 +1,75 @@
+using iRLeagueApiCore.Common.Enums;
+using iRLeagueApiCore.Services.ResultService.Extensions;
+using iRLeagueApiCore.Services.ResultService.Models;
+using MySqlX.XDevAPI.Relational;
+using System.Globalization;
+using System.Reflection;
+
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
+
+internal sealed class ColumnValueRowFilter : RowFilter
+{
+ ///
+ /// Create an instance of a
+ ///
+ /// Name of the property matching the desired column
+ /// Type of comparison to execute
+ /// Values corresponding to comparator
+ /// Action to perform: Keep / Remove
+ /// Enable multiplication of rows using "ForEach" comparator
+ ///
+ public ColumnValueRowFilter(string propertyName, ComparatorType comparatorType, IEnumerable values, MatchedValueAction action, bool allowForEach = false)
+ {
+ try
+ {
+ ColumnProperty = GetColumnPropertyInfo(propertyName);
+ }
+ catch (InvalidOperationException ex)
+ {
+ throw new ArgumentException($"Parameter value {propertyName} did not target a valid column property on type {typeof(ResultRowCalculationResult)}",
+ nameof(propertyName), ex);
+ }
+ try
+ {
+ FilterValues = GetFilterValuesOfType(ColumnProperty.PropertyType, values).ToList();
+ }
+ catch (Exception ex) when (ex is InvalidCastException ||
+ ex is FormatException ||
+ ex is OverflowException ||
+ ex is ArgumentNullException)
+ {
+ throw new ArgumentException($"Parameter was not of type {ColumnProperty.PropertyType} of column property {ColumnProperty.Name}", nameof(values), ex);
+ }
+ Comparator = new(comparatorType, action, allowForEach);
+ }
+
+ public PropertyInfo ColumnProperty { get; }
+ public ComparatorFilter Comparator { get; }
+ public IEnumerable FilterValues { get; }
+
+ public override IEnumerable FilterRows(IEnumerable rows)
+ {
+ return Comparator.GetFilteredRows(rows, (row) => (IComparable?)ColumnProperty.GetValue(row), FilterValues);
+ }
+
+ private static PropertyInfo GetColumnPropertyInfo(string propertyName)
+ {
+ var sourceType = typeof(ResultRowCalculationResult);
+ var propertyInfo = sourceType.GetProperty(propertyName)
+ ?? throw new InvalidOperationException($"{typeof(ResultRowCalculationResult)} does not have a property with name {propertyName}");
+ if (typeof(IComparable).IsAssignableFrom(propertyInfo.PropertyType) == false)
+ {
+ throw new InvalidOperationException($"Column {propertyName} of type {typeof(ResultRowCalculationResult)} does not implement IComparable");
+ }
+ return propertyInfo;
+ }
+
+ private static IEnumerable GetFilterValuesOfType(Type type, IEnumerable values)
+ {
+ if (type.Equals(typeof(TimeSpan)))
+ {
+ return values.Select(x => TimeSpan.Parse(x)).Cast();
+ }
+ return values.Select(x => Convert.ChangeType(x, type, CultureInfo.InvariantCulture)).Cast();
+ }
+}
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/ComparatorFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/ComparatorFilter.cs
new file mode 100644
index 00000000..4fb1c713
--- /dev/null
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/ComparatorFilter.cs
@@ -0,0 +1,99 @@
+using iRLeagueApiCore.Common.Enums;
+using iRLeagueApiCore.Services.ResultService.Models;
+using System.Reflection;
+
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
+internal sealed class ComparatorFilter
+{
+ public bool AllowForEach { get; private set; }
+ public ComparatorType ComparatorType { get; }
+ private Func, bool> CompareFunc { get; }
+ public MatchedValueAction Action { get; }
+
+ public ComparatorFilter(ComparatorType type, MatchedValueAction action, bool allowForEach = false)
+ {
+ ComparatorType = type;
+ CompareFunc = GetCompareFunction(ComparatorType);
+ Action = action;
+ AllowForEach = allowForEach;
+ }
+
+ public IEnumerable GetFilteredRows(IEnumerable rows, Func getCompareValue, IEnumerable filterValues)
+ {
+ if (rows.Any()) {
+ filterValues = ComparatorType switch
+ {
+ // Min and Max comaparators do not have fixed compare values - fetch value from row column
+ ComparatorType.Min => [rows.Min(x => getCompareValue(x))],
+ ComparatorType.Max => [rows.Max(x => getCompareValue(x))],
+ // Use user provided values otherwise
+ _ => filterValues,
+ };
+ }
+
+ var match = rows.Where(x => MatchFilterValues(x, getCompareValue, filterValues, CompareFunc));
+ if (ComparatorType == ComparatorType.ForEach && AllowForEach)
+ {
+ // special handling for ForEach --> duplicate rows by multiple of values
+ match = MultiplyRows(match, getCompareValue, filterValues);
+ }
+ return Action switch
+ {
+ MatchedValueAction.Keep => match,
+ MatchedValueAction.Remove => rows.Except(match),
+ _ => rows,
+ };
+ }
+
+ private static bool MatchFilterValues(T row, Func getCompareValue, IEnumerable filterValues,
+ Func, bool> compare)
+ {
+ var value = getCompareValue(row);
+ return compare(value, filterValues);
+ }
+
+ private static IEnumerable MultiplyRows(IEnumerable rows, Func getCompareValue,
+ IEnumerable filterValues)
+ {
+ if (filterValues.Any() == false)
+ {
+ return rows;
+ }
+ var compareValue = Convert.ToDouble(filterValues.First());
+ List multipliedRows = [];
+ foreach (var row in rows)
+ {
+ var value = Convert.ToDouble(getCompareValue(row));
+ var count = (int)(value / compareValue);
+ for (int i = 0; i < count; i++)
+ {
+ multipliedRows.Add(row);
+ }
+ }
+ return multipliedRows;
+ }
+
+ private static Func, bool> GetCompareFunction(ComparatorType comparatorType)
+ => comparatorType switch
+ {
+ ComparatorType.IsBigger => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1; }
+ ,
+ ComparatorType.IsBiggerOrEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1 || c == 0; }
+ ,
+ ComparatorType.IsEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 0; }
+ ,
+ ComparatorType.IsSmallerOrEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == -1 || c == 0; }
+ ,
+ ComparatorType.IsSmaller => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == -1; }
+ ,
+ ComparatorType.NotEqual => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1 || c == -1; }
+ ,
+ ComparatorType.InList => (x, y) => { var c = y.Any(z => x?.CompareTo(z) == 0); return c; }
+ ,
+ ComparatorType.ForEach => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 1 || c == 0; }
+ ,
+ ComparatorType.Max or ComparatorType.Min => (x, y) => { var c = x?.CompareTo(y.FirstOrDefault()); return c == 0; }
+ ,
+ _ => (x, y) => true,
+ };
+}
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/CountRowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/CountRowFilter.cs
new file mode 100644
index 00000000..40d72099
--- /dev/null
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/CountRowFilter.cs
@@ -0,0 +1,36 @@
+
+using Google.Protobuf.WellKnownTypes;
+using iRLeagueApiCore.Common.Enums;
+using iRLeagueApiCore.Services.ResultService.Models;
+
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
+
+///
+/// Filter that counts the number of rows in the filter input and either matches all rows if the conditions match or none
+///
+internal sealed class CountRowFilter : RowFilter
+{
+ public ComparatorFilter Comparator { get; }
+ public IEnumerable FilterValues { get; }
+
+ public CountRowFilter(ComparatorType comparator, IEnumerable filterValues, MatchedValueAction action, bool allowForEach = false)
+ {
+ Comparator = new(comparator, action, allowForEach);
+ try
+ {
+ FilterValues = filterValues.Select(x => Convert.ToInt32(x)).Cast().ToList();
+ }
+ catch (Exception ex) when (ex is InvalidCastException ||
+ ex is FormatException ||
+ ex is OverflowException)
+ {
+ throw new ArgumentException($"Parameter was not of type {typeof(int)}", nameof(filterValues), ex);
+ }
+ }
+
+ public override IEnumerable FilterRows(IEnumerable rows)
+ {
+ int count = rows.Count();
+ return Comparator.GetFilteredRows(rows, _ => count, FilterValues);
+ }
+}
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultRowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/DefaultRowFilter.cs
similarity index 70%
rename from src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultRowFilter.cs
rename to src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/DefaultRowFilter.cs
index d1efc03f..245caf95 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/DefaultRowFilter.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/DefaultRowFilter.cs
@@ -1,4 +1,4 @@
-namespace iRLeagueApiCore.Services.ResultService.Calculation;
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
internal sealed class DefaultRowFilter : RowFilter
{
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/FilterGroupRowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/FilterGroupRowFilter.cs
similarity index 88%
rename from src/iRLeagueApiCore.Services/ResultService/Calculation/FilterGroupRowFilter.cs
rename to src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/FilterGroupRowFilter.cs
index 83a24f16..b7869b84 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/FilterGroupRowFilter.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/FilterGroupRowFilter.cs
@@ -1,4 +1,4 @@
-namespace iRLeagueApiCore.Services.ResultService.Calculation;
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
internal sealed class FilterGroupRowFilter : RowFilter
{
private readonly IList<(FilterCombination combination, RowFilter rowFilter)> filters;
@@ -18,7 +18,7 @@ public FilterGroupRowFilter(IEnumerable<(FilterCombination, RowFilter)> filte
public override IEnumerable FilterRows(IEnumerable rows)
{
var originalRows = rows.ToList();
- foreach(var (combination, filter) in filters)
+ foreach (var (combination, filter) in filters)
{
rows = combination switch
{
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/IdRowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/IdRowFilter.cs
similarity index 92%
rename from src/iRLeagueApiCore.Services/ResultService/Calculation/IdRowFilter.cs
rename to src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/IdRowFilter.cs
index 72316bed..6529186d 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/IdRowFilter.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/IdRowFilter.cs
@@ -3,7 +3,7 @@
using System.Diagnostics.Eventing.Reader;
using System.Globalization;
-namespace iRLeagueApiCore.Services.ResultService.Calculation;
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
internal sealed class IdRowFilter : RowFilter
{
public IReadOnlyCollection MatchIds;
@@ -40,7 +40,7 @@ public override IEnumerable FilterRows(IEnumerable rows)
{
return (TId?)Convert.ChangeType(idString, typeof(TId?), CultureInfo.InvariantCulture);
}
- catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is ArgumentException)
+ catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is ArgumentException)
{
throw new ArgumentException($"Argument \"{idString}\" is not a valid id");
}
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/RowFilter.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/RowFilter.cs
similarity index 74%
rename from src/iRLeagueApiCore.Services/ResultService/Calculation/RowFilter.cs
rename to src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/RowFilter.cs
index d39f1257..a8bd917b 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/RowFilter.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/Filters/RowFilter.cs
@@ -1,4 +1,4 @@
-namespace iRLeagueApiCore.Services.ResultService.Calculation;
+namespace iRLeagueApiCore.Services.ResultService.Calculation.Filters;
internal abstract class RowFilter
{
diff --git a/src/iRLeagueApiCore.Services/ResultService/Calculation/PointRule.cs b/src/iRLeagueApiCore.Services/ResultService/Calculation/PointRule.cs
index 881c8ba8..664f9519 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Calculation/PointRule.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Calculation/PointRule.cs
@@ -1,4 +1,5 @@
using iRLeagueApiCore.Common.Models;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
using iRLeagueApiCore.Services.ResultService.Models;
namespace iRLeagueApiCore.Services.ResultService.Calculation;
diff --git a/src/iRLeagueApiCore.Services/ResultService/DataAccess/SessionCalculationConfigurationProvider.cs b/src/iRLeagueApiCore.Services/ResultService/DataAccess/SessionCalculationConfigurationProvider.cs
index 8b10797b..84e09d14 100644
--- a/src/iRLeagueApiCore.Services/ResultService/DataAccess/SessionCalculationConfigurationProvider.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/DataAccess/SessionCalculationConfigurationProvider.cs
@@ -1,6 +1,7 @@
using iRLeagueApiCore.Common.Enums;
using iRLeagueApiCore.Common.Models;
using iRLeagueApiCore.Services.ResultService.Calculation;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
using iRLeagueApiCore.Services.ResultService.Extensions;
using iRLeagueApiCore.Services.ResultService.Models;
using iRLeagueDatabaseCore.Models;
@@ -247,6 +248,7 @@ private static FilterGroupRowFilter MapToFilterGroup
condition.FilterValues, condition.Action, allowForEach: allowForEach),
FilterType.Member => new IdRowFilter(condition.FilterValues, x => x.MemberId.GetValueOrDefault(), condition.Action),
FilterType.Team => new IdRowFilter(condition.FilterValues, x => x.TeamId.GetValueOrDefault(), condition.Action),
+ FilterType.Count => new CountRowFilter(condition.Comparator, condition.FilterValues, condition.Action),
_ => null,
};
}
diff --git a/src/iRLeagueApiCore.Services/ResultService/Models/AutoPenaltyConfigurationData.cs b/src/iRLeagueApiCore.Services/ResultService/Models/AutoPenaltyConfigurationData.cs
index e2f6529b..30427e76 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Models/AutoPenaltyConfigurationData.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Models/AutoPenaltyConfigurationData.cs
@@ -1,5 +1,5 @@
using iRLeagueApiCore.Common.Enums;
-using iRLeagueApiCore.Services.ResultService.Calculation;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
namespace iRLeagueApiCore.Services.ResultService.Models;
internal sealed class AutoPenaltyConfigurationData
diff --git a/src/iRLeagueApiCore.Services/ResultService/Models/BonusPointConfiguration.cs b/src/iRLeagueApiCore.Services/ResultService/Models/BonusPointConfiguration.cs
index 82614a25..258feef6 100644
--- a/src/iRLeagueApiCore.Services/ResultService/Models/BonusPointConfiguration.cs
+++ b/src/iRLeagueApiCore.Services/ResultService/Models/BonusPointConfiguration.cs
@@ -1,4 +1,4 @@
-using iRLeagueApiCore.Services.ResultService.Calculation;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
namespace iRLeagueApiCore.Services.ResultService.Models;
internal sealed class BonusPointConfiguration
diff --git a/src/iRLeagueDatabaseCore/iRLeagueDatabaseCore.csproj b/src/iRLeagueDatabaseCore/iRLeagueDatabaseCore.csproj
index 3fd1684d..e3fee16a 100644
--- a/src/iRLeagueDatabaseCore/iRLeagueDatabaseCore.csproj
+++ b/src/iRLeagueDatabaseCore/iRLeagueDatabaseCore.csproj
@@ -28,7 +28,7 @@
net6.0
11
iRLeagueDatabaseCore
- 0.12.1
+ 0.12.2
enable
diff --git a/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/CalculationMockHelper.cs b/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/CalculationMockHelper.cs
index 14b43680..f8214fbd 100644
--- a/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/CalculationMockHelper.cs
+++ b/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/CalculationMockHelper.cs
@@ -1,5 +1,6 @@
using iRLeagueApiCore.Common.Models;
using iRLeagueApiCore.Services.ResultService.Calculation;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
using iRLeagueApiCore.Services.ResultService.Models;
namespace iRLeagueApiCore.Services.Tests.ResultService.Calculation;
diff --git a/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/ColumnValueRowFilterTests.cs b/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/ColumnValueRowFilterTests.cs
index 7dc396a4..d425a89f 100644
--- a/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/ColumnValueRowFilterTests.cs
+++ b/test/iRLeagueApiCore.Services.Tests/ResultService/Calculation/ColumnValueRowFilterTests.cs
@@ -1,6 +1,6 @@
using iRLeagueApiCore.Common.Enums;
using iRLeagueApiCore.Mocking.Extensions;
-using iRLeagueApiCore.Services.ResultService.Calculation;
+using iRLeagueApiCore.Services.ResultService.Calculation.Filters;
using iRLeagueApiCore.Services.ResultService.Extensions;
using iRLeagueApiCore.Services.ResultService.Models;
using System.Globalization;
@@ -11,32 +11,32 @@ public sealed class ColumnValueRowFilterTests
{
private static readonly Fixture fixture = new();
- private static IEnumerable