Initial Commit
This commit is contained in:
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal 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
12
SportsDivision.slnx
Normal 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
1034
project.md
Normal file
File diff suppressed because it is too large
Load Diff
4
src/SportsDivision.Application/DTOs/DashboardDto.cs
Normal file
4
src/SportsDivision.Application/DTOs/DashboardDto.cs
Normal 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; } }
|
||||||
5
src/SportsDivision.Application/DTOs/EventDto.cs
Normal file
5
src/SportsDivision.Application/DTOs/EventDto.cs
Normal 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; } }
|
||||||
4
src/SportsDivision.Application/DTOs/EventLevelDto.cs
Normal file
4
src/SportsDivision.Application/DTOs/EventLevelDto.cs
Normal 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; } }
|
||||||
@@ -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; } }
|
||||||
3
src/SportsDivision.Application/DTOs/HeatDto.cs
Normal file
3
src/SportsDivision.Application/DTOs/HeatDto.cs
Normal 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(); }
|
||||||
4
src/SportsDivision.Application/DTOs/HeatLaneDto.cs
Normal file
4
src/SportsDivision.Application/DTOs/HeatLaneDto.cs
Normal 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; } }
|
||||||
6
src/SportsDivision.Application/DTOs/HighJumpDto.cs
Normal file
6
src/SportsDivision.Application/DTOs/HighJumpDto.cs
Normal 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; } }
|
||||||
@@ -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; } }
|
||||||
@@ -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; } }
|
||||||
@@ -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; } }
|
||||||
@@ -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; } }
|
||||||
@@ -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; } }
|
||||||
@@ -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; } }
|
||||||
@@ -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(); }
|
||||||
4
src/SportsDivision.Application/DTOs/RoundDto.cs
Normal file
4
src/SportsDivision.Application/DTOs/RoundDto.cs
Normal 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; } }
|
||||||
5
src/SportsDivision.Application/DTOs/SchoolDto.cs
Normal file
5
src/SportsDivision.Application/DTOs/SchoolDto.cs
Normal 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; } }
|
||||||
3
src/SportsDivision.Application/DTOs/ScoreDto.cs
Normal file
3
src/SportsDivision.Application/DTOs/ScoreDto.cs
Normal 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; } }
|
||||||
@@ -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; } }
|
||||||
5
src/SportsDivision.Application/DTOs/StudentDto.cs
Normal file
5
src/SportsDivision.Application/DTOs/StudentDto.cs
Normal 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; } }
|
||||||
5
src/SportsDivision.Application/DTOs/TournamentDto.cs
Normal file
5
src/SportsDivision.Application/DTOs/TournamentDto.cs
Normal 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; } }
|
||||||
@@ -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; } }
|
||||||
4
src/SportsDivision.Application/DTOs/UserDto.cs
Normal file
4
src/SportsDivision.Application/DTOs/UserDto.cs
Normal 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; } }
|
||||||
3
src/SportsDivision.Application/DTOs/ZoneDto.cs
Normal file
3
src/SportsDivision.Application/DTOs/ZoneDto.cs
Normal 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; }
|
||||||
27
src/SportsDivision.Application/DependencyInjection.cs
Normal file
27
src/SportsDivision.Application/DependencyInjection.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using SportsDivision.Application.DTOs;
|
||||||
|
namespace SportsDivision.Application.Interfaces;
|
||||||
|
public interface IDashboardService { Task<DashboardDto> GetDashboardAsync(int? tournamentId = null); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
@@ -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); }
|
||||||
75
src/SportsDivision.Application/Mappings/MappingProfile.cs
Normal file
75
src/SportsDivision.Application/Mappings/MappingProfile.cs
Normal 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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/SportsDivision.Application/Services/DashboardService.cs
Normal file
47
src/SportsDivision.Application/Services/DashboardService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/SportsDivision.Application/Services/EventService.cs
Normal file
77
src/SportsDivision.Application/Services/EventService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
218
src/SportsDivision.Application/Services/HeatManagementService.cs
Normal file
218
src/SportsDivision.Application/Services/HeatManagementService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
174
src/SportsDivision.Application/Services/HighJumpService.cs
Normal file
174
src/SportsDivision.Application/Services/HighJumpService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
225
src/SportsDivision.Application/Services/ReportService.cs
Normal file
225
src/SportsDivision.Application/Services/ReportService.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/SportsDivision.Application/Services/SchoolService.cs
Normal file
69
src/SportsDivision.Application/Services/SchoolService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
181
src/SportsDivision.Application/Services/ScoringService.cs
Normal file
181
src/SportsDivision.Application/Services/ScoringService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/SportsDivision.Application/Services/StudentService.cs
Normal file
74
src/SportsDivision.Application/Services/StudentService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/SportsDivision.Application/Services/TournamentService.cs
Normal file
121
src/SportsDivision.Application/Services/TournamentService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/SportsDivision.Application/Validators/SchoolValidator.cs
Normal file
15
src/SportsDivision.Application/Validators/SchoolValidator.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/SportsDivision.Domain/Entities/Event.cs
Normal file
17
src/SportsDivision.Domain/Entities/Event.cs
Normal 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>();
|
||||||
|
}
|
||||||
17
src/SportsDivision.Domain/Entities/EventLevel.cs
Normal file
17
src/SportsDivision.Domain/Entities/EventLevel.cs
Normal 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>();
|
||||||
|
}
|
||||||
16
src/SportsDivision.Domain/Entities/EventRegistration.cs
Normal file
16
src/SportsDivision.Domain/Entities/EventRegistration.cs
Normal 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; }
|
||||||
|
}
|
||||||
13
src/SportsDivision.Domain/Entities/Heat.cs
Normal file
13
src/SportsDivision.Domain/Entities/Heat.cs
Normal 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>();
|
||||||
|
}
|
||||||
21
src/SportsDivision.Domain/Entities/HeatLane.cs
Normal file
21
src/SportsDivision.Domain/Entities/HeatLane.cs
Normal 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!;
|
||||||
|
}
|
||||||
30
src/SportsDivision.Domain/Entities/HighJumpAttempt.cs
Normal file
30
src/SportsDivision.Domain/Entities/HighJumpAttempt.cs
Normal 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);
|
||||||
|
}
|
||||||
11
src/SportsDivision.Domain/Entities/HighJumpHeight.cs
Normal file
11
src/SportsDivision.Domain/Entities/HighJumpHeight.cs
Normal 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>();
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
11
src/SportsDivision.Domain/Entities/RelayTeam.cs
Normal file
11
src/SportsDivision.Domain/Entities/RelayTeam.cs
Normal 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>();
|
||||||
|
}
|
||||||
11
src/SportsDivision.Domain/Entities/RelayTeamMember.cs
Normal file
11
src/SportsDivision.Domain/Entities/RelayTeamMember.cs
Normal 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!;
|
||||||
|
}
|
||||||
16
src/SportsDivision.Domain/Entities/Round.cs
Normal file
16
src/SportsDivision.Domain/Entities/Round.cs
Normal 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>();
|
||||||
|
}
|
||||||
15
src/SportsDivision.Domain/Entities/School.cs
Normal file
15
src/SportsDivision.Domain/Entities/School.cs
Normal 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>();
|
||||||
|
}
|
||||||
14
src/SportsDivision.Domain/Entities/Score.cs
Normal file
14
src/SportsDivision.Domain/Entities/Score.cs
Normal 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!;
|
||||||
|
}
|
||||||
12
src/SportsDivision.Domain/Entities/ScoringConstant.cs
Normal file
12
src/SportsDivision.Domain/Entities/ScoringConstant.cs
Normal 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!;
|
||||||
|
}
|
||||||
27
src/SportsDivision.Domain/Entities/Student.cs
Normal file
27
src/SportsDivision.Domain/Entities/Student.cs
Normal 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}";
|
||||||
|
}
|
||||||
17
src/SportsDivision.Domain/Entities/Tournament.cs
Normal file
17
src/SportsDivision.Domain/Entities/Tournament.cs
Normal 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>();
|
||||||
|
}
|
||||||
16
src/SportsDivision.Domain/Entities/TournamentEventLevel.cs
Normal file
16
src/SportsDivision.Domain/Entities/TournamentEventLevel.cs
Normal 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>();
|
||||||
|
}
|
||||||
10
src/SportsDivision.Domain/Entities/Zone.cs
Normal file
10
src/SportsDivision.Domain/Entities/Zone.cs
Normal 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>();
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/AdvanceReason.cs
Normal file
8
src/SportsDivision.Domain/Enums/AdvanceReason.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum AdvanceReason
|
||||||
|
{
|
||||||
|
TopN,
|
||||||
|
FastestLoser,
|
||||||
|
Manual
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/EventCategory.cs
Normal file
8
src/SportsDivision.Domain/Enums/EventCategory.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum EventCategory
|
||||||
|
{
|
||||||
|
Track,
|
||||||
|
Field,
|
||||||
|
HighJump
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/HeatStatus.cs
Normal file
8
src/SportsDivision.Domain/Enums/HeatStatus.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum HeatStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
InProgress,
|
||||||
|
Completed
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/HighJumpAttemptResult.cs
Normal file
8
src/SportsDivision.Domain/Enums/HighJumpAttemptResult.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum HighJumpAttemptResult
|
||||||
|
{
|
||||||
|
Clear,
|
||||||
|
Fail,
|
||||||
|
Pass
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/RoundStatus.cs
Normal file
8
src/SportsDivision.Domain/Enums/RoundStatus.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum RoundStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
InProgress,
|
||||||
|
Completed
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/RoundType.cs
Normal file
8
src/SportsDivision.Domain/Enums/RoundType.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum RoundType
|
||||||
|
{
|
||||||
|
Heat,
|
||||||
|
SemiFinal,
|
||||||
|
Final
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/SchoolLevel.cs
Normal file
8
src/SportsDivision.Domain/Enums/SchoolLevel.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum SchoolLevel
|
||||||
|
{
|
||||||
|
Primary,
|
||||||
|
Secondary,
|
||||||
|
College
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Enums/SeedingMethod.cs
Normal file
8
src/SportsDivision.Domain/Enums/SeedingMethod.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum SeedingMethod
|
||||||
|
{
|
||||||
|
Random,
|
||||||
|
ByPerformance,
|
||||||
|
Manual
|
||||||
|
}
|
||||||
7
src/SportsDivision.Domain/Enums/Sex.cs
Normal file
7
src/SportsDivision.Domain/Enums/Sex.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum Sex
|
||||||
|
{
|
||||||
|
Male,
|
||||||
|
Female
|
||||||
|
}
|
||||||
10
src/SportsDivision.Domain/Enums/TournamentStatus.cs
Normal file
10
src/SportsDivision.Domain/Enums/TournamentStatus.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace SportsDivision.Domain.Enums;
|
||||||
|
|
||||||
|
public enum TournamentStatus
|
||||||
|
{
|
||||||
|
Draft,
|
||||||
|
Registration,
|
||||||
|
InProgress,
|
||||||
|
Completed,
|
||||||
|
Archived
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
11
src/SportsDivision.Domain/Interfaces/IEventRepository.cs
Normal file
11
src/SportsDivision.Domain/Interfaces/IEventRepository.cs
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
8
src/SportsDivision.Domain/Interfaces/IHeatRepository.cs
Normal file
8
src/SportsDivision.Domain/Interfaces/IHeatRepository.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using SportsDivision.Domain.Entities;
|
||||||
|
|
||||||
|
namespace SportsDivision.Domain.Interfaces;
|
||||||
|
|
||||||
|
public interface IHeatRepository : IRepository<Heat>
|
||||||
|
{
|
||||||
|
Task<Heat?> GetWithLanesAsync(int heatId);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using SportsDivision.Domain.Entities;
|
||||||
|
|
||||||
|
namespace SportsDivision.Domain.Interfaces;
|
||||||
|
|
||||||
|
public interface IPlacementPointConfigRepository : IRepository<PlacementPointConfig>
|
||||||
|
{
|
||||||
|
Task<PlacementPointConfig?> GetByPlacementAsync(int placement);
|
||||||
|
}
|
||||||
16
src/SportsDivision.Domain/Interfaces/IRepository.cs
Normal file
16
src/SportsDivision.Domain/Interfaces/IRepository.cs
Normal 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);
|
||||||
|
}
|
||||||
9
src/SportsDivision.Domain/Interfaces/IRoundRepository.cs
Normal file
9
src/SportsDivision.Domain/Interfaces/IRoundRepository.cs
Normal 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);
|
||||||
|
}
|
||||||
11
src/SportsDivision.Domain/Interfaces/ISchoolRepository.cs
Normal file
11
src/SportsDivision.Domain/Interfaces/ISchoolRepository.cs
Normal 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);
|
||||||
|
}
|
||||||
10
src/SportsDivision.Domain/Interfaces/IScoreRepository.cs
Normal file
10
src/SportsDivision.Domain/Interfaces/IScoreRepository.cs
Normal 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using SportsDivision.Domain.Entities;
|
||||||
|
|
||||||
|
namespace SportsDivision.Domain.Interfaces;
|
||||||
|
|
||||||
|
public interface IScoringConstantRepository : IRepository<ScoringConstant>
|
||||||
|
{
|
||||||
|
Task<ScoringConstant?> GetByEventAsync(int eventId);
|
||||||
|
}
|
||||||
11
src/SportsDivision.Domain/Interfaces/IStudentRepository.cs
Normal file
11
src/SportsDivision.Domain/Interfaces/IStudentRepository.cs
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
21
src/SportsDivision.Domain/Interfaces/IUnitOfWork.cs
Normal file
21
src/SportsDivision.Domain/Interfaces/IUnitOfWork.cs
Normal 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();
|
||||||
|
}
|
||||||
9
src/SportsDivision.Domain/Interfaces/IZoneRepository.cs
Normal file
9
src/SportsDivision.Domain/Interfaces/IZoneRepository.cs
Normal 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();
|
||||||
|
}
|
||||||
7
src/SportsDivision.Domain/SportsDivision.Domain.csproj
Normal file
7
src/SportsDivision.Domain/SportsDivision.Domain.csproj
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -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
Reference in New Issue
Block a user