using Asp.Versioning; using Cronos; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Mirea.Api.DataAccess.Persistence; using Mirea.Api.Dto.Common; using Mirea.Api.Dto.Responses.Configuration; using Mirea.Api.Endpoint.Common.Exceptions; using Mirea.Api.Endpoint.Common.MapperDto; using Mirea.Api.Endpoint.Common.Services; using Mirea.Api.Endpoint.Configuration.Model; using Mirea.Api.Endpoint.Configuration.Model.GeneralSettings; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; namespace Mirea.Api.Endpoint.Controllers.V1.Configuration; [ApiVersion("1.0")] public class ScheduleController(ILogger logger, IOptionsSnapshot config, UberDbContext dbContext) : ConfigurationBaseController { /// /// Retrieves the cron update schedule and calculates the next scheduled tasks based on the provided depth. /// /// The depth of the next tasks to retrieve. /// Cron expression and the list of next scheduled task dates. [HttpGet("CronUpdateSchedule")] public ActionResult CronUpdateSchedule([FromQuery][Range(0, 5)] int depth = 2) { var cronExpression = CronExpression.Parse(config.Value.ScheduleSettings!.CronUpdateSchedule); var nextTasks = config.Value.ScheduleSettings!.CronUpdateSkipDateList.GetNextTask(cronExpression, depth); return new CronUpdateScheduleResponse() { Cron = config.Value.ScheduleSettings!.CronUpdateSchedule, NextStart = nextTasks.Select(x => DateTime.SpecifyKind(x.DateTime, DateTimeKind.Local)).ToList() }; } /// /// Updates the cron update schedule with the provided cron expression. /// /// The cron expression to set as the new schedule. /// Cron expression and the list of next scheduled task dates. /// Thrown if the provided cron expression is invalid. [HttpPost("CronUpdateSchedule")] public ActionResult CronUpdateSchedule([FromBody] string cron) { cron = cron.Trim(); if (!CronExpression.TryParse(cron, CronFormat.Standard, out _)) throw new ControllerArgumentException("Incorrect cron value."); if (config.Value.ScheduleSettings!.CronUpdateSchedule == cron) return Ok(CronUpdateSchedule()); config.Value.ScheduleSettings!.CronUpdateSchedule = cron; config.Value.SaveSetting(); return Ok(CronUpdateSchedule()); } /// /// Retrieves the start term date from the configuration. /// /// Start term date. [HttpGet("StartTerm")] public ActionResult StartTerm() => config.Value.ScheduleSettings!.StartTerm; /// /// Updates the start term date in the configuration. /// /// The new start term date to set. /// If true, forces an update by deleting all existing lessons. /// Success or failure. /// Thrown if the start term date is more than 6 months in the past or future. [HttpPost("StartTerm")] public ActionResult StartTerm([FromBody] DateOnly startTerm, [FromQuery] bool force = false) { var differentByTime = DateTime.Now - startTerm.ToDateTime(new TimeOnly(0, 0, 0)); if (differentByTime > TimeSpan.FromDays(190) || differentByTime.Multiply(-1) > TimeSpan.FromDays(190)) throw new ControllerArgumentException("The semester can't start more than 6 months from now, and it can't have started more than 6 months ago either."); config.Value.ScheduleSettings!.StartTerm = startTerm; config.Value.SaveSetting(); if (!force) return Ok(); logger.LogWarning("A force update is being performed at the beginning of the semester (all classes will be deleted)."); dbContext.Lessons.RemoveRange(dbContext.Lessons.ToList()); dbContext.SaveChanges(); return Ok(); } /// /// Retrieves the list of cron update skip dates filtered by the current date. /// /// Cron update skip dates. [HttpGet("CronUpdateSkip")] public ActionResult> CronUpdateSkip() => config.Value.ScheduleSettings!.CronUpdateSkipDateList.Filter(DateTime.Now).ConvertToDto(); /// /// Updates the list of cron update skip dates in the configuration. /// /// The list of cron update skip dates to set. /// Success or failure. /// Thrown if the provided list of cron update skip dates is invalid. [HttpPost("CronUpdateSkip")] public ActionResult CronUpdateSkip([FromBody] List cronUpdateDate) { List result; try { result = cronUpdateDate.ConvertFromDto(); } catch (ArgumentException ex) { throw new ControllerArgumentException(ex.Message); } config.Value.ScheduleSettings!.CronUpdateSkipDateList = result.Filter(DateTime.Now); config.Value.SaveSetting(); return Ok(); } }