Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions ginos-gelato/server/Controllers/OrderQueueController.cs
Original file line number Diff line number Diff line change
@@ -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;
}

/// <summary>
/// Returns the current queue status, including the number of pending orders
/// and the estimated wait time in minutes.
/// </summary>
[HttpGet("status")]
public ActionResult<QueueStatusResponse> GetQueueStatus()
{
return Ok(_orderQueueService.GetQueueStatus());
}

/// <summary>
/// Returns the average preparation time in minutes based on completed orders.
/// </summary>
[HttpGet("averagetime")]
public ActionResult<double> GetAveragePreparationTime()
{
return Ok(_orderQueueService.GetAveragePreparationTimeMinutes());
}

/// <summary>
/// Returns all queue entries (pending and completed).
/// </summary>
[HttpGet]
public ActionResult<IEnumerable<OrderQueueEntry>> GetQueueEntries()
{
return Ok(_orderQueueService.GetAllEntries());
}

/// <summary>
/// Marks an order as completed, recording its actual preparation time.
/// </summary>
[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();
}
}
}
21 changes: 21 additions & 0 deletions ginos-gelato/server/Models/OrderQueueEntry.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
1 change: 1 addition & 0 deletions ginos-gelato/server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
options.UseInMemoryDatabase("GinosGelatoDb")); // Using in-memory database for simplicity

// Add custom services
builder.Services.AddSingleton<OrderQueueService>();
builder.Services.AddScoped<OrderService>();

// Add Swagger/OpenAPI
Expand Down
105 changes: 105 additions & 0 deletions ginos-gelato/server/Services/OrderQueueService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using GinosGelato.Models;

namespace GinosGelato.Services
{
public class OrderQueueService
{
private readonly List<OrderQueueEntry> _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<OrderQueueEntry> 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; }
}
}
6 changes: 5 additions & 1 deletion ginos-gelato/server/Services/OrderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Order> CreateOrderAsync(List<IceCream> iceCreams)
Expand All @@ -28,6 +30,8 @@ public async Task<Order> CreateOrderAsync(List<IceCream> iceCreams)
_context.Orders.Add(order);
await _context.SaveChangesAsync();

_orderQueueService.EnqueueOrder(order.Id);

return order;
}

Expand Down