forked from ScoDoc/ScoDoc
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(ScoValueError, handle_sco_value_error)
|
||||
|
||||
app.register_error_handler(AccessDenied, handle_access_denied)
|
||||
app.register_error_handler(500, internal_server_error)
|
||||
app.register_error_handler(503, postgresql_server_error)
|
||||
|
@ -11,7 +11,7 @@ from time import time
|
||||
from typing import Optional
|
||||
|
||||
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 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)
|
||||
|
||||
def get_reset_password_token(self, expires_in=600):
|
||||
"Un token pour réinitialiser son mot de passe"
|
||||
return jwt.encode(
|
||||
{"reset_password": self.id, "exp": time() + expires_in},
|
||||
current_app.config["SECRET_KEY"],
|
||||
@ -144,15 +145,17 @@ class User(UserMixin, db.Model):
|
||||
|
||||
@staticmethod
|
||||
def verify_reset_password_token(token):
|
||||
"Vérification du token de reéinitialisation du mot de passe"
|
||||
try:
|
||||
id = jwt.decode(
|
||||
user_id = jwt.decode(
|
||||
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
|
||||
)["reset_password"]
|
||||
except:
|
||||
return
|
||||
return User.query.get(id)
|
||||
return User.query.get(user_id)
|
||||
|
||||
def to_dict(self, include_email=True):
|
||||
"""l'utilisateur comme un dict, avec des champs supplémentaires"""
|
||||
data = {
|
||||
"date_expiration": self.date_expiration.isoformat() + "Z"
|
||||
if self.date_expiration
|
||||
@ -472,5 +475,5 @@ def get_super_admin():
|
||||
|
||||
|
||||
@login.user_loader
|
||||
def load_user(id):
|
||||
return User.query.get(int(id))
|
||||
def load_user(uid):
|
||||
return User.query.get(int(uid))
|
||||
|
@ -72,7 +72,7 @@ def bulletin_but_xml_compat(
|
||||
etud = Identite.query.get_or_404(etudid)
|
||||
results = bulletin_but.ResultatsSemestreBUT(sem)
|
||||
nb_inscrits = len(results.etuds)
|
||||
if sem.bul_hide_xml or force_publishing:
|
||||
if (not sem.bul_hide_xml) or force_publishing:
|
||||
published = "1"
|
||||
else:
|
||||
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))
|
||||
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 = np.stack(modimpls_notes_arr)
|
||||
# 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):
|
||||
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
|
||||
}
|
||||
|
||||
@ -242,9 +242,9 @@ class NotesTableCompat(ResultatsSemestre):
|
||||
|
||||
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
||||
"liste des évaluations valides dans un module"
|
||||
mi = ModuleImpl.query.get(moduleimpl_id)
|
||||
modimpl = ModuleImpl.query.get(moduleimpl_id)
|
||||
evals_results = []
|
||||
for e in mi.evaluations:
|
||||
for e in modimpl.evaluations:
|
||||
d = e.to_dict()
|
||||
d["heure_debut"] = e.heure_debut # datetime.time
|
||||
d["heure_fin"] = e.heure_fin
|
||||
|
@ -57,6 +57,23 @@ class EntrepriseContact(db.Model):
|
||||
"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):
|
||||
__tablename__ = "entreprise_offre"
|
||||
@ -71,6 +88,15 @@ class EntrepriseOffre(db.Model):
|
||||
missions = 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):
|
||||
__tablename__ = "entreprise_log"
|
||||
@ -100,3 +126,12 @@ class EntrepriseEnvoiOffre(db.Model):
|
||||
receiver_id = db.Column(db.Integer, db.ForeignKey("user.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())
|
||||
|
||||
|
||||
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
|
||||
from config import Config
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
import glob
|
||||
import shutil
|
||||
|
||||
@ -45,6 +45,18 @@ from werkzeug.utils import secure_filename
|
||||
|
||||
@bp.route("/", methods=["GET"])
|
||||
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()
|
||||
logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all()
|
||||
return render_template(
|
||||
@ -57,6 +69,18 @@ def index():
|
||||
|
||||
@bp.route("/contacts", methods=["GET"])
|
||||
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 = (
|
||||
db.session.query(EntrepriseContact, Entreprise)
|
||||
.join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id)
|
||||
@ -70,10 +94,39 @@ def contacts():
|
||||
|
||||
@bp.route("/fiche_entreprise/<int:id>", methods=["GET"])
|
||||
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()
|
||||
offres = entreprise.offres
|
||||
offres_with_files = []
|
||||
for offre in offres:
|
||||
if datetime.now() - offre.date_ajout.replace(tzinfo=None) >= timedelta(
|
||||
days=90
|
||||
): # pour une date d'expiration ?
|
||||
break
|
||||
files = []
|
||||
path = os.path.join(
|
||||
Config.SCODOC_VAR_DIR,
|
||||
@ -116,19 +169,32 @@ def fiche_entreprise(id):
|
||||
|
||||
@bp.route("/offres", methods=["GET"])
|
||||
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)
|
||||
.filter(EntrepriseEnvoiOffre.receiver_id == current_user.id)
|
||||
.join(EntrepriseOffre, EntrepriseOffre.id == EntrepriseEnvoiOffre.offre_id)
|
||||
.all()
|
||||
)
|
||||
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"])
|
||||
def add_entreprise():
|
||||
"""
|
||||
Permet d'ajouter une entreprise dans la base avec un formulaire
|
||||
"""
|
||||
form = EntrepriseCreationForm()
|
||||
if form.validate_on_submit():
|
||||
entreprise = Entreprise(
|
||||
@ -170,6 +236,13 @@ def add_entreprise():
|
||||
|
||||
@bp.route("/edit_entreprise/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = EntrepriseModificationForm()
|
||||
if form.validate_on_submit():
|
||||
@ -231,6 +304,13 @@ def edit_entreprise(id):
|
||||
|
||||
@bp.route("/delete_entreprise/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = SuppressionConfirmationForm()
|
||||
if form.validate_on_submit():
|
||||
@ -253,6 +333,13 @@ def delete_entreprise(id):
|
||||
|
||||
@bp.route("/add_offre/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = OffreCreationForm()
|
||||
if form.validate_on_submit():
|
||||
@ -279,6 +366,13 @@ def add_offre(id):
|
||||
|
||||
@bp.route("/edit_offre/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = OffreModificationForm()
|
||||
if form.validate_on_submit():
|
||||
@ -309,6 +403,13 @@ def edit_offre(id):
|
||||
|
||||
@bp.route("/delete_offre/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
entreprise_id = offre.entreprise.id
|
||||
form = SuppressionConfirmationForm()
|
||||
@ -330,6 +431,13 @@ def delete_offre(id):
|
||||
|
||||
@bp.route("/add_contact/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = ContactCreationForm(hidden_entreprise_id=entreprise.id)
|
||||
if form.validate_on_submit():
|
||||
@ -357,6 +465,13 @@ def add_contact(id):
|
||||
|
||||
@bp.route("/edit_contact/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = ContactModificationForm()
|
||||
if form.validate_on_submit():
|
||||
@ -391,6 +506,13 @@ def edit_contact(id):
|
||||
|
||||
@bp.route("/delete_contact/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
entreprise_id = contact.entreprise.id
|
||||
form = SuppressionConfirmationForm()
|
||||
@ -421,6 +543,13 @@ def delete_contact(id):
|
||||
|
||||
@bp.route("/add_historique/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = HistoriqueCreationForm()
|
||||
if form.validate_on_submit():
|
||||
@ -458,6 +587,13 @@ def add_historique(id):
|
||||
|
||||
@bp.route("/envoyer_offre/<int:id>", methods=["GET", "POST"])
|
||||
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()
|
||||
form = EnvoiOffreForm()
|
||||
if form.validate_on_submit():
|
||||
@ -484,6 +620,18 @@ def envoyer_offre(id):
|
||||
|
||||
@bp.route("/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()
|
||||
etudiants = Identite.query.filter(Identite.nom.ilike(f"%{term}%")).all()
|
||||
list = []
|
||||
@ -505,6 +653,18 @@ def json_etudiants():
|
||||
|
||||
@bp.route("/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()
|
||||
responsables = User.query.filter(
|
||||
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")
|
||||
def export_entreprises():
|
||||
"""
|
||||
Permet d'exporter la liste des entreprises sous format excel (.xlsx)
|
||||
"""
|
||||
entreprises = Entreprise.query.all()
|
||||
if entreprises:
|
||||
keys = ["siret", "nom", "adresse", "ville", "codepostal", "pays"]
|
||||
@ -539,6 +702,9 @@ def export_entreprises():
|
||||
|
||||
@bp.route("/export_contacts")
|
||||
def export_contacts():
|
||||
"""
|
||||
Permet d'exporter la liste des contacts sous format excel (.xlsx)
|
||||
"""
|
||||
contacts = EntrepriseContact.query.all()
|
||||
if contacts:
|
||||
keys = ["nom", "prenom", "telephone", "mail", "poste", "service"]
|
||||
@ -552,10 +718,56 @@ def export_contacts():
|
||||
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(
|
||||
"/get_offre_file/<int:entreprise_id>/<int:offre_id>/<string:filedir>/<string: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(
|
||||
os.path.join(
|
||||
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"])
|
||||
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()
|
||||
form = AjoutFichierForm()
|
||||
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"])
|
||||
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()
|
||||
form = SuppressionConfirmationForm()
|
||||
if form.validate_on_submit():
|
||||
|
@ -245,7 +245,7 @@ class DeptForm(FlaskForm):
|
||||
|
||||
|
||||
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)
|
||||
-> [ (None, None), (dept_id, dept_name)... ]"""
|
||||
depts = [(None, GLOBAL)]
|
||||
|
@ -31,16 +31,10 @@ Formulaires création département
|
||||
|
||||
from flask import flash, url_for, redirect, render_template
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SelectField, SubmitField, FormField, validators, FieldList
|
||||
from wtforms.fields.simple import StringField, HiddenField
|
||||
from wtforms import SubmitField, validators
|
||||
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.scodoc import sco_utils as scu
|
||||
|
||||
from flask_login import current_user
|
||||
|
||||
|
||||
class CreateDeptForm(FlaskForm):
|
||||
@ -60,5 +54,10 @@ class CreateDeptForm(FlaskForm):
|
||||
validators.DataRequired("acronyme du département requis"),
|
||||
],
|
||||
)
|
||||
# description = StringField(label="Description")
|
||||
visible = BooleanField(
|
||||
"Visible sur page d'accueil",
|
||||
default=True,
|
||||
)
|
||||
submit = SubmitField("Valider")
|
||||
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})
|
||||
|
@ -12,8 +12,10 @@ class Departement(db.Model):
|
||||
"""Un département ScoDoc"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
acronym = db.Column(db.String(SHORT_STR_LEN), nullable=False, index=True)
|
||||
description = db.Column(db.Text())
|
||||
acronym = db.Column(
|
||||
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())
|
||||
visible = db.Column(
|
||||
db.Boolean(), nullable=False, default=True, server_default="true"
|
||||
@ -49,11 +51,11 @@ class Departement(db.Model):
|
||||
return dept
|
||||
|
||||
|
||||
def create_dept(acronym: str) -> Departement:
|
||||
def create_dept(acronym: str, visible=True) -> Departement:
|
||||
"Create new departement"
|
||||
from app.models import ScoPreference
|
||||
|
||||
departement = Departement(acronym=acronym)
|
||||
departement = Departement(acronym=acronym, visible=visible)
|
||||
p1 = ScoPreference(name="DeptName", value=acronym, departement=departement)
|
||||
db.session.add(p1)
|
||||
db.session.add(departement)
|
||||
|
@ -49,9 +49,7 @@ class Module(db.Model):
|
||||
super(Module, self).__init__(**kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"<Module{ModuleType(self.module_type).name} id={self.id} code={self.code}>"
|
||||
)
|
||||
return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code}>"
|
||||
|
||||
def to_dict(self):
|
||||
e = dict(self.__dict__)
|
||||
|
@ -78,7 +78,8 @@ def html_edit_formation_apc(
|
||||
alt="supprimer",
|
||||
),
|
||||
"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
|
||||
from flask import g, url_for, request
|
||||
from app.models.formations import Matiere
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
from app.models import Formation
|
||||
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
|
||||
|
||||
_matiereEditor = ndb.EditableTable(
|
||||
@ -156,6 +161,16 @@ associé.
|
||||
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):
|
||||
"delete matiere and attached modules"
|
||||
from app.scodoc import sco_formations
|
||||
@ -165,17 +180,16 @@ def do_matiere_delete(oid):
|
||||
|
||||
cnx = ndb.GetDBConnexion()
|
||||
# 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]
|
||||
locked = matiere_is_locked(mat["matiere_id"])
|
||||
if locked:
|
||||
log("do_matiere_delete: mat=%s" % mat)
|
||||
log("do_matiere_delete: ue=%s" % ue)
|
||||
log("do_matiere_delete: locked sems: %s" % locked)
|
||||
raise ScoLockedFormError()
|
||||
log("do_matiere_delete: matiere_id=%s" % oid)
|
||||
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)
|
||||
|
||||
log("do_matiere_delete: matiere_id=%s" % matiere.id)
|
||||
# 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:
|
||||
sco_edit_module.do_module_delete(mod["module_id"])
|
||||
_matiereEditor.delete(cnx, oid)
|
||||
@ -194,11 +208,25 @@ def matiere_delete(matiere_id=None):
|
||||
"""Delete matière"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
M = matiere_list(args={"matiere_id": matiere_id})[0]
|
||||
UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0]
|
||||
matiere = Matiere.query.get_or_404(matiere_id)
|
||||
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 = [
|
||||
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,
|
||||
]
|
||||
dest_url = url_for(
|
||||
@ -210,7 +238,7 @@ def matiere_delete(matiere_id=None):
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(("matiere_id", {"input_type": "hidden"}),),
|
||||
initvalues=M,
|
||||
initvalues=mat,
|
||||
submitlabel="Confirmer la suppression",
|
||||
cancelbutton="Annuler",
|
||||
)
|
||||
|
@ -43,7 +43,12 @@ from app import models
|
||||
from app.models import Formation
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
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 sco_codes_parcours
|
||||
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):
|
||||
"delete module"
|
||||
from app.scodoc import sco_formations
|
||||
|
||||
mod = module_list({"module_id": oid})[0]
|
||||
if module_is_locked(mod["module_id"]):
|
||||
module = Module.query.get_or_404(oid)
|
||||
mod = module_list({"module_id": oid})[0] # sco7
|
||||
if module_is_locked(module.id):
|
||||
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 !
|
||||
mods = sco_moduleimpl.moduleimpl_list(module_id=oid)
|
||||
if mods:
|
||||
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
|
||||
laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
|
||||
<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 sans affecter les semestres déjà en place.
|
||||
</p>
|
||||
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
|
||||
formation_id=mod["formation_id"])}">reprendre</a>
|
||||
@ -365,12 +387,21 @@ def do_module_delete(oid):
|
||||
|
||||
def module_delete(module_id=None):
|
||||
"""Delete a module"""
|
||||
if not module_id:
|
||||
raise ScoValueError("invalid module !")
|
||||
modules = module_list(args={"module_id": module_id})
|
||||
if not modules:
|
||||
raise ScoValueError("Module inexistant !")
|
||||
mod = modules[0]
|
||||
module = Module.query.get_or_404(module_id)
|
||||
mod = module_list(args={"module_id": module_id})[0] # sco7
|
||||
|
||||
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,
|
||||
),
|
||||
)
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
||||
"""<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.gen_tables import GenTable
|
||||
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 sco_cache
|
||||
@ -130,64 +135,83 @@ def do_ue_create(args):
|
||||
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):
|
||||
"delete UE and attached matieres (but not modules)"
|
||||
from app.scodoc import sco_formations
|
||||
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()
|
||||
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
|
||||
ue = ue_list({"ue_id": ue_id})
|
||||
if not ue:
|
||||
raise ScoValueError("UE inexistante !")
|
||||
ue = ue[0]
|
||||
if ue_is_locked(ue["ue_id"]):
|
||||
raise ScoLockedFormError()
|
||||
# if ue_is_locked(ue.id):
|
||||
# raise ScoLockedFormError()
|
||||
# Il y a-t-il des etudiants ayant validé cette UE ?
|
||||
# si oui, propose de supprimer les validations
|
||||
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:
|
||||
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>"
|
||||
% (len(validations), ue["acronyme"], ue["titre"]),
|
||||
% (len(validations), ue.acronyme, ue.titre),
|
||||
dest_url="",
|
||||
target_variable="delete_validations",
|
||||
cancel_url=url_for(
|
||||
"notes.ue_table",
|
||||
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:
|
||||
log("deleting all validations of UE %s" % ue_id)
|
||||
log("deleting all validations of UE %s" % ue.id)
|
||||
ndb.SimpleQuery(
|
||||
"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
|
||||
mats = sco_edit_matiere.matiere_list({"ue_id": ue_id})
|
||||
mats = sco_edit_matiere.matiere_list({"ue_id": ue.id})
|
||||
for mat in mats:
|
||||
sco_edit_matiere.do_matiere_delete(mat["matiere_id"])
|
||||
# delete uecoef and events
|
||||
ndb.SimpleQuery(
|
||||
"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()
|
||||
_ueEditor.delete(cnx, ue_id)
|
||||
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement utilisé: acceptable de tout invalider ?):
|
||||
_ueEditor.delete(cnx, ue.id)
|
||||
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement
|
||||
# utilisé: acceptable de tout invalider):
|
||||
sco_cache.invalidate_formsemestre()
|
||||
# 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(
|
||||
typ=sco_news.NEWS_FORM,
|
||||
object=ue["formation_id"],
|
||||
object=ue.formation_id,
|
||||
text="Modification de la formation %(acronyme)s" % F,
|
||||
max_frequency=3,
|
||||
)
|
||||
@ -197,11 +221,11 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
||||
url_for(
|
||||
"notes.ue_table",
|
||||
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):
|
||||
@ -211,8 +235,6 @@ def ue_create(formation_id=None):
|
||||
|
||||
def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
"""Modification ou création d'une UE"""
|
||||
from app.scodoc import sco_formations
|
||||
|
||||
create = int(create)
|
||||
if not create:
|
||||
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):
|
||||
"""Delete an UE"""
|
||||
ues = ue_list(args={"ue_id": ue_id})
|
||||
if not ues:
|
||||
raise ScoValueError("UE inexistante !")
|
||||
ue = ues[0]
|
||||
|
||||
if not dialog_confirmed:
|
||||
return scu.confirm_dialog(
|
||||
"<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue,
|
||||
dest_url="",
|
||||
parameters={"ue_id": ue_id},
|
||||
cancel_url=url_for(
|
||||
ue = UniteEns.query.get_or_404(ue_id)
|
||||
if ue.modules.all():
|
||||
raise ScoValueError(
|
||||
f"""Suppression de l'UE {ue.titre} impossible car
|
||||
des modules (ou SAÉ ou ressources) lui sont rattachés."""
|
||||
)
|
||||
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=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
|
||||
@ -1010,12 +1046,14 @@ def _ue_table_modules(
|
||||
H.append(arrow_none)
|
||||
im += 1
|
||||
if mod["nb_moduleimpls"] == 0 and editable:
|
||||
H.append(
|
||||
'<a class="smallbutton" href="module_delete?module_id=%s">%s</a>'
|
||||
% (mod["module_id"], delete_icon)
|
||||
)
|
||||
icon = delete_icon
|
||||
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>")
|
||||
|
||||
mod_editable = (
|
||||
|
@ -52,7 +52,7 @@ class InvalidNoteValue(ScoException):
|
||||
# Exception qui stoque dest_url, utilisee dans Zope standard_error_message
|
||||
class ScoValueError(ScoException):
|
||||
def __init__(self, msg, dest_url=None):
|
||||
ScoException.__init__(self, msg)
|
||||
super().__init__(msg)
|
||||
self.dest_url = dest_url
|
||||
|
||||
|
||||
@ -72,20 +72,35 @@ class ScoConfigurationError(ScoValueError):
|
||||
pass
|
||||
|
||||
|
||||
class ScoLockedFormError(ScoException):
|
||||
def __init__(self, msg=""):
|
||||
class ScoLockedFormError(ScoValueError):
|
||||
"Modification d'une formation verrouillée"
|
||||
|
||||
def __init__(self, msg="", dest_url=None):
|
||||
msg = (
|
||||
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
|
||||
+ 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):
|
||||
"exception avec affichage d'une page explicative ad-hoc"
|
||||
|
||||
def __init__(self, msg=""):
|
||||
ScoException.__init__(self, msg)
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class AccessDenied(ScoGenError):
|
||||
@ -101,7 +116,7 @@ class APIInvalidParams(Exception):
|
||||
status_code = 400
|
||||
|
||||
def __init__(self, message, status_code=None, payload=None):
|
||||
Exception.__init__(self)
|
||||
super().__init__()
|
||||
self.message = message
|
||||
if status_code is not None:
|
||||
self.status_code = status_code
|
||||
|
@ -198,10 +198,13 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
NB_SEM = parcours.NB_SEM
|
||||
else:
|
||||
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 = []
|
||||
for sid in semestre_id_list:
|
||||
if sid == "-1":
|
||||
if sid == -1:
|
||||
semestre_id_labels.append("pas de semestres")
|
||||
else:
|
||||
semestre_id_labels.append(f"S{sid}")
|
||||
@ -329,6 +332,8 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
"labels": modalites_titles,
|
||||
},
|
||||
),
|
||||
]
|
||||
modform.append(
|
||||
(
|
||||
"semestre_id",
|
||||
{
|
||||
@ -338,7 +343,7 @@ def do_formsemestre_createwithmodules(edit=False):
|
||||
"labels": semestre_id_labels,
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
etapes = sco_portal_apogee.get_etapes_apogee_dept()
|
||||
# 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)
|
||||
|
@ -49,16 +49,12 @@ from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_excel
|
||||
from app.scodoc import sco_formsemestre
|
||||
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_pdf
|
||||
from app.scodoc import sco_preferences
|
||||
import sco_version
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app import log
|
||||
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
|
||||
|
||||
@ -121,9 +117,9 @@ def _categories_and_results(etuds, category, result):
|
||||
categories[etud[category]] = True
|
||||
results[etud[result]] = True
|
||||
categories = list(categories.keys())
|
||||
categories.sort()
|
||||
categories.sort(key=scu.heterogeneous_sorting_key)
|
||||
results = list(results.keys())
|
||||
results.sort()
|
||||
results.sort(key=scu.heterogeneous_sorting_key)
|
||||
return categories, results
|
||||
|
||||
|
||||
@ -166,7 +162,7 @@ def _results_by_category(
|
||||
l["sumpercent"] = "%2.1f%%" % ((100.0 * l["sum"]) / tot)
|
||||
#
|
||||
codes = list(results.keys())
|
||||
codes.sort()
|
||||
codes.sort(key=scu.heterogeneous_sorting_key)
|
||||
|
||||
bottom_titles = []
|
||||
if C: # ligne du bas avec totaux:
|
||||
@ -314,7 +310,7 @@ def formsemestre_report_counts(
|
||||
"type_admission",
|
||||
"boursier_prec",
|
||||
]
|
||||
keys.sort()
|
||||
keys.sort(key=scu.heterogeneous_sorting_key)
|
||||
F = [
|
||||
"""<form name="f" method="get" action="%s"><p>
|
||||
Colonnes: <select name="result" onchange="document.f.submit()">"""
|
||||
@ -497,7 +493,7 @@ def table_suivi_cohorte(
|
||||
P.append(p)
|
||||
|
||||
# 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()
|
||||
for p in P:
|
||||
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"""
|
||||
bacs = list(bacs)
|
||||
bacs.sort()
|
||||
bacs.sort(key=scu.heterogeneous_sorting_key)
|
||||
bacspecialites = list(bacspecialites)
|
||||
bacspecialites.sort()
|
||||
bacspecialites.sort(key=scu.heterogeneous_sorting_key)
|
||||
# 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.sort()
|
||||
|
@ -93,6 +93,7 @@ MODULE_TYPE_NAMES = {
|
||||
ModuleType.MALUS: "Malus",
|
||||
ModuleType.RESSOURCE: "Ressource",
|
||||
ModuleType.SAE: "SAÉ",
|
||||
None: "Module",
|
||||
}
|
||||
|
||||
MALUS_MAX = 20.0
|
||||
@ -897,6 +898,11 @@ def sort_dates(L, reverse=False):
|
||||
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):
|
||||
"""Retreives external data using HTTP request
|
||||
(used to connect to Apogee portal, or ScoDoc server)
|
||||
|
@ -5,6 +5,12 @@
|
||||
Prénom : {{ contact.prenom }}<br>
|
||||
Téléphone : {{ contact.telephone }}<br>
|
||||
Mail : {{ contact.mail }}<br>
|
||||
{% if contact.poste %}
|
||||
Poste : {{ contact.poste }}<br>
|
||||
{% endif %}
|
||||
{% if contact.service %}
|
||||
Service : {{ contact.service }}<br>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<div style="margin-bottom: 10px;">
|
||||
|
@ -22,6 +22,8 @@
|
||||
<th>Prenom</th>
|
||||
<th>Telephone</th>
|
||||
<th>Mail</th>
|
||||
<th>Poste</th>
|
||||
<th>Service</th>
|
||||
<th>Entreprise</th>
|
||||
</tr>
|
||||
{% for contact in contacts %}
|
||||
@ -30,6 +32,8 @@
|
||||
<th>{{ contact[0].prenom }}</th>
|
||||
<th>{{ contact[0].telephone }}</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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -41,6 +45,7 @@
|
||||
<div>
|
||||
{% 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_bis') }}">Exporter la liste des contacts avec leur entreprise</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -24,8 +24,8 @@
|
||||
<span style="margin-right: 10px;">{{ data[0].date_debut.strftime('%d/%m/%Y') }} - {{
|
||||
data[0].date_fin.strftime('%d/%m/%Y') }}</span>
|
||||
<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].formation_text }}
|
||||
{{ data[0].type_offre }} réalisé par {{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}
|
||||
{% if data[0].formation_text %} en {{ data[0].formation_text }}{% endif %}
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
@ -24,14 +24,11 @@
|
||||
{{icons.arrow_none|safe}}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if editable and not ue.modules.count() %}
|
||||
|
||||
<a class="smallbutton" href="{{ url_for('notes.ue_delete',
|
||||
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||
}}">{{icons.delete|safe}}</a>
|
||||
{% else %}
|
||||
{{icons.delete_disabled|safe}}
|
||||
{% endif %}
|
||||
|
||||
}}">{% if editable and not ue.modules.count() %}{{icons.delete|safe}}{% else %}{{icons.delete_disabled|safe}}{% endif %}</a>
|
||||
|
||||
<b>{{ue.acronyme}}</b> <a class="discretelink" href="{{
|
||||
url_for('notes.ue_infos', scodoc_dept=g.scodoc_dept, ue_id=ue.id)}}"
|
||||
>{{ue.titre}}</a>
|
||||
|
@ -8,10 +8,9 @@
|
||||
|
||||
{{ exc | safe }}
|
||||
|
||||
<p class="footer">
|
||||
<p>
|
||||
{% if g.scodoc_dept %}
|
||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">retour page d'accueil
|
||||
departement {{ g.scodoc_dept }}</a>
|
||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
|
||||
{% else %}
|
||||
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
|
||||
{% endif %}
|
||||
|
@ -13,11 +13,23 @@
|
||||
|
||||
<ul class="main">
|
||||
{% for dept in depts %}
|
||||
{% if dept.visible or current_user.is_administrator() %}
|
||||
<li>
|
||||
<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
|
||||
{{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>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li>
|
||||
<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
|
||||
|
||||
import app
|
||||
from app import db
|
||||
from app.forms.main import config_forms
|
||||
from app.forms.main.create_dept import CreateDeptForm
|
||||
from app.models import Departement, Identite
|
||||
@ -82,9 +83,7 @@ from PIL import Image as PILImage
|
||||
@bp.route("/ScoDoc/index")
|
||||
def index():
|
||||
"Page d'accueil: liste des départements"
|
||||
depts = (
|
||||
Departement.query.filter_by(visible=True).order_by(Departement.acronym).all()
|
||||
)
|
||||
depts = Departement.query.filter_by().order_by(Departement.acronym).all()
|
||||
return render_template(
|
||||
"scodoc.html",
|
||||
title=sco_version.SCONAME,
|
||||
@ -108,7 +107,11 @@ def create_dept():
|
||||
if request.method == "POST" and form.cancel.data: # cancel button
|
||||
return redirect(url_for("scodoc.index"))
|
||||
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éé.")
|
||||
return redirect(url_for("scodoc.index"))
|
||||
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"])
|
||||
@login_required
|
||||
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):
|
||||
"form. création ou edition utilisateur"
|
||||
auth_dept = current_user.dept
|
||||
auth_username = current_user.user_name
|
||||
from_mail = current_user.email
|
||||
initvalues = {}
|
||||
edit = int(edit)
|
||||
@ -204,7 +203,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
|
||||
administrable_dept_acronyms = sorted(
|
||||
set(
|
||||
[
|
||||
x.dept
|
||||
x.dept or ""
|
||||
for x in UserRole.query.filter_by(user=current_user)
|
||||
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
|
||||
]
|
||||
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)):
|
||||
if displayed_roles_strings[i] not in editable_roles_strings:
|
||||
disabled_roles[i] = True
|
||||
@ -375,7 +374,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1):
|
||||
can_choose_dept = True
|
||||
else:
|
||||
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}
|
||||
if len(selectable_dept_acronyms) > 1:
|
||||
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""",
|
||||
"labels": 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"),
|
||||
)
|
||||
|
||||
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(
|
||||
"entreprise_contact_entreprise_corresp_id_fkey",
|
||||
"entreprise_contact",
|
||||
@ -249,6 +276,7 @@ def downgrade():
|
||||
op.drop_column("entreprise_contact", "nom")
|
||||
|
||||
op.drop_table("entreprise_envoi_offre")
|
||||
op.drop_table("entreprise_envoi_offre_etudiant")
|
||||
op.drop_table("entreprise_offre")
|
||||
op.drop_table("entreprise_etudiant")
|
||||
op.drop_table("entreprise_log")
|
||||
|
@ -29,7 +29,6 @@ from app.models import ModuleImpl, ModuleImplInscription
|
||||
from app.models import Identite
|
||||
from app.models import departements
|
||||
from app.models.evaluations import Evaluation
|
||||
from app.scodoc.sco_etud import identite_create
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.views import notes, scolar
|
||||
import tools
|
||||
@ -248,6 +247,7 @@ def edit_role(rolename, addpermissionname=None, removepermissionname=None): # e
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@click.argument("rolename")
|
||||
def delete_role(rolename):
|
||||
@ -259,6 +259,7 @@ def delete_role(rolename):
|
||||
db.session.delete(role)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@click.argument("username")
|
||||
@click.option("-d", "--dept", "dept_acronym")
|
||||
|
@ -42,17 +42,17 @@
|
||||
# - do_formation_delete
|
||||
|
||||
import json
|
||||
import xml.dom.minidom
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
|
||||
import pytest
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_edit_formation
|
||||
from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_exceptions
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_formsemestre_edit
|
||||
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
|
||||
|
||||
# on doit d'abbord 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
|
||||
# on doit d'abord supprimer le semestre:
|
||||
|
||||
sco_formsemestre_edit.do_formsemestre_delete(
|
||||
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()
|
||||
assert len(li_module) == 4
|
||||
sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre
|
||||
# sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]])
|
||||
# deuxieme methode de supression d'un module
|
||||
li_module2 = sco_edit_module.module_list()
|
||||
# Suppression impossible car utilisé dans le semestre semt:
|
||||
with pytest.raises(sco_exceptions.ScoNonEmptyFormationObject):
|
||||
sco_edit_module.module_delete(module_id=mi3["module_id"])
|
||||
|
||||
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"])
|
||||
|
||||
@ -316,10 +316,6 @@ def test_formations(test_client):
|
||||
assert len(li_ue2) == 3 # verification de la suppression de l'UE
|
||||
|
||||
# --- 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"])
|
||||
lif3 = notes.formation_list(format="json").get_data(as_text=True)
|
||||
|
@ -64,7 +64,7 @@ fi
|
||||
|
||||
# ------------ LIEN VERS .env
|
||||
# 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" ]
|
||||
then
|
||||
ln -s "$SCODOC_VAR_DIR/.env" "$SCODOC_DIR"
|
||||
|
@ -24,53 +24,74 @@ usage() {
|
||||
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))
|
||||
then
|
||||
usage
|
||||
elif [ $# -eq 2 -a $1 != '--keep-env' -a $2 != '--keep-env' ] ; then
|
||||
elif [ $# -eq 2 ] && [ "$1" != "--keep-env" ] && [ "$2" != "--keep-env" ]
|
||||
then
|
||||
usage
|
||||
elif [ $# -eq 1 ] ; then
|
||||
elif [ $# -eq 1 ]
|
||||
then
|
||||
echo "restauration des données et de la configuration originale (production)"
|
||||
SRC=$1
|
||||
SRC="$1"
|
||||
DB_DEST="SCODOC"
|
||||
else
|
||||
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_DEST="$DB_CURRENT"
|
||||
KEEP=1
|
||||
if [ $1 = '--keep-env' ]; then
|
||||
SRC=$2
|
||||
KEEP_ENV="Y"
|
||||
if [ "$1" = "--keep-env" ]
|
||||
then
|
||||
SRC="$2"
|
||||
else
|
||||
SRC=$1
|
||||
SRC="$1"
|
||||
fi
|
||||
fi
|
||||
DB_DUMP="${SCODOC_VAR_DIR}"/SCODOC.dump
|
||||
|
||||
# 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 "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 "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 "La base SQL $DB_CURRENT sera effacée et remplacée !!!"
|
||||
echo
|
||||
echo -n "Voulez vous poursuivre cette operation ? (y/n) [n]"
|
||||
read -r ans
|
||||
if [ ! "$(norm_ans "$ans")" = 'Y' ]
|
||||
# Préparation si une copie 'antique' doit être effacée, demander confirmation, puis effacer
|
||||
SCODOC_VAR_OLD=${SCODOC_VAR_DIR}.old
|
||||
if [ -e "$SCODOC_VAR_DIR" ] && [ -e "$SCODOC_VAR_OLD" ]
|
||||
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
|
||||
fi
|
||||
|
||||
rm -rf "$SCODOC_VAR_OLD" || die "Erreur suppression $SCODOC_VAR_OLD"
|
||||
|
||||
# -- Stop ScoDoc
|
||||
if [ $KEEP -ne 1 ]; then
|
||||
if [ -z "$KEEP_ENV" ]
|
||||
then
|
||||
echo "Arrêt de scodoc9..."
|
||||
systemctl stop scodoc9
|
||||
else
|
||||
echo -n "Assurez-vous d'avoir arrété le serveur scodoc (validez pour continuer)"
|
||||
read ans
|
||||
fi
|
||||
|
||||
# Clear caches
|
||||
@ -86,7 +107,7 @@ fi
|
||||
|
||||
# -- Ouverture archive
|
||||
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
|
||||
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
|
||||
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..."
|
||||
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"
|
||||
@ -119,5 +140,4 @@ else
|
||||
systemctl start scodoc9
|
||||
fi
|
||||
|
||||
|
||||
echo "Terminé."
|
||||
|
Loading…
Reference in New Issue
Block a user