Skip to content

Commit dce33ca

Browse files
committed
fix(crn): robustly parse university CRN table rows
1 parent 5ff1090 commit dce33ca

1 file changed

Lines changed: 38 additions & 13 deletions

File tree

src/utils/crnImporter.js

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ export function parseCrnList(text) {
1010
if (!text || typeof text !== 'string') return [];
1111

1212
const entries = [];
13+
const normalize = (value) => String(value || '')
14+
.replace(/\u00a0/g, ' ')
15+
.replace(/\s+/g, ' ')
16+
.trim();
1317

1418
for (const rawLine of text.split(/\r?\n/)) {
15-
const line = rawLine.trim();
19+
const line = normalize(rawLine);
1620
if (!line) continue;
1721

1822
// Standard course format: CODE-NUMBER (section/crn) - name
@@ -21,37 +25,58 @@ export function parseCrnList(text) {
2125
);
2226

2327
if (standard) {
24-
entries.push({
25-
courseCode: standard[1].toUpperCase(),
26-
courseNumber: standard[2],
27-
crn: standard[3].trim().toUpperCase(),
28-
courseName: standard[4].trim(),
29-
source: line
30-
});
28+
const crn = normalize(standard[3]).toUpperCase();
29+
if (crn) {
30+
entries.push({
31+
courseCode: standard[1].toUpperCase(),
32+
courseNumber: standard[2],
33+
crn,
34+
courseName: normalize(standard[4]),
35+
source: rawLine.trim()
36+
});
37+
}
3138
continue;
3239
}
3340

34-
// Tabular university copy/paste format:
35-
// No. | Course name | Code | Number | Section/CRN | Units
36-
const cells = line.split('\t').map(cell => cell.trim());
41+
// University table copy/paste:
42+
// No. | Course name | Code | Number | Section/CRN | Units | ...
43+
const cells = rawLine.split('\t').map(cell => normalize(cell));
3744
if (cells.length >= 5) {
3845
const start = /^\d+$/.test(cells[0]) ? 1 : 0;
3946
const courseName = cells[start] || '';
4047
const courseCode = cells[start + 1] || '';
4148
const courseNumber = cells[start + 2] || '';
4249
const crn = cells[start + 3] || '';
4350

44-
if (/^[A-Za-z][A-Za-z0-9]*$/.test(courseCode) && /^\d+$/.test(courseNumber) && crn) {
51+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(courseCode)
52+
&& /^\d+$/.test(courseNumber)
53+
&& /^[A-Za-z0-9]+$/.test(crn)) {
4554
entries.push({
4655
courseCode: courseCode.toUpperCase(),
4756
courseNumber,
4857
crn: crn.toUpperCase(),
4958
courseName,
50-
source: line
59+
source: rawLine.trim()
5160
});
5261
continue;
5362
}
5463
}
64+
65+
// Same table copied with spaces instead of tabs. Course name may contain spaces,
66+
// so locate the code/number/CRN triplet rather than splitting the name blindly.
67+
const spaced = line.match(
68+
/^(?:\d+\s+)?(.+?)\s+([A-Za-z][A-Za-z0-9]*)\s+(\d+)\s+([A-Za-z0-9]+)(?:\s+\d+)?$/
69+
);
70+
71+
if (spaced) {
72+
entries.push({
73+
courseCode: spaced[2].toUpperCase(),
74+
courseNumber: spaced[3],
75+
crn: spaced[4].toUpperCase(),
76+
courseName: normalize(spaced[1]),
77+
source: rawLine.trim()
78+
});
79+
}
5580
}
5681

5782
return entries;

0 commit comments

Comments
 (0)