Skip to content

Commit f877881

Browse files
author
Gerit Wagner
committed
calendar update: python (not node)
1 parent 95da643 commit f877881

File tree

5 files changed

+718
-1007
lines changed

5 files changed

+718
-1007
lines changed

.github/workflows/generate_ical.js

Lines changed: 0 additions & 160 deletions
This file was deleted.

.github/workflows/update_calendar_ics.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
branches:
77
- main
88
paths:
9-
- 'assets/calendar/calendar.js'
9+
- 'src/update_calendar.py'
1010
- 'data/events.yaml'
1111

1212
permissions:
@@ -22,16 +22,18 @@ jobs:
2222
with:
2323
fetch-depth: 0
2424

25-
- name: Set up Node.js
26-
uses: actions/setup-node@v4
25+
- name: Set up Python
26+
uses: actions/setup-python@v5
2727
with:
28-
node-version: '20'
28+
python-version: '3.11'
2929

3030
- name: Install dependencies
31-
run: npm install js-yaml ics luxon rrule
31+
run: |
32+
python -m pip install --upgrade pip
33+
python -m pip install pyyaml python-dateutil icalendar
3234
3335
- name: Generate iCal File
34-
run: node .github/workflows/generate_ical.js
36+
run: python src/update_calendar.py
3537

3638
- name: Commit and Push Changes
3739
env:
@@ -46,8 +48,7 @@ jobs:
4648
exit 0
4749
fi
4850
49-
git commit -m "Update fs-ise.cal"
51+
git commit -m "Update fs-ise.ical"
5052
51-
# Ensure push uses the workflow token
5253
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
5354
git push origin HEAD:${GITHUB_REF_NAME}

_quarto.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ format:
245245
highlight-style: github
246246

247247

248+
249+
resources:
250+
- assets/calendar/fs-ise.ical
251+
252+
248253
extensions:
249254
iconify:
250255
style: "color: #002EFF;"

assets/calendar/calendar.js

Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -71,62 +71,83 @@ async function initCalendar() {
7171
window.ec = ec;
7272
}
7373

74-
// Parse events from iCal file
75-
async function loadEventsFromICal() {
74+
async function fetchFirstOk(urls) {
75+
let lastErr;
76+
for (const url of urls) {
7677
try {
77-
const response = await fetch('https://raw.githubusercontent.com/fs-ise/handbook/main/assets/calendar/fs-ise.ical');
78-
if (!response.ok) {
79-
throw new Error("Network response was not ok: " + response.statusText);
80-
}
81-
const iCalText = await response.text();
82-
const jcalData = ICAL.parse(iCalText);
83-
const vcalendar = new ICAL.Component(jcalData);
84-
const vevents = vcalendar.getAllSubcomponents('vevent');
85-
86-
return vevents.map((vevent) => {
87-
const event = new ICAL.Event(vevent);
88-
89-
const title = (event.summary || "").toLowerCase();
90-
91-
// Default: teaching (blue)
92-
let category = "teaching";
93-
let color = "#007acc";
94-
95-
// General (light grey)
96-
if (title.includes("vacation") || title.includes("remote work") || title.includes("professorium")) {
97-
category = "general";
98-
color = "#C8D1DC";
99-
}
100-
101-
// Events (green) — add/remove keywords as you like
102-
const eventKeywords = [
103-
"feier",
104-
"conference",
105-
"dies academicus",
106-
"weihnachtsfeier",
107-
"end-of-year",
108-
"choose-a-chair",
109-
];
110-
if (eventKeywords.some((kw) => title.includes(kw))) {
111-
category = "events";
112-
color = "#2e7d32";
113-
}
114-
115-
return {
116-
start: event.startDate.toJSDate(),
117-
end: event.endDate.toJSDate(),
118-
title: event.summary,
119-
description: event.description,
120-
location: event.location,
121-
category, // optional, but handy for filtering/legends later
122-
color,
123-
};
124-
});
125-
126-
} catch (error) {
127-
console.error("Error fetching or parsing iCal file:", error);
128-
return [];
78+
const res = await fetch(url, { cache: "no-cache" });
79+
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
80+
return await res.text();
81+
} catch (err) {
82+
lastErr = err;
83+
console.warn("Failed to fetch", url, err);
12984
}
85+
}
86+
throw lastErr || new Error("No URL succeeded");
87+
}
88+
89+
// Parse events from iCal file
90+
async function loadEventsFromICal() {
91+
try {
92+
// 1) Prefer local/relative (works when served by your site or local server)
93+
// Adjust these paths to match where the .ical is in your built site.
94+
const localCandidates = [
95+
"./assets/calendar/fs-ise.ical",
96+
"/assets/calendar/fs-ise.ical",
97+
];
98+
99+
// 2) Fallback: GitHub raw
100+
const githubRaw =
101+
"https://raw.githubusercontent.com/fs-ise/handbook/main/assets/calendar/fs-ise.ical";
102+
103+
const iCalText = await fetchFirstOk([...localCandidates, githubRaw]);
104+
105+
const jcalData = ICAL.parse(iCalText);
106+
const vcalendar = new ICAL.Component(jcalData);
107+
const vevents = vcalendar.getAllSubcomponents("vevent");
108+
109+
return vevents.map((vevent) => {
110+
const event = new ICAL.Event(vevent);
111+
const title = (event.summary || "").toLowerCase();
112+
113+
// Default: teaching (blue)
114+
let category = "teaching";
115+
let color = "#007acc";
116+
117+
// General
118+
if (title.includes("vacation") || title.includes("remote work") || title.includes("professorium")) {
119+
category = "general";
120+
color = "#C8D1DC";
121+
}
122+
123+
// Events
124+
const eventKeywords = [
125+
"feier",
126+
"conference",
127+
"dies academicus",
128+
"weihnachtsfeier",
129+
"end-of-year",
130+
"choose-a-chair",
131+
];
132+
if (eventKeywords.some((kw) => title.includes(kw))) {
133+
category = "events";
134+
color = "#2e7d32";
135+
}
136+
137+
return {
138+
start: event.startDate.toJSDate(),
139+
end: event.endDate.toJSDate(),
140+
title: event.summary,
141+
description: event.description,
142+
location: event.location,
143+
category,
144+
color,
145+
};
146+
});
147+
} catch (error) {
148+
console.error("Error fetching or parsing iCal file:", error);
149+
return [];
150+
}
130151
}
131152

132153
function _pad(num) {

0 commit comments

Comments
 (0)