forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -41,50 +41,58 @@ from app.scodoc.sco_exceptions import ScoValueError
|
|||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
def formsemestre_load_calendar(
|
def get_ics_filename(edt_id: str) -> str:
|
||||||
formsemestre: FormSemestre,
|
"Le chemin vers l'ics de cet edt_id"
|
||||||
) -> 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
|
|
||||||
edt_ics_path = ScoDocSiteConfig.get("edt_ics_path")
|
edt_ics_path = ScoDocSiteConfig.get("edt_ics_path")
|
||||||
if not edt_ics_path.strip():
|
if not edt_ics_path.strip():
|
||||||
return None
|
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:
|
try:
|
||||||
with open(ics_filename, "rb") as file:
|
with open(ics_filename, "rb") as file:
|
||||||
log(f"Loading edt from {ics_filename}")
|
log(f"Loading edt from {ics_filename}")
|
||||||
|
data = file.read()
|
||||||
try:
|
try:
|
||||||
calendar = icalendar.Calendar.from_ical(file.read())
|
calendar = icalendar.Calendar.from_ical(data)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
log(
|
log(
|
||||||
f"""formsemestre_load_calendar: error importing ics for {
|
f"""formsemestre_load_calendar: error importing ics for {
|
||||||
formsemestre}\npath='{ics_filename}'"""
|
formsemestre or ''}\npath='{ics_filename}'"""
|
||||||
)
|
)
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
f"calendrier ics illisible (edt_id={edt_id})"
|
f"calendrier ics illisible (edt_id={edt_id})"
|
||||||
) from exc
|
) from exc
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
log(
|
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(
|
raise ScoValueError(
|
||||||
f"Fichier ics introuvable (filename={ics_filename})"
|
f"Fichier ics introuvable (filename={ics_filename})"
|
||||||
) from exc
|
) from exc
|
||||||
except PermissionError as exc:
|
except PermissionError as exc:
|
||||||
log(
|
log(
|
||||||
f"""formsemestre_load_calendar: permission denied for {formsemestre
|
f"""formsemestre_load_calendar: permission denied for {formsemestre or ''
|
||||||
}\npath='{ics_filename}'"""
|
}\npath='{ics_filename}'"""
|
||||||
)
|
)
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
f"Fichier ics inaccessible: vérifier permissions (filename={ics_filename})"
|
f"Fichier ics inaccessible: vérifier permissions (filename={ics_filename})"
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
return calendar
|
return data, calendar
|
||||||
|
|
||||||
|
|
||||||
# --- Couleurs des évènements emploi du temps
|
# --- 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]:
|
def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
|
||||||
"chargement fichier, filtrage et extraction des identifiants."
|
"chargement fichier, filtrage et extraction des identifiants."
|
||||||
# Chargement du calendier ics
|
# Chargement du calendier ics
|
||||||
calendar = formsemestre_load_calendar(formsemestre)
|
_, calendar = formsemestre_load_calendar(formsemestre)
|
||||||
if not calendar:
|
if not calendar:
|
||||||
return []
|
return []
|
||||||
# --- Paramètres d'extraction
|
# --- Paramètres d'extraction
|
||||||
@ -236,7 +244,8 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]:
|
|||||||
edt_group = extract_event_data(
|
edt_group = extract_event_data(
|
||||||
event, edt_ics_group_field, edt_ics_group_pattern
|
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 = (
|
group: GroupDescr = (
|
||||||
edt2group.get(edt_group, default_group)
|
edt2group.get(edt_group, default_group)
|
||||||
if edt_group
|
if edt_group
|
||||||
|
@ -10,9 +10,50 @@ div.config-section {
|
|||||||
margin-right: -15px;
|
margin-right: -15px;
|
||||||
margin-left: -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 %}
|
{% block app_content %}
|
||||||
<div class="row">
|
<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) }}
|
{{ wtf.form_field(form.edt_ics_path) }}
|
||||||
</div>
|
</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="config-section">Extraction des identifiants depuis les calendriers</div>
|
||||||
<div class="help">
|
<div class="help">
|
||||||
Indiquer ici comment récupérer les informations (titre, groupe, module)
|
Indiquer ici comment récupérer les informations (titre, groupe, module)
|
||||||
|
@ -81,7 +81,7 @@ from app.models import (
|
|||||||
from app.models import departements
|
from app.models import departements
|
||||||
from app.models.config import PersonalizedLink
|
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_find_etud
|
||||||
from app.scodoc import sco_logos
|
from app.scodoc import sco_logos
|
||||||
from app.scodoc import sco_utils as scu
|
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"])
|
@bp.route("/ScoDoc/config_codes_decisions", methods=["GET", "POST"])
|
||||||
@admin_required
|
@admin_required
|
||||||
def config_codes_decisions():
|
def config_codes_decisions():
|
||||||
|
Loading…
Reference in New Issue
Block a user