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