1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-16 21:42:55 +08:00

Implement ranked and submitted date filtering

This commit is contained in:
WitherFlower 2024-10-08 13:46:58 +02:00
parent f8e43fd8d3
commit 2369e98cfc
3 changed files with 189 additions and 0 deletions

View File

@ -66,6 +66,8 @@ namespace osu.Game.Screens.Select.Carousel
match &= !criteria.OverallDifficulty.HasFilter || criteria.OverallDifficulty.IsInRange(BeatmapInfo.Difficulty.OverallDifficulty); match &= !criteria.OverallDifficulty.HasFilter || criteria.OverallDifficulty.IsInRange(BeatmapInfo.Difficulty.OverallDifficulty);
match &= !criteria.Length.HasFilter || criteria.Length.IsInRange(BeatmapInfo.Length); match &= !criteria.Length.HasFilter || criteria.Length.IsInRange(BeatmapInfo.Length);
match &= !criteria.LastPlayed.HasFilter || criteria.LastPlayed.IsInRange(BeatmapInfo.LastPlayed ?? DateTimeOffset.MinValue); match &= !criteria.LastPlayed.HasFilter || criteria.LastPlayed.IsInRange(BeatmapInfo.LastPlayed ?? DateTimeOffset.MinValue);
match &= !criteria.DateRanked.HasFilter || criteria.DateRanked.IsInRange(BeatmapInfo.BeatmapSet?.DateRanked ?? DateTimeOffset.MinValue);
match &= !criteria.DateSubmitted.HasFilter || criteria.DateSubmitted.IsInRange(BeatmapInfo.BeatmapSet?.DateSubmitted ?? DateTimeOffset.MinValue);
match &= !criteria.BPM.HasFilter || criteria.BPM.IsInRange(BeatmapInfo.BPM); match &= !criteria.BPM.HasFilter || criteria.BPM.IsInRange(BeatmapInfo.BPM);
match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor); match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor);

View File

@ -37,6 +37,8 @@ namespace osu.Game.Screens.Select
public OptionalRange<int> BeatDivisor; public OptionalRange<int> BeatDivisor;
public OptionalSet<BeatmapOnlineStatus> OnlineStatus = new OptionalSet<BeatmapOnlineStatus>(); public OptionalSet<BeatmapOnlineStatus> OnlineStatus = new OptionalSet<BeatmapOnlineStatus>();
public OptionalRange<DateTimeOffset> LastPlayed; public OptionalRange<DateTimeOffset> LastPlayed;
public OptionalRange<DateTimeOffset> DateRanked;
public OptionalRange<DateTimeOffset> DateSubmitted;
public OptionalTextFilter Creator; public OptionalTextFilter Creator;
public OptionalTextFilter Artist; public OptionalTextFilter Artist;
public OptionalTextFilter Title; public OptionalTextFilter Title;

View File

@ -65,6 +65,12 @@ namespace osu.Game.Screens.Select
case "lastplayed": case "lastplayed":
return tryUpdateDateAgoRange(ref criteria.LastPlayed, op, value); return tryUpdateDateAgoRange(ref criteria.LastPlayed, op, value);
case "ranked":
return tryUpdateRankedDateRange(ref criteria.DateRanked, op, value);
case "submitted":
return tryUpdateRankedDateRange(ref criteria.DateSubmitted, op, value);
case "played": case "played":
if (!tryParseBool(value, out bool played)) if (!tryParseBool(value, out bool played))
return false; return false;
@ -592,5 +598,184 @@ namespace osu.Game.Screens.Select
return tryUpdateCriteriaRange(ref dateRange, op, dateTimeOffset.Value); return tryUpdateCriteriaRange(ref dateRange, op, dateTimeOffset.Value);
} }
/// <summary>
/// Helper function for building a UTC date from only the year, month and day.
/// UTC is used to keep consistent search results with osu!web.
/// </summary>
private static DateTimeOffset dateTimeOffsetFromDateOnly(int year, int month, int day) =>
new DateTimeOffset(year, month, day, 0, 0, 0, TimeSpan.Zero);
/// <summary>
/// Parses a string containing a ranked or submitted date filter.
/// Returns a boolean depending on whether parsing was successful or not.
/// Accepted dates are in the formats `yyyy`, `yyyy-mm` and `yyyy-mm-dd`. Leading zeros are accepted.
/// </summary>
/// <param name="dateRange">The <see cref="FilterCriteria.OptionalRange{DateTimeOffset}"/> to store the parsed data into, if successful.</param>
/// <param name="op">The operator of the filtering query</param>
/// <param name="val">The string value to attempt parsing for.</param>
private static bool tryUpdateRankedDateRange(ref FilterCriteria.OptionalRange<DateTimeOffset> dateRange, Operator op, string val)
{
GroupCollection? match = null;
match ??= tryMatchRegex(val, @"^(?<year>\d+)(-(?<month>\d+)(-(?<day>\d+))?)?$");
if (match == null)
return false;
int? year = null;
int? month = null;
int? day = null;
List<string> keys = new List<string> { @"year", @"month", @"day" };
foreach (string key in keys)
{
if (!match.TryGetValue(key, out var group) || !group.Success)
continue;
if (group.Success)
{
if (!tryParseDoubleWithPoint(group.Value, out double value))
return false;
switch (key)
{
case @"year":
year = (int)value;
break;
case @"month":
month = (int)value;
break;
case @"day":
day = (int)value;
break;
}
}
}
if (year == null)
{
return false;
}
try
{
DateTimeOffset dateTimeOffset;
switch (op)
{
case Operator.Less:
if (month == null)
{
month = 1;
day = 1;
}
if (day == null)
day = 1;
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
return tryUpdateCriteriaRange(ref dateRange, op, dateTimeOffset);
case Operator.LessOrEqual:
if (month == null)
{
month = 1;
day = 1;
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddYears(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.Less, dateTimeOffset);
}
if (day == null)
{
day = 1;
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddMonths(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.Less, dateTimeOffset);
}
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddDays(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.Less, dateTimeOffset);
case Operator.GreaterOrEqual:
if (month == null)
{
month = 1;
day = 1;
}
if (day == null)
day = 1;
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
return tryUpdateCriteriaRange(ref dateRange, op, dateTimeOffset);
case Operator.Greater:
if (month == null)
{
month = 1;
day = 1;
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddYears(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, dateTimeOffset);
}
if (day == null)
{
day = 1;
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddMonths(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, dateTimeOffset);
}
dateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddDays(1);
return tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, dateTimeOffset);
case Operator.Equal:
DateTimeOffset minDateTimeOffset;
DateTimeOffset maxDateTimeOffset;
if (month == null)
{
month = 1;
day = 1;
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddYears(1);
return (
tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, minDateTimeOffset)
&&
tryUpdateCriteriaRange(ref dateRange, Operator.Less, maxDateTimeOffset)
);
}
if (day == null)
{
day = 1;
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddMonths(1);
return (
tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, minDateTimeOffset)
&&
tryUpdateCriteriaRange(ref dateRange, Operator.Less, maxDateTimeOffset)
);
}
minDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value);
maxDateTimeOffset = dateTimeOffsetFromDateOnly(year.Value, month.Value, day.Value).AddDays(1);
return (
tryUpdateCriteriaRange(ref dateRange, Operator.GreaterOrEqual, minDateTimeOffset)
&&
tryUpdateCriteriaRange(ref dateRange, Operator.Less, maxDateTimeOffset)
);
default:
return false;
}
}
catch (ArgumentOutOfRangeException)
{
return false;
}
}
} }
} }