-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcourse_manager.py
More file actions
142 lines (110 loc) · 5.02 KB
/
course_manager.py
File metadata and controls
142 lines (110 loc) · 5.02 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
import os
import yaml
from pathlib import Path
import re
class CourseManager:
def __init__(self, repo_path):
self.repo_path = Path(repo_path)
self.courses_path = self.repo_path / 'courses'
def get_course_list(self):
"""Get list of all available courses"""
if not self.courses_path.exists():
return []
courses = []
for course_dir in self.courses_path.iterdir():
if course_dir.is_dir() and not course_dir.name.startswith('.'):
course_yml = course_dir / 'course.yml'
if course_yml.exists():
courses.append(course_dir.name)
return sorted(courses)
def get_course_info(self, course_id):
"""Extract course title from en.md and UUID from course.yml"""
course_yml_path = self.courses_path / course_id / 'course.yml'
en_md_path = self.courses_path / course_id / 'en.md'
if not course_yml_path.exists():
raise FileNotFoundError(f"Course file not found: {course_yml_path}")
if not en_md_path.exists():
raise FileNotFoundError(f"English markdown file not found: {en_md_path}")
# Get UUID from course.yml
with open(course_yml_path, 'r', encoding='utf-8') as f:
course_data = yaml.safe_load(f)
# The id field in course.yml is the UUID
uuid = course_data.get('id', '')
if not uuid:
raise ValueError(f"Missing id (UUID) in course.yml for {course_id}")
# Get title from en.md header
with open(en_md_path, 'r', encoding='utf-8') as f:
content = f.read()
# Extract title from the first H1 header
title_match = re.match(r'^#+\s+(.+)$', content.strip(), re.MULTILINE)
if title_match:
title = title_match.group(1).strip()
else:
# Fallback to name from course.yml if no header found
title = course_data.get('name', course_id)
# Generate title slug for URL
title_slug = title.lower()
title_slug = re.sub(r'[^\w\s-]', '', title_slug)
title_slug = re.sub(r'[-\s]+', '-', title_slug)
title_slug = title_slug.strip('-')
return {
'id': course_id,
'uuid': uuid,
'title': title,
'title_slug': title_slug
}
def build_pbn_url(self, course_title, uuid, lang='en'):
"""Build PlanB Network URL"""
# Clean and format the title
clean_title = course_title.lower()
# Replace special characters and spaces with hyphens
clean_title = re.sub(r'[^\w\s-]', '', clean_title)
clean_title = re.sub(r'[-\s]+', '-', clean_title)
clean_title = clean_title.strip('-')
return f"https://planb.academy/{lang}/courses/{clean_title}-{uuid}"
def build_github_urls(self, course_id, lang, branch='dev'):
"""Build GitHub URLs (EN + selected language if different)"""
base_url = f"https://github.com/PlanB-Network/bitcoin-educational-content/blob/{branch}/courses/{course_id}"
urls = []
# Always include EN version
en_url = f"{base_url}/en.md"
urls.append(('en', en_url))
# Add selected language if not EN
if lang != 'en':
lang_url = f"{base_url}/{lang}.md"
urls.append((lang, lang_url))
return urls
def check_language_file_exists(self, course_id, lang):
"""Check if a language file exists for the course"""
lang_file = self.courses_path / course_id / f"{lang}.md"
return lang_file.exists()
def get_course_size(self, course_id, lang='en'):
"""Estimate course size based on content"""
lang_file = self.courses_path / course_id / f"{lang}.md"
if not lang_file.exists():
# Try English as fallback
lang_file = self.courses_path / course_id / "en.md"
if not lang_file.exists():
return "medium" # Default size
with open(lang_file, 'r', encoding='utf-8') as f:
content = f.read()
# Estimate based on character count
char_count = len(content)
if char_count < 10000:
return "small"
elif char_count < 50000:
return "medium"
else:
return "large"
def validate_course_structure(self, course_id):
"""Validate that course has proper structure"""
course_path = self.courses_path / course_id
if not course_path.exists():
return False, "Course directory does not exist"
course_yml = course_path / 'course.yml'
if not course_yml.exists():
return False, "course.yml file not found"
en_md = course_path / 'en.md'
if not en_md.exists():
return False, "English markdown file (en.md) not found"
return True, "Course structure is valid"