Repository navigation
Expand file tree
/
Copy pathCourseStructure.cs
More file actions
272 lines (231 loc) · 7.96 KB
/
Copy pathCourseStructure.cs
File metadata and controls
272 lines (231 loc) · 7.96 KB
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
namespace Tutor.Core.Models;
/// <summary>
/// Status of the course structure generation process.
/// </summary>
public enum CourseStructureStatus
{
/// <summary>
/// Structure has not been generated yet.
/// </summary>
NotStarted,
/// <summary>
/// Currently generating lessons from the knowledge base.
/// </summary>
GeneratingLessons,
/// <summary>
/// Currently organizing topics within lessons.
/// </summary>
OrganizingTopics,
/// <summary>
/// Currently ordering concepts within topics.
/// </summary>
OrderingConcepts,
/// <summary>
/// Currently generating hierarchical sections within lessons.
/// </summary>
GeneratingSections,
/// <summary>
/// Currently generating content for sections.
/// </summary>
GeneratingContent,
/// <summary>
/// Structure is ready for use.
/// </summary>
Ready,
/// <summary>
/// Structure generation failed.
/// </summary>
Failed
}
/// <summary>
/// Represents a curated learning path for a Course.
///
/// Key architectural points:
/// - CourseStructure defines the hierarchical learning path: Lessons -> Topics -> Concepts
/// - It REFERENCES Concepts from the KnowledgeBase by ID (no duplication of data)
/// - A Course has one CourseStructure and one assigned KnowledgeBase
/// - The CourseStructure is generated FROM the KnowledgeBase but is a separate entity
/// - Multiple Courses can share the same KnowledgeBase but have different structures
///
/// The goal is for the student to eventually understand all Concepts in the KnowledgeBase,
/// but in an ordered and guided way defined by the CourseStructure.
/// </summary>
public class CourseStructure
{
/// <summary>
/// Unique identifier for this course structure.
/// </summary>
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// The name of this course structure.
/// </summary>
public string Name { get; set; } = "";
/// <summary>
/// A description of this learning path.
/// </summary>
public string Description { get; set; } = "";
/// <summary>
/// ID of the Course this structure belongs to.
/// </summary>
public string CourseId { get; set; } = "";
/// <summary>
/// ID of the KnowledgeBase this structure references.
/// All ConceptIds in this structure point to Concepts in this KnowledgeBase.
/// </summary>
public string KnowledgeBaseId { get; set; } = "";
/// <summary>
/// Current status of the structure generation.
/// </summary>
public CourseStructureStatus Status { get; set; } = CourseStructureStatus.NotStarted;
/// <summary>
/// Error message if status is Failed.
/// </summary>
public string? ErrorMessage { get; set; }
/// <summary>
/// Progress percentage (0-100) during generation.
/// </summary>
public int Progress { get; set; }
/// <summary>
/// The lessons in this course structure, in learning order.
/// </summary>
public List<Lesson> Lessons { get; set; } = [];
/// <summary>
/// Total estimated duration for the course in minutes.
/// </summary>
public int TotalEstimatedMinutes { get; set; }
/// <summary>
/// When this structure was created.
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// When this structure was last modified.
/// </summary>
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
/// <summary>
/// Version number for tracking changes.
/// </summary>
public int Version { get; set; } = 1;
/// <summary>
/// Gets the total number of lessons.
/// </summary>
public int TotalLessons => Lessons.Count;
/// <summary>
/// Gets the total number of topics across all lessons.
/// </summary>
public int TotalTopics => Lessons.Sum(l => l.Topics.Count);
/// <summary>
/// Gets the total number of concept references across all topics.
/// Note: This counts references, not unique concepts.
/// </summary>
public int TotalConceptReferences => Lessons.Sum(l => l.TotalConceptCount);
/// <summary>
/// Gets a Lesson by ID.
/// </summary>
public Lesson? GetLesson(string lessonId)
{
return Lessons.FirstOrDefault(l => l.Id == lessonId);
}
/// <summary>
/// Gets a LessonTopic by ID (searches all lessons).
/// </summary>
public LessonTopic? GetTopic(string topicId)
{
return Lessons
.SelectMany(l => l.Topics)
.FirstOrDefault(t => t.Id == topicId);
}
/// <summary>
/// Gets all Lessons in order.
/// </summary>
public IEnumerable<Lesson> GetLessonsInOrder()
{
return Lessons.OrderBy(l => l.Order);
}
/// <summary>
/// Gets all ConceptIds in the course structure in learning order.
/// </summary>
public IEnumerable<string> GetAllConceptIdsInOrder()
{
return GetLessonsInOrder()
.SelectMany(l => l.GetAllConceptIds());
}
/// <summary>
/// Finds the Lesson containing a specific ConceptId.
/// </summary>
public Lesson? FindLessonForConcept(string conceptId)
{
return Lessons.FirstOrDefault(l =>
l.Topics.Any(t => t.ConceptIds.Contains(conceptId)));
}
/// <summary>
/// Finds the LessonTopic containing a specific ConceptId.
/// </summary>
public LessonTopic? FindTopicForConcept(string conceptId)
{
return Lessons
.SelectMany(l => l.Topics)
.FirstOrDefault(t => t.ConceptIds.Contains(conceptId));
}
/// <summary>
/// Gets the next ConceptId in the learning order after the given concept.
/// </summary>
public string? GetNextConceptId(string conceptId)
{
var allIds = GetAllConceptIdsInOrder().ToList();
var currentIndex = allIds.IndexOf(conceptId);
if (currentIndex < 0 || currentIndex >= allIds.Count - 1)
return null;
return allIds[currentIndex + 1];
}
/// <summary>
/// Gets the previous ConceptId in the learning order before the given concept.
/// </summary>
public string? GetPreviousConceptId(string conceptId)
{
var allIds = GetAllConceptIdsInOrder().ToList();
var currentIndex = allIds.IndexOf(conceptId);
if (currentIndex <= 0)
return null;
return allIds[currentIndex - 1];
}
/// <summary>
/// Gets the position of a concept in the overall learning order (1-based).
/// </summary>
public int GetConceptPosition(string conceptId)
{
var allIds = GetAllConceptIdsInOrder().ToList();
var index = allIds.IndexOf(conceptId);
return index >= 0 ? index + 1 : 0;
}
/// <summary>
/// Gets the total number of sections across all lessons.
/// </summary>
public int TotalSections => Lessons.Sum(l => l.GetTotalSectionCount());
/// <summary>
/// Finds a section by ID across all lessons.
/// </summary>
public Section? FindSection(string sectionId)
{
foreach (var lesson in Lessons)
{
var section = lesson.FindSection(sectionId);
if (section != null)
return section;
}
return null;
}
/// <summary>
/// Gets all sections flattened in learning order.
/// </summary>
public IEnumerable<Section> GetAllSectionsFlattened()
{
return GetLessonsInOrder().SelectMany(l => l.GetAllSectionsFlattened());
}
/// <summary>
/// Finds the lesson containing a specific section.
/// </summary>
public Lesson? FindLessonForSection(string sectionId)
{
return Lessons.FirstOrDefault(l => l.FindSection(sectionId) != null);
}
}