forked from ScoDoc/ScoDoc
FormSemestreDescription: champs pour spécifs EL. Formulaire saisie.
This commit is contained in:
parent
3971145abd
commit
513fb3d46d
@ -826,8 +826,25 @@ def formsemestre_get_description_image(formsemestre_id: int):
|
|||||||
if not formsemestre.description or not formsemestre.description.image:
|
if not formsemestre.description or not formsemestre.description.image:
|
||||||
return make_response("", 204) # 204 No Content status
|
return make_response("", 204) # 204 No Content status
|
||||||
|
|
||||||
|
return _image_response(formsemestre.description.image)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/formsemestre/<int:formsemestre_id>/description/photo_ens")
|
||||||
|
@api_web_bp.route("/formsemestre/<int:formsemestre_id>/description/photo_ens")
|
||||||
|
@login_required
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
def formsemestre_get_photo_ens(formsemestre_id: int):
|
||||||
|
"""Photo du responsable, ou illustration du formsemestre. Peut être vide."""
|
||||||
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
|
if not formsemestre.description or not formsemestre.description.photo_ens:
|
||||||
|
return make_response("", 204) # 204 No Content status
|
||||||
|
|
||||||
|
return _image_response(formsemestre.description.photo_ens)
|
||||||
|
|
||||||
|
|
||||||
|
def _image_response(image_data):
|
||||||
# Guess the mimetype based on the image data
|
# Guess the mimetype based on the image data
|
||||||
image_data = formsemestre.description.image
|
|
||||||
mimetype = mimetypes.guess_type("image")[0]
|
mimetype = mimetypes.guess_type("image")[0]
|
||||||
|
|
||||||
if not mimetype:
|
if not mimetype:
|
||||||
|
@ -1 +1,23 @@
|
|||||||
# empty but required for pylint
|
"""WTF Forms for ScoDoc
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
|
|
||||||
|
class ScoDocForm(FlaskForm):
|
||||||
|
"""Super class for ScoDoc forms
|
||||||
|
(inspired by @iziram)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"Init form, adding a filed for our error messages"
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.ok = True
|
||||||
|
self.error_messages: list[str] = [] # used to report our errors
|
||||||
|
|
||||||
|
def set_error(self, err_msg, field=None):
|
||||||
|
"Set error message both in form and field"
|
||||||
|
self.ok = False
|
||||||
|
self.error_messages.append(err_msg)
|
||||||
|
if field:
|
||||||
|
field.errors.append(err_msg)
|
||||||
|
@ -6,13 +6,42 @@
|
|||||||
|
|
||||||
"""Formulaire édition description formsemestre
|
"""Formulaire édition description formsemestre
|
||||||
"""
|
"""
|
||||||
|
from wtforms import (
|
||||||
|
BooleanField,
|
||||||
|
FileField,
|
||||||
|
SelectField,
|
||||||
|
StringField,
|
||||||
|
TextAreaField,
|
||||||
|
SubmitField,
|
||||||
|
)
|
||||||
|
from wtforms.validators import AnyOf, Optional
|
||||||
|
|
||||||
from flask_wtf import FlaskForm
|
from app.forms import ScoDocForm
|
||||||
from wtforms import StringField, TextAreaField, FileField, SubmitField
|
from app.models import FORMSEMESTRE_DISPOSITIFS
|
||||||
from wtforms.validators import Optional
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
class FormSemestreDescriptionForm(FlaskForm):
|
class DateDMYField(StringField):
|
||||||
|
"Champ date JJ/MM/AAAA"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
render_kw = kwargs.pop("render_kw", {})
|
||||||
|
render_kw.update({"class": "datepicker", "size": 10})
|
||||||
|
super().__init__(*args, render_kw=render_kw, **kwargs)
|
||||||
|
|
||||||
|
# note: process_formdata(self, valuelist) ne fonctionne pas
|
||||||
|
# en cas d'erreur de saisie les valeurs ne sont pas ré-affichées.
|
||||||
|
# On vérifie donc les valeurs dans le code de la vue.
|
||||||
|
|
||||||
|
def process_data(self, value):
|
||||||
|
"Process data from model to form"
|
||||||
|
if value:
|
||||||
|
self.data = value.strftime(scu.DATE_FMT)
|
||||||
|
else:
|
||||||
|
self.data = ""
|
||||||
|
|
||||||
|
|
||||||
|
class FormSemestreDescriptionForm(ScoDocForm):
|
||||||
"Formulaire édition description formsemestre"
|
"Formulaire édition description formsemestre"
|
||||||
description = TextAreaField(
|
description = TextAreaField(
|
||||||
"Description",
|
"Description",
|
||||||
@ -23,12 +52,60 @@ class FormSemestreDescriptionForm(FlaskForm):
|
|||||||
horaire = StringField(
|
horaire = StringField(
|
||||||
"Horaire", validators=[Optional()], description="ex: les lundis 9h-12h"
|
"Horaire", validators=[Optional()], description="ex: les lundis 9h-12h"
|
||||||
)
|
)
|
||||||
|
date_debut_inscriptions = DateDMYField(
|
||||||
|
"Date de début des inscriptions",
|
||||||
|
description="""date d'ouverture des inscriptions
|
||||||
|
(laisser vide pour autoriser tout le temps)""",
|
||||||
|
render_kw={
|
||||||
|
"id": "date_debut_inscriptions",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
date_fin_inscriptions = DateDMYField(
|
||||||
|
"Date de fin des inscriptions",
|
||||||
|
render_kw={
|
||||||
|
"id": "date_fin_inscriptions",
|
||||||
|
},
|
||||||
|
)
|
||||||
image = FileField(
|
image = FileField(
|
||||||
"Image", validators=[Optional()], description="Image illustrant cette formation"
|
"Image", validators=[Optional()], description="Image illustrant cette formation"
|
||||||
)
|
)
|
||||||
lieu = StringField("Lieu", validators=[Optional()], description="ex: salle 123")
|
campus = StringField(
|
||||||
|
"Campus", validators=[Optional()], description="ex: Villetaneuse"
|
||||||
|
)
|
||||||
|
salle = StringField("Salle", validators=[Optional()], description="ex: salle 123")
|
||||||
|
dispositif = SelectField(
|
||||||
|
"Dispositif",
|
||||||
|
choices=FORMSEMESTRE_DISPOSITIFS.items(),
|
||||||
|
coerce=int,
|
||||||
|
description="modalité de formation",
|
||||||
|
validators=[AnyOf(FORMSEMESTRE_DISPOSITIFS.keys())],
|
||||||
|
)
|
||||||
|
modalites_mcc = TextAreaField(
|
||||||
|
"Modalités de contrôle des connaissances",
|
||||||
|
validators=[Optional()],
|
||||||
|
description="texte libre",
|
||||||
|
)
|
||||||
|
photo_ens = FileField(
|
||||||
|
"Photo de l'enseignant(e)",
|
||||||
|
validators=[Optional()],
|
||||||
|
description="ou autre illustration",
|
||||||
|
)
|
||||||
|
public = StringField(
|
||||||
|
"Public visé", validators=[Optional()], description="ex: débutants"
|
||||||
|
)
|
||||||
|
prerequis = TextAreaField(
|
||||||
|
"Prérequis", validators=[Optional()], description="texte libre"
|
||||||
|
)
|
||||||
responsable = StringField(
|
responsable = StringField(
|
||||||
"Responsable", validators=[Optional()], description="ex: nom de l'enseignant"
|
"Responsable",
|
||||||
|
validators=[Optional()],
|
||||||
|
description="""nom de l'enseignant de la formation, ou personne
|
||||||
|
chargée de l'organisation du semestre.""",
|
||||||
|
)
|
||||||
|
|
||||||
|
wip = BooleanField(
|
||||||
|
"Travaux en cours",
|
||||||
|
description="work in progress: si coché, affichera juste le titre du semestre",
|
||||||
)
|
)
|
||||||
|
|
||||||
submit = SubmitField("Enregistrer")
|
submit = SubmitField("Enregistrer")
|
||||||
|
@ -188,7 +188,10 @@ from app.models.formsemestre import (
|
|||||||
NotesSemSet,
|
NotesSemSet,
|
||||||
notes_semset_formsemestre,
|
notes_semset_formsemestre,
|
||||||
)
|
)
|
||||||
from app.models.formsemestre_descr import FormSemestreDescription
|
from app.models.formsemestre_descr import (
|
||||||
|
FormSemestreDescription,
|
||||||
|
FORMSEMESTRE_DISPOSITIFS,
|
||||||
|
)
|
||||||
from app.models.moduleimpls import (
|
from app.models.moduleimpls import (
|
||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
notes_modules_enseignants,
|
notes_modules_enseignants,
|
||||||
|
@ -22,15 +22,33 @@ class FormSemestreDescription(models.ScoDocModel):
|
|||||||
__tablename__ = "notes_formsemestre_description"
|
__tablename__ = "notes_formsemestre_description"
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
# Storing image data directly in the database:
|
description = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
image = db.Column(db.LargeBinary(), nullable=True)
|
"description du cours, html autorisé"
|
||||||
description = db.Column(
|
|
||||||
db.Text(), nullable=False, default="", server_default=""
|
|
||||||
) # HTML allowed
|
|
||||||
responsable = db.Column(db.Text(), nullable=False, default="", server_default="")
|
|
||||||
lieu = db.Column(db.Text(), nullable=False, default="", server_default="")
|
|
||||||
horaire = db.Column(db.Text(), nullable=False, default="", server_default="")
|
horaire = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
"indication sur l'horaire, texte libre"
|
||||||
|
date_debut_inscriptions = db.Column(db.DateTime(timezone=True), nullable=True)
|
||||||
|
date_fin_inscriptions = db.Column(db.DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
wip = db.Column(db.Boolean, nullable=False, default=False, server_default="false")
|
||||||
|
"work in progress: si vrai, affichera juste le titre du semestre"
|
||||||
|
|
||||||
|
# Store image data directly in the database:
|
||||||
|
image = db.Column(db.LargeBinary(), nullable=True)
|
||||||
|
campus = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
salle = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
|
||||||
|
dispositif = db.Column(db.Integer, nullable=False, default=0, server_default="0")
|
||||||
|
"0 présentiel, 1 online, 2 hybride"
|
||||||
|
modalites_mcc = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
"modalités de contrôle des connaissances"
|
||||||
|
photo_ens = db.Column(db.LargeBinary(), nullable=True)
|
||||||
|
"photo de l'enseignant(e)"
|
||||||
|
public = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
"public visé"
|
||||||
|
prerequis = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
"prérequis (texte libre, html autorisé)"
|
||||||
|
responsable = db.Column(db.Text(), nullable=False, default="", server_default="")
|
||||||
|
"responsable du cours (texte libre, html autorisé)"
|
||||||
formsemestre_id = db.Column(
|
formsemestre_id = db.Column(
|
||||||
db.Integer,
|
db.Integer,
|
||||||
db.ForeignKey("notes_formsemestre.id", ondelete="CASCADE"),
|
db.ForeignKey("notes_formsemestre.id", ondelete="CASCADE"),
|
||||||
@ -40,20 +58,6 @@ class FormSemestreDescription(models.ScoDocModel):
|
|||||||
"FormSemestre", back_populates="description", uselist=False
|
"FormSemestre", back_populates="description", uselist=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
image=None,
|
|
||||||
description="",
|
|
||||||
responsable="",
|
|
||||||
lieu="",
|
|
||||||
horaire="",
|
|
||||||
):
|
|
||||||
self.description = description
|
|
||||||
self.horaire = horaire
|
|
||||||
self.image = image
|
|
||||||
self.lieu = lieu
|
|
||||||
self.responsable = responsable
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<FormSemestreDescription {self.id} {self.formsemestre}>"
|
return f"<FormSemestreDescription {self.id} {self.formsemestre}>"
|
||||||
|
|
||||||
@ -61,11 +65,18 @@ class FormSemestreDescription(models.ScoDocModel):
|
|||||||
"""clone instance"""
|
"""clone instance"""
|
||||||
return super().clone(not_copying=not_copying + ("formsemestre_id",))
|
return super().clone(not_copying=not_copying + ("formsemestre_id",))
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self, exclude_images=True) -> dict:
|
||||||
return {
|
"dict, tous les attributs sauf les images"
|
||||||
"formsemestre_id": self.formsemestre_id,
|
d = dict(self.__dict__)
|
||||||
"description": self.description,
|
d.pop("_sa_instance_state", None)
|
||||||
"responsable": self.responsable,
|
if exclude_images:
|
||||||
"lieu": self.lieu,
|
d.pop("image", None)
|
||||||
"horaire": self.horaire,
|
d.pop("photo_ens", None)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
FORMSEMESTRE_DISPOSITIFS = {
|
||||||
|
0: "présentiel",
|
||||||
|
1: "en ligne",
|
||||||
|
2: "hybride",
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: green;
|
color: green;
|
||||||
}
|
}
|
||||||
|
div.field_descr {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
.submit {
|
.submit {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -16,6 +19,7 @@
|
|||||||
}
|
}
|
||||||
div.image {
|
div.image {
|
||||||
margin-left: 32px;
|
margin-left: 32px;
|
||||||
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
div.image img {
|
div.image img {
|
||||||
border: 1px dashed #b60c0c;
|
border: 1px dashed #b60c0c;
|
||||||
@ -23,6 +27,29 @@ div.image img {
|
|||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% macro render_string_field(field, size=64) %}
|
||||||
|
<div>
|
||||||
|
<span class="wtf-field">{{ field.label }} :</span>
|
||||||
|
<span class="wtf-field">{{ field(size=size)|safe }}</span>
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<span class="text-danger">{{ error }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% if field.description %}
|
||||||
|
<div class="field_descr">{{ field.description }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro render_textarea_field(field, cols=80, rows=12) %}
|
||||||
|
<div>
|
||||||
|
<div class="wtf-field">{{ field.label }} :</div>
|
||||||
|
<div class="wtf-field">{{ field(cols=cols, rows=rows)|safe }}</div>
|
||||||
|
{% if field.description %}
|
||||||
|
<div class="field_descr">{{ field.description }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
@ -32,35 +59,50 @@ div.image img {
|
|||||||
<p>Les informations saisies ici ne sont pas utilisées par ScoDoc mais
|
<p>Les informations saisies ici ne sont pas utilisées par ScoDoc mais
|
||||||
mises à disposition des applications tierces comme AutoSco.
|
mises à disposition des applications tierces comme AutoSco.
|
||||||
</p>
|
</p>
|
||||||
<p>La description du semestre est un </p>
|
|
||||||
<p>Il est possible d'ajouter une image pour illustrer le semestre.</p>
|
|
||||||
<p>Le responsable est généralement l'enseignant en charge de la formation, ou
|
|
||||||
la personne qui s'occupant de l'organisation du semestre.</p>
|
|
||||||
<p>Tous les champs sont optionnels.</p>
|
<p>Tous les champs sont optionnels.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
|
<span class="wtf-field">{{ form.wip.label }}</span> <span class="wtf-field">{{form.wip() }}</span>
|
||||||
|
<span class="field_descr">{{ form.wip.description }}</span>
|
||||||
|
{{ render_string_field(form.date_debut_inscriptions, size=10) }}
|
||||||
|
{{ render_string_field(form.date_fin_inscriptions, size=10) }}
|
||||||
|
|
||||||
|
{{ render_textarea_field(form.description) }}
|
||||||
|
{{ render_string_field(form.responsable) }}
|
||||||
|
|
||||||
|
{{ form.photo_ens.label }}
|
||||||
|
<div class="image">
|
||||||
|
{% if formsemestre_description.photo_ens %}
|
||||||
|
<img src="{{ url_for('apiweb.formsemestre_get_photo_ens',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id) }}"
|
||||||
|
alt="Current Image" style="max-width: 200px;">
|
||||||
<div>
|
<div>
|
||||||
{{ form.description.label }}<br>
|
Changer l'image: {{ form.photo_ens() }}
|
||||||
{{ form.description(cols=80, rows=8) }}
|
|
||||||
<div class="field_descr">{{ form.description.description }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<em>Aucune photo ou illustration chargée.</em>
|
||||||
|
{{ form.photo_ens() }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ render_string_field(form.campus) }}
|
||||||
|
{{ render_string_field(form.salle, size=32) }}
|
||||||
|
{{ render_string_field(form.horaire) }}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{{ form.responsable.label }}<br>
|
<span class="wtf-field">{{ form.dispositif.label }} :</span>
|
||||||
{{ form.responsable(size=64) }}<br>
|
<span class="wtf-field">{{ form.dispositif }}</span>
|
||||||
<div class="field_descr">{{ form.responsable.description }}</div>
|
{% if form.dispositif.description %}
|
||||||
</div>
|
<div class="field_descr">{{ form.dispositif.description }}</div>
|
||||||
<div>
|
{% endif %}
|
||||||
{{ form.lieu.label }}<br>
|
|
||||||
{{ form.lieu(size=48) }}
|
|
||||||
<div class="field_descr">{{ form.lieu.description }}</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{{ form.horaire.label }}<br>
|
|
||||||
{{ form.horaire(size=64) }}<br>
|
|
||||||
<div class="field_descr">{{ form.horaire.description }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{{ render_string_field(form.public) }}
|
||||||
|
{{ render_textarea_field(form.modalites_mcc, rows=8) }}
|
||||||
|
{{ render_textarea_field(form.prerequis, rows=5) }}
|
||||||
|
|
||||||
{{ form.image.label }}
|
{{ form.image.label }}
|
||||||
<div class="image">
|
<div class="image">
|
||||||
@ -68,7 +110,7 @@ div.image img {
|
|||||||
<img src="{{ url_for('apiweb.formsemestre_get_description_image',
|
<img src="{{ url_for('apiweb.formsemestre_get_description_image',
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre.id) }}"
|
formsemestre_id=formsemestre.id) }}"
|
||||||
alt="Current Image" style="max-width: 200px;">
|
alt="Current Image" style="max-width: 400px;">
|
||||||
<div>
|
<div>
|
||||||
Changer l'image: {{ form.image() }}
|
Changer l'image: {{ form.image() }}
|
||||||
</div>
|
</div>
|
||||||
@ -76,8 +118,8 @@ div.image img {
|
|||||||
<em>Aucune image n'est actuellement associée à ce semestre.</em>
|
<em>Aucune image n'est actuellement associée à ce semestre.</em>
|
||||||
{{ form.image() }}
|
{{ form.image() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="submit">
|
<div class="submit">
|
||||||
{{ form.submit }} {{ form.cancel }}
|
{{ form.submit }} {{ form.cancel }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +29,8 @@ Vues "modernes" des formsemestres
|
|||||||
Emmanuel Viennet, 2023
|
Emmanuel Viennet, 2023
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
from flask import flash, redirect, render_template, url_for
|
from flask import flash, redirect, render_template, url_for
|
||||||
from flask import current_app, g, request
|
from flask import current_app, g, request
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ from app.models import (
|
|||||||
Formation,
|
Formation,
|
||||||
FormSemestre,
|
FormSemestre,
|
||||||
FormSemestreDescription,
|
FormSemestreDescription,
|
||||||
|
FORMSEMESTRE_DISPOSITIFS,
|
||||||
ScoDocSiteConfig,
|
ScoDocSiteConfig,
|
||||||
)
|
)
|
||||||
from app.scodoc import (
|
from app.scodoc import (
|
||||||
@ -248,7 +251,7 @@ def edit_formsemestre_description(formsemestre_id: int):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
formsemestre_description = formsemestre.description
|
formsemestre_description = formsemestre.description
|
||||||
form = edit_description.FormSemestreDescriptionForm(obj=formsemestre_description)
|
form = edit_description.FormSemestreDescriptionForm(obj=formsemestre_description)
|
||||||
|
ok = True
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
if form.cancel.data: # cancel button
|
if form.cancel.data: # cancel button
|
||||||
return redirect(
|
return redirect(
|
||||||
@ -258,16 +261,55 @@ def edit_formsemestre_description(formsemestre_id: int):
|
|||||||
formsemestre_id=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# Vérification valeur dispositif
|
||||||
|
if form.dispositif.data not in FORMSEMESTRE_DISPOSITIFS:
|
||||||
|
flash("Dispositif inconnu", "danger")
|
||||||
|
ok = False
|
||||||
|
|
||||||
|
# Vérification dates inscriptions
|
||||||
|
if form.date_debut_inscriptions.data:
|
||||||
|
try:
|
||||||
|
date_debut_inscriptions_dt = datetime.datetime.strptime(
|
||||||
|
form.date_debut_inscriptions.data, scu.DATE_FMT
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
flash("Date de début des inscriptions invalide", "danger")
|
||||||
|
form.set_error("date début invalide", form.date_debut_inscriptions)
|
||||||
|
ok = False
|
||||||
|
else:
|
||||||
|
date_debut_inscriptions_dt = None
|
||||||
|
if form.date_fin_inscriptions.data:
|
||||||
|
try:
|
||||||
|
date_fin_inscriptions_dt = datetime.datetime.strptime(
|
||||||
|
form.date_fin_inscriptions.data, scu.DATE_FMT
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
flash("Date de fin des inscriptions invalide", "danger")
|
||||||
|
form.set_error("date fin invalide", form.date_fin_inscriptions)
|
||||||
|
ok = False
|
||||||
|
else:
|
||||||
|
date_fin_inscriptions_dt = None
|
||||||
|
if ok:
|
||||||
|
# dates converties
|
||||||
|
form.date_debut_inscriptions.data = date_debut_inscriptions_dt
|
||||||
|
form.date_fin_inscriptions.data = date_fin_inscriptions_dt
|
||||||
|
# Affecte tous les champs sauf les images:
|
||||||
form_image = form.image
|
form_image = form.image
|
||||||
del form.image
|
del form.image
|
||||||
|
form_photo_ens = form.photo_ens
|
||||||
|
del form.photo_ens
|
||||||
form.populate_obj(formsemestre_description)
|
form.populate_obj(formsemestre_description)
|
||||||
|
# Affecte les images:
|
||||||
if form_image.data:
|
for field, form_field in (
|
||||||
image_data = form_image.data.read()
|
("image", form_image),
|
||||||
|
("photo_ens", form_photo_ens),
|
||||||
|
):
|
||||||
|
if form_field.data:
|
||||||
|
image_data = form_field.data.read()
|
||||||
max_length = current_app.config.get("MAX_CONTENT_LENGTH")
|
max_length = current_app.config.get("MAX_CONTENT_LENGTH")
|
||||||
if max_length and len(image_data) > max_length:
|
if max_length and len(image_data) > max_length:
|
||||||
flash(
|
flash(
|
||||||
f"Image too large, max {max_length} bytes",
|
f"Image trop grande ({field}), max {max_length} octets",
|
||||||
"danger",
|
"danger",
|
||||||
)
|
)
|
||||||
return redirect(
|
return redirect(
|
||||||
@ -277,7 +319,7 @@ def edit_formsemestre_description(formsemestre_id: int):
|
|||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
formsemestre_description.image = image_data
|
setattr(formsemestre_description, field, image_data)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Description enregistrée", "success")
|
flash("Description enregistrée", "success")
|
||||||
@ -295,4 +337,5 @@ def edit_formsemestre_description(formsemestre_id: int):
|
|||||||
formsemestre=formsemestre,
|
formsemestre=formsemestre,
|
||||||
formsemestre_description=formsemestre_description,
|
formsemestre_description=formsemestre_description,
|
||||||
sco=ScoData(formsemestre=formsemestre),
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
|
title="Modif. description semestre",
|
||||||
)
|
)
|
||||||
|
@ -21,11 +21,20 @@ def upgrade():
|
|||||||
op.create_table(
|
op.create_table(
|
||||||
"notes_formsemestre_description",
|
"notes_formsemestre_description",
|
||||||
sa.Column("id", sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column("image", sa.LargeBinary(), nullable=True),
|
|
||||||
sa.Column("description", sa.Text(), server_default="", nullable=False),
|
sa.Column("description", sa.Text(), server_default="", nullable=False),
|
||||||
sa.Column("responsable", sa.Text(), server_default="", nullable=False),
|
|
||||||
sa.Column("lieu", sa.Text(), server_default="", nullable=False),
|
|
||||||
sa.Column("horaire", sa.Text(), server_default="", nullable=False),
|
sa.Column("horaire", sa.Text(), server_default="", nullable=False),
|
||||||
|
sa.Column("date_debut_inscriptions", sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column("date_fin_inscriptions", sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column("wip", sa.Boolean(), server_default="false", nullable=False),
|
||||||
|
sa.Column("image", sa.LargeBinary(), nullable=True),
|
||||||
|
sa.Column("campus", sa.Text(), server_default="", nullable=False),
|
||||||
|
sa.Column("salle", sa.Text(), server_default="", nullable=False),
|
||||||
|
sa.Column("dispositif", sa.Integer(), server_default="0", nullable=False),
|
||||||
|
sa.Column("modalites_mcc", sa.Text(), server_default="", nullable=False),
|
||||||
|
sa.Column("photo_ens", sa.LargeBinary(), nullable=True),
|
||||||
|
sa.Column("public", sa.Text(), server_default="", nullable=False),
|
||||||
|
sa.Column("prerequis", sa.Text(), server_default="", nullable=False),
|
||||||
|
sa.Column("responsable", sa.Text(), server_default="", nullable=False),
|
||||||
sa.Column("formsemestre_id", sa.Integer(), nullable=False),
|
sa.Column("formsemestre_id", sa.Integer(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(
|
sa.ForeignKeyConstraint(
|
||||||
["formsemestre_id"], ["notes_formsemestre.id"], ondelete="CASCADE"
|
["formsemestre_id"], ["notes_formsemestre.id"], ondelete="CASCADE"
|
||||||
|
@ -223,11 +223,14 @@ def test_formsemestre_description(test_client):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
assert formsemestre.description is None
|
assert formsemestre.description is None
|
||||||
# Association d'une description
|
# Association d'une description
|
||||||
formsemestre.description = FormSemestreDescription(
|
formsemestre.description = FormSemestreDescription.create_from_dict(
|
||||||
description="Description 2",
|
{
|
||||||
responsable="Responsable 2",
|
"description": "Description",
|
||||||
lieu="Lieu 2",
|
"responsable": "Responsable",
|
||||||
horaire="Horaire 2",
|
"campus": "Sorbonne",
|
||||||
|
"salle": "L214",
|
||||||
|
"horaire": "23h à l'aube",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
db.session.add(formsemestre)
|
db.session.add(formsemestre)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
Loading…
Reference in New Issue
Block a user