forked from ScoDoc/ScoDoc
api assiduite faite, test unitaire à venir
This commit is contained in:
parent
3e0f43d5ea
commit
b7fb8879df
@ -34,6 +34,7 @@ def requested_format(default_format="json", allowed_formats=None):
|
||||
from app.api import tokens
|
||||
from app.api import (
|
||||
absences,
|
||||
assiduites,
|
||||
billets_absences,
|
||||
departements,
|
||||
etudiants,
|
||||
|
309
app/api/assiduites.py
Normal file
309
app/api/assiduites.py
Normal file
@ -0,0 +1,309 @@
|
||||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
"""ScoDoc 9 API : Assiduités
|
||||
"""
|
||||
from datetime import datetime
|
||||
from pytz import UTC
|
||||
|
||||
from typing import List
|
||||
from flask import g, jsonify, request
|
||||
|
||||
from app import db
|
||||
|
||||
from app.api import api_bp as bp
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from flask_login import login_required
|
||||
|
||||
|
||||
from app.models import Identite, Assiduite
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
|
||||
@bp.route("/assiduite/<int:assiduiteid>")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def assiduite(assiduiteid: int = None):
|
||||
"""Retourne un objet assiduité à partir de son id
|
||||
|
||||
Exemple de résultat:
|
||||
{
|
||||
"assiduiteid": 1,
|
||||
"etuid": 2,
|
||||
"moduleimpl_id": 3,
|
||||
"date_debut": "2022-10-31T08:00",
|
||||
"date_fin": "2022-10-31T10:00",
|
||||
"etat": "retard"
|
||||
}
|
||||
"""
|
||||
|
||||
assiduite = Assiduite.query.get(assiduiteid)
|
||||
if assiduite is None:
|
||||
return json_error(404, message="assiduité inexistante")
|
||||
|
||||
data = assiduite.to_dict()
|
||||
|
||||
return jsonify(change_etat(data))
|
||||
|
||||
|
||||
@bp.route("/assiduites/<int:etuid>", defaults={"with_query": False})
|
||||
@bp.route("/assiduites/<int:etuid>/query", defaults={"with_query": True})
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def assiduites(etuid: int = None, with_query: bool = False):
|
||||
"""Retourne toutes les assiduités d'un étudiant"""
|
||||
query = Identite.query.filter_by(id=etuid)
|
||||
if g.scodoc_dept:
|
||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||
|
||||
etud: Identite = query.first_or_404(etuid)
|
||||
assiduites: List[Assiduite] = etud.assiduites.all()
|
||||
|
||||
if with_query:
|
||||
# cas 1 : etat assiduite
|
||||
etat = request.args.get("etat")
|
||||
if etat is not None:
|
||||
etat = list(etat.split(","))
|
||||
etat = [scu.ETATS_ASSIDUITE.get(e, "absent") for e in etat]
|
||||
assiduites = [ass for ass in assiduites if ass.etat in etat]
|
||||
|
||||
# cas 2 : date de début
|
||||
deb = request.args.get("date_debut")
|
||||
deb: datetime = is_iso_formated(deb, True)
|
||||
|
||||
if deb is not None:
|
||||
filtered_assiduites = []
|
||||
for ass in assiduites:
|
||||
if deb.tzinfo is None:
|
||||
deb: datetime = deb.replace(tzinfo=ass.date_debut.tzinfo)
|
||||
|
||||
if ass.date_debut >= deb:
|
||||
filtered_assiduites.append(ass)
|
||||
assiduites.clear()
|
||||
assiduites.extend(filtered_assiduites)
|
||||
|
||||
# cas 3 : date de fin
|
||||
fin = request.args.get("date_fin")
|
||||
fin = is_iso_formated(fin, True)
|
||||
|
||||
if fin is not None:
|
||||
filtered_assiduites = []
|
||||
for ass in assiduites:
|
||||
if fin.tzinfo is None:
|
||||
fin: datetime = fin.replace(tzinfo=ass.date_fin.tzinfo)
|
||||
|
||||
if ass.date_fin <= fin:
|
||||
filtered_assiduites.append(ass)
|
||||
assiduites.clear()
|
||||
assiduites.extend(filtered_assiduites)
|
||||
|
||||
# cas 4 : moduleimpl_id
|
||||
module = request.args.get("moduleimpl_id")
|
||||
try:
|
||||
module = int(module)
|
||||
except Exception:
|
||||
module = None
|
||||
|
||||
if module is not None:
|
||||
assiduites = [ass for ass in assiduites if ass.moduleimpl_id == module]
|
||||
|
||||
data_set: List[dict] = []
|
||||
for ass in assiduites:
|
||||
data = ass.to_dict()
|
||||
data_set.append(change_etat(data))
|
||||
|
||||
return jsonify(data_set)
|
||||
|
||||
|
||||
@bp.route("/assiduite/<int:etuid>/create", methods=["POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def create(etuid: int = None):
|
||||
"""
|
||||
Création d'une assiduité pour l'étudiant (etuid)
|
||||
La requête doit avoir un content type "application/json":
|
||||
{
|
||||
"date_debut": str,
|
||||
"date_fin": str,
|
||||
"etat": str,
|
||||
}
|
||||
ou
|
||||
{
|
||||
"date_debut": str,
|
||||
"date_fin": str,
|
||||
"etat": str,
|
||||
"moduleimpl_id": int,
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
etud: Identite = Identite.query.filter_by(id=etuid).first_or_404()
|
||||
|
||||
data = request.get_json(force=True)
|
||||
errors: List[str] = []
|
||||
|
||||
# -- vérifications de l'objet json --
|
||||
# cas 1 : ETAT
|
||||
etat = data.get("etat", None)
|
||||
if etat is None:
|
||||
errors.append("param 'etat': manquant")
|
||||
elif etat not in scu.ETATS_ASSIDUITE.keys():
|
||||
errors.append("param 'etat': invalide")
|
||||
|
||||
data = change_etat(data, False)
|
||||
etat = data.get("etat", None)
|
||||
|
||||
# cas 2 : date_debut
|
||||
date_debut = data.get("date_debut", None)
|
||||
if date_debut is None:
|
||||
errors.append("param 'date_debut': manquant")
|
||||
deb = is_iso_formated(date_debut, True)
|
||||
if deb is None:
|
||||
errors.append("param 'date_debut': format invalide")
|
||||
|
||||
# cas 3 : date_fin
|
||||
date_fin = data.get("date_fin", None)
|
||||
if date_fin is None:
|
||||
errors.append("param 'date_fin': manquant")
|
||||
fin = is_iso_formated(date_fin, True)
|
||||
if fin is None:
|
||||
errors.append(f"param 'date_fin': format invalide")
|
||||
|
||||
# cas 4 : moduleimpl_id
|
||||
|
||||
moduleimpl_id = data.get("moduleimpl_id", None)
|
||||
if moduleimpl_id is not None:
|
||||
try:
|
||||
moduleimpl_id: int = int(moduleimpl_id)
|
||||
if moduleimpl_id < 0:
|
||||
raise Exception
|
||||
except:
|
||||
errors.append("param 'moduleimpl_id': invalide")
|
||||
|
||||
if errors != []:
|
||||
err: str = ", ".join(errors)
|
||||
return json_error(404, err)
|
||||
|
||||
# TOUT EST OK
|
||||
nouv_assiduite: Assiduite or str = Assiduite.create_assiduite(
|
||||
date_debut=deb,
|
||||
date_fin=fin,
|
||||
etat=etat,
|
||||
etud=etud,
|
||||
module=moduleimpl_id,
|
||||
)
|
||||
|
||||
if type(nouv_assiduite) is Assiduite:
|
||||
return jsonify({"assiduiteid": nouv_assiduite.assiduiteid})
|
||||
|
||||
return json_error(
|
||||
404,
|
||||
{
|
||||
1: "La période sélectionnée est déjà couverte par une autre assiduite",
|
||||
2: "L'étudiant ne participe pas au moduleimpl sélectionné",
|
||||
}.get(nouv_assiduite),
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/assiduite/<int:assiduiteid>/delete", methods=["POST"])
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAssiduiteChange)
|
||||
def delete(assiduiteid: int):
|
||||
"""
|
||||
Suppression d'une assiduité à partir de son id
|
||||
"""
|
||||
assiduite: Assiduite = Assiduite.query.filter_by(id=assiduiteid).first_or_404()
|
||||
db.session.delete(assiduite)
|
||||
db.session.commit()
|
||||
return jsonify({"OK": True})
|
||||
|
||||
|
||||
@bp.route("/assiduite/<int:assiduiteid>/edit", methods=["POST"])
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAssiduiteChange)
|
||||
def edit(assiduiteid: int):
|
||||
"""
|
||||
Edition d'une assiduité à partir de son id
|
||||
La requête doit avoir un content type "application/json":
|
||||
{
|
||||
"etat": str,
|
||||
"moduleimpl_id": int
|
||||
}
|
||||
"""
|
||||
assiduite: Assiduite = Assiduite.query.filter_by(id=assiduiteid).first_or_404()
|
||||
errors: List[str] = []
|
||||
data = request.get_json(force=True)
|
||||
|
||||
# Vérifications de data
|
||||
|
||||
# Cas 1 : Etat
|
||||
if data.get("etat") is not None:
|
||||
data = change_etat(data, False)
|
||||
if data.get("etat") is None:
|
||||
errors.append("param 'etat': invalide")
|
||||
else:
|
||||
assiduite.etat = data.get("etat")
|
||||
|
||||
# Cas 2 : Moduleimpl_id
|
||||
moduleimpl_id = data.get("moduleimpl_id", False)
|
||||
if moduleimpl_id is not False:
|
||||
try:
|
||||
if moduleimpl_id is not None:
|
||||
moduleimpl_id: int = int(moduleimpl_id)
|
||||
if moduleimpl_id < 0 or not Assiduite.verif_moduleimpl(
|
||||
moduleimpl_id, assiduite.etudid
|
||||
):
|
||||
raise Exception
|
||||
|
||||
assiduite.moduleimpl_id = moduleimpl_id
|
||||
except:
|
||||
errors.append("param 'moduleimpl_id': invalide")
|
||||
|
||||
if errors != []:
|
||||
err: str = ", ".join(errors)
|
||||
return json_error(404, err)
|
||||
|
||||
db.session.add(assiduite)
|
||||
db.session.commit()
|
||||
return jsonify({"OK": True})
|
||||
|
||||
|
||||
# -- Utils --
|
||||
|
||||
|
||||
def change_etat(data: dict, from_int: bool = True):
|
||||
"""change dans un json la valeur du champs état"""
|
||||
if from_int:
|
||||
data["etat"] = scu.ETAT_ASSIDUITE_NAME.get(data["etat"])
|
||||
else:
|
||||
data["etat"] = scu.ETATS_ASSIDUITE.get(data["etat"])
|
||||
return data
|
||||
|
||||
|
||||
def is_iso_formated(date: str, convert=False) -> bool or datetime or None:
|
||||
"""
|
||||
Vérifie si une date est au format iso
|
||||
|
||||
Retourne un booléen Vrai (ou un objet Datetime si convert = True)
|
||||
si l'objet est au format iso
|
||||
|
||||
Retourne Faux si l'objet n'est pas au format et convert = False
|
||||
|
||||
Retourne None sinon
|
||||
"""
|
||||
import dateutil.parser as dtparser
|
||||
|
||||
try:
|
||||
date: datetime = dtparser.isoparse(date)
|
||||
if date.tzinfo is None:
|
||||
date = UTC.localize(date)
|
||||
return date if convert else True
|
||||
except Exception:
|
||||
return None if convert else False
|
@ -82,3 +82,5 @@ from app.models.but_refcomp import (
|
||||
from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
|
||||
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
|
||||
from app.models.assiduites import Assiduite, Justificatif
|
||||
|
@ -1,9 +1,14 @@
|
||||
# -*- coding: UTF-8 -*
|
||||
"""Gestion de l'assiduité (assiduités + justificatifs)
|
||||
"""
|
||||
|
||||
from app import db
|
||||
from app.models import CODE_STR_LEN, SHORT_STR_LEN
|
||||
from app.models import ModuleImpl
|
||||
from app.models.etudiants import Identite
|
||||
from app.models.formsemestre import FormSemestre
|
||||
from app.scodoc.sco_utils import EtatAssiduite
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Tuple, List
|
||||
|
||||
|
||||
class Assiduite(db.Model):
|
||||
@ -15,7 +20,8 @@ class Assiduite(db.Model):
|
||||
|
||||
__tablename__ = "assiduites"
|
||||
|
||||
assiduiteid = db.Column(db.Integer, primary_key=True)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
assiduiteid = db.synonym("id")
|
||||
|
||||
date_debut = db.Column(
|
||||
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
|
||||
@ -34,37 +40,92 @@ class Assiduite(db.Model):
|
||||
index=True,
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
etat = db.Column(db.String(CODE_STR_LEN), nullable=False)
|
||||
etat = db.Column(db.Integer, nullable=False)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
data = {
|
||||
"assiduiteid": self.assiduiteid,
|
||||
"etudid": self.etudid,
|
||||
"moduleid": self.moduleimpl_id,
|
||||
"moduleimpl_id": self.moduleimpl_id,
|
||||
"date_debut": self.date_debut,
|
||||
"date_fin": self.date_fin,
|
||||
"etat": self.etat,
|
||||
}
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def create_assiduite(
|
||||
cls,
|
||||
etud: Identite,
|
||||
date_debut: datetime,
|
||||
date_fin: datetime,
|
||||
etat: EtatAssiduite,
|
||||
module: int or None = None or int,
|
||||
) -> object or int:
|
||||
"Créer une nouvelle assiduité pour l'étudiant"
|
||||
|
||||
class EtatJustificatif(db.Model):
|
||||
"""
|
||||
Représente les différents états de validation d'un justificatif:
|
||||
- un couple ID et description (32 caractères max)
|
||||
Par Défaut :
|
||||
0 -> Non validé
|
||||
1 -> Validé
|
||||
# Vérification de non duplication des périodes
|
||||
assiduites: List[Assiduite] = etud.assiduites.all()
|
||||
assiduites = [
|
||||
ass
|
||||
for ass in assiduites
|
||||
if verif_interval((date_debut, date_fin), (ass.date_debut, ass.date_fin))
|
||||
]
|
||||
if len(assiduites) != 0:
|
||||
return 1
|
||||
|
||||
Tout id différent de 1 sera considéré par ScoDoc comme "Non Justifié"
|
||||
mais cela permet d'avoir des états transitoires (Modifié, en attente, etc)
|
||||
"""
|
||||
if module is not None:
|
||||
# Vérification de l'existance du module pour l'étudiant
|
||||
if cls.verif_moduleimpl(module, etud):
|
||||
nouv_assiduite = Assiduite(
|
||||
date_debut=date_debut.isoformat(),
|
||||
date_fin=date_fin.isoformat(),
|
||||
etat=etat,
|
||||
etudiant=etud,
|
||||
moduleimpl_id=module,
|
||||
)
|
||||
else:
|
||||
return 2
|
||||
else:
|
||||
nouv_assiduite = Assiduite(
|
||||
date_debut=date_debut.isoformat(),
|
||||
date_fin=date_fin.isoformat(),
|
||||
etat=etat,
|
||||
etudiant=etud,
|
||||
)
|
||||
db.session.add(nouv_assiduite)
|
||||
db.session.commit()
|
||||
return nouv_assiduite
|
||||
|
||||
__tablename__ = "etat_justificatif"
|
||||
@staticmethod
|
||||
def verif_moduleimpl(moduleimpl_id: int, etud: Identite or int) -> bool:
|
||||
"""
|
||||
Vérifie si l'étudiant est bien inscrit au moduleimpl
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
description = db.Column(db.String(SHORT_STR_LEN), nullable=False)
|
||||
Retourne Vrai si c'est le cas, faux sinon
|
||||
"""
|
||||
# -> get obj module impl -> get obj formsemestres -> query etuds avec etuid -> si vide = Error sinon good
|
||||
|
||||
module: ModuleImpl = ModuleImpl.query.filter_by(
|
||||
moduleimpl_id=moduleimpl_id
|
||||
).first()
|
||||
if module is None:
|
||||
retour = False
|
||||
|
||||
semestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=module.formsemestre_id
|
||||
).first()
|
||||
if semestre is None:
|
||||
retour = False
|
||||
|
||||
etudiants: List[Identite] = semestre.etuds.all()
|
||||
|
||||
if type(etud) is Identite:
|
||||
retour = etud in etudiants
|
||||
else:
|
||||
retour = etud in [e.id for e in etudiants]
|
||||
|
||||
return retour
|
||||
|
||||
|
||||
class Justificatif(db.Model):
|
||||
@ -94,7 +155,6 @@ class Justificatif(db.Model):
|
||||
)
|
||||
etat = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("etat_justificatif.id", ondelete="SET NULL"),
|
||||
)
|
||||
|
||||
raison = db.Column(db.Text())
|
||||
@ -107,9 +167,30 @@ class Justificatif(db.Model):
|
||||
"date_debut": self.date_debut,
|
||||
"date_fin": self.date_fin,
|
||||
"etat": self.etat,
|
||||
"raison": self.raison,
|
||||
"fichier": self.fichier,
|
||||
}
|
||||
if self.raison != None:
|
||||
data["raison"] = self.raison
|
||||
if self.fichier != None:
|
||||
data["fichier"] = self.fichier
|
||||
return data
|
||||
|
||||
|
||||
def verif_interval(periode: Tuple[datetime], interval: Tuple[datetime]) -> bool:
|
||||
"""
|
||||
Vérifie si une période est comprise dans un interval, chevauche l'interval ou comprend l'interval
|
||||
|
||||
Retourne Vrai si c'est le cas, faux sinon
|
||||
"""
|
||||
p_deb, p_fin = periode
|
||||
i_deb, i_fin = interval
|
||||
|
||||
from app.scodoc.intervals import intervalmap
|
||||
|
||||
i = intervalmap()
|
||||
p = intervalmap()
|
||||
i[:] = 0
|
||||
p[:] = 0
|
||||
i[i_deb:i_fin] = 1
|
||||
p[p_deb:p_fin] = 1
|
||||
|
||||
res: int = sum((i[p_deb], i[p_fin], p[i_deb], p[i_fin]))
|
||||
|
||||
return res > 0
|
||||
|
@ -59,6 +59,10 @@ class Identite(db.Model):
|
||||
#
|
||||
admission = db.relationship("Admission", backref="identite", lazy="dynamic")
|
||||
|
||||
# Relations avec les assiduites et les justificatifs
|
||||
assiduites = db.relationship("Assiduite", backref="etudiant", lazy="dynamic")
|
||||
justificatifs = db.relationship("Justificatif", backref="etudiant", lazy="dynamic")
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"<Etud {self.id}/{self.departement.acronym} {self.nom!r} {self.prenom!r}>"
|
||||
|
@ -56,6 +56,8 @@ _SCO_PERMISSIONS = (
|
||||
# 27 à 39 ... réservé pour "entreprises"
|
||||
# Api scodoc9
|
||||
# XXX à revoir
|
||||
(1 << 40, "ScoAssiduiteChange", "Modifier les assiduités"),
|
||||
(1 << 41, "ScoJustifChange", "Modifier les justificatifs"),
|
||||
# (1 << 42, "APIEditAllNotes", "API: Modifier toutes les notes"),
|
||||
# (1 << 43, "APIAbsChange", "API: Saisir des absences"),
|
||||
)
|
||||
|
@ -88,6 +88,46 @@ ETATS_INSCRIPTION = {
|
||||
}
|
||||
|
||||
|
||||
class EtatAssiduite(IntEnum):
|
||||
"""Code des états d'assiduité"""
|
||||
|
||||
# Stockés en BD ne pas modifier
|
||||
|
||||
PRESENT = 0
|
||||
RETARD = 1
|
||||
ABSENT = 2
|
||||
|
||||
|
||||
ETAT_ASSIDUITE_NAME = {
|
||||
EtatAssiduite.PRESENT: "present",
|
||||
EtatAssiduite.RETARD: "retard",
|
||||
EtatAssiduite.ABSENT: "absent",
|
||||
}
|
||||
ETATS_ASSIDUITE = {
|
||||
"present": EtatAssiduite.PRESENT,
|
||||
"retard": EtatAssiduite.RETARD,
|
||||
"absent": EtatAssiduite.ABSENT,
|
||||
}
|
||||
|
||||
|
||||
class EtatJustificatif(IntEnum):
|
||||
"""Code des états des justificatifs"""
|
||||
|
||||
# Stockés en BD ne pas modifier
|
||||
|
||||
VALIDE = 0
|
||||
NON_VALIDE = 1
|
||||
ATTENTE = 2
|
||||
MODIFIE = 3
|
||||
|
||||
|
||||
ETAT_JUSTIFICATIF_NAME = {
|
||||
EtatJustificatif.VALIDE: "validé",
|
||||
EtatJustificatif.NON_VALIDE: "non validé",
|
||||
EtatJustificatif.ATTENTE: "en attente",
|
||||
EtatJustificatif.MODIFIE: "modifié",
|
||||
}
|
||||
|
||||
# Types de modules
|
||||
class ModuleType(IntEnum):
|
||||
"""Code des types de module."""
|
||||
|
54
migrations/versions/7b762fcbf644_models_assiduites.py
Normal file
54
migrations/versions/7b762fcbf644_models_assiduites.py
Normal file
@ -0,0 +1,54 @@
|
||||
"""models assiduites
|
||||
|
||||
Revision ID: 7b762fcbf644
|
||||
Revises: 52f5f35c077f
|
||||
Create Date: 2022-11-03 09:09:19.213260
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7b762fcbf644'
|
||||
down_revision = '52f5f35c077f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('justificatifs',
|
||||
sa.Column('justifid', sa.Integer(), nullable=False),
|
||||
sa.Column('date_debut', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('date_fin', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('etudid', sa.Integer(), nullable=False),
|
||||
sa.Column('etat', sa.Integer(), nullable=True),
|
||||
sa.Column('raison', sa.Text(), nullable=True),
|
||||
sa.Column('fichier', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['etudid'], ['identite.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('justifid')
|
||||
)
|
||||
op.create_index(op.f('ix_justificatifs_etudid'), 'justificatifs', ['etudid'], unique=False)
|
||||
op.create_table('assiduites',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_debut', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('date_fin', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('moduleimpl_id', sa.Integer(), nullable=True),
|
||||
sa.Column('etudid', sa.Integer(), nullable=False),
|
||||
sa.Column('etat', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['etudid'], ['identite.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['moduleimpl_id'], ['notes_moduleimpl.id'], ondelete='SET NULL'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_assiduites_etudid'), 'assiduites', ['etudid'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_assiduites_etudid'), table_name='assiduites')
|
||||
op.drop_table('assiduites')
|
||||
op.drop_index(op.f('ix_justificatifs_etudid'), table_name='justificatifs')
|
||||
op.drop_table('justificatifs')
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user