FormSemestreDescription: informations pour applications tierces: Modèle, API, éditeur.
This commit is contained in:
parent
fc036705e8
commit
525d0446cc
@ -13,6 +13,7 @@
|
|||||||
FormSemestre
|
FormSemestre
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import mimetypes
|
||||||
from operator import attrgetter, itemgetter
|
from operator import attrgetter, itemgetter
|
||||||
|
|
||||||
from flask import g, make_response, request
|
from flask import g, make_response, request
|
||||||
@ -790,3 +791,49 @@ def formsemestre_edt(formsemestre_id: int):
|
|||||||
return sco_edt_cal.formsemestre_edt_dict(
|
return sco_edt_cal.formsemestre_edt_dict(
|
||||||
formsemestre, group_ids=group_ids, show_modules_titles=show_modules_titles
|
formsemestre, group_ids=group_ids, show_modules_titles=show_modules_titles
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/formsemestre/<int:formsemestre_id>/description")
|
||||||
|
@api_web_bp.route("/formsemestre/<int:formsemestre_id>/description")
|
||||||
|
@login_required
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
@as_json
|
||||||
|
def formsemestre_get_description(formsemestre_id: int):
|
||||||
|
"""Description externe du formsemestre. Peut être vide.
|
||||||
|
|
||||||
|
formsemestre_id : l'id du formsemestre
|
||||||
|
|
||||||
|
SAMPLES
|
||||||
|
-------
|
||||||
|
/formsemestre/1/description
|
||||||
|
"""
|
||||||
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
|
return formsemestre.description.to_dict() if formsemestre.description else {}
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/formsemestre/<int:formsemestre_id>/description/image")
|
||||||
|
@api_web_bp.route("/formsemestre/<int:formsemestre_id>/description/image")
|
||||||
|
@login_required
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
def formsemestre_get_description_image(formsemestre_id: int):
|
||||||
|
"""Image de la description externe du formsemestre. Peut être vide.
|
||||||
|
|
||||||
|
formsemestre_id : l'id du formsemestre
|
||||||
|
"""
|
||||||
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
|
if not formsemestre.description or not formsemestre.description.image:
|
||||||
|
return make_response("", 204) # 204 No Content status
|
||||||
|
|
||||||
|
# Guess the mimetype based on the image data
|
||||||
|
image_data = formsemestre.description.image
|
||||||
|
mimetype = mimetypes.guess_type("image")[0]
|
||||||
|
|
||||||
|
if not mimetype:
|
||||||
|
# Default to binary stream if mimetype cannot be determined
|
||||||
|
mimetype = "application/octet-stream"
|
||||||
|
|
||||||
|
response = make_response(image_data)
|
||||||
|
response.headers["Content-Type"] = mimetype
|
||||||
|
return response
|
||||||
|
35
app/forms/formsemestre/edit_description.py
Normal file
35
app/forms/formsemestre/edit_description.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Formulaire édition description formsemestre
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, TextAreaField, FileField, SubmitField
|
||||||
|
from wtforms.validators import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class FormSemestreDescriptionForm(FlaskForm):
|
||||||
|
"Formulaire édition description formsemestre"
|
||||||
|
description = TextAreaField(
|
||||||
|
"Description",
|
||||||
|
validators=[Optional()],
|
||||||
|
description="""texte libre : informations
|
||||||
|
sur le contenu, les objectifs, les modalités d'évaluation, etc.""",
|
||||||
|
)
|
||||||
|
horaire = StringField(
|
||||||
|
"Horaire", validators=[Optional()], description="ex: les lundis 9h-12h"
|
||||||
|
)
|
||||||
|
image = FileField(
|
||||||
|
"Image", validators=[Optional()], description="Image illustrant cette formation"
|
||||||
|
)
|
||||||
|
lieu = StringField("Lieu", validators=[Optional()], description="ex: salle 123")
|
||||||
|
responsable = StringField(
|
||||||
|
"Responsable", validators=[Optional()], description="ex: nom de l'enseignant"
|
||||||
|
)
|
||||||
|
|
||||||
|
submit = SubmitField("Enregistrer")
|
||||||
|
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})
|
@ -14,12 +14,13 @@ class _EditModimplsCodesForm(FlaskForm):
|
|||||||
# construit dynamiquement ci-dessous
|
# construit dynamiquement ci-dessous
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
def EditModimplsCodesForm(formsemestre: FormSemestre) -> _EditModimplsCodesForm:
|
def EditModimplsCodesForm(formsemestre: FormSemestre) -> _EditModimplsCodesForm:
|
||||||
"Création d'un formulaire pour éditer les codes"
|
"Création d'un formulaire pour éditer les codes"
|
||||||
|
|
||||||
# Formulaire dynamique, on créé une classe ad-hoc
|
# Formulaire dynamique, on créé une classe ad-hoc
|
||||||
class F(_EditModimplsCodesForm):
|
class F(_EditModimplsCodesForm):
|
||||||
pass
|
"class factory"
|
||||||
|
|
||||||
def _gen_mod_form(modimpl: ModuleImpl):
|
def _gen_mod_form(modimpl: ModuleImpl):
|
||||||
field = StringField(
|
field = StringField(
|
||||||
|
@ -38,7 +38,7 @@ class ScoDocModel(db.Model):
|
|||||||
__abstract__ = True # declare an abstract class for SQLAlchemy
|
__abstract__ = True # declare an abstract class for SQLAlchemy
|
||||||
|
|
||||||
def clone(self, not_copying=()):
|
def clone(self, not_copying=()):
|
||||||
"""Clone, not copying the given attrs
|
"""Clone, not copying the given attrs, and add to session.
|
||||||
Attention: la copie n'a pas d'id avant le prochain flush ou commit.
|
Attention: la copie n'a pas d'id avant le prochain flush ou commit.
|
||||||
"""
|
"""
|
||||||
d = dict(self.__dict__)
|
d = dict(self.__dict__)
|
||||||
@ -188,6 +188,7 @@ from app.models.formsemestre import (
|
|||||||
NotesSemSet,
|
NotesSemSet,
|
||||||
notes_semset_formsemestre,
|
notes_semset_formsemestre,
|
||||||
)
|
)
|
||||||
|
from app.models.formsemestre_descr import FormSemestreDescription
|
||||||
from app.models.moduleimpls import (
|
from app.models.moduleimpls import (
|
||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
notes_modules_enseignants,
|
notes_modules_enseignants,
|
||||||
|
@ -143,6 +143,12 @@ class FormSemestre(models.ScoDocModel):
|
|||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
)
|
)
|
||||||
|
description = db.relationship(
|
||||||
|
"FormSemestreDescription",
|
||||||
|
back_populates="formsemestre",
|
||||||
|
cascade="all, delete-orphan",
|
||||||
|
uselist=False,
|
||||||
|
)
|
||||||
etuds = db.relationship(
|
etuds = db.relationship(
|
||||||
"Identite",
|
"Identite",
|
||||||
secondary="notes_formsemestre_inscription",
|
secondary="notes_formsemestre_inscription",
|
||||||
|
71
app/models/formsemestre_descr.py
Normal file
71
app/models/formsemestre_descr.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Description d'un formsemestre pour applications tierces.
|
||||||
|
|
||||||
|
Ces informations sont éditables dans ScoDoc et publiés sur l'API
|
||||||
|
pour affichage dans l'application tierce.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
from app import models
|
||||||
|
|
||||||
|
|
||||||
|
class FormSemestreDescription(models.ScoDocModel):
|
||||||
|
"""Informations décrivant un "semestre" (session) de formation
|
||||||
|
pour un apprenant.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "notes_formsemestre_description"
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
# Storing image data directly in the database:
|
||||||
|
image = db.Column(db.LargeBinary(), nullable=True)
|
||||||
|
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="")
|
||||||
|
|
||||||
|
formsemestre_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_formsemestre.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
formsemestre = db.relationship(
|
||||||
|
"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):
|
||||||
|
return f"<FormSemestreDescription {self.id} {self.formsemestre}>"
|
||||||
|
|
||||||
|
def clone(self, not_copying=()) -> "FormSemestreDescription":
|
||||||
|
"""clone instance"""
|
||||||
|
return super().clone(not_copying=not_copying + ("formsemestre_id",))
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"formsemestre_id": self.formsemestre_id,
|
||||||
|
"description": self.description,
|
||||||
|
"responsable": self.responsable,
|
||||||
|
"lieu": self.lieu,
|
||||||
|
"horaire": self.horaire,
|
||||||
|
}
|
@ -508,6 +508,12 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
||||||
}">Modifier les codes Apogée et emploi du temps des modules</a>
|
}">Modifier les codes Apogée et emploi du temps des modules</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><a class="stdlink" href="{url_for("notes.edit_formsemestre_description",
|
||||||
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
||||||
|
}">Éditer la description externe du semestre</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3>Sélectionner les modules, leurs responsables et les étudiants
|
<h3>Sélectionner les modules, leurs responsables et les étudiants
|
||||||
à inscrire:</h3>
|
à inscrire:</h3>
|
||||||
"""
|
"""
|
||||||
@ -1287,6 +1293,7 @@ def do_formsemestre_clone(
|
|||||||
clone_partitions=False,
|
clone_partitions=False,
|
||||||
):
|
):
|
||||||
"""Clone a semestre: make copy, same modules, same options, same resps, same partitions.
|
"""Clone a semestre: make copy, same modules, same options, same resps, same partitions.
|
||||||
|
Clone description.
|
||||||
New dates, responsable_id
|
New dates, responsable_id
|
||||||
"""
|
"""
|
||||||
log(f"do_formsemestre_clone: {orig_formsemestre_id}")
|
log(f"do_formsemestre_clone: {orig_formsemestre_id}")
|
||||||
@ -1375,10 +1382,14 @@ def do_formsemestre_clone(
|
|||||||
|
|
||||||
# 5- Copie les parcours
|
# 5- Copie les parcours
|
||||||
formsemestre.parcours = formsemestre_orig.parcours
|
formsemestre.parcours = formsemestre_orig.parcours
|
||||||
|
|
||||||
|
# 6- Copy description
|
||||||
|
formsemestre.description = formsemestre_orig.description.clone()
|
||||||
|
|
||||||
db.session.add(formsemestre)
|
db.session.add(formsemestre)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# 6- Copy partitions and groups
|
# 7- Copy partitions and groups
|
||||||
if clone_partitions:
|
if clone_partitions:
|
||||||
sco_groups_copy.clone_partitions_and_groups(
|
sco_groups_copy.clone_partitions_and_groups(
|
||||||
orig_formsemestre_id, formsemestre.id
|
orig_formsemestre_id, formsemestre.id
|
||||||
|
88
app/templates/formsemestre/edit_description.j2
Normal file
88
app/templates/formsemestre/edit_description.j2
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{% extends "sco_page.j2" %}
|
||||||
|
{% import 'wtf.j2' as wtf %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{super()}}
|
||||||
|
<style>
|
||||||
|
.field_descr {
|
||||||
|
font-style: italic;
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.submit {
|
||||||
|
margin-top: 32px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
div.image {
|
||||||
|
margin-left: 32px;
|
||||||
|
}
|
||||||
|
div.image img {
|
||||||
|
border: 1px dashed #b60c0c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<h2>Édition de la description du semestre</h2>
|
||||||
|
|
||||||
|
<div class="help">
|
||||||
|
<p>Les informations saisies ici ne sont pas utilisées par ScoDoc mais
|
||||||
|
mises à disposition des applications tierces comme AutoSco.
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<div>
|
||||||
|
{{ form.description.label }}<br>
|
||||||
|
{{ form.description(cols=80, rows=8) }}
|
||||||
|
<div class="field_descr">{{ form.description.description }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ form.responsable.label }}<br>
|
||||||
|
{{ form.responsable(size=64) }}<br>
|
||||||
|
<div class="field_descr">{{ form.responsable.description }}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ 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>
|
||||||
|
|
||||||
|
{{ form.image.label }}
|
||||||
|
<div class="image">
|
||||||
|
{% if formsemestre_description.image %}
|
||||||
|
<img src="{{ url_for('apiweb.formsemestre_get_description_image',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id) }}"
|
||||||
|
alt="Current Image" style="max-width: 200px;">
|
||||||
|
<div>
|
||||||
|
Changer l'image: {{ form.image() }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<em>Aucune image n'est actuellement associée à ce semestre.</em>
|
||||||
|
{{ form.image() }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="submit">
|
||||||
|
{{ form.submit }} {{ form.cancel }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -25,20 +25,29 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Vues "modernes" des formsemestre
|
Vues "modernes" des formsemestres
|
||||||
Emmanuel Viennet, 2023
|
Emmanuel Viennet, 2023
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import flash, redirect, render_template, url_for
|
from flask import flash, redirect, render_template, url_for
|
||||||
from flask import g, request
|
from flask import current_app, g, request
|
||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.decorators import (
|
from app.decorators import (
|
||||||
scodoc,
|
scodoc,
|
||||||
permission_required,
|
permission_required,
|
||||||
)
|
)
|
||||||
from app.forms.formsemestre import change_formation, edit_modimpls_codes_apo
|
from app.forms.formsemestre import (
|
||||||
from app.models import Formation, FormSemestre, ScoDocSiteConfig
|
change_formation,
|
||||||
|
edit_modimpls_codes_apo,
|
||||||
|
edit_description,
|
||||||
|
)
|
||||||
|
from app.models import (
|
||||||
|
Formation,
|
||||||
|
FormSemestre,
|
||||||
|
FormSemestreDescription,
|
||||||
|
ScoDocSiteConfig,
|
||||||
|
)
|
||||||
from app.scodoc import (
|
from app.scodoc import (
|
||||||
sco_edt_cal,
|
sco_edt_cal,
|
||||||
sco_formations,
|
sco_formations,
|
||||||
@ -223,3 +232,67 @@ def formsemestre_edt_help_config(formsemestre_id: int):
|
|||||||
ScoDocSiteConfig=ScoDocSiteConfig,
|
ScoDocSiteConfig=ScoDocSiteConfig,
|
||||||
title="Aide configuration EDT",
|
title="Aide configuration EDT",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route(
|
||||||
|
"/formsemestre_description/<int:formsemestre_id>/edit", methods=["GET", "POST"]
|
||||||
|
)
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.EditFormSemestre)
|
||||||
|
def edit_formsemestre_description(formsemestre_id: int):
|
||||||
|
"Edition de la description d'un formsemestre"
|
||||||
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
|
if not formsemestre.description:
|
||||||
|
formsemestre.description = FormSemestreDescription()
|
||||||
|
db.session.add(formsemestre)
|
||||||
|
db.session.commit()
|
||||||
|
formsemestre_description = formsemestre.description
|
||||||
|
form = edit_description.FormSemestreDescriptionForm(obj=formsemestre_description)
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
if form.cancel.data: # cancel button
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"notes.formsemestre_editwithmodules",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
form_image = form.image
|
||||||
|
del form.image
|
||||||
|
form.populate_obj(formsemestre_description)
|
||||||
|
|
||||||
|
if form_image.data:
|
||||||
|
image_data = form_image.data.read()
|
||||||
|
max_length = current_app.config.get("MAX_CONTENT_LENGTH")
|
||||||
|
if max_length and len(image_data) > max_length:
|
||||||
|
flash(
|
||||||
|
f"Image too large, max {max_length} bytes",
|
||||||
|
"danger",
|
||||||
|
)
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"notes.edit_formsemestre_description",
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
formsemestre_description.image = image_data
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
flash("Description enregistrée", "success")
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"notes.formsemestre_status",
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"formsemestre/edit_description.j2",
|
||||||
|
form=form,
|
||||||
|
formsemestre=formsemestre,
|
||||||
|
formsemestre_description=formsemestre_description,
|
||||||
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
|
)
|
||||||
|
38
migrations/versions/2640b7686de6_formsemestre_description.py
Normal file
38
migrations/versions/2640b7686de6_formsemestre_description.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""FormSemestreDescription
|
||||||
|
|
||||||
|
Revision ID: 2640b7686de6
|
||||||
|
Revises: f6cb3d4e44ec
|
||||||
|
Create Date: 2024-08-11 15:44:32.560054
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "2640b7686de6"
|
||||||
|
down_revision = "f6cb3d4e44ec"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table(
|
||||||
|
"notes_formsemestre_description",
|
||||||
|
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("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("formsemestre_id", sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["formsemestre_id"], ["notes_formsemestre.id"], ondelete="CASCADE"
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("notes_formsemestre_description")
|
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
""" Test création/accès/clonage formsemestre
|
""" Test création/accès/clonage formsemestre
|
||||||
"""
|
"""
|
||||||
from flask import Response
|
from flask import g, Response
|
||||||
import pytest
|
import pytest
|
||||||
from tests.unit import yaml_setup, call_view
|
|
||||||
|
|
||||||
import app
|
import app
|
||||||
from app.models import Formation, FormSemestre
|
from app import db
|
||||||
|
from app.models import Formation, FormSemestre, FormSemestreDescription
|
||||||
from app.scodoc import (
|
from app.scodoc import (
|
||||||
sco_archives_formsemestre,
|
sco_archives_formsemestre,
|
||||||
sco_cost_formation,
|
sco_cost_formation,
|
||||||
@ -35,6 +35,7 @@ from app.scodoc import (
|
|||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.views import notes, scolar
|
from app.views import notes, scolar
|
||||||
from config import TestConfig
|
from config import TestConfig
|
||||||
|
from tests.unit import yaml_setup, call_view
|
||||||
|
|
||||||
DEPT = TestConfig.DEPT_TEST
|
DEPT = TestConfig.DEPT_TEST
|
||||||
|
|
||||||
@ -203,3 +204,36 @@ def test_formsemestre_misc_views(test_client):
|
|||||||
ans = sco_debouche.report_debouche_date(start_year=2000)
|
ans = sco_debouche.report_debouche_date(start_year=2000)
|
||||||
ans = sco_cost_formation.formsemestre_estim_cost(formsemestre.id)
|
ans = sco_cost_formation.formsemestre_estim_cost(formsemestre.id)
|
||||||
# pas de test des indicateurs de suivi BUT
|
# pas de test des indicateurs de suivi BUT
|
||||||
|
|
||||||
|
|
||||||
|
def test_formsemestre_description(test_client):
|
||||||
|
"""Test FormSemestreDescription"""
|
||||||
|
app.set_sco_dept(DEPT)
|
||||||
|
#
|
||||||
|
nb_descriptions = FormSemestreDescription.query.count()
|
||||||
|
# Création d'un semestre
|
||||||
|
|
||||||
|
formsemestre = FormSemestre(
|
||||||
|
dept_id=g.scodoc_dept_id,
|
||||||
|
titre="test description",
|
||||||
|
date_debut="2024-08-01",
|
||||||
|
date_fin="2024-08-31",
|
||||||
|
)
|
||||||
|
db.session.add(formsemestre)
|
||||||
|
db.session.commit()
|
||||||
|
assert formsemestre.description is None
|
||||||
|
# Association d'une description
|
||||||
|
formsemestre.description = FormSemestreDescription(
|
||||||
|
description="Description 2",
|
||||||
|
responsable="Responsable 2",
|
||||||
|
lieu="Lieu 2",
|
||||||
|
horaire="Horaire 2",
|
||||||
|
)
|
||||||
|
db.session.add(formsemestre)
|
||||||
|
db.session.commit()
|
||||||
|
assert formsemestre.description.formsemestre.id == formsemestre.id
|
||||||
|
assert FormSemestreDescription.query.count() == nb_descriptions + 1
|
||||||
|
# Suppression / cascade
|
||||||
|
db.session.delete(formsemestre)
|
||||||
|
db.session.commit()
|
||||||
|
assert FormSemestreDescription.query.count() == nb_descriptions
|
||||||
|
Loading…
Reference in New Issue
Block a user