Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
7 changed files with 246 additions and 106 deletions
Showing only changes of commit d796c7db93 - Show all commits

View File

@ -316,10 +316,10 @@ def compute_assiduites_justified(
for justi in justificatifs: for justi in justificatifs:
assiduites: Assiduite = ( assiduites: Assiduite = (
Assiduite.query.join(Justificatif, Justificatif.etudid == Assiduite.etudid) Assiduite.query.join(Justificatif, Justificatif.etudid == Assiduite.etudid)
.filter(Assiduite.etat != EtatAssiduite.PRESENT) .filter(justi.etat == EtatJustificatif.VALIDE)
.filter( .filter(
Assiduite.date_debut <= justi.date_fin, Assiduite.date_debut < justi.date_fin,
Assiduite.date_fin >= justi.date_debut, Assiduite.date_fin > justi.date_debut,
) )
) )
@ -329,11 +329,9 @@ def compute_assiduites_justified(
db.session.add(assi) db.session.add(assi)
if reset: if reset:
un_justified: Assiduite = ( un_justified: Assiduite = Assiduite.query.filter(
Assiduite.query.filter(Assiduite.id.not_in(list_assiduites_id)) Assiduite.id.not_in(list_assiduites_id)
.filter(Assiduite.etat != EtatAssiduite.PRESENT) ).join(Justificatif, Justificatif.etudid == Assiduite.etudid)
.join(Justificatif, Justificatif.etudid == Assiduite.etudid)
)
for assi in un_justified: for assi in un_justified:
assi.est_just = False assi.est_just = False

View File

@ -20,7 +20,6 @@ class CountCalculator:
evening: time = time(18, 0), evening: time = time(18, 0),
skip_saturday: bool = True, skip_saturday: bool = True,
) -> None: ) -> None:
self.morning: time = morning self.morning: time = morning
self.noon: time = noon self.noon: time = noon
self.after_noon: time = after_noon self.after_noon: time = after_noon
@ -318,13 +317,11 @@ def justifies(justi: Justificatif, obj: bool = False) -> list[int]:
if justi.etat != scu.EtatJustificatif.VALIDE: if justi.etat != scu.EtatJustificatif.VALIDE:
return [] return []
assiduites_query: Assiduite = ( assiduites_query: Assiduite = Assiduite.query.join(
Assiduite.query.join(Justificatif, Assiduite.etudid == Justificatif.etudid) Justificatif, Assiduite.etudid == Justificatif.etudid
.filter(Assiduite.etat != scu.EtatAssiduite.PRESENT) ).filter(
.filter( Assiduite.date_debut <= justi.date_fin,
Assiduite.date_debut <= justi.date_fin, Assiduite.date_fin >= justi.date_debut,
Assiduite.date_fin >= justi.date_debut,
)
) )
if not obj: if not obj:

View File

@ -192,6 +192,14 @@
background-color: #F1D99C !important; background-color: #F1D99C !important;
} }
.etud_row .assiduites_bar .justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #7059FF 4px, #7059FF 8px);
}
.etud_row .assiduites_bar .invalid_justified {
background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #d61616 4px, #d61616 8px);
}
/* --- Boutons assiduités --- */ /* --- Boutons assiduités --- */
.etud_row .btns_field { .etud_row .btns_field {

View File

@ -16,6 +16,7 @@ let currentValues = [8.0, 10.0];
//Objet stockant les étudiants et les assiduités //Objet stockant les étudiants et les assiduités
let etuds = {}; let etuds = {};
let assiduites = {}; let assiduites = {};
let justificatifs = {};
// Variable qui définit si le processus d'action de masse est lancé // Variable qui définit si le processus d'action de masse est lancé
let currentMassAction = false; let currentMassAction = false;
@ -1094,12 +1095,40 @@ function createMiniTimeline(assiduitesArray) {
setPeriodValues(deb, fin); setPeriodValues(deb, fin);
updateSelectedSelect(getCurrentAssiduiteModuleImplId()); updateSelectedSelect(getCurrentAssiduiteModuleImplId());
updateJustifyBtn();
}); });
} }
//ajouter affichage assiduites on over //ajouter affichage assiduites on over
setupAssiduiteBuble(block, assiduité); setupAssiduiteBuble(block, assiduité);
if (assiduité.est_just) {
block.classList.add("justified");
}
} }
const action = (justificatifs) => {
if (justificatifs.length > 0) {
let j = "invalid_justified";
justificatifs.forEach((ju) => {
if (ju.etat == "VALIDE") {
j = "justified";
}
});
block.classList.add(j);
}
};
getJustificatifFromPeriod(
{
deb: new moment.tz(assiduité.date_debut, TIMEZONE),
fin: new moment.tz(assiduité.date_fin, TIMEZONE),
},
assiduité.etudid,
action
);
switch (assiduité.etat) { switch (assiduité.etat) {
case "PRESENT": case "PRESENT":
block.classList.add("present"); block.classList.add("present");
@ -1773,31 +1802,33 @@ function getCurrentAssiduite(etudid) {
// <<== Gestion de la justification ==>> // <<== Gestion de la justification ==>>
function getJustificatifFromPeriod(date) { function getJustificatifFromPeriod(date, etudid, update) {
let justifs = []; $.ajax({
sync_get( async: true,
getUrl() + type: "GET",
`/api/justificatifs/${etudid}/query?date_debut=${date.deb.format()}&date_fin=${date.fin.format()}`, url:
(data) => { getUrl() +
justifs = data; `/api/justificatifs/${etudid}/query?date_debut=${date.deb
} .add(1, "s")
); .format()}&date_fin=${date.fin.subtract(1, "s").format()}`,
success: (data) => {
return justifs; update(data);
},
error: () => {},
});
} }
function updateJustifieButton(isJustified, isDisabled = true) { function updateJustifyBtn() {
const btn = document.getElementById("justif-rapide"); if (isSingleEtud()) {
if (isJustified) { const assi = getCurrentAssiduite(etudid);
btn.classList.add("justifie");
} else {
btn.classList.remove("justifie");
}
if (isDisabled) { const just = assi ? !assi.est_just : false;
btn.setAttribute("disabled", "true"); const btn = document.getElementById("justif-rapide");
} else { if (!just) {
btn.removeAttribute("disabled"); btn.setAttribute("disabled", "true");
} else {
btn.removeAttribute("disabled");
}
} }
} }
@ -1806,12 +1837,100 @@ function fastJustify(assiduite) {
deb: new moment.tz(assiduite.date_debut, TIMEZONE), deb: new moment.tz(assiduite.date_debut, TIMEZONE),
fin: new moment.tz(assiduite.date_fin, TIMEZONE), fin: new moment.tz(assiduite.date_fin, TIMEZONE),
}; };
const justifs = getJustificatifFromPeriod(period); const action = (justifs) => {
if (justifs.length > 0) {
justifyAssiduite(assiduite.assiduite_id, !assiduite.est_just);
} else {
console.debug("WIP");
//créer un nouveau justificatif
// Afficher prompt -> demander raison et état
if (justifs.length > 0) { const success = () => {
//modifier l'assiduité const raison = document.getElementById("promptText").value;
} else { const etat = document.getElementById("promptSelect").value;
//créer un nouveau justificatif
// Afficher prompt -> demander raison et état //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(),
raison: raison,
etat: etat,
};
createJustificatif(justif);
// justifyAssiduite(assiduite.assiduite_id, true);
generateAllEtudRow();
};
const content = document.createElement("fieldset");
const htmlPrompt = `<legend>Entrez l'état du justificatif :</legend>
<select name="promptSelect" id="promptSelect" required>
<option value="valide">Valide</option>
<option value="attente">En Attente de validation</option>
<option value="non_valide">Non Valide</option>
<option value="modifie">Modifié</option>
</select>
<legend>Raison:</legend>
<textarea type="text" placeholder="Explication du justificatif (non obligatoire)" id="promptText" style="width:100%;"></textarea>
`;
content.innerHTML = htmlPrompt;
openPromptModal(
"Nouveau justificatif (Rapide)",
content,
success,
() => {},
"#7059FF"
);
}
};
getJustificatifFromPeriod(period, assiduite.etudid, action);
}
function justifyAssiduite(assiduite_id, justified) {
const assiduite = {
est_just: justified,
};
const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`;
let bool = false;
sync_post(
path,
assiduite,
(data, status) => {
bool = true;
},
(data, status) => {
//error
console.error(data, status);
}
);
return bool;
}
function createJustificatif(justif) {
const path = getUrl() + `/api/justificatif/${etudid}/create`;
sync_post(
path,
[justif],
(data, status) => {
//success
if (data.success.length > 0) {
console.table(data[0]);
}
console.warn(data);
},
(data, status) => {
//error
console.error(data, status);
}
);
} }

View File

@ -29,6 +29,8 @@
</div> </div>
</div> </div>
{% include "assiduites/toast.j2" %} {% include "assiduites/toast.j2" %}
{% include "assiduites/alert.j2" %}
{% include "assiduites/prompt.j2" %}
<div id="page-assiduite-content"> <div id="page-assiduite-content">
{% block content %} {% block content %}
<h2>Signalement de l'assiduité de <span class="rouge">{{sco.etud.nomprenom}}</span></h2> <h2>Signalement de l'assiduité de <span class="rouge">{{sco.etud.nomprenom}}</span></h2>
@ -43,13 +45,13 @@
<div> <div>
{% include "assiduites/moduleimpl_dynamic_selector.j2" %} {% include "assiduites/moduleimpl_dynamic_selector.j2" %}
<button class="btn" onclick="justify()" disabled id="justif-rapide">Justifier</button> <button class="btn" onclick="fastJustify(getCurrentAssiduite(etudid))" id="justif-rapide">Justifier</button>
</div> </div>
<div class="btn_group"> <div class="btn_group">
<button class="btn" onclick="setPeriodValues(8,18)">Journée</button> <button class="btn" onclick="setTimeLineTimes(8,18)">Journée</button>
<button class="btn" onclick="setPeriodValues(8,13)">Matin</button> <button class="btn" onclick="setTimeLineTimes(8,13)">Matin</button>
<button class="btn" onclick="setPeriodValues(13,18)">Après-midi</button> <button class="btn" onclick="setTimeLineTimes(13,18)">Après-midi</button>
</div> </div>
<div class="etud_holder"> <div class="etud_holder">
@ -64,8 +66,7 @@
<div class="loader"></div> <div class="loader"></div>
</div> </div>
{% include "assiduites/alert.j2" %}
{% include "assiduites/prompt.j2" %}
<script> <script>
const etudid = {{ sco.etud.id }}; const etudid = {{ sco.etud.id }};
@ -74,20 +75,29 @@
updateSelect() updateSelect()
}); });
setupTimeLine(() => {
updateJustifyBtn();
});
updateDate(); updateDate();
getSingleEtud({{ sco.etud.id }}); getSingleEtud({{ sco.etud.id }});
actualizeEtud({{ sco.etud.id }}); actualizeEtud({{ sco.etud.id }});
updateSelect() updateSelect()
updateJustifyBtn();
function setTimeLineTimes(a, b) {
setPeriodValues(a, b);
updateJustifyBtn();
}
</script> </script>
<style> <style>
.rouge {
color: crimson;
}
.justifie { .justifie {
background-color: rgb(104, 104, 252); background-color: rgb(104, 104, 252);
color: whitesmoke; color: whitesmoke;

View File

@ -72,5 +72,6 @@
<script> <script>
updateDate(); updateDate();
setupDate(); setupDate();
setupTimeLine();
</script> </script>
</section> </section>

View File

@ -38,62 +38,69 @@
return Math.round(value * 4) / 4; return Math.round(value * 4) / 4;
} }
timelineContainer.addEventListener("mousedown", (event) => { function setupTimeLine(callback) {
const startX = event.clientX; const func_call = callback ? callback : () => { }
timelineContainer.addEventListener("mousedown", (event) => {
const startX = event.clientX;
if (event.target === periodTimeLine) { if (event.target === periodTimeLine) {
const startLeft = parseFloat(periodTimeLine.style.left); const startLeft = parseFloat(periodTimeLine.style.left);
const onMouseMove = (moveEvent) => { const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX; const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth; const containerWidth = timelineContainer.clientWidth;
const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
};
document.addEventListener("mousemove", onMouseMove);
document.addEventListener(
"mouseup",
() => {
generateAllEtudRow();
snapHandlesToQuarters()
document.removeEventListener("mousemove", onMouseMove);
},
{ once: true }
);
} else 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");
const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth;
const newWidth =
startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
if (isLeftHandle) {
const newLeft = startLeft + (deltaX / containerWidth) * 100; const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, newWidth);
} else {
adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
}
};
document.addEventListener("mousemove", onMouseMove); adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
document.addEventListener( };
"mouseup",
() => {
snapHandlesToQuarters()
generateAllEtudRow();
document.removeEventListener("mousemove", onMouseMove); document.addEventListener("mousemove", onMouseMove);
}, document.addEventListener(
{ once: true } "mouseup",
); () => {
} generateAllEtudRow();
}); snapHandlesToQuarters()
document.removeEventListener("mousemove", onMouseMove);
func_call()
},
{ once: true }
);
} else 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");
const onMouseMove = (moveEvent) => {
const deltaX = moveEvent.clientX - startX;
const containerWidth = timelineContainer.clientWidth;
const newWidth =
startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
if (isLeftHandle) {
const newLeft = startLeft + (deltaX / containerWidth) * 100;
adjustPeriodPosition(newLeft, newWidth);
} else {
adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
}
};
document.addEventListener("mousemove", onMouseMove);
document.addEventListener(
"mouseup",
() => {
snapHandlesToQuarters()
generateAllEtudRow();
document.removeEventListener("mousemove", onMouseMove);
func_call()
},
{ once: true }
);
}
});
}
function adjustPeriodPosition(newLeft, newWidth) { function adjustPeriodPosition(newLeft, newWidth) {