Rend l'utilisation de l'onglet Compétences de Gandalf plus intuitif
// ==UserScript==
// @name Better Competencies Gandalf
// @namespace http://tampermonkey.net/
// @version 2024-02-20
// @description Rend l'utilisation de l'onglet Compétences de Gandalf plus intuitif
// @author Arthur Decaen
// @match https://gandalf.epitech.eu/local/graph/view.php
// @icon https://www.google.com/s2/favicons?sz=64&domain=epitech.eu
// @grant none
// ==/UserScript==
class Competency
{
constructor(fill, progress, median, threshold, html, title) {
this.fill = parseInt(fill.replace("%", ""))
this.progress = parseInt(progress.replace("%", ""))
this.median = parseInt(median.replace("%", ""))
this.threshold = parseInt(threshold.replace("%", ""))
this.html = html
this.title = title
this.median = parseInt(this.median * 100 / this.threshold)
if (Math.abs(this.median - this.progress) <= 2) this.median = this.progress
this.subComps = []
}
GetSubCompsNeeded() {
const nbSuccess = this.subComps.filter((subComp) => subComp.status == "success").length
if (nbSuccess == 0) return this.subComps.length
const valueBySuccess = this.progress / nbSuccess
return Math.ceil((100 - this.progress) / valueBySuccess)
}
}
class SubComp
{
constructor(title, status, id)
{
this.title = title.split(" - ")[1]
this.status = status
this.id = id
switch (this.status) {
case 'success': this.icon = "proficiencyIcon fa fa-check-circle-o success"; break;
case 'failed': this.icon = "proficiencyIcon fa fa-times-circle-o failed"; break;
case 'unrated': this.icon = "proficiencyIcon fa fa-times-circle-o unrated"; break;
}
}
}
(function() {
'use strict';
// ---------- CSS ----------
const styles = `
.style {
display: flex;
flex-direction: column;
gap: 5px;
}
.styleSkill {
display: grid;
grid-template-columns: 4fr 60px 60px 40px;
grid-template-rows: 2fr;
grid-column-gap: 0px;
grid-row-gap: -1px;
text-wrap: nowrap;
padding-left: 7px;
padding-right: 7px;
padding-top: 2px;
padding-bottom: 2px;
}
.styleSkill:nth-child(even) {
background: #e9e9e9;
}
.styleSkill:nth-child(1) {
font-weight: bold;
border-bottom: solid 1px;
font-style: italic;
}
.styleSkillTitle {
width: fit-content !important;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
}
.styleSubComp {
display: flex;
flex-direction: column;
}
.styleSubCompChild {
display: flex;
justify-content: flex-start;
gap: 5px;
font-size: .9rem;
padding-left: 1.5rem;
}
.info button {
background: none;
border: none;
font-family: inherit;
font-size: inherit;
font-weight: bold !important;
text-wrap: nowrap;
margin: 0;
padding: 0;
}
.listInfo {
width: 100% !important;
max-width: 100% !important;
}
.studentInfo {
width: 100%;
}
`
var styleSheet = document.createElement("style")
styleSheet.innerText = styles
document.head.appendChild(styleSheet)
// ---------- Get Data ----------
const competencies = []
const skillContainer = document.querySelectorAll('.skillProgressContainer:not(.legendSkillProgressSample)');
var cent = 0
for (var i = 0; i != skillContainer.length; i++) {
const container = skillContainer[i]
const comp = new Competency(
container.getElementsByClassName("skillProgress")[0].style.width,
container.title,
container.getElementsByClassName("skillProgressMedian")[0].style.left,
container.getElementsByClassName("skillProgressThreshold")[0].style.left,
container,
container.previousElementSibling.innerHTML.replaceAll("\t", "").replaceAll("\n", "")
)
const progressBar = comp.html.getElementsByClassName("skillProgress")[0]
if (comp.progress < comp.median) progressBar.style.backgroundColor = "#ffc689"
if (comp.progress >= 100) {
cent += 1;
progressBar.style.backgroundColor = "#40f499"
}
const parent = comp.html.parentElement.parentElement.parentElement
const subComps = parent.querySelectorAll(".competencyLine.behaviorLine")
for (var j = 0; j != subComps.length; j++)
{
const status = subComps[j].querySelectorAll(".proficiencyIcon")[0].title
const title = status == "unrated"
? subComps[j].children[2].innerHTML.replaceAll("\t", "").replaceAll("\n", "")
: subComps[j].querySelectorAll(".competencyTitle")[0].innerHTML
const id = `subcomp-${i}-${j}`
subComps[j].id = id
const subComp = new SubComp(title, status, id)
comp.subComps.push(subComp)
}
competencies.push(comp)
}
// ---------- Button functions ----------
function firstSetSubComp() {
const state = localStorage.getItem("subCompState") === "true"
const subCompDivs = document.querySelectorAll(".styleSubComp")
for (var i = 0; i != subCompDivs.length; i++) {
subCompDivs[i].style.display = state ? "none" : "flex"
}
}
function toggleSubComp() {
const state = localStorage.getItem("subCompState") === "true"
const subCompDivs = document.querySelectorAll(".styleSubComp")
for (var i = 0; i != subCompDivs.length; i++) {
subCompDivs[i].style.display = state ? "flex" : "none"
}
localStorage.setItem("subCompState", !state)
}
// ---------- Add to page ----------
const listInfo = document.getElementsByClassName("listInfo")[0]
listInfo.innerHTML += `
<div class="content-line">
<span class="info">Validated skills: </span>
<span class="content">${cent}/${competencies.length}</span>
</div>`
const unfinished = competencies.map((comp) => {
return comp.progress < 100 ? `<span class="styleSkill">
<span class="styleSkillTitle" title="${comp.title}">${comp.title}</span>
<b style="color: #76c893;" title="Ma progression">${comp.progress}%</b>
<b style="color: #34a0a4;" title="Progression moyenne">${comp.median}%</b>
<b style="color: #184e77;" title="Compétences restantes">${comp.GetSubCompsNeeded()}</b>
<span class="styleSubComp">
${
comp.subComps.filter(function(objet) {
return objet.status !== 'success';
}).sort((a, b) => {
return a.status === 'failed' ? 1 : -1
}).map((subComp) => {
return `
<span class="styleSubCompChild">
<span class="${subComp.icon}"></span>
<span onclick="document.getElementById('${subComp.id}').scrollIntoView({ behavior: 'smooth', block: 'center' })">${subComp.title}</span>
</span>`
}).join("")
}
</span>
</span>
`
: ""
}).join("")
listInfo.innerHTML += `<div class="content-line">
<span class="info"><button id="unfinished">Unfinished skills: </button></span>
<span class="content style">
<span class="styleSkill">
<span class="styleSkillTitle">Skill name</span>
<b style="color: #76c893;">Current</b>
<b style="color: #34a0a4;">Median</b>
<b style="color: #184e77;">Need</b>
</span>
${unfinished}
</span>
</div>`
firstSetSubComp()
document.querySelector("#unfinished").addEventListener("click", toggleSubComp)
})();