(function (root, factory) { if (typeof module === 'object' && module.exports) { module.exports = factory(); } else { root.ISMIStools = Object.assign(root.ISMIStools || {}, factory()); } })(typeof self !== 'undefined' ? self : this, function () { 'use strict'; // RFC-4180 field encoding: quote fields containing commas, quotes, or // newlines; double up embedded quotes. function csvField(value) { let s = String(value == null ? '' : value); if (/[",\r\n]/.test(s)) { s = '"' + s.replace(/"/g, '""') + '"'; } return s; } function csvRow(cells) { return cells.map(csvField).join(',') + '\r\n'; } // Build a CSV representation of the study load. // data: { header, courses, totalUnits } // header fields: program, studentName, studentId, semester, yearLevel function buildStudyLoadCsv(data) { const header = data.header || {}; const lines = []; lines.push(csvRow(['ISMIS HELPER - STUDY LOAD'])); lines.push(csvRow(['Program', header.program || ''])); lines.push(csvRow(['Student Name', header.fullName || header.studentName || ''])); lines.push(csvRow(['Student ID', header.studentId || ''])); lines.push(csvRow(['Semester', header.semester || ''])); lines.push(csvRow(['Year Level', header.yearLevel || ''])); lines.push(csvRow(['Total Units', data.totalUnits || ''])); lines.push(''); // blank separator line lines.push(csvRow(['Group', 'Course Code', 'Description', 'Schedule', 'Faculty', 'Units', 'Status'])); for (const c of (data.courses || [])) { lines.push(csvRow([ c.group, c.courseCode, c.description, c.schedule, c.faculty || c.teacher, c.units, c.status ])); } lines.push(csvRow(['Total', '', '', '', '', data.totalUnits || ''])); lines.push(''); // final blank line, per RFC // UTF-8 BOM so Excel opens it without mojibake. return '\uFEFF' + lines.join(''); } // Build a Next Up import CSV following the exact column contract in // raycast/nextup/src/lib/csv-utils.ts: // title, courseCode, days, startTime, endTime, room, meetingLink, // color, icon, units, classLink, professorName, professorEmail, groupName // One row per schedule block (a course may have multiple meeting segments). function buildNextUpCsv(data, parseSchedule) { const lines = []; lines.push(csvRow(['title', 'courseCode', 'days', 'startTime', 'endTime', 'room', 'meetingLink', 'color', 'icon', 'units', 'classLink', 'professorName', 'professorEmail', 'groupName'])); const resolver = parseSchedule || parseScheduleBlocks; const groupName = (data.header && (data.header.semester || '')) || ''; for (const c of (data.courses || [])) { const blocks = resolver(c.schedule); if (!blocks.length) { lines.push(csvRow([c.description || c.courseCode, c.courseCode, '', '', '', '', '', '', '', c.units, '', c.faculty || '', '', groupName])); continue; } for (const b of blocks) { lines.push(csvRow([ c.description || c.courseCode, c.courseCode, b.days, b.startTime, b.endTime, b.room || '', '', '', '', c.units, '', c.faculty || '', '', groupName ])); } } return '\uFEFF' + lines.join(''); } // Split a study-load schedule string like // "F 01:30 PM - 04:30 PM LB486TC TC; T Th 03:00 PM - 04:30 PM LB468" // into [{ days: 'MO,TU', startTime: '01:30', endTime: '16:30', room }] // Day codes map to the 3-letter codes Next Up expects (MO,TU,WE,TH,FR). function parseScheduleBlocks(schedule) { const s = String(schedule || '').trim(); if (!s) return []; const blocks = []; const segRe = /([A-Za-z]{1,3}(?:\s+[A-Za-z]{1,3})*)\s+(\d{1,2}):(\d{2})\s*(AM|PM)\s*-\s*(\d{1,2}):(\d{2})\s*(AM|PM)\s*([A-Za-z0-9]*)/gi; const dayMap = { M: 'MO', MO: 'MO', MON: 'MO', T: 'TU', TU: 'TU', TUE: 'TU', W: 'WE', WE: 'WE', WED: 'WE', TH: 'TH', THU: 'TH', THUR: 'TH', THURS: 'TH', F: 'FR', FR: 'FR', FRI: 'FR', SA: 'SA', SAT: 'SA', S: 'SU', SU: 'SU', SUN: 'SU', SUNDAY: 'SU' }; let m; while ((m = segRe.exec(s)) !== null) { const dayToCode = (d) => dayMap[d.toUpperCase().replace(/\./g, '')] || ''; const days = m[1].split(/\s+/).map(dayToCode).filter(Boolean); const to24 = (h, ampm, minute) => { let hour = parseInt(h, 10) % 12; if (/PM/i.test(ampm)) hour += 12; return String(hour).padStart(2, '0') + ':' + minute; }; blocks.push({ days: days.join(','), startTime: to24(m[2], m[4], m[3]), endTime: to24(m[5], m[7], m[6]), room: m[8] || '' }); } return blocks; } return { buildStudyLoadCsv, buildNextUpCsv, parseScheduleBlocks, csvField }; });