From a8dd7154dd0527cf29303c455104c599e5806e93 Mon Sep 17 00:00:00 2001 From: sabazadam Date: Sun, 30 Nov 2025 17:56:12 +0300 Subject: [PATCH] Preparation and Planing Documents added to the github --- .github/workflows/ci.yml | 55 + .gitignore | 115 + ALGORITHM_DESIGN.md | 1279 ++++++++ DATABASE_SCHEMA.md | 1213 ++++++++ GETTING_STARTED.md | 496 +++ GIT_WORKFLOW.md | 873 ++++++ MEMORY_BANK.md | 2741 +++++++++++++++++ README.md | 563 +++- ROADMAP.md | 1069 +++++++ Requirements.pdf | Bin 0 -> 67140 bytes .../sampleData_AllAttendanceLists.csv | 59 + ...leData_AllClassroomsAndTheirCapacities.csv | 11 + Sample Data list/sampleData_AllCourses.csv | 21 + Sample Data list/sampleData_AllStudents.csv | 251 ++ Sample Design Document.pdf | Bin 0 -> 334466 bytes TEAM_ASSIGNMENTS.md | 836 +++++ agents.md | 115 + pom.xml | 248 ++ project_description_text.pdf | 356 +++ 19 files changed, 10240 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 ALGORITHM_DESIGN.md create mode 100644 DATABASE_SCHEMA.md create mode 100644 GETTING_STARTED.md create mode 100644 GIT_WORKFLOW.md create mode 100644 MEMORY_BANK.md create mode 100644 ROADMAP.md create mode 100644 Requirements.pdf create mode 100644 Sample Data list/sampleData_AllAttendanceLists.csv create mode 100644 Sample Data list/sampleData_AllClassroomsAndTheirCapacities.csv create mode 100644 Sample Data list/sampleData_AllCourses.csv create mode 100644 Sample Data list/sampleData_AllStudents.csv create mode 100644 Sample Design Document.pdf create mode 100644 TEAM_ASSIGNMENTS.md create mode 100644 agents.md create mode 100644 pom.xml create mode 100644 project_description_text.pdf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e01d416 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: Java CI with Maven + +on: + push: + branches: [ develop, main ] + pull_request: + branches: [ develop, main ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build with Maven + run: mvn clean compile + + - name: Run tests + run: mvn test + + - name: Generate test coverage report + run: mvn jacoco:report + + - name: Check code coverage + run: mvn jacoco:check + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./target/site/jacoco/jacoco.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + - name: Archive test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: target/surefire-reports/ + + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: code-coverage-report + path: target/site/jacoco/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13bb6cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +# Compiled class files +*.class + +# Log files +*.log + +# Package Files +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +# Gradle +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +# IntelliJ IDEA +.idea/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +# Eclipse +.classpath +.project +.settings/ +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.loadpath +.recommenders + +# NetBeans +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +# VS Code +.vscode/ +*.code-workspace + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Windows +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db +*.stackdump +[Dd]esktop.ini +$RECYCLE.BIN/ +*.lnk + +# Database files (exclude runtime DB, keep schema) +*.db +*.sqlite +*.sqlite3 +!src/main/resources/database/schema.sql + +# Test output +test-output/ +test-results/ + +# Coverage reports +.jacoco/ +jacoco.exec + +# Application data +data/ +*.db-journal + +# Logs +logs/ +*.log.* + +# Temporary files +*.tmp +*.temp + +# IDE-specific files +nbactions.xml + +# JavaDoc +javadoc/ +docs/api/ diff --git a/ALGORITHM_DESIGN.md b/ALGORITHM_DESIGN.md new file mode 100644 index 0000000..ec029ba --- /dev/null +++ b/ALGORITHM_DESIGN.md @@ -0,0 +1,1279 @@ +# ALGORITHM DESIGN - EXAM SCHEDULING SYSTEM + +**Project:** Exam Planner Desktop Application +**Algorithm:** Backtracking CSP Solver with Constraint Propagation +**Version:** 1.0 +**Last Updated:** November 26, 2025 + +--- + +## TABLE OF CONTENTS + +1. [Overview](#overview) +2. [Problem Formulation](#problem-formulation) +3. [Algorithm Architecture](#algorithm-architecture) +4. [Backtracking Algorithm](#backtracking-algorithm) +5. [Constraint Validation](#constraint-validation) +6. [Heuristics](#heuristics) +7. [Optimization Strategies](#optimization-strategies) +8. [Conflict Detection](#conflict-detection) +9. [Performance Optimizations](#performance-optimizations) +10. [Implementation Guide](#implementation-guide) +11. [Testing Strategy](#testing-strategy) +12. [Complexity Analysis](#complexity-analysis) + +--- + +## OVERVIEW + +### Problem Statement + +Given: +- **N courses** that need exam scheduling +- **M classrooms** with specific capacities +- **S students** enrolled in various courses +- **Exam period configuration** (D days, T time slots per day) + +Find: +- An assignment of each course to exactly one {Classroom, Day, TimeSlot} triple + +Such that: +- **Hard Constraint 1:** No student has consecutive exams +- **Hard Constraint 2:** No student has more than 2 exams per day +- **Hard Constraint 3:** Classroom capacity is not exceeded +- **Implicit Constraint 4:** No classroom double-booking + +### Algorithm Choice: Backtracking CSP + +**Why Backtracking?** +- ✅ **Completeness:** Guarantees finding solution if one exists +- ✅ **Soundness:** Only returns valid solutions +- ✅ **Systematic:** Explores search space methodically +- ✅ **Constraint-friendly:** Natural fit for hard constraints + +**Why Not Greedy?** +- ❌ May fail even when solution exists +- ❌ No guarantee of correctness +- ✅ Faster but less reliable + +**Why Not Genetic Algorithm?** +- ❌ Probabilistic (no guarantee) +- ❌ Complex to ensure 100% constraint satisfaction +- ✅ Good for optimization but not for hard constraints + +**Decision:** Backtracking with heuristics provides best balance of correctness and performance. + +--- + +## PROBLEM FORMULATION + +### CSP Components + +#### 1. Variables + +**Definition:** Each course is a variable + +``` +Variables = {Course_1, Course_2, ..., Course_N} +``` + +**Example:** +``` +Variables = {CourseCode_01, CourseCode_02, ..., CourseCode_20} +N = 20 +``` + +--- + +#### 2. Domain + +**Definition:** For each course, the domain is the set of all possible {Classroom, Day, TimeSlot} assignments + +``` +Domain(Course_i) = {(c, d, t) | c ∈ Classrooms, + d ∈ {1, 2, ..., D}, + t ∈ {1, 2, ..., T}} +``` + +**Example:** +``` +Classrooms = {Classroom_01, ..., Classroom_10} # 10 classrooms +Days = {1, 2, 3, 4, 5} # 5 days +TimeSlots = {1, 2, 3, 4} # 4 slots per day + +Domain(CourseCode_01) = { + (Classroom_01, 1, 1), # Classroom 1, Day 1, Slot 1 + (Classroom_01, 1, 2), # Classroom 1, Day 1, Slot 2 + ..., + (Classroom_10, 5, 4) # Classroom 10, Day 5, Slot 4 +} + +|Domain| = 10 × 5 × 4 = 200 possible assignments per course +``` + +**Domain Filtering (Pre-processing):** + +Before backtracking, filter out invalid assignments: + +```java +Domain(Course_i) = {(c, d, t) | enrolledCount(Course_i) ≤ capacity(c)} +``` + +**Example:** +``` +CourseCode_01 has 40 students enrolled +Classroom_01 has capacity 40 → Valid ✓ +Classroom_02 has capacity 35 → Invalid ✗ (removed from domain) +``` + +After filtering, domain size reduced significantly (only valid classrooms remain). + +--- + +#### 3. Constraints + +**Unary Constraints (Pre-processing):** +``` +enrolledCount(Course_i) ≤ capacity(Classroom) +``` + +**Binary Constraints (Between variables):** + +**Constraint 1: No Consecutive Exams** +``` +For any two courses c1, c2 with shared students: + If c1 assigned to (classroom1, day1, slot1) + And c2 assigned to (classroom2, day2, slot2) + Then: NOT consecutive(day1, slot1, day2, slot2) +``` + +**Constraint 2: Max 2 Exams Per Day** +``` +For any student s: + Count of exams on any day d ≤ 2 +``` + +**Constraint 3: No Classroom Double-Booking (Implicit)** +``` +For any two courses c1 ≠ c2: + If c1 assigned to (classroom, day, slot) + Then c2 cannot be assigned to (classroom, day, slot) +``` + +--- + +### Search Space Size + +**Theoretical maximum:** +``` +Search Space = |Domain|^N + = 200^20 + = 1.024 × 10^46 possible assignments +``` + +**With constraint propagation and heuristics:** +``` +Effective search space << 10^46 +``` + +Heuristics reduce this dramatically (typically explore < 1000 nodes for sample data). + +--- + +## ALGORITHM ARCHITECTURE + +### High-Level Flow + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CSPSolver.solve() │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 1. Pre-processing │ +│ • Load data (courses, classrooms, enrollments) │ +│ • Build enrollment maps (student→courses, course→students) │ +│ • Filter domains (remove invalid classrooms) │ +│ • Check basic feasibility (enough slots?) │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 2. Backtracking Search │ +│ WHILE unassigned courses exist: │ +│ • Select next course (MRV heuristic) │ +│ • Order domain values (Least Constraining Value) │ +│ • Try each value: │ +│ - Check constraints │ +│ - If valid: assign and recurse │ +│ - If fails: backtrack │ +│ • If no value works: return FAILURE │ +└─────────────────────────────────────────────────────────────┘ + │ + ┌───────────┴───────────┐ + │ │ + ▼ ▼ + ┌─────────────┐ ┌──────────────┐ + │ SUCCESS │ │ FAILURE │ + │ Return │ │ Analyze │ + │ Schedule │ │ Conflicts │ + └─────────────┘ └──────────────┘ +``` + +--- + +### Component Diagram + +``` +┌──────────────────────────────────────────────────────────────┐ +│ CSPSolver │ +├──────────────────────────────────────────────────────────────┤ +│ + solve(courses, classrooms, enrollments, config, strategy) │ +│ - backtrack(assignment, domains) │ +│ - selectUnassignedVariable() │ +│ - orderDomainValues(course, strategy) │ +│ - forwardCheck(course, assignment) │ +└──────────────────────────────────────────────────────────────┘ + │ + │ uses + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ ConstraintValidator │ +├──────────────────────────────────────────────────────────────┤ +│ + isValid(course, classroom, day, slot, assignment) │ +│ + checkNoConsecutiveExams(...) │ +│ + checkMaxTwoPerDay(...) │ +│ + checkClassroomCapacity(...) │ +│ + getViolations(...) │ +└──────────────────────────────────────────────────────────────┘ + │ + │ uses + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ OptimizationStrategy │ +├──────────────────────────────────────────────────────────────┤ +│ + compareAssignments(a1, a2) // for value ordering │ +│ + evaluateAssignment(assignment) │ +└──────────────────────────────────────────────────────────────┘ + │ + │ implementations + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ MinimizeDaysStrategy | BalanceDistributionStrategy │ +│ MinimizeClassroomsStrategy | BalanceClassroomsStrategy │ +└──────────────────────────────────────────────────────────────┘ +``` + +--- + +## BACKTRACKING ALGORITHM + +### Pseudocode (High-Level) + +``` +FUNCTION CSPSolver.solve(courses, classrooms, enrollments, config, strategy): + // Pre-processing + enrollmentMap ← buildEnrollmentMap(enrollments) + domains ← initializeDomains(courses, classrooms, enrollmentMap) + + // Check basic feasibility + IF count(courses) > config.totalSlots THEN + THROW NotEnoughSlotsException + END IF + + FOR EACH course IN courses DO + IF domains[course].isEmpty() THEN + THROW NoValidClassroomException(course) + END IF + END FOR + + // Start backtracking search + assignment ← {} // Empty assignment + result ← BACKTRACK(assignment, domains, enrollmentMap, strategy) + + IF result = FAILURE THEN + conflicts ← ConflictDetector.analyze(courses, classrooms, enrollments, config) + THROW NoSolutionException(conflicts) + ELSE + RETURN buildSchedule(result) + END IF +END FUNCTION +``` + +--- + +### Pseudocode (Detailed Backtracking) + +``` +FUNCTION BACKTRACK(assignment, domains, enrollmentMap, strategy): + // Base case: all courses assigned + IF assignment.isComplete() THEN + RETURN assignment // SUCCESS + END IF + + // Select next unassigned course (MRV heuristic) + course ← SELECT_UNASSIGNED_VARIABLE(assignment, domains) + + // Order domain values (Least Constraining Value + Strategy) + orderedValues ← ORDER_DOMAIN_VALUES(course, domains[course], assignment, strategy) + + FOR EACH (classroom, day, slot) IN orderedValues DO + // Check if this assignment violates any constraints + IF CONSTRAINTS_SATISFIED(course, classroom, day, slot, assignment, enrollmentMap) THEN + // Make assignment + assignment[course] ← (classroom, day, slot) + + // Forward checking: update domains of unassigned courses + removedValues ← FORWARD_CHECK(course, classroom, day, slot, assignment, domains, enrollmentMap) + + IF NOT removedValues.causedEmptyDomain() THEN + // Recurse + result ← BACKTRACK(assignment, domains, enrollmentMap, strategy) + + IF result ≠ FAILURE THEN + RETURN result // Found solution! + END IF + END IF + + // Backtrack: undo assignment and restore domains + DELETE assignment[course] + RESTORE_DOMAINS(domains, removedValues) + END IF + END FOR + + RETURN FAILURE // No valid assignment for this course +END FUNCTION +``` + +--- + +### Key Functions Explained + +#### SELECT_UNASSIGNED_VARIABLE (MRV Heuristic) + +**Minimum Remaining Values:** Choose the course with fewest valid domain values remaining. + +``` +FUNCTION SELECT_UNASSIGNED_VARIABLE(assignment, domains): + minCourse ← NULL + minDomainSize ← INFINITY + + FOR EACH course IN courses DO + IF course NOT IN assignment THEN // Unassigned + domainSize ← count(domains[course]) + + IF domainSize < minDomainSize THEN + minDomainSize ← domainSize + minCourse ← course + ELSE IF domainSize = minDomainSize THEN + // Tie-breaking: choose course with most students (harder to place) + IF enrollmentMap[course].size() > enrollmentMap[minCourse].size() THEN + minCourse ← course + END IF + END IF + END IF + END FOR + + RETURN minCourse +END FUNCTION +``` + +**Why MRV?** +- Fail-fast on difficult variables +- Prunes search space early +- Empirically proven to reduce backtracking + +--- + +#### ORDER_DOMAIN_VALUES (Least Constraining Value) + +**Least Constraining Value:** Try values that leave most flexibility for remaining courses. + +``` +FUNCTION ORDER_DOMAIN_VALUES(course, domain, assignment, strategy): + // Calculate how constraining each value is + valueCounts ← {} + + FOR EACH (classroom, day, slot) IN domain DO + // Count how many other courses can still use this {classroom, day, slot} + conflictCount ← countConflicts(classroom, day, slot, assignment) + valueCounts[(classroom, day, slot)] ← conflictCount + END FOR + + // Sort by least constraining first + sortedValues ← SORT(domain, BY valueCounts, ASCENDING) + + // Apply optimization strategy to break ties + strategyOrderedValues ← strategy.reorder(sortedValues) + + RETURN strategyOrderedValues +END FUNCTION +``` + +--- + +#### CONSTRAINTS_SATISFIED + +``` +FUNCTION CONSTRAINTS_SATISFIED(course, classroom, day, slot, assignment, enrollmentMap): + // Constraint 1: Classroom capacity + IF enrollmentMap[course].size() > classroom.capacity THEN + RETURN FALSE // Already filtered in pre-processing, but double-check + END IF + + // Constraint 2: No classroom double-booking + FOR EACH assignedCourse IN assignment.keys() DO + (c, d, s) ← assignment[assignedCourse] + IF c = classroom AND d = day AND s = slot THEN + RETURN FALSE // Classroom already used at this time + END IF + END FOR + + // Constraint 3: No consecutive exams for any student + students ← enrollmentMap[course] + FOR EACH student IN students DO + IF hasConsecutiveExam(student, day, slot, assignment, enrollmentMap) THEN + RETURN FALSE + END IF + END FOR + + // Constraint 4: Max 2 exams per day for any student + FOR EACH student IN students DO + examsOnDay ← countExamsForStudentOnDay(student, day, assignment, enrollmentMap) + IF examsOnDay >= 2 THEN + RETURN FALSE // Would cause student to have 3+ exams + END IF + END FOR + + RETURN TRUE // All constraints satisfied +END FUNCTION +``` + +--- + +#### FORWARD_CHECK (Constraint Propagation) + +**Purpose:** After assigning a course, reduce domains of unassigned courses by removing values that would violate constraints. + +``` +FUNCTION FORWARD_CHECK(assignedCourse, classroom, day, slot, assignment, domains, enrollmentMap): + removedValues ← {} + studentsInAssignedCourse ← enrollmentMap[assignedCourse] + + FOR EACH unassignedCourse IN courses DO + IF unassignedCourse NOT IN assignment THEN + studentsInUnassignedCourse ← enrollmentMap[unassignedCourse] + sharedStudents ← INTERSECTION(studentsInAssignedCourse, studentsInUnassignedCourse) + + IF sharedStudents.isNotEmpty() THEN + toRemove ← [] + + FOR EACH (c, d, s) IN domains[unassignedCourse] DO + // Remove if same classroom at same time + IF c = classroom AND d = day AND s = slot THEN + toRemove.add((c, d, s)) + END IF + + // Remove if consecutive slot for shared students + IF areConsecutive(day, slot, d, s) THEN + toRemove.add((c, d, s)) + END IF + + // Remove if would cause 3+ exams on same day for shared students + FOR EACH student IN sharedStudents DO + examsOnDay ← countExamsForStudentOnDay(student, d, assignment, enrollmentMap) + IF examsOnDay >= 2 AND d = day THEN + toRemove.add((c, d, s)) + END IF + END FOR + END FOR + + // Remove invalid values from domain + domains[unassignedCourse].removeAll(toRemove) + removedValues[unassignedCourse] ← toRemove + + // Check if domain became empty (dead-end) + IF domains[unassignedCourse].isEmpty() THEN + removedValues.setEmptyDomainFlag(TRUE) + END IF + END IF + END IF + END FOR + + RETURN removedValues +END FUNCTION +``` + +--- + +## CONSTRAINT VALIDATION + +### Constraint 1: No Consecutive Exams + +**Definition:** If a student has an exam in slot N, they cannot have another exam in slot N+1. + +**Slot Numbering:** + +``` +Absolute Slot Number = (day - 1) × slotsPerDay + slot + +Example (5 days, 4 slots per day): +Day 1, Slot 1 → Absolute slot 1 +Day 1, Slot 2 → Absolute slot 2 +Day 1, Slot 4 → Absolute slot 4 +Day 2, Slot 1 → Absolute slot 5 ← Consecutive with Day 1, Slot 4! +Day 2, Slot 2 → Absolute slot 6 +... +Day 5, Slot 4 → Absolute slot 20 +``` + +**Implementation:** + +```java +public boolean areConsecutive(int day1, int slot1, int day2, int slot2, int slotsPerDay) { + int absoluteSlot1 = (day1 - 1) * slotsPerDay + slot1; + int absoluteSlot2 = (day2 - 1) * slotsPerDay + slot2; + + return Math.abs(absoluteSlot1 - absoluteSlot2) == 1; +} + +public boolean hasConsecutiveExam(Student student, int day, int slot, + Assignment assignment, EnrollmentMap enrollmentMap) { + // Get all courses this student is enrolled in + List studentCourses = enrollmentMap.getCoursesForStudent(student); + + for (Course course : studentCourses) { + if (assignment.contains(course)) { + ExamAssignment exam = assignment.get(course); + + if (areConsecutive(day, slot, exam.day, exam.slot, config.slotsPerDay)) { + return true; // Found consecutive exam + } + } + } + + return false; // No consecutive exams +} +``` + +**Edge Cases:** +- ✅ Day 1, Slot 4 and Day 2, Slot 1 ARE consecutive +- ✅ Day 1, Slot 1 has no previous slot (boundary) +- ✅ Day 5, Slot 4 has no next slot (boundary) + +--- + +### Constraint 2: Max 2 Exams Per Day + +**Definition:** A student cannot have more than 2 exams on any single day. + +**Implementation:** + +```java +public int countExamsForStudentOnDay(Student student, int day, + Assignment assignment, EnrollmentMap enrollmentMap) { + List studentCourses = enrollmentMap.getCoursesForStudent(student); + int count = 0; + + for (Course course : studentCourses) { + if (assignment.contains(course)) { + ExamAssignment exam = assignment.get(course); + + if (exam.day == day) { + count++; + } + } + } + + return count; +} + +public boolean violatesMaxTwoPerDay(Student student, int day, + Assignment assignment, EnrollmentMap enrollmentMap) { + int currentCount = countExamsForStudentOnDay(student, day, assignment, enrollmentMap); + + // Would cause 3+ exams if we add another exam on this day + return currentCount >= 2; +} +``` + +--- + +### Constraint 3: Classroom Capacity + +**Definition:** Number of students in a course cannot exceed classroom capacity. + +**Implementation:** + +```java +public boolean violatesCapacity(Course course, Classroom classroom, EnrollmentMap enrollmentMap) { + int enrolledCount = enrollmentMap.getStudentsForCourse(course).size(); + + return enrolledCount > classroom.getCapacity(); +} +``` + +**Note:** This is pre-filtered during domain initialization, so should always be true during search. + +--- + +### Constraint 4: No Classroom Double-Booking + +**Definition:** A classroom cannot host two exams at the same time. + +**Implementation:** + +```java +public boolean isClassroomOccupied(Classroom classroom, int day, int slot, Assignment assignment) { + for (ExamAssignment exam : assignment.values()) { + if (exam.classroom.equals(classroom) && exam.day == day && exam.slot == slot) { + return true; // Classroom already used + } + } + + return false; // Classroom available +} +``` + +--- + +## HEURISTICS + +### 1. Minimum Remaining Values (MRV) + +**Purpose:** Variable ordering - choose which course to assign next + +**Strategy:** Select course with fewest valid assignments remaining + +**Why it works:** +- Courses with few options are harder to satisfy +- If we can't satisfy them, fail early (prune search tree) +- Empirically reduces backtracking by 10-100x + +**Example:** + +``` +Unassigned courses: +- CourseCode_01: 50 valid assignments remaining +- CourseCode_02: 5 valid assignments remaining ← Choose this one (MRV) +- CourseCode_03: 30 valid assignments remaining + +Reason: CourseCode_02 is most constrained, so try it first. +If it fails, we know early and can backtrack sooner. +``` + +--- + +### 2. Least Constraining Value (LCV) + +**Purpose:** Value ordering - which {classroom, day, slot} to try first + +**Strategy:** Try values that eliminate fewest options for other courses + +**Why it works:** +- Maximize flexibility for future assignments +- Reduce probability of backtracking + +**Example:** + +``` +CourseCode_01 can be assigned to: +- (Classroom_01, Day 1, Slot 1) → Eliminates 10 options for other courses +- (Classroom_05, Day 3, Slot 2) → Eliminates 2 options for other courses ← Try this first (LCV) + +Reason: Choosing Classroom_05 leaves more flexibility. +``` + +--- + +### 3. Degree Heuristic (Tie-breaking for MRV) + +**Purpose:** When multiple courses have same MRV, choose one with most conflicts + +**Strategy:** Choose course enrolled by most students (more potential conflicts) + +**Why it works:** +- Harder courses should be assigned earlier +- Similar to MRV (fail-fast on difficult variables) + +**Example:** + +``` +Two courses both have 10 valid assignments remaining: +- CourseCode_01: 40 students enrolled ← Choose this (more conflicts) +- CourseCode_02: 15 students enrolled + +Reason: CourseCode_01 affects more students, so constrain it first. +``` + +--- + +## OPTIMIZATION STRATEGIES + +### Strategy 1: Minimize Days Used + +**Goal:** Pack exams into as few days as possible + +**Value Ordering Modification:** + +```java +public int compareAssignments(Assignment a1, Assignment a2) { + // Prefer earlier days + if (a1.day != a2.day) { + return Integer.compare(a1.day, a2.day); // Ascending + } + + // Within same day, prefer earlier slots + if (a1.slot != a2.slot) { + return Integer.compare(a1.slot, a2.slot); // Ascending + } + + // Tie-break by classroom + return a1.classroom.compareTo(a2.classroom); +} +``` + +**Effect:** Tries to fill Day 1 completely before Day 2, etc. + +**Use Case:** Shorter exam period, reduced facility costs + +--- + +### Strategy 2: Balance Distribution Across Days + +**Goal:** Spread exams evenly across all available days + +**Value Ordering Modification:** + +```java +public int compareAssignments(Assignment a1, Assignment a2) { + // Count how many exams already scheduled on each day + int count1 = countExamsOnDay(a1.day, currentAssignment); + int count2 = countExamsOnDay(a2.day, currentAssignment); + + // Prefer day with fewer exams (balance) + if (count1 != count2) { + return Integer.compare(count1, count2); // Ascending + } + + // Tie-break by slot + return Integer.compare(a1.slot, a2.slot); +} +``` + +**Effect:** Distributes exam load evenly + +**Use Case:** Reduce student/proctor stress, balanced workload + +--- + +### Strategy 3: Minimize Classrooms Needed + +**Goal:** Use as few different classrooms as possible + +**Value Ordering Modification:** + +```java +public int compareAssignments(Assignment a1, Assignment a2) { + // Check if classroom already used + boolean a1Used = isClassroomUsed(a1.classroom, currentAssignment); + boolean a2Used = isClassroomUsed(a2.classroom, currentAssignment); + + // Prefer already-used classrooms + if (a1Used && !a2Used) { + return -1; // a1 first + } else if (!a1Used && a2Used) { + return 1; // a2 first + } + + // Both used or both unused: tie-break by day + return Integer.compare(a1.day, a2.day); +} +``` + +**Effect:** Reuses classrooms + +**Use Case:** Centralize exam locations, reduce setup costs + +--- + +### Strategy 4: Balance Classrooms Per Day + +**Goal:** Use similar number of classrooms each day + +**Value Ordering Modification:** + +```java +public int compareAssignments(Assignment a1, Assignment a2) { + // Count unique classrooms used on each day + int classrooms1 = countUniqueClassroomsOnDay(a1.day, currentAssignment); + int classrooms2 = countUniqueClassroomsOnDay(a2.day, currentAssignment); + + // Prefer day with fewer classrooms (balance) + if (classrooms1 != classrooms2) { + return Integer.compare(classrooms1, classrooms2); + } + + // Tie-break by classroom + return a1.classroom.compareTo(a2.classroom); +} +``` + +**Effect:** Even classroom distribution across days + +**Use Case:** Balance proctoring staff assignments + +--- + +## CONFLICT DETECTION + +### When Backtracking Fails + +If backtracking returns FAILURE, it means no valid schedule exists. Analyze why: + +``` +FUNCTION ConflictDetector.analyze(courses, classrooms, enrollments, config): + conflicts ← ConflictReport() + + // Check 1: Students with too many courses + FOR EACH student IN students DO + courseCount ← enrollments.getCoursesForStudent(student).size() + maxPossible ← config.numDays × 2 // Max 2 per day + + IF courseCount > maxPossible THEN + conflicts.addStudentConflict( + student, + "Enrolled in " + courseCount + " courses, max possible " + maxPossible + ) + END IF + END FOR + + // Check 2: Courses with insufficient capacity + FOR EACH course IN courses DO + enrolledCount ← enrollments.getStudentsForCourse(course).size() + maxCapacity ← MAX(classrooms.capacity) + + IF enrolledCount > maxCapacity THEN + conflicts.addCourseConflict( + course, + "Enrolled count " + enrolledCount + " exceeds max capacity " + maxCapacity + ) + END IF + END FOR + + // Check 3: Insufficient total slots + totalSlots ← config.numDays × config.slotsPerDay + IF courses.size() > totalSlots THEN + conflicts.addConfigurationConflict( + "Need " + courses.size() + " slots but only " + totalSlots + " available" + ) + END IF + + // Check 4: Overlapping enrollment patterns (complex graph analysis) + conflicts.addAll(detectEnrollmentCliques(enrollments)) + + // Generate recommendations + conflicts.setRecommendations(generateRecommendations(conflicts)) + + RETURN conflicts +END FUNCTION +``` + +--- + +### Recommendations + +``` +FUNCTION generateRecommendations(conflicts): + recommendations ← [] + + IF conflicts.hasStudentWithTooManyCourses() THEN + recommendations.add("Extend exam period to " + (requiredDays) + " days") + recommendations.add("Reduce course enrollments for affected students") + END IF + + IF conflicts.hasCourseExceedingCapacity() THEN + recommendations.add("Add classroom with capacity ≥ " + (maxRequired)) + recommendations.add("Split large course into multiple exam sessions") + END IF + + IF conflicts.hasInsufficientSlots() THEN + recommendations.add("Increase slots per day to " + (requiredSlots)) + recommendations.add("Extend exam period by " + (additionalDays) + " days") + END IF + + RETURN recommendations +END FUNCTION +``` + +--- + +## PERFORMANCE OPTIMIZATIONS + +### 1. Pre-compute Enrollment Maps + +**Before backtracking:** + +```java +// Build once, reuse throughout search +Map> studentToCourses = new HashMap<>(); +Map> courseToStudents = new HashMap<>(); + +for (Enrollment e : enrollments) { + studentToCourses.computeIfAbsent(e.student, k -> new ArrayList<>()).add(e.course); + courseToStudents.computeIfAbsent(e.course, k -> new ArrayList<>()).add(e.student); +} +``` + +**Benefit:** O(1) lookup instead of O(n) database query + +--- + +### 2. Domain Pre-filtering + +**Before backtracking:** + +```java +for (Course course : courses) { + int enrolledCount = courseToStudents.get(course).size(); + + // Remove classrooms that are too small + domain.get(course).removeIf(assignment -> + assignment.classroom.capacity < enrolledCount + ); +} +``` + +**Benefit:** Reduces domain size by 50-90% typically + +--- + +### 3. Memoization of Constraint Checks + +**Cache repeated constraint checks:** + +```java +Map constraintCache = new HashMap<>(); + +public boolean hasConsecutiveExam(Student student, int day, int slot) { + String key = student.id + ":" + day + ":" + slot; + + if (constraintCache.containsKey(key)) { + return constraintCache.get(key); // O(1) cache hit + } + + boolean result = computeConsecutiveExam(student, day, slot); + constraintCache.put(key, result); + return result; +} +``` + +**Benefit:** 10x faster constraint checking for repeated calls + +--- + +### 4. Early Termination + +**Stop as soon as solution found:** + +```java +if (assignment.isComplete()) { + return assignment; // Don't search for other solutions +} +``` + +**Benefit:** Finds first valid solution quickly (don't need all solutions) + +--- + +### 5. Parallel Domain Evaluation (Advanced) + +**Use Java 8 streams for parallel constraint checking:** + +```java +List validAssignments = domain.parallelStream() + .filter(a -> constraintsValidator.isValid(a, assignment, enrollmentMap)) + .collect(Collectors.toList()); +``` + +**Benefit:** Utilize multiple CPU cores, 2-4x speedup on multi-core machines + +**Trade-off:** More complex, may not be needed for small datasets + +--- + +## IMPLEMENTATION GUIDE + +### Class Structure + +```java +// Main solver class +public class CSPSolver { + private ConstraintValidator validator; + private OptimizationStrategy strategy; + private ProgressTracker progressTracker; + private volatile boolean cancelled = false; + + public Schedule solve( + List courses, + List classrooms, + List enrollments, + ScheduleConfiguration config, + OptimizationStrategy strategy, + ProgressTracker progressTracker + ) throws NoSolutionException, CancelledException; + + private Map backtrack( + Map assignment, + Map> domains, + Map> enrollmentMap + ); + + private Course selectUnassignedVariable( + Map assignment, + Map> domains + ); + + private List orderDomainValues( + Course course, + Set domain, + Map assignment + ); + + private Map> forwardCheck( + Course course, + AssignmentOption value, + Map assignment, + Map> domains + ); + + public void cancel() { + this.cancelled = true; + } +} +``` + +--- + +### Progress Tracking + +```java +public interface ProgressTracker { + void updateProgress(int assignedCourses, int totalCourses); + void updateMessage(String message); + boolean isCancelled(); +} + +// Usage in solver: +private Map backtrack(...) { + int assignedCount = assignment.size(); + int totalCourses = courses.size(); + + progressTracker.updateProgress(assignedCount, totalCourses); + + if (progressTracker.isCancelled()) { + throw new CancelledException(); + } + + // Continue backtracking... +} +``` + +--- + +### Integration with UI (JavaFX Task) + +```java +public class ScheduleGenerationTask extends Task { + @Override + protected Schedule call() throws Exception { + updateMessage("Initializing solver..."); + updateProgress(0, 100); + + CSPSolver solver = new CSPSolver(validator, strategy); + + ProgressTracker tracker = new ProgressTracker() { + @Override + public void updateProgress(int assigned, int total) { + double percentage = (double) assigned / total * 100; + ScheduleGenerationTask.this.updateProgress(assigned, total); + ScheduleGenerationTask.this.updateMessage( + "Assigned " + assigned + " / " + total + " courses" + ); + } + + @Override + public boolean isCancelled() { + return ScheduleGenerationTask.this.isCancelled(); + } + }; + + Schedule schedule = solver.solve(courses, classrooms, enrollments, config, strategy, tracker); + + return schedule; + } +} +``` + +--- + +## TESTING STRATEGY + +### Unit Tests for Constraints + +```java +@Test +public void testNoConsecutiveExams_WithConsecutiveSlots_ShouldReturnTrue() { + // Arrange + int day1 = 1, slot1 = 4; + int day2 = 2, slot2 = 1; + int slotsPerDay = 4; + + // Act + boolean result = validator.areConsecutive(day1, slot1, day2, slot2, slotsPerDay); + + // Assert + assertTrue(result, "Day 1 Slot 4 and Day 2 Slot 1 should be consecutive"); +} + +@Test +public void testMaxTwoPerDay_WithTwoExams_ShouldAllowThird() { + // Arrange + Student student = new Student("Std_001"); + Assignment assignment = createAssignmentWithTwoExamsOnDay1(student); + + // Act + boolean violates = validator.violatesMaxTwoPerDay(student, 1, assignment); + + // Assert + assertTrue(violates, "Should violate max-2-per-day constraint"); +} +``` + +--- + +### Integration Tests for Solver + +```java +@Test +public void testSolve_WithSolvableDataset_ShouldReturnValidSchedule() { + // Arrange + List students = TestDataBuilder.createStudents(10); + List courses = TestDataBuilder.createCourses(5); + List classrooms = TestDataBuilder.createClassrooms(3, 50); + List enrollments = TestDataBuilder.createRandomEnrollments(students, courses); + ScheduleConfiguration config = new ScheduleConfiguration(3, 2); // 6 slots + + CSPSolver solver = new CSPSolver(validator, new MinimizeDaysStrategy()); + + // Act + Schedule schedule = solver.solve(courses, classrooms, enrollments, config, strategy, null); + + // Assert + assertNotNull(schedule); + assertEquals(5, schedule.getAssignments().size()); + assertTrue(validator.validateSchedule(schedule)); +} + +@Test(expected = NoSolutionException.class) +public void testSolve_WithUnsolvableDataset_ShouldThrowException() { + // Arrange: Student enrolled in 10 courses, only 6 slots available + Student student = new Student("Std_001"); + List courses = TestDataBuilder.createCourses(10); + List enrollments = TestDataBuilder.enrollStudentInAllCourses(student, courses); + ScheduleConfiguration config = new ScheduleConfiguration(3, 2); // 6 slots, max 6 exams + + CSPSolver solver = new CSPSolver(validator, strategy); + + // Act & Assert + solver.solve(courses, classrooms, enrollments, config, strategy, null); + // Should throw NoSolutionException +} +``` + +--- + +## COMPLEXITY ANALYSIS + +### Time Complexity + +**Worst case (no heuristics):** +``` +O(d^n) where: + d = domain size (classrooms × days × slots) + n = number of courses + +For sample data: O(200^20) ≈ 10^46 operations +``` + +**With MRV and LCV heuristics:** +``` +Effective complexity: O(b^d) where: + b = effective branching factor (reduced by pruning) + d = depth of search tree + +Empirically: ~O(n^2) to O(n^3) for solvable instances +``` + +**Forward checking:** +``` +O(n^2 × d) per assignment where: + n = number of courses + d = domain size + +But reduces search space exponentially, so net benefit. +``` + +--- + +### Space Complexity + +``` +O(n × d) where: + n = number of courses + d = domain size + +Storage needed: + - Domains: n × d assignments + - Assignment: n course assignments + - Enrollment map: s × c (students × avg courses) + +For sample data: + 20 courses × 200 domain size = 4,000 assignment options + Memory: ~100 KB +``` + +--- + +### Performance Benchmarks + +| Dataset | Courses | Students | Classrooms | Expected Time | +|---------|---------|----------|------------|---------------| +| Tiny | 5 | 50 | 3 | < 1 second | +| Small | 20 | 250 | 10 | < 5 seconds | +| Medium | 50 | 500 | 15 | < 10 seconds | +| Large | 100 | 1000 | 20 | < 60 seconds | + +**Assumptions:** Well-distributed enrollments, solvable configuration + +--- + +## VERSION HISTORY + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0 | 2025-11-26 | Claude Code | Initial algorithm design | + +--- + +## REFERENCES + +- Russell & Norvig, "Artificial Intelligence: A Modern Approach", Chapter 6 (Constraint Satisfaction Problems) +- Bacchus & van Run, "Dynamic Variable Ordering in CSPs" +- Haralick & Elliott, "Increasing Tree Search Efficiency for CSPs" +- Online CSP Tutorial: https://artint.info/2e/html/ArtInt2e.Ch4.html + +--- + +**END OF ALGORITHM DESIGN DOCUMENTATION** diff --git a/DATABASE_SCHEMA.md b/DATABASE_SCHEMA.md new file mode 100644 index 0000000..27ecfa2 --- /dev/null +++ b/DATABASE_SCHEMA.md @@ -0,0 +1,1213 @@ +# DATABASE SCHEMA - EXAM SCHEDULING SYSTEM + +**Project:** Exam Planner Desktop Application +**Database:** SQLite 3.44 +**Version:** 1.0 +**Last Updated:** November 26, 2025 + +--- + +## TABLE OF CONTENTS + +1. [Overview](#overview) +2. [Entity-Relationship Diagram](#entity-relationship-diagram) +3. [Database Tables](#database-tables) +4. [Relationships](#relationships) +5. [Indexes](#indexes) +6. [Sample Data](#sample-data) +7. [SQL Schema Script](#sql-schema-script) +8. [Common Queries](#common-queries) +9. [Database Initialization](#database-initialization) +10. [Best Practices](#best-practices) + +--- + +## OVERVIEW + +### Database Purpose + +The database stores all data required for the exam scheduling system: +- **Student records** - All students who will take exams +- **Course catalog** - All courses that need exams scheduled +- **Classroom inventory** - Available classrooms with capacities +- **Enrollment data** - Which students are enrolled in which courses +- **Generated schedules** - Complete exam schedules with metadata +- **Exam assignments** - Individual course-to-classroom-time assignments + +### Database Technology + +**SQLite 3.44** - Chosen for: +- **Zero configuration** - No server setup required +- **Embedded** - Single file database +- **Cross-platform** - Works on Windows, macOS, Linux +- **ACID compliant** - Full transaction support +- **Lightweight** - Perfect for desktop applications +- **Easy backup** - Just copy the .db file + +### Database File Location + +- **Development:** `./data/exam_scheduler.db` +- **Production:** User's Documents folder or application data directory +- **Backup:** Automatic export to CSV on schedule save + +--- + +## ENTITY-RELATIONSHIP DIAGRAM + +### High-Level ER Diagram + +``` +┌─────────────┐ ┌──────────────┐ ┌──────────────┐ +│ Student │ │ Enrollment │ │ Course │ +├─────────────┤ ├──────────────┤ ├──────────────┤ +│ student_id │◄───────►│ enrollment_id│◄───────►│ course_code │ +│ created_at │ M:N │ student_id │ M:N │ created_at │ +└─────────────┘ │ course_code │ └──────┬───────┘ + │ created_at │ │ + └──────────────┘ │ 1:M + │ + ▼ +┌─────────────┐ ┌──────────────────────────────────────┐ +│ Classroom │ │ ExamAssignment │ +├─────────────┤ ├──────────────────────────────────────┤ +│classroom_id │◄────────│ exam_id │ +│ capacity │ 1:M │ schedule_id (FK) │ +│ created_at │ │ course_code (FK) │ +└─────────────┘ │ classroom_id (FK) │ + │ day │ + │ time_slot │ + │ created_at │ + └──────────────┬───────────────────────┘ + │ M:1 + ▼ + ┌──────────────────────────────────────┐ + │ Schedule │ + ├──────────────────────────────────────┤ + │ schedule_id │ + │ created_date │ + │ optimization_strategy │ + │ num_days │ + │ slots_per_day │ + │ status │ + └──────────────────────────────────────┘ +``` + +### Relationship Summary + +| Relationship | Type | Description | +|-------------|------|-------------| +| Student ↔ Enrollment | 1:M | One student has many enrollments | +| Course ↔ Enrollment | 1:M | One course has many enrollments | +| Student ↔ Course | M:N | Many-to-many through Enrollment | +| Course ↔ ExamAssignment | 1:M | One course can have multiple assignments (across schedules) | +| Classroom ↔ ExamAssignment | 1:M | One classroom hosts many exams | +| Schedule ↔ ExamAssignment | 1:M | One schedule contains many exam assignments | + +--- + +## DATABASE TABLES + +### 1. students + +**Purpose:** Store all student records who will participate in exams. + +**Table Definition:** + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| student_id | TEXT | PRIMARY KEY, NOT NULL | Unique student identifier (e.g., "Std_ID_001") | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | + +**Business Rules:** +- Student IDs must be unique +- Student IDs are imported from CSV +- Cannot be deleted if enrollments exist (protected by foreign key) + +**Sample Data:** +```sql +INSERT INTO students (student_id) VALUES +('Std_ID_001'), +('Std_ID_002'), +('Std_ID_003'); +``` + +**Expected Size:** 250-500 students (sample data has 250) + +--- + +### 2. courses + +**Purpose:** Store all courses that require exam scheduling. + +**Table Definition:** + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| course_code | TEXT | PRIMARY KEY, NOT NULL | Unique course identifier (e.g., "CourseCode_01") | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | + +**Business Rules:** +- Course codes must be unique +- Course codes are imported from CSV +- Cannot be deleted if enrollments exist (protected by foreign key) + +**Sample Data:** +```sql +INSERT INTO courses (course_code) VALUES +('CourseCode_01'), +('CourseCode_02'), +('CourseCode_03'); +``` + +**Expected Size:** 20-50 courses (sample data has 20) + +--- + +### 3. classrooms + +**Purpose:** Store available classrooms with their seating capacities. + +**Table Definition:** + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| classroom_id | TEXT | PRIMARY KEY, NOT NULL | Unique classroom identifier (e.g., "Classroom_01") | +| capacity | INTEGER | NOT NULL, CHECK(capacity > 0) | Maximum seating capacity | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | + +**Business Rules:** +- Classroom IDs must be unique +- Capacity must be positive integer +- Imported from CSV format: `ClassroomID;Capacity` +- Cannot be deleted if used in exam assignments (protected by foreign key) + +**Sample Data:** +```sql +INSERT INTO classrooms (classroom_id, capacity) VALUES +('Classroom_01', 40), +('Classroom_02', 35), +('Classroom_03', 50); +``` + +**Expected Size:** 10-20 classrooms (sample data has 10, all capacity 40) + +--- + +### 4. enrollments + +**Purpose:** Many-to-many relationship between students and courses (which students are enrolled in which courses). + +**Table Definition:** + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| enrollment_id | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-generated unique ID | +| student_id | TEXT | NOT NULL, FK → students(student_id) | Reference to student | +| course_code | TEXT | NOT NULL, FK → courses(course_code) | Reference to course | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| UNIQUE | | (student_id, course_code) | Prevent duplicate enrollments | + +**Indexes:** +```sql +CREATE INDEX idx_enrollments_student ON enrollments(student_id); +CREATE INDEX idx_enrollments_course ON enrollments(course_code); +``` + +**Business Rules:** +- Each student-course pair can only appear once +- Cannot enroll student in non-existent course +- Cannot enroll non-existent student +- Imported from attendance list CSV + +**Sample Data:** +```sql +INSERT INTO enrollments (student_id, course_code) VALUES +('Std_ID_001', 'CourseCode_01'), +('Std_ID_001', 'CourseCode_05'), +('Std_ID_002', 'CourseCode_01'), +('Std_ID_002', 'CourseCode_03'); +``` + +**Expected Size:** 800-10,000 records (sample: ~800 enrollments, ~40 students per course) + +**Cascade Behavior:** +- If student deleted → all enrollments deleted (CASCADE) +- If course deleted → all enrollments deleted (CASCADE) + +--- + +### 5. schedules + +**Purpose:** Store metadata for each generated exam schedule. + +**Table Definition:** + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| schedule_id | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-generated unique ID | +| created_date | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP, NOT NULL | When schedule was generated | +| optimization_strategy | TEXT | NOT NULL | Strategy used (see values below) | +| num_days | INTEGER | NOT NULL, CHECK(num_days > 0) | Number of exam days configured | +| slots_per_day | INTEGER | NOT NULL, CHECK(slots_per_day > 0) | Number of time slots per day | +| status | TEXT | DEFAULT 'generated', CHECK(status IN (...)) | Schedule status (see values below) | + +**Optimization Strategy Values:** +- `'minimize_days'` - Pack exams into fewest days +- `'balance_distribution'` - Spread exams evenly across days +- `'minimize_classrooms'` - Use fewest classrooms +- `'balance_classrooms'` - Balance classroom usage per day + +**Status Values:** +- `'generated'` - Just created +- `'saved'` - User explicitly saved +- `'archived'` - Old schedule kept for history + +**Business Rules:** +- num_days and slots_per_day must be positive integers +- Total slots (num_days × slots_per_day) should be ≥ number of courses +- Cannot delete schedule if it's the only one (application logic) + +**Sample Data:** +```sql +INSERT INTO schedules (optimization_strategy, num_days, slots_per_day, status) VALUES +('balance_distribution', 5, 4, 'saved'), +('minimize_days', 5, 4, 'generated'), +('minimize_classrooms', 6, 3, 'archived'); +``` + +**Expected Size:** 10-100 records (historical schedules kept for comparison) + +--- + +### 6. exam_assignments + +**Purpose:** Store individual exam assignments (which course exam is in which classroom at what time). + +**Table Definition:** + +| Column | Type | Constraints | Description | +|--------|------|-------------|-------------| +| exam_id | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-generated unique ID | +| schedule_id | INTEGER | NOT NULL, FK → schedules(schedule_id) | Which schedule this belongs to | +| course_code | TEXT | NOT NULL, FK → courses(course_code) | Which course exam | +| classroom_id | TEXT | NOT NULL, FK → classrooms(classroom_id) | Which classroom | +| day | INTEGER | NOT NULL, CHECK(day > 0) | Which day (1, 2, 3, ...) | +| time_slot | INTEGER | NOT NULL, CHECK(time_slot > 0) | Which time slot (1, 2, 3, ...) | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| UNIQUE | | (schedule_id, course_code) | Each course once per schedule | +| UNIQUE | | (schedule_id, classroom_id, day, time_slot) | No classroom double-booking | + +**Indexes:** +```sql +CREATE INDEX idx_assignments_schedule ON exam_assignments(schedule_id); +CREATE INDEX idx_assignments_course ON exam_assignments(course_code); +CREATE INDEX idx_assignments_classroom_time ON exam_assignments(classroom_id, day, time_slot); +``` + +**Business Rules:** +- Each course appears exactly once per schedule +- Each {classroom, day, time_slot} combination used at most once per schedule +- Day and time_slot must be positive integers +- day must be ≤ num_days from schedule +- time_slot must be ≤ slots_per_day from schedule (enforced by application) + +**Sample Data:** +```sql +INSERT INTO exam_assignments (schedule_id, course_code, classroom_id, day, time_slot) VALUES +(1, 'CourseCode_01', 'Classroom_01', 1, 1), +(1, 'CourseCode_02', 'Classroom_02', 1, 1), +(1, 'CourseCode_03', 'Classroom_01', 1, 2), +(1, 'CourseCode_04', 'Classroom_03', 1, 3); +``` + +**Expected Size:** 20-50 assignments per schedule (one per course) + +**Cascade Behavior:** +- If schedule deleted → all assignments deleted (CASCADE) +- If course deleted → all assignments deleted (CASCADE) +- If classroom deleted → all assignments deleted (CASCADE) + +--- + +## RELATIONSHIPS + +### Primary Key - Foreign Key Relationships + +#### 1. students → enrollments (1:M) + +```sql +FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE +``` + +**Meaning:** Each student can have many enrollments. If a student is deleted, all their enrollments are automatically deleted. + +**Query Example:** +```sql +-- Get all courses for a student +SELECT c.course_code +FROM courses c +JOIN enrollments e ON c.course_code = e.course_code +WHERE e.student_id = 'Std_ID_001'; +``` + +--- + +#### 2. courses → enrollments (1:M) + +```sql +FOREIGN KEY (course_code) REFERENCES courses(course_code) ON DELETE CASCADE +``` + +**Meaning:** Each course can have many enrollments. If a course is deleted, all enrollments in that course are automatically deleted. + +**Query Example:** +```sql +-- Get all students in a course +SELECT s.student_id +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +WHERE e.course_code = 'CourseCode_01'; +``` + +--- + +#### 3. courses → exam_assignments (1:M) + +```sql +FOREIGN KEY (course_code) REFERENCES courses(course_code) ON DELETE CASCADE +``` + +**Meaning:** Each course can have multiple exam assignments (across different schedules). If a course is deleted, all its exam assignments are deleted. + +--- + +#### 4. classrooms → exam_assignments (1:M) + +```sql +FOREIGN KEY (classroom_id) REFERENCES classrooms(classroom_id) ON DELETE CASCADE +``` + +**Meaning:** Each classroom can host many exams. If a classroom is deleted, all exam assignments using it are deleted. + +--- + +#### 5. schedules → exam_assignments (1:M) + +```sql +FOREIGN KEY (schedule_id) REFERENCES schedules(schedule_id) ON DELETE CASCADE +``` + +**Meaning:** Each schedule contains many exam assignments. If a schedule is deleted, all its assignments are automatically deleted. + +--- + +## INDEXES + +### Purpose of Indexes + +Indexes speed up queries by creating a data structure that allows fast lookups. Critical for: +- JOIN operations +- WHERE clauses +- ORDER BY operations + +### Index Definitions + +#### 1. enrollments Indexes + +```sql +CREATE INDEX idx_enrollments_student ON enrollments(student_id); +``` +**Purpose:** Fast lookup of all courses for a student +**Used by:** Constraint validation (check if student has consecutive exams) + +```sql +CREATE INDEX idx_enrollments_course ON enrollments(course_code); +``` +**Purpose:** Fast lookup of all students in a course +**Used by:** Capacity validation, solver domain creation + +--- + +#### 2. exam_assignments Indexes + +```sql +CREATE INDEX idx_assignments_schedule ON exam_assignments(schedule_id); +``` +**Purpose:** Fast retrieval of all assignments in a schedule +**Used by:** Loading schedules, display views + +```sql +CREATE INDEX idx_assignments_course ON exam_assignments(course_code); +``` +**Purpose:** Fast lookup of assignment for a specific course +**Used by:** Manual editing, validation + +```sql +CREATE INDEX idx_assignments_classroom_time ON exam_assignments(classroom_id, day, time_slot); +``` +**Purpose:** Fast check if classroom is available at specific time +**Used by:** Solver (check classroom availability), validation + +--- + +### Index Performance + +**Without indexes:** +- Query time for "students in course": O(n) - scan all enrollments +- Query time for "classroom availability": O(n) - scan all assignments + +**With indexes:** +- Query time: O(log n) - binary search on index +- 10x-100x faster for typical dataset sizes + +**Trade-off:** +- Indexes use disk space (~10-20% more) +- Inserts/updates slightly slower (must update indexes) +- For this application: Read-heavy, so indexes are beneficial + +--- + +## SAMPLE DATA + +### Sample Dataset Statistics + +Based on provided sample data: + +``` +Students: 250 (Std_ID_001 to Std_ID_250) +Courses: 20 (CourseCode_01 to CourseCode_20) +Classrooms: 10 (Classroom_01 to Classroom_10) +Capacity: 40 seats per classroom (all classrooms) +Enrollments: ~800 total (~40 students per course average) +``` + +### Sample Enrollment Distribution + +``` +CourseCode_01: 40 students +CourseCode_02: 40 students +CourseCode_03: 38 students +... +CourseCode_20: 42 students +``` + +**Characteristics:** +- Fairly balanced enrollment across courses +- No course exceeds max classroom capacity (40) +- Average student takes 3-4 courses +- Solvable with 5 days × 4 slots = 20 total slots + +--- + +## SQL SCHEMA SCRIPT + +### Complete Schema (schema.sql) + +This script creates all tables with proper constraints, indexes, and foreign keys. + +```sql +-- ============================================================ +-- EXAM SCHEDULING SYSTEM - DATABASE SCHEMA +-- Database: SQLite 3.44 +-- Version: 1.0 +-- Created: November 26, 2025 +-- ============================================================ + +-- Enable foreign key constraints (required for SQLite) +PRAGMA foreign_keys = ON; + +-- ============================================================ +-- TABLE: students +-- Purpose: Store all student records +-- ============================================================ + +CREATE TABLE IF NOT EXISTS students ( + student_id TEXT PRIMARY KEY NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- ============================================================ +-- TABLE: courses +-- Purpose: Store all course records +-- ============================================================ + +CREATE TABLE IF NOT EXISTS courses ( + course_code TEXT PRIMARY KEY NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- ============================================================ +-- TABLE: classrooms +-- Purpose: Store classroom information with capacities +-- ============================================================ + +CREATE TABLE IF NOT EXISTS classrooms ( + classroom_id TEXT PRIMARY KEY NOT NULL, + capacity INTEGER NOT NULL CHECK(capacity > 0), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- ============================================================ +-- TABLE: enrollments +-- Purpose: Many-to-many relationship between students and courses +-- ============================================================ + +CREATE TABLE IF NOT EXISTS enrollments ( + enrollment_id INTEGER PRIMARY KEY AUTOINCREMENT, + student_id TEXT NOT NULL, + course_code TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (student_id) REFERENCES students(student_id) ON DELETE CASCADE, + FOREIGN KEY (course_code) REFERENCES courses(course_code) ON DELETE CASCADE, + + UNIQUE(student_id, course_code) +); + +-- Indexes for enrollments (for fast lookups) +CREATE INDEX IF NOT EXISTS idx_enrollments_student ON enrollments(student_id); +CREATE INDEX IF NOT EXISTS idx_enrollments_course ON enrollments(course_code); + +-- ============================================================ +-- TABLE: schedules +-- Purpose: Store metadata for generated exam schedules +-- ============================================================ + +CREATE TABLE IF NOT EXISTS schedules ( + schedule_id INTEGER PRIMARY KEY AUTOINCREMENT, + created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + optimization_strategy TEXT NOT NULL CHECK( + optimization_strategy IN ( + 'minimize_days', + 'balance_distribution', + 'minimize_classrooms', + 'balance_classrooms' + ) + ), + num_days INTEGER NOT NULL CHECK(num_days > 0), + slots_per_day INTEGER NOT NULL CHECK(slots_per_day > 0), + status TEXT DEFAULT 'generated' CHECK( + status IN ('generated', 'saved', 'archived') + ) +); + +-- ============================================================ +-- TABLE: exam_assignments +-- Purpose: Store individual exam assignments +-- ============================================================ + +CREATE TABLE IF NOT EXISTS exam_assignments ( + exam_id INTEGER PRIMARY KEY AUTOINCREMENT, + schedule_id INTEGER NOT NULL, + course_code TEXT NOT NULL, + classroom_id TEXT NOT NULL, + day INTEGER NOT NULL CHECK(day > 0), + time_slot INTEGER NOT NULL CHECK(time_slot > 0), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (schedule_id) REFERENCES schedules(schedule_id) ON DELETE CASCADE, + FOREIGN KEY (course_code) REFERENCES courses(course_code) ON DELETE CASCADE, + FOREIGN KEY (classroom_id) REFERENCES classrooms(classroom_id) ON DELETE CASCADE, + + -- Each course appears once per schedule + UNIQUE(schedule_id, course_code), + + -- No classroom double-booking + UNIQUE(schedule_id, classroom_id, day, time_slot) +); + +-- Indexes for exam_assignments (for fast queries) +CREATE INDEX IF NOT EXISTS idx_assignments_schedule ON exam_assignments(schedule_id); +CREATE INDEX IF NOT EXISTS idx_assignments_course ON exam_assignments(course_code); +CREATE INDEX IF NOT EXISTS idx_assignments_classroom_time ON exam_assignments(classroom_id, day, time_slot); + +-- ============================================================ +-- END OF SCHEMA +-- ============================================================ +``` + +--- + +## COMMON QUERIES + +### Query 1: Get All Courses for a Student + +```sql +SELECT c.course_code +FROM courses c +JOIN enrollments e ON c.course_code = e.course_code +WHERE e.student_id = ? +ORDER BY c.course_code; +``` + +**Parameters:** `student_id` (e.g., 'Std_ID_001') + +**Returns:** List of course codes the student is enrolled in + +**Usage:** Constraint validation, student view display + +--- + +### Query 2: Get All Students in a Course + +```sql +SELECT s.student_id +FROM students s +JOIN enrollments e ON s.student_id = e.student_id +WHERE e.course_code = ? +ORDER BY s.student_id; +``` + +**Parameters:** `course_code` (e.g., 'CourseCode_01') + +**Returns:** List of student IDs enrolled in the course + +**Usage:** Capacity validation, solver domain creation + +--- + +### Query 3: Get Enrollment Count for a Course + +```sql +SELECT COUNT(*) as enrollment_count +FROM enrollments +WHERE course_code = ?; +``` + +**Parameters:** `course_code` + +**Returns:** Number of students enrolled + +**Usage:** Capacity validation (must be ≤ classroom capacity) + +--- + +### Query 4: Get All Exam Assignments for a Schedule + +```sql +SELECT + ea.course_code, + ea.classroom_id, + ea.day, + ea.time_slot, + cl.capacity, + COUNT(e.student_id) as enrolled_count +FROM exam_assignments ea +JOIN classrooms cl ON ea.classroom_id = cl.classroom_id +LEFT JOIN enrollments e ON ea.course_code = e.course_code +WHERE ea.schedule_id = ? +GROUP BY ea.exam_id +ORDER BY ea.day, ea.time_slot, ea.classroom_id; +``` + +**Parameters:** `schedule_id` + +**Returns:** Complete schedule with capacities and enrollment counts + +**Usage:** Display all views, validation + +--- + +### Query 5: Get Student's Personal Exam Schedule + +```sql +SELECT + c.course_code, + ea.day, + ea.time_slot, + ea.classroom_id +FROM courses c +JOIN enrollments e ON c.course_code = e.course_code +JOIN exam_assignments ea ON c.course_code = ea.course_code +WHERE e.student_id = ? + AND ea.schedule_id = ? +ORDER BY ea.day, ea.time_slot; +``` + +**Parameters:** `student_id`, `schedule_id` + +**Returns:** Student's personal exam schedule + +**Usage:** Student view display + +--- + +### Query 6: Check if Classroom is Available + +```sql +SELECT COUNT(*) as is_occupied +FROM exam_assignments +WHERE schedule_id = ? + AND classroom_id = ? + AND day = ? + AND time_slot = ?; +``` + +**Parameters:** `schedule_id`, `classroom_id`, `day`, `time_slot` + +**Returns:** 0 if available, 1 if occupied + +**Usage:** Solver domain filtering, validation + +--- + +### Query 7: Get Exams for Specific Day and Time Slot + +```sql +SELECT + ea.course_code, + ea.classroom_id, + COUNT(e.student_id) as student_count +FROM exam_assignments ea +LEFT JOIN enrollments e ON ea.course_code = e.course_code +WHERE ea.schedule_id = ? + AND ea.day = ? + AND ea.time_slot = ? +GROUP BY ea.exam_id +ORDER BY ea.classroom_id; +``` + +**Parameters:** `schedule_id`, `day`, `time_slot` + +**Returns:** All exams in that time slot + +**Usage:** Day view display + +--- + +### Query 8: Get All Schedules (History) + +```sql +SELECT + s.schedule_id, + s.created_date, + s.optimization_strategy, + s.num_days, + s.slots_per_day, + s.status, + COUNT(DISTINCT ea.course_code) as course_count, + COUNT(DISTINCT ea.classroom_id) as classroom_count +FROM schedules s +LEFT JOIN exam_assignments ea ON s.schedule_id = ea.schedule_id +GROUP BY s.schedule_id +ORDER BY s.created_date DESC; +``` + +**Returns:** All schedules with metadata and statistics + +**Usage:** Schedule history view + +--- + +### Query 9: Check for Student Conflicts (Consecutive Exams) + +```sql +-- Check if student has exam in previous slot +SELECT COUNT(*) as has_conflict +FROM enrollments e1 +JOIN exam_assignments ea1 ON e1.course_code = ea1.course_code +WHERE e1.student_id = ? + AND ea1.schedule_id = ? + AND ( + -- Check previous slot same day + (ea1.day = ? AND ea1.time_slot = ? - 1) + OR + -- Check last slot of previous day + (ea1.day = ? - 1 AND ea1.time_slot = ( + SELECT slots_per_day FROM schedules WHERE schedule_id = ? + )) + ); +``` + +**Parameters:** `student_id`, `schedule_id`, `day`, `time_slot` + +**Returns:** Count of conflicts (should be 0) + +**Usage:** Constraint validation + +--- + +### Query 10: Delete Schedule and All Assignments + +```sql +-- Due to CASCADE, this automatically deletes all exam_assignments +DELETE FROM schedules WHERE schedule_id = ?; +``` + +**Parameters:** `schedule_id` + +**Effect:** Deletes schedule and all associated assignments + +**Usage:** Schedule deletion + +--- + +## DATABASE INITIALIZATION + +### Step 1: Create Database File + +```java +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.Statement; + +public class DatabaseInitializer { + + private static final String DB_URL = "jdbc:sqlite:./data/exam_scheduler.db"; + + public static void initializeDatabase() throws SQLException { + // Create data directory if it doesn't exist + new File("./data").mkdirs(); + + // Connect to database (creates file if doesn't exist) + try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement()) { + + // Enable foreign keys + stmt.execute("PRAGMA foreign_keys = ON;"); + + // Read schema.sql and execute + String schema = readFile("src/main/resources/database/schema.sql"); + stmt.executeUpdate(schema); + + System.out.println("Database initialized successfully!"); + } + } +} +``` + +--- + +### Step 2: Verify Schema + +```sql +-- Check all tables exist +SELECT name FROM sqlite_master WHERE type='table'; + +-- Expected output: +-- students +-- courses +-- classrooms +-- enrollments +-- schedules +-- exam_assignments + +-- Check indexes exist +SELECT name FROM sqlite_master WHERE type='index'; + +-- Check foreign keys are enabled +PRAGMA foreign_keys; +-- Should return: 1 +``` + +--- + +### Step 3: Import Sample Data + +```java +public void importSampleData() throws SQLException { + try (Connection conn = DriverManager.getConnection(DB_URL)) { + conn.setAutoCommit(false); // Start transaction + + try { + importStudents(conn, "data/sample/students.csv"); + importCourses(conn, "data/sample/courses.csv"); + importClassrooms(conn, "data/sample/classrooms.csv"); + importEnrollments(conn, "data/sample/enrollments.csv"); + + conn.commit(); // Commit transaction + System.out.println("Sample data imported successfully!"); + } catch (Exception e) { + conn.rollback(); // Rollback on error + throw e; + } + } +} +``` + +--- + +## BEST PRACTICES + +### 1. Always Use Transactions for Multiple Operations + +```java +try (Connection conn = getConnection()) { + conn.setAutoCommit(false); + + try { + // Multiple inserts/updates + insertStudent(conn, student1); + insertStudent(conn, student2); + insertEnrollment(conn, enrollment1); + + conn.commit(); // Success - commit all + } catch (Exception e) { + conn.rollback(); // Error - rollback all + throw e; + } +} +``` + +**Why:** Ensures data consistency. If any operation fails, all are rolled back. + +--- + +### 2. Use Prepared Statements (Prevent SQL Injection) + +**❌ BAD (Vulnerable to SQL injection):** +```java +String sql = "SELECT * FROM students WHERE student_id = '" + studentId + "'"; +Statement stmt = conn.createStatement(); +ResultSet rs = stmt.executeQuery(sql); +``` + +**✅ GOOD (Safe):** +```java +String sql = "SELECT * FROM students WHERE student_id = ?"; +PreparedStatement pstmt = conn.prepareStatement(sql); +pstmt.setString(1, studentId); +ResultSet rs = pstmt.executeQuery(); +``` + +--- + +### 3. Close Resources Properly (Use Try-With-Resources) + +**❌ BAD:** +```java +Connection conn = DriverManager.getConnection(DB_URL); +Statement stmt = conn.createStatement(); +ResultSet rs = stmt.executeQuery(sql); +// If exception occurs, resources not closed! +``` + +**✅ GOOD:** +```java +try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + // Process results +} // Resources automatically closed +``` + +--- + +### 4. Use Batch Inserts for Performance + +**Slow (one at a time):** +```java +for (Student student : students) { + String sql = "INSERT INTO students (student_id) VALUES (?)"; + PreparedStatement pstmt = conn.prepareStatement(sql); + pstmt.setString(1, student.getId()); + pstmt.executeUpdate(); // 250 database round-trips! +} +``` + +**Fast (batch):** +```java +String sql = "INSERT INTO students (student_id) VALUES (?)"; +PreparedStatement pstmt = conn.prepareStatement(sql); +for (Student student : students) { + pstmt.setString(1, student.getId()); + pstmt.addBatch(); +} +pstmt.executeBatch(); // 1 database round-trip! +``` + +**Performance:** Batch inserts are 10-100x faster for large datasets. + +--- + +### 5. Enable Foreign Keys + +SQLite doesn't enable foreign keys by default! Must enable: + +```java +try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement()) { + stmt.execute("PRAGMA foreign_keys = ON;"); +} +``` + +**Without this:** Foreign key constraints are ignored (data corruption possible) + +--- + +### 6. Use Indexes for Frequently Queried Columns + +Already created in schema: +- `idx_enrollments_student` - Fast student lookup +- `idx_enrollments_course` - Fast course lookup +- `idx_assignments_schedule` - Fast schedule lookup +- `idx_assignments_classroom_time` - Fast availability check + +**When to add more indexes:** If queries are slow, profile and add indexes to WHERE/JOIN columns. + +--- + +### 7. Regular Database Maintenance + +```sql +-- Analyze database for query optimization +ANALYZE; + +-- Rebuild indexes (occasionally, if database grows large) +REINDEX; + +-- Vacuum database to reclaim space (after many deletes) +VACUUM; +``` + +--- + +### 8. Backup Strategy + +**Manual backup:** +```bash +# Just copy the database file +cp data/exam_scheduler.db data/exam_scheduler_backup.db +``` + +**Automatic backup on schedule save:** +```java +public void saveSchedule(Schedule schedule) { + // Save to database + scheduleDAO.save(schedule); + + // Also export to CSV as backup + exportService.exportToCSV(schedule, "backups/"); +} +``` + +--- + +## DATABASE FILE STRUCTURE + +### Database File Location + +``` +exam-scheduler/ +├── data/ +│ ├── exam_scheduler.db # Main database +│ ├── exam_scheduler_backup.db # Backup (optional) +│ └── sample/ +│ ├── students.csv +│ ├── courses.csv +│ ├── classrooms.csv +│ └── enrollments.csv +``` + +### Database File Size Estimates + +| Data Size | Students | Courses | Enrollments | DB Size | +|-----------|----------|---------|-------------|---------| +| Small | 100 | 10 | 200 | ~50 KB | +| Sample | 250 | 20 | 800 | ~100 KB | +| Medium | 500 | 50 | 2,500 | ~500 KB | +| Large | 1,000 | 100 | 10,000 | ~2 MB | + +**Note:** SQLite is very efficient. Even with 10 schedules saved (10 × 100 assignments = 1,000 rows), database remains < 5 MB. + +--- + +## TROUBLESHOOTING + +### Issue 1: Foreign Key Constraint Violation + +**Error:** +``` +FOREIGN KEY constraint failed +``` + +**Causes:** +- Trying to insert enrollment for non-existent student/course +- Trying to insert exam assignment for non-existent course/classroom + +**Solution:** +```java +// Always check references exist first +if (studentDAO.exists(studentId) && courseDAO.exists(courseCode)) { + enrollmentDAO.insert(enrollment); +} else { + throw new IllegalArgumentException("Student or course does not exist"); +} +``` + +--- + +### Issue 2: Unique Constraint Violation + +**Error:** +``` +UNIQUE constraint failed: enrollments.student_id, enrollments.course_code +``` + +**Cause:** Trying to enroll student in same course twice + +**Solution:** +```java +// Check if enrollment exists before inserting +if (!enrollmentDAO.exists(studentId, courseCode)) { + enrollmentDAO.insert(enrollment); +} +``` + +--- + +### Issue 3: Database Locked + +**Error:** +``` +database is locked +``` + +**Cause:** Another connection has an open transaction + +**Solution:** +```java +// Set busy timeout (wait instead of failing immediately) +try (Connection conn = DriverManager.getConnection(DB_URL)) { + Statement stmt = conn.createStatement(); + stmt.execute("PRAGMA busy_timeout = 5000"); // Wait up to 5 seconds +} +``` + +--- + +### Issue 4: Foreign Keys Not Enforced + +**Symptom:** Can insert invalid foreign keys without error + +**Cause:** Foreign keys not enabled + +**Solution:** +```java +// Enable on EVERY connection +try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement()) { + stmt.execute("PRAGMA foreign_keys = ON;"); + // Now do queries +} +``` + +--- + +## VERSION HISTORY + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0 | 2025-11-26 | Claude Code | Initial schema design | + +--- + +## REFERENCES + +- SQLite Documentation: https://www.sqlite.org/docs.html +- SQLite JDBC: https://github.com/xerial/sqlite-jdbc +- SQL Tutorial: https://www.sqlitetutorial.net/ + +--- + +**END OF DATABASE SCHEMA DOCUMENTATION** diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md new file mode 100644 index 0000000..28a696c --- /dev/null +++ b/GETTING_STARTED.md @@ -0,0 +1,496 @@ +# GETTING STARTED - Exam Scheduling System + +**Welcome to the Exam Scheduling System project!** + +This document will help you get started with the project quickly. + +--- + +## Project Setup Complete! ✅ + +The project foundation has been fully set up with: + +### Documentation Created +- ✅ **MEMORY_BANK.md** - Comprehensive project documentation (100+ pages) +- ✅ **ROADMAP.md** - Detailed 10-week development plan +- ✅ **TEAM_ASSIGNMENTS.md** - Team roles and responsibilities +- ✅ **GIT_WORKFLOW.md** - Git branching strategy and PR process +- ✅ **README.md** - Project overview and quick start guide + +### Project Structure +- ✅ Maven `pom.xml` with all dependencies configured +- ✅ Directory structure created (`src/main/java`, `src/test/java`, `src/main/resources`) +- ✅ Package structure ready (`model`, `dao`, `service`, `algorithm`, `controller`, `util`) +- ✅ `.gitignore` configured for Java/Maven project +- ✅ GitHub Actions CI/CD pipeline (`.github/workflows/ci.yml`) + +--- + +## Quick Start for Team Members + +### 1. Clone the Repository + +```bash +git clone https://github.com/sabazadam/se302.git +cd se302 +``` + +### 2. Install Prerequisites + +**Required:** +- Java JDK 17 or higher - [Download](https://adoptium.net/) +- Maven 3.8+ - [Download](https://maven.apache.org/download.cgi) +- Git - [Download](https://git-scm.com/) + +**Recommended:** +- IntelliJ IDEA (recommended) or Eclipse - [Download IntelliJ](https://www.jetbrains.com/idea/download/) +- Scene Builder (for FXML editing) - [Download](https://gluonhq.com/products/scene-builder/) + +### 3. Build the Project + +```bash +mvn clean install +``` + +This will: +- Download all dependencies +- Compile the code +- Run tests (when we have them) +- Create a JAR file + +### 4. Open in IDE + +**IntelliJ IDEA:** +1. File → Open +2. Select the `pom.xml` file +3. Click "Open as Project" +4. Wait for dependencies to download + +**Eclipse:** +1. File → Import +2. Maven → Existing Maven Projects +3. Select the project directory +4. Finish + +### 5. Configure JDK 17 + +**IntelliJ:** +- File → Project Structure → Project → SDK → Select JDK 17 + +**Eclipse:** +- Right-click project → Properties → Java Build Path → Libraries → Add JDK 17 + +--- + +## First Steps for Each Team Member + +### Database Lead (Kerem) + +**Week 1 Tasks:** +1. Read [MEMORY_BANK.md](MEMORY_BANK.md) - Database Schema section +2. Create `schema.sql` in `src/main/resources/database/` +3. Implement DAO interfaces +4. Start with StudentDAO and test it + +**Getting Started:** +```bash +git checkout develop +git pull origin develop +git checkout -b feature/database-schema + +# Create your database schema file +# Edit src/main/resources/database/schema.sql +# Then commit and push +``` + +### Algorithm Specialist (Milena) + +**Week 1 Tasks:** +1. Read [MEMORY_BANK.md](MEMORY_BANK.md) - Algorithm Design section +2. Create domain model classes (Student, Course, Classroom, etc.) +3. Start designing constraint validator + +**Getting Started:** +```bash +git checkout develop +git pull origin develop +git checkout -b feature/domain-models + +# Create model classes in src/main/java/com/se302/examscheduler/model/ +# Start with Student.java, Course.java +``` + +### UI/FXML Specialist (Rabia) + +**Week 1 Tasks:** +1. Read [MEMORY_BANK.md](MEMORY_BANK.md) - User Interface Design section +2. Install Scene Builder +3. Create wireframes for main window +4. Start FXML for MainWindow + +**Getting Started:** +```bash +git checkout develop +git pull origin develop +git checkout -b feature/ui-main-window + +# Create FXML files in src/main/resources/fxml/ +# Design with Scene Builder +``` + +### Controller Lead (Sefa) + +**Week 1 Tasks:** +1. Read [MEMORY_BANK.md](MEMORY_BANK.md) - Controller Layer section +2. Create Main.java application entry point +3. Design controller architecture + +**Getting Started:** +```bash +git checkout develop +git pull origin develop +git checkout -b feature/main-controller + +# Create Main.java in src/main/java/com/se302/examscheduler/ +# Set up JavaFX application structure +``` + +### Testing Lead (Feyza) + +**Week 1 Tasks:** +1. Read [MEMORY_BANK.md](MEMORY_BANK.md) - Testing Strategy section +2. Set up test utilities (TestDataBuilder) +3. Verify CI/CD pipeline works +4. Create test data fixtures + +**Getting Started:** +```bash +git checkout develop +git pull origin develop +git checkout -b feature/test-infrastructure + +# Create test utilities in src/test/java/com/se302/examscheduler/util/ +# Create test CSV files in src/test/resources/ +``` + +### Documentation Lead (Emin) + +**Week 1 Tasks:** +1. Review all documentation created +2. Set up JavaDoc generation +3. Create documentation templates +4. Review first PRs from team + +**Getting Started:** +```bash +# Generate JavaDoc +mvn javadoc:javadoc +open target/site/apidocs/index.html + +# Set up documentation watching +# Review team members' code for documentation quality +``` + +--- + +## Important Files to Read + +### MUST READ (Everyone) +1. **README.md** - Project overview and quick start +2. **MEMORY_BANK.md** - Comprehensive documentation (read your section) +3. **ROADMAP.md** - Week-by-week plan (see your tasks) +4. **TEAM_ASSIGNMENTS.md** - Your role and responsibilities +5. **GIT_WORKFLOW.md** - How to use Git, commit messages, PRs + +### Your Section in MEMORY_BANK.md +- **Kerem:** Database Schema, DAO Layer +- **Milena:** Algorithm Design, Constraint Validation +- **Rabia:** User Interface Design, UI Components +- **Sefa:** API Contracts (Controller), MVC Architecture +- **Feyza:** Testing Strategy, Test Types +- **Emin:** All sections (for review) + +--- + +## Git Workflow Reminder + +### Creating a Feature Branch + +```bash +# Always start from latest develop +git checkout develop +git pull origin develop + +# Create your feature branch +git checkout -b feature/your-feature-name + +# Make changes, then commit +git add . +git commit -m "feat(scope): brief description" + +# Push to remote +git push -u origin feature/your-feature-name +``` + +### Commit Message Format + +``` +(): + +Examples: +feat(dao): Add StudentDAO with batch insert +fix(algorithm): Correct consecutive exam constraint +test(dao): Add unit tests for StudentDAO +docs(readme): Update installation instructions +``` + +### Creating a Pull Request + +1. Push your feature branch to GitHub +2. Go to GitHub repository +3. Click "New Pull Request" +4. Base: `develop`, Compare: `feature/your-feature-name` +5. Fill in PR template +6. Request at least 1 reviewer +7. Wait for approval and CI to pass +8. Merge when approved + +--- + +## Useful Commands + +### Maven Commands + +```bash +# Clean and build +mvn clean install + +# Run tests +mvn test + +# Run specific test +mvn test -Dtest=StudentDAOTest + +# Generate test coverage report +mvn clean test jacoco:report +open target/site/jacoco/index.html + +# Generate JavaDoc +mvn javadoc:javadoc +open target/site/apidocs/index.html + +# Run application (when Main.java is ready) +mvn javafx:run + +# Package into JAR +mvn package +``` + +### Git Commands + +```bash +# Check status +git status + +# View changes +git diff + +# Stage files +git add file.java +git add . + +# Commit +git commit -m "message" + +# Push +git push origin feature-name + +# Pull latest develop +git checkout develop +git pull origin develop + +# Rebase your branch on develop +git checkout feature-name +git rebase develop + +# View commit history +git log --oneline + +# View branches +git branch -a +``` + +--- + +## Project Statistics + +- **Team Size:** 6 members +- **Duration:** 10 weeks (Nov 26, 2025 - Feb 4, 2026) +- **Lines of Documentation:** ~15,000+ lines +- **Files Created:** 8 core documentation files +- **Technology Stack:** Java 17, JavaFX 21, SQLite, Maven +- **Target:** Fully functional desktop application with >70% test coverage + +--- + +## Weekly Meeting Schedule + +**Monday (30 min) - Sprint Planning:** +- Review last week's progress +- Assign this week's tasks +- Discuss blockers + +**Wednesday (15-30 min) - Mid-Week Sync:** +- Quick status updates +- Identify issues early +- Schedule pair programming if needed + +**Friday (30-45 min) - Demo & Retrospective:** +- Each person demos their work +- Celebrate wins +- Discuss improvements +- Brief planning for next week + +--- + +## Communication Channels + +**Synchronous:** +- Weekly meetings (Monday, Wednesday, Friday) +- Pair programming sessions (as needed) + +**Asynchronous:** +- WhatsApp/Discord - Daily updates, quick questions +- GitHub - Code reviews, PR discussions +- Email - Formal communication with professor + +--- + +## Success Metrics + +### Week 1 (Current Week) +- **Expected Completion:** 10% +- **Key Milestone:** Project setup, infrastructure, initial designs +- **Deliverables:** + - Database schema script + - Algorithm design document + - UI wireframes + - CI/CD pipeline working + +### By Week 3 (End of Phase 1) +- **Expected Completion:** 30% +- **Key Milestone:** Foundation complete +- **Deliverables:** + - Database layer working + - CSV import functional + - Configuration screen working + - All team members can run project locally + +--- + +## Getting Help + +### If You're Stuck + +1. **Try for 2 hours** - Google, documentation, StackOverflow +2. **Ask in team chat** - Someone may have solved it +3. **Pair program** - Schedule session with teammate +4. **Ask Emin** - Integration Lead can help with architecture questions +5. **Ask Professor** - For requirements clarification + +### Common Issues + +**Maven dependency download fails:** +```bash +# Clear Maven cache and retry +rm -rf ~/.m2/repository +mvn clean install +``` + +**IDE doesn't recognize Java 17:** +- Verify JDK 17 is installed: `java -version` +- Configure project SDK in IDE settings + +**Git merge conflicts:** +- See GIT_WORKFLOW.md section on "Merge Conflict Resolution" +- Ask Emin if you need help resolving + +**Tests won't run:** +- Ensure Maven Surefire plugin is configured in pom.xml (already done) +- Check test file naming: must end with `Test.java` + +--- + +## Next Steps + +### For the Team + +1. **Today (Nov 26):** + - All members clone repository + - Set up development environment + - Read assigned sections of MEMORY_BANK.md + +2. **This Week (Week 1):** + - Each member starts assigned tasks from ROADMAP.md + - Create first feature branches + - Make first commits + +3. **Friday Meeting (Nov 30):** + - Each person demos progress + - Database schema presented + - UI wireframes presented + - Algorithm pseudocode presented + +--- + +## Resources + +### Learning Resources + +**Java & JavaFX:** +- JavaFX Official Docs: https://openjfx.io/javadoc/17/ +- JavaFX Tutorial: https://openjfx.io/openjfx-docs/ + +**SQLite:** +- SQLite Tutorial: https://www.sqlitetutorial.net/ +- SQLite Java: https://www.sqlitetutorial.net/sqlite-java/ + +**CSP Algorithms:** +- Russell & Norvig AI Textbook: Chapter 6 +- Online: https://artint.info/2e/html/ArtInt2e.Ch4.html + +**Testing:** +- JUnit 5: https://junit.org/junit5/docs/current/user-guide/ +- Mockito: https://site.mockito.org/ + +### Tools + +**Required:** +- Java JDK 17: https://adoptium.net/ +- Maven: https://maven.apache.org/download.cgi +- Git: https://git-scm.com/ + +**Recommended:** +- IntelliJ IDEA: https://www.jetbrains.com/idea/download/ +- Scene Builder: https://gluonhq.com/products/scene-builder/ +- DB Browser for SQLite: https://sqlitebrowser.org/ + +--- + +## Questions? + +- Check MEMORY_BANK.md for detailed answers +- Ask in team chat +- Contact Emin (Documentation Lead) for documentation questions +- Contact team members for their specialty areas +- Email professor for requirements clarification + +--- + +**Last Updated:** November 26, 2025 + +**Good luck team! Let's build an amazing exam scheduler! 🚀** + +--- + +*For comprehensive documentation, see [MEMORY_BANK.md](MEMORY_BANK.md)* diff --git a/GIT_WORKFLOW.md b/GIT_WORKFLOW.md new file mode 100644 index 0000000..088b2a5 --- /dev/null +++ b/GIT_WORKFLOW.md @@ -0,0 +1,873 @@ +# GIT WORKFLOW - EXAM SCHEDULING SYSTEM + +**Project:** Exam Planner Desktop Application +**Team:** Team 3, Section 1 +**Version Control:** Git + GitHub + +--- + +## BRANCHING STRATEGY + +### Branch Structure + +``` +main (production-ready code) + └── develop (integration branch) + ├── feature/database-schema (Kerem's work) + ├── feature/csv-import (Kerem's work) + ├── feature/csp-solver (Milena's work) + ├── feature/constraint-validation (Milena's work) + ├── feature/ui-main-window (Rabia's work) + ├── feature/import-view (Rabia's work) + ├── feature/import-controller (Sefa's work) + ├── feature/generation-controller (Sefa's work) + ├── feature/dao-tests (Feyza's work) + ├── feature/algorithm-tests (Feyza's work) + └── bugfix/constraint-validation-issue (Any team member) +``` + +### Branch Types + +#### 1. `main` Branch +- **Purpose:** Production-ready, stable code only +- **Protection:** Requires PR approval from Emin (Documentation Lead) +- **Merging:** Only from `develop` branch +- **Tagging:** Every merge to main gets a version tag (v1.0.0, v1.1.0, etc.) +- **Never:** Commit directly to `main` + +#### 2. `develop` Branch +- **Purpose:** Integration branch for all features +- **Protection:** Requires at least 1 PR approval +- **Testing:** All tests must pass before merge +- **Never:** Commit directly to `develop` (always use feature branches) + +#### 3. `feature/*` Branches +- **Purpose:** New features or enhancements +- **Naming:** `feature/short-description` (e.g., `feature/csv-import`) +- **Base:** Created from `develop` +- **Merge to:** `develop` via Pull Request +- **Lifetime:** Delete after successful merge +- **Examples:** + - `feature/database-schema` + - `feature/csp-solver` + - `feature/classroom-view` + - `feature/export-excel` + +#### 4. `bugfix/*` Branches +- **Purpose:** Fix bugs found during development +- **Naming:** `bugfix/issue-description` (e.g., `bugfix/constraint-validation`) +- **Base:** Created from `develop` +- **Merge to:** `develop` via Pull Request +- **Examples:** + - `bugfix/duplicate-student-detection` + - `bugfix/constraint-validation-boundary` + - `bugfix/ui-table-alignment` + +#### 5. `hotfix/*` Branches (Emergency Only) +- **Purpose:** Critical fixes for production code +- **Naming:** `hotfix/critical-issue` +- **Base:** Created from `main` +- **Merge to:** Both `main` AND `develop` +- **Use:** Only for critical bugs in production +- **Examples:** + - `hotfix/database-corruption` + - `hotfix/solver-infinite-loop` + +#### 6. `refactor/*` Branches +- **Purpose:** Code refactoring (no functional changes) +- **Naming:** `refactor/component-name` +- **Base:** Created from `develop` +- **Merge to:** `develop` via Pull Request +- **Examples:** + - `refactor/dao-layer-optimization` + - `refactor/algorithm-performance` + +#### 7. `test/*` Branches +- **Purpose:** Adding or improving tests +- **Naming:** `test/component-name` +- **Base:** Created from `develop` +- **Merge to:** `develop` via Pull Request +- **Examples:** + - `test/dao-integration-tests` + - `test/constraint-validator-edge-cases` + +#### 8. `docs/*` Branches +- **Purpose:** Documentation updates +- **Naming:** `docs/document-name` +- **Base:** Created from `develop` +- **Merge to:** `develop` via Pull Request +- **Examples:** + - `docs/algorithm-design` + - `docs/user-manual` + +--- + +## GIT WORKFLOW COMMANDS + +### 1. Starting a New Feature + +```bash +# Make sure you have latest develop +git checkout develop +git pull origin develop + +# Create new feature branch +git checkout -b feature/csv-import + +# Work on your feature (make changes, add files) +# ... + +# Stage and commit your changes +git add src/main/java/com/se302/examscheduler/service/ImportService.java +git commit -m "feat(import): Add CSV parser for student data" + +# Push to remote +git push origin feature/csv-import + +# If branch doesn't exist on remote yet +git push -u origin feature/csv-import +``` + +### 2. Making Commits + +```bash +# Check what files have changed +git status + +# View changes before committing +git diff + +# Stage specific files +git add file1.java file2.java + +# Or stage all changes +git add . + +# Commit with descriptive message +git commit -m "feat(dao): Implement StudentDAO with batch insert" + +# Push to remote branch +git push origin feature/csv-import +``` + +### 3. Keeping Your Branch Up-to-Date + +```bash +# While working on feature branch, periodically sync with develop + +# Fetch latest changes from remote +git fetch origin + +# Rebase your branch on latest develop (preferred for clean history) +git rebase origin/develop + +# Or merge develop into your branch (creates merge commit) +git merge origin/develop + +# If conflicts occur during rebase +# 1. Resolve conflicts in files +# 2. Stage resolved files +git add resolved-file.java +# 3. Continue rebase +git rebase --continue + +# Force push after rebase (only if you haven't pushed yet or working alone on branch) +git push origin feature/csv-import --force-with-lease +``` + +### 4. Creating a Pull Request + +**After pushing your feature branch:** + +1. Go to GitHub repository +2. Click "Pull requests" tab +3. Click "New pull request" +4. Set base branch: `develop` +5. Set compare branch: `feature/csv-import` +6. Fill in PR template: + - Title: Brief description (e.g., "Add CSV import functionality") + - Description: Detailed changes, testing done, screenshots if UI + - Link related issues: "Closes #15" +7. Request reviewers (at least one team member) +8. Assign yourself +9. Add labels (feature, bug, documentation, etc.) +10. Create pull request + +### 5. Reviewing a Pull Request + +**As a reviewer:** + +```bash +# Fetch the PR branch +git fetch origin +git checkout feature/csv-import + +# Test the changes locally +mvn clean test +mvn javafx:run + +# Review code in IDE +# Leave comments on GitHub PR page + +# If changes requested +# - Author makes changes +# - Author pushes to same branch (PR updates automatically) + +# If approved +# - Click "Approve" on GitHub +# - Merge when all checks pass +``` + +### 6. Merging a Pull Request + +**After PR is approved:** + +**Option A: Squash and Merge (Recommended)** +- Combines all commits into one clean commit +- Use for most feature branches +- Keeps develop history clean +- Click "Squash and merge" on GitHub + +**Option B: Rebase and Merge** +- Replays all commits on top of develop +- Use for well-organized commit history +- Click "Rebase and merge" on GitHub + +**Option C: Merge Commit** +- Creates a merge commit preserving all commits +- Use for large features with meaningful commit history +- Click "Merge pull request" on GitHub + +**After merging:** + +```bash +# Delete the remote feature branch (GitHub does this automatically) + +# Delete your local feature branch +git checkout develop +git pull origin develop +git branch -d feature/csv-import + +# If git complains, force delete +git branch -D feature/csv-import +``` + +### 7. Releasing to Main + +**When develop is ready for release:** + +```bash +# Ensure develop is stable +git checkout develop +git pull origin develop +mvn clean test # All tests must pass + +# Create PR from develop to main +# On GitHub: +# 1. New Pull Request: develop -> main +# 2. Title: "Release v1.0.0" +# 3. Description: List of features included +# 4. Request review from Emin (Documentation Lead) + +# After approval and merge +git checkout main +git pull origin main + +# Tag the release +git tag -a v1.0.0 -m "Release version 1.0.0 - Initial delivery" +git push origin v1.0.0 +``` + +--- + +## COMMIT MESSAGE GUIDELINES + +### Format + +``` +(): + + + +