diff --git a/app/__init__.py b/app/__init__.py index 51e122cd..72cc1cb7 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -26,6 +26,7 @@ from flask_mail import Mail from flask_bootstrap import Bootstrap from flask_moment import Moment from flask_caching import Cache +from jinja2 import select_autoescape import sqlalchemy from app.scodoc.sco_exceptions import ( @@ -61,11 +62,11 @@ cache = Cache( def handle_sco_value_error(exc): - return render_template("sco_value_error.html", exc=exc), 404 + return render_template("sco_value_error.j2", exc=exc), 404 def handle_access_denied(exc): - return render_template("error_access_denied.html", exc=exc), 403 + return render_template("error_access_denied.j2", exc=exc), 403 def internal_server_error(exc): @@ -75,7 +76,7 @@ def internal_server_error(exc): return ( render_template( - "error_500.html", + "error_500.j2", SCOVERSION=sco_version.SCOVERSION, date=datetime.datetime.now().isoformat(), exc=exc, @@ -146,7 +147,7 @@ def render_raw_html(template_filename: str, **args) -> str: def postgresql_server_error(e): """Erreur de connection au serveur postgresql (voir notesdb.open_db_connection)""" - return render_raw_html("error_503.html", SCOVERSION=sco_version.SCOVERSION), 503 + return render_raw_html("error_503.j2", SCOVERSION=sco_version.SCOVERSION), 503 class LogRequestFormatter(logging.Formatter): @@ -275,6 +276,9 @@ def create_app(config_class=DevConfig): from app.api import api_bp from app.api import api_web_bp + # Enable autoescaping of all templates, including .j2 + app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True) + # https://scodoc.fr/ScoDoc app.register_blueprint(scodoc_bp) # https://scodoc.fr/ScoDoc/RT/Scolarite/... diff --git a/app/auth/email.py b/app/auth/email.py index 61759691..9ea8f23e 100644 --- a/app/auth/email.py +++ b/app/auth/email.py @@ -11,5 +11,5 @@ def send_password_reset_email(user): sender=current_app.config["SCODOC_MAIL_FROM"], recipients=[user.email], text_body=render_template("email/reset_password.txt", user=user, token=token), - html_body=render_template("email/reset_password.html", user=user, token=token), + html_body=render_template("email/reset_password.j2", user=user, token=token), ) diff --git a/app/auth/routes.py b/app/auth/routes.py index 46d144d6..2c1594bc 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -42,7 +42,7 @@ def login(): return form.redirect("scodoc.index") message = request.args.get("message", "") return render_template( - "auth/login.html", title=_("Sign In"), form=form, message=message + "auth/login.j2", title=_("Sign In"), form=form, message=message ) @@ -65,9 +65,7 @@ def create_user(): db.session.commit() flash(f"Utilisateur {user.user_name} créé") return redirect(url_for("scodoc.index")) - return render_template( - "auth/register.html", title="Création utilisateur", form=form - ) + return render_template("auth/register.j2", title="Création utilisateur", form=form) @bp.route("/reset_password_request", methods=["GET", "POST"]) @@ -98,7 +96,7 @@ def reset_password_request(): ) return redirect(url_for("auth.login")) return render_template( - "auth/reset_password_request.html", title=_("Reset Password"), form=form + "auth/reset_password_request.j2", title=_("Reset Password"), form=form ) @@ -116,7 +114,7 @@ def reset_password(token): db.session.commit() flash(_("Votre mot de passe a été changé.")) return redirect(url_for("auth.login")) - return render_template("auth/reset_password.html", form=form, user=user) + return render_template("auth/reset_password.j2", form=form, user=user) @bp.route("/reset_standard_roles_permissions", methods=["GET", "POST"]) diff --git a/app/but/jury_but.py b/app/but/jury_but.py index 7ab98972..273d1451 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -841,6 +841,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): et autorisations d'inscription émises. Efface même si étudiant DEM ou DEF. Si à cheval, n'efface que pour le semestre d'origine du deca. + (commite la session.) """ if only_one_sem or self.a_cheval: # N'efface que les autorisations venant de ce semestre, diff --git a/app/but/jury_but_view.py b/app/but/jury_but_view.py index a58f41e2..15934474 100644 --- a/app/but/jury_but_view.py +++ b/app/but/jury_but_view.py @@ -500,7 +500,7 @@ def jury_but_semestriel( H.append("") H.append( render_template( - "but/documentation_codes_jury.html", + "but/documentation_codes_jury.j2", nom_univ=f"""Export {sco_preferences.get_preference("InstituteName") or sco_preferences.get_preference("UnivName") or "Apogée"}""", diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index 2728f336..6374e007 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -430,6 +430,22 @@ class BonusAmiens(BonusSportAdditif): # ) +class BonusBesanconVesoul(BonusSportAdditif): + """Bonus IUT Besançon - Vesoul pour les UE libres + +

Toute note non nulle, peu importe sa valeur, entraine un bonus de 0,2 point + sur toutes les moyennes d'UE. +

+ """ + + name = "bonus_besancon_vesoul" + displayed_name = "IUT de Besançon - Vesoul" + classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP + seuil_moy_gen = 0.0 # tous les points sont comptés + proportion_point = 1e10 # infini + bonus_max = 0.2 + + class BonusBethune(BonusSportMultiplicatif): """ Calcul bonus modules optionnels (sport, culture), règle IUT de Béthune. @@ -647,7 +663,10 @@ class BonusCalais(BonusSportAdditif): dans la limite de 10 points. 6% de ces points cumulés s'ajoutent : """ @@ -1339,6 +1358,44 @@ class BonusIUTvannes(BonusSportAdditif): classic_use_bonus_ues = False # seulement sur moy gen. +class BonusValenciennes(BonusDirect): + """Article 7 des RCC de l’IUT de Valenciennes + +

+ Une bonification maximale de 0.25 point (1/4 de point) peut être ajoutée + à la moyenne de chaque Unité d’Enseignement pour : +

+ + +

+ Une bonification accordée par la commission des sports de l’UPHF peut être attribuée + aux sportifs de haut niveau. Cette bonification est appliquée à l’ensemble des + Unités d’Enseignement. Ce bonus est : +

+ +

Le cumul de bonifications est possible mais ne peut excéder 0.5 point (un demi-point). +

+

Dans ScoDoc, saisir directement la valeur désirée du bonus + dans une évaluation notée sur 20. +

+ """ + + name = "bonus_valenciennes" + displayed_name = "IUT de Valenciennes" + bonus_max = 0.5 + + class BonusVilleAvray(BonusSportAdditif): """Bonus modules optionnels (sport, culture), règle IUT Ville d'Avray. diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index 2d419500..e50ebf71 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -89,7 +89,7 @@ def index(): visible=True, association=True, siret_provisoire=True ) return render_template( - "entreprises/entreprises.html", + "entreprises/entreprises.j2", title="Entreprises", entreprises=entreprises, logs=logs, @@ -109,7 +109,7 @@ def logs(): EntrepriseHistorique.date.desc() ).paginate(page=page, per_page=20) return render_template( - "entreprises/logs.html", + "entreprises/logs.j2", title="Logs", logs=logs, ) @@ -134,7 +134,7 @@ def correspondants(): .all() ) return render_template( - "entreprises/correspondants.html", + "entreprises/correspondants.j2", title="Correspondants", correspondants=correspondants, logs=logs, @@ -149,7 +149,7 @@ def validation(): """ entreprises = Entreprise.query.filter_by(visible=False).all() return render_template( - "entreprises/entreprises_validation.html", + "entreprises/entreprises_validation.j2", title="Validation entreprises", entreprises=entreprises, ) @@ -167,7 +167,7 @@ def fiche_entreprise_validation(entreprise_id): description=f"fiche entreprise (validation) {entreprise_id} inconnue" ) return render_template( - "entreprises/fiche_entreprise_validation.html", + "entreprises/fiche_entreprise_validation.j2", title="Validation fiche entreprise", entreprise=entreprise, ) @@ -205,7 +205,7 @@ def validate_entreprise(entreprise_id): flash("L'entreprise a été validé et ajouté à la liste.") return redirect(url_for("entreprises.validation")) return render_template( - "entreprises/form_validate_confirmation.html", + "entreprises/form_validate_confirmation.j2", title="Validation entreprise", form=form, ) @@ -242,7 +242,7 @@ def delete_validation_entreprise(entreprise_id): flash("L'entreprise a été supprimé de la liste des entreprise à valider.") return redirect(url_for("entreprises.validation")) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Supression entreprise", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -282,7 +282,7 @@ def offres_recues(): files.append(file) offres_recues_with_files.append([envoi_offre, offre, files, correspondant]) return render_template( - "entreprises/offres_recues.html", + "entreprises/offres_recues.j2", title="Offres reçues", offres_recues=offres_recues_with_files, ) @@ -321,7 +321,7 @@ def preferences(): form.mail_entreprise.data = EntreprisePreferences.get_email_notifications() form.check_siret.data = int(EntreprisePreferences.get_check_siret()) return render_template( - "entreprises/preferences.html", + "entreprises/preferences.j2", title="Préférences", form=form, ) @@ -357,7 +357,7 @@ def add_entreprise(): db.session.rollback() flash("Une erreur est survenue veuillez réessayer.") return render_template( - "entreprises/form_ajout_entreprise.html", + "entreprises/form_ajout_entreprise.j2", title="Ajout entreprise avec correspondant", form=form, ) @@ -408,7 +408,7 @@ def add_entreprise(): flash("L'entreprise a été ajouté à la liste pour la validation.") return redirect(url_for("entreprises.index")) return render_template( - "entreprises/form_ajout_entreprise.html", + "entreprises/form_ajout_entreprise.j2", title="Ajout entreprise avec correspondant", form=form, ) @@ -446,7 +446,7 @@ def fiche_entreprise(entreprise_id): .all() ) return render_template( - "entreprises/fiche_entreprise.html", + "entreprises/fiche_entreprise.j2", title="Fiche entreprise", entreprise=entreprise, offres=offres_with_files, @@ -472,7 +472,7 @@ def logs_entreprise(entreprise_id): .paginate(page=page, per_page=20) ) return render_template( - "entreprises/logs_entreprise.html", + "entreprises/logs_entreprise.j2", title="Logs", logs=logs, entreprise=entreprise, @@ -490,7 +490,7 @@ def offres_expirees(entreprise_id): ).first_or_404(description=f"fiche entreprise {entreprise_id} inconnue") offres_with_files = are.get_offres_expirees_with_files(entreprise.offres) return render_template( - "entreprises/offres_expirees.html", + "entreprises/offres_expirees.j2", title="Offres expirées", entreprise=entreprise, offres_expirees=offres_with_files, @@ -574,7 +574,7 @@ def edit_entreprise(entreprise_id): form.pays.data = entreprise.pays form.association.data = entreprise.association return render_template( - "entreprises/form_modification_entreprise.html", + "entreprises/form_modification_entreprise.j2", title="Modification entreprise", form=form, ) @@ -610,7 +610,7 @@ def fiche_entreprise_desactiver(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Désactiver entreprise", form=form, info_message="Cliquez sur le bouton Modifier pour confirmer la désactivation", @@ -646,7 +646,7 @@ def fiche_entreprise_activer(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Activer entreprise", form=form, info_message="Cliquez sur le bouton Modifier pour confirmer l'activaction", @@ -692,7 +692,7 @@ def add_taxe_apprentissage(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Ajout taxe apprentissage", form=form, ) @@ -735,7 +735,7 @@ def edit_taxe_apprentissage(entreprise_id, taxe_id): form.montant.data = taxe.montant form.notes.data = taxe.notes return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Modification taxe apprentissage", form=form, ) @@ -775,7 +775,7 @@ def delete_taxe_apprentissage(entreprise_id, taxe_id): url_for("entreprises.fiche_entreprise", entreprise_id=taxe.entreprise_id) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Supprimer taxe apprentissage", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -845,7 +845,7 @@ def add_offre(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Ajout offre", form=form, ) @@ -921,7 +921,7 @@ def edit_offre(entreprise_id, offre_id): form.expiration_date.data = offre.expiration_date form.depts.data = offre_depts_list return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Modification offre", form=form, ) @@ -971,7 +971,7 @@ def delete_offre(entreprise_id, offre_id): url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Supression offre", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1047,7 +1047,7 @@ def add_site(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Ajout site", form=form, ) @@ -1098,7 +1098,7 @@ def edit_site(entreprise_id, site_id): form.ville.data = site.ville form.pays.data = site.pays return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Modification site", form=form, ) @@ -1154,7 +1154,7 @@ def add_correspondant(entreprise_id, site_id): url_for("entreprises.fiche_entreprise", entreprise_id=site.entreprise_id) ) return render_template( - "entreprises/form_ajout_correspondants.html", + "entreprises/form_ajout_correspondants.j2", title="Ajout correspondant", form=form, ) @@ -1234,7 +1234,7 @@ def edit_correspondant(entreprise_id, site_id, correspondant_id): form.origine.data = correspondant.origine form.notes.data = correspondant.notes return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Modification correspondant", form=form, ) @@ -1290,7 +1290,7 @@ def delete_correspondant(entreprise_id, site_id, correspondant_id): ) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Supression correspondant", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1308,7 +1308,7 @@ def contacts(entreprise_id): ).first_or_404(description=f"entreprise {entreprise_id} inconnue") contacts = EntrepriseContact.query.filter_by(entreprise=entreprise.id).all() return render_template( - "entreprises/contacts.html", + "entreprises/contacts.j2", title="Liste des contacts", contacts=contacts, entreprise=entreprise, @@ -1365,7 +1365,7 @@ def add_contact(entreprise_id): db.session.commit() return redirect(url_for("entreprises.contacts", entreprise_id=entreprise.id)) return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Ajout contact", form=form, ) @@ -1421,7 +1421,7 @@ def edit_contact(entreprise_id, contact_id): ) form.notes.data = contact.notes return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Modification contact", form=form, ) @@ -1459,7 +1459,7 @@ def delete_contact(entreprise_id, contact_id): url_for("entreprises.contacts", entreprise_id=contact.entreprise) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Supression contact", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1525,7 +1525,7 @@ def add_stage_apprentissage(entreprise_id): url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) ) return render_template( - "entreprises/form_ajout_stage_apprentissage.html", + "entreprises/form_ajout_stage_apprentissage.j2", title="Ajout stage / apprentissage", form=form, ) @@ -1599,7 +1599,7 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id): form.date_fin.data = stage_apprentissage.date_fin form.notes.data = stage_apprentissage.notes return render_template( - "entreprises/form_ajout_stage_apprentissage.html", + "entreprises/form_ajout_stage_apprentissage.j2", title="Modification stage / apprentissage", form=form, ) @@ -1640,7 +1640,7 @@ def delete_stage_apprentissage(entreprise_id, stage_apprentissage_id): ) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Supression stage/apprentissage", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1690,7 +1690,7 @@ def envoyer_offre(entreprise_id, offre_id): url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) ) return render_template( - "entreprises/form_envoi_offre.html", + "entreprises/form_envoi_offre.j2", title="Envoyer une offre", form=form, ) @@ -1816,7 +1816,7 @@ def import_donnees(): db.session.rollback() flash("Une erreur est survenue veuillez réessayer.") return render_template( - "entreprises/import_donnees.html", + "entreprises/import_donnees.j2", title="Importation données", form=form, ) @@ -1845,7 +1845,7 @@ def import_donnees(): db.session.commit() flash(f"Importation réussie") return render_template( - "entreprises/import_donnees.html", + "entreprises/import_donnees.j2", title="Importation données", form=form, entreprises_import=entreprises_import, @@ -1853,7 +1853,7 @@ def import_donnees(): correspondants_import=correspondants, ) return render_template( - "entreprises/import_donnees.html", title="Importation données", form=form + "entreprises/import_donnees.j2", title="Importation données", form=form ) @@ -1927,7 +1927,7 @@ def add_offre_file(entreprise_id, offre_id): url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) ) return render_template( - "entreprises/form.html", + "entreprises/form.j2", title="Ajout fichier à une offre", form=form, ) @@ -1969,7 +1969,7 @@ def delete_offre_file(entreprise_id, offre_id, filedir): ) ) return render_template( - "entreprises/form_confirmation.html", + "entreprises/form_confirmation.j2", title="Suppression fichier d'une offre", form=form, info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", @@ -1981,4 +1981,4 @@ def not_found_error_handler(e): """ Renvoie une page d'erreur pour l'erreur 404 """ - return render_template("entreprises/error.html", title="Erreur", e=e) + return render_template("entreprises/error.j2", title="Erreur", e=e) diff --git a/app/forms/main/config_logos.py b/app/forms/main/config_logos.py index 2a0051f0..2a54dd7c 100644 --- a/app/forms/main/config_logos.py +++ b/app/forms/main/config_logos.py @@ -171,7 +171,7 @@ class AddLogoForm(FlaskForm): class LogoForm(FlaskForm): - """Embed both presentation of a logo (cf. template file configuration.html) + """Embed both presentation of a logo (cf. template file configuration.j2) and all its data and UI action (change, delete)""" dept_key = HiddenField() @@ -434,7 +434,7 @@ def config_logos(): scu.flash_errors(form) return render_template( - "config_logos.html", + "config_logos.j2", scodoc_dept=None, title="Configuration ScoDoc", form=form, diff --git a/app/forms/main/config_main.py b/app/forms/main/config_main.py index 205c88fa..4cc539fb 100644 --- a/app/forms/main/config_main.py +++ b/app/forms/main/config_main.py @@ -133,7 +133,7 @@ def configuration(): return redirect(url_for("scodoc.index")) return render_template( - "configuration.html", + "configuration.j2", form_bonus=form_bonus, form_scodoc=form_scodoc, scu=scu, diff --git a/app/models/formations.py b/app/models/formations.py index 986ef7e7..5bd4a8d9 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -36,6 +36,7 @@ class Formation(db.Model): titre = db.Column(db.Text(), nullable=False) titre_officiel = db.Column(db.Text(), nullable=False) version = db.Column(db.Integer, default=1, server_default="1") + commentaire = db.Column(db.Text()) formation_code = db.Column( db.String(SHORT_STR_LEN), server_default=db.text("notes_newid_fcod()"), diff --git a/app/scodoc/html_sco_header.py b/app/scodoc/html_sco_header.py index 7098757c..e4d330a7 100644 --- a/app/scodoc/html_sco_header.py +++ b/app/scodoc/html_sco_header.py @@ -274,7 +274,7 @@ def sco_header( H.append("""
""") # En attendant le replacement complet de cette fonction, # inclusion ici des messages flask - H.append(render_template("flashed_messages.html")) + H.append(render_template("flashed_messages.j2")) # # Barre menu semestre: H.append(formsemestre_page_title(formsemestre_id)) diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index a487fcf0..33132a05 100644 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -166,6 +166,6 @@ def sidebar(etudid: int = None): def sidebar_dept(): """Partie supérieure de la marge de gauche""" return render_template( - "sidebar_dept.html", + "sidebar_dept.j2", prefs=sco_preferences.SemPreferences(), ) diff --git a/app/scodoc/sco_archives_etud.py b/app/scodoc/sco_archives_etud.py index 799024db..27189214 100644 --- a/app/scodoc/sco_archives_etud.py +++ b/app/scodoc/sco_archives_etud.py @@ -373,7 +373,7 @@ def etudarchive_import_files( filename_title="fichier_a_charger", ) return render_template( - "scolar/photos_import_files.html", + "scolar/photos_import_files.j2", page_title="Téléchargement de fichiers associés aux étudiants", ignored_zipfiles=ignored_zipfiles, unmatched_files=unmatched_files, diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index e47177d6..392a20bd 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -926,7 +926,7 @@ def formsemestre_bulletinetud( _formsemestre_bulletinetud_header_html(etud, formsemestre, format, version), bulletin, render_template( - "bul_foot.html", + "bul_foot.j2", appreciations=None, # déjà affichées css_class="bul_classic_foot", etud=etud, @@ -1259,7 +1259,7 @@ def _formsemestre_bulletinetud_header_html( cssstyles=["css/radar_bulletin.css"], ), render_template( - "bul_head.html", + "bul_head.j2", etud=etud, format=format, formsemestre=formsemestre, diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index c64d732e..b52e3d35 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -234,6 +234,16 @@ def formation_edit(formation_id=None, create=False): "explanation": "optionel: code utilisé pour échanger avec d'autres logiciels et identifiant la filière ou spécialité (exemple: ASUR). N'est utilisé que s'il n'y a pas de numéro de semestre.", }, ), + ( + "commentaire", + { + "input_type": "textarea", + "rows": 3, + "cols": 77, + "title": "Commentaire", + "explanation": "commentaire libre.", + }, + ), ), initvalues=initvalues, submitlabel=submitlabel, diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index c8e6d0fd..c4b1624c 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -385,7 +385,7 @@ def module_edit( ), f"""

{title}

""", render_template( - "scodoc/help/modules.html", + "scodoc/help/modules.j2", is_apc=is_apc, semestre_id=semestre_id, formsemestres=FormSemestre.query.filter( @@ -396,6 +396,7 @@ def module_edit( .all() if not create else None, + create=create, ), ] if not unlocked: @@ -655,7 +656,8 @@ def module_edit( ( "numero", { - "size": 2, + "title": "Numéro", + "size": 4, "explanation": "numéro (1, 2, 3, 4, ...) pour ordre d'affichage", "type": "int", "default": default_num, diff --git a/app/scodoc/sco_evaluation_edit.py b/app/scodoc/sco_evaluation_edit.py index 900827d4..6c418519 100644 --- a/app/scodoc/sco_evaluation_edit.py +++ b/app/scodoc/sco_evaluation_edit.py @@ -345,7 +345,7 @@ def evaluation_create_form( + "\n".join(H) + "\n" + tf[1] - + render_template("scodoc/help/evaluations.html", is_apc=is_apc) + + render_template("scodoc/help/evaluations.j2", is_apc=is_apc) + html_sco_header.sco_footer() ) elif tf[0] == -1: diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index b28590eb..9be52db4 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -75,6 +75,7 @@ _formationEditor = ndb.EditableTable( "type_parcours", "code_specialite", "referentiel_competence_id", + "commentaire", ), filter_dept=True, sortkey="acronyme", diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index c3ab9690..40224408 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -541,7 +541,7 @@ def formsemestre_page_title(formsemestre_id=None): formsemestre = FormSemestre.query.get_or_404(formsemestre_id) h = render_template( - "formsemestre_page_title.html", + "formsemestre_page_title.j2", formsemestre=formsemestre, scu=scu, sem_menu_bar=formsemestre_status_menubar(formsemestre), diff --git a/app/scodoc/sco_groups_edit.py b/app/scodoc/sco_groups_edit.py index 3af2c963..5caba3be 100644 --- a/app/scodoc/sco_groups_edit.py +++ b/app/scodoc/sco_groups_edit.py @@ -46,7 +46,7 @@ def affect_groups(partition_id): raise AccessDenied("vous n'avez pas la permission de modifier les groupes") partition.formsemestre.setup_parcours_groups() return render_template( - "scolar/affect_groups.html", + "scolar/affect_groups.j2", sco_header=html_sco_header.sco_header( page_title="Affectation aux groupes", javascripts=["js/groupmgr.js"], diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index 0e0ec844..6ce07aa8 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -215,7 +215,7 @@ def placement_eval_selectetuds(evaluation_id): html_sco_header.sco_header(), sco_evaluations.evaluation_describe(evaluation_id=evaluation_id), "

Placement et émargement des étudiants

", - render_template("scodoc/forms/placement.html", form=form), + render_template("scodoc/forms/placement.j2", form=form), ] footer = html_sco_header.sco_footer() return "\n".join(htmls) + "

" + footer diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index a92366ba..f9ffb565 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -166,9 +166,15 @@ def formsemestre_recapcomplet( H.append("

") if mode_jury: H.append( - f"""Calcul automatique des décisions du jury

""" + }">Calcul automatique des décisions du jury +

Effacer toutes les décisions de jury du semestre +

+

+ """ ) else: H.append( diff --git a/app/scodoc/sco_trombino.py b/app/scodoc/sco_trombino.py index cf725794..140d1634 100644 --- a/app/scodoc/sco_trombino.py +++ b/app/scodoc/sco_trombino.py @@ -554,7 +554,7 @@ def photos_import_files_form(group_ids=()): back_url=back_url, ) return render_template( - "scolar/photos_import_files.html", + "scolar/photos_import_files.j2", page_title="Téléchargement des photos des étudiants", ignored_zipfiles=ignored_zipfiles, unmatched_files=unmatched_files, diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index ff76d3c4..d1b5fb79 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -4,6 +4,7 @@ :root { --sco-content-min-width: 600px; --sco-content-max-width: 1024px; + --sco-color-explication: rgb(10, 58, 140); } html, @@ -2115,6 +2116,11 @@ div.formation_descr span.fd_n { margin-left: 6em; } +span.explication { + font-style: italic; + color: var(--sco-color-explication); +} + div.formation_ue_list { border: 1px solid black; margin-top: 5px; diff --git a/app/static/js/table_recap.js b/app/static/js/table_recap.js index 3e59dc51..7431ae5a 100644 --- a/app/static/js/table_recap.js +++ b/app/static/js/table_recap.js @@ -14,6 +14,8 @@ $(function () { const url = new URL(document.URL); const formsemestre_id = url.searchParams.get("formsemestre_id"); const order_info_key = JSON.stringify([url.pathname, formsemestre_id]); + const etudids_key = JSON.stringify(["etudids", url.origin, formsemestre_id]); + const noms_key = JSON.stringify(["noms", url.origin, formsemestre_id]); let order_info; if (formsemestre_id) { const x = localStorage.getItem(order_info_key); @@ -157,83 +159,89 @@ $(function () { } }); } - let table = $('table.table_recap').DataTable( - { - paging: false, - searching: true, - info: false, - autoWidth: false, - fixedHeader: { - header: true, - footer: false - }, - orderCellsTop: true, // cellules ligne 1 pour tri - aaSorting: [], // Prevent initial sorting - colReorder: true, - stateSave: true, // enregistre état de la table (tris, ...) - "columnDefs": [ - { - // cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides - targets: hidden_colums, - visible: false, + try { + let table = $('table.table_recap').DataTable( + { + paging: false, + searching: true, + info: false, + autoWidth: false, + fixedHeader: { + header: true, + footer: false }, - { - // Elimine les 0 à gauche pour les exports excel et les "copy" - targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation", "col_rcue"], - render: function (data, type, row) { - return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data; + orderCellsTop: true, // cellules ligne 1 pour tri + aaSorting: [], // Prevent initial sorting + colReorder: true, + stateSave: true, // enregistre état de la table (tris, ...) + "columnDefs": [ + { + // cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides + targets: hidden_colums, + visible: false, + }, + { + // Elimine les 0 à gauche pour les exports excel et les "copy" + targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation", "col_rcue"], + render: function (data, type, row) { + return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data; + } + }, + { + // Elimine les décorations (fleches bonus/malus) pour les exports + targets: ["col_ue_bonus", "col_malus"], + render: function (data, type, row) { + return type === 'export' ? data.replace(/.*(\d\d\.\d\d)/, '$1').replace(/0(\d\..*)/, '$1') : data; + } + }, + ], + dom: 'Bfrtip', + buttons: [ + { + extend: 'copyHtml5', + text: 'Copier', + exportOptions: { orthogonal: 'export' } + }, + { + extend: 'excelHtml5', + // footer: true, // ne fonctionne pas ? + exportOptions: { orthogonal: 'export' }, + title: document.querySelector('table.table_recap').dataset.filename + }, + { + extend: 'collection', + text: 'Colonnes affichées', + autoClose: true, + buttons: buttons, + }, + ], + "drawCallback": function (settings) { + // permet de conserver l'ordre de tri des colonnes + let order_info = JSON.stringify($('table.table_recap').DataTable().order()); + if (formsemestre_id) { + localStorage.setItem(order_info_key, order_info); } + let etudids = []; + document.querySelectorAll("td.identite_court").forEach(e => { + etudids.push(e.dataset.etudid); + }); + let noms = []; + document.querySelectorAll("td.identite_court").forEach(e => { + noms.push(e.dataset.nomprenom); + }); + localStorage.setItem(etudids_key, JSON.stringify(etudids)); + localStorage.setItem(noms_key, JSON.stringify(noms)); }, - { - // Elimine les décorations (fleches bonus/malus) pour les exports - targets: ["col_ue_bonus", "col_malus"], - render: function (data, type, row) { - return type === 'export' ? data.replace(/.*(\d\d\.\d\d)/, '$1').replace(/0(\d\..*)/, '$1') : data; - } - }, - ], - dom: 'Bfrtip', - buttons: [ - { - extend: 'copyHtml5', - text: 'Copier', - exportOptions: { orthogonal: 'export' } - }, - { - extend: 'excelHtml5', - // footer: true, // ne fonctionne pas ? - exportOptions: { orthogonal: 'export' }, - title: document.querySelector('table.table_recap').dataset.filename - }, - { - extend: 'collection', - text: 'Colonnes affichées', - autoClose: true, - buttons: buttons, - }, - ], - "drawCallback": function (settings) { - // permet de conserver l'ordre de tri des colonnes - let order_info = JSON.stringify($('table.table_recap').DataTable().order()); - if (formsemestre_id) { - localStorage.setItem(order_info_key, order_info); - } - let etudids = []; - document.querySelectorAll("td.identite_court").forEach(e => { - etudids.push(e.dataset.etudid); - }); - let noms = []; - document.querySelectorAll("td.identite_court").forEach(e => { - noms.push(e.dataset.nomprenom); - }); - const etudids_key = JSON.stringify(["etudids", url.origin, formsemestre_id]); - localStorage.setItem(etudids_key, JSON.stringify(etudids)); - const noms_key = JSON.stringify(["noms", url.origin, formsemestre_id]); - localStorage.setItem(noms_key, JSON.stringify(noms)); - }, - "order": order_info, - } - ); + "order": order_info, + } + ); + } catch (error) { + // l'erreur peut etre causee par un ancien storage: + localStorage.removeItem(etudids_key); + localStorage.removeItem(noms_key); + localStorage.removeItem(order_info_key); + location.reload(); + } update_buttons_labels(table); }); $('table.table_recap tbody').on('click', 'tr', function () { diff --git a/app/templates/about.html b/app/templates/about.j2 similarity index 56% rename from app/templates/about.html rename to app/templates/about.j2 index 0b2eed38..23dc1ce5 100644 --- a/app/templates/about.html +++ b/app/templates/about.j2 @@ -1,5 +1,5 @@ {# -*- mode: jinja-html -*- #} -{% extends 'base.html' %} +{% extends 'base.j2' %} {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} @@ -9,10 +9,10 @@

© Emmanuel Viennet 2021

Version {{ version }}

- -

ScoDoc est un logiciel libre écrit en -Python. -Information et documentation sur scodoc.org. + +

ScoDoc est un logiciel libre écrit en + Python. + Information et documentation sur scodoc.org.

Dernières évolutions

diff --git a/app/templates/auth/change_password.html b/app/templates/auth/change_password.j2 similarity index 57% rename from app/templates/auth/change_password.html rename to app/templates/auth/change_password.j2 index 11e84481..5f0dbe00 100644 --- a/app/templates/auth/change_password.html +++ b/app/templates/auth/change_password.j2 @@ -1,14 +1,14 @@ {# -*- mode: jinja-html -*- #} -{% extends "base.html" %} +{% extends "base.j2" %} {% import 'bootstrap/wtf.html' as wtf %} {% macro render_field(field, auth_name=None) %} - {% if auth_name %} - {{ field.label }} ({{ auth_name }}): + {% if auth_name %} + {{ field.label }} ({{ auth_name }}): {% else %} - {{ field.label }} - {% endif %} + {{ field.label }} + {% endif %} {{ field(**kwargs)|safe }} {% if field.errors %}