Fix parsing shift times

This commit is contained in:
Claudio Schaad 2025-09-03 22:09:14 +02:00
parent 803c390c81
commit 85a4ecb2aa
5 changed files with 99 additions and 49 deletions

View file

@ -15,7 +15,7 @@ namespace ShiftScheduler.Client.Pages
private Dictionary<DateTime, string> SelectedSchedule { get; set; } = new(); private Dictionary<DateTime, string> SelectedSchedule { get; set; } = new();
private Dictionary<DateTime, ShiftWithTransport> SelectedShiftsWithTransport { get; set; } = new(); private Dictionary<DateTime, ShiftWithTransport> SelectedShiftsWithTransport { get; set; } = new();
private bool _isCurrentMonth = false; private bool _isCurrentMonth = true;
private bool _isLoadingTransport = false; private bool _isLoadingTransport = false;
private bool _isLoadingInitial = false; private bool _isLoadingInitial = false;
private bool _showConfigDialog = false; private bool _showConfigDialog = false;

View file

@ -45,14 +45,15 @@ namespace ShiftScheduler.Server.Controllers
var transportConfig = _configurationService.GetTransportConfiguration(); var transportConfig = _configurationService.GetTransportConfiguration();
TransportConnection? morningTransport = null; TransportConnection? morningTransport = null;
TransportConnection? afternoonTransport = null; TransportConnection? afternoonTransport = null;
var shifTimes = _configurationService.ParseShiftTimes(request.Date, shift);
// Get transport for morning shift if it has morning time // Get transport for morning shift if it has morning time
if (!string.IsNullOrEmpty(shift.MorningTime)) if (!string.IsNullOrEmpty(shift.MorningTime))
{ {
var morningStartTime = ParseShiftTime(request.Date, shift.MorningTime); if (shifTimes.MorningStart.HasValue)
if (morningStartTime.HasValue)
{ {
morningTransport = await _transportService.GetConnectionAsync(morningStartTime.Value); morningTransport = await _transportService.GetConnectionAsync(shifTimes.MorningStart.Value);
} }
} }
@ -65,22 +66,18 @@ namespace ShiftScheduler.Server.Controllers
// If both morning and afternoon shifts exist, check break duration // If both morning and afternoon shifts exist, check break duration
if (!string.IsNullOrEmpty(shift.MorningTime) && !string.IsNullOrEmpty(shift.AfternoonTime)) if (!string.IsNullOrEmpty(shift.MorningTime) && !string.IsNullOrEmpty(shift.AfternoonTime))
{ {
var morningEndTime = ParseShiftEndTime(request.Date, shift.MorningTime); if (shifTimes.MorningEnd.HasValue && shifTimes.AfternoonStart.HasValue)
var afternoonStartTime = ParseShiftTime(request.Date, shift.AfternoonTime);
if (morningEndTime.HasValue && afternoonStartTime.HasValue)
{ {
var breakDurationMinutes = (afternoonStartTime.Value - morningEndTime.Value).TotalMinutes; var breakDurationMinutes = (shifTimes.AfternoonStart.Value - shifTimes.MorningEnd.Value).TotalMinutes;
shouldLoadAfternoonTransport = breakDurationMinutes >= transportConfig.MinBreakMinutes; shouldLoadAfternoonTransport = breakDurationMinutes >= transportConfig.MinBreakMinutes;
} }
} }
if (shouldLoadAfternoonTransport) if (shouldLoadAfternoonTransport)
{ {
var afternoonStartTime = ParseShiftTime(request.Date, shift.AfternoonTime); if (shifTimes.AfternoonStart.HasValue)
if (afternoonStartTime.HasValue)
{ {
afternoonTransport = await _transportService.GetConnectionAsync(afternoonStartTime.Value); afternoonTransport = await _transportService.GetConnectionAsync(shifTimes.AfternoonStart.Value);
} }
} }
} }
@ -96,40 +93,6 @@ namespace ShiftScheduler.Server.Controllers
return Ok(shiftWithTransport); return Ok(shiftWithTransport);
} }
private static DateTime? ParseShiftTime(DateTime date, string timeRange)
{
try
{
var times = timeRange.Split('-');
if (times.Length > 0 && TimeSpan.TryParse(times[0], out var startTime))
{
return date.Add(startTime);
}
}
catch
{
// Ignore parsing errors
}
return null;
}
private static DateTime? ParseShiftEndTime(DateTime date, string timeRange)
{
try
{
var times = timeRange.Split('-');
if (times.Length > 1 && TimeSpan.TryParse(times[1], out var endTime))
{
return date.Add(endTime);
}
}
catch
{
// Ignore parsing errors
}
return null;
}
[HttpPost("export_ics")] [HttpPost("export_ics")]
public IActionResult ExportIcsWithTransport([FromBody] List<ShiftWithTransport> shiftsWithTransport) public IActionResult ExportIcsWithTransport([FromBody] List<ShiftWithTransport> shiftsWithTransport)
{ {

View file

@ -12,12 +12,15 @@ namespace ShiftScheduler.Services
List<Shift> GetShifts(); List<Shift> GetShifts();
TransportConfiguration GetTransportConfiguration(); TransportConfiguration GetTransportConfiguration();
// Schedule persistence methods
Task SaveScheduleAsync(int year, int month, Dictionary<DateTime, string> schedule); Task SaveScheduleAsync(int year, int month, Dictionary<DateTime, string> schedule);
Task<Dictionary<DateTime, string>> LoadScheduleAsync(int year, int month); Task<Dictionary<DateTime, string>> LoadScheduleAsync(int year, int month);
Task DeleteScheduleAsync(int year, int month); Task DeleteScheduleAsync(int year, int month);
ShiftTimes ParseShiftTimes(DateTime date, Shift shift);
} }
public record ShiftTimes(DateTimeOffset? MorningStart, DateTimeOffset? MorningEnd, DateTimeOffset? AfternoonStart, DateTimeOffset? AfternoonEnd);
public class ConfigurationService : IConfigurationService public class ConfigurationService : IConfigurationService
{ {
private ApplicationConfiguration _configuration; private ApplicationConfiguration _configuration;
@ -201,7 +204,45 @@ namespace ShiftScheduler.Services
// Log error if needed, but don't throw // Log error if needed, but don't throw
} }
} }
public ShiftTimes ParseShiftTimes(DateTime date, Shift shift)
{
DateTimeOffset? morningStart = null;
DateTimeOffset? morningEnd = null;
DateTimeOffset? afternoonStart = null;
DateTimeOffset? afternoonEnd = null;
if (!string.IsNullOrEmpty(shift.MorningTime))
{
var times = shift.MorningTime.Split('-');
if (times.Length == 2)
{
var start = $"{date:yyyy-MM-dd}T{times[0]}:00";
var end = $"{date:yyyy-MM-dd}T{times[1]}:00";
morningStart = DateTimeOffset.Parse(start);
morningEnd = DateTimeOffset.Parse(end);
}
}
if (!string.IsNullOrEmpty(shift.AfternoonTime))
{
var times = shift.AfternoonTime.Split('-');
if (times.Length == 2)
{
var start = $"{date:yyyy-MM-dd}T{times[0]}:00";
var end = $"{date:yyyy-MM-dd}T{times[1]}:00";
afternoonStart = DateTimeOffset.Parse(start);
afternoonEnd = DateTimeOffset.Parse(end);
}
}
return new ShiftTimes(
MorningStart: morningStart,
MorningEnd: morningEnd,
AfternoonStart: afternoonStart,
AfternoonEnd: afternoonEnd);
}
private ApplicationConfiguration LoadConfigurationFromFiles(ApplicationConfiguration fallbackConfiguration) private ApplicationConfiguration LoadConfigurationFromFiles(ApplicationConfiguration fallbackConfiguration)
{ {
try try

View file

@ -0,0 +1,47 @@
using ShiftScheduler.Shared;
using Shouldly;
namespace ShiftScheduler.Services.Tests;
public class ConfigurationServiceTests
{
[Fact]
public void ParseShiftTimes_WithMorningAndAfternoon_ShouldReturnValid()
{
var applicationConfig = new ApplicationConfiguration();
var configurationService = new ConfigurationService(applicationConfig);
var shift = new Shift
{
MorningTime = "06:00-12:30",
AfternoonTime = "13:00-17:30"
};
var shiftTimes = configurationService.ParseShiftTimes(DateTime.Parse("2025-07-13T13:00:00"), shift);
shiftTimes.MorningStart.ShouldBe(T("2025-07-13T06:00:00+02:00"));
shiftTimes.MorningEnd.ShouldBe(T("2025-07-13T12:30:00+02:00"));
shiftTimes.AfternoonStart.ShouldBe(T("2025-07-13T13:00:00+02:00"));
shiftTimes.AfternoonEnd.ShouldBe(T("2025-07-13T17:30:00+02:00"));
}
[Fact]
public void ParseShiftTimes_WithMorning_ShouldReturnValid()
{
var applicationConfig = new ApplicationConfiguration();
var configurationService = new ConfigurationService(applicationConfig);
var shift = new Shift
{
MorningTime = "06:00-12:30"
};
var shiftTimes = configurationService.ParseShiftTimes(DateTime.Parse("2025-07-13T13:00:00"), shift);
shiftTimes.MorningStart.ShouldBe(T("2025-07-13T06:00:00+02:00"));
shiftTimes.MorningEnd.ShouldBe(T("2025-07-13T12:30:00+02:00"));
shiftTimes.AfternoonStart.ShouldBeNull();
shiftTimes.AfternoonEnd.ShouldBeNull();
}
private DateTimeOffset T(string dateTimeString)
{
return DateTimeOffset.Parse(dateTimeString);
}
}

View file

@ -9,7 +9,6 @@ public class TransportConnectionCalculatorTests
{ {
private readonly Mock<ILogger> loggerMock = new(); private readonly Mock<ILogger> loggerMock = new();
// Tests for new enhanced logic
[Fact] [Fact]
public void FindBestConnectionEnhanced_WithGoodTimingConnection_ShouldReturnLatestValidConnection() public void FindBestConnectionEnhanced_WithGoodTimingConnection_ShouldReturnLatestValidConnection()
{ {