# Dominica Sports Division Application — Project Management Document

## 1. Project Overview

**Project Name:** Dominica Sports Division Application
**Platform:** ASP.NET Core MVC (.NET 10)
**Purpose:** A web application for the Dominica Sports Division to manage school athletics tournaments — from student registration and event setup through score tracking, school points tallying, and printable report generation.

**Current State:** Fresh `dotnet new mvc` scaffold with default HomeController, ErrorViewModel, and standard layout views. No domain logic, no database, no authentication.

**Target Architecture:** Clean Architecture (detailed in Section 10).

---

## 2. Requirements Summary

| # | Requirement | Priority |
|---|------------|----------|
| R1 | Student Registration | High |
| R2 | Three Event Categories (Track, Field, High Jump) | High |
| R3 | Event Levels (18 divisions by age/label + sex) | High |
| R4 | School Levels (Primary, Secondary, College) | High |
| R5 | Adjustable Heights for Jumps | Medium |
| R6 | Event Definitions with Equipment Specs | High |
| R7 | Zones | High |
| R8 | Tournaments (with archive option) | High |
| R9 | Register Students to Events (age/sex rules) | High |
| R10 | User Management & Role Management | High |
| R11 | Event Score Tracking & School Points | High |
| R12 | Admin Dashboard & Printable Reports | High |
| R13 | Refactor to Clean Architecture | High |

---

## 3. Detailed Requirements

### R1 — Student Registration

Each student record contains:

| Field | Type | Constraints |
|-------|------|------------|
| `StudentId` | int | **Primary Key**, auto-increment |
| `ExistingStudentId` | string | **Candidate Key** (unique, required) — the student's pre-existing school-assigned ID number |
| `FirstName` | string | Required, max 100 chars |
| `LastName` | string | Required, max 100 chars |
| `DateOfBirth` | date | Required, must be in the past |
| `Sex` | enum | `Male`, `Female` |
| `SchoolId` | FK | Required — references the School entity |
| `IsActive` | bool | Defaults to `true` |

**Key design:**
- `StudentId` is the internal surrogate primary key (auto-increment integer). Used in all foreign key relationships.
- `ExistingStudentId` is the natural/business key — the ID the student already has from their school (e.g., school registration number). It has a unique index and can be used to look up students. This prevents duplicate registrations of the same real-world student.

**Derived fields (computed, not stored):**
- `Age` — calculated from `DateOfBirth` relative to the tournament date (or current date).
- `EventLevel` — resolved from `Age`, `Sex`, and school level using the Event Level rules (R3).

**Business rules:**
- A student must belong to exactly one school.
- Deactivated students (`IsActive = false`) cannot be registered to new events but their historical scores are preserved.

---

### R2 — Three Event Categories

Events are classified into three categories because their scoring mechanics differ fundamentally:

| Category | Measurement | Scoring Basis | Competition Format | Examples |
|----------|------------|---------------|-------------------|---------|
| **Track Events** | Time (seconds) | Lower is better — `Points = A * (B - T)^C` | Seeding → Heats → (Semi-finals) → Finals | 100m, 200m, 400m, 800m, 1200m, 4x100m Relay |
| **Field Events** | Distance (metres) | Higher is better — `Points = A * (D - B)^C` | Best of N attempts | Long Jump, Triple Jump, Shot Put, Discus, Javelin |
| **High Jump** | Height (metres) | Higher is better — `Points = A * (M - B)^C` with attempt/clearance tracking | Progressive bar heights with 3 attempts per height | High Jump |

**Why High Jump is separate:**
High Jump has a unique competition format. Athletes choose heights to attempt, get three attempts per height, and are eliminated after three consecutive failures. The bar height progresses upward. This requires tracking individual attempts (clear / fail / pass) at each height — a data model fundamentally different from track (single time) or field (best of N throws/jumps).

#### Track Event Round Progression (Seeding, Heats, Semi-finals, Finals)

Track events follow a multi-round elimination structure. The number of rounds depends on participant count.

**Rounds (in order):**

| Round | Purpose | Required |
|-------|---------|----------|
| **Heats** | First round — split registered students across multiple heats to reduce field size | Yes (if > 1 heat needed) |
| **Semi-finals** | Intermediate round — top qualifiers from heats compete to advance to finals | **Skippable** — only used if participant count warrants it |
| **Finals** | Deciding round — determines placement and points | Yes |

**When to skip semi-finals:**
Semi-finals are skipped when the number of registered students is small enough that heats can feed directly into finals. The admin configures the round structure per TournamentEventLevel based on registration count. Typical thresholds:

| Registered Students | Round Structure |
|--------------------|----------------|
| ≤ 8 (1 heat) | Finals only (no heats, no semis) |
| 9 – 24 | Heats → Finals |
| 25+ | Heats → Semi-finals → Finals |

> These thresholds are **guidelines, not hard rules**. The admin has full control to add or skip semi-finals for any event.

**Seeding:**
Before heats begin, registered students are **seeded** (assigned to heats and lanes). Seeding can be:
- **Random** — students are randomly distributed across heats.
- **By performance** — if prior times are available (e.g., from zone prelims), students are spread across heats so each heat has a mix of fast and slow runners (serpentine seeding).
- **Manual** — admin manually assigns students to heats and lanes.

**Advancement rules:**
After each round, students advance to the next round based on:
- **Top N per heat** — e.g., top 3 from each heat advance automatically.
- **Fastest losers** — additional qualifiers taken from remaining athletes by best time across all heats (e.g., "top 3 + 2 fastest losers").
- The admin configures `AdvanceTopN` and `AdvanceFastestLosers` per round.

**Lane assignment:**
Each student (or relay team) in a heat is assigned a lane number. Lane count depends on the facility (typically 6 or 8 lanes).

#### Track Round Entities

**Round:**

| Field | Type | Notes |
|-------|------|-------|
| `RoundId` | int | PK, auto-increment |
| `TournamentEventLevelId` | FK | The tournament-event-level this round belongs to |
| `RoundType` | enum | `Heat`, `SemiFinal`, `Final` |
| `RoundOrder` | int | 1 = Heats, 2 = Semi-finals, 3 = Finals (or 1 = Heats, 2 = Finals if semis skipped) |
| `AdvanceTopN` | int (nullable) | Top N per heat auto-advance (null for Finals) |
| `AdvanceFastestLosers` | int (nullable) | Additional qualifiers by best time across heats (null for Finals) |
| `Status` | enum | `Pending`, `InProgress`, `Completed` |

**Heat:**

| Field | Type | Notes |
|-------|------|-------|
| `HeatId` | int | PK, auto-increment |
| `RoundId` | FK | The round this heat belongs to |
| `HeatNumber` | int | e.g., Heat 1, Heat 2, Heat 3 |
| `Status` | enum | `Pending`, `InProgress`, `Completed` |

**HeatLane (lane assignment per student per heat):**

| Field | Type | Notes |
|-------|------|-------|
| `HeatLaneId` | int | PK, auto-increment |
| `HeatId` | FK | |
| `RegistrationId` | FK | The student's event registration |
| `LaneNumber` | int | 1–8 (or facility max) |
| `Time` | decimal (nullable) | Recorded time in seconds |
| `IsAdvanced` | bool | Did this student advance to the next round? |
| `AdvanceReason` | enum (nullable) | `TopN`, `FastestLoser`, `Manual` — how they qualified |
| `RecordedBy` | FK (nullable) | Official who recorded the time |
| `RecordedAt` | datetime (nullable) | |

**Workflow:**
1. Admin creates round structure for a track TournamentEventLevel (e.g., Heats + Finals, or Heats + Semis + Finals).
2. Admin or system seeds students into Heat 1, Heat 2, etc. with lane assignments.
3. Officials record times per lane per heat.
4. System identifies qualifiers (top N + fastest losers) and marks `IsAdvanced = true`.
5. Next round's heats are populated from advancing students (re-seeded for semis/finals).
6. Finals results determine placement and school points. **Only the Finals round produces placement points.**

---

### R3 — Event Levels

An **Event Level** is the competitive division in which a student participates. It combines a **division name** (age-based or label-based) with **sex**. Event levels are a first-class entity in the system, not just an enum.

#### Primary School Event Levels

Primary schools do not use age-based divisions because groups are too small. Instead, they use label-based divisions:

| Event Level | Sex | School Level |
|------------|-----|-------------|
| Junior Boys | Male | Primary |
| Junior Girls | Female | Primary |
| Senior Boys | Male | Primary |
| Senior Girls | Female | Primary |

The distinction between "Junior" and "Senior" at the primary level is determined by the school or tournament admin (e.g., grades 1-3 vs 4-6). There is no strict age cutoff enforced by the system for these levels.

#### Secondary / College Event Levels

Secondary and College competitions use age-based divisions:

| Event Level | Sex | Max Age | School Level(s) |
|------------|-----|---------|-----------------|
| Under 14 Boys | Male | 13 | Secondary |
| Under 14 Girls | Female | 13 | Secondary |
| Under 15 Boys | Male | 14 | Secondary |
| Under 15 Girls | Female | 14 | Secondary |
| Under 16 Boys | Male | 15 | Secondary |
| Under 16 Girls | Female | 15 | Secondary |
| Under 17 Boys | Male | 16 | Secondary / College |
| Under 17 Girls | Female | 16 | Secondary / College |
| Under 20 Boys | Male | 19 | Secondary / College |
| Under 20 Girls | Female | 19 | Secondary / College |
| Under 21 Boys | Male | 20 | Secondary / College |
| Under 21 Girls | Female | 20 | Secondary / College |
| Open Boys | Male | No limit | Secondary / College |
| Open Girls | Female | No limit | Secondary / College |

**Age calculation:** A student's age is determined as of the **tournament start date** (not the current date), ensuring consistency throughout a tournament.

**Compete-up rule (R9):** For age-based levels, students may compete in a level **above** their age but never below. For example, an Under 14 student may enter Under 16 events but not the reverse. "Open" levels have no age ceiling.

#### Age Limit Waiver

Some events may have their age restriction **waived** on a per-tournament basis when the number of eligible registrants is too small to form a viable competition group. An admin can toggle an `AgeRestrictionWaived` flag on any tournament-event-level combination, which opens the event to all students in that sex category regardless of age. This is common in smaller zones where strict event leveling would result in events with very few competitors.

#### EventLevel Entity

| Field | Type | Notes |
|-------|------|-------|
| `EventLevelId` | int | PK, auto-increment |
| `Name` | string | e.g., "Junior Boys", "Under 17 Girls", "Open Boys" |
| `Sex` | enum | `Male` or `Female` |
| `MaxAge` | int (nullable) | Age ceiling; null for Primary levels (Junior/Senior) and Open levels |
| `SchoolLevel` | enum | `Primary`, `Secondary`, `College` — which school level(s) this level applies to |
| `IsAgeBased` | bool | `false` for Junior/Senior (Primary); `true` for Under-X and Open (Secondary/College) |
| `SortOrder` | int | Display ordering |
| `IsActive` | bool | Soft delete |

---

### R4 — School Levels

| School Level | Typical Ages | Event Levels |
|-------------|-------------|-------------|
| **Primary** | 5 – 12 | Junior Boys/Girls, Senior Boys/Girls (no age cutoffs) |
| **Secondary** | 12 – 18 | Under 14 through Under 21, Open (age-based, by sex) |
| **College** | 16 – 20 | Under 17 through Under 21, Open (age-based, by sex) |

**Key rule:** Secondary and College students compete together because there are only three colleges on the island (DSC, BTC, ACADEMIX). The system must support a combined competition bracket for Secondary/College while still tracking school-level origin for reporting.

**School entity fields:**

| Field | Type | Notes |
|-------|------|-------|
| `SchoolId` | int | PK, auto-increment |
| `Name` | string | Required, e.g., "Convent High School" |
| `ShortName` | string (nullable) | Abbreviation, e.g., "CHS". Primarily used by Secondary/College schools. |
| `SchoolLevel` | enum | `Primary`, `Secondary`, `College` |
| `ZoneId` | FK | Required — references Zone |
| `IsActive` | bool | Defaults to `true` |

---

### R5 — Adjustable Heights for Jumps

High Jump events require configurable height progressions per tournament:

| Field | Type | Notes |
|-------|------|-------|
| `EventId` | FK | The High Jump event |
| `TournamentId` | FK | The tournament |
| `StartingHeight` | decimal (m) | The first bar height |
| `HeightIncrement` | decimal (m) | Default increment between rounds |
| `Heights` | collection | Ordered list of heights to be attempted |

**Admins can:**
- Set the starting height and default increment.
- Manually add, remove, or reorder individual heights in the progression.
- Adjust heights mid-competition if needed.

**Attempt tracking per student per height:**

| Field | Type | Values |
|-------|------|--------|
| `StudentId` | FK | |
| `Height` | decimal (m) | |
| `Attempt1` | enum | `Clear`, `Fail`, `Pass` |
| `Attempt2` | enum | `Clear`, `Fail`, `Pass`, `null` |
| `Attempt3` | enum | `Clear`, `Fail`, `Pass`, `null` |

A student is eliminated after three consecutive failures (across any combination of heights). A student may **pass** a height to conserve attempts.

---

### R6 — Event Definitions

Events are the specific competitions students can enter. Each event belongs to exactly one category (R2). Equipment weight is part of the event name (e.g., "Shot Put 3kg" and "Shot Put 5kg" are distinct events, not one event with variable weight).

#### Complete Event List (32 unique events)

**Track Events (15)**

| # | Event Name | Primary | Secondary/College | Relay | Notes |
|---|-----------|---------|-------------------|-------|-------|
| 1 | 80m | Yes | — | No | Primary only |
| 2 | 100m | — | Yes | No | |
| 3 | 150m | Yes | — | No | Primary only |
| 4 | 200m | — | Yes | No | |
| 5 | 300m | Yes | — | No | Primary only |
| 6 | 400m | — | Yes | No | |
| 7 | 800m | — | Yes | No | |
| 8 | 1200m | Yes | — | No | Primary only |
| 9 | 1500m | — | Yes | No | |
| 10 | 3000m | — | Yes | No | |
| 11 | 5000m | — | Yes | No | |
| 12 | 80mH | — | Yes | No | 80m Hurdles |
| 13 | 4X80m Relay | Yes | — | Yes | Primary only; 4 students per school |
| 14 | 4X100m Relay | — | Yes | Yes | 4 students per school |
| 15 | 4X400m | — | Yes | Yes | 4 students per school |

**Field Events (16)**

| # | Event Name | Primary | Secondary/College | Notes |
|---|-----------|---------|-------------------|-------|
| 16 | Long Jump | Yes | Yes | Both levels |
| 17 | Triple Jump | — | Yes | |
| 18 | Shot Put 3kg | — | Yes | |
| 19 | Shot Put 4kg | — | Yes | |
| 20 | Shot Put 5kg | — | Yes | |
| 21 | Shot Put 6kg | — | Yes | |
| 22 | Discus 1kg | — | Yes | |
| 23 | Discus 1.25kg | — | Yes | |
| 24 | Discus 1.5kg | — | Yes | |
| 25 | Discus 1.75kg | — | Yes | |
| 26 | Javelin 400g | — | Yes | |
| 27 | Javelin 500g | — | Yes | |
| 28 | Javelin 600g | — | Yes | |
| 29 | Javelin 700g | — | Yes | |
| 30 | Javelin 800g | — | Yes | |
| 31 | Throwing the Cricket Ball | Yes | — | Primary only |

**High Jump (1)**

| # | Event Name | Primary | Secondary/College | Notes |
|---|-----------|---------|-------------------|-------|
| 32 | High Jump | Yes | Yes | Both levels; bar heights adjustable per tournament |

#### School Level Summary

- **Primary only (8):** 80m, 150m, 300m, 1200m, 4X80m Relay, Throwing the Cricket Ball, (+ Long Jump & High Jump shared)
- **Secondary/College only (22):** 100m, 200m, 400m, 800m, 1500m, 3000m, 5000m, 80mH, 4X100m Relay, 4X400m, Triple Jump, all Shot Put/Discus/Javelin variants, (+ Long Jump & High Jump shared)
- **Both levels (2):** Long Jump, High Jump

**Event entity fields:**

| Field | Type | Notes |
|-------|------|-------|
| `EventId` | int | PK, auto-increment |
| `Name` | string | e.g., "100m", "Shot Put 3kg", "Throwing the Cricket Ball" |
| `Category` | enum | `Track`, `Field`, `HighJump` |
| `IsRelay` | bool | If true, registration is per-school team |
| `PrimarySchool` | bool | Available for primary school tournaments |
| `SecondarySchool` | bool | Available for secondary/college tournaments |
| `AllowedEventLevels` | collection | Which EventLevels this event can be offered at (many-to-many) |
| `IsActive` | bool | Soft delete |

**Tournament-Event-Level link (TournamentEventLevel):**

This junction entity defines which events are offered at which levels in a specific tournament. It is the core scheduling unit.

| Field | Type | Notes |
|-------|------|-------|
| `TournamentId` | FK | |
| `EventId` | FK | |
| `EventLevelId` | FK | References the EventLevel (e.g., "Under 17 Boys") |
| `AgeRestrictionWaived` | bool | When `true`, the level's age limit is ignored — all students of the matching sex may register. Used when groups are too small. |

---

### R7 — Zones

Dominica is divided into geographic zones for organizing preliminary competitions:

| Zone | Code |
|------|------|
| North West | NW |
| North | N |
| South | S |
| South East | SE |
| North East | NE |
| South West | SW |
| West Central | WC |
| Secondary School / College | SSC |

**Notes:**
- The first seven zones are geographic and primarily serve primary schools.
- The "Secondary School / College" zone is a special zone that groups all secondary schools and colleges together for their combined competitions.
- Each school belongs to exactly one zone.
- Tournaments can be scoped to a single zone (zone preliminaries) or island-wide (national championships).

---

### R8 — Tournaments

A tournament represents a competition event (e.g., "2026 Independence Athletics Championship — North East Zone Preliminaries" or "2026 National Primary Schools Championship").

| Field | Type | Notes |
|-------|------|-------|
| `Name` | string | Required |
| `StartDate` | date | Used for student age calculation |
| `EndDate` | date | |
| `ZoneId` | FK (nullable) | Null = island-wide; otherwise zone-specific |
| `SchoolLevel` | enum (nullable) | Filter by Primary / Secondary / College or null for all |
| `IsArchived` | bool | Archived tournaments are read-only and hidden from active lists |
| `Status` | enum | `Draft`, `Registration`, `InProgress`, `Completed`, `Archived` |

**Tournament lifecycle:**
1. **Draft** — Admin creates the tournament and configures events, event levels, and height progressions.
2. **Registration** — Coaches/principals register their students to events.
3. **InProgress** — Competition is underway; scores are being recorded.
4. **Completed** — All events are scored; results are final.
5. **Archived** — Tournament is archived for historical reference; no further edits allowed.

**Archive feature:** Admins can archive any completed tournament. Archived tournaments:
- Do not appear in active tournament lists (unless a filter is toggled).
- Remain fully accessible in read-only mode for reports and historical reference.
- Can be un-archived by an admin if corrections are needed.

---

### R9 — Register Students to Events

**Registration rules:**

1. **Event level matching:** A student can only be registered to an event at an EventLevel that matches their sex and school level.
2. **Primary school levels (Junior/Senior):** No age restrictions — the student's sex must match the level's sex. The choice of Junior vs Senior is at the coach's/admin's discretion.
3. **Secondary/College age-based levels:** The student's age (computed from DOB and tournament start date) must be ≤ the level's `MaxAge`. Students may compete **up** (enter a higher age level) but never **down**. "Open" levels have no age ceiling.
4. **Age limit waiver:** If `AgeRestrictionWaived` is true on the TournamentEventLevel, the age check is skipped — any student of the matching sex may register.
5. **School level:** The student's school level must be compatible with the EventLevel's school level.
6. **Active status:** Only students with `IsActive = true` can be registered.
7. **Relay teams:** For relay events, the coach registers a team of 4 students from the same school, all meeting level eligibility.
8. **Duplicate prevention:** A student cannot be registered to the same event-level twice in the same tournament.

**Registration entity:**

| Field | Type | Notes |
|-------|------|-------|
| `TournamentId` | FK | |
| `EventId` | FK | |
| `EventLevelId` | FK | The level the student is competing at (e.g., "Under 17 Boys") |
| `StudentId` | FK | References `Student.StudentId`; null for relay teams |
| `RelayTeamId` | FK (nullable) | References a relay team if applicable |
| `RegisteredBy` | FK | The user (coach/principal/admin) who registered them |
| `RegisteredAt` | datetime | |

---

### R10 — User Management & Role Management

#### Roles

| Role | Permissions | School Required |
|------|------------|-----------------|
| **Student** | View own profile, view own scores and results | Yes |
| **Coach** | Register students from their school to events, view school results | Yes |
| **Principal** | Same as Coach + approve registrations, view school reports | Yes |
| **Sports Division Official** | Record scores, manage event logistics during tournaments | No |
| **Sports Division Admin** | Full system access: CRUD all entities, manage users/roles, run reports, archive tournaments | No |

**Business rules:**
- Students, Coaches, and Principals **must belong to a school** upon registration.
- Sports Division Officials and Admins do not belong to a school.
- A user can hold only one role.
- User authentication will use ASP.NET Core Identity.

**User entity fields:**

| Field | Type | Notes |
|-------|------|-------|
| `Email` | string | Used for login |
| `FirstName` | string | |
| `LastName` | string | |
| `Role` | enum | One of the five roles |
| `SchoolId` | FK (nullable) | Required for Student, Coach, Principal; null for officials/admins |
| `IsActive` | bool | |

---

### R11 — Event Score Tracking & Points

#### Scoring Formula (World Athletics Standard)

All three event categories use the World Athletics polynomial scoring formula with event-specific constants:

**Track Events** (lower time = better):
```
Points = A * (B - T)^C
```
Where `T` = time in seconds; if `T >= B`, points = 0.

**Field Events** (greater distance = better):
```
Points = A * (D - B)^C
```
Where `D` = distance in metres; if `D <= B`, points = 0.

**High Jump** (greater height = better):
```
Points = A * (M - B)^C
```
Where `M` = height cleared in centimetres; if `M <= B`, points = 0.

Points are **truncated** to integers (not rounded).

#### Reference Constants (Combined Events / Decathlon Standard)

| Event | A | B | C | Unit |
|-------|-------|------|------|------|
| 100m | 25.4347 | 18 | 1.81 | seconds |
| 400m | 1.53775 | 82 | 1.81 | seconds |
| 1500m | 0.03768 | 480 | 1.85 | seconds |
| 110m Hurdles | 5.74352 | 28.5 | 1.92 | seconds |
| Long Jump | 0.14354 | 220 | 1.4 | centimetres |
| Triple Jump | 0.14354 | 220 | 1.4 | centimetres |
| High Jump | 0.8465 | 75 | 1.42 | centimetres |
| Shot Put | 51.39 | 1.5 | 1.05 | metres |
| Discus | 12.91 | 4 | 1.1 | metres |
| Javelin | 10.14 | 7 | 1.08 | metres |

> **Note:** The admin should be able to view and adjust scoring constants in the system to accommodate future World Athletics updates or local adaptations. Constants are stored in the database, not hard-coded.

#### School Points

School rankings within a tournament are determined by aggregating individual student points:

- Each event awards **placement points** to the top finishers (e.g., 1st = 10 pts, 2nd = 8 pts, 3rd = 6 pts, 4th = 5 pts, 5th = 4 pts, 6th = 3 pts, 7th = 2 pts, 8th = 1 pt). These values should be admin-configurable.
- Relay events also award placement points to the school.
- The school with the highest total placement points wins the tournament (or zone championship).
- Ties are broken by number of 1st-place finishes, then 2nd-place finishes, etc.

**Score entity:**

Scores represent the **final result** for a student in an event (used for placement and school points). For track events, this is derived from the Finals round. For field events and high jump, it is recorded directly.

| Field | Type | Notes |
|-------|------|-------|
| `ScoreId` | int | PK, auto-increment |
| `RegistrationId` | FK | Links to the student's event registration |
| `RawPerformance` | decimal | Best time in seconds (track), distance in metres (field), or height in cm (high jump) |
| `CalculatedPoints` | int | From the scoring formula |
| `Placement` | int (nullable) | 1st, 2nd, 3rd, etc. |
| `PlacementPoints` | int | Points awarded to the school |
| `RecordedBy` | FK | The official who entered the score |
| `RecordedAt` | datetime | |

> **Track events:** The final Score record is created from the student's Finals `HeatLane.Time`. Heat and semi-final times are stored in `HeatLane` and used only for advancement, not for placement points. If a student does not reach Finals, no Score record is created for them.

---

### R12 — Admin Dashboard & Printable Reports

#### Dashboard

The admin dashboard provides at-a-glance metrics for the active tournament:
- Total registered students (by gender, by school level)
- Events in progress / completed / upcoming
- Top schools by points
- Recent score entries

#### Reports (all printable to PDF)

| Report | Description | Filters |
|--------|------------|---------|
| **Score Sheets** | Blank or pre-filled score sheets for officials. For track events: one sheet per heat showing lane assignments and time columns. For field/high jump: lists registered students with performance columns. | Event, Event Level, Round, Heat |
| **Popular Events** | Ranked list of events by registration count. | Tournament, School Level, Zone |
| **Registration by Gender** | Breakdown of registrations by Male / Female across events and event levels. | Tournament, Zone, School |
| **Event-Gender-Level Report** | Matrix of events vs. gender vs. event level showing registration counts and results. | Tournament |
| **Event-School Report** | For each event, lists participating schools and their students' results. | Tournament, Event, Event Level |
| **Students by School Report** | For each school, lists all registered students and their events. | Tournament, School, Zone |
| **Scores by Event Report** | Detailed results for an event: all students ranked by performance with points. | Tournament, Event, Event Level |
| **Student Points Report** | Individual student point totals across all events in a tournament. | Tournament, School, Zone |

**Print requirements:**
- All reports should render cleanly for A4 paper printing via browser print or PDF export.
- Reports should include the tournament name, date, and generation timestamp in the header.
- School crests/logos are optional future enhancements.

---

### R13 — Refactor to Clean Architecture

The current scaffolded MVC project will be restructured into Clean Architecture layers:

#### Target Project Structure

```
SportsDivision.sln
|
+-- src/
|   +-- SportsDivision.Domain/              # Core domain entities, enums, interfaces
|   |   +-- Entities/
|   |   |   +-- Student.cs
|   |   |   +-- School.cs
|   |   |   +-- Zone.cs
|   |   |   +-- Event.cs
|   |   |   +-- Tournament.cs
|   |   |   +-- EventLevel.cs
|   |   |   +-- TournamentEventLevel.cs
|   |   |   +-- EventRegistration.cs
|   |   |   +-- Score.cs
|   |   |   +-- HighJumpAttempt.cs
|   |   |   +-- HighJumpHeight.cs
|   |   |   +-- Round.cs
|   |   |   +-- Heat.cs
|   |   |   +-- HeatLane.cs
|   |   |   +-- RelayTeam.cs
|   |   |   +-- ScoringConstant.cs
|   |   |   +-- PlacementPointConfig.cs
|   |   |   +-- EquipmentSpec.cs
|   |   |   +-- User.cs
|   |   +-- Enums/
|   |   |   +-- Sex.cs
|   |   |   +-- SchoolLevel.cs
|   |   |   +-- EventCategory.cs
|   |   |   +-- TournamentStatus.cs
|   |   |   +-- UserRole.cs
|   |   |   +-- HighJumpAttemptResult.cs
|   |   |   +-- RoundType.cs
|   |   |   +-- RoundStatus.cs
|   |   |   +-- AdvanceReason.cs
|   |   |   +-- SeedingMethod.cs
|   |   +-- Interfaces/
|   |   |   +-- IStudentRepository.cs
|   |   |   +-- ISchoolRepository.cs
|   |   |   +-- IEventRepository.cs
|   |   |   +-- ITournamentRepository.cs
|   |   |   +-- IScoreRepository.cs
|   |   |   +-- IUnitOfWork.cs
|   |   +-- SportsDivision.Domain.csproj
|   |
|   +-- SportsDivision.Application/         # Use cases, DTOs, service interfaces
|   |   +-- DTOs/
|   |   |   +-- StudentDto.cs
|   |   |   +-- EventDto.cs
|   |   |   +-- ScoreDto.cs
|   |   |   +-- TournamentDto.cs
|   |   |   +-- ReportDtos/
|   |   +-- Interfaces/
|   |   |   +-- IStudentService.cs
|   |   |   +-- IEventService.cs
|   |   |   +-- ITournamentService.cs
|   |   |   +-- IScoringService.cs
|   |   |   +-- IRegistrationService.cs
|   |   |   +-- IHeatService.cs
|   |   |   +-- IReportService.cs
|   |   +-- Services/
|   |   |   +-- StudentService.cs
|   |   |   +-- EventService.cs
|   |   |   +-- TournamentService.cs
|   |   |   +-- ScoringService.cs
|   |   |   +-- RegistrationService.cs
|   |   |   +-- HeatService.cs
|   |   |   +-- ReportService.cs
|   |   +-- Validators/
|   |   |   +-- RegistrationValidator.cs
|   |   +-- Mappings/
|   |   |   +-- MappingProfile.cs
|   |   +-- SportsDivision.Application.csproj
|   |
|   +-- SportsDivision.Infrastructure/      # EF Core, Identity, external services
|   |   +-- Data/
|   |   |   +-- ApplicationDbContext.cs
|   |   |   +-- Configurations/            # EF Fluent API entity configs
|   |   |   +-- Migrations/
|   |   +-- Repositories/
|   |   |   +-- StudentRepository.cs
|   |   |   +-- SchoolRepository.cs
|   |   |   +-- EventRepository.cs
|   |   |   +-- TournamentRepository.cs
|   |   |   +-- ScoreRepository.cs
|   |   |   +-- UnitOfWork.cs
|   |   +-- Identity/
|   |   |   +-- ApplicationUser.cs
|   |   +-- SportsDivision.Infrastructure.csproj
|   |
|   +-- SportsDivision.Web/                 # ASP.NET Core MVC (presentation)
|       +-- Controllers/
|       +-- Views/
|       +-- ViewModels/
|       +-- wwwroot/
|       +-- Program.cs
|       +-- SportsDivision.Web.csproj
|
+-- tests/
    +-- SportsDivision.Domain.Tests/
    +-- SportsDivision.Application.Tests/
    +-- SportsDivision.Infrastructure.Tests/
```

#### Dependency Flow

```
Web --> Application --> Domain
 |          |
 +-----> Infrastructure --> Domain
```

- **Domain** has zero external dependencies (pure C#).
- **Application** depends only on Domain. Defines service interfaces and use cases.
- **Infrastructure** implements interfaces from Domain and Application (EF Core, Identity).
- **Web** is the composition root — wires up DI, hosts controllers and views.

---

## 4. Domain Model (Entity Relationship Overview)

```
Zone (1) ----< School (1) ----< Student (PK: StudentId, CK: ExistingStudentId)
                  |                 |
                  |                 +----< EventRegistration
                  |                 |             |
                  +----< User       |             +---- Score
                                    |
                          RelayTeam >----< Student
                              |
                              +---- EventRegistration

EventRegistration >---- TournamentEventLevel

Tournament (1) ----< TournamentEventLevel >---- Event
                            |
                            +---- EventLevel
                            +-- AgeRestrictionWaived (bool)
                            |
                            +----< Round (Heat/SemiFinal/Final)
                                      |
                                      +----< Heat
                                                |
                                                +----< HeatLane >---- EventRegistration
                                                         |
                                                         +-- LaneNumber, Time, IsAdvanced

Tournament (1) ----< HighJumpHeight ----< HighJumpAttempt >---- Student

EventLevel (18 levels: Junior/Senior Boys/Girls + Under 14-21 Boys/Girls + Open Boys/Girls)

Event (1) ----< EquipmentSpec (per EventLevel)
Event (1) ----< ScoringConstant (A, B, C per event)
Event (1) >----< EventLevel (allowed levels, many-to-many)

PlacementPointConfig (placement rank -> points, per tournament or global)

Notes:
- Student.StudentId = auto-increment PK (surrogate key)
- Student.ExistingStudentId = unique candidate key (natural/business key)
- EventLevel combines division + sex (e.g., "Under 17 Boys", "Junior Girls")
- Primary levels (Junior/Senior): no age cutoff, school determines division
- TournamentEventLevel.AgeRestrictionWaived = true skips age check for that level
- Track events: TournamentEventLevel has Rounds → Heats → HeatLanes (with times)
- Semi-finals are optional; admin configures round structure per event
- Only Finals round produces Score records (placement + school points)
```

---

## 5. Technology Stack

| Layer | Technology |
|-------|-----------|
| **Framework** | .NET 10, ASP.NET Core MVC |
| **ORM** | Entity Framework Core 10 |
| **Database** | PostgreSQL (via Npgsql.EntityFrameworkCore.PostgreSQL) |
| **Authentication** | ASP.NET Core Identity |
| **Authorization** | Role-based (5 roles) via `[Authorize(Roles = "...")]` |
| **Mapping** | AutoMapper |
| **Validation** | FluentValidation |
| **PDF Reports** | QuestPDF or similar |
| **Front-end** | Razor Views, Bootstrap 5, jQuery for interactivity |
| **Testing** | xUnit, Moq |

---

## 6. Implementation Phases

### Phase 1 — Foundation (Clean Architecture + Core Entities)
- [ ] Restructure solution into Clean Architecture (4 projects + tests)
- [ ] Define Domain entities: `Zone`, `School`, `Student`, `EventLevel`
- [ ] Define Enums: `Sex`, `SchoolLevel`, `EventCategory`, `TournamentStatus`, `UserRole`, `HighJumpAttemptResult`, `RoundType`, `RoundStatus`, `AdvanceReason`, `SeedingMethod`
- [ ] Seed all 18 EventLevel records (Junior/Senior Boys/Girls + Under 14–21 Boys/Girls + Open Boys/Girls)
- [ ] Set up EF Core DbContext with entity configurations
- [ ] Configure database connection and run initial migration
- [ ] Set up dependency injection in Web project

### Phase 2 — User Management & Authentication
- [ ] Integrate ASP.NET Core Identity with custom `ApplicationUser`
- [ ] Implement role seeding (Student, Coach, Principal, Official, Admin)
- [ ] Build registration and login views
- [ ] Enforce school-required rule for Student/Coach/Principal roles
- [ ] Build user management CRUD for admins

### Phase 3 — Core Domain (Events, Tournaments, Zones)
- [ ] Build Zone CRUD
- [ ] Build School CRUD (with zone assignment and school level)
- [ ] Build EventLevel management (admin can add/edit levels)
- [ ] Build Event CRUD (with category, allowed event levels, relay flag)
- [ ] Build Equipment Spec management per event/event level
- [ ] Build Tournament CRUD with status lifecycle
- [ ] Build TournamentEventLevel setup (assign events to levels per tournament, with age waiver toggle)
- [ ] Implement tournament archiving and un-archiving

### Phase 4 — Student Registration & Event Registration
- [ ] Build Student CRUD (with `StudentId` PK, `ExistingStudentId` unique candidate key, school assignment, age calculation)
- [ ] Build event registration workflow
- [ ] Implement event level eligibility validation (sex match, school level match)
- [ ] Implement Primary level registration (Junior/Senior — no age check)
- [ ] Implement Secondary/College age-based level validation (compete-up rule)
- [ ] Implement age limit waiver per TournamentEventLevel
- [ ] Implement sex eligibility validation
- [ ] Implement relay team registration (4 students per school)
- [ ] Build registration views for coaches/principals

### Phase 5 — Track Event Heats & Rounds
- [ ] Build `HeatService` for round/heat/lane management
- [ ] Implement round structure configuration per TournamentEventLevel (Heats only, Heats + Finals, Heats + Semis + Finals)
- [ ] Implement seeding logic (random, by performance, manual)
- [ ] Build heat/lane assignment UI for admins
- [ ] Build time recording UI for officials (per heat per lane)
- [ ] Implement advancement logic (top N per heat + fastest losers)
- [ ] Implement auto-population of next round from advancing students
- [ ] Allow admin to skip semi-finals or adjust round structure mid-tournament

### Phase 6 — Scoring Engine
- [ ] Build `ScoringService` with World Athletics formula
- [ ] Store scoring constants (A, B, C) per event in the database, seed defaults
- [ ] Implement track event scoring — derive final Score from Finals round HeatLane times
- [ ] Implement field event scoring (distance-based)
- [ ] Implement high jump scoring with attempt tracking and adjustable heights
- [ ] Build score entry UI for officials (field events + high jump)
- [ ] Implement placement calculation and placement points
- [ ] Implement school points aggregation with tiebreaker logic
- [ ] Build admin UI for configuring scoring constants and placement point values

### Phase 7 — Dashboard & Reports
- [ ] Build admin dashboard (stats, top schools, recent scores)
- [ ] Implement report generation service
- [ ] Build each report:
  - [ ] Score Sheets (blank + pre-filled)
  - [ ] Popular Events
  - [ ] Registration by Gender
  - [ ] Event-Gender-Level Report
  - [ ] Event-School Report
  - [ ] Students by School Report
  - [ ] Scores by Event Report
  - [ ] Student Points Report
- [ ] Implement print-friendly CSS and/or PDF export
- [ ] Add report filters (tournament, zone, school, event, event level)

### Phase 8 — Polish & Testing
- [ ] Write unit tests for Domain logic (age calculation, eligibility, scoring)
- [ ] Write unit tests for Application services
- [ ] Write integration tests for repositories
- [ ] UI/UX review and polish
- [ ] Role-based access control testing across all controllers
- [ ] Performance testing with realistic data volumes
- [ ] Seed database with sample data for demonstration

---

## 7. Key Business Rules (Quick Reference)

1. **Event Levels combine division + sex** (e.g., "Under 17 Boys", "Junior Girls") and are a first-class entity.
2. **Primary levels (Junior/Senior)** have no age cutoffs — all primary students compete; division is at coach/admin discretion.
3. **Secondary/College levels are age-based** (Under 14 through Under 21, plus Open) — student age is computed from DOB relative to tournament start date.
4. **Students may compete up in age level, never down** (Secondary/College only). Open has no age ceiling.
5. **Events can have their age restriction waived** per tournament-event-level when groups are too small.
6. **Secondary and College students compete together** in the SSC zone.
7. **Students, Coaches, and Principals must belong to a school.**
8. **Relay teams consist of exactly 4 students from the same school**, all meeting eligibility.
9. **Track events progress through rounds:** Heats → (Semi-finals) → Finals. Semi-finals are skippable.
10. **Seeding** assigns students to heats/lanes (random, by performance, or manual).
11. **Advancement** from heats/semis is by top N per heat + fastest losers (admin-configurable).
12. **Only the Finals round produces placement points** for track events. Heat/semi times are for advancement only.
13. **High Jump tracks individual attempts** (Clear/Fail/Pass) at each height; three consecutive failures = elimination.
14. **Scoring constants are stored in the database** and are admin-editable.
15. **Placement points are admin-configurable** (default: 1st=10, 2nd=8, 3rd=6, 4th=5, 5th=4, 6th=3, 7th=2, 8th=1).
16. **Tiebreakers** for school rankings: most 1st-place finishes, then 2nd, then 3rd, etc.
17. **Archived tournaments are read-only** but remain accessible for reports.
18. **`StudentId`** is the auto-increment surrogate PK; **`ExistingStudentId`** is the unique candidate key (school-assigned ID).

---

## 8. Seed Data Requirements

The following data should be seeded for development and demonstration:

- **8 Zones** as listed in R7
- **18 Event Levels**: Junior Boys/Girls, Senior Boys/Girls (Primary); Under 14/15/16/17/20/21 Boys/Girls, Open Boys/Girls (Secondary/College)
- **69 Schools** — the complete list below (all real Dominica schools)
- **Standard events**: All events listed in R6 with their equipment specs
- **Scoring constants**: Seeded from the World Athletics table
- **Default placement points**: 10-8-6-5-4-3-2-1
- **Admin user**: Default admin account for initial access
- **5 roles**: Seeded via Identity role manager

#### Complete School List (69 schools)

**Secondary School / College Zone (17 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Academic School of Learning | ACADEMIX | College |
| Arthur Waldron Seventh-day Adventist Academy | AWSDAA | Secondary |
| Business Training Center | BTC | College |
| Castle Bruce Secondary School | CBSS | Secondary |
| Convent High School | CHS | Secondary |
| Dominica Community High School | DCHS | Secondary |
| Dominica Grammar School | DGS | Secondary |
| Dominica State College | DSC | College |
| Goodwill Secondary School | GSS | Secondary |
| Isaiah Thomas Secondary School | ITSS | Secondary |
| North East Comprehensive School | NECS | Secondary |
| Orion Academy | ORION | Secondary |
| Pierre Charles Secondary School | PCSS | Secondary |
| Portsmouth Secondary School | PSS | Secondary |
| St Martin's Secondary School | SMSS | Secondary |
| St Mary's Academy | SMA | Secondary |
| Wesley High School | WHS | Secondary |

**North West Zone (8 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Cambell Primary School | — | Primary |
| Colihaut Primary School | — | Primary |
| Coulibistrie Primary School | — | Primary |
| Dublanc Primary School | — | Primary |
| Kelleb Laurent Primary School | — | Primary |
| Mahaut Primary School | — | Primary |
| Salisbury Primary School | — | Primary |
| Warner Primary School | — | Primary |

**North Zone (9 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Baroness Patricia Scotland Primary School | — | Primary |
| Bense Primary School | — | Primary |
| Clifton Primary School | — | Primary |
| Paix Bouche Primary School | — | Primary |
| Penville Primary School | — | Primary |
| Roosevelt Douglas Primary School | — | Primary |
| Savanne Paille Primary School | — | Primary |
| St. John's Primary School | — | Primary |
| Thibaud Primary School | — | Primary |

**North East Zone (8 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Atkinson Primary School | — | Primary |
| Calibishie Primary School | — | Primary |
| Concord Primary School | — | Primary |
| Lighthouse Christian Academy | — | Primary |
| Salybia Primary School | — | Primary |
| Wesley Primary School | — | Primary |
| Wills Stratmore Steven Primary School | — | Primary |
| Woodford Hill Primary School | — | Primary |

**South East Zone (8 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Belles Primary School | — | Primary |
| Castle Bruce Primary School | — | Primary |
| Delices Primary School | — | Primary |
| Grand Fond Primary School | — | Primary |
| Jones Beaupierre Primary School | — | Primary |
| Morne Jaune Primary School | — | Primary |
| San Sauver Primary School | — | Primary |
| Sineku Primary School | — | Primary |

**South Zone (5 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Bagatelle Primary School | — | Primary |
| Bellevue Chopin Primary | — | Primary |
| Grand Bay Primary School | — | Primary |
| Petite Savanne Primary School | — | Primary |
| Tete Morne Primary School | — | Primary |

**South West Zone (7 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Giraudel Primary School | — | Primary |
| Morne Prosper Primary School | — | Primary |
| Newtown Primary School | — | Primary |
| Soufriere Primary School | — | Primary |
| St. Luke's Primary School | — | Primary |
| Trafalgar Primary School | — | Primary |
| Wotton Waven Primary School | — | Primary |

**West Central Zone (7 schools)**

| School Name | Short Name | Level |
|------------|-----------|-------|
| Berean Academy | — | Primary |
| Christian Union Primary | — | Primary |
| Goodwill Primary School | — | Primary |
| Massacre Primary School | — | Primary |
| Pioneer Preparatory | — | Primary |
| St. Martin's Primary School | — | Primary |
| St. Mary's Primary | — | Primary |

---

## 9. Security Considerations

- All passwords hashed via ASP.NET Core Identity (bcrypt/PBKDF2).
- Role-based authorization on every controller action.
- CSRF protection via antiforgery tokens on all forms.
- Input validation on both client (JavaScript) and server (FluentValidation).
- SQL injection prevention via EF Core parameterized queries.
- HTTPS enforced in production.
- Sensitive configuration (connection strings) stored in user secrets or environment variables, not in `appsettings.json`.

---

## 10. Glossary

| Term | Definition |
|------|-----------|
| **Advancement** | Process of qualifying students from one round to the next (top N per heat + fastest losers) |
| **Age Limit Waiver** | An admin toggle on a TournamentEventLevel that removes the age check, opening the event to all students of the matching sex |
| **Compete-Up** | Rule allowing Secondary/College students to enter events at a higher age-based level than their own |
| **Event Category** | One of Track, Field, or High Jump |
| **Event Level** | A competitive division combining division name + sex (e.g., "Under 17 Boys", "Junior Girls"). First-class entity with 18 seeded records. |
| **ExistingStudentId** | The student's pre-existing school-assigned ID number, used as a unique candidate key |
| **Fastest Losers** | Non-automatic qualifiers who advance to the next round by having the best times among athletes who didn't finish top N in their heat |
| **Heat** | A single race within a round; multiple heats run when there are more athletes than lanes |
| **HeatLane** | Assignment of a student to a specific lane in a specific heat, with recorded time and advancement status |
| **Placement Points** | Points awarded to a school based on a student's finishing position in an event's Finals |
| **Round** | A stage of a track event: Heats, Semi-finals (optional), or Finals |
| **Scoring Constants** | The A, B, C coefficients used in the World Athletics scoring formula |
| **Seeding** | Assigning students to heats and lanes before a round begins (random, by performance, or manual) |
| **StudentId** | Auto-increment surrogate primary key for the Student entity |
| **TournamentEventLevel** | Junction entity linking a Tournament + Event + EventLevel, with optional age waiver flag. The core scheduling/registration unit. |
| **Zone** | A geographic (or organizational) grouping of schools for preliminary competitions |
| **SSC Zone** | The Secondary School / College zone where secondary and college students compete together |
