using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Infrastructure.Persistence; using Jiaowu.Api.Infrastructure.Timetables; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Controllers; [ApiController] [AllowAnonymous] [Route("api/venue-displays")] public sealed class VenueDisplaysController( AppDbContext db, TimetableDataService timetableDataService, ClassroomReservationAvailabilityService availabilityService) : ControllerBase { [HttpGet("classrooms/{classroomId:guid}")] public async Task Classroom(Guid classroomId, CancellationToken cancellationToken) { var term = await CurrentTermAsync(cancellationToken); var classroom = await db.Classrooms.AsNoTracking() .Where(x => x.Id == classroomId && x.IsEnabled && x.Building!.IsEnabled) .Select(x => new { x.Id, x.Name, x.Code, BuildingName = x.Building!.Name, CampusName = x.Building.Campus!.Name }) .FirstOrDefaultAsync(cancellationToken); if (classroom is null) return NotFound(); if (term is null) return Ok(new { Classroom = classroom, Today = DateOnly.FromDateTime(DateTime.Now), Entries = Array.Empty() }); var sheet = await timetableDataService.BuildAsync(TimetableResourceType.Classroom, classroomId, term.Id, null, false, null, null, cancellationToken); var today = DateOnly.FromDateTime(DateTime.Now); var (week, day) = ClassroomReservationAvailabilityService.ResolveTeachingWeek(term, today); var entries = sheet?.Entries.Where(x => x.DayOfWeek == day && x.StartWeek <= week && x.EndWeek >= week && (x.WeekPattern == WeekPattern.All || x.WeekPattern == WeekPattern.Odd && week % 2 == 1 || x.WeekPattern == WeekPattern.Even && week % 2 == 0)) .Select(x => new { x.CourseName, x.TeacherNames, x.ClassNames, x.StartPeriod, x.PeriodCount }) ?? []; return Ok(new { Classroom = classroom, Term = term.Name, Today = today, Week = week, Slots = sheet?.Slots ?? [], Entries = entries }); } [HttpGet("buildings/{buildingId:guid}")] public async Task Building(Guid buildingId, CancellationToken cancellationToken) { var term = await CurrentTermAsync(cancellationToken); var building = await db.Buildings.AsNoTracking().Where(x => x.Id == buildingId && x.IsEnabled) .Select(x => new { x.Id, x.Name, x.Code, CampusName = x.Campus!.Name }).FirstOrDefaultAsync(cancellationToken); if (building is null) return NotFound(); if (term is null) return Ok(new { Building = building, Today = DateOnly.FromDateTime(DateTime.Now), Slots = Array.Empty(), Rooms = Array.Empty() }); var today = DateOnly.FromDateTime(DateTime.Now); var slots = await db.ScheduleTimeSlots.AsNoTracking().Where(x => x.AcademicTermId == term.Id && x.IsEnabled) .OrderBy(x => x.PeriodNumber).Select(x => new { x.PeriodNumber, x.Name, x.StartsAt, x.EndsAt }).ToListAsync(cancellationToken); var rooms = await db.Classrooms.AsNoTracking().Where(x => x.BuildingId == buildingId && x.IsEnabled) .OrderBy(x => x.Code).Select(x => new { x.Id, x.Code, x.Name, x.Capacity }).ToListAsync(cancellationToken); var occupied = new Dictionary>(); foreach (var slot in slots) occupied[slot.PeriodNumber] = await availabilityService.GetOccupiedClassroomIdsAsync(term, today, slot.PeriodNumber, 1, null, cancellationToken); return Ok(new { Building = building, Term = term.Name, Today = today, Slots = slots, Rooms = rooms.Select(room => new { room.Code, room.Name, room.Capacity, FreePeriods = slots.Where(slot => !occupied[slot.PeriodNumber].Contains(room.Id)).Select(slot => slot.PeriodNumber) }) }); } private Task CurrentTermAsync(CancellationToken cancellationToken) => db.AcademicTerms.AsNoTracking() .Where(x => x.IsEnabled && x.IsCurrent).FirstOrDefaultAsync(cancellationToken); }