This commit is contained in:
Éric Li 2021-06-01 19:31:24 +02:00
parent 2c470cfdeb
commit 4e9c35b9e6
16 changed files with 639 additions and 277 deletions

View File

@ -5,6 +5,7 @@
pip install flask-wtf
pip install flask-sqlalchemy
pip install flask-migrate
pip install wtforms-alchemy
### Pour lancer l'app

BIN
app.db

Binary file not shown.

View File

@ -1,8 +1,11 @@
from flask import redirect, url_for
from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed
from wtforms import StringField, SubmitField, FileField, TextAreaField, RadioField
from wtforms import StringField, SubmitField, FileField, TextAreaField, RadioField, SelectMultipleField, widgets, FieldList, FormField, HiddenField
from wtforms.validators import DataRequired, Regexp, Optional, ValidationError
from wtforms_alchemy import model_form_factory, ClassMap
from wtforms_alchemy.fields import QuerySelectMultipleField
from sqlalchemy_utils import ChoiceType
from app import db
import app.models as models
@ -15,14 +18,40 @@ REPERTOIRE_YAML = "./export/"
if not os.path.exists(REPERTOIRE_YAML):
os.makedirs(REPERTOIRE_YAML)
def validate_ref_list(regex, message):
def _validate_ref_list(form, field):
refs = field.data.strip() + " "
if re.match(regex, refs) == None:
raise ValidationError(message)
return _validate_ref_list
categorie_liste = ["saes","ressources","acs"]
categorie_to_model = {"saes": "SAE", "ressources": "Ressource", "acs": "AC"}
separateur = None
class Form(FlaskForm):
BaseModelForm = model_form_factory(FlaskForm, include_primary_keys=True)
class ModelForm(BaseModelForm):
@classmethod
def get_session(self):
return db.session
class RefListField(QuerySelectMultipleField):
option_widget=widgets.CheckboxInput()
class AccueilForm(FlaskForm):
ajouter = SubmitField("Ajouter")
reset = SubmitField("Reset")
exporter = SubmitField("Exporter")
importer = SubmitField("Importer")
class UEForm(FlaskForm):
ue = HiddenField("Competence")
acs = RefListField("Liste des ACs", query_factory=lambda: models.AC.query.all(), get_label=lambda ref: ref.getLabel())
ressources = RefListField("Liste des Ressources inclus dans les ACs", query_factory=lambda: [], get_label=lambda ref: ref.getLabel())
saes = RefListField("Liste des SAEs inclus dans les ACs", query_factory=lambda: [], get_label=lambda ref: ref.getLabel())
class SemestreForm(FlaskForm):
ues = RefListField("Liste des UEs", query_factory=lambda: models.Competence.query.all(), get_label=lambda ref: ref.getLabel())
ueform = FieldList(FormField(UEForm))
update = SubmitField("Update")
class Form(ModelForm):
class Meta:
not_null_validator_type_map = ClassMap({db.String: [DataRequired()]})
sauvegarder = SubmitField("Sauvegarder")
charger = SubmitField("Charger")
@ -32,154 +61,105 @@ class Form(FlaskForm):
importer = SubmitField("Importer")
referentiel = RadioField("Liste des référentiels", validators=[Optional()])
class PNForm(Form):
regex = "^PN\d$"
code = StringField("Code", validators=[DataRequired(), Regexp("^PN\d$", message="Le code doit être de la forme PN0.")])
nom = StringField("Nom", validators=[DataRequired()] )
diminutif = StringField("Diminutif", validators=[DataRequired()] )
description = TextAreaField("Description", validators=[DataRequired()] )
type = RadioField("Type", choices=["1","2","3"], validators=[DataRequired()])
class ACForm(Form):
regex = "^AC\d{4}$"
code = StringField("Code", validators=[DataRequired(), Regexp("^AC\d{4}$", message="Le code doit être de la forme AC0000.")])
titre = StringField("Titre", validators=[DataRequired()] )
saes = StringField("SAEs", validators=[DataRequired(), validate_ref_list("^(\s*(SAE\d{2}\s+)*)$", message="Les saes doit être de la forme SAE00.")] )
ressources = StringField("Ressources", validators=[DataRequired(), validate_ref_list("^(\s*(R\d{3}\s+)*)$", message="Les ressources doit être de la forme R000.")] )
class SAEForm(Form):
regex = "^SAE\d{2}$"
code = StringField("Code", validators=[DataRequired(), Regexp("^SAE\d{2}$", message="Le code doit être de la forme SAE00.")])
titre = StringField("Titre", validators=[DataRequired()] )
semestre = StringField("Semestre", validators=[DataRequired()] )
heures_encadrees = StringField("Heures encadrées", validators=[DataRequired()] )
heures_tp = StringField("Heures TP", validators=[DataRequired()] )
projet = StringField("Projet", validators=[DataRequired()] )
description = TextAreaField("Description", validators=[DataRequired()] )
coef = StringField("Coef.", validators=[DataRequired()] )
acs = StringField("ACs", validators=[DataRequired(), validate_ref_list("^(\s*(AC\d{4}\s+)*)$", message="Les acs être de la forme AC0000.")] )
ressources = StringField("Ressources", validators=[DataRequired(), validate_ref_list("^(\s*(R\d{3}\s+)*)$", message="Les ressources doit être de la forme R000.")] )
livrables = TextAreaField("Livrables", validators=[DataRequired()] )
motscles = StringField("Mots clés", validators=[DataRequired()] )
class RessourceForm(Form):
regex = "^R\{3}$"
code = StringField("Code", validators=[DataRequired(), Regexp("^R\{3}$", message="Le code doit être de la forme R000.")])
nom = StringField("Nom", validators=[DataRequired()] )
semestre = StringField("Semestre", validators=[DataRequired()] )
heures_formation = StringField("Heures formation", validators=[DataRequired()] )
heures_tp = StringField("Heures TP", validators=[DataRequired()] )
coef = StringField("Coef.", validators=[DataRequired()] )
acs = StringField("ACs", validators=[DataRequired(), validate_ref_list("^(\s*(AC\d{4}\s+)*)$", message="Les acs doit être de la forme AC0000.")] )
saes = StringField("SAEs", validators=[DataRequired(), validate_ref_list("^(\s*(SAE\d{2}\s+)*)$", message="Les saes doit être de la forme SAE00.")] )
prerequis = StringField("Prérequis", validators=[DataRequired()] )
contexte = TextAreaField("Contexte", validators=[DataRequired()] )
contenu = TextAreaField("Contenu", validators=[DataRequired()] )
motscles = StringField("Mots clés", validators=[DataRequired()] )
class CompetenceForm(Form):
regex = "^RT\d$"
code = StringField("Code", validators=[DataRequired(), Regexp("^RT\d$", message="Le code doit être de la forme RT0.")])
nom = StringField("Nom", validators=[DataRequired()] )
diminutif = StringField("Diminutif", validators=[DataRequired()] )
description = TextAreaField("Description", validators=[DataRequired()] )
composantes = TextAreaField("Composantes", validators=[DataRequired()] )
situations = TextAreaField("Situations", validators=[DataRequired()] )
niveaux = TextAreaField("Niveaux", validators=[DataRequired()] )
categorie_liste = ["saes","ressources","acs"]
categorie_to_model = {"saes": "SAE", "ressources": "Ressource", "acs": "AC"}
separateur = None
def form_import(form):
""" Si le bouton import a été appuyé et qu'il n'y a pas d'erreur d'import => importe le fichier yaml"""
# Bouton import appuyé et fichier yaml selectionné
if form.importer.data and len(form.fichier.errors) == 0:
if form.fichier.data.filename == "":
form.fichier.errors.append("Aucun fichier selectioné.")
return form
if re.match(form.regex, form.fichier.data.filename[:-4]) == None:
form.fichier.errors.append("Mauvais type de référentiel!")
return form
fichier_Yaml = yaml.safe_load(form.fichier.data.read())
for categorie, valeur in fichier_Yaml.items():
if categorie in categorie_liste:
form[categorie].process_data(" ".join(valeur))
else:
form[categorie].process_data(valeur)
form.validate_on_submit() # Réinitialise les messages d'erreur
return form
def form_export(form):
""" Si le formulaire est valide => exporte dans un fichier yaml avec les informations du formulaire """
output = {}
for categorie, valeur in list(form.data.items())[7:-1]:
if categorie in categorie_liste:
output[categorie] = [ referentiel for referentiel in valeur.split(separateur) ]
def chargerRef(self):
if self.referentiel.data == None:
self.referentiel.errors.append("Aucun référentiel n'a été selectionné!")
return False
else:
output[categorie] = valeur
fichier = REPERTOIRE_YAML + form.code.data + ".yml"
return True
def chargerBDD(self, referentiel):
for categorie in list(self.data.keys())[7:-1]:
self[categorie].process_data(referentiel.__dict__[categorie])
self.referentiel.process_data(referentiel)
def importerRef(self):
if len(self.fichier.errors) == 0:
if self.fichier.data.filename == "":
self.fichier.errors.append("Aucun fichier selectioné.")
return
if re.match(self.regex, self.fichier.data.filename[:-4]) == None:
self.fichier.errors.append("Mauvais type de référentiel!")
return
fichier_Yaml = yaml.safe_load(self.fichier.data.read())
for categorie, valeur in fichier_Yaml.items():
if categorie in categorie_liste:
model = getattr(models, categorie_to_model[categorie])
self[categorie].process_data([model.query.filter_by(code=ref).first() for ref in valeur])
else:
self[categorie].process_data(valeur)
self.validate()
def exporterRef(self):
""" Exporte dans un fichier yaml les informations du formulaire rempli """
output = {}
for categorie, valeur in list(self.data.items())[7:-1]:
if categorie in categorie_liste:
output[categorie] = [ referentiel.code for referentiel in valeur ]
else:
output[categorie] = valeur
fichier = REPERTOIRE_YAML + self.code.data + ".yml"
with open(fichier, "w", encoding="utf8") as fid:
fid.write(yaml.dump(output))
print(yaml.dump(output))
def form_charger(form):
""" Si le bouton charger est appuyé et qu'un référentiel du BDD a été selectionné => remplie le formulaire avec ses informations """
if form.charger.data:
if form.referentiel.data == None:
form.referentiel.errors.append("Aucun référentiel n'a été selectionné!")
def supprimerRef(self):
if self.referentiel.data == None:
self.referentiel.erros.append("Aucun référentiel n'a été selectionné!")
return False
else:
temp = form.referentiel.data[1:-1].split()
model = getattr(models, temp[0])
referentiel = model.query.filter_by(code=temp[1]).first()
for categorie in list(referentiel.__dict__)[1:]:
if categorie in categorie_liste:
form[categorie].process_data(" ".join(ref.code for ref in referentiel.__dict__[categorie]))
else:
form[categorie].process_data(referentiel.__dict__[categorie])
form.validate_on_submit()
return form
def form_sauvegarder(form):
""" Si le bouton sauvegarder est appuyé et que le formulaire est valide => ajoute le référentiel dans la table respectif du base de données """
if form.sauvegarder.data:
model = getattr(models, "".join( c for c in form.code.data if not c.isdigit()))
referentiel = model.query.filter_by(code=form.code.data).first()
if referentiel == None:
referentiel = model()
# Si le référentiel contient une catégorie avec une liste de référentiels
for categorie, valeur in list(form.data.items())[7:-1]:
if categorie in categorie_liste:
resultat = []
refs = [ ref for ref in form[categorie].data.split(separateur) ]
ref_model = getattr(models, categorie_to_model[categorie])
for ref in refs:
ref_bdd = ref_model.query.filter_by(code=ref).first()
if ref_bdd == None:
ref_new = ref_model(code=ref)
resultat.append(ref_new)
db.session.add(ref_new)
else:
resultat.append(ref_bdd)
form[categorie].process_data(resultat)
form.populate_obj(referentiel)
db.session.add(referentiel)
db.session.commit()
def form_supprimer(form):
if form.supprimer.data:
if form.referentiel.data == None:
form.referentiel.errors.append("Aucun référentiel n'a été selectionné!")
else:
temp = form.referentiel.data[1:-1].split()
temp = self.referentiel.data[1:-1].split()
model = getattr(models, temp[0])
referentiel = model.query.filter_by(code=temp[1]).first()
db.session.delete(referentiel)
db.session.commit()
return True, redirect(url_for(temp[0]))
return False, form
return True
def sauvegarderRef(self):
model = getattr(models, self.__class__.__name__[:-4])
referentiel = model.query.filter_by(code=self.code.data).first()
if referentiel == None: referentiel = model()
self.populate_obj(referentiel)
db.session.add(referentiel)
db.session.commit()
class PNForm(Form):
regex = "^PN\d$"
class Meta:
model = models.PN
type_map = ClassMap({ChoiceType: RadioField})
class ACForm(Form):
regex = "^AC\d{4}$"
class Meta:
model = models.AC
saes = RefListField("Liste des SAEs", query_factory=lambda: models.SAE.query.all(), get_label=lambda ref: ref.getLabel())
ressources = RefListField("Liste des Ressources", query_factory=lambda: models.Ressource.query.all(), get_label=lambda ref: ref.getLabel())
class SAEForm(Form):
regex = "^SAE\d{2}$"
class Meta:
model = models.SAE
acs = RefListField("Liste des ACs", query_factory=lambda: models.AC.query.all(), get_label=lambda ref: ref.getLabel())
ressources = RefListField("Liste des Ressources", query_factory=lambda: models.Ressource.query.all(), get_label=lambda ref: ref.getLabel())
class RessourceForm(Form):
regex = "^R\d{3}$"
class Meta:
model = models.Ressource
acs = RefListField("Liste des ACs", query_factory=lambda: models.AC.query.all(), get_label=lambda ref: ref.getLabel())
saes = RefListField("Liste des SAEs", query_factory=lambda: models.SAE.query.all(), get_label=lambda ref: ref.getLabel())
class CompetenceForm(Form):
regex = "^RT\d$"
class Meta:
model = models.Competence

View File

@ -1,4 +1,25 @@
from app import db
from sqlalchemy_utils import ChoiceType
Semestres_Competences = db.Table("Semestres_Competences",
db.Column("Semestre_num", db.String(1), db.ForeignKey("semestre.num")),
db.Column("Competence_code", db.String(3), db.ForeignKey("competence.code"))
)
ACs_Competences = db.Table("ACs_Competences",
db.Column("AC_code", db.String(6), db.ForeignKey("AC.code")),
db.Column("Competence_code", db.String(3), db.ForeignKey("competence.code"))
)
SAEs_Competences = db.Table("SAEs_Competences",
db.Column("SAE_code", db.String(6), db.ForeignKey("SAE.code")),
db.Column("Competence_code", db.String(3), db.ForeignKey("competence.code"))
)
Ressources_Competences = db.Table("Ressources_Competences",
db.Column("Ressource_code", db.String(6), db.ForeignKey("ressource.code")),
db.Column("Competence_code", db.String(3), db.ForeignKey("competence.code"))
)
Ressources_ACs = db.Table("Ressources_ACs",
db.Column("Ressource_code", db.String(4), db.ForeignKey("ressource.code")),
@ -15,64 +36,90 @@ SAEs_ACs = db.Table("SAEs_ACs",
db.Column("AC_code", db.String(6), db.ForeignKey("AC.code"))
)
class Semestre(db.Model):
num = db.Column(db.String(1), primary_key = True)
ues = db.relationship("Competence", order_by="Competence.code", secondary=Semestres_Competences, lazy=False, backref=db.backref("semestres", lazy=False))
def __repr__(self):
return "<Semestre {}>".format(self.num)
class Competence(db.Model):
code = db.Column(db.String(3), primary_key = True, info={'label': 'Code'})
nom = db.Column(db.String(255), info={'label': 'Nom'})
diminutif = db.Column(db.String(30), info={'label': 'Diminutif'})
description = db.Column(db.Text(), info={'label': 'Description'})
composantes = db.Column(db.Text(), info={'label': 'Composantes'})
situations = db.Column(db.Text(), info={'label': 'Situations'})
niveaux = db.Column(db.Text(), info={'label': 'Niveaux'})
acs = db.relationship("AC", order_by="AC.code", secondary=ACs_Competences, lazy=False, backref=db.backref("competences", lazy=False))
#objets de formation
saes = db.relationship("SAE", secondary=SAEs_Competences, lazy=False, backref=db.backref("competences", lazy=False))
ressources = db.relationship("Ressource", secondary=Ressources_Competences, lazy=False, backref=db.backref("competences", lazy=False))
def getLabel(self):
return self.code + " - " + self.nom
def __repr__(self):
return "<Competence {}>".format(self.code)
class AC(db.Model):
code = db.Column(db.String(6), primary_key = True, info={'label': 'Code'})
titre = db.Column(db.String(255), info={'label': 'Titre'})
saes = db.relationship("SAE", secondary=SAEs_ACs, lazy=False, backref=db.backref("acs", lazy=False))
ressources = db.relationship("Ressource", order_by="Ressource.code", secondary=Ressources_ACs, lazy=False, backref=db.backref("acs", lazy=False))
def getLabel(self):
return self.code + " - " + self.titre
def __repr__(self):
return "<AC {}>".format(self.code)
class PN(db.Model):
code = db.Column(db.String(3), primary_key = True)
nom = db.Column(db.String(255))
diminutif = db.Column(db.String(30))
description = db.Column(db.Text())
type = db.Column(db.String(1))
code = db.Column(db.String(3), primary_key = True, info={'label': 'Code'})
nom = db.Column(db.String(255), info={'label': 'Nom'})
diminutif = db.Column(db.String(30), info={'label': 'Diminutif'})
description = db.Column(db.Text(), info={'label': 'Description'})
TYPES = [("1","1"),("2","2"),("3","3")]
type = db.Column(ChoiceType(TYPES))
def __repr__(self):
return "<PN {}>".format(self.code)
class AC(db.Model):
code = db.Column(db.String(6), primary_key = True)
titre = db.Column(db.String(255))
saes = db.relationship("SAE", secondary=SAEs_ACs, cascade="all, delete", lazy=False, backref=db.backref("acs", lazy=False))
ressources = db.relationship("Ressource", secondary=Ressources_ACs, cascade="all, delete", lazy=False, backref=db.backref("acs", lazy=False))
def __repr__(self):
return "<AC {}>".format(self.code)
class SAE(db.Model):
code = db.Column(db.String(5), primary_key = True)
titre = db.Column(db.String(255))
semestre = db.Column(db.String(255))
heures_encadrees = db.Column(db.String(3))
heures_tp = db.Column(db.String(3))
projet = db.Column(db.String(3))
description = db.Column(db.Text())
coef = db.Column(db.String(255))
ressources = db.relationship("Ressource", secondary=Ressources_SAEs, cascade="all, delete", lazy=False, backref=db.backref("saes", lazy=False))
livrables = db.Column(db.Text())
motscles = db.Column(db.String(255))
code = db.Column(db.String(5), primary_key = True, info={'label': 'Code'})
titre = db.Column(db.String(255), info={'label': 'Titre'})
semestre = db.Column(db.String(255), info={'label': 'Semestre'})
heures_encadrees = db.Column(db.String(3), info={'label': 'Heures Encadrées'})
heures_tp = db.Column(db.String(3), info={'label': 'Heures TP'})
projet = db.Column(db.String(3), info={'label': 'Projet'})
description = db.Column(db.Text(), info={'label': 'Description'})
coef = db.Column(db.String(255), info={'label': 'Coef.'})
ressources = db.relationship("Ressource", order_by="Ressource.code", secondary=Ressources_SAEs, lazy=False, backref=db.backref("saes", lazy=False))
livrables = db.Column(db.Text(), info={'label': 'Livrables'})
motscles = db.Column(db.String(255), info={'label': 'Mots Clés'})
def getLabel(self):
return self.code + " - " + self.titre
def __repr__(self):
return "<SAE {}>".format(self.code)
class Ressource(db.Model):
code = db.Column(db.String(4), primary_key = True)
nom = db.Column(db.String(255))
semestre = db.Column(db.String(255))
heures_formation = db.Column(db.String(3))
heures_tp = db.Column(db.String(3))
coef = db.Column(db.String(255))
prerequis = db.Column(db.String(255))
contexte = db.Column(db.Text())
contenu = db.Column(db.Text())
motscles = db.Column(db.String(255))
code = db.Column(db.String(4), primary_key = True, info={'label': 'Code'})
nom = db.Column(db.String(255), info={'label': 'Nom'})
semestre = db.Column(db.String(255), info={'label': 'Semestre'})
heures_formation = db.Column(db.String(3), info={'label': 'Heures Formation'})
heures_tp = db.Column(db.String(3), info={'label': 'Heures TP'})
coef = db.Column(db.String(255), info={'label': 'Coef.'})
prerequis = db.Column(db.String(255), info={'label': 'Prerequis'})
contexte = db.Column(db.Text(), info={'label': 'Contexte'})
contenu = db.Column(db.Text(), info={'label': 'Contenu'})
motscles = db.Column(db.String(255), info={'label': 'Mots Clés'})
def getLabel(self):
return self.code + " - " + self.nom
def __repr__(self):
return "<Ressource {}>".format(self.code)
class Competence(db.Model):
code = db.Column(db.String(3), primary_key = True)
nom = db.Column(db.String(255))
diminutif = db.Column(db.String(30))
description = db.Column(db.Text())
composantes = db.Column(db.Text())
situations = db.Column(db.Text())
niveaux = db.Column(db.Text())
def __repr__(self):
return "<Compétence {}>".format(self.code)

View File

@ -6,96 +6,178 @@ import app.models as models
import yaml
@app.route("/")
@app.route("/index")
@app.route("/index", methods=["GET","POST"])
def index():
return render_template("base.html")
form = AccueilForm()
if form.ajouter.data:
for i in range(1,7):
semestre = models.Semestre(num=i)
db.session.add(semestre)
db.session.commit()
elif form.reset.data:
for semestre in models.Semestre.query.all():
db.session.delete(semestre)
db.session.commit()
semestres = sorted(models.Semestre.query.all(), key=lambda semestre: semestre.num)
return render_template("index.html", semestres=semestres, form=form)
@app.route("/PN", methods=["GET","POST"])
def PN():
@app.route("/Semestre<num>", methods=["GET","POST"])
def Semestre(num):
form = SemestreForm()
semestre = models.Semestre.query.filter_by(num=num).first()
for i, ue in enumerate(semestre.ues):
if form.ueform.__len__() < len(semestre.ues): form.ueform.append_entry(data={"ue": ue.code})
# Donne aux dropdowns la liste des objets de formations que les ACs possèdent sans doublons
querySAE = []
queryRessource = []
for ac in ue.acs:
for sae in ac.saes:
if sae not in querySAE: querySAE.append(sae)
for ressource in ac.ressources:
if ressource not in queryRessource: queryRessource.append(ressource)
form.ueform.__getitem__(i).saes.query=querySAE
form.ueform.__getitem__(i).ressources.query=queryRessource
# Appuie sur Update
if form.update.data:
semestre.ues = [ue for ue in form.ues.data]
for i, field in enumerate(form.ueform):
ue = models.Competence.query.filter_by(code=field.ue.data).first()
ue.acs = field.acs.data
ue.saes = field.saes.data
ue.ressources = field.ressources.data
db.session.commit()
return redirect(url_for("Semestre", num=num))
# Coche les référentiels que l'ue possède
for i, ue in enumerate(semestre.ues):
form.ueform.__getitem__(i).acs.process_data(ue.acs)
form.ueform.__getitem__(i).saes.process_data(ue.saes)
form.ueform.__getitem__(i).ressources.process_data(ue.ressources)
# Coche les ues que le semestre possède
form.ues.process_data(semestre.ues)
return render_template("semestre.html", semestre=semestre, form=form)
@app.route("/PN", defaults={"code":None}, methods=["GET","POST"])
@app.route("/PN<code>", methods=["GET","POST"])
def PN(code):
form = PNForm()
form.referentiel.choices = [x for x in models.PN.query.all()]
form_validation = form.validate_on_submit()
form = form_import(form)
form = form_charger(form)
temp = form_supprimer(form)
if temp[0] == True: return temp[1]
else: form = temp[1]
if form_validation and not form.charger.data:
form.validate_on_submit()
if form.charger.data:
if form.chargerRef(): return redirect(url_for("PN", code=form.referentiel.data.split()[1][2:-1]))
elif form.importer.data:
form.importerRef()
elif form.supprimer.data:
if form.supprimerRef(): return redirect(url_for("PN"))
elif form.validate_on_submit() and not form.charger.data:
if form.exporter.data:
flash("Ajout du référentiel PN: {} ".format(form.code.data))
form_export(form)
form_sauvegarder(form)
flash("Export du référentiel PN: {} ".format(form.code.data))
form.exporterRef()
elif form.sauvegarder.data:
form.sauvegarderRef()
return redirect(url_for("PN"))
elif code:
referentiel = models.PN.query.filter_by(code="PN"+code).first()
if referentiel == None: return redirect(url_for("PN"))
else: form.chargerBDD(referentiel)
return render_template("PN.html", form = form)
@app.route("/AC", methods=["GET","POST"])
def AC():
@app.route("/AC", defaults={"code":None}, methods=["GET","POST"])
@app.route("/AC<code>", methods=["GET","POST"])
def AC(code):
form = ACForm()
form.referentiel.choices = [x for x in models.AC.query.all()]
form_validation = form.validate_on_submit()
form = form_import(form)
form = form_charger(form)
temp = form_supprimer(form)
if temp[0] == True: return temp[1]
else: form = temp[1]
if form_validation and not form.charger.data:
form.validate_on_submit()
if form.charger.data:
if form.chargerRef(): return redirect(url_for("AC", code=form.referentiel.data.split()[1][2:-1]))
elif form.importer.data:
form.importerRef()
elif form.supprimer.data:
if form.supprimerRef(): return redirect(url_for("AC"))
elif form.validate_on_submit() and not form.charger.data:
if form.exporter.data:
flash("Ajout du référentiel AC: {} ".format(form.code.data))
form_export(form)
form_sauvegarder(form)
flash("Export du référentiel AC: {} ".format(form.code.data))
form.exporterRef()
elif form.sauvegarder.data:
form.sauvegarderRef()
return redirect(url_for("AC"))
elif code:
referentiel = models.AC.query.filter_by(code="AC"+code).first()
if referentiel == None: return redirect(url_for("AC"))
else: form.chargerBDD(referentiel)
return render_template("AC.html", form = form)
@app.route("/SAE", methods=["GET","POST"])
def SAE():
@app.route("/SAE", defaults={"code":None}, methods=["GET","POST"])
@app.route("/SAE<code>", methods=["GET","POST"])
def SAE(code):
form = SAEForm()
form.referentiel.choices = [x for x in models.SAE.query.all()]
form_validation = form.validate_on_submit()
form = form_import(form)
form = form_charger(form)
temp = form_supprimer(form)
if temp[0] == True: return temp[1]
else: form = temp[1]
if form_validation and not form.charger.data:
form.validate_on_submit()
if form.charger.data:
if form.chargerRef(): return redirect(url_for("SAE", code=form.referentiel.data.split()[1][3:-1]))
elif form.importer.data:
form.importerRef()
elif form.supprimer.data:
if form.supprimerRef(): return redirect(url_for("SAE"))
elif form.validate_on_submit() and not form.charger.data:
if form.exporter.data:
flash("Ajout du référentiel SAE: {} ".format(form.code.data))
form_export(form)
form_sauvegarder(form)
flash("Export du référentiel SAE: {} ".format(form.code.data))
form.exporterRef()
elif form.sauvegarder.data:
form.sauvegarderRef()
return redirect(url_for("SAE"))
elif code:
referentiel = models.SAE.query.filter_by(code="SAE"+code).first()
if referentiel == None: return redirect(url_for("SAE"))
else: form.chargerBDD(referentiel)
return render_template("SAE.html", form = form)
@app.route("/Ressource", methods=["GET","POST"])
def Ressource():
@app.route("/Ressource", defaults={"code":None}, methods=["GET","POST"])
@app.route("/Ressource<code>", methods=["GET","POST"])
def Ressource(code):
form = RessourceForm()
form.referentiel.choices = [x for x in models.Ressource.query.all()]
form_validation = form.validate_on_submit()
form = form_import(form)
form = form_charger(form)
temp = form_supprimer(form)
if temp[0] == True: return temp[1]
else: form = temp[1]
if form_validation and not form.charger.data:
form.validate_on_submit()
if form.charger.data:
if form.chargerRef(): return redirect(url_for("Ressource", code=form.referentiel.data.split()[1][1:-1]))
elif form.importer.data:
form.importerRef()
elif form.supprimer.data:
if form.supprimerRef(): return redirect(url_for("Ressource"))
elif form.validate_on_submit() and not form.charger.data:
if form.exporter.data:
flash("Ajout du référentiel Ressource: {} ".format(form.code.data))
form_export(form)
form_sauvegarder(form)
flash("Export du référentiel Ressource: {} ".format(form.code.data))
form.exporterRef()
elif form.sauvegarder.data:
form.sauvegarderRef()
return redirect(url_for("Ressource"))
elif code:
referentiel = models.Ressource.query.filter_by(code="R"+code).first()
if referentiel == None: return redirect(url_for("Ressource"))
else: form.chargerBDD(referentiel)
return render_template("Ressource.html", form = form)
@app.route("/Competence", methods=["GET","POST"])
def Competence():
@app.route("/Competence", defaults={"code":None}, methods=["GET","POST"])
@app.route("/Competence<code>", methods=["GET","POST"])
def Competence(code):
form = CompetenceForm()
form.referentiel.choices = [x for x in models.Competence.query.all()]
form_validation = form.validate_on_submit()
form = form_import(form)
form = form_charger(form)
temp = form_supprimer(form)
if temp[0] == True: return temp[1]
else: form = temp[1]
if form_validation and not form.charger.data:
form.validate_on_submit()
if form.charger.data:
if form.chargerRef(): return redirect(url_for("Competence", code=form.referentiel.data.split()[1][2:-1]))
elif form.importer.data:
form.importerRef()
elif form.supprimer.data:
if form.supprimerRef(): return redirect(url_for("Compentence"))
elif form.validate_on_submit() and not form.charger.data:
if form.exporter.data:
flash("Ajout du référentielCompetence: {} ".format(form.code.data))
form_export(form)
form_sauvegarder(form)
flash("Export du référentielCompetence: {} ".format(form.code.data))
form.exporterRef()
if form.sauvegarder.data:
form.sauvegarderRef()
return redirect(url_for("Competence"))
elif code:
referentiel = models.Competence.query.filter_by(code="RT"+code).first()
if referentiel == None: return redirect(url_for("Competence"))
else: form.chargerBDD(referentiel)
return render_template("Competence.html", form = form)

12
app/static/js/base.js Normal file
View File

@ -0,0 +1,12 @@
$("document").ready(function() {
// Affichage mobile du menu | affiche/cache le menu contenant la liste des catégories
// en appuyant sur le burger/les trois traits
$(".navbar-burger").click(function() {
$(".navbar-burger").toggleClass("is-active");
$(".navbar-menu").toggleClass("is-active");
});
$(".navbar-item.has-dropdown").click(function() {
$(this).toggleClass("is-active");
});
});

View File

@ -7,7 +7,9 @@
{{ render_field(form.code,"input") }}
{{ render_field(form.titre,"input") }}
{{ render_field(form.saes,"input") }}
{{ render_field(form.ressources,"input") }}
{{ render_list(form.saes) }}
<div class="field">{{ render_dropdown(form.saes) }}</div>
{{ render_list(form.ressources) }}
<div class="field">{{ render_dropdown(form.ressources) }}</div>
{% endblock %}

View File

@ -11,8 +11,10 @@
{{ render_field(form.heures_formation,"input") }}
{{ render_field(form.heures_tp,"input") }}
{{ render_field(form.coef,"input") }}
{{ render_field(form.acs,"input") }}
{{ render_field(form.saes,"input") }}
{{ render_list(form.acs) }}
<div class="field">{{ render_dropdown(form.acs) }}</div>
{{ render_list(form.saes) }}
<div class="field">{{ render_dropdown(form.saes) }}</div>
{{ render_field(form.prerequis,"input") }}
{{ render_field(form.contexte,"textarea") }}
{{ render_field(form.contenu,"textarea") }}

View File

@ -13,8 +13,10 @@
{{ render_field(form.projet,"input") }}
{{ render_field(form.description,"textarea") }}
{{ render_field(form.coef,"input") }}
{{ render_field(form.acs,"input") }}
{{ render_field(form.ressources,"input") }}
{{ render_list(form.acs) }}
<div class="field">{{ render_dropdown(form.acs) }}</div>
{{ render_list(form.ressources) }}
<div class="field">{{ render_dropdown(form.ressources) }}</div>
{{ render_field(form.livrables,"textarea") }}
{{ render_field(form.motscles,"input") }}

View File

@ -1,3 +1,47 @@
{% macro render_dropdown(field) %}
<div class="control is-expanded">
<div class="dropdown is-hoverable" style="width: 100%">
<div class="dropdown-trigger" style="width: 100%">
<span class="button is-fullwidth" aria-controls="dropdown-menu">
<span>{{ field.label }}</span>
<span class="icon is-small">
<i class="fas fa-angle-down"></i>
</span>
</button>
</div>
<div class="dropdown-menu" style="width: 100%">
<div class="dropdown-content">
{% for val in field %}
<div class="dropdown-item">
{{ val }} {{ val.label }}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endmacro %}
{% macro render_field(field,class) %}
<div class="field">
<label class="label">{{ field.label }}</label>
<div class="control">
{{ field(class=class)}}
</div>
{% for error in field.errors %}
<p class="help is-danger">{{error}}</p>
{% endfor %}
</div>
{% endmacro %}
{% macro render_list(field) %}
<ul>
{% for ref in field.data %}
<li>{{ ref.code }} - {{ ref.titre }}{{ ref.nom }}</li>
{% endfor %}
</ul>
{% endmacro %}
<!DOCTYPE html>
<html class="has-navbar-fixed-top">
<head>
@ -7,6 +51,7 @@
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css">
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{{ url_for('static', filename='js/base.js') }}"></script>
{% block head %}{% endblock %}
</head>
<body>
@ -25,6 +70,18 @@
<div class="navbar-menu">
<div class="navbar-start">
<!-- Liste des catégories -->
<a class="navbar-item" href="{{ url_for('index') }}">Accueil</a>
<div class="navbar-item has-dropdown">
<a class="navbar-link">Semestres</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="{{ url_for('Semestre', num=1) }}">Semestre 1</a>
<a class="navbar-item" href="{{ url_for('Semestre', num=2) }}">Semestre 2</a>
<a class="navbar-item" href="{{ url_for('Semestre', num=3) }}">Semestre 3</a>
<a class="navbar-item" href="{{ url_for('Semestre', num=4) }}">Semestre 4</a>
<a class="navbar-item" href="{{ url_for('Semestre', num=5) }}">Semestre 5</a>
<a class="navbar-item" href="{{ url_for('Semestre', num=6) }}">Semestre 6</a>
</div>
</div>
<a class="navbar-item" href="{{ url_for('PN') }}">PN</a>
<a class="navbar-item" href="{{ url_for('AC') }}">AC</a>
<a class="navbar-item" href="{{ url_for('SAE') }}">SAÉ</a>
@ -51,4 +108,5 @@
</div>
</div>
</body>
</html>
</html>

View File

@ -1,14 +1,3 @@
{% macro render_field(field,class) %}
<div class="field">
<label class="label">{{ field.label }}</label>
<div class="control">
{{ field(class=class)}}
</div>
{% for error in field.errors %}
<p class="help is-danger">{{error}}</p>
{% endfor %}
</div>
{% endmacro %}
{% extends "base.html" %}
{% block content %}
@ -49,7 +38,7 @@
{% endfor %}
</div>
</div>
{% for error in form.charger.errors %}
{% for error in form.referentiel.errors %}
<p class="help is-danger">{{error}}</p>
{% endfor %}
{% if form.referentiel.choices|length != 0 %}

35
app/templates/index.html Normal file
View File

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% block title %}Accueil{% endblock %}
{% block content %}
<div class="content">
{% for semestre in semestres %}
<h1 class="title"><a style="color: inherit;" href="{{ url_for('Semestre', num=semestre.num) }}">Semestre {{ semestre.num }} <i class="far fa-edit fa-sm"></i></a></h1>
<ul>
{% for competence in semestre.ues %}
<li><h2 class="title is-4"><a style="color: inherit;" href="{{ url_for('Competence', code=competence.code[-1]) }}">UE{{ semestre.num }}.{{ competence.code[-1] }} - {{ competence.nom }} <i class="far fa-edit fa-sm"></i></a></h2></li>
<ul>
{% for ac in competence.acs %}
<li><a style="color: inherit;" href="{{ url_for('AC', code=ac.code[2:]) }}"><span class="tag is-info">{{ ac.code }}</span> - {{ ac.titre }} <i class="far fa-edit fa-sm"></i></a></li>
{% endfor %}
</ul>
{% endfor %}
</ul>
{% endfor %}
</div>
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<div class="buttons">
{% if not semestres %}
{{ form.ajouter(class="button") }}
{{ form.reset(class="button is-static") }}
{% else %}
{{ form.ajouter(class="button is-static") }}
{{ form.reset(class="button") }}
{% endif %}
{{ form.exporter(class="button is-static") }}
{{ form.importer(class="button is-static") }}
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block title %}Semestre 1{% endblock %}
{% block content %}
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<div class="content">
{% for competence in semestre.ues %}
<h2 class="title"><a style="color: inherit;" href="{{ url_for('Competence', code=competence.code[-1]) }}">UE{{ semestre.num }}.{{ competence.code[-1] }} - {{ competence.nom }} <i class="far fa-edit fa-sm"></i></a></h2>
<ul>
{% for ac in competence.acs %}
<li><a style="color: inherit;" href="{{ url_for('AC', code=ac.code[2:]) }}"><span class="tag is-info">{{ ac.code }}</span> - {{ ac.titre }} <i class="far fa-edit fa-sm"></i></a></li>
{% endfor %}
</ul>
{{ form.ueform.__getitem__(loop.index0).ue }}
{{ render_dropdown(form.ueform.__getitem__(loop.index0).acs) }}
<p>Objets de formation</p>
<ul>
{% for sae in competence.saes %}
<li><a style="color: inherit;" href="{{ url_for('SAE', code=sae.code[3:]) }}"><span class="tag is-info">{{ sae.code }}</span> - {{ sae.titre }} <i class="far fa-edit fa-sm"></i></a></li>
{% endfor %}
</ul>
{{ render_dropdown(form.ueform.__getitem__(loop.index0).saes) }}
<ul>
{% for ressource in competence.ressources %}
<li><a style="color: inherit;" href="{{ url_for('Ressource', code=ressource.code[1:]) }}"><span class="tag is-info">{{ ressource.code }}</span> - {{ ressource.nom }} <i class="far fa-edit fa-sm"></i></a></li>
{% endfor %}
</ul>
{{ render_dropdown(form.ueform.__getitem__(loop.index0).ressources) }}
{% endfor %}
</div>
<div class="field is-grouped">
{{ render_dropdown(form.ues) }}
<div class="control">
{{ form.update(class="button") }}
</div>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,38 @@
"""empty message
Revision ID: 03ec797adcc8
Revises: 15feabeae315
Create Date: 2021-05-12 12:41:11.703242
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '03ec797adcc8'
down_revision = '15feabeae315'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('semestre',
sa.Column('num', sa.String(length=1), nullable=False),
sa.PrimaryKeyConstraint('num')
)
op.create_table('Semestres_Competences',
sa.Column('Semestre_num', sa.String(length=1), nullable=True),
sa.Column('Competence_code', sa.String(length=3), nullable=True),
sa.ForeignKeyConstraint(['Competence_code'], ['competence.code'], ),
sa.ForeignKeyConstraint(['Semestre_num'], ['semestre.num'], )
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('Semestres_Competences')
op.drop_table('semestre')
# ### end Alembic commands ###

View File

@ -0,0 +1,40 @@
"""empty message
Revision ID: 8b0862eb0856
Revises: cff96c022884
Create Date: 2021-06-01 18:50:16.604312
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '8b0862eb0856'
down_revision = 'cff96c022884'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('Ressources_Competences',
sa.Column('Ressource_code', sa.String(length=6), nullable=True),
sa.Column('Competence_code', sa.String(length=3), nullable=True),
sa.ForeignKeyConstraint(['Competence_code'], ['competence.code'], ),
sa.ForeignKeyConstraint(['Ressource_code'], ['ressource.code'], )
)
op.create_table('SAEs_Competences',
sa.Column('SAE_code', sa.String(length=6), nullable=True),
sa.Column('Competence_code', sa.String(length=3), nullable=True),
sa.ForeignKeyConstraint(['Competence_code'], ['competence.code'], ),
sa.ForeignKeyConstraint(['SAE_code'], ['SAE.code'], )
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('SAEs_Competences')
op.drop_table('Ressources_Competences')
# ### end Alembic commands ###

View File

@ -0,0 +1,33 @@
"""empty message
Revision ID: cff96c022884
Revises: 03ec797adcc8
Create Date: 2021-05-13 15:30:50.203249
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'cff96c022884'
down_revision = '03ec797adcc8'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('ACs_Competences',
sa.Column('AC_code', sa.String(length=6), nullable=True),
sa.Column('Competence_code', sa.String(length=3), nullable=True),
sa.ForeignKeyConstraint(['AC_code'], ['AC.code'], ),
sa.ForeignKeyConstraint(['Competence_code'], ['competence.code'], )
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('ACs_Competences')
# ### end Alembic commands ###