1
0
forked from ScoDoc/ScoDoc

Merge branch 'modif' of https://scodoc.org/git/iziram/ScoDoc into iziram-rev

This commit is contained in:
Emmanuel Viennet 2023-02-02 06:42:57 -03:00
commit 43849007fb
157 changed files with 4032 additions and 2487 deletions

View File

@ -26,6 +26,7 @@ from flask_mail import Mail
from flask_bootstrap import Bootstrap from flask_bootstrap import Bootstrap
from flask_moment import Moment from flask_moment import Moment
from flask_caching import Cache from flask_caching import Cache
from jinja2 import select_autoescape
import sqlalchemy import sqlalchemy
from app.scodoc.sco_exceptions import ( from app.scodoc.sco_exceptions import (
@ -61,11 +62,11 @@ cache = Cache(
def handle_sco_value_error(exc): def handle_sco_value_error(exc):
return render_template("sco_value_error.html", exc=exc), 404 return render_template("sco_value_error.j2", exc=exc), 404
def handle_access_denied(exc): def handle_access_denied(exc):
return render_template("error_access_denied.html", exc=exc), 403 return render_template("error_access_denied.j2", exc=exc), 403
def internal_server_error(exc): def internal_server_error(exc):
@ -75,7 +76,7 @@ def internal_server_error(exc):
return ( return (
render_template( render_template(
"error_500.html", "error_500.j2",
SCOVERSION=sco_version.SCOVERSION, SCOVERSION=sco_version.SCOVERSION,
date=datetime.datetime.now().isoformat(), date=datetime.datetime.now().isoformat(),
exc=exc, exc=exc,
@ -146,7 +147,7 @@ def render_raw_html(template_filename: str, **args) -> str:
def postgresql_server_error(e): def postgresql_server_error(e):
"""Erreur de connection au serveur postgresql (voir notesdb.open_db_connection)""" """Erreur de connection au serveur postgresql (voir notesdb.open_db_connection)"""
return render_raw_html("error_503.html", SCOVERSION=sco_version.SCOVERSION), 503 return render_raw_html("error_503.j2", SCOVERSION=sco_version.SCOVERSION), 503
class LogRequestFormatter(logging.Formatter): class LogRequestFormatter(logging.Formatter):
@ -275,6 +276,9 @@ def create_app(config_class=DevConfig):
from app.api import api_bp from app.api import api_bp
from app.api import api_web_bp from app.api import api_web_bp
# Enable autoescaping of all templates, including .j2
app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True)
# https://scodoc.fr/ScoDoc # https://scodoc.fr/ScoDoc
app.register_blueprint(scodoc_bp) app.register_blueprint(scodoc_bp)
# https://scodoc.fr/ScoDoc/RT/Scolarite/... # https://scodoc.fr/ScoDoc/RT/Scolarite/...

View File

@ -42,6 +42,7 @@ from app.api import (
formations, formations,
formsemestres, formsemestres,
jury, jury,
justificatif,
logos, logos,
partitions, partitions,
users, users,

View File

@ -6,23 +6,19 @@
"""ScoDoc 9 API : Assiduités """ScoDoc 9 API : Assiduités
""" """
from datetime import datetime from datetime import datetime
from typing import List
from flask import g, jsonify, request from flask import g, jsonify, request
from app import db
from app.api import api_bp as bp, api_web_bp
from app.scodoc.sco_utils import json_error
from app.decorators import scodoc, permission_required
from app.scodoc.sco_permissions import Permission
from flask_login import login_required from flask_login import login_required
from app.models import Identite, Assiduite, FormSemestre, ModuleImpl
from app.scodoc.sco_exceptions import ScoValueError
import app.scodoc.sco_utils as scu
import app.scodoc.sco_assiduites as scass import app.scodoc.sco_assiduites as scass
import app.scodoc.sco_utils as scu
from app import db
from app.api import api_bp as bp
from app.api import api_web_bp
from app.decorators import permission_required, scodoc
from app.models import Assiduite, FormSemestre, Identite, ModuleImpl
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import json_error
@bp.route("/assiduite/<int:assiduite_id>") @bp.route("/assiduite/<int:assiduite_id>")
@ -47,12 +43,11 @@ def assiduite(assiduite_id: int = None):
query = Assiduite.query.filter_by(id=assiduite_id) query = Assiduite.query.filter_by(id=assiduite_id)
# if g.scodoc_dept: # if g.scodoc_dept:
# query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id) # query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
assiduite_query = query.first_or_404()
assiduite = query.first_or_404() data = assiduite_query.to_dict()
data = assiduite.to_dict() return jsonify(_change_etat(data))
return jsonify(change_etat(data))
@bp.route("/assiduites/<int:etudid>/count", defaults={"with_query": False}) @bp.route("/assiduites/<int:etudid>/count", defaults={"with_query": False})
@ -104,15 +99,15 @@ def count_assiduites(etudid: int = None, with_query: bool = False):
query = query.filter_by(dept_id=g.scodoc_dept_id) query = query.filter_by(dept_id=g.scodoc_dept_id)
etud: Identite = query.first_or_404(etudid) etud: Identite = query.first_or_404(etudid)
filter: dict[str, object] = {} filtered: dict[str, object] = {}
metric: str = "all" metric: str = "all"
if with_query: if with_query:
metric, filter = count_manager(request) metric, filtered = _count_manager(request)
return jsonify( return jsonify(
scass.get_assiduites_stats( scass.get_assiduites_stats(
assiduites=etud.assiduites, metric=metric, filter=filter assiduites=etud.assiduites, metric=metric, filtered=filtered
) )
) )
@ -162,15 +157,15 @@ def assiduites(etudid: int = None, with_query: bool = False):
query = query.filter_by(dept_id=g.scodoc_dept_id) query = query.filter_by(dept_id=g.scodoc_dept_id)
etud: Identite = query.first_or_404(etudid) etud: Identite = query.first_or_404(etudid)
assiduites = etud.assiduites assiduites_query = etud.assiduites
if with_query: if with_query:
assiduites = filter_manager(request, assiduites) assiduites_query = _filter_manager(request, assiduites_query)
data_set: List[dict] = [] data_set: list[dict] = []
for ass in assiduites.all(): for ass in assiduites_query.all():
data = ass.to_dict() data = ass.to_dict()
data_set.append(change_etat(data)) data_set.append(_change_etat(data))
return jsonify(data_set) return jsonify(data_set)
@ -200,19 +195,15 @@ def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
if formsemestre is None: if formsemestre is None:
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas") return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
etuds = formsemestre.etuds.all() assiduites_query = scass.filter_by_formsemestre(Assiduite.query, formsemestre)
etuds_id = [etud.id for etud in etuds]
assiduites = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id))
assiduites = scass.filter_by_formsemstre(assiduites, formsemestre)
if with_query: if with_query:
assiduites = filter_manager(request, assiduites) assiduites_query = _filter_manager(request, assiduites_query)
data_set: List[dict] = [] data_set: list[dict] = []
for ass in assiduites.all(): for ass in assiduites_query.all():
data = ass.to_dict() data = ass.to_dict()
data_set.append(change_etat(data)) data_set.append(_change_etat(data))
return jsonify(data_set) return jsonify(data_set)
@ -249,14 +240,14 @@ def count_assiduites_formsemestre(
etuds = formsemestre.etuds.all() etuds = formsemestre.etuds.all()
etuds_id = [etud.id for etud in etuds] etuds_id = [etud.id for etud in etuds]
assiduites = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id)) assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id))
assiduites = scass.filter_by_formsemstre(assiduites, formsemestre) assiduites_query = scass.filter_by_formsemestre(assiduites_query, formsemestre)
metric: str = "all" metric: str = "all"
filter: dict = {} filtered: dict = {}
if with_query: if with_query:
metric, filter = count_manager(request) metric, filtered = _count_manager(request)
return jsonify(scass.get_assiduites_stats(assiduites, metric, filter)) return jsonify(scass.get_assiduites_stats(assiduites_query, metric, filtered))
@bp.route("/assiduite/<int:etudid>/create", methods=["POST"]) @bp.route("/assiduite/<int:etudid>/create", methods=["POST"])
@ -265,31 +256,38 @@ def count_assiduites_formsemestre(
@login_required @login_required
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange) # @permission_required(Permission.ScoAssiduiteChange)
def create(etudid: int = None): def assiduite_create(etudid: int = None):
""" """
Création d'une assiduité pour l'étudiant (etudid) Création d'une assiduité pour l'étudiant (etudid)
La requête doit avoir un content type "application/json": La requête doit avoir un content type "application/json":
{ [
"date_debut": str, {
"date_fin": str, "date_debut": str,
"etat": str, "date_fin": str,
} "etat": str,
ou },
{ {
"date_debut": str, "date_debut": str,
"date_fin": str, "date_fin": str,
"etat": str, "etat": str,
"moduleimpl_id": int, "moduleimpl_id": int,
} "desc":str,
}
...
]
""" """
etud: Identite = Identite.query.filter_by(id=etudid).first_or_404() etud: Identite = Identite.query.filter_by(id=etudid).first_or_404()
create_list: list[object] = request.get_json(force=True)
if not isinstance(create_list, list):
return json_error(404, "Le contenu envoyé n'est pas une liste")
errors: dict[int, str] = {} errors: dict[int, str] = {}
success: dict[int, object] = {} success: dict[int, object] = {}
for i, data in enumerate(request.get_json(force=True)): for i, data in enumerate(create_list):
code, obj = create_singular(data, etud) code, obj = _create_singular(data, etud)
if code == 404: if code == 404:
errors[i] = obj errors[i] = obj
else: else:
@ -298,21 +296,21 @@ def create(etudid: int = None):
return jsonify({"errors": errors, "success": success}) return jsonify({"errors": errors, "success": success})
def create_singular( def _create_singular(
data: dict, data: dict,
etud: Identite, etud: Identite,
) -> tuple[int, object]: ) -> tuple[int, object]:
errors: List[str] = [] errors: list[str] = []
# -- vérifications de l'objet json -- # -- vérifications de l'objet json --
# cas 1 : ETAT # cas 1 : ETAT
etat = data.get("etat", None) etat = data.get("etat", None)
if etat is None: if etat is None:
errors.append("param 'etat': manquant") errors.append("param 'etat': manquant")
elif etat not in scu.ETATS_ASSIDUITE.keys(): elif etat not in scu.ETATS_ASSIDUITE:
errors.append("param 'etat': invalide") errors.append("param 'etat': invalide")
data = change_etat(data, False) data = _change_etat(data, False)
etat = data.get("etat", None) etat = data.get("etat", None)
# cas 2 : date_debut # cas 2 : date_debut
@ -329,7 +327,7 @@ def create_singular(
errors.append("param 'date_fin': manquant") errors.append("param 'date_fin': manquant")
fin = scu.is_iso_formated(date_fin, convert=True) fin = scu.is_iso_formated(date_fin, convert=True)
if fin is None: if fin is None:
errors.append(f"param 'date_fin': format invalide") errors.append("param 'date_fin': format invalide")
# cas 4 : moduleimpl_id # cas 4 : moduleimpl_id
@ -343,9 +341,9 @@ def create_singular(
# cas 5 : desc # cas 5 : desc
desc:str = data.get("desc", None) desc: str = data.get("desc", None)
if errors != []: if errors:
err: str = ", ".join(errors) err: str = ", ".join(errors)
return (404, err) return (404, err)
@ -358,6 +356,7 @@ def create_singular(
etat=etat, etat=etat,
etud=etud, etud=etud,
moduleimpl=moduleimpl, moduleimpl=moduleimpl,
description=desc,
) )
db.session.add(nouv_assiduite) db.session.add(nouv_assiduite)
@ -376,15 +375,27 @@ def create_singular(
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange) # @permission_required(Permission.ScoAssiduiteChange)
def delete(): def assiduite_cdelete():
""" """
Suppression d'une assiduité à partir de son id Suppression d'une assiduité à partir de son id
Forme des données envoyées :
[
<assiduite_id:int>,
...
]
""" """
assiduites: list[int] = request.get_json(force=True) assiduites_list: list[int] = request.get_json(force=True)
if not isinstance(assiduites_list, list):
return json_error(404, "Le contenu envoyé n'est pas une liste")
output = {"errors": {}, "success": {}} output = {"errors": {}, "success": {}}
for i, ass in enumerate(assiduites): for i, ass in enumerate(assiduites_list):
code, msg = delete_singular(ass, db) code, msg = _delete_singular(ass, db)
if code == 404: if code == 404:
output["errors"][f"{i}"] = msg output["errors"][f"{i}"] = msg
else: else:
@ -393,11 +404,11 @@ def delete():
return jsonify(output) return jsonify(output)
def delete_singular(assiduite_id: int, db): def _delete_singular(assiduite_id: int, database):
assiduite: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first() assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first()
if assiduite is None: if assiduite_unique is None:
return (404, "Assiduite non existante") return (404, "Assiduite non existante")
db.session.delete(assiduite) database.session.delete(assiduite_unique)
return (200, "OK") return (200, "OK")
@ -407,28 +418,31 @@ def delete_singular(assiduite_id: int, db):
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange) # @permission_required(Permission.ScoAssiduiteChange)
def edit(assiduite_id: int): def assiduite_cedit(assiduite_id: int):
""" """
Edition d'une assiduité à partir de son id Edition d'une assiduité à partir de son id
La requête doit avoir un content type "application/json": La requête doit avoir un content type "application/json":
{ {
"etat": str, "etat"?: str,
"moduleimpl_id": int "moduleimpl_id"?: int
"desc"?: str
} }
""" """
assiduite: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first_or_404() assiduite_unique: Assiduite = Assiduite.query.filter_by(
errors: List[str] = [] id=assiduite_id
).first_or_404()
errors: list[str] = []
data = request.get_json(force=True) data = request.get_json(force=True)
# Vérifications de data # Vérifications de data
# Cas 1 : Etat # Cas 1 : Etat
if data.get("etat") is not None: if data.get("etat") is not None:
data = change_etat(data, False) data = _change_etat(data, False)
if data.get("etat") is None: if data.get("etat") is None:
errors.append("param 'etat': invalide") errors.append("param 'etat': invalide")
else: else:
assiduite.etat = data.get("etat") assiduite_unique.etat = data.get("etat")
# Cas 2 : Moduleimpl_id # Cas 2 : Moduleimpl_id
moduleimpl_id = data.get("moduleimpl_id", False) moduleimpl_id = data.get("moduleimpl_id", False)
@ -441,24 +455,30 @@ def edit(assiduite_id: int):
errors.append("param 'moduleimpl_id': invalide") errors.append("param 'moduleimpl_id': invalide")
else: else:
if not moduleimpl.est_inscrit( if not moduleimpl.est_inscrit(
Identite.query.filter_by(id=assiduite.etudid).first() Identite.query.filter_by(id=assiduite_unique.etudid).first()
): ):
errors.append("param 'moduleimpl_id': etud non inscrit") errors.append("param 'moduleimpl_id': etud non inscrit")
else: else:
assiduite.moduleimpl_id = moduleimpl_id assiduite_unique.moduleimpl_id = moduleimpl_id
else: else:
assiduite.moduleimpl_id = moduleimpl_id assiduite_unique.moduleimpl_id = moduleimpl_id
if errors != []:
# Cas 3 : desc
desc = data.get("desc", False)
if desc is not False:
assiduite_unique.desc = desc
if errors:
err: str = ", ".join(errors) err: str = ", ".join(errors)
return json_error(404, err) return json_error(404, err)
db.session.add(assiduite) db.session.add(assiduite_unique)
db.session.commit() db.session.commit()
return jsonify({"OK": True}) return jsonify({"OK": True})
# -- Utils -- # -- Utils --
def change_etat(data: dict, from_int: bool = True): def _change_etat(data: dict, from_int: bool = True):
"""change dans un json la valeur du champs état""" """change dans un json la valeur du champs état"""
if from_int: if from_int:
data["etat"] = scu.ETAT_ASSIDUITE_NAME.get(data["etat"]) data["etat"] = scu.ETAT_ASSIDUITE_NAME.get(data["etat"])
@ -467,104 +487,108 @@ def change_etat(data: dict, from_int: bool = True):
return data return data
def count_manager(request) -> tuple[str, dict]: def _count_manager(requested) -> tuple[str, dict]:
""" """
Retourne la/les métriques à utiliser ainsi que le filtre donnés en query de la requête Retourne la/les métriques à utiliser ainsi que le filtre donnés en query de la requête
""" """
filter: dict = {} filtered: dict = {}
# cas 1 : etat assiduite # cas 1 : etat assiduite
etat = request.args.get("etat") etat = requested.args.get("etat")
if etat is not None: if etat is not None:
filter["etat"] = etat filtered["etat"] = etat
# cas 2 : date de début # cas 2 : date de début
deb = request.args.get("date_debut") deb = requested.args.get("date_debut")
deb: datetime = scu.is_iso_formated(deb, True) deb: datetime = scu.is_iso_formated(deb, True)
if deb is not None: if deb is not None:
filter["date_debut"] = deb filtered["date_debut"] = deb
# cas 3 : date de fin # cas 3 : date de fin
fin = request.args.get("date_fin") fin = requested.args.get("date_fin")
fin = scu.is_iso_formated(fin, True) fin = scu.is_iso_formated(fin, True)
if fin is not None: if fin is not None:
filter["date_fin"] = fin filtered["date_fin"] = fin
# cas 4 : moduleimpl_id # cas 4 : moduleimpl_id
module = request.args.get("moduleimpl_id", False) module = requested.args.get("moduleimpl_id", False)
try: try:
if module is False: if module is False:
raise Exception raise ValueError
if module != "": if module != "":
module = int(module) module = int(module)
else: else:
module = None module = None
except Exception: except ValueError:
module = False module = False
if module is not False: if module is not False:
filter["moduleimpl_id"] = module filtered["moduleimpl_id"] = module
# cas 5 : formsemestre_id # cas 5 : formsemestre_id
formsemestre_id = request.args.get("formsemestre_id") formsemestre_id = requested.args.get("formsemestre_id")
if formsemestre_id is not None: if formsemestre_id is not None:
formsemestre: FormSemestre = None formsemestre: FormSemestre = None
formsemestre_id = int(formsemestre_id) formsemestre_id = int(formsemestre_id)
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
filter["formsemestre"] = formsemestre filtered["formsemestre"] = formsemestre
# cas 6 : type # cas 6 : type
metric = request.args.get("metric", "all") metric = requested.args.get("metric", "all")
return (metric, filter) return (metric, filtered)
def filter_manager(request, assiduites): def _filter_manager(requested, assiduites_query):
""" """
Retourne les assiduites entrées filtrées en fonction de la request Retourne les assiduites entrées filtrées en fonction de la request
""" """
# cas 1 : etat assiduite # cas 1 : etat assiduite
etat = request.args.get("etat") etat = requested.args.get("etat")
if etat is not None: if etat is not None:
assiduites = scass.filter_by_etat(assiduites, etat) assiduites_query = scass.filter_assiduites_by_etat(assiduites_query, etat)
# cas 2 : date de début # cas 2 : date de début
deb = request.args.get("date_debut") deb = requested.args.get("date_debut")
deb: datetime = scu.is_iso_formated(deb, True) deb: datetime = scu.is_iso_formated(deb, True)
if deb is not None: if deb is not None:
assiduites = scass.filter_by_date(assiduites, deb, sup=True) assiduites_query = scass.filter_assiduites_by_date(
assiduites_query, deb, sup=True
)
# cas 3 : date de fin # cas 3 : date de fin
fin = request.args.get("date_fin") fin = requested.args.get("date_fin")
fin = scu.is_iso_formated(fin, True) fin = scu.is_iso_formated(fin, True)
if fin is not None: if fin is not None:
assiduites = scass.filter_by_date(assiduites, fin, sup=False) assiduites_query = scass.filter_assiduites_by_date(
assiduites_query, fin, sup=False
)
# cas 4 : moduleimpl_id # cas 4 : moduleimpl_id
module = request.args.get("moduleimpl_id", False) module = requested.args.get("moduleimpl_id", False)
try: try:
if module is False: if module is False:
raise Exception raise ValueError
if module != "": if module != "":
module = int(module) module = int(module)
else: else:
module = None module = None
except Exception: except ValueError:
module = False module = False
if module is not False: if module is not False:
assiduites = scass.filter_by_module_impl(assiduites, module) assiduites_query = scass.filter_by_module_impl(assiduites_query, module)
# cas 5 : formsemestre_id # cas 5 : formsemestre_id
formsemestre_id = request.args.get("formsemestre_id") formsemestre_id = requested.args.get("formsemestre_id")
if formsemestre_id is not None: if formsemestre_id is not None:
formsemestre: FormSemestre = None formsemestre: FormSemestre = None
formsemestre_id = int(formsemestre_id) formsemestre_id = int(formsemestre_id)
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
assiduites = scass.filter_by_formsemstre(assiduites, formsemestre) assiduites_query = scass.filter_by_formsemestre(assiduites_query, formsemestre)
return assiduites return assiduites_query

536
app/api/justificatif.py Normal file
View File

@ -0,0 +1,536 @@
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc 9 API : Assiduités
"""
import os
from datetime import datetime
import app.scodoc.sco_assiduites as scass
import app.scodoc.sco_utils as scu
from app import db
from app.api import api_bp as bp
from app.api import api_web_bp
from app.scodoc.sco_exceptions import ScoValueError
from app.decorators import permission_required, scodoc
from app.models import Identite, Justificatif
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
from app.scodoc.sco_permissions import Permission
from flask import g, jsonify, request
from flask_login import login_required
from app.scodoc.sco_utils import json_error
# @bp.route("/justificatif/remove")
# @api_web_bp.route("/justificatif/remove")
# @scodoc
# def justremove():
# """ """
# archiver: JustificatifArchiver = JustificatifArchiver()
# archiver.delete_justificatif(etudid=1, archive_id="2023-02-01-10-29-20")
# return jsonify("done")
# Partie Modèle
# TODO: justificatif
@bp.route("/justificatif/<int:justif_id>")
@api_web_bp.route("/assiduite/<int:justif_id>")
@scodoc
@permission_required(Permission.ScoView)
def justificatif(justif_id: int = None):
"""Retourne un objet justificatif à partir de son id
Exemple de résultat:
{
"justif_id": 1,
"etudid": 2,
"date_debut": "2022-10-31T08:00+01:00",
"date_fin": "2022-10-31T10:00+01:00",
"etat": "valide",
"fichier": "archive_id",
"raison": "une raison",
"entry_date": "2022-10-31T08:00+01:00",
}
"""
query = Justificatif.query.filter_by(id=justif_id)
if g.scodoc_dept:
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
justificatif_unique = query.first_or_404()
data = justificatif_unique.to_dict()
return jsonify(_change_etat(data))
# TODO: justificatifs[-query]
@bp.route("/justificatifs/<int:etudid>", defaults={"with_query": False})
@bp.route("/justificatifs/<int:etudid>/query", defaults={"with_query": True})
@api_web_bp.route("/justificatifs/<int:etudid>", defaults={"with_query": False})
@api_web_bp.route("/justificatifs/<int:etudid>/query", defaults={"with_query": True})
@login_required
@scodoc
@permission_required(Permission.ScoView)
def justificatifs(etudid: int = None, with_query: bool = False):
"""
Retourne toutes les assiduités d'un étudiant
chemin : /justificatifs/<int:etudid>
Un filtrage peut être donné avec une query
chemin : /justificatifs/<int:etudid>/query?
Les différents filtres :
Etat (etat du justificatif -> validé, non validé, modifé, en attente):
query?etat=[- liste des états séparé par une virgule -]
ex: .../query?etat=validé,modifié
Date debut
(date de début du justificatif, sont affichés les justificatifs
dont la date de début est supérieur ou égale à la valeur donnée):
query?date_debut=[- date au format iso -]
ex: query?date_debut=2022-11-03T08:00+01:00
Date fin
(date de fin du justificatif, sont affichés les justificatifs
dont la date de fin est inférieure ou égale à la valeur donnée):
query?date_fin=[- date au format iso -]
ex: query?date_fin=2022-11-03T10:00+01:00
"""
query = Identite.query.filter_by(id=etudid)
if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id)
etud: Identite = query.first_or_404(etudid)
justificatifs_query = etud.justificatifs
if with_query:
justificatifs_query = _filter_manager(request, justificatifs_query)
data_set: list[dict] = []
for just in justificatifs_query.all():
data = just.to_dict()
data_set.append(_change_etat(data))
return jsonify(data_set)
# TODO: justificatif-create
@bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
@api_web_bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
@scodoc
@login_required
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_create(etudid: int = None):
"""
Création d'un justificatif pour l'étudiant (etudid)
La requête doit avoir un content type "application/json":
[
{
"date_debut": str,
"date_fin": str,
"etat": str,
},
{
"date_debut": str,
"date_fin": str,
"etat": str,
"raison":str,
}
...
]
"""
etud: Identite = Identite.query.filter_by(id=etudid).first_or_404()
create_list: list[object] = request.get_json(force=True)
if not isinstance(create_list, list):
return json_error(404, "Le contenu envoyé n'est pas une liste")
errors: dict[int, str] = {}
success: dict[int, object] = {}
for i, data in enumerate(create_list):
code, obj = _create_singular(data, etud)
if code == 404:
errors[i] = obj
else:
success[i] = obj
return jsonify({"errors": errors, "success": success})
def _create_singular(
data: dict,
etud: Identite,
) -> tuple[int, object]:
errors: list[str] = []
# -- vérifications de l'objet json --
# cas 1 : ETAT
etat = data.get("etat", None)
if etat is None:
errors.append("param 'etat': manquant")
elif etat not in scu.ETATS_JUSTIFICATIF:
errors.append("param 'etat': invalide")
data = _change_etat(data, False)
etat = data.get("etat", None)
# cas 2 : date_debut
date_debut = data.get("date_debut", None)
if date_debut is None:
errors.append("param 'date_debut': manquant")
deb = scu.is_iso_formated(date_debut, convert=True)
if deb is None:
errors.append("param 'date_debut': format invalide")
# cas 3 : date_fin
date_fin = data.get("date_fin", None)
if date_fin is None:
errors.append("param 'date_fin': manquant")
fin = scu.is_iso_formated(date_fin, convert=True)
if fin is None:
errors.append("param 'date_fin': format invalide")
# cas 4 : raison
raison: str = data.get("raison", None)
if errors:
err: str = ", ".join(errors)
return (404, err)
# TOUT EST OK
try:
nouv_justificatif: Justificatif = Justificatif.create_justificatif(
date_debut=deb,
date_fin=fin,
etat=etat,
etud=etud,
raison=raison,
)
db.session.add(nouv_justificatif)
db.session.commit()
return (200, {"justif_id": nouv_justificatif.id})
except ScoValueError as excp:
return (
404,
excp.args[0],
)
# TODO: justificatif-edit
@bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
@api_web_bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_edit(justif_id: int):
"""
Edition d'un justificatif à partir de son id
La requête doit avoir un content type "application/json":
{
"etat"?: str,
"raison"?: str
}
"""
justificatif_unique: Justificatif = Justificatif.query.filter_by(
id=justif_id
).first_or_404()
errors: list[str] = []
data = request.get_json(force=True)
# Vérifications de data
# Cas 1 : Etat
if data.get("etat") is not None:
data = _change_etat(data, False)
if data.get("etat") is None:
errors.append("param 'etat': invalide")
else:
justificatif_unique.etat = data.get("etat")
# Cas 2 : raison
raison = data.get("raison", False)
if raison is not False:
justificatif_unique.raison = raison
if errors:
err: str = ", ".join(errors)
return json_error(404, err)
db.session.add(justificatif_unique)
db.session.commit()
return jsonify({"OK": True})
# TODO: justificatif-delete
@bp.route("/justificatif/delete", methods=["POST"])
@api_web_bp.route("/justificatif/delete", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_delete():
"""
Suppression d'un justificatif à partir de son id
Forme des données envoyées :
[
<justif_id:int>,
...
]
"""
justificatifs_list: list[int] = request.get_json(force=True)
if not isinstance(justificatifs_list, list):
return json_error(404, "Le contenu envoyé n'est pas une liste")
output = {"errors": {}, "success": {}}
for i, ass in enumerate(justificatifs_list):
code, msg = _delete_singular(ass, db)
if code == 404:
output["errors"][f"{i}"] = msg
else:
output["success"][f"{i}"] = {"OK": True}
db.session.commit()
return jsonify(output)
def _delete_singular(justif_id: int, database):
justificatif_unique: Justificatif = Justificatif.query.filter_by(
id=justif_id
).first()
if justificatif_unique is None:
return (404, "Justificatif non existant")
database.session.delete(justificatif_unique)
return (200, "OK")
# Partie archivage
# TODO: justificatif-import
@bp.route("/justificatif/import/<int:justif_id>", methods=["POST"])
@api_web_bp.route("/justificatif/import/<int:justif_id>", methods=["POST"])
@scodoc
@login_required
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_import(justif_id: int = None):
"""
Importation d'un fichier (création d'archive)
"""
if len(request.files) == 0:
return json_error(404, "Il n'y a pas de fichier joint")
file = list(request.files.values())[0]
if file.filename == "":
return json_error(404, "Il n'y a pas de fichier joint")
query = Justificatif.query.filter_by(id=justif_id)
if g.scodoc_dept:
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
justificatif_unique: Justificatif = query.first_or_404()
archive_name: str = justificatif_unique.fichier
archiver: JustificatifArchiver = JustificatifArchiver()
try:
archive_name: str = archiver.save_justificatif(
etudid=justificatif_unique.etudid,
filename=file.filename,
data=file.stream.read(),
archive_name=archive_name,
)
justificatif_unique.fichier = archive_name
db.session.add(justificatif_unique)
db.session.commit()
return jsonify({"response": "imported"})
except ScoValueError as err:
return json_error(404, err.args[1])
# TODO: justificatif-export
@bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["GET"])
@api_web_bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["GET"])
@scodoc
@login_required
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_export(justif_id: int = None, filename: str = None):
"""
Retourne un fichier d'une archive d'un justificatif
"""
query = Justificatif.query.filter_by(id=justif_id)
if g.scodoc_dept:
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
justificatif_unique: Justificatif = query.first_or_404()
archive_name: str = justificatif_unique.fichier
if archive_name is None:
return json_error(404, "le justificatif ne possède pas de fichier")
archiver: JustificatifArchiver = JustificatifArchiver()
try:
return archiver.get_justificatif_file(
archive_name, justificatif_unique.etudid, filename
)
except ScoValueError as err:
return json_error(404, err.args[1])
# TODO: justificatif-remove
@bp.route("/justificatif/remove/<int:justif_id>", methods=["POST"])
@api_web_bp.route("/justificatif/remove/<int:justif_id>", methods=["POST"])
@scodoc
@login_required
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_remove(justif_id: int = None):
"""
Supression d'un fichier ou d'une archive
{
"remove": <"all"/"list">
"filenames"?: [
<filename:str>,
...
]
}
"""
data: dict = request.get_json(force=True)
query = Justificatif.query.filter_by(id=justif_id)
if g.scodoc_dept:
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
justificatif_unique: Justificatif = query.first_or_404()
archive_name: str = justificatif_unique.fichier
if archive_name is None:
return json_error(404, "le justificatif ne possède pas de fichier")
remove: str = data.get("remove")
if remove is None or remove not in ("all", "list"):
return json_error(404, "param 'remove': Valeur invalide")
archiver: JustificatifArchiver = JustificatifArchiver()
etudid: int = justificatif_unique.etudid
try:
if remove == "all":
archiver.delete_justificatif(etudid=etudid, archive_name=archive_name)
justificatif_unique.fichier = None
db.session.add(justificatif_unique)
db.session.commit()
else:
for fname in data.get("filenames", []):
archiver.delete_justificatif(
etudid=etudid,
archive_name=archive_name,
filename=fname,
)
if len(archiver.list_justificatifs(archive_name, etudid)) == 0:
archiver.delete_justificatif(etudid, archive_name)
justificatif_unique.fichier = None
db.session.add(justificatif_unique)
db.session.commit()
except ScoValueError as err:
return json_error(404, err.args[1])
return jsonify({"response": "removed"})
# TODO: justificatif-list
@bp.route("/justificatif/list/<int:justif_id>", methods=["GET"])
@api_web_bp.route("/justificatif/list/<int:justif_id>", methods=["GET"])
@scodoc
@login_required
@permission_required(Permission.ScoView)
# @permission_required(Permission.ScoAssiduiteChange)
def justif_list(justif_id: int = None):
"""
Liste les fichiers du justificatif
"""
query = Justificatif.query.filter_by(id=justif_id)
if g.scodoc_dept:
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
justificatif_unique: Justificatif = query.first_or_404()
archive_name: str = justificatif_unique.fichier
filenames: list[str] = []
archiver: JustificatifArchiver = JustificatifArchiver()
if archive_name is not None:
filenames = archiver.list_justificatifs(
archive_name, justificatif_unique.etudid
)
return jsonify(filenames)
# Partie justification
# TODO: justificatif-justified
# -- Utils --
def _change_etat(data: dict, from_int: bool = True):
"""change dans un json la valeur du champs état"""
if from_int:
data["etat"] = scu.ETAT_JUSTIFICATIF_NAME.get(data["etat"])
else:
data["etat"] = scu.ETATS_JUSTIFICATIF.get(data["etat"])
return data
def _filter_manager(requested, justificatifs_query):
"""
Retourne les justificatifs entrés filtrés en fonction de la request
"""
# cas 1 : etat justificatif
etat = requested.args.get("etat")
if etat is not None:
justificatifs_query = scass.filter_justificatifs_by_etat(
justificatifs_query, etat
)
# cas 2 : date de début
deb = requested.args.get("date_debut")
deb: datetime = scu.is_iso_formated(deb, True)
if deb is not None:
justificatifs_query = scass.filter_justificatifs_by_date(
justificatifs_query, deb, sup=True
)
# cas 3 : date de fin
fin = requested.args.get("date_fin")
fin = scu.is_iso_formated(fin, True)
if fin is not None:
justificatifs_query = scass.filter_justificatifs_by_date(
justificatifs_query, fin, sup=False
)
return justificatifs_query

View File

@ -11,5 +11,5 @@ def send_password_reset_email(user):
sender=current_app.config["SCODOC_MAIL_FROM"], sender=current_app.config["SCODOC_MAIL_FROM"],
recipients=[user.email], recipients=[user.email],
text_body=render_template("email/reset_password.txt", user=user, token=token), text_body=render_template("email/reset_password.txt", user=user, token=token),
html_body=render_template("email/reset_password.html", user=user, token=token), html_body=render_template("email/reset_password.j2", user=user, token=token),
) )

View File

@ -42,7 +42,7 @@ def login():
return form.redirect("scodoc.index") return form.redirect("scodoc.index")
message = request.args.get("message", "") message = request.args.get("message", "")
return render_template( return render_template(
"auth/login.html", title=_("Sign In"), form=form, message=message "auth/login.j2", title=_("Sign In"), form=form, message=message
) )
@ -65,9 +65,7 @@ def create_user():
db.session.commit() db.session.commit()
flash(f"Utilisateur {user.user_name} créé") flash(f"Utilisateur {user.user_name} créé")
return redirect(url_for("scodoc.index")) return redirect(url_for("scodoc.index"))
return render_template( return render_template("auth/register.j2", title="Création utilisateur", form=form)
"auth/register.html", title="Création utilisateur", form=form
)
@bp.route("/reset_password_request", methods=["GET", "POST"]) @bp.route("/reset_password_request", methods=["GET", "POST"])
@ -98,7 +96,7 @@ def reset_password_request():
) )
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
return render_template( return render_template(
"auth/reset_password_request.html", title=_("Reset Password"), form=form "auth/reset_password_request.j2", title=_("Reset Password"), form=form
) )
@ -116,7 +114,7 @@ def reset_password(token):
db.session.commit() db.session.commit()
flash(_("Votre mot de passe a été changé.")) flash(_("Votre mot de passe a été changé."))
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
return render_template("auth/reset_password.html", form=form, user=user) return render_template("auth/reset_password.j2", form=form, user=user)
@bp.route("/reset_standard_roles_permissions", methods=["GET", "POST"]) @bp.route("/reset_standard_roles_permissions", methods=["GET", "POST"])

View File

@ -841,6 +841,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
et autorisations d'inscription émises. et autorisations d'inscription émises.
Efface même si étudiant DEM ou DEF. Efface même si étudiant DEM ou DEF.
Si à cheval, n'efface que pour le semestre d'origine du deca. Si à cheval, n'efface que pour le semestre d'origine du deca.
(commite la session.)
""" """
if only_one_sem or self.a_cheval: if only_one_sem or self.a_cheval:
# N'efface que les autorisations venant de ce semestre, # N'efface que les autorisations venant de ce semestre,

View File

@ -500,7 +500,7 @@ def jury_but_semestriel(
H.append("</div>") H.append("</div>")
H.append( H.append(
render_template( render_template(
"but/documentation_codes_jury.html", "but/documentation_codes_jury.j2",
nom_univ=f"""Export {sco_preferences.get_preference("InstituteName") nom_univ=f"""Export {sco_preferences.get_preference("InstituteName")
or sco_preferences.get_preference("UnivName") or sco_preferences.get_preference("UnivName")
or "Apogée"}""", or "Apogée"}""",

View File

@ -1358,6 +1358,44 @@ class BonusIUTvannes(BonusSportAdditif):
classic_use_bonus_ues = False # seulement sur moy gen. classic_use_bonus_ues = False # seulement sur moy gen.
class BonusValenciennes(BonusDirect):
"""Article 7 des RCC de lIUT de Valenciennes
<p>
Une bonification maximale de 0.25 point (1/4 de point) peut être ajoutée
à la moyenne de chaque Unité dEnseignement pour :
</p>
<ul>
<li>l'engagement citoyen ;</li>
<li>la participation à un module de sport.</li>
</ul>
<p>
Une bonification accordée par la commission des sports de lUPHF peut être attribuée
aux sportifs de haut niveau. Cette bonification est appliquée à lensemble des
Unités dEnseignement. Ce bonus est :
</p>
<ul>
<li> 0.5 pour la catégorie <em>or</em> (sportif inscrit sur liste ministérielle
jeunesse et sport) ;
</li>
<li> 0.45 pour la catégorie <em>argent</em> (sportif en club professionnel) ;
</li>
<li> 0.40 pour le <em>bronze</em> (sportif de niveau départemental, régional ou national).
</li>
</ul>
<p>Le cumul de bonifications est possible mais ne peut excéder 0.5 point (un demi-point).
</p>
<p><em>Dans ScoDoc, saisir directement la valeur désirée du bonus
dans une évaluation notée sur 20.</em>
</p>
"""
name = "bonus_valenciennes"
displayed_name = "IUT de Valenciennes"
bonus_max = 0.5
class BonusVilleAvray(BonusSportAdditif): class BonusVilleAvray(BonusSportAdditif):
"""Bonus modules optionnels (sport, culture), règle IUT Ville d'Avray. """Bonus modules optionnels (sport, culture), règle IUT Ville d'Avray.

View File

@ -33,7 +33,10 @@ import pandas as pd
from app import db from app import db
from app import models from app import models
from app.models import ( from app.models import (
DispenseUE,
FormSemestre, FormSemestre,
FormSemestreInscription,
Identite,
Module, Module,
ModuleImpl, ModuleImpl,
ModuleUECoef, ModuleUECoef,
@ -215,6 +218,31 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
) )
def load_dispense_ues(
formsemestre: FormSemestre, etudids: pd.Index, ues: list[UniteEns]
) -> set[tuple[int, int]]:
"""Construit l'ensemble des
etudids = modimpl_inscr_df.index, # les etudids
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
Résultat: set de (etudid, ue_id).
"""
dispense_ues = set()
ue_sem_by_code = {ue.ue_code: ue for ue in ues}
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
# puis filtre sur inscrits et code d'UE UE
for dispense_ue in DispenseUE.query.join(
Identite, FormSemestreInscription
).filter_by(formsemestre_id=formsemestre.id):
if dispense_ue.etudid in etudids:
# UE dans le semestre avec même code ?
ue = ue_sem_by_code.get(dispense_ue.ue.ue_code)
if ue is not None:
dispense_ues.add((dispense_ue.etudid, ue.id))
return dispense_ues
def compute_ue_moys_apc( def compute_ue_moys_apc(
sem_cube: np.array, sem_cube: np.array,
etuds: list, etuds: list,

View File

@ -72,7 +72,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
modimpl.module.ue.type != UE_SPORT modimpl.module.ue.type != UE_SPORT
for modimpl in self.formsemestre.modimpls_sorted for modimpl in self.formsemestre.modimpls_sorted
] ]
self.dispense_ues = DispenseUE.load_formsemestre_dispense_ues_set( self.dispense_ues = moy_ue.load_dispense_ues(
self.formsemestre, self.modimpl_inscr_df.index, self.ues self.formsemestre, self.modimpl_inscr_df.index, self.ues
) )
self.etud_moy_ue = moy_ue.compute_ue_moys_apc( self.etud_moy_ue = moy_ue.compute_ue_moys_apc(

View File

@ -89,7 +89,7 @@ def index():
visible=True, association=True, siret_provisoire=True visible=True, association=True, siret_provisoire=True
) )
return render_template( return render_template(
"entreprises/entreprises.html", "entreprises/entreprises.j2",
title="Entreprises", title="Entreprises",
entreprises=entreprises, entreprises=entreprises,
logs=logs, logs=logs,
@ -109,7 +109,7 @@ def logs():
EntrepriseHistorique.date.desc() EntrepriseHistorique.date.desc()
).paginate(page=page, per_page=20) ).paginate(page=page, per_page=20)
return render_template( return render_template(
"entreprises/logs.html", "entreprises/logs.j2",
title="Logs", title="Logs",
logs=logs, logs=logs,
) )
@ -134,7 +134,7 @@ def correspondants():
.all() .all()
) )
return render_template( return render_template(
"entreprises/correspondants.html", "entreprises/correspondants.j2",
title="Correspondants", title="Correspondants",
correspondants=correspondants, correspondants=correspondants,
logs=logs, logs=logs,
@ -149,7 +149,7 @@ def validation():
""" """
entreprises = Entreprise.query.filter_by(visible=False).all() entreprises = Entreprise.query.filter_by(visible=False).all()
return render_template( return render_template(
"entreprises/entreprises_validation.html", "entreprises/entreprises_validation.j2",
title="Validation entreprises", title="Validation entreprises",
entreprises=entreprises, entreprises=entreprises,
) )
@ -167,7 +167,7 @@ def fiche_entreprise_validation(entreprise_id):
description=f"fiche entreprise (validation) {entreprise_id} inconnue" description=f"fiche entreprise (validation) {entreprise_id} inconnue"
) )
return render_template( return render_template(
"entreprises/fiche_entreprise_validation.html", "entreprises/fiche_entreprise_validation.j2",
title="Validation fiche entreprise", title="Validation fiche entreprise",
entreprise=entreprise, entreprise=entreprise,
) )
@ -205,7 +205,7 @@ def validate_entreprise(entreprise_id):
flash("L'entreprise a été validé et ajouté à la liste.") flash("L'entreprise a été validé et ajouté à la liste.")
return redirect(url_for("entreprises.validation")) return redirect(url_for("entreprises.validation"))
return render_template( return render_template(
"entreprises/form_validate_confirmation.html", "entreprises/form_validate_confirmation.j2",
title="Validation entreprise", title="Validation entreprise",
form=form, form=form,
) )
@ -242,7 +242,7 @@ def delete_validation_entreprise(entreprise_id):
flash("L'entreprise a été supprimé de la liste des entreprise à valider.") flash("L'entreprise a été supprimé de la liste des entreprise à valider.")
return redirect(url_for("entreprises.validation")) return redirect(url_for("entreprises.validation"))
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Supression entreprise", title="Supression entreprise",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -282,7 +282,7 @@ def offres_recues():
files.append(file) files.append(file)
offres_recues_with_files.append([envoi_offre, offre, files, correspondant]) offres_recues_with_files.append([envoi_offre, offre, files, correspondant])
return render_template( return render_template(
"entreprises/offres_recues.html", "entreprises/offres_recues.j2",
title="Offres reçues", title="Offres reçues",
offres_recues=offres_recues_with_files, offres_recues=offres_recues_with_files,
) )
@ -321,7 +321,7 @@ def preferences():
form.mail_entreprise.data = EntreprisePreferences.get_email_notifications() form.mail_entreprise.data = EntreprisePreferences.get_email_notifications()
form.check_siret.data = int(EntreprisePreferences.get_check_siret()) form.check_siret.data = int(EntreprisePreferences.get_check_siret())
return render_template( return render_template(
"entreprises/preferences.html", "entreprises/preferences.j2",
title="Préférences", title="Préférences",
form=form, form=form,
) )
@ -357,7 +357,7 @@ def add_entreprise():
db.session.rollback() db.session.rollback()
flash("Une erreur est survenue veuillez réessayer.") flash("Une erreur est survenue veuillez réessayer.")
return render_template( return render_template(
"entreprises/form_ajout_entreprise.html", "entreprises/form_ajout_entreprise.j2",
title="Ajout entreprise avec correspondant", title="Ajout entreprise avec correspondant",
form=form, form=form,
) )
@ -408,7 +408,7 @@ def add_entreprise():
flash("L'entreprise a été ajouté à la liste pour la validation.") flash("L'entreprise a été ajouté à la liste pour la validation.")
return redirect(url_for("entreprises.index")) return redirect(url_for("entreprises.index"))
return render_template( return render_template(
"entreprises/form_ajout_entreprise.html", "entreprises/form_ajout_entreprise.j2",
title="Ajout entreprise avec correspondant", title="Ajout entreprise avec correspondant",
form=form, form=form,
) )
@ -446,7 +446,7 @@ def fiche_entreprise(entreprise_id):
.all() .all()
) )
return render_template( return render_template(
"entreprises/fiche_entreprise.html", "entreprises/fiche_entreprise.j2",
title="Fiche entreprise", title="Fiche entreprise",
entreprise=entreprise, entreprise=entreprise,
offres=offres_with_files, offres=offres_with_files,
@ -472,7 +472,7 @@ def logs_entreprise(entreprise_id):
.paginate(page=page, per_page=20) .paginate(page=page, per_page=20)
) )
return render_template( return render_template(
"entreprises/logs_entreprise.html", "entreprises/logs_entreprise.j2",
title="Logs", title="Logs",
logs=logs, logs=logs,
entreprise=entreprise, entreprise=entreprise,
@ -490,7 +490,7 @@ def offres_expirees(entreprise_id):
).first_or_404(description=f"fiche entreprise {entreprise_id} inconnue") ).first_or_404(description=f"fiche entreprise {entreprise_id} inconnue")
offres_with_files = are.get_offres_expirees_with_files(entreprise.offres) offres_with_files = are.get_offres_expirees_with_files(entreprise.offres)
return render_template( return render_template(
"entreprises/offres_expirees.html", "entreprises/offres_expirees.j2",
title="Offres expirées", title="Offres expirées",
entreprise=entreprise, entreprise=entreprise,
offres_expirees=offres_with_files, offres_expirees=offres_with_files,
@ -574,7 +574,7 @@ def edit_entreprise(entreprise_id):
form.pays.data = entreprise.pays form.pays.data = entreprise.pays
form.association.data = entreprise.association form.association.data = entreprise.association
return render_template( return render_template(
"entreprises/form_modification_entreprise.html", "entreprises/form_modification_entreprise.j2",
title="Modification entreprise", title="Modification entreprise",
form=form, form=form,
) )
@ -610,7 +610,7 @@ def fiche_entreprise_desactiver(entreprise_id):
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id)
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Désactiver entreprise", title="Désactiver entreprise",
form=form, form=form,
info_message="Cliquez sur le bouton Modifier pour confirmer la désactivation", info_message="Cliquez sur le bouton Modifier pour confirmer la désactivation",
@ -646,7 +646,7 @@ def fiche_entreprise_activer(entreprise_id):
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id)
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Activer entreprise", title="Activer entreprise",
form=form, form=form,
info_message="Cliquez sur le bouton Modifier pour confirmer l'activaction", info_message="Cliquez sur le bouton Modifier pour confirmer l'activaction",
@ -692,7 +692,7 @@ def add_taxe_apprentissage(entreprise_id):
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id)
) )
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Ajout taxe apprentissage", title="Ajout taxe apprentissage",
form=form, form=form,
) )
@ -735,7 +735,7 @@ def edit_taxe_apprentissage(entreprise_id, taxe_id):
form.montant.data = taxe.montant form.montant.data = taxe.montant
form.notes.data = taxe.notes form.notes.data = taxe.notes
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Modification taxe apprentissage", title="Modification taxe apprentissage",
form=form, form=form,
) )
@ -775,7 +775,7 @@ def delete_taxe_apprentissage(entreprise_id, taxe_id):
url_for("entreprises.fiche_entreprise", entreprise_id=taxe.entreprise_id) url_for("entreprises.fiche_entreprise", entreprise_id=taxe.entreprise_id)
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Supprimer taxe apprentissage", title="Supprimer taxe apprentissage",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -845,7 +845,7 @@ def add_offre(entreprise_id):
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id)
) )
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Ajout offre", title="Ajout offre",
form=form, form=form,
) )
@ -921,7 +921,7 @@ def edit_offre(entreprise_id, offre_id):
form.expiration_date.data = offre.expiration_date form.expiration_date.data = offre.expiration_date
form.depts.data = offre_depts_list form.depts.data = offre_depts_list
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Modification offre", title="Modification offre",
form=form, form=form,
) )
@ -971,7 +971,7 @@ def delete_offre(entreprise_id, offre_id):
url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id)
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Supression offre", title="Supression offre",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -1047,7 +1047,7 @@ def add_site(entreprise_id):
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id)
) )
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Ajout site", title="Ajout site",
form=form, form=form,
) )
@ -1098,7 +1098,7 @@ def edit_site(entreprise_id, site_id):
form.ville.data = site.ville form.ville.data = site.ville
form.pays.data = site.pays form.pays.data = site.pays
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Modification site", title="Modification site",
form=form, form=form,
) )
@ -1154,7 +1154,7 @@ def add_correspondant(entreprise_id, site_id):
url_for("entreprises.fiche_entreprise", entreprise_id=site.entreprise_id) url_for("entreprises.fiche_entreprise", entreprise_id=site.entreprise_id)
) )
return render_template( return render_template(
"entreprises/form_ajout_correspondants.html", "entreprises/form_ajout_correspondants.j2",
title="Ajout correspondant", title="Ajout correspondant",
form=form, form=form,
) )
@ -1234,7 +1234,7 @@ def edit_correspondant(entreprise_id, site_id, correspondant_id):
form.origine.data = correspondant.origine form.origine.data = correspondant.origine
form.notes.data = correspondant.notes form.notes.data = correspondant.notes
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Modification correspondant", title="Modification correspondant",
form=form, form=form,
) )
@ -1290,7 +1290,7 @@ def delete_correspondant(entreprise_id, site_id, correspondant_id):
) )
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Supression correspondant", title="Supression correspondant",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -1308,7 +1308,7 @@ def contacts(entreprise_id):
).first_or_404(description=f"entreprise {entreprise_id} inconnue") ).first_or_404(description=f"entreprise {entreprise_id} inconnue")
contacts = EntrepriseContact.query.filter_by(entreprise=entreprise.id).all() contacts = EntrepriseContact.query.filter_by(entreprise=entreprise.id).all()
return render_template( return render_template(
"entreprises/contacts.html", "entreprises/contacts.j2",
title="Liste des contacts", title="Liste des contacts",
contacts=contacts, contacts=contacts,
entreprise=entreprise, entreprise=entreprise,
@ -1365,7 +1365,7 @@ def add_contact(entreprise_id):
db.session.commit() db.session.commit()
return redirect(url_for("entreprises.contacts", entreprise_id=entreprise.id)) return redirect(url_for("entreprises.contacts", entreprise_id=entreprise.id))
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Ajout contact", title="Ajout contact",
form=form, form=form,
) )
@ -1421,7 +1421,7 @@ def edit_contact(entreprise_id, contact_id):
) )
form.notes.data = contact.notes form.notes.data = contact.notes
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Modification contact", title="Modification contact",
form=form, form=form,
) )
@ -1459,7 +1459,7 @@ def delete_contact(entreprise_id, contact_id):
url_for("entreprises.contacts", entreprise_id=contact.entreprise) url_for("entreprises.contacts", entreprise_id=contact.entreprise)
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Supression contact", title="Supression contact",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -1525,7 +1525,7 @@ def add_stage_apprentissage(entreprise_id):
url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id) url_for("entreprises.fiche_entreprise", entreprise_id=entreprise.id)
) )
return render_template( return render_template(
"entreprises/form_ajout_stage_apprentissage.html", "entreprises/form_ajout_stage_apprentissage.j2",
title="Ajout stage / apprentissage", title="Ajout stage / apprentissage",
form=form, form=form,
) )
@ -1599,7 +1599,7 @@ def edit_stage_apprentissage(entreprise_id, stage_apprentissage_id):
form.date_fin.data = stage_apprentissage.date_fin form.date_fin.data = stage_apprentissage.date_fin
form.notes.data = stage_apprentissage.notes form.notes.data = stage_apprentissage.notes
return render_template( return render_template(
"entreprises/form_ajout_stage_apprentissage.html", "entreprises/form_ajout_stage_apprentissage.j2",
title="Modification stage / apprentissage", title="Modification stage / apprentissage",
form=form, form=form,
) )
@ -1640,7 +1640,7 @@ def delete_stage_apprentissage(entreprise_id, stage_apprentissage_id):
) )
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Supression stage/apprentissage", title="Supression stage/apprentissage",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -1690,7 +1690,7 @@ def envoyer_offre(entreprise_id, offre_id):
url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id)
) )
return render_template( return render_template(
"entreprises/form_envoi_offre.html", "entreprises/form_envoi_offre.j2",
title="Envoyer une offre", title="Envoyer une offre",
form=form, form=form,
) )
@ -1816,7 +1816,7 @@ def import_donnees():
db.session.rollback() db.session.rollback()
flash("Une erreur est survenue veuillez réessayer.") flash("Une erreur est survenue veuillez réessayer.")
return render_template( return render_template(
"entreprises/import_donnees.html", "entreprises/import_donnees.j2",
title="Importation données", title="Importation données",
form=form, form=form,
) )
@ -1845,7 +1845,7 @@ def import_donnees():
db.session.commit() db.session.commit()
flash(f"Importation réussie") flash(f"Importation réussie")
return render_template( return render_template(
"entreprises/import_donnees.html", "entreprises/import_donnees.j2",
title="Importation données", title="Importation données",
form=form, form=form,
entreprises_import=entreprises_import, entreprises_import=entreprises_import,
@ -1853,7 +1853,7 @@ def import_donnees():
correspondants_import=correspondants, correspondants_import=correspondants,
) )
return render_template( return render_template(
"entreprises/import_donnees.html", title="Importation données", form=form "entreprises/import_donnees.j2", title="Importation données", form=form
) )
@ -1927,7 +1927,7 @@ def add_offre_file(entreprise_id, offre_id):
url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id) url_for("entreprises.fiche_entreprise", entreprise_id=offre.entreprise_id)
) )
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.j2",
title="Ajout fichier à une offre", title="Ajout fichier à une offre",
form=form, form=form,
) )
@ -1969,7 +1969,7 @@ def delete_offre_file(entreprise_id, offre_id, filedir):
) )
) )
return render_template( return render_template(
"entreprises/form_confirmation.html", "entreprises/form_confirmation.j2",
title="Suppression fichier d'une offre", title="Suppression fichier d'une offre",
form=form, form=form,
info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression", info_message="Cliquez sur le bouton Supprimer pour confirmer votre supression",
@ -1981,4 +1981,4 @@ def not_found_error_handler(e):
""" """
Renvoie une page d'erreur pour l'erreur 404 Renvoie une page d'erreur pour l'erreur 404
""" """
return render_template("entreprises/error.html", title="Erreur", e=e) return render_template("entreprises/error.j2", title="Erreur", e=e)

View File

@ -171,7 +171,7 @@ class AddLogoForm(FlaskForm):
class LogoForm(FlaskForm): class LogoForm(FlaskForm):
"""Embed both presentation of a logo (cf. template file configuration.html) """Embed both presentation of a logo (cf. template file configuration.j2)
and all its data and UI action (change, delete)""" and all its data and UI action (change, delete)"""
dept_key = HiddenField() dept_key = HiddenField()
@ -434,7 +434,7 @@ def config_logos():
scu.flash_errors(form) scu.flash_errors(form)
return render_template( return render_template(
"config_logos.html", "config_logos.j2",
scodoc_dept=None, scodoc_dept=None,
title="Configuration ScoDoc", title="Configuration ScoDoc",
form=form, form=form,

View File

@ -133,7 +133,7 @@ def configuration():
return redirect(url_for("scodoc.index")) return redirect(url_for("scodoc.index"))
return render_template( return render_template(
"configuration.html", "configuration.j2",
form_bonus=form_bonus, form_bonus=form_bonus,
form_scodoc=form_scodoc, form_scodoc=form_scodoc,
scu=scu, scu=scu,

View File

@ -1,13 +1,19 @@
# -*- coding: UTF-8 -* # -*- coding: UTF-8 -*
"""Gestion de l'assiduité (assiduités + justificatifs) """Gestion de l'assiduité (assiduités + justificatifs)
""" """
from app import db
from app.models import ModuleImpl, ModuleImplInscription
from app.models.etudiants import Identite
from app.scodoc.sco_utils import EtatAssiduite, localize_datetime, is_period_overlapping
from app.scodoc.sco_exceptions import ScoValueError
from datetime import datetime from datetime import datetime
from app import db
from app.models import ModuleImpl
from app.models.etudiants import Identite
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_utils import (
EtatAssiduite,
EtatJustificatif,
is_period_overlapping,
localize_datetime,
)
class Assiduite(db.Model): class Assiduite(db.Model):
""" """
@ -43,6 +49,8 @@ class Assiduite(db.Model):
desc = db.Column(db.Text) desc = db.Column(db.Text)
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
def to_dict(self) -> dict: def to_dict(self) -> dict:
data = { data = {
"assiduite_id": self.assiduite_id, "assiduite_id": self.assiduite_id,
@ -52,6 +60,7 @@ class Assiduite(db.Model):
"date_fin": self.date_fin, "date_fin": self.date_fin,
"etat": self.etat, "etat": self.etat,
"desc": self.desc, "desc": self.desc,
"entry_date": self.entry_date,
} }
return data return data
@ -119,7 +128,8 @@ class Justificatif(db.Model):
__tablename__ = "justificatifs" __tablename__ = "justificatifs"
justif_id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
justif_id = db.synonym("id")
date_debut = db.Column( date_debut = db.Column(
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
@ -136,26 +146,63 @@ class Justificatif(db.Model):
) )
etat = db.Column( etat = db.Column(
db.Integer, db.Integer,
nullable=False,
) )
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
raison = db.Column(db.Text()) raison = db.Column(db.Text())
""" # Archive_id -> sco_archives_justificatifs.py
Les justificatifs sont enregistrés dans
<archivedir>/justificatifs/<dept_id>/<etudid>/<nom_fichier.extension>
d'après sco_archives.py#JustificatifArchiver
"""
fichier = db.Column(db.Text()) fichier = db.Column(db.Text())
def to_dict(self) -> dict: def to_dict(self) -> dict:
data = { data = {
"justif_id": self.assiduite_id, "justif_id": self.justif_id,
"etudid": self.etudid, "etudid": self.etudid,
"date_debut": self.date_debut, "date_debut": self.date_debut,
"date_fin": self.date_fin, "date_fin": self.date_fin,
"etat": self.etat, "etat": self.etat,
"raison": self.raison, "raison": self.raison,
"fichier": self.fichier, "fichier": self.fichier,
"entry_date": self.entry_date,
} }
return data return data
@classmethod
def create_justificatif(
cls,
etud: Identite,
date_debut: datetime,
date_fin: datetime,
etat: EtatJustificatif,
raison: str = None,
) -> object or int:
"""Créer un nouveau justificatif pour l'étudiant"""
# Vérification de non duplication des périodes
justificatifs: list[Justificatif] = etud.justificatifs.all()
date_debut = localize_datetime(date_debut)
date_fin = localize_datetime(date_fin)
justificatifs = [
just
for just in justificatifs
if is_period_overlapping(
(date_debut, date_fin),
(just.date_debut, just.date_fin),
)
]
if len(justificatifs) != 0:
raise ScoValueError(
"Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
)
nouv_assiduite = Justificatif(
date_debut=date_debut,
date_fin=date_fin,
etat=etat,
etudiant=etud,
raison=raison,
)
return nouv_assiduite

View File

@ -36,6 +36,7 @@ class Formation(db.Model):
titre = db.Column(db.Text(), nullable=False) titre = db.Column(db.Text(), nullable=False)
titre_officiel = db.Column(db.Text(), nullable=False) titre_officiel = db.Column(db.Text(), nullable=False)
version = db.Column(db.Integer, default=1, server_default="1") version = db.Column(db.Integer, default=1, server_default="1")
commentaire = db.Column(db.Text())
formation_code = db.Column( formation_code = db.Column(
db.String(SHORT_STR_LEN), db.String(SHORT_STR_LEN),
server_default=db.text("notes_newid_fcod()"), server_default=db.text("notes_newid_fcod()"),
@ -63,7 +64,7 @@ class Formation(db.Model):
return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}""" return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}"""
def to_dict(self, with_refcomp_attrs=False): def to_dict(self, with_refcomp_attrs=False):
""" "as a dict. """As a dict.
Si with_refcomp_attrs, ajoute attributs permettant de retrouver le ref. de comp. Si with_refcomp_attrs, ajoute attributs permettant de retrouver le ref. de comp.
""" """
e = dict(self.__dict__) e = dict(self.__dict__)

View File

@ -256,23 +256,12 @@ class UniteEns(db.Model):
class DispenseUE(db.Model): class DispenseUE(db.Model):
"""Dispense d'UE """Dispense d'UE
Utilisé en APC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée Utilisé en PCC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée
qu'ils ne refont pas. qu'ils ne refont pas.
La dispense d'UE n'est PAS une validation:
- elle n'est pas affectée par les décisions de jury (pas effacée)
- elle est associée à un formsemestre
- elle ne permet pas la délivrance d'ECTS ou du diplôme.
On utilise cette dispense et non une "inscription" par souci d'efficacité:
en général, la grande majorité des étudiants suivront toutes les UEs de leur parcours,
la dispense étant une exception.
""" """
__table_args__ = (db.UniqueConstraint("formsemestre_id", "ue_id", "etudid"),) __table_args__ = (db.UniqueConstraint("ue_id", "etudid"),)
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
formsemestre_id = formsemestre_id = db.Column(
db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True
)
ue_id = db.Column( ue_id = db.Column(
db.Integer, db.Integer,
db.ForeignKey(UniteEns.id, ondelete="CASCADE"), db.ForeignKey(UniteEns.id, ondelete="CASCADE"),
@ -291,25 +280,3 @@ class DispenseUE(db.Model):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"""<{self.__class__.__name__} {self.id} etud={ return f"""<{self.__class__.__name__} {self.id} etud={
repr(self.etud)} ue={repr(self.ue)}>""" repr(self.etud)} ue={repr(self.ue)}>"""
@classmethod
def load_formsemestre_dispense_ues_set(
cls, formsemestre: "FormSemestre", etudids: pd.Index, ues: list[UniteEns]
) -> set[tuple[int, int]]:
"""Construit l'ensemble des
etudids = modimpl_inscr_df.index, # les etudids
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
Résultat: set de (etudid, ue_id).
"""
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
# puis filtre sur inscrits et ues
ue_ids = {ue.id for ue in ues}
dispense_ues = {
(dispense_ue.etudid, dispense_ue.ue_id)
for dispense_ue in DispenseUE.query.filter_by(
formsemestre_id=formsemestre.id
)
if dispense_ue.etudid in etudids and dispense_ue.ue_id in ue_ids
}
return dispense_ues

View File

@ -274,7 +274,7 @@ def sco_header(
H.append("""<div id="gtrcontent">""") H.append("""<div id="gtrcontent">""")
# En attendant le replacement complet de cette fonction, # En attendant le replacement complet de cette fonction,
# inclusion ici des messages flask # inclusion ici des messages flask
H.append(render_template("flashed_messages.html")) H.append(render_template("flashed_messages.j2"))
# #
# Barre menu semestre: # Barre menu semestre:
H.append(formsemestre_page_title(formsemestre_id)) H.append(formsemestre_page_title(formsemestre_id))

View File

@ -166,6 +166,6 @@ def sidebar(etudid: int = None):
def sidebar_dept(): def sidebar_dept():
"""Partie supérieure de la marge de gauche""" """Partie supérieure de la marge de gauche"""
return render_template( return render_template(
"sidebar_dept.html", "sidebar_dept.j2",
prefs=sco_preferences.SemPreferences(), prefs=sco_preferences.SemPreferences(),
) )

View File

@ -43,6 +43,7 @@ Pour chaque étudiant commun:
comparer les résultats comparer les résultats
""" """
from flask import g, url_for
from app import log from app import log
from app.scodoc import sco_apogee_csv from app.scodoc import sco_apogee_csv
@ -72,11 +73,11 @@ def apo_compare_csv_form():
""" """
<div class="apo_compare_csv_form_but"> <div class="apo_compare_csv_form_but">
Fichier Apogée A: Fichier Apogée A:
<input type="file" size="30" name="A_file"/> <input type="file" size="30" name="file_a"/>
</div> </div>
<div class="apo_compare_csv_form_but"> <div class="apo_compare_csv_form_but">
Fichier Apogée B: Fichier Apogée B:
<input type="file" size="30" name="B_file"/> <input type="file" size="30" name="file_b"/>
</div> </div>
<input type="checkbox" name="autodetect" checked/>autodétecter encodage</input> <input type="checkbox" name="autodetect" checked/>autodétecter encodage</input>
<div class="apo_compare_csv_form_submit"> <div class="apo_compare_csv_form_submit">
@ -88,17 +89,36 @@ def apo_compare_csv_form():
return "\n".join(H) return "\n".join(H)
def apo_compare_csv(A_file, B_file, autodetect=True): def apo_compare_csv(file_a, file_b, autodetect=True):
"""Page comparing 2 Apogee CSV files""" """Page comparing 2 Apogee CSV files"""
A = _load_apo_data(A_file, autodetect=autodetect) try:
B = _load_apo_data(B_file, autodetect=autodetect) apo_data_a = _load_apo_data(file_a, autodetect=autodetect)
apo_data_b = _load_apo_data(file_b, autodetect=autodetect)
except (UnicodeDecodeError, UnicodeEncodeError) as exc:
dest_url = url_for("notes.semset_page", scodoc_dept=g.scodoc_dept)
if autodetect:
raise ScoValueError(
"""
Erreur: l'encodage de l'un des fichiers est mal détecté.
Essayez sans auto-détection, ou vérifiez le codage et le contenu
des fichiers.
""",
dest_url=dest_url,
) from exc
else:
raise ScoValueError(
f"""
Erreur: l'encodage de l'un des fichiers est incorrect.
Vérifiez qu'il est bien en {sco_apogee_csv.APO_INPUT_ENCODING}
""",
dest_url=dest_url,
) from exc
H = [ H = [
html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"), html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"),
"<h2>Comparaison de fichiers Apogée</h2>", "<h2>Comparaison de fichiers Apogée</h2>",
_help_txt, _help_txt,
'<div class="apo_compare_csv">', '<div class="apo_compare_csv">',
_apo_compare_csv(A, B), _apo_compare_csv(apo_data_a, apo_data_b),
"</div>", "</div>",
"""<p><a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a></p>""", """<p><a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a></p>""",
html_sco_header.sco_footer(), html_sco_header.sco_footer(),
@ -112,9 +132,9 @@ def _load_apo_data(csvfile, autodetect=True):
if autodetect: if autodetect:
data_b, message = sco_apogee_csv.fix_data_encoding(data_b) data_b, message = sco_apogee_csv.fix_data_encoding(data_b)
if message: if message:
log("apo_compare_csv: %s" % message) log(f"apo_compare_csv: {message}")
if not data_b: if not data_b:
raise ScoValueError("apo_compare_csv: no data") raise ScoValueError("fichier vide ? (apo_compare_csv: no data)")
data = data_b.decode(sco_apogee_csv.APO_INPUT_ENCODING) data = data_b.decode(sco_apogee_csv.APO_INPUT_ENCODING)
apo_data = sco_apogee_csv.ApoData(data, orig_filename=csvfile.filename) apo_data = sco_apogee_csv.ApoData(data, orig_filename=csvfile.filename)
return apo_data return apo_data

View File

@ -155,28 +155,25 @@ def fix_data_encoding(
text: bytes, text: bytes,
default_source_encoding=APO_INPUT_ENCODING, default_source_encoding=APO_INPUT_ENCODING,
dest_encoding=APO_INPUT_ENCODING, dest_encoding=APO_INPUT_ENCODING,
) -> bytes: ) -> tuple[bytes, str]:
"""Try to ensure that text is using dest_encoding """Try to ensure that text is using dest_encoding
returns converted text, and a message describing the conversion. returns converted text, and a message describing the conversion.
Raises UnicodeEncodeError en cas de problème, en général liée à
une auto-détection errornée.
""" """
message = "" message = ""
detected_encoding = guess_data_encoding(text) detected_encoding = guess_data_encoding(text)
if not detected_encoding: if not detected_encoding:
if default_source_encoding != dest_encoding: if default_source_encoding != dest_encoding:
message = "converting from %s to %s" % ( message = f"converting from {default_source_encoding} to {dest_encoding}"
default_source_encoding, text = text.decode(default_source_encoding).encode(dest_encoding)
dest_encoding,
)
text = text.decode(default_source_encoding).encode(
dest_encoding
) # XXX #py3 #sco8 à tester
else: else:
if detected_encoding != dest_encoding: if detected_encoding != dest_encoding:
message = "converting from detected %s to %s" % ( message = (
detected_encoding, f"converting from detected {default_source_encoding} to {dest_encoding}"
dest_encoding,
) )
text = text.decode(detected_encoding).encode(dest_encoding) # XXX text = text.decode(detected_encoding).encode(dest_encoding)
return text, message return text, message

View File

@ -373,7 +373,7 @@ def etudarchive_import_files(
filename_title="fichier_a_charger", filename_title="fichier_a_charger",
) )
return render_template( return render_template(
"scolar/photos_import_files.html", "scolar/photos_import_files.j2",
page_title="Téléchargement de fichiers associés aux étudiants", page_title="Téléchargement de fichiers associés aux étudiants",
ignored_zipfiles=ignored_zipfiles, ignored_zipfiles=ignored_zipfiles,
unmatched_files=unmatched_files, unmatched_files=unmatched_files,

View File

@ -0,0 +1,112 @@
from app.scodoc.sco_archives import BaseArchiver
from app.scodoc.sco_exceptions import ScoValueError
from app.models import Identite, Departement
from flask import g
import os
class JustificatifArchiver(BaseArchiver):
"""
TOTALK:
- oid -> etudid
- archive_id -> date de création de l'archive (une archive par dépot de document)
justificatif
<dept_id>
<etudid/oid>
<archive_id>
[_description.txt]
[<filename.ext>]
TODO:
- Faire fonction suppression fichier unique dans archive
"""
def __init__(self):
BaseArchiver.__init__(self, archive_type="justificatifs")
def save_justificatif(
self,
etudid: int,
filename: str,
data: bytes or str,
archive_name: str = None,
description: str = "",
) -> str:
"""
Ajoute un fichier dans une archive "justificatif" pour l'etudid donné
Retourne l'archive_name utilisé
"""
self._set_dept(etudid)
if archive_name is None:
archive_id: str = self.create_obj_archive(
oid=etudid, description=description
)
else:
archive_id: str = self.get_id_from_name(etudid, archive_name)
self.store(archive_id, filename, data)
return self.get_archive_name(archive_id)
def delete_justificatif(self, etudid: int, archive_name: str, filename: str = None):
"""
Supprime une archive ou un fichier particulier de l'archivage de l'étudiant donné
"""
self._set_dept(etudid)
if str(etudid) not in self.list_oids():
raise ValueError(f"Aucune archive pour etudid[{etudid}]")
archive_id = self.get_id_from_name(etudid, archive_name)
if filename is not None:
if filename not in self.list_archive(archive_id):
raise ValueError(
f"filename {filename} inconnu dans l'archive archive_id[{archive_id}] -> etudid[{etudid}]"
)
path: str = os.path.join(self.get_obj_dir(etudid), archive_id, filename)
if os.path.isfile(path):
os.remove(path)
else:
self.delete_archive(
os.path.join(
self.get_obj_dir(etudid),
archive_id,
)
)
def list_justificatifs(self, archive_name: str, etudid: int) -> list[str]:
"""
Retourne la liste des noms de fichiers dans l'archive donnée
"""
self._set_dept(etudid)
filenames: list[str] = []
archive_id = self.get_id_from_name(etudid, archive_name)
filenames = self.list_archive(archive_id)
return filenames
def get_justificatif_file(self, archive_name: str, etudid: int, filename: str):
"""
Retourne une réponse de téléchargement de fichier si le fichier existe
"""
self._set_dept(etudid)
archive_id: str = self.get_id_from_name(etudid, archive_name)
if filename in self.list_archive(archive_id):
return self.get_archived_file(etudid, archive_name, filename)
raise ScoValueError(
f"Fichier {filename} introuvable dans l'archive {archive_name}"
)
def _set_dept(self, etudid: int):
if g.scodoc_dept is None or g.scodoc_dept_id is None:
etud: Identite = Identite.query.filter_by(id=etudid).first()
dept: Departement = Departement.query.filter_by(id=etud.dept_id).first()
g.scodoc_dept = dept.acronym
g.scodoc_dept_id = dept.id

View File

@ -1,27 +1,33 @@
from app.models.etudiants import Identite from datetime import date, datetime, time, timedelta
from app.models.formsemestre import FormSemestre
from app.models.assiduites import Assiduite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from datetime import datetime, date, time, timedelta from app.models.assiduites import Assiduite, Justificatif
from app.models.etudiants import Identite
from app.models.formsemestre import FormSemestre, FormSemestreInscription
# TOTALK: Réfléchir sur le fractionnement d'une assiduite prolongée # TOTALK: Réfléchir sur le fractionnement d'une assiduite prolongée
def get_assiduites_stats( def get_assiduites_stats(
assiduites: Assiduite, metric: str = "all", filter: dict[str, object] = {} assiduites: Assiduite, metric: str = "all", filtered: dict[str, object] = None
) -> Assiduite: ) -> Assiduite:
if filter != {}:
for key in filter: if filtered is not None:
for key in filtered:
if key == "etat": if key == "etat":
assiduites = filter_by_etat(assiduites, filter[key]) assiduites = filter_assiduites_by_etat(assiduites, filtered[key])
elif key == "date_fin": elif key == "date_fin":
assiduites = filter_by_date(assiduites, filter[key], sup=False) assiduites = filter_assiduites_by_date(
assiduites, filtered[key], sup=False
)
elif key == "date_debut": elif key == "date_debut":
assiduites = filter_by_date(assiduites, filter[key], sup=True) assiduites = filter_assiduites_by_date(
assiduites, filtered[key], sup=True
)
elif key == "moduleimpl_id": elif key == "moduleimpl_id":
assiduites = filter_by_module_impl(assiduites, filter[key]) assiduites = filter_by_module_impl(assiduites, filtered[key])
elif key == "formsemestre": elif key == "formsemestre":
assiduites = filter_by_formsemstre(assiduites, filter[key]) assiduites = filter_by_formsemestre(assiduites, filtered[key])
count: dict = get_count(assiduites) count: dict = get_count(assiduites)
@ -29,10 +35,10 @@ def get_assiduites_stats(
output: dict = {} output: dict = {}
for key in count: for key, val in count.items():
if key in metrics: if key in metrics:
output[key] = count[key] output[key] = val
return output if output != {} else count return output if output else count
def get_count(assiduites: Assiduite) -> dict[str, int or float]: def get_count(assiduites: Assiduite) -> dict[str, int or float]:
@ -48,9 +54,11 @@ def get_count(assiduites: Assiduite) -> dict[str, int or float]:
current_day: date = None current_day: date = None
current_time: str = None current_time: str = None
MIDNIGHT: time = time(hour=0) midnight: time = time(hour=0)
NOON: time = time(hour=12) noon: time = time(hour=12)
time_check = lambda d: (MIDNIGHT <= d.time() <= NOON)
def time_check(dtime):
return midnight <= dtime.time() <= noon
for ass in all_assiduites: for ass in all_assiduites:
delta: timedelta = ass.date_fin - ass.date_debut delta: timedelta = ass.date_fin - ass.date_debut
@ -72,7 +80,7 @@ def get_count(assiduites: Assiduite) -> dict[str, int or float]:
return output return output
def filter_by_etat(assiduites: Assiduite, etat: str) -> Assiduite: def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
""" """
Filtrage d'une collection d'assiduites en fonction de leur état Filtrage d'une collection d'assiduites en fonction de leur état
""" """
@ -81,8 +89,8 @@ def filter_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
return assiduites.filter(Assiduite.etat.in_(etats)) return assiduites.filter(Assiduite.etat.in_(etats))
def filter_by_date( def filter_assiduites_by_date(
assiduites: Assiduite, date: datetime, sup: bool = True assiduites: Assiduite, date_: datetime, sup: bool = True
) -> Assiduite: ) -> Assiduite:
""" """
Filtrage d'une collection d'assiduites en fonction d'une date Filtrage d'une collection d'assiduites en fonction d'une date
@ -91,15 +99,47 @@ def filter_by_date(
Sup == False -> les assiduites doivent finir avant 'date' Sup == False -> les assiduites doivent finir avant 'date'
""" """
if date.tzinfo is None: if date_.tzinfo is None:
first_assiduite: Assiduite = assiduites.first() first_assiduite: Assiduite = assiduites.first()
if first_assiduite is not None: if first_assiduite is not None:
date: datetime = date.replace(tzinfo=first_assiduite.date_debut.tzinfo) date_: datetime = date_.replace(tzinfo=first_assiduite.date_debut.tzinfo)
if sup: if sup:
return assiduites.filter(Assiduite.date_debut >= date) return assiduites.filter(Assiduite.date_debut >= date_)
else:
return assiduites.filter(Assiduite.date_fin <= date) return assiduites.filter(Assiduite.date_fin <= date_)
def filter_justificatifs_by_etat(
justificatifs: Justificatif, etat: str
) -> Justificatif:
"""
Filtrage d'une collection de justificatifs en fonction de leur état
"""
etats: list[str] = list(etat.split(","))
etats = [scu.ETATS_JUSTIFICATIF.get(e, -1) for e in etats]
return justificatifs.filter(Justificatif.etat.in_(etats))
def filter_justificatifs_by_date(
justificatifs: Justificatif, date_: datetime, sup: bool = True
) -> Assiduite:
"""
Filtrage d'une collection d'assiduites en fonction d'une date
Sup == True -> les assiduites doivent débuter après 'date'\n
Sup == False -> les assiduites doivent finir avant 'date'
"""
if date_.tzinfo is None:
first_justificatif: Justificatif = justificatifs.first()
if first_justificatif is not None:
date_: datetime = date_.replace(tzinfo=first_justificatif.date_debut.tzinfo)
if sup:
return justificatifs.filter(Justificatif.date_debut >= date_)
return justificatifs.filter(Justificatif.date_fin <= date_)
def filter_by_module_impl( def filter_by_module_impl(
@ -111,18 +151,24 @@ def filter_by_module_impl(
return assiduites.filter(Assiduite.moduleimpl_id == module_impl_id) return assiduites.filter(Assiduite.moduleimpl_id == module_impl_id)
def filter_by_formsemstre(assiduites: Assiduite, formsemestre: FormSemestre): def filter_by_formsemestre(assiduites_query: Assiduite, formsemestre: FormSemestre):
""" """
Filtrage d'une collection d'assiduites en fonction d'un formsemestre Filtrage d'une collection d'assiduites en fonction d'un formsemestre
""" """
if formsemestre is None: if formsemestre is None:
return assiduites.filter(False) return assiduites_query.filter(False)
assiduites = assiduites.filter( assiduites_query = (
Identite.query.filter_by(id=Assiduite.etudid).first() assiduites_query.join(Identite, Assiduite.etudid == Identite.id)
in formsemestre.etuds.all() .join(
FormSemestreInscription,
Identite.id == FormSemestreInscription.etudid,
)
.filter(FormSemestreInscription.formsemestre_id == formsemestre.id)
) )
assiduites = assiduites.filter(Assiduite.date_debut >= formsemestre.date_debut) assiduites_query = assiduites_query.filter(
return assiduites.filter(Assiduite.date_fin <= formsemestre.date_fin) Assiduite.date_debut >= formsemestre.date_debut
)
return assiduites_query.filter(Assiduite.date_fin <= formsemestre.date_fin)

View File

@ -926,7 +926,7 @@ def formsemestre_bulletinetud(
_formsemestre_bulletinetud_header_html(etud, formsemestre, format, version), _formsemestre_bulletinetud_header_html(etud, formsemestre, format, version),
bulletin, bulletin,
render_template( render_template(
"bul_foot.html", "bul_foot.j2",
appreciations=None, # déjà affichées appreciations=None, # déjà affichées
css_class="bul_classic_foot", css_class="bul_classic_foot",
etud=etud, etud=etud,
@ -1259,7 +1259,7 @@ def _formsemestre_bulletinetud_header_html(
cssstyles=["css/radar_bulletin.css"], cssstyles=["css/radar_bulletin.css"],
), ),
render_template( render_template(
"bul_head.html", "bul_head.j2",
etud=etud, etud=etud,
format=format, format=format,
formsemestre=formsemestre, formsemestre=formsemestre,

View File

@ -234,6 +234,16 @@ def formation_edit(formation_id=None, create=False):
"explanation": "optionel: code utilisé pour échanger avec d'autres logiciels et identifiant la filière ou spécialité (exemple: ASUR). N'est utilisé que s'il n'y a pas de numéro de semestre.", "explanation": "optionel: code utilisé pour échanger avec d'autres logiciels et identifiant la filière ou spécialité (exemple: ASUR). N'est utilisé que s'il n'y a pas de numéro de semestre.",
}, },
), ),
(
"commentaire",
{
"input_type": "textarea",
"rows": 3,
"cols": 77,
"title": "Commentaire",
"explanation": "commentaire libre.",
},
),
), ),
initvalues=initvalues, initvalues=initvalues,
submitlabel=submitlabel, submitlabel=submitlabel,

View File

@ -385,7 +385,7 @@ def module_edit(
), ),
f"""<h2>{title}</h2>""", f"""<h2>{title}</h2>""",
render_template( render_template(
"scodoc/help/modules.html", "scodoc/help/modules.j2",
is_apc=is_apc, is_apc=is_apc,
semestre_id=semestre_id, semestre_id=semestre_id,
formsemestres=FormSemestre.query.filter( formsemestres=FormSemestre.query.filter(
@ -396,6 +396,7 @@ def module_edit(
.all() .all()
if not create if not create
else None, else None,
create=create,
), ),
] ]
if not unlocked: if not unlocked:
@ -655,7 +656,8 @@ def module_edit(
( (
"numero", "numero",
{ {
"size": 2, "title": "Numéro",
"size": 4,
"explanation": "numéro (1, 2, 3, 4, ...) pour ordre d'affichage", "explanation": "numéro (1, 2, 3, 4, ...) pour ordre d'affichage",
"type": "int", "type": "int",
"default": default_num, "default": default_num,

View File

@ -591,19 +591,45 @@ def view_apo_csv_store(semset_id="", csvfile=None, data: bytes = "", autodetect=
if not semset_id: if not semset_id:
raise ValueError("invalid null semset_id") raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(semset_id=semset_id) semset = sco_semset.SemSet(semset_id=semset_id)
if csvfile: try:
data = csvfile.read() # bytes if csvfile:
data = csvfile.read() # bytes
if autodetect:
# check encoding (although documentation states that users SHOULD upload LATIN1)
data, message = sco_apogee_csv.fix_data_encoding(data)
if message:
log(f"view_apo_csv_store: {message}")
else:
log("view_apo_csv_store: autodetection of encoding disabled by user")
if not data:
raise ScoValueError("view_apo_csv_store: no data")
# data est du bytes, encodé en APO_INPUT_ENCODING
data_str = data.decode(APO_INPUT_ENCODING)
except (UnicodeDecodeError, UnicodeEncodeError) as exc:
dest_url = url_for(
"notes.apo_semset_maq_status",
scodoc_dept=g.scodoc_dept,
semset_id=semset_id,
)
if autodetect: if autodetect:
# check encoding (although documentation states that users SHOULD upload LATIN1) raise ScoValueError(
data, message = sco_apogee_csv.fix_data_encoding(data) f"""
if message: Erreur: l'encodage du fichier est mal détecté.
log("view_apo_csv_store: %s" % message) Essayez sans auto-détection, ou vérifiez le codage et le contenu
du fichier (qui doit être en {sco_apogee_csv.APO_INPUT_ENCODING}).
""",
dest_url=dest_url,
) from exc
else: else:
log("view_apo_csv_store: autodetection of encoding disabled by user") raise ScoValueError(
if not data: f"""
raise ScoValueError("view_apo_csv_store: no data") Erreur: l'encodage du fichier est incorrect.
# data est du bytes, encodé en APO_INPUT_ENCODING Vérifiez qu'il est bien en {sco_apogee_csv.APO_INPUT_ENCODING}
data_str = data.decode(APO_INPUT_ENCODING) """,
dest_url=dest_url,
) from exc
# check si etape maquette appartient bien au semset # check si etape maquette appartient bien au semset
apo_data = sco_apogee_csv.ApoData( apo_data = sco_apogee_csv.ApoData(
data_str, periode=semset["sem_id"] data_str, periode=semset["sem_id"]

View File

@ -36,7 +36,7 @@ from flask_login import current_user
from app import db, log from app import db, log
from app.models import Evaluation, ModuleImpl, ScolarNews from app.models import ModuleImpl, ScolarNews
from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb

View File

@ -345,7 +345,7 @@ def evaluation_create_form(
+ "\n".join(H) + "\n".join(H)
+ "\n" + "\n"
+ tf[1] + tf[1]
+ render_template("scodoc/help/evaluations.html", is_apc=is_apc) + render_template("scodoc/help/evaluations.j2", is_apc=is_apc)
+ html_sco_header.sco_footer() + html_sco_header.sco_footer()
) )
elif tf[0] == -1: elif tf[0] == -1:

View File

@ -75,6 +75,7 @@ _formationEditor = ndb.EditableTable(
"type_parcours", "type_parcours",
"code_specialite", "code_specialite",
"referentiel_competence_id", "referentiel_competence_id",
"commentaire",
), ),
filter_dept=True, filter_dept=True,
sortkey="acronyme", sortkey="acronyme",
@ -118,6 +119,7 @@ def formation_export(
formation: Formation = Formation.query.get_or_404(formation_id) formation: Formation = Formation.query.get_or_404(formation_id)
f_dict = formation.to_dict(with_refcomp_attrs=True) f_dict = formation.to_dict(with_refcomp_attrs=True)
if not export_ids: if not export_ids:
del f_dict["id"]
del f_dict["formation_id"] del f_dict["formation_id"]
del f_dict["dept_id"] del f_dict["dept_id"]
ues = formation.ues ues = formation.ues

View File

@ -541,7 +541,7 @@ def formsemestre_page_title(formsemestre_id=None):
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
h = render_template( h = render_template(
"formsemestre_page_title.html", "formsemestre_page_title.j2",
formsemestre=formsemestre, formsemestre=formsemestre,
scu=scu, scu=scu,
sem_menu_bar=formsemestre_status_menubar(formsemestre), sem_menu_bar=formsemestre_status_menubar(formsemestre),

View File

@ -46,7 +46,7 @@ def affect_groups(partition_id):
raise AccessDenied("vous n'avez pas la permission de modifier les groupes") raise AccessDenied("vous n'avez pas la permission de modifier les groupes")
partition.formsemestre.setup_parcours_groups() partition.formsemestre.setup_parcours_groups()
return render_template( return render_template(
"scolar/affect_groups.html", "scolar/affect_groups.j2",
sco_header=html_sco_header.sco_header( sco_header=html_sco_header.sco_header(
page_title="Affectation aux groupes", page_title="Affectation aux groupes",
javascripts=["js/groupmgr.js"], javascripts=["js/groupmgr.js"],

View File

@ -400,9 +400,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
# Etudiants "dispensés" d'une UE (capitalisée) # Etudiants "dispensés" d'une UE (capitalisée)
ues_cap_info = get_etuds_with_capitalized_ue(formsemestre_id) ues_cap_info = get_etuds_with_capitalized_ue(formsemestre_id)
if ues_cap_info: if ues_cap_info:
H.append( H.append('<h3>Étudiants avec UEs capitalisées:</h3><ul class="ue_inscr_list">')
'<h3>Étudiants avec UEs capitalisées (ADM):</h3><ul class="ue_inscr_list">'
)
ues = [ ues = [
sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys() sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys()
] ]
@ -470,9 +468,8 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
if can_change: if can_change:
H.append( H.append(
f"""<div><a class="stdlink" href="{ f"""<div><a class="stdlink" href="{
url_for("notes.etud_inscrit_ue", url_for("notes.etud_inscrit_ue", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"],
scodoc_dept=g.scodoc_dept, etudid=etud["etudid"], formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
}">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div> }">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div>
""" """
) )

View File

@ -215,7 +215,7 @@ def placement_eval_selectetuds(evaluation_id):
html_sco_header.sco_header(), html_sco_header.sco_header(),
sco_evaluations.evaluation_describe(evaluation_id=evaluation_id), sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
"<h3>Placement et émargement des étudiants</h3>", "<h3>Placement et émargement des étudiants</h3>",
render_template("scodoc/forms/placement.html", form=form), render_template("scodoc/forms/placement.j2", form=form),
] ]
footer = html_sco_header.sco_footer() footer = html_sco_header.sco_footer()
return "\n".join(htmls) + "<p>" + footer return "\n".join(htmls) + "<p>" + footer

View File

@ -166,9 +166,15 @@ def formsemestre_recapcomplet(
H.append("<p>") H.append("<p>")
if mode_jury: if mode_jury:
H.append( H.append(
f"""<a class="stdlink" href="{url_for('notes.formsemestre_validation_auto', f"""<p><a class="stdlink" href="{url_for('notes.formsemestre_validation_auto',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id) scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
}">Calcul automatique des décisions du jury</a></p>""" }">Calcul automatique des décisions du jury</a>
</p><a class="stdlink" href="{url_for('notes.formsemestre_jury_but_erase',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, only_one_sem=1)
}">Effacer <em>toutes</em> les décisions de jury du semestre</a>
<p>
</p>
"""
) )
else: else:
H.append( H.append(

View File

@ -554,7 +554,7 @@ def photos_import_files_form(group_ids=()):
back_url=back_url, back_url=back_url,
) )
return render_template( return render_template(
"scolar/photos_import_files.html", "scolar/photos_import_files.j2",
page_title="Téléchargement des photos des étudiants", page_title="Téléchargement des photos des étudiants",
ignored_zipfiles=ignored_zipfiles, ignored_zipfiles=ignored_zipfiles,
unmatched_files=unmatched_files, unmatched_files=unmatched_files,

View File

@ -128,6 +128,13 @@ ETAT_JUSTIFICATIF_NAME = {
EtatJustificatif.MODIFIE: "modifié", EtatJustificatif.MODIFIE: "modifié",
} }
ETATS_JUSTIFICATIF = {
"validé": EtatJustificatif.VALIDE,
"non vaidé": EtatJustificatif.NON_VALIDE,
"en attente": EtatJustificatif.ATTENTE,
"modifié": EtatJustificatif.MODIFIE,
}
def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or None: def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or None:
""" """

View File

@ -4,6 +4,7 @@
:root { :root {
--sco-content-min-width: 600px; --sco-content-min-width: 600px;
--sco-content-max-width: 1024px; --sco-content-max-width: 1024px;
--sco-color-explication: rgb(10, 58, 140);
} }
html, html,
@ -325,9 +326,9 @@ div.logo-logo img {
box-sizing: content-box; box-sizing: content-box;
margin-top: 10px; margin-top: 10px;
/* -10px */ /* -10px */
width: 80px; width: 130px;
/* adapter suivant image */ /* adapter suivant image */
padding-right: 5px; /* padding-right: 5px; */
} }
div.sidebar-bottom { div.sidebar-bottom {
@ -2115,6 +2116,11 @@ div.formation_descr span.fd_n {
margin-left: 6em; margin-left: 6em;
} }
span.explication {
font-style: italic;
color: var(--sco-color-explication);
}
div.formation_ue_list { div.formation_ue_list {
border: 1px solid black; border: 1px solid black;
margin-top: 5px; margin-top: 5px;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -14,6 +14,8 @@ $(function () {
const url = new URL(document.URL); const url = new URL(document.URL);
const formsemestre_id = url.searchParams.get("formsemestre_id"); const formsemestre_id = url.searchParams.get("formsemestre_id");
const order_info_key = JSON.stringify([url.pathname, formsemestre_id]); const order_info_key = JSON.stringify([url.pathname, formsemestre_id]);
const etudids_key = JSON.stringify(["etudids", url.origin, formsemestre_id]);
const noms_key = JSON.stringify(["noms", url.origin, formsemestre_id]);
let order_info; let order_info;
if (formsemestre_id) { if (formsemestre_id) {
const x = localStorage.getItem(order_info_key); const x = localStorage.getItem(order_info_key);
@ -157,83 +159,89 @@ $(function () {
} }
}); });
} }
let table = $('table.table_recap').DataTable( try {
{ let table = $('table.table_recap').DataTable(
paging: false, {
searching: true, paging: false,
info: false, searching: true,
autoWidth: false, info: false,
fixedHeader: { autoWidth: false,
header: true, fixedHeader: {
footer: false header: true,
}, footer: false
orderCellsTop: true, // cellules ligne 1 pour tri
aaSorting: [], // Prevent initial sorting
colReorder: true,
stateSave: true, // enregistre état de la table (tris, ...)
"columnDefs": [
{
// cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides
targets: hidden_colums,
visible: false,
}, },
{ orderCellsTop: true, // cellules ligne 1 pour tri
// Elimine les 0 à gauche pour les exports excel et les "copy" aaSorting: [], // Prevent initial sorting
targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation", "col_rcue"], colReorder: true,
render: function (data, type, row) { stateSave: true, // enregistre état de la table (tris, ...)
return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data; "columnDefs": [
{
// cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides
targets: hidden_colums,
visible: false,
},
{
// Elimine les 0 à gauche pour les exports excel et les "copy"
targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation", "col_rcue"],
render: function (data, type, row) {
return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data;
}
},
{
// Elimine les décorations (fleches bonus/malus) pour les exports
targets: ["col_ue_bonus", "col_malus"],
render: function (data, type, row) {
return type === 'export' ? data.replace(/.*(\d\d\.\d\d)/, '$1').replace(/0(\d\..*)/, '$1') : data;
}
},
],
dom: 'Bfrtip',
buttons: [
{
extend: 'copyHtml5',
text: 'Copier',
exportOptions: { orthogonal: 'export' }
},
{
extend: 'excelHtml5',
// footer: true, // ne fonctionne pas ?
exportOptions: { orthogonal: 'export' },
title: document.querySelector('table.table_recap').dataset.filename
},
{
extend: 'collection',
text: 'Colonnes affichées',
autoClose: true,
buttons: buttons,
},
],
"drawCallback": function (settings) {
// permet de conserver l'ordre de tri des colonnes
let order_info = JSON.stringify($('table.table_recap').DataTable().order());
if (formsemestre_id) {
localStorage.setItem(order_info_key, order_info);
} }
let etudids = [];
document.querySelectorAll("td.identite_court").forEach(e => {
etudids.push(e.dataset.etudid);
});
let noms = [];
document.querySelectorAll("td.identite_court").forEach(e => {
noms.push(e.dataset.nomprenom);
});
localStorage.setItem(etudids_key, JSON.stringify(etudids));
localStorage.setItem(noms_key, JSON.stringify(noms));
}, },
{ "order": order_info,
// Elimine les décorations (fleches bonus/malus) pour les exports }
targets: ["col_ue_bonus", "col_malus"], );
render: function (data, type, row) { } catch (error) {
return type === 'export' ? data.replace(/.*(\d\d\.\d\d)/, '$1').replace(/0(\d\..*)/, '$1') : data; // l'erreur peut etre causee par un ancien storage:
} localStorage.removeItem(etudids_key);
}, localStorage.removeItem(noms_key);
], localStorage.removeItem(order_info_key);
dom: 'Bfrtip', location.reload();
buttons: [ }
{
extend: 'copyHtml5',
text: 'Copier',
exportOptions: { orthogonal: 'export' }
},
{
extend: 'excelHtml5',
// footer: true, // ne fonctionne pas ?
exportOptions: { orthogonal: 'export' },
title: document.querySelector('table.table_recap').dataset.filename
},
{
extend: 'collection',
text: 'Colonnes affichées',
autoClose: true,
buttons: buttons,
},
],
"drawCallback": function (settings) {
// permet de conserver l'ordre de tri des colonnes
let order_info = JSON.stringify($('table.table_recap').DataTable().order());
if (formsemestre_id) {
localStorage.setItem(order_info_key, order_info);
}
let etudids = [];
document.querySelectorAll("td.identite_court").forEach(e => {
etudids.push(e.dataset.etudid);
});
let noms = [];
document.querySelectorAll("td.identite_court").forEach(e => {
noms.push(e.dataset.nomprenom);
});
const etudids_key = JSON.stringify(["etudids", url.origin, formsemestre_id]);
localStorage.setItem(etudids_key, JSON.stringify(etudids));
const noms_key = JSON.stringify(["noms", url.origin, formsemestre_id]);
localStorage.setItem(noms_key, JSON.stringify(noms));
},
"order": order_info,
}
);
update_buttons_labels(table); update_buttons_labels(table);
}); });
$('table.table_recap tbody').on('click', 'tr', function () { $('table.table_recap tbody').on('click', 'tr', function () {

View File

@ -1,18 +1,18 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends 'base.html' %} {% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
<h2>Système de gestion scolarité</h2> <h2>Système de gestion scolarité</h2>
<p>&copy; Emmanuel Viennet 2021</p> <p>&copy; Emmanuel Viennet 2023</p>
<p>Version {{ version }}</p> <p>Version {{ version }}</p>
<p>ScoDoc est un logiciel libre écrit en <p>ScoDoc est un logiciel libre écrit en
<a href="http://www.python.org" target="_blank" rel="noopener noreferrer">Python</a>. <a href="http://www.python.org" target="_blank" rel="noopener noreferrer">Python</a>.
Information et documentation sur <a href="https://scodoc.org" target="_blank">scodoc.org</a>. Information et documentation sur <a href="https://scodoc.org" target="_blank">scodoc.org</a>.
</p> </p>
<h2>Dernières évolutions</h2> <h2>Dernières évolutions</h2>

View File

@ -1,14 +1,14 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% macro render_field(field, auth_name=None) %} {% macro render_field(field, auth_name=None) %}
<tr style=""> <tr style="">
{% if auth_name %} {% if auth_name %}
<td class="wtf-field"> {{ field.label }}<span style="font-weight:700;"> ({{ auth_name }}):</span></td> <td class="wtf-field"> {{ field.label }}<span style="font-weight:700;"> ({{ auth_name }}):</span></td>
{% else %} {% else %}
<td class="wtf-field">{{ field.label }}</td> <td class="wtf-field">{{ field.label }}</td>
{% endif %} {% endif %}
<td class="wtf-field">{{ field(**kwargs)|safe }} <td class="wtf-field">{{ field(**kwargs)|safe }}
{% if field.errors %} {% if field.errors %}
<ul class=errors> <ul class=errors>
@ -23,29 +23,31 @@
{% block app_content %} {% block app_content %}
<h1>Modification du compte ScoDoc <tt>{{form.user_name.data}}</tt></h1> <h1>Modification du compte ScoDoc <tt>{{form.user_name.data}}</tt></h1>
<div class="help"> <div class="help">
<p>Identifiez-vous avec votre mot de passe actuel</p> <p>Identifiez-vous avec votre mot de passe actuel</p>
</div> </div>
<form method=post> <form method=post>
{{ form.user_name }} {{ form.user_name }}
{{ form.csrf_token }} {{ form.csrf_token }}
<table class="tf"><tbody> <table class="tf">
<tbody>
{{ render_field(form.old_password, size=14, auth_name=auth_username, {{ render_field(form.old_password, size=14, auth_name=auth_username,
style="padding:1px; margin-left: 1em; margin-top: 4px;") }} style="padding:1px; margin-left: 1em; margin-top: 4px;") }}
<tr> <tr>
<td colspan=""2"> <td colspan="" 2">
<p>Vous pouvez changer le mot de passe et/ou l'adresse email.</p> <p>Vous pouvez changer le mot de passe et/ou l'adresse email.</p>
<p>Les champs laissés vides ne seront pas modifiés.</p> <p>Les champs laissés vides ne seront pas modifiés.</p>
</td> </td>
</tr> </tr>
{{ render_field(form.new_password, size=14, {{ render_field(form.new_password, size=14,
style="padding:1px; margin-left: 1em; margin-top: 12px;") }} style="padding:1px; margin-left: 1em; margin-top: 12px;") }}
{{ render_field(form.bis_password, size=14, {{ render_field(form.bis_password, size=14,
style="padding:1px; margin-left: 1em; margin-top: 4px;") }} style="padding:1px; margin-left: 1em; margin-top: 4px;") }}
{{ render_field(form.email, size=40, {{ render_field(form.email, size=40,
style="padding:1px; margin-top: 12px;margin-bottom: 16px; margin-left: 1em;") }} style="padding:1px; margin-top: 12px;margin-bottom: 16px; margin-left: 1em;") }}
</tbody></table> </tbody>
<input type="submit" value="Valider"> </table>
<input type="submit" name="cancel" value="Annuler" style="margin-left: 1em;> <input type="submit" value="Valider">
<input type="submit" name="cancel" value="Annuler" style="margin-left: 1em;>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends 'base.html' %} {% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
@ -19,8 +19,8 @@
<p> <p>
<ul> <ul>
{% if ( {% if (
current_user.is_administrator() current_user.is_administrator()
or current_user.has_permission(Permission.ScoUsersAdmin, user.dept) or current_user.has_permission(Permission.ScoUsersAdmin, user.dept)
) %} ) %}
<li><a class="stdlink" href="{{ <li><a class="stdlink" href="{{
url_for( 'users.form_change_password', url_for( 'users.form_change_password',
@ -36,14 +36,14 @@
</li> </li>
{% endif %} {% endif %}
{% if ( {% if (
current_user.is_administrator() current_user.is_administrator()
or current_user.has_permission(Permission.ScoUsersAdmin, user.dept) or current_user.has_permission(Permission.ScoUsersAdmin, user.dept)
) %} ) %}
<li><a class="stdlink" href="{{ <li><a class="stdlink" href="{{
url_for('users.toggle_active_user', scodoc_dept=g.scodoc_dept, url_for('users.toggle_active_user', scodoc_dept=g.scodoc_dept,
user_name=user.user_name) user_name=user.user_name)
}}">{{"désactiver" if user.active else "activer"}} ce compte</a> }}">{{"désactiver" if user.active else "activer"}} ce compte</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -24,21 +24,23 @@
</button> </button>
<a class="navbar-brand" href="{{ url_for('scodoc.index') }}">ScoDoc</a> <a class="navbar-brand" href="{{ url_for('scodoc.index') }}">ScoDoc</a>
</div> </div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
{% if current_user.is_administrator() %} {% if current_user.is_administrator() %}
<li><a href="{{ url_for('scodoc.configuration') }}">Configuration</a></li> <li><a href="{{ url_for('scodoc.configuration') }}">Configuration</a></li>
{% endif %} {% endif %}
{% if g.scodoc_dept %} {% if g.scodoc_dept %}
<li><a href="{{ <li><a href="{{
url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)
}}">Dept. {{ g.scodoc_dept }}</a></li> }}">Dept. {{ g.scodoc_dept }}</a></li>
{% endif %} {% endif %}
{% if not current_user.is_anonymous and current_user.has_permission(current_user.Permission.RelationsEntreprisesView, None) and scu and scu.is_entreprises_enabled() %} {% if not current_user.is_anonymous and
current_user.has_permission(current_user.Permission.RelationsEntreprisesView, None) and scu and
scu.is_entreprises_enabled() %}
<li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li> <li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li>
{% endif %} {% endif %}
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if current_user.is_anonymous %} {% if current_user.is_anonymous %}
@ -62,9 +64,9 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %} {% for category, message in messages %}
<div class="alert alert-info alert-{{ category }}" role="alert">{{ message }}</div> <div class="alert alert-info alert-{{ category }}" role="alert">{{ message }}</div>
{% endfor %} {% endfor %}
{% endwith %} {% endwith %}
{# application content needs to be provided in the app_content block #} {# application content needs to be provided in the app_content block #}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "sco_page.html" %} {% extends "sco_page.j2" %}
{% block styles %} {% block styles %}
{{super()}} {{super()}}
@ -7,12 +7,12 @@
{% block app_content %} {% block app_content %}
{% include 'bul_head.html' %} {% include 'bul_head.j2' %}
<releve-but></releve-but> <releve-but></releve-but>
<script src="{{sco.scu.STATIC_DIR}}/js/releve-but.js"></script> <script src="{{sco.scu.STATIC_DIR}}/js/releve-but.js"></script>
{% include 'bul_foot.html' %} {% include 'bul_foot.j2' %}
<script> <script>
let dataSrc = "{{bul_url|safe}}"; let dataSrc = "{{bul_url|safe}}";

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "sco_page.html" %} {% extends "sco_page.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block styles %} {% block styles %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
@ -11,15 +11,15 @@
}}">{{formation.titre}} ({{formation.acronyme}})</a> }}">{{formation.titre}} ({{formation.acronyme}})</a>
</div> </div>
<div style="margin-top: 20px; margin-bottom: 20px;"> <div style="margin-top: 20px; margin-bottom: 20px;">
Référentiel actuellement associé: Référentiel actuellement associé:
{% if formation.referentiel_competence is not none %} {% if formation.referentiel_competence is not none %}
<b>{{ formation.referentiel_competence.specialite_long }}</b> <b>{{ formation.referentiel_competence.specialite_long }}</b>
<a href="{{ <a href="{{
url_for('notes.refcomp_desassoc_formation', scodoc_dept=g.scodoc_dept, formation_id=formation.id) url_for('notes.refcomp_desassoc_formation', scodoc_dept=g.scodoc_dept, formation_id=formation.id)
}}" class="stdlink">supprimer</a> }}" class="stdlink">supprimer</a>
{% else %} {% else %}
<b>aucun</b> <b>aucun</b>
{% endif %} {% endif %}
<div class="row" style="margin-top: 20px;"> <div class="row" style="margin-top: 20px;">
<div class="col-md-4"> <div class="col-md-4">

View File

@ -1,30 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Charger un référentiel de compétences</h1>
<div class="row">
<div class="col-md-5">
{{ wtf.quick_form(form) }}
</div>
</div>
<div class="row">
<div class="col-md-5">
<ul>
<li>
<a href="{{ url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept, ) }}">
Liste des référentiels de compétences chargés</a>
</li>
{% if formation is not none %}
<li>
<a href="{{ url_for('notes.refcomp_assoc_formation', scodoc_dept=g.scodoc_dept, formation_id=formation.id) }}">
Association à la formation {{ formation.acronyme }}</a>
</li>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,31 @@
{# -*- mode: jinja-html -*- #}
{% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Charger un référentiel de compétences</h1>
<div class="row">
<div class="col-md-5">
{{ wtf.quick_form(form) }}
</div>
</div>
<div class="row">
<div class="col-md-5">
<ul>
<li>
<a href="{{ url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept, ) }}">
Liste des référentiels de compétences chargés</a>
</li>
{% if formation is not none %}
<li>
<a
href="{{ url_for('notes.refcomp_assoc_formation', scodoc_dept=g.scodoc_dept, formation_id=formation.id) }}">
Association à la formation {{ formation.acronyme }}</a>
</li>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "sco_page.html" %} {% extends "sco_page.j2" %}
{% block styles %} {% block styles %}
{{super()}} {{super()}}
{% endblock %} {% endblock %}

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "sco_page.html" %} {% extends "sco_page.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,132 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% macro render_field(field, with_label=True) %}
<div>
{% if with_label %}
<span class="wtf-field">{{ field.label }} :</span>
{% endif %}
<span class="wtf-field">{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</span>
</div>
{% endmacro %}
{% macro render_add_logo(add_logo_form) %}
<details {{ add_logo_form.opened() }}>
<summary>
<h3>Ajouter un logo</h3>
</summary>
<div>
{{ render_field(add_logo_form.name) }}
{{ render_field(add_logo_form.upload) }}
{{ render_field(add_logo_form.do_insert, False, onSubmit="submit_form") }}
</div>
</details>
{% endmacro %}
{% macro render_logo(dept_form, logo_form) %}
<details {{ logo_form.opened() }}>
{{ logo_form.hidden_tag() }}
<summary>
{% if logo_form.titre %}
<h3 class="titre_logo">{{ logo_form.titre }}</h3>
{% if logo_form.description %}
<div class="sco_help">{{ logo_form.description }}</div>
{% endif %}
{% else %}
<h3 class="titre_logo">Logo personalisé: {{ logo_form.logo_id.data }}</h3>
{% if logo_form.description %}
<div class="sco_help">{{ logo_form.description }}</div>
{% endif %}
{% endif %}
</summary>
<div class="content">
<div class="image_logo">
<img src="{{ logo_form.logo.get_url_small() }}" alt="pas de logo chargé" />
</div>
<div class="infos_logo">
<h4>{{ logo_form.logo.logoname }} (Format: {{ logo_form.logo.suffix }})</h4>
Taille: {{ logo_form.logo.size }} px
{% if logo_form.logo.mm %} &nbsp; / &nbsp; {{ logo_form.logo.mm }} mm {% endif %}<br />
Aspect ratio: {{ logo_form.logo.aspect_ratio }}<br />
Usage: <span style="font-family: system-ui">{{ logo_form.logo.get_usage() }}</span>
</div>
<div class="actions_logo">
<div class="action_label">Modifier l'image</div>
<div class="action_button">
<span class="wtf-field">{{ render_field(logo_form.upload, False, onchange="submit_form()") }}</span>
</div>
{% if logo_form.can_delete %}
<div class="action_label">Renommer</div>
<div class="action_button">
{{ render_field(logo_form.new_name, False) }}
{{ render_field(logo_form.do_rename, False, onSubmit="submit_form()") }}
</div>
<div class="action_label">Supprimer l'image</div>
<div class="action_button">
{{ render_field(logo_form.do_delete, False, onSubmit="submit_form()") }}
</div>
{% endif %}
</div>
</div>
</details>
{% endmacro %}
{% macro render_logos(dept_form) %}
{% for logo_entry in dept_form.logos.entries %}
{% set logo_form = logo_entry.form %}
{{ render_logo(dept_form, logo_form) }}
{% else %}
<p class="logo-titre_logo">
<h3 class="titre_logo">Aucun logo défini en propre à ce département</h3>
</p>
{% endfor %}
{% endmacro %}
{% block app_content %}
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
<script src="/ScoDoc/static/js/config_logos.js"></script>
<form id="config_logos_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
{{ form.hidden_tag() }}
<div class="configuration_logo">
<h1>Bibliothèque de logos</h1>
{% for dept_entry in form.depts.entries %}
{% set dept_form = dept_entry.form %}
{{ dept_entry.form.hidden_tag() }}
<details {{ dept_form.opened() }}>
<summary>
<span class="entete_dept">
{% if dept_entry.form.is_local() %}
<h2>Département {{ dept_form.dept_name.data }}</h2>
<h3 class="effectifs">{{ dept_form.count() }}</h3>
<div class="sco_help">Les paramètres donnés sont spécifiques à ce département.<br />
Les logos du département se substituent aux logos de même nom définis globalement:</div>
{% else %}
<h2>Logos généraux</h2>
<h3 class="effectifs">{{ dept_form.count() }}</h3>
<div class="sco_help">Les images de cette section sont utilisé pour tous les départements,
mais peuvent être redéfinies localement au niveau de chaque département
(il suffit de définir un logo local de même nom)</div>
{% endif %}
</span>
</summary>
<div>
{{ render_logos(dept_form) }}
{{ render_add_logo(dept_form.add_logo.form) }}
</div>
</details>
{% endfor %}
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,132 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% macro render_field(field, with_label=True) %}
<div>
{% if with_label %}
<span class="wtf-field">{{ field.label }} :</span>
{% endif %}
<span class="wtf-field">{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</span>
</div>
{% endmacro %}
{% macro render_add_logo(add_logo_form) %}
<details {{ add_logo_form.opened() }}>
<summary>
<h3>Ajouter un logo</h3>
</summary>
<div>
{{ render_field(add_logo_form.name) }}
{{ render_field(add_logo_form.upload) }}
{{ render_field(add_logo_form.do_insert, False, onSubmit="submit_form") }}
</div>
</details>
{% endmacro %}
{% macro render_logo(dept_form, logo_form) %}
<details {{ logo_form.opened() }}>
{{ logo_form.hidden_tag() }}
<summary>
{% if logo_form.titre %}
<h3 class="titre_logo">{{ logo_form.titre }}</h3>
{% if logo_form.description %}
<div class="sco_help">{{ logo_form.description }}</div>
{% endif %}
{% else %}
<h3 class="titre_logo">Logo personalisé: {{ logo_form.logo_id.data }}</h3>
{% if logo_form.description %}
<div class="sco_help">{{ logo_form.description }}</div>
{% endif %}
{% endif %}
</summary>
<div class="content">
<div class="image_logo">
<img src="{{ logo_form.logo.get_url_small() }}" alt="pas de logo chargé" />
</div>
<div class="infos_logo">
<h4>{{ logo_form.logo.logoname }} (Format: {{ logo_form.logo.suffix }})</h4>
Taille: {{ logo_form.logo.size }} px
{% if logo_form.logo.mm %} &nbsp; / &nbsp; {{ logo_form.logo.mm }} mm {% endif %}<br />
Aspect ratio: {{ logo_form.logo.aspect_ratio }}<br />
Usage: <span style="font-family: system-ui">{{ logo_form.logo.get_usage() }}</span>
</div>
<div class="actions_logo">
<div class="action_label">Modifier l'image</div>
<div class="action_button">
<span class="wtf-field">{{ render_field(logo_form.upload, False, onchange="submit_form()") }}</span>
</div>
{% if logo_form.can_delete %}
<div class="action_label">Renommer</div>
<div class="action_button">
{{ render_field(logo_form.new_name, False) }}
{{ render_field(logo_form.do_rename, False, onSubmit="submit_form()") }}
</div>
<div class="action_label">Supprimer l'image</div>
<div class="action_button">
{{ render_field(logo_form.do_delete, False, onSubmit="submit_form()") }}
</div>
{% endif %}
</div>
</div>
</details>
{% endmacro %}
{% macro render_logos(dept_form) %}
{% for logo_entry in dept_form.logos.entries %}
{% set logo_form = logo_entry.form %}
{{ render_logo(dept_form, logo_form) }}
{% else %}
<p class="logo-titre_logo">
<h3 class="titre_logo">Aucun logo défini en propre à ce département</h3>
</p>
{% endfor %}
{% endmacro %}
{% block app_content %}
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
<script src="/ScoDoc/static/js/config_logos.js"></script>
<form id="config_logos_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
{{ form.hidden_tag() }}
<div class="configuration_logo">
<h1>Bibliothèque de logos</h1>
{% for dept_entry in form.depts.entries %}
{% set dept_form = dept_entry.form %}
{{ dept_entry.form.hidden_tag() }}
<details {{ dept_form.opened() }}>
<summary>
<span class="entete_dept">
{% if dept_entry.form.is_local() %}
<h2>Département {{ dept_form.dept_name.data }}</h2>
<h3 class="effectifs">{{ dept_form.count() }}</h3>
<div class="sco_help">Les paramètres donnés sont spécifiques à ce département.<br />
Les logos du département se substituent aux logos de même nom définis globalement:</div>
{% else %}
<h2>Logos généraux</h2>
<h3 class="effectifs">{{ dept_form.count() }}</h3>
<div class="sco_help">Les images de cette section sont utilisé pour tous les départements,
mais peuvent être redéfinies localement au niveau de chaque département
(il suffit de définir un logo local de même nom)</div>
{% endif %}
</span>
</summary>
<div>
{{ render_logos(dept_form) }}
{{ render_add_logo(dept_form.add_logo.form) }}
</div>
</details>
{% endfor %}
</div>
</form>
{% endblock %}

View File

@ -1,12 +1,12 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends 'base.html' %} {% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% macro render_field(field, with_label=True) %} {% macro render_field(field, with_label=True) %}
<div> <div>
{% if with_label %} {% if with_label %}
<span class="wtf-field">{{ field.label }} :</span> <span class="wtf-field">{{ field.label }} :</span>
{% endif %} {% endif %}
<span class="wtf-field">{{ field(**kwargs)|safe }} <span class="wtf-field">{{ field(**kwargs)|safe }}
{% if field.errors %} {% if field.errors %}
<ul class=errors> <ul class=errors>
@ -22,10 +22,10 @@
{% block app_content %} {% block app_content %}
<h1>Configuration générale</h1> <h1>Configuration générale</h1>
<div class="sco_help greenboldtext">Les paramètres donnés ici s'appliquent à tout ScoDoc (tous les départements).</div> <div class="sco_help greenboldtext">Les paramètres donnés ici s'appliquent à tout ScoDoc (tous les départements).</div>
<section> <section>
<h2>Calcul des "bonus" définis par l'établissement</h2> <h2>Calcul des "bonus" définis par l'établissement</h2>
<form id="configuration_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate> <form id="configuration_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
{{ form_bonus.hidden_tag() }} {{ form_bonus.hidden_tag() }}
<div class="row"> <div class="row">
@ -39,35 +39,36 @@
</section> </section>
<section> <section>
<h2>Gestion des images: logos, signatures, ...</h2> <h2>Gestion des images: logos, signatures, ...</h2>
<div class="sco_help">Ces images peuvent être intégrées dans les documents <div class="sco_help">Ces images peuvent être intégrées dans les documents
générés par ScoDoc: bulletins, PV, etc. générés par ScoDoc: bulletins, PV, etc.
</div> </div>
<p><a class="stdlink" href="{{url_for('scodoc.configure_logos')}}">configuration des images et logos</a> <p><a class="stdlink" href="{{url_for('scodoc.configure_logos')}}">configuration des images et logos</a>
</p> </p>
</section> </section>
<section> <section>
<h2>Exports Apogée</h2> <h2>Exports Apogée</h2>
<p><a class="stdlink" href="{{url_for('scodoc.config_codes_decisions')}}">configuration des codes de décision</a></p> <p><a class="stdlink" href="{{url_for('scodoc.config_codes_decisions')}}">configuration des codes de décision</a>
</p>
</section> </section>
<h2>Utilisateurs</h2> <h2>Utilisateurs</h2>
<section> <section>
<p><a class="stdlink" href="{{url_for('auth.reset_standard_roles_permissions')}}">remettre <p><a class="stdlink" href="{{url_for('auth.reset_standard_roles_permissions')}}">remettre
les permissions des rôles standards à leurs valeurs par défaut</a> (efface les modifications apportées) les permissions des rôles standards à leurs valeurs par défaut</a> (efface les modifications apportées)
</p> </p>
</section> </section>
<h2>ScoDoc</h2> <h2>ScoDoc</h2>
<form id="configuration_form_scodoc" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate> <form id="configuration_form_scodoc" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
{{ form_scodoc.hidden_tag() }} {{ form_scodoc.hidden_tag() }}
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
{{ wtf.quick_form(form_scodoc) }} {{ wtf.quick_form(form_scodoc) }}
</div>
</div> </div>
</form> </div>
</form>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
@ -75,20 +76,19 @@
<script> <script>
function update_bonus_description() { function update_bonus_description() {
var query = "/ScoDoc/get_bonus_description/" + $("#configuration_form select")[0].value; var query = "/ScoDoc/get_bonus_description/" + $("#configuration_form select")[0].value;
$.get(query, '', function (data) { $.get(query, '', function (data) {
$("#bonus_description").html(data); $("#bonus_description").html(data);
}); });
} }
$(function () {
$("#configuration_form select").change(function () {
update_bonus_description();
});
$(function()
{
$("#configuration_form select").change(function(){
update_bonus_description(); update_bonus_description();
}); });
update_bonus_description();
});
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,4 @@
{# -*- mode: jinja-html -*- #} {% extends 'base.j2' %}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
@ -7,7 +6,7 @@
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
<div style="margin-top: 16px;"> <div style="margin-top: 16px;">
{{ explanation }} {{ explanation|safe }}
</div> </div>
<div style="margin-top: 16px;"> <div style="margin-top: 16px;">
<form method="post"> <form method="post">

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.html" %} {% extends "base.j2" %}
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -1,47 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends "sco_page.html" %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h2>Opérations dans le département {{g.scodoc_dept}}</h2>
<table id="dept_news" class="table table-striped">
<thead>
<tr>
<th>Date</th>
<th>Type</th>
<th>Auteur</th>
<th>Détail</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block scripts %}
{{super()}}
<script>
$(document).ready(function () {
$('#dept_news').DataTable({
ajax: '{{url_for("scolar.dept_news_json", scodoc_dept=g.scodoc_dept)}}',
serverSide: true,
columns: [
{
data: {
_: "date.display",
sort: "date.timestamp"
}
},
{data: 'type', searchable: false},
{data: 'authenticated_user', orderable: false, searchable: true},
{data: 'text', orderable: false, searchable: true}
],
"order": [[ 0, "desc" ]]
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,47 @@
{# -*- mode: jinja-html -*- #}
{% extends "sco_page.j2" %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h2>Opérations dans le département {{g.scodoc_dept}}</h2>
<table id="dept_news" class="table table-striped">
<thead>
<tr>
<th>Date</th>
<th>Type</th>
<th>Auteur</th>
<th>Détail</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block scripts %}
{{super()}}
<script>
$(document).ready(function () {
$('#dept_news').DataTable({
ajax: '{{url_for("scolar.dept_news_json", scodoc_dept=g.scodoc_dept)}}',
serverSide: true,
columns: [
{
data: {
_: "date.display",
sort: "date.timestamp"
}
},
{ data: 'type', searchable: false },
{ data: 'authenticated_user', orderable: false, searchable: true },
{ data: 'text', orderable: false, searchable: true }
],
"order": [[0, "desc"]]
});
});
</script>
{% endblock %}

View File

@ -1,104 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.fiche_entreprise', entreprise_id=entreprise.id) }}" class="breadcrumbs_link">Fiche entreprise</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Contacts</a>
</li>
</ul>
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des contacts</h1>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" style="margin-bottom:10px;" href="{{ url_for('entreprises.add_contact', entreprise_id=entreprise.id) }}">Ajouter contact</a>
{% endif %}
<table id="table-contacts">
<thead>
<tr>
<td data-priority="">Date</td>
<td data-priority="">Utilisateur</td>
<td data-priority="">Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for contact in contacts %}
<tr>
<td>{{ contact.date.strftime('%d/%m/%Y %Hh%M') }}</td>
<td>{{ contact.user|get_nomcomplet_by_id }}</td>
<td>{{ contact.notes }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a href="{{ url_for('entreprises.edit_contact', entreprise_id=entreprise.id, contact_id=contact.id) }}">Modifier</a></li>
<li><a href="{{ url_for('entreprises.delete_contact', entreprise_id=entreprise.id, contact_id=contact.id) }}" style="color:red">Supprimer</a></li>
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Date</td>
<td>Utilisateur</td>
<td>Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-contacts',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,109 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.fiche_entreprise', entreprise_id=entreprise.id) }}"
class="breadcrumbs_link">Fiche entreprise</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Contacts</a>
</li>
</ul>
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des contacts</h1>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" style="margin-bottom:10px;"
href="{{ url_for('entreprises.add_contact', entreprise_id=entreprise.id) }}">Ajouter contact</a>
{% endif %}
<table id="table-contacts">
<thead>
<tr>
<td data-priority="">Date</td>
<td data-priority="">Utilisateur</td>
<td data-priority="">Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for contact in contacts %}
<tr>
<td>{{ contact.date.strftime('%d/%m/%Y %Hh%M') }}</td>
<td>{{ contact.user|get_nomcomplet_by_id }}</td>
<td>{{ contact.notes }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a
href="{{ url_for('entreprises.edit_contact', entreprise_id=entreprise.id, contact_id=contact.id) }}">Modifier</a>
</li>
<li><a href="{{ url_for('entreprises.delete_contact', entreprise_id=entreprise.id, contact_id=contact.id) }}"
style="color:red">Supprimer</a></li>
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Date</td>
<td>Utilisateur</td>
<td>Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-contacts',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -1,93 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations <a href="{{ url_for('entreprises.logs') }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des correspondants</h1>
<table id="table-correspondants">
<thead>
<tr>
<td data-priority="1">Nom</td>
<td data-priority="3">Prenom</td>
<td data-priority="4">Téléphone</td>
<td data-priority="5">Mail</td>
<td data-priority="6">Poste</td>
<td data-priority="7">Service</td>
<td data-priority="2">Entreprise</td>
</tr>
</thead>
<tbody>
{% for correspondant, site in correspondants %}
<tr>
<td>{{ correspondant.nom }}</td>
<td>{{ correspondant.prenom }}</td>
<td>{{ correspondant.telephone }}</td>
<td>{{ correspondant.mail }}</td>
<td>{{ correspondant.poste}}</td>
<td>{{ correspondant.service}}</td>
<td><a href="{{ url_for('entreprises.fiche_entreprise', entreprise_id=site.entreprise.id) }}">{{ site.nom }}</a></td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Nom</td>
<td>Prenom</td>
<td>Téléphone</td>
<td>Mail</td>
<td>Poste</td>
<td>Service</td>
<td>Entreprise</td>
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-correspondants',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,95 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations <a href="{{ url_for('entreprises.logs') }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par
{{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des correspondants</h1>
<table id="table-correspondants">
<thead>
<tr>
<td data-priority="1">Nom</td>
<td data-priority="3">Prenom</td>
<td data-priority="4">Téléphone</td>
<td data-priority="5">Mail</td>
<td data-priority="6">Poste</td>
<td data-priority="7">Service</td>
<td data-priority="2">Entreprise</td>
</tr>
</thead>
<tbody>
{% for correspondant, site in correspondants %}
<tr>
<td>{{ correspondant.nom }}</td>
<td>{{ correspondant.prenom }}</td>
<td>{{ correspondant.telephone }}</td>
<td>{{ correspondant.mail }}</td>
<td>{{ correspondant.poste}}</td>
<td>{{ correspondant.service}}</td>
<td><a href="{{ url_for('entreprises.fiche_entreprise', entreprise_id=site.entreprise.id) }}">{{
site.nom }}</a></td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Nom</td>
<td>Prenom</td>
<td>Téléphone</td>
<td>Mail</td>
<td>Poste</td>
<td>Service</td>
<td>Entreprise</td>
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-correspondants',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -1,133 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations <a href="{{ url_for('entreprises.logs') }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container boutons">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.add_entreprise') }}">Ajouter une entreprise</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_donnees') }}">Importer des données</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and entreprises %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_donnees') }}">Exporter des données</a>
{% endif %}
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des entreprises</h1>
{% if form %}
<form id="form-entreprise-filter" method="POST" action="">
{{ form.hidden_tag() }}
<div><input id="active" name="active" type="checkbox" onChange="form.submit()" {% if checked[0] %} checked {% endif %}> {{ form.active.label }}</div>
<div><input id="association" name="association" type="checkbox" onChange="form.submit()" {% if checked[1] %} checked {% endif %}> {{ form.association.label }}</div>
<div><input id="siret_provisoire" name="siret_provisoire" type="checkbox" onChange="form.submit()" {% if checked[2] %} checked {% endif %}> {{ form.siret_provisoire.label }}</div>
</form>
{% endif %}
<table id="table-entreprises">
<thead>
<tr>
<td data-priority="2">SIRET</td>
<td data-priority="1">Nom</td>
<td data-priority="4">Adresse</td>
<td data-priority="6">Code postal</td>
<td data-priority="5">Ville</td>
<td data-priority="7">Pays</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="3">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for entreprise in entreprises %}
<tr>
<td><a href="{{ url_for('entreprises.fiche_entreprise', entreprise_id=entreprise.id) }}" style="{% if not entreprise.active %}color:red;{% endif %}{% if entreprise.siret_provisoire %}font-weight:bold;{% endif %}" >{{ entreprise.siret }}</a></td>
<td>{{ entreprise.nom }}</td>
<td>{{ entreprise.adresse }}</td>
<td>{{ entreprise.codepostal }}</td>
<td>{{ entreprise.ville }}</td>
<td>{{ entreprise.pays }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a href="{{ url_for('entreprises.edit_entreprise', entreprise_id=entreprise.id) }}">Modifier</a></li>
{% if entreprise.active %}
<li><a href="{{ url_for('entreprises.fiche_entreprise_desactiver', entreprise_id=entreprise.id)}}" style="color:red">Désactiver</a></li>
{% else %}
<li><a href="{{ url_for('entreprises.fiche_entreprise_activer', entreprise_id=entreprise.id)}}" style="color:lightgreen">Activer</a></li>
{% endif %}
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>SIRET</td>
<td>Nom</td>
<td>Adresse</td>
<td>Code postal</td>
<td>Ville</td>
<td>Pays</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-entreprises',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,143 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations <a href="{{ url_for('entreprises.logs') }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par
{{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container boutons">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.add_entreprise') }}">Ajouter une entreprise</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_donnees') }}">Importer des données</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and entreprises %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_donnees') }}">Exporter des données</a>
{% endif %}
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des entreprises</h1>
{% if form %}
<form id="form-entreprise-filter" method="POST" action="">
{{ form.hidden_tag() }}
<div><input id="active" name="active" type="checkbox" onChange="form.submit()" {% if checked[0] %} checked {%
endif %}> {{ form.active.label }}</div>
<div><input id="association" name="association" type="checkbox" onChange="form.submit()" {% if checked[1] %}
checked {% endif %}> {{ form.association.label }}</div>
<div><input id="siret_provisoire" name="siret_provisoire" type="checkbox" onChange="form.submit()" {% if
checked[2] %} checked {% endif %}> {{ form.siret_provisoire.label }}</div>
</form>
{% endif %}
<table id="table-entreprises">
<thead>
<tr>
<td data-priority="2">SIRET</td>
<td data-priority="1">Nom</td>
<td data-priority="4">Adresse</td>
<td data-priority="6">Code postal</td>
<td data-priority="5">Ville</td>
<td data-priority="7">Pays</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="3">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for entreprise in entreprises %}
<tr>
<td><a href="{{ url_for('entreprises.fiche_entreprise', entreprise_id=entreprise.id) }}"
style="{% if not entreprise.active %}color:red;{% endif %}{% if entreprise.siret_provisoire %}font-weight:bold;{% endif %}">{{
entreprise.siret }}</a></td>
<td>{{ entreprise.nom }}</td>
<td>{{ entreprise.adresse }}</td>
<td>{{ entreprise.codepostal }}</td>
<td>{{ entreprise.ville }}</td>
<td>{{ entreprise.pays }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a
href="{{ url_for('entreprises.edit_entreprise', entreprise_id=entreprise.id) }}">Modifier</a>
</li>
{% if entreprise.active %}
<li><a href="{{ url_for('entreprises.fiche_entreprise_desactiver', entreprise_id=entreprise.id)}}"
style="color:red">Désactiver</a></li>
{% else %}
<li><a href="{{ url_for('entreprises.fiche_entreprise_activer', entreprise_id=entreprise.id)}}"
style="color:lightgreen">Activer</a></li>
{% endif %}
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>SIRET</td>
<td>Nom</td>
<td>Adresse</td>
<td>Code postal</td>
<td>Ville</td>
<td>Pays</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-entreprises',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -1,95 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations</h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des entreprises à valider</h1>
<table id="table-entreprises-validation">
<thead>
<tr>
<td data-priority="3">SIRET</td>
<td data-priority="1">Nom</td>
<td data-priority="4">Adresse</td>
<td data-priority="5">Code postal</td>
<td data-priority="6">Ville</td>
<td data-priority="7">Pays</td>
<td data-priority="2">Action</td>
</tr>
</thead>
<tbody>
{% for entreprise in entreprises %}
<tr class="table-row active">
<th><a href="{{ url_for('entreprises.fiche_entreprise_validation', entreprise_id=entreprise.id) }}">{{ entreprise.siret }}</a></th>
<th>{{ entreprise.nom }}</th>
<th>{{ entreprise.adresse }}</th>
<th>{{ entreprise.codepostal }}</th>
<th>{{ entreprise.ville }}</th>
<th>{{ entreprise.pays }}</th>
<th>
<a class="btn btn-default" href="{{ url_for('entreprises.fiche_entreprise_validation', entreprise_id=entreprise.id) }}">Voir</a>
</th>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>SIRET</td>
<td>Nom</td>
<td>Adresse</td>
<td>Code postal</td>
<td>Ville</td>
<td>Pays</td>
<td>Action</td>
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-entreprises-validation',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,98 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations</h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par
{{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des entreprises à valider</h1>
<table id="table-entreprises-validation">
<thead>
<tr>
<td data-priority="3">SIRET</td>
<td data-priority="1">Nom</td>
<td data-priority="4">Adresse</td>
<td data-priority="5">Code postal</td>
<td data-priority="6">Ville</td>
<td data-priority="7">Pays</td>
<td data-priority="2">Action</td>
</tr>
</thead>
<tbody>
{% for entreprise in entreprises %}
<tr class="table-row active">
<th><a href="{{ url_for('entreprises.fiche_entreprise_validation', entreprise_id=entreprise.id) }}">{{
entreprise.siret }}</a></th>
<th>{{ entreprise.nom }}</th>
<th>{{ entreprise.adresse }}</th>
<th>{{ entreprise.codepostal }}</th>
<th>{{ entreprise.ville }}</th>
<th>{{ entreprise.pays }}</th>
<th>
<a class="btn btn-default"
href="{{ url_for('entreprises.fiche_entreprise_validation', entreprise_id=entreprise.id) }}">Voir</a>
</th>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>SIRET</td>
<td>Nom</td>
<td>Adresse</td>
<td>Code postal</td>
<td>Ville</td>
<td>Pays</td>
<td>Action</td>
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-entreprises-validation',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -1,14 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<h2>Erreur !</h2>
{{ e }}
<p>
<a href="{{ url_for('entreprises.index') }}">Retour</a>
</p>
{% endblock %}

View File

@ -0,0 +1,14 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block app_content %}
<h2>Erreur !</h2>
{{ e }}
<p>
<a href="{{ url_for('entreprises.index') }}">Retour</a>
</p>
{% endblock %}

View File

@ -1,227 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Fiche entreprise</a>
</li>
</ul>
</div>
{% if logs %}
<div class="container">
<h3>Dernières opérations sur cette fiche <a href="{{ url_for('entreprises.logs_entreprise', entreprise_id=entreprise.id) }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li>
<span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span>
<span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container fiche-entreprise">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
{% if not entreprise.active %}
<div class="info-active">
La fiche entreprise est désactivée<br>
{% if entreprise.notes_active != "" %}
Notes : {{ entreprise.notes_active }}
{% endif %}
</div>
{% endif %}
<div class="entreprise">
<div>
SIRET {% if entreprise.siret_provisoire %}provisoire{% endif %} : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}<br>
{% if entreprise.association %}
Association
{% endif %}
</div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div>
Taxe d'apprentissage<br>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_taxe_apprentissage', entreprise_id=entreprise.id) }}">Ajouter taxe apprentissage</a>
<div class="taxe-apprentissage">
<ul id="liste-taxes-apprentissages">
{% if not taxes|check_taxe_now %}
<li>année actuelle : non versée</li>
{% endif %}
{% for taxe in taxes %}
<li>
<a href="{{ url_for('entreprises.delete_taxe_apprentissage', entreprise_id=entreprise.id, taxe_id=taxe.id) }}"><img title="Supprimer taxe d'apprentissage" alt="supprimer" width="10" height="9" border="0" src="/ScoDoc/static/icons/delete_small_img.png" /></a>
<a href="{{ url_for('entreprises.edit_taxe_apprentissage', entreprise_id=entreprise.id, taxe_id=taxe.id) }}">{{ taxe.annee }}</a> : {{ taxe.montant }} euros {% if taxe.notes %}- {{ taxe.notes}} {% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_entreprise', entreprise_id=entreprise.id) }}">Modifier</a>
{% if entreprise.active %}
<a class="btn btn-danger" href="{{ url_for('entreprises.fiche_entreprise_desactiver', entreprise_id=entreprise.id) }}">Désactiver</a>
{% else %}
<a class="btn btn-success" href="{{ url_for('entreprises.fiche_entreprise_activer', entreprise_id=entreprise.id) }}">Activer</a>
{% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.add_site', entreprise_id=entreprise.id) }}">Ajouter site</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_offre', entreprise_id=entreprise.id) }}">Ajouter offre</a>
{% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.contacts', entreprise_id=entreprise.id) }}">Liste contacts</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.offres_expirees', entreprise_id=entreprise.id) }}">Voir les offres expirées</a>
</div>
<div class="sites-et-offres">
{% if entreprise.sites %}
<div>
<h3>Sites</h3>
{% for site in entreprise.sites %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div>
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_site', entreprise_id=entreprise.id, site_id=site.id) }}">Modifier</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_correspondant', entreprise_id=entreprise.id, site_id=site.id) }}">Ajouter correspondant</a>
</div>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
{% for correspondant in site.correspondants %}
{% include 'entreprises/_correspondant.html' %}
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{% if offres %}
<div>
<h3>Offres - <a href="{{ url_for('entreprises.offres_expirees', entreprise_id=entreprise.id) }}">Voir les offres expirées</a></h3>
{% for offre, files, offre_depts, correspondant in offres %}
{% include 'entreprises/_offre.html' %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div style="margin-bottom: 10px;">
<h3>Liste des stages et apprentissages réalisés au sein de l'entreprise</h3>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" href="{{ url_for('entreprises.add_stage_apprentissage', entreprise_id=entreprise.id) }}" style="margin-bottom:10px;">Ajouter stage ou apprentissage</a>
{% endif %}
<table id="table-stages-apprentissages">
<thead>
<tr>
<td data-priority="3">Date début</td>
<td data-priority="4">Date fin</td>
<td data-priority="5">Durée</td>
<td data-priority="2">Type</td>
<td data-priority="1">Étudiant</td>
<td data-priority="6">Formation</td>
<td data-priority="7">Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="3">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for stage_apprentissage, etudiant in stages_apprentissages %}
<tr>
<td>{{ stage_apprentissage.date_debut.strftime('%d/%m/%Y') }}</td>
<td>{{ stage_apprentissage.date_fin.strftime('%d/%m/%Y') }}</td>
<td>{{ (stage_apprentissage.date_fin-stage_apprentissage.date_debut).days//7 }} semaines</td>
<td>{{ stage_apprentissage.type_offre }}</td>
<td><a href="{{ url_for('scolar.ficheEtud', scodoc_dept=etudiant.dept_id|get_dept_acronym, etudid=stage_apprentissage.etudid) }}">{{ etudiant.nom|format_nom }} {{ etudiant.prenom|format_prenom }}</a></td>
<td>{% if stage_apprentissage.formation_text %}{{ stage_apprentissage.formation_text }}{% endif %}</td>
<td>{{ stage_apprentissage.notes }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a href="{{ url_for('entreprises.edit_stage_apprentissage', entreprise_id=entreprise.id, stage_apprentissage_id=stage_apprentissage.id) }}">Modifier</a></li>
<li><a href="{{ url_for('entreprises.delete_stage_apprentissage', entreprise_id=entreprise.id, stage_apprentissage_id=stage_apprentissage.id) }}" style="color:red">Supprimer</a></li>
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Date début</td>
<td>Date fin</td>
<td>Durée</td>
<td>Type</td>
<td>Étudiant</td>
<td>Formation</td>
<td>Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-stages-apprentissages',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,253 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.index') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Fiche entreprise</a>
</li>
</ul>
</div>
{% if logs %}
<div class="container">
<h3>Dernières opérations sur cette fiche <a
href="{{ url_for('entreprises.logs_entreprise', entreprise_id=entreprise.id) }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li>
<span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span>
<span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container fiche-entreprise">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
{% if not entreprise.active %}
<div class="info-active">
La fiche entreprise est désactivée<br>
{% if entreprise.notes_active != "" %}
Notes : {{ entreprise.notes_active }}
{% endif %}
</div>
{% endif %}
<div class="entreprise">
<div>
SIRET {% if entreprise.siret_provisoire %}provisoire{% endif %} : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}<br>
{% if entreprise.association %}
Association
{% endif %}
</div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div>
Taxe d'apprentissage<br>
<a class="btn btn-primary"
href="{{ url_for('entreprises.add_taxe_apprentissage', entreprise_id=entreprise.id) }}">Ajouter taxe
apprentissage</a>
<div class="taxe-apprentissage">
<ul id="liste-taxes-apprentissages">
{% if not taxes|check_taxe_now %}
<li>année actuelle : non versée</li>
{% endif %}
{% for taxe in taxes %}
<li>
<a
href="{{ url_for('entreprises.delete_taxe_apprentissage', entreprise_id=entreprise.id, taxe_id=taxe.id) }}"><img
title="Supprimer taxe d'apprentissage" alt="supprimer" width="10" height="9" border="0"
src="/ScoDoc/static/icons/delete_small_img.png" /></a>
<a
href="{{ url_for('entreprises.edit_taxe_apprentissage', entreprise_id=entreprise.id, taxe_id=taxe.id) }}">{{
taxe.annee }}</a> : {{ taxe.montant }} euros {% if taxe.notes %}- {{ taxe.notes}} {% endif
%}
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary"
href="{{ url_for('entreprises.edit_entreprise', entreprise_id=entreprise.id) }}">Modifier</a>
{% if entreprise.active %}
<a class="btn btn-danger"
href="{{ url_for('entreprises.fiche_entreprise_desactiver', entreprise_id=entreprise.id) }}">Désactiver</a>
{% else %}
<a class="btn btn-success"
href="{{ url_for('entreprises.fiche_entreprise_activer', entreprise_id=entreprise.id) }}">Activer</a>
{% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.add_site', entreprise_id=entreprise.id) }}">Ajouter
site</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_offre', entreprise_id=entreprise.id) }}">Ajouter
offre</a>
{% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.contacts', entreprise_id=entreprise.id) }}">Liste
contacts</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.offres_expirees', entreprise_id=entreprise.id) }}">Voir
les offres expirées</a>
</div>
<div class="sites-et-offres">
{% if entreprise.sites %}
<div>
<h3>Sites</h3>
{% for site in entreprise.sites %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div>
<a class="btn btn-primary"
href="{{ url_for('entreprises.edit_site', entreprise_id=entreprise.id, site_id=site.id) }}">Modifier</a>
<a class="btn btn-primary"
href="{{ url_for('entreprises.add_correspondant', entreprise_id=entreprise.id, site_id=site.id) }}">Ajouter
correspondant</a>
</div>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
{% for correspondant in site.correspondants %}
{% include 'entreprises/_correspondant.html' %}
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{% if offres %}
<div>
<h3>Offres - <a href="{{ url_for('entreprises.offres_expirees', entreprise_id=entreprise.id) }}">Voir les
offres expirées</a></h3>
{% for offre, files, offre_depts, correspondant in offres %}
{% include 'entreprises/_offre.html' %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div style="margin-bottom: 10px;">
<h3>Liste des stages et apprentissages réalisés au sein de l'entreprise</h3>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" href="{{ url_for('entreprises.add_stage_apprentissage', entreprise_id=entreprise.id) }}"
style="margin-bottom:10px;">Ajouter stage ou apprentissage</a>
{% endif %}
<table id="table-stages-apprentissages">
<thead>
<tr>
<td data-priority="3">Date début</td>
<td data-priority="4">Date fin</td>
<td data-priority="5">Durée</td>
<td data-priority="2">Type</td>
<td data-priority="1">Étudiant</td>
<td data-priority="6">Formation</td>
<td data-priority="7">Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="3">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for stage_apprentissage, etudiant in stages_apprentissages %}
<tr>
<td>{{ stage_apprentissage.date_debut.strftime('%d/%m/%Y') }}</td>
<td>{{ stage_apprentissage.date_fin.strftime('%d/%m/%Y') }}</td>
<td>{{ (stage_apprentissage.date_fin-stage_apprentissage.date_debut).days//7 }} semaines</td>
<td>{{ stage_apprentissage.type_offre }}</td>
<td><a
href="{{ url_for('scolar.ficheEtud', scodoc_dept=etudiant.dept_id|get_dept_acronym, etudid=stage_apprentissage.etudid) }}">{{
etudiant.nom|format_nom }} {{ etudiant.prenom|format_prenom }}</a></td>
<td>{% if stage_apprentissage.formation_text %}{{ stage_apprentissage.formation_text }}{% endif %}</td>
<td>{{ stage_apprentissage.notes }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a
href="{{ url_for('entreprises.edit_stage_apprentissage', entreprise_id=entreprise.id, stage_apprentissage_id=stage_apprentissage.id) }}">Modifier</a>
</li>
<li><a href="{{ url_for('entreprises.delete_stage_apprentissage', entreprise_id=entreprise.id, stage_apprentissage_id=stage_apprentissage.id) }}"
style="color:red">Supprimer</a></li>
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Date début</td>
<td>Date fin</td>
<td>Durée</td>
<td>Type</td>
<td>Étudiant</td>
<td>Formation</td>
<td>Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-stages-apprentissages',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -1,84 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.validation') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Fiche entreprise</a>
</li>
</ul>
</div>
<div class="container">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
<div class="entreprise">
<div>
SIRET {% if entreprise.siret_provisoire %}provisoire{% endif %} : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}<br>
{% if entreprise.association %}
Association
{% endif %}
</div>
</div>
<div class="sites-et-offres">
{% if entreprise.sites %}
<div>
<h3>Sites</h3>
{% for site in entreprise.sites %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
{% for correspondant in site.correspondants %}
<div class="correspondant">
<div>
Civilité : {{ correspondant.civilite|get_civilité }}<br>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
{% if correspondant.origine %}
Origine : {{ correspondant.origine }}<br>
{% endif %}
{% if correspondant.notes %}
Notes : {{ correspondant.notes }}<br>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
<div>
<a class="btn btn-success" href="{{ url_for('entreprises.validate_entreprise', entreprise_id=entreprise.id) }}">Valider</a>
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_validation_entreprise', entreprise_id=entreprise.id) }}">Supprimer</a>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,86 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% block app_content %}
<div class="container">
<ul class="breadcrumbs">
<li class="breadcrumbs_item">
<a href="{{ url_for('entreprises.validation') }}" class="breadcrumbs_link">Entreprises</a>
</li>
<li class="breadcrumbs_item">
<a href="" class="breadcrumbs_link breadcrumbs_link-active">Fiche entreprise</a>
</li>
</ul>
</div>
<div class="container">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
<div class="entreprise">
<div>
SIRET {% if entreprise.siret_provisoire %}provisoire{% endif %} : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}<br>
{% if entreprise.association %}
Association
{% endif %}
</div>
</div>
<div class="sites-et-offres">
{% if entreprise.sites %}
<div>
<h3>Sites</h3>
{% for site in entreprise.sites %}
<div class="site">
Nom : {{ site.nom }}<br>
Adresse : {{ site.adresse }}<br>
Code postal : {{ site.codepostal }}<br>
Ville : {{ site.ville }}<br>
Pays : {{ site.pays }}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesCorrespondants, None) %}
{% for correspondant in site.correspondants %}
<div class="correspondant">
<div>
Civilité : {{ correspondant.civilite|get_civilité }}<br>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
{% if correspondant.origine %}
Origine : {{ correspondant.origine }}<br>
{% endif %}
{% if correspondant.notes %}
Notes : {{ correspondant.notes }}<br>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
<div>
<a class="btn btn-success"
href="{{ url_for('entreprises.validate_entreprise', entreprise_id=entreprise.id) }}">Valider</a>
<a class="btn btn-danger"
href="{{ url_for('entreprises.delete_validation_entreprise', entreprise_id=entreprise.id) }}">Supprimer</a>
</div>
</div>
{% endblock %}

View File

@ -1,62 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
{# pour les formulaires offre #}
var champ_depts = document.getElementById("depts")
if (champ_depts) {
var closest_form_control = champ_depts.closest(".form-control")
closest_form_control.classList.remove("form-control")
}
if(document.getElementById("expiration_date") && document.getElementById("expiration_date").value === "")
expiration()
if(document.getElementById("type_offre"))
document.getElementById("type_offre").addEventListener("change", expiration);
function expiration() {
var date = new Date()
var expiration = document.getElementById("expiration_date")
var type_offre = document.getElementById("type_offre").value
if (type_offre === "Alternance") {
expiration.value = `${date.getFullYear() + 1}-01-01`
} else {
if(date.getMonth() + 1 < 8)
expiration.value = `${date.getFullYear()}-08-01`
else
expiration.value = `${date.getFullYear() + 1}-08-01`
}
}
var responsables_options = {
script: "/ScoDoc/entreprises/responsables?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
var as_utilisateurs = new bsn.AutoSuggest('utilisateur', responsables_options);
</script>
{% endblock %}

View File

@ -0,0 +1,62 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
{# pour les formulaires offre # }
var champ_depts = document.getElementById("depts")
if (champ_depts) {
var closest_form_control = champ_depts.closest(".form-control")
closest_form_control.classList.remove("form-control")
}
if (document.getElementById("expiration_date") && document.getElementById("expiration_date").value === "")
expiration()
if (document.getElementById("type_offre"))
document.getElementById("type_offre").addEventListener("change", expiration);
function expiration() {
var date = new Date()
var expiration = document.getElementById("expiration_date")
var type_offre = document.getElementById("type_offre").value
if (type_offre === "Alternance") {
expiration.value = `${date.getFullYear() + 1}-01-01`
} else {
if (date.getMonth() + 1 < 8)
expiration.value = `${date.getFullYear()}-08-01`
else
expiration.value = `${date.getFullYear() + 1}-08-01`
}
}
var responsables_options = {
script: "/ScoDoc/entreprises/responsables?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
var as_utilisateurs = new bsn.AutoSuggest('utilisateur', responsables_options);
</script>
{% endblock %}

View File

@ -1,91 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.correspondants.label }}
{% for subfield in form.correspondants %}
{% if subfield.errors %}
<p class="title-form-error">Formulaire {{ subfield.label.text }}</p>
{% endif %}
{% for subsubfield in subfield %}
{% if subsubfield.errors %}
{% for error in subsubfield.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{{ form.correspondants }}
<div style="margin-bottom: 10px;">
<button class="btn btn-default" id="add-correspondant-field">Ajouter un correspondant</button>
{{ form.submit(class_="btn btn-default") }}
{{ form.cancel(class_="btn btn-default") }}
</div>
</form>
</div>
</div>
<script>
let allCorrepondantsFieldWrapper = document.getElementById('correspondants');
let allCorrepondantsForm = allCorrepondantsFieldWrapper.getElementsByTagName('li');
for(let i = 0; i < allCorrepondantsForm.length; i++) {
let form_id = allCorrepondantsForm[i].getElementsByTagName('table')[0].id
if(form_id.split('-')[1] != 0)
allCorrepondantsForm[i].insertAdjacentHTML('beforeend', `<div class="btn btn-default btn-remove" onclick="deleteForm('${form_id}')">Retirer ce correspondant</div>`)
}
window.onload = function(e) {
let addCorrespondantFieldBtn = document.getElementById('add-correspondant-field');
addCorrespondantFieldBtn.addEventListener('click', function(e){
e.preventDefault();
let allCorrepondantsFieldWrapper = document.getElementById('correspondants');
let allCorrepondantsField = allCorrepondantsFieldWrapper.getElementsByTagName('input');
let correspondantInputIds = []
let csrf_token = document.getElementById('csrf_token').value;
for(let i = 0; i < allCorrepondantsField.length; i++) {
correspondantInputIds.push(parseInt(allCorrepondantsField[i].name.split('-')[1]));
}
let newFieldName = `correspondants-${Math.max(...correspondantInputIds) + 1}`;
allCorrepondantsFieldWrapper.insertAdjacentHTML('beforeend',`
<li>
<label for="${newFieldName}">Correspondants-${Math.max(...correspondantInputIds) + 1}</label>
<table id="${newFieldName}">
<tr><th><label for="${newFieldName}-civilite">Civilité (*)</label></th><td><select class="form-control" id="${newFieldName}-civilite" name="${newFieldName}-civilite" required><option value="H">Monsieur</option><option value="F">Madame</option></select></td></tr>
<tr><th><label for="${newFieldName}-nom">Nom (*)</label></th><td><input class="form-control" id="${newFieldName}-nom" name="${newFieldName}-nom" required type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-prenom">Prénom (*)</label></th><td><input class="form-control" id="${newFieldName}-prenom" name="${newFieldName}-prenom" required type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-telephone">Téléphone (*)</label></th><td><input class="form-control" id="${newFieldName}-telephone" name="${newFieldName}-telephone" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-mail">Mail (*)</label></th><td><input class="form-control" id="${newFieldName}-mail" name="${newFieldName}-mail" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-poste">Poste</label></th><td><input class="form-control" id="${newFieldName}-poste" name="${newFieldName}-poste" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-service">Service</label></th><td><input class="form-control" id="${newFieldName}-service" name="${newFieldName}-service" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-origine">Origine</label></th><td><input class="form-control" id="${newFieldName}-origine" name="${newFieldName}-origine" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-notes">Notes</label></th><td><input class="form-control" id="${newFieldName}-notes" name="${newFieldName}-notes" type="text" value=""></td></tr>
</table>
<input id="${newFieldName}-csrf_token" name="${newFieldName}-csrf_token" type="hidden" value=${csrf_token}>
<div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer ce correspondant</div>
</li>
`);
});
}
function deleteForm(x) {
var li_form = document.querySelector(`label[for=${x}]`).closest('li');
if (li_form) {
li_form.remove()
}
}
</script>
{% endblock %}

View File

@ -0,0 +1,91 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.correspondants.label }}
{% for subfield in form.correspondants %}
{% if subfield.errors %}
<p class="title-form-error">Formulaire {{ subfield.label.text }}</p>
{% endif %}
{% for subsubfield in subfield %}
{% if subsubfield.errors %}
{% for error in subsubfield.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{{ form.correspondants }}
<div style="margin-bottom: 10px;">
<button class="btn btn-default" id="add-correspondant-field">Ajouter un correspondant</button>
{{ form.submit(class_="btn btn-default") }}
{{ form.cancel(class_="btn btn-default") }}
</div>
</form>
</div>
</div>
<script>
let allCorrepondantsFieldWrapper = document.getElementById('correspondants');
let allCorrepondantsForm = allCorrepondantsFieldWrapper.getElementsByTagName('li');
for (let i = 0; i < allCorrepondantsForm.length; i++) {
let form_id = allCorrepondantsForm[i].getElementsByTagName('table')[0].id
if (form_id.split('-')[1] != 0)
allCorrepondantsForm[i].insertAdjacentHTML('beforeend', `<div class="btn btn-default btn-remove" onclick="deleteForm('${form_id}')">Retirer ce correspondant</div>`)
}
window.onload = function (e) {
let addCorrespondantFieldBtn = document.getElementById('add-correspondant-field');
addCorrespondantFieldBtn.addEventListener('click', function (e) {
e.preventDefault();
let allCorrepondantsFieldWrapper = document.getElementById('correspondants');
let allCorrepondantsField = allCorrepondantsFieldWrapper.getElementsByTagName('input');
let correspondantInputIds = []
let csrf_token = document.getElementById('csrf_token').value;
for (let i = 0; i < allCorrepondantsField.length; i++) {
correspondantInputIds.push(parseInt(allCorrepondantsField[i].name.split('-')[1]));
}
let newFieldName = `correspondants-${Math.max(...correspondantInputIds) + 1}`;
allCorrepondantsFieldWrapper.insertAdjacentHTML('beforeend', `
<li>
<label for="${newFieldName}">Correspondants-${Math.max(...correspondantInputIds) + 1}</label>
<table id="${newFieldName}">
<tr><th><label for="${newFieldName}-civilite">Civilité (*)</label></th><td><select class="form-control" id="${newFieldName}-civilite" name="${newFieldName}-civilite" required><option value="H">Monsieur</option><option value="F">Madame</option></select></td></tr>
<tr><th><label for="${newFieldName}-nom">Nom (*)</label></th><td><input class="form-control" id="${newFieldName}-nom" name="${newFieldName}-nom" required type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-prenom">Prénom (*)</label></th><td><input class="form-control" id="${newFieldName}-prenom" name="${newFieldName}-prenom" required type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-telephone">Téléphone (*)</label></th><td><input class="form-control" id="${newFieldName}-telephone" name="${newFieldName}-telephone" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-mail">Mail (*)</label></th><td><input class="form-control" id="${newFieldName}-mail" name="${newFieldName}-mail" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-poste">Poste</label></th><td><input class="form-control" id="${newFieldName}-poste" name="${newFieldName}-poste" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-service">Service</label></th><td><input class="form-control" id="${newFieldName}-service" name="${newFieldName}-service" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-origine">Origine</label></th><td><input class="form-control" id="${newFieldName}-origine" name="${newFieldName}-origine" type="text" value=""></td></tr>
<tr><th><label for="${newFieldName}-notes">Notes</label></th><td><input class="form-control" id="${newFieldName}-notes" name="${newFieldName}-notes" type="text" value=""></td></tr>
</table>
<input id="${newFieldName}-csrf_token" name="${newFieldName}-csrf_token" type="hidden" value=${csrf_token}>
<div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer ce correspondant</div>
</li>
`);
});
}
function deleteForm(x) {
var li_form = document.querySelector(`label[for=${x}]`).closest('li');
if (li_form) {
li_form.remove()
}
}
</script>
{% endblock %}

View File

@ -1,58 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Ajout entreprise</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
Les champs s'auto complète selon le SIRET<br>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
{# ajout margin-bottom sur le champ pays #}
var champ_pays = document.getElementById("pays")
if (champ_pays !== null) {
var closest_form_group = champ_pays.closest(".form-group")
closest_form_group.style.marginBottom = "50px"
}
document.getElementById("siret").addEventListener("keyup", autocomplete);
function autocomplete() {
var input = document.getElementById("siret").value.replaceAll(" ", "")
if(input.length >= 14) {
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + input)
.then(response => {
if(response.ok)
return response.json()
else {
emptyForm()
}
})
.then(response => fillForm(response))
.catch(err => err)
}
}
function fillForm(response) {
document.getElementById("nom_entreprise").value = response.etablissement.l1_normalisee
document.getElementById("adresse").value = response.etablissement.l4_normalisee
document.getElementById("codepostal").value = response.etablissement.code_postal
document.getElementById("ville").value = response.etablissement.libelle_commune
}
function emptyForm() {
document.getElementById("nom_entreprise").value = ''
document.getElementById("adresse").value = ''
document.getElementById("codepostal").value = ''
document.getElementById("ville").value = ''
}
</script>
{% endblock %}

View File

@ -0,0 +1,58 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Ajout entreprise</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
Les champs s'auto complète selon le SIRET<br>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
{# ajout margin - bottom sur le champ pays # }
var champ_pays = document.getElementById("pays")
if (champ_pays !== null) {
var closest_form_group = champ_pays.closest(".form-group")
closest_form_group.style.marginBottom = "50px"
}
document.getElementById("siret").addEventListener("keyup", autocomplete);
function autocomplete() {
var input = document.getElementById("siret").value.replaceAll(" ", "")
if (input.length >= 14) {
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + input)
.then(response => {
if (response.ok)
return response.json()
else {
emptyForm()
}
})
.then(response => fillForm(response))
.catch(err => err)
}
}
function fillForm(response) {
document.getElementById("nom_entreprise").value = response.etablissement.l1_normalisee
document.getElementById("adresse").value = response.etablissement.l4_normalisee
document.getElementById("codepostal").value = response.etablissement.code_postal
document.getElementById("ville").value = response.etablissement.libelle_commune
}
function emptyForm() {
document.getElementById("nom_entreprise").value = ''
document.getElementById("adresse").value = ''
document.getElementById("codepostal").value = ''
document.getElementById("ville").value = ''
}
</script>
{% endblock %}

View File

@ -1,36 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
window.onload = function(e) {
var etudiants_options = {
script: "/ScoDoc/entreprises/etudiants?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
var as_etudiants = new bsn.AutoSuggest('etudiant', etudiants_options);
}
</script>
{% endblock %}

View File

@ -0,0 +1,36 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
window.onload = function (e) {
var etudiants_options = {
script: "/ScoDoc/entreprises/etudiants?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
var as_etudiants = new bsn.AutoSuggest('etudiant', etudiants_options);
}
</script>
{% endblock %}

View File

@ -1,15 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div>{{ info_message }}</div>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div>{{ info_message }}</div>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}

View File

@ -1,88 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
<h1>Envoyer une offre</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.responsables.label }}
{% for error in form.responsables.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{{ form.responsables }}
<div style="margin-bottom: 10px;">
<button class="btn btn-default" id="add-responsable-field">Ajouter un responsable</button>
{{ form.submit(class_="btn btn-default") }}
{{ form.cancel(class_="btn btn-default") }}
</div>
</form>
</div>
</div>
<script>
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('li');
for(let i = 0; i < allResponsablesField.length; i++) {
let form_id = allResponsablesField[i].getElementsByTagName('input')[0].id
if(form_id.split('-')[1] != 0)
allResponsablesField[i].insertAdjacentHTML('beforeend', `<div class="btn btn-default btn-remove" onclick="deleteForm('${form_id}')">Retirer</div>`)
}
window.onload = function(e) {
var responsables_options = {
script: "/ScoDoc/entreprises/responsables?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('input');
for(let i = 0; i < allResponsablesField.length; i++) {
new bsn.AutoSuggest(allResponsablesField[i].id, responsables_options);
}
let addResponsableFieldBtn = document.getElementById('add-responsable-field');
addResponsableFieldBtn.addEventListener('click', function(e){
e.preventDefault();
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('input');
let responsableInputIds = []
for(let i = 0; i < allResponsablesField.length; i++) {
responsableInputIds.push(parseInt(allResponsablesField[i].name.split('-')[1]));
}
let newFieldName = `responsables-${Math.max(...responsableInputIds) + 1}`;
allResponsablesFieldWrapper.insertAdjacentHTML('beforeend',`
<li>
<label for="${newFieldName}">Responsable (*)</label>
<input class="form-control" id="${newFieldName}" name="${newFieldName}" type="text" value="" placeholder="Tapez le nom du responsable de formation">
<div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer</div>
</li>
`);
var as_r = new bsn.AutoSuggest(newFieldName, responsables_options);
});
}
function deleteForm(x) {
var li_form = document.querySelector(`label[for=${x}]`).closest('li');
if (li_form) {
li_form.remove()
}
}
</script>
{% endblock %}

View File

@ -0,0 +1,88 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.j2' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
<h1>Envoyer une offre</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.responsables.label }}
{% for error in form.responsables.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{{ form.responsables }}
<div style="margin-bottom: 10px;">
<button class="btn btn-default" id="add-responsable-field">Ajouter un responsable</button>
{{ form.submit(class_="btn btn-default") }}
{{ form.cancel(class_="btn btn-default") }}
</div>
</form>
</div>
</div>
<script>
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('li');
for (let i = 0; i < allResponsablesField.length; i++) {
let form_id = allResponsablesField[i].getElementsByTagName('input')[0].id
if (form_id.split('-')[1] != 0)
allResponsablesField[i].insertAdjacentHTML('beforeend', `<div class="btn btn-default btn-remove" onclick="deleteForm('${form_id}')">Retirer</div>`)
}
window.onload = function (e) {
var responsables_options = {
script: "/ScoDoc/entreprises/responsables?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('input');
for (let i = 0; i < allResponsablesField.length; i++) {
new bsn.AutoSuggest(allResponsablesField[i].id, responsables_options);
}
let addResponsableFieldBtn = document.getElementById('add-responsable-field');
addResponsableFieldBtn.addEventListener('click', function (e) {
e.preventDefault();
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('input');
let responsableInputIds = []
for (let i = 0; i < allResponsablesField.length; i++) {
responsableInputIds.push(parseInt(allResponsablesField[i].name.split('-')[1]));
}
let newFieldName = `responsables-${Math.max(...responsableInputIds) + 1}`;
allResponsablesFieldWrapper.insertAdjacentHTML('beforeend', `
<li>
<label for="${newFieldName}">Responsable (*)</label>
<input class="form-control" id="${newFieldName}" name="${newFieldName}" type="text" value="" placeholder="Tapez le nom du responsable de formation">
<div class="btn btn-default btn-remove" onclick="deleteForm('${newFieldName}')">Retirer</div>
</li>
`);
var as_r = new bsn.AutoSuggest(newFieldName, responsables_options);
});
}
function deleteForm(x) {
var li_form = document.querySelector(`label[for=${x}]`).closest('li');
if (li_form) {
li_form.remove()
}
}
</script>
{% endblock %}

View File

@ -1,68 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
<div id="sirene-data" class="col-md-5">
<b>Informations de la base SIRENE</b>
<div id="nom_entreprise_base"></div>
<div id="adresse_base"></div>
<div id="codepostal_base"></div>
<div id="ville_base"></div>
<a class="btn btn-primary" onclick="getData()">Copier</a>
</div>
</div>
<script>
var value = document.getElementById("siret").value;
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + value)
.then(response => {
if(response.ok)
return response.json()
})
.then(response => showSireneData(response))
.catch(err => {
document.getElementById("sirene-data").style.display = "none"
return err
})
function showSireneData(response) {
document.getElementById("nom_entreprise_base").innerHTML = "Nom de l'entreprise: " + response.etablissement.l1_normalisee
document.getElementById("adresse_base").innerHTML = "Adresse: " + response.etablissement.l4_normalisee
document.getElementById("codepostal_base").innerHTML = "Code postal: " + response.etablissement.code_postal
document.getElementById("ville_base").innerHTML = "Ville: " + response.etablissement.libelle_commune
}
function getData() {
var value = document.getElementById("siret").value;
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + value)
.then(response => {
if(response.ok)
return response.json()
})
.then(response => fillForm(response))
.catch(err => err)
}
function fillForm(response) {
document.getElementById("nom").value = response.etablissement.l1_normalisee
document.getElementById("adresse").value = response.etablissement.l4_normalisee
document.getElementById("codepostal").value = response.etablissement.code_postal
document.getElementById("ville").value = response.etablissement.libelle_commune
}
</script>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More