Initial Commit

This commit is contained in:
2026-03-01 15:14:42 -04:00
commit f60174625e
377 changed files with 98166 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
## .NET
bin/
obj/
*.user
*.suo
*.cache
*.log
*.vs/
.vs/
## IDE
.idea/
*.swp
*~
## OS
.DS_Store
Thumbs.db
## Secrets
appsettings.*.local.json

12
SportsDivision.slnx Normal file
View File

@@ -0,0 +1,12 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/SportsDivision.Application/SportsDivision.Application.csproj" />
<Project Path="src/SportsDivision.Domain/SportsDivision.Domain.csproj" />
<Project Path="src/SportsDivision.Infrastructure/SportsDivision.Infrastructure.csproj" />
<Project Path="src/SportsDivision.Web/SportsDivision.Web.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/SportsDivision.Application.Tests/SportsDivision.Application.Tests.csproj" />
<Project Path="tests/SportsDivision.Domain.Tests/SportsDivision.Domain.Tests.csproj" />
</Folder>
</Solution>

1034
project.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
namespace SportsDivision.Application.DTOs;
public class DashboardDto { public int TotalStudents { get; set; } public int MaleStudents { get; set; } public int FemaleStudents { get; set; } public int TotalSchools { get; set; } public int ActiveTournaments { get; set; } public int TotalRegistrations { get; set; } public int EventsInProgress { get; set; } public int EventsCompleted { get; set; } public List<SchoolPointsSummaryDto> TopSchools { get; set; } = new(); public List<RecentScoreDto> RecentScores { get; set; } = new(); }
public class SchoolPointsSummaryDto { public int SchoolId { get; set; } public string SchoolName { get; set; } = string.Empty; public string? ShortName { get; set; } public int TotalPoints { get; set; } public int FirstPlaceCount { get; set; } public int SecondPlaceCount { get; set; } public int ThirdPlaceCount { get; set; } }
public class RecentScoreDto { public string StudentName { get; set; } = string.Empty; public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public decimal RawPerformance { get; set; } public int? Placement { get; set; } public DateTime RecordedAt { get; set; } }

View File

@@ -0,0 +1,5 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class EventDto { public int EventId { get; set; } public string Name { get; set; } = string.Empty; public EventCategory Category { get; set; } public bool IsRelay { get; set; } public bool PrimarySchool { get; set; } public bool SecondarySchool { get; set; } public bool IsActive { get; set; } public List<EventLevelDto> EventLevels { get; set; } = new(); }
public class EventCreateDto { public string Name { get; set; } = string.Empty; public EventCategory Category { get; set; } public bool IsRelay { get; set; } public bool PrimarySchool { get; set; } public bool SecondarySchool { get; set; } public List<int> EventLevelIds { get; set; } = new(); }
public class EventUpdateDto : EventCreateDto { public int EventId { get; set; } public bool IsActive { get; set; } }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class EventLevelDto { public int EventLevelId { get; set; } public string Name { get; set; } = string.Empty; public Sex Sex { get; set; } public int? MaxAge { get; set; } public SchoolLevel SchoolLevel { get; set; } public bool IsAgeBased { get; set; } public int SortOrder { get; set; } public bool IsActive { get; set; } }
public class EventLevelCreateDto { public string Name { get; set; } = string.Empty; public Sex Sex { get; set; } public int? MaxAge { get; set; } public SchoolLevel SchoolLevel { get; set; } public bool IsAgeBased { get; set; } public int SortOrder { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs;
public class EventRegistrationDto { public int EventRegistrationId { get; set; } public int TournamentEventLevelId { get; set; } public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public int? StudentId { get; set; } public string? StudentName { get; set; } public string? SchoolName { get; set; } public int? RelayTeamId { get; set; } public string? RelayTeamName { get; set; } public DateTime RegisteredAt { get; set; } public ScoreDto? Score { get; set; } }
public class EventRegistrationCreateDto { public int TournamentEventLevelId { get; set; } public int? StudentId { get; set; } public int? RelayTeamId { get; set; } }

View File

@@ -0,0 +1,3 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class HeatDto { public int HeatId { get; set; } public int RoundId { get; set; } public int HeatNumber { get; set; } public HeatStatus Status { get; set; } public List<HeatLaneDto> HeatLanes { get; set; } = new(); }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class HeatLaneDto { public int HeatLaneId { get; set; } public int HeatId { get; set; } public int EventRegistrationId { get; set; } public int LaneNumber { get; set; } public decimal? Time { get; set; } public bool IsAdvanced { get; set; } public AdvanceReason? AdvanceReason { get; set; } public bool IsDNS { get; set; } public bool IsDNF { get; set; } public bool IsDQ { get; set; } public string? StudentName { get; set; } public string? SchoolName { get; set; } }
public class HeatLaneUpdateDto { public int HeatLaneId { get; set; } public decimal? Time { get; set; } public bool IsDNS { get; set; } public bool IsDNF { get; set; } public bool IsDQ { get; set; } }

View File

@@ -0,0 +1,6 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class HighJumpHeightDto { public int HighJumpHeightId { get; set; } public int TournamentEventLevelId { get; set; } public decimal Height { get; set; } public int SortOrder { get; set; } public List<HighJumpAttemptDto> Attempts { get; set; } = new(); }
public class HighJumpAttemptDto { public int HighJumpAttemptId { get; set; } public int HighJumpHeightId { get; set; } public int EventRegistrationId { get; set; } public string? StudentName { get; set; } public string? SchoolName { get; set; } public HighJumpAttemptResult? Attempt1 { get; set; } public HighJumpAttemptResult? Attempt2 { get; set; } public HighJumpAttemptResult? Attempt3 { get; set; } public bool IsEliminated { get; set; } public bool HasCleared { get; set; } }
public class HighJumpAttemptUpdateDto { public int HighJumpHeightId { get; set; } public int EventRegistrationId { get; set; } public int AttemptNumber { get; set; } public HighJumpAttemptResult Result { get; set; } }
public class HighJumpHeightCreateDto { public int TournamentEventLevelId { get; set; } public decimal Height { get; set; } }

View File

@@ -0,0 +1,2 @@
namespace SportsDivision.Application.DTOs;
public class PlacementPointConfigDto { public int PlacementPointConfigId { get; set; } public int Placement { get; set; } public int Points { get; set; } }

View File

@@ -0,0 +1,4 @@
namespace SportsDivision.Application.DTOs.ReportDtos;
public class EventSchoolReportDto { public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public List<EventSchoolEntryDto> Schools { get; set; } = new(); }
public class EventSchoolEntryDto { public string SchoolName { get; set; } = string.Empty; public List<EventSchoolStudentDto> Students { get; set; } = new(); }
public class EventSchoolStudentDto { public string StudentName { get; set; } = string.Empty; public decimal? RawPerformance { get; set; } public int? Placement { get; set; } public int PlacementPoints { get; set; } }

View File

@@ -0,0 +1,2 @@
namespace SportsDivision.Application.DTOs.ReportDtos;
public class PopularEventsReportDto { public string EventName { get; set; } = string.Empty; public string Category { get; set; } = string.Empty; public int RegistrationCount { get; set; } public int MaleCount { get; set; } public int FemaleCount { get; set; } }

View File

@@ -0,0 +1,2 @@
namespace SportsDivision.Application.DTOs.ReportDtos;
public class RegistrationByGenderReportDto { public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public int MaleCount { get; set; } public int FemaleCount { get; set; } public int TotalCount { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs.ReportDtos;
public class ScoresByEventReportDto { public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public string Category { get; set; } = string.Empty; public List<ScoreEntryDto> Scores { get; set; } = new(); }
public class ScoreEntryDto { public int? Placement { get; set; } public string StudentName { get; set; } = string.Empty; public string SchoolName { get; set; } = string.Empty; public decimal RawPerformance { get; set; } public int CalculatedPoints { get; set; } public int PlacementPoints { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs.ReportDtos;
public class StudentPointsReportDto { public string StudentName { get; set; } = string.Empty; public string SchoolName { get; set; } = string.Empty; public string Sex { get; set; } = string.Empty; public int TotalPlacementPoints { get; set; } public int EventCount { get; set; } public List<StudentEventScoreDto> EventScores { get; set; } = new(); }
public class StudentEventScoreDto { public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public int? Placement { get; set; } public int PlacementPoints { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs.ReportDtos;
public class StudentsBySchoolReportDto { public string SchoolName { get; set; } = string.Empty; public string ZoneName { get; set; } = string.Empty; public List<StudentEventEntryDto> Students { get; set; } = new(); }
public class StudentEventEntryDto { public string StudentName { get; set; } = string.Empty; public string Sex { get; set; } = string.Empty; public List<string> Events { get; set; } = new(); }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class RoundDto { public int RoundId { get; set; } public int TournamentEventLevelId { get; set; } public RoundType RoundType { get; set; } public int RoundOrder { get; set; } public int? AdvanceTopN { get; set; } public int? AdvanceFastestLosers { get; set; } public RoundStatus Status { get; set; } public List<HeatDto> Heats { get; set; } = new(); }
public class RoundCreateDto { public int TournamentEventLevelId { get; set; } public RoundType RoundType { get; set; } public int RoundOrder { get; set; } public int? AdvanceTopN { get; set; } public int? AdvanceFastestLosers { get; set; } }

View File

@@ -0,0 +1,5 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class SchoolDto { public int SchoolId { get; set; } public string Name { get; set; } = string.Empty; public string? ShortName { get; set; } public SchoolLevel SchoolLevel { get; set; } public int ZoneId { get; set; } public string ZoneName { get; set; } = string.Empty; public bool IsActive { get; set; } public int StudentCount { get; set; } }
public class SchoolCreateDto { public string Name { get; set; } = string.Empty; public string? ShortName { get; set; } public SchoolLevel SchoolLevel { get; set; } public int ZoneId { get; set; } }
public class SchoolUpdateDto : SchoolCreateDto { public int SchoolId { get; set; } public bool IsActive { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs;
public class ScoreDto { public int ScoreId { get; set; } public int EventRegistrationId { get; set; } public decimal RawPerformance { get; set; } public int CalculatedPoints { get; set; } public int? Placement { get; set; } public int PlacementPoints { get; set; } public string? StudentName { get; set; } public string? SchoolName { get; set; } public string? EventName { get; set; } public DateTime RecordedAt { get; set; } }
public class ScoreCreateDto { public int EventRegistrationId { get; set; } public decimal RawPerformance { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs;
public class ScoringConstantDto { public int ScoringConstantId { get; set; } public int EventId { get; set; } public string EventName { get; set; } = string.Empty; public decimal A { get; set; } public decimal B { get; set; } public decimal C { get; set; } public string Unit { get; set; } = string.Empty; }
public class ScoringConstantUpdateDto { public int ScoringConstantId { get; set; } public decimal A { get; set; } public decimal B { get; set; } public decimal C { get; set; } }

View File

@@ -0,0 +1,5 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class StudentDto { public int StudentId { get; set; } public string ExistingStudentId { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string FullName { get; set; } = string.Empty; public DateOnly DateOfBirth { get; set; } public Sex Sex { get; set; } public int SchoolId { get; set; } public string SchoolName { get; set; } = string.Empty; public bool IsActive { get; set; } public int? Age { get; set; } }
public class StudentCreateDto { public string ExistingStudentId { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public DateOnly DateOfBirth { get; set; } public Sex Sex { get; set; } public int SchoolId { get; set; } }
public class StudentUpdateDto : StudentCreateDto { public int StudentId { get; set; } public bool IsActive { get; set; } }

View File

@@ -0,0 +1,5 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.DTOs;
public class TournamentDto { public int TournamentId { get; set; } public string Name { get; set; } = string.Empty; public DateOnly StartDate { get; set; } public DateOnly EndDate { get; set; } public int? ZoneId { get; set; } public string? ZoneName { get; set; } public SchoolLevel? SchoolLevel { get; set; } public bool IsArchived { get; set; } public TournamentStatus Status { get; set; } public int EventCount { get; set; } public int RegistrationCount { get; set; } }
public class TournamentCreateDto { public string Name { get; set; } = string.Empty; public DateOnly StartDate { get; set; } public DateOnly EndDate { get; set; } public int? ZoneId { get; set; } public SchoolLevel? SchoolLevel { get; set; } }
public class TournamentUpdateDto : TournamentCreateDto { public int TournamentId { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs;
public class TournamentEventLevelDto { public int TournamentEventLevelId { get; set; } public int TournamentId { get; set; } public int EventId { get; set; } public string EventName { get; set; } = string.Empty; public int EventLevelId { get; set; } public string EventLevelName { get; set; } = string.Empty; public bool AgeRestrictionWaived { get; set; } public int RegistrationCount { get; set; } }
public class TournamentEventLevelCreateDto { public int TournamentId { get; set; } public int EventId { get; set; } public int EventLevelId { get; set; } public bool AgeRestrictionWaived { get; set; } }

View File

@@ -0,0 +1,4 @@
namespace SportsDivision.Application.DTOs;
public class UserDto { public string Id { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string FullName { get; set; } = string.Empty; public string Role { get; set; } = string.Empty; public int? SchoolId { get; set; } public string? SchoolName { get; set; } public bool IsActive { get; set; } }
public class UserCreateDto { public string Email { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string Role { get; set; } = string.Empty; public int? SchoolId { get; set; } }
public class UserUpdateDto { public string Id { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string Role { get; set; } = string.Empty; public int? SchoolId { get; set; } public bool IsActive { get; set; } }

View File

@@ -0,0 +1,3 @@
namespace SportsDivision.Application.DTOs;
public class ZoneDto { public int ZoneId { get; set; } public string Name { get; set; } = string.Empty; public string Code { get; set; } = string.Empty; public bool IsActive { get; set; } public int SchoolCount { get; set; } }
public class ZoneCreateDto { public string Name { get; set; } = string.Empty; public string Code { get; set; } = string.Empty; }

View File

@@ -0,0 +1,27 @@
using Microsoft.Extensions.DependencyInjection;
using SportsDivision.Application.Interfaces;
using SportsDivision.Application.Mappings;
using SportsDivision.Application.Services;
namespace SportsDivision.Application;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddAutoMapper(typeof(MappingProfile).Assembly);
services.AddScoped<IStudentService, StudentService>();
services.AddScoped<ISchoolService, SchoolService>();
services.AddScoped<IEventService, EventService>();
services.AddScoped<ITournamentService, TournamentService>();
services.AddScoped<IRegistrationService, RegistrationService>();
services.AddScoped<IHeatManagementService, HeatManagementService>();
services.AddScoped<IScoringService, ScoringService>();
services.AddScoped<IHighJumpService, HighJumpService>();
services.AddScoped<IReportService, ReportService>();
services.AddScoped<IDashboardService, DashboardService>();
return services;
}
}

View File

@@ -0,0 +1,3 @@
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Interfaces;
public interface IDashboardService { Task<DashboardDto> GetDashboardAsync(int? tournamentId = null); }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Application.DTOs;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.Interfaces;
public interface IEventService { Task<IEnumerable<EventDto>> GetAllAsync(); Task<EventDto?> GetByIdAsync(int id); Task<IEnumerable<EventDto>> GetByCategoryAsync(EventCategory category); Task<EventDto> CreateAsync(EventCreateDto dto); Task UpdateAsync(EventUpdateDto dto); Task DeleteAsync(int id); }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Application.DTOs;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.Interfaces;
public interface IHeatManagementService { Task<RoundDto> CreateRoundAsync(RoundCreateDto dto); Task<IEnumerable<RoundDto>> GetRoundsAsync(int tournamentEventLevelId); Task<RoundDto?> GetRoundWithHeatsAsync(int roundId); Task SeedHeatsAsync(int roundId, SeedingMethod method, int lanesPerHeat = 8); Task SaveHeatTimesAsync(int heatId, List<HeatLaneUpdateDto> lanes, string recordedBy); Task CalculateAdvancementAsync(int roundId); Task PopulateNextRoundAsync(int roundId); Task CompleteHeatAsync(int heatId); Task CompleteRoundAsync(int roundId); }

View File

@@ -0,0 +1,3 @@
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Interfaces;
public interface IHighJumpService { Task<IEnumerable<HighJumpHeightDto>> GetHeightsAsync(int tournamentEventLevelId); Task<HighJumpHeightDto> AddHeightAsync(HighJumpHeightCreateDto dto); Task RemoveHeightAsync(int heightId); Task RecordAttemptAsync(HighJumpAttemptUpdateDto dto); Task<bool> IsEliminatedAsync(int tournamentEventLevelId, int eventRegistrationId); Task CalculateResultsAsync(int tournamentEventLevelId, string recordedBy); }

View File

@@ -0,0 +1,3 @@
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Interfaces;
public interface IRegistrationService { Task<IEnumerable<EventRegistrationDto>> GetByTournamentEventLevelAsync(int tournamentEventLevelId); Task<IEnumerable<EventRegistrationDto>> GetByStudentAsync(int studentId); Task<EventRegistrationDto> RegisterStudentAsync(EventRegistrationCreateDto dto, string registeredBy); Task UnregisterAsync(int eventRegistrationId); Task<(bool IsEligible, string? Reason)> CheckEligibilityAsync(int tournamentEventLevelId, int studentId); }

View File

@@ -0,0 +1,3 @@
using SportsDivision.Application.DTOs.ReportDtos;
namespace SportsDivision.Application.Interfaces;
public interface IReportService { Task<IEnumerable<PopularEventsReportDto>> GetPopularEventsAsync(int tournamentId); Task<IEnumerable<RegistrationByGenderReportDto>> GetRegistrationByGenderAsync(int tournamentId, int? zoneId = null); Task<IEnumerable<EventSchoolReportDto>> GetEventSchoolReportAsync(int tournamentId, int? eventId = null, int? eventLevelId = null); Task<IEnumerable<StudentsBySchoolReportDto>> GetStudentsBySchoolAsync(int tournamentId, int? schoolId = null, int? zoneId = null); Task<IEnumerable<ScoresByEventReportDto>> GetScoresByEventAsync(int tournamentId, int? eventId = null, int? eventLevelId = null); Task<IEnumerable<StudentPointsReportDto>> GetStudentPointsAsync(int tournamentId, int? schoolId = null, int? zoneId = null); }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Application.DTOs;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.Interfaces;
public interface ISchoolService { Task<IEnumerable<SchoolDto>> GetAllAsync(); Task<SchoolDto?> GetByIdAsync(int id); Task<IEnumerable<SchoolDto>> GetByZoneAsync(int zoneId); Task<IEnumerable<SchoolDto>> GetBySchoolLevelAsync(SchoolLevel level); Task<SchoolDto> CreateAsync(SchoolCreateDto dto); Task UpdateAsync(SchoolUpdateDto dto); Task DeleteAsync(int id); }

View File

@@ -0,0 +1,3 @@
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Interfaces;
public interface IScoringService { int CalculatePoints(decimal rawPerformance, decimal a, decimal b, decimal c, bool isTrack); Task<ScoreDto> RecordScoreAsync(ScoreCreateDto dto, string recordedBy); Task CalculateFinalScoresAsync(int tournamentEventLevelId, string recordedBy); Task CalculatePlacementsAsync(int tournamentEventLevelId); Task<IEnumerable<SchoolPointsSummaryDto>> GetSchoolStandingsAsync(int tournamentId); Task<IEnumerable<ScoringConstantDto>> GetScoringConstantsAsync(); Task UpdateScoringConstantAsync(ScoringConstantUpdateDto dto); Task<IEnumerable<PlacementPointConfigDto>> GetPlacementPointConfigsAsync(); Task UpdatePlacementPointConfigAsync(PlacementPointConfigDto dto); }

View File

@@ -0,0 +1,3 @@
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Interfaces;
public interface IStudentService { Task<IEnumerable<StudentDto>> GetAllAsync(); Task<StudentDto?> GetByIdAsync(int id); Task<StudentDto?> GetByExistingIdAsync(string existingStudentId); Task<IEnumerable<StudentDto>> GetBySchoolAsync(int schoolId); Task<IEnumerable<StudentDto>> SearchAsync(string searchTerm); Task<StudentDto> CreateAsync(StudentCreateDto dto); Task UpdateAsync(StudentUpdateDto dto); Task DeleteAsync(int id); }

View File

@@ -0,0 +1,4 @@
using SportsDivision.Application.DTOs;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Application.Interfaces;
public interface ITournamentService { Task<IEnumerable<TournamentDto>> GetAllAsync(bool includeArchived = false); Task<TournamentDto?> GetByIdAsync(int id); Task<TournamentDto> CreateAsync(TournamentCreateDto dto); Task UpdateAsync(TournamentUpdateDto dto); Task UpdateStatusAsync(int id, TournamentStatus status); Task ArchiveAsync(int id); Task UnarchiveAsync(int id); Task DeleteAsync(int id); Task<IEnumerable<TournamentEventLevelDto>> GetEventLevelsAsync(int tournamentId); Task<TournamentEventLevelDto> AddEventLevelAsync(TournamentEventLevelCreateDto dto); Task RemoveEventLevelAsync(int tournamentEventLevelId); Task ToggleAgeWaiverAsync(int tournamentEventLevelId); }

View File

@@ -0,0 +1,75 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Domain.Entities;
namespace SportsDivision.Application.Mappings;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Zone, ZoneDto>()
.ForMember(d => d.SchoolCount, o => o.MapFrom(s => s.Schools != null ? s.Schools.Count : 0));
CreateMap<School, SchoolDto>()
.ForMember(d => d.ZoneName, o => o.MapFrom(s => s.Zone != null ? s.Zone.Name : string.Empty))
.ForMember(d => d.StudentCount, o => o.MapFrom(s => s.Students != null ? s.Students.Count : 0));
CreateMap<SchoolCreateDto, School>();
CreateMap<SchoolUpdateDto, School>();
CreateMap<Student, StudentDto>()
.ForMember(d => d.SchoolName, o => o.MapFrom(s => s.School != null ? s.School.Name : string.Empty))
.ForMember(d => d.FullName, o => o.MapFrom(s => s.FullName));
CreateMap<StudentCreateDto, Student>();
CreateMap<StudentUpdateDto, Student>();
CreateMap<Event, EventDto>();
CreateMap<EventCreateDto, Event>();
CreateMap<EventUpdateDto, Event>();
CreateMap<EventLevel, EventLevelDto>();
CreateMap<EventLevelCreateDto, EventLevel>();
CreateMap<Tournament, TournamentDto>()
.ForMember(d => d.ZoneName, o => o.MapFrom(s => s.Zone != null ? s.Zone.Name : null))
.ForMember(d => d.EventCount, o => o.MapFrom(s => s.TournamentEventLevels != null ? s.TournamentEventLevels.Count : 0));
CreateMap<TournamentCreateDto, Tournament>();
CreateMap<TournamentUpdateDto, Tournament>();
CreateMap<TournamentEventLevel, TournamentEventLevelDto>()
.ForMember(d => d.EventName, o => o.MapFrom(s => s.Event != null ? s.Event.Name : string.Empty))
.ForMember(d => d.EventLevelName, o => o.MapFrom(s => s.EventLevel != null ? s.EventLevel.Name : string.Empty))
.ForMember(d => d.RegistrationCount, o => o.MapFrom(s => s.Registrations != null ? s.Registrations.Count : 0));
CreateMap<TournamentEventLevelCreateDto, TournamentEventLevel>();
CreateMap<EventRegistration, EventRegistrationDto>()
.ForMember(d => d.EventName, o => o.MapFrom(s => s.TournamentEventLevel != null && s.TournamentEventLevel.Event != null ? s.TournamentEventLevel.Event.Name : string.Empty))
.ForMember(d => d.EventLevelName, o => o.MapFrom(s => s.TournamentEventLevel != null && s.TournamentEventLevel.EventLevel != null ? s.TournamentEventLevel.EventLevel.Name : string.Empty))
.ForMember(d => d.StudentName, o => o.MapFrom(s => s.Student != null ? s.Student.FullName : null))
.ForMember(d => d.SchoolName, o => o.MapFrom(s => s.Student != null && s.Student.School != null ? s.Student.School.Name : null));
CreateMap<Score, ScoreDto>()
.ForMember(d => d.StudentName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.Student != null ? s.EventRegistration.Student.FullName : null))
.ForMember(d => d.SchoolName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.Student != null && s.EventRegistration.Student.School != null ? s.EventRegistration.Student.School.Name : null))
.ForMember(d => d.EventName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.TournamentEventLevel != null && s.EventRegistration.TournamentEventLevel.Event != null ? s.EventRegistration.TournamentEventLevel.Event.Name : null));
CreateMap<Round, RoundDto>();
CreateMap<RoundCreateDto, Round>();
CreateMap<Heat, HeatDto>();
CreateMap<HeatLane, HeatLaneDto>()
.ForMember(d => d.StudentName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.Student != null ? s.EventRegistration.Student.FullName : null))
.ForMember(d => d.SchoolName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.Student != null && s.EventRegistration.Student.School != null ? s.EventRegistration.Student.School.Name : null));
CreateMap<HighJumpHeight, HighJumpHeightDto>();
CreateMap<HighJumpAttempt, HighJumpAttemptDto>()
.ForMember(d => d.StudentName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.Student != null ? s.EventRegistration.Student.FullName : null))
.ForMember(d => d.SchoolName, o => o.MapFrom(s => s.EventRegistration != null && s.EventRegistration.Student != null && s.EventRegistration.Student.School != null ? s.EventRegistration.Student.School.Name : null));
CreateMap<ScoringConstant, ScoringConstantDto>()
.ForMember(d => d.EventName, o => o.MapFrom(s => s.Event != null ? s.Event.Name : string.Empty));
CreateMap<PlacementPointConfig, PlacementPointConfigDto>();
}
}

View File

@@ -0,0 +1,47 @@
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class DashboardService : IDashboardService
{
private readonly IUnitOfWork _uow;
private readonly IScoringService _scoringService;
public DashboardService(IUnitOfWork uow, IScoringService scoringService)
{
_uow = uow;
_scoringService = scoringService;
}
public async Task<DashboardDto> GetDashboardAsync(int? tournamentId = null)
{
var dashboard = new DashboardDto
{
TotalStudents = await _uow.Students.CountAsync(),
MaleStudents = await _uow.Students.CountAsync(s => s.Sex == Sex.Male),
FemaleStudents = await _uow.Students.CountAsync(s => s.Sex == Sex.Female),
TotalSchools = await _uow.Schools.CountAsync(),
ActiveTournaments = await _uow.Tournaments.CountAsync(t => t.Status == TournamentStatus.InProgress && !t.IsArchived)
};
if (tournamentId.HasValue)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId.Value);
int regCount = 0;
foreach (var tel in tels)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
regCount += regs.Count();
}
dashboard.TotalRegistrations = regCount;
var standings = await _scoringService.GetSchoolStandingsAsync(tournamentId.Value);
dashboard.TopSchools = standings.Take(10).ToList();
}
return dashboard;
}
}

View File

@@ -0,0 +1,77 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class EventService : IEventService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public EventService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<IEnumerable<EventDto>> GetAllAsync()
{
var events = await _uow.Events.GetAllAsync();
return _mapper.Map<IEnumerable<EventDto>>(events);
}
public async Task<EventDto?> GetByIdAsync(int id)
{
var evt = await _uow.Events.GetByIdAsync(id);
return evt == null ? null : _mapper.Map<EventDto>(evt);
}
public async Task<IEnumerable<EventDto>> GetByCategoryAsync(EventCategory category)
{
var events = await _uow.Events.FindAsync(e => e.Category == category);
return _mapper.Map<IEnumerable<EventDto>>(events);
}
public async Task<EventDto> CreateAsync(EventCreateDto dto)
{
var evt = _mapper.Map<Event>(dto);
if (dto.EventLevelIds.Count > 0)
{
foreach (var levelId in dto.EventLevelIds)
{
var level = await _uow.EventLevels.GetByIdAsync(levelId);
if (level != null) evt.EventLevels.Add(level);
}
}
await _uow.Events.AddAsync(evt);
await _uow.SaveChangesAsync();
return _mapper.Map<EventDto>(evt);
}
public async Task UpdateAsync(EventUpdateDto dto)
{
var evt = await _uow.Events.GetWithEventLevelsAsync(dto.EventId)
?? throw new KeyNotFoundException("Event not found.");
_mapper.Map(dto, evt);
evt.EventLevels.Clear();
foreach (var levelId in dto.EventLevelIds)
{
var level = await _uow.EventLevels.GetByIdAsync(levelId);
if (level != null) evt.EventLevels.Add(level);
}
_uow.Events.Update(evt);
await _uow.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var evt = await _uow.Events.GetByIdAsync(id)
?? throw new KeyNotFoundException("Event not found.");
_uow.Events.Remove(evt);
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,218 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class HeatManagementService : IHeatManagementService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public HeatManagementService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<RoundDto> CreateRoundAsync(RoundCreateDto dto)
{
var round = _mapper.Map<Round>(dto);
await _uow.Rounds.AddAsync(round);
await _uow.SaveChangesAsync();
return _mapper.Map<RoundDto>(round);
}
public async Task<IEnumerable<RoundDto>> GetRoundsAsync(int tournamentEventLevelId)
{
var rounds = await _uow.Rounds.GetByTournamentEventLevelAsync(tournamentEventLevelId);
return _mapper.Map<IEnumerable<RoundDto>>(rounds);
}
public async Task<RoundDto?> GetRoundWithHeatsAsync(int roundId)
{
var round = await _uow.Rounds.GetWithHeatsAsync(roundId);
return round == null ? null : _mapper.Map<RoundDto>(round);
}
public async Task SeedHeatsAsync(int roundId, SeedingMethod method, int lanesPerHeat = 8)
{
var round = await _uow.Rounds.GetWithHeatsAsync(roundId)
?? throw new KeyNotFoundException("Round not found.");
var tel = await _uow.TournamentEventLevels.GetWithRegistrationsAsync(round.TournamentEventLevelId)
?? throw new KeyNotFoundException("Tournament event level not found.");
var registrations = tel.Registrations.ToList();
if (method == SeedingMethod.Random)
{
var rng = new Random();
registrations = registrations.OrderBy(_ => rng.Next()).ToList();
}
// Clear existing heats and lanes
foreach (var heat in round.Heats.ToList())
{
var lanes = await _uow.HeatLanes.GetByHeatAsync(heat.HeatId);
foreach (var lane in lanes)
_uow.HeatLanes.Remove(lane);
_uow.Heats.Remove(heat);
}
await _uow.SaveChangesAsync();
int heatCount = (int)Math.Ceiling((double)registrations.Count / lanesPerHeat);
for (int h = 0; h < heatCount; h++)
{
var heat = new Heat
{
RoundId = roundId,
HeatNumber = h + 1,
Status = HeatStatus.Pending
};
await _uow.Heats.AddAsync(heat);
await _uow.SaveChangesAsync();
var heatRegs = registrations.Skip(h * lanesPerHeat).Take(lanesPerHeat).ToList();
for (int l = 0; l < heatRegs.Count; l++)
{
var lane = new HeatLane
{
HeatId = heat.HeatId,
EventRegistrationId = heatRegs[l].EventRegistrationId,
LaneNumber = l + 1
};
await _uow.HeatLanes.AddAsync(lane);
}
await _uow.SaveChangesAsync();
}
}
public async Task SaveHeatTimesAsync(int heatId, List<HeatLaneUpdateDto> lanes, string recordedBy)
{
foreach (var laneDto in lanes)
{
var lane = await _uow.HeatLanes.GetByIdAsync(laneDto.HeatLaneId);
if (lane == null) continue;
lane.Time = laneDto.Time;
lane.IsDNS = laneDto.IsDNS;
lane.IsDNF = laneDto.IsDNF;
lane.IsDQ = laneDto.IsDQ;
lane.RecordedBy = recordedBy;
lane.RecordedAt = DateTime.UtcNow;
_uow.HeatLanes.Update(lane);
}
await _uow.SaveChangesAsync();
}
public async Task CalculateAdvancementAsync(int roundId)
{
var round = await _uow.Rounds.GetWithHeatsAsync(roundId)
?? throw new KeyNotFoundException("Round not found.");
var allLanes = round.Heats.SelectMany(h => h.HeatLanes)
.Where(l => l.Time.HasValue && !l.IsDNS && !l.IsDNF && !l.IsDQ)
.OrderBy(l => l.Time)
.ToList();
// Top N from each heat
if (round.AdvanceTopN.HasValue)
{
foreach (var heat in round.Heats)
{
var topN = heat.HeatLanes
.Where(l => l.Time.HasValue && !l.IsDNS && !l.IsDNF && !l.IsDQ)
.OrderBy(l => l.Time)
.Take(round.AdvanceTopN.Value);
foreach (var lane in topN)
{
lane.IsAdvanced = true;
lane.AdvanceReason = AdvanceReason.TopN;
_uow.HeatLanes.Update(lane);
}
}
}
// Fastest losers
if (round.AdvanceFastestLosers.HasValue && round.AdvanceFastestLosers.Value > 0)
{
var nonAdvanced = allLanes.Where(l => !l.IsAdvanced)
.OrderBy(l => l.Time)
.Take(round.AdvanceFastestLosers.Value);
foreach (var lane in nonAdvanced)
{
lane.IsAdvanced = true;
lane.AdvanceReason = AdvanceReason.FastestLoser;
_uow.HeatLanes.Update(lane);
}
}
await _uow.SaveChangesAsync();
}
public async Task PopulateNextRoundAsync(int roundId)
{
var round = await _uow.Rounds.GetWithHeatsAsync(roundId)
?? throw new KeyNotFoundException("Round not found.");
var advancedLanes = round.Heats.SelectMany(h => h.HeatLanes)
.Where(l => l.IsAdvanced)
.OrderBy(l => l.Time)
.ToList();
var nextRounds = await _uow.Rounds.GetByTournamentEventLevelAsync(round.TournamentEventLevelId);
var nextRound = nextRounds.FirstOrDefault(r => r.RoundOrder == round.RoundOrder + 1)
?? throw new InvalidOperationException("No next round exists.");
int lanesPerHeat = 8;
int heatCount = (int)Math.Ceiling((double)advancedLanes.Count / lanesPerHeat);
for (int h = 0; h < heatCount; h++)
{
var heat = new Heat
{
RoundId = nextRound.RoundId,
HeatNumber = h + 1,
Status = HeatStatus.Pending
};
await _uow.Heats.AddAsync(heat);
await _uow.SaveChangesAsync();
var heatLanes = advancedLanes.Skip(h * lanesPerHeat).Take(lanesPerHeat).ToList();
for (int l = 0; l < heatLanes.Count; l++)
{
var lane = new HeatLane
{
HeatId = heat.HeatId,
EventRegistrationId = heatLanes[l].EventRegistrationId,
LaneNumber = l + 1
};
await _uow.HeatLanes.AddAsync(lane);
}
await _uow.SaveChangesAsync();
}
}
public async Task CompleteHeatAsync(int heatId)
{
var heat = await _uow.Heats.GetByIdAsync(heatId)
?? throw new KeyNotFoundException("Heat not found.");
heat.Status = HeatStatus.Completed;
_uow.Heats.Update(heat);
await _uow.SaveChangesAsync();
}
public async Task CompleteRoundAsync(int roundId)
{
var round = await _uow.Rounds.GetByIdAsync(roundId)
?? throw new KeyNotFoundException("Round not found.");
round.Status = RoundStatus.Completed;
_uow.Rounds.Update(round);
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,174 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class HighJumpService : IHighJumpService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public HighJumpService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<IEnumerable<HighJumpHeightDto>> GetHeightsAsync(int tournamentEventLevelId)
{
var heights = await _uow.HighJumpHeights.GetByTournamentEventLevelAsync(tournamentEventLevelId);
return _mapper.Map<IEnumerable<HighJumpHeightDto>>(heights);
}
public async Task<HighJumpHeightDto> AddHeightAsync(HighJumpHeightCreateDto dto)
{
var existingHeights = await _uow.HighJumpHeights.GetByTournamentEventLevelAsync(dto.TournamentEventLevelId);
var maxOrder = existingHeights.Any() ? existingHeights.Max(h => h.SortOrder) : 0;
var height = new HighJumpHeight
{
TournamentEventLevelId = dto.TournamentEventLevelId,
Height = dto.Height,
SortOrder = maxOrder + 1
};
await _uow.HighJumpHeights.AddAsync(height);
await _uow.SaveChangesAsync();
return _mapper.Map<HighJumpHeightDto>(height);
}
public async Task RemoveHeightAsync(int heightId)
{
var height = await _uow.HighJumpHeights.GetByIdAsync(heightId)
?? throw new KeyNotFoundException("Height not found.");
_uow.HighJumpHeights.Remove(height);
await _uow.SaveChangesAsync();
}
public async Task RecordAttemptAsync(HighJumpAttemptUpdateDto dto)
{
var height = await _uow.HighJumpHeights.GetWithAttemptsAsync(dto.HighJumpHeightId)
?? throw new KeyNotFoundException("Height not found.");
var attempt = height.Attempts.FirstOrDefault(a => a.EventRegistrationId == dto.EventRegistrationId);
if (attempt == null)
{
attempt = new HighJumpAttempt
{
HighJumpHeightId = dto.HighJumpHeightId,
EventRegistrationId = dto.EventRegistrationId
};
height.Attempts.Add(attempt);
}
switch (dto.AttemptNumber)
{
case 1: attempt.Attempt1 = dto.Result; break;
case 2: attempt.Attempt2 = dto.Result; break;
case 3: attempt.Attempt3 = dto.Result; break;
default: throw new ArgumentException("Attempt number must be 1, 2, or 3.");
}
await _uow.SaveChangesAsync();
}
public async Task<bool> IsEliminatedAsync(int tournamentEventLevelId, int eventRegistrationId)
{
var heights = await _uow.HighJumpHeights.GetByTournamentEventLevelAsync(tournamentEventLevelId);
int consecutiveFails = 0;
foreach (var height in heights.OrderBy(h => h.SortOrder))
{
var fullHeight = await _uow.HighJumpHeights.GetWithAttemptsAsync(height.HighJumpHeightId);
if (fullHeight == null) continue;
var attempt = fullHeight.Attempts.FirstOrDefault(a => a.EventRegistrationId == eventRegistrationId);
if (attempt == null) continue;
if (attempt.HasCleared)
consecutiveFails = 0;
else if (attempt.IsEliminated)
consecutiveFails += 3;
else
consecutiveFails += attempt.FailCount;
if (consecutiveFails >= 3)
return true;
}
return false;
}
public async Task CalculateResultsAsync(int tournamentEventLevelId, string recordedBy)
{
var heights = (await _uow.HighJumpHeights.GetByTournamentEventLevelAsync(tournamentEventLevelId))
.OrderBy(h => h.SortOrder).ToList();
var tel = await _uow.TournamentEventLevels.GetWithRegistrationsAsync(tournamentEventLevelId)
?? throw new KeyNotFoundException("Tournament event level not found.");
var results = new List<(int RegId, decimal HighestCleared, int TotalFails, int FailsAtHighest)>();
foreach (var reg in tel.Registrations)
{
decimal highestCleared = 0;
int totalFails = 0;
int failsAtHighest = 0;
foreach (var height in heights)
{
var fullHeight = await _uow.HighJumpHeights.GetWithAttemptsAsync(height.HighJumpHeightId);
if (fullHeight == null) continue;
var attempt = fullHeight.Attempts.FirstOrDefault(a => a.EventRegistrationId == reg.EventRegistrationId);
if (attempt == null) continue;
totalFails += attempt.FailCount;
if (attempt.HasCleared)
{
highestCleared = height.Height;
failsAtHighest = attempt.FailCount;
}
}
if (highestCleared > 0)
results.Add((reg.EventRegistrationId, highestCleared, totalFails, failsAtHighest));
}
// Sort: highest cleared DESC, then fewest fails at that height, then fewest total fails
var sorted = results
.OrderByDescending(r => r.HighestCleared)
.ThenBy(r => r.FailsAtHighest)
.ThenBy(r => r.TotalFails)
.ToList();
// Record scores
for (int i = 0; i < sorted.Count; i++)
{
var score = await _uow.Scores.GetByRegistrationAsync(sorted[i].RegId);
if (score == null)
{
score = new Score
{
EventRegistrationId = sorted[i].RegId,
RawPerformance = sorted[i].HighestCleared,
RecordedBy = recordedBy,
RecordedAt = DateTime.UtcNow
};
await _uow.Scores.AddAsync(score);
}
else
{
score.RawPerformance = sorted[i].HighestCleared;
score.RecordedBy = recordedBy;
score.RecordedAt = DateTime.UtcNow;
_uow.Scores.Update(score);
}
}
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,97 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class RegistrationService : IRegistrationService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public RegistrationService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<IEnumerable<EventRegistrationDto>> GetByTournamentEventLevelAsync(int tournamentEventLevelId)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tournamentEventLevelId);
return _mapper.Map<IEnumerable<EventRegistrationDto>>(regs);
}
public async Task<IEnumerable<EventRegistrationDto>> GetByStudentAsync(int studentId)
{
var regs = await _uow.EventRegistrations.GetByStudentAsync(studentId);
return _mapper.Map<IEnumerable<EventRegistrationDto>>(regs);
}
public async Task<EventRegistrationDto> RegisterStudentAsync(EventRegistrationCreateDto dto, string registeredBy)
{
var (isEligible, reason) = await CheckEligibilityAsync(dto.TournamentEventLevelId, dto.StudentId!.Value);
if (!isEligible)
throw new InvalidOperationException($"Student is not eligible: {reason}");
var reg = new EventRegistration
{
TournamentEventLevelId = dto.TournamentEventLevelId,
StudentId = dto.StudentId,
RelayTeamId = dto.RelayTeamId,
RegisteredBy = registeredBy,
RegisteredAt = DateTime.UtcNow
};
await _uow.EventRegistrations.AddAsync(reg);
await _uow.SaveChangesAsync();
return _mapper.Map<EventRegistrationDto>(reg);
}
public async Task UnregisterAsync(int eventRegistrationId)
{
var reg = await _uow.EventRegistrations.GetByIdAsync(eventRegistrationId)
?? throw new KeyNotFoundException("Registration not found.");
_uow.EventRegistrations.Remove(reg);
await _uow.SaveChangesAsync();
}
public async Task<(bool IsEligible, string? Reason)> CheckEligibilityAsync(int tournamentEventLevelId, int studentId)
{
var tel = await _uow.TournamentEventLevels.GetWithRegistrationsAsync(tournamentEventLevelId);
if (tel == null) return (false, "Event level not found.");
var student = await _uow.Students.GetByIdAsync(studentId);
if (student == null) return (false, "Student not found.");
// Check sex match
if (student.Sex != tel.EventLevel.Sex)
return (false, $"Student sex ({student.Sex}) does not match event level ({tel.EventLevel.Sex}).");
// Check school level compatibility
var school = await _uow.Schools.GetByIdAsync(student.SchoolId);
if (school == null) return (false, "Student's school not found.");
bool schoolLevelMatch = tel.EventLevel.SchoolLevel == school.SchoolLevel;
// Allow "compete up" — primary students can enter secondary events, but not the reverse
if (!schoolLevelMatch && !(school.SchoolLevel == Domain.Enums.SchoolLevel.Primary && tel.EventLevel.SchoolLevel == Domain.Enums.SchoolLevel.Secondary))
return (false, $"School level ({school.SchoolLevel}) is not compatible with event level ({tel.EventLevel.SchoolLevel}).");
// Check age eligibility (if age-based and not waived)
if (tel.EventLevel.IsAgeBased && tel.EventLevel.MaxAge.HasValue && !tel.AgeRestrictionWaived)
{
var referenceDate = tel.Tournament.StartDate;
var age = student.GetAge(referenceDate);
if (age > tel.EventLevel.MaxAge.Value)
return (false, $"Student age ({age}) exceeds maximum ({tel.EventLevel.MaxAge.Value}) for this event level.");
}
// Check duplicate registration
var alreadyRegistered = await _uow.EventRegistrations.IsStudentRegisteredAsync(tournamentEventLevelId, studentId);
if (alreadyRegistered)
return (false, "Student is already registered for this event level.");
return (true, null);
}
}

View File

@@ -0,0 +1,225 @@
using SportsDivision.Application.DTOs.ReportDtos;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class ReportService : IReportService
{
private readonly IUnitOfWork _uow;
public ReportService(IUnitOfWork uow)
{
_uow = uow;
}
public async Task<IEnumerable<PopularEventsReportDto>> GetPopularEventsAsync(int tournamentId)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var report = new Dictionary<string, PopularEventsReportDto>();
foreach (var tel in tels)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
var eventName = tel.Event?.Name ?? "Unknown";
var category = tel.Event?.Category.ToString() ?? "Unknown";
if (!report.ContainsKey(eventName))
report[eventName] = new PopularEventsReportDto { EventName = eventName, Category = category };
var entry = report[eventName];
foreach (var reg in regs)
{
entry.RegistrationCount++;
if (reg.Student?.Sex == Domain.Enums.Sex.Male) entry.MaleCount++;
else if (reg.Student?.Sex == Domain.Enums.Sex.Female) entry.FemaleCount++;
}
}
return report.Values.OrderByDescending(r => r.RegistrationCount);
}
public async Task<IEnumerable<RegistrationByGenderReportDto>> GetRegistrationByGenderAsync(int tournamentId, int? zoneId = null)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var report = new List<RegistrationByGenderReportDto>();
foreach (var tel in tels)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
var filteredRegs = zoneId.HasValue
? regs.Where(r => r.Student?.School?.ZoneId == zoneId.Value)
: regs;
var dto = new RegistrationByGenderReportDto
{
EventName = tel.Event?.Name ?? "Unknown",
EventLevelName = tel.EventLevel?.Name ?? "Unknown",
MaleCount = filteredRegs.Count(r => r.Student?.Sex == Domain.Enums.Sex.Male),
FemaleCount = filteredRegs.Count(r => r.Student?.Sex == Domain.Enums.Sex.Female)
};
dto.TotalCount = dto.MaleCount + dto.FemaleCount;
if (dto.TotalCount > 0) report.Add(dto);
}
return report.OrderBy(r => r.EventName).ThenBy(r => r.EventLevelName);
}
public async Task<IEnumerable<EventSchoolReportDto>> GetEventSchoolReportAsync(int tournamentId, int? eventId = null, int? eventLevelId = null)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var filtered = tels.AsEnumerable();
if (eventId.HasValue) filtered = filtered.Where(t => t.EventId == eventId.Value);
if (eventLevelId.HasValue) filtered = filtered.Where(t => t.EventLevelId == eventLevelId.Value);
var report = new List<EventSchoolReportDto>();
foreach (var tel in filtered)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
var schoolGroups = regs
.Where(r => r.Student?.School != null)
.GroupBy(r => r.Student!.School!.Name);
var dto = new EventSchoolReportDto
{
EventName = tel.Event?.Name ?? "Unknown",
EventLevelName = tel.EventLevel?.Name ?? "Unknown"
};
foreach (var group in schoolGroups)
{
var schoolEntry = new EventSchoolEntryDto
{
SchoolName = group.Key,
Students = group.Select(r => new EventSchoolStudentDto
{
StudentName = r.Student?.FullName ?? "Unknown",
RawPerformance = r.Score?.RawPerformance,
Placement = r.Score?.Placement,
PlacementPoints = r.Score?.PlacementPoints ?? 0
}).ToList()
};
dto.Schools.Add(schoolEntry);
}
report.Add(dto);
}
return report;
}
public async Task<IEnumerable<StudentsBySchoolReportDto>> GetStudentsBySchoolAsync(int tournamentId, int? schoolId = null, int? zoneId = null)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var schoolStudents = new Dictionary<string, StudentsBySchoolReportDto>();
foreach (var tel in tels)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
foreach (var reg in regs)
{
if (reg.Student?.School == null) continue;
if (schoolId.HasValue && reg.Student.SchoolId != schoolId.Value) continue;
if (zoneId.HasValue && reg.Student.School.ZoneId != zoneId.Value) continue;
var schoolName = reg.Student.School.Name;
if (!schoolStudents.ContainsKey(schoolName))
{
schoolStudents[schoolName] = new StudentsBySchoolReportDto
{
SchoolName = schoolName,
ZoneName = reg.Student.School.Zone?.Name ?? "Unknown"
};
}
var existingStudent = schoolStudents[schoolName].Students
.FirstOrDefault(s => s.StudentName == reg.Student.FullName);
if (existingStudent == null)
{
existingStudent = new StudentEventEntryDto
{
StudentName = reg.Student.FullName,
Sex = reg.Student.Sex.ToString()
};
schoolStudents[schoolName].Students.Add(existingStudent);
}
var eventDesc = $"{tel.Event?.Name} ({tel.EventLevel?.Name})";
existingStudent.Events.Add(eventDesc);
}
}
return schoolStudents.Values.OrderBy(s => s.SchoolName);
}
public async Task<IEnumerable<ScoresByEventReportDto>> GetScoresByEventAsync(int tournamentId, int? eventId = null, int? eventLevelId = null)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var filtered = tels.AsEnumerable();
if (eventId.HasValue) filtered = filtered.Where(t => t.EventId == eventId.Value);
if (eventLevelId.HasValue) filtered = filtered.Where(t => t.EventLevelId == eventLevelId.Value);
var report = new List<ScoresByEventReportDto>();
foreach (var tel in filtered)
{
var scores = await _uow.Scores.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
if (!scores.Any()) continue;
var dto = new ScoresByEventReportDto
{
EventName = tel.Event?.Name ?? "Unknown",
EventLevelName = tel.EventLevel?.Name ?? "Unknown",
Category = tel.Event?.Category.ToString() ?? "Unknown",
Scores = scores.OrderBy(s => s.Placement ?? int.MaxValue).Select(s => new ScoreEntryDto
{
Placement = s.Placement,
StudentName = s.EventRegistration?.Student?.FullName ?? "Unknown",
SchoolName = s.EventRegistration?.Student?.School?.Name ?? "Unknown",
RawPerformance = s.RawPerformance,
CalculatedPoints = s.CalculatedPoints,
PlacementPoints = s.PlacementPoints
}).ToList()
};
report.Add(dto);
}
return report;
}
public async Task<IEnumerable<StudentPointsReportDto>> GetStudentPointsAsync(int tournamentId, int? schoolId = null, int? zoneId = null)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var studentPoints = new Dictionary<int, StudentPointsReportDto>();
foreach (var tel in tels)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
foreach (var reg in regs)
{
if (reg.Student == null || reg.Score == null) continue;
if (schoolId.HasValue && reg.Student.SchoolId != schoolId.Value) continue;
if (zoneId.HasValue && reg.Student.School?.ZoneId != zoneId.Value) continue;
var studentId = reg.Student.StudentId;
if (!studentPoints.ContainsKey(studentId))
{
studentPoints[studentId] = new StudentPointsReportDto
{
StudentName = reg.Student.FullName,
SchoolName = reg.Student.School?.Name ?? "Unknown",
Sex = reg.Student.Sex.ToString()
};
}
var entry = studentPoints[studentId];
entry.TotalPlacementPoints += reg.Score.PlacementPoints;
entry.EventCount++;
entry.EventScores.Add(new StudentEventScoreDto
{
EventName = tel.Event?.Name ?? "Unknown",
EventLevelName = tel.EventLevel?.Name ?? "Unknown",
Placement = reg.Score.Placement,
PlacementPoints = reg.Score.PlacementPoints
});
}
}
return studentPoints.Values.OrderByDescending(s => s.TotalPlacementPoints);
}
}

View File

@@ -0,0 +1,69 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class SchoolService : ISchoolService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public SchoolService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<IEnumerable<SchoolDto>> GetAllAsync()
{
var schools = await _uow.Schools.GetAllAsync();
return _mapper.Map<IEnumerable<SchoolDto>>(schools);
}
public async Task<SchoolDto?> GetByIdAsync(int id)
{
var school = await _uow.Schools.GetByIdAsync(id);
return school == null ? null : _mapper.Map<SchoolDto>(school);
}
public async Task<IEnumerable<SchoolDto>> GetByZoneAsync(int zoneId)
{
var schools = await _uow.Schools.GetByZoneAsync(zoneId);
return _mapper.Map<IEnumerable<SchoolDto>>(schools);
}
public async Task<IEnumerable<SchoolDto>> GetBySchoolLevelAsync(SchoolLevel level)
{
var schools = await _uow.Schools.GetBySchoolLevelAsync(level);
return _mapper.Map<IEnumerable<SchoolDto>>(schools);
}
public async Task<SchoolDto> CreateAsync(SchoolCreateDto dto)
{
var school = _mapper.Map<School>(dto);
await _uow.Schools.AddAsync(school);
await _uow.SaveChangesAsync();
return _mapper.Map<SchoolDto>(school);
}
public async Task UpdateAsync(SchoolUpdateDto dto)
{
var school = await _uow.Schools.GetByIdAsync(dto.SchoolId)
?? throw new KeyNotFoundException("School not found.");
_mapper.Map(dto, school);
_uow.Schools.Update(school);
await _uow.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var school = await _uow.Schools.GetByIdAsync(id)
?? throw new KeyNotFoundException("School not found.");
_uow.Schools.Remove(school);
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,181 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class ScoringService : IScoringService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public ScoringService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
// World Athletics scoring formula
// Track: Points = A * (B - T)^C where T = time in seconds
// Field/Jump: Points = A * (P - B)^C where P = performance
public int CalculatePoints(decimal rawPerformance, decimal a, decimal b, decimal c, bool isTrack)
{
double diff = isTrack
? (double)(b - rawPerformance)
: (double)(rawPerformance - b);
if (diff <= 0) return 0;
double points = (double)a * Math.Pow(diff, (double)c);
return (int)Math.Floor(points);
}
public async Task<ScoreDto> RecordScoreAsync(ScoreCreateDto dto, string recordedBy)
{
var reg = await _uow.EventRegistrations.GetByIdAsync(dto.EventRegistrationId)
?? throw new KeyNotFoundException("Registration not found.");
var existingScore = await _uow.Scores.GetByRegistrationAsync(dto.EventRegistrationId);
if (existingScore != null)
{
existingScore.RawPerformance = dto.RawPerformance;
existingScore.RecordedBy = recordedBy;
existingScore.RecordedAt = DateTime.UtcNow;
_uow.Scores.Update(existingScore);
await _uow.SaveChangesAsync();
return _mapper.Map<ScoreDto>(existingScore);
}
var score = new Score
{
EventRegistrationId = dto.EventRegistrationId,
RawPerformance = dto.RawPerformance,
RecordedBy = recordedBy,
RecordedAt = DateTime.UtcNow
};
await _uow.Scores.AddAsync(score);
await _uow.SaveChangesAsync();
return _mapper.Map<ScoreDto>(score);
}
public async Task CalculateFinalScoresAsync(int tournamentEventLevelId, string recordedBy)
{
var tel = await _uow.TournamentEventLevels.GetWithRegistrationsAsync(tournamentEventLevelId)
?? throw new KeyNotFoundException("Tournament event level not found.");
var scoringConstant = await _uow.ScoringConstants.GetByEventAsync(tel.EventId);
if (scoringConstant == null) return;
bool isTrack = tel.Event.Category == Domain.Enums.EventCategory.Track;
foreach (var reg in tel.Registrations)
{
var score = await _uow.Scores.GetByRegistrationAsync(reg.EventRegistrationId);
if (score == null || score.RawPerformance == 0) continue;
score.CalculatedPoints = CalculatePoints(
score.RawPerformance,
scoringConstant.A,
scoringConstant.B,
scoringConstant.C,
isTrack);
score.RecordedBy = recordedBy;
score.RecordedAt = DateTime.UtcNow;
_uow.Scores.Update(score);
}
await _uow.SaveChangesAsync();
}
public async Task CalculatePlacementsAsync(int tournamentEventLevelId)
{
var scores = (await _uow.Scores.GetByTournamentEventLevelAsync(tournamentEventLevelId))
.Where(s => s.RawPerformance > 0)
.OrderByDescending(s => s.CalculatedPoints)
.ToList();
var placementPoints = (await _uow.PlacementPointConfigs.GetAllAsync())
.ToDictionary(p => p.Placement, p => p.Points);
for (int i = 0; i < scores.Count; i++)
{
scores[i].Placement = i + 1;
scores[i].PlacementPoints = placementPoints.TryGetValue(i + 1, out var pts) ? pts : 0;
_uow.Scores.Update(scores[i]);
}
await _uow.SaveChangesAsync();
}
public async Task<IEnumerable<SchoolPointsSummaryDto>> GetSchoolStandingsAsync(int tournamentId)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
var schoolPoints = new Dictionary<int, SchoolPointsSummaryDto>();
foreach (var tel in tels)
{
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
foreach (var reg in regs)
{
if (reg.Student == null || reg.Score == null) continue;
var schoolId = reg.Student.SchoolId;
if (!schoolPoints.ContainsKey(schoolId))
{
schoolPoints[schoolId] = new SchoolPointsSummaryDto
{
SchoolId = schoolId,
SchoolName = reg.Student.School?.Name ?? string.Empty,
ShortName = reg.Student.School?.ShortName
};
}
var summary = schoolPoints[schoolId];
summary.TotalPoints += reg.Score.PlacementPoints;
if (reg.Score.Placement == 1) summary.FirstPlaceCount++;
else if (reg.Score.Placement == 2) summary.SecondPlaceCount++;
else if (reg.Score.Placement == 3) summary.ThirdPlaceCount++;
}
}
return schoolPoints.Values
.OrderByDescending(s => s.TotalPoints)
.ThenByDescending(s => s.FirstPlaceCount)
.ThenByDescending(s => s.SecondPlaceCount)
.ThenByDescending(s => s.ThirdPlaceCount);
}
public async Task<IEnumerable<ScoringConstantDto>> GetScoringConstantsAsync()
{
var constants = await _uow.ScoringConstants.GetAllAsync();
return _mapper.Map<IEnumerable<ScoringConstantDto>>(constants);
}
public async Task UpdateScoringConstantAsync(ScoringConstantUpdateDto dto)
{
var constant = await _uow.ScoringConstants.GetByIdAsync(dto.ScoringConstantId)
?? throw new KeyNotFoundException("Scoring constant not found.");
constant.A = dto.A;
constant.B = dto.B;
constant.C = dto.C;
_uow.ScoringConstants.Update(constant);
await _uow.SaveChangesAsync();
}
public async Task<IEnumerable<PlacementPointConfigDto>> GetPlacementPointConfigsAsync()
{
var configs = await _uow.PlacementPointConfigs.GetAllAsync();
return _mapper.Map<IEnumerable<PlacementPointConfigDto>>(configs);
}
public async Task UpdatePlacementPointConfigAsync(PlacementPointConfigDto dto)
{
var config = await _uow.PlacementPointConfigs.GetByIdAsync(dto.PlacementPointConfigId)
?? throw new KeyNotFoundException("Placement point config not found.");
config.Placement = dto.Placement;
config.Points = dto.Points;
_uow.PlacementPointConfigs.Update(config);
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,74 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class StudentService : IStudentService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public StudentService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<IEnumerable<StudentDto>> GetAllAsync()
{
var students = await _uow.Students.GetAllAsync();
return _mapper.Map<IEnumerable<StudentDto>>(students);
}
public async Task<StudentDto?> GetByIdAsync(int id)
{
var student = await _uow.Students.GetByIdAsync(id);
return student == null ? null : _mapper.Map<StudentDto>(student);
}
public async Task<StudentDto?> GetByExistingIdAsync(string existingStudentId)
{
var student = await _uow.Students.GetByExistingIdAsync(existingStudentId);
return student == null ? null : _mapper.Map<StudentDto>(student);
}
public async Task<IEnumerable<StudentDto>> GetBySchoolAsync(int schoolId)
{
var students = await _uow.Students.GetBySchoolAsync(schoolId);
return _mapper.Map<IEnumerable<StudentDto>>(students);
}
public async Task<IEnumerable<StudentDto>> SearchAsync(string searchTerm)
{
var students = await _uow.Students.SearchAsync(searchTerm);
return _mapper.Map<IEnumerable<StudentDto>>(students);
}
public async Task<StudentDto> CreateAsync(StudentCreateDto dto)
{
var student = _mapper.Map<Student>(dto);
await _uow.Students.AddAsync(student);
await _uow.SaveChangesAsync();
return _mapper.Map<StudentDto>(student);
}
public async Task UpdateAsync(StudentUpdateDto dto)
{
var student = await _uow.Students.GetByIdAsync(dto.StudentId)
?? throw new KeyNotFoundException("Student not found.");
_mapper.Map(dto, student);
_uow.Students.Update(student);
await _uow.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var student = await _uow.Students.GetByIdAsync(id)
?? throw new KeyNotFoundException("Student not found.");
_uow.Students.Remove(student);
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,121 @@
using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;
namespace SportsDivision.Application.Services;
public class TournamentService : ITournamentService
{
private readonly IUnitOfWork _uow;
private readonly IMapper _mapper;
public TournamentService(IUnitOfWork uow, IMapper mapper)
{
_uow = uow;
_mapper = mapper;
}
public async Task<IEnumerable<TournamentDto>> GetAllAsync(bool includeArchived = false)
{
var tournaments = includeArchived
? await _uow.Tournaments.GetAllAsync()
: await _uow.Tournaments.GetActiveAsync();
return _mapper.Map<IEnumerable<TournamentDto>>(tournaments);
}
public async Task<TournamentDto?> GetByIdAsync(int id)
{
var tournament = await _uow.Tournaments.GetWithEventLevelsAsync(id);
return tournament == null ? null : _mapper.Map<TournamentDto>(tournament);
}
public async Task<TournamentDto> CreateAsync(TournamentCreateDto dto)
{
var tournament = _mapper.Map<Tournament>(dto);
await _uow.Tournaments.AddAsync(tournament);
await _uow.SaveChangesAsync();
return _mapper.Map<TournamentDto>(tournament);
}
public async Task UpdateAsync(TournamentUpdateDto dto)
{
var tournament = await _uow.Tournaments.GetByIdAsync(dto.TournamentId)
?? throw new KeyNotFoundException("Tournament not found.");
_mapper.Map(dto, tournament);
_uow.Tournaments.Update(tournament);
await _uow.SaveChangesAsync();
}
public async Task UpdateStatusAsync(int id, TournamentStatus status)
{
var tournament = await _uow.Tournaments.GetByIdAsync(id)
?? throw new KeyNotFoundException("Tournament not found.");
tournament.Status = status;
_uow.Tournaments.Update(tournament);
await _uow.SaveChangesAsync();
}
public async Task ArchiveAsync(int id)
{
var tournament = await _uow.Tournaments.GetByIdAsync(id)
?? throw new KeyNotFoundException("Tournament not found.");
tournament.IsArchived = true;
_uow.Tournaments.Update(tournament);
await _uow.SaveChangesAsync();
}
public async Task UnarchiveAsync(int id)
{
var tournament = await _uow.Tournaments.GetByIdAsync(id)
?? throw new KeyNotFoundException("Tournament not found.");
tournament.IsArchived = false;
_uow.Tournaments.Update(tournament);
await _uow.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var tournament = await _uow.Tournaments.GetByIdAsync(id)
?? throw new KeyNotFoundException("Tournament not found.");
_uow.Tournaments.Remove(tournament);
await _uow.SaveChangesAsync();
}
public async Task<IEnumerable<TournamentEventLevelDto>> GetEventLevelsAsync(int tournamentId)
{
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
return _mapper.Map<IEnumerable<TournamentEventLevelDto>>(tels);
}
public async Task<TournamentEventLevelDto> AddEventLevelAsync(TournamentEventLevelCreateDto dto)
{
var existing = await _uow.TournamentEventLevels.FindAsync(dto.TournamentId, dto.EventId, dto.EventLevelId);
if (existing != null)
throw new InvalidOperationException("This event/level combination already exists for this tournament.");
var tel = _mapper.Map<TournamentEventLevel>(dto);
await _uow.TournamentEventLevels.AddAsync(tel);
await _uow.SaveChangesAsync();
return _mapper.Map<TournamentEventLevelDto>(tel);
}
public async Task RemoveEventLevelAsync(int tournamentEventLevelId)
{
var tel = await _uow.TournamentEventLevels.GetByIdAsync(tournamentEventLevelId)
?? throw new KeyNotFoundException("Tournament event level not found.");
_uow.TournamentEventLevels.Remove(tel);
await _uow.SaveChangesAsync();
}
public async Task ToggleAgeWaiverAsync(int tournamentEventLevelId)
{
var tel = await _uow.TournamentEventLevels.GetByIdAsync(tournamentEventLevelId)
?? throw new KeyNotFoundException("Tournament event level not found.");
tel.AgeRestrictionWaived = !tel.AgeRestrictionWaived;
_uow.TournamentEventLevels.Update(tel);
await _uow.SaveChangesAsync();
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SportsDivision.Domain\SportsDivision.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.*" />
<PackageReference Include="FluentValidation" Version="11.*" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.*" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using FluentValidation;
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Validators;
public class EventRegistrationValidator : AbstractValidator<EventRegistrationCreateDto>
{
public EventRegistrationValidator()
{
RuleFor(x => x.TournamentEventLevelId).GreaterThan(0);
RuleFor(x => x).Must(x => x.StudentId.HasValue || x.RelayTeamId.HasValue)
.WithMessage("Either a student or relay team must be specified.");
}
}

View File

@@ -0,0 +1,15 @@
using FluentValidation;
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Validators;
public class SchoolCreateValidator : AbstractValidator<SchoolCreateDto>
{
public SchoolCreateValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
RuleFor(x => x.ShortName).MaximumLength(20);
RuleFor(x => x.SchoolLevel).IsInEnum();
RuleFor(x => x.ZoneId).GreaterThan(0);
}
}

View File

@@ -0,0 +1,18 @@
using FluentValidation;
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Validators;
public class StudentCreateValidator : AbstractValidator<StudentCreateDto>
{
public StudentCreateValidator()
{
RuleFor(x => x.FirstName).NotEmpty().MaximumLength(100);
RuleFor(x => x.LastName).NotEmpty().MaximumLength(100);
RuleFor(x => x.DateOfBirth).NotEmpty()
.LessThan(DateOnly.FromDateTime(DateTime.Today)).WithMessage("Date of birth must be in the past.");
RuleFor(x => x.Sex).IsInEnum();
RuleFor(x => x.SchoolId).GreaterThan(0);
RuleFor(x => x.ExistingStudentId).MaximumLength(50);
}
}

View File

@@ -0,0 +1,15 @@
using FluentValidation;
using SportsDivision.Application.DTOs;
namespace SportsDivision.Application.Validators;
public class TournamentCreateValidator : AbstractValidator<TournamentCreateDto>
{
public TournamentCreateValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
RuleFor(x => x.StartDate).NotEmpty();
RuleFor(x => x.EndDate).NotEmpty()
.GreaterThanOrEqualTo(x => x.StartDate).WithMessage("End date must be on or after start date.");
}
}

View File

@@ -0,0 +1,17 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class Event
{
public int EventId { get; set; }
public string Name { get; set; } = string.Empty;
public EventCategory Category { get; set; }
public bool IsRelay { get; set; }
public bool PrimarySchool { get; set; }
public bool SecondarySchool { get; set; }
public bool IsActive { get; set; } = true;
public ICollection<EventLevel> EventLevels { get; set; } = new List<EventLevel>();
public ICollection<TournamentEventLevel> TournamentEventLevels { get; set; } = new List<TournamentEventLevel>();
public ICollection<ScoringConstant> ScoringConstants { get; set; } = new List<ScoringConstant>();
}

View File

@@ -0,0 +1,17 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class EventLevel
{
public int EventLevelId { get; set; }
public string Name { get; set; } = string.Empty;
public Sex Sex { get; set; }
public int? MaxAge { get; set; }
public SchoolLevel SchoolLevel { get; set; }
public bool IsAgeBased { get; set; }
public int SortOrder { get; set; }
public bool IsActive { get; set; } = true;
public ICollection<Event> Events { get; set; } = new List<Event>();
public ICollection<TournamentEventLevel> TournamentEventLevels { get; set; } = new List<TournamentEventLevel>();
}

View File

@@ -0,0 +1,16 @@
namespace SportsDivision.Domain.Entities;
public class EventRegistration
{
public int EventRegistrationId { get; set; }
public int TournamentEventLevelId { get; set; }
public int? StudentId { get; set; }
public int? RelayTeamId { get; set; }
public string RegisteredBy { get; set; } = string.Empty;
public DateTime RegisteredAt { get; set; } = DateTime.UtcNow;
public TournamentEventLevel TournamentEventLevel { get; set; } = null!;
public Student? Student { get; set; }
public RelayTeam? RelayTeam { get; set; }
public ICollection<HeatLane> HeatLanes { get; set; } = new List<HeatLane>();
public Score? Score { get; set; }
}

View File

@@ -0,0 +1,13 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class Heat
{
public int HeatId { get; set; }
public int RoundId { get; set; }
public int HeatNumber { get; set; }
public HeatStatus Status { get; set; } = HeatStatus.Pending;
public Round Round { get; set; } = null!;
public ICollection<HeatLane> HeatLanes { get; set; } = new List<HeatLane>();
}

View File

@@ -0,0 +1,21 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class HeatLane
{
public int HeatLaneId { get; set; }
public int HeatId { get; set; }
public int EventRegistrationId { get; set; }
public int LaneNumber { get; set; }
public decimal? Time { get; set; }
public bool IsAdvanced { get; set; }
public AdvanceReason? AdvanceReason { get; set; }
public bool IsDNS { get; set; }
public bool IsDNF { get; set; }
public bool IsDQ { get; set; }
public string? RecordedBy { get; set; }
public DateTime? RecordedAt { get; set; }
public Heat Heat { get; set; } = null!;
public EventRegistration EventRegistration { get; set; } = null!;
}

View File

@@ -0,0 +1,30 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class HighJumpAttempt
{
public int HighJumpAttemptId { get; set; }
public int HighJumpHeightId { get; set; }
public int EventRegistrationId { get; set; }
public HighJumpAttemptResult? Attempt1 { get; set; }
public HighJumpAttemptResult? Attempt2 { get; set; }
public HighJumpAttemptResult? Attempt3 { get; set; }
public HighJumpHeight HighJumpHeight { get; set; } = null!;
public EventRegistration EventRegistration { get; set; } = null!;
public bool IsEliminated =>
Attempt1 == HighJumpAttemptResult.Fail &&
Attempt2 == HighJumpAttemptResult.Fail &&
Attempt3 == HighJumpAttemptResult.Fail;
public bool HasCleared =>
Attempt1 == HighJumpAttemptResult.Clear ||
Attempt2 == HighJumpAttemptResult.Clear ||
Attempt3 == HighJumpAttemptResult.Clear;
public int FailCount =>
(Attempt1 == HighJumpAttemptResult.Fail ? 1 : 0) +
(Attempt2 == HighJumpAttemptResult.Fail ? 1 : 0) +
(Attempt3 == HighJumpAttemptResult.Fail ? 1 : 0);
}

View File

@@ -0,0 +1,11 @@
namespace SportsDivision.Domain.Entities;
public class HighJumpHeight
{
public int HighJumpHeightId { get; set; }
public int TournamentEventLevelId { get; set; }
public decimal Height { get; set; }
public int SortOrder { get; set; }
public TournamentEventLevel TournamentEventLevel { get; set; } = null!;
public ICollection<HighJumpAttempt> Attempts { get; set; } = new List<HighJumpAttempt>();
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Entities;
public class PlacementPointConfig
{
public int PlacementPointConfigId { get; set; }
public int Placement { get; set; }
public int Points { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace SportsDivision.Domain.Entities;
public class RelayTeam
{
public int RelayTeamId { get; set; }
public string Name { get; set; } = string.Empty;
public int SchoolId { get; set; }
public School School { get; set; } = null!;
public ICollection<RelayTeamMember> Members { get; set; } = new List<RelayTeamMember>();
public ICollection<EventRegistration> Registrations { get; set; } = new List<EventRegistration>();
}

View File

@@ -0,0 +1,11 @@
namespace SportsDivision.Domain.Entities;
public class RelayTeamMember
{
public int RelayTeamMemberId { get; set; }
public int RelayTeamId { get; set; }
public int StudentId { get; set; }
public int RunOrder { get; set; }
public RelayTeam RelayTeam { get; set; } = null!;
public Student Student { get; set; } = null!;
}

View File

@@ -0,0 +1,16 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class Round
{
public int RoundId { get; set; }
public int TournamentEventLevelId { get; set; }
public RoundType RoundType { get; set; }
public int RoundOrder { get; set; }
public int? AdvanceTopN { get; set; }
public int? AdvanceFastestLosers { get; set; }
public RoundStatus Status { get; set; } = RoundStatus.Pending;
public TournamentEventLevel TournamentEventLevel { get; set; } = null!;
public ICollection<Heat> Heats { get; set; } = new List<Heat>();
}

View File

@@ -0,0 +1,15 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class School
{
public int SchoolId { get; set; }
public string Name { get; set; } = string.Empty;
public string? ShortName { get; set; }
public SchoolLevel SchoolLevel { get; set; }
public int ZoneId { get; set; }
public bool IsActive { get; set; } = true;
public Zone Zone { get; set; } = null!;
public ICollection<Student> Students { get; set; } = new List<Student>();
}

View File

@@ -0,0 +1,14 @@
namespace SportsDivision.Domain.Entities;
public class Score
{
public int ScoreId { get; set; }
public int EventRegistrationId { get; set; }
public decimal RawPerformance { get; set; }
public int CalculatedPoints { get; set; }
public int? Placement { get; set; }
public int PlacementPoints { get; set; }
public string? RecordedBy { get; set; }
public DateTime RecordedAt { get; set; } = DateTime.UtcNow;
public EventRegistration EventRegistration { get; set; } = null!;
}

View File

@@ -0,0 +1,12 @@
namespace SportsDivision.Domain.Entities;
public class ScoringConstant
{
public int ScoringConstantId { get; set; }
public int EventId { get; set; }
public decimal A { get; set; }
public decimal B { get; set; }
public decimal C { get; set; }
public string Unit { get; set; } = string.Empty;
public Event Event { get; set; } = null!;
}

View File

@@ -0,0 +1,27 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class Student
{
public int StudentId { get; set; }
public string ExistingStudentId { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateOnly DateOfBirth { get; set; }
public Sex Sex { get; set; }
public int SchoolId { get; set; }
public bool IsActive { get; set; } = true;
public School School { get; set; } = null!;
public ICollection<EventRegistration> Registrations { get; set; } = new List<EventRegistration>();
public int GetAge(DateOnly referenceDate)
{
var age = referenceDate.Year - DateOfBirth.Year;
if (DateOfBirth > referenceDate.AddYears(-age))
age--;
return age;
}
public string FullName => $"{FirstName} {LastName}";
}

View File

@@ -0,0 +1,17 @@
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Entities;
public class Tournament
{
public int TournamentId { get; set; }
public string Name { get; set; } = string.Empty;
public DateOnly StartDate { get; set; }
public DateOnly EndDate { get; set; }
public int? ZoneId { get; set; }
public SchoolLevel? SchoolLevel { get; set; }
public bool IsArchived { get; set; }
public TournamentStatus Status { get; set; } = TournamentStatus.Draft;
public Zone? Zone { get; set; }
public ICollection<TournamentEventLevel> TournamentEventLevels { get; set; } = new List<TournamentEventLevel>();
}

View File

@@ -0,0 +1,16 @@
namespace SportsDivision.Domain.Entities;
public class TournamentEventLevel
{
public int TournamentEventLevelId { get; set; }
public int TournamentId { get; set; }
public int EventId { get; set; }
public int EventLevelId { get; set; }
public bool AgeRestrictionWaived { get; set; }
public Tournament Tournament { get; set; } = null!;
public Event Event { get; set; } = null!;
public EventLevel EventLevel { get; set; } = null!;
public ICollection<EventRegistration> Registrations { get; set; } = new List<EventRegistration>();
public ICollection<Round> Rounds { get; set; } = new List<Round>();
public ICollection<HighJumpHeight> HighJumpHeights { get; set; } = new List<HighJumpHeight>();
}

View File

@@ -0,0 +1,10 @@
namespace SportsDivision.Domain.Entities;
public class Zone
{
public int ZoneId { get; set; }
public string Name { get; set; } = string.Empty;
public string Code { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public ICollection<School> Schools { get; set; } = new List<School>();
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum AdvanceReason
{
TopN,
FastestLoser,
Manual
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum EventCategory
{
Track,
Field,
HighJump
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum HeatStatus
{
Pending,
InProgress,
Completed
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum HighJumpAttemptResult
{
Clear,
Fail,
Pass
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum RoundStatus
{
Pending,
InProgress,
Completed
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum RoundType
{
Heat,
SemiFinal,
Final
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum SchoolLevel
{
Primary,
Secondary,
College
}

View File

@@ -0,0 +1,8 @@
namespace SportsDivision.Domain.Enums;
public enum SeedingMethod
{
Random,
ByPerformance,
Manual
}

View File

@@ -0,0 +1,7 @@
namespace SportsDivision.Domain.Enums;
public enum Sex
{
Male,
Female
}

View File

@@ -0,0 +1,10 @@
namespace SportsDivision.Domain.Enums;
public enum TournamentStatus
{
Draft,
Registration,
InProgress,
Completed,
Archived
}

View File

@@ -0,0 +1,10 @@
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Interfaces;
public interface IEventLevelRepository : IRepository<EventLevel>
{
Task<IEnumerable<EventLevel>> GetBySchoolLevelAsync(SchoolLevel level);
Task<IEnumerable<EventLevel>> GetBySexAsync(Sex sex);
}

View File

@@ -0,0 +1,11 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IEventRegistrationRepository : IRepository<EventRegistration>
{
Task<IEnumerable<EventRegistration>> GetByTournamentEventLevelAsync(int tournamentEventLevelId);
Task<IEnumerable<EventRegistration>> GetByStudentAsync(int studentId);
Task<bool> IsStudentRegisteredAsync(int tournamentEventLevelId, int studentId);
Task<IEnumerable<EventRegistration>> GetBySchoolAndTournamentAsync(int schoolId, int tournamentId);
}

View File

@@ -0,0 +1,11 @@
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Interfaces;
public interface IEventRepository : IRepository<Event>
{
Task<IEnumerable<Event>> GetByCategoryAsync(EventCategory category);
Task<Event?> GetWithEventLevelsAsync(int eventId);
Task<IEnumerable<Event>> GetBySchoolLevelAsync(bool primarySchool, bool secondarySchool);
}

View File

@@ -0,0 +1,9 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IHeatLaneRepository : IRepository<HeatLane>
{
Task<IEnumerable<HeatLane>> GetByHeatAsync(int heatId);
Task<HeatLane?> GetWithDetailsAsync(int heatLaneId);
}

View File

@@ -0,0 +1,8 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IHeatRepository : IRepository<Heat>
{
Task<Heat?> GetWithLanesAsync(int heatId);
}

View File

@@ -0,0 +1,9 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IHighJumpHeightRepository : IRepository<HighJumpHeight>
{
Task<IEnumerable<HighJumpHeight>> GetByTournamentEventLevelAsync(int tournamentEventLevelId);
Task<HighJumpHeight?> GetWithAttemptsAsync(int heightId);
}

View File

@@ -0,0 +1,8 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IPlacementPointConfigRepository : IRepository<PlacementPointConfig>
{
Task<PlacementPointConfig?> GetByPlacementAsync(int placement);
}

View File

@@ -0,0 +1,16 @@
using System.Linq.Expressions;
namespace SportsDivision.Domain.Interfaces;
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
Task<T> AddAsync(T entity);
Task AddRangeAsync(IEnumerable<T> entities);
void Update(T entity);
void Remove(T entity);
Task<bool> AnyAsync(Expression<Func<T, bool>> predicate);
Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null);
}

View File

@@ -0,0 +1,9 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IRoundRepository : IRepository<Round>
{
Task<IEnumerable<Round>> GetByTournamentEventLevelAsync(int tournamentEventLevelId);
Task<Round?> GetWithHeatsAsync(int roundId);
}

View File

@@ -0,0 +1,11 @@
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
namespace SportsDivision.Domain.Interfaces;
public interface ISchoolRepository : IRepository<School>
{
Task<IEnumerable<School>> GetByZoneAsync(int zoneId);
Task<IEnumerable<School>> GetBySchoolLevelAsync(SchoolLevel level);
Task<School?> GetWithStudentsAsync(int schoolId);
}

View File

@@ -0,0 +1,10 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IScoreRepository : IRepository<Score>
{
Task<IEnumerable<Score>> GetByTournamentEventLevelAsync(int tournamentEventLevelId);
Task<Score?> GetByRegistrationAsync(int eventRegistrationId);
Task<IEnumerable<Score>> GetBySchoolAndTournamentAsync(int schoolId, int tournamentId);
}

View File

@@ -0,0 +1,8 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IScoringConstantRepository : IRepository<ScoringConstant>
{
Task<ScoringConstant?> GetByEventAsync(int eventId);
}

View File

@@ -0,0 +1,11 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IStudentRepository : IRepository<Student>
{
Task<Student?> GetByExistingIdAsync(string existingStudentId);
Task<IEnumerable<Student>> GetBySchoolAsync(int schoolId);
Task<Student?> GetWithRegistrationsAsync(int studentId);
Task<IEnumerable<Student>> SearchAsync(string searchTerm);
}

View File

@@ -0,0 +1,11 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface ITournamentEventLevelRepository : IRepository<TournamentEventLevel>
{
Task<TournamentEventLevel?> GetWithRegistrationsAsync(int id);
Task<TournamentEventLevel?> GetWithRoundsAsync(int id);
Task<IEnumerable<TournamentEventLevel>> GetByTournamentAsync(int tournamentId);
Task<TournamentEventLevel?> FindAsync(int tournamentId, int eventId, int eventLevelId);
}

View File

@@ -0,0 +1,10 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface ITournamentRepository : IRepository<Tournament>
{
Task<IEnumerable<Tournament>> GetActiveAsync();
Task<Tournament?> GetWithEventLevelsAsync(int tournamentId);
Task<Tournament?> GetFullTournamentAsync(int tournamentId);
}

View File

@@ -0,0 +1,21 @@
namespace SportsDivision.Domain.Interfaces;
public interface IUnitOfWork : IDisposable
{
IZoneRepository Zones { get; }
ISchoolRepository Schools { get; }
IStudentRepository Students { get; }
IEventRepository Events { get; }
IEventLevelRepository EventLevels { get; }
ITournamentRepository Tournaments { get; }
ITournamentEventLevelRepository TournamentEventLevels { get; }
IEventRegistrationRepository EventRegistrations { get; }
IScoreRepository Scores { get; }
IRoundRepository Rounds { get; }
IHeatRepository Heats { get; }
IHeatLaneRepository HeatLanes { get; }
IHighJumpHeightRepository HighJumpHeights { get; }
IScoringConstantRepository ScoringConstants { get; }
IPlacementPointConfigRepository PlacementPointConfigs { get; }
Task<int> SaveChangesAsync();
}

View File

@@ -0,0 +1,9 @@
using SportsDivision.Domain.Entities;
namespace SportsDivision.Domain.Interfaces;
public interface IZoneRepository : IRepository<Zone>
{
Task<Zone?> GetByCodeAsync(string code);
Task<IEnumerable<Zone>> GetAllWithSchoolsAsync();
}

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using SportsDivision.Domain.Entities;
using SportsDivision.Infrastructure.Identity;
namespace SportsDivision.Infrastructure.Data;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
public DbSet<Zone> Zones => Set<Zone>();
public DbSet<School> Schools => Set<School>();
public DbSet<Student> Students => Set<Student>();
public DbSet<EventLevel> EventLevels => Set<EventLevel>();
public DbSet<Event> Events => Set<Event>();
public DbSet<Tournament> Tournaments => Set<Tournament>();
public DbSet<TournamentEventLevel> TournamentEventLevels => Set<TournamentEventLevel>();
public DbSet<EventRegistration> EventRegistrations => Set<EventRegistration>();
public DbSet<RelayTeam> RelayTeams => Set<RelayTeam>();
public DbSet<RelayTeamMember> RelayTeamMembers => Set<RelayTeamMember>();
public DbSet<Round> Rounds => Set<Round>();
public DbSet<Heat> Heats => Set<Heat>();
public DbSet<HeatLane> HeatLanes => Set<HeatLane>();
public DbSet<HighJumpHeight> HighJumpHeights => Set<HighJumpHeight>();
public DbSet<HighJumpAttempt> HighJumpAttempts => Set<HighJumpAttempt>();
public DbSet<Score> Scores => Set<Score>();
public DbSet<ScoringConstant> ScoringConstants => Set<ScoringConstant>();
public DbSet<PlacementPointConfig> PlacementPointConfigs => Set<PlacementPointConfig>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
}
}

Some files were not shown because too many files have changed in this diff Show More