const { readFile } = require("node:fs/promises") const FILES = [ "01.json", "06.json", "10.json" ] // just some asumption, usually if there's a class in a certain day, the room is open for the entire day const OPEN_TIME = "08:00" const CLOSE_TIME = "17:00" const DAY_ORDER = { "Senin": 1, "Selasa": 2, "Rabu": 3, "Kamis": 4, "Jumat": 5, "Sabtu": 6, "Minggu": 7 } async function loadFiles() { const results = await Promise.all(FILES.map(async file => { const content = await readFile(file, "utf8") const json = JSON.parse(content) return json.data })) return results.flat() } function parseDateRoom(value) { /* * Senin, 10:00-11:40 (A1.51 (Ged Baru)) * Selasa, 10.00-11.50 (A6.12 (Ged Baru)) * * some entries is malformed (top 1 uni btw) * so why even bother to match the closing ")" lolol */ const match = value.match(/^([^,]+),\s*(\d{2}[:.]\d{2})-(\d{2}[:.]\d{2})\s+\((.*)$/) if (!match) return null const [ , day, rawStart, rawEnd, rawRoom] = match const start = rawStart.replace(".", ":") const end = rawEnd.replace(".", ":") /* * Remove the outer ')' from normal entries. * * "(A1.51 (Ged Baru))" * ^ remove only the final ')' * * For malformed entries: * "(Lab A1.04+Lab A3.02 - Gabungan (Gd. Baru)" * the final ')' is also removed. */ const room = rawRoom.endsWith(")") ? rawRoom.slice(0, -1) : rawRoom return { day, start, end, room } } function timeToMinutes(time) { const [hours, minutes] = time.split(":").map(Number) return hours * 60 + minutes } function buildSchedule(classes) { /* * Map * room * -> day * -> classes */ const schedule = new Map() for (const classInfo of classes) { if (!Array.isArray(classInfo.dates_rooms)) { console.warn(`Skipping ${classInfo.classname}: invalid dates_rooms`) continue } for (const dateRoom of classInfo.dates_rooms) { const parsed = parseDateRoom(dateRoom) if (!parsed) { console.warn(`Could not parse: ${dateRoom}`) continue } const { day, start, end, room } = parsed if (!schedule.has(room)) { schedule.set(room, new Map()) } const roomSchedule = schedule.get(room) if (!roomSchedule.has(day)) { roomSchedule.set(day, []) } roomSchedule.get(day).push({ start, end, className: classInfo.classname }) } } return schedule } function formatSchedule(schedule) { const lines = [] const rooms = [...schedule.keys()].sort((a, b) => a.localeCompare(b)) for (const room of rooms) { const roomSchedule = schedule.get(room) lines.push(`## ${room} \`${OPEN_TIME} - ${CLOSE_TIME}\``) const days = [...roomSchedule.keys()].sort((a, b) => DAY_ORDER[a] - DAY_ORDER[b]) for (const day of days) { const classes = roomSchedule.get(day).sort((a, b) => timeToMinutes(a.start) - timeToMinutes(b.start)) lines.push(` - **${day}:**`) for (const classInfo of classes) { lines.push(` - \`${classInfo.start} - ${classInfo.end}\`: **${classInfo.className}**`) } } lines.push("\n") } return lines.join("\n") } ///////////////// async function main() { const cl = await loadFiles() const sc = buildSchedule(cl) console.log(formatSchedule(sc)) } main().catch(error => { console.error(error) process.exitCode = 1 })