mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat: supporting improved assessments and improved parsing
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import type { Job, IndexItem } from "../types";
|
||||
import { htmlToPlainText } from "../utils";
|
||||
import { fetchMessageContent } from "./messages";
|
||||
|
||||
/* ------------- Notification types ------------- */
|
||||
interface MessageNotification {
|
||||
@@ -44,6 +46,55 @@ const fetchNotifications = async () => {
|
||||
return (json.notifications ?? []) as Notification[];
|
||||
};
|
||||
|
||||
const fetchAssessmentName = async (
|
||||
assessmentId: number,
|
||||
metaclassId: number,
|
||||
programmeId: number
|
||||
): Promise<string> => {
|
||||
const searchAssessment = (data: any): string | null => {
|
||||
// Search syllabus
|
||||
for (const item of data.syllabus || []) {
|
||||
const found = (item.assessments || []).find((a: any) => a.id === assessmentId);
|
||||
if (found) return found.title;
|
||||
}
|
||||
|
||||
// Search pending
|
||||
const foundPending = (data.pending || []).find((a: any) => a.id === assessmentId);
|
||||
if (foundPending) return foundPending.title;
|
||||
|
||||
// Search tasks
|
||||
const foundTask = (data.tasks || []).find((a: any) => a.id === assessmentId);
|
||||
if (foundTask) return foundTask.title;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const fetchAssessments = async (endpoint: string) => {
|
||||
const res = await fetch(`${location.origin}${endpoint}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
metaclass: metaclassId,
|
||||
programme: programmeId,
|
||||
}),
|
||||
});
|
||||
const json = await res.json();
|
||||
return json.payload;
|
||||
};
|
||||
|
||||
// Try from /past
|
||||
let payload = await fetchAssessments("/seqta/student/assessment/list/past");
|
||||
let title = searchAssessment(payload);
|
||||
if (title) return title;
|
||||
|
||||
// Try from /upcoming if not found in /past
|
||||
const upcomingPayload = await fetchAssessments("/seqta/student/assessment/list/upcoming");
|
||||
const foundUpcoming = (upcomingPayload || []).find((a: any) => a.id === assessmentId);
|
||||
if (foundUpcoming) return foundUpcoming.title;
|
||||
|
||||
throw new Error(`Assessment with ID ${assessmentId} not found in past or upcoming.`);
|
||||
};
|
||||
|
||||
/* ------------- Job ------------- */
|
||||
export const assessmentsJob: Job = {
|
||||
id: "assessments",
|
||||
@@ -79,11 +130,13 @@ export const assessmentsJob: Job = {
|
||||
|
||||
if (notif.type === "coneqtassessments") {
|
||||
const a = notif.coneqtAssessments;
|
||||
|
||||
const content = await fetchAssessmentName(a.assessmentID, a.metaclassID, a.programmeID);
|
||||
items.push({
|
||||
id,
|
||||
text: a.title,
|
||||
category: "assessments",
|
||||
content: a.subtitle,
|
||||
content: content,
|
||||
dateAdded: new Date(notif.timestamp).getTime(),
|
||||
metadata: {
|
||||
assessmentId: a.assessmentID,
|
||||
@@ -96,13 +149,15 @@ export const assessmentsJob: Job = {
|
||||
actionId: "assessment",
|
||||
renderComponentId: "assessment",
|
||||
});
|
||||
} else {
|
||||
} else if (notif.type === "message") {
|
||||
const content = await fetchMessageContent(notif.message.messageID);
|
||||
|
||||
await ctx.addItem(
|
||||
{
|
||||
id,
|
||||
text: notif.message.title,
|
||||
category: "messages",
|
||||
content: `From: ${notif.message.subtitle}`,
|
||||
content: `${htmlToPlainText(content.payload.contents)}\nFrom: ${notif.message.subtitle}`,
|
||||
dateAdded: new Date(notif.timestamp).getTime(),
|
||||
metadata: {
|
||||
messageId: notif.message.messageID,
|
||||
@@ -125,7 +180,7 @@ export const assessmentsJob: Job = {
|
||||
);
|
||||
await ctx.setProgress({ lastTs: latest });
|
||||
}
|
||||
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { Job, IndexItem } from "../types";
|
||||
|
||||
const stripHtmlTags = (html: string) => html.replace(/<[^>]*>/g, "");
|
||||
import { htmlToPlainText } from "../utils";
|
||||
|
||||
const fetchMessages = async (offset = 0, limit = 100) => {
|
||||
const res = await fetch(`${location.origin}/seqta/student/load/message`, {
|
||||
@@ -24,7 +23,7 @@ const fetchMessages = async (offset = 0, limit = 100) => {
|
||||
}>;
|
||||
};
|
||||
|
||||
const fetchMessageContent = async (id: number) => {
|
||||
export const fetchMessageContent = async (id: number) => {
|
||||
const res = await fetch(`${location.origin}/seqta/student/load/message`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
@@ -96,7 +95,7 @@ export const messagesJob: Job = {
|
||||
id,
|
||||
text: msg.subject,
|
||||
category: "messages",
|
||||
content: `From: ${msg.sender}\n\n${stripHtmlTags(full.payload.contents)}`,
|
||||
content: `${htmlToPlainText(full.payload.contents)}\nFrom: ${msg.sender}`,
|
||||
dateAdded: new Date(msg.date).getTime(),
|
||||
metadata: {
|
||||
messageId: msg.id,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
export function htmlToPlainText(rawHtml: string): string {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(rawHtml, 'text/html');
|
||||
const { body } = doc;
|
||||
|
||||
body.querySelectorAll('script,style,template,noscript,meta,link').forEach(el => el.remove());
|
||||
|
||||
body.querySelectorAll('.forward').forEach(el => {
|
||||
let n: ChildNode | null = el;
|
||||
while (n) {
|
||||
const next = n.nextSibling as ChildNode | null;
|
||||
n.remove();
|
||||
n = next;
|
||||
}
|
||||
});
|
||||
|
||||
let text = body.innerText || '';
|
||||
|
||||
text = text
|
||||
.replace(/\u00A0/g, ' ')
|
||||
.replace(/[ \t]{2,}/g, ' ')
|
||||
.replace(/\r\n|\r/g, '\n')
|
||||
.replace(/\n{3,}/g, '\n\n')
|
||||
.replace(/^[.\w#][^{]{0,100}\{[^}]*\}$/gm, '')
|
||||
.split('\n')
|
||||
.map(line => line.trimEnd())
|
||||
.filter(line => line.trim().length > 0 || line === '')
|
||||
.join('\n')
|
||||
.trim();
|
||||
|
||||
return text;
|
||||
}
|
||||
Reference in New Issue
Block a user