diff --git a/.gitignore b/.gitignore index c6aa9b8..5061802 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,6 @@ Thumbs.db venv/ env/ .venv/ - +TutorAI/lib/ +TutorAI/pyvenv.cfg +TutorAI/bin/ \ No newline at end of file diff --git a/app/api/v1/config.json b/app/api/v1/config.json new file mode 100644 index 0000000..9d2825d --- /dev/null +++ b/app/api/v1/config.json @@ -0,0 +1,30 @@ +{ + "programs": { + "AI-BOOTCAMP": { + "default_frequency": "weekly", + "groups": { + "AI-101": { + "frequency": "biweekly", + "start_date": "2025-10-28" + }, + "AI-102": { + "frequency": "weekly", + "start_date": "2025-10-21" + } + } + }, + "ML-BASICS": { + "default_frequency": "weekly", + "groups": { + "ML-101": { + "frequency": "biweekly", + "start_date": "2025-10-17" + }, + "ML-102": { + "frequency": "weekly", + "start_date": "2025-11-01" + } + } + } + } +} \ No newline at end of file diff --git a/app/api/v1/facts.py b/app/api/v1/facts.py new file mode 100644 index 0000000..0695226 --- /dev/null +++ b/app/api/v1/facts.py @@ -0,0 +1,54 @@ +from fastapi import APIRouter, Depends +from app.core.database import get_db +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select +from app.models.education import Assignment +from datetime import datetime, timedelta, timezone + +import json +from pathlib import Path + +MOSCOW_TZ = timezone(timedelta(hours=3)) + +def get_days_ago_start(days_ago: int): + now_msk = datetime.now(MOSCOW_TZ) + target_date = now_msk - timedelta(days=days_ago) + target_date = target_date.replace(hour=0, minute=0, second=0, microsecond=0) + return target_date + +# Получение фактов недели на студента +router = APIRouter(prefix="/students", tags=["students"]) + +@router.get("/facts") +async def get_weekly_facts(student_id: str = None, period: int = 7, db: AsyncSession = Depends(get_db)): + # period_start = get_days_ago_start(period) + # query = select(Assignment).where(Assignment.submitted_at >= period_start) + + # if student_id: + # query = query.where(Assignment.student_id == student_id) + + # result = await db.execute(query) + # facts = result.scalars.all() + + # return [{ + # "id": a.student_id, + # "grade": a.grade, + # "submitted_at": a.submitted_at + # } + # for a in facts] + + mock_path = Path("app/api/v1/mock.json") + mock_data = json.loads(mock_path.read_text()) + + student = next((s for s in mock_data["students"] if s["student_id"] == student_id), None) + if not student: + return {"error": f"Student with id {student_id} not found"} + + return [ + { + "id": student["student_id"], + "grade": a["grade"], + "submitted_at": a["submitted_at"] + } + for a in student["assignments"] + ] \ No newline at end of file diff --git a/app/api/v1/get_config.py b/app/api/v1/get_config.py new file mode 100644 index 0000000..007f5cf --- /dev/null +++ b/app/api/v1/get_config.py @@ -0,0 +1,38 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session +from app.core.database import get_db +from app.models.education import Stream +import json +from pathlib import Path + +router = APIRouter(prefix="/config", tags=["config"]) + +@router.get("/") +async def get_group_config(program_id: str = None, group_id: str = None, db: Session = Depends(get_db)): + config_path = Path("app/api/v1/config.json") + config = json.loads(config_path.read_text()) + + # Загружаем базовую структуру + if not program_id: + return config + + program = config["programs"].get(program_id) + if not program: + return {"error": "Program not found"} + + if not group_id: + return program + + group = program.get("groups", {}).get(group_id) + if not group: + return {"error": "Group not found"} + + # Получаем дату начала из базы + stream = db.query(Stream).filter(Stream.name == group_id).first() + start_date = stream.start_date if stream else None + + final = program.copy() + if group: + final.update(group) + final["start_date"] = str(start_date) if start_date else None + return final diff --git a/app/api/v1/listeners.py b/app/api/v1/listeners.py new file mode 100644 index 0000000..6e48651 --- /dev/null +++ b/app/api/v1/listeners.py @@ -0,0 +1,63 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session +from app.core.database import get_db +from app.models.education import Stream + +router = APIRouter(prefix="/students", tags=["students"]) + +@router.get("/") +async def get_students(group_id: str, db: Session = Depends(get_db)): + """ + Возвращает список студентов по названию группы (stream.stream_id = group_id) + """ + + if group_id == "AI-102": + return [ + { + "student_id": "st_001", + "group_id": group_id + }, + { + "student_id": "st_002", + "group_id": group_id + }, + { + "student_id": "st_003", + "group_id": group_id + }, + { + "student_id": "st_004", + "group_id": group_id + }, + { + "student_id": "st_005", + "group_id": group_id + } +] + if group_id == "ML-102": + return [ + { + "student_id": "st_101", + "group_id": group_id + }, + { + "student_id": "st_102", + "group_id": group_id + }, + { + "student_id": "st_103", + "group_id": group_id + }, + { + "student_id": "st_104", + "group_id": group_id + }, + { + "student_id": "st_105", + "group_id": group_id + } +] + # Ищем поток по имени (названию группы) + # stream = db.query(Stream).filter(Stream.stream_id == group_id).first() + + # return [{"student_id": s.student_id} for s in stream.students] diff --git a/app/api/v1/mock.json b/app/api/v1/mock.json new file mode 100644 index 0000000..ccb6255 --- /dev/null +++ b/app/api/v1/mock.json @@ -0,0 +1,174 @@ +{ + "students": [ + { + "student_id": "st_101", + "name": "Alice Johnson", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_001", + "grade": 88, + "submitted_at": "2025-11-03T14:32:00Z" + }, + { + "id": "as_002", + "grade": 95, + "submitted_at": "2025-11-07T10:18:00Z" + } + ] + }, + { + "student_id": "st_102", + "name": "Michael Smith", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_010", + "grade": 76, + "submitted_at": "2025-11-04T09:40:00Z" + }, + { + "id": "as_011", + "grade": 82, + "submitted_at": "2025-11-06T15:10:00Z" + }, + { + "id": "as_012", + "grade": 79, + "submitted_at": "2025-11-08T11:55:00Z" + } + ] + }, + { + "student_id": "st_103", + "name": "Sofia Martinez", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_020", + "grade": 90, + "submitted_at": "2025-11-02T13:22:00Z" + }, + { + "id": "as_021", + "grade": 93, + "submitted_at": "2025-11-05T08:40:00Z" + } + ] + }, + { + "student_id": "st_104", + "name": "David Lee", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_030", + "grade": 100, + "submitted_at": "2025-11-01T16:05:00Z" + }, + { + "id": "as_031", + "grade": 97, + "submitted_at": "2025-11-06T19:30:00Z" + } + ] + }, + { + "student_id": "st_105", + "name": "Emma Brown", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_040", + "grade": 65, + "submitted_at": "2025-11-03T12:00:00Z" + } + ] + }, + { + "student_id": "st_001", + "name": "Alice Johnson", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_001", + "grade": 88, + "submitted_at": "2025-11-03T14:32:00Z" + }, + { + "id": "as_002", + "grade": 95, + "submitted_at": "2025-11-07T10:18:00Z" + } + ] + }, + { + "student_id": "st_002", + "name": "Michael Smith", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_010", + "grade": 76, + "submitted_at": "2025-11-04T09:40:00Z" + }, + { + "id": "as_011", + "grade": 82, + "submitted_at": "2025-11-06T15:10:00Z" + }, + { + "id": "as_012", + "grade": 79, + "submitted_at": "2025-11-08T11:55:00Z" + } + ] + }, + { + "student_id": "st_003", + "name": "Sofia Martinez", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_020", + "grade": 90, + "submitted_at": "2025-11-02T13:22:00Z" + }, + { + "id": "as_021", + "grade": 93, + "submitted_at": "2025-11-05T08:40:00Z" + } + ] + }, + { + "student_id": "st_004", + "name": "David Lee", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_030", + "grade": 100, + "submitted_at": "2025-11-01T16:05:00Z" + }, + { + "id": "as_031", + "grade": 97, + "submitted_at": "2025-11-06T19:30:00Z" + } + ] + }, + { + "student_id": "st_005", + "name": "Emma Brown", + "group_id": "group_2025_B", + "assignments": [ + { + "id": "as_040", + "grade": 65, + "submitted_at": "2025-11-03T12:00:00Z" + } + ] + } + ] +} diff --git a/app/api/v1/students.py b/app/api/v1/students.py index c1c91d5..f557e1c 100644 --- a/app/api/v1/students.py +++ b/app/api/v1/students.py @@ -4,4 +4,4 @@ @router.get("/") async def get_students(): - return {"message": "Students API - coming soon"} + return {"message": "Students API - coming soon"} \ No newline at end of file diff --git a/app/main.py b/app/main.py index a504962..05214e0 100644 --- a/app/main.py +++ b/app/main.py @@ -1,6 +1,7 @@ from fastapi import FastAPI from fastapi.responses import RedirectResponse from app.admin.views import setup_admin +from app.api.v1 import get_config, facts, listeners app = FastAPI( title="AI Tutor API", @@ -18,4 +19,9 @@ def root(): @app.get("/health") def health_check(): - return {"status": "ok"} \ No newline at end of file + return {"status": "ok"} + +# Подключение API маршрутов +app.include_router(facts.router) +app.include_router(get_config.router) +app.include_router(listeners.router) \ No newline at end of file