diff --git a/bun.lock b/bun.lock index 428f687..629b17e 100755 --- a/bun.lock +++ b/bun.lock @@ -3,6 +3,7 @@ "workspaces": { "": { "dependencies": { + "buffer": "^6.0.3", "fast-xml-parser": "^4.5.1", "file-type": "^19.6.0", }, @@ -306,7 +307,7 @@ "browserslist": ["browserslist@4.24.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA=="], - "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], "bun-types": ["bun-types@1.1.37", "", { "dependencies": { "@types/node": "~20.12.8", "@types/ws": "~8.5.10" } }, "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA=="], @@ -864,6 +865,8 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], diff --git a/package.json b/package.json index 06abc7e..3400412 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "vite": "^6.0.5" }, "dependencies": { + "buffer": "^6.0.3", "fast-xml-parser": "^4.5.1", "file-type": "^19.6.0" }, diff --git a/src/lib/cache.ts b/src/lib/cache.ts index c0608bc..d3a8aa8 100644 --- a/src/lib/cache.ts +++ b/src/lib/cache.ts @@ -1,16 +1,12 @@ import { attendance, attendanceLoaded, - documentsList, - documentsListLoaded, + documents, + documentsLoaded, gradebook, gradebookLoaded, - mail, - mailLoaded, - messages, - messagesLoaded, - reportCardList, - reportCardListLoaded, + mailData, + mailDataLoaded, studentAccount, studentInfo, studentInfoLoaded @@ -46,7 +42,7 @@ export const loadGradebook = async () => { periodOverrideState.new = restoredState.new; periodOverrideState.original = restoredState.original; - const grades = await get(studentAccount)?.grades(periodOverrideState.new?.index); + const grades = await get(studentAccount)?.gradebook(periodOverrideState.new?.index); gradebook.set(grades); @@ -55,7 +51,7 @@ export const loadGradebook = async () => { return; } - const grades = await get(studentAccount)?.grades(); + const grades = await get(studentAccount)?.gradebook(); gradebook.set(grades); localStorage.setItem('gradebook', JSON.stringify(grades)); @@ -89,54 +85,28 @@ export const loadStudentInfo = async () => { studentInfoLoaded.set(true); }; -export const loadReportCardList = async () => { - reportCardListLoaded.set(false); +export const loadDocuments = async () => { + documentsLoaded.set(false); - writeCacheToStore('reportCardList', reportCardList); + writeCacheToStore('documents', documents); - const reportCardListRecord = await get(studentAccount)?.reportCardList(); + const documentsRecord = await get(studentAccount)?.documents(); - reportCardList.set(reportCardListRecord); - localStorage.setItem('reportCardList', JSON.stringify(reportCardListRecord)); + documents.set(documentsRecord); + localStorage.setItem('documents', JSON.stringify(documentsRecord)); - reportCardListLoaded.set(true); + documentsLoaded.set(true); }; -export const loadDocumentsList = async () => { - documentsListLoaded.set(false); +export const loadMailData = async () => { + mailDataLoaded.set(false); - writeCacheToStore('documentsList', documentsList); + writeCacheToStore('mailData', mailData); - const documentsListRecord = await get(studentAccount)?.documentsList(); + const mailDataRecord = await get(studentAccount)?.mailData(); - documentsList.set(documentsListRecord); - localStorage.setItem('documentsList', JSON.stringify(documentsListRecord)); + mailData.set(mailDataRecord); + localStorage.setItem('mailData', JSON.stringify(mailDataRecord)); - documentsListLoaded.set(true); -}; - -export const loadMessages = async () => { - messagesLoaded.set(false); - - writeCacheToStore('messages', messages); - - const messagesRecord = await get(studentAccount)?.messages(); - - messages.set(messagesRecord); - localStorage.setItem('messages', JSON.stringify(messagesRecord)); - - messagesLoaded.set(true); -}; - -export const loadMail = async () => { - mailLoaded.set(false); - - writeCacheToStore('mail', mail); - - const mailRecord = await get(studentAccount)?.mail(); - - mail.set(mailRecord); - localStorage.setItem('mail', JSON.stringify(mailRecord)); - - mailLoaded.set(true); + mailDataLoaded.set(true); }; diff --git a/src/lib/index.ts b/src/lib/index.ts index bb1d4ca..952dc07 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,3 +1,6 @@ +import { fileTypeFromBuffer } from 'file-type'; +import { Buffer } from 'buffer'; + export function getColorForGrade(grade: string | number) { if (typeof grade == 'number') { if (grade > 100) return 'blue'; @@ -47,3 +50,15 @@ export const shortDateFormatter = new Intl.DateTimeFormat('en-US', { export const fullDateFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'full' }); + +export async function getBlobURLFromBase64String(base64: string) { + const byteArray = new Uint8Array(Buffer.from(base64, 'base64')); + + const mimeType = (await fileTypeFromBuffer(byteArray))?.mime; + + if (!mimeType) throw new Error('Could not determine MIME type'); + + const blob = new Blob([byteArray], { type: mimeType }); + + return URL.createObjectURL(blob); +} diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 2fa7b29..cd3ad00 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -1,14 +1,11 @@ -import type { Writable } from 'svelte/store'; -import { writable } from 'svelte/store'; - import type { StudentAccount } from '$lib/synergy'; import type { Attendance } from '$lib/types/Attendance'; -import type { DocumentsList } from '$lib/types/DocumentsList'; +import type { Documents } from '$lib/types/Documents'; import type { Gradebook } from '$lib/types/Gradebook'; -import type { ReportCardListEntity } from '$lib/types/ReportCardListEntity'; -import type { StudentInfo } from '$lib/types/StudentInfo'; import type { MailData } from '$lib/types/MailData'; -import type { Message } from '$lib/types/Message'; +import type { StudentInfo } from '$lib/types/StudentInfo'; +import type { Writable } from 'svelte/store'; +import { writable } from 'svelte/store'; export const studentAccount: Writable = writable(); @@ -24,18 +21,10 @@ export const studentInfo: Writable = writable(); export const studentInfoLoaded = writable(false); -export const reportCardList: Writable = writable(); - -export const reportCardListLoaded = writable(false); - -export const documentsList: Writable = writable(); - -export const documentsListLoaded = writable(false); - -export const messages: Writable = writable(); +export const documents: Writable = writable(); -export const messagesLoaded = writable(false); +export const documentsLoaded = writable(false); -export const mail: Writable = writable(); +export const mailData: Writable = writable(); -export const mailLoaded = writable(false); +export const mailDataLoaded = writable(false); diff --git a/src/lib/synergy.ts b/src/lib/synergy.ts index c694926..8c933fa 100644 --- a/src/lib/synergy.ts +++ b/src/lib/synergy.ts @@ -1,12 +1,10 @@ import type { Attachment } from '$lib/types/Attachment'; import type { Attendance } from '$lib/types/Attendance'; import type { AuthToken } from '$lib/types/AuthToken'; -import type { DocumentsList } from '$lib/types/DocumentsList'; +import type { Documents } from '$lib/types/Documents'; import type { Gradebook } from '$lib/types/Gradebook'; import type { MailData } from '$lib/types/MailData'; -import type { Message } from '$lib/types/Message'; -import type { ReportCardDocument } from '$lib/types/ReportCardDocument'; -import type { ReportCardListEntity } from '$lib/types/ReportCardListEntity'; +import type { ReportCard, ReportCardNotFound } from '$lib/types/ReportCard'; import type { StudentInfo } from '$lib/types/StudentInfo'; import { XMLBuilder, XMLParser } from 'fast-xml-parser'; @@ -101,7 +99,7 @@ export class StudentAccount { ).AuthToken; } - async grades(reportPeriod?: number): Promise { + async gradebook(reportPeriod?: number): Promise { if (reportPeriod) { // May return current reporting period instead of the one requested if the one requested cannot be found @@ -119,29 +117,20 @@ export class StudentAccount { return (await this.request('StudentInfo')).StudentInfo; } - async reportCardList(): Promise { - return (await this.request('GetReportCardInitialData')).RCReportingPeriodData.RCReportingPeriods - .RCReportingPeriod; + async documents(): Promise { + return (await this.request('GetStudentDocumentInitialData')).StudentDocuments; } - async reportCard(documentGU: string): Promise { + async reportCard(documentGU: string): Promise { return (await this.request('GetReportCardDocumentData', { DocumentGU: documentGU })) .DocumentData; } - async documentsList(): Promise { - return (await this.request('GetStudentDocumentInitialData')).StudentDocuments; - } - - async messages(): Promise { - return (await this.request('GetPXPMessages')).PXPMessagesData.MessageListings.MessageListing; - } - - async mail(): Promise { + async mailData(): Promise { return (await this.request('SynergyMailGetData')).SynergyMailDataXML; } - async attachmentBase64(attachmentGU: string): Promise { + async attachment(attachmentGU: string): Promise { return (await this.request('SynergyMailGetAttachment', { SmAttachmentGU: attachmentGU })) .AttachmentXML; } diff --git a/src/lib/types/Attendance.ts b/src/lib/types/Attendance.ts index 4d75fae..1022cac 100644 --- a/src/lib/types/Attendance.ts +++ b/src/lib/types/Attendance.ts @@ -1,10 +1,10 @@ export interface Attendance { Absences: Absences; - TotalExcused: TotalAttendanceEvents; - TotalTardies: TotalAttendanceEvents; - TotalUnexcused: TotalAttendanceEvents; - TotalActivities: TotalAttendanceEvents; - TotalUnexcusedTardies: TotalAttendanceEvents; + TotalExcused: AttendanceEventTotal; + TotalTardies: AttendanceEventTotal; + TotalUnexcused: AttendanceEventTotal; + TotalActivities: AttendanceEventTotal; + TotalUnexcusedTardies: AttendanceEventTotal; ConcurrentSchoolsLists: string; '_xmlns:xsd': string; '_xmlns:xsi': string; @@ -14,10 +14,12 @@ export interface Attendance { _PeriodCount: string; _SchoolName: string; } + export interface Absences { - Absence?: AbsenceEntity[] | null; + Absence?: Absence[] | null; } -export interface AbsenceEntity { + +export interface Absence { Periods: Periods; _AbsenceDate: string; _Reason: string; @@ -26,10 +28,12 @@ export interface AbsenceEntity { _CodeAllDayReasonType: string; _CodeAllDayDescription: string; } + export interface Periods { - Period?: PeriodEntity[] | null; + Period?: Period[] | null; } -export interface PeriodEntity { + +export interface Period { _Number: string; _Name: string; _Reason: string; @@ -41,10 +45,12 @@ export interface PeriodEntity { _StaffGU: string; _OrgYearGU: string; } -export interface TotalAttendanceEvents { - PeriodTotal?: PeriodTotalEntity[] | null; + +export interface AttendanceEventTotal { + PeriodTotal?: PeriodTotal[] | null; } -export interface PeriodTotalEntity { + +export interface PeriodTotal { _Number: string; _Total: string; } diff --git a/src/lib/types/DocumentsList.ts b/src/lib/types/Documents.ts similarity index 74% rename from src/lib/types/DocumentsList.ts rename to src/lib/types/Documents.ts index 521d752..eeb9b98 100644 --- a/src/lib/types/DocumentsList.ts +++ b/src/lib/types/Documents.ts @@ -1,4 +1,4 @@ -export interface DocumentsList { +export interface Documents { StudentDocumentDatas: StudentDocumentDatas; '_xmlns:xsd': string; '_xmlns:xsi': string; @@ -8,10 +8,12 @@ export interface DocumentsList { _StudentGU: string; _StudentSSY: string; } + export interface StudentDocumentDatas { - StudentDocumentData?: StudentDocumentDataEntity[] | null; + StudentDocumentData?: StudentDocumentData[] | null; } -export interface StudentDocumentDataEntity { + +export interface StudentDocumentData { _DocumentGU: string; _DocumentFileName: string; _DocumentDate: string; diff --git a/src/lib/types/Message.ts b/src/lib/types/Message.ts deleted file mode 100644 index 187d371..0000000 --- a/src/lib/types/Message.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface Message { - AttachmentDatas: string; - _IconURL: string; - _ID: string; - _BeginDate: string; - _Type: string; - _Subject: string; - _Read: string; - _Deletable: string; - _SubjectNoHTML: string; - _Module: string; - _Email: string; - _SMMsgPersonGU: string; -} diff --git a/src/lib/types/ReportCardDocument.ts b/src/lib/types/ReportCard.ts similarity index 60% rename from src/lib/types/ReportCardDocument.ts rename to src/lib/types/ReportCard.ts index 5fb94dc..dca1c3b 100644 --- a/src/lib/types/ReportCardDocument.ts +++ b/src/lib/types/ReportCard.ts @@ -1,7 +1,10 @@ -export interface ReportCardDocument { - Base64Code: string; +export interface ReportCardNotFound { '_xmlns:xsd': string; '_xmlns:xsi': string; +} + +export interface ReportCard extends ReportCardNotFound { + Base64Code: string; _DocumentGU: string; _FileName: string; _DocFileName: string; diff --git a/src/lib/types/ReportCardListEntity.ts b/src/lib/types/ReportCardListEntity.ts deleted file mode 100644 index 44ac027..0000000 --- a/src/lib/types/ReportCardListEntity.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ReportCardListEntity { - _ReportingPeriodGU: string; - _ReportingPeriodName: string; - _EndDate: string; - _Message: string; - _DocumentGU: string; -} diff --git a/src/routes/(authed)/attendance/+page.svelte b/src/routes/(authed)/attendance/+page.svelte index d368c2a..5f7466e 100644 --- a/src/routes/(authed)/attendance/+page.svelte +++ b/src/routes/(authed)/attendance/+page.svelte @@ -4,26 +4,22 @@ import { loadAttendance } from '$lib/cache'; import LoadingBanner from '$lib/components/LoadingBanner.svelte'; import { attendance, attendanceLoaded } from '$lib/stores'; - import type { PeriodEntity } from '$lib/types/Attendance'; + import type { Period } from '$lib/types/Attendance'; import { Accordion, AccordionItem, Badge } from 'flowbite-svelte'; if (!$attendance && browser) loadAttendance(); - function getAbsenceType(periods: PeriodEntity[]) { - const reasons = periods.map((period: PeriodEntity) => period._Name); + const excusedReasonRegex = + /Field Trip|School Pass|Excused|Medical\/Dent|Comp Ed\/Court-Religi|Illness or Sickness|SB14 Wellness\/Illnes/; + + function getAbsenceType(periods: Period[]) { + const reasons = periods.map((period: Period) => period._Name); if (reasons.some((reason) => reason === 'Absent' || reason === 'Non ADA')) return 'Absent'; - if (reasons.some((reason) => reason.match(/Tardy/))) return 'Tardy'; + if (reasons.some((reason) => /Tardy/.test(reason))) return 'Tardy'; - if ( - reasons.some((reason) => - reason.match( - /Field Trip|School Pass|Excused|Medical\/Dent|Comp Ed\/Court-Religi|Illness or Sickness|SB14 Wellness\/Illnes/ - ) - ) - ) - return 'Excused'; + if (reasons.some((reason) => excusedReasonRegex.test(reason))) return 'Excused'; if (reasons.some((reason) => reason === 'Present')) return 'Present'; diff --git a/src/routes/(authed)/documents/+page.svelte b/src/routes/(authed)/documents/+page.svelte index da1c522..714fa5c 100644 --- a/src/routes/(authed)/documents/+page.svelte +++ b/src/routes/(authed)/documents/+page.svelte @@ -1,12 +1,12 @@ - - - Document - GradeVue - - -{#if reportCardPromise} - {#await reportCardPromise} - - {:then reportCard} - - {/await} -{/if} diff --git a/src/routes/(authed)/documents/document/+page.svelte b/src/routes/(authed)/documents/document/+page.svelte new file mode 100644 index 0000000..be7e5f8 --- /dev/null +++ b/src/routes/(authed)/documents/document/+page.svelte @@ -0,0 +1,64 @@ + + + + Document - GradeVue + + +{#if reportCardURLPromise} + {#await reportCardURLPromise} + + {:then} + + {:catch error} +
+ +

{error}

+ + +
+
+ {/await} +{/if} diff --git a/src/routes/(authed)/grades/[index]/+page.svelte b/src/routes/(authed)/grades/[index]/+page.svelte index 3508fc2..d4ed2dd 100644 --- a/src/routes/(authed)/grades/[index]/+page.svelte +++ b/src/routes/(authed)/grades/[index]/+page.svelte @@ -70,8 +70,6 @@ calculateAssignmentGPCs(synergyAssignments.map(parseSynergyAssignment), gradeCategories) ); - $inspect(synergyAssignments); - const hiddenAssignments = $derived( categories ? getHiddenAssignmentsFromCategories( diff --git a/src/routes/(authed)/grades/reportingPeriods.svelte.ts b/src/routes/(authed)/grades/reportingPeriods.svelte.ts index b996459..f8071ae 100644 --- a/src/routes/(authed)/grades/reportingPeriods.svelte.ts +++ b/src/routes/(authed)/grades/reportingPeriods.svelte.ts @@ -29,7 +29,7 @@ export async function changeReportPeriod(newPeriod: Period, index: number) { if (!studentAccount) return; - const newGradebook = await studentAccount.grades(index); + const newGradebook = await studentAccount.gradebook(index); gradebook.set(newGradebook); gradebookLoaded.set(true); diff --git a/src/routes/(authed)/mail/+page.svelte b/src/routes/(authed)/mail/+page.svelte index 0cb12b1..feb9603 100644 --- a/src/routes/(authed)/mail/+page.svelte +++ b/src/routes/(authed)/mail/+page.svelte @@ -1,15 +1,14 @@ Attachment - GradeVue -{#if $studentAccount} - {#if attachmentGU && attachmentPromise} - {#await attachmentPromise} - - {:then { attachment, mimeType }} - - {:catch error} -
- -

{error}

- - -
-
- {/await} - {:else} +{#if attachmentURLPromise} + {#await attachmentURLPromise} + + {:then} + + {:catch error}
-

AttachmentGU Required

+

{error}

- {/if} + {/await} {/if} diff --git a/src/routes/(authed)/studentinfo/+page.svelte b/src/routes/(authed)/studentinfo/+page.svelte index 950c40e..9fe441b 100644 --- a/src/routes/(authed)/studentinfo/+page.svelte +++ b/src/routes/(authed)/studentinfo/+page.svelte @@ -16,7 +16,7 @@ if (!$studentInfo && browser) loadStudentInfo(); - const dataSources = ['gradebook', 'attendance', 'studentInfo', 'documentsList', 'mail']; + const dataSources = ['gradebook', 'attendance', 'studentInfo', 'documents', 'mailData']; function copy(key: string) { const data = localStorage.getItem(key);