forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -27,7 +27,7 @@ from app.models import (
|
||||
Justificatif,
|
||||
)
|
||||
from flask_sqlalchemy.query import Query
|
||||
from app.models.assiduites import get_assiduites_justif
|
||||
from app.models.assiduites import get_assiduites_justif, get_justifs_from_date
|
||||
from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.sco_utils import json_error
|
||||
@ -728,7 +728,6 @@ def assiduite_edit(assiduite_id: int):
|
||||
assiduite_unique.etudiant.id,
|
||||
msg=f"assiduite: modif {assiduite_unique}",
|
||||
)
|
||||
db.session.add(assiduite_unique)
|
||||
db.session.commit()
|
||||
scass.simple_invalidate_cache(assiduite_unique.to_dict())
|
||||
|
||||
@ -848,15 +847,23 @@ def _edit_singular(assiduite_unique, data):
|
||||
# Cas 3 : desc
|
||||
desc = data.get("desc", False)
|
||||
if desc is not False:
|
||||
assiduite_unique.desc = desc
|
||||
assiduite_unique.description = desc
|
||||
|
||||
# Cas 4 : est_just
|
||||
est_just = data.get("est_just")
|
||||
if est_just is not None:
|
||||
if not isinstance(est_just, bool):
|
||||
errors.append("param 'est_just' : booléen non reconnu")
|
||||
if assiduite_unique.etat == scu.EtatAssiduite.PRESENT:
|
||||
assiduite_unique.est_just = False
|
||||
else:
|
||||
assiduite_unique.est_just = est_just
|
||||
assiduite_unique.est_just = (
|
||||
len(
|
||||
get_justifs_from_date(
|
||||
assiduite_unique.etudiant.id,
|
||||
assiduite_unique.date_debut,
|
||||
assiduite_unique.date_fin,
|
||||
valid=True,
|
||||
)
|
||||
)
|
||||
> 0
|
||||
)
|
||||
|
||||
if errors:
|
||||
err: str = ", ".join(errors)
|
||||
@ -1024,6 +1031,19 @@ def _filter_manager(requested, assiduites_query: Query) -> Query:
|
||||
if user_id is not False:
|
||||
assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id)
|
||||
|
||||
order = requested.args.get("order", None)
|
||||
if order is not None:
|
||||
assiduites_query: Query = assiduites_query.order_by(Assiduite.date_debut.desc())
|
||||
|
||||
courant = requested.args.get("courant", None)
|
||||
if courant is not None:
|
||||
annee: int = scu.annee_scolaire()
|
||||
|
||||
assiduites_query: Query = assiduites_query.filter(
|
||||
Assiduite.date_debut >= scu.date_debut_anne_scolaire(annee),
|
||||
Assiduite.date_fin <= scu.date_fin_anne_scolaire(annee),
|
||||
)
|
||||
|
||||
return assiduites_query
|
||||
|
||||
|
||||
|
@ -130,6 +130,8 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
|
||||
@api_web_bp.route(
|
||||
"/justificatifs/dept/<int:dept_id>/query", defaults={"with_query": True}
|
||||
)
|
||||
@bp.route("/justificatifs/dept/<int:dept_id>", defaults={"with_query": False})
|
||||
@bp.route("/justificatifs/dept/<int:dept_id>/query", defaults={"with_query": True})
|
||||
@login_required
|
||||
@scodoc
|
||||
@as_json
|
||||
@ -151,6 +153,48 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
||||
return data_set
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/justificatifs/formsemestre/<int:formsemestre_id>", defaults={"with_query": False}
|
||||
)
|
||||
@api_web_bp.route(
|
||||
"/justificatifs/formsemestre/<int:formsemestre_id>", defaults={"with_query": False}
|
||||
)
|
||||
@bp.route(
|
||||
"/justificatifs/formsemestre/<int:formsemestre_id>/query",
|
||||
defaults={"with_query": True},
|
||||
)
|
||||
@api_web_bp.route(
|
||||
"/justificatifs/formsemestre/<int:formsemestre_id>/query",
|
||||
defaults={"with_query": True},
|
||||
)
|
||||
@login_required
|
||||
@scodoc
|
||||
@as_json
|
||||
@permission_required(Permission.ScoView)
|
||||
def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
||||
"""Retourne tous les justificatifs du formsemestre"""
|
||||
formsemestre: FormSemestre = None
|
||||
formsemestre_id = int(formsemestre_id)
|
||||
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
||||
|
||||
if formsemestre is None:
|
||||
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
||||
|
||||
justificatifs_query = scass.filter_by_formsemestre(
|
||||
Justificatif.query, Justificatif, formsemestre
|
||||
)
|
||||
|
||||
if with_query:
|
||||
justificatifs_query = _filter_manager(request, justificatifs_query)
|
||||
|
||||
data_set: list[dict] = []
|
||||
for justi in justificatifs_query.all():
|
||||
data = justi.to_dict(format_api=True)
|
||||
data_set.append(data)
|
||||
|
||||
return data_set
|
||||
|
||||
|
||||
@bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
|
||||
@api_web_bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
|
||||
@bp.route("/justificatif/etudid/<etudid>/create", methods=["POST"])
|
||||
@ -696,4 +740,19 @@ def _filter_manager(requested, justificatifs_query):
|
||||
justificatifs_query, Justificatif, formsemestre
|
||||
)
|
||||
|
||||
order = requested.args.get("order", None)
|
||||
if order is not None:
|
||||
justificatifs_query: Query = justificatifs_query.order_by(
|
||||
Justificatif.date_debut.desc()
|
||||
)
|
||||
|
||||
courant = requested.args.get("courant", None)
|
||||
if courant is not None:
|
||||
annee: int = scu.annee_scolaire()
|
||||
|
||||
justificatifs_query: Query = justificatifs_query.filter(
|
||||
Justificatif.date_debut >= scu.date_debut_anne_scolaire(annee),
|
||||
Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee),
|
||||
)
|
||||
|
||||
return justificatifs_query
|
||||
|
@ -134,7 +134,10 @@ class Assiduite(db.Model):
|
||||
|
||||
if not est_just:
|
||||
est_just = (
|
||||
len(_get_assiduites_justif(etud.etudid, date_debut, date_fin)) > 0
|
||||
len(
|
||||
get_justifs_from_date(etud.etudid, date_debut, date_fin, valid=True)
|
||||
)
|
||||
> 0
|
||||
)
|
||||
|
||||
if moduleimpl is not None:
|
||||
@ -375,16 +378,23 @@ def compute_assiduites_justified(
|
||||
|
||||
def get_assiduites_justif(assiduite_id: int, long: bool):
|
||||
assi: Assiduite = Assiduite.query.get_or_404(assiduite_id)
|
||||
return _get_assiduites_justif(assi.etudid, assi.date_debut, assi.date_fin, long)
|
||||
return get_justifs_from_date(assi.etudid, assi.date_debut, assi.date_fin, long)
|
||||
|
||||
|
||||
def _get_assiduites_justif(
|
||||
etudid: int, date_debut: datetime, date_fin: datetime, long: bool = False
|
||||
def get_justifs_from_date(
|
||||
etudid: int,
|
||||
date_debut: datetime,
|
||||
date_fin: datetime,
|
||||
long: bool = False,
|
||||
valid: bool = False,
|
||||
):
|
||||
justifs: Justificatif = Justificatif.query.filter(
|
||||
justifs: Query = Justificatif.query.filter(
|
||||
Justificatif.etudid == etudid,
|
||||
Justificatif.date_debut <= date_debut,
|
||||
Justificatif.date_fin >= date_fin,
|
||||
)
|
||||
|
||||
if valid:
|
||||
justifs = justifs.filter(Justificatif.etat == EtatJustificatif.VALIDE)
|
||||
|
||||
return [j.justif_id if not long else j.to_dict(True) for j in justifs]
|
||||
|
@ -311,7 +311,7 @@ def filter_by_date(
|
||||
)
|
||||
|
||||
|
||||
def filter_justificatifs_by_etat(justificatifs: Justificatif, etat: str) -> Query:
|
||||
def filter_justificatifs_by_etat(justificatifs: Query, etat: str) -> Query:
|
||||
"""
|
||||
Filtrage d'une collection de justificatifs en fonction de leur état
|
||||
"""
|
||||
|
@ -610,6 +610,17 @@ class BasePreferences:
|
||||
},
|
||||
),
|
||||
# Assiduités
|
||||
(
|
||||
"assi_limit_annee",
|
||||
{
|
||||
"initvalue": 1,
|
||||
"title": "Ne lister que les assiduités de l'année",
|
||||
"explanation": "Limite l'affichage des listes d'assiduités et de justificatifs à l'année en cours",
|
||||
"input_type": "boolcheckbox",
|
||||
"labels": ["non", "oui"],
|
||||
"category": "assi",
|
||||
},
|
||||
),
|
||||
(
|
||||
"forcer_module",
|
||||
{
|
||||
|
@ -1107,6 +1107,7 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in
|
||||
evaluation.date_debut.date().isoformat() if evaluation.date_debut else ""
|
||||
)
|
||||
warn_abs_lst = []
|
||||
# XXX TODO-ASSIDUITE (issue #686)
|
||||
if evaluation.is_matin():
|
||||
nbabs = 0 # TODO-ASSIDUITE sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=True)
|
||||
nbabsjust = 0 # TODO-ASSIDUITE sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=True)
|
||||
|
@ -136,6 +136,8 @@
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 0 5%;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.etud_row.def .nom::after,
|
||||
@ -268,6 +270,7 @@
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
|
||||
.rbtn.present::before {
|
||||
background-image: url(../icons/present.svg);
|
||||
}
|
||||
@ -285,8 +288,8 @@
|
||||
}
|
||||
|
||||
.rbtn:checked:before {
|
||||
outline: 3px solid #7059FF;
|
||||
border-radius: 5px;
|
||||
outline: 5px solid #7059FF;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.rbtn:focus {
|
||||
|
@ -1065,8 +1065,22 @@ function actualizeEtudAssiduite(etudid) {
|
||||
});
|
||||
}
|
||||
|
||||
function getAllAssiduitesFromEtud(etudid, action) {
|
||||
const url_api = getUrl() + `/api/assiduites/${etudid}`;
|
||||
function getAllAssiduitesFromEtud(
|
||||
etudid,
|
||||
action,
|
||||
order = false,
|
||||
justifs = false,
|
||||
courant = false
|
||||
) {
|
||||
const url_api =
|
||||
getUrl() +
|
||||
`/api/assiduites/${etudid}${
|
||||
order
|
||||
? "/query?order%°"
|
||||
.replace("%", justifs ? "&with_justifs" : "")
|
||||
.replace("°", courant ? "&courant" : "")
|
||||
: ""
|
||||
}`;
|
||||
|
||||
$.ajax({
|
||||
async: true,
|
||||
@ -1240,12 +1254,10 @@ function generateEtudRow(
|
||||
|
||||
<img class="pdp" src="${pdp_url}">
|
||||
|
||||
<div class="name_set">
|
||||
|
||||
<a class="name_set" href="BilanEtud?etudid=${etud.id}">
|
||||
<h4 class="nom">${etud.nom}</h4>
|
||||
<h5 class="prenom">${etud.prenom}</h5>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="assiduites_bar">
|
||||
@ -1558,20 +1570,18 @@ function fastJustify(assiduite) {
|
||||
//créer justificatif
|
||||
|
||||
const justif = {
|
||||
date_debut: new moment.tz(assiduite.date_debut, TIMEZONE)
|
||||
.add(1, "s")
|
||||
.format(),
|
||||
date_fin: new moment.tz(assiduite.date_fin, TIMEZONE)
|
||||
.subtract(1, "s")
|
||||
.format(),
|
||||
date_debut: new moment.tz(assiduite.date_debut, TIMEZONE).format(),
|
||||
date_fin: new moment.tz(assiduite.date_fin, TIMEZONE).format(),
|
||||
raison: raison,
|
||||
etat: etat,
|
||||
};
|
||||
|
||||
createJustificatif(justif);
|
||||
|
||||
// justifyAssiduite(assiduite.assiduite_id, true);
|
||||
generateAllEtudRow();
|
||||
try {
|
||||
loadAll();
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const content = document.createElement("fieldset");
|
||||
@ -1634,8 +1644,17 @@ function createJustificatif(justif, success = () => {}) {
|
||||
});
|
||||
}
|
||||
|
||||
function getAllJustificatifsFromEtud(etudid, action) {
|
||||
const url_api = getUrl() + `/api/justificatifs/${etudid}`;
|
||||
function getAllJustificatifsFromEtud(
|
||||
etudid,
|
||||
action,
|
||||
order = false,
|
||||
courant = false
|
||||
) {
|
||||
const url_api =
|
||||
getUrl() +
|
||||
`/api/justificatifs/${etudid}${
|
||||
order ? "/query?order°".replace("°", courant ? "&courant" : "") : ""
|
||||
}`;
|
||||
$.ajax({
|
||||
async: true,
|
||||
type: "GET",
|
||||
|
@ -114,9 +114,18 @@ class RowAssi(tb.Row):
|
||||
|
||||
compte_justificatifs = scass.filter_by_date(
|
||||
etud.justificatifs, Justificatif, self.dates[0], self.dates[1]
|
||||
).count()
|
||||
)
|
||||
|
||||
self.add_cell("justificatifs", "Justificatifs", f"{compte_justificatifs}")
|
||||
compte_justificatifs_att = compte_justificatifs.filter(Justificatif.etat == 2)
|
||||
|
||||
self.add_cell(
|
||||
"justificatifs_att",
|
||||
"Justificatifs en Attente",
|
||||
f"{compte_justificatifs_att.count()}",
|
||||
)
|
||||
self.add_cell(
|
||||
"justificatifs", "Justificatifs", f"{compte_justificatifs.count()}"
|
||||
)
|
||||
|
||||
def _get_etud_stats(self, etud: Identite) -> dict[str, list[str, float, float]]:
|
||||
retour: dict[str, tuple[str, float, float]] = {
|
||||
|
@ -19,8 +19,9 @@
|
||||
<div class="justi-label">
|
||||
<legend for="justi_date_debut" required>Date de début</legend>
|
||||
<input type="datetime-local" name="justi_date_debut" id="justi_date_debut">
|
||||
<span>Journée entière</span> <input type="checkbox" name="justi_journee" id="justi_journee">
|
||||
</div>
|
||||
<div class="justi-label">
|
||||
<div class="justi-label" id="date_fin">
|
||||
<legend for="justi_date_fin" required>Date de fin</legend>
|
||||
<input type="datetime-local" name="justi_date_fin" id="justi_date_fin">
|
||||
</div>
|
||||
@ -110,16 +111,15 @@
|
||||
|
||||
function validateFields() {
|
||||
const field = document.querySelector('.justi-form')
|
||||
const in_date_debut = field.querySelector('#justi_date_debut');
|
||||
const in_date_fin = field.querySelector('#justi_date_fin');
|
||||
const { deb, fin } = getDates()
|
||||
|
||||
if (in_date_debut.value == "" || in_date_fin.value == "") {
|
||||
if (deb.value == "" || fin.value == "") {
|
||||
openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin."), "", color = "crimson");
|
||||
return false;
|
||||
}
|
||||
|
||||
const date_debut = moment.tz(in_date_debut.value, TIMEZONE);
|
||||
const date_fin = moment.tz(in_date_fin.value, TIMEZONE);
|
||||
const date_debut = moment.tz(deb.value, TIMEZONE);
|
||||
const date_fin = moment.tz(fin.value, TIMEZONE);
|
||||
|
||||
if (date_fin.isBefore(date_debut)) {
|
||||
openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson");
|
||||
@ -132,14 +132,14 @@
|
||||
function fieldsToJustificatif() {
|
||||
const field = document.querySelector('.justi-form')
|
||||
|
||||
const date_debut = field.querySelector('#justi_date_debut').value;
|
||||
const date_fin = field.querySelector('#justi_date_fin').value;
|
||||
const { deb, fin } = getDates()
|
||||
|
||||
const etat = field.querySelector('#justi_etat').value;
|
||||
const raison = field.querySelector('#justi_raison').value;
|
||||
|
||||
return {
|
||||
date_debut: date_debut,
|
||||
date_fin: date_fin,
|
||||
date_debut: deb,
|
||||
date_fin: fin,
|
||||
etat: etat,
|
||||
raison: raison,
|
||||
}
|
||||
@ -218,11 +218,43 @@
|
||||
|
||||
}
|
||||
|
||||
function dayOnly() {
|
||||
|
||||
if (document.getElementById('justi_journee').checked) {
|
||||
document.getElementById("date_fin").style.display = "none";
|
||||
document.getElementById("justi_date_debut").type = "date"
|
||||
} else {
|
||||
document.getElementById("date_fin").style.display = "block";
|
||||
document.getElementById("justi_date_debut").type = "datetime-local"
|
||||
}
|
||||
}
|
||||
|
||||
function getDates() {
|
||||
if (document.getElementById('justi_journee').checked) {
|
||||
const date_str = document.getElementById("justi_date_debut").value
|
||||
|
||||
return {
|
||||
"deb": `${date_str}T${assi_morning}`,
|
||||
"fin": `${date_str}T${assi_evening}`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"deb": document.getElementById("justi_date_debut").value,
|
||||
"fin": document.getElementById("justi_date_fin").value,
|
||||
}
|
||||
}
|
||||
|
||||
const etudid = {{ sco.etud.id }};
|
||||
|
||||
const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
|
||||
const assi_morning = '{{assi_morning}}';
|
||||
const assi_evening = '{{assi_evening}}';
|
||||
|
||||
window.onload = () => {
|
||||
loadAll();
|
||||
document.getElementById('justi_journee').addEventListener('click', () => { dayOnly() });
|
||||
dayOnly()
|
||||
}
|
||||
</script>
|
||||
{% endblock pageContent %}
|
@ -29,6 +29,7 @@
|
||||
</div>
|
||||
<script>
|
||||
|
||||
|
||||
function loadAll() {
|
||||
generate(defAnnee)
|
||||
}
|
||||
@ -57,7 +58,7 @@
|
||||
}
|
||||
bornes = {
|
||||
deb: `${annee}-09-01T00:00`,
|
||||
fin: `${annee + 1}-06-30T23:59`
|
||||
fin: `${annee + 1}-08-31T23:59`
|
||||
}
|
||||
|
||||
defAnnee = annee;
|
||||
@ -75,10 +76,14 @@
|
||||
let defAnnee = {{ annee }};
|
||||
let bornes = {
|
||||
deb: `${defAnnee}-09-01T00:00`,
|
||||
fin: `${defAnnee + 1}-06-30T23:59`
|
||||
fin: `${defAnnee + 1}-08-31T23:59`
|
||||
}
|
||||
const dept_id = {{ dept_id }};
|
||||
|
||||
let annees = {{ annees | safe}}
|
||||
|
||||
annees = annees.filter((x, i) => annees.indexOf(x) === i)
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
|
||||
filterJustificatifs = {
|
||||
@ -99,15 +104,16 @@
|
||||
}
|
||||
}
|
||||
const select = document.querySelector('#annee');
|
||||
for (let i = defAnnee + 1; i > defAnnee - 6; i--) {
|
||||
|
||||
annees.forEach((a) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = i + "",
|
||||
opt.textContent = i + "";
|
||||
if (i === defAnnee) {
|
||||
opt.value = a + "",
|
||||
opt.textContent = `${a} - ${a + 1}`;
|
||||
if (a === defAnnee) {
|
||||
opt.selected = true;
|
||||
}
|
||||
select.appendChild(opt)
|
||||
}
|
||||
})
|
||||
setterAnnee(defAnnee)
|
||||
})
|
||||
|
||||
|
@ -266,6 +266,9 @@
|
||||
const assi_date_debut = "{{date_debut}}";
|
||||
const assi_date_fin = "{{date_fin}}";
|
||||
|
||||
const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
|
||||
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
filterAssiduites = {
|
||||
"columns": [
|
||||
|
@ -354,5 +354,7 @@
|
||||
setterAnnee(defAnnee)
|
||||
};
|
||||
|
||||
|
||||
function isCalendrier() { return true }
|
||||
</script>
|
||||
{% endblock pageContent %}
|
@ -48,8 +48,43 @@
|
||||
|
||||
<script>
|
||||
const etudid = {{ sco.etud.id }}
|
||||
|
||||
const assiduite_unique_id = {{ assi_id }};
|
||||
|
||||
const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
|
||||
|
||||
|
||||
function wayForFilter() {
|
||||
if (typeof assiduites[etudid] !== "undefined") {
|
||||
console.log("Done")
|
||||
let assiduite = assiduites[etudid].filter((a) => { return a.assiduite_id == assiduite_unique_id });
|
||||
|
||||
if (assiduite) {
|
||||
assiduite = assiduite[0]
|
||||
filterAssiduites["filters"] = {
|
||||
"obj_id": [
|
||||
assiduite.assiduite_id,
|
||||
]
|
||||
}
|
||||
const obj_ids = assiduite.justificatifs ? assiduite.justificatifs.map((j) => { return j.justif_id }) : []
|
||||
filterJustificatifs["filters"] = {
|
||||
"obj_id": obj_ids
|
||||
}
|
||||
|
||||
loadAll();
|
||||
}
|
||||
} else {
|
||||
setTimeout(wayForFilter, 250)
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
loadAll();
|
||||
|
||||
if (assiduite_unique_id != -1) {
|
||||
wayForFilter()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
@ -71,6 +71,11 @@
|
||||
updateSelectedSelect(getCurrentAssiduiteModuleImplId());
|
||||
updateJustifyBtn();
|
||||
}
|
||||
try {
|
||||
if (isCalendrier()) {
|
||||
window.location = `ListeAssiduitesEtud?etudid=${etudid}&assiduite_id=${assiduité.assiduite_id}`
|
||||
}
|
||||
} catch { }
|
||||
});
|
||||
//ajouter affichage assiduites on over
|
||||
setupAssiduiteBuble(block, assiduité);
|
||||
|
@ -147,7 +147,7 @@
|
||||
<span class="obj-content">${etat}</span>
|
||||
</div>
|
||||
<div id="user" class="obj-part">
|
||||
<span class="obj-title">Créer par</span>
|
||||
<span class="obj-title">Créée par</span>
|
||||
<span class="obj-content">${user}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -239,7 +239,7 @@
|
||||
edit = setModuleImplId(edit, module);
|
||||
|
||||
fullEditAssiduites(data.assiduite_id, edit, () => {
|
||||
try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { }
|
||||
loadAll();
|
||||
})
|
||||
|
||||
|
||||
|
@ -18,6 +18,9 @@
|
||||
|
||||
document.addEventListener("click", () => {
|
||||
contextMenu.style.display = "none";
|
||||
if (contextMenu.childElementCount > 3) {
|
||||
contextMenu.removeChild(contextMenu.lastElementChild)
|
||||
}
|
||||
});
|
||||
|
||||
editOption.addEventListener("click", () => {
|
||||
@ -94,6 +97,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (k == "obj_id") {
|
||||
const obj_id = el.assiduite_id || el.justif_id;
|
||||
return f.obj_id.includes(obj_id)
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
@ -150,7 +158,7 @@
|
||||
paginationContainerAssiduites.querySelector('.pagination_moins').addEventListener('click', () => {
|
||||
if (currentPageAssiduites > 1) {
|
||||
currentPageAssiduites--;
|
||||
paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites
|
||||
paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + ""
|
||||
assiduiteCallBack(array);
|
||||
|
||||
}
|
||||
@ -159,7 +167,7 @@
|
||||
paginationContainerAssiduites.querySelector('.pagination_plus').addEventListener('click', () => {
|
||||
if (currentPageAssiduites < totalPages) {
|
||||
currentPageAssiduites++;
|
||||
paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites
|
||||
paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + ""
|
||||
assiduiteCallBack(array);
|
||||
}
|
||||
})
|
||||
@ -199,8 +207,12 @@
|
||||
|
||||
if (assi) {
|
||||
paginationContainerAssiduites.querySelector('#paginationAssi').appendChild(paginationButton)
|
||||
if (i == currentPageAssiduites)
|
||||
paginationContainerAssiduites.querySelector('#paginationAssi').value = i + "";
|
||||
} else {
|
||||
paginationContainerJustificatifs.querySelector('#paginationJusti').appendChild(paginationButton)
|
||||
if (i == currentPageJustificatifs)
|
||||
paginationContainerJustificatifs.querySelector('#paginationJusti').value = i + "";
|
||||
}
|
||||
}
|
||||
updateActivePaginationButton(assi);
|
||||
@ -230,8 +242,8 @@
|
||||
}
|
||||
|
||||
function loadAll() {
|
||||
try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { }
|
||||
try { getAllJustificatifsFromEtud(etudid, justificatifCallBack) } catch (_) { }
|
||||
try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack, true, true, assi_limit_annee) } catch (_) { }
|
||||
try { getAllJustificatifsFromEtud(etudid, justificatifCallBack, true, assi_limit_annee) } catch (_) { }
|
||||
}
|
||||
|
||||
function order(keyword, callback = () => { }, el, assi = true) {
|
||||
@ -641,6 +653,27 @@
|
||||
contextMenu.style.top = `${e.clientY - contextMenu.offsetHeight}px`;
|
||||
contextMenu.style.left = `${e.clientX}px`;
|
||||
contextMenu.style.display = "block";
|
||||
if (contextMenu.childElementCount > 3) {
|
||||
contextMenu.removeChild(contextMenu.lastElementChild)
|
||||
}
|
||||
if (selectedRow.getAttribute('type') == "assiduite") {
|
||||
|
||||
const li = document.createElement('li')
|
||||
li.textContent = "Justifier"
|
||||
|
||||
li.addEventListener('click', () => {
|
||||
let obj_id = selectedRow.getAttribute('obj_id');
|
||||
assiduite = Object.values(assiduites).flat().filter((a) => { return a.assiduite_id == obj_id })
|
||||
console.log(assiduite[0])
|
||||
if (assiduite && !assiduite[0].est_just && assiduite[0].etat != "PRESENT") {
|
||||
fastJustify(assiduite[0])
|
||||
} else {
|
||||
openAlertModal("Erreur", document.createTextNode("L'assiduité est déjà justifiée ou ne peut pas l'être."))
|
||||
}
|
||||
})
|
||||
|
||||
contextMenu.appendChild(li)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
@ -169,7 +169,7 @@
|
||||
<span class="obj-content">${etat}</span>
|
||||
</div>
|
||||
<div id="user" class="obj-part">
|
||||
<span class="obj-title">Créer par</span>
|
||||
<span class="obj-title">Créé par</span>
|
||||
<span class="obj-content">${user}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
const period_default = {{ periode_defaut }};
|
||||
|
||||
let handleMoving = false;
|
||||
|
||||
function createTicks() {
|
||||
let i = t_start;
|
||||
|
||||
@ -87,42 +89,21 @@
|
||||
|
||||
}
|
||||
|
||||
function setupTimeLine(callback) {
|
||||
function timelineMainEvent(event, callback) {
|
||||
const func_call = callback ? callback : () => { };
|
||||
timelineContainer.addEventListener("mousedown", (event) => {
|
||||
const startX = event.clientX;
|
||||
|
||||
if (event.target === periodTimeLine) {
|
||||
const startLeft = parseFloat(periodTimeLine.style.left);
|
||||
const startX = (event.clientX || event.changedTouches[0].clientX);
|
||||
|
||||
const onMouseMove = (moveEvent) => {
|
||||
const deltaX = moveEvent.clientX - startX;
|
||||
const containerWidth = timelineContainer.clientWidth;
|
||||
const newLeft = startLeft + (deltaX / containerWidth) * 100;
|
||||
|
||||
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
|
||||
|
||||
updatePeriodTimeLabel();
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener(
|
||||
"mouseup",
|
||||
() => {
|
||||
generateAllEtudRow();
|
||||
snapHandlesToQuarters();
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
func_call();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
} else if (event.target.classList.contains("period-handle")) {
|
||||
if (event.target.classList.contains("period-handle")) {
|
||||
const startWidth = parseFloat(periodTimeLine.style.width);
|
||||
const startLeft = parseFloat(periodTimeLine.style.left);
|
||||
const isLeftHandle = event.target.classList.contains("left");
|
||||
|
||||
handleMoving = true
|
||||
const onMouseMove = (moveEvent) => {
|
||||
const deltaX = moveEvent.clientX - startX;
|
||||
|
||||
if (!handleMoving) return;
|
||||
|
||||
const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX;
|
||||
const containerWidth = timelineContainer.clientWidth;
|
||||
const newWidth =
|
||||
startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
|
||||
@ -136,23 +117,64 @@
|
||||
|
||||
updatePeriodTimeLabel();
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
document.addEventListener(
|
||||
"mouseup",
|
||||
() => {
|
||||
const mouseUp = () => {
|
||||
snapHandlesToQuarters();
|
||||
generateAllEtudRow();
|
||||
|
||||
document.removeEventListener("mousemove", onMouseMove);
|
||||
|
||||
timelineContainer.removeEventListener("mousemove", onMouseMove);
|
||||
handleMoving = false;
|
||||
func_call();
|
||||
|
||||
},
|
||||
}
|
||||
timelineContainer.addEventListener("mousemove", onMouseMove);
|
||||
timelineContainer.addEventListener("touchmove", onMouseMove);
|
||||
document.addEventListener(
|
||||
"mouseup",
|
||||
mouseUp,
|
||||
);
|
||||
document.addEventListener(
|
||||
"touchend",
|
||||
mouseUp,
|
||||
);
|
||||
} else if (event.target === periodTimeLine) {
|
||||
|
||||
const startLeft = parseFloat(periodTimeLine.style.left);
|
||||
|
||||
const onMouseMove = (moveEvent) => {
|
||||
console.warn("move Period")
|
||||
if (handleMoving) return;
|
||||
const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX;
|
||||
const containerWidth = timelineContainer.clientWidth;
|
||||
const newLeft = startLeft + (deltaX / containerWidth) * 100;
|
||||
|
||||
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
|
||||
|
||||
updatePeriodTimeLabel();
|
||||
};
|
||||
const mouseUp = () => {
|
||||
generateAllEtudRow();
|
||||
snapHandlesToQuarters();
|
||||
timelineContainer.removeEventListener("mousemove", onMouseMove);
|
||||
func_call();
|
||||
}
|
||||
timelineContainer.addEventListener("mousemove", onMouseMove);
|
||||
timelineContainer.addEventListener("touchmove", onMouseMove);
|
||||
document.addEventListener(
|
||||
"mouseup",
|
||||
mouseUp,
|
||||
{ once: true }
|
||||
);
|
||||
document.addEventListener(
|
||||
"touchend",
|
||||
mouseUp,
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupTimeLine(callback) {
|
||||
timelineContainer.addEventListener("mousedown", (e) => { timelineMainEvent(e, callback) });
|
||||
timelineContainer.addEventListener("touchstart", (e) => { timelineMainEvent(e, callback) });
|
||||
}
|
||||
|
||||
function adjustPeriodPosition(newLeft, newWidth) {
|
||||
|
@ -162,24 +162,35 @@ def index_html():
|
||||
"""<p class="help">Pour signaler, annuler ou justifier une assiduité pour un seul étudiant,
|
||||
choisissez d'abord le concerné:</p>"""
|
||||
)
|
||||
H.append(sco_find_etud.form_search_etud())
|
||||
# if current_user.has_permission(
|
||||
# Permission.ScoAbsChange
|
||||
# ) and sco_preferences.get_preference("handle_billets_abs"):
|
||||
# H.append(
|
||||
# f"""
|
||||
# <h2 style="margin-top: 30px;">Billets d'absence</h2>
|
||||
# <ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
|
||||
# }">Traitement des billets d'absence en attente</a>
|
||||
# </li></ul>
|
||||
# """
|
||||
# )
|
||||
H.append(sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"))
|
||||
if current_user.has_permission(
|
||||
Permission.ScoAbsChange
|
||||
) and sco_preferences.get_preference("handle_billets_abs"):
|
||||
H.append(
|
||||
f"""
|
||||
<h2 style="margin-top: 30px;">Billets d'absence</h2>
|
||||
<ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
|
||||
}">Traitement des billets d'absence en attente</a>
|
||||
</li></ul>
|
||||
"""
|
||||
)
|
||||
dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first()
|
||||
annees: list[int] = sorted(
|
||||
[f.date_debut.year for f in dept.formsemestres],
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
annees_str: str = "["
|
||||
for ann in annees:
|
||||
annees_str += f"{ann},"
|
||||
annees_str += "]"
|
||||
|
||||
H.append(
|
||||
render_template(
|
||||
"assiduites/pages/bilan_dept.j2",
|
||||
dept_id=g.scodoc_dept_id,
|
||||
annee=scu.annee_scolaire(),
|
||||
annees=annees_str,
|
||||
),
|
||||
)
|
||||
H.append(html_sco_header.sco_footer())
|
||||
@ -299,6 +310,8 @@ def liste_assiduites_etud():
|
||||
if etud.dept_id != g.scodoc_dept_id:
|
||||
abort(404, "étudiant inexistant dans ce département")
|
||||
|
||||
assiduite_id: int = request.args.get("assiduite_id", -1)
|
||||
|
||||
header: str = html_sco_header.sco_header(
|
||||
page_title="Liste des assiduités",
|
||||
init_qtip=True,
|
||||
@ -319,6 +332,11 @@ def liste_assiduites_etud():
|
||||
"assiduites/pages/liste_assiduites.j2",
|
||||
sco=ScoData(etud),
|
||||
date=datetime.date.today().isoformat(),
|
||||
assi_id=assiduite_id,
|
||||
assi_limit_annee=sco_preferences.get_preference(
|
||||
"assi_limit_annee",
|
||||
dept_id=g.scodoc_dept_id,
|
||||
),
|
||||
),
|
||||
).build()
|
||||
|
||||
@ -371,6 +389,10 @@ def bilan_etud():
|
||||
date_fin=date_fin,
|
||||
assi_metric=assi_metric,
|
||||
assi_seuil=_get_seuil(),
|
||||
assi_limit_annee=sco_preferences.get_preference(
|
||||
"assi_limit_annee",
|
||||
dept_id=g.scodoc_dept_id,
|
||||
),
|
||||
),
|
||||
).build()
|
||||
|
||||
@ -412,6 +434,12 @@ def ajout_justificatif_etud():
|
||||
render_template(
|
||||
"assiduites/pages/ajout_justificatif.j2",
|
||||
sco=ScoData(etud),
|
||||
assi_limit_annee=sco_preferences.get_preference(
|
||||
"assi_limit_annee",
|
||||
dept_id=g.scodoc_dept_id,
|
||||
),
|
||||
assi_morning=ScoDocSiteConfig.get("assi_morning_time", "08:00"),
|
||||
assi_evening=ScoDocSiteConfig.get("assi_evening_time", "18:00"),
|
||||
),
|
||||
).build()
|
||||
|
||||
|
@ -157,7 +157,6 @@ def test_general(test_client):
|
||||
editer_supprimer_justificatif(etuds[0])
|
||||
|
||||
|
||||
# XXX TODO-ASSIDUITE (issue #696)
|
||||
def verif_migration_abs_assiduites():
|
||||
"""Vérification que le script de migration fonctionne correctement"""
|
||||
downgrade_module(assiduites=True, justificatifs=True)
|
||||
|
Loading…
Reference in New Issue
Block a user