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
6 changed files with 213 additions and 64 deletions
Showing only changes of commit 2b04c952c4 - Show all commits

View File

@ -427,7 +427,7 @@ def create_absence(
db.session.commit()
if est_just:
justi = Justificatif.create_justificatif(
etud=etud,
etudiant=etud,
date_debut=date_debut,
date_fin=date_fin,
etat=scu.EtatJustificatif.VALIDE,

View File

@ -277,8 +277,10 @@ class RowAssiJusti(tb.Row):
self.add_cell(
"entry_date",
"Saisie le",
self.ligne["entry_date"].strftime("%d/%m/%y à %H:%M"),
data={"order": self.ligne["entry_date"]},
self.ligne["entry_date"].strftime("%d/%m/%y à %H:%M")
if self.ligne["entry_date"]
else "?",
data={"order": self.ligne["entry_date"] or ""},
raw_content=self.ligne["entry_date"],
classes=["small-font"],
column_classes={"entry_date"},
@ -387,6 +389,13 @@ class RowAssiJusti(tb.Row):
html.append(f'<a title="Détails" href="{url}"></a>')
# Modifier
if self.ligne["type"] == "justificatif":
url = url_for(
"assiduites.edit_justificatif_etud",
justif_id=self.ligne["obj_id"],
scodoc_dept=g.scodoc_dept,
)
else:
url = url_for(
"assiduites.tableau_assiduite_actions",
type=self.ligne["type"],

View File

@ -90,9 +90,9 @@ div.submit > input {
</div>
{# Raison #}
<div>
<div>{{ form.assi_raison.label }}</div>
{{ form.assi_raison() }}
{{ render_field_errors(form, 'assi_raison') }}
<div>{{ form.raison.label }}</div>
{{ form.raison() }}
{{ render_field_errors(form, 'raison') }}
</div>
{# Date dépot #}
{{ form.entry_date.label }}&nbsp;: {{ form.entry_date }}

View File

@ -52,8 +52,8 @@
<div class="assi-row">
<div class="assi-label">
<legend for="assi_raison">Raison</legend>
<textarea name="assi_raison" id="assi_raison" cols="75" rows="4" maxlength="500"></textarea>
<legend for="raison">Raison</legend>
<textarea name="raison" id="raison" cols="75" rows="4" maxlength="500"></textarea>
</div>
</div>
@ -135,7 +135,7 @@
const { deb, fin } = getDates()
const etat = field.querySelector('#assi_etat').value;
const raison = field.querySelector('#assi_raison').value;
const raison = field.querySelector('#raison').value;
const module = field.querySelector("#ajout_assiduite_module_impl").value;
return {
@ -168,7 +168,7 @@
field.querySelector('#assi_date_debut').value = "";
field.querySelector('#assi_date_fin').value = "";
field.querySelector('#assi_etat').value = "attente";
field.querySelector('#assi_raison').value = "";
field.querySelector('#raison').value = "";
}

View File

@ -1,3 +1,5 @@
{# Formulaire ajout ou modification de justificatif
Si justif, edit #}
{% extends "sco_page.j2" %}
{% import 'wtf.j2' as wtf %}
@ -61,11 +63,27 @@ div.submit > input {
</div>
{# Raison #}
<div>
<div>{{ form.assi_raison.label }}</div>
{{ form.assi_raison() }}
{{ render_field_errors(form, 'assi_raison') }}
<div>{{ form.raison.label }}</div>
{{ form.raison() }}
{{ render_field_errors(form, 'raison') }}
</div>
{# Fichier(s) justificatif(s) #}
{# Liste des fichiers existants #}
{% if justif and nb_files > 0 %}
<div>
<div>{{nb_files}} fichiers justificatifs déposés
{% if filenames|length < nb_files %}
, dont {{filenames|length}} vous sont accessibles
{% endif %}
<ul>
{% for filename in filenames %}
<li><span data-justif_id="{{justif.id}}" class="suppr_fichier_just"
>{{scu.icontag("delete_img", alt="supprimer", title="Supprimer")|safe}}</span>
{{filename}}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{# Ajout fichier(s) justificatif(s) #}
<div>
<div>{{ form.fichiers.label }}</div>
{{ form.fichiers() }}
@ -83,10 +101,11 @@ div.submit > input {
</fieldset>
</form>
</section>
{% if tableau %}
<section class="assi-liste">
{{tableau | safe }}
</section>
{% endif %}
</div>
{% endblock app_content %}
@ -108,4 +127,54 @@ $('.timepicker').timepicker({
scrollbar: false
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Suppression d'un fichier justificatif
function delete_file(justif_id, fileName, liElement) {
// Construct the URL
var url = "{{url_for('apiweb.justif_remove', justif_id=-1, scodoc_dept=g.scodoc_dept)}}".replace('-1', justif_id);
payload = {
"remove": "list",
"filenames" : [ fileName ],
}
// Send API request
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => {
if (response.ok) {
// Hide the <li> element on successful deletion
liElement.style.display = 'none';
sco_message("fichier supprimé");
} else {
// Handle non-successful responses here
console.error('Deletion failed:', response.statusText);
sco_error_message("erreur lors de la suppression du fichier");
}
})
.catch(error => {
console.error('Error:', error);
sco_error_message("erreur lors de la suppression du fichier (2)");
});
}
// Add event listeners to all elements with class 'suppr_fichier_just'
var deleteButtons = document.querySelectorAll('.suppr_fichier_just');
deleteButtons.forEach(function(button) {
button.addEventListener('click', function() {
// Get the text content of the next sibling node
var justif_id = this.dataset.justif_id;
var fileName = this.nextSibling.nodeValue.trim();
var liElement = this.parentNode; // Get the parent <li> element
delete_file(justif_id, fileName, liElement);
});
});
});
</script>
{% endblock scripts %}

View File

@ -400,7 +400,7 @@ def _get_dates_from_assi_form(
dt_entry_date = (
datetime.datetime.strptime(form.entry_date.data, "%d/%m/%Y")
if form.entry_date.data
else None
else datetime.datetime.now() # local tz
)
except ValueError:
dt_entry_date = None
@ -464,7 +464,7 @@ def _record_assiduite_etud(
dt_debut_tz_server,
dt_fin_tz_server,
scu.EtatAssiduite.get(form.assi_etat.data),
description=form.assi_raison.data,
description=form.raison.data,
entry_date=dt_entry_date_tz_server,
external_data=external_data,
moduleimpl=moduleimpl,
@ -596,6 +596,64 @@ def bilan_etud():
).build()
@bp.route("/edit_justificatif_etud/<int:justif_id>", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.AbsChange)
def edit_justificatif_etud(justif_id: int):
"""
Edition d'un justificatif
Args:
justif_id (int): l'identifiant du justificatif
Returns:
str: l'html généré
"""
justif = Justificatif.get_justificatif(justif_id)
form = AjoutJustificatifEtudForm(obj=justif)
# Set the default value for the etat field
if request.method == "GET":
form.date_debut.data = justif.date_debut.strftime("%d/%m/%Y")
form.date_fin.data = justif.date_fin.strftime("%d/%m/%Y")
if form.date_fin.data == form.date_debut.data:
# un seul jour: pas de date de fin, indique les heures
form.date_fin.data = ""
form.heure_debut.data = justif.date_debut.strftime("%H:%M")
form.heure_fin.data = justif.date_fin.strftime("%H:%M")
form.entry_date.data = (
justif.entry_date.strftime("%d/%m/%Y") if justif.entry_date else ""
)
form.etat.data = str(justif.etat)
redirect_url = url_for(
"assiduites.liste_assiduites_etud",
scodoc_dept=g.scodoc_dept,
etudid=justif.etudiant.id,
)
if form.validate_on_submit():
if _record_justificatif_etud(justif.etudiant, form, justif):
return redirect(redirect_url)
# Fichiers
filenames, nb_files = justif.get_fichiers()
return render_template(
"assiduites/pages/ajout_justificatif_etud.j2",
assi_limit_annee=sco_preferences.get_preference(
"assi_limit_annee",
dept_id=g.scodoc_dept_id,
),
etud=justif.etudiant,
filenames=filenames,
form=form,
justif=justif,
nb_files=nb_files,
page_title="Modification justificatif",
redirect_url=redirect_url,
sco=ScoData(justif.etudiant),
scu=scu,
)
@bp.route(
"/ajout_justificatif_etud", methods=["GET", "POST"]
) # was AjoutJustificatifEtud
@ -603,7 +661,7 @@ def bilan_etud():
@permission_required(Permission.AbsChange)
def ajout_justificatif_etud():
"""
ajout_justificatif_etud : Affichage et création/modification des justificatifs de l'étudiant
ajout_justificatif_etud : Affichage et création des justificatifs de l'étudiant
Args:
etudid (int): l'identifiant de l'étudiant
@ -654,8 +712,7 @@ def ajout_justificatif_etud():
def _record_justificatif_etud(
etud: Identite,
form: AjoutJustificatifEtudForm,
etud: Identite, form: AjoutJustificatifEtudForm, justif: Justificatif | None = None
) -> bool:
"""Enregistre les données du formulaire de saisie justificatif (et ses fichiers).
Returns ok if successfully recorded, else put error info in the form.
@ -663,6 +720,7 @@ def _record_justificatif_etud(
form.assi_etat.data : 'absent'
form.date_debut.data : '05/12/2023'
form.heure_debut.data : '09:06' (heure locale du serveur)
Si justif, modifie le justif existant, sinon en crée un nouveau
"""
(
ok,
@ -672,30 +730,53 @@ def _record_justificatif_etud(
) = _get_dates_from_assi_form(form)
if not ok:
log("_record_justificatif_etud: dates invalides")
form.set_error("Erreur: dates invalides")
return False
etat = scu.EtatJustificatif.get(form.etat.data)
if not form.etat.data:
log("_record_justificatif_etud: etat invalide")
form.set_error("Erreur: état invalide")
return False
etat = int(form.etat.data)
if not scu.EtatJustificatif.is_valid_etat(etat):
log(f"_record_justificatif_etud: etat invalide ({etat})")
form.set_error("Erreur: état invalide")
return False
try:
just = Justificatif.create_justificatif(
message = ""
if justif:
form.date_debut.data = dt_debut_tz_server
form.date_fin.data = dt_fin_tz_server
form.entry_date.data = dt_entry_date_tz_server
if justif.edit_from_form(form):
message = "Justificatif modifié"
else:
message = "Pas de modification"
else:
justif = Justificatif.create_justificatif(
etud,
dt_debut_tz_server,
dt_fin_tz_server,
etat=etat,
raison=form.assi_raison.data,
raison=form.raison.data,
entry_date=dt_entry_date_tz_server,
user_id=current_user.id,
)
db.session.add(just)
if not _upload_justificatif_files(just, form):
message = "Justificatif créé"
db.session.add(justif)
if not _upload_justificatif_files(justif, form):
flash("Erreur enregistrement fichiers")
log("problem in _upload_justificatif_files, rolling back")
db.session.rollback()
return False
db.session.commit()
compute_assiduites_justified(etud.id, [just])
scass.simple_invalidate_cache(just.to_dict(), etud.id)
flash("Justificatif enregistré")
compute_assiduites_justified(etud.id, [justif])
scass.simple_invalidate_cache(justif.to_dict(), etud.id)
flash(message)
return True
except ScoValueError as exc:
log(f"_record_justificatif_etud: erreur {exc.args[0]}")
db.session.rollback()
form.set_error(f"Erreur: {exc.args[0]}")
return False
@ -1474,10 +1555,10 @@ def _action_modifier_assiduite(assi: Assiduite):
def _action_modifier_justificatif(justi: Justificatif):
"Modifie le justificatif avec les valeurs dans le form"
form = request.form
# Gestion des Dates
date_debut: datetime = scu.is_iso_formated(form["date_debut"], True)
date_fin: datetime = scu.is_iso_formated(form["date_fin"], True)
if date_debut is None or date_fin is None or date_fin < date_debut:
@ -1556,40 +1637,30 @@ def _preparer_objet(
_preparer_objet("justificatif", justi, sans_gros_objet=True)
)
else:
else: # objet == "justificatif"
justif: Justificatif = objet
objet_prepare["etat"] = (
scu.EtatJustificatif(objet.etat).version_lisible().capitalize()
scu.EtatJustificatif(justif.etat).version_lisible().capitalize()
)
objet_prepare["real_etat"] = scu.EtatJustificatif(objet.etat).name.lower()
objet_prepare["raison"] = "" if objet.raison is None else objet.raison
objet_prepare["real_etat"] = scu.EtatJustificatif(justif.etat).name.lower()
objet_prepare["raison"] = "" if justif.raison is None else justif.raison
objet_prepare["raison"] = objet_prepare["raison"].strip()
objet_prepare["justification"] = {"assiduites": [], "fichiers": {}}
if not sans_gros_objet:
assiduites: list[int] = scass.justifies(objet)
assiduites: list[int] = scass.justifies(justif)
for assi_id in assiduites:
assi: Assiduite = Assiduite.query.get(assi_id)
objet_prepare["justification"]["assiduites"].append(
_preparer_objet("assiduite", assi, sans_gros_objet=True)
)
# Récupération de l'archive avec l'archiver
archive_name: str = objet.fichier
filenames: list[str] = []
archiver: JustificatifArchiver = JustificatifArchiver()
if archive_name is not None:
filenames = archiver.list_justificatifs(archive_name, objet.etudiant)
# fichiers justificatifs archivés:
filenames, nb_files = justif.get_fichiers()
objet_prepare["justification"]["fichiers"] = {
"total": len(filenames),
"filenames": [],
"total": nb_files,
"filenames": filenames,
}
for filename in filenames:
if int(filename[1]) == current_user.id or current_user.has_permission(
Permission.AbsJustifView
):
objet_prepare["justification"]["fichiers"]["filenames"].append(
filename[0]
)
objet_prepare["date_fin"] = objet.date_fin.strftime("%d/%m/%y à %H:%M")
objet_prepare["real_date_fin"] = objet.date_fin.isoformat()
@ -1600,7 +1671,7 @@ def _preparer_objet(
objet_prepare["etud_nom"] = objet.etudiant.nomprenom
if objet.user_id != None:
if objet.user_id is not None:
user: User = User.query.get(objet.user_id)
objet_prepare["saisie_par"] = user.get_nomprenom()
else: