Release v1.0.0 #16

Merged
Wesser merged 492 commits from release/v1.0.0 into master 2025-02-12 09:19:32 +03:00
298 changed files with 15620 additions and 99 deletions
Showing only changes of commit ce6b0f2673 - Show all commits
.editorconfig.env
.gitea/workflows
.gitignore
ApiDto
Backend.slnDockerfile
Endpoint
Backend.http
Common
Configuration
Controllers
Endpoint.csprojProgram.cs
Sync
WeatherForecast.cs
wwwroot
css
README.md
Security
SqlData
Application
Application.csproj
Common
Cqrs
Campus
Discipline
Faculty
Group
LectureHall
Professor
Schedule
TypeOfOccupation
DependencyInjection.cs
Interfaces
Domain
Migrations
Persistence
nuget.config

@ -0,0 +1,69 @@
using Cronos;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Mirea.Api.Endpoint.Common.Services;
public static class CronUpdateSkipService
{
public static ScheduleSettings.CronUpdateSkip Get(this Dto.Common.CronUpdateSkip date)
{
if (date.Date.HasValue)
return new ScheduleSettings.CronUpdateSkip(date.Date.Value);
if (date is { Start: not null, End: not null })
return new ScheduleSettings.CronUpdateSkip(date.Start.Value, date.End.Value);
throw new ArgumentException("It is impossible to create a structure because it has incorrect values.");
}
public static List<ScheduleSettings.CronUpdateSkip> Filter(this List<ScheduleSettings.CronUpdateSkip> data, DateOnly? currentDate = null)
{
currentDate ??= DateOnly.FromDateTime(DateTime.Now);
return data.OrderBy(x => x.End ?? x.Date)
.Where(x => x.Date == currentDate || (x.Start <= currentDate && x.End >= currentDate))
.ToList();
}
public static List<ScheduleSettings.CronUpdateSkip> Filter(this List<ScheduleSettings.CronUpdateSkip> data, DateTime? currentDate = null) =>
data.Filter(DateOnly.FromDateTime(currentDate ?? DateTime.Now));
public static List<DateTimeOffset> GetNextTask(this List<ScheduleSettings.CronUpdateSkip> data,
CronExpression expression, int depth = 1, DateOnly? currentDate = null)
{
if (depth <= 0)
return [];
currentDate ??= DateOnly.FromDateTime(DateTime.UtcNow);
DateTimeOffset nextRunTime = currentDate.Value.ToDateTime(new TimeOnly(0, 0, 0));
List<DateTimeOffset> result = [];
do
{
var lastSkip = data.Filter(nextRunTime.DateTime).LastOrDefault();
if (lastSkip is { Start: not null, End: not null })
nextRunTime = new DateTimeOffset(lastSkip.End.Value.AddDays(1), new TimeOnly(0, 0, 0), TimeSpan.Zero);
else if (lastSkip.Date.HasValue)
nextRunTime = new DateTimeOffset(lastSkip.Date.Value.AddDays(1), new TimeOnly(0, 0, 0), TimeSpan.Zero);
var next = expression.GetNextOccurrence(nextRunTime, TimeZoneInfo.Local);
if (!next.HasValue)
return result;
nextRunTime = next.Value;
if (!data.Filter(nextRunTime.DateTime).Any())
continue;
result.Add(nextRunTime);
nextRunTime = nextRunTime.AddMinutes(1);
} while (result.Count < depth);
return result;
}
}

@ -5,8 +5,11 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings;
using Mirea.Api.Endpoint.Sync;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -16,6 +19,7 @@ public class ScheduleSyncService : IHostedService, IDisposable
{
private Timer? _timer;
private string _cronUpdate;
private List<ScheduleSettings.CronUpdateSkip> _cronUpdateSkip;
private readonly ILogger<ScheduleSyncService> _logger;
private CancellationTokenSource _cancellationTokenSource = new();
private readonly IServiceProvider _serviceProvider;
@ -26,15 +30,28 @@ public class ScheduleSyncService : IHostedService, IDisposable
_logger = logger;
_serviceProvider = serviceProvider;
_cronUpdate = generalConfigMonitor.CurrentValue.ScheduleSettings!.CronUpdateSchedule;
_cronUpdateSkip = generalConfigMonitor.CurrentValue.ScheduleSettings!.CronUpdateSkipDateList;
ScheduleSyncManager.OnForceSyncRequested += OnForceSyncRequested;
_onChangeUpdateCron = generalConfigMonitor.OnChange((config) =>
{
if (config.ScheduleSettings?.CronUpdateSchedule == null || _cronUpdate == config.ScheduleSettings.CronUpdateSchedule)
return;
var updated = false;
if (config.ScheduleSettings?.CronUpdateSchedule != null && _cronUpdate != config.ScheduleSettings.CronUpdateSchedule)
{
_cronUpdate = config.ScheduleSettings.CronUpdateSchedule;
updated = true;
}
_cronUpdate = config.ScheduleSettings.CronUpdateSchedule;
OnUpdateIntervalRequested();
if (config.ScheduleSettings?.CronUpdateSkipDateList != null && !config.ScheduleSettings.CronUpdateSkipDateList.SequenceEqual(_cronUpdateSkip))
{
_cronUpdateSkip = config.ScheduleSettings.CronUpdateSkipDateList
.OrderBy(x => x.End ?? x.Date)
.ToList();
updated = true;
}
if (updated)
OnUpdateIntervalRequested();
});
}
@ -65,17 +82,20 @@ public class ScheduleSyncService : IHostedService, IDisposable
return;
}
var nextRunTime = CronExpression.Parse(_cronUpdate).GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);
var expression = CronExpression.Parse(_cronUpdate);
if (!nextRunTime.HasValue)
var nextRunTime = _cronUpdateSkip.GetNextTask(expression).FirstOrDefault();
if (nextRunTime == default)
{
_logger.LogWarning("No next run time found. The task will not be scheduled. Timezone: {TimeZone}", TimeZoneInfo.Local.DisplayName);
_logger.LogWarning("No next run time found. The task will not be scheduled. Timezone: {TimeZone}",
TimeZoneInfo.Local.DisplayName);
return;
}
_logger.LogInformation("Next task run in {Time}", nextRunTime.Value.ToString("G"));
_logger.LogInformation("Next task run in {Time}", nextRunTime.ToString("G"));
var delay = (nextRunTime.Value - DateTimeOffset.Now).TotalMilliseconds;
var delay = (nextRunTime - DateTimeOffset.Now).TotalMilliseconds;
// The chance is small, but it's better to check
if (delay <= 0)

@ -31,9 +31,33 @@ public class ScheduleSettings : IIsConfigured
public PairPeriodTime(Dto.Common.PairPeriodTime time) : this(time.Start, time.End) { }
}
public record struct CronUpdateSkip
{
public DateOnly? Start { get; set; }
public DateOnly? End { get; set; }
public DateOnly? Date { get; set; }
public CronUpdateSkip(DateOnly d1, DateOnly d2)
{
if (d1 > d2)
{
Start = d2;
End = d1;
}
else
{
Start = d1;
End = d2;
}
}
public CronUpdateSkip(DateOnly d1) => Date = d1;
}
public required string CronUpdateSchedule { get; set; }
public DateOnly StartTerm { get; set; }
public required IDictionary<int, PairPeriodTime> PairPeriod { get; set; }
public List<CronUpdateSkip> CronUpdateSkipDateList { get; set; } = [];
public bool IsConfigured()
{