Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
3 changed files with 101 additions and 21 deletions
Showing only changes of commit 9a0e64effd - Show all commits

View File

@ -41,50 +41,58 @@ from app.scodoc.sco_exceptions import ScoValueError
import app.scodoc.sco_utils as scu
def formsemestre_load_calendar(
formsemestre: FormSemestre,
) -> icalendar.cal.Calendar | None:
"""Load ics data, return calendar or None if not configured or not available"""
edt_id = formsemestre.get_edt_id()
if not edt_id:
flash(
"accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)"
)
return None
def get_ics_filename(edt_id: str) -> str:
"Le chemin vers l'ics de cet edt_id"
edt_ics_path = ScoDocSiteConfig.get("edt_ics_path")
if not edt_ics_path.strip():
return None
ics_filename = edt_ics_path.format(edt_id=edt_id)
return edt_ics_path.format(edt_id=edt_id)
def formsemestre_load_calendar(
formsemestre: FormSemestre = None, edt_id: str = None
) -> tuple[bytes, icalendar.cal.Calendar]:
"""Load ics data, return raw ics and decoded calendar.
Raises ScoValueError if not configured or not available or invalid format.
"""
if edt_id is None and formsemestre:
edt_id = formsemestre.get_edt_id()
if not edt_id:
raise ScoValueError(
"accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)"
)
ics_filename = get_ics_filename(edt_id)
try:
with open(ics_filename, "rb") as file:
log(f"Loading edt from {ics_filename}")
data = file.read()
try:
calendar = icalendar.Calendar.from_ical(file.read())
calendar = icalendar.Calendar.from_ical(data)
except ValueError as exc:
log(
f"""formsemestre_load_calendar: error importing ics for {
formsemestre}\npath='{ics_filename}'"""
formsemestre or ''}\npath='{ics_filename}'"""
)
raise ScoValueError(
f"calendrier ics illisible (edt_id={edt_id})"
) from exc
except FileNotFoundError as exc:
log(
f"formsemestre_load_calendar: ics not found for {formsemestre}\npath='{ics_filename}'"
f"formsemestre_load_calendar: ics not found for {formsemestre or ''}\npath='{ics_filename}'"
)
raise ScoValueError(
f"Fichier ics introuvable (filename={ics_filename})"
) from exc
except PermissionError as exc:
log(
f"""formsemestre_load_calendar: permission denied for {formsemestre
f"""formsemestre_load_calendar: permission denied for {formsemestre or ''
}\npath='{ics_filename}'"""
)
raise ScoValueError(
f"Fichier ics inaccessible: vérifier permissions (filename={ics_filename})"
) from exc
return calendar
return data, calendar
# --- Couleurs des évènements emploi du temps
@ -178,7 +186,7 @@ def formsemestre_edt_dict(formsemestre: FormSemestre) -> list[dict]:
def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
"chargement fichier, filtrage et extraction des identifiants."
# Chargement du calendier ics
calendar = formsemestre_load_calendar(formsemestre)
_, calendar = formsemestre_load_calendar(formsemestre)
if not calendar:
return []
# --- Paramètres d'extraction
@ -236,7 +244,8 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
edt_group = extract_event_data(
event, edt_ics_group_field, edt_ics_group_pattern
)
# si pas de groupe dans l'event, oi si groupe non reconnu, prend toute la promo ("tous")
# si pas de groupe dans l'event, ou si groupe non reconnu,
# prend toute la promo ("tous")
group: GroupDescr = (
edt2group.get(edt_group, default_group)
if edt_group

View File

@ -10,9 +10,50 @@ div.config-section {
margin-right: -15px;
margin-left: -15px;
}
</style>
{% endblock %}
#zone-test {
margin-bottom: 12px;
}
#raw-ics-sample-zone {
display: none;
}
#raw-ics-sample-zone>div {
font-style: italic;
}
#test_load_ics:disabled {
background-color: gray;
color: white; /* Optional: change text color if needed */
cursor: not-allowed;
}
</style>
{% endblock styles %}
{% block scripts %}
{{ super() }}
<script>
function update_test_button_state() {
var inputValue = document.getElementById('test_edt_id').value;
document.getElementById('test_load_ics').disabled = inputValue.length === 0;
}
async function load_ics_sample() {
var edt_id = document.getElementById('test_edt_id').value;
try {
const response = await fetch(`${SCO_URL}/../ics_raw_sample/${edt_id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.text();
document.getElementById('raw-ics-sample').value = data;
document.getElementById('raw-ics-sample-zone').style.display = 'block';
} catch (error) {
console.error('Error fetching data: ', error);
}
}
</script>
{% endblock scripts %}
{% block app_content %}
<div class="row">
@ -46,6 +87,18 @@ affectent notamment les comptages d'absences de tous les bulletins des
{{ wtf.form_field(form.edt_ics_path) }}
</div>
<div id="zone-test">
Pour essayer, indiquer un <tt>edt_id</tt> :
<input type="text" id="test_edt_id" oninput="update_test_button_state()" size="12" />
<button id="test_load_ics" type="button" onclick="load_ics_sample()" disabled>
Essayer de charger l'ics</button>
<div id="raw-ics-sample-zone">
<div>Voici un évènement chargé au milieu de ce calendrier:
</div>
<textarea id="raw-ics-sample" rows="13" cols="90">
</textarea>
</div>
</div>
<div class="config-section">Extraction des identifiants depuis les calendriers</div>
<div class="help">
Indiquer ici comment récupérer les informations (titre, groupe, module)

View File

@ -81,7 +81,7 @@ from app.models import (
from app.models import departements
from app.models.config import PersonalizedLink
from app.scodoc import sco_edt_cal
from app.scodoc import sco_find_etud
from app.scodoc import sco_logos
from app.scodoc import sco_utils as scu
@ -376,6 +376,24 @@ def config_assiduites():
)
@bp.route("/ScoDoc/ics_raw_sample/<string:edt_id>")
@admin_required
def ics_raw_sample(edt_id: str):
"Renvoie un extrait de l'ics brut, pour aider à configurer les extractions"
try:
raw_ics, _ = sco_edt_cal.formsemestre_load_calendar(edt_id=edt_id)
except ScoValueError as exc:
return exc.args[0]
try:
ics = raw_ics.decode(scu.SCO_ENCODING)
except SyntaxError:
return f"Erreur lors de la conversion vers {scu.SCO_ENCODING}"
evs = ics.split("BEGIN:VEVENT")
if len(evs) < 1:
return "pas d'évènements VEVENT détectés dans ce fichier"
return "BEGIN:VEVENT" + evs[len(evs) // 2]
@bp.route("/ScoDoc/config_codes_decisions", methods=["GET", "POST"])
@admin_required
def config_codes_decisions():