Merge branch 'scodoc-master' into pe-moy-par-ue
This commit is contained in:
commit
d8381884dc
@ -38,14 +38,11 @@ import datetime
|
||||
from xml.etree import ElementTree
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
from app import log
|
||||
from app import db, log
|
||||
from app.but import bulletin_but
|
||||
from app.models import BulAppreciations, FormSemestre, Identite
|
||||
from app.models import BulAppreciations, FormSemestre, Identite, UniteEns
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_photos
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_xml
|
||||
@ -202,12 +199,12 @@ def bulletin_but_xml_compat(
|
||||
if e.visibulletin or version == "long":
|
||||
x_eval = Element(
|
||||
"evaluation",
|
||||
date_debut=e.date_debut.isoformat()
|
||||
if e.date_debut
|
||||
else "",
|
||||
date_fin=e.date_fin.isoformat()
|
||||
if e.date_debut
|
||||
else "",
|
||||
date_debut=(
|
||||
e.date_debut.isoformat() if e.date_debut else ""
|
||||
),
|
||||
date_fin=(
|
||||
e.date_fin.isoformat() if e.date_debut else ""
|
||||
),
|
||||
coefficient=str(e.coefficient),
|
||||
# pas les poids en XML compat
|
||||
evaluation_type=str(e.evaluation_type),
|
||||
@ -215,9 +212,9 @@ def bulletin_but_xml_compat(
|
||||
# notes envoyées sur 20, ceci juste pour garder trace:
|
||||
note_max_origin=str(e.note_max),
|
||||
# --- deprecated
|
||||
jour=e.date_debut.isoformat()
|
||||
if e.date_debut
|
||||
else "",
|
||||
jour=(
|
||||
e.date_debut.isoformat() if e.date_debut else ""
|
||||
),
|
||||
heure_debut=e.heure_debut(),
|
||||
heure_fin=e.heure_fin(),
|
||||
)
|
||||
@ -294,17 +291,18 @@ def bulletin_but_xml_compat(
|
||||
"decisions_ue"
|
||||
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
|
||||
for ue_id in decision["decisions_ue"].keys():
|
||||
ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
|
||||
doc.append(
|
||||
Element(
|
||||
"decision_ue",
|
||||
ue_id=str(ue["ue_id"]),
|
||||
numero=quote_xml_attr(ue["numero"]),
|
||||
acronyme=quote_xml_attr(ue["acronyme"]),
|
||||
titre=quote_xml_attr(ue["titre"]),
|
||||
code=decision["decisions_ue"][ue_id]["code"],
|
||||
ue = db.session.get(UniteEns, ue_id)
|
||||
if ue:
|
||||
doc.append(
|
||||
Element(
|
||||
"decision_ue",
|
||||
ue_id=str(ue.id),
|
||||
numero=quote_xml_attr(ue.numero),
|
||||
acronyme=quote_xml_attr(ue.acronyme),
|
||||
titre=quote_xml_attr(ue.titre or ""),
|
||||
code=decision["decisions_ue"][ue_id]["code"],
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
for aut in decision["autorisations"]:
|
||||
doc.append(
|
||||
|
@ -58,7 +58,6 @@ class NotesTableCompat(ResultatsSemestre):
|
||||
self.moy_moy = "NA"
|
||||
self.moy_gen_rangs_by_group = {} # { group_id : (Series, Series) }
|
||||
self.ue_rangs_by_group = {} # { ue_id : {group_id : (Series, Series)}}
|
||||
self.expr_diagnostics = ""
|
||||
self.parcours = self.formsemestre.formation.get_cursus()
|
||||
self._modimpls_dict_by_ue = {} # local cache
|
||||
|
||||
@ -217,9 +216,9 @@ class NotesTableCompat(ResultatsSemestre):
|
||||
# Rangs / UEs:
|
||||
for ue in ues:
|
||||
group_moys_ue = self.etud_moy_ue[ue.id][group_members]
|
||||
self.ue_rangs_by_group.setdefault(ue.id, {})[
|
||||
group.id
|
||||
] = moy_sem.comp_ranks_series(group_moys_ue * mask_inscr)
|
||||
self.ue_rangs_by_group.setdefault(ue.id, {})[group.id] = (
|
||||
moy_sem.comp_ranks_series(group_moys_ue * mask_inscr)
|
||||
)
|
||||
|
||||
def get_etud_rang(self, etudid: int) -> str:
|
||||
"""Le rang (classement) de l'étudiant dans le semestre.
|
||||
|
@ -79,6 +79,7 @@ class Evaluation(db.Model):
|
||||
):
|
||||
"""Create an evaluation. Check permission and all arguments.
|
||||
Ne crée pas les poids vers les UEs.
|
||||
Add to session, do not commit.
|
||||
"""
|
||||
if not moduleimpl.can_edit_evaluation(current_user):
|
||||
raise AccessDenied(
|
||||
@ -94,6 +95,8 @@ class Evaluation(db.Model):
|
||||
args["numero"] = cls.get_new_numero(moduleimpl, args["date_debut"])
|
||||
#
|
||||
evaluation = Evaluation(**args)
|
||||
db.session.add(evaluation)
|
||||
db.session.flush()
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id=moduleimpl.formsemestre_id)
|
||||
url = url_for(
|
||||
"notes.moduleimpl_status",
|
||||
@ -210,9 +213,9 @@ class Evaluation(db.Model):
|
||||
"visibulletin": self.visibulletin,
|
||||
# Deprecated (supprimer avant #sco9.7)
|
||||
"date": self.date_debut.date().isoformat() if self.date_debut else "",
|
||||
"heure_debut": self.date_debut.time().isoformat()
|
||||
if self.date_debut
|
||||
else "",
|
||||
"heure_debut": (
|
||||
self.date_debut.time().isoformat() if self.date_debut else ""
|
||||
),
|
||||
"heure_fin": self.date_fin.time().isoformat() if self.date_fin else "",
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
"""ScoDoc 9 models : Modules
|
||||
"""
|
||||
|
||||
from flask import current_app, g
|
||||
|
||||
from app import db
|
||||
from app import models
|
||||
from app.models import APO_CODE_STR_LEN
|
||||
from app.models.but_refcomp import ApcParcours, app_critiques_modules, parcours_modules
|
||||
from app.scodoc import sco_utils as scu
|
||||
@ -11,7 +13,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
|
||||
|
||||
class Module(db.Model):
|
||||
class Module(models.ScoDocModel):
|
||||
"""Module"""
|
||||
|
||||
__tablename__ = "notes_modules"
|
||||
@ -76,6 +78,28 @@ class Module(db.Model):
|
||||
return f"""<Module{ModuleType(self.module_type or ModuleType.STANDARD).name
|
||||
} id={self.id} code={self.code!r} semestre_id={self.semestre_id}>"""
|
||||
|
||||
@classmethod
|
||||
def convert_dict_fields(cls, args: dict) -> dict:
|
||||
"""Convert fields in the given dict. No other side effect.
|
||||
returns: dict to store in model's db.
|
||||
"""
|
||||
# s'assure que ects etc est non ''
|
||||
fs_empty_stored_as_nulls = {
|
||||
"coefficient",
|
||||
"ects",
|
||||
"heures_cours",
|
||||
"heures_td",
|
||||
"heures_tp",
|
||||
}
|
||||
args_dict = {}
|
||||
for key, value in args.items():
|
||||
if hasattr(cls, key) and not isinstance(getattr(cls, key, None), property):
|
||||
if key in fs_empty_stored_as_nulls and value == "":
|
||||
value = None
|
||||
args_dict[key] = value
|
||||
|
||||
return args_dict
|
||||
|
||||
def clone(self):
|
||||
"""Create a new copy of this module."""
|
||||
mod = Module(
|
||||
|
@ -409,6 +409,7 @@ class CursusBUT(TypeCursus):
|
||||
APC_SAE = True
|
||||
USE_REFERENTIEL_COMPETENCES = True
|
||||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT]
|
||||
ECTS_DIPLOME = 180
|
||||
|
||||
|
||||
register_cursus(CursusBUT())
|
||||
|
@ -44,13 +44,15 @@ import random
|
||||
from collections import OrderedDict
|
||||
from xml.etree import ElementTree
|
||||
import json
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse, urlencode, parse_qs, urlunparse
|
||||
|
||||
from openpyxl.utils import get_column_letter
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
|
||||
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
|
||||
from reportlab.platypus import Paragraph, Spacer
|
||||
from reportlab.platypus import Table, KeepInFrame
|
||||
from reportlab.lib.colors import Color
|
||||
from reportlab.lib import styles
|
||||
from reportlab.lib.units import inch, cm, mm
|
||||
from reportlab.rl_config import defaultPageSize # pylint: disable=no-name-in-module
|
||||
from reportlab.lib.units import cm
|
||||
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_utils as scu
|
||||
@ -62,16 +64,32 @@ from app.scodoc.sco_pdf import SU
|
||||
from app import log, ScoDocJSONEncoder
|
||||
|
||||
|
||||
def mark_paras(L, tags) -> list[str]:
|
||||
"""Put each (string) element of L between <tag>...</tag>,
|
||||
def mark_paras(items: list[Any], tags: list[str]) -> list[str]:
|
||||
"""Put each string element of items between <tag>...</tag>,
|
||||
for each supplied tag.
|
||||
Leave non string elements untouched.
|
||||
"""
|
||||
for tag in tags:
|
||||
start = "<" + tag + ">"
|
||||
end = "</" + tag.split()[0] + ">"
|
||||
L = [(start + (x or "") + end) if isinstance(x, str) else x for x in L]
|
||||
return L
|
||||
items = [(start + (x or "") + end) if isinstance(x, str) else x for x in items]
|
||||
return items
|
||||
|
||||
|
||||
def add_query_param(url: str, key: str, value: str) -> str:
|
||||
"add parameter key=value to the given URL"
|
||||
# Parse the URL
|
||||
parsed_url = urlparse(url)
|
||||
# Parse the query parameters
|
||||
query_params = parse_qs(parsed_url.query)
|
||||
# Add or update the query parameter
|
||||
query_params[key] = [value]
|
||||
# Encode the query parameters
|
||||
encoded_query_params = urlencode(query_params, doseq=True)
|
||||
# Construct the new URL
|
||||
new_url_parts = parsed_url._replace(query=encoded_query_params)
|
||||
new_url = urlunparse(new_url_parts)
|
||||
return new_url
|
||||
|
||||
|
||||
class DEFAULT_TABLE_PREFERENCES(object):
|
||||
@ -477,13 +495,15 @@ class GenTable:
|
||||
H.append('<span class="gt_export_icons">')
|
||||
if self.xls_link:
|
||||
H.append(
|
||||
' <a href="%s&fmt=xls">%s</a>' % (self.base_url, scu.ICON_XLS)
|
||||
f""" <a href="{add_query_param(self.base_url, "fmt", "xls")
|
||||
}">{scu.ICON_XLS}</a>"""
|
||||
)
|
||||
if self.xls_link and self.pdf_link:
|
||||
H.append(" ")
|
||||
if self.pdf_link:
|
||||
H.append(
|
||||
' <a href="%s&fmt=pdf">%s</a>' % (self.base_url, scu.ICON_PDF)
|
||||
f""" <a href="{add_query_param(self.base_url, "fmt", "pdf")
|
||||
}">{scu.ICON_PDF}</a>"""
|
||||
)
|
||||
H.append("</span>")
|
||||
H.append("</p>")
|
||||
@ -582,9 +602,11 @@ class GenTable:
|
||||
for line in data_list:
|
||||
Pt.append(
|
||||
[
|
||||
Paragraph(SU(str(x)), CellStyle)
|
||||
if (not isinstance(x, Paragraph))
|
||||
else x
|
||||
(
|
||||
Paragraph(SU(str(x)), CellStyle)
|
||||
if (not isinstance(x, Paragraph))
|
||||
else x
|
||||
)
|
||||
for x in line
|
||||
]
|
||||
)
|
||||
|
@ -109,7 +109,7 @@ def sidebar_common():
|
||||
{sidebar_dept()}
|
||||
<h2 class="insidebar">Scolarité</h2>
|
||||
<a href="{scu.ScoURL()}" class="sidebar">Semestres</a> <br>
|
||||
<a href="{scu.NotesURL()}" class="sidebar">Programmes</a> <br>
|
||||
<a href="{scu.NotesURL()}" class="sidebar">Formations</a> <br>
|
||||
"""
|
||||
]
|
||||
if current_user.has_permission(Permission.AbsChange):
|
||||
|
@ -114,7 +114,7 @@ def index_html(showcodes=0, showsemtable=0):
|
||||
# aucun semestre courant: affiche aide
|
||||
H.append(
|
||||
"""<h2 class="listesems">Aucune session en cours !</h2>
|
||||
<p>Pour ajouter une session, aller dans <a href="Notes" id="link-programmes">Programmes</a>,
|
||||
<p>Pour ajouter une session, aller dans <a href="Notes" id="link-programmes">Formations</a>,
|
||||
choisissez une formation, puis suivez le lien "<em>UE, modules, semestres</em>".
|
||||
</p><p>
|
||||
Là, en bas de page, suivez le lien
|
||||
@ -336,15 +336,15 @@ def _style_sems(sems):
|
||||
else:
|
||||
sem["semestre_id_n"] = sem["semestre_id"]
|
||||
# pour édition codes Apogée:
|
||||
sem[
|
||||
"_etapes_apo_str_td_attrs"
|
||||
] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['etapes_apo_str']}" """
|
||||
sem[
|
||||
"_elt_annee_apo_td_attrs"
|
||||
] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_annee_apo']}" """
|
||||
sem[
|
||||
"_elt_sem_apo_td_attrs"
|
||||
] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_sem_apo']}" """
|
||||
sem["_etapes_apo_str_td_attrs"] = (
|
||||
f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['etapes_apo_str']}" """
|
||||
)
|
||||
sem["_elt_annee_apo_td_attrs"] = (
|
||||
f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_annee_apo']}" """
|
||||
)
|
||||
sem["_elt_sem_apo_td_attrs"] = (
|
||||
f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_sem_apo']}" """
|
||||
)
|
||||
|
||||
|
||||
def delete_dept(dept_id: int) -> str:
|
||||
|
@ -412,7 +412,7 @@ def module_move(module_id, after=0, redirect=True):
|
||||
db.session.add(neigh)
|
||||
db.session.commit()
|
||||
module.formation.invalidate_cached_sems()
|
||||
# redirect to ue_list page:
|
||||
# redirect to ue_table page:
|
||||
if redirect:
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
@ -454,7 +454,7 @@ def ue_move(ue_id, after=0, redirect=1):
|
||||
db.session.commit()
|
||||
ue.formation.invalidate_cached_sems()
|
||||
|
||||
# redirect to ue_list page
|
||||
# redirect to ue_table page
|
||||
if redirect:
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
|
@ -106,9 +106,9 @@ def do_module_create(args) -> int:
|
||||
if int(args.get("semestre_id", 0)) != ue.semestre_idx:
|
||||
raise ScoValueError("Formation incompatible: contacter le support ScoDoc")
|
||||
# create
|
||||
cnx = ndb.GetDBConnexion()
|
||||
module_id = _moduleEditor.create(cnx, args)
|
||||
log(f"do_module_create: created {module_id} with {args}")
|
||||
module = Module.create_from_dict(args)
|
||||
db.session.commit()
|
||||
log(f"do_module_create: created {module.id} with {args}")
|
||||
|
||||
# news
|
||||
ScolarNews.add(
|
||||
@ -117,7 +117,7 @@ def do_module_create(args) -> int:
|
||||
text=f"Modification de la formation {formation.acronyme}",
|
||||
)
|
||||
formation.invalidate_cached_sems()
|
||||
return module_id
|
||||
return module.id
|
||||
|
||||
|
||||
def module_create(
|
||||
@ -666,7 +666,7 @@ def module_edit(
|
||||
"explanation": "numéro (1, 2, 3, 4, ...) pour ordre d'affichage",
|
||||
"type": "int",
|
||||
"default": default_num,
|
||||
"allow_null": False,
|
||||
"allow_null": True,
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -811,6 +811,10 @@ def module_edit(
|
||||
)
|
||||
)
|
||||
else:
|
||||
if isinstance(tf[2]["numero"], str):
|
||||
tf[2]["numero"] = tf[2]["numero"].strip()
|
||||
if not isinstance(tf[2]["numero"], int) and not tf[2]["numero"]:
|
||||
tf[2]["numero"] = tf[2]["numero"] or default_num
|
||||
if create:
|
||||
if not matiere_id:
|
||||
# formulaire avec choix UE de rattachement
|
||||
|
@ -766,7 +766,7 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
|
||||
"libjs/jQuery-tagEditor/jquery.caret.min.js",
|
||||
"js/module_tag_editor.js",
|
||||
],
|
||||
page_title=f"Programme {formation.acronyme} v{formation.version}",
|
||||
page_title=f"Formation {formation.acronyme} v{formation.version}",
|
||||
),
|
||||
f"""<h2>{formation.html()} {lockicon}
|
||||
</h2>
|
||||
@ -888,7 +888,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
||||
H.append(
|
||||
f"""
|
||||
<div class="formation_ue_list">
|
||||
<div class="ue_list_tit">Programme pédagogique:</div>
|
||||
<div class="ue_list_tit">Formation (programme pédagogique):</div>
|
||||
<form>
|
||||
<input type="checkbox" class="sco_tag_checkbox"
|
||||
{'checked' if show_tags else ''}
|
||||
@ -1054,7 +1054,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
||||
)
|
||||
# <li>(debug) <a class="stdlink" href="check_form_integrity?formation_id=%(formation_id)s">Vérifier cohérence</a></li>
|
||||
|
||||
warn, _ = sco_formsemestre_validation.check_formation_ues(formation_id)
|
||||
warn, _ = sco_formsemestre_validation.check_formation_ues(formation)
|
||||
H.append(warn)
|
||||
|
||||
H.append(html_sco_header.sco_footer())
|
||||
|
@ -30,7 +30,7 @@
|
||||
import xml.dom.minidom
|
||||
|
||||
import flask
|
||||
from flask import flash, g, url_for
|
||||
from flask import flash, g, request, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -495,7 +495,7 @@ def formation_list_table() -> GenTable:
|
||||
returns a table
|
||||
"""
|
||||
formations: list[Formation] = Formation.query.filter_by(dept_id=g.scodoc_dept_id)
|
||||
title = "Programmes pédagogiques"
|
||||
title = "Formations (programmes pédagogiques)"
|
||||
lockicon = scu.icontag(
|
||||
"lock32_img", title="Comporte des semestres verrouillés", border="0"
|
||||
)
|
||||
@ -627,7 +627,7 @@ def formation_list_table() -> GenTable:
|
||||
html_class="formation_list_table table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
html_sortable=True,
|
||||
base_url="{request.base_url}?formation_id={formation_id}",
|
||||
base_url=f"{request.base_url}",
|
||||
page_title=title,
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
|
@ -304,12 +304,16 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
{
|
||||
"input_type": "text_suggest",
|
||||
"size": 50,
|
||||
"title": "(Co-)Directeur(s) des études"
|
||||
if index
|
||||
else "Directeur des études",
|
||||
"explanation": "(facultatif) taper le début du nom et choisir dans le menu"
|
||||
if index
|
||||
else "(obligatoire) taper le début du nom et choisir dans le menu",
|
||||
"title": (
|
||||
"(Co-)Directeur(s) des études"
|
||||
if index
|
||||
else "Directeur des études"
|
||||
),
|
||||
"explanation": (
|
||||
"(facultatif) taper le début du nom et choisir dans le menu"
|
||||
if index
|
||||
else "(obligatoire) taper le début du nom et choisir dans le menu"
|
||||
),
|
||||
"allowed_values": allowed_user_names,
|
||||
"allow_null": index, # > 0, # il faut au moins un responsable de semestre
|
||||
"text_suggest_options": {
|
||||
@ -356,9 +360,11 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
"title": "Semestre dans la formation",
|
||||
"allowed_values": semestre_id_list,
|
||||
"labels": semestre_id_labels,
|
||||
"explanation": "en BUT, on ne peut pas modifier le semestre après création"
|
||||
if is_apc
|
||||
else "",
|
||||
"explanation": (
|
||||
"en BUT, on ne peut pas modifier le semestre après création"
|
||||
if is_apc
|
||||
else ""
|
||||
),
|
||||
"attributes": ['onchange="change_semestre_id();"'] if is_apc else "",
|
||||
},
|
||||
),
|
||||
@ -1636,13 +1642,13 @@ def formsemestre_change_publication_bul(
|
||||
|
||||
def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
"""Changement manuel des coefficients des UE capitalisées."""
|
||||
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
||||
if not ok:
|
||||
return err
|
||||
|
||||
footer = html_sco_header.sco_footer()
|
||||
help = """<p class="help">
|
||||
help_msg = """<p class="help">
|
||||
Seuls les modules ont un coefficient. Cependant, il est nécessaire d'affecter un coefficient aux UE capitalisée pour pouvoir les prendre en compte dans la moyenne générale.
|
||||
</p>
|
||||
<p class="help">ScoDoc calcule normalement le coefficient d'une UE comme la somme des
|
||||
@ -1665,17 +1671,16 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
"""
|
||||
H = [
|
||||
html_sco_header.html_sem_header("Coefficients des UE du semestre"),
|
||||
help,
|
||||
help_msg,
|
||||
]
|
||||
#
|
||||
ues, modimpls = _get_sem_ues_modimpls(formsemestre_id)
|
||||
ues, modimpls = _get_sem_ues_modimpls(formsemestre)
|
||||
sum_coefs_by_ue_id = {}
|
||||
for ue in ues:
|
||||
ue["sum_coefs"] = sum(
|
||||
[
|
||||
mod["module"]["coefficient"]
|
||||
for mod in modimpls
|
||||
if mod["module"]["ue_id"] == ue["ue_id"]
|
||||
]
|
||||
sum_coefs_by_ue_id[ue.id] = sum(
|
||||
modimpl.module.coefficient
|
||||
for modimpl in modimpls
|
||||
if modimpl.module.ue_id == ue.id
|
||||
)
|
||||
|
||||
cnx = ndb.GetDBConnexion()
|
||||
@ -1684,20 +1689,20 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
form = [("formsemestre_id", {"input_type": "hidden"})]
|
||||
for ue in ues:
|
||||
coefs = sco_formsemestre.formsemestre_uecoef_list(
|
||||
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue["ue_id"]}
|
||||
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue.id}
|
||||
)
|
||||
if coefs:
|
||||
initvalues["ue_" + str(ue["ue_id"])] = coefs[0]["coefficient"]
|
||||
initvalues["ue_" + str(ue.id)] = coefs[0]["coefficient"]
|
||||
else:
|
||||
initvalues["ue_" + str(ue["ue_id"])] = "auto"
|
||||
initvalues["ue_" + str(ue.id)] = "auto"
|
||||
descr = {
|
||||
"size": 10,
|
||||
"title": ue["acronyme"],
|
||||
"explanation": "somme coefs modules = %s" % ue["sum_coefs"],
|
||||
"title": ue.acronyme,
|
||||
"explanation": f"somme coefs modules = {sum_coefs_by_ue_id[ue.id]}",
|
||||
}
|
||||
if ue["ue_id"] == err_ue_id:
|
||||
if ue.id == err_ue_id:
|
||||
descr["dom_id"] = "erroneous_ue"
|
||||
form.append(("ue_" + str(ue["ue_id"]), descr))
|
||||
form.append(("ue_" + str(ue.id), descr))
|
||||
|
||||
tf = TrivialFormulator(
|
||||
request.base_url,
|
||||
@ -1722,12 +1727,12 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
# 1- supprime les coef qui ne sont plus forcés
|
||||
# 2- modifie ou cree les coefs
|
||||
ue_deleted = []
|
||||
ue_modified = []
|
||||
ue_modified: list[tuple[UniteEns, float]] = []
|
||||
msg = []
|
||||
for ue in ues:
|
||||
val = tf[2]["ue_" + str(ue["ue_id"])]
|
||||
val = tf[2]["ue_" + str(ue.id)]
|
||||
coefs = sco_formsemestre.formsemestre_uecoef_list(
|
||||
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue["ue_id"]}
|
||||
cnx, args={"formsemestre_id": formsemestre_id, "ue_id": ue.id}
|
||||
)
|
||||
if val == "" or val == "auto":
|
||||
# supprime ce coef (il sera donc calculé automatiquement)
|
||||
@ -1737,13 +1742,11 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
try:
|
||||
val = float(val)
|
||||
if (not coefs) or (coefs[0]["coefficient"] != val):
|
||||
ue["coef"] = val
|
||||
ue_modified.append(ue)
|
||||
except:
|
||||
ue_modified.append((ue, val))
|
||||
except ValueError:
|
||||
ok = False
|
||||
msg.append(
|
||||
"valeur invalide (%s) pour le coefficient de l'UE %s"
|
||||
% (val, ue["acronyme"])
|
||||
f"valeur invalide ({val}) pour le coefficient de l'UE {ue.acronyme}"
|
||||
)
|
||||
|
||||
if not ok:
|
||||
@ -1755,26 +1758,24 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
)
|
||||
|
||||
# apply modifications
|
||||
for ue in ue_modified:
|
||||
for ue, val in ue_modified:
|
||||
sco_formsemestre.do_formsemestre_uecoef_edit_or_create(
|
||||
cnx, formsemestre_id, ue["ue_id"], ue["coef"]
|
||||
cnx, formsemestre_id, ue.id, val
|
||||
)
|
||||
for ue in ue_deleted:
|
||||
sco_formsemestre.do_formsemestre_uecoef_delete(
|
||||
cnx, formsemestre_id, ue["ue_id"]
|
||||
)
|
||||
sco_formsemestre.do_formsemestre_uecoef_delete(cnx, formsemestre_id, ue.id)
|
||||
|
||||
if ue_modified or ue_deleted:
|
||||
message = ["""<h3>Modification effectuées</h3>"""]
|
||||
if ue_modified:
|
||||
message.append("""<h4>Coefs modifiés dans les UE:<h4><ul>""")
|
||||
for ue in ue_modified:
|
||||
message.append("<li>%(acronyme)s : %(coef)s</li>" % ue)
|
||||
for ue, val in ue_modified:
|
||||
message.append(f"<li>{ue.acronyme} : {val}</li>")
|
||||
message.append("</ul>")
|
||||
if ue_deleted:
|
||||
message.append("""<h4>Coefs supprimés dans les UE:<h4><ul>""")
|
||||
for ue in ue_deleted:
|
||||
message.append("<li>%(acronyme)s</li>" % ue)
|
||||
message.append(f"<li>{ue.acronyme}</li>")
|
||||
message.append("</ul>")
|
||||
else:
|
||||
message = ["""<h3>Aucune modification</h3>"""]
|
||||
@ -1792,21 +1793,19 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
"""
|
||||
|
||||
|
||||
def _get_sem_ues_modimpls(formsemestre_id, modimpls=None):
|
||||
def _get_sem_ues_modimpls(
|
||||
formsemestre: FormSemestre,
|
||||
) -> tuple[list[UniteEns], list[ModuleImpl]]:
|
||||
"""Get liste des UE du semestre (à partir des moduleimpls)
|
||||
(utilisé quand on ne peut pas construire nt et faire nt.get_ues_stat_dict())
|
||||
"""
|
||||
if modimpls is None:
|
||||
modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
|
||||
uedict = {}
|
||||
modimpls = formsemestre.modimpls.all()
|
||||
for modimpl in modimpls:
|
||||
mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0]
|
||||
modimpl["module"] = mod
|
||||
if not mod["ue_id"] in uedict:
|
||||
ue = sco_edit_ue.ue_list(args={"ue_id": mod["ue_id"]})[0]
|
||||
uedict[ue["ue_id"]] = ue
|
||||
if not modimpl.module.ue_id in uedict:
|
||||
uedict[modimpl.module.ue.id] = modimpl.module.ue
|
||||
ues = list(uedict.values())
|
||||
ues.sort(key=lambda u: u["numero"])
|
||||
ues.sort(key=lambda u: u.numero)
|
||||
return ues, modimpls
|
||||
|
||||
|
||||
|
@ -305,18 +305,15 @@ def do_formsemestre_inscription_with_modules(
|
||||
# 2- inscrit aux groupes
|
||||
for group_id in group_ids:
|
||||
if group_id and group_id not in gdone:
|
||||
group = GroupDescr.query.get_or_404(group_id)
|
||||
_ = GroupDescr.query.get_or_404(group_id)
|
||||
sco_groups.set_group(etudid, group_id)
|
||||
gdone[group_id] = 1
|
||||
|
||||
# Inscription à tous les modules de ce semestre
|
||||
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||
formsemestre_id=formsemestre_id
|
||||
)
|
||||
for mod in modimpls:
|
||||
if mod["ue"]["type"] != UE_SPORT:
|
||||
for modimpl in formsemestre.modimpls:
|
||||
if modimpl.module.ue.type != UE_SPORT:
|
||||
sco_moduleimpl.do_moduleimpl_inscription_create(
|
||||
{"moduleimpl_id": mod["moduleimpl_id"], "etudid": etudid},
|
||||
{"moduleimpl_id": modimpl.id, "etudid": etudid},
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
# Mise à jour des inscriptions aux parcours:
|
||||
@ -531,19 +528,17 @@ def formsemestre_inscription_option(etudid, formsemestre_id):
|
||||
if not sem["etat"]:
|
||||
raise ScoValueError("Modification impossible: semestre verrouille")
|
||||
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
etud = Identite.get_etud(etudid)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
|
||||
footer = html_sco_header.sco_footer()
|
||||
H = [
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Inscription de %s aux modules de %s (%s - %s)</h2>"
|
||||
% (etud["nomprenom"], sem["titre_num"], sem["date_debut"], sem["date_fin"])
|
||||
html_sco_header.sco_header(),
|
||||
f"""<h2>Inscription de {etud.nomprenom} aux modules de {formsemestre.titre_mois()}</h2>""",
|
||||
]
|
||||
|
||||
# Cherche les moduleimpls et les inscriptions
|
||||
mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
|
||||
inscr = sco_moduleimpl.do_moduleimpl_inscription_list(etudid=etudid)
|
||||
# Formulaire
|
||||
modimpls_by_ue_ids = collections.defaultdict(list) # ue_id : [ moduleimpl_id ]
|
||||
@ -551,26 +546,26 @@ def formsemestre_inscription_option(etudid, formsemestre_id):
|
||||
ues = []
|
||||
ue_ids = set()
|
||||
initvalues = {}
|
||||
for mod in mods:
|
||||
ue_id = mod["ue"]["ue_id"]
|
||||
for modimpl in formsemestre.modimpls:
|
||||
ue_id = modimpl.module.ue.id
|
||||
if not ue_id in ue_ids:
|
||||
ues.append(mod["ue"])
|
||||
ues.append(modimpl.module.ue)
|
||||
ue_ids.add(ue_id)
|
||||
modimpls_by_ue_ids[ue_id].append(mod["moduleimpl_id"])
|
||||
modimpls_by_ue_ids[ue_id].append(modimpl.id)
|
||||
|
||||
modimpls_by_ue_names[ue_id].append(
|
||||
"%s %s" % (mod["module"]["code"] or "", mod["module"]["titre"] or "")
|
||||
f"{modimpl.module.code or ''} {modimpl.module.titre or ''}"
|
||||
)
|
||||
vals = scu.get_request_args()
|
||||
if not vals.get("tf_submitted", False):
|
||||
# inscrit ?
|
||||
for ins in inscr:
|
||||
if ins["moduleimpl_id"] == mod["moduleimpl_id"]:
|
||||
key = "moduleimpls_%s" % ue_id
|
||||
if ins["moduleimpl_id"] == modimpl.id:
|
||||
key = f"moduleimpls_{ue_id}"
|
||||
if key in initvalues:
|
||||
initvalues[key].append(str(mod["moduleimpl_id"]))
|
||||
initvalues[key].append(str(modimpl.id))
|
||||
else:
|
||||
initvalues[key] = [str(mod["moduleimpl_id"])]
|
||||
initvalues[key] = [str(modimpl.id)]
|
||||
break
|
||||
|
||||
descr = [
|
||||
@ -578,10 +573,10 @@ def formsemestre_inscription_option(etudid, formsemestre_id):
|
||||
("etudid", {"input_type": "hidden"}),
|
||||
]
|
||||
for ue in ues:
|
||||
ue_id = ue["ue_id"]
|
||||
ue_descr = ue["acronyme"]
|
||||
if ue["type"] != UE_STANDARD:
|
||||
ue_descr += " <em>%s</em>" % UE_TYPE_NAME[ue["type"]]
|
||||
ue_id = ue.id
|
||||
ue_descr = ue.acronyme
|
||||
if ue.type != UE_STANDARD:
|
||||
ue_descr += " <em>%s</em>" % UE_TYPE_NAME[ue.type]
|
||||
ue_status = nt.get_etud_ue_status(etudid, ue_id)
|
||||
if ue_status and ue_status["is_capitalized"]:
|
||||
sem_origin = sco_formsemestre.get_formsemestre(ue_status["formsemestre_id"])
|
||||
@ -606,7 +601,7 @@ def formsemestre_inscription_option(etudid, formsemestre_id):
|
||||
)
|
||||
descr.append(
|
||||
(
|
||||
"moduleimpls_%s" % ue_id,
|
||||
f"moduleimpls_{ue_id}",
|
||||
{
|
||||
"input_type": "checkbox",
|
||||
"title": "",
|
||||
@ -654,112 +649,98 @@ function chkbx_select(field_id, state) {
|
||||
"""
|
||||
)
|
||||
return "\n".join(H) + "\n" + tf[1] + footer
|
||||
elif tf[0] == -1:
|
||||
if tf[0] == -1:
|
||||
return flask.redirect(
|
||||
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
)
|
||||
else:
|
||||
# Inscriptions aux modules choisis
|
||||
# il faut desinscrire des modules qui ne figurent pas
|
||||
# et inscrire aux autres, sauf si deja inscrit
|
||||
a_desinscrire = {}.fromkeys([x["moduleimpl_id"] for x in mods])
|
||||
insdict = {}
|
||||
for ins in inscr:
|
||||
insdict[ins["moduleimpl_id"]] = ins
|
||||
for ue in ues:
|
||||
ue_id = ue["ue_id"]
|
||||
for moduleimpl_id in [int(x) for x in tf[2]["moduleimpls_%s" % ue_id]]:
|
||||
if moduleimpl_id in a_desinscrire:
|
||||
del a_desinscrire[moduleimpl_id]
|
||||
# supprime ceux auxquel pas inscrit
|
||||
moduleimpls_a_desinscrire = list(a_desinscrire.keys())
|
||||
for moduleimpl_id in moduleimpls_a_desinscrire:
|
||||
if moduleimpl_id not in insdict:
|
||||
|
||||
# Inscriptions aux modules choisis
|
||||
# il faut desinscrire des modules qui ne figurent pas
|
||||
# et inscrire aux autres, sauf si deja inscrit
|
||||
a_desinscrire = {}.fromkeys([x.id for x in formsemestre.modimpls])
|
||||
insdict = {}
|
||||
for ins in inscr:
|
||||
insdict[ins["moduleimpl_id"]] = ins
|
||||
for ue in ues:
|
||||
for moduleimpl_id in [int(x) for x in tf[2][f"moduleimpls_{ue.id}"]]:
|
||||
if moduleimpl_id in a_desinscrire:
|
||||
del a_desinscrire[moduleimpl_id]
|
||||
# supprime ceux auxquel pas inscrit
|
||||
moduleimpls_a_desinscrire = list(a_desinscrire.keys())
|
||||
for moduleimpl_id in moduleimpls_a_desinscrire:
|
||||
if moduleimpl_id not in insdict:
|
||||
del a_desinscrire[moduleimpl_id]
|
||||
|
||||
a_inscrire = set()
|
||||
for ue in ues:
|
||||
ue_id = ue["ue_id"]
|
||||
a_inscrire.update(
|
||||
int(x) for x in tf[2]["moduleimpls_%s" % ue_id]
|
||||
) # conversion en int !
|
||||
# supprime ceux auquel deja inscrit:
|
||||
for ins in inscr:
|
||||
if ins["moduleimpl_id"] in a_inscrire:
|
||||
a_inscrire.remove(ins["moduleimpl_id"])
|
||||
# dict des modules:
|
||||
modsdict = {}
|
||||
for mod in mods:
|
||||
modsdict[mod["moduleimpl_id"]] = mod
|
||||
#
|
||||
if (not a_inscrire) and (not a_desinscrire):
|
||||
H.append(
|
||||
"""<h3>Aucune modification à effectuer</h3>
|
||||
<p><a class="stdlink" href="%s">retour à la fiche étudiant</a></p>
|
||||
"""
|
||||
% url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
)
|
||||
return "\n".join(H) + footer
|
||||
|
||||
H.append("<h3>Confirmer les modifications:</h3>")
|
||||
if a_desinscrire:
|
||||
H.append(
|
||||
"<p>%s va être <b>désinscrit%s</b> des modules:<ul><li>"
|
||||
% (etud["nomprenom"], etud["ne"])
|
||||
)
|
||||
H.append(
|
||||
"</li><li>".join(
|
||||
[
|
||||
"%s (%s)"
|
||||
% (
|
||||
modsdict[x]["module"]["titre"],
|
||||
modsdict[x]["module"]["code"] or "(module sans code)",
|
||||
)
|
||||
for x in a_desinscrire
|
||||
]
|
||||
)
|
||||
+ "</p>"
|
||||
)
|
||||
H.append("</li></ul>")
|
||||
if a_inscrire:
|
||||
H.append(
|
||||
"<p>%s va être <b>inscrit%s</b> aux modules:<ul><li>"
|
||||
% (etud["nomprenom"], etud["ne"])
|
||||
)
|
||||
H.append(
|
||||
"</li><li>".join(
|
||||
[
|
||||
"%s (%s)"
|
||||
% (
|
||||
modsdict[x]["module"]["titre"],
|
||||
modsdict[x]["module"]["code"] or "(module sans code)",
|
||||
)
|
||||
for x in a_inscrire
|
||||
]
|
||||
)
|
||||
+ "</p>"
|
||||
)
|
||||
H.append("</li></ul>")
|
||||
modulesimpls_ainscrire = ",".join(str(x) for x in a_inscrire)
|
||||
modulesimpls_adesinscrire = ",".join(str(x) for x in a_desinscrire)
|
||||
a_inscrire = set()
|
||||
for ue in ues:
|
||||
a_inscrire.update(
|
||||
int(x) for x in tf[2][f"moduleimpls_{ue.id}"]
|
||||
) # conversion en int !
|
||||
# supprime ceux auquel deja inscrit:
|
||||
for ins in inscr:
|
||||
if ins["moduleimpl_id"] in a_inscrire:
|
||||
a_inscrire.remove(ins["moduleimpl_id"])
|
||||
# dict des modules:
|
||||
modimpls_by_id = {modimpl.id: modimpl for modimpl in formsemestre.modimpls}
|
||||
#
|
||||
if (not a_inscrire) and (not a_desinscrire):
|
||||
H.append(
|
||||
"""<form action="do_moduleimpl_incription_options">
|
||||
<input type="hidden" name="etudid" value="%s"/>
|
||||
<input type="hidden" name="modulesimpls_ainscrire" value="%s"/>
|
||||
<input type="hidden" name="modulesimpls_adesinscrire" value="%s"/>
|
||||
<input type ="submit" value="Confirmer"/>
|
||||
<input type ="button" value="Annuler" onclick="document.location='%s';"/>
|
||||
</form>
|
||||
f"""<h3>Aucune modification à effectuer</h3>
|
||||
<p><a class="stdlink" href="{
|
||||
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
}">retour à la fiche étudiant</a></p>
|
||||
"""
|
||||
% (
|
||||
etudid,
|
||||
modulesimpls_ainscrire,
|
||||
modulesimpls_adesinscrire,
|
||||
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
)
|
||||
)
|
||||
return "\n".join(H) + footer
|
||||
|
||||
H.append("<h3>Confirmer les modifications:</h3>")
|
||||
if a_desinscrire:
|
||||
H.append(
|
||||
f"""<p>{etud.nomprenom} va être <b>désinscrit{etud.e}</b> des modules:<ul><li>"""
|
||||
)
|
||||
H.append(
|
||||
"</li><li>".join(
|
||||
[
|
||||
f"""{modimpls_by_id[x].module.titre or ''} ({
|
||||
modimpls_by_id[x].module.code or '(module sans code)'})"""
|
||||
for x in a_desinscrire
|
||||
]
|
||||
)
|
||||
+ "</p>"
|
||||
)
|
||||
H.append("</li></ul>")
|
||||
if a_inscrire:
|
||||
H.append(
|
||||
f"""<p>{etud.nomprenom} va être <b>inscrit{etud.e}</b> aux modules:<ul><li>"""
|
||||
)
|
||||
H.append(
|
||||
"</li><li>".join(
|
||||
[
|
||||
f"""{modimpls_by_id[x].module.titre or ''} ({
|
||||
modimpls_by_id[x].module.code or '(module sans code)'})"""
|
||||
for x in a_inscrire
|
||||
]
|
||||
)
|
||||
+ "</p>"
|
||||
)
|
||||
H.append("</li></ul>")
|
||||
modulesimpls_ainscrire = ",".join(str(x) for x in a_inscrire)
|
||||
modulesimpls_adesinscrire = ",".join(str(x) for x in a_desinscrire)
|
||||
H.append(
|
||||
f"""
|
||||
<form action="do_moduleimpl_incription_options">
|
||||
<input type="hidden" name="etudid" value="{etudid}"/>
|
||||
<input type="hidden" name="modulesimpls_ainscrire" value="{modulesimpls_ainscrire}"/>
|
||||
<input type="hidden" name="modulesimpls_adesinscrire" value="{modulesimpls_adesinscrire}"/>
|
||||
<input type ="submit" value="Confirmer"/>
|
||||
<input type ="button" value="Annuler" onclick="document.location='{
|
||||
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
}';"/>
|
||||
</form>
|
||||
"""
|
||||
)
|
||||
return "\n".join(H) + footer
|
||||
|
||||
|
||||
def do_moduleimpl_incription_options(
|
||||
etudid, modulesimpls_ainscrire, modulesimpls_adesinscrire
|
||||
|
@ -909,37 +909,6 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str:
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def html_expr_diagnostic(diagnostics):
|
||||
"""Affiche messages d'erreur des formules utilisateurs"""
|
||||
H = []
|
||||
H.append('<div class="ue_warning">Erreur dans des formules utilisateurs:<ul>')
|
||||
last_id, last_msg = None, None
|
||||
for diag in diagnostics:
|
||||
if "moduleimpl_id" in diag:
|
||||
mod = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||
moduleimpl_id=diag["moduleimpl_id"]
|
||||
)[0]
|
||||
H.append(
|
||||
'<li>module <a href="moduleimpl_status?moduleimpl_id=%s">%s</a>: %s</li>'
|
||||
% (
|
||||
diag["moduleimpl_id"],
|
||||
mod["module"]["abbrev"] or mod["module"]["code"] or "?",
|
||||
diag["msg"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
if diag["ue_id"] != last_id or diag["msg"] != last_msg:
|
||||
ue = sco_edit_ue.ue_list({"ue_id": diag["ue_id"]})[0]
|
||||
H.append(
|
||||
'<li>UE "%s": %s</li>'
|
||||
% (ue["acronyme"] or ue["titre"] or "?", diag["msg"])
|
||||
)
|
||||
last_id, last_msg = diag["ue_id"], diag["msg"]
|
||||
|
||||
H.append("</ul></div>")
|
||||
return "".join(H)
|
||||
|
||||
|
||||
def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None):
|
||||
"""En-tête HTML des pages "semestre" """
|
||||
formsemestre: FormSemestre = db.session.get(FormSemestre, formsemestre_id)
|
||||
@ -1081,9 +1050,6 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
||||
>Toutes évaluations (même incomplètes) visibles</div>"""
|
||||
)
|
||||
|
||||
if nt.expr_diagnostics:
|
||||
H.append(html_expr_diagnostic(nt.expr_diagnostics))
|
||||
|
||||
if nt.parcours.APC_SAE:
|
||||
# BUT: tableau ressources puis SAE
|
||||
ressources = [
|
||||
|
@ -1217,7 +1217,7 @@ def formsemestre_validate_previous_ue(formsemestre: FormSemestre, etud: Identite
|
||||
<div id="ue_list_code" class="sco_box sco_green_bg">
|
||||
<!-- filled by ue_sharing_code -->
|
||||
</div>
|
||||
{check_formation_ues(formation.id)[0]}
|
||||
{check_formation_ues(formation)[0]}
|
||||
{html_sco_header.sco_footer()}
|
||||
"""
|
||||
|
||||
@ -1376,15 +1376,14 @@ def _invalidate_etud_formation_caches(etudid, formation_id):
|
||||
) # > modif decision UE (inval tous semestres avec cet etudiant, ok mais conservatif)
|
||||
|
||||
|
||||
def check_formation_ues(formation_id):
|
||||
def check_formation_ues(formation: Formation) -> tuple[str, dict[int, list[UniteEns]]]:
|
||||
"""Verifie que les UE d'une formation sont chacune utilisée dans un seul semestre_id
|
||||
Si ce n'est pas le cas, c'est probablement (mais pas forcément) une erreur de
|
||||
définition du programme: cette fonction retourne un bout de HTML
|
||||
à afficher pour prévenir l'utilisateur, ou '' si tout est ok.
|
||||
"""
|
||||
ues = sco_edit_ue.ue_list({"formation_id": formation_id})
|
||||
ue_multiples = {} # { ue_id : [ liste des formsemestre ] }
|
||||
for ue in ues:
|
||||
for ue in formation.ues:
|
||||
# formsemestres utilisant cette ue ?
|
||||
sems = ndb.SimpleDictFetch(
|
||||
"""SELECT DISTINCT sem.id AS formsemestre_id, sem.*
|
||||
@ -1394,9 +1393,9 @@ def check_formation_ues(formation_id):
|
||||
AND mi.formsemestre_id = sem.id
|
||||
AND mod.ue_id = %(ue_id)s
|
||||
""",
|
||||
{"ue_id": ue["ue_id"], "formation_id": formation_id},
|
||||
{"ue_id": ue.id, "formation_id": formation.id},
|
||||
)
|
||||
semestre_ids = set([x["semestre_id"] for x in sems])
|
||||
semestre_ids = {x["semestre_id"] for x in sems}
|
||||
if (
|
||||
len(semestre_ids) > 1
|
||||
): # plusieurs semestres d'indices differents dans le cursus
|
||||
@ -1416,11 +1415,11 @@ def check_formation_ues(formation_id):
|
||||
<ul>
|
||||
"""
|
||||
]
|
||||
for ue in ues:
|
||||
if ue["ue_id"] in ue_multiples:
|
||||
for ue in formation.ues:
|
||||
if ue.id in ue_multiples:
|
||||
sems = [
|
||||
sco_formsemestre.get_formsemestre(x["formsemestre_id"])
|
||||
for x in ue_multiples[ue["ue_id"]]
|
||||
for x in ue_multiples[ue.id]
|
||||
]
|
||||
slist = ", ".join(
|
||||
[
|
||||
@ -1429,7 +1428,7 @@ def check_formation_ues(formation_id):
|
||||
for s in sems
|
||||
]
|
||||
)
|
||||
H.append("<li><b>%s</b> : %s</li>" % (ue["acronyme"], slist))
|
||||
H.append("<li><b>%s</b> : %s</li>" % (ue.acronyme, slist))
|
||||
H.append("</ul></div>")
|
||||
|
||||
return "\n".join(H), ue_multiples
|
||||
|
@ -56,6 +56,7 @@ _moduleimplEditor = ndb.EditableTable(
|
||||
|
||||
def do_moduleimpl_create(args):
|
||||
"create a moduleimpl"
|
||||
# TODO remplacer par une methode de ModuleImpl qui appelle super().create_from_dict() puis invalide le formsemestre
|
||||
cnx = ndb.GetDBConnexion()
|
||||
r = _moduleimplEditor.create(cnx, args)
|
||||
sco_cache.invalidate_formsemestre(
|
||||
@ -109,91 +110,6 @@ def do_moduleimpl_edit(args, formsemestre_id=None, cnx=None):
|
||||
) # > modif moduleimpl
|
||||
|
||||
|
||||
def moduleimpl_withmodule_list(
|
||||
moduleimpl_id=None, formsemestre_id=None, module_id=None, sort_by_ue=False
|
||||
) -> list:
|
||||
"""Liste les moduleimpls et ajoute dans chacun
|
||||
l'UE, la matière et le module auxquels ils appartiennent.
|
||||
Tri la liste par:
|
||||
- pour les formations classiques: semestre/UE/numero_matiere/numero_module;
|
||||
- pour le BUT: ignore UEs sauf si sort_by_ue et matières dans le tri.
|
||||
|
||||
NB: Cette fonction faisait partie de l'API ScoDoc 7.
|
||||
"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_edit_module
|
||||
|
||||
modimpls = moduleimpl_list(
|
||||
**{
|
||||
"moduleimpl_id": moduleimpl_id,
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"module_id": module_id,
|
||||
}
|
||||
)
|
||||
if not modimpls:
|
||||
return []
|
||||
ues = {}
|
||||
matieres = {}
|
||||
modules = {}
|
||||
for mi in modimpls:
|
||||
module_id = mi["module_id"]
|
||||
if not mi["module_id"] in modules:
|
||||
modules[module_id] = sco_edit_module.module_list(
|
||||
args={"module_id": module_id}
|
||||
)[0]
|
||||
mi["module"] = modules[module_id]
|
||||
ue_id = mi["module"]["ue_id"]
|
||||
if not ue_id in ues:
|
||||
ues[ue_id] = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
|
||||
mi["ue"] = ues[ue_id]
|
||||
matiere_id = mi["module"]["matiere_id"]
|
||||
if not matiere_id in matieres:
|
||||
matieres[matiere_id] = sco_edit_matiere.matiere_list(
|
||||
args={"matiere_id": matiere_id}
|
||||
)[0]
|
||||
mi["matiere"] = matieres[matiere_id]
|
||||
|
||||
mod = modimpls[0]["module"]
|
||||
formation = db.session.get(Formation, mod["formation_id"])
|
||||
|
||||
if formation.is_apc():
|
||||
# tri par numero_module
|
||||
if sort_by_ue:
|
||||
modimpls.sort(
|
||||
key=lambda x: (
|
||||
x["ue"]["numero"],
|
||||
x["ue"]["ue_id"],
|
||||
x["module"]["module_type"],
|
||||
x["module"]["numero"],
|
||||
x["module"]["code"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
modimpls.sort(
|
||||
key=lambda x: (
|
||||
x["module"]["module_type"],
|
||||
x["module"]["numero"],
|
||||
x["module"]["code"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Formations classiques, avec matières:
|
||||
# tri par semestre/UE/numero_matiere/numero_module
|
||||
modimpls.sort(
|
||||
key=lambda x: (
|
||||
x["ue"]["numero"],
|
||||
x["ue"]["ue_id"],
|
||||
x["matiere"]["numero"],
|
||||
x["matiere"]["matiere_id"],
|
||||
x["module"]["numero"],
|
||||
x["module"]["code"],
|
||||
)
|
||||
)
|
||||
|
||||
return modimpls
|
||||
|
||||
|
||||
def moduleimpls_in_external_ue(ue_id):
|
||||
"""List of modimpls in this ue"""
|
||||
cursor = ndb.SimpleQuery(
|
||||
@ -254,9 +170,9 @@ _moduleimpl_inscriptionEditor = ndb.EditableTable(
|
||||
)
|
||||
|
||||
|
||||
def do_moduleimpl_inscription_create(args, formsemestre_id=None):
|
||||
def do_moduleimpl_inscription_create(args, formsemestre_id=None, cnx=None):
|
||||
"create a moduleimpl_inscription"
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cnx = cnx or ndb.GetDBConnexion()
|
||||
try:
|
||||
r = _moduleimpl_inscriptionEditor.create(cnx, args)
|
||||
except psycopg2.errors.UniqueViolation as exc:
|
||||
@ -270,7 +186,7 @@ def do_moduleimpl_inscription_create(args, formsemestre_id=None):
|
||||
cnx,
|
||||
method="moduleimpl_inscription",
|
||||
etudid=args["etudid"],
|
||||
msg="inscription module %s" % args["moduleimpl_id"],
|
||||
msg=f"inscription module {args['moduleimpl_id']}",
|
||||
commit=False,
|
||||
)
|
||||
return r
|
||||
@ -297,32 +213,29 @@ def do_moduleimpl_inscrit_etuds(moduleimpl_id, formsemestre_id, etudids, reset=F
|
||||
args={"formsemestre_id": formsemestre_id, "etudid": etudid}
|
||||
)
|
||||
if not insem:
|
||||
raise ScoValueError("%s n'est pas inscrit au semestre !" % etudid)
|
||||
raise ScoValueError(f"{etudid} n'est pas inscrit au semestre !")
|
||||
|
||||
cnx = ndb.GetDBConnexion()
|
||||
# Desinscriptions
|
||||
if reset:
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
cursor.execute(
|
||||
"delete from notes_moduleimpl_inscription where moduleimpl_id = %(moduleimpl_id)s",
|
||||
{"moduleimpl_id": moduleimpl_id},
|
||||
)
|
||||
# Inscriptions au module:
|
||||
inmod_set = set(
|
||||
[
|
||||
# hum ?
|
||||
x["etudid"]
|
||||
for x in do_moduleimpl_inscription_list(moduleimpl_id=moduleimpl_id)
|
||||
]
|
||||
)
|
||||
inmod_set = {
|
||||
x["etudid"] for x in do_moduleimpl_inscription_list(moduleimpl_id=moduleimpl_id)
|
||||
}
|
||||
for etudid in etudids:
|
||||
# deja inscrit ?
|
||||
# déja inscrit ?
|
||||
if not etudid in inmod_set:
|
||||
do_moduleimpl_inscription_create(
|
||||
{"moduleimpl_id": moduleimpl_id, "etudid": etudid},
|
||||
formsemestre_id=formsemestre_id,
|
||||
cnx=cnx,
|
||||
)
|
||||
|
||||
cnx.commit()
|
||||
sco_cache.invalidate_formsemestre(
|
||||
formsemestre_id=formsemestre_id
|
||||
) # > moduleimpl_inscrit_etuds
|
||||
|
@ -409,34 +409,32 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
||||
H.append(
|
||||
'<h3>Étudiants avec UEs capitalisées (ADM):</h3><ul class="ue_inscr_list">'
|
||||
)
|
||||
ues = [
|
||||
sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys()
|
||||
]
|
||||
ues.sort(key=lambda u: u["numero"])
|
||||
ues = [UniteEns.query.get_or_404(ue_id) for ue_id in ues_cap_info.keys()]
|
||||
ues.sort(key=lambda u: u.numero)
|
||||
for ue in ues:
|
||||
H.append(
|
||||
f"""<li class="tit"><span class="tit">{ue['acronyme']}: {ue['titre']}</span>"""
|
||||
f"""<li class="tit"><span class="tit">{ue.acronyme}: {ue.titre or ''}</span>"""
|
||||
)
|
||||
H.append("<ul>")
|
||||
for info in ues_cap_info[ue["ue_id"]]:
|
||||
etud = sco_etud.get_etud_info(etudid=info["etudid"], filled=True)[0]
|
||||
for info in ues_cap_info[ue.id]:
|
||||
etud = Identite.get_etud(info["etudid"])
|
||||
H.append(
|
||||
f"""<li class="etud"><a class="discretelink etudinfo"
|
||||
id="{info['etudid']}"
|
||||
id="{etud.id}"
|
||||
href="{
|
||||
url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etud["etudid"],
|
||||
etudid=etud.id,
|
||||
)
|
||||
}">{etud["nomprenom"]}</a>"""
|
||||
}">{etud.nomprenom}</a>"""
|
||||
)
|
||||
if info["ue_status"]["event_date"]:
|
||||
H.append(
|
||||
f"""(cap. le {info["ue_status"]["event_date"].strftime("%d/%m/%Y")})"""
|
||||
)
|
||||
if is_apc:
|
||||
is_inscrit_ue = (etud["etudid"], ue["id"]) not in res.dispense_ues
|
||||
is_inscrit_ue = (etud.id, ue.id) not in res.dispense_ues
|
||||
else:
|
||||
# CLASSIQUE
|
||||
is_inscrit_ue = info["is_ins"]
|
||||
@ -468,8 +466,8 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
||||
H.append(
|
||||
f"""<div><a class="stdlink" href="{
|
||||
url_for("notes.etud_desinscrit_ue",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud["etudid"],
|
||||
formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud.id,
|
||||
formsemestre_id=formsemestre_id, ue_id=ue.id)
|
||||
}">désinscrire {"des modules" if not is_apc else ""} de cette UE</a></div>
|
||||
"""
|
||||
)
|
||||
@ -479,8 +477,8 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
||||
H.append(
|
||||
f"""<div><a class="stdlink" href="{
|
||||
url_for("notes.etud_inscrit_ue",
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud["etudid"],
|
||||
formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
|
||||
scodoc_dept=g.scodoc_dept, etudid=etud.id,
|
||||
formsemestre_id=formsemestre_id, ue_id=ue.id)
|
||||
}">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div>
|
||||
"""
|
||||
)
|
||||
|
@ -127,12 +127,12 @@ def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str:
|
||||
"args": {
|
||||
"group_ids": group_id,
|
||||
"evaluation_id": evaluation.id,
|
||||
"date_debut": evaluation.date_debut.isoformat()
|
||||
if evaluation.date_debut
|
||||
else "",
|
||||
"date_fin": evaluation.date_fin.isoformat()
|
||||
if evaluation.date_fin
|
||||
else "",
|
||||
"date_debut": (
|
||||
evaluation.date_debut.isoformat() if evaluation.date_debut else ""
|
||||
),
|
||||
"date_fin": (
|
||||
evaluation.date_fin.isoformat() if evaluation.date_fin else ""
|
||||
),
|
||||
},
|
||||
"enabled": evaluation.date_debut is not None
|
||||
and evaluation.date_fin is not None,
|
||||
@ -355,10 +355,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
Ses notes ne peuvent pas être prises en compte dans les moyennes d'UE.
|
||||
</div>"""
|
||||
)
|
||||
#
|
||||
if has_expression and nt.expr_diagnostics:
|
||||
H.append(sco_formsemestre_status.html_expr_diagnostic(nt.expr_diagnostics))
|
||||
#
|
||||
|
||||
if formsemestre_has_decisions(formsemestre_id):
|
||||
H.append(
|
||||
"""<ul class="tf-msg">
|
||||
|
@ -139,9 +139,8 @@ def dict_pvjury(
|
||||
dec_ue_list = _descr_decisions_ues(
|
||||
nt, etudid, d["decisions_ue"], d["decision_sem"]
|
||||
)
|
||||
d["decisions_ue_nb"] = len(
|
||||
dec_ue_list
|
||||
) # avec les UE capitalisées, donc des éventuels doublons
|
||||
# avec les UE capitalisées, donc des éventuels doublons:
|
||||
d["decisions_ue_nb"] = len(dec_ue_list)
|
||||
# Mais sur la description (eg sur les bulletins), on ne veut pas
|
||||
# afficher ces doublons: on uniquifie sur ue_code
|
||||
_codes = set()
|
||||
@ -291,8 +290,10 @@ def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem) -> list[dict]:
|
||||
)
|
||||
)
|
||||
):
|
||||
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
|
||||
uelist.append(ue)
|
||||
ue = UniteEns.query.get(ue_id)
|
||||
assert ue
|
||||
# note modernisation code: on utilise des dict tant que get_etud_ue_status renvoie des dicts
|
||||
uelist.append(ue.to_dict())
|
||||
# Les UE capitalisées dans d'autres semestres:
|
||||
if etudid in nt.validations.ue_capitalisees.index:
|
||||
for ue_id in nt.validations.ue_capitalisees.loc[[etudid]]["ue_id"]:
|
||||
|
@ -528,6 +528,7 @@ def notes_add(
|
||||
|
||||
Return: tuple (etudids_changed, nb_suppress, etudids_with_decision)
|
||||
"""
|
||||
assert evaluation_id is not None
|
||||
now = psycopg2.Timestamp(*time.localtime()[:6])
|
||||
|
||||
# Vérifie inscription et valeur note
|
||||
@ -539,7 +540,7 @@ def notes_add(
|
||||
}
|
||||
for etudid, value in notes:
|
||||
if check_inscription and (etudid not in inscrits):
|
||||
raise NoteProcessError(f"etudiant {etudid} non inscrit dans ce module")
|
||||
raise NoteProcessError(f"étudiant {etudid} non inscrit dans ce module")
|
||||
if (value is not None) and not isinstance(value, float):
|
||||
raise NoteProcessError(
|
||||
f"etudiant {etudid}: valeur de note invalide ({value})"
|
||||
|
@ -60,16 +60,14 @@ from app.models.formsemestre import FormSemestre
|
||||
|
||||
|
||||
from app import db, log
|
||||
from app.models import Evaluation, ModuleImpl, UniteEns
|
||||
from app.models import Evaluation, Identite, ModuleImpl, UniteEns
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_saisie_notes
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||
@ -83,10 +81,10 @@ def external_ue_create(
|
||||
acronyme="",
|
||||
ue_type=codes_cursus.UE_STANDARD,
|
||||
ects=0.0,
|
||||
) -> int:
|
||||
) -> ModuleImpl:
|
||||
"""Crée UE/matiere/module dans la formation du formsemestre
|
||||
puis un moduleimpl.
|
||||
Return: moduleimpl_id
|
||||
Return: moduleimpl
|
||||
"""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
log(f"creating external UE in {formsemestre}: {acronyme}")
|
||||
@ -139,28 +137,30 @@ def external_ue_create(
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre_id,
|
||||
# affecte le 1er responsable du semestre comme resp. du module
|
||||
"responsable_id": formsemestre.responsables[0].id
|
||||
if len(formsemestre.responsables)
|
||||
else None,
|
||||
"responsable_id": (
|
||||
formsemestre.responsables[0].id
|
||||
if len(formsemestre.responsables)
|
||||
else None
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
return moduleimpl_id
|
||||
modimpl = ModuleImpl.query.get(moduleimpl_id)
|
||||
assert modimpl
|
||||
return modimpl
|
||||
|
||||
|
||||
def external_ue_inscrit_et_note(
|
||||
moduleimpl_id: int, formsemestre_id: int, notes_etuds: dict
|
||||
moduleimpl: ModuleImpl, formsemestre_id: int, notes_etuds: dict
|
||||
):
|
||||
"""Inscrit les étudiants au moduleimpl, crée au besoin une évaluation
|
||||
et enregistre les notes.
|
||||
"""
|
||||
moduleimpl: ModuleImpl = db.session.get(ModuleImpl, moduleimpl_id)
|
||||
log(
|
||||
f"external_ue_inscrit_et_note(moduleimpl_id={moduleimpl_id}, notes_etuds={notes_etuds})"
|
||||
f"external_ue_inscrit_et_note(moduleimpl_id={moduleimpl.id}, notes_etuds={notes_etuds})"
|
||||
)
|
||||
# Inscription des étudiants
|
||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||
moduleimpl_id,
|
||||
moduleimpl.id,
|
||||
formsemestre_id,
|
||||
list(notes_etuds.keys()),
|
||||
)
|
||||
@ -188,12 +188,12 @@ def external_ue_inscrit_et_note(
|
||||
)
|
||||
|
||||
|
||||
def get_existing_external_ue(formation_id: int) -> list[dict]:
|
||||
"Liste de toutes les UE externes définies dans cette formation"
|
||||
return sco_edit_ue.ue_list(args={"formation_id": formation_id, "is_external": True})
|
||||
def get_existing_external_ue(formation_id: int) -> list[UniteEns]:
|
||||
"Liste de toutes les UEs externes définies dans cette formation"
|
||||
return UniteEns.query.filter_by(formation_id=formation_id, is_external=True).all()
|
||||
|
||||
|
||||
def get_external_moduleimpl_id(formsemestre_id: int, ue_id: int) -> int:
|
||||
def get_external_moduleimpl(formsemestre_id: int, ue_id: int) -> ModuleImpl:
|
||||
"moduleimpl correspondant à l'UE externe indiquée de ce formsemestre"
|
||||
r = ndb.SimpleDictFetch(
|
||||
"""
|
||||
@ -205,7 +205,10 @@ def get_external_moduleimpl_id(formsemestre_id: int, ue_id: int) -> int:
|
||||
{"ue_id": ue_id, "formsemestre_id": formsemestre_id},
|
||||
)
|
||||
if r:
|
||||
return r[0]["moduleimpl_id"]
|
||||
modimpl_id = r[0]["moduleimpl_id"]
|
||||
modimpl = ModuleImpl.query.get(modimpl_id)
|
||||
assert modimpl
|
||||
return modimpl
|
||||
else:
|
||||
raise ScoValueError(
|
||||
f"""Aucun module externe ne correspond
|
||||
@ -225,20 +228,20 @@ def external_ue_create_form(formsemestre_id: int, etudid: int):
|
||||
|
||||
En BUT, pas d'UEs externes. Voir https://scodoc.org/git/ScoDoc/ScoDoc/issues/542
|
||||
"""
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
# Contrôle d'accès:
|
||||
if not formsemestre.can_be_edited_by(current_user):
|
||||
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
|
||||
if formsemestre.formation.is_apc():
|
||||
raise ScoValueError("Impossible d'ajouter une UE externe en BUT")
|
||||
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etud = Identite.get_etud(etudid)
|
||||
formation_id = formsemestre.formation.id
|
||||
existing_external_ue = get_existing_external_ue(formation_id)
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
"Ajout d'une UE externe pour %(nomprenom)s" % etud,
|
||||
f"Ajout d'une UE externe pour {etud.nomprenom}",
|
||||
javascripts=["js/sco_ue_external.js"],
|
||||
),
|
||||
"""<p class="help">Cette page permet d'indiquer que l'étudiant a suivi une UE
|
||||
@ -275,10 +278,10 @@ def external_ue_create_form(formsemestre_id: int, etudid: int):
|
||||
"input_type": "menu",
|
||||
"title": "UE externe existante:",
|
||||
"allowed_values": [""]
|
||||
+ [str(ue["ue_id"]) for ue in existing_external_ue],
|
||||
+ [str(ue.id) for ue in existing_external_ue],
|
||||
"labels": [default_label]
|
||||
+ [
|
||||
"%s (%s)" % (ue["titre"], ue["acronyme"])
|
||||
f"{ue.titre or ''} ({ue.acronyme})"
|
||||
for ue in existing_external_ue
|
||||
],
|
||||
"attributes": ['onchange="update_external_ue_form();"'],
|
||||
@ -364,7 +367,7 @@ def external_ue_create_form(formsemestre_id: int, etudid: int):
|
||||
)
|
||||
if tf[2]["existing_ue"]:
|
||||
ue_id = int(tf[2]["existing_ue"])
|
||||
moduleimpl_id = get_external_moduleimpl_id(formsemestre_id, ue_id)
|
||||
modimpl = get_external_moduleimpl(formsemestre_id, ue_id)
|
||||
else:
|
||||
acronyme = tf[2]["acronyme"].strip()
|
||||
if not acronyme:
|
||||
@ -375,7 +378,7 @@ def external_ue_create_form(formsemestre_id: int, etudid: int):
|
||||
+ tf[1]
|
||||
+ html_footer
|
||||
)
|
||||
moduleimpl_id = external_ue_create(
|
||||
modimpl = external_ue_create(
|
||||
formsemestre_id,
|
||||
titre=tf[2]["titre"],
|
||||
acronyme=acronyme,
|
||||
@ -384,7 +387,7 @@ def external_ue_create_form(formsemestre_id: int, etudid: int):
|
||||
)
|
||||
|
||||
external_ue_inscrit_et_note(
|
||||
moduleimpl_id,
|
||||
modimpl,
|
||||
formsemestre_id,
|
||||
{etudid: note_value},
|
||||
)
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
<h2 class="insidebar">Scolarité</h2>
|
||||
<a href="{{url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Semestres</a> <br>
|
||||
<a href="{{url_for('notes.index_html', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Programmes</a> <br>
|
||||
<a href="{{url_for('notes.index_html', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Formations</a> <br>
|
||||
|
||||
{% if current_user.has_permission(sco.Permission.AbsChange)%}
|
||||
<a href="{{url_for('assiduites.bilan_dept', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Assiduité</a> <br>
|
||||
|
@ -30,6 +30,7 @@ Module notes: issu de ScoDoc7 / ZNotes.py
|
||||
|
||||
Emmanuel Viennet, 2021
|
||||
"""
|
||||
import html
|
||||
from operator import itemgetter
|
||||
import time
|
||||
|
||||
@ -487,7 +488,6 @@ def get_ue_niveaux_options_html():
|
||||
return apc_edit_ue.get_ue_niveaux_options_html(ue)
|
||||
|
||||
|
||||
@bp.route("/ue_list") # backward compat
|
||||
@bp.route("/ue_table")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@ -682,21 +682,21 @@ def module_clone():
|
||||
@bp.route("/index_html")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def index_html():
|
||||
"Page accueil formations"
|
||||
|
||||
fmt = request.args.get("fmt", "html")
|
||||
editable = current_user.has_permission(Permission.EditFormation)
|
||||
table = sco_formations.formation_list_table()
|
||||
|
||||
if fmt != "html":
|
||||
return table.make_page(fmt=fmt, filename=f"Formations-{g.scodoc_dept}")
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Programmes formations"),
|
||||
"""<h2>Programmes pédagogiques</h2>
|
||||
""",
|
||||
html_sco_header.sco_header(page_title="Formations (programmes)"),
|
||||
"""<h2>Formations (programmes pédagogiques)</h2>
|
||||
""",
|
||||
table.html(),
|
||||
]
|
||||
T = sco_formations.formation_list_table()
|
||||
|
||||
H.append(T.html())
|
||||
|
||||
if editable:
|
||||
H.append(
|
||||
f"""
|
||||
@ -804,7 +804,7 @@ def formation_import_xml_form():
|
||||
<h2>Import effectué !</h2>
|
||||
<ul>
|
||||
<li><a class="stdlink" href="{
|
||||
url_for("notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id
|
||||
url_for("notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
|
||||
)}">Voir la formation</a>
|
||||
</li>
|
||||
<li><a class="stdlink" href="{
|
||||
@ -817,19 +817,6 @@ def formation_import_xml_form():
|
||||
"""
|
||||
|
||||
|
||||
# sco_publish(
|
||||
# "/formation_create_new_version",
|
||||
# sco_formations.formation_create_new_version,
|
||||
# Permission.EditFormation,
|
||||
# )
|
||||
|
||||
# --- UE
|
||||
sco_publish(
|
||||
"/ue_list",
|
||||
sco_edit_ue.ue_list,
|
||||
Permission.ScoView,
|
||||
)
|
||||
|
||||
sco_publish("/module_move", sco_edit_formation.module_move, Permission.EditFormation)
|
||||
sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.EditFormation)
|
||||
|
||||
@ -3284,11 +3271,12 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
for modimpl in modimpls:
|
||||
mod = sco_edit_module.module_list({"module_id": modimpl["module_id"]})[0]
|
||||
formations_set.add(mod["formation_id"])
|
||||
ue = sco_edit_ue.ue_list({"ue_id": mod["ue_id"]})[0]
|
||||
formations_set.add(ue["formation_id"])
|
||||
if ue["formation_id"] != mod["formation_id"]:
|
||||
ue = UniteEns.query.get_or_404(mod["ue_id"])
|
||||
ue_dict = ue.to_dict()
|
||||
formations_set.add(ue_dict["formation_id"])
|
||||
if ue_dict["formation_id"] != mod["formation_id"]:
|
||||
modimpl["mod"] = mod
|
||||
modimpl["ue"] = ue
|
||||
modimpl["ue"] = ue_dict
|
||||
bad_ue.append(modimpl)
|
||||
if sem["formation_id"] != mod["formation_id"]:
|
||||
bad_sem.append(modimpl)
|
||||
@ -3341,30 +3329,28 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def check_form_integrity(formation_id, fix=False):
|
||||
"debug"
|
||||
log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix))
|
||||
ues = sco_edit_ue.ue_list(args={"formation_id": formation_id})
|
||||
"debug (obsolete)"
|
||||
log(f"check_form_integrity: formation_id={formation_id} fix={fix}")
|
||||
formation: Formation = Formation.query.filter_by(
|
||||
dept_id=g.scodoc_dept_id, formation_id=formation_id
|
||||
).first_or_404()
|
||||
bad = []
|
||||
for ue in ues:
|
||||
mats = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||
for mat in mats:
|
||||
mods = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]})
|
||||
for mod in mods:
|
||||
if mod["ue_id"] != ue["ue_id"]:
|
||||
for ue in formation.ues:
|
||||
for matiere in ue.matieres:
|
||||
for mod in matiere.modules:
|
||||
if mod.ue_id != ue.id:
|
||||
if fix:
|
||||
# fix mod.ue_id
|
||||
log(
|
||||
"fix: mod.ue_id = %s (was %s)" % (ue["ue_id"], mod["ue_id"])
|
||||
)
|
||||
mod["ue_id"] = ue["ue_id"]
|
||||
sco_edit_module.do_module_edit(mod)
|
||||
log(f"fix: mod.ue_id = {ue.id} (was {mod.ue_id})")
|
||||
mod.ue_id = ue.id
|
||||
db.session.add(mod)
|
||||
bad.append(mod)
|
||||
if mod["formation_id"] != formation_id:
|
||||
if mod.formation_id != formation_id:
|
||||
bad.append(mod)
|
||||
if bad:
|
||||
txth = "<br>".join([str(x) for x in bad])
|
||||
txth = "<br>".join([html.escape(str(x)) for x in bad])
|
||||
txt = "\n".join([str(x) for x in bad])
|
||||
log("check_form_integrity: formation_id=%s\ninconsistencies:" % formation_id)
|
||||
log(f"check_form_integrity: formation_id={formation_id}\ninconsistencies:")
|
||||
log(txt)
|
||||
# Notify by e-mail
|
||||
send_scodoc_alarm("Notes: formation incoherente !", txt)
|
||||
@ -3380,39 +3366,31 @@ def check_form_integrity(formation_id, fix=False):
|
||||
@scodoc7func
|
||||
def check_formsemestre_integrity(formsemestre_id):
|
||||
"debug"
|
||||
log("check_formsemestre_integrity: formsemestre_id=%s" % (formsemestre_id))
|
||||
log(f"check_formsemestre_integrity: formsemestre_id={formsemestre_id}")
|
||||
# verifie que tous les moduleimpl d'un formsemestre
|
||||
# se réfèrent à un module dont l'UE appartient a la même formation
|
||||
# Ancien bug: les ue_id étaient mal copiés lors des création de versions
|
||||
# de formations
|
||||
diag = []
|
||||
|
||||
Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
|
||||
for mod in Mlist:
|
||||
if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]:
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
for modimpl in formsemestre.modimpls:
|
||||
if modimpl.module.ue_id != modimpl.module.matiere.ue_id:
|
||||
diag.append(
|
||||
"moduleimpl %s: module.ue_id=%s != matiere.ue_id=%s"
|
||||
% (
|
||||
mod["moduleimpl_id"],
|
||||
mod["module"]["ue_id"],
|
||||
mod["matiere"]["ue_id"],
|
||||
)
|
||||
f"""moduleimpl {modimpl.id}: module.ue_id={modimpl.module.ue_id
|
||||
} != matiere.ue_id={modimpl.module.matiere.ue_id}"""
|
||||
)
|
||||
if mod["ue"]["formation_id"] != mod["module"]["formation_id"]:
|
||||
if modimpl.module.ue.formation_id != modimpl.module.formation_id:
|
||||
diag.append(
|
||||
"moduleimpl %s: ue.formation_id=%s != mod.formation_id=%s"
|
||||
% (
|
||||
mod["moduleimpl_id"],
|
||||
mod["ue"]["formation_id"],
|
||||
mod["module"]["formation_id"],
|
||||
)
|
||||
f"""moduleimpl {modimpl.id}: ue.formation_id={
|
||||
modimpl.module.ue.formation_id} != mod.formation_id={
|
||||
modimpl.module.formation_id}"""
|
||||
)
|
||||
if diag:
|
||||
send_scodoc_alarm(
|
||||
"Notes: formation incoherente dans semestre %s !" % formsemestre_id,
|
||||
f"Notes: formation incoherente dans semestre {formsemestre_id} !",
|
||||
"\n".join(diag),
|
||||
)
|
||||
log("check_formsemestre_integrity: formsemestre_id=%s" % formsemestre_id)
|
||||
log(f"check_formsemestre_integrity: formsemestre_id={formsemestre_id}")
|
||||
log("inconsistencies:\n" + "\n".join(diag))
|
||||
else:
|
||||
diag = ["OK"]
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.939"
|
||||
SCOVERSION = "9.6.940"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -249,37 +249,7 @@ def test_formations(test_client):
|
||||
assert len(lim_modid) == 1
|
||||
|
||||
lim_modimpl_id = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
|
||||
# print(lim_modimpl_id)
|
||||
|
||||
# ---- Test de moduleimpl_withmodule_list
|
||||
|
||||
assert lim_modid == lim_modimpl_id # doit etre le meme resultat
|
||||
|
||||
liimp_sem1 = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||
formsemestre_id=sem1["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(liimp_sem1) == 2
|
||||
assert module_id in (
|
||||
liimp_sem1[0]["module_id"],
|
||||
liimp_sem1[1]["module_id"],
|
||||
)
|
||||
assert module_id2 in (
|
||||
liimp_sem1[0]["module_id"],
|
||||
liimp_sem1[1]["module_id"],
|
||||
)
|
||||
liimp_sem2 = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
assert module_id_t == liimp_sem2[0]["module_id"]
|
||||
liimp_modid = sco_moduleimpl.moduleimpl_withmodule_list(module_id=module_id)
|
||||
assert len(liimp_modid) == 1
|
||||
|
||||
liimp_modimplid = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||
moduleimpl_id=moduleimpl_id
|
||||
)
|
||||
|
||||
assert liimp_modid == liimp_modimplid
|
||||
assert lim_modid == lim_modimpl_id
|
||||
|
||||
# --- Suppression du module, matiere et ue test du semestre 2
|
||||
|
||||
|
@ -54,7 +54,7 @@ RELEASE=1
|
||||
ARCH="amd64"
|
||||
FACTORY_DIR="/opt/factory"
|
||||
DEST_DIR="$PACKAGE_NAME"_"$VERSION"-"$RELEASE"_"$ARCH"
|
||||
GIT_RELEASE_URL="https://scodoc.org/git/viennet/ScoDoc/archive/${RELEASE_TAG}.tar.gz"
|
||||
GIT_RELEASE_URL="https://scodoc.org/git/ScoDoc/ScoDoc/archive/${RELEASE_TAG}.tar.gz"
|
||||
UNIT_TESTS_DIR="/opt/scodoc" # on lance les tests dans le rep. de travail, pas idéal
|
||||
|
||||
echo "Le paquet sera $DEST_DIR.deb"
|
||||
|
Loading…
Reference in New Issue
Block a user