Added a back to main clicking on the link, added help page, added a last 5 viewed
This commit is contained in:
+97
-2
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user