feat: supporting improved assessments and improved parsing

This commit is contained in:
SethBurkart123
2025-05-05 17:58:40 +10:00
parent ec42f1bb27
commit 771169348f
3 changed files with 94 additions and 8 deletions
@@ -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,
@@ -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;
}