| from fastapi import APIRouter, HTTPException, Query, Depends |
| from typing import List, Optional |
| from ...models.schemas import CourseProgressUpdate |
| from ...core.cache import api_cache |
| from ...core.database import db_manager |
| from scraper.courses import courses_scraper |
| import logging |
|
|
| router = APIRouter(prefix="/courses", tags=["courses"]) |
| logger = logging.getLogger("api.courses") |
|
|
| @router.get("/latest") |
| async def get_latest_courses(page: int = 1): |
| cache_key = f"courses_latest_{page}" |
| cached = await api_cache.get(cache_key) |
| if cached: return cached |
| |
| try: |
| items = await courses_scraper.fetch_latest_courses(page=page) |
| if items: await api_cache.set(cache_key, items) |
| return items |
| except Exception as e: |
| logger.error(f"Error fetching latest courses: {e}") |
| raise HTTPException(status_code=500, detail="Failed to fetch latest courses") |
|
|
| @router.get("/category/{cat_id}") |
| async def get_category_courses(cat_id: str, page: int = 1): |
| cache_key = f"courses_cat_{cat_id}_{page}" |
| cached = await api_cache.get(cache_key) |
| if cached: return cached |
| |
| try: |
| items = await courses_scraper.fetch_category_courses(cat_id, page=page) |
| if items: await api_cache.set(cache_key, items) |
| return items |
| except Exception as e: |
| logger.error(f"Error fetching course category {cat_id}: {e}") |
| raise HTTPException(status_code=500, detail=f"Failed to fetch course category {cat_id}") |
|
|
| @router.get("/details/{safe_id}") |
| async def get_course_details(safe_id: str, user_id: Optional[str] = None): |
| cache_key = f"course_details_{safe_id}" |
| details = await api_cache.get(cache_key) |
| |
| if not details: |
| try: |
| details = await courses_scraper.fetch_course_details(safe_id) |
| if not details: |
| raise HTTPException(status_code=404, detail="Course not found") |
| await api_cache.set(cache_key, details, ttl_seconds=86400) |
| except HTTPException: raise |
| except Exception as e: |
| logger.error(f"Error fetching course details for {safe_id}: {e}") |
| raise HTTPException(status_code=500, detail="Failed to fetch course details") |
|
|
| if user_id and details: |
| async with db_manager.get_connection() as db: |
| cursor = await db.execute( |
| "SELECT lesson_id, completed FROM course_progress WHERE user_id = ? AND course_id = ?", |
| (user_id, safe_id) |
| ) |
| rows = await cursor.fetchall() |
| completed_map = {row['lesson_id']: row['completed'] for row in rows} |
| |
| total_completed = 0 |
| for lesson in details.get('lessons', []): |
| lesson['completed'] = completed_map.get(lesson['id'], 0) == 1 |
| if lesson['completed']: total_completed += 1 |
| |
| total_lessons = len(details.get('lessons', [])) |
| details['progress_percentage'] = (total_completed / total_lessons * 100) if total_lessons > 0 else 0 |
| details['completed_count'] = total_completed |
|
|
| return details |
|
|
| @router.post("/progress") |
| async def update_course_progress(data: CourseProgressUpdate): |
| async with db_manager.get_connection() as db: |
| await db.execute(""" |
| INSERT INTO course_progress (user_id, course_id, lesson_id, completed) |
| VALUES (?, ?, ?, ?) |
| ON CONFLICT(user_id, course_id, lesson_id) |
| DO UPDATE SET completed = excluded.completed, updated_at = CURRENT_TIMESTAMP |
| """, (data.user_id, data.course_id, data.lesson_id, data.completed)) |
| |
| if data.completed == 1: |
| await db.execute("UPDATE users SET points = points + 5 WHERE id = ?", (data.user_id,)) |
| |
| await db.commit() |
| return {"status": "success"} |
|
|
| @router.get("/lesson/{lesson_id}") |
| async def get_lesson_video(lesson_id: str): |
| cache_key = f"lesson_video_{lesson_id}" |
| cached = await api_cache.get(cache_key) |
| if cached: return {"video_url": cached} |
| |
| try: |
| video_url = await courses_scraper.fetch_lesson_video(lesson_id) |
| if video_url: |
| await api_cache.set(cache_key, video_url) |
| return {"video_url": video_url} |
| raise HTTPException(status_code=404, detail="Video not found") |
| except Exception as e: |
| logger.error(f"Error fetching lesson video {lesson_id}: {e}") |
| raise HTTPException(status_code=500, detail="Failed to fetch lesson video") |
|
|