Compare commits

..

No commits in common. 'main' and 'main' have entirely different histories.
main ... main

Binary file not shown.

@ -1,7 +1,6 @@
import _ from 'lodash'
import { render } from 'preact'
import { useEffect, useState } from 'preact/hooks'
import semesterData from './semester-data.json'
// import { ToolOverlay } from './components/ToolOverlay.jsx'
//
@ -11,24 +10,32 @@ import semesterData from './semester-data.json'
// MODE_SCHEDULE,
// } from './components/EventsView.jsx'
import { Courses } from './components/view/Courses.jsx'
import { Schedule } from './components/view/Schedule.jsx'
import { HamburgerMenu } from './components/HamburgerMenu.jsx'
import { Help } from './components/Help.jsx'
import { Icon } from './components/Icon.jsx'
import { OptionBar } from './components/OptionBar.jsx'
import { Popup } from './components/Popup.jsx'
import { Toolbar } from './components/Toolbar.jsx'
import { Courses } from './components/view/Courses.jsx'
import { Schedule } from './components/view/Schedule.jsx'
import { prettyAulaName, prettyProfName, usePersistentState } from './utils.jsx'
import { OptionBar } from './components/OptionBar.jsx'
import {
prettyAulaName,
prettyProfName,
clearOldPersistentStates,
usePersistentState,
} from './utils.jsx'
import { SettingsBar } from './components/SettingsBar.jsx'
// Che fanno queste due righe?
window._ = _
window.dataBuffer = {}
// NOTA: magistrale *non* è quello con i corsi a cavallo
const TIMETABLE_IDS = semesterData.timetableIds
const TIMETABLE_IDS = {
'anno-1': '6798fa77c5e9ff00195153f0',
'anno-2': '6798fab7c5e9ff00195153f2',
'anno-3': '6798fb0038d6380019ee9e57',
'magistrale': '679a6b874528520019aa9e66',
}
// const DEFAULT_DATE_RANGE = {
// from: '2023-10-09T00:00:00.000Z',
@ -45,8 +52,6 @@ const TIMETABLE_IDS = semesterData.timetableIds
function specialEventPatches(eventi) {
// Il laboratorio del primo anno in realtà è in due gruppi separati
eventi.forEach(evento => {
console.log(evento.id, evento.nome, evento.dataInizio, evento.dataFine)
if (
evento.nome === 'LABORATORIO DI INTRODUZIONE ALLA MATEMATICA COMPUTAZIONALE'
) {
@ -110,7 +115,7 @@ async function loadCalendari(date) {
method: 'POST',
mode: 'cors',
credentials: 'omit',
},
}
)
return await req.json()
@ -124,9 +129,9 @@ async function loadCalendari(date) {
]
const results = await Promise.all(requests)
const timetablesRaw = results.map(timetable =>
specialEventPatches(_.uniqBy(timetable, 'id')),
specialEventPatches(_.uniqBy(timetable, 'id'))
)
const allRaw = _.uniqBy(specialEventPatches(_.concat(...results)), 'id')
const allRaw = specialEventPatches(_.concat(...results), 'id')
return {
'anno-1': formatEvents(timetablesRaw[0]),
@ -173,7 +178,7 @@ const App = ({}) => {
const [date, setDate] = useState(new Date().toISOString())
// Data Sources
const [view, setView] = usePersistentState('view', 'tutti')
const [view, setView] = usePersistentState('view', 'magistrale')
const [timetables, setTimetables] = useState(null)
useEffect(async () => {
setTimetables(await loadCalendari(new Date(date)))
@ -191,7 +196,7 @@ const App = ({}) => {
const [theme, setTheme] = usePersistentState(
'theme',
'light',
'light'
// window.matchMedia('(prefers-color-scheme: dark)').matches
// ? 'dark'
// : 'light'
@ -260,10 +265,10 @@ const App = ({}) => {
</p>
<button
onClick={() => {
setDate(semesterData.firstMondayDate)
setDate('2025-03-03T00:00:00.000Z')
}}
>
{semesterData.buttonText}
Vai al 3 marzo! 🚀
</button>
{/* <p>

@ -1,186 +0,0 @@
#!/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);
});

@ -1,16 +0,0 @@
{
"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…
Cancel
Save