BetterOghma

A userscript to improve https://oghma.epcc.pt.

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

(function () {
'use strict';

const whiteSpaces = new RegExp(/\s/g);
const rmWhiteSpaces = str => str.toString().toLowerCase().replace(whiteSpaces, '');
function setItem(key, value) {
  if (value == undefined) {
    return;
  }
  const k = rmWhiteSpaces(key);
  const v = rmWhiteSpaces(value);
  localStorage.setItem(k, v);
}
function getItem(key, defaultValue) {
  var _localStorage$getItem;
  const k = rmWhiteSpaces(key);
  return (_localStorage$getItem = localStorage.getItem(k)) != null ? _localStorage$getItem : defaultValue;
}
function checkItem(key, value) {
  if (value == undefined) {
    return false;
  }
  const k = rmWhiteSpaces(key);
  const v = rmWhiteSpaces(value);
  return localStorage.getItem(k) === v;
}

const hide = element => element.style.display = 'none';
const grid = element => element.style.display = 'grid';
const flex = element => element.style.display = 'flex';
function get(pattern, callback) {
  const element = document.querySelector(pattern);
  if (element === null) {
    return;
  }
  callback(element);
}
function getAll(pattern, callback) {
  const element = document.querySelectorAll(pattern);
  for (const i of element) {
    callback(i);
  }
}
function getByClass(className, callback) {
  const element = document.getElementsByClassName(className);
  for (const i of element) {
    callback(i);
  }
}
function getByText(text, callback) {
  const elements = document.getElementsByTagName('*');
  for (const i of elements) {
    if (i.textContent === text) {
      callback(i);
    }
  }
}

const AVARAGE_DECIMAL_PARTS = 2;
setItem('DECREASE', getItem('DECREASE', 'true'));

function getAvarage(page) {
  let avarage = 0;
  let sumAll = 0;
  let countAll = 0;
  page.querySelectorAll('tr').forEach(tr => {
    if (tr.className != 'header') {
      var _tds$2$textContent, _tds$;
      const tds = tr.querySelectorAll('td');
      sumAll += parseInt((_tds$2$textContent = (_tds$ = tds[2]) == null ? void 0 : _tds$.textContent) != null ? _tds$2$textContent : '0', 10);
      countAll++;
    }
  });
  avarage = sumAll / countAll;
  return avarage;
}
async function getAllSubjectsFromCourse(id) {
  const couseURL = new URL(`https://oghma.epcc.pt/courses/${id}`);
  const data = {
    subjects: [],
    ids: new Map(),
    evaluations: new Map()
  };
  await fetch(couseURL).then(res => {
    if (!res.ok) {
      throw new Error( /* TODO */);
    }
    return res;
  }).then(res => res.text()).then(html => {
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');
    const trs = doc.querySelectorAll('tbody > tr');
    trs.forEach(tr => {
      const td = tr.querySelector('td');
      if (td === null) {
        return;
      }
      const subject = td.querySelector('a');
      if (subject === null) {
        return;
      }
      let subjectTitle = subject.textContent;
      if (subjectTitle === null) {
        return;
      }
      if (subjectTitle.endsWith(' ')) {
        subjectTitle = subjectTitle.trimEnd();
      }
      const subjectId = subject.href.split('/')[4];
      if (subjectId === undefined) {
        return;
      }
      data.ids.set(subjectTitle, subjectId);
      data.subjects.push(subjectTitle);
    });
  });
  return data;
}
async function getSubjectNyUsername(userName, subjects) {
  const newSubjects = subjects;
  for (const i of newSubjects.subjects) {
    const subjectEvaluationsURL = `https://oghma.epcc.pt/units/${newSubjects.ids.get(i)}/evaluations`;
    await fetch(subjectEvaluationsURL).then(async html => {
      const parser = new DOMParser();
      const doc = parser.parseFromString(await html.text(), 'text/html');
      const trs = doc.querySelectorAll('tbody > tr');
      trs.forEach(function (tr) {
        const tds = tr.querySelectorAll('td');
        if (tds.length > 0) {
          var _tds$2, _tds$3;
          const td1 = (_tds$2 = tds[1]) == null ? void 0 : _tds$2.textContent;
          const td2 = (_tds$3 = tds[2]) == null ? void 0 : _tds$3.textContent;
          if (td1 == undefined || td2 == undefined) {
            return;
          }
          if (td1.replace(/\s/g, '').toLowerCase() === userName.replace(/\s/g, '').toLowerCase()) {
            const evaluation = td2.split(' ')[0];
            if (typeof evaluation === 'string') {
              newSubjects.evaluations.set(i, evaluation);
            }
          }
        }
      });
    });
  }
  return newSubjects;
}

const url = new URL(window.location.href);
const segments = url.pathname.split('/');
const page = segments.at(-1);

async function evaluations () {
  if (!checkItem('lastUser', segments[4])) {
    const itemToKeep = 'decrease';
    for (let i = localStorage.length - 1; i >= 0; i--) {
      const key = localStorage.key(i);
      if (key != itemToKeep && key != undefined) {
        localStorage.removeItem(key);
      }
    }
    setItem('lastUser', segments[4]);
  }
  get('tbody', grid);
  getAll('tr', grid);
  getAll('tr', function (element) {
    const sons = element.querySelectorAll(element.className == 'header' ? 'th' : 'td');
    for (let i = 0; i < sons.length; i++) {
      const son = sons[i];
      if (son === undefined) {
        continue;
      }
      flex(son);
      if (element.className == 'header') {
        element.style.order = '-21';
        if (i == 0) {
          son.style.gridColumn = 'span 2 / span 2';
        }
        if (i > 0) {
          son.style.justifyContent = 'end';
        }
        if (i > 2) {
          hide(son);
        }
      } else {
        if (i > 1) {
          son.style.justifyContent = 'end';
        }
        if (i == 2) {
          element.style.order = (checkItem('DECREASE', 'true') ? '-' : '') + son.textContent;
        }
        if (i > 3) {
          hide(son);
        }
      }
    }
    element.style.gridTemplateColumns = '3fr 5fr 1fr 1fr';
  });
  let subjects;
  const coursesURL = 'https://oghma.epcc.pt/users/' + segments[4] + '/subscriptions';
  let courseId = 0;
  await fetch(coursesURL).then(async html => {
    const parser = new DOMParser();
    const doc = parser.parseFromString(await html.text(), 'text/html');
    const tr = doc.querySelector('tbody > tr');
    if (tr === null) {
      return;
    }
    const anchor = tr.querySelector('td:nth(2) a');
    if (anchor === null) {
      return;
    }
    const id = anchor.href.split('/')[4];
    if (id === undefined) {
      return;
    }
    courseId = parseInt(id, 10);
  });
  if (!checkItem('allSubjectsSet', 'true')) {
    var _document$getElements, _userh1$textContent;
    const userh1 = (_document$getElements = document.getElementsByClassName('well clearfix')[0]) == null ? void 0 : _document$getElements.querySelector('h1');
    if (userh1 == undefined) {
      return;
    }
    const smallElement = userh1.querySelector('small');
    if (smallElement === null) {
      return;
    }
    const smallText = smallElement.textContent;
    if (smallText === null) {
      return;
    }
    const userName = (_userh1$textContent = userh1.textContent) == null ? void 0 : _userh1$textContent.substring(smallText.length);
    if (userName === undefined) {
      return;
    }
    subjects = await getAllSubjectsFromCourse(courseId.toString());
    subjects = await getSubjectNyUsername(userName, subjects);
    for (let i = 0; i < subjects.subjects.length; i++) {
      const title = subjects.subjects[i];
      if (title === undefined) {
        continue;
      }
      setItem(title, getItem(title, subjects.evaluations.get(title)));
      setItem(title + '_isActived', getItem(title + '_isActived', 'true'));
    }
    setItem('allSubjectsSet', 'true');
  } else {
    subjects = await getAllSubjectsFromCourse(courseId.toString());
    subjects.evaluations = new Map();
    subjects.subjects.forEach(function (title) {
      const evaluation = getItem(title, '0');
      if (typeof evaluation === 'string') {
        subjects.evaluations.set(title, evaluation);
      }
    });
  }
  await getByClass('well clearfix', async function (element) {
    let avarageSubjectSum = 0;
    let subjectsCount = 0;
    subjects.subjects.forEach(function (title) {
      if (subjects.evaluations.get(title) != '' && checkItem(title + '_isActived', 'true')) {
        subjectsCount++;
        const evaluation = subjects.evaluations.get(title);
        if (evaluation === undefined) {
          return;
        }
        avarageSubjectSum += parseFloat(evaluation);
      }
    });
    const avarage = (avarageSubjectSum / subjectsCount).toFixed(AVARAGE_DECIMAL_PARTS);
    let averageElement = null;
    if (document.getElementById('alunoAvarageText')) {
      averageElement = document.getElementById('alunoAvarageText');
    } else {
      averageElement = document.createElement('p');
      averageElement.id = 'alunoAvarageText';
    }
    if (averageElement === null) {
      return;
    }
    averageElement.innerText = `O aluno tem uma média de ${avarage} pontos`;
    if (!document.getElementById('alunoAvarageText')) {
      element.appendChild(averageElement);
    }
  });
  getByClass('span2 sidebar', function (element) {
    if (document.getElementById('evaluationsCheckers')) return;
    const div = document.createElement('div');
    div.id = 'evaluationsCheckers';
    div.style.paddingLeft = '0.8rem';
    div.style.paddingRight = '0.8rem';
    element.appendChild(div);
    for (let i = 0; i < subjects.subjects.length; i++) {
      const title = subjects.subjects[i];
      const subjectElementDiv = document.createElement('div');
      subjectElementDiv.style.display = 'flex';
      subjectElementDiv.style.flexDirection = 'row';
      subjectElementDiv.style.alignItems = 'center';
      subjectElementDiv.style.justifyContent = 'start';
      subjectElementDiv.style.padding = '0.25rem';
      const subjectElementCheckbox = document.createElement('input');
      subjectElementCheckbox.type = 'checkbox';
      subjectElementCheckbox.id = title + '_checkbox';
      if (checkItem(title + '_isActived', 'true')) {
        subjectElementCheckbox.checked = true;
      }
      subjectElementCheckbox.onclick = function () {
        setItem(title + '_isActived', checkItem(title + '_isActived', 'true') ? 'false' : 'true');
        executeSite();
      };
      const subjectElementText = document.createElement('label');
      subjectElementText.style.fontSize = '0.8rem';
      subjectElementText.style.marginBottom = '0rem';
      subjectElementText.style.userSelect = 'none';
      subjectElementText.htmlFor = title + '_checkbox';
      if (typeof title === 'string') {
        subjectElementText.innerText = title === 'Tecnologias de Informação e Comunicação' ? 'TIC' : title;
      }
      subjectElementText.style.paddingLeft = '0.5rem';
      const subjectAvarageScore = document.createElement('span');
      if (typeof title === 'string') {
        var _subjects$evaluations;
        subjectAvarageScore.innerText = (_subjects$evaluations = subjects.evaluations.get(title)) != null ? _subjects$evaluations : NaN.toString();
      }
      subjectAvarageScore.style.paddingLeft = '0.5rem';
      subjectElementDiv.appendChild(subjectElementCheckbox);
      subjectElementDiv.appendChild(subjectElementText);
      subjectElementDiv.appendChild(subjectAvarageScore);
      div.appendChild(subjectElementDiv);
    }
  });
}

function subscriptions () {
  getByClass('student active', function (element) {
    element.querySelectorAll('a').forEach(function (a) {
      a.href += '/evaluations';
    });
  });
  getAll('.student:not(.active)', hide);
  getByClass('users-list photo', flex);
  getByClass('users-list photo', function (element) {
    element.style.flexWrap = 'wrap';
  });
  let avarageSum = 0;
  let totalStudents = 0;
  getByClass('student active', async function (element) {
    var _element$querySelecto;
    const id = (_element$querySelecto = element.querySelector('a')) == null ? void 0 : _element$querySelecto.href.split('/')[4];
    const 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');
        const avarage = getAvarage(doc);
        avarageSum += avarage;
        totalStudents++;
        element.style.order = (checkItem('DECREASE', 'true') ? '-' : '') + (avarage * 1000).toFixed(0);
        const averageElement = document.createElement('p');
        averageElement.textContent = 'Média de ' + avarage.toFixed(AVARAGE_DECIMAL_PARTS) + ' pontos';
        element.appendChild(averageElement);
      });
    } else {
      const totalAvarage = avarageSum / totalStudents;
      const p = element.querySelector('p');
      if (p === null) {
        return;
      }
      p.textContent = `Média de ${totalAvarage.toFixed(AVARAGE_DECIMAL_PARTS)} pontos`;
      element.style.order = (checkItem('DECREASE', 'true') ? '-' : '') + (totalAvarage * 1000).toFixed(0);
    }
  });
  getByClass('users-list photo', function (element) {
    const avarageStudent = document.createElement('li');
    const imageA = document.createElement('a');
    const image = document.createElement('img');
    const br = document.createElement('br');
    const span = document.createElement('span');
    const a = document.createElement('a');
    const 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);
  });
}

const sites = new Map([['evaluations', evaluations], ['subscriptions', subscriptions]]);
const executeSite = () => {
  if (typeof page === 'string') {
    var _sites$get;
    (_sites$get = sites.get(page)) == null ? void 0 : _sites$get();
  }
};

getByText('Inscrições nos Exames', hide);
getByClass('events announcements', hide);
getByText('Importante!', hide);
executeSite();
getByClass('nav pull-right', element => {
  flex(element);
  element.style.alignItems = 'center';
  const li = document.createElement('li');
  const checkbox = document.createElement('input');
  checkbox.type = 'checkbox';
  if (checkItem('DECREASE', 'true')) checkbox.checked = true;
  checkbox.onclick = function () {
    setItem('DECREASE', checkItem('DECREASE', 'true') ? 'false' : 'true');
    executeSite();
  };
  const text = document.createElement('span');
  text.textContent = 'Ordem decrescente';
  text.style.padding = '1rem';
  li.style.display = 'flex';
  li.appendChild(text);
  li.appendChild(checkbox);
  li.style.order = '-1';
  element.appendChild(li);
});

})();