fix @SethBurkart123 's comments

This commit is contained in:
2026-04-30 21:17:48 +09:30
parent c987e4d54e
commit f3f4491f04
2 changed files with 40 additions and 95 deletions
@@ -1,7 +1,6 @@
import { settingsState } from "@/seqta/utils/listeners/SettingsState"; import { settingsState } from "@/seqta/utils/listeners/SettingsState";
import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage"; import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage";
import { waitForElm } from "@/seqta/utils/waitForElm"; import { waitForElm } from "@/seqta/utils/waitForElm";
import { getCurrentStudentId } from "../indexing/api";
export interface BaseCommandItem { export interface BaseCommandItem {
id: string; id: string;
@@ -24,11 +23,6 @@ async function getCurrentLesson() {
const todayFormatted = formatDate(date); const todayFormatted = formatDate(date);
try { try {
const student = await getCurrentStudentId();
if (typeof student !== "number") {
alert("Could not determine the active SEQTA student.");
return null;
}
const response = await fetch(`${location.origin}/seqta/student/load/timetable?`, { const response = await fetch(`${location.origin}/seqta/student/load/timetable?`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
@@ -36,7 +30,6 @@ async function getCurrentLesson() {
body: JSON.stringify({ body: JSON.stringify({
from: todayFormatted, from: todayFormatted,
until: todayFormatted, until: todayFormatted,
student,
}), }),
}); });
@@ -1,67 +1,41 @@
import type { IndexItem, Job } from "../types"; import type { IndexItem, Job } from "../types";
import { getCurrentStudentId, seqtaFetchPayload } from "../api";
import { getUserInfo } from "@/seqta/ui/AddBetterSEQTAElements";
/** const fetchJSON = async (url: string, body: any) => {
* Resolves the active student id from whatever source is available. const res = await fetch(`${location.origin}${url}`, {
* method: "POST",
* The shared `getCurrentStudentId()` calls `/seqta/student/login` with a credentials: "include",
* specific body shape; on some SEQTA installs that endpoint can return a headers: { "Content-Type": "application/json; charset=utf-8" },
* response that confuses the helper (no `id`, or a non-"200" envelope). body: JSON.stringify(body),
* To make sure we never silently skip the entire assignments pass, we });
* also fall back to `getUserInfo()` from `AddBetterSEQTAElements.ts` — return res.json();
* it's the same handshake the host page uses to render the avatar, so };
* if the user is logged in at all this path resolves.
*/ const fetchUpcomingAssessments = async (student: number = 69) => {
async function resolveStudentId(): Promise<number | undefined> {
try { try {
const direct = await getCurrentStudentId(); const res = await fetchJSON("/seqta/student/assessment/list/upcoming?", {
if (typeof direct === "number" && Number.isFinite(direct)) return direct; student,
});
// Match analytics.rs: payload is an array, return empty array if not found
return Array.isArray(res.payload) ? res.payload : [];
} catch (e) { } catch (e) {
console.warn( console.error("[Assignments job] Failed to fetch upcoming assessments:", e);
"[Assignments job] getCurrentStudentId() threw, falling back to getUserInfo()", return [];
e,
);
} }
try {
const info = (await getUserInfo()) as { id?: unknown } | null;
const id = info?.id;
if (typeof id === "number" && Number.isFinite(id)) return id;
if (typeof id === "string" && id && Number.isFinite(Number(id))) {
return Number(id);
}
} catch (e) {
console.warn("[Assignments job] getUserInfo() fallback failed:", e);
}
return undefined;
}
const fetchUpcomingAssessments = async (student: number) => {
const payload = await seqtaFetchPayload<any[]>(
"/seqta/student/assessment/list/upcoming",
{ student },
);
return Array.isArray(payload) ? payload : [];
}; };
const fetchSubjects = async () => { const fetchSubjects = async () => {
// SEQTA accepts both `{}` and `{ mode: "list" }` here; the latter is the try {
// shape every BetterSEQTA-Plus path uses elsewhere and is the more const res = await fetchJSON("/seqta/student/load/subjects?", {});
// reliable response format on schools that customize the `student/load` return res.payload
// endpoint. ?.filter((s: any) => s.active === 1)
const payload = await seqtaFetchPayload<any[]>( ?.flatMap((s: any) => s.subjects) || [];
"/seqta/student/load/subjects", } catch (e) {
{ mode: "list" }, console.error("[Assignments job] Failed to fetch subjects:", e);
); return [];
if (!Array.isArray(payload)) return []; }
return payload
.filter((s: any) => s && s.active === 1)
.flatMap((s: any) => (Array.isArray(s.subjects) ? s.subjects : []));
}; };
const fetchPastAssessments = async (student: number, subjects: any[]) => { const fetchPastAssessments = async (student: number = 69, subjects: any[]) => {
const map: Record<number, any> = {}; const map: Record<number, any> = {};
// Fetch past assessments for all subjects in parallel (like assessmentsOverview does) // Fetch past assessments for all subjects in parallel (like assessmentsOverview does)
@@ -69,16 +43,12 @@ const fetchPastAssessments = async (student: number, subjects: any[]) => {
await Promise.all( await Promise.all(
subjects.map(async (subject) => { subjects.map(async (subject) => {
try { try {
const payload = await seqtaFetchPayload<any>( // Match analytics.rs exactly: parameter order is programme, metaclass, student
"/seqta/student/assessment/list/past", const res = await fetchJSON("/seqta/student/assessment/list/past?", {
{
programme: subject.programme, programme: subject.programme,
metaclass: subject.metaclass, metaclass: subject.metaclass,
student, student,
}, });
);
if (!payload) return;
// Past assessments API can return data in payload.tasks OR payload.pending (or both) // Past assessments API can return data in payload.tasks OR payload.pending (or both)
// Based on analytics.rs fetch_past_assessments, we need to check both arrays // Based on analytics.rs fetch_past_assessments, we need to check both arrays
@@ -98,13 +68,13 @@ const fetchPastAssessments = async (student: number, subjects: any[]) => {
// Match analytics.rs: Check both pending and tasks arrays // Match analytics.rs: Check both pending and tasks arrays
// Check for pending array first (matching Rust code order) // Check for pending array first (matching Rust code order)
if (payload?.pending && Array.isArray(payload.pending)) { if (res.payload?.pending && Array.isArray(res.payload.pending)) {
payload.pending.forEach(processAssessment); res.payload.pending.forEach(processAssessment);
} }
// Check for tasks array // Check for tasks array
if (payload?.tasks && Array.isArray(payload.tasks)) { if (res.payload?.tasks && Array.isArray(res.payload.tasks)) {
payload.tasks.forEach(processAssessment); res.payload.tasks.forEach(processAssessment);
} }
} catch (e) { } catch (e) {
console.warn(`[Assignments job] Failed to fetch past assessments for subject ${subject.code || subject.subject || 'unknown'}:`, e); console.warn(`[Assignments job] Failed to fetch past assessments for subject ${subject.code || subject.subject || 'unknown'}:`, e);
@@ -156,27 +126,9 @@ export const assignmentsJob: Job = {
const existingItems = await ctx.getStoredItems("assignments"); const existingItems = await ctx.getStoredItems("assignments");
const existingIds = new Set(existingItems.map((i) => i.id)); const existingIds = new Set(existingItems.map((i) => i.id));
// Resolve the active student id from the live SEQTA session. Historically const student = 69; // TODO: Get from context if available
// this was hard-coded to 69, which only happens to be correct on a few
// local dev instances; the shared helper now reuses the same `login`
// handshake that the host page performs so every install gets the right
// value without configuration.
//
// We *throw* instead of returning [] when resolution fails, so the
// indexer's "lastRun" meta is NOT updated. Otherwise the job would be
// marked complete (with zero items) and `shouldRun` would skip it for
// the entire 24h frequency window — meaning a single bad page load
// could leave the user without any assessment results until tomorrow.
const student = await resolveStudentId();
if (typeof student !== "number") {
throw new Error(
"[Assignments job] Could not resolve current student id from /seqta/student/login. The job will retry on the next page load.",
);
}
console.debug( console.debug("[Assignments job] Starting indexing - fetching all assessments (upcoming and past)...");
`[Assignments job] Starting indexing for student=${student} - fetching all assessments (upcoming and past)...`,
);
// Fetch data in parallel // Fetch data in parallel
const [upcoming, subjects] = await Promise.all([ const [upcoming, subjects] = await Promise.all([