File size: 5,871 Bytes
4b94493
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
use crate::domain::Lesson;

use super::vocabulary::{group_specs, subjects, teacher_index, LESSON_DURATION_MINUTES};

/// Builds lessons for all groups based on subject configuration.
///
/// For each group, creates lessons for all subjects with their allocated hours_per_week.
/// Each lesson is assigned to a group, a teacher, and a required room kind.
///
/// Teacher assignment: for a given subject, group 0 gets teachers[0], group 1 gets teachers[1],
/// etc. (wrapping around if there are more groups than teachers for that subject).
///
/// Total lessons = GROUP_COUNT * sum(hours_per_week for all subjects).
/// With 12 groups and current config: 12 * 25 = 300 lessons.
pub(super) fn build_lessons(group_count: usize) -> Vec<Lesson> {
    let mut lessons = Vec::new();
    let mut lesson_index = 0;

    // For each group
    for group_idx in 0..group_count {
        let student_count = group_specs()[group_idx].student_count;

        // For each subject, create hours_per_week lessons
        for subject_config in subjects() {
            // Determine which teacher to use for this subject and group
            // Use round-robin: group_idx % number_of_teachers_for_subject
            for lesson_num in 0..subject_config.hours_per_week {
                let teacher_name = subject_config.teachers
                    [(group_idx + lesson_num) % subject_config.teachers.len()];
                let teacher_idx = teacher_index(teacher_name);

                lessons.push(Lesson::with_details(
                    lesson_index,
                    subject_config.name.to_string(),
                    group_idx,
                    student_count,
                    Some(teacher_idx),
                    LESSON_DURATION_MINUTES,
                    subject_config.room_kind,
                ));
                lesson_index += 1;
            }
        }
    }

    lessons
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_build_lessons() {
        let lessons = build_lessons(12);
        assert_eq!(lessons.len(), 300);
    }

    #[test]
    fn test_all_groups_have_all_subjects() {
        let lessons = build_lessons(12);
        let subject_names: Vec<&str> = subjects().iter().map(|subject| subject.name).collect();

        // Check each group has lessons for all subjects
        for group_idx in 0..12 {
            let group_lessons: Vec<_> = lessons
                .iter()
                .filter(|l| l.group_idx == group_idx)
                .collect();

            for subject_name in &subject_names {
                let subject_lessons: Vec<_> = group_lessons
                    .iter()
                    .filter(|l| l.subject == *subject_name)
                    .collect();

                let expected_count = subjects()
                    .iter()
                    .find(|subject| subject.name == *subject_name)
                    .unwrap()
                    .hours_per_week;
                assert_eq!(
                    subject_lessons.len(),
                    expected_count,
                    "Group {} should have {} lessons for {}",
                    group_idx,
                    expected_count,
                    subject_name
                );
            }
        }
    }

    #[test]
    fn test_teacher_assignment() {
        let lessons = build_lessons(12);

        // The generated catalog has four English teachers, assigned round-robin.
        let english_lessons: Vec<_> = lessons.iter().filter(|l| l.subject == "English").collect();

        assert_eq!(english_lessons.len(), 48);

        // Check teacher assignment pattern for English
        for group_idx in 0..12 {
            let group_english: Vec<_> = english_lessons
                .iter()
                .filter(|l| l.group_idx == group_idx)
                .collect();

            let expected_teacher_idx = teacher_index(
                [
                    "Jane Austen",
                    "William Shakespeare",
                    "Chinua Achebe",
                    "Mary Shelley",
                ][group_idx % 4],
            );

            assert_eq!(group_english[0].teacher_idx, Some(expected_teacher_idx));
        }
    }

    #[test]
    fn test_lesson_duration() {
        let lessons = build_lessons(12);
        for lesson in &lessons {
            assert_eq!(lesson.duration, LESSON_DURATION_MINUTES);
        }
    }

    #[test]
    fn test_subjects_are_classic_uk_school() {
        let subject_names: Vec<&str> = subjects().iter().map(|subject| subject.name).collect();

        assert!(subject_names.contains(&"Mathematics"));
        assert!(subject_names.contains(&"Physics"));
        assert!(subject_names.contains(&"Chemistry"));
        assert!(subject_names.contains(&"Biology"));
        assert!(subject_names.contains(&"Computer Science"));
        assert!(subject_names.contains(&"English"));
        assert!(subject_names.contains(&"History"));
        assert!(subject_names.contains(&"Geography"));
        assert!(subject_names.contains(&"French"));
        assert!(subject_names.contains(&"German"));
        assert_eq!(subject_names.len(), 10);
    }

    #[test]
    fn test_total_lessons_per_group() {
        let lessons = build_lessons(12);
        // Each group should have 25 lessons (sum of hours_per_week)
        for group_idx in 0..12 {
            let group_lessons: Vec<_> = lessons
                .iter()
                .filter(|l| l.group_idx == group_idx)
                .collect();
            assert_eq!(group_lessons.len(), 25);
        }
    }

    #[test]
    fn test_all_lessons_have_teacher() {
        let lessons = build_lessons(12);
        for lesson in &lessons {
            assert!(
                lesson.teacher_idx.is_some(),
                "Lesson {} has no teacher",
                lesson.id
            );
        }
    }
}