Kepler Script

Kepler için notlar

当前为 2025-01-07 提交的版本,查看 最新版本

// ==UserScript==
// @name         Kepler Script
// @author       KazuroAkashi
// @match        https://obs.itu.edu.tr/ogrenci/
// @license MIT
// @version 0.0.1.20250107090026
// @namespace https://greasyfork.org/users/1419483
// @description Kepler için notlar
// ==/UserScript==

(function() {
    'use strict';

    async function getJWT() {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", "https://obs.itu.edu.tr/ogrenci/auth/jwt");

            xhr.onload = () => {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    resolve(xhr.responseText);
                } else {
                    reject(xhr.status);
                }
            };
            xhr.send();
        })
    }

    async function getDonemListesi(jwt) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/DonemListesi");
            xhr.setRequestHeader("Authorization", "Bearer " + jwt);

            xhr.onload = () => {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    resolve(JSON.parse(xhr.response));
                } else {
                    reject(xhr.status);
                }
            };
            xhr.send();
        })
    }

    async function getSinifListesi(jwt, donemId) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/sinif/KayitliSinifListesi/" + donemId);
            xhr.setRequestHeader("Authorization", "Bearer " + jwt);

            xhr.onload = () => {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    resolve(JSON.parse(xhr.response));
                } else {
                    reject(xhr.status);
                }
            };
            xhr.send();
        })
    }

    async function getHarfNotuListesi(jwt, donemId) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/Sinif/SinifHarfNotuListesi/" + donemId);
            xhr.setRequestHeader("Authorization", "Bearer " + jwt);

            xhr.onload = () => {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    const arr = JSON.parse(xhr.response).sinifHarfNotuResultList;
                    const obj = {};

                    for (const not of arr) {
                        obj[not.crn] = not.harfNotu;
                    }

                    resolve(obj);
                } else {
                    reject(xhr.status);
                }
            };
            xhr.send();
        })
    }

    async function getNotListesi(jwt, sinifId) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/Sinif/SinifDonemIciNotListesi/" + sinifId);
            xhr.setRequestHeader("Authorization", "Bearer " + jwt);

            xhr.onload = () => {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    resolve(JSON.parse(xhr.response));
                } else {
                    reject(xhr.status);
                }
            };
            xhr.send();
        })
    }

    String.prototype.formatStr = String.prototype.formatStr ||
        function () {
        "use strict";
        var str = this.toString();
        if (arguments.length) {
            var t = typeof arguments[0];
            var key;
            var args = ("string" === t || "number" === t) ?
                Array.prototype.slice.call(arguments)
            : arguments[0];

            for (key in args) {
                str = str.replace(new RegExp("\\{" + key + "\\}", "gi"), args[key]);
            }
        }

        return str;
    };

    const htmlParser = new DOMParser();
    function createHTMLElement(str) {
        const doc = htmlParser.parseFromString(str, "text/html");
        return doc.body.firstChild;
    }

    function insertBeforeHTMLElement(str, el) {
        const insert = createHTMLElement(str);
        el.parentElement.insertBefore(insert, el);
    }

    const newOptionTemplate = `
<option value="{id}">{name}</option>
`;

    const newCardTemplate = `
<div class="row">
  <div class="col-md-12 mb-5">
    <div class="card info-graphic info-graphic--service">
      <div class="card-body">
        <h2>Notlar</h2>
        <select id="notscript-donemlist" class="form-control" style="margin: 10px">
          {1}
        </select>
        <div class="row" style="justify-content: center; align-items: center;">
          <input id="notscript-hidetrivial" type="checkbox">
          <label for="notscript-hidetrivial" style="padding-left: 10px; margin: 0; user-select: none;">Hiç not girilmemiş dersleri gizle</label>
        </div>
        {0}
      </div>
    </div>
  </div>
</div>
`;

    const newDonemTemplate = `
<div style="display: none" class="notscript-donem" data-id={id}>{classes}</div>
`;

    const newClassTemplateTable = `
<div class="col-lg-12 mb-3 notscript-class" data-crn="{crn}">
  <h4 style="font-weight: 600">{name}</h4>
  <div class="table-vertical table-vertical--unheight">
    <table class="table table-striped table-bordered" style="table-layout: fixed">
      <tbody>
        {notes}
        <tr>
          <th class="title" style="width: 40%">Ağırlıklı Ortalama</th>
          <td>{average}</td>
        </tr>
        <tr style="display: {harfnotudisp}">
          <th class="title" style="width: 40%">Harf Notu</th>
          <td style="font-weight: 600; font-size: 1.8rem; color: {harfnotucolor}">{harfnotu}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>
`;

    const newNoteTemplateTable = `
<tr>
  <th class="title" style="width: 40%">{name} - %{perc} - Sıra: {pos}/{enrolled} - Ort. {avg} - S.Sap {devi}</th>
  <td>{note}</td>
</tr>
`;

    const classLineTemplate = `
<div id="notscript-line-{crn}" style="width: 100%; height: 1px; background: #358aed; margin-bottom: 15px; margin-top: 13px;"></div>
`;

    // TODO: Option to hide ignorable classes
    // TODO: Term Selection
    async function printNotlar() {
        const jwt = await getJWT();
        const donemListesi = (await getDonemListesi(jwt)).ogrenciDonemListesi;
        const sonDonem = donemListesi[donemListesi.length - 1];
        const sonDonemId = sonDonem.akademikDonemId;

        const addBefore = document.querySelectorAll(".obs > .container-fluid > div > .row")[1];

        const hidecrns = [];

        let donemOptList = "";
        let donems = "";
        for (let i = donemListesi.length - 1; i >= 0; i--) {
            const donem = donemListesi[i];
            donemOptList += newOptionTemplate.formatStr({ id: donem.akademikDonemId, name: donem.akademikDonemAdi });

            const sinifListesi = (await getSinifListesi(jwt, donem.akademikDonemId)).kayitSinifResultList;
            const harfNotuListesi = (await getHarfNotuListesi(jwt, donem.akademikDonemId));

            let classesEl = "";
            for (const sinif of sinifListesi) {
                if (classesEl !== "") classesEl += classLineTemplate.formatStr({ crn: sinif.crn });

                const sinifNameEn = sinif.bransKodu + sinif.dersKodu + " - " + sinif.dersAdiEN + " (CRN: " + sinif.crn + ")";
                const sinifNameTr = sinif.bransKodu + sinif.dersKodu + " - " + sinif.dersAdiTR + " (CRN: " + sinif.crn + ")";
                const sinifId = sinif.sinifId;

                const notListesiObj = (await getNotListesi(jwt, sinifId));
                const notListesi = notListesiObj.sinifDonemIciNotListesi;
                const ortalama = notListesiObj.ortalama;

                let notesEl = "";
                for (const not of notListesi) {
                    const name = not.degerlendirmeOlcutuAdi;
                    const perc = not.degerlendirmeKatkisi;
                    const note = not.not;
                    const pos = not.sinifSirasi;
                    const enrolled = not.ogrenciSayisi;
                    const avg = not.ortalama;
                    const devi = not.standartSapma;

                    notesEl += newNoteTemplateTable.formatStr({ name, perc, note, pos, enrolled, avg, devi });
                }

                if (notListesi.length === 0 && !harfNotuListesi[sinif.crn]) {
                    hidecrns.push(sinif.crn);
                }

                let harfnotudisp = "none";
                let harfnotu = "";
                let harfnotucolor = "red"
                if (harfNotuListesi[sinif.crn]) {
                    harfnotudisp = "";
                    harfnotu = harfNotuListesi[sinif.crn];

                    if (harfnotu === "AA" || harfnotu === "BL") {
                        harfnotucolor = "#22dd22";
                    } else if (harfnotu === "BA+" || harfnotu === "BA" || harfnotu === "BB+" || harfnotu === "BB") {
                        harfnotucolor = "#aadd22"
                    } else if (harfnotu === "CB+" || harfnotu === "CB" || harfnotu === "CC+" || harfnotu === "CC") {
                        harfnotucolor = "#dddd22"
                    } else if (harfnotu === "DC+" || harfnotu === "DC" || harfnotu === "DD+" || harfnotu === "DD") {
                        harfnotucolor = "#dd9922"
                    } else if (harfnotu === "VF" || harfnotu === "FF" || harfnotu === "BZ") {
                        harfnotucolor = "#dd2222"
                    }
                }

                classesEl += newClassTemplateTable.formatStr({ crn: sinif.crn, name: sinifNameTr, notes: notesEl, average: ortalama, harfnotudisp, harfnotu, harfnotucolor });
            }

            donems += newDonemTemplate.formatStr({ id: donem.akademikDonemId, classes: classesEl });
        }

        const cardEl = newCardTemplate.formatStr(donems, donemOptList);
        const cardEll = createHTMLElement(cardEl);
        addBefore.parentElement.insertBefore(cardEll, addBefore);

        const donemlistEl = cardEll.querySelector("#notscript-donemlist");
        const donemElList = cardEll.querySelectorAll(".notscript-donem");
        donemlistEl.onchange = (e) => {
            for (const donem of donemElList) {
                if (donem.dataset.id === donemlistEl.value) donem.style.display = "";
                else donem.style.display = "none";
            }
        }
        donemlistEl.onchange();

        const hidetrivialEl = cardEll.querySelector("#notscript-hidetrivial");
        hidetrivialEl.checked = window.localStorage.getItem("hide_trivial_classes");

        const classes = cardEll.querySelectorAll(".notscript-class");

        const trivialClasses = classes.values().filter(cl => hidecrns.includes(cl.dataset.crn)).toArray();

        hidetrivialEl.onchange = (e) => {
            const disp = hidetrivialEl.checked ? "none" : "";
            window.localStorage.setItem("hide_trivial_classes", hidetrivialEl.checked);
            for (const cl of trivialClasses) {
                cl.style.display = disp;

                const line = cardEll.querySelector("#notscript-line-" + cl.dataset.crn);
                line.style.display = disp;
            }
        }

        hidetrivialEl.onchange();
    }

    printNotlar();



})();