Merge pull request 'Ajout état-civil' (#629) from jmplace/ScoDoc-Lille:PR_etat-civil into master

Reviewed-on: https://scodoc.org/git/ScoDoc/ScoDoc/pulls/629
This commit is contained in:
Emmanuel Viennet 2023-05-11 14:04:38 +02:00
commit 590dd2e8b5
9 changed files with 129 additions and 17 deletions

View File

@ -484,6 +484,7 @@ class BulletinBUT:
d["etudid"] = etud.id d["etudid"] = etud.id
d["etud"] = d["etudiant"] d["etud"] = d["etudiant"]
d["etud"]["nomprenom"] = etud.nomprenom d["etud"]["nomprenom"] = etud.nomprenom
d["etud"]["etat_civil"] = etud.etat_civil
d.update(self.res.sem) d.update(self.res.sem)
etud_etat = self.res.get_etud_etat(etud.id) etud_etat = self.res.get_etud_etat(etud.id)
d["filigranne"] = sco_bulletins_pdf.get_filigranne( d["filigranne"] = sco_bulletins_pdf.get_filigranne(

View File

@ -30,6 +30,7 @@ class Identite(db.Model):
db.UniqueConstraint("dept_id", "code_nip"), db.UniqueConstraint("dept_id", "code_nip"),
db.UniqueConstraint("dept_id", "code_ine"), db.UniqueConstraint("dept_id", "code_ine"),
db.CheckConstraint("civilite IN ('M', 'F', 'X')"), db.CheckConstraint("civilite IN ('M', 'F', 'X')"),
db.CheckConstraint("civilite_etat_civil IN ('M', 'F', 'X')"),
) )
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -41,6 +42,12 @@ class Identite(db.Model):
nom_usuel = db.Column(db.Text()) nom_usuel = db.Column(db.Text())
"optionnel (si present, affiché à la place du nom)" "optionnel (si present, affiché à la place du nom)"
civilite = db.Column(db.String(1), nullable=False) civilite = db.Column(db.String(1), nullable=False)
# données d'état-civil. Si présent remplace les données d'usage dans les documents officiels (bulletins, PV)
# cf nomprenom_etat_civil()
civilite_etat_civil = db.Column(db.String(1), nullable=False, server_default="X")
prenom_etat_civil = db.Column(db.Text(), nullable=True)
date_naissance = db.Column(db.Date) date_naissance = db.Column(db.Date)
lieu_naissance = db.Column(db.Text()) lieu_naissance = db.Column(db.Text())
dept_naissance = db.Column(db.Text()) dept_naissance = db.Column(db.Text())
@ -104,6 +111,13 @@ class Identite(db.Model):
""" """
return {"M": "M.", "F": "Mme", "X": ""}[self.civilite] return {"M": "M.", "F": "Mme", "X": ""}[self.civilite]
@property
def civilite_etat_civil_str(self):
"""returns 'M.' ou 'Mme' ou '' (pour le genre neutre,
personnes ne souhaitant pas d'affichage).
"""
return {"M": "M.", "F": "Mme", "X": ""}[self.civilite_etat_civil]
def sex_nom(self, no_accents=False) -> str: def sex_nom(self, no_accents=False) -> str:
"'M. DUPONTÉ', ou si no_accents, 'M. DUPONTE'" "'M. DUPONTÉ', ou si no_accents, 'M. DUPONTE'"
s = f"{self.civilite_str} {(self.nom_usuel or self.nom).upper()}" s = f"{self.civilite_str} {(self.nom_usuel or self.nom).upper()}"
@ -150,6 +164,14 @@ class Identite(db.Model):
r.append("-".join([x.lower().capitalize() for x in fields])) r.append("-".join([x.lower().capitalize() for x in fields]))
return " ".join(r) return " ".join(r)
@property
def etat_civil(self):
if self.prenom_etat_civil:
civ = {"M": "M.", "F": "Mme", "X": ""}[self.civilite_etat_civil]
return f"{civ} {self.prenom_etat_civil} {self.nom}"
else:
return self.nomprenom
@property @property
def nom_short(self): def nom_short(self):
"Nom et début du prénom pour table recap: 'DUPONT Pi.'" "Nom et début du prénom pour table recap: 'DUPONT Pi.'"
@ -191,6 +213,8 @@ class Identite(db.Model):
"nom_usuel": self.nom_usuel, "nom_usuel": self.nom_usuel,
"prenom": self.prenom, "prenom": self.prenom,
"sort_key": self.sort_key, "sort_key": self.sort_key,
"civilite_etat_civil": self.civilite_etat_civil,
"prenom_etat_civil": self.prenom_etat_civil,
} }
def to_dict_scodoc7(self) -> dict: def to_dict_scodoc7(self) -> dict:
@ -234,6 +258,8 @@ class Identite(db.Model):
"dept_naissance": self.dept_naissance or "", "dept_naissance": self.dept_naissance or "",
"nationalite": self.nationalite or "", "nationalite": self.nationalite or "",
"boursier": self.boursier or "", "boursier": self.boursier or "",
"civilite_etat_civil": self.civilite_etat_civil,
"prenom_etat_civil": self.prenom_etat_civil,
} }
if include_urls and has_request_context(): if include_urls and has_request_context():
# test request context so we can use this func in tests under the flask shell # test request context so we can use this func in tests under the flask shell
@ -450,10 +476,10 @@ class Identite(db.Model):
M. Pierre Dupont M. Pierre Dupont
""" """
if with_paragraph: if with_paragraph:
return f"""{self.nomprenom}{line_sep}{self.code_nip or ""}{line_sep}{self.e} le { return f"""{self.etat_civil}{line_sep}{self.code_nip or ""}{line_sep}{self.e} le {
self.date_naissance.strftime("%d/%m/%Y") if self.date_naissance else ""}{ self.date_naissance.strftime("%d/%m/%Y") if self.date_naissance else ""}{
line_sep}à {self.lieu_naissance or ""}""" line_sep}à {self.lieu_naissance or ""}"""
return self.nomprenom return self.etat_civil
def photo_html(self, title=None, size="small") -> str: def photo_html(self, title=None, size="small") -> str:
"""HTML img tag for the photo, either in small size (h90) """HTML img tag for the photo, either in small size (h90)

View File

@ -167,8 +167,9 @@ class BulletinGenerator:
formsemestre_id = self.bul_dict["formsemestre_id"] formsemestre_id = self.bul_dict["formsemestre_id"]
nomprenom = self.bul_dict["etud"]["nomprenom"] nomprenom = self.bul_dict["etud"]["nomprenom"]
etat_civil = self.bul_dict["etud"]["etat_civil"]
marque_debut_bulletin = sco_pdf.DebutBulletin( marque_debut_bulletin = sco_pdf.DebutBulletin(
nomprenom, self.bul_dict["etat_civil"],
filigranne=self.bul_dict["filigranne"], filigranne=self.bul_dict["filigranne"],
footer_content=f"""ScoDoc - Bulletin de {nomprenom} - {time.strftime("%d/%m/%Y %H:%M")}""", footer_content=f"""ScoDoc - Bulletin de {nomprenom} - {time.strftime("%d/%m/%Y %H:%M")}""",
) )
@ -211,7 +212,7 @@ class BulletinGenerator:
document, document,
author="%s %s (E. Viennet) [%s]" author="%s %s (E. Viennet) [%s]"
% (sco_version.SCONAME, sco_version.SCOVERSION, self.description), % (sco_version.SCONAME, sco_version.SCOVERSION, self.description),
title=f"""Bulletin {sem["titremois"]} de {nomprenom}""", title=f"""Bulletin {sem["titremois"]} de {etat_civil}""",
subject="Bulletin de note", subject="Bulletin de note",
margins=self.margins, margins=self.margins,
server_name=self.server_name, server_name=self.server_name,

View File

@ -57,7 +57,12 @@ def format_etud_ident(etud):
else: else:
etud["nom_usuel"] = "" etud["nom_usuel"] = ""
etud["prenom"] = format_prenom(etud["prenom"]) etud["prenom"] = format_prenom(etud["prenom"])
if "prenom_etat_civil" in etud:
etud["prenom_etat_civil"] = format_prenom(etud["prenom_etat_civil"])
else:
etud["prenom_etat_civil"] = ""
etud["civilite_str"] = format_civilite(etud["civilite"]) etud["civilite_str"] = format_civilite(etud["civilite"])
etud["civilite_etat_civil_str"] = format_civilite(etud["civilite_etat_civil"])
# Nom à afficher: # Nom à afficher:
if etud["nom_usuel"]: if etud["nom_usuel"]:
etud["nom_disp"] = etud["nom_usuel"] etud["nom_disp"] = etud["nom_usuel"]
@ -225,7 +230,12 @@ _identiteEditor = ndb.EditableTable(
"nom", "nom",
"nom_usuel", "nom_usuel",
"prenom", "prenom",
"prenom_etat_civil",
"cas_id",
"cas_allow_login",
"cas_allow_scodoc_login",
"civilite", # 'M", "F", or "X" "civilite", # 'M", "F", or "X"
"civilite_etat_civil",
"date_naissance", "date_naissance",
"lieu_naissance", "lieu_naissance",
"dept_naissance", "dept_naissance",
@ -242,7 +252,9 @@ _identiteEditor = ndb.EditableTable(
input_formators={ input_formators={
"nom": force_uppercase, "nom": force_uppercase,
"prenom": force_uppercase, "prenom": force_uppercase,
"prenom_etat_civil": force_uppercase,
"civilite": input_civilite, "civilite": input_civilite,
"civilite_etat_civil": input_civilite,
"date_naissance": ndb.DateDMYtoISO, "date_naissance": ndb.DateDMYtoISO,
"boursier": bool, "boursier": bool,
}, },
@ -263,6 +275,7 @@ def identite_list(cnx, *a, **kw):
else: else:
o["annee_naissance"] = o["date_naissance"] o["annee_naissance"] = o["date_naissance"]
o["civilite_str"] = format_civilite(o["civilite"]) o["civilite_str"] = format_civilite(o["civilite"])
o["civilite_etat_civil_str"] = format_civilite(o["civilite_etat_civil"])
return objs return objs

View File

@ -71,6 +71,8 @@ FORMAT_FILE = "format_import_etudiants.txt"
ADMISSION_MODIFIABLE_FIELDS = ( ADMISSION_MODIFIABLE_FIELDS = (
"code_nip", "code_nip",
"code_ine", "code_ine",
"prenom_etat_civil",
"civilite_etat_civil",
"date_naissance", "date_naissance",
"lieu_naissance", "lieu_naissance",
"bac", "bac",

View File

@ -176,6 +176,18 @@ def ficheEtud(etudid=None):
sco_etud.fill_etuds_info([etud_]) sco_etud.fill_etuds_info([etud_])
# #
info = etud_ info = etud_
if etud.prenom_etat_civil:
info["etat_civil"] = (
"<h3>Etat-civil: "
+ etud.civilite_etat_civil_str
+ " "
+ etud.prenom_etat_civil
+ " "
+ etud.nom
+ "</h3>"
)
else:
info["etat_civil"] = ""
info["ScoURL"] = scu.ScoURL() info["ScoURL"] = scu.ScoURL()
info["authuser"] = authuser info["authuser"] = authuser
info["info_naissance"] = info["date_naissance"] info["info_naissance"] = info["date_naissance"]
@ -325,18 +337,17 @@ def ficheEtud(etudid=None):
if not sco_permissions_check.can_suppress_annotation(a["id"]): if not sco_permissions_check.can_suppress_annotation(a["id"]):
a["dellink"] = "" a["dellink"] = ""
else: else:
a["dellink"] = ( a[
'<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>' "dellink"
% ( ] = '<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>' % (
etudid, etudid,
a["id"], a["id"],
scu.icontag( scu.icontag(
"delete_img", "delete_img",
border="0", border="0",
alt="suppress", alt="suppress",
title="Supprimer cette annotation", title="Supprimer cette annotation",
), ),
)
) )
author = sco_users.user_info(a["author"]) author = sco_users.user_info(a["author"])
alist.append( alist.append(
@ -473,7 +484,7 @@ def ficheEtud(etudid=None):
<div class="ficheEtud" id="ficheEtud"><table> <div class="ficheEtud" id="ficheEtud"><table>
<tr><td> <tr><td>
<h2>%(nomprenom)s (%(inscription)s)</h2> <h2>%(nomprenom)s (%(inscription)s)</h2>
%(etat_civil)s
<span>%(emaillink)s</span> <span>%(emaillink)s</span>
</td><td class="photocell"> </td><td class="photocell">
<a href="etud_photo_orig_page?etudid=%(etudid)s">%(etudfoto)s</a> <a href="etud_photo_orig_page?etudid=%(etudid)s">%(etudfoto)s</a>

View File

@ -557,6 +557,7 @@ def etud_info(etudid=None, format="xml"):
"nom_usuel", "nom_usuel",
"prenom", "prenom",
"nomprenom", "nomprenom",
"prenom_etat_civil",
"email", "email",
"emailperso", "emailperso",
"domicile", "domicile",
@ -577,6 +578,9 @@ def etud_info(etudid=None, format="xml"):
): ):
d[a] = etud[a] # ne pas quoter car ElementTree.tostring quote déjà d[a] = etud[a] # ne pas quoter car ElementTree.tostring quote déjà
d["civilite"] = etud["civilite_str"] # exception: ne sort pas la civilite brute d["civilite"] = etud["civilite_str"] # exception: ne sort pas la civilite brute
d["civilite_etat_civil"] = etud[
"civilite_etat_civil_str"
] # exception: ne sort pas la civilite brute
d["sexe"] = d["civilite"] # backward compat pour anciens clients d["sexe"] = d["civilite"] # backward compat pour anciens clients
d["photo_url"] = sco_photos.etud_photo_url(etud) d["photo_url"] = sco_photos.etud_photo_url(etud)
@ -1442,6 +1446,25 @@ def _etudident_create_or_edit_form(edit):
"title": "Civilité", "title": "Civilité",
}, },
), ),
(
"prenom_etat_civil",
{
"size": 25,
"title": "Prénom (état-civil)",
"allow_null": True,
"explanation": "Si précisé, remplace le prénom d'usage dans les documents officiels",
},
),
(
"civilite_etat_civil",
{
"input_type": "menu",
"labels": ["Homme", "Femme", "Autre/neutre"],
"allowed_values": ["M", "F", "X"],
"title": "Civilité (état-civil)",
"explanation": "Si précisé: remplace la civilité d'usage dans les documents officiels",
},
),
( (
"date_naissance", "date_naissance",
{ {

View File

@ -0,0 +1,33 @@
"""ajout (prenom,civilite)_etat_civil
Revision ID: cf29790ca6f6
Revises: 6520faf67508
Create Date: 2023-02-25 10:55:42.831526
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "cf29790ca6f6"
down_revision = "6520faf67508"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - checked ###
op.add_column(
"identite",
sa.Column("civilite_etat_civil", sa.Text(), nullable=True, server_default="X"),
)
op.add_column("identite", sa.Column("prenom_etat_civil", sa.Text(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - checked ###
op.drop_column("identite", "prenom_etat_civil")
op.drop_column("identite", "civilite_etat_civil")
# ### end Alembic commands ###

View File

@ -9,6 +9,8 @@ nom; text; identite; 0; nom de l'etudiant;
nom_usuel; text; identite; 1; nom usuel (si different); nom_usuel; text; identite; 1; nom usuel (si different);
prenom; text; identite; 0; prenom de l'etudiant prenom; text; identite; 0; prenom de l'etudiant
civilite; text; identite; 1; sexe ('M', 'F', 'X');sexe;genre civilite; text; identite; 1; sexe ('M', 'F', 'X');sexe;genre
prenom_etat_civil; text; identite; 1; prenom à l'état-civil (si différent);prenom_etat_civil
civilite_etat_civil; text; identite; 1; sexe ('M', 'F', 'X') à l'état civil;civilite_etat_civil
date_naissance;text;identite; 1; date de naissance (jj/mm/aaaa) date_naissance;text;identite; 1; date de naissance (jj/mm/aaaa)
lieu_naissance;text;identite; 1; lieu de naissance lieu_naissance;text;identite; 1; lieu de naissance
nationalite; text; identite; 1; nationalite nationalite; text; identite; 1; nationalite