'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 }; });