diff --git a/ginos-gelato/server/Controllers/OrderQueueController.cs b/ginos-gelato/server/Controllers/OrderQueueController.cs new file mode 100644 index 0000000..3be353f --- /dev/null +++ b/ginos-gelato/server/Controllers/OrderQueueController.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNetCore.Mvc; +using GinosGelato.Models; +using GinosGelato.Services; + +namespace GinosGelato.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class OrderQueueController : ControllerBase + { + private readonly OrderQueueService _orderQueueService; + + public OrderQueueController(OrderQueueService orderQueueService) + { + _orderQueueService = orderQueueService; + } + + /// + /// Returns the current queue status, including the number of pending orders + /// and the estimated wait time in minutes. + /// + [HttpGet("status")] + public ActionResult GetQueueStatus() + { + return Ok(_orderQueueService.GetQueueStatus()); + } + + /// + /// Returns the average preparation time in minutes based on completed orders. + /// + [HttpGet("averagetime")] + public ActionResult GetAveragePreparationTime() + { + return Ok(_orderQueueService.GetAveragePreparationTimeMinutes()); + } + + /// + /// Returns all queue entries (pending and completed). + /// + [HttpGet] + public ActionResult> GetQueueEntries() + { + return Ok(_orderQueueService.GetAllEntries()); + } + + /// + /// Marks an order as completed, recording its actual preparation time. + /// + [HttpPost("{orderId}/complete")] + public ActionResult CompleteOrder(int orderId) + { + var success = _orderQueueService.CompleteOrder(orderId); + if (!success) + { + return NotFound(new { message = $"Pending order with id {orderId} not found in queue." }); + } + return NoContent(); + } + } +} diff --git a/ginos-gelato/server/Models/OrderQueueEntry.cs b/ginos-gelato/server/Models/OrderQueueEntry.cs new file mode 100644 index 0000000..14d37dc --- /dev/null +++ b/ginos-gelato/server/Models/OrderQueueEntry.cs @@ -0,0 +1,21 @@ +namespace GinosGelato.Models +{ + public enum OrderQueueStatus + { + Pending, + Completed + } + + public class OrderQueueEntry + { + public int OrderId { get; set; } + public DateTime EnqueuedAt { get; set; } + public DateTime? CompletedAt { get; set; } + public OrderQueueStatus Status { get; set; } = OrderQueueStatus.Pending; + + public double? PreparationTimeMinutes => + CompletedAt.HasValue + ? (CompletedAt.Value - EnqueuedAt).TotalMinutes + : null; + } +} diff --git a/ginos-gelato/server/Program.cs b/ginos-gelato/server/Program.cs index 7746fd5..f738f56 100644 --- a/ginos-gelato/server/Program.cs +++ b/ginos-gelato/server/Program.cs @@ -16,6 +16,7 @@ options.UseInMemoryDatabase("GinosGelatoDb")); // Using in-memory database for simplicity // Add custom services +builder.Services.AddSingleton(); builder.Services.AddScoped(); // Add Swagger/OpenAPI diff --git a/ginos-gelato/server/Services/OrderQueueService.cs b/ginos-gelato/server/Services/OrderQueueService.cs new file mode 100644 index 0000000..1e4e653 --- /dev/null +++ b/ginos-gelato/server/Services/OrderQueueService.cs @@ -0,0 +1,105 @@ +using GinosGelato.Models; + +namespace GinosGelato.Services +{ + public class OrderQueueService + { + private readonly List _queue = new(); + private readonly object _lock = new(); + + // Default estimated preparation time when no historical data is available (minutes) + private const double DefaultPreparationTimeMinutes = 5.0; + + // Maximum number of completed entries to retain for average calculation + private const int MaxCompletedEntries = 100; + + public void EnqueueOrder(int orderId) + { + lock (_lock) + { + _queue.Add(new OrderQueueEntry + { + OrderId = orderId, + EnqueuedAt = DateTime.UtcNow, + Status = OrderQueueStatus.Pending + }); + } + } + + public bool CompleteOrder(int orderId) + { + lock (_lock) + { + var entry = _queue.FirstOrDefault(e => e.OrderId == orderId && e.Status == OrderQueueStatus.Pending); + if (entry == null) + return false; + + entry.CompletedAt = DateTime.UtcNow; + entry.Status = OrderQueueStatus.Completed; + + // Prune oldest completed entries beyond the retention limit + var completed = _queue.Where(e => e.Status == OrderQueueStatus.Completed).ToList(); + while (completed.Count > MaxCompletedEntries) + { + var oldest = completed[0]; + _queue.Remove(oldest); + completed.RemoveAt(0); + } + + return true; + } + } + + public double GetAveragePreparationTimeMinutes() + { + lock (_lock) + { + return CalculateAveragePreparationTime(); + } + } + + public QueueStatusResponse GetQueueStatus() + { + lock (_lock) + { + var pendingCount = _queue.Count(e => e.Status == OrderQueueStatus.Pending); + var averagePrep = CalculateAveragePreparationTime(); + + return new QueueStatusResponse + { + PendingOrderCount = pendingCount, + AveragePreparationTimeMinutes = Math.Round(averagePrep, 1), + EstimatedWaitMinutes = Math.Round(pendingCount * averagePrep, 1) + }; + } + } + + public IReadOnlyList GetAllEntries() + { + lock (_lock) + { + return _queue.ToList().AsReadOnly(); + } + } + + // Must be called within a lock + private double CalculateAveragePreparationTime() + { + var completedTimes = _queue + .Where(e => e.Status == OrderQueueStatus.Completed && e.PreparationTimeMinutes.HasValue) + .Select(e => e.PreparationTimeMinutes!.Value) + .ToList(); + + return completedTimes.Count > 0 + ? completedTimes.Average() + : DefaultPreparationTimeMinutes; + } + } + + public class QueueStatusResponse + { + public int PendingOrderCount { get; set; } + public double AveragePreparationTimeMinutes { get; set; } + public double EstimatedWaitMinutes { get; set; } + } +} diff --git a/ginos-gelato/server/Services/OrderService.cs b/ginos-gelato/server/Services/OrderService.cs index cc87625..2b51940 100644 --- a/ginos-gelato/server/Services/OrderService.cs +++ b/ginos-gelato/server/Services/OrderService.cs @@ -10,10 +10,12 @@ namespace GinosGelato.Services public class OrderService { private readonly ApplicationDbContext _context; + private readonly OrderQueueService _orderQueueService; - public OrderService(ApplicationDbContext context) + public OrderService(ApplicationDbContext context, OrderQueueService orderQueueService) { _context = context; + _orderQueueService = orderQueueService; } public async Task CreateOrderAsync(List iceCreams) @@ -28,6 +30,8 @@ public async Task CreateOrderAsync(List iceCreams) _context.Orders.Add(order); await _context.SaveChangesAsync(); + _orderQueueService.EnqueueOrder(order.Id); + return order; }