201 lines
5.4 KiB
JavaScript
201 lines
5.4 KiB
JavaScript
// this one written by claude, it is shit so dont blame me for any misaccuracy LOL
|
|
|
|
const { readFile } = require("node:fs/promises")
|
|
const FILES = [
|
|
"01.json",
|
|
"06.json",
|
|
"10.json"
|
|
]
|
|
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)
|
|
{
|
|
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(".", ":")
|
|
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 minutesToTime(minutes)
|
|
{
|
|
const hours = Math.floor(minutes / 60)
|
|
const mins = minutes % 60
|
|
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`
|
|
}
|
|
|
|
function buildSchedule(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 buildEmptySchedule(schedule)
|
|
{
|
|
const emptySchedule = new Map()
|
|
const dayStartMin = timeToMinutes(OPEN_TIME)
|
|
const dayEndMin = timeToMinutes(CLOSE_TIME)
|
|
|
|
for (const [room, roomSchedule] of schedule)
|
|
{
|
|
emptySchedule.set(room, new Map())
|
|
|
|
// Process all days
|
|
for (const day of Object.keys(DAY_ORDER))
|
|
{
|
|
const classes = roomSchedule.get(day) || []
|
|
|
|
if (classes.length === 0)
|
|
{
|
|
// No classes = room won't be opened that day
|
|
continue
|
|
}
|
|
|
|
// Sort classes by start time
|
|
const sorted = classes.sort((a, b) => timeToMinutes(a.start) - timeToMinutes(b.start))
|
|
const emptySlots = []
|
|
|
|
let currentTime = dayStartMin
|
|
|
|
for (const classInfo of sorted)
|
|
{
|
|
const classStart = timeToMinutes(classInfo.start)
|
|
const classEnd = timeToMinutes(classInfo.end)
|
|
|
|
// Gap before this class
|
|
if (currentTime < classStart)
|
|
{
|
|
emptySlots.push({
|
|
start: minutesToTime(currentTime),
|
|
end: classInfo.start
|
|
})
|
|
}
|
|
|
|
// Move current time to after this class (or keep it if overlapping)
|
|
currentTime = Math.max(currentTime, classEnd)
|
|
}
|
|
|
|
// Gap after last class
|
|
if (currentTime < dayEndMin)
|
|
{
|
|
emptySlots.push({
|
|
start: minutesToTime(currentTime),
|
|
end: CLOSE_TIME
|
|
})
|
|
}
|
|
|
|
// Only add day if there are empty slots
|
|
if (emptySlots.length > 0)
|
|
{
|
|
emptySchedule.get(room).set(day, emptySlots)
|
|
}
|
|
}
|
|
}
|
|
|
|
return emptySchedule
|
|
}
|
|
|
|
function formatEmptySchedule(emptySchedule)
|
|
{
|
|
const lines = []
|
|
const rooms = [...emptySchedule.keys()].sort((a, b) => a.localeCompare(b))
|
|
|
|
for (const room of rooms)
|
|
{
|
|
const roomSchedule = emptySchedule.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 emptySlots = roomSchedule.get(day)
|
|
lines.push(` - **${day}:**`)
|
|
|
|
for (const slot of emptySlots)
|
|
{
|
|
lines.push(` - \`${slot.start} - ${slot.end}\` (KOSONG)`)
|
|
}
|
|
}
|
|
lines.push("")
|
|
}
|
|
|
|
return lines.join("\n")
|
|
}
|
|
|
|
/////////////////
|
|
async function main()
|
|
{
|
|
const cl = await loadFiles()
|
|
const sc = buildSchedule(cl)
|
|
const emptySchedule = buildEmptySchedule(sc)
|
|
console.log(formatEmptySchedule(emptySchedule))
|
|
}
|
|
|
|
main().catch(error =>
|
|
{
|
|
console.error(error)
|
|
process.exitCode = 1
|
|
})
|