forked from ScoDoc/ScoDoc
API: etudiant/create (WIP), refactoring.
This commit is contained in:
parent
532fb3e701
commit
2377918b54
@ -18,6 +18,7 @@ from sqlalchemy import desc, func, or_
|
|||||||
from sqlalchemy.dialects.postgresql import VARCHAR
|
from sqlalchemy.dialects.postgresql import VARCHAR
|
||||||
|
|
||||||
import app
|
import app
|
||||||
|
from app import db
|
||||||
from app.api import api_bp as bp, api_web_bp
|
from app.api import api_bp as bp, api_web_bp
|
||||||
from app.api import tools
|
from app.api import tools
|
||||||
from app.but import bulletin_but_court
|
from app.but import bulletin_but_court
|
||||||
@ -28,10 +29,12 @@ from app.models import (
|
|||||||
FormSemestreInscription,
|
FormSemestreInscription,
|
||||||
FormSemestre,
|
FormSemestre,
|
||||||
Identite,
|
Identite,
|
||||||
|
ScolarNews,
|
||||||
)
|
)
|
||||||
from app.scodoc import sco_bulletins
|
from app.scodoc import sco_bulletins
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
||||||
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_utils import json_error, suppress_accents
|
from app.scodoc.sco_utils import json_error, suppress_accents
|
||||||
|
|
||||||
@ -475,3 +478,48 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
|||||||
data = sco_groups.get_etud_groups(etud.id, formsemestre.id)
|
data = sco_groups.get_etud_groups(etud.id, formsemestre.id)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/etudiant/create", methods=["POST"], defaults={"force": False})
|
||||||
|
@bp.route("/etudiant/create/force", methods=["POST"], defaults={"force": True})
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.EtudInscrit)
|
||||||
|
@as_json
|
||||||
|
def etudiant_create(force=False):
|
||||||
|
"""Création d'un nouvel étudiant
|
||||||
|
Si force, crée même si homonymie détectée.
|
||||||
|
L'étudiant créé n'est pas inscrit à un semestre.
|
||||||
|
Champs requis: nom, prenom (sauf si config sans prénom), dept (string:acronyme)
|
||||||
|
"""
|
||||||
|
args = request.get_json(force=True) # may raise 400 Bad Request
|
||||||
|
dept = args.get("dept", None)
|
||||||
|
if not dept:
|
||||||
|
return scu.json_error(400, "dept requis")
|
||||||
|
dept_o = Departement.query.filter_by(acronym=dept).first()
|
||||||
|
if not dept_o:
|
||||||
|
return scu.json_error(400, "dept invalide")
|
||||||
|
app.set_sco_dept(dept)
|
||||||
|
args["dept_id"] = dept_o.id
|
||||||
|
# vérifie que le département de création est bien autorisé
|
||||||
|
if not current_user.has_permission(Permission.EtudInscrit, dept):
|
||||||
|
return json_error(403, "departement non autorisé")
|
||||||
|
nom = args.get("nom", None)
|
||||||
|
prenom = args.get("prenom", None)
|
||||||
|
ok, homonyms = sco_etud.check_nom_prenom_homonyms(nom=nom, prenom=prenom)
|
||||||
|
if not ok:
|
||||||
|
return scu.json_error(400, "nom ou prénom invalide")
|
||||||
|
if len(homonyms) > 0 and not force:
|
||||||
|
return scu.json_error(
|
||||||
|
400, f"{len(homonyms)} homonymes détectés. Vous pouvez utiliser /force."
|
||||||
|
)
|
||||||
|
etud = Identite.create_etud(**args)
|
||||||
|
# Poste une nouvelle dans le département concerné:
|
||||||
|
ScolarNews.add(
|
||||||
|
typ=ScolarNews.NEWS_INSCR,
|
||||||
|
text=f"Nouvel étudiant {etud.html_link_fiche()}",
|
||||||
|
url=etud.url_fiche(),
|
||||||
|
max_frequency=0,
|
||||||
|
dept_id=dept_o.id,
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
return etud.to_dict_short()
|
||||||
|
@ -27,7 +27,6 @@ from app.scodoc.sco_exceptions import ScoValueError
|
|||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
|
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc import sco_etud # a deplacer dans scu
|
|
||||||
|
|
||||||
VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$")
|
VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$")
|
||||||
|
|
||||||
@ -462,8 +461,8 @@ class User(UserMixin, db.Model, ScoDocModel):
|
|||||||
"""nomplogin est le nom en majuscules suivi du prénom et du login
|
"""nomplogin est le nom en majuscules suivi du prénom et du login
|
||||||
e.g. Dupont Pierre (dupont)
|
e.g. Dupont Pierre (dupont)
|
||||||
"""
|
"""
|
||||||
nom = sco_etud.format_nom(self.nom) if self.nom else self.user_name.upper()
|
nom = scu.format_nom(self.nom) if self.nom else self.user_name.upper()
|
||||||
return f"{nom} {sco_etud.format_prenom(self.prenom)} ({self.user_name})"
|
return f"{nom} {scu.format_prenom(self.prenom)} ({self.user_name})"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_id_from_nomplogin(nomplogin: str) -> Optional[int]:
|
def get_user_id_from_nomplogin(nomplogin: str) -> Optional[int]:
|
||||||
@ -481,29 +480,29 @@ class User(UserMixin, db.Model, ScoDocModel):
|
|||||||
def get_nom_fmt(self):
|
def get_nom_fmt(self):
|
||||||
"""Nom formaté: "Martin" """
|
"""Nom formaté: "Martin" """
|
||||||
if self.nom:
|
if self.nom:
|
||||||
return sco_etud.format_nom(self.nom, uppercase=False)
|
return scu.format_nom(self.nom, uppercase=False)
|
||||||
else:
|
else:
|
||||||
return self.user_name
|
return self.user_name
|
||||||
|
|
||||||
def get_prenom_fmt(self):
|
def get_prenom_fmt(self):
|
||||||
"""Prénom formaté (minuscule capitalisées)"""
|
"""Prénom formaté (minuscule capitalisées)"""
|
||||||
return sco_etud.format_prenom(self.prenom)
|
return scu.format_prenom(self.prenom)
|
||||||
|
|
||||||
def get_nomprenom(self):
|
def get_nomprenom(self):
|
||||||
"""Nom capitalisé suivi de l'initiale du prénom:
|
"""Nom capitalisé suivi de l'initiale du prénom:
|
||||||
Viennet E.
|
Viennet E.
|
||||||
"""
|
"""
|
||||||
prenom_abbrv = scu.abbrev_prenom(sco_etud.format_prenom(self.prenom))
|
prenom_abbrv = scu.abbrev_prenom(scu.format_prenom(self.prenom))
|
||||||
return (self.get_nom_fmt() + " " + prenom_abbrv).strip()
|
return (self.get_nom_fmt() + " " + prenom_abbrv).strip()
|
||||||
|
|
||||||
def get_prenomnom(self):
|
def get_prenomnom(self):
|
||||||
"""L'initiale du prénom suivie du nom: "J.-C. Dupont" """
|
"""L'initiale du prénom suivie du nom: "J.-C. Dupont" """
|
||||||
prenom_abbrv = scu.abbrev_prenom(sco_etud.format_prenom(self.prenom))
|
prenom_abbrv = scu.abbrev_prenom(scu.format_prenom(self.prenom))
|
||||||
return (prenom_abbrv + " " + self.get_nom_fmt()).strip()
|
return (prenom_abbrv + " " + self.get_nom_fmt()).strip()
|
||||||
|
|
||||||
def get_nomcomplet(self):
|
def get_nomcomplet(self):
|
||||||
"Prénom et nom complets"
|
"Prénom et nom complets"
|
||||||
return sco_etud.format_prenom(self.prenom) + " " + self.get_nom_fmt()
|
return scu.format_prenom(self.prenom) + " " + self.get_nom_fmt()
|
||||||
|
|
||||||
# nomnoacc était le nom en minuscules sans accents (inutile)
|
# nomnoacc était le nom en minuscules sans accents (inutile)
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ from flask import Blueprint
|
|||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.models import Departement
|
from app.models import Departement
|
||||||
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
bp = Blueprint("entreprises", __name__)
|
bp = Blueprint("entreprises", __name__)
|
||||||
|
|
||||||
@ -15,12 +16,12 @@ SIRET_PROVISOIRE_START = "xx"
|
|||||||
|
|
||||||
@bp.app_template_filter()
|
@bp.app_template_filter()
|
||||||
def format_prenom(s):
|
def format_prenom(s):
|
||||||
return sco_etud.format_prenom(s)
|
return scu.format_prenom(s)
|
||||||
|
|
||||||
|
|
||||||
@bp.app_template_filter()
|
@bp.app_template_filter()
|
||||||
def format_nom(s):
|
def format_nom(s):
|
||||||
return sco_etud.format_nom(s)
|
return scu.format_nom(s)
|
||||||
|
|
||||||
|
|
||||||
@bp.app_template_filter()
|
@bp.app_template_filter()
|
||||||
|
@ -1580,8 +1580,8 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif request.method == "GET":
|
elif request.method == "GET":
|
||||||
form.etudiant.data = f"""{sco_etud.format_nom(etudiant.nom)} {
|
form.etudiant.data = f"""{scu.format_nom(etudiant.nom)} {
|
||||||
sco_etud.format_prenom(etudiant.prenom)}"""
|
scu.format_prenom(etudiant.prenom)}"""
|
||||||
form.etudid.data = etudiant.id
|
form.etudid.data = etudiant.id
|
||||||
form.type_offre.data = stage_apprentissage.type_offre
|
form.type_offre.data = stage_apprentissage.type_offre
|
||||||
form.date_debut.data = stage_apprentissage.date_debut
|
form.date_debut.data = stage_apprentissage.date_debut
|
||||||
@ -1699,7 +1699,7 @@ def json_etudiants():
|
|||||||
list = []
|
list = []
|
||||||
for etudiant in etudiants:
|
for etudiant in etudiants:
|
||||||
content = {}
|
content = {}
|
||||||
value = f"{sco_etud.format_nom(etudiant.nom)} {sco_etud.format_prenom(etudiant.prenom)}"
|
value = f"{scu.format_nom(etudiant.nom)} {scu.format_prenom(etudiant.prenom)}"
|
||||||
if etudiant.inscription_courante() is not None:
|
if etudiant.inscription_courante() is not None:
|
||||||
content = {
|
content = {
|
||||||
"id": f"{etudiant.id}",
|
"id": f"{etudiant.id}",
|
||||||
|
@ -71,7 +71,7 @@ class ScoDocModel:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_dict_fields(cls, args: dict) -> dict:
|
def convert_dict_fields(cls, args: dict) -> dict:
|
||||||
"""Convert fields in the given dict. No side effect.
|
"""Convert fields from the given dict to model's attributes values. No side effect.
|
||||||
By default, do nothing, but is overloaded by some subclasses.
|
By default, do nothing, but is overloaded by some subclasses.
|
||||||
args: dict with args in application.
|
args: dict with args in application.
|
||||||
returns: dict to store in model's db.
|
returns: dict to store in model's db.
|
||||||
@ -133,7 +133,6 @@ from app.models.notes import (
|
|||||||
NotesNotesLog,
|
NotesNotesLog,
|
||||||
)
|
)
|
||||||
from app.models.validations import (
|
from app.models.validations import (
|
||||||
ScolarEvent,
|
|
||||||
ScolarFormSemestreValidation,
|
ScolarFormSemestreValidation,
|
||||||
ScolarAutorisationInscription,
|
ScolarAutorisationInscription,
|
||||||
)
|
)
|
||||||
@ -152,3 +151,4 @@ from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
|
|||||||
from app.models.config import ScoDocSiteConfig
|
from app.models.config import ScoDocSiteConfig
|
||||||
|
|
||||||
from app.models.assiduites import Assiduite, Justificatif
|
from app.models.assiduites import Assiduite, Justificatif
|
||||||
|
from app.models.scolar_event import ScolarEvent
|
||||||
|
@ -15,7 +15,7 @@ from sqlalchemy import desc, text
|
|||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app import models
|
from app import models
|
||||||
|
from app.models.scolar_event import ScolarEvent
|
||||||
from app.scodoc import notesdb as ndb
|
from app.scodoc import notesdb as ndb
|
||||||
from app.scodoc.sco_bac import Baccalaureat
|
from app.scodoc.sco_bac import Baccalaureat
|
||||||
from app.scodoc.sco_exceptions import ScoInvalidParamError, ScoValueError
|
from app.scodoc.sco_exceptions import ScoInvalidParamError, ScoValueError
|
||||||
@ -170,9 +170,13 @@ class Identite(db.Model, models.ScoDocModel):
|
|||||||
|
|
||||||
def html_link_fiche(self) -> str:
|
def html_link_fiche(self) -> str:
|
||||||
"lien vers la fiche"
|
"lien vers la fiche"
|
||||||
return f"""<a class="stdlink" href="{
|
return f"""<a class="stdlink" href="{self.url_fiche()}">{self.nomprenom}</a>"""
|
||||||
url_for("scolar.ficheEtud", scodoc_dept=self.departement.acronym, etudid=self.id)
|
|
||||||
}">{self.nomprenom}</a>"""
|
def url_fiche(self) -> str:
|
||||||
|
"url de la fiche étudiant"
|
||||||
|
return url_for(
|
||||||
|
"scolar.ficheEtud", scodoc_dept=self.departement.acronym, etudid=self.id
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_request(cls, etudid=None, code_nip=None) -> "Identite":
|
def from_request(cls, etudid=None, code_nip=None) -> "Identite":
|
||||||
@ -211,6 +215,10 @@ class Identite(db.Model, models.ScoDocModel):
|
|||||||
etud.admission = Admission()
|
etud.admission = Admission()
|
||||||
etud.adresses.append(Adresse(typeadresse="domicile"))
|
etud.adresses.append(Adresse(typeadresse="domicile"))
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
||||||
|
event = ScolarEvent(etud=etud, event_type="CREATION")
|
||||||
|
db.session.add(event)
|
||||||
|
log(f"Identite.create {etud}")
|
||||||
return etud
|
return etud
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -133,7 +133,7 @@ class ScolarNews(db.Model):
|
|||||||
return query.order_by(cls.date.desc()).limit(n).all()
|
return query.order_by(cls.date.desc()).limit(n).all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, typ, obj=None, text="", url=None, max_frequency=600):
|
def add(cls, typ, obj=None, text="", url=None, max_frequency=600, dept_id=None):
|
||||||
"""Enregistre une nouvelle
|
"""Enregistre une nouvelle
|
||||||
Si max_frequency, ne génère pas 2 nouvelles "identiques"
|
Si max_frequency, ne génère pas 2 nouvelles "identiques"
|
||||||
à moins de max_frequency secondes d'intervalle (10 minutes par défaut).
|
à moins de max_frequency secondes d'intervalle (10 minutes par défaut).
|
||||||
@ -141,10 +141,11 @@ class ScolarNews(db.Model):
|
|||||||
même (obj, typ, user).
|
même (obj, typ, user).
|
||||||
La nouvelle enregistrée est aussi envoyée par mail.
|
La nouvelle enregistrée est aussi envoyée par mail.
|
||||||
"""
|
"""
|
||||||
|
dept_id = dept_id if dept_id is not None else g.scodoc_dept_id
|
||||||
if max_frequency:
|
if max_frequency:
|
||||||
last_news = (
|
last_news = (
|
||||||
cls.query.filter_by(
|
cls.query.filter_by(
|
||||||
dept_id=g.scodoc_dept_id,
|
dept_id=dept_id,
|
||||||
authenticated_user=current_user.user_name,
|
authenticated_user=current_user.user_name,
|
||||||
type=typ,
|
type=typ,
|
||||||
object=obj,
|
object=obj,
|
||||||
@ -163,7 +164,7 @@ class ScolarNews(db.Model):
|
|||||||
return
|
return
|
||||||
|
|
||||||
news = ScolarNews(
|
news = ScolarNews(
|
||||||
dept_id=g.scodoc_dept_id,
|
dept_id=dept_id,
|
||||||
authenticated_user=current_user.user_name,
|
authenticated_user=current_user.user_name,
|
||||||
type=typ,
|
type=typ,
|
||||||
object=obj,
|
object=obj,
|
||||||
|
48
app/models/scolar_event.py
Normal file
48
app/models/scolar_event.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"""évènements scolaires dans la vie d'un étudiant(inscription, ...)
|
||||||
|
"""
|
||||||
|
from app import db
|
||||||
|
from app.models import SHORT_STR_LEN
|
||||||
|
|
||||||
|
|
||||||
|
class ScolarEvent(db.Model):
|
||||||
|
"""Evenement dans le parcours scolaire d'un étudiant"""
|
||||||
|
|
||||||
|
__tablename__ = "scolar_events"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
event_id = db.synonym("id")
|
||||||
|
etudid = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
||||||
|
)
|
||||||
|
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
|
formsemestre_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_formsemestre.id", ondelete="SET NULL"),
|
||||||
|
)
|
||||||
|
ue_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_ue.id", ondelete="SET NULL"),
|
||||||
|
)
|
||||||
|
# 'CREATION', 'INSCRIPTION', 'DEMISSION',
|
||||||
|
# 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM'
|
||||||
|
# 'ECHEC_SEM'
|
||||||
|
# 'UTIL_COMPENSATION'
|
||||||
|
event_type = db.Column(db.String(SHORT_STR_LEN))
|
||||||
|
# Semestre compensé par formsemestre_id:
|
||||||
|
comp_formsemestre_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_formsemestre.id"),
|
||||||
|
)
|
||||||
|
etud = db.relationship("Identite", lazy="select", backref="events", uselist=False)
|
||||||
|
formsemestre = db.relationship(
|
||||||
|
"FormSemestre", lazy="select", uselist=False, foreign_keys=[formsemestre_id]
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"as a dict"
|
||||||
|
d = dict(self.__dict__)
|
||||||
|
d.pop("_sa_instance_state", None)
|
||||||
|
return d
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}({self.event_type}, {self.event_date.isoformat()}, {self.formsemestre})"
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: UTF-8 -*
|
# -*- coding: UTF-8 -*
|
||||||
|
|
||||||
"""Notes, décisions de jury, évènements scolaires
|
"""Notes, décisions de jury
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
@ -218,47 +218,3 @@ class ScolarAutorisationInscription(db.Model):
|
|||||||
msg=f"Passage vers S{autorisation.semestre_id}: effacé",
|
msg=f"Passage vers S{autorisation.semestre_id}: effacé",
|
||||||
)
|
)
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
||||||
|
|
||||||
class ScolarEvent(db.Model):
|
|
||||||
"""Evenement dans le parcours scolaire d'un étudiant"""
|
|
||||||
|
|
||||||
__tablename__ = "scolar_events"
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
event_id = db.synonym("id")
|
|
||||||
etudid = db.Column(
|
|
||||||
db.Integer,
|
|
||||||
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
|
||||||
)
|
|
||||||
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
|
||||||
formsemestre_id = db.Column(
|
|
||||||
db.Integer,
|
|
||||||
db.ForeignKey("notes_formsemestre.id", ondelete="SET NULL"),
|
|
||||||
)
|
|
||||||
ue_id = db.Column(
|
|
||||||
db.Integer,
|
|
||||||
db.ForeignKey("notes_ue.id", ondelete="SET NULL"),
|
|
||||||
)
|
|
||||||
# 'CREATION', 'INSCRIPTION', 'DEMISSION',
|
|
||||||
# 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM'
|
|
||||||
# 'ECHEC_SEM'
|
|
||||||
# 'UTIL_COMPENSATION'
|
|
||||||
event_type = db.Column(db.String(SHORT_STR_LEN))
|
|
||||||
# Semestre compensé par formsemestre_id:
|
|
||||||
comp_formsemestre_id = db.Column(
|
|
||||||
db.Integer,
|
|
||||||
db.ForeignKey("notes_formsemestre.id"),
|
|
||||||
)
|
|
||||||
etud = db.relationship("Identite", lazy="select", backref="events", uselist=False)
|
|
||||||
formsemestre = db.relationship(
|
|
||||||
"FormSemestre", lazy="select", uselist=False, foreign_keys=[formsemestre_id]
|
|
||||||
)
|
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
|
||||||
"as a dict"
|
|
||||||
d = dict(self.__dict__)
|
|
||||||
d.pop("_sa_instance_state", None)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.__class__.__name__}({self.event_type}, {self.event_date.isoformat()}, {self.formsemestre})"
|
|
||||||
|
@ -45,6 +45,12 @@ from app.models.etudiants import (
|
|||||||
pivot_year,
|
pivot_year,
|
||||||
)
|
)
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
from app.scodoc.sco_utils import (
|
||||||
|
format_civilite,
|
||||||
|
format_nom,
|
||||||
|
format_nomprenom,
|
||||||
|
format_prenom,
|
||||||
|
)
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
||||||
from app.scodoc import safehtml
|
from app.scodoc import safehtml
|
||||||
@ -102,60 +108,6 @@ def force_uppercase(s):
|
|||||||
return s.upper() if s else s
|
return s.upper() if s else s
|
||||||
|
|
||||||
|
|
||||||
def format_nomprenom(etud, reverse=False):
|
|
||||||
"""Formatte civilité/nom/prenom pour affichages: "M. Pierre Dupont"
|
|
||||||
Si reverse, "Dupont Pierre", sans civilité.
|
|
||||||
|
|
||||||
DEPRECATED: utiliser Identite.nomprenom
|
|
||||||
"""
|
|
||||||
nom = etud.get("nom_disp", "") or etud.get("nom_usuel", "") or etud["nom"]
|
|
||||||
prenom = format_prenom(etud["prenom"])
|
|
||||||
civilite = format_civilite(etud["civilite"])
|
|
||||||
if reverse:
|
|
||||||
fs = [nom, prenom]
|
|
||||||
else:
|
|
||||||
fs = [civilite, prenom, nom]
|
|
||||||
return " ".join([x for x in fs if x])
|
|
||||||
|
|
||||||
|
|
||||||
def format_prenom(s):
|
|
||||||
"""Formatte prenom etudiant pour affichage
|
|
||||||
DEPRECATED: utiliser Identite.prenom_str
|
|
||||||
"""
|
|
||||||
if not s:
|
|
||||||
return ""
|
|
||||||
frags = s.split()
|
|
||||||
r = []
|
|
||||||
for frag in frags:
|
|
||||||
fs = frag.split("-")
|
|
||||||
r.append("-".join([x.lower().capitalize() for x in fs]))
|
|
||||||
return " ".join(r)
|
|
||||||
|
|
||||||
|
|
||||||
def format_nom(s, uppercase=True):
|
|
||||||
if not s:
|
|
||||||
return ""
|
|
||||||
if uppercase:
|
|
||||||
return s.upper()
|
|
||||||
else:
|
|
||||||
return format_prenom(s)
|
|
||||||
|
|
||||||
|
|
||||||
def format_civilite(civilite):
|
|
||||||
"""returns 'M.' ou 'Mme' ou '' (pour le genre neutre,
|
|
||||||
personne ne souhaitant pas d'affichage).
|
|
||||||
Raises ScoValueError if conversion fails.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return {
|
|
||||||
"M": "M.",
|
|
||||||
"F": "Mme",
|
|
||||||
"X": "",
|
|
||||||
}[civilite]
|
|
||||||
except KeyError as exc:
|
|
||||||
raise ScoValueError(f"valeur invalide pour la civilité: {civilite}") from exc
|
|
||||||
|
|
||||||
|
|
||||||
def _format_etat_civil(etud: dict) -> str:
|
def _format_etat_civil(etud: dict) -> str:
|
||||||
"Mme Béatrice DUPONT, en utilisant les données d'état civil si indiquées."
|
"Mme Béatrice DUPONT, en utilisant les données d'état civil si indiquées."
|
||||||
if etud["prenom_etat_civil"] or etud["civilite_etat_civil"]:
|
if etud["prenom_etat_civil"] or etud["civilite_etat_civil"]:
|
||||||
@ -657,16 +609,6 @@ def create_etud(cnx, args: dict = None):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
etudid = etud.id
|
etudid = etud.id
|
||||||
|
|
||||||
# event
|
|
||||||
scolar_events_create(
|
|
||||||
cnx,
|
|
||||||
args={
|
|
||||||
"etudid": etudid,
|
|
||||||
"event_date": time.strftime("%d/%m/%Y"),
|
|
||||||
"formsemestre_id": None,
|
|
||||||
"event_type": "CREATION",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# log
|
# log
|
||||||
logdb(
|
logdb(
|
||||||
cnx,
|
cnx,
|
||||||
@ -674,16 +616,18 @@ def create_etud(cnx, args: dict = None):
|
|||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
msg="creation initiale",
|
msg="creation initiale",
|
||||||
)
|
)
|
||||||
etud = etudident_list(cnx, {"etudid": etudid})[0]
|
etud_dict = etudident_list(cnx, {"etudid": etudid})[0]
|
||||||
fill_etuds_info([etud])
|
fill_etuds_info([etud_dict])
|
||||||
etud["url"] = url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
etud_dict["url"] = url_for(
|
||||||
|
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid
|
||||||
|
)
|
||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
typ=ScolarNews.NEWS_INSCR,
|
typ=ScolarNews.NEWS_INSCR,
|
||||||
text='Nouvel étudiant <a href="%(url)s">%(nomprenom)s</a>' % etud,
|
text=f"Nouvel étudiant {etud.html_link_fiche()}",
|
||||||
url=etud["url"],
|
url=etud["url"],
|
||||||
max_frequency=0,
|
max_frequency=0,
|
||||||
)
|
)
|
||||||
return etud
|
return etud_dict
|
||||||
|
|
||||||
|
|
||||||
# ---------- "EVENTS"
|
# ---------- "EVENTS"
|
||||||
|
@ -42,6 +42,7 @@ from app.scodoc import sco_groups
|
|||||||
from app.scodoc.sco_exceptions import ScoException
|
from app.scodoc.sco_exceptions import ScoException
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
def form_search_etud(
|
def form_search_etud(
|
||||||
@ -271,7 +272,7 @@ def search_etud_by_name(term: str) -> list:
|
|||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
"label": "%s %s %s"
|
"label": "%s %s %s"
|
||||||
% (x["code_nip"], x["nom"], sco_etud.format_prenom(x["prenom"])),
|
% (x["code_nip"], x["nom"], scu.format_prenom(x["prenom"])),
|
||||||
"value": x["code_nip"],
|
"value": x["code_nip"],
|
||||||
}
|
}
|
||||||
for x in r
|
for x in r
|
||||||
@ -290,7 +291,7 @@ def search_etud_by_name(term: str) -> list:
|
|||||||
|
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
"label": "%s %s" % (x["nom"], sco_etud.format_prenom(x["prenom"])),
|
"label": "%s %s" % (x["nom"], scu.format_prenom(x["prenom"])),
|
||||||
"value": x["etudid"],
|
"value": x["etudid"],
|
||||||
}
|
}
|
||||||
for x in r
|
for x in r
|
||||||
|
@ -39,7 +39,7 @@ from app.comp.res_compat import NotesTableCompat
|
|||||||
from app.models import Formation, FormSemestre, FormSemestreInscription, Scolog
|
from app.models import Formation, FormSemestre, FormSemestreInscription, Scolog
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.groups import Partition, GroupDescr
|
from app.models.groups import Partition, GroupDescr
|
||||||
from app.models.validations import ScolarEvent
|
from app.models.scolar_event import ScolarEvent
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app import log
|
from app import log
|
||||||
from app.scodoc.scolog import logdb
|
from app.scodoc.scolog import logdb
|
||||||
|
@ -53,6 +53,7 @@ from app.scodoc import codes_cursus
|
|||||||
from app.scodoc import sco_cursus
|
from app.scodoc import sco_cursus
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc.sco_etud import etud_sort_key
|
from app.scodoc.sco_etud import etud_sort_key
|
||||||
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc import sco_xml
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
@ -573,8 +574,8 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
|
|||||||
etudid=str(e["etudid"]),
|
etudid=str(e["etudid"]),
|
||||||
civilite=etud["civilite_str"] or "",
|
civilite=etud["civilite_str"] or "",
|
||||||
sexe=etud["civilite_str"] or "", # compat
|
sexe=etud["civilite_str"] or "", # compat
|
||||||
nom=sco_etud.format_nom(etud["nom"] or ""),
|
nom=scu.format_nom(etud["nom"] or ""),
|
||||||
prenom=sco_etud.format_prenom(etud["prenom"] or ""),
|
prenom=scu.format_prenom(etud["prenom"] or ""),
|
||||||
origin=_comp_etud_origin(etud, formsemestre),
|
origin=_comp_etud_origin(etud, formsemestre),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -599,8 +600,8 @@ def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
|
|||||||
"etud",
|
"etud",
|
||||||
etudid=str(etud["etudid"]),
|
etudid=str(etud["etudid"]),
|
||||||
sexe=etud["civilite_str"] or "",
|
sexe=etud["civilite_str"] or "",
|
||||||
nom=sco_etud.format_nom(etud["nom"] or ""),
|
nom=scu.format_nom(etud["nom"] or ""),
|
||||||
prenom=sco_etud.format_prenom(etud["prenom"] or ""),
|
prenom=scu.format_prenom(etud["prenom"] or ""),
|
||||||
origin=_comp_etud_origin(etud, formsemestre),
|
origin=_comp_etud_origin(etud, formsemestre),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -244,8 +244,8 @@ def feuille_preparation_jury(formsemestre_id):
|
|||||||
[
|
[
|
||||||
etud.id,
|
etud.id,
|
||||||
etud.civilite_str,
|
etud.civilite_str,
|
||||||
sco_etud.format_nom(etud.nom),
|
scu.format_nom(etud.nom),
|
||||||
sco_etud.format_prenom(etud.prenom),
|
scu.format_prenom(etud.prenom),
|
||||||
etud.date_naissance,
|
etud.date_naissance,
|
||||||
etud.admission.bac if etud.admission else "",
|
etud.admission.bac if etud.admission else "",
|
||||||
etud.admission.specialite if etud.admission else "",
|
etud.admission.specialite if etud.admission else "",
|
||||||
|
@ -1244,7 +1244,7 @@ def _form_saisie_notes(
|
|||||||
'<span class="%s">' % classdem
|
'<span class="%s">' % classdem
|
||||||
+ e["civilite_str"]
|
+ e["civilite_str"]
|
||||||
+ " "
|
+ " "
|
||||||
+ sco_etud.format_nomprenom(e, reverse=True)
|
+ scu.format_nomprenom(e, reverse=True)
|
||||||
+ "</span>"
|
+ "</span>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -164,9 +164,9 @@ def trombino_html(groups_infos):
|
|||||||
H.append("</span>")
|
H.append("</span>")
|
||||||
H.append(
|
H.append(
|
||||||
'<span class="trombi_legend"><span class="trombi_prenom">'
|
'<span class="trombi_legend"><span class="trombi_prenom">'
|
||||||
+ sco_etud.format_prenom(t["prenom"])
|
+ scu.format_prenom(t["prenom"])
|
||||||
+ '</span><span class="trombi_nom">'
|
+ '</span><span class="trombi_nom">'
|
||||||
+ sco_etud.format_nom(t["nom"])
|
+ scu.format_nom(t["nom"])
|
||||||
+ (" <i>(dem.)</i>" if t["etat"] == "D" else "")
|
+ (" <i>(dem.)</i>" if t["etat"] == "D" else "")
|
||||||
)
|
)
|
||||||
H.append("</span></span></span>")
|
H.append("</span></span></span>")
|
||||||
@ -349,7 +349,7 @@ def _trombino_pdf(groups_infos):
|
|||||||
[img],
|
[img],
|
||||||
[
|
[
|
||||||
Paragraph(
|
Paragraph(
|
||||||
SU(sco_etud.format_nomprenom(t)),
|
SU(scu.format_nomprenom(t)),
|
||||||
style_sheet["Normal"],
|
style_sheet["Normal"],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -428,7 +428,7 @@ def _listeappel_photos_pdf(groups_infos):
|
|||||||
t = groups_infos.members[i]
|
t = groups_infos.members[i]
|
||||||
img = _get_etud_platypus_image(t, image_width=PHOTO_WIDTH)
|
img = _get_etud_platypus_image(t, image_width=PHOTO_WIDTH)
|
||||||
txt = Paragraph(
|
txt = Paragraph(
|
||||||
SU(sco_etud.format_nomprenom(t)),
|
SU(scu.format_nomprenom(t)),
|
||||||
style_sheet["Normal"],
|
style_sheet["Normal"],
|
||||||
)
|
)
|
||||||
if currow:
|
if currow:
|
||||||
|
@ -55,7 +55,7 @@ def trombino_doc(groups_infos):
|
|||||||
cell = table.rows[2 * li + 1].cells[co]
|
cell = table.rows[2 * li + 1].cells[co]
|
||||||
cell.vertical_alignment = WD_ALIGN_VERTICAL.TOP
|
cell.vertical_alignment = WD_ALIGN_VERTICAL.TOP
|
||||||
cell_p, cell_f, cell_r = _paragraph_format_run(cell)
|
cell_p, cell_f, cell_r = _paragraph_format_run(cell)
|
||||||
cell_r.add_text(sco_etud.format_nomprenom(t))
|
cell_r.add_text(scu.format_nomprenom(t))
|
||||||
cell_f.space_after = Mm(8)
|
cell_f.space_after = Mm(8)
|
||||||
|
|
||||||
return scu.send_docx(document, filename)
|
return scu.send_docx(document, filename)
|
||||||
|
@ -196,9 +196,9 @@ def pdf_trombino_tours(
|
|||||||
Paragraph(
|
Paragraph(
|
||||||
SU(
|
SU(
|
||||||
"<para align=center><font size=8>"
|
"<para align=center><font size=8>"
|
||||||
+ sco_etud.format_prenom(m["prenom"])
|
+ scu.format_prenom(m["prenom"])
|
||||||
+ " "
|
+ " "
|
||||||
+ sco_etud.format_nom(m["nom"])
|
+ scu.format_nom(m["nom"])
|
||||||
+ text_group
|
+ text_group
|
||||||
+ "</font></para>"
|
+ "</font></para>"
|
||||||
),
|
),
|
||||||
@ -413,11 +413,7 @@ def pdf_feuille_releve_absences(
|
|||||||
for m in members:
|
for m in members:
|
||||||
currow = [
|
currow = [
|
||||||
Paragraph(
|
Paragraph(
|
||||||
SU(
|
SU(scu.format_nom(m["nom"]) + " " + scu.format_prenom(m["prenom"])),
|
||||||
sco_etud.format_nom(m["nom"])
|
|
||||||
+ " "
|
|
||||||
+ sco_etud.format_prenom(m["prenom"])
|
|
||||||
),
|
|
||||||
StyleSheet["Normal"],
|
StyleSheet["Normal"],
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -64,6 +64,7 @@ from config import Config
|
|||||||
from app import log, ScoDocJSONEncoder
|
from app import log, ScoDocJSONEncoder
|
||||||
|
|
||||||
from app.scodoc.codes_cursus import NOTES_TOLERANCE, CODES_EXPL
|
from app.scodoc.codes_cursus import NOTES_TOLERANCE, CODES_EXPL
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
from app.scodoc import sco_xml
|
from app.scodoc import sco_xml
|
||||||
import sco_version
|
import sco_version
|
||||||
|
|
||||||
@ -1139,6 +1140,61 @@ def abbrev_prenom(prenom):
|
|||||||
return abrv
|
return abrv
|
||||||
|
|
||||||
|
|
||||||
|
def format_civilite(civilite):
|
||||||
|
"""returns 'M.' ou 'Mme' ou '' (pour le genre neutre,
|
||||||
|
personne ne souhaitant pas d'affichage).
|
||||||
|
Raises ScoValueError if conversion fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return {
|
||||||
|
"M": "M.",
|
||||||
|
"F": "Mme",
|
||||||
|
"X": "",
|
||||||
|
}[civilite]
|
||||||
|
except KeyError as exc:
|
||||||
|
raise ScoValueError(f"valeur invalide pour la civilité: {civilite}") from exc
|
||||||
|
|
||||||
|
|
||||||
|
def format_nomprenom(etud, reverse=False):
|
||||||
|
"""Formatte civilité/nom/prenom pour affichages: "M. Pierre Dupont"
|
||||||
|
Si reverse, "Dupont Pierre", sans civilité.
|
||||||
|
|
||||||
|
DEPRECATED: utiliser Identite.nomprenom
|
||||||
|
"""
|
||||||
|
nom = etud.get("nom_disp", "") or etud.get("nom_usuel", "") or etud["nom"]
|
||||||
|
prenom = format_prenom(etud["prenom"])
|
||||||
|
civilite = format_civilite(etud["civilite"])
|
||||||
|
if reverse:
|
||||||
|
fs = [nom, prenom]
|
||||||
|
else:
|
||||||
|
fs = [civilite, prenom, nom]
|
||||||
|
return " ".join([x for x in fs if x])
|
||||||
|
|
||||||
|
|
||||||
|
def format_nom(s, uppercase=True):
|
||||||
|
"Formatte le nom"
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
if uppercase:
|
||||||
|
return s.upper()
|
||||||
|
else:
|
||||||
|
return format_prenom(s)
|
||||||
|
|
||||||
|
|
||||||
|
def format_prenom(s):
|
||||||
|
"""Formatte prenom etudiant pour affichage
|
||||||
|
DEPRECATED: utiliser Identite.prenom_str
|
||||||
|
"""
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
frags = s.split()
|
||||||
|
r = []
|
||||||
|
for frag in frags:
|
||||||
|
fs = frag.split("-")
|
||||||
|
r.append("-".join([x.lower().capitalize() for x in fs]))
|
||||||
|
return " ".join(r)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
def timedate_human_repr():
|
def timedate_human_repr():
|
||||||
"representation du temps courant pour utilisateur"
|
"representation du temps courant pour utilisateur"
|
||||||
@ -1480,6 +1536,7 @@ def is_assiduites_module_forced(
|
|||||||
|
|
||||||
def get_assiduites_time_config(config_type: str) -> str:
|
def get_assiduites_time_config(config_type: str) -> str:
|
||||||
from app.models import ScoDocSiteConfig
|
from app.models import ScoDocSiteConfig
|
||||||
|
|
||||||
match config_type:
|
match config_type:
|
||||||
case "matin":
|
case "matin":
|
||||||
return ScoDocSiteConfig.get("assi_morning_time", "08:00:00")
|
return ScoDocSiteConfig.get("assi_morning_time", "08:00:00")
|
||||||
|
@ -28,10 +28,11 @@ from tests.api.setup_test_api import (
|
|||||||
API_URL,
|
API_URL,
|
||||||
API_USER_ADMIN,
|
API_USER_ADMIN,
|
||||||
CHECK_CERTIFICATE,
|
CHECK_CERTIFICATE,
|
||||||
|
DEPT_ACRONYM,
|
||||||
POST_JSON,
|
POST_JSON,
|
||||||
api_headers,
|
|
||||||
get_auth_headers,
|
get_auth_headers,
|
||||||
)
|
)
|
||||||
|
from tests.api.setup_test_api import api_headers # pylint: disable=unused-import
|
||||||
from tests.api.tools_test_api import (
|
from tests.api.tools_test_api import (
|
||||||
BULLETIN_ETUDIANT_FIELDS,
|
BULLETIN_ETUDIANT_FIELDS,
|
||||||
BULLETIN_FIELDS,
|
BULLETIN_FIELDS,
|
||||||
@ -923,3 +924,20 @@ def test_etudiant_groups(api_headers):
|
|||||||
group = groups[0]
|
group = groups[0]
|
||||||
fields_ok = verify_fields(group, fields)
|
fields_ok = verify_fields(group, fields)
|
||||||
assert fields_ok is True
|
assert fields_ok is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_etudiant_create(api_headers):
|
||||||
|
"""/etudiant/create"""
|
||||||
|
admin_header = get_auth_headers(API_USER_ADMIN, API_PASSWORD_ADMIN)
|
||||||
|
args = {
|
||||||
|
"prenom": "Carl Philipp Emanuel",
|
||||||
|
"nom": "Bach",
|
||||||
|
"dept": DEPT_ACRONYM,
|
||||||
|
"civilite": "M",
|
||||||
|
}
|
||||||
|
etud = POST_JSON(
|
||||||
|
"/etudiant/create",
|
||||||
|
args,
|
||||||
|
headers=admin_header,
|
||||||
|
)
|
||||||
|
assert etud["nom"] == args["nom"].upper()
|
||||||
|
Loading…
Reference in New Issue
Block a user