Kepler Script

Kepler için notlar

目前為 2025-01-07 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Kepler Script
  3. // @author KazuroAkashi
  4. // @match https://obs.itu.edu.tr/ogrenci/
  5. // @license MIT
  6. // @version 0.0.1.20250107063842
  7. // @namespace https://greasyfork.org/users/1419483
  8. // @description Kepler için notlar
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. async function getJWT() {
  15. return new Promise((resolve, reject) => {
  16. const xhr = new XMLHttpRequest();
  17. xhr.open("GET", "https://obs.itu.edu.tr/ogrenci/auth/jwt");
  18.  
  19. xhr.onload = () => {
  20. if (xhr.readyState == 4 && xhr.status == 200) {
  21. resolve(xhr.responseText);
  22. } else {
  23. reject(xhr.status);
  24. }
  25. };
  26. xhr.send();
  27. })
  28. }
  29.  
  30. async function getDonemListesi(jwt) {
  31. return new Promise((resolve, reject) => {
  32. const xhr = new XMLHttpRequest();
  33. xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/DonemListesi");
  34. xhr.setRequestHeader("Authorization", "Bearer " + jwt);
  35.  
  36. xhr.onload = () => {
  37. if (xhr.readyState == 4 && xhr.status == 200) {
  38. resolve(JSON.parse(xhr.response));
  39. } else {
  40. reject(xhr.status);
  41. }
  42. };
  43. xhr.send();
  44. })
  45. }
  46.  
  47. async function getSinifListesi(jwt, donemId) {
  48. return new Promise((resolve, reject) => {
  49. const xhr = new XMLHttpRequest();
  50. xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/sinif/KayitliSinifListesi/" + donemId);
  51. xhr.setRequestHeader("Authorization", "Bearer " + jwt);
  52.  
  53. xhr.onload = () => {
  54. if (xhr.readyState == 4 && xhr.status == 200) {
  55. resolve(JSON.parse(xhr.response));
  56. } else {
  57. reject(xhr.status);
  58. }
  59. };
  60. xhr.send();
  61. })
  62. }
  63.  
  64. async function getNotListesi(jwt, sinifId) {
  65. return new Promise((resolve, reject) => {
  66. const xhr = new XMLHttpRequest();
  67. xhr.open("GET", "https://obs.itu.edu.tr/api/ogrenci/Sinif/SinifDonemIciNotListesi/" + sinifId);
  68. xhr.setRequestHeader("Authorization", "Bearer " + jwt);
  69.  
  70. xhr.onload = () => {
  71. if (xhr.readyState == 4 && xhr.status == 200) {
  72. resolve(JSON.parse(xhr.response));
  73. } else {
  74. reject(xhr.status);
  75. }
  76. };
  77. xhr.send();
  78. })
  79. }
  80.  
  81. String.prototype.formatStr = String.prototype.formatStr ||
  82. function () {
  83. "use strict";
  84. var str = this.toString();
  85. if (arguments.length) {
  86. var t = typeof arguments[0];
  87. var key;
  88. var args = ("string" === t || "number" === t) ?
  89. Array.prototype.slice.call(arguments)
  90. : arguments[0];
  91.  
  92. for (key in args) {
  93. str = str.replace(new RegExp("\\{" + key + "\\}", "gi"), args[key]);
  94. }
  95. }
  96.  
  97. return str;
  98. };
  99.  
  100. const htmlParser = new DOMParser();
  101. function createHTMLElement(str) {
  102. const doc = htmlParser.parseFromString(str, "text/html");
  103. return doc.body.firstChild;
  104. }
  105.  
  106. function insertBeforeHTMLElement(str, el) {
  107. const insert = createHTMLElement(str);
  108. el.parentElement.insertBefore(insert, el);
  109. }
  110.  
  111. const newCardTemplate = `
  112. <div class="row">
  113. <div class="col-md-12 mb-5">
  114. <div class="card info-graphic info-graphic--service">
  115. <div class="card-body">
  116. <h2>Notlar</h2>
  117. <div class="row" style="justify-content: center; align-items: center;">
  118. <input id="notscript-hidetrivial" type="checkbox">
  119. <label for="notscript-hidetrivial" style="padding-left: 10px; margin: 0; user-select: none;">Hiç not girilmemiş dersleri gizle</label>
  120. </div>
  121. {0}
  122. </div>
  123. </div>
  124. </div>
  125. </div>
  126. `;
  127.  
  128. const newClassTemplateTable = `
  129. <div class="col-lg-12 mb-3 notscript-class" data-crn="{crn}">
  130. <h4 style="font-weight: 600">{name}</h4>
  131. <div class="table-vertical table-vertical--unheight">
  132. <table class="table table-striped table-bordered" style="table-layout: fixed">
  133. <tbody>
  134. {notes}
  135. <tr>
  136. <th class="title" style="width: 40%">Ağırlıklı Ortalama</th>
  137. <td>{average}</td>
  138. </tr>
  139. </tbody>
  140. </table>
  141. </div>
  142. </div>
  143. `;
  144.  
  145. const newNoteTemplateTable = `
  146. <tr>
  147. <th class="title" style="width: 40%">{name} (%{perc})</th>
  148. <td>{note}</td>
  149. </tr>
  150. `;
  151.  
  152. const classLineTemplate = `
  153. <div id="notscript-line-{crn}" style="width: 100%; height: 1px; background: #358aed; margin-bottom: 15px; margin-top: 13px;"></div>
  154. `;
  155.  
  156. // TODO: Option to hide ignorable classes
  157. // TODO: Term Selection
  158. async function printNotlar() {
  159. const jwt = await getJWT();
  160. const donemListesi = (await getDonemListesi(jwt)).ogrenciDonemListesi;
  161. const sonDonem = donemListesi[donemListesi.length - 1];
  162. const sonDonemId = sonDonem.akademikDonemId;
  163. const sinifListesi = (await getSinifListesi(jwt, sonDonemId)).kayitSinifResultList;
  164.  
  165. const addBefore = document.querySelectorAll(".obs > .container-fluid > div > .row")[1];
  166.  
  167. const hidecrns = [];
  168.  
  169. let classesEl = "";
  170. for (const sinif of sinifListesi) {
  171. if (classesEl !== "") classesEl += classLineTemplate.formatStr({ crn: sinif.crn });
  172.  
  173. const sinifNameEn = sinif.bransKodu + sinif.dersKodu + " - " + sinif.dersAdiEN + " (CRN: " + sinif.crn + ")";
  174. const sinifNameTr = sinif.bransKodu + sinif.dersKodu + " - " + sinif.dersAdiTR + " (CRN: " + sinif.crn + ")";
  175. const sinifId = sinif.sinifId;
  176.  
  177. const notListesiObj = (await getNotListesi(jwt, sinifId));
  178. const notListesi = notListesiObj.sinifDonemIciNotListesi;
  179. const ortalama = notListesiObj.ortalama;
  180.  
  181. let notesEl = "";
  182. for (const not of notListesi) {
  183. const notName = not.degerlendirmeOlcutuAdi;
  184. const notPerc = not.degerlendirmeKatkisi;
  185. const notValue = not.not;
  186.  
  187. notesEl += newNoteTemplateTable.formatStr({ name: notName, perc: notPerc, note: notValue });
  188. }
  189.  
  190. if (notListesi.length === 0) {
  191. hidecrns.push(sinif.crn);
  192. }
  193.  
  194. classesEl += newClassTemplateTable.formatStr({ crn: sinif.crn, name: sinifNameTr, notes: notesEl, average: ortalama });
  195. }
  196.  
  197. const cardEl = newCardTemplate.formatStr(classesEl);
  198. const cardEll = createHTMLElement(cardEl);
  199. addBefore.parentElement.insertBefore(cardEll, addBefore);
  200.  
  201. const hidetrivialEl = cardEll.querySelector("#notscript-hidetrivial");
  202. hidetrivialEl.checked = window.localStorage.getItem("hide_trivial_classes");
  203.  
  204. const classes = cardEll.querySelectorAll(".notscript-class");
  205.  
  206. const trivialClasses = classes.values().filter(cl => hidecrns.includes(cl.dataset.crn)).toArray();
  207.  
  208. hidetrivialEl.onchange = (e) => {
  209. const disp = hidetrivialEl.checked ? "none" : "block";
  210. window.localStorage.setItem("hide_trivial_classes", hidetrivialEl.checked);
  211. for (const cl of trivialClasses) {
  212. cl.style.display = disp;
  213.  
  214. const line = cardEll.querySelector("#notscript-line-" + cl.dataset.crn);
  215. line.style.display = disp;
  216. }
  217. }
  218.  
  219. hidetrivialEl.onchange();
  220. }
  221.  
  222. printNotlar();
  223.  
  224. })();