Documentation Index
Fetch the complete documentation index at: https://mintlify.com/DavidCevallos15/inforario-IA-null/llms.txt
Use this file to discover all available pages before exploring further.
sguRegexParser.ts is Inforario’s fully local, zero-network schedule parser. It processes the official UTM Sistema de Gestión Universitaria (SGU) schedule PDF (or an image scan) entirely inside the browser using pdfjs-dist for PDF text extraction and Tesseract.js for OCR fallback. The output is a structured ParseResult ready to be rendered in the schedule grid.
The two public exports are parseScheduleFile — the main entry point — and resolveConflicts — a standalone utility used both here and by the Supabase Edge path.
Types
ParseResult is the local return type for parser output. ClassSession is imported from types/sgu.ts — see the Types Reference page for its full field documentation.
Exported functions
parseScheduleFile()
data:*;base64, prefix from base64Data, then routes to the appropriate back-end parser based on mimeType:
application/pdf→parsePDF()(pdfjs-dist positional column parser)image/*→parseImage()(Tesseract.js OCR →parseRawText())- Anything else → throws
"Tipo de archivo no soportado: <mimeType>"
resolveConflicts() is called internally by both paths before the result is returned, so the sessions array in the resolved ParseResult is always conflict-annotated and sorted.
A base64-encoded data URL of the file, e.g.
data:application/pdf;base64,JVBERi0x…. The data:*;base64, prefix is stripped automatically before decoding.MIME type of the uploaded file. Determines which parser is invoked.
| Value | Parser used |
|---|---|
application/pdf | parsePDF() via pdfjs-dist |
image/png, image/jpeg, … | parseImage() via Tesseract.js OCR |
Promise<ParseResult>
"No se pudo" or "Tipo de archivo" is re-thrown verbatim. All other unexpected errors are wrapped in a generic user-friendly message:
resolveConflicts()
ClassSession objects (possibly mixed schedulable and unscheduled) and returns a new sorted array with conflict flags set correctly.
Algorithm
- Split sessions into two buckets:
- Schedulable: sessions that have a
day,startTime, andendTime - Unscheduled: virtual classes or sessions missing any time field
- Schedulable: sessions that have a
- Reset
conflict = falseon all sessions. - Sort schedulable sessions by
day(locale string order) thenstartTimeascending. - O(n²) pairwise scan over same-day sessions. For each pair, convert
HH:mmto total minutes and detect overlap with:If overlapping, setconflict = trueon both sessions. - Return
[...schedulable, ...unscheduled]— schedulable sessions first, unscheduled appended at the end.
Array of class sessions. May contain any mix of schedulable and unscheduled entries. The original objects are mutated in place (the
conflict flag is written directly) but none are removed.resolveConflicts is non-destructive: it never removes sessions. Every session in the input appears exactly once in the output. Only the conflict property is changed.Constants
SUBJECT_COLORS
The ordered palette used to assign deterministic colors to subjects. Colors are assigned round-robin based on the number of distinct subjects already seen, using subject name (uppercased) as the map key.
getSubjectColor(subject, subjectColors) maintains a Map<string, string> across a single parse run. The same subject name always receives the same color within a parse, and the color can be later overridden by the user via the CUSTOMIZE_COLOR feature.
Internal functions
The following functions are not exported. They are documented here as implementation reference.parsePDF(base64)
parsePDF(base64)
Decodes the raw base64 string with
Subject names spanning multiple rows are concatenated in y-order. Items within 15 px vertically are grouped into the same subject. The day and time are extracted from strings matching:
atob() (browser) or Buffer.from() (Node.js) and loads the binary into pdfjs-dist. For every page it:- Calls
page.getTextContent()and retrieves each item’s text content plus itstransform[4](x) andviewport.height − transform[5](y) coordinates. - Passes all
TextItemobjects throughextractMetadata()andextractSubjectBlocks(). - Calls
resolveConflicts()on the resulting sessions.
extractSubjectBlocks():| Constant | Value | Purpose |
|---|---|---|
SUBJECT_MAX_X | 170 | Right boundary of the subject/asignatura column |
DOCENTE_MIN_X | 260 | Left boundary of the teacher name column |
DOCENTE_MAX_X | 415 | Right boundary of the teacher name column |
HORARIO_MIN_X | 495 | Left boundary of the schedule/location column |
parseImage(base64, mimeType)
parseImage(base64, mimeType)
Reconstructs a data URL from the raw base64 and MIME type, then calls The raw OCR string is forwarded to
Tesseract.recognize(imageDataUrl, 'spa') for Spanish OCR. Progress is logged to the console at the recognizing text status.If the recognised text is shorter than 20 characters the function throws:parseRawText(), which applies the same day-time regex on a line-by-line basis. OCR output is inherently noisier than PDF text, so accuracy depends on image resolution and contrast.extractMetadata(items)
extractMetadata(items)
Reads document metadata from the header region of page 1. Only items where
Value discovery: after finding a label item at index
page === 1 and y < 210 are considered.For each header item, the text is compared against these label strings:| Label | Stored as | Post-processed with |
|---|---|---|
PERIODO: | academicPeriod | normalizeAcademicPeriod() |
FACULTAD: | faculty | .toUpperCase() |
ESCUELA: | career | .toUpperCase() |
ESTUDIANTE: | studentName | Raw value |
NIVEL: | level | Raw value |
i, the function scans forward in the same headerItems array and returns the first item that is on the same row (|Δy| ≤ 5 px) and to the right (x > label.x).normalizeTeacherName(rawName)
normalizeTeacherName(rawName)
Converts UTM raw teacher name format into a human-readable “Firstname Lastname” string.UTM format:
Output format:
LASTNAME1 LASTNAME2 FIRSTNAME1 [FIRSTNAME2]Output format:
Firstname1 Lastname1Steps:- Returns
'Sin asignar'for empty strings, names beginning withTEMP, or names containingTEMPORAL. - Strips title prefixes matching:
ing,lic,dr,dra,msc,mgtr,phd,abg,arq,econ,prof,sr,sra,srta(run twice to catch double titles like “DR. ING.”). - Splits by whitespace into
parts:- ≥ 3 parts →
"${capitalize(parts[2])} ${capitalize(parts[0])}"(Firstname Lastname1) - 2 parts →
"${capitalize(parts[1])} ${capitalize(parts[0])}"(reversed) - 1 part → capitalized as-is
- ≥ 3 parts →
normalizeLocation(codAmb, tipo?)
normalizeLocation(codAmb, tipo?)
Parses UTM classroom codes of the form
Output rules:
1-59-FLOOR-ROOM-TYPE into human-readable location strings.Regex: /\d+-\d+-(\d+)-(\d+)-?([\w]*)/| Code segment | Meaning |
|---|---|
| Group 1 | Floor number |
| Group 2 | Room number (zero-padded to 2 digits) |
| Group 3 | Type code (e.g. LC for Lab. Computación, A for Aula) |
- Type code
LCortipocontaining"laboratorio"→"Lab. Computación {floor}{room} - Piso {floor}" - Default →
"Aula {floor}{room} - Piso {floor}"
tipo.Examples:| Input | Output |
|---|---|
1-59-2-04-A | Aula 204 - Piso 2 |
1-59-3-06-LC | Lab. Computación 306 - Piso 3 |
VIRTUAL | VIRTUAL (no match → raw value) |
normalizeAcademicPeriod(raw)
normalizeAcademicPeriod(raw)
Converts verbose SGU period strings into a compact form.Regex:
If no match is found, the raw string is returned uppercased.
/([A-ZÁÉÍÓÚÜÑa-záéíóúüñ]+)\s+(?:DE(?:L)?)\s+(\d{4})\s+HASTA\s+([A-ZÁÉÍÓÚÜÑa-záéíóúüñ]+)\s+(?:DE(?:L)?)\s+(\d{4})/i| Input | Output |
|---|---|
ABRIL DE 2026 HASTA AGOSTO DE 2026 | ABRIL 2026 - AGOSTO 2026 |
SEPTIEMBRE DEL 2025 HASTA ENERO DEL 2026 | SEPTIEMBRE 2025 - ENERO 2026 |