achsheet.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /**
  2. * ЭЛЕКТРОННЫЙ ЖУРНАЛ «ШКАЛА»: ГЕНЕРИРОВАНИЕ ТАБЕЛЕЙ УСПЕВАЕМОСТИ
  3. * Copyright © 2021, А.М.Гольдин. Modified BSD License
  4. */
  5. "use strict";
  6. // Объект с предметами (подгружается в конце страницы)
  7. let achSbList = {};
  8. // Замена отметок на расшифровку
  9. const grFull = {
  10. "0":"н/а", "2":"2 (неуд.)", "3":"3 (удовл.)",
  11. "4":"4 (хор.)", "5":"5 (отл.)", "999":"зач."
  12. };
  13. // Собственно генерирование табеля одного ученика
  14. // В аргументе приходит что-то вроде "pupkin^Пупкин Василий, 8Б класс"
  15. const achShow = async (pupil) => {
  16. dqs("#tabel").innerHTML = "<img src='static/preloader.gif'>";
  17. // Получаем фамилию, имя и класс ученика для подзаголовка
  18. let famImCl = pupil.split('^')[1];
  19. let podzag = famImCl ? `<p><b>${famImCl}</b></p>` : '';
  20. // Получаем итоговые отметки с помощью API в объект
  21. // gradesObj = {"s410": {d628a: "5", d831b: "0", ...} ...}
  22. info(0, "Пожалуйста, дождитесь<br>загрузки данных.");
  23. let apiResp = await apireq("tabelGet", [pupil.split('^')[0]]);
  24. info(2);
  25. if (apiResp == "none") {
  26. dqs("#tabel").innerHTML = "<p>Не удалось получить данные</p>";
  27. return;
  28. }
  29. let gradesObj = JSON.parse(apiResp);
  30. // Упорядоченный список кодов предметов (ключи объекта gradesObj)
  31. let subjs = Object.keys(gradesObj).sort((a, b) => {
  32. if (a.length != b.length) return a.length < b.length ? 1 : -1;
  33. else if (a.length == 3) return a > b ? 1 : -1;
  34. else return a.substr(1, 3) > b.substr(1, 3) ? 1 : -1;
  35. });
  36. // Публикуем
  37. if (!subjs.length) {
  38. dqs("#tabel").innerHTML =
  39. "<p>Табель не сгенерирован:<br>не выставлено ни одной " +
  40. "отметки промежуточной аттестации</p>";
  41. return;
  42. }
  43. let tabel = "<h3>ТАБЕЛЬ ОТМЕТОК ПРОМЕЖУТОЧНОЙ АТТЕСТАЦИИ</h3>" + podzag;
  44. tabel += "<table><tr><th>Предмет</th>";
  45. for (let period of Object.keys(DTSIT))
  46. tabel += `<th>${DTSIT[period][0]}</th>`;
  47. tabel += "</tr>";
  48. for (let sbCode of subjs) {
  49. tabel += `<tr><td>${achSbList[sbCode]}</td>`;
  50. for (let period of Object.keys(DTSIT)) {
  51. let grade = gradesObj[sbCode][period] ?
  52. gradesObj[sbCode][period] : "–";
  53. grade = grFull[grade] ? grFull[grade] : grade;
  54. tabel += `<td>${grade}</td>`;
  55. }
  56. tabel += "</tr>";
  57. }
  58. tabel += "</table>";
  59. dqs("#tabel").innerHTML =
  60. tabel + "<p><a id='achPrint'>Версия для печати</a></p>";
  61. // Подготавливаем версию для печати (HTML определен в ini.js)
  62. let tabelPrn = tabel + "<p class='sgn'>Директор<br>Классный руководитель</p>";
  63. let printCont = HTML.replace("{{body}}", tabelPrn).replace("8pt", "9pt")
  64. . replace("<h3>", "<h3 style='margin-top:5cm'>");
  65. let dataLink = new Blob([printCont], {type: "text/html"});
  66. dqs("#achPrint").href = window.URL.createObjectURL(dataLink);
  67. dqs("#achPrint").download = "tabel.html";
  68. }
  69. // Формирование списка детей в селекте выбора учащегося
  70. const achPupListShow = async () => {
  71. dqs("#tabel").innerHTML = '';
  72. let clName = dqs("#achSelClass").value;
  73. let apiResp = await apireq("pupilsList", [clName]);
  74. if (apiResp != "none") {
  75. let achClList = JSON.parse(apiResp);
  76. let selPupilInner = `<option value=''>== Выберите учащегося ==</option>`;
  77. for (let pup of achClList) {
  78. let imya = pup[0].split(' ')[1] || 'N';
  79. let famI = pup[0].split(' ')[0] + ` ${imya[0]}.`;
  80. selPupilInner += `<option value="${pup[1]}^${pup[0]}, `
  81. + `${clName} класс">${famI}</option>`;
  82. }
  83. dqs("#achSelPupil").innerHTML = selPupilInner;
  84. }
  85. else {
  86. dqs("#achSelPupil").innerHTML = '';
  87. dqs("#tabel").innerHTML = "<h3>В этом классе нет учащихся</h3>";
  88. }
  89. genButtonTabelAll(); // Показываем кнопку генер. табеля всего класса
  90. }
  91. // Подстановка кнопки для генерирования табеля класса в соотв. div
  92. const genButtonTabelAll = () => {
  93. dqs("#tabelAllClass").innerHTML =
  94. "<button type='button' onclick='tabGenAllClass()'>"
  95. + "Табель всего класса</button>";
  96. }
  97. // Преобразование текста utf-8 в кодировку win-1251 (возвращает Uint8Array)
  98. const utf8to1251 = stroka => {
  99. const cp1251 = {
  100. '\n': '0A','\r': '0D', ' ': '20', '!': '21', '"': '22', '#': '23',
  101. '$': '24', '%': '25', '&': '26','\'': '27', '(': '28', ')': '29',
  102. '*': '2A', '+': '2B', ',': '2C', '-': '2D', '.': '2E', '/': '2F',
  103. '0': '30', '1': '31', '2': '32', '3': '33', '4': '34', '5': '35',
  104. '6': '36', '7': '37', '8': '38', '9': '39', ':': '3A', ';': '3B',
  105. '<': '3C', '=': '3D', '>': '3E', '?': '3F', '@': '40', 'A': '41',
  106. 'B': '42', 'C': '43', 'D': '44', 'E': '45', 'F': '46', 'G': '47',
  107. 'H': '48', 'I': '49', 'J': '4A', 'K': '4B', 'L': '4C', 'M': '4D',
  108. 'N': '4E', 'O': '4F', 'P': '50', 'Q': '51', 'R': '52', 'S': '53',
  109. 'T': '54', 'U': '55', 'V': '56', 'W': '57', 'X': '58', 'Y': '59',
  110. 'Z': '5A', '[': '5B','\\': '5C', ']': '5D', '^': '5E', '_': '5F',
  111. '`': '60', 'a': '61', 'b': '62', 'c': '63', 'd': '64', 'e': '65',
  112. 'f': '66', 'g': '67', 'h': '68', 'i': '69', 'j': '6A', 'k': '6B',
  113. 'l': '6C', 'm': '6D', 'n': '6E', 'o': '6F', 'p': '70', 'q': '71',
  114. 'r': '72', 's': '73', 't': '74', 'u': '75', 'v': '76', 'w': '77',
  115. 'x': '78', 'y': '79', 'z': '7A', '{': '7B', '|': '7C', '}': '7D',
  116. '~': '7E', '–': '96', '—': '97', 'Ё': 'A8', '«': 'AB', 'ё': 'B8',
  117. '№': 'B9', '»': 'BB', 'А': 'C0', 'Б': 'C1', 'В': 'C2', 'Г': 'C3',
  118. 'Д': 'C4', 'Е': 'C5', 'Ж': 'C6', 'З': 'C7', 'И': 'C8', 'Й': 'C9',
  119. 'К': 'CA', 'Л': 'CB', 'М': 'CC', 'Н': 'CD', 'О': 'CE', 'П': 'CF',
  120. 'Р': 'D0', 'С': 'D1', 'Т': 'D2', 'У': 'D3', 'Ф': 'D4', 'Х': 'D5',
  121. 'Ц': 'D6', 'Ч': 'D7', 'Ш': 'D8', 'Щ': 'D9', 'Ъ': 'DA', 'Ы': 'DB',
  122. 'Ь': 'DC', 'Э': 'DD', 'Ю': 'DE', 'Я': 'DF', 'а': 'E0', 'б': 'E1',
  123. 'в': 'E2', 'г': 'E3', 'д': 'E4', 'е': 'E5', 'ж': 'E6', 'з': 'E7',
  124. 'и': 'E8', 'й': 'E9', 'к': 'EA', 'л': 'EB', 'м': 'EC', 'н': 'ED',
  125. 'о': 'EE', 'п': 'EF', 'р': 'F0', 'с': 'F1', 'т': 'F2', 'у': 'F3',
  126. 'ф': 'F4', 'х': 'F5', 'ц': 'F6', 'ч': 'F7', 'ш': 'F8', 'щ': 'F9',
  127. 'ъ': 'FA', 'ы': 'FB', 'ь': 'FC', 'э': 'FD', 'ю': 'FE', 'я': 'FF'
  128. };
  129. return new Uint8Array(
  130. stroka.split('').map(x => parseInt(cp1251[x] || '3F', 16))
  131. );
  132. }
  133. // Запрос и формирование ссылки на скачивание табеля всего класса
  134. // (отдается в кодировке windows-1251)
  135. const tabGenAllClass = async () => {
  136. dqs("#tabelAllClass").innerHTML = "<p>Табель генерируется, ждите...</p>";
  137. let clName = dqs("#achSelClass").value;
  138. let apiResp = await apireq("tabelGenAll", [clName]);
  139. if (apiResp != "none") {
  140. let dataLink = new Blob([utf8to1251(apiResp)], {type: "text/csv"}),
  141. hr = window.URL.createObjectURL(dataLink);
  142. dqs("#tabelAllClass").innerHTML =
  143. `<a href="${hr}" download='tabel${clName}.csv'>`
  144. + `Скачать табель всего ${clName}</a>`;
  145. }
  146. else {
  147. dqs("#tabelAllClass").innerHTML = "<p>Не удалось получить табель</p>";
  148. return;
  149. }
  150. }
  151. // Формирование контента страницы
  152. createSection("achsheet", `
  153. <select id="achSelClass" onChange="achPupListShow()"></select>
  154. <select id="achSelPupil" onChange="achShow(this.value)"></select>
  155. <div id="tabel" style="margin-top:20px"></div>
  156. <div id="tabelAllClass" style="margin-top:20px"></div>
  157. `);
  158. // Динамически подгружаем контент страницы (имя метода = имени пункта меню!)
  159. getContent.achsheet = async () => {
  160. // Получаем глобальный объект со списком всех предметов
  161. // achSbList = {"s110": "Русский язык", ...}
  162. let apiResp = await apireq("subjList");
  163. let achListDop = JSON.parse(apiResp);
  164. achSbList = {...subjDef, ...achListDop};
  165. let achRole = dqs("#selRole").value;
  166. let selClassInner = '';
  167. // Если он учащийся или родитель, показываем ему его табель
  168. if (achRole == "pupil" || achRole == "parent") {
  169. dqs("#achSelClass").style.display = "none";
  170. dqs("#achSelPupil").style.display = "none";
  171. achShow(`${uLogin}^`);
  172. }
  173. // Если он администратор, показываем ему все классы
  174. else if (achRole == "admin") {
  175. let apiResp = await apireq("classesList");
  176. if (apiResp == "none") {info(1, "Не могу получить данные"); return;}
  177. let achAllClasses = classSort(JSON.parse(apiResp));
  178. for (let cl of achAllClasses) selClassInner += `<option>${cl}</option>`;
  179. dqs("#achSelClass").innerHTML = selClassInner;
  180. achPupListShow(); // показываем список детей
  181. genButtonTabelAll(); // Показываем кнопку генер. табеля всего класса
  182. }
  183. // Если он классный руководитель, показываем ему его классы
  184. else if (achRole == "tutor") {
  185. for (let cl of uTutorCls) selClassInner += `<option>${cl}</option>`;
  186. dqs("#achSelClass").innerHTML = selClassInner;
  187. achPupListShow(); // показываем список детей
  188. genButtonTabelAll(); // Показываем кнопку генер. табеля всего класса
  189. }
  190. dqs("#tabel").innerHTML = '';
  191. };