From 12d0eeb3b4f37c49462f2a424ba556f57ab470ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:13:03 +0000 Subject: [PATCH 1/2] Initial plan From ad041b946912f0aa80a378c460b756e7cc1b6801 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:18:38 +0000 Subject: [PATCH 2/2] Add OrderQueueService to track order preparation times and estimated wait times Co-authored-by: PagelsR <4490855+PagelsR@users.noreply.github.com> --- .../Controllers/OrderQueueController.cs | 60 ++++++++++ ginos-gelato/server/Models/OrderQueueEntry.cs | 21 ++++ ginos-gelato/server/Program.cs | 1 + .../server/Services/OrderQueueService.cs | 105 ++++++++++++++++++ ginos-gelato/server/Services/OrderService.cs | 6 +- 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 ginos-gelato/server/Controllers/OrderQueueController.cs create mode 100644 ginos-gelato/server/Models/OrderQueueEntry.cs create mode 100644 ginos-gelato/server/Services/OrderQueueService.cs 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; }