Added a back to main clicking on the link, added help page, added a last 5 viewed

This commit is contained in:
2026-08-20 18:57:27 -04:00
parent 66dff7e69f
commit 5b8bef1451
5 changed files with 306 additions and 27 deletions
+97 -2
View File
@@ -439,6 +439,66 @@ def get_library_root() -> str:
return LIBRARY_PATH
RECENT_VIEWS_FILE = os.path.join(DATA_DIR, 'recent_views.json')
MAX_RECENT_VIEWS = 5
def record_recent_view(course_name: str, course_path: str, lesson_path: str, lesson_title: str) -> None:
"""
Record a lesson view for the cross-course 'Recently Viewed' list on the
dashboard, so jumping back to where you left off doesn't require
re-browsing the library - even for a different course than whichever
one happens to be loaded right now.
"""
entries = get_recent_views()
# Drop any existing entry for this exact lesson so it moves to the
# front instead of appearing twice.
entries = [
e for e in entries
if not (e.get('course_path') == course_path and e.get('lesson_path') == lesson_path)
]
entries.insert(0, {
'course_name': course_name,
'course_path': course_path,
'lesson_path': lesson_path,
'lesson_title': lesson_title,
'viewed_at': datetime.now().isoformat()
})
entries = entries[:MAX_RECENT_VIEWS]
try:
os.makedirs(DATA_DIR, exist_ok=True)
with open(RECENT_VIEWS_FILE, 'w') as f:
json.dump(entries, f, indent=2)
except OSError as e:
print(f"Could not save recent views: {e}")
def get_recent_views() -> List[Dict[str, Any]]:
"""Load the persisted 'Recently Viewed' list, newest first."""
try:
if os.path.exists(RECENT_VIEWS_FILE):
with open(RECENT_VIEWS_FILE, 'r') as f:
return json.load(f)
except (json.JSONDecodeError, OSError) as e:
print(f"Could not load recent views: {e}")
return []
def get_recent_views_for_display() -> List[Dict[str, Any]]:
"""Recent views with a human-friendly timestamp added for the template."""
entries = get_recent_views()
for entry in entries:
try:
dt = datetime.fromisoformat(entry['viewed_at'])
entry['viewed_display'] = dt.strftime('%b %d, %I:%M %p').replace(' 0', ' ')
except (KeyError, ValueError):
entry['viewed_display'] = ''
return entries
class ProgressTracker:
"""Handles progress tracking and persistence"""
@@ -527,7 +587,8 @@ def index():
# Show dashboard with course selection option
return render_template('course_dashboard.html',
course=None,
stats={'total_lessons': 0, 'completed_lessons': 0, 'completion_percentage': 0})
stats={'total_lessons': 0, 'completed_lessons': 0, 'completion_percentage': 0},
recent_views=get_recent_views_for_display())
# Apply progress data to tree
ProgressTracker.apply_progress_to_tree(current_course)
@@ -535,7 +596,8 @@ def index():
return render_template('course_dashboard.html',
course=current_course,
stats=stats)
stats=stats,
recent_views=get_recent_views_for_display())
@app.route('/browse')
@@ -724,6 +786,36 @@ def load_course():
return jsonify({'error': str(e)}), 500
@app.route('/recent/open')
def open_recent():
"""
Jump straight to a "Recently Viewed" entry from the dashboard: load its
course if it isn't already the active one, then go to that lesson.
"""
global current_course
course_path = request.args.get('course_path', '')
lesson_path = request.args.get('lesson_path', '')
if not course_path or not lesson_path or not os.path.exists(course_path):
return redirect(url_for('index'))
try:
if not current_course or current_course.path != course_path:
current_course = DynamicCourseParser.scan_directory(course_path)
except Exception as e:
print(f"Could not load course for recent view: {e}")
return redirect(url_for('index'))
return redirect(url_for('view_lesson', lesson_path=lesson_path))
@app.route('/help')
def help_page():
"""Static help page: how to use the app, supported file types."""
return render_template('help.html')
@app.route('/lesson/<path:lesson_path>')
def view_lesson(lesson_path: str):
"""View specific lesson by path"""
@@ -761,6 +853,9 @@ def view_lesson(lesson_path: str):
# Update last accessed
ProgressTracker.update_lesson_progress(current_course, lesson_path)
# Record for the cross-course "Recently Viewed" list on the dashboard
record_recent_view(current_course.name, current_course.path, lesson_path, lesson.title)
return render_template('lesson_view.html',
course=current_course,
lesson=lesson,