diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf6f679 --- /dev/null +++ b/.gitignore @@ -0,0 +1,185 @@ +# ============================================================================== +# Created by https://gitignores.com/ +# COMPREHENSIVE FRAMEWORK TEMPLATE for Flask +# Website: https://flask.palletsprojects.com/ +# Repository: https://github.com/pallets/flask +# ============================================================================== + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# TEMPLATE OVERVIEW & USAGE NOTES +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# • TEMPLATE TYPE: COMPREHENSIVE FRAMEWORK TEMPLATE +# • PURPOSE: Complete Flask Python web framework patterns for web applications and development artifacts +# • DESIGN PHILOSOPHY: Self-contained with all Flask and Python-specific patterns +# • COMBINATION GUIDANCE: Use standalone; optionally add IDE/OS templates +# • SECURITY CONSIDERATIONS: Includes security patterns for .env files and credentials +# • BEST PRACTICES: Review patterns before use, test with git check-ignore, customize for your project +# • OFFICIAL SOURCES: Flask documentation and community best practices + +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# SECURITY & SENSITIVE DATA PROTECTION (ALWAYS FIRST!) +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# CRITICAL: Protect sensitive data from accidental commits to version control + +*.crt +*.key +*.keystore +*.pem +*.secret +*.token +*_keys +*_secrets +*_tokens +.env +.env.* +.env.*.local +.env.local +id_dsa +id_rsa + +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# BUILD ARTIFACTS & DISTRIBUTION +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• + +*.egg +*.egg-info/ +*.so +*.tar.gz +*.whl +*.zip + +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# DEPENDENCY MANAGEMENT & PACKAGE CACHE +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• + +.cache/ +.eggs/ +.pybuilder/ +.python-eggs/ +.venv +__pypackages__/ +build/ +develop-eggs/ +dist/ +docs/_build/ +eggs/ +env.bak/ +ENV/ +parts/ +pip-wheel-metadata/ +profile_default/ +sdist/ +share/python-wheels/ +venv.bak/ +venv/ +wheels/ + +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# DEVELOPMENT & RUNTIME ARTIFACTS +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• + +*.log +*.log.* +.dmypy.json +.mypy_cache/ +.nox/ +.pyre/ +.pytest_cache/ +.pytype/ +.tox/ +dmypy.json +pip-delete-this-directory.txt +pip-log.txt + +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# FRAMEWORK-SPECIFIC PATTERNS +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• + +*.cer +*.csr +*.der +*.jks +*.manifest +*.nupkg +*.o +*.obj +*.p12 +*.p7b +*.p7c +*.pfx +*.pid +*.pid.lock +*.py[co] +*.pyc +*.pyo +*.spec +*.tgz +*.truststore +.Python +.flask-debug +.python-version +.flaskenv +.installed.cfg +cython_debug/ +downloads/ +instance/ +ipython_config.py +lib/ +lib64/ +MANIFEST +out/ +pdm.toml +.webassets-cache +var/ + +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• +# TESTING & QUALITY ASSURANCE +# •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• + +.coverage +.coverage.* +.coverage.xml +.coverage_html/ +.hypothesis/ +.nyc_output/ +htmlcov/ +junit.xml +nosetests.xml +test-output/ +test-reports/ +test-results/ +testng-results.xml + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# TEMPLATE CUSTOMIZATION & BEST PRACTICES +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# 1. REVIEW: Examine all patterns before use +# 2. CUSTOMIZE: Adapt to your project's specific structure +# 3. TEST: Use `git check-ignore` to verify patterns +# 4. SECURE: Always protect sensitive data and credentials +# 5. UPDATE: Review periodically as technology evolves + +# RECOMMENDED USAGE PATTERNS: +# ============================================================================== +# +# BASIC USAGE (STANDALONE): +# ------------------------- +# cp frameworks/flask.gitignore .gitignore +# +# WITH IDE SUPPORT: +# ---------------- +# cat frameworks/flask.gitignore \ +# ides/intellij.gitignore | sort -u > .gitignore +# +# CROSS-PLATFORM DEVELOPMENT: +# --------------------------- +# cat frameworks/flask.gitignore \ +# os/linux.gitignore \ +# os/macos.gitignore \ +# os/windows.gitignore | sort -u > .gitignore +# +# IMPORTANT NOTES: +# ============================================================================== +# 1. This template is self-contained and includes security patterns +# 2. No need to add common/security.gitignore separately +# 3. Test with `git status --ignored` to ensure proper coverage diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..1c2c97a Binary files /dev/null and b/favicon.ico differ diff --git a/static/builder.js b/static/builder.js new file mode 100644 index 0000000..5d392b6 --- /dev/null +++ b/static/builder.js @@ -0,0 +1,347 @@ +const dayMap = { + "Lundi": 0, + "Mardi": 1, + "Mercredi": 2, + "Jeudi": 3, + "Vendredi": 4, + "Samedi": 5, + "Dimanche": 6 +}; + + +// ====================================================== +// Cours ajoutés manuellement +// ====================================================== + +function addCourse(course = {}) { + const tbody = document.getElementById("courses"); + + const row = document.createElement("tr"); + + row.innerHTML = ` + + + + + + + + + + + + + + + + + + + `; + + tbody.appendChild(row); + + + row.querySelector(".day").value = course.day ?? 0; + row.querySelector(".start").value = course.start ?? ""; + row.querySelector(".end").value = course.end ?? ""; + row.querySelector(".name").value = course.name ?? ""; + row.querySelector(".prof").value = course.prof ?? ""; + row.querySelector(".room").value = course.room ?? ""; + row.querySelector(".color").value = + course.color ?? "#80b3ff"; + + + row.querySelector(".remove").onclick = () => { + row.remove(); + }; +} + + +// ====================================================== +// Gestion des UE fondamentales +// ====================================================== + +const fonda = + [...document.querySelectorAll( + "#courses-base .ue-selector" + )]; + + +fonda.forEach(cb => { + + cb.addEventListener("change", () => { + + const selected = + fonda.filter(x => x.checked); + + /* + if (selected.length > 2) { + cb.checked = false; + alert("Choisissez seulement 2 UE parmi les 3."); + } + */ + + + updateTD(); + }); + +}); + + + +function updateTD() { + + document + .querySelectorAll( + "#courses-base input[type=radio]" + ) + .forEach(radio => { + + const ue = + radio.dataset.ue; + + + const selected = + document.querySelector( + `.ue-selector[data-ue="${ue}"]` + )?.checked; + + + radio.disabled = !selected; + + + if (!selected) + radio.checked = false; + }); +} + + +// ====================================================== +// Conversion d'une ligne HTML en cours JSON +// ====================================================== + +function rowToCourse(row) { + return { + + day: + Number(row.dataset.day), + + start: + row.dataset.start, + + end: + row.dataset.end, + + name: + row.dataset.name ?? "", + + prof: + row.dataset.prof ?? "", + + room: + row.dataset.room ?? "", + + color: + row.dataset.color ?? "#ffffff" + }; +} + + + +// ====================================================== +// Extraction des cours fixes +// ====================================================== + +function extractFixedCourses(container) { + + const courses = []; + + + container + .querySelectorAll("tr") + .forEach(row => { + //console.log(row); + + const checkbox = + document.getElementById(row.getAttribute("checkbox-id")) + + enabled = checkbox.checked; + + if (!enabled) + return; + + const course = + rowToCourse(row); + console.log(course); + + + if (course) + courses.push(course); + + }); + + + return courses; +} + + +// ====================================================== +// Submit PDF +// ====================================================== + +document + .getElementById("courseForm") + .addEventListener( + "submit", + async e => { + + e.preventDefault(); + + + let cours = []; + + + cours.push( + ...extractFixedCourses( + document.getElementById( + "courses-base" + ) + ) + ); + + + cours.push( + ...extractFixedCourses( + document.getElementById( + "courses-autre" + ) + ) + ); + + + + // Cours ajoutés manuellement + + document + .querySelectorAll( + "#courses tr" + ) + .forEach(row => { + + + if ( + !row.querySelector( + ".select-row" + ).checked + ) + return; + + + cours.push({ + + day: + Number( + row.querySelector( + ".day" + ).value + ), + + start: + row.querySelector( + ".start" + ).value, + + end: + row.querySelector( + ".end" + ).value, + + name: + row.querySelector( + ".name" + ).value, + + prof: + row.querySelector( + ".prof" + ).value, + + room: + row.querySelector( + ".room" + ).value, + + color: + row.querySelector( + ".color" + ).value + }); + + }); + + + + const response = + await fetch( + "/edt.pdf", + { + method: "POST", + headers: { + "Content-Type": + "application/json" + }, + body: + JSON.stringify({ + cours + }) + } + ); + + + const blob = + await response.blob(); + + + window.open( + URL.createObjectURL(blob) + ); + + } + ); + + + +// ====================================================== +// Select all cours ajoutés +// ====================================================== + +const selectAll = + document.getElementById( + "select-all" + ); + + +if (selectAll) { + + selectAll.onchange = () => { + + document + .querySelectorAll( + ".select-row" + ) + .forEach(cb => { + cb.checked = + selectAll.checked; + }); + + }; +} \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..61d03e1 --- /dev/null +++ b/static/style.css @@ -0,0 +1,139 @@ +* { + box-sizing: border-box; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + } + + body { + background: #f5f6fa; + padding: 30px; + color: #222; + } + + form { + background: white; + padding: 25px; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.08); + max-width: 1200px; + margin: auto; + } + + table { + width: 100%; + border-collapse: collapse; + overflow: hidden; + border-radius: 8px; + } + + thead { + background: #333; + color: white; + } + + tbody tbody:nth-child(even) td { + background-color: #000; + } + + th { + padding: 12px; + text-align: center; + font-weight: 600; + } + + td { + padding: 8px; + text-align: center; + border-bottom: 1px solid #ddd; + } + + tbody tr:hover { + background: #f1f5ff; + } + + input, + select { + width: 100%; + padding: 6px 8px; + border: 1px solid #ccc; + border-radius: 6px; + background: white; + } + + input[type="checkbox"] { + width: 18px; + height: 18px; + cursor: pointer; + } + + input[type="color"] { + height: 35px; + padding: 2px; + cursor: pointer; + } + + input:focus, + select:focus { + outline: none; + border-color: #4a90e2; + box-shadow: 0 0 0 2px rgba(74,144,226,0.2); + } + + + button { + margin-top: 15px; + padding: 10px 18px; + border: none; + border-radius: 8px; + cursor: pointer; + font-size: 15px; + transition: 0.15s; + } + + button:hover { + transform: translateY(-1px); + opacity: 0.9; + } + + + button[type="submit"] { + background: #2563eb; + color: white; + } + + button[type="button"] { + background: #e5e7eb; + color: #111; + } + + + .remove { + background: #333 !important; + color: white; + padding: 6px 10px; + font-size: 13px; + margin: 0; + } + + + #courses tr { + transition: background 0.2s; + } + + + /* Better mobile display */ + @media (max-width: 900px) { + + body { + padding: 10px; + } + + form { + padding: 15px; + } + + table { + display: block; + overflow-x: auto; + white-space: nowrap; + } + } \ No newline at end of file diff --git a/templates/edt.typ b/templates/edt.typ new file mode 100644 index 0000000..02b4b11 --- /dev/null +++ b/templates/edt.typ @@ -0,0 +1,112 @@ +#import "@preview/cetz:0.5.1" + +#let time(h,m) = datetime(hour:h, minute: m, second: 0) + +#let parse_time(s) = { + let (h, m) = s.match(regex("(\d{2}):(\d{2})")).captures.map(int) + time(h,m) +} + +#let gety(s) = { + let dt = parse_time(s) + 11-(dt - time(8,0)).hours() +} + +#let sans_font = "Fira Sans" + + +#let titre = "EDT 2026/2027 -- M1 S1" + +#let data = json(bytes(sys.inputs.data)) + +#let cours = data.cours +/* +Un exemple : + +#let cours = ( + (day: 0, start: "08:15", end: "10:15", name:"Algèbre (CM)", prof:"Jean-Benoît Bost", room: "OA1", color: "#ff80b3"), + (day: 0, start: "10:30", end: "13:00", name:"Algèbre (TD)", prof:"x", room: "OA1", color: "#ff80b3"), + (day: 3, start: "14:00", end: "16:00", name:"Algèbre (CM)", prof:"Jean-Benoît Bost", room: "OA1", color: "#ff80b3"), + (day: 3, start: "16:15", end: "18:45", name:"Algèbre (TD)", prof:"x", room: "OA1", color: "#ff80b3"), + + (day: 0, start: "14:00", end: "16:00", name:"Analyse (CM)", prof:"Blanche Buet", room: "OA1", color: "#80ffcc"), + (day: 0, start: "16:15", end: "18:45", name:"Analyse (TD)", prof:"x", room: "OA1", color: "#80ffcc"), + (day: 2, start: "08:15", end: "10:15", name:"Analyse (CM)", prof:"Blanche Buet", room: "OA1", color: "#80ffcc"), + (day: 2, start: "10:30", end: "13:00", name:"Analyse (TD)", prof:"x", room: "OA1", color: "#80ffcc"), + + (day: 1, start: "14:00", end: "16:00", name:"Cryptographie (CM)", prof:"Stéphane Fischler", room: "OA1", color: "#e6b3ff"), + (day: 4, start: "10:30", end: "12:30", name:"Cryptographie (TP)", prof:"Chimène Fischler", room: "OA1", color: "#e6b3ff") +) +*/ + +#set page( + paper: "a4", + flipped: true, + footer: [ + #set align(right) + #set text(font: sans_font, size: 9pt) + $copyright$ EDT Valentin Moguérou 2026 + ], + margin: ( + x: 1.5cm, + top: 1cm, + bottom: 1cm, + ) +) +#set par(justify: true) +#set text( + font: "New Computer Modern", + size: 11pt, +) + +#set align(center) + +#let jours = ("Lundi","Mardi","Mercredi","Jeudi","Vendredi") + +#align(center, text(titre, size: 15pt, weight: "bold")) + +#cetz.canvas( + length: 1.5cm, +{ + import cetz.draw: * + + for heure in range(8,20) { + content((-.5,18.85-heure), + text([#heure:00], font: sans_font, size: 9pt)) + + line((-0.5,19-heure),(0,19-heure), stroke:1pt) + + + if calc.even(heure) { line((0,19-heure),(15,19-heure), stroke:0.5pt+gray) } + + } + line((0,11),(15,11), stroke:1pt) // 8h + line((0,0),(15,0), stroke:1pt) // 19h + + + + + for c in cours { + content( + (3*c.day, gety(c.start)+.05), + (3*(c.day+1), gety(c.end)-.05), + frame: "rect", + fill: rgb(c.color), + stroke: 0.5pt, + align(center+horizon)[ + *#c.name* \ + #c.prof + + #set text(size: 9pt, font: sans_font) + #if c.room != "" [*#c.room*, ] + #(c.start)--#c.end + ] + ) + } + + for jour in range(5) { + content((3*jour+1.5,11.3), text([#jours.at(jour)], font: sans_font, size: 11pt)) + line((3*jour,11.3),(3*jour,0), stroke:1pt) + } + line((15,11.5),(15,0), stroke:1pt) +}) \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..db26745 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,244 @@ + + + + + EDT Generator + + + + + +

#m1edt

+ +
+ +

Cours fondamentaux : choisir 2 parmi 3

+ + + + + + + + + + + + + + + + + +{% set jours = ["Lundi","Mardi","Mercredi","Jeudi","Vendredi"] %} + +{% for ue in cours_base %} + +{% set rowspan = ue.cm|length + 2 * (ue.td|length) %} + +{# ---------------- CM ---------------- #} + +{% for cm in ue.cm %} + + + + {% if loop.first %} + + + + {% endif %} + + + + + + {% if loop.first %} + + + {% endif %} + + + + + +{% endfor %} + + +{# ---------------- TD ---------------- #} + +{% for td in ue.td %} + +{% for slot in td.slots %} + + + + {% if loop.first %} + + {% endif %} + + + + + + {% if loop.first %} + + + {% endif %} + + + + + +{% endfor %} +{% endfor %} + +{% endfor %} + +
UESélectionJourDébutFinAtomeEnseignantSalle
{{ ue.name }} + + {{ jours[cm.day] }}{{ cm.start }}{{ cm.end }}CM{{ cm.prof }}{{ cm.room }}
+ + {{ jours[slot.day] }}{{ slot.start }}{{ slot.end }}TD G{{ td.group }}{{ td.prof }}{{ slot.room }}
+ + + +

Autres cours

+ + + + + + + + + + + + + + + + + + +{% set jours = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"] %} + +{% for ue in cours_autres %} + +{% set rowspan = ue.atoms|length %} + +{% for atom in ue.atoms %} + + + + {% if loop.first %} + + + + + {% endif %} + + + + + + + + + + +{% endfor %} +{% endfor %} + + +
UESélectionJourDébutFinAtomeEnseignantSalle
{{ ue.name }} + + {{ jours[atom.day] }}{{ atom.start }}{{ atom.end }}{{ atom.label }}{{ atom.prof }}{{ atom.room }}
+ +

Ajouter des cours

+ + + + + + + + + + + + + + + + + + + + + +
+ +JourDébutFinNomEnseignantSalleCouleur
+ + + + +
+ + + + + +
+ + + + + + \ No newline at end of file diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..fa0e6b7 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,242 @@ +from flask import Flask +from flask import render_template, send_file, request, redirect, url_for +import typst +import json +from io import BytesIO +import random + +from pprint import pprint + +app = Flask(__name__) + +@app.route("/") +def index(): + cours_base = [ + # ALGEBRE + { + "ue": "algebre", + "name": "Algèbre", + "color": "#ff80b3", + "cm": [ + { + "prof": "Jean-Benoît Bost", + "day": 0, "start": "08:15", "end": "10:15", "room": "Amphi Yoccoz", + }, + { + "prof": "Jean-Benoît Bost", + "day": 3, "start": "14:00", "end": "16:00", "room": "Amphi Yoccoz", + }, + ], + "td": [ + { + "group": 1, + "prof": "Kévin Destagnol", + "slots": [ + {"day": 0, "start": "10:30", "end": "13:00", "room": "0A2"}, + {"day": 3, "start": "16:15", "end": "18:45", "room": "0A2"}, + ], + }, + { + "group": 2, + "prof": "Pierre Lorenzon", + "slots": [ + {"day": 0, "start": "10:30", "end": "13:00", "room": "0A7"}, + {"day": 3, "start": "16:15", "end": "18:45", "room": "0E1"}, + ], + }, + ], + }, + # ANALYSE + { + "ue": "analyse", + "name": "Analyse", + "color": "#80ffcc", + "cm": [ + { + "prof": "Blanche Buet", + "day": 0, "start": "14:00", "end": "16:00", "room": "OA2", + }, + { + "prof": "Blanche Buet", + "day": 2, "start": "08:15", "end": "10:15", "room": "OA1", + }, + ], + "td": [ + { + "group": 1, + "prof": "Frédéric Valet", + "slots": [ + {"day": 0, "start": "16:15", "end": "18:45", "room": "0A2"}, + {"day": 2, "start": "10:30", "end": "13:00", "room": "0A1"}, + ], + }, + { + "group": 2, + "prof": "Sebastian Bechtel", + "slots": [ + {"day": 0, "start": "16:15", "end": "18:45", "room": "0A7"}, + {"day": 2, "start": "10:30", "end": "13:00", "room": "0D1"}, + ], + }, + ], + }, + # PROBAS + { + "ue": "probas", + "name": "Probabilités", + "color": "#ffcc99", + "cm": [ + { + "prof": "Pierre-Loïc Méliot", + "day": 1, "start": "08:15", "end": "10:15", "room": "Amphi Yoccoz", + }, + { + "prof": "Pierre-Loïc Méliot", + "day": 3, "start": "08:15", "end": "10:15", "room": "Amphi Yoccoz", + }, + ], + "td": [ + { + "group": 1, + "prof": "Sophie Lemaire", + "slots": [ + {"day": 1, "start": "10:30", "end": "13:00", "room": "0D1"}, + {"day": 3, "start": "10:30", "end": "13:00", "room": "0E1"}, + ], + }, + { + "group": 2, + "prof": "A. Legrand", + "slots": [ + {"day": 1, "start": "10:30", "end": "13:00", "room": "Amphi Yoccoz"}, + {"day": 3, "start": "10:30", "end": "13:00", "room": "0A2"}, + ], + }, + ], + }, + ] + + + cours_autres = [ + { + "ue": "crypto", + "name": "Cryptographie", + "color": "#e6b3ff", + "atoms": [ + { + "label": "CM", + "prof": "Stéphane Fischler", + "day": 1, + "start": "14:00", + "end": "16:00", + "room": "0A2", + }, + { + "label": "TP", + "prof": "Chimène Fischler", + "day": 4, + "start": "10:30", + "end": "12:30", + "room": "0E10", + }, + ], + }, + { + "ue": "geo_alea", + "name": "Géométrie aléatoire", + "color": "#ccb3ff", + "atoms": [ + { + "label": "CM", + "prof": "N. Curien", + "day": 1, + "start": "16:15", + "end": "18:15", + "room": "1A7", + }, + { + "label": "TP", + "prof": "A. Legrand", + "day": 4, + "start": "08:15", + "end": "10:15", + "room": "0D10", + }, + ], + }, + { + "ue": "edp_num", + "name": "EDP numériques", + "color": "#ccebff", + "atoms": [ + { + "label": "CM", + "prof": "B. Maury", + "day": 1, + "start": "16:15", + "end": "18:15", + "room": "0A5", + }, + { + "label": "TP", + "prof": "B. Graille", + "day": 4, + "start": "08:15", + "end": "10:15", + "room": "0E10", + }, + ], + }, + { + "ue": "topalg", + "name": "Topologie algébrisée", + "color": "#a366ff", + "atoms": [ + { + "label": "CI", + "prof": "Patrick Massot", + "day": 2, + "start": "14:00", + "end": "18:15", + "room": "0A1", + }, + ], + }, + ] + + return render_template('index.html', cours_base=cours_base, cours_autres=cours_autres) + + +@app.route("/favicon.ico") +def favicon(): + return send_file("favicon.ico") + +@app.route('/edt.pdf', methods=["GET", "POST"]) +def generate_pdf(): + if request.method != "POST": + data = { + "cours": [ + {"day": 0, "start": "08:15", "end": "10:xx", "name": "Algèbre (CM)", "prof": "Jean-Benoît Bost", "room": "OA1", "color": "#ff80b3"}, + {"day": 0, "start": "10:30", "end": "13:00", "name": "Algèbre (TD)", "prof": "x", "room": "OA1", "color": "#ff80b3"}, + {"day": 3, "start": "14:00", "end": "16:00", "name": "Algèbre (CM)", "prof": "Jean-Benoît Bost", "room": "OA1", "color": "#ff80b3"}, + {"day": 3, "start": "16:15", "end": "18:45", "name": "Algèbre (TD)", "prof": "x", "room": "OA1", "color": "#ff80b3"}, + + {"day": 0, "start": "14:00", "end": "16:00", "name": "Analyse (CM)", "prof": "Blanche Buet", "room": "OA1", "color": "#80ffcc"}, + {"day": 0, "start": "16:15", "end": "18:45", "name": "Analyse (TD)", "prof": "x", "room": "OA1", "color": "#80ffcc"}, + {"day": 2, "start": "08:15", "end": "10:15", "name": "Analyse (CM)", "prof": "Blanche Buet", "room": "OA1", "color": "#80ffcc"}, + {"day": 2, "start": "10:30", "end": "13:00", "name": "Analyse (TD)", "prof": "x", "room": "OA1", "color": "#80ffcc"}, + + {"day": 1, "start": "14:00", "end": "16:00", "name": "Cryptographie (CM)", "prof": "Stéphane Fischler","room": "OA1", "color": "#e6b3ff"}, + {"day": 4, "start": "10:30", "end": "12:30", "name": "Cryptographie (TP)", "prof": "Chimène Fischler", "room": "OA1", "color": "#e6b3ff"}, + ] + } + print(data) + else: + data = json.loads(request.data) + pprint(data) + + sys_inputs = {"data": json.dumps(data)} + + pdf_file = BytesIO(typst.compile(input="templates/edt.typ", sys_inputs=sys_inputs)) + + return send_file(pdf_file, download_name="edt.pdf") \ No newline at end of file