-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode.gs
More file actions
142 lines (121 loc) · 5.81 KB
/
Copy pathcode.gs
File metadata and controls
142 lines (121 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
* Zermelo naar Google Calendar Sync
* * Dit script haalt roostergegevens op uit de Zermelo iCal-api en synchroniseert deze
* met een specifieke Google Agenda. Het voorkomt dubbele afspraken door gebruik te
* maken van unieke Zermelo ID's (UID's) die als 'tags' in de agenda worden opgeslagen.
*/
function syncSchoolCalendar() {
// CONFIGURATIE
const ICAL_URL = "JOUW_ZERMELO_ICAL_LINK_HIER";
const TARGET_CALENDAR_ID = "JOUW_GOOGLE_AGENDA_LINK_HIER";
const calendar = CalendarApp.getCalendarById(TARGET_CALENDAR_ID);
// 1. ZOEKBEREIK DEFINIËREN
// We zoeken in de Google Agenda van 2 dagen geleden tot 28 dagen in de toekomst.
// Dit bereik moet ALTIJD groter zijn dan de endWeekOffset in je ICAL_URL om dubbele afspraken te voorkomen.
const startSearch = new Date();
startSearch.setDate(startSearch.getDate() - 2);
const endSearch = new Date();
endSearch.setDate(endSearch.getDate() + 28);
// 2. BESTAANDE AFSPRAKEN OPHALEN
// We halen alle afspraken op en slaan ze op in 'eventMap' met de Zermelo UID als sleutel.
const existingEvents = calendar.getEvents(startSearch, endSearch);
const eventMap = {};
existingEvents.forEach(ev => {
const uid = ev.getTag("zermelo_uid");
if (uid) {
eventMap[uid] = ev;
}
});
// 3. ZERMELO DATA OPHALEN
const response = UrlFetchApp.fetch(ICAL_URL);
const icalData = response.getContentText();
const vevents = icalData.split("BEGIN:VEVENT");
// Set om bij te houden wat we in deze huidige run verwerken (voorkomt dubbelen in iCal zelf)
const processedUidsInThisRun = new Set();
vevents.forEach(e => {
if (!e.includes("SUMMARY")) return;
// Unieke ID (UID) van de les uit de iCal tekst halen
const uidMatch = e.match(/UID:([^\r\n]+)/);
const uid = uidMatch ? uidMatch[1].trim() : null;
// Stop als er geen UID is of als we deze UID al verwerkt hebben in deze run
if (!uid || processedUidsInThisRun.has(uid)) return;
processedUidsInThisRun.add(uid);
// Titel (Summary) ophalen
const summaryMatch = e.match(/SUMMARY:([^\r\n]+)/);
let rawTitle = summaryMatch ? summaryMatch[1].trim() : "School";
// 4. UITVAL AFHANDELING
// Als de les gemarkeerd is als uitval ([x]), verwijderen we de bestaande afspraak uit de agenda.
if (rawTitle.includes("[x]") || rawTitle.toLowerCase().includes("uitval")) {
if (eventMap[uid]) {
eventMap[uid].deleteEvent();
delete eventMap[uid]; // Verwijder uit map zodat Stap 6 deze niet nogmaals probeert te wissen
Logger.log("Verwijderd (uitval): " + rawTitle);
}
return; // Stop met verwerken van deze les
}
// 5. TIJD EN DETAILS VERWERKEN
const dtStartMatch = e.match(/DTSTART[:;](VALUE=DATE:)?([^\r\n]+)/);
const dtEndMatch = e.match(/DTEND[:;](VALUE=DATE:)?([^\r\n]+)/);
if (!dtStartMatch || !dtEndMatch) return;
const start = ZparseICalDate(dtStartMatch[2].trim());
const end = ZparseICalDate(dtEndMatch[2].trim());
// Opmaak van de titel (Alles naar hoofdletters en bullets tussen woorden)
const isImportant = rawTitle.includes("[!]");
const cleanTitle = rawTitle.replace(/\[.*?\]/g, "").trim();
const formattedTitle = cleanTitle.split(" ").map(p => p.toUpperCase()).join(" • ");
const locationMatch = e.match(/LOCATION:([^\r\n]+)/);
const location = locationMatch ? locationMatch[1].trim() : "";
const descMatch = e.match(/DESCRIPTION:([^\r\n]+)/);
const description = descMatch ? descMatch[1].trim().replace(/\\n/g, "\n") : "";
// 6. AGENDA BIJWERKEN OF AANMAKEN
if (eventMap[uid]) {
// Het event bestaat al -> we updaten de gegevens voor het geval er iets gewijzigd is (bijv. lokaal)
const ev = eventMap[uid];
ev.setTitle(formattedTitle);
ev.setTime(start, end);
ev.setLocation(location);
ev.setDescription(description);
ev.setColor(isImportant ? CalendarApp.EventColor.PALE_RED : CalendarApp.EventColor.PALE_BLUE);
// Belangrijk: haal uit de eventMap zodat we weten dat deze les nog steeds bestaat
delete eventMap[uid];
Logger.log("Geupdate: " + formattedTitle);
} else {
// Het event bestaat nog niet -> nieuw aanmaken
const newEvent = calendar.createEvent(formattedTitle, start, end, {
location: location,
description: description
});
// We voegen de UID toe als verborgen tag zodat we de afspraak volgende keer herkennen
newEvent.setTag("zermelo_uid", uid);
newEvent.setColor(isImportant ? CalendarApp.EventColor.PALE_RED : CalendarApp.EventColor.PALE_BLUE);
Logger.log("Nieuw aangemaakt: " + formattedTitle);
}
});
// 7. OPRUIMEN (STILLE UITVAL)
// Alle events die na de loop nog in 'eventMap' staan, komen niet meer voor in de Zermelo link.
// Dit gebeurt bijv. als een les volledig uit het rooster wordt gewist.
for (const uid in eventMap) {
Logger.log("Verwijderen (niet meer in rooster): " + eventMap[uid].getTitle());
eventMap[uid].deleteEvent();
}
}
/**
* Hulpfunctie om iCal datumstrings om te zetten naar JavaScript Date objecten.
* Ondersteunt zowel specifieke tijden (T-formaat) als 'Hele dag' events.
*/
function ZparseICalDate(dateStr) {
const year = parseInt(dateStr.substring(0, 4));
const month = parseInt(dateStr.substring(4, 6)) - 1; // Januari = 0
const day = parseInt(dateStr.substring(6, 8));
if (dateStr.includes("T")) {
// Formaat met tijdstip (bijv. 20240520T083000Z)
const hour = parseInt(dateStr.substring(9, 11));
const min = parseInt(dateStr.substring(11, 13));
// Omzetten van UTC naar de lokale tijdzone (Europe/Amsterdam)
const date = new Date(Date.UTC(year, month, day, hour, min));
return new Date(Utilities.formatDate(date, "Europe/Amsterdam", "yyyy-MM-dd'T'HH:mm:ss"));
} else {
// Formaat voor hele dagen (bijv. 20240520)
return new Date(year, month, day);
}
}