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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
# learning_tracker.py - Track progress through OSSU curriculum
import json
import datetime
from dataclasses import dataclass, asdict
from typing import List, Dict, Optional
from enum import Enum
class CourseStatus(Enum):
NOT_STARTED = "not_started"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
AUDITED = "audited" # Watched but didn't complete assignments
@dataclass
class Course:
name: str
provider: str
url: str
estimated_hours: int
prerequisites: List[str]
status: CourseStatus = CourseStatus.NOT_STARTED
start_date: Optional[datetime.date] = None
completion_date: Optional[datetime.date] = None
notes: str = ""
@dataclass
class Subject:
name: str
courses: List[Course]
def completion_percentage(self) -> float:
if not self.courses:
return 0.0
completed = sum(1 for course in self.courses
if course.status == CourseStatus.COMPLETED)
return (completed / len(self.courses)) * 100
class OSLearningTracker:
"""Track progress through self-directed learning curriculum"""
def __init__(self, data_file: str = "learning_progress.json"):
self.data_file = data_file
self.subjects = self.load_curriculum()
def load_curriculum(self) -> Dict[str, Subject]:
"""Load or create curriculum structure"""
try:
with open(self.data_file, 'r') as f:
data = json.load(f)
return self.deserialize_subjects(data)
except FileNotFoundError:
return self.create_default_curriculum()
def create_default_curriculum(self) -> Dict[str, Subject]:
"""Create the default OSSU curriculum structure"""
curriculum = {
"intro_cs": Subject("Introduction to CS", [
Course("Python for Everybody", "University of Michigan",
"https://www.coursera.org/specializations/python",
120, []),
Course("Introduction to CS", "MIT",
"https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/",
150, ["Python for Everybody"])
]),
"core_programming": Subject("Core Programming", [
Course("How to Code: Simple Data", "UBC",
"https://www.edx.org/course/how-to-code-simple-data",
60, ["Introduction to CS"]),
Course("How to Code: Complex Data", "UBC",
"https://www.edx.org/course/how-to-code-complex-data",
60, ["How to Code: Simple Data"]),
Course("Programming Languages A", "University of Washington",
"https://www.coursera.org/learn/programming-languages",
60, ["How to Code: Complex Data"])
]),
"core_math": Subject("Core Mathematics", [
Course("Calculus 1A", "MIT",
"https://www.edx.org/course/calculus-1a-differentiation",
80, []),
Course("Mathematics for Computer Science", "MIT",
"https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-mathematics-for-computer-science-spring-2015/",
120, ["Calculus 1A"])
]),
"core_systems": Subject("Core Systems", [
Course("Build a Modern Computer", "Hebrew University",
"https://www.coursera.org/learn/build-a-computer",
60, ["Core Programming"]),
Course("Computer Systems", "Carnegie Mellon",
"https://www.coursera.org/learn/introduction-computer-systems",
80, ["Build a Modern Computer"])
]),
"core_theory": Subject("Core Theory", [
Course("Algorithms Specialization", "Stanford",
"https://www.coursera.org/specializations/algorithms",
160, ["Mathematics for Computer Science"]),
Course("Data Structures", "UC San Diego",
"https://www.coursera.org/specializations/data-structures-algorithms",
120, ["Algorithms Specialization"])
])
}
return curriculum
def start_course(self, subject_name: str, course_name: str):
"""Mark a course as started"""
subject = self.subjects.get(subject_name)
if not subject:
print(f"Subject '{subject_name}' not found")
return
course = next((c for c in subject.courses if c.name == course_name), None)
if not course:
print(f"Course '{course_name}' not found in {subject_name}")
return
# Check prerequisites
if not self.check_prerequisites(course):
print(f"Prerequisites not met for '{course_name}'")
return
course.status = CourseStatus.IN_PROGRESS
course.start_date = datetime.date.today()
print(f"Started '{course_name}' in {subject_name}")
self.save_progress()
def complete_course(self, subject_name: str, course_name: str, notes: str = ""):
"""Mark a course as completed"""
subject = self.subjects.get(subject_name)
if not subject:
return
course = next((c for c in subject.courses if c.name == course_name), None)
if not course:
return
course.status = CourseStatus.COMPLETED
course.completion_date = datetime.date.today()
course.notes = notes
print(f"Completed '{course_name}' in {subject_name}!")
self.save_progress()
# Check for newly available courses
self.suggest_next_courses()
def check_prerequisites(self, course: Course) -> bool:
"""Check if all prerequisites are completed"""
for prereq_name in course.prerequisites:
prereq_completed = False
for subject in self.subjects.values():
for c in subject.courses:
if c.name == prereq_name and c.status == CourseStatus.COMPLETED:
prereq_completed = True
break
if prereq_completed:
break
if not prereq_completed:
return False
return True
def suggest_next_courses(self):
"""Suggest courses that can be started now"""
available_courses = []
for subject_name, subject in self.subjects.items():
for course in subject.courses:
if (course.status == CourseStatus.NOT_STARTED and
self.check_prerequisites(course)):
available_courses.append((subject_name, course))
if available_courses:
print("\nAvailable courses you can start:")
for subject_name, course in available_courses:
print(f" • {course.name} ({subject_name}) - {course.estimated_hours}h")
def progress_report(self):
"""Generate comprehensive progress report"""
print("=== Learning Progress Report ===\n")
total_courses = 0
completed_courses = 0
in_progress_courses = 0
total_hours = 0
completed_hours = 0
for subject_name, subject in self.subjects.items():
subject_completed = sum(1 for c in subject.courses
if c.status == CourseStatus.COMPLETED)
subject_in_progress = sum(1 for c in subject.courses
if c.status == CourseStatus.IN_PROGRESS)
subject_total = len(subject.courses)
print(f"📚 {subject.name}")
print(f" Progress: {subject_completed}/{subject_total} courses")
print(f" Percentage: {subject.completion_percentage():.1f}%")
if subject_in_progress > 0:
print(f" In Progress: {subject_in_progress} courses")
# Show individual course status
for course in subject.courses:
status_icon = {
CourseStatus.NOT_STARTED: "⭕",
CourseStatus.IN_PROGRESS: "🔄",
CourseStatus.COMPLETED: "✅",
CourseStatus.AUDITED: "👁️"
}[course.status]
print(f" {status_icon} {course.name} ({course.estimated_hours}h)")
if course.completion_date:
print(f" Completed: {course.completion_date}")
if course.notes:
print(f" Notes: {course.notes}")
print()
# Update totals
total_courses += subject_total
completed_courses += subject_completed
in_progress_courses += subject_in_progress
for course in subject.courses:
total_hours += course.estimated_hours
if course.status == CourseStatus.COMPLETED:
completed_hours += course.estimated_hours
# Overall statistics
overall_percentage = (completed_courses / total_courses) * 100 if total_courses > 0 else 0
print("📊 Overall Statistics:")
print(f" Courses: {completed_courses}/{total_courses} ({overall_percentage:.1f}%)")
print(f" Hours: {completed_hours}/{total_hours} ({(completed_hours/total_hours)*100:.1f}%)")
print(f" In Progress: {in_progress_courses} courses")
# Time projections
if in_progress_courses > 0:
remaining_hours = total_hours - completed_hours
print(f" Estimated Remaining: {remaining_hours} hours")
# Estimate completion time based on study rate
hours_per_week = 10 # Configurable
weeks_remaining = remaining_hours / hours_per_week
completion_date = datetime.date.today() + datetime.timedelta(weeks=weeks_remaining)
print(f" Projected Completion: {completion_date} (at {hours_per_week}h/week)")
def save_progress(self):
"""Save progress to JSON file"""
data = {}
for name, subject in self.subjects.items():
data[name] = {
'name': subject.name,
'courses': [self.serialize_course(course) for course in subject.courses]
}
with open(self.data_file, 'w') as f:
json.dump(data, f, indent=2, default=str)
def serialize_course(self, course: Course) -> dict:
"""Convert course to serializable dictionary"""
return {
'name': course.name,
'provider': course.provider,
'url': course.url,
'estimated_hours': course.estimated_hours,
'prerequisites': course.prerequisites,
'status': course.status.value,
'start_date': course.start_date.isoformat() if course.start_date else None,
'completion_date': course.completion_date.isoformat() if course.completion_date else None,
'notes': course.notes
}
def deserialize_subjects(self, data: dict) -> Dict[str, Subject]:
"""Convert dictionary data back to Subject objects"""
subjects = {}
for key, subject_data in data.items():
courses = []
for course_data in subject_data['courses']:
course = Course(
name=course_data['name'],
provider=course_data['provider'],
url=course_data['url'],
estimated_hours=course_data['estimated_hours'],
prerequisites=course_data['prerequisites'],
status=CourseStatus(course_data['status']),
start_date=datetime.date.fromisoformat(course_data['start_date']) if course_data['start_date'] else None,
completion_date=datetime.date.fromisoformat(course_data['completion_date']) if course_data['completion_date'] else None,
notes=course_data['notes']
)
courses.append(course)
subjects[key] = Subject(subject_data['name'], courses)
return subjects
# Command-line interface
def main():
import argparse
parser = argparse.ArgumentParser(description="Track learning progress through OSSU curriculum")
parser.add_argument("--start", nargs=2, metavar=("SUBJECT", "COURSE"),
help="Start a course")
parser.add_argument("--complete", nargs=2, metavar=("SUBJECT", "COURSE"),
help="Complete a course")
parser.add_argument("--notes", help="Add notes when completing a course")
parser.add_argument("--report", action="store_true", help="Show progress report")
parser.add_argument("--suggest", action="store_true", help="Suggest next courses")
args = parser.parse_args()
tracker = OSLearningTracker()
if args.start:
tracker.start_course(args.start[0], args.start[1])
elif args.complete:
notes = args.notes or ""
tracker.complete_course(args.complete[0], args.complete[1], notes)
elif args.suggest:
tracker.suggest_next_courses()
else:
tracker.progress_report()
if __name__ == "__main__":
main()
|