Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix partly broken import of excluded match dates #196

Merged
merged 1 commit into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Axuno.Tools/GermanHoliday.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class GermanHoliday
/// <summary>
/// CTOR.
/// </summary>
/// <param name="id">Nullable HolidayId</param>
/// <param name="id">A well-known <see cref="GermanHolidays.Id"/> or <see langword="null"/> </param>
/// <param name="type">HolidayType</param>
/// <param name="name">Holiday name</param>
/// <param name="calcDateFunc">Function for date calculation</param>
Expand All @@ -26,7 +26,7 @@ internal GermanHoliday(GermanHolidays.Id? id, GermanHolidays.Type type, string n
}

/// <summary>
/// Constructor for usage from inside of class GermanHolidays.
/// Constructor for usage from class GermanHolidays.
/// </summary>
/// <param name="id">HolidayId</param>
/// <param name="type">HolidayType</param>
Expand Down
18 changes: 12 additions & 6 deletions Axuno.Tools/GermanHolidays.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@ private DateTime GetEasterSunday()
int tA, tB, tC, tD, tE; // tables A to E

var firstDigits = Year / 100;
var remainding19 = Year % 19;
var remainder19 = Year % 19;

// Calculate Paschal Full Moon
var temp = (firstDigits - 15) / 2 + 202 - 11 * remainding19;
var temp = (firstDigits - 15) / 2 + 202 - 11 * remainder19;
switch (firstDigits)
{
case 21:
Expand Down Expand Up @@ -168,7 +168,7 @@ private DateTime GetEasterSunday()
tA = temp + 21;
if (temp == 29)
tA -= 1;
if (temp == 28 && remainding19 > 10)
if (temp == 28 && remainder19 > 10)
tA -= 1;

// Calculate next Sunday
Expand Down Expand Up @@ -206,7 +206,7 @@ private DateTime GetEasterSunday()
/// <returns>Advent date</returns>
private DateTime GetAdventDate(int num)
{
if (num < 1 || num > 4)
if (num is < 1 or > 4)
throw new InvalidOperationException("Only Advents 1 to 4 are allowed.");

// 4th Advent is the latest Sunday before 25th December
Expand Down Expand Up @@ -294,7 +294,7 @@ private DateTime GetMuttertag()

switch (holidayId)
{
// general public holidays, are those where all federal states have the same public holidays defined
// national holidays, are those where all federal states have the same public holidays defined
case Id.Neujahr:
case Id.KarFreitag:
case Id.OsterSonntag:
Expand Down Expand Up @@ -458,6 +458,12 @@ private void ProcessHoliday(Id? holidayId, ActionType action, DateTime dateFrom,

ValidateDateRange(action, dateFrom, dateTo);

var dateIsSet = dateFrom != DateTime.MinValue && dateTo != DateTime.MinValue;
if (holidayId.HasValue && !dateIsSet && Exists(h => h.Id == holidayId))
{
dateFrom = dateTo = this[holidayId.Value]!.CalcDateFunc();
}

while (dateFrom <= dateTo)
{
var tmpDateFrom = dateFrom; // no capture of modified closure
Expand Down Expand Up @@ -508,7 +514,7 @@ private void ReplaceHoliday(Id? holidayId, GermanHoliday newHoliday)
{
if (!holidayId.HasValue || this[holidayId.Value] == null)
throw new InvalidOperationException("Holiday to replace not found.");

var existingHoliday = this[holidayId.Value]!;

// Replace the existing holiday with the new one
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding="UTF-8" standalone='yes'?>
<Holidays>
<Holiday Action="add" Id="Neujahr">
<Type>Public</Type>
<Name>Sample New Year</Name>
<DateFrom>2024-01-01</DateFrom>
<DateTo>2024-01-01</DateTo>
<PublicHolidayStateIds>
<StateId>Bayern</StateId>
</PublicHolidayStateIds>
</Holiday>
</Holidays>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version='1.0' encoding="UTF-8" standalone='yes'?>
<Holidays>
<Holiday Action="remove" Id="Neujahr">
<Type>Public</Type>
<DateFrom>2024-01-01</DateFrom>
<DateTo>2024-01-01</DateTo>
</Holiday>
</Holidays>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using NUnit.Framework;
using TournamentManager.Importers.ExcludeDates;

namespace TournamentManager.Tests.Importers.ExcludeDates;

[TestFixture]
internal class EnumerableValueTupleExtensionsTests
{
[Test]
public void IntegerRanges_ShouldBeConsecutive()
{
// Unsorted list of integers with missing numbers
var intList = new List<int>
{
// group #3: number 7 missing
10,
9,
8,
// group #1
2,
3,
4,
// group #2: number 5 missing
6
};

var ranges = intList.ConsecutiveRanges().ToList();
Assert.Multiple(() =>
{
Assert.That(ranges, Has.Count.EqualTo(3));
Assert.That(ranges[0], Is.EqualTo((2, 4)));
Assert.That(ranges[1], Is.EqualTo((6, 6)));
Assert.That(ranges[2], Is.EqualTo((8, 10)));
});
}

[Test]
public void DateOnlyRanges_ShouldBeConsecutive()
{
// Unsorted list of DateTime with missing dates
var dateOnlyList = new List<DateOnly>
{
// group #3: number 7 Oct missing
new (2024, 10, 10),
new (2024, 10, 9),
new (2024, 10, 8),
// group #1
new (2024, 10, 2),
new (2024, 10, 3),
new (2024, 10, 4),
// group #2: number 5 Oct missing
new (2024, 10, 6)
};

var ranges = dateOnlyList.ConsecutiveRanges().ToList();
Assert.Multiple(() =>
{
Assert.That(ranges, Has.Count.EqualTo(3));
Assert.That(ranges[0], Is.EqualTo((new DateOnly(2024, 10, 2), new DateOnly(2024, 10, 4))));
Assert.That(ranges[1], Is.EqualTo((new DateOnly(2024, 10, 6), new DateOnly(2024, 10, 6))));
Assert.That(ranges[2], Is.EqualTo((new DateOnly(2024, 10, 8), new DateOnly(2024, 10, 10))));
});
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ public void Import_HolidaysInAllFederalStates(DateTime from, DateTime to, int ex
// using CET as time zone
var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter(
"Europe/Berlin", CultureInfo.CurrentCulture);
var holidayFilter = new Predicate<GermanHoliday>(h => h.Type == GermanHolidays.Type.Public && h.PublicHolidayStateIds.Count == new GermanFederalStates().Count);
var hImporter = new GermanHolidayImporter(null, holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);
var holidayFilter = new Predicate<GermanHoliday>(h =>
h.Type == GermanHolidays.Type.Public && h.PublicHolidayStateIds.Count == new GermanFederalStates().Count);
var hImporter =
new GermanHolidayImporter(holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);

var imported = hImporter.Import(new DateTimePeriod(from, to)).ToList();

Expand All @@ -33,8 +35,10 @@ public void Import_HolidaysInBavaria(DateTime from, DateTime to, int expectedCou
// using CET as time zone
var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter(
"Europe/Berlin", CultureInfo.CurrentCulture);
var holidayFilter = new Predicate<GermanHoliday>(h => h.Type == GermanHolidays.Type.Public && h.PublicHolidayStateIds.Contains(GermanFederalStates.Id.Bayern));
var hImporter = new GermanHolidayImporter(null, holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);
var holidayFilter = new Predicate<GermanHoliday>(h =>
h.Type == GermanHolidays.Type.Public && h.PublicHolidayStateIds.Contains(GermanFederalStates.Id.Bayern));
var hImporter =
new GermanHolidayImporter(holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);

var imported = hImporter.Import(new DateTimePeriod(from, to)).ToList();

Expand All @@ -52,34 +56,66 @@ public void Import_Holidays_Volleyball_League_Augsburg(DateTime from, DateTime t
var holidayFilter = new Predicate<GermanHoliday>(
h =>
h.Type == GermanHolidays.Type.Public &&
h.PublicHolidayStateIds.Contains(GermanFederalStates.Id.Bayern)
h.PublicHolidayStateIds.Contains(GermanFederalStates.Id.Bayern)
// add 5 more local holidays
|| h.Id == GermanHolidays.Id.AugsburgerFriedensfest || h.Id == GermanHolidays.Id.HeiligerAbend ||
h.Id == GermanHolidays.Id.RosenMontag || h.Id == GermanHolidays.Id.FaschingsDienstag ||
h.Id == GermanHolidays.Id.Silvester);
h.Id == GermanHolidays.Id.RosenMontag || h.Id == GermanHolidays.Id.FaschingsDienstag ||
h.Id == GermanHolidays.Id.Silvester);

var hImporter = new GermanHolidayImporter(null, holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);
var hImporter =
new GermanHolidayImporter(holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);

var imported = hImporter.Import(new DateTimePeriod(from, to)).ToList();

Assert.That(imported, Has.Count.EqualTo(expectedCount));
}
[Ignore("Tests fails and requires refactoring of GermanHolidays", Until = "2024-09-30")]
[TestCase("2019-09-01", "2020-06-30", 9)]

[TestCase("2019-09-01", "2020-06-30", 6)]
public void Import_With_Custom_School_Holidays(DateTime from, DateTime to, int expectedCount)
{
// Note: Custom_Holidays_Sample.xml contains 6 school holidays.
// But as the command for them is "Merge", existing holidays persist unchanged,
// and additional holiday periods are added.
var customHolidayFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "Custom_Holidays_Sample.xml");
var customHolidayFilePath =
Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "Custom_Holidays_Sample.xml");
// using CET as time zone
var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter(
"Europe/Berlin", CultureInfo.CurrentCulture);

var holidayFilter = new Predicate<GermanHoliday>(h => h.Type == GermanHolidays.Type.School);
var hImporter = new GermanHolidayImporter(customHolidayFilePath, holidayFilter, tzConverter, NullLogger<GermanHolidayImporter>.Instance);
var hImporter = new GermanHolidayImporter(customHolidayFilePath, holidayFilter, tzConverter,
NullLogger<GermanHolidayImporter>.Instance);

// The period from 2019-09-01 to 2020-06-30 contains 6 imported school holidays.
var imported = hImporter.Import(new DateTimePeriod(from, to)).ToList();

Assert.That(imported, Has.Count.EqualTo(expectedCount));
}

[Test]
public void Add_And_Remove_Holiday()
{
// Note: Custom_Holidays_Sample.xml contains 6 school holidays.
// But as the command for them is "Merge", existing holidays persist unchanged,
// and additional holiday periods are added.
var addHolidayFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "Single_Holiday_To_Add.xml");
var removeHolidayFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "Single_Holiday_To_Remove.xml");
// using CET as time zone
var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter(
"Europe/Berlin", CultureInfo.CurrentCulture);

var holidayFilter = new Predicate<GermanHoliday>(h => h.Type == GermanHolidays.Type.Public);
var hImporter = new GermanHolidayImporter(new[] { removeHolidayFilePath, addHolidayFilePath }, holidayFilter,
tzConverter, NullLogger<GermanHolidayImporter>.Instance);

var imported = hImporter.Import(new DateTimePeriod(new DateTime(2024, 1, 1), new DateTime(2024, 1, 1))).ToList();


Assert.Multiple(() =>
{
Assert.That(imported, Has.Count.EqualTo(1));
Assert.That(imported[0].Reason, Is.EqualTo("Sample New Year"));
});

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
<ProjectReference Include="..\TournamentManager\TournamentManager.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Assets\Single_Holiday_To_Remove.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Assets\Single_Holiday_To_Add.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Assets\Custom_Holidays_Sample.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down

This file was deleted.

Loading