Better Competencies Gandalf

Rend l'utilisation de l'onglet Compétences de Gandalf plus intuitif

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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)
})();