forked from phc/orario
Compare commits
11 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
125b7d925e | 4 months ago |
|
|
0784a3512d | 4 months ago |
|
|
c761faa787 | 8 months ago |
|
|
97e3758bc1 | 9 months ago |
|
|
48445020c8 | 9 months ago |
|
|
880061e54d | 9 months ago |
|
|
0c6f60e461 | 9 months ago |
|
|
992d00a9a9 | 9 months ago |
|
|
e6c5db05a6 | 9 months ago |
|
|
b21cc30c8a | 9 months ago |
|
|
d0c2f58094 | 9 months ago |
@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
// Manual update script for semester timetable data
|
||||||
|
// Run this script manually each semester to update timetable IDs and dates
|
||||||
|
// Usage: node src/scripts/update-semester-data.js
|
||||||
|
|
||||||
|
import https from 'https';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
// Get current academic year
|
||||||
|
function getCurrentAcademicYear() {
|
||||||
|
const now = new Date();
|
||||||
|
const currentYear = now.getFullYear();
|
||||||
|
const currentMonth = now.getMonth(); // 0-based
|
||||||
|
|
||||||
|
// Academic year starts in September, but August belongs to the new year
|
||||||
|
const startYear = currentMonth >= 7 ? currentYear : currentYear - 1;
|
||||||
|
const endYear = startYear + 1;
|
||||||
|
|
||||||
|
return { startYear, endYear };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch webpage content
|
||||||
|
function fetchPage(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
https.get(url, (res) => {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
}).on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse timetable IDs from the schedule page
|
||||||
|
async function parseTimetableIds() {
|
||||||
|
try {
|
||||||
|
const html = await fetchPage('https://www.dm.unipi.it/didattica/lezioni-esami/orario-delle-lezioni/');
|
||||||
|
const ids = {};
|
||||||
|
|
||||||
|
// Extract links with their text to match correctly
|
||||||
|
const linkPattern = /<a[^>]*href="[^"]*linkCalendarioId=([a-f0-9]{24})"[^>]*>([^<]+)<\/a>/g;
|
||||||
|
const links = [...html.matchAll(linkPattern)];
|
||||||
|
|
||||||
|
for (const [fullMatch, id, text] of links) {
|
||||||
|
const cleanText = text.trim();
|
||||||
|
|
||||||
|
if (cleanText === 'Triennale I anno') {
|
||||||
|
ids['anno-1'] = id;
|
||||||
|
} else if (cleanText === 'Triennale II anno') {
|
||||||
|
ids['anno-2'] = id;
|
||||||
|
} else if (cleanText === 'Triennale III anno') {
|
||||||
|
ids['anno-3'] = id;
|
||||||
|
} else if (cleanText === 'Magistrale') {
|
||||||
|
ids['magistrale'] = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(ids).length > 0 ? ids : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing timetable IDs:', error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse semester start date from calendar page
|
||||||
|
async function parseSemesterStartDate() {
|
||||||
|
try {
|
||||||
|
const { startYear, endYear } = getCurrentAcademicYear();
|
||||||
|
const currentMonth = new Date().getMonth();
|
||||||
|
const currentSemester = currentMonth >= 0 && currentMonth <= 5 ? 2 : 1;
|
||||||
|
|
||||||
|
const url = `https://www.dm.unipi.it/didattica/lezioni-esami/calendario-delle-attivita-didattiche/calendario-delle-attivita-didattiche-a-a-${startYear}-${endYear.toString().slice(-2)}/`;
|
||||||
|
|
||||||
|
const html = await fetchPage(url);
|
||||||
|
|
||||||
|
// Look for the appropriate semester start date
|
||||||
|
const semesterText = currentSemester === 1 ? 'I semestre' : 'II semestre';
|
||||||
|
const semesterRegex = new RegExp(`Lezioni\\s+${semesterText}.*?(\\d{1,2}\\s+\\w+\\s+\\d{4})`, 'si');
|
||||||
|
const match = html.match(semesterRegex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return match[1];
|
||||||
|
} else {
|
||||||
|
console.error(`Could not find ${semesterText} start date in calendar`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing semester start date:', error.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first Monday after a given date string
|
||||||
|
function getFirstMondayAfter(dateStr) {
|
||||||
|
// Parse Italian date format (e.g., "24 settembre 2025")
|
||||||
|
const [day, monthName, year] = dateStr.split(' ');
|
||||||
|
|
||||||
|
const monthMap = {
|
||||||
|
'gennaio': 0, 'febbraio': 1, 'marzo': 2, 'aprile': 3,
|
||||||
|
'maggio': 4, 'giugno': 5, 'luglio': 6, 'agosto': 7,
|
||||||
|
'settembre': 8, 'ottobre': 9, 'novembre': 10, 'dicembre': 11
|
||||||
|
};
|
||||||
|
|
||||||
|
const month = monthMap[monthName.toLowerCase()];
|
||||||
|
if (month === undefined) {
|
||||||
|
throw new Error(`Unknown month: ${monthName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDate = new Date(parseInt(year), month, parseInt(day));
|
||||||
|
const dayOfWeek = startDate.getDay(); // 0=Sunday, 1=Monday, etc.
|
||||||
|
|
||||||
|
// Calculate days to add to get to the next Monday
|
||||||
|
let daysToAdd;
|
||||||
|
if (dayOfWeek === 0) { // Sunday
|
||||||
|
daysToAdd = 1; // Next day is Monday
|
||||||
|
} else if (dayOfWeek === 1) { // Monday
|
||||||
|
daysToAdd = 7; // Next Monday
|
||||||
|
} else { // Tuesday-Saturday (2-6)
|
||||||
|
daysToAdd = 8 - dayOfWeek; // Days until next Monday
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = new Date(startDate);
|
||||||
|
result.setDate(startDate.getDate() + daysToAdd);
|
||||||
|
|
||||||
|
// Create a new date in UTC to avoid timezone issues with the calendar
|
||||||
|
return new Date(Date.UTC(result.getFullYear(), result.getMonth(), result.getDate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format date for button text (Italian format)
|
||||||
|
function formatDateForButton(date) {
|
||||||
|
const day = date.getUTCDate();
|
||||||
|
const monthNames = [
|
||||||
|
'gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
|
||||||
|
'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'
|
||||||
|
];
|
||||||
|
const month = monthNames[date.getUTCMonth()];
|
||||||
|
|
||||||
|
return `${day} ${month}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
async function updateAcademicData() {
|
||||||
|
const [timetableIds, semesterStartDateStr] = await Promise.all([
|
||||||
|
parseTimetableIds(),
|
||||||
|
parseSemesterStartDate()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Fail fast if parsing failed
|
||||||
|
if (!timetableIds) {
|
||||||
|
throw new Error('Failed to parse timetable IDs from the website');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!semesterStartDateStr) {
|
||||||
|
throw new Error('Failed to parse semester start date from the calendar');
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstMonday = getFirstMondayAfter(semesterStartDateStr);
|
||||||
|
|
||||||
|
const semesterData = {
|
||||||
|
timetableIds,
|
||||||
|
semesterStartDateStr,
|
||||||
|
firstMondayDate: firstMonday.toISOString(),
|
||||||
|
buttonText: `Vai al ${formatDateForButton(firstMonday)}! 🚀`,
|
||||||
|
lastUpdated: new Date().toISOString(),
|
||||||
|
academicYear: getCurrentAcademicYear()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save to src/semester-data.json
|
||||||
|
fs.writeFileSync('src/semester-data.json', JSON.stringify(semesterData, null, 2));
|
||||||
|
|
||||||
|
console.log('✅ Semester data updated successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the script
|
||||||
|
updateAcademicData().catch(error => {
|
||||||
|
console.error('💥 Script failed:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"timetableIds": {
|
||||||
|
"anno-1": "6966206f0f456f00552cec75",
|
||||||
|
"anno-2": "696622ec8a872b0073c0e54d",
|
||||||
|
"anno-3": "6966259a7727c0007dce3bd2",
|
||||||
|
"magistrale": "6966272716f73b007d88fd8f"
|
||||||
|
},
|
||||||
|
"semesterStartDateStr": "25 febbraio 2026",
|
||||||
|
"firstMondayDate": "2026-03-02T00:00:00.000Z",
|
||||||
|
"buttonText": "Vai al 2 marzo! 🚀",
|
||||||
|
"lastUpdated": "2026-01-23T15:15:31.641Z",
|
||||||
|
"academicYear": {
|
||||||
|
"startYear": 2025,
|
||||||
|
"endYear": 2026
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue