Python 64.6%
TypeScript 33.7%
CSS 0.8%
1"""Course records (seeded), professor settings, announcements."""23from __future__ import annotations45from typing import Any67from sqlalchemy import select89from app.core.config import get_settings10from app.db import SessionLocal11from app.models import Course1213SEED = {14 "IMM1003": {15 "title": "Éléments d'évaluation immobilière",16 "site": "https://www.uqo-imm1003.app",17 "modules": [18 "Introduction à l'évaluation immobilière", "Le cadre professionnel au Québec",19 "Les principes fondamentaux de l'évaluation", "Le marché immobilier québécois",20 "Collecte et analyse des données", "La valeur : concepts et types",21 "Examen mi-session : révision des blocs 1 et 2",22 "La méthode de comparaison — Partie 1", "La méthode de comparaison — Partie 2",23 "La méthode du coût", "La méthode du revenu — Partie 1",24 "La méthode du revenu — Partie 2", "Le rapport d'évaluation",25 "Révision générale et cas intégrateur",26 ],27 "suggestions": [28 "Explique-moi les 8 étapes du processus d'évaluation",29 "Différence entre valeur marchande et valeur d'usage ?",30 "Quiz sur les principes de la valeur",31 "Comment ajuster des comparables vers le sujet ?",32 "Crée un Excel de grille de comparables ajustés",33 ],34 },35 "IMM1033": {36 "title": "Méthodes du coût en évaluation immobilière",37 "site": "https://www.uqo-imm1033.app",38 "modules": [39 "Introduction à la méthode du coût", "Cadre conceptuel et terminologie",40 "Méthodes d'évaluation du terrain", "Analyse du terrain et ajustements",41 "Coût de reproduction et coût de remplacement", "Méthodes d'estimation des coûts",42 "Coûts directs de construction", "Coûts indirects et profit entrepreneurial",43 "Concepts de dépréciation", "Dépréciation physique", "Dépréciation fonctionnelle",44 "Dépréciation économique (externe)", "Applications spécialisées",45 "Synthèse et révision",46 ],47 "suggestions": [48 "Calcule la dépréciation par la méthode âge-vie",49 "Crée un Excel de la méthode du coût pour un plex",50 "Coût de reproduction vs remplacement ?",51 "Quiz sur les types de dépréciation",52 "Comment estimer la valeur d'un terrain par extraction ?",53 ],54 },55}565758async def seed_courses() -> None:59 s = get_settings()60 async with SessionLocal() as session:61 for code in s.courses:62 existing = await session.scalar(select(Course).where(Course.code == code))63 meta = SEED.get(code, {"title": code, "modules": [], "suggestions": []})64 if existing is None:65 session.add(Course(code=code, title=meta["title"], term=s.TERM_LABEL,66 syllabus={"modules": meta["modules"],67 "suggestions": meta["suggestions"],68 "site": meta.get("site", ""), "deadlines": []},69 settings={"tools_enabled": None, "model_primary": None}))70 await session.commit()717273async def list_courses(include_private: bool = False) -> list[dict[str, Any]]:74 async with SessionLocal() as session:75 rows = (await session.execute(select(Course).where(Course.active.is_(True))76 .order_by(Course.code))).scalars()77 out = []78 for c in rows:79 d: dict[str, Any] = {"code": c.code, "title": c.title, "term": c.term,80 "modules": c.syllabus.get("modules", []),81 "suggestions": c.syllabus.get("suggestions", []),82 "site": c.syllabus.get("site", ""),83 "deadlines": c.syllabus.get("deadlines", []),84 "announcement": c.announcement}85 if include_private:86 d["extra_system_prompt"] = c.extra_system_prompt87 d["settings"] = c.settings88 out.append(d)89 return out909192async def get_course(code: str) -> Course | None:93 async with SessionLocal() as session:94 return await session.scalar(select(Course).where(Course.code == code.upper()))959697async def update_course(code: str, data: dict[str, Any]) -> dict[str, Any] | None:98 async with SessionLocal() as session:99 c = await session.scalar(select(Course).where(Course.code == code.upper()))100 if c is None:101 return None102 if "extra_system_prompt" in data:103 c.extra_system_prompt = str(data["extra_system_prompt"] or "")[:6000]104 if "announcement" in data:105 c.announcement = str(data["announcement"] or "")[:1000]106 if "deadlines" in data or "suggestions" in data:107 syl = dict(c.syllabus)108 if "deadlines" in data:109 syl["deadlines"] = list(data["deadlines"] or [])[:20]110 if "suggestions" in data:111 syl["suggestions"] = [str(x)[:120] for x in (data["suggestions"] or [])][:8]112 c.syllabus = syl113 if "settings" in data:114 st = dict(c.settings or {})115 st.update({k: v for k, v in (data["settings"] or {}).items() if k in116 {"tools_enabled", "model_primary", "budget_usd", "tone"}})117 c.settings = st118 await session.commit()119 return {"code": c.code, "extra_system_prompt": c.extra_system_prompt,120 "announcement": c.announcement, "syllabus": c.syllabus, "settings": c.settings}121