BetterOghma

This is a userscript.

// ==UserScript==
// @name        BetterOghma
// @namespace   Violentmonkey Scripts
// @description This is a userscript.
// @match       https://oghma.epcc.pt/*
// @version     2.0.0
// @author      Leonardo
// @require     https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@2,npm/@violentmonkey/[email protected]
// @require     https://cdn.jsdelivr.net/npm/@violentmonkey/dom@2
// @grant       GM_addStyle
// ==/UserScript==

(function () {
'use strict';

var css_248z = "";

// global CSS
document.head.append(VM.m(VM.h("style", null, css_248z)));

// CSS modules

class LocalStorage {
  set(key, value) {
    key = key.toString().toLowerCase().replace(/\s/g, "");
    value = value.toString();
    localStorage.setItem(key, value);
  }
  get(key, defaultValue = "") {
    var _localStorage$getItem;
    key = key.toString().toLowerCase().replace(/\s/g, "");
    return (_localStorage$getItem = localStorage.getItem(key)) != null ? _localStorage$getItem : defaultValue;
  }
  check(key, value) {
    key = key.toString().toLowerCase().replace(/\s/g, "");
    value = value.toString();
    return localStorage.getItem(key) == value;
  }
}

class MainController {
  constructor(url) {
    this.AVARAGE_DECIMAL_PARTS = 2;
    this.hide = function (element) {
      element.style.display = "none";
    };
    this.grid = function (element) {
      element.style.display = "grid";
    };
    this.flex = function (element) {
      element.style.display = "flex";
    };
    this.url = url;
    this.storage = new LocalStorage();
    this.getByText("Inscrições nos Exames", this.hide);
    this.getByClass("events announcements", this.hide);
    this.getByText("Importante!", this.hide);
  }
  async get(pattern, callback) {
    let element = document.querySelector(pattern);
    if (!element) return;
    callback(element);
  }
  async getAll(pattern, callback) {
    let element = document.querySelectorAll(pattern);
    if (!element) return;
    for (var i = 0; i < element.length; i++) callback(element[i]);
  }
  async getByClass(className, callback) {
    let element = document.getElementsByClassName(className);
    if (!element) return;
    for (var i = 0; i < element.length; i++) await callback(element[i]);
  }
  async getByText(text, callback) {
    var elements = document.getElementsByTagName("*");
    for (var i = 0; i < elements.length; i++) if (elements[i].textContent === text) callback(elements[i]);
  }
  setDecrescent() {
    this.getByClass("nav pull-right", element => {
      element.style.display = "flex";
      element.style.alignItems = "center";
    });
    this.getByClass("nav pull-right", element => {
      if (document.querySelector("#decrease")) return;
      const Checkbox = () => VM.h("input", {
        id: "decrease",
        type: "checkbox",
        checked: this.storage.check("DECREASE", "true"),
        onclick: () => {
          this.storage.set('DECREASE', this.storage.check("DECREASE", "true") ? "false" : "true");
          this.index();
        }
      });
      const Text = () => VM.h("span", {
        style: "padding: 1rem;"
      }, "Ordem decrescente");
      const Li = () => VM.h("li", {
        style: "display: flex; order: -1"
      }, VM.h(Text, null), VM.h(Checkbox, null));
      element.append(VM.m(VM.h(Li, null)));
    });
  }
}

class Subject {
  setName(name) {
    this.name = name;
  }
  setId(id) {
    this.id = id;
  }
  setEvaluation(evaluation) {
    this.evaluation = evaluation;
  }
  getName() {
    return this.name;
  }
  getId() {
    return this.id;
  }
  getEvaluation() {
    return this.evaluation;
  }
}

class Evaluations extends MainController {
  async index() {
    this.setDecrescent();
    this.subjects = [];

    // Verifica em qual user está salvo a atual data
    if (!this.storage.check("lastUser", this.url[4])) {
      const itemToKeep = "decrease";
      for (let i = localStorage.length - 1; i >= 0; i--) {
        const key = localStorage.key(i);
        if (key != itemToKeep) {
          localStorage.removeItem(key);
        }
      }
      this.storage.set("lastUser", this.url[4]);
    }

    // CSS
    this.get("tbody", this.grid);
    this.getAll("tr", this.grid);
    this.getAll("tr", element => {
      let sons = element.querySelectorAll(element.className == "header" ? "th" : "td");
      for (var i = 0; i < sons.length; i++) {
        sons[i].style.display = "flex";
        if (element.className == "header") {
          element.style.order = '-21';
          if (i == 0) sons[i].style.gridColumn = "span 2 / span 2";
          if (i > 0) sons[i].style.justifyContent = "end";
          if (i > 2) this.hide(sons[i]);
        } else {
          if (i > 1) sons[i].style.justifyContent = "end";
          if (i == 2) element.style.order = (this.storage.check("DECREASE", "true") ? '-' : '') + sons[i].textContent;
          if (i > 3) this.hide(sons[i]);
        }
      }
      element.style.gridTemplateColumns = "30% 50% 10% 10%";
    });

    // Pega o id do curso
    let coursesURL = "https://oghma.epcc.pt/users/" + this.url[4] + "/subscriptions";
    let courseId;
    await fetch(coursesURL).then(async html => {
      const parser = new DOMParser();
      const doc = parser.parseFromString(await html.text(), 'text/html');
      let tr = doc.querySelector("tbody > tr");
      courseId = tr.querySelectorAll("td")[1].querySelector("a").href.split("/")[4];
    });

    // Salva todas as notas
    if (!this.storage.check("allSubjectsSet", "true")) {
      let userh1 = document.getElementsByClassName("well clearfix")[0].querySelector("h1");
      var smallElement = userh1.querySelector('small');
      var smallText = smallElement.textContent;
      let username = userh1.textContent.substring(smallText.length);
      let allSubjects = await this.getAllSubjectsFromCourse(courseId);
      for (var i = 0; i < allSubjects.names.length; i++) {
        let subject = new Subject();
        subject.setName(allSubjects.names[i]);
        subject.setId(allSubjects.ids[subject.getName()]);
        let evaluation = await this.getEvaluationByUsername(username, subject.getId());
        subject.setEvaluation(evaluation);
        this.subjects.push(subject);
      }
      this.subjects.forEach((subject, i) => {
        this.storage.set(i.toString(), subject.getName().toString());
        this.storage.set(subject.getName() + "_id", subject.getId().toString());
        this.storage.set(subject.getName() + "_evaluation", subject.getEvaluation().toString());
        this.storage.set(subject.getName() + "_isActived", "true");
      });
      this.storage.set("allSubjectsSet", "true");
      this.storage.set("subjectsLength", this.subjects.length.toString());
    } else {
      for (var i = 0; i < parseFloat(this.storage.get("subjectsLength")); i++) {
        let subject = new Subject();
        let name = this.storage.get(i.toString());
        subject.setName(name);
        let id = this.storage.get(name + "_id");
        subject.setId(parseFloat(id));
        let evaluation = this.storage.get(name + "_evaluation");
        subject.setEvaluation(parseFloat(evaluation));
        this.subjects.push(subject);
      }
    }
    await this.getByClass("well clearfix", async element => {
      let avarageSubjectSum = 0;
      let subjectsCount = 0;
      this.subjects.forEach(subject => {
        if (!isNaN(subject.getEvaluation()) && this.storage.check(subject.getName() + "_isActived", "true")) {
          subjectsCount++;
          avarageSubjectSum += subject.getEvaluation();
        }
      });
      let avarage = (avarageSubjectSum / subjectsCount).toFixed(this.AVARAGE_DECIMAL_PARTS);
      var averageElement = null;
      if (document.getElementById("alunoAvarageText")) averageElement = document.getElementById("alunoAvarageText");else {
        averageElement = document.createElement("p");
        averageElement.id = "alunoAvarageText";
      }
      averageElement.innerHTML = "O aluno tem uma média de " + avarage + " pontos";
      if (!document.getElementById("alunoAvarageText")) element.appendChild(averageElement);
    });
    this.getByClass("span2 sidebar", element => {
      if (document.getElementById("evaluationsCheckers")) return;
      const subjects = this.subjects.map(subject => {
        const SubjectElementCheckbox = () => VM.h("input", {
          type: "checkbox",
          id: subject.getName() + "_checkbox",
          onclick: () => {
            this.storage.set(subject.getName() + "_isActived", this.storage.check(subject.getName() + "_isActived", "true") ? "false" : "true");
            this.index();
          },
          checked: this.storage.check(subject.getName() + "_isActived", "true")
        });
        const SubjectElementText = () => VM.h("label", {
          for: subject.getName() + "_checkbox",
          style: "\r font-size: 0.8rem;\r margin-bottom: 0rem;\r user-select: none;\r padding-left: 0.5rem;\r fontSize: 0.8rem;"
        }, subject.getName() == "Tecnologias de Informação e Comunicação" ? "TIC" : subject.getName());
        const SubjectAvarageScore = () => VM.h("span", {
          style: "padding-left: 0.5rem;"
        }, subject.getEvaluation().toString());
        return VM.h("div", {
          style: "display: flex; flex-direction: row; align-items: center; justify-content: start; padding: 0.25rem;"
        }, VM.h(SubjectElementCheckbox, null), VM.h(SubjectElementText, null), VM.h(SubjectAvarageScore, null));
      });
      const Div = () => VM.h("div", {
        id: "evaluationsCheckers",
        style: "padding-left: 0.8rem; padding-right: 0.8rem;"
      }, subjects);
      element.append(VM.m(VM.h(Div, null)));
    });
  }
  async getAllSubjectsFromCourse(id) {
    let couseURL = "https://oghma.epcc.pt/courses/" + id;
    let subjects = {
      ids: {},
      names: []
    };
    await fetch(couseURL).then(async html => {
      const parser = new DOMParser();
      const doc = parser.parseFromString(await html.text(), 'text/html');
      let trs = doc.querySelectorAll("tbody > tr");
      trs.forEach(function (tr) {
        let td = tr.querySelector("td");
        let subject = td.querySelector("a");
        let subjectTitle = subject.textContent;
        if (subjectTitle.endsWith(" ")) {
          subjectTitle = subjectTitle.trimEnd();
        }
        let subjectId = subject.href.split("/")[4];
        subjects.ids[subjectTitle] = parseFloat(subjectId);
        subjects.names.push(subjectTitle);
      });
    });
    return subjects;
  }
  async getEvaluationByUsername(username, subjectId) {
    let subjectEvaluationsURL = "https://oghma.epcc.pt/units/" + subjectId + "/evaluations";
    let evaluation = 0;
    await fetch(subjectEvaluationsURL).then(async html => {
      const parser = new DOMParser();
      const doc = parser.parseFromString(await html.text(), 'text/html');
      let trs = doc.querySelectorAll("tbody > tr");
      trs.forEach(function (tr) {
        let tds = tr.querySelectorAll("td");
        if (tds.length > 0) if (tds[1].textContent.replace(/\s/g, "").toLowerCase() == username.replace(/\s/g, "").toLowerCase()) evaluation = parseFloat(tds[2].textContent.split(" ")[0]);
      });
    });
    return evaluation;
  }
}

class Subscriptions extends MainController {
  async index() {
    this.setDecrescent();
    this.getByClass("student active", element => {
      element.querySelectorAll("a").forEach(function (a) {
        a.href += "/evaluations";
      });
    });
    this.getAll(".student:not(.active)", this.hide);
    this.getByClass("users-list photo", this.flex);
    this.getByClass("users-list photo", element => {
      element.style.flexWrap = "wrap";
    });
    let avarageSum = 0;
    let totalStudents = 0;
    this.getByClass("student active", async element => {
      var id = element.querySelector("a").href.split("/")[4];
      var evaluationsURL = "https://oghma.epcc.pt/users/" + id + "/evaluations";
      if (id) await fetch(evaluationsURL).then(async html => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(await html.text(), 'text/html');
        var avarage = this.getAvarage(doc);
        avarageSum += avarage;
        totalStudents++;
        element.style.order = (this.storage.check("DECREASE", "true") ? '-' : '') + (avarage * 1000).toFixed(0);
        var averageElement = document.createElement("p");
        averageElement.textContent = "Média de " + avarage.toFixed(this.AVARAGE_DECIMAL_PARTS) + " pontos";
        element.appendChild(averageElement);
      });else {
        this.totalAvarage = avarageSum / totalStudents;
        element.querySelector("p").textContent = "Média de " + this.totalAvarage.toFixed(this.AVARAGE_DECIMAL_PARTS) + " pontos";
        element.style.order = (this.storage.check("DECREASE", "true") ? '-' : '') + (this.totalAvarage * 1000).toFixed(0);
      }
    });
    this.getByClass("users-list photo", element => {
      var avarageStudent = document.createElement("li");
      var imageA = document.createElement("a");
      var image = document.createElement("img");
      var br = document.createElement("br");
      var span = document.createElement("span");
      var a = document.createElement("a");
      var p = document.createElement("p");
      avarageStudent.className = "student active";
      image.src = "https://upload.wikimedia.org/wikipedia/commons/9/99/Sample_User_Icon.png";
      image.style.height = "79px";
      image.style.width = "auto";
      span.textContent = "Média";
      a.textContent = "Aluno Médio";
      imageA.appendChild(image);
      avarageStudent.appendChild(imageA);
      avarageStudent.appendChild(span);
      avarageStudent.appendChild(br);
      avarageStudent.appendChild(a);
      avarageStudent.appendChild(p);
      element.appendChild(avarageStudent);
    });
  }
  getAvarage(page) {
    var avarage = 0;
    var sumAll = 0;
    var countAll = 0;
    page.querySelectorAll("tr").forEach(tr => {
      if (tr.className != "header") {
        var tds = tr.querySelectorAll("td");
        sumAll += parseInt(tds[2].textContent);
        countAll++;
      }
    });
    avarage = sumAll / countAll;
    return avarage;
  }
}

class Default extends MainController {
  async index() {
    this.getByClass("well clearfix", element => {
      const App = () => VM.h("div", null, VM.h("h1", null, "Welcome to BetterOghma"));
      element.append(VM.m(VM.h(App, null)));
    });
  }
}

// setItem("DECREASE", getItem("DECREASE", "true"));

let url = window.location.href;
let urlArray = url.split("/");
let page = urlArray[urlArray.length - 1].replace(/#/g, "");
let routes = {
  default: Default,
  evaluations: Evaluations,
  subscriptions: Subscriptions
};
if (!routes[page]) page = 'default';
let controller = new routes[page](urlArray);
controller.index();

})();
// Leonardo Abreu de Paulo 2023