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 type { Job, IndexItem } from "../types";
|
||||||
|
import { htmlToPlainText } from "../utils";
|
||||||
|
import { fetchMessageContent } from "./messages";
|
||||||
|
|
||||||
/* ------------- Notification types ------------- */
|
/* ------------- Notification types ------------- */
|
||||||
interface MessageNotification {
|
interface MessageNotification {
|
||||||
@@ -44,6 +46,55 @@ const fetchNotifications = async () => {
|
|||||||
return (json.notifications ?? []) as Notification[];
|
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 ------------- */
|
/* ------------- Job ------------- */
|
||||||
export const assessmentsJob: Job = {
|
export const assessmentsJob: Job = {
|
||||||
id: "assessments",
|
id: "assessments",
|
||||||
@@ -79,11 +130,13 @@ export const assessmentsJob: Job = {
|
|||||||
|
|
||||||
if (notif.type === "coneqtassessments") {
|
if (notif.type === "coneqtassessments") {
|
||||||
const a = notif.coneqtAssessments;
|
const a = notif.coneqtAssessments;
|
||||||
|
|
||||||
|
const content = await fetchAssessmentName(a.assessmentID, a.metaclassID, a.programmeID);
|
||||||
items.push({
|
items.push({
|
||||||
id,
|
id,
|
||||||
text: a.title,
|
text: a.title,
|
||||||
category: "assessments",
|
category: "assessments",
|
||||||
content: a.subtitle,
|
content: content,
|
||||||
dateAdded: new Date(notif.timestamp).getTime(),
|
dateAdded: new Date(notif.timestamp).getTime(),
|
||||||
metadata: {
|
metadata: {
|
||||||
assessmentId: a.assessmentID,
|
assessmentId: a.assessmentID,
|
||||||
@@ -96,13 +149,15 @@ export const assessmentsJob: Job = {
|
|||||||
actionId: "assessment",
|
actionId: "assessment",
|
||||||
renderComponentId: "assessment",
|
renderComponentId: "assessment",
|
||||||
});
|
});
|
||||||
} else {
|
} else if (notif.type === "message") {
|
||||||
|
const content = await fetchMessageContent(notif.message.messageID);
|
||||||
|
|
||||||
await ctx.addItem(
|
await ctx.addItem(
|
||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
text: notif.message.title,
|
text: notif.message.title,
|
||||||
category: "messages",
|
category: "messages",
|
||||||
content: `From: ${notif.message.subtitle}`,
|
content: `${htmlToPlainText(content.payload.contents)}\nFrom: ${notif.message.subtitle}`,
|
||||||
dateAdded: new Date(notif.timestamp).getTime(),
|
dateAdded: new Date(notif.timestamp).getTime(),
|
||||||
metadata: {
|
metadata: {
|
||||||
messageId: notif.message.messageID,
|
messageId: notif.message.messageID,
|
||||||
@@ -125,7 +180,7 @@ export const assessmentsJob: Job = {
|
|||||||
);
|
);
|
||||||
await ctx.setProgress({ lastTs: latest });
|
await ctx.setProgress({ lastTs: latest });
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { Job, IndexItem } from "../types";
|
import type { Job, IndexItem } from "../types";
|
||||||
|
import { htmlToPlainText } from "../utils";
|
||||||
const stripHtmlTags = (html: string) => html.replace(/<[^>]*>/g, "");
|
|
||||||
|
|
||||||
const fetchMessages = async (offset = 0, limit = 100) => {
|
const fetchMessages = async (offset = 0, limit = 100) => {
|
||||||
const res = await fetch(`${location.origin}/seqta/student/load/message`, {
|
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`, {
|
const res = await fetch(`${location.origin}/seqta/student/load/message`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
@@ -96,7 +95,7 @@ export const messagesJob: Job = {
|
|||||||
id,
|
id,
|
||||||
text: msg.subject,
|
text: msg.subject,
|
||||||
category: "messages",
|
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(),
|
dateAdded: new Date(msg.date).getTime(),
|
||||||
metadata: {
|
metadata: {
|
||||||
messageId: msg.id,
|
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