0d4c658d93
Логика классификации типа задания и статуса «сдано» дублировалась в трёх местах (dashboard.html, homework.html, assignmentController.js) и начала расходиться. Вынес в один UMD-модуль frontend/js/assignment-utils.js (грузится и в браузере, и в Node через require — как svg-sanitize.js): type(a), isDone(a, sub, opts), urgencyScore(a). Нюанс «сдано» для upload/file параметризован: вид ученика (acceptedOnly) — закрыто только при принятой сдаче; учитель/обзор долгов — любая сдача не на доработке. Поведение всех трёх поверхностей сохранено 1:1. - homework.html: asgnType/asgnDone/urgencyScore → тонкие делегаты в AssignmentUtils. - dashboard.html: urgencyScore делегирует; classify и upload-ветка buildAssignCard через AssignmentUtils.type/isDone (заодно корректнее: учебник-ДЗ больше не путается с upload). - assignmentController: classOutstanding/_assignTypeOf → AssignmentUtils. Verified: AU-контракт 25/25 (типы, isDone teacher vs acceptedOnly, порядок urgency); интеграция 8/8 (classOutstanding те же 14 уч./42 просрочено; homework делегирует). node --check всех файлов + инлайна обоих HTML. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
62 lines
3.2 KiB
JavaScript
62 lines
3.2 KiB
JavaScript
'use strict';
|
|
/* assignment-utils.js — единый источник правды для классификации заданий и статуса «сдано».
|
|
*
|
|
* Раньше эта логика дублировалась в трёх местах (dashboard.html, homework.html,
|
|
* assignmentController.js) и начала расходиться. Теперь — один модуль, который грузится
|
|
* и в браузере (window.AssignmentUtils), и в Node (module.exports) — как svg-sanitize.js.
|
|
*
|
|
* Поля задания (как их отдаёт /assignments/my и assignmentRowsForUser):
|
|
* textbook_id, file_id, is_homework, count, subject_slug, mode, session_status,
|
|
* max_attempts, attempts_used, deadline, textbook_all_read, completed_at.
|
|
*/
|
|
(function (root, factory) {
|
|
const api = factory();
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
if (typeof window !== 'undefined') window.AssignmentUtils = api;
|
|
})(this, function () {
|
|
|
|
/* Тип задания: textbook | file | upload | test.
|
|
Порядок проверки: учебник → файл → загрузка работы → тест. */
|
|
function type(a) {
|
|
if (a.textbook_id) return 'textbook';
|
|
if (a.file_id) return 'file';
|
|
if (a.is_homework && (a.count == null || a.count <= 1)
|
|
&& (!a.subject_slug || a.subject_slug === 'other')) return 'upload';
|
|
return 'test';
|
|
}
|
|
|
|
/* «Закрыто» (сдано/выполнено/прочитано) — задание уходит из активных/долгов.
|
|
sub — последняя сдача (объект с .status) для upload/file, иначе null/undefined.
|
|
opts.acceptedOnly=true (вид ученика на /homework): upload/file закрыт ТОЛЬКО при
|
|
status==='accepted' (пока не приняли — у ученика «висит»).
|
|
по умолчанию (учитель / обзор долгов): любая сдача не на доработке = закрыто
|
|
(ученик свою часть сделал — это уже не его долг). */
|
|
function isDone(a, sub, opts) {
|
|
opts = opts || {};
|
|
const t = type(a);
|
|
if (t === 'textbook') return !!(a.textbook_all_read || a.completed_at);
|
|
if (t === 'upload' || t === 'file') {
|
|
const st = sub && sub.status;
|
|
if (!st || st === 'revision') return false;
|
|
return opts.acceptedOnly ? st === 'accepted' : true;
|
|
}
|
|
// test
|
|
const maxAtt = a.max_attempts || 0;
|
|
const used = (a.attempts_used != null) ? a.attempts_used : 0;
|
|
if (maxAtt > 0 && used >= maxAtt) return true;
|
|
return a.session_status === 'completed' && a.mode !== 'repeat';
|
|
}
|
|
|
|
/* Срочность для сортировки (меньше — выше): идёт → просрочено → <24ч → по дедлайну → без срока. */
|
|
function urgencyScore(a) {
|
|
if (a.session_status === 'in_progress') return -4;
|
|
const dlMs = a.deadline ? (new Date(a.deadline).getTime() - Date.now()) : Infinity;
|
|
if (dlMs < 0) return -3;
|
|
if (dlMs < 24 * 3600 * 1000) return -2;
|
|
if (dlMs < Infinity) return dlMs;
|
|
return 1e12;
|
|
}
|
|
|
|
return { type, isDone, urgencyScore };
|
|
});
|