# -*- coding: UTF-8 -* """Définition d'un étudiant et données rattachées (adresses, annotations, ...) """ from flask import g, url_for from app import db from app import models from app.models import APO_CODE_STR_LEN from app.models import SHORT_STR_LEN from app.models import CODE_STR_LEN class Identite(db.Model): """étudiant""" __tablename__ = "identite" __table_args__ = ( db.UniqueConstraint("dept_id", "code_nip"), db.UniqueConstraint("dept_id", "code_ine"), ) id = db.Column(db.Integer, primary_key=True) etudid = db.synonym("id") dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) nom = db.Column(db.Text()) prenom = db.Column(db.Text()) nom_usuel = db.Column(db.Text()) # optionnel (si present, affiché à la place du nom) civilite = db.Column(db.String(1), nullable=False) __table_args__ = (db.CheckConstraint("civilite IN ('M', 'F', 'X')"),) date_naissance = db.Column(db.Date) lieu_naissance = db.Column(db.Text()) dept_naissance = db.Column(db.Text()) nationalite = db.Column(db.Text()) statut = db.Column(db.Text()) boursier = db.Column(db.Boolean()) # True si boursier ('O' en ScoDoc7) photo_filename = db.Column(db.Text()) # Codes INE et NIP pas unique car le meme etud peut etre ds plusieurs dept code_nip = db.Column(db.Text()) code_ine = db.Column(db.Text()) # Ancien id ScoDoc7 pour les migrations de bases anciennes # ne pas utiliser après migrate_scodoc7_dept_archives scodoc7_id = db.Column(db.Text(), nullable=True) # adresses = db.relationship("Adresse", lazy="dynamic", backref="etud") billets = db.relationship("BilletAbsence", backref="etudiant", lazy="dynamic") def __repr__(self): return f"" def civilite_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] def nom_disp(self): "nom à afficher" if self.nom_usuel: return ( (self.nom_usuel + " (" + self.nom + ")") if self.nom else self.nom_usuel ) else: return self.nom def get_first_email(self, field="email") -> str: "le mail associé à la première adrese de l'étudiant, ou None" return self.adresses[0].email or None if self.adresses.count() > 0 else None def to_dict_bul(self, include_urls=True): """Infos exportées dans les bulletins""" from app.scodoc import sco_photos d = { "civilite": self.civilite, "code_ine": self.code_ine, "code_nip": self.code_nip, "date_naissance": self.date_naissance.isoformat() if self.date_naissance else None, "email": self.get_first_email(), "emailperso": self.get_first_email("emailperso"), "etudid": self.id, "nom": self.nom_disp(), "prenom": self.prenom, } if include_urls: d["fiche_url"] = url_for( "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=self.id ) d["photo_url"] = (sco_photos.get_etud_photo_url(self.id),) return d def inscription_courante(self): """La première inscription à un formsemestre _actuellement_ en cours. None s'il n'y en a pas (ou plus, ou pas encore). """ r = [ ins for ins in self.formsemestre_inscriptions if ins.formsemestre.est_courant() ] return r[0] if r else None def inscription_courante_date(self, date_debut, date_fin): """La première inscription à un formsemestre entre date_debut et date_fin. None s'il n'y en a pas (ou plus, ou pas encore). """ r = [ ins for ins in self.formsemestre_inscriptions if ins.formsemestre.est_courant_date(date_debut, date_fin) ] return r[0] if r else None def etat_inscription(self, formsemestre_id): """etat de l'inscription de cet étudiant au semestre: False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF """ # voir si ce n'est pas trop lent: ins = models.FormSemestreInscription.query.filter_by( etudid=self.id, formsemestre_id=formsemestre_id ).first() if ins: return ins.etat return False class Adresse(db.Model): """Adresse d'un étudiant (le modèle permet plusieurs adresses, mais l'UI n'en gère qu'une seule) """ __tablename__ = "adresse" id = db.Column(db.Integer, primary_key=True) adresse_id = db.synonym("id") etudid = db.Column( db.Integer, db.ForeignKey("identite.id"), ) email = db.Column(db.Text()) # mail institutionnel emailperso = db.Column(db.Text) # email personnel (exterieur) domicile = db.Column(db.Text) codepostaldomicile = db.Column(db.Text) villedomicile = db.Column(db.Text) paysdomicile = db.Column(db.Text) telephone = db.Column(db.Text) telephonemobile = db.Column(db.Text) fax = db.Column(db.Text) typeadresse = db.Column( db.Text, default="domicile", server_default="domicile", nullable=False ) description = db.Column(db.Text) class Admission(db.Model): """Informations liées à l'admission d'un étudiant""" __tablename__ = "admissions" id = db.Column(db.Integer, primary_key=True) adm_id = db.synonym("id") etudid = db.Column( db.Integer, db.ForeignKey("identite.id"), ) # Anciens champs de ScoDoc7, à revoir pour être plus générique et souple # notamment dans le cadre du bac 2021 # de plus, certaines informations liées à APB ne sont plus disponibles # avec Parcoursup annee = db.Column(db.Integer) bac = db.Column(db.Text) specialite = db.Column(db.Text) annee_bac = db.Column(db.Integer) math = db.Column(db.Text) physique = db.Column(db.Float) anglais = db.Column(db.Float) francais = db.Column(db.Float) # Rang dans les voeux du candidat (inconnu avec APB et PS) rang = db.Column(db.Integer) # Qualité et décision du jury d'admission (ou de l'examinateur) qualite = db.Column(db.Float) rapporteur = db.Column(db.Text) decision = db.Column(db.Text) score = db.Column(db.Float) commentaire = db.Column(db.Text) # Lycée d'origine: nomlycee = db.Column(db.Text) villelycee = db.Column(db.Text) codepostallycee = db.Column(db.Text) codelycee = db.Column(db.Text) # 'APB', 'APC-PC', 'CEF', 'Direct', '?' (autre) type_admission = db.Column(db.Text) # était boursier dans le cycle precedent (lycee) ? boursier_prec = db.Column(db.Boolean()) # classement par le jury d'admission (1 à N), # global (pas celui d'APB si il y a des groupes) classement = db.Column(db.Integer) # code du groupe APB apb_groupe = db.Column(db.Text) # classement (1..Ngr) par le jury dans le groupe APB apb_classement_gr = db.Column(db.Integer) # Suivi scolarité / débouchés class ItemSuivi(db.Model): __tablename__ = "itemsuivi" id = db.Column(db.Integer, primary_key=True) itemsuivi_id = db.synonym("id") etudid = db.Column( db.Integer, db.ForeignKey("identite.id"), ) item_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) situation = db.Column(db.Text) class ItemSuiviTag(db.Model): __tablename__ = "itemsuivi_tags" id = db.Column(db.Integer, primary_key=True) dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) tag_id = db.synonym("id") title = db.Column(db.Text(), nullable=False, unique=True) # Association tag <-> module itemsuivi_tags_assoc = db.Table( "itemsuivi_tags_assoc", db.Column( "tag_id", db.Integer, db.ForeignKey("itemsuivi_tags.id", ondelete="CASCADE") ), db.Column( "itemsuivi_id", db.Integer, db.ForeignKey("itemsuivi.id", ondelete="CASCADE") ), ) class EtudAnnotation(db.Model): """Annotation sur un étudiant""" __tablename__ = "etud_annotations" id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) etudid = db.Column(db.Integer) # sans contrainte (compat ScoDoc 7)) author = db.Column(db.Text) # le pseudo (user_name), was zope_authenticated_user comment = db.Column(db.Text)