Merge branch 'iziram-remove-bs'

This commit is contained in:
Emmanuel Viennet 2024-08-25 07:52:58 +02:00
commit 11379e8110
75 changed files with 560 additions and 59860 deletions

View File

@ -353,8 +353,7 @@ def create_app(config_class=DevConfig):
app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True) app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True)
app.jinja_env.trim_blocks = True app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True app.jinja_env.lstrip_blocks = True
# previously in Flask-Bootstrap: app.jinja_env.globals["is_hidden_field"] = lambda field: isinstance(
app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance(
field, HiddenField field, HiddenField
) )

View File

@ -39,15 +39,13 @@ from wtforms import ValidationError
from wtforms.fields.simple import StringField, HiddenField from wtforms.fields.simple import StringField, HiddenField
from app.models import Departement from app.models import Departement
from app.scodoc import sco_logos, html_sco_header from app.scodoc import sco_logos
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
from app.scodoc.sco_config_actions import LogoInsert from app.scodoc.sco_config_actions import LogoInsert
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_logos import find_logo from app.scodoc.sco_logos import find_logo
CSSSTYLES = html_sco_header.BOOTSTRAP_CSS
JAVASCRIPTS = html_sco_header.BOOTSTRAP_JS
# class ItemForm(FlaskForm): # class ItemForm(FlaskForm):
# """Unused Generic class to document common behavior for classes # """Unused Generic class to document common behavior for classes
@ -367,6 +365,7 @@ def _make_data(modele):
class LogosConfigurationForm(FlaskForm): class LogosConfigurationForm(FlaskForm):
"Panneau de configuration des logos" "Panneau de configuration des logos"
depts = FieldList(FormField(DeptForm)) depts = FieldList(FormField(DeptForm))
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -40,6 +40,7 @@ def TrivialFormulator(
submitbuttonattributes=None, submitbuttonattributes=None,
top_buttons=False, # place buttons at top of form top_buttons=False, # place buttons at top of form
bottom_buttons=True, # buttons after form bottom_buttons=True, # buttons after form
html_head_markup="",
html_foot_markup="", html_foot_markup="",
readonly=False, readonly=False,
is_submitted=False, is_submitted=False,
@ -116,6 +117,7 @@ def TrivialFormulator(
submitbuttonattributes=submitbuttonattributes or [], submitbuttonattributes=submitbuttonattributes or [],
top_buttons=top_buttons, top_buttons=top_buttons,
bottom_buttons=bottom_buttons, bottom_buttons=bottom_buttons,
html_head_markup=html_head_markup,
html_foot_markup=html_foot_markup, html_foot_markup=html_foot_markup,
readonly=readonly, readonly=readonly,
is_submitted=is_submitted, is_submitted=is_submitted,
@ -152,6 +154,7 @@ class TF(object):
submitbuttonattributes=None, submitbuttonattributes=None,
top_buttons=False, # place buttons at top of form top_buttons=False, # place buttons at top of form
bottom_buttons=True, # buttons after form bottom_buttons=True, # buttons after form
html_head_markup="", # html snippet put at the beginning, just before the table
html_foot_markup="", # html snippet put at the end, just after the table html_foot_markup="", # html snippet put at the end, just after the table
readonly=False, readonly=False,
is_submitted=False, is_submitted=False,
@ -178,6 +181,7 @@ class TF(object):
self.submitbuttonattributes = submitbuttonattributes or [] self.submitbuttonattributes = submitbuttonattributes or []
self.top_buttons = top_buttons self.top_buttons = top_buttons
self.bottom_buttons = bottom_buttons self.bottom_buttons = bottom_buttons
self.html_head_markup = html_head_markup
self.html_foot_markup = html_foot_markup self.html_foot_markup = html_foot_markup
self.title = title self.title = title
self.after_table = after_table self.after_table = after_table
@ -425,22 +429,15 @@ class TF(object):
hiddenitemtemplate = "%(elem)s" hiddenitemtemplate = "%(elem)s"
separatortemplate = '<tr%(item_dom_attr)s><td colspan="2">%(label)s</td></tr>' separatortemplate = '<tr%(item_dom_attr)s><td colspan="2">%(label)s</td></tr>'
# ---- build form # ---- build form
buttons_markup = "" buttons_markup = """<div class="form-group">"""
if self.submitbutton: if self.submitbutton:
buttons_markup += ( buttons_markup += f"""<input class="btn btn-default" type="submit" name="{
'<input type="submit" name="%s_submit" id="%s_submit" value="%s" %s/>' self.formid}_submit" id="{self.formid}_submit" value="{self.submitlabel
% ( }" {' '.join(self.submitbuttonattributes)}/>"""
self.formid,
self.formid,
self.submitlabel,
" ".join(self.submitbuttonattributes),
)
)
if self.cancelbutton: if self.cancelbutton:
buttons_markup += ( buttons_markup += f""" <input class="btn btn-default" type="submit" name="cancel" id="{
' <input type="submit" name="%s_cancel" id="%s_cancel" value="%s">' self.formid}_cancel" value="{self.cancelbutton}">"""
% (self.formid, self.formid, self.cancelbutton) buttons_markup += "</div>"
)
R = [] R = []
suggest_js = [] suggest_js = []
@ -476,6 +473,7 @@ class TF(object):
if self.top_buttons: if self.top_buttons:
R.append(buttons_markup + "<p></p>") R.append(buttons_markup + "<p></p>")
R.append(self.before_table.format(title=self.title)) R.append(self.before_table.format(title=self.title))
R.append(self.html_head_markup)
R.append('<table class="tf">') R.append('<table class="tf">')
for field, descr in self.formdescription: for field, descr in self.formdescription:
if descr.get("readonly", False): if descr.get("readonly", False):
@ -803,8 +801,10 @@ var {field}_as = new bsn.AutoSuggest('{field}', {field}_opts);
} }
// Selections etendues avec shift (use jquery.field) // Selections etendues avec shift (use jquery.field)
$('input[name="tf-checked:list"]').createCheckboxRange(); window.onload = function() {
</script>""" $('input[name="tf-checked:list"]').createCheckboxRange();
}
</script>"""
) )
R.append("</form>") R.append("</form>")
return R return R

View File

@ -26,8 +26,3 @@
############################################################################## ##############################################################################
"""Legacy constants (TODO: remove)""" """Legacy constants (TODO: remove)"""
# Some constants:
BOOTSTRAP_JS = ["libjs/bootstrap/js/bootstrap.min.js", "libjs/purl.js"]
BOOTSTRAP_CSS = ["libjs/bootstrap/css/bootstrap.min.css"]

View File

@ -289,7 +289,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
] ]
menu_choix_groupe = ( menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """ """<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """
+ sco_groups_view.menu_groups_choice(groups_infos) + sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
+ """(pour les PV et lettres)</div>""" + """(pour les PV et lettres)</div>"""
) )
@ -307,8 +307,6 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
title="Archiver les PV et résultats", title="Archiver les PV et résultats",
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
content="<h2>Archiver les PV et résultats du semestre</h2>" content="<h2>Archiver les PV et résultats du semestre</h2>"
+ "\n".join(H) + "\n".join(H)
+ "\n" + "\n"

View File

@ -1059,10 +1059,8 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
"sco_page_dept.j2", "sco_page_dept.j2",
content="".join(H), content="".join(H),
page_title=f"Formation {formation.acronyme} v{formation.version}", page_title=f"Formation {formation.acronyme} v{formation.version}",
cssstyles=html_sco_header.BOOTSTRAP_CSS cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css", "css/ue_table.css"],
+ ["libjs/jQuery-tagEditor/jquery.tag-editor.css", "css/ue_table.css"], javascripts=[
javascripts=html_sco_header.BOOTSTRAP_JS
+ [
"libjs/jinplace-1.2.1.min.js", "libjs/jinplace-1.2.1.min.js",
"js/ue_list.js", "js/ue_list.js",
"js/edit_ue.js", "js/edit_ue.js",
@ -1130,9 +1128,7 @@ def _ue_table_ues(
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
ue_id=ue["ue_id"], ue_id=ue["ue_id"],
) )
ue[ ue["code_apogee_str"] = f""", Apo: <span
"code_apogee_str"
] = f""", Apo: <span
class="{klass}" data-url="{edit_url}" id="{ue['ue_id']}" class="{klass}" data-url="{edit_url}" id="{ue['ue_id']}"
data-placeholder="{scu.APO_MISSING_CODE_STR}">{ data-placeholder="{scu.APO_MISSING_CODE_STR}">{
ue["code_apogee"] or "" ue["code_apogee"] or ""

View File

@ -732,7 +732,9 @@ def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True)
H.append( H.append(
f""" f"""
<a style="margin-left: 12px;" class="stdlink" href="{url_for( <a style="margin-left: 12px;" class="stdlink" href="{url_for(
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id) "notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept,
evaluation_id=evaluation_id)
}">saisie des notes</a> }">saisie des notes</a>
""" """
) )

View File

@ -309,8 +309,7 @@ def scodoc_table_results(
return render_template( return render_template(
"sco_page_dept.j2", "sco_page_dept.j2",
title="Export résultats", title="Export résultats",
javascripts=html_sco_header.BOOTSTRAP_JS + ["js/export_results.js"], javascripts=["js/export_results.js"],
cssstyles=html_sco_header.BOOTSTRAP_CSS,
content="\n".join(H), content="\n".join(H),
) )

View File

@ -57,13 +57,6 @@ from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_exceptions import ScoValueError, ScoPermissionDenied from app.scodoc.sco_exceptions import ScoValueError, ScoPermissionDenied
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
JAVASCRIPTS = html_sco_header.BOOTSTRAP_JS + [
"js/groups_view.js",
"js/multi-select.js",
]
CSSSTYLES = html_sco_header.BOOTSTRAP_CSS
# view # view
def groups_lists( def groups_lists(

View File

@ -72,7 +72,7 @@ def moduleimpl_evaluation_menu(evaluation: Evaluation, nbnotes: int = 0) -> str:
menu_eval = [ menu_eval = [
{ {
"title": "Saisir les notes", "title": "Saisir les notes",
"endpoint": "notes.saisie_notes", "endpoint": "notes.form_saisie_notes",
"args": { "args": {
"evaluation_id": evaluation_id, "evaluation_id": evaluation_id,
}, },
@ -745,7 +745,7 @@ def _ligne_evaluation(
) )
if can_edit_notes: if can_edit_notes:
H.append( H.append(
f"""<a class="smallbutton" href="{url_for('notes.saisie_notes', f"""<a class="smallbutton" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}</a>""" }">{scu.icontag("notes_img", alt="saisie notes", title="Saisie des notes")}</a>"""
) )
@ -824,7 +824,7 @@ def _ligne_evaluation(
) )
else: else:
H.append( H.append(
f"""<a class="redlink" href="{url_for('notes.saisie_notes', f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">saisir notes</a> }">saisir notes</a>
""" """
@ -880,7 +880,7 @@ def _ligne_evaluation(
H.append("""[<font color="red">""") H.append("""[<font color="red">""")
if can_edit_notes: if can_edit_notes:
H.append( H.append(
f"""<a class="redlink" href="{url_for('notes.saisie_notes', f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id, scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
**{'group_ids:list': gr_moyenne["group_id"]}) **{'group_ids:list': gr_moyenne["group_id"]})
}">incomplet&nbsp;: terminer saisie</a></font>]""" }">incomplet&nbsp;: terminer saisie</a></font>]"""
@ -891,9 +891,9 @@ def _ligne_evaluation(
H.append("""<span class="redboldtext">&nbsp; """) H.append("""<span class="redboldtext">&nbsp; """)
if can_edit_notes: if can_edit_notes:
H.append( H.append(
f"""<a class="redlink" href="{url_for('notes.saisie_notes', f"""<a class="redlink" href="{url_for('notes.form_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id, scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id,
**{'group_ids:list': gr_moyenne["group_id"]}) **{'group_ids': gr_moyenne["group_id"]})
}">""" }">"""
) )
H.append("pas de notes") H.append("pas de notes")

View File

@ -50,6 +50,7 @@ from app.scodoc import sco_pdf
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pv_pdf from app.scodoc import sco_pv_pdf
from app.scodoc import sco_pv_lettres_inviduelles from app.scodoc import sco_pv_lettres_inviduelles
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.gen_tables import GenTable from app.scodoc.gen_tables import GenTable
from app.scodoc.codes_cursus import NO_SEMESTRE_ID from app.scodoc.codes_cursus import NO_SEMESTRE_ID
from app.scodoc.sco_pdf import PDFLOCK from app.scodoc.sco_pdf import PDFLOCK
@ -336,12 +337,20 @@ def formsemestre_pvjury(formsemestre_id, fmt="html", publish=True):
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid=None): def formsemestre_pvjury_pdf(formsemestre_id, etudid=None):
"""Generation PV jury en PDF: saisie des paramètres """Génération PV jury en PDF: saisie des paramètres
Si etudid, PV pour un seul etudiant. Sinon, tout les inscrits au groupe indiqué. Si etudid, PV pour un seul etudiant.
Sinon, tout les inscrits au(x) groupe(s) indiqué(s).
""" """
group_ids = group_ids or [] if request.method == "POST":
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
# Mise à jour des groupes d'étapes: # Mise à jour des groupes d'étapes:
sco_groups.create_etapes_partition(formsemestre_id) sco_groups.create_etapes_partition(formsemestre_id)
groups_infos = None groups_infos = None
@ -361,7 +370,8 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
etudids = [m["etudid"] for m in groups_infos.members] etudids = [m["etudid"] for m in groups_infos.members]
H = [ H = [
f"""<div class="help">Utiliser cette page pour éditer des versions provisoires des PV. f"""<div class="help space-after-24">
Utiliser cette page pour éditer des versions provisoires des PV.
<span class="fontred">Il est recommandé d'archiver les versions définitives: <span class="fontred">Il est recommandé d'archiver les versions définitives:
<a class="stdlink" href="{url_for( <a class="stdlink" href="{url_for(
'notes.formsemestre_archive', 'notes.formsemestre_archive',
@ -381,7 +391,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
if groups_infos: if groups_infos:
menu_choix_groupe = ( menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister sur le PV: """ """<div class="group_ids_sel_menu">Groupes d'étudiants à lister sur le PV: """
+ sco_groups_view.menu_groups_choice(groups_infos) + sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
+ """</div>""" + """</div>"""
) )
else: else:
@ -394,24 +404,28 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
submitlabel="Générer document", submitlabel="Générer document",
name="tf", name="tf",
formid="group_selector", formid="group_selector",
html_foot_markup=menu_choix_groupe, html_head_markup=menu_choix_groupe,
) )
if tf[0] == 0: if tf[0] == 0:
info_etud = (
f"""de <a class="discretelink" href="{
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}">{etud.nomprenom}</a>"""
if etud
else ""
)
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
title=f"Édition du PV de jury {('de ' + etud.nom_prenom()) if etud else ''}", title=f"Édition du PV de jury {('de ' + etud.nom_prenom()) if etud else ''}",
content=f"""<h2 class="formsemestre">Édition du PV de jury content=f"""<h2 class="formsemestre">Édition du PV de jury
de <a class="discretelink" href="{ {info_etud}</h2>"""
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}">{etud.nomprenom}</a></h2>"""
+ "\n".join(H) + "\n".join(H)
+ "\n" + "\n"
+ tf[1] + tf[1]
+ "\n".join(F), + "\n".join(F),
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=["js/groups_view.js"],
cssstyles=sco_groups_view.CSSSTYLES,
) )
elif tf[0] == -1: if tf[0] == -1:
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.formsemestre_pvjury", "notes.formsemestre_pvjury",
@ -419,34 +433,34 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )
) )
# submit
tf[2]["show_title"] = bool(tf[2]["show_title"])
tf[2]["anonymous"] = bool(tf[2]["anonymous"])
try:
PDFLOCK.acquire()
pdfdoc = sco_pv_pdf.pvjury_pdf(
formsemestre,
etudids,
numero_arrete=tf[2]["numero_arrete"],
code_vdi=tf[2]["code_vdi"],
date_commission=tf[2]["date_commission"],
date_jury=tf[2]["date_jury"],
show_title=tf[2]["show_title"],
pv_title_session=tf[2]["pv_title_session"],
pv_title=tf[2]["pv_title"],
with_paragraph_nom=tf[2]["with_paragraph_nom"],
anonymous=tf[2]["anonymous"],
)
finally:
PDFLOCK.release()
date_iso = time.strftime("%Y-%m-%d")
if groups_infos:
groups_filename = "-" + groups_infos.groups_filename
else: else:
# submit groups_filename = ""
tf[2]["show_title"] = bool(tf[2]["show_title"]) filename = f"""PV-{formsemestre.titre_num()}{groups_filename}-{date_iso}.pdf"""
tf[2]["anonymous"] = bool(tf[2]["anonymous"]) return scu.sendPDFFile(pdfdoc, filename)
try:
PDFLOCK.acquire()
pdfdoc = sco_pv_pdf.pvjury_pdf(
formsemestre,
etudids,
numero_arrete=tf[2]["numero_arrete"],
code_vdi=tf[2]["code_vdi"],
date_commission=tf[2]["date_commission"],
date_jury=tf[2]["date_jury"],
show_title=tf[2]["show_title"],
pv_title_session=tf[2]["pv_title_session"],
pv_title=tf[2]["pv_title"],
with_paragraph_nom=tf[2]["with_paragraph_nom"],
anonymous=tf[2]["anonymous"],
)
finally:
PDFLOCK.release()
date_iso = time.strftime("%Y-%m-%d")
if groups_infos:
groups_filename = "-" + groups_infos.groups_filename
else:
groups_filename = ""
filename = f"""PV-{formsemestre.titre_num()}{groups_filename}-{date_iso}.pdf"""
return scu.sendPDFFile(pdfdoc, filename)
def descrform_pvjury(formsemestre: FormSemestre): def descrform_pvjury(formsemestre: FormSemestre):
@ -542,9 +556,17 @@ def descrform_pvjury(formsemestre: FormSemestre):
] ]
def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()): def formsemestre_lettres_individuelles(formsemestre_id):
"Lettres avis jury en PDF" "Lettres avis jury en PDF"
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
if not group_ids: if not group_ids:
# tous les inscrits du semestre # tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(formsemestre_id)] group_ids = [sco_groups.get_default_group(formsemestre_id)]
@ -556,20 +578,22 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
H = [ H = [
f""" f"""
<h2 class="formsemestre">Édition des lettres individuelles</h2> <h2 class="formsemestre">Édition des lettres individuelles</h2>
<p class="help">Utiliser cette page pour éditer des versions provisoires des PV. <div class="help space-after-24">
Utiliser cette page pour éditer des versions provisoires des PV.
<span class="fontred">Il est recommandé d'archiver les versions définitives: <a <span class="fontred">Il est recommandé d'archiver les versions définitives: <a
href="{url_for( href="{url_for(
"notes.formsemestre_archive", "notes.formsemestre_archive",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
)}" )}"
>voir cette page</a></span></p> >voir cette page</a></span>
</div>
""", """,
] ]
descr = descrform_lettres_individuelles() descr = descrform_lettres_individuelles()
menu_choix_groupe = ( menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """ """<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """
+ sco_groups_view.menu_groups_choice(groups_infos) + sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
+ """</div>""" + """</div>"""
) )
@ -581,15 +605,14 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=()):
submitlabel="Générer document", submitlabel="Générer document",
name="tf", name="tf",
formid="group_selector", formid="group_selector",
html_foot_markup=menu_choix_groupe, html_head_markup=menu_choix_groupe,
) )
if tf[0] == 0: if tf[0] == 0:
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
title="Édition des lettres individuelles", title="Édition des lettres individuelles",
content="\n".join(H) + "\n" + tf[1], content="\n".join(H) + "\n" + tf[1],
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=["js/groups_view.js"],
cssstyles=sco_groups_view.CSSSTYLES,
) )
elif tf[0] == -1: elif tf[0] == -1:
return flask.redirect( return flask.redirect(

View File

@ -296,6 +296,14 @@ def formsemestre_report_counts(
sinon liste prédéfinie (voir ci-dessous) sinon liste prédéfinie (voir ci-dessous)
""" """
formsemestre = FormSemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, group_ids,
formsemestre_id=formsemestre.id, formsemestre_id=formsemestre.id,
@ -420,8 +428,7 @@ def formsemestre_report_counts(
] ]
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
cssstyles=sco_groups_view.CSSSTYLES, javascripts=["js/groups_view.js"],
javascripts=sco_groups_view.JAVASCRIPTS,
title=title, title=title,
content="\n".join(H), content="\n".join(H),
) )
@ -740,7 +747,6 @@ def table_suivi_cohorte(
def formsemestre_suivi_cohorte( def formsemestre_suivi_cohorte(
formsemestre_id, formsemestre_id,
fmt="html", fmt="html",
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
percent=1, percent=1,
bac="", bac="",
bacspecialite="", bacspecialite="",
@ -759,6 +765,14 @@ def formsemestre_suivi_cohorte(
raise ScoValueError("formsemestre_suivi_cohorte: argument invalide") from exc raise ScoValueError("formsemestre_suivi_cohorte: argument invalide") from exc
formsemestre = FormSemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, group_ids,
formsemestre_id=formsemestre.id, formsemestre_id=formsemestre.id,
@ -850,8 +864,7 @@ def formsemestre_suivi_cohorte(
] ]
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
cssstyles=sco_groups_view.CSSSTYLES, javascripts=["js/groups_view.js"],
javascripts=sco_groups_view.JAVASCRIPTS,
title=tab.page_title, title=tab.page_title,
content="\n".join(H), content="\n".join(H),
) )
@ -1629,7 +1642,6 @@ def graph_cursus(
def formsemestre_graph_cursus( def formsemestre_graph_cursus(
formsemestre_id, formsemestre_id,
group_ids: list[int] = None, # si indiqué, ne prend que ces groupes
fmt="html", fmt="html",
only_primo=False, only_primo=False,
bac="", # selection sur type de bac bac="", # selection sur type de bac
@ -1644,6 +1656,15 @@ def formsemestre_graph_cursus(
annee_bac = str(annee_bac or "") annee_bac = str(annee_bac or "")
annee_admission = str(annee_admission or "") annee_admission = str(annee_admission or "")
formsemestre = FormSemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if request.method == "POST":
group_ids = request.form.getlist("group_ids")
else:
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
groups_infos = sco_groups_view.DisplayedGroupsInfos( groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, group_ids,
formsemestre_id=formsemestre.id, formsemestre_id=formsemestre.id,
@ -1781,8 +1802,7 @@ def formsemestre_graph_cursus(
] ]
return render_template( return render_template(
"sco_page.j2", "sco_page.j2",
cssstyles=sco_groups_view.CSSSTYLES, javascripts=["js/groups_view.js"],
javascripts=sco_groups_view.JAVASCRIPTS,
page_title=f"Graphe cursus de {sem['titreannee']}", page_title=f"Graphe cursus de {sem['titreannee']}",
no_sidebar=True, no_sidebar=True,
content="\n".join(H), content="\n".join(H),

View File

@ -68,7 +68,9 @@ from app.views import ScoData
FONT_NAME = "Arial" FONT_NAME = "Arial"
def excel_feuille_saisie(evaluation: "Evaluation", rows: list[dict]) -> AnyStr: def excel_feuille_saisie(
evaluation: "Evaluation", rows: list[dict], groups_titles: str = ""
) -> AnyStr:
"""Génère feuille excel pour saisie des notes dans l'evaluation """Génère feuille excel pour saisie des notes dans l'evaluation
- evaluation - evaluation
- rows: liste de dict - rows: liste de dict
@ -77,7 +79,9 @@ def excel_feuille_saisie(evaluation: "Evaluation", rows: list[dict]) -> AnyStr:
""" """
ws = ScoExcelSheet("Saisie notes") ws = ScoExcelSheet("Saisie notes")
styles = _build_styles() styles = _build_styles()
nb_lines_titles = _insert_top_title(ws, styles, evaluation=evaluation) nb_lines_titles = _insert_top_title(
ws, styles, evaluation=evaluation, groups_titles=groups_titles
)
_insert_line_titles( _insert_line_titles(
ws, ws,
@ -263,6 +267,7 @@ def _insert_top_title(
evaluation: Evaluation | None = None, evaluation: Evaluation | None = None,
formsemestre: FormSemestre | None = None, formsemestre: FormSemestre | None = None,
description="", description="",
groups_titles: str = "",
) -> int: ) -> int:
"""Insère les lignes de titre de la feuille (suivies d'une ligne blanche). """Insère les lignes de titre de la feuille (suivies d'une ligne blanche).
Si evaluation, indique son titre. Si evaluation, indique son titre.
@ -298,7 +303,7 @@ def _insert_top_title(
evaluation.moduleimpl.formsemestre.titre_annee() evaluation.moduleimpl.formsemestre.titre_annee()
if evaluation if evaluation
else (formsemestre.titre_annee() if formsemestre else "") else (formsemestre.titre_annee() if formsemestre else "")
) ) + ((" - " + groups_titles) if groups_titles else "")
ws.append_single_cell_row( ws.append_single_cell_row(
scu.unescape_html(titre_annee), styles["titres"], prefix=[""] scu.unescape_html(titre_annee), styles["titres"], prefix=[""]
) )
@ -372,15 +377,16 @@ def _insert_bottom_help(ws, styles: dict):
) )
def feuille_saisie_notes( def feuille_saisie_notes(evaluation_id: int): # TODO ré-écrire et passer dans notes.py
evaluation_id, group_ids: list[int] = None
): # TODO ré-écrire et passer dans notes.py
"""Vue: document Excel pour saisie notes dans l'évaluation et les groupes indiqués""" """Vue: document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
evaluation = Evaluation.get_evaluation(evaluation_id) evaluation = Evaluation.get_evaluation(evaluation_id)
group_ids = group_ids or [] group_ids = request.args.getlist("group_ids") or []
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
modimpl = evaluation.moduleimpl modimpl = evaluation.moduleimpl
formsemestre = modimpl.formsemestre formsemestre = modimpl.formsemestre
if evaluation.date_debut: if evaluation.date_debut:
indication_date = evaluation.date_debut.date().isoformat() indication_date = evaluation.date_debut.date().isoformat()
else: else:
@ -430,7 +436,9 @@ def feuille_saisie_notes(
eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}" eval_name = f"{evaluation.moduleimpl.module.code}-{indication_date}"
filename = f"notes_{eval_name}_{gr_title_filename}" filename = f"notes_{eval_name}_{gr_title_filename}"
xls = excel_feuille_saisie(evaluation, rows=rows) xls = excel_feuille_saisie(
evaluation, rows=rows, groups_titles=groups_infos.groups_titles
)
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE) return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
@ -962,9 +970,14 @@ def _get_sheet_evaluations(
raise ValueError("_get_sheet_evaluations") raise ValueError("_get_sheet_evaluations")
def saisie_notes_tableur(evaluation_id: int, group_ids=()): def saisie_notes_tableur(evaluation_id: int):
"""Saisie des notes via un fichier Excel""" """Saisie des notes via un fichier Excel"""
evaluation = Evaluation.query.get_or_404(evaluation_id) group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
evaluation = Evaluation.get_evaluation(evaluation_id)
moduleimpl_id = evaluation.moduleimpl.id moduleimpl_id = evaluation.moduleimpl.id
formsemestre_id = evaluation.moduleimpl.formsemestre_id formsemestre_id = evaluation.moduleimpl.formsemestre_id
if not evaluation.moduleimpl.can_edit_notes(current_user): if not evaluation.moduleimpl.can_edit_notes(current_user):
@ -1004,18 +1017,20 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
# Menu choix groupe: # Menu choix groupe:
H.append("""<div id="group-tabs"><table><tr><td>""") H.append("""<div id="group-tabs"><table><tr><td>""")
H.append(sco_groups_view.form_groups_choice(groups_infos)) H.append(sco_groups_view.form_groups_choice(groups_infos, submit_on_change=True))
H.append("</td></tr></table></div>") H.append("</td></tr></table></div>")
H.append( H.append(
f"""<div class="saisienote_etape1"> f"""<div class="saisienote_etape1">
<span class="titredivsaisienote">Étape 1 : </span> <span class="titredivsaisienote">Étape 1 : </span>
<ul> <ul>
<li><a class="stdlink" href="feuille_saisie_notes?evaluation_id={evaluation_id}&{ <li><a class="stdlink" href="{
url_for('notes.feuille_saisie_notes',
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)}&{
groups_infos.groups_query_args}" groups_infos.groups_query_args}"
id="lnk_feuille_saisie">obtenir le fichier tableur à remplir</a> id="lnk_feuille_saisie">obtenir le fichier tableur à remplir</a>
</li> </li>
<li>ou <a class="stdlink" href="{url_for("notes.saisie_notes", <li>ou <a class="stdlink" href="{url_for("notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
}">aller au formulaire de saisie</a></li> }">aller au formulaire de saisie</a></li>
</ul> </ul>
@ -1085,7 +1100,7 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Charger un autre fichier de notes</a> }">Charger un autre fichier de notes</a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a class="stdlink" href="{url_for("notes.saisie_notes", <a class="stdlink" href="{url_for("notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Formulaire de saisie des notes</a> }">Formulaire de saisie des notes</a>
</div>""" </div>"""
@ -1113,7 +1128,9 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
<div> <div>
<ul> <ul>
<li> <li>
<form action="do_evaluation_set_missing" method="POST"> <form action="{
url_for("notes.do_evaluation_set_missing", scodoc_dept=g.scodoc_dept)
}" method="POST">
Mettre toutes les notes manquantes à <input type="text" size="5" name="value"/> Mettre toutes les notes manquantes à <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/> <input type="submit" value="OK"/>
<input type="hidden" name="evaluation_id" value="{evaluation_id}"/> <input type="hidden" name="evaluation_id" value="{evaluation_id}"/>
@ -1129,7 +1146,7 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id) scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
}">Revenir au module</a> }">Revenir au module</a>
</li> </li>
<li><a class="stdlink" href="{url_for("notes.saisie_notes", <li><a class="stdlink" href="{url_for("notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id) scodoc_dept=g.scodoc_dept, evaluation_id=evaluation.id)
}">Revenir au formulaire de saisie</a> }">Revenir au formulaire de saisie</a>
</li> </li>
@ -1176,8 +1193,7 @@ def saisie_notes_tableur(evaluation_id: int, group_ids=()):
"sco_page.j2", "sco_page.j2",
content="\n".join(H), content="\n".join(H),
page_title=page_title, page_title=page_title,
javascripts=sco_groups_view.JAVASCRIPTS, javascripts=["js/groups_view.js"],
cssstyles=sco_groups_view.CSSSTYLES,
) )

View File

@ -54,7 +54,6 @@ from app.scodoc.sco_exceptions import (
AccessDenied, AccessDenied,
NoteProcessError, NoteProcessError,
ScoException, ScoException,
ScoInvalidParamError,
ScoValueError, ScoValueError,
) )
from app.scodoc import htmlutils from app.scodoc import htmlutils
@ -71,6 +70,7 @@ from app.scodoc.TrivialFormulator import TF
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
from app.views import ScoData
def convert_note_from_string( def convert_note_from_string(
@ -212,7 +212,7 @@ def do_evaluation_set_missing(
evaluation_id, value, dialog_confirmed=False, group_ids_str: str = "" evaluation_id, value, dialog_confirmed=False, group_ids_str: str = ""
): ):
"""Initialisation des notes manquantes""" """Initialisation des notes manquantes"""
evaluation = Evaluation.query.get_or_404(evaluation_id) evaluation = Evaluation.get_evaluation(evaluation_id)
modimpl = evaluation.moduleimpl modimpl = evaluation.moduleimpl
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
@ -222,8 +222,12 @@ def do_evaluation_set_missing(
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
if not group_ids_str: if not group_ids_str:
groups = None groups = None
groups_infos = None
else: else:
group_ids = [int(x) for x in str(group_ids_str).split(",")] group_ids = [int(x) for x in str(group_ids_str).split(",")]
groups_infos = sco_groups_view.DisplayedGroupsInfos(
group_ids, formsemestre_id=modimpl.formsemestre.id
)
groups = sco_groups.listgroups(group_ids) groups = sco_groups.listgroups(group_ids)
etudid_etats = sco_groups.do_evaluation_listeetuds_groups( etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
@ -240,7 +244,9 @@ def do_evaluation_set_missing(
# Convert and check values # Convert and check values
valid_notes, invalids, _, _, _ = check_notes(notes, evaluation) valid_notes, invalids, _, _, _ = check_notes(notes, evaluation)
dest_url = url_for( dest_url = url_for(
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id "notes.form_saisie_notes",
scodoc_dept=g.scodoc_dept,
evaluation_id=evaluation_id,
) )
diag = "" diag = ""
if len(invalids) > 0: if len(invalids) > 0:
@ -260,12 +266,17 @@ def do_evaluation_set_missing(
plural = len(valid_notes) > 1 plural = len(valid_notes) > 1
return scu.confirm_dialog( return scu.confirm_dialog(
f"""<h2>Mettre toutes les notes manquantes de l'évaluation f"""<h2>Mettre toutes les notes manquantes de l'évaluation
à la valeur {value} ?</h2> à la valeur <span class="fontred">{value} / {evaluation.note_max:g}</span> ?</h2>
<p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC) <p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
n'a été rentrée seront affectés.</p> n'a été rentrée seront affectés.</p>
<p><b>{len(valid_notes)} étudiant{"s" if plural else ""} concerné{"s" if plural else ""} <div>
<b>Groupes: {groups_infos.groups_titles if groups_infos else "tous"},
dont
<span class="fontred">
{len(valid_notes)} étudiant{"s" if plural else ""} concerné{"s" if plural else ""}
</span>
par ce changement de note.</b> par ce changement de note.</b>
</p> </div>
""", """,
dest_url="", dest_url="",
cancel_url=dest_url, cancel_url=dest_url,
@ -624,14 +635,8 @@ def _record_note(
# Nouveau formulaire saisie notes (2016) # Nouveau formulaire saisie notes (2016)
def saisie_notes(evaluation_id: int, group_ids: list = None): def saisie_notes(evaluation: Evaluation, group_ids: list[int] | tuple[int] = ()):
"""Formulaire saisie notes d'une évaluation pour un groupe""" """Formulaire saisie notes d'une évaluation pour un groupe"""
if not isinstance(evaluation_id, int):
raise ScoInvalidParamError()
group_ids = [int(group_id) for group_id in (group_ids or [])]
evaluation: Evaluation = db.session.get(Evaluation, evaluation_id)
if evaluation is None:
raise ScoValueError("évaluation inexistante")
modimpl = evaluation.moduleimpl modimpl = evaluation.moduleimpl
moduleimpl_status_url = url_for( moduleimpl_status_url = url_for(
"notes.moduleimpl_status", "notes.moduleimpl_status",
@ -660,7 +665,6 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
select_all_when_unspecified=True, select_all_when_unspecified=True,
etat=None, etat=None,
) )
page_title = ( page_title = (
f'Saisie "{evaluation.description}"' f'Saisie "{evaluation.description}"'
if evaluation.description if evaluation.description
@ -669,12 +673,12 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
# HTML page: # HTML page:
H = [ H = [
sco_evaluations.evaluation_describe( sco_evaluations.evaluation_describe(
evaluation_id=evaluation_id, link_saisie=False evaluation_id=evaluation.id, link_saisie=False
), ),
'<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>', '<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>',
] ]
H.append("""<div id="group-tabs"><table><tr><td>""") H.append("""<div id="group-tabs"><table><tr><td>""")
H.append(sco_groups_view.form_groups_choice(groups_infos)) H.append(sco_groups_view.form_groups_choice(groups_infos, submit_on_change=True))
H.append('</td><td style="padding-left: 35px;">') H.append('</td><td style="padding-left: 35px;">')
H.append( H.append(
htmlutils.make_menu( htmlutils.make_menu(
@ -755,8 +759,8 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
"sco_page.j2", "sco_page.j2",
content="\n".join(H), content="\n".join(H),
title=page_title, title=page_title,
javascripts=sco_groups_view.JAVASCRIPTS + ["js/saisie_notes.js"], javascripts=["js/groups_view.js", "js/saisie_notes.js"],
cssstyles=sco_groups_view.CSSSTYLES, sco=ScoData(formsemestre=modimpl.formsemestre),
) )
@ -840,11 +844,15 @@ def _form_saisie_notes(
""" """
formsemestre_id = modimpl.formsemestre_id formsemestre_id = modimpl.formsemestre_id
formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre formsemestre: FormSemestre = evaluation.moduleimpl.formsemestre
groups = sco_groups.listgroups(groups_infos.group_ids)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etudids = [ etudids = [
x[0] x[0]
for x in sco_groups.do_evaluation_listeetuds_groups( for x in sco_groups.do_evaluation_listeetuds_groups(
evaluation.id, getallstudents=True, include_demdef=True evaluation.id,
groups=groups,
getallstudents=groups is None,
include_demdef=True,
) )
] ]
if not etudids: if not etudids:
@ -1002,7 +1010,9 @@ def _form_saisie_notes(
H.append( H.append(
f""" f"""
<div> <div>
<form id="do_evaluation_set_missing" action="do_evaluation_set_missing" method="POST"> <form id="do_evaluation_set_missing" action="{
url_for("notes.do_evaluation_set_missing", scodoc_dept=g.scodoc_dept)
}" method="POST">
Mettre les notes manquantes à Mettre les notes manquantes à
<input type="text" size="5" name="value"/> <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/> <input type="submit" value="OK"/>

View File

@ -12,7 +12,6 @@ from docx.shared import Mm
from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_ALIGN_VERTICAL from docx.enum.table import WD_ALIGN_VERTICAL
from app.scodoc import sco_etud
from app.scodoc import sco_photos from app.scodoc import sco_photos
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import sco_version import sco_version
@ -31,9 +30,9 @@ def trombino_doc(groups_infos):
) )
section = document.sections[0] section = document.sections[0]
footer = section.footer footer = section.footer
footer.paragraphs[ footer.paragraphs[0].text = (
0 f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}"
].text = f"Généré par {sco_version.SCONAME} le {scu.timedate_human_repr()}" )
nb_images = len(groups_infos.members) nb_images = len(groups_infos.members)
table = document.add_table(rows=2 * (nb_images // N_PER_ROW + 1), cols=N_PER_ROW) table = document.add_table(rows=2 * (nb_images // N_PER_ROW + 1), cols=N_PER_ROW)

View File

@ -1543,15 +1543,18 @@ def confirm_dialog(
H = [ H = [
f"""<form {action} method="POST"> f"""<form {action} method="POST">
{message} {message}
<div class="form-group space-before-24">
""", """,
] ]
if OK or not cancel_url: if OK or not cancel_url:
H.append(f'<input type="submit" value="{OK}"/>') H.append(f'<input class="btn btn-default" type="submit" value="{OK}"/>')
if cancel_url: if cancel_url:
H.append( H.append(
f"""<input type ="button" value="{cancel_label}" f"""<input class="btn btn-default" type="submit" name="cancel" type ="button" value="{cancel_label}"
onClick="document.location='{cancel_url}';"/>""" onClick="event.preventDefault(); document.location='{cancel_url}';"/>"""
) )
H.append("</div>")
for param in parameters.keys(): for param in parameters.keys():
if parameters[param] is None: if parameters[param] is None:
parameters[param] = "" parameters[param] = ""

View File

@ -66,6 +66,10 @@ div.sco-app-content {
margin-top: 24px !important; margin-top: 24px !important;
} }
.space-after-24 {
margin-bottom: 24px !important;
}
div.scobox.maxwidth { div.scobox.maxwidth {
max-width: none; max-width: none;
} }
@ -480,7 +484,7 @@ div.box-chercheetud {
} }
div.box-chercheetud form input { div.box-chercheetud form input {
width: 120px; max-width: 120px;
} }
/* Page accueil général */ /* Page accueil général */

View File

@ -324,3 +324,319 @@ body {
#tableau-modules-details:not([open]) #tableau_modules { #tableau-modules-details:not([open]) #tableau_modules {
display: none; display: none;
} }
/* #region bootstrap <== Remplacement styles bootstrap ==> */
* {
box-sizing: border-box;
}
.alert {
text-wrap: balance;
}
.container {
max-width: 1140px;
--x: 1.5rem;
--y: 0;
width: 100%;
padding-right: calc(var(--x) * 0.5);
padding-left: calc(var(--x) * 0.5);
margin-right: auto;
margin-left: auto;
}
/* #region btn */
.btn {
appearance: none;
background-color: #FAFBFC;
border: 1px solid rgba(27, 31, 35, 0.15);
border-radius: 6px;
box-shadow: rgba(27, 31, 35, 0.04) 0 1px 0, rgba(255, 255, 255, 0.25) 0 1px 0 inset;
box-sizing: border-box;
color: #24292E;
cursor: pointer;
display: inline-block;
font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
font-size: 14px;
font-weight: 500;
line-height: 20px;
list-style: none;
padding: 6px 16px;
position: relative;
transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: middle;
white-space: nowrap;
word-wrap: break-word;
margin: 4px 2px;
}
a.btn {
text-decoration: none;
}
.btn:hover {
background-color: #F3F4F6;
text-decoration: none;
transition-duration: 0.1s;
}
.btn:disabled {
background-color: #FAFBFC;
border-color: rgba(27, 31, 35, 0.15);
color: #959DA5;
cursor: default;
}
.btn:active {
background-color: #EDEFF2;
box-shadow: rgba(225, 228, 232, 0.2) 0 1px 0 inset;
transition: none 0s;
}
.btn:focus {
outline: 1px transparent;
}
.btn:before {
display: none;
}
.btn:-webkit-details-marker {
display: none;
}
/* #endregion btn */
/* #region form */
label,
input,
select,
textarea {
margin: 4px 2px;
}
.control-label {
display: inline-block;
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #333;
}
.form-group input,
.form-control {
width: 100%;
padding: 10px;
margin-bottom: 16px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.2s ease-in-out;
}
/* Media query for desktop devices
évite que les boutons submit ne deviennent trop larges
*/
@media (min-width: 769px) {
.form-group input[type="submit"] {
width: 192px;
}
}
.form-group input:focus,
.form-control:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.form-group input[type="submit"],
.form-control[type="submit"] {
background-color: #007bff;
color: white;
padding: 10px 16px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
}
.form-group input[type="submit"]:hover,
.form-control[type="submit"]:hover {
background-color: #0056b3;
color: #fff;
}
.form-group input[type="submit"][name="cancel"]:hover,
.form-control[type="submit"][name="cancel"]:hover {
background-color: #606060;
color: #fff;
}
.form-group input[type="submit"][name="cancel"],
.form-control[type="submit"][name="cancel"] {
background-color: #8a8a8a;
color: #fff;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
/* #endregion form */
/* #region pagination */
.pagination {
list-style: none;
display: flex;
}
/* #endregion pagination */
/* #region navbar */
/* Navbar container */
.navbar {
background-color: #f8f9fa;
padding: 8px 2px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: center;
}
.navbar>.container.container-fluid {
margin: 4px 2px;
}
.container-fluid {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
}
.navbar-collapse {
width: 100%;
display: flex;
}
/* Brand/logo */
.navbar-brand {
font-size: 1.25rem;
font-weight: bold;
color: #333;
text-decoration: none;
}
/* Navigation links container */
.navbar-nav {
display: flex;
list-style-type: none;
margin: 0;
padding: 0;
}
/* Navigation links */
.nav-item {
margin: 0 10px;
}
.nav-link {
color: #333;
text-decoration: none;
font-size: 1rem;
transition: color 0.2s ease-in-out;
}
.nav-link:hover {
color: #007bff;
}
/* Responsive toggle button */
.navbar-toggler {
display: none;
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
}
.navbar-toggler-icon {
display: inline-block;
width: 1.5em;
height: 1.5em;
vertical-align: middle;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
}
/* Media query for responsive design */
@media (max-width: 767px) {
.navbar-collapse {
display: none;
width: 100%;
}
.navbar-collapse.show {
display: block;
}
.navbar-nav {
flex-direction: column;
width: 100%;
}
.nav-item {
margin: 10px 0;
}
.navbar-toggler {
display: block;
}
.container-fluid {
flex-wrap: wrap;
}
.navbar-brand {
flex-grow: 1;
}
}
/* Specific styles for your layout */
.navbar-nav.justify-content-center {
flex-grow: 1;
justify-content: center;
}
.navbar-nav.ms-auto {
justify-content: flex-end;
margin-left: auto !important;
}
.logout .nav-link {
color: #dc3545;
}
.logout .nav-link:hover {
color: #a71d2a;
}
/* #endregion navbar */
/* #endregion bootstrap ==> */

View File

@ -1 +0,0 @@
bootstrap-5.3.3-dist/

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,597 +0,0 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,594 +0,0 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,44 +0,0 @@
/**
* Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/)
*
* Apache License, Version 2.0:
* Copyright (c) 2012 - 2022 David Stutz
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* BSD 3-Clause License:
* Copyright (c) 2012 - 2022 David Stutz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of David Stutz nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect.dropdown-toggle:after{display:none}.multiselect{overflow:hidden;text-overflow:ellipsis}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .multiselect-reset .input-group{width:93%}.multiselect-container .multiselect-filter>.fa-search{z-index:1;padding-left:.75rem}.multiselect-container .multiselect-filter>input.multiselect-search{border:none;border-bottom:1px solid #d3d3d3;padding-left:2rem;margin-left:-1.625rem;border-bottom-right-radius:0;border-bottom-left-radius:0}.multiselect-container .multiselect-filter>input.multiselect-search:focus{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.multiselect-container .multiselect-filter>.multiselect-moz-clear-filter{margin-left:-1.5rem;display:none}.multiselect-container .multiselect-option.multiselect-group-option-indented-full{padding-left:2.6rem}.multiselect-container .multiselect-option.multiselect-group-option-indented{padding-left:1.8rem}.multiselect-container .multiselect-group{cursor:pointer}.multiselect-container .multiselect-group.closed .dropdown-toggle::after{transform:rotate(-90deg)}.multiselect-container .multiselect-group .caret-container~.form-check{margin-left:.5rem}.multiselect-container .multiselect-all,.multiselect-container .multiselect-group,.multiselect-container .multiselect-option{padding:.25rem .25rem .25rem .75rem}.multiselect-container .multiselect-all.dropdown-item,.multiselect-container .multiselect-all.dropdown-toggle,.multiselect-container .multiselect-group.dropdown-item,.multiselect-container .multiselect-group.dropdown-toggle,.multiselect-container .multiselect-option.dropdown-item,.multiselect-container .multiselect-option.dropdown-toggle{cursor:pointer}.multiselect-container .multiselect-all .form-check-label,.multiselect-container .multiselect-group .form-check-label,.multiselect-container .multiselect-option .form-check-label{cursor:pointer}.multiselect-container .multiselect-all.active:not(.multiselect-active-item-fallback),.multiselect-container .multiselect-all:not(.multiselect-active-item-fallback):active,.multiselect-container .multiselect-group.active:not(.multiselect-active-item-fallback),.multiselect-container .multiselect-group:not(.multiselect-active-item-fallback):active,.multiselect-container .multiselect-option.active:not(.multiselect-active-item-fallback),.multiselect-container .multiselect-option:not(.multiselect-active-item-fallback):active{background-color:#d3d3d3;color:#000}.multiselect-container .multiselect-all:focus,.multiselect-container .multiselect-all:hover,.multiselect-container .multiselect-group:focus,.multiselect-container .multiselect-group:hover,.multiselect-container .multiselect-option:focus,.multiselect-container .multiselect-option:hover{background-color:#a9a9a9!important}.multiselect-container .multiselect-all .form-check,.multiselect-container .multiselect-group .form-check,.multiselect-container .multiselect-option .form-check{padding:0 5px 0 20px}.multiselect-container .multiselect-all:focus,.multiselect-container .multiselect-group:focus,.multiselect-container .multiselect-option:focus{outline:0}.form-inline .multiselect-container span.form-check{padding:3px 20px 3px 40px}.input-group.input-group-sm>.multiselect-native-select .multiselect{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;padding-right:1.75rem;height:calc(1.5em + .5rem + 2px)}.input-group>.multiselect-native-select{flex:1 1 auto;width:1%}.input-group>.multiselect-native-select>div.btn-group{width:100%}.input-group>.multiselect-native-select:not(:first-child) .multiselect{border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.multiselect-native-select:not(:last-child) .multiselect{border-top-right-radius:0;border-bottom-right-radius:0}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Scodoc Mobile"/><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"><link rel="apple-touch-icon" href="./scologo.png"/><link rel="manifest" href="./manifest.json"/><title>Scodoc Mobile</title><link href="./static/css/2.4c97ca4f.chunk.css" rel="stylesheet"><link href="./static/css/main.6be5a531.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"b810fcea"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpscodocmobile=this.webpackJsonpscodocmobile||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="./static/js/2.fb06b1f5.chunk.js"></script><script src="./static/js/main.1a008285.chunk.js"></script></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Scodoc Mobile"/><link rel="apple-touch-icon" href="./scologo.png"/><link rel="manifest" href="./manifest.json"/><title>Scodoc Mobile</title><link href="./static/css/2.4c97ca4f.chunk.css" rel="stylesheet"><link href="./static/css/main.6be5a531.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"b810fcea"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpscodocmobile=this.webpackJsonpscodocmobile||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="./static/js/2.fb06b1f5.chunk.js"></script><script src="./static/js/main.1a008285.chunk.js"></script></body></html>

View File

@ -102,7 +102,6 @@
{% block styles %} {% block styles %}
{{ super() }} {{ super() }}
{# <link rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/bootstrap/css/bootstrap.min.css"> #}
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css"> <link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/minitimeline.css"> <link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/minitimeline.css">

View File

@ -14,9 +14,6 @@
href="{{scu.STATIC_DIR}}/libjs/jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.min.css" /> href="{{scu.STATIC_DIR}}/libjs/jquery-ui-1.10.4.custom/css/smoothness/jquery-ui-1.10.4.custom.min.css" />
<link type="text/css" rel="stylesheet" <link type="text/css" rel="stylesheet"
href="{{scu.STATIC_DIR}}/libjs/timepicker-1.3.5/jquery.timepicker.min.css" /> href="{{scu.STATIC_DIR}}/libjs/timepicker-1.3.5/jquery.timepicker.min.css" />
<!-- Bootstrap -->
<link href="{{scu.STATIC_DIR}}/libjs/bootstrap/css/bootstrap.css" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/bootstrap-multiselect-1.1.2/bootstrap-multiselect.min.css">
{% for css in cssstyles %} {% for css in cssstyles %}
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/{{css}}"> <link rel="stylesheet" href="{{scu.STATIC_DIR}}/{{css}}">
{% endfor %} {% endfor %}
@ -35,8 +32,6 @@
<script src="{{scu.STATIC_DIR}}/jQuery/jquery-migrate-3.5.2.min.js"></script> <script src="{{scu.STATIC_DIR}}/jQuery/jquery-migrate-3.5.2.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/jquery.field.min.js"></script> <script src="{{scu.STATIC_DIR}}/libjs/jquery.field.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script> <script src="{{scu.STATIC_DIR}}/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/bootstrap/js/bootstrap.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/bootstrap-multiselect-1.1.2/bootstrap-multiselect.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/timepicker-1.3.5/jquery.timepicker.min.js"></script> <script src="{{scu.STATIC_DIR}}/libjs/timepicker-1.3.5/jquery.timepicker.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/qtip/jquery.qtip-3.0.3.min.js"></script> <script src="{{scu.STATIC_DIR}}/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/purl.js"></script> <script src="{{scu.STATIC_DIR}}/libjs/purl.js"></script>

View File

@ -4,6 +4,7 @@
{% block styles %} {% block styles %}
{{super()}} {{super()}}
<link rel="stylesheet" href="{{ scu.STATIC_DIR }}/css/scodoc.css"> <link rel="stylesheet" href="{{ scu.STATIC_DIR }}/css/scodoc.css">
<link rel="stylesheet" href="{{ scu.STATIC_DIR }}/css/scodoc97.css">
<link rel="stylesheet" href="{{ scu.STATIC_DIR }}/css/entreprises.css"> <link rel="stylesheet" href="{{ scu.STATIC_DIR }}/css/entreprises.css">
{% endblock %} {% endblock %}
@ -21,7 +22,7 @@
{# NAVBAR CONTENT #} {# NAVBAR CONTENT #}
<div class="collapse navbar-collapse justify-content-center align-items-center" id="index-nav"> <div class="collapse navbar-collapse justify-content-center align-items-center" id="index-nav">
<ul class="navbar-nav justify-content-center"> <ul class="navbar-nav">
{% if current_user.is_administrator() %} {% if current_user.is_administrator() %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('scodoc.configuration') }}">Configuration</a></li> <li class="nav-item"><a class="nav-link" href="{{ url_for('scodoc.configuration') }}">Configuration</a></li>
{% endif %} {% endif %}
@ -88,5 +89,9 @@
<script> <script>
const SCO_URL = "{% if g.scodoc_dept %}{{ const SCO_URL = "{% if g.scodoc_dept %}{{
url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)}}{% endif %}"; url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)}}{% endif %}";
document.querySelector('.navbar-toggler').addEventListener('click', function() {
document.querySelector('.navbar-collapse').classList.toggle('show');
});
</script> </script>
{% endblock %} {% endblock %}

View File

@ -4,7 +4,6 @@
{% block styles %} {% block styles %}
{{super()}} {{super()}}
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/bootstrap/css/bootstrap.min.css">
<style> <style>
div{ div{

View File

@ -2,8 +2,8 @@
{% macro form_errors(form, hiddens=True) %} {% macro form_errors(form, hiddens=True) %}
{%- if form.errors %} {%- if form.errors %}
{%- for fieldname, errors in form.errors.items() %} {%- for fieldname, errors in form.errors.items() %}
{%- if bootstrap_is_hidden_field(form[fieldname]) and hiddens or {%- if is_hidden_field(form[fieldname]) and hiddens or
not bootstrap_is_hidden_field(form[fieldname]) and hiddens != 'only' %} not is_hidden_field(form[fieldname]) and hiddens != 'only' %}
{%- for error in errors %} {%- for error in errors %}
<p class="error">{{error}}</p> <p class="error">{{error}}</p>
{%- endfor %} {%- endfor %}
@ -75,7 +75,7 @@ the necessary fix for required=False attributes, but will also not set the requi
<fieldset> <fieldset>
<legend>{{field.label}}</legend> <legend>{{field.label}}</legend>
{%- for subfield in field %} {%- for subfield in field %}
{% if not bootstrap_is_hidden_field(subfield) -%} {% if not is_hidden_field(subfield) -%}
{{ form_field(subfield, {{ form_field(subfield,
form_type=form_type, form_type=form_type,
horizontal_columns=horizontal_columns, horizontal_columns=horizontal_columns,
@ -202,7 +202,7 @@ action="" is what we want, from http://www.ietf.org/rfc/rfc2396.txt:
{{ form_errors(form, hiddens='only') }} {{ form_errors(form, hiddens='only') }}
{%- for field in form %} {%- for field in form %}
{% if not bootstrap_is_hidden_field(field) -%} {% if not is_hidden_field(field) -%}
{{ form_field(field, {{ form_field(field,
form_type=form_type, form_type=form_type,
horizontal_columns=horizontal_columns, horizontal_columns=horizontal_columns,

View File

@ -96,9 +96,6 @@ from app.tables.visu_assiduites import TableAssi, etuds_sorted_from_ids
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
CSSSTYLES = html_sco_header.BOOTSTRAP_CSS
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# #
# Assiduité (/ScoDoc/<dept>/Scolarite/Assiduites/...) # Assiduité (/ScoDoc/<dept>/Scolarite/Assiduites/...)

View File

@ -1836,7 +1836,6 @@ sco_publish(
sco_saisie_excel.feuille_saisie_notes, sco_saisie_excel.feuille_saisie_notes,
Permission.EnsView, Permission.EnsView,
) )
sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.EnsView)
sco_publish( sco_publish(
"/do_evaluation_set_missing", "/do_evaluation_set_missing",
sco_saisie_notes.do_evaluation_set_missing, sco_saisie_notes.do_evaluation_set_missing,
@ -1851,6 +1850,20 @@ sco_publish(
) )
@bp.route("/form_saisie_notes/<int:evaluation_id>")
@scodoc
@permission_required(Permission.EnsView) # + controle contextuel
def form_saisie_notes(evaluation_id: int):
"Formulaire de saisie des notes d'une évaluation"
evaluation = Evaluation.get_evaluation(evaluation_id)
group_ids = request.args.getlist("group_ids")
try:
group_ids = [int(gid) for gid in group_ids]
except ValueError as exc:
raise ScoValueError("group_ids invalide") from exc
return sco_saisie_notes.saisie_notes(evaluation, group_ids)
@bp.route("/formsemestre_import_notes/<int:formsemestre_id>", methods=["GET", "POST"]) @bp.route("/formsemestre_import_notes/<int:formsemestre_id>", methods=["GET", "POST"])
@scodoc @scodoc
@permission_required(Permission.ScoView) # controle contextuel @permission_required(Permission.ScoView) # controle contextuel
@ -2117,7 +2130,9 @@ def _formsemestre_bulletins_choice(
explanation=explanation, explanation=explanation,
choose_mail=choose_mail, choose_mail=choose_mail,
formsemestre=formsemestre, formsemestre=formsemestre,
menu_groups_choice=sco_groups_view.menu_groups_choice(groups_infos), menu_groups_choice=sco_groups_view.menu_groups_choice(
groups_infos, submit_on_change=True
),
sco=ScoData(formsemestre=formsemestre), sco=ScoData(formsemestre=formsemestre),
sco_groups_view=sco_groups_view, sco_groups_view=sco_groups_view,
title=title, title=title,

View File

@ -30,7 +30,6 @@ class Config:
# Le "from" des mails émis. Attention: peut être remplacée par la préférence email_from_addr: # Le "from" des mails émis. Attention: peut être remplacée par la préférence email_from_addr:
SCODOC_MAIL_FROM = os.environ.get("SCODOC_MAIL_FROM") or ("no-reply@" + MAIL_SERVER) SCODOC_MAIL_FROM = os.environ.get("SCODOC_MAIL_FROM") or ("no-reply@" + MAIL_SERVER)
BOOTSTRAP_SERVE_LOCAL = os.environ.get("BOOTSTRAP_SERVE_LOCAL")
SCODOC_DIR = os.environ.get("SCODOC_DIR", "/opt/scodoc") SCODOC_DIR = os.environ.get("SCODOC_DIR", "/opt/scodoc")
SCODOC_VAR_DIR = os.environ.get("SCODOC_VAR_DIR", "/opt/scodoc-data") SCODOC_VAR_DIR = os.environ.get("SCODOC_VAR_DIR", "/opt/scodoc-data")
SCODOC_LOG_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc.log") SCODOC_LOG_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc.log")
@ -46,6 +45,7 @@ class Config:
class ProdConfig(Config): class ProdConfig(Config):
"mode production, normalement derrière nginx/gunicorn" "mode production, normalement derrière nginx/gunicorn"
FLASK_ENV = "production" FLASK_ENV = "production"
DEBUG = False DEBUG = False
TESTING = False TESTING = False
@ -57,6 +57,7 @@ class ProdConfig(Config):
class DevConfig(Config): class DevConfig(Config):
"mode développement" "mode développement"
FLASK_ENV = "development" FLASK_ENV = "development"
DEBUG = True DEBUG = True
TESTING = False TESTING = False
@ -70,6 +71,7 @@ class DevConfig(Config):
class TestConfig(DevConfig): class TestConfig(DevConfig):
"Pour les tests unitaires" "Pour les tests unitaires"
TESTING = True TESTING = True
DEBUG = False DEBUG = False
SQLALCHEMY_DATABASE_URI = ( SQLALCHEMY_DATABASE_URI = (
@ -82,6 +84,7 @@ class TestConfig(DevConfig):
class TestAPIConfig(Config): class TestAPIConfig(Config):
"Pour les tests de l'API" "Pour les tests de l'API"
FLASK_ENV = "test_api" FLASK_ENV = "test_api"
TESTING = False TESTING = False
DEBUG = True DEBUG = True

View File

@ -35,14 +35,6 @@ server {
alias /opt/scodoc/app/static; alias /opt/scodoc/app/static;
expires 1d; expires 1d;
} }
location /ScoDoc/static/bootstrap {
# (temp.) exception: home page using flask_bootstrap !
proxy_pass http://localhost:8000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /favicon.ico { location /favicon.ico {
alias /opt/scodoc/app/static/icons/favicon.ico; alias /opt/scodoc/app/static/icons/favicon.ico;
} }