forked from ScoDoc/DocScoDoc
Merge branch 'refactor_nt' of https://scodoc.org/git/viennet/ScoDoc into orebut
This commit is contained in:
commit
96130f1a75
@ -190,6 +190,7 @@ def create_app(config_class=DevConfig):
|
|||||||
|
|
||||||
app.register_error_handler(ScoGenError, handle_sco_value_error)
|
app.register_error_handler(ScoGenError, handle_sco_value_error)
|
||||||
app.register_error_handler(ScoValueError, handle_sco_value_error)
|
app.register_error_handler(ScoValueError, handle_sco_value_error)
|
||||||
|
|
||||||
app.register_error_handler(AccessDenied, handle_access_denied)
|
app.register_error_handler(AccessDenied, handle_access_denied)
|
||||||
app.register_error_handler(500, internal_server_error)
|
app.register_error_handler(500, internal_server_error)
|
||||||
app.register_error_handler(503, postgresql_server_error)
|
app.register_error_handler(503, postgresql_server_error)
|
||||||
|
@ -11,7 +11,7 @@ from time import time
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import cracklib # pylint: disable=import-error
|
import cracklib # pylint: disable=import-error
|
||||||
from flask import current_app, url_for, g
|
from flask import current_app, g
|
||||||
from flask_login import UserMixin, AnonymousUserMixin
|
from flask_login import UserMixin, AnonymousUserMixin
|
||||||
|
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
@ -136,6 +136,7 @@ class User(UserMixin, db.Model):
|
|||||||
return check_password_hash(self.password_hash, password)
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
def get_reset_password_token(self, expires_in=600):
|
def get_reset_password_token(self, expires_in=600):
|
||||||
|
"Un token pour réinitialiser son mot de passe"
|
||||||
return jwt.encode(
|
return jwt.encode(
|
||||||
{"reset_password": self.id, "exp": time() + expires_in},
|
{"reset_password": self.id, "exp": time() + expires_in},
|
||||||
current_app.config["SECRET_KEY"],
|
current_app.config["SECRET_KEY"],
|
||||||
@ -144,15 +145,17 @@ class User(UserMixin, db.Model):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def verify_reset_password_token(token):
|
def verify_reset_password_token(token):
|
||||||
|
"Vérification du token de reéinitialisation du mot de passe"
|
||||||
try:
|
try:
|
||||||
id = jwt.decode(
|
user_id = jwt.decode(
|
||||||
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
|
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
|
||||||
)["reset_password"]
|
)["reset_password"]
|
||||||
except:
|
except:
|
||||||
return
|
return
|
||||||
return User.query.get(id)
|
return User.query.get(user_id)
|
||||||
|
|
||||||
def to_dict(self, include_email=True):
|
def to_dict(self, include_email=True):
|
||||||
|
"""l'utilisateur comme un dict, avec des champs supplémentaires"""
|
||||||
data = {
|
data = {
|
||||||
"date_expiration": self.date_expiration.isoformat() + "Z"
|
"date_expiration": self.date_expiration.isoformat() + "Z"
|
||||||
if self.date_expiration
|
if self.date_expiration
|
||||||
@ -472,5 +475,5 @@ def get_super_admin():
|
|||||||
|
|
||||||
|
|
||||||
@login.user_loader
|
@login.user_loader
|
||||||
def load_user(id):
|
def load_user(uid):
|
||||||
return User.query.get(int(id))
|
return User.query.get(int(uid))
|
||||||
|
@ -72,7 +72,7 @@ def bulletin_but_xml_compat(
|
|||||||
etud = Identite.query.get_or_404(etudid)
|
etud = Identite.query.get_or_404(etudid)
|
||||||
results = bulletin_but.ResultatsSemestreBUT(sem)
|
results = bulletin_but.ResultatsSemestreBUT(sem)
|
||||||
nb_inscrits = len(results.etuds)
|
nb_inscrits = len(results.etuds)
|
||||||
if sem.bul_hide_xml or force_publishing:
|
if (not sem.bul_hide_xml) or force_publishing:
|
||||||
published = "1"
|
published = "1"
|
||||||
else:
|
else:
|
||||||
published = "0"
|
published = "0"
|
||||||
|
@ -121,6 +121,8 @@ def notes_sem_assemble_cube(modimpls_notes: list[pd.DataFrame]) -> np.ndarray:
|
|||||||
(DataFrames rendus par compute_module_moy, (etud x UE))
|
(DataFrames rendus par compute_module_moy, (etud x UE))
|
||||||
Resultat: ndarray (etud x module x UE)
|
Resultat: ndarray (etud x module x UE)
|
||||||
"""
|
"""
|
||||||
|
if len(modimpls_notes) == 0:
|
||||||
|
return np.zeros((0, 0, 0), dtype=float)
|
||||||
modimpls_notes_arr = [df.values for df in modimpls_notes]
|
modimpls_notes_arr = [df.values for df in modimpls_notes]
|
||||||
modimpls_notes = np.stack(modimpls_notes_arr)
|
modimpls_notes = np.stack(modimpls_notes_arr)
|
||||||
# passe de (mod x etud x ue) à (etud x mod x UE)
|
# passe de (mod x etud x ue) à (etud x mod x UE)
|
||||||
|
@ -230,7 +230,7 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
|
|
||||||
def get_etud_ue_status(self, etudid: int, ue_id: int):
|
def get_etud_ue_status(self, etudid: int, ue_id: int):
|
||||||
return {
|
return {
|
||||||
"cur_moy_ue": self.results.etud_moy_ue[ue_id][etudid],
|
"cur_moy_ue": self.etud_moy_ue[ue_id][etudid],
|
||||||
"is_capitalized": False, # XXX TODO
|
"is_capitalized": False, # XXX TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,9 +242,9 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
|
|
||||||
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
||||||
"liste des évaluations valides dans un module"
|
"liste des évaluations valides dans un module"
|
||||||
mi = ModuleImpl.query.get(moduleimpl_id)
|
modimpl = ModuleImpl.query.get(moduleimpl_id)
|
||||||
evals_results = []
|
evals_results = []
|
||||||
for e in mi.evaluations:
|
for e in modimpl.evaluations:
|
||||||
d = e.to_dict()
|
d = e.to_dict()
|
||||||
d["heure_debut"] = e.heure_debut # datetime.time
|
d["heure_debut"] = e.heure_debut # datetime.time
|
||||||
d["heure_fin"] = e.heure_fin
|
d["heure_fin"] = e.heure_fin
|
||||||
|
@ -57,6 +57,23 @@ class EntrepriseContact(db.Model):
|
|||||||
"service": self.service,
|
"service": self.service,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def to_dict_export(self):
|
||||||
|
entreprise = Entreprise.query.get(self.entreprise_id)
|
||||||
|
return {
|
||||||
|
"nom": self.nom,
|
||||||
|
"prenom": self.prenom,
|
||||||
|
"telephone": self.telephone,
|
||||||
|
"mail": self.mail,
|
||||||
|
"poste": self.poste,
|
||||||
|
"service": self.service,
|
||||||
|
"siret": entreprise.siret,
|
||||||
|
"nom_entreprise": entreprise.nom,
|
||||||
|
"adresse_entreprise": entreprise.adresse,
|
||||||
|
"codepostal": entreprise.codepostal,
|
||||||
|
"ville": entreprise.ville,
|
||||||
|
"pays": entreprise.pays,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class EntrepriseOffre(db.Model):
|
class EntrepriseOffre(db.Model):
|
||||||
__tablename__ = "entreprise_offre"
|
__tablename__ = "entreprise_offre"
|
||||||
@ -71,6 +88,15 @@ class EntrepriseOffre(db.Model):
|
|||||||
missions = db.Column(db.Text)
|
missions = db.Column(db.Text)
|
||||||
duree = db.Column(db.Text)
|
duree = db.Column(db.Text)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"intitule": self.intitule,
|
||||||
|
"description": self.description,
|
||||||
|
"type_offre": self.type_offre,
|
||||||
|
"missions": self.missions,
|
||||||
|
"duree": self.duree,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class EntrepriseLog(db.Model):
|
class EntrepriseLog(db.Model):
|
||||||
__tablename__ = "entreprise_log"
|
__tablename__ = "entreprise_log"
|
||||||
@ -100,3 +126,12 @@ class EntrepriseEnvoiOffre(db.Model):
|
|||||||
receiver_id = db.Column(db.Integer, db.ForeignKey("user.id"))
|
receiver_id = db.Column(db.Integer, db.ForeignKey("user.id"))
|
||||||
offre_id = db.Column(db.Integer, db.ForeignKey("entreprise_offre.id"))
|
offre_id = db.Column(db.Integer, db.ForeignKey("entreprise_offre.id"))
|
||||||
date_envoi = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
date_envoi = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
|
|
||||||
|
|
||||||
|
class EntrepriseEnvoiOffreEtudiant(db.Model):
|
||||||
|
__tablename__ = "entreprise_envoi_offre_etudiant"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
sender_id = db.Column(db.Integer, db.ForeignKey("user.id"))
|
||||||
|
receiver_id = db.Column(db.Integer, db.ForeignKey("identite.id"))
|
||||||
|
offre_id = db.Column(db.Integer, db.ForeignKey("entreprise_offre.id"))
|
||||||
|
date_envoi = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from config import Config
|
from config import Config
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
import glob
|
import glob
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
@ -45,6 +45,18 @@ from werkzeug.utils import secure_filename
|
|||||||
|
|
||||||
@bp.route("/", methods=["GET"])
|
@bp.route("/", methods=["GET"])
|
||||||
def index():
|
def index():
|
||||||
|
"""
|
||||||
|
Permet d'afficher une page avec la liste des entreprises et une liste des dernières opérations
|
||||||
|
|
||||||
|
Retourne: template de la page (entreprises.html)
|
||||||
|
Arguments du template:
|
||||||
|
title:
|
||||||
|
titre de la page
|
||||||
|
entreprises:
|
||||||
|
liste des entreprises
|
||||||
|
logs:
|
||||||
|
liste des logs
|
||||||
|
"""
|
||||||
entreprises = Entreprise.query.all()
|
entreprises = Entreprise.query.all()
|
||||||
logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all()
|
logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all()
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -57,6 +69,18 @@ def index():
|
|||||||
|
|
||||||
@bp.route("/contacts", methods=["GET"])
|
@bp.route("/contacts", methods=["GET"])
|
||||||
def contacts():
|
def contacts():
|
||||||
|
"""
|
||||||
|
Permet d'afficher une page la liste des contacts et une liste des dernières opérations
|
||||||
|
|
||||||
|
Retourne: template de la page (contacts.html)
|
||||||
|
Arguments du template:
|
||||||
|
title:
|
||||||
|
titre de la page
|
||||||
|
contacts:
|
||||||
|
liste des contacts
|
||||||
|
logs:
|
||||||
|
liste des logs
|
||||||
|
"""
|
||||||
contacts = (
|
contacts = (
|
||||||
db.session.query(EntrepriseContact, Entreprise)
|
db.session.query(EntrepriseContact, Entreprise)
|
||||||
.join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id)
|
.join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id)
|
||||||
@ -70,10 +94,39 @@ def contacts():
|
|||||||
|
|
||||||
@bp.route("/fiche_entreprise/<int:id>", methods=["GET"])
|
@bp.route("/fiche_entreprise/<int:id>", methods=["GET"])
|
||||||
def fiche_entreprise(id):
|
def fiche_entreprise(id):
|
||||||
|
"""
|
||||||
|
Permet d'afficher la fiche entreprise d'une entreprise avec une liste des dernières opérations et
|
||||||
|
l'historique des étudiants ayant réaliser un stage ou une alternance dans cette entreprise.
|
||||||
|
La fiche entreprise comporte les informations de l'entreprise, les contacts de l'entreprise et
|
||||||
|
les offres de l'entreprise.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
|
||||||
|
Retourne: template de la page (fiche_entreprise.html)
|
||||||
|
Arguments du template:
|
||||||
|
title:
|
||||||
|
titre de la page
|
||||||
|
entreprise:
|
||||||
|
un objet entreprise
|
||||||
|
contacts:
|
||||||
|
liste des contacts de l'entreprise
|
||||||
|
offres:
|
||||||
|
liste des offres de l'entreprise avec leurs fichiers
|
||||||
|
logs:
|
||||||
|
liste des logs
|
||||||
|
historique:
|
||||||
|
liste des étudiants ayant réaliser un stage ou une alternance dans l'entreprise
|
||||||
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
||||||
offres = entreprise.offres
|
offres = entreprise.offres
|
||||||
offres_with_files = []
|
offres_with_files = []
|
||||||
for offre in offres:
|
for offre in offres:
|
||||||
|
if datetime.now() - offre.date_ajout.replace(tzinfo=None) >= timedelta(
|
||||||
|
days=90
|
||||||
|
): # pour une date d'expiration ?
|
||||||
|
break
|
||||||
files = []
|
files = []
|
||||||
path = os.path.join(
|
path = os.path.join(
|
||||||
Config.SCODOC_VAR_DIR,
|
Config.SCODOC_VAR_DIR,
|
||||||
@ -116,19 +169,32 @@ def fiche_entreprise(id):
|
|||||||
|
|
||||||
@bp.route("/offres", methods=["GET"])
|
@bp.route("/offres", methods=["GET"])
|
||||||
def offres():
|
def offres():
|
||||||
offres_recus = (
|
"""
|
||||||
|
Permet d'afficher la page où l'on recoit les offres
|
||||||
|
|
||||||
|
Retourne: template de la page (offres.html)
|
||||||
|
Arguments du template:
|
||||||
|
title:
|
||||||
|
titre de la page
|
||||||
|
offres_recus:
|
||||||
|
liste des offres reçues
|
||||||
|
"""
|
||||||
|
offres_recues = (
|
||||||
db.session.query(EntrepriseEnvoiOffre, EntrepriseOffre)
|
db.session.query(EntrepriseEnvoiOffre, EntrepriseOffre)
|
||||||
.filter(EntrepriseEnvoiOffre.receiver_id == current_user.id)
|
.filter(EntrepriseEnvoiOffre.receiver_id == current_user.id)
|
||||||
.join(EntrepriseOffre, EntrepriseOffre.id == EntrepriseEnvoiOffre.offre_id)
|
.join(EntrepriseOffre, EntrepriseOffre.id == EntrepriseEnvoiOffre.offre_id)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
return render_template(
|
return render_template(
|
||||||
"entreprises/offres.html", title=("Offres"), offres_recus=offres_recus
|
"entreprises/offres.html", title=("Offres"), offres_recues=offres_recues
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/add_entreprise", methods=["GET", "POST"])
|
@bp.route("/add_entreprise", methods=["GET", "POST"])
|
||||||
def add_entreprise():
|
def add_entreprise():
|
||||||
|
"""
|
||||||
|
Permet d'ajouter une entreprise dans la base avec un formulaire
|
||||||
|
"""
|
||||||
form = EntrepriseCreationForm()
|
form = EntrepriseCreationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
entreprise = Entreprise(
|
entreprise = Entreprise(
|
||||||
@ -170,6 +236,13 @@ def add_entreprise():
|
|||||||
|
|
||||||
@bp.route("/edit_entreprise/<int:id>", methods=["GET", "POST"])
|
@bp.route("/edit_entreprise/<int:id>", methods=["GET", "POST"])
|
||||||
def edit_entreprise(id):
|
def edit_entreprise(id):
|
||||||
|
"""
|
||||||
|
Permet de modifier une entreprise de la base avec un formulaire
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
||||||
form = EntrepriseModificationForm()
|
form = EntrepriseModificationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -231,6 +304,13 @@ def edit_entreprise(id):
|
|||||||
|
|
||||||
@bp.route("/delete_entreprise/<int:id>", methods=["GET", "POST"])
|
@bp.route("/delete_entreprise/<int:id>", methods=["GET", "POST"])
|
||||||
def delete_entreprise(id):
|
def delete_entreprise(id):
|
||||||
|
"""
|
||||||
|
Permet de supprimer une entreprise de la base avec un formulaire de confirmation
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
||||||
form = SuppressionConfirmationForm()
|
form = SuppressionConfirmationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -253,6 +333,13 @@ def delete_entreprise(id):
|
|||||||
|
|
||||||
@bp.route("/add_offre/<int:id>", methods=["GET", "POST"])
|
@bp.route("/add_offre/<int:id>", methods=["GET", "POST"])
|
||||||
def add_offre(id):
|
def add_offre(id):
|
||||||
|
"""
|
||||||
|
Permet d'ajouter une offre a une entreprise
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
||||||
form = OffreCreationForm()
|
form = OffreCreationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -279,6 +366,13 @@ def add_offre(id):
|
|||||||
|
|
||||||
@bp.route("/edit_offre/<int:id>", methods=["GET", "POST"])
|
@bp.route("/edit_offre/<int:id>", methods=["GET", "POST"])
|
||||||
def edit_offre(id):
|
def edit_offre(id):
|
||||||
|
"""
|
||||||
|
Permet de modifier une offre
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'offre
|
||||||
|
"""
|
||||||
offre = EntrepriseOffre.query.filter_by(id=id).first_or_404()
|
offre = EntrepriseOffre.query.filter_by(id=id).first_or_404()
|
||||||
form = OffreModificationForm()
|
form = OffreModificationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -309,6 +403,13 @@ def edit_offre(id):
|
|||||||
|
|
||||||
@bp.route("/delete_offre/<int:id>", methods=["GET", "POST"])
|
@bp.route("/delete_offre/<int:id>", methods=["GET", "POST"])
|
||||||
def delete_offre(id):
|
def delete_offre(id):
|
||||||
|
"""
|
||||||
|
Permet de supprimer une offre
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'offre
|
||||||
|
"""
|
||||||
offre = EntrepriseOffre.query.filter_by(id=id).first_or_404()
|
offre = EntrepriseOffre.query.filter_by(id=id).first_or_404()
|
||||||
entreprise_id = offre.entreprise.id
|
entreprise_id = offre.entreprise.id
|
||||||
form = SuppressionConfirmationForm()
|
form = SuppressionConfirmationForm()
|
||||||
@ -330,6 +431,13 @@ def delete_offre(id):
|
|||||||
|
|
||||||
@bp.route("/add_contact/<int:id>", methods=["GET", "POST"])
|
@bp.route("/add_contact/<int:id>", methods=["GET", "POST"])
|
||||||
def add_contact(id):
|
def add_contact(id):
|
||||||
|
"""
|
||||||
|
Permet d'ajouter un contact a une entreprise
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
||||||
form = ContactCreationForm(hidden_entreprise_id=entreprise.id)
|
form = ContactCreationForm(hidden_entreprise_id=entreprise.id)
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -357,6 +465,13 @@ def add_contact(id):
|
|||||||
|
|
||||||
@bp.route("/edit_contact/<int:id>", methods=["GET", "POST"])
|
@bp.route("/edit_contact/<int:id>", methods=["GET", "POST"])
|
||||||
def edit_contact(id):
|
def edit_contact(id):
|
||||||
|
"""
|
||||||
|
Permet de modifier un contact
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id du contact
|
||||||
|
"""
|
||||||
contact = EntrepriseContact.query.filter_by(id=id).first_or_404()
|
contact = EntrepriseContact.query.filter_by(id=id).first_or_404()
|
||||||
form = ContactModificationForm()
|
form = ContactModificationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -391,6 +506,13 @@ def edit_contact(id):
|
|||||||
|
|
||||||
@bp.route("/delete_contact/<int:id>", methods=["GET", "POST"])
|
@bp.route("/delete_contact/<int:id>", methods=["GET", "POST"])
|
||||||
def delete_contact(id):
|
def delete_contact(id):
|
||||||
|
"""
|
||||||
|
Permet de supprimer un contact
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id du contact
|
||||||
|
"""
|
||||||
contact = EntrepriseContact.query.filter_by(id=id).first_or_404()
|
contact = EntrepriseContact.query.filter_by(id=id).first_or_404()
|
||||||
entreprise_id = contact.entreprise.id
|
entreprise_id = contact.entreprise.id
|
||||||
form = SuppressionConfirmationForm()
|
form = SuppressionConfirmationForm()
|
||||||
@ -421,6 +543,13 @@ def delete_contact(id):
|
|||||||
|
|
||||||
@bp.route("/add_historique/<int:id>", methods=["GET", "POST"])
|
@bp.route("/add_historique/<int:id>", methods=["GET", "POST"])
|
||||||
def add_historique(id):
|
def add_historique(id):
|
||||||
|
"""
|
||||||
|
Permet d'ajouter un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
"""
|
||||||
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
entreprise = Entreprise.query.filter_by(id=id).first_or_404()
|
||||||
form = HistoriqueCreationForm()
|
form = HistoriqueCreationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -458,6 +587,13 @@ def add_historique(id):
|
|||||||
|
|
||||||
@bp.route("/envoyer_offre/<int:id>", methods=["GET", "POST"])
|
@bp.route("/envoyer_offre/<int:id>", methods=["GET", "POST"])
|
||||||
def envoyer_offre(id):
|
def envoyer_offre(id):
|
||||||
|
"""
|
||||||
|
Permet d'envoyer une offre à un utilisateur
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
id:
|
||||||
|
l'id de l'offre
|
||||||
|
"""
|
||||||
offre = EntrepriseOffre.query.filter_by(id=id).first_or_404()
|
offre = EntrepriseOffre.query.filter_by(id=id).first_or_404()
|
||||||
form = EnvoiOffreForm()
|
form = EnvoiOffreForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -484,6 +620,18 @@ def envoyer_offre(id):
|
|||||||
|
|
||||||
@bp.route("/etudiants")
|
@bp.route("/etudiants")
|
||||||
def json_etudiants():
|
def json_etudiants():
|
||||||
|
"""
|
||||||
|
Permet de récuperer un JSON avec tous les étudiants
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
term:
|
||||||
|
le terme utilisé pour le filtre de l'autosuggest
|
||||||
|
|
||||||
|
Retourne:
|
||||||
|
le JSON de tous les étudiants (nom, prenom, formation actuelle?) correspondant au terme
|
||||||
|
"""
|
||||||
|
if request.args.get("term") == None:
|
||||||
|
abort(400)
|
||||||
term = request.args.get("term").strip()
|
term = request.args.get("term").strip()
|
||||||
etudiants = Identite.query.filter(Identite.nom.ilike(f"%{term}%")).all()
|
etudiants = Identite.query.filter(Identite.nom.ilike(f"%{term}%")).all()
|
||||||
list = []
|
list = []
|
||||||
@ -505,6 +653,18 @@ def json_etudiants():
|
|||||||
|
|
||||||
@bp.route("/responsables")
|
@bp.route("/responsables")
|
||||||
def json_responsables():
|
def json_responsables():
|
||||||
|
"""
|
||||||
|
Permet de récuperer un JSON avec tous les étudiants
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
term:
|
||||||
|
le terme utilisé pour le filtre de l'autosuggest
|
||||||
|
|
||||||
|
Retourne:
|
||||||
|
le JSON de tous les utilisateurs (nom, prenom, login) correspondant au terme
|
||||||
|
"""
|
||||||
|
if request.args.get("term") == None:
|
||||||
|
abort(400)
|
||||||
term = request.args.get("term").strip()
|
term = request.args.get("term").strip()
|
||||||
responsables = User.query.filter(
|
responsables = User.query.filter(
|
||||||
User.nom.ilike(f"%{term}%"), User.nom.is_not(None), User.prenom.is_not(None)
|
User.nom.ilike(f"%{term}%"), User.nom.is_not(None), User.prenom.is_not(None)
|
||||||
@ -521,6 +681,9 @@ def json_responsables():
|
|||||||
|
|
||||||
@bp.route("/export_entreprises")
|
@bp.route("/export_entreprises")
|
||||||
def export_entreprises():
|
def export_entreprises():
|
||||||
|
"""
|
||||||
|
Permet d'exporter la liste des entreprises sous format excel (.xlsx)
|
||||||
|
"""
|
||||||
entreprises = Entreprise.query.all()
|
entreprises = Entreprise.query.all()
|
||||||
if entreprises:
|
if entreprises:
|
||||||
keys = ["siret", "nom", "adresse", "ville", "codepostal", "pays"]
|
keys = ["siret", "nom", "adresse", "ville", "codepostal", "pays"]
|
||||||
@ -539,6 +702,9 @@ def export_entreprises():
|
|||||||
|
|
||||||
@bp.route("/export_contacts")
|
@bp.route("/export_contacts")
|
||||||
def export_contacts():
|
def export_contacts():
|
||||||
|
"""
|
||||||
|
Permet d'exporter la liste des contacts sous format excel (.xlsx)
|
||||||
|
"""
|
||||||
contacts = EntrepriseContact.query.all()
|
contacts = EntrepriseContact.query.all()
|
||||||
if contacts:
|
if contacts:
|
||||||
keys = ["nom", "prenom", "telephone", "mail", "poste", "service"]
|
keys = ["nom", "prenom", "telephone", "mail", "poste", "service"]
|
||||||
@ -552,10 +718,56 @@ def export_contacts():
|
|||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/export_contacts_bis")
|
||||||
|
def export_contacts_bis():
|
||||||
|
"""
|
||||||
|
Permet d'exporter la liste des contacts avec leur entreprise sous format excel (.xlsx)
|
||||||
|
"""
|
||||||
|
contacts = EntrepriseContact.query.all()
|
||||||
|
if contacts:
|
||||||
|
keys = [
|
||||||
|
"nom",
|
||||||
|
"prenom",
|
||||||
|
"telephone",
|
||||||
|
"mail",
|
||||||
|
"poste",
|
||||||
|
"service",
|
||||||
|
"nom_entreprise",
|
||||||
|
"siret",
|
||||||
|
"adresse_entreprise",
|
||||||
|
"ville",
|
||||||
|
"codepostal",
|
||||||
|
"pays",
|
||||||
|
]
|
||||||
|
titles = keys[:]
|
||||||
|
L = [
|
||||||
|
[contact.to_dict_export().get(k, "") for k in keys] for contact in contacts
|
||||||
|
]
|
||||||
|
title = "contacts"
|
||||||
|
xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title)
|
||||||
|
filename = title
|
||||||
|
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/get_offre_file/<int:entreprise_id>/<int:offre_id>/<string:filedir>/<string:filename>"
|
"/get_offre_file/<int:entreprise_id>/<int:offre_id>/<string:filedir>/<string:filename>"
|
||||||
)
|
)
|
||||||
def get_offre_file(entreprise_id, offre_id, filedir, filename):
|
def get_offre_file(entreprise_id, offre_id, filedir, filename):
|
||||||
|
"""
|
||||||
|
Permet de télécharger un fichier d'une offre
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
entreprise_id:
|
||||||
|
l'id de l'entreprise
|
||||||
|
offre_id:
|
||||||
|
l'id de l'offre
|
||||||
|
filedir:
|
||||||
|
le répertoire du fichier
|
||||||
|
filename:
|
||||||
|
le nom du fichier
|
||||||
|
"""
|
||||||
if os.path.isfile(
|
if os.path.isfile(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
Config.SCODOC_VAR_DIR,
|
Config.SCODOC_VAR_DIR,
|
||||||
@ -583,6 +795,13 @@ def get_offre_file(entreprise_id, offre_id, filedir, filename):
|
|||||||
|
|
||||||
@bp.route("/add_offre_file/<int:offre_id>", methods=["GET", "POST"])
|
@bp.route("/add_offre_file/<int:offre_id>", methods=["GET", "POST"])
|
||||||
def add_offre_file(offre_id):
|
def add_offre_file(offre_id):
|
||||||
|
"""
|
||||||
|
Permet d'ajouter un fichier à une offre
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
offre_id:
|
||||||
|
l'id de l'offre
|
||||||
|
"""
|
||||||
offre = EntrepriseOffre.query.filter_by(id=offre_id).first_or_404()
|
offre = EntrepriseOffre.query.filter_by(id=offre_id).first_or_404()
|
||||||
form = AjoutFichierForm()
|
form = AjoutFichierForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -607,6 +826,15 @@ def add_offre_file(offre_id):
|
|||||||
|
|
||||||
@bp.route("/delete_offre_file/<int:offre_id>/<string:filedir>", methods=["GET", "POST"])
|
@bp.route("/delete_offre_file/<int:offre_id>/<string:filedir>", methods=["GET", "POST"])
|
||||||
def delete_offre_file(offre_id, filedir):
|
def delete_offre_file(offre_id, filedir):
|
||||||
|
"""
|
||||||
|
Permet de supprimer un fichier d'une offre
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
offre_id:
|
||||||
|
l'id de l'offre
|
||||||
|
filedir:
|
||||||
|
le répertoire du fichier
|
||||||
|
"""
|
||||||
offre = EntrepriseOffre.query.filter_by(id=offre_id).first_or_404()
|
offre = EntrepriseOffre.query.filter_by(id=offre_id).first_or_404()
|
||||||
form = SuppressionConfirmationForm()
|
form = SuppressionConfirmationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
@ -245,7 +245,7 @@ class DeptForm(FlaskForm):
|
|||||||
|
|
||||||
|
|
||||||
def _make_dept_id_name():
|
def _make_dept_id_name():
|
||||||
"""Cette section assute que tous les départements sont traités (y compris ceux qu'ont pas de logo au départ)
|
"""Cette section assure que tous les départements sont traités (y compris ceux qu'ont pas de logo au départ)
|
||||||
et détermine l'ordre d'affichage des DeptForm (GLOBAL d'abord, puis par ordre alpha de nom de département)
|
et détermine l'ordre d'affichage des DeptForm (GLOBAL d'abord, puis par ordre alpha de nom de département)
|
||||||
-> [ (None, None), (dept_id, dept_name)... ]"""
|
-> [ (None, None), (dept_id, dept_name)... ]"""
|
||||||
depts = [(None, GLOBAL)]
|
depts = [(None, GLOBAL)]
|
||||||
|
@ -31,16 +31,10 @@ Formulaires création département
|
|||||||
|
|
||||||
from flask import flash, url_for, redirect, render_template
|
from flask import flash, url_for, redirect, render_template
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import SelectField, SubmitField, FormField, validators, FieldList
|
from wtforms import SubmitField, validators
|
||||||
from wtforms.fields.simple import StringField, HiddenField
|
from wtforms.fields.simple import StringField, BooleanField
|
||||||
|
|
||||||
from app import AccessDenied
|
|
||||||
from app.models import Departement
|
|
||||||
from app.models import ScoPreference
|
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.scodoc import sco_utils as scu
|
|
||||||
|
|
||||||
from flask_login import current_user
|
|
||||||
|
|
||||||
|
|
||||||
class CreateDeptForm(FlaskForm):
|
class CreateDeptForm(FlaskForm):
|
||||||
@ -60,5 +54,10 @@ class CreateDeptForm(FlaskForm):
|
|||||||
validators.DataRequired("acronyme du département requis"),
|
validators.DataRequired("acronyme du département requis"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
# description = StringField(label="Description")
|
||||||
|
visible = BooleanField(
|
||||||
|
"Visible sur page d'accueil",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
submit = SubmitField("Valider")
|
submit = SubmitField("Valider")
|
||||||
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})
|
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})
|
||||||
|
@ -12,8 +12,10 @@ class Departement(db.Model):
|
|||||||
"""Un département ScoDoc"""
|
"""Un département ScoDoc"""
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
acronym = db.Column(db.String(SHORT_STR_LEN), nullable=False, index=True)
|
acronym = db.Column(
|
||||||
description = db.Column(db.Text())
|
db.String(SHORT_STR_LEN), nullable=False, index=True
|
||||||
|
) # ne change jamais, voir la pref. DeptName
|
||||||
|
description = db.Column(db.Text()) # pas utilisé par ScoDoc : voir DeptFullName
|
||||||
date_creation = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
date_creation = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
visible = db.Column(
|
visible = db.Column(
|
||||||
db.Boolean(), nullable=False, default=True, server_default="true"
|
db.Boolean(), nullable=False, default=True, server_default="true"
|
||||||
@ -49,11 +51,11 @@ class Departement(db.Model):
|
|||||||
return dept
|
return dept
|
||||||
|
|
||||||
|
|
||||||
def create_dept(acronym: str) -> Departement:
|
def create_dept(acronym: str, visible=True) -> Departement:
|
||||||
"Create new departement"
|
"Create new departement"
|
||||||
from app.models import ScoPreference
|
from app.models import ScoPreference
|
||||||
|
|
||||||
departement = Departement(acronym=acronym)
|
departement = Departement(acronym=acronym, visible=visible)
|
||||||
p1 = ScoPreference(name="DeptName", value=acronym, departement=departement)
|
p1 = ScoPreference(name="DeptName", value=acronym, departement=departement)
|
||||||
db.session.add(p1)
|
db.session.add(p1)
|
||||||
db.session.add(departement)
|
db.session.add(departement)
|
||||||
|
@ -49,9 +49,7 @@ class Module(db.Model):
|
|||||||
super(Module, self).__init__(**kwargs)
|
super(Module, self).__init__(**kwargs)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return (
|
return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code}>"
|
||||||
f"<Module{ModuleType(self.module_type).name} id={self.id} code={self.code}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
e = dict(self.__dict__)
|
e = dict(self.__dict__)
|
||||||
|
@ -78,7 +78,8 @@ def html_edit_formation_apc(
|
|||||||
alt="supprimer",
|
alt="supprimer",
|
||||||
),
|
),
|
||||||
"delete_disabled": scu.icontag(
|
"delete_disabled": scu.icontag(
|
||||||
"delete_small_dis_img", title="Suppression impossible (module utilisé)"
|
"delete_small_dis_img",
|
||||||
|
title="Suppression impossible (utilisé dans des semestres)",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,13 +30,18 @@
|
|||||||
"""
|
"""
|
||||||
import flask
|
import flask
|
||||||
from flask import g, url_for, request
|
from flask import g, url_for, request
|
||||||
|
from app.models.formations import Matiere
|
||||||
|
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app import log
|
from app import log
|
||||||
from app.models import Formation
|
from app.models import Formation
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||||
from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError
|
from app.scodoc.sco_exceptions import (
|
||||||
|
ScoValueError,
|
||||||
|
ScoLockedFormError,
|
||||||
|
ScoNonEmptyFormationObject,
|
||||||
|
)
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
|
|
||||||
_matiereEditor = ndb.EditableTable(
|
_matiereEditor = ndb.EditableTable(
|
||||||
@ -156,6 +161,16 @@ associé.
|
|||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
|
||||||
|
def can_delete_matiere(matiere: Matiere) -> tuple[bool, str]:
|
||||||
|
"True si la matiere n'est pas utilisée dans des formsemestre"
|
||||||
|
locked = matiere_is_locked(matiere.id)
|
||||||
|
if locked:
|
||||||
|
return False
|
||||||
|
if any(m.modimpls.all() for m in matiere.modules):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def do_matiere_delete(oid):
|
def do_matiere_delete(oid):
|
||||||
"delete matiere and attached modules"
|
"delete matiere and attached modules"
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
@ -165,17 +180,16 @@ def do_matiere_delete(oid):
|
|||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
# check
|
# check
|
||||||
mat = matiere_list({"matiere_id": oid})[0]
|
matiere = Matiere.query.get_or_404(oid)
|
||||||
|
mat = matiere_list({"matiere_id": oid})[0] # compat sco7
|
||||||
ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
|
ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
|
||||||
locked = matiere_is_locked(mat["matiere_id"])
|
if not can_delete_matiere(matiere):
|
||||||
if locked:
|
# il y a au moins un modimpl dans un module de cette matière
|
||||||
log("do_matiere_delete: mat=%s" % mat)
|
raise ScoNonEmptyFormationObject("Matière", matiere.titre)
|
||||||
log("do_matiere_delete: ue=%s" % ue)
|
|
||||||
log("do_matiere_delete: locked sems: %s" % locked)
|
log("do_matiere_delete: matiere_id=%s" % matiere.id)
|
||||||
raise ScoLockedFormError()
|
|
||||||
log("do_matiere_delete: matiere_id=%s" % oid)
|
|
||||||
# delete all modules in this matiere
|
# delete all modules in this matiere
|
||||||
mods = sco_edit_module.module_list({"matiere_id": oid})
|
mods = sco_edit_module.module_list({"matiere_id": matiere.id})
|
||||||
for mod in mods:
|
for mod in mods:
|
||||||
sco_edit_module.do_module_delete(mod["module_id"])
|
sco_edit_module.do_module_delete(mod["module_id"])
|
||||||
_matiereEditor.delete(cnx, oid)
|
_matiereEditor.delete(cnx, oid)
|
||||||
@ -194,11 +208,25 @@ def matiere_delete(matiere_id=None):
|
|||||||
"""Delete matière"""
|
"""Delete matière"""
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
|
|
||||||
M = matiere_list(args={"matiere_id": matiere_id})[0]
|
matiere = Matiere.query.get_or_404(matiere_id)
|
||||||
UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
|
if not can_delete_matiere(matiere):
|
||||||
|
# il y a au moins un modimpl dans un module de cette matière
|
||||||
|
raise ScoNonEmptyFormationObject(
|
||||||
|
"Matière",
|
||||||
|
matiere.titre,
|
||||||
|
dest_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
formation_id=matiere.ue.formation_id,
|
||||||
|
semestre_idx=matiere.ue.semestre_idx,
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
mat = matiere_list(args={"matiere_id": matiere_id})[0]
|
||||||
|
UE = sco_edit_ue.ue_list(args={"ue_id": mat["ue_id"]})[0]
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Suppression d'une matière"),
|
html_sco_header.sco_header(page_title="Suppression d'une matière"),
|
||||||
"<h2>Suppression de la matière %(titre)s" % M,
|
"<h2>Suppression de la matière %(titre)s" % mat,
|
||||||
" dans l'UE (%(acronyme)s))</h2>" % UE,
|
" dans l'UE (%(acronyme)s))</h2>" % UE,
|
||||||
]
|
]
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
@ -210,7 +238,7 @@ def matiere_delete(matiere_id=None):
|
|||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
(("matiere_id", {"input_type": "hidden"}),),
|
(("matiere_id", {"input_type": "hidden"}),),
|
||||||
initvalues=M,
|
initvalues=mat,
|
||||||
submitlabel="Confirmer la suppression",
|
submitlabel="Confirmer la suppression",
|
||||||
cancelbutton="Annuler",
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
|
@ -43,7 +43,12 @@ from app import models
|
|||||||
from app.models import Formation
|
from app.models import Formation
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError
|
from app.scodoc.sco_exceptions import (
|
||||||
|
ScoValueError,
|
||||||
|
ScoLockedFormError,
|
||||||
|
ScoGenError,
|
||||||
|
ScoNonEmptyFormationObject,
|
||||||
|
)
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_codes_parcours
|
from app.scodoc import sco_codes_parcours
|
||||||
from app.scodoc import sco_edit_matiere
|
from app.scodoc import sco_edit_matiere
|
||||||
@ -330,20 +335,37 @@ def module_create(matiere_id=None, module_type=None, semestre_id=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def can_delete_module(module):
|
||||||
|
"True si le module n'est pas utilisée dans des formsemestre"
|
||||||
|
return len(module.modimpls.all()) == 0
|
||||||
|
|
||||||
|
|
||||||
def do_module_delete(oid):
|
def do_module_delete(oid):
|
||||||
"delete module"
|
"delete module"
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
|
|
||||||
mod = module_list({"module_id": oid})[0]
|
module = Module.query.get_or_404(oid)
|
||||||
if module_is_locked(mod["module_id"]):
|
mod = module_list({"module_id": oid})[0] # sco7
|
||||||
|
if module_is_locked(module.id):
|
||||||
raise ScoLockedFormError()
|
raise ScoLockedFormError()
|
||||||
|
if not can_delete_module(module):
|
||||||
|
raise ScoNonEmptyFormationObject(
|
||||||
|
"Module",
|
||||||
|
msg=module.titre,
|
||||||
|
dest_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=module.formation_id,
|
||||||
|
semestre_idx=module.ue.semestre_idx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# S'il y a des moduleimpls, on ne peut pas detruire le module !
|
# S'il y a des moduleimpls, on ne peut pas detruire le module !
|
||||||
mods = sco_moduleimpl.moduleimpl_list(module_id=oid)
|
mods = sco_moduleimpl.moduleimpl_list(module_id=oid)
|
||||||
if mods:
|
if mods:
|
||||||
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
||||||
<p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
|
<p class="help">Il faut d'abord supprimer le semestre (ou en retirer ce module). Mais il est peut être préférable de
|
||||||
laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
|
laisser ce programme intact et d'en créer une nouvelle version pour la modifier sans affecter les semestres déjà en place.
|
||||||
</p>
|
</p>
|
||||||
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
|
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=mod["formation_id"])}">reprendre</a>
|
formation_id=mod["formation_id"])}">reprendre</a>
|
||||||
@ -365,12 +387,21 @@ def do_module_delete(oid):
|
|||||||
|
|
||||||
def module_delete(module_id=None):
|
def module_delete(module_id=None):
|
||||||
"""Delete a module"""
|
"""Delete a module"""
|
||||||
if not module_id:
|
module = Module.query.get_or_404(module_id)
|
||||||
raise ScoValueError("invalid module !")
|
mod = module_list(args={"module_id": module_id})[0] # sco7
|
||||||
modules = module_list(args={"module_id": module_id})
|
|
||||||
if not modules:
|
if not can_delete_module(module):
|
||||||
raise ScoValueError("Module inexistant !")
|
raise ScoNonEmptyFormationObject(
|
||||||
mod = modules[0]
|
"Module",
|
||||||
|
msg=module.titre,
|
||||||
|
dest_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=module.formation_id,
|
||||||
|
semestre_idx=module.ue.semestre_idx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
||||||
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % mod,
|
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % mod,
|
||||||
|
@ -42,7 +42,12 @@ from app import log
|
|||||||
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_exceptions import ScoValueError, ScoLockedFormError
|
from app.scodoc.sco_exceptions import (
|
||||||
|
ScoGenError,
|
||||||
|
ScoValueError,
|
||||||
|
ScoLockedFormError,
|
||||||
|
ScoNonEmptyFormationObject,
|
||||||
|
)
|
||||||
|
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
@ -130,64 +135,83 @@ def do_ue_create(args):
|
|||||||
return ue_id
|
return ue_id
|
||||||
|
|
||||||
|
|
||||||
|
def can_delete_ue(ue: UniteEns) -> bool:
|
||||||
|
"""True si l'UE n'est pas utilisée dans des formsemestre
|
||||||
|
et n'a pas de module rattachés
|
||||||
|
"""
|
||||||
|
# "pas un seul module de cette UE n'a de modimpl...""
|
||||||
|
return (not len(ue.modules.all())) and not any(m.modimpls.all() for m in ue.modules)
|
||||||
|
|
||||||
|
|
||||||
def do_ue_delete(ue_id, delete_validations=False, force=False):
|
def do_ue_delete(ue_id, delete_validations=False, force=False):
|
||||||
"delete UE and attached matieres (but not modules)"
|
"delete UE and attached matieres (but not modules)"
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc import sco_parcours_dut
|
from app.scodoc import sco_parcours_dut
|
||||||
|
|
||||||
|
ue = UniteEns.query.get_or_404(ue_id)
|
||||||
|
if not can_delete_ue(ue):
|
||||||
|
raise ScoNonEmptyFormationObject(
|
||||||
|
"UE",
|
||||||
|
msg=ue.titre,
|
||||||
|
dest_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=ue.formation_id,
|
||||||
|
semestre_idx=ue.semestre_idx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue_id, delete_validations))
|
log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue.id, delete_validations))
|
||||||
# check
|
# check
|
||||||
ue = ue_list({"ue_id": ue_id})
|
# if ue_is_locked(ue.id):
|
||||||
if not ue:
|
# raise ScoLockedFormError()
|
||||||
raise ScoValueError("UE inexistante !")
|
|
||||||
ue = ue[0]
|
|
||||||
if ue_is_locked(ue["ue_id"]):
|
|
||||||
raise ScoLockedFormError()
|
|
||||||
# Il y a-t-il des etudiants ayant validé cette UE ?
|
# Il y a-t-il des etudiants ayant validé cette UE ?
|
||||||
# si oui, propose de supprimer les validations
|
# si oui, propose de supprimer les validations
|
||||||
validations = sco_parcours_dut.scolar_formsemestre_validation_list(
|
validations = sco_parcours_dut.scolar_formsemestre_validation_list(
|
||||||
cnx, args={"ue_id": ue_id}
|
cnx, args={"ue_id": ue.id}
|
||||||
)
|
)
|
||||||
if validations and not delete_validations and not force:
|
if validations and not delete_validations and not force:
|
||||||
return scu.confirm_dialog(
|
return scu.confirm_dialog(
|
||||||
"<p>%d étudiants ont validé l'UE %s (%s)</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p>"
|
"<p>%d étudiants ont validé l'UE %s (%s)</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p>"
|
||||||
% (len(validations), ue["acronyme"], ue["titre"]),
|
% (len(validations), ue.acronyme, ue.titre),
|
||||||
dest_url="",
|
dest_url="",
|
||||||
target_variable="delete_validations",
|
target_variable="delete_validations",
|
||||||
cancel_url=url_for(
|
cancel_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=str(ue["formation_id"]),
|
formation_id=ue.formation_id,
|
||||||
|
semestre_idx=ue.semestre_idx,
|
||||||
),
|
),
|
||||||
parameters={"ue_id": ue_id, "dialog_confirmed": 1},
|
parameters={"ue_id": ue.id, "dialog_confirmed": 1},
|
||||||
)
|
)
|
||||||
if delete_validations:
|
if delete_validations:
|
||||||
log("deleting all validations of UE %s" % ue_id)
|
log("deleting all validations of UE %s" % ue.id)
|
||||||
ndb.SimpleQuery(
|
ndb.SimpleQuery(
|
||||||
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
|
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
|
||||||
{"ue_id": ue_id},
|
{"ue_id": ue.id},
|
||||||
)
|
)
|
||||||
|
|
||||||
# delete all matiere in this UE
|
# delete all matiere in this UE
|
||||||
mats = sco_edit_matiere.matiere_list({"ue_id": ue_id})
|
mats = sco_edit_matiere.matiere_list({"ue_id": ue.id})
|
||||||
for mat in mats:
|
for mat in mats:
|
||||||
sco_edit_matiere.do_matiere_delete(mat["matiere_id"])
|
sco_edit_matiere.do_matiere_delete(mat["matiere_id"])
|
||||||
# delete uecoef and events
|
# delete uecoef and events
|
||||||
ndb.SimpleQuery(
|
ndb.SimpleQuery(
|
||||||
"DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s",
|
"DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s",
|
||||||
{"ue_id": ue_id},
|
{"ue_id": ue.id},
|
||||||
)
|
)
|
||||||
ndb.SimpleQuery("DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id})
|
ndb.SimpleQuery("DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue.id})
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
_ueEditor.delete(cnx, ue_id)
|
_ueEditor.delete(cnx, ue.id)
|
||||||
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement utilisé: acceptable de tout invalider ?):
|
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement
|
||||||
|
# utilisé: acceptable de tout invalider):
|
||||||
sco_cache.invalidate_formsemestre()
|
sco_cache.invalidate_formsemestre()
|
||||||
# news
|
# news
|
||||||
F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0]
|
F = sco_formations.formation_list(args={"formation_id": ue.formation_id})[0]
|
||||||
sco_news.add(
|
sco_news.add(
|
||||||
typ=sco_news.NEWS_FORM,
|
typ=sco_news.NEWS_FORM,
|
||||||
object=ue["formation_id"],
|
object=ue.formation_id,
|
||||||
text="Modification de la formation %(acronyme)s" % F,
|
text="Modification de la formation %(acronyme)s" % F,
|
||||||
max_frequency=3,
|
max_frequency=3,
|
||||||
)
|
)
|
||||||
@ -197,11 +221,11 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue["formation_id"],
|
formation_id=ue.formation_id,
|
||||||
|
semestre_idx=ue.semestre_idx,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
return None
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def ue_create(formation_id=None):
|
def ue_create(formation_id=None):
|
||||||
@ -211,8 +235,6 @@ def ue_create(formation_id=None):
|
|||||||
|
|
||||||
def ue_edit(ue_id=None, create=False, formation_id=None):
|
def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||||
"""Modification ou création d'une UE"""
|
"""Modification ou création d'une UE"""
|
||||||
from app.scodoc import sco_formations
|
|
||||||
|
|
||||||
create = int(create)
|
create = int(create)
|
||||||
if not create:
|
if not create:
|
||||||
U = ue_list(args={"ue_id": ue_id})
|
U = ue_list(args={"ue_id": ue_id})
|
||||||
@ -444,24 +466,38 @@ def next_ue_numero(formation_id, semestre_id=None):
|
|||||||
|
|
||||||
def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
||||||
"""Delete an UE"""
|
"""Delete an UE"""
|
||||||
ues = ue_list(args={"ue_id": ue_id})
|
ue = UniteEns.query.get_or_404(ue_id)
|
||||||
if not ues:
|
if ue.modules.all():
|
||||||
raise ScoValueError("UE inexistante !")
|
raise ScoValueError(
|
||||||
ue = ues[0]
|
f"""Suppression de l'UE {ue.titre} impossible car
|
||||||
|
des modules (ou SAÉ ou ressources) lui sont rattachés."""
|
||||||
if not dialog_confirmed:
|
)
|
||||||
return scu.confirm_dialog(
|
if not can_delete_ue(ue):
|
||||||
"<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue,
|
raise ScoNonEmptyFormationObject(
|
||||||
dest_url="",
|
"UE",
|
||||||
parameters={"ue_id": ue_id},
|
msg=ue.titre,
|
||||||
cancel_url=url_for(
|
dest_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=str(ue["formation_id"]),
|
formation_id=ue.formation_id,
|
||||||
|
semestre_idx=ue.semestre_idx,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return do_ue_delete(ue_id, delete_validations=delete_validations)
|
if not dialog_confirmed:
|
||||||
|
return scu.confirm_dialog(
|
||||||
|
f"<h2>Suppression de l'UE {ue.titre} ({ue.acronyme})</h2>",
|
||||||
|
dest_url="",
|
||||||
|
parameters={"ue_id": ue.id},
|
||||||
|
cancel_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=ue.formation_id,
|
||||||
|
semestre_idx=ue.semestre_idx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return do_ue_delete(ue.id, delete_validations=delete_validations)
|
||||||
|
|
||||||
|
|
||||||
def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
|
def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
|
||||||
@ -1010,12 +1046,14 @@ def _ue_table_modules(
|
|||||||
H.append(arrow_none)
|
H.append(arrow_none)
|
||||||
im += 1
|
im += 1
|
||||||
if mod["nb_moduleimpls"] == 0 and editable:
|
if mod["nb_moduleimpls"] == 0 and editable:
|
||||||
H.append(
|
icon = delete_icon
|
||||||
'<a class="smallbutton" href="module_delete?module_id=%s">%s</a>'
|
|
||||||
% (mod["module_id"], delete_icon)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
H.append(delete_disabled_icon)
|
icon = delete_disabled_icon
|
||||||
|
H.append(
|
||||||
|
'<a class="smallbutton" href="module_delete?module_id=%s">%s</a>'
|
||||||
|
% (mod["module_id"], icon)
|
||||||
|
)
|
||||||
|
|
||||||
H.append("</span>")
|
H.append("</span>")
|
||||||
|
|
||||||
mod_editable = (
|
mod_editable = (
|
||||||
|
@ -52,7 +52,7 @@ class InvalidNoteValue(ScoException):
|
|||||||
# Exception qui stoque dest_url, utilisee dans Zope standard_error_message
|
# Exception qui stoque dest_url, utilisee dans Zope standard_error_message
|
||||||
class ScoValueError(ScoException):
|
class ScoValueError(ScoException):
|
||||||
def __init__(self, msg, dest_url=None):
|
def __init__(self, msg, dest_url=None):
|
||||||
ScoException.__init__(self, msg)
|
super().__init__(msg)
|
||||||
self.dest_url = dest_url
|
self.dest_url = dest_url
|
||||||
|
|
||||||
|
|
||||||
@ -72,20 +72,35 @@ class ScoConfigurationError(ScoValueError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ScoLockedFormError(ScoException):
|
class ScoLockedFormError(ScoValueError):
|
||||||
def __init__(self, msg=""):
|
"Modification d'une formation verrouillée"
|
||||||
|
|
||||||
|
def __init__(self, msg="", dest_url=None):
|
||||||
msg = (
|
msg = (
|
||||||
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
|
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
|
||||||
+ str(msg)
|
+ str(msg)
|
||||||
)
|
)
|
||||||
ScoException.__init__(self, msg)
|
super().__init__(msg=msg, dest_url=dest_url)
|
||||||
|
|
||||||
|
|
||||||
|
class ScoNonEmptyFormationObject(ScoValueError):
|
||||||
|
"""On ne peut pas supprimer un module/matiere ou UE si des formsemestre s'y réfèrent"""
|
||||||
|
|
||||||
|
def __init__(self, type_objet="objet'", msg="", dest_url=None):
|
||||||
|
msg = f"""<h3>{type_objet} "{msg}" utilisé dans des semestres: suppression impossible.</h3>
|
||||||
|
<p class="help">Il faut d'abord supprimer le semestre (ou en retirer ce {type_objet}).
|
||||||
|
Mais il est peut-être préférable de laisser ce programme intact et d'en créer une
|
||||||
|
nouvelle version pour la modifier sans affecter les semestres déjà en place.
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
super().__init__(msg=msg, dest_url=dest_url)
|
||||||
|
|
||||||
|
|
||||||
class ScoGenError(ScoException):
|
class ScoGenError(ScoException):
|
||||||
"exception avec affichage d'une page explicative ad-hoc"
|
"exception avec affichage d'une page explicative ad-hoc"
|
||||||
|
|
||||||
def __init__(self, msg=""):
|
def __init__(self, msg=""):
|
||||||
ScoException.__init__(self, msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class AccessDenied(ScoGenError):
|
class AccessDenied(ScoGenError):
|
||||||
@ -101,7 +116,7 @@ class APIInvalidParams(Exception):
|
|||||||
status_code = 400
|
status_code = 400
|
||||||
|
|
||||||
def __init__(self, message, status_code=None, payload=None):
|
def __init__(self, message, status_code=None, payload=None):
|
||||||
Exception.__init__(self)
|
super().__init__()
|
||||||
self.message = message
|
self.message = message
|
||||||
if status_code is not None:
|
if status_code is not None:
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
|
@ -198,10 +198,13 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
NB_SEM = parcours.NB_SEM
|
NB_SEM = parcours.NB_SEM
|
||||||
else:
|
else:
|
||||||
NB_SEM = 10 # fallback, max 10 semestres
|
NB_SEM = 10 # fallback, max 10 semestres
|
||||||
semestre_id_list = [-1] + list(range(1, NB_SEM + 1))
|
if NB_SEM == 1:
|
||||||
|
semestre_id_list = [-1]
|
||||||
|
else:
|
||||||
|
semestre_id_list = [-1] + list(range(1, NB_SEM + 1))
|
||||||
semestre_id_labels = []
|
semestre_id_labels = []
|
||||||
for sid in semestre_id_list:
|
for sid in semestre_id_list:
|
||||||
if sid == "-1":
|
if sid == -1:
|
||||||
semestre_id_labels.append("pas de semestres")
|
semestre_id_labels.append("pas de semestres")
|
||||||
else:
|
else:
|
||||||
semestre_id_labels.append(f"S{sid}")
|
semestre_id_labels.append(f"S{sid}")
|
||||||
@ -329,6 +332,8 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
"labels": modalites_titles,
|
"labels": modalites_titles,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
]
|
||||||
|
modform.append(
|
||||||
(
|
(
|
||||||
"semestre_id",
|
"semestre_id",
|
||||||
{
|
{
|
||||||
@ -338,7 +343,7 @@ def do_formsemestre_createwithmodules(edit=False):
|
|||||||
"labels": semestre_id_labels,
|
"labels": semestre_id_labels,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
)
|
||||||
etapes = sco_portal_apogee.get_etapes_apogee_dept()
|
etapes = sco_portal_apogee.get_etapes_apogee_dept()
|
||||||
# Propose les etapes renvoyées par le portail
|
# Propose les etapes renvoyées par le portail
|
||||||
# et ajoute les étapes du semestre qui ne sont pas dans la liste (soit la liste a changé, soit l'étape a été ajoutée manuellement)
|
# et ajoute les étapes du semestre qui ne sont pas dans la liste (soit la liste a changé, soit l'étape a été ajoutée manuellement)
|
||||||
|
@ -49,16 +49,12 @@ from app.scodoc import sco_etud
|
|||||||
from app.scodoc import sco_excel
|
from app.scodoc import sco_excel
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_formsemestre_status
|
|
||||||
from app.scodoc import sco_parcours_dut
|
from app.scodoc import sco_parcours_dut
|
||||||
from app.scodoc import sco_pdf
|
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
import sco_version
|
import sco_version
|
||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app import log
|
from app import log
|
||||||
from app.scodoc.sco_codes_parcours import code_semestre_validant
|
from app.scodoc.sco_codes_parcours import code_semestre_validant
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
|
||||||
from app.scodoc.sco_pdf import SU
|
|
||||||
|
|
||||||
MAX_ETUD_IN_DESCR = 20
|
MAX_ETUD_IN_DESCR = 20
|
||||||
|
|
||||||
@ -121,9 +117,9 @@ def _categories_and_results(etuds, category, result):
|
|||||||
categories[etud[category]] = True
|
categories[etud[category]] = True
|
||||||
results[etud[result]] = True
|
results[etud[result]] = True
|
||||||
categories = list(categories.keys())
|
categories = list(categories.keys())
|
||||||
categories.sort()
|
categories.sort(key=scu.heterogeneous_sorting_key)
|
||||||
results = list(results.keys())
|
results = list(results.keys())
|
||||||
results.sort()
|
results.sort(key=scu.heterogeneous_sorting_key)
|
||||||
return categories, results
|
return categories, results
|
||||||
|
|
||||||
|
|
||||||
@ -166,7 +162,7 @@ def _results_by_category(
|
|||||||
l["sumpercent"] = "%2.1f%%" % ((100.0 * l["sum"]) / tot)
|
l["sumpercent"] = "%2.1f%%" % ((100.0 * l["sum"]) / tot)
|
||||||
#
|
#
|
||||||
codes = list(results.keys())
|
codes = list(results.keys())
|
||||||
codes.sort()
|
codes.sort(key=scu.heterogeneous_sorting_key)
|
||||||
|
|
||||||
bottom_titles = []
|
bottom_titles = []
|
||||||
if C: # ligne du bas avec totaux:
|
if C: # ligne du bas avec totaux:
|
||||||
@ -314,7 +310,7 @@ def formsemestre_report_counts(
|
|||||||
"type_admission",
|
"type_admission",
|
||||||
"boursier_prec",
|
"boursier_prec",
|
||||||
]
|
]
|
||||||
keys.sort()
|
keys.sort(key=scu.heterogeneous_sorting_key)
|
||||||
F = [
|
F = [
|
||||||
"""<form name="f" method="get" action="%s"><p>
|
"""<form name="f" method="get" action="%s"><p>
|
||||||
Colonnes: <select name="result" onchange="document.f.submit()">"""
|
Colonnes: <select name="result" onchange="document.f.submit()">"""
|
||||||
@ -497,7 +493,7 @@ def table_suivi_cohorte(
|
|||||||
P.append(p)
|
P.append(p)
|
||||||
|
|
||||||
# 4-- regroupe par indice de semestre S_i
|
# 4-- regroupe par indice de semestre S_i
|
||||||
indices_sems = list(set([s["semestre_id"] for s in sems]))
|
indices_sems = list({s["semestre_id"] for s in sems})
|
||||||
indices_sems.sort()
|
indices_sems.sort()
|
||||||
for p in P:
|
for p in P:
|
||||||
p.nb_etuds = 0 # nombre total d'etudiants dans la periode
|
p.nb_etuds = 0 # nombre total d'etudiants dans la periode
|
||||||
@ -788,9 +784,9 @@ def _gen_form_selectetuds(
|
|||||||
):
|
):
|
||||||
"""HTML form pour choix criteres selection etudiants"""
|
"""HTML form pour choix criteres selection etudiants"""
|
||||||
bacs = list(bacs)
|
bacs = list(bacs)
|
||||||
bacs.sort()
|
bacs.sort(key=scu.heterogeneous_sorting_key)
|
||||||
bacspecialites = list(bacspecialites)
|
bacspecialites = list(bacspecialites)
|
||||||
bacspecialites.sort()
|
bacspecialites.sort(key=scu.heterogeneous_sorting_key)
|
||||||
# on peut avoir un mix de chaines vides et d'entiers:
|
# on peut avoir un mix de chaines vides et d'entiers:
|
||||||
annee_bacs = [int(x) if x else 0 for x in annee_bacs]
|
annee_bacs = [int(x) if x else 0 for x in annee_bacs]
|
||||||
annee_bacs.sort()
|
annee_bacs.sort()
|
||||||
|
@ -93,6 +93,7 @@ MODULE_TYPE_NAMES = {
|
|||||||
ModuleType.MALUS: "Malus",
|
ModuleType.MALUS: "Malus",
|
||||||
ModuleType.RESSOURCE: "Ressource",
|
ModuleType.RESSOURCE: "Ressource",
|
||||||
ModuleType.SAE: "SAÉ",
|
ModuleType.SAE: "SAÉ",
|
||||||
|
None: "Module",
|
||||||
}
|
}
|
||||||
|
|
||||||
MALUS_MAX = 20.0
|
MALUS_MAX = 20.0
|
||||||
@ -897,6 +898,11 @@ def sort_dates(L, reverse=False):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def heterogeneous_sorting_key(x):
|
||||||
|
"key to sort non homogeneous sequences"
|
||||||
|
return (float(x), "") if isinstance(x, (bool, float, int)) else (-1e34, str(x))
|
||||||
|
|
||||||
|
|
||||||
def query_portal(req, msg="Portail Apogee", timeout=3):
|
def query_portal(req, msg="Portail Apogee", timeout=3):
|
||||||
"""Retreives external data using HTTP request
|
"""Retreives external data using HTTP request
|
||||||
(used to connect to Apogee portal, or ScoDoc server)
|
(used to connect to Apogee portal, or ScoDoc server)
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
Prénom : {{ contact.prenom }}<br>
|
Prénom : {{ contact.prenom }}<br>
|
||||||
Téléphone : {{ contact.telephone }}<br>
|
Téléphone : {{ contact.telephone }}<br>
|
||||||
Mail : {{ contact.mail }}<br>
|
Mail : {{ contact.mail }}<br>
|
||||||
|
{% if contact.poste %}
|
||||||
|
Poste : {{ contact.poste }}<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if contact.service %}
|
||||||
|
Service : {{ contact.service }}<br>
|
||||||
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div style="margin-bottom: 10px;">
|
<div style="margin-bottom: 10px;">
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
<th>Prenom</th>
|
<th>Prenom</th>
|
||||||
<th>Telephone</th>
|
<th>Telephone</th>
|
||||||
<th>Mail</th>
|
<th>Mail</th>
|
||||||
|
<th>Poste</th>
|
||||||
|
<th>Service</th>
|
||||||
<th>Entreprise</th>
|
<th>Entreprise</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for contact in contacts %}
|
{% for contact in contacts %}
|
||||||
@ -30,6 +32,8 @@
|
|||||||
<th>{{ contact[0].prenom }}</th>
|
<th>{{ contact[0].prenom }}</th>
|
||||||
<th>{{ contact[0].telephone }}</th>
|
<th>{{ contact[0].telephone }}</th>
|
||||||
<th>{{ contact[0].mail }}</th>
|
<th>{{ contact[0].mail }}</th>
|
||||||
|
<th>{{ contact[0].poste}}</th>
|
||||||
|
<th>{{ contact[0].service}}</th>
|
||||||
<th><a href="{{ url_for('entreprises.fiche_entreprise', id=contact[1].id) }}">{{ contact[1].nom }}</a></th>
|
<th><a href="{{ url_for('entreprises.fiche_entreprise', id=contact[1].id) }}">{{ contact[1].nom }}</a></th>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -41,6 +45,7 @@
|
|||||||
<div>
|
<div>
|
||||||
{% if contacts %}
|
{% if contacts %}
|
||||||
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts') }}">Exporter la liste des contacts</a>
|
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts') }}">Exporter la liste des contacts</a>
|
||||||
|
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts_bis') }}">Exporter la liste des contacts avec leur entreprise</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
<span style="margin-right: 10px;">{{ data[0].date_debut.strftime('%d/%m/%Y') }} - {{
|
<span style="margin-right: 10px;">{{ data[0].date_debut.strftime('%d/%m/%Y') }} - {{
|
||||||
data[0].date_fin.strftime('%d/%m/%Y') }}</span>
|
data[0].date_fin.strftime('%d/%m/%Y') }}</span>
|
||||||
<span style="margin-right: 10px;">
|
<span style="margin-right: 10px;">
|
||||||
{{ data[0].type_offre }} réalisé par {{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }} en
|
{{ data[0].type_offre }} réalisé par {{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}
|
||||||
{{ data[0].formation_text }}
|
{% if data[0].formation_text %} en {{ data[0].formation_text }}{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -24,14 +24,11 @@
|
|||||||
{{icons.arrow_none|safe}}
|
{{icons.arrow_none|safe}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% if editable and not ue.modules.count() %}
|
|
||||||
<a class="smallbutton" href="{{ url_for('notes.ue_delete',
|
<a class="smallbutton" href="{{ url_for('notes.ue_delete',
|
||||||
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
}}">{{icons.delete|safe}}</a>
|
}}">{% if editable and not ue.modules.count() %}{{icons.delete|safe}}{% else %}{{icons.delete_disabled|safe}}{% endif %}</a>
|
||||||
{% else %}
|
|
||||||
{{icons.delete_disabled|safe}}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<b>{{ue.acronyme}}</b> <a class="discretelink" href="{{
|
<b>{{ue.acronyme}}</b> <a class="discretelink" href="{{
|
||||||
url_for('notes.ue_infos', scodoc_dept=g.scodoc_dept, ue_id=ue.id)}}"
|
url_for('notes.ue_infos', scodoc_dept=g.scodoc_dept, ue_id=ue.id)}}"
|
||||||
>{{ue.titre}}</a>
|
>{{ue.titre}}</a>
|
||||||
|
@ -8,10 +8,9 @@
|
|||||||
|
|
||||||
{{ exc | safe }}
|
{{ exc | safe }}
|
||||||
|
|
||||||
<p class="footer">
|
<p>
|
||||||
{% if g.scodoc_dept %}
|
{% if g.scodoc_dept %}
|
||||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">retour page d'accueil
|
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
|
||||||
departement {{ g.scodoc_dept }}</a>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
|
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -13,11 +13,23 @@
|
|||||||
|
|
||||||
<ul class="main">
|
<ul class="main">
|
||||||
{% for dept in depts %}
|
{% for dept in depts %}
|
||||||
|
{% if dept.visible or current_user.is_administrator() %}
|
||||||
<li>
|
<li>
|
||||||
<a class="stdlink {{'link_accessible' if current_user.has_permission(Permission.ScoView, dept=dept.acronym) else 'link_unauthorized'}}"
|
<a class="stdlink {{'link_accessible' if current_user.has_permission(Permission.ScoView, dept=dept.acronym) else 'link_unauthorized'}}"
|
||||||
href="{{url_for('scolar.index_html', scodoc_dept=dept.acronym)}}">Département
|
href="{{url_for('scolar.index_html', scodoc_dept=dept.acronym)}}">Département
|
||||||
{{dept.preferences.filter_by(name="DeptName").first().value}}</a>
|
{{dept.preferences.filter_by(name="DeptName").first().value}}
|
||||||
|
{{ dept.preferences.filter_by( name="DeptFullName" ).first().value or "" }}
|
||||||
|
</a>
|
||||||
|
{% if current_user.is_administrator() %}
|
||||||
|
<span class="dept_visibility">
|
||||||
|
{% if dept.visible %}visible{% else %}caché aux utilisateurs{% endif %}
|
||||||
|
<a href="{{ url_for('scodoc.toggle_dept_vis', dept_id=dept.id) }}">
|
||||||
|
{% if dept.visible %}cacher{% else %}rendre visible{% endif %}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<li>
|
<li>
|
||||||
<b>Aucun département défini !</b>
|
<b>Aucun département défini !</b>
|
||||||
|
@ -53,6 +53,7 @@ from wtforms.fields.simple import BooleanField, StringField, TextAreaField, Hidd
|
|||||||
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
||||||
|
|
||||||
import app
|
import app
|
||||||
|
from app import db
|
||||||
from app.forms.main import config_forms
|
from app.forms.main import config_forms
|
||||||
from app.forms.main.create_dept import CreateDeptForm
|
from app.forms.main.create_dept import CreateDeptForm
|
||||||
from app.models import Departement, Identite
|
from app.models import Departement, Identite
|
||||||
@ -82,9 +83,7 @@ from PIL import Image as PILImage
|
|||||||
@bp.route("/ScoDoc/index")
|
@bp.route("/ScoDoc/index")
|
||||||
def index():
|
def index():
|
||||||
"Page d'accueil: liste des départements"
|
"Page d'accueil: liste des départements"
|
||||||
depts = (
|
depts = Departement.query.filter_by().order_by(Departement.acronym).all()
|
||||||
Departement.query.filter_by(visible=True).order_by(Departement.acronym).all()
|
|
||||||
)
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"scodoc.html",
|
"scodoc.html",
|
||||||
title=sco_version.SCONAME,
|
title=sco_version.SCONAME,
|
||||||
@ -108,7 +107,11 @@ def create_dept():
|
|||||||
if request.method == "POST" and form.cancel.data: # cancel button
|
if request.method == "POST" and form.cancel.data: # cancel button
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
departements.create_dept(form.acronym.data)
|
departements.create_dept(
|
||||||
|
form.acronym.data,
|
||||||
|
visible=form.visible.data,
|
||||||
|
# description=form.description.data,
|
||||||
|
)
|
||||||
flash(f"Département {form.acronym.data} créé.")
|
flash(f"Département {form.acronym.data} créé.")
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -118,6 +121,17 @@ def create_dept():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/ScoDoc/toggle_dept_vis/<dept_id>", methods=["GET", "POST"])
|
||||||
|
@admin_required
|
||||||
|
def toggle_dept_vis(dept_id):
|
||||||
|
"""Cache ou rend visible un dept"""
|
||||||
|
dept = Departement.query.get_or_404(dept_id)
|
||||||
|
dept.visible = not dept.visible
|
||||||
|
db.session.add(dept)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(url_for("scodoc.index"))
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/ScoDoc/table_etud_in_accessible_depts", methods=["POST"])
|
@bp.route("/ScoDoc/table_etud_in_accessible_depts", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def table_etud_in_accessible_depts():
|
def table_etud_in_accessible_depts():
|
||||||
|
@ -152,7 +152,6 @@ def user_info(user_name, format="json"):
|
|||||||
def create_user_form(user_name=None, edit=0, all_roles=1):
|
def create_user_form(user_name=None, edit=0, all_roles=1):
|
||||||
"form. création ou edition utilisateur"
|
"form. création ou edition utilisateur"
|
||||||
auth_dept = current_user.dept
|
auth_dept = current_user.dept
|
||||||
auth_username = current_user.user_name
|
|
||||||
from_mail = current_user.email
|
from_mail = current_user.email
|
||||||
initvalues = {}
|
initvalues = {}
|
||||||
edit = int(edit)
|
edit = int(edit)
|
||||||
@ -204,7 +203,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
|
|||||||
administrable_dept_acronyms = sorted(
|
administrable_dept_acronyms = sorted(
|
||||||
set(
|
set(
|
||||||
[
|
[
|
||||||
x.dept
|
x.dept or ""
|
||||||
for x in UserRole.query.filter_by(user=current_user)
|
for x in UserRole.query.filter_by(user=current_user)
|
||||||
if x.role.has_permission(Permission.ScoUsersAdmin) and x.dept
|
if x.role.has_permission(Permission.ScoUsersAdmin) and x.dept
|
||||||
]
|
]
|
||||||
@ -249,7 +248,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
|
|||||||
r.name + "_" + (dept or "") for (r, dept) in displayed_roles
|
r.name + "_" + (dept or "") for (r, dept) in displayed_roles
|
||||||
]
|
]
|
||||||
displayed_roles_labels = [f"{dept}: {r.name}" for (r, dept) in displayed_roles]
|
displayed_roles_labels = [f"{dept}: {r.name}" for (r, dept) in displayed_roles]
|
||||||
disabled_roles = {} # pour desactiver les roles que l'on ne peut pas editer
|
disabled_roles = {} # pour désactiver les roles que l'on ne peut pas éditer
|
||||||
for i in range(len(displayed_roles_strings)):
|
for i in range(len(displayed_roles_strings)):
|
||||||
if displayed_roles_strings[i] not in editable_roles_strings:
|
if displayed_roles_strings[i] not in editable_roles_strings:
|
||||||
disabled_roles[i] = True
|
disabled_roles[i] = True
|
||||||
@ -375,7 +374,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
|
|||||||
can_choose_dept = True
|
can_choose_dept = True
|
||||||
else:
|
else:
|
||||||
selectable_dept_acronyms = set(administrable_dept_acronyms)
|
selectable_dept_acronyms = set(administrable_dept_acronyms)
|
||||||
if edit: # ajoute dept actuel de l'utilisateur
|
if edit and the_user.dept is not None: # ajoute dept actuel de l'utilisateur
|
||||||
selectable_dept_acronyms |= {the_user.dept}
|
selectable_dept_acronyms |= {the_user.dept}
|
||||||
if len(selectable_dept_acronyms) > 1:
|
if len(selectable_dept_acronyms) > 1:
|
||||||
can_choose_dept = True
|
can_choose_dept = True
|
||||||
@ -389,6 +388,9 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
|
|||||||
"explanation": """département de rattachement de l'utilisateur""",
|
"explanation": """département de rattachement de l'utilisateur""",
|
||||||
"labels": selectable_dept_acronyms,
|
"labels": selectable_dept_acronyms,
|
||||||
"allowed_values": selectable_dept_acronyms,
|
"allowed_values": selectable_dept_acronyms,
|
||||||
|
"default": g.scodoc_dept
|
||||||
|
if g.scodoc_dept in selectable_dept_acronyms
|
||||||
|
else "",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -97,6 +97,33 @@ def upgrade():
|
|||||||
sa.PrimaryKeyConstraint("id"),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
"entreprise_envoi_offre_etudiant",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("sender_id", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("receiver_id", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("offre_id", sa.Integer(), nullable=True),
|
||||||
|
sa.Column(
|
||||||
|
"date_envoi",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("now()"),
|
||||||
|
nullable=True,
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["offre_id"],
|
||||||
|
["entreprise_offre.id"],
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["sender_id"],
|
||||||
|
["user.id"],
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["receiver_id"],
|
||||||
|
["identite.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
|
||||||
op.drop_constraint(
|
op.drop_constraint(
|
||||||
"entreprise_contact_entreprise_corresp_id_fkey",
|
"entreprise_contact_entreprise_corresp_id_fkey",
|
||||||
"entreprise_contact",
|
"entreprise_contact",
|
||||||
@ -249,6 +276,7 @@ def downgrade():
|
|||||||
op.drop_column("entreprise_contact", "nom")
|
op.drop_column("entreprise_contact", "nom")
|
||||||
|
|
||||||
op.drop_table("entreprise_envoi_offre")
|
op.drop_table("entreprise_envoi_offre")
|
||||||
|
op.drop_table("entreprise_envoi_offre_etudiant")
|
||||||
op.drop_table("entreprise_offre")
|
op.drop_table("entreprise_offre")
|
||||||
op.drop_table("entreprise_etudiant")
|
op.drop_table("entreprise_etudiant")
|
||||||
op.drop_table("entreprise_log")
|
op.drop_table("entreprise_log")
|
||||||
|
@ -29,7 +29,6 @@ from app.models import ModuleImpl, ModuleImplInscription
|
|||||||
from app.models import Identite
|
from app.models import Identite
|
||||||
from app.models import departements
|
from app.models import departements
|
||||||
from app.models.evaluations import Evaluation
|
from app.models.evaluations import Evaluation
|
||||||
from app.scodoc.sco_etud import identite_create
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.views import notes, scolar
|
from app.views import notes, scolar
|
||||||
import tools
|
import tools
|
||||||
@ -248,6 +247,7 @@ def edit_role(rolename, addpermissionname=None, removepermissionname=None): # e
|
|||||||
db.session.add(role)
|
db.session.add(role)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
@click.argument("rolename")
|
@click.argument("rolename")
|
||||||
def delete_role(rolename):
|
def delete_role(rolename):
|
||||||
@ -259,6 +259,7 @@ def delete_role(rolename):
|
|||||||
db.session.delete(role)
|
db.session.delete(role)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
@click.argument("username")
|
@click.argument("username")
|
||||||
@click.option("-d", "--dept", "dept_acronym")
|
@click.option("-d", "--dept", "dept_acronym")
|
||||||
|
@ -42,17 +42,17 @@
|
|||||||
# - do_formation_delete
|
# - do_formation_delete
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import xml.dom.minidom
|
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import g
|
from flask import g
|
||||||
|
import pytest
|
||||||
from tests.unit import sco_fake_gen
|
from tests.unit import sco_fake_gen
|
||||||
|
|
||||||
from app.scodoc import sco_edit_formation
|
from app.scodoc import sco_edit_formation
|
||||||
from app.scodoc import sco_edit_matiere
|
from app.scodoc import sco_edit_matiere
|
||||||
from app.scodoc import sco_edit_module
|
from app.scodoc import sco_edit_module
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
|
from app.scodoc import sco_exceptions
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc import sco_formsemestre_edit
|
from app.scodoc import sco_formsemestre_edit
|
||||||
from app.scodoc import sco_moduleimpl
|
from app.scodoc import sco_moduleimpl
|
||||||
@ -273,31 +273,31 @@ def test_formations(test_client):
|
|||||||
|
|
||||||
# --- Suppression du module, matiere et ue test du semestre 2
|
# --- Suppression du module, matiere et ue test du semestre 2
|
||||||
|
|
||||||
# on doit d'abbord supprimer le semestre
|
# on doit d'abord supprimer le semestre:
|
||||||
|
|
||||||
# sco_formsemestre_edit.formsemestre_delete( formsemestre_id=sem2["formsemestre_id"])
|
|
||||||
# sco_formsemestre_edit.formsemestre_createwithmodules( formsemestre_id=sem2["formsemestre_id"])
|
|
||||||
|
|
||||||
# RIEN NE SE PASSE AVEC CES FONCTIONS
|
|
||||||
|
|
||||||
sco_formsemestre_edit.do_formsemestre_delete(
|
sco_formsemestre_edit.do_formsemestre_delete(
|
||||||
formsemestre_id=sem2["formsemestre_id"]
|
formsemestre_id=sem2["formsemestre_id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# sco_edit_module.module_delete( module_id=modt["module_id"])
|
|
||||||
# sco_edit_matiere.matiere_delete( matiere_id=matt["matiere_id"])
|
|
||||||
# sco_edit_ue.ue_delete( ue_id=uet["ue_id"])
|
|
||||||
|
|
||||||
# RIEN NE SE PASSE AVEC CES FONCTIONS
|
|
||||||
|
|
||||||
li_module = sco_edit_module.module_list()
|
li_module = sco_edit_module.module_list()
|
||||||
assert len(li_module) == 4
|
assert len(li_module) == 4
|
||||||
sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre
|
# Suppression impossible car utilisé dans le semestre semt:
|
||||||
# sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]])
|
with pytest.raises(sco_exceptions.ScoNonEmptyFormationObject):
|
||||||
# deuxieme methode de supression d'un module
|
sco_edit_module.module_delete(module_id=mi3["module_id"])
|
||||||
li_module2 = sco_edit_module.module_list()
|
|
||||||
|
|
||||||
assert len(li_module2) == 3 # verification de la suppression du module
|
sco_formsemestre_edit.do_formsemestre_delete(semt["formsemestre_id"])
|
||||||
|
|
||||||
|
li_module2_before = sco_edit_module.module_list()
|
||||||
|
|
||||||
|
sco_edit_module.do_module_delete(mi3["module_id"])
|
||||||
|
sco_edit_module.do_module_delete(modt["module_id"])
|
||||||
|
|
||||||
|
# deuxieme methode de supression d'un module
|
||||||
|
li_module2_after = sco_edit_module.module_list()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
len(li_module2_after) == len(li_module2_before) - 2
|
||||||
|
) # verification de la suppression
|
||||||
|
|
||||||
lim_sem2 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem2["formsemestre_id"])
|
lim_sem2 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem2["formsemestre_id"])
|
||||||
|
|
||||||
@ -316,10 +316,6 @@ def test_formations(test_client):
|
|||||||
assert len(li_ue2) == 3 # verification de la suppression de l'UE
|
assert len(li_ue2) == 3 # verification de la suppression de l'UE
|
||||||
|
|
||||||
# --- Suppression d'une formation
|
# --- Suppression d'une formation
|
||||||
# Il faut d'abbord supprimer le semestre aussi.
|
|
||||||
sco_formsemestre_edit.do_formsemestre_delete(
|
|
||||||
formsemestre_id=semt["formsemestre_id"]
|
|
||||||
)
|
|
||||||
|
|
||||||
sco_edit_formation.do_formation_delete(oid=f2["formation_id"])
|
sco_edit_formation.do_formation_delete(oid=f2["formation_id"])
|
||||||
lif3 = notes.formation_list(format="json").get_data(as_text=True)
|
lif3 = notes.formation_list(format="json").get_data(as_text=True)
|
||||||
|
@ -64,7 +64,7 @@ fi
|
|||||||
|
|
||||||
# ------------ LIEN VERS .env
|
# ------------ LIEN VERS .env
|
||||||
# Pour conserver le .env entre les mises à jour, on le génère dans
|
# Pour conserver le .env entre les mises à jour, on le génère dans
|
||||||
# /opt/scodoc-data/;env et on le lie:
|
# /opt/scodoc-data/.env et on le lie:
|
||||||
if [ ! -e "$SCODOC_DIR/.env" ] && [ ! -L "$SCODOC_DIR/.env" ]
|
if [ ! -e "$SCODOC_DIR/.env" ] && [ ! -L "$SCODOC_DIR/.env" ]
|
||||||
then
|
then
|
||||||
ln -s "$SCODOC_VAR_DIR/.env" "$SCODOC_DIR"
|
ln -s "$SCODOC_VAR_DIR/.env" "$SCODOC_DIR"
|
||||||
|
@ -24,53 +24,74 @@ usage() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# analyse de la ligne de commande
|
||||||
|
# calcule:
|
||||||
|
# SRC = fichier source de la restauration
|
||||||
|
# DB_DEST = base de données destination
|
||||||
|
# KEEP_ENV = vide si restauration à l'identique (i.e. production)
|
||||||
if (($# < 1 || $# > 2))
|
if (($# < 1 || $# > 2))
|
||||||
then
|
then
|
||||||
usage
|
usage
|
||||||
elif [ $# -eq 2 -a $1 != '--keep-env' -a $2 != '--keep-env' ] ; then
|
elif [ $# -eq 2 ] && [ "$1" != "--keep-env" ] && [ "$2" != "--keep-env" ]
|
||||||
|
then
|
||||||
usage
|
usage
|
||||||
elif [ $# -eq 1 ] ; then
|
elif [ $# -eq 1 ]
|
||||||
|
then
|
||||||
echo "restauration des données et de la configuration originale (production)"
|
echo "restauration des données et de la configuration originale (production)"
|
||||||
SRC=$1
|
SRC="$1"
|
||||||
DB_DEST="SCODOC"
|
DB_DEST="SCODOC"
|
||||||
else
|
else
|
||||||
echo "restauration des données dans la configuration actuelle"
|
echo "restauration des données dans la configuration actuelle"
|
||||||
DB_CURRENT=$(su -c "(cd $SCODOC_DIR && source venv/bin/activate && flask scodoc-database -n)")
|
DB_CURRENT=$(su -c "(cd $SCODOC_DIR && source venv/bin/activate && flask scodoc-database -n)")
|
||||||
DB_DEST="$DB_CURRENT"
|
DB_DEST="$DB_CURRENT"
|
||||||
KEEP=1
|
KEEP_ENV="Y"
|
||||||
if [ $1 = '--keep-env' ]; then
|
if [ "$1" = "--keep-env" ]
|
||||||
SRC=$2
|
then
|
||||||
|
SRC="$2"
|
||||||
else
|
else
|
||||||
SRC=$1
|
SRC="$1"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
DB_DUMP="${SCODOC_VAR_DIR}"/SCODOC.dump
|
DB_DUMP="${SCODOC_VAR_DIR}"/SCODOC.dump
|
||||||
|
|
||||||
# Safety check
|
# Safety check
|
||||||
echo "Ce script va remplacer les donnees de votre installation ScoDoc par celles"
|
echo "Ce script va remplacer les données de votre installation ScoDoc par celles"
|
||||||
echo "enregistrées dans le fichier fourni."
|
echo "enregistrées dans le fichier fourni."
|
||||||
echo "Ce fichier doit avoir ete cree par le script save_scodoc9_data.sh."
|
echo "Ce fichier doit avoir été créé par le script save_scodoc9_data.sh."
|
||||||
echo
|
echo
|
||||||
echo "Attention: TOUTES LES DONNEES DE CE SCODOC SERONT REMPLACEES !"
|
echo "Attention: TOUTES LES DONNEES DE CE SCODOC SERONT REMPLACEES !"
|
||||||
echo "Notamment, tous les utilisateurs et departements existants seront effaces !"
|
echo "Notamment, tous les utilisateurs et départements existants seront effacés !"
|
||||||
echo
|
echo
|
||||||
echo "La base SQL $DB_CURRENT sera effacée et remplacée !!!"
|
echo "La base SQL $DB_CURRENT sera effacée et remplacée !!!"
|
||||||
echo
|
echo
|
||||||
echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]"
|
# Préparation si une copie 'antique' doit être effacée, demander confirmation, puis effacer
|
||||||
read -r ans
|
SCODOC_VAR_OLD=${SCODOC_VAR_DIR}.old
|
||||||
if [ ! "$(norm_ans "$ans")" = 'Y' ]
|
if [ -e "$SCODOC_VAR_DIR" ] && [ -e "$SCODOC_VAR_OLD" ]
|
||||||
then
|
then
|
||||||
echo "Annulation"
|
echo "Une ancienne sauvegarde (\"$SCODOC_VAR_OLD\" en date du $(stat -c %w "$SCODOC_VAR_OLD") ) va être effacée."
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
if [ -n "$KEEP_ENV" ]
|
||||||
|
then
|
||||||
|
echo -n "Restauration des données sans changement de configuration: Assurez-vous d'avoir arrêté le serveur scodoc."
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "Voulez-vous poursuivre la restauration ? (y/n) [n]"
|
||||||
|
read -r ans
|
||||||
|
if [ ! "$(norm_ans "$ans")" = "Y" ]
|
||||||
|
then
|
||||||
|
echo "Annulation de la restauration par l\'utilisateur"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
rm -rf "$SCODOC_VAR_OLD" || die "Erreur suppression $SCODOC_VAR_OLD"
|
||||||
|
|
||||||
# -- Stop ScoDoc
|
# -- Stop ScoDoc
|
||||||
if [ $KEEP -ne 1 ]; then
|
if [ -z "$KEEP_ENV" ]
|
||||||
|
then
|
||||||
echo "Arrêt de scodoc9..."
|
echo "Arrêt de scodoc9..."
|
||||||
systemctl stop scodoc9
|
systemctl stop scodoc9
|
||||||
else
|
|
||||||
echo -n "Assurez-vous d'avoir arrété le serveur scodoc (validez pour continuer)"
|
|
||||||
read ans
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clear caches
|
# Clear caches
|
||||||
@ -86,7 +107,7 @@ fi
|
|||||||
|
|
||||||
# -- Ouverture archive
|
# -- Ouverture archive
|
||||||
echo "Ouverture archive $SRC..."
|
echo "Ouverture archive $SRC..."
|
||||||
(cd $(dirname "$SCODOC_VAR_DIR"); tar xfz "$SRC") || die "Error opening archive"
|
(cd "$(dirname "$SCODOC_VAR_DIR")"; tar xfz "$SRC") || die "Error opening archive"
|
||||||
|
|
||||||
# -- Ckeck/fix owner
|
# -- Ckeck/fix owner
|
||||||
echo "Vérification du propriétaire..."
|
echo "Vérification du propriétaire..."
|
||||||
@ -103,7 +124,7 @@ su -c "createdb $DB_DEST" "$SCODOC_USER" || die "Erreur création db"
|
|||||||
|
|
||||||
if [ ! -z $KEEP_ENV ] ; then
|
if [ ! -z $KEEP_ENV ] ; then
|
||||||
echo "conservation de la configuration actuelle"
|
echo "conservation de la configuration actuelle"
|
||||||
cp "$SCODOC_VAR_DIR".old/.env "$SCODOC_VAR_DIR"/.env
|
cp -p "$SCODOC_VAR_OLD"/.env "$SCODOC_VAR_DIR"/.env
|
||||||
echo "récupération des données..."
|
echo "récupération des données..."
|
||||||
su -c "pg_restore -f - $DB_DUMP | psql -q $DB_DEST" "$SCODOC_USER" >/dev/null || die "Erreur chargement/renommage de la base SQL"
|
su -c "pg_restore -f - $DB_DUMP | psql -q $DB_DEST" "$SCODOC_USER" >/dev/null || die "Erreur chargement/renommage de la base SQL"
|
||||||
su -c "(cd $SCODOC_DIR && source venv/bin/activate && flask db upgrade)" "$SCODOC_USER"
|
su -c "(cd $SCODOC_DIR && source venv/bin/activate && flask db upgrade)" "$SCODOC_USER"
|
||||||
@ -119,5 +140,4 @@ else
|
|||||||
systemctl start scodoc9
|
systemctl start scodoc9
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
echo "Terminé."
|
echo "Terminé."
|
||||||
|
Loading…
Reference in New Issue
Block a user