diff --git a/README.md b/README.md index dffcde4..2073093 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ pip install flask-wtf pip install flask-sqlalchemy pip install flask-migrate + pip install wtforms-alchemy ### Pour lancer l'app diff --git a/app.db b/app.db index 5e8f862..a80baa8 100644 Binary files a/app.db and b/app.db differ diff --git a/app/forms.py b/app/forms.py index d8d700c..8025ff2 100644 --- a/app/forms.py +++ b/app/forms.py @@ -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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/app/models.py b/app/models.py index d276a74..9aa1c46 100644 --- a/app/models.py +++ b/app/models.py @@ -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 "".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 "".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 "".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 "".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 "".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 "".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 "".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 "".format(self.code) \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 3735b11..022d01c 100644 --- a/app/routes.py +++ b/app/routes.py @@ -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", 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", 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", 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", 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", 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", 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) \ No newline at end of file diff --git a/app/static/js/base.js b/app/static/js/base.js new file mode 100644 index 0000000..8eb9920 --- /dev/null +++ b/app/static/js/base.js @@ -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"); + }); +}); \ No newline at end of file diff --git a/app/templates/AC.html b/app/templates/AC.html index e371e32..7b9e9f4 100644 --- a/app/templates/AC.html +++ b/app/templates/AC.html @@ -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) }} +
{{ render_dropdown(form.saes) }}
+{{ render_list(form.ressources) }} +
{{ render_dropdown(form.ressources) }}
{% endblock %} \ No newline at end of file diff --git a/app/templates/Ressource.html b/app/templates/Ressource.html index 3db47e8..4516d22 100644 --- a/app/templates/Ressource.html +++ b/app/templates/Ressource.html @@ -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) }} +
{{ render_dropdown(form.acs) }}
+{{ render_list(form.saes) }} +
{{ render_dropdown(form.saes) }}
{{ render_field(form.prerequis,"input") }} {{ render_field(form.contexte,"textarea") }} {{ render_field(form.contenu,"textarea") }} diff --git a/app/templates/SAE.html b/app/templates/SAE.html index eff6845..75961ef 100644 --- a/app/templates/SAE.html +++ b/app/templates/SAE.html @@ -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) }} +
{{ render_dropdown(form.acs) }}
+{{ render_list(form.ressources) }} +
{{ render_dropdown(form.ressources) }}
{{ render_field(form.livrables,"textarea") }} {{ render_field(form.motscles,"input") }} diff --git a/app/templates/base.html b/app/templates/base.html index 378085b..554bbb0 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -1,3 +1,47 @@ +{% macro render_dropdown(field) %} +
+ +
+{% endmacro %} + +{% macro render_field(field,class) %} +
+ +
+ {{ field(class=class)}} +
+ {% for error in field.errors %} +

{{error}}

+ {% endfor %} +
+{% endmacro %} + +{% macro render_list(field) %} +
    + {% for ref in field.data %} +
  • {{ ref.code }} - {{ ref.titre }}{{ ref.nom }}
  • + {% endfor %} +
+{% endmacro %} + @@ -7,6 +51,7 @@ + {% block head %}{% endblock %} @@ -25,6 +70,18 @@ - \ No newline at end of file + + diff --git a/app/templates/form.html b/app/templates/form.html index 6f7e987..29d15d1 100644 --- a/app/templates/form.html +++ b/app/templates/form.html @@ -1,14 +1,3 @@ -{% macro render_field(field,class) %} -
- -
- {{ field(class=class)}} -
- {% for error in field.errors %} -

{{error}}

- {% endfor %} -
-{% endmacro %} {% extends "base.html" %} {% block content %} @@ -49,7 +38,7 @@ {% endfor %} - {% for error in form.charger.errors %} + {% for error in form.referentiel.errors %}

{{error}}

{% endfor %} {% if form.referentiel.choices|length != 0 %} diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..6d5b668 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% block title %}Accueil{% endblock %} + +{% block content %} +
+{% for semestre in semestres %} +

Semestre {{ semestre.num }}

+ +{% endfor %} +
+
+ {{ form.hidden_tag() }} +
+ {% 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") }} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/semestre.html b/app/templates/semestre.html new file mode 100644 index 0000000..afedc2b --- /dev/null +++ b/app/templates/semestre.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} + +{% block title %}Semestre 1{% endblock %} + +{% block content %} + +
+ {{ form.hidden_tag() }} +
+ {% for competence in semestre.ues %} +

UE{{ semestre.num }}.{{ competence.code[-1] }} - {{ competence.nom }}

+ + {{ form.ueform.__getitem__(loop.index0).ue }} + {{ render_dropdown(form.ueform.__getitem__(loop.index0).acs) }} +

Objets de formation

+ + {{ render_dropdown(form.ueform.__getitem__(loop.index0).saes) }} + + {{ render_dropdown(form.ueform.__getitem__(loop.index0).ressources) }} + {% endfor %} +
+
+ {{ render_dropdown(form.ues) }} +
+ {{ form.update(class="button") }} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/migrations/versions/03ec797adcc8_.py b/migrations/versions/03ec797adcc8_.py new file mode 100644 index 0000000..8620327 --- /dev/null +++ b/migrations/versions/03ec797adcc8_.py @@ -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 ### diff --git a/migrations/versions/8b0862eb0856_.py b/migrations/versions/8b0862eb0856_.py new file mode 100644 index 0000000..7a9682f --- /dev/null +++ b/migrations/versions/8b0862eb0856_.py @@ -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 ### diff --git a/migrations/versions/cff96c022884_.py b/migrations/versions/cff96c022884_.py new file mode 100644 index 0000000..ee10011 --- /dev/null +++ b/migrations/versions/cff96c022884_.py @@ -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 ###