2022-07-26 02:32:26 +02:00
|
|
|
import STATIC_LOCATIONS from './static-ens-locations.json'
|
2022-07-25 22:57:05 +02:00
|
|
|
|
2022-03-29 20:39:06 +02:00
|
|
|
// https://stackoverflow.com/a/35970186
|
|
|
|
function invertColor(hex) {
|
|
|
|
if (hex.indexOf('#') === 0) {
|
|
|
|
hex = hex.slice(1)
|
|
|
|
}
|
|
|
|
// convert 3-digit hex to 6-digits.
|
|
|
|
if (hex.length === 3) {
|
|
|
|
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
|
|
|
|
}
|
|
|
|
if (hex.length !== 6) {
|
|
|
|
throw new Error('Invalid HEX color.')
|
|
|
|
}
|
|
|
|
var r = parseInt(hex.slice(0, 2), 16),
|
|
|
|
g = parseInt(hex.slice(2, 4), 16),
|
|
|
|
b = parseInt(hex.slice(4, 6), 16)
|
|
|
|
// https://stackoverflow.com/a/3943023/112731
|
|
|
|
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF'
|
|
|
|
}
|
|
|
|
|
2022-03-01 17:28:32 +01:00
|
|
|
const clouds = {
|
2022-03-06 00:59:11 +01:00
|
|
|
KLUB_RESEAU: 'klub-reseau',
|
2022-03-12 15:21:13 +01:00
|
|
|
ELEVES_ENS: 'eleves-ens',
|
2022-07-23 00:59:27 +02:00
|
|
|
FRAMA_AGENDA: 'frama-agenda'
|
2022-03-06 00:59:11 +01:00
|
|
|
}
|
2022-03-01 17:28:32 +01:00
|
|
|
|
2022-02-20 22:29:15 +01:00
|
|
|
const calendars = {
|
2022-03-06 00:59:11 +01:00
|
|
|
'5WrcagPPARQ3BD87': {
|
2022-03-01 17:28:32 +01:00
|
|
|
cloud: clouds.KLUB_RESEAU,
|
2022-03-06 00:59:11 +01:00
|
|
|
name: 'Club réseau',
|
2022-03-11 18:27:20 +01:00
|
|
|
color: null,
|
2022-03-12 16:39:58 +01:00
|
|
|
default_location: "Cave d'hackENS"
|
2022-02-20 22:29:15 +01:00
|
|
|
},
|
2022-03-06 00:59:11 +01:00
|
|
|
TFEAKjAgNFQZpNjo: {
|
2022-03-01 17:28:32 +01:00
|
|
|
cloud: clouds.KLUB_RESEAU,
|
2022-03-06 00:59:11 +01:00
|
|
|
name: 'hackENS',
|
2022-03-11 18:27:20 +01:00
|
|
|
color: null,
|
|
|
|
default_location: "Cave d'hackENS"
|
2022-03-01 17:28:32 +01:00
|
|
|
},
|
2022-03-06 00:59:11 +01:00
|
|
|
LLWm8qK9iC5YGrrR: {
|
2022-03-01 17:28:32 +01:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-03-06 00:59:11 +01:00
|
|
|
name: 'Délégation Générale',
|
|
|
|
short_name: 'DG',
|
2022-03-01 17:28:32 +01:00
|
|
|
color: null
|
2022-03-06 15:21:46 +01:00
|
|
|
},
|
2022-07-03 16:30:25 +02:00
|
|
|
'2KGkWzBJGorxzyTW': {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-07-24 18:52:58 +02:00
|
|
|
name: "La Nuit de l'ENS",
|
|
|
|
short_name: 'La Nuit',
|
2022-07-03 16:32:48 +02:00
|
|
|
color: null
|
2022-07-03 16:30:25 +02:00
|
|
|
},
|
2022-03-06 15:21:46 +01:00
|
|
|
w442JdS5AaQ6czrP: {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: "Écriv'ENS",
|
2022-03-06 19:38:26 +01:00
|
|
|
color: null
|
2022-03-06 21:22:42 +01:00
|
|
|
},
|
|
|
|
fRtjDkjrZyn6fxd8: {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-03-07 08:33:46 +01:00
|
|
|
name: 'K-Fêt',
|
2022-03-07 10:59:05 +01:00
|
|
|
color: '#c63b52',
|
|
|
|
default_location: 'K-Fêt'
|
2022-03-11 18:27:20 +01:00
|
|
|
},
|
|
|
|
gsZtZK8c9EmREofn: {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-03-11 22:43:13 +01:00
|
|
|
name: 'Ernestophone',
|
2022-03-11 18:27:20 +01:00
|
|
|
color: null
|
2022-03-12 15:21:13 +01:00
|
|
|
},
|
2022-07-19 00:27:55 +02:00
|
|
|
dTHrXnYgsEoSTjWB: {
|
2022-07-18 13:47:05 +02:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-07-19 14:07:15 +02:00
|
|
|
name: 'Évènements (COF)',
|
|
|
|
short_name: 'COF',
|
2022-07-18 13:47:05 +02:00
|
|
|
color: null
|
|
|
|
},
|
|
|
|
bCgRFByHLiCCNc55: {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-07-19 14:07:15 +02:00
|
|
|
name: 'Assemblées Générales (COF)',
|
|
|
|
short_name: 'AG COF',
|
2022-07-18 13:47:05 +02:00
|
|
|
color: null
|
|
|
|
},
|
|
|
|
r4yJZDHjwNtH8wkR: {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'BdA',
|
|
|
|
color: null
|
|
|
|
},
|
2022-09-12 13:30:29 +02:00
|
|
|
ZtWm3MYSi388k2yk : {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'DDR',
|
|
|
|
color: null
|
|
|
|
},
|
2022-03-12 15:21:13 +01:00
|
|
|
T5WoHbs4FT5A945Z: {
|
|
|
|
cloud: clouds.FRAMA_AGENDA,
|
|
|
|
name: 'CinéClub',
|
|
|
|
color: null
|
2022-03-29 20:39:06 +02:00
|
|
|
},
|
|
|
|
'6SHG6cg9d7S3qqwD': {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Club Inutile ☔',
|
|
|
|
color: null,
|
|
|
|
initial: false
|
2022-07-23 00:58:16 +02:00
|
|
|
},
|
2022-07-26 02:32:26 +02:00
|
|
|
Ekjb4kDqMMqwJXZF: {
|
2022-07-23 00:58:16 +02:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
2022-07-25 23:35:38 +02:00
|
|
|
name: 'Rentrée des départements',
|
|
|
|
short_name: 'Dpt',
|
|
|
|
color: null
|
2022-07-25 23:50:12 +02:00
|
|
|
},
|
2022-07-25 23:35:38 +02:00
|
|
|
'8SKP62tQJP65K8EW': {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Conférences de recherche',
|
|
|
|
short_name: 'Conf',
|
|
|
|
color: null
|
2022-07-25 23:50:12 +02:00
|
|
|
},
|
2022-07-26 02:32:26 +02:00
|
|
|
PnRXqeq4SsSC33FM: {
|
2022-07-25 23:35:38 +02:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Visites de bibliothèques',
|
|
|
|
short_name: 'Bibli',
|
|
|
|
initial: false,
|
|
|
|
color: null
|
2022-07-25 23:50:12 +02:00
|
|
|
},
|
2022-07-26 02:32:26 +02:00
|
|
|
NWPtiEiz62LTtjo2: {
|
2022-07-25 23:35:38 +02:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Amphis de rentrée',
|
|
|
|
short_name: 'Prés. de rentrée',
|
|
|
|
color: null
|
2022-07-25 23:50:12 +02:00
|
|
|
},
|
2022-07-26 02:32:26 +02:00
|
|
|
JiRt58aJXay9kfyk: {
|
2022-07-25 23:35:38 +02:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Réunions de rentrée des Masters',
|
|
|
|
short_name: 'Masters',
|
|
|
|
color: null
|
2022-07-25 23:50:12 +02:00
|
|
|
},
|
2022-07-25 23:35:38 +02:00
|
|
|
'5Rb4bRjCDcsFjDdQ': {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Activités pour les étudiants internationaux',
|
|
|
|
short_name: 'Internationaux',
|
|
|
|
color: null
|
2022-07-25 23:50:12 +02:00
|
|
|
},
|
2022-07-25 23:35:38 +02:00
|
|
|
'62wKfQRrLNz2WXjt': {
|
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'Divers',
|
2022-07-23 00:58:16 +02:00
|
|
|
color: null
|
2022-08-27 20:55:40 +02:00
|
|
|
},
|
2022-09-12 13:30:29 +02:00
|
|
|
TFjE83ASCMK9rfRi: {
|
|
|
|
cloud: clouds.FRAMA_AGENDA,
|
|
|
|
name: 'BandarrêtdurgENS',
|
|
|
|
short_name: 'Banda',
|
|
|
|
color: null
|
|
|
|
},
|
|
|
|
TyMrLaPPDzT7yAGC: {
|
2022-08-27 20:55:40 +02:00
|
|
|
cloud: clouds.ELEVES_ENS,
|
|
|
|
name: 'BDS',
|
|
|
|
color: null
|
2022-03-06 00:59:11 +01:00
|
|
|
}
|
|
|
|
}
|
2022-02-20 22:29:15 +01:00
|
|
|
|
2022-03-06 17:30:42 +01:00
|
|
|
const calendarsByName = Object.fromEntries(
|
2022-03-06 19:38:26 +01:00
|
|
|
Object.entries(calendars).map(([id, { name }]) => [name, id])
|
|
|
|
)
|
2022-03-06 17:30:42 +01:00
|
|
|
|
2022-03-29 20:33:05 +02:00
|
|
|
export const initialCalendars = Array.from(
|
|
|
|
Object.entries(calendars).map(([_, { name, initial }]) => [name, initial ?? true])
|
|
|
|
)
|
|
|
|
.filter(cal => cal[1])
|
|
|
|
.map(cal => cal[0])
|
|
|
|
|
2022-02-20 22:29:15 +01:00
|
|
|
export const calendarTree = {
|
2022-07-24 18:52:58 +02:00
|
|
|
COF: {
|
2022-07-18 13:47:05 +02:00
|
|
|
BdA: {},
|
2022-07-19 14:07:15 +02:00
|
|
|
'Évènements (COF)': {},
|
|
|
|
'Assemblées Générales (COF)': {}
|
2022-07-18 13:47:05 +02:00
|
|
|
},
|
2022-03-06 00:59:11 +01:00
|
|
|
'Clubs COF': {
|
|
|
|
'Club réseau': {},
|
2022-03-06 15:21:46 +01:00
|
|
|
hackENS: {},
|
2022-03-11 18:27:20 +01:00
|
|
|
"Écriv'ENS": {},
|
2022-03-12 16:39:58 +01:00
|
|
|
CinéClub: {},
|
2022-03-11 22:43:13 +01:00
|
|
|
Ernestophone: {},
|
2022-09-12 13:30:29 +02:00
|
|
|
DDR: {},
|
|
|
|
'BandarrêtdurgENS': {},
|
2022-03-29 20:39:06 +02:00
|
|
|
'Club Inutile ☔': {}
|
2022-03-05 02:38:58 +01:00
|
|
|
},
|
2022-08-27 20:55:40 +02:00
|
|
|
BDS: {},
|
2022-07-24 18:52:58 +02:00
|
|
|
"La Nuit de l'ENS": {},
|
2022-03-06 21:22:42 +01:00
|
|
|
'Délégation Générale': {},
|
2022-07-18 13:47:05 +02:00
|
|
|
'K-Fêt': {},
|
2022-07-25 23:35:38 +02:00
|
|
|
'Rentrée académique': {
|
|
|
|
'Amphis de rentrée': {},
|
|
|
|
'Rentrée des départements': {},
|
|
|
|
'Conférences de recherche': {},
|
|
|
|
'Visites de bibliothèques': {},
|
|
|
|
'Réunions de rentrée des Masters': {},
|
|
|
|
'Activités pour les étudiants internationaux': {}
|
|
|
|
},
|
2022-07-26 02:32:26 +02:00
|
|
|
Divers: {}
|
2022-03-06 00:59:11 +01:00
|
|
|
}
|
2022-02-20 22:29:15 +01:00
|
|
|
|
2022-08-02 18:08:58 +02:00
|
|
|
const dfs = (p, t, l) => {
|
|
|
|
for (const [c, s] of Object.entries(t)) {
|
|
|
|
l[c] = p === null ? [] : [p, ...l[p]]
|
|
|
|
dfs(c, s, l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const ancestors = (() => {
|
|
|
|
let l = []
|
|
|
|
dfs(null, calendarTree, l)
|
|
|
|
return l
|
|
|
|
})()
|
|
|
|
|
2022-03-29 20:33:05 +02:00
|
|
|
export function getSubCalendars(name, tree = calendarTree) {
|
|
|
|
let ret
|
|
|
|
for (const [cal, subTree] of Object.entries(tree)) {
|
|
|
|
if (cal === name) {
|
|
|
|
ret = subTree
|
|
|
|
} else {
|
|
|
|
ret = ret || getSubCalendars(name, subTree)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2022-03-06 00:59:11 +01:00
|
|
|
const calendarIds = Object.keys(calendars)
|
2022-02-20 19:16:33 +01:00
|
|
|
|
2022-03-06 19:38:26 +01:00
|
|
|
function mkCalendarUrl(id, { cloud }, extra = {}) {
|
|
|
|
return (
|
|
|
|
`/cal/${cloud}/${id}/?` +
|
|
|
|
new URLSearchParams({
|
|
|
|
...extra,
|
|
|
|
export: true,
|
|
|
|
expand: true,
|
|
|
|
accept: 'jcal'
|
|
|
|
})
|
|
|
|
)
|
2022-02-20 19:16:33 +01:00
|
|
|
}
|
|
|
|
|
2022-03-29 20:22:05 +02:00
|
|
|
function mkExportUrl(id, { cloud }) {
|
|
|
|
return `/cal/${cloud}/${id}/?export`
|
|
|
|
}
|
|
|
|
|
2022-03-06 19:38:26 +01:00
|
|
|
function fetchCalendar(id, cal, extra = {}) {
|
2022-03-06 17:30:42 +01:00
|
|
|
return fetch(mkCalendarUrl(id, cal, extra), { credentials: 'omit' })
|
2022-03-06 00:59:11 +01:00
|
|
|
.then(resp => resp.json())
|
|
|
|
.catch(err => console.error(err))
|
2022-02-20 19:16:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class Calendar {
|
2022-03-06 00:59:11 +01:00
|
|
|
constructor(id, calendar) {
|
|
|
|
const metadata = calendars[id]
|
|
|
|
this.name = metadata.name
|
2022-03-06 13:47:05 +01:00
|
|
|
this.short_name = metadata.short_name
|
2022-03-06 00:59:11 +01:00
|
|
|
this.color = metadata.color || calendar[1][4][3]
|
2022-03-07 10:59:05 +01:00
|
|
|
this.default_location = metadata.default_location
|
2022-03-06 00:59:11 +01:00
|
|
|
this.events = calendar[2]
|
|
|
|
.filter(item => item[0] === 'vevent')
|
|
|
|
.map(item => this._parse_vevent(item[1]))
|
2022-02-20 19:16:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_parse_vevent(vevent) {
|
2022-03-06 00:59:11 +01:00
|
|
|
const event = {}
|
2022-02-20 19:16:33 +01:00
|
|
|
vevent.forEach(elt => {
|
2022-03-06 00:59:11 +01:00
|
|
|
event[elt[0]] = elt[3]
|
|
|
|
})
|
|
|
|
return event
|
2022-02-20 19:16:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-25 22:57:05 +02:00
|
|
|
function findLocationId(location) {
|
2022-07-26 13:07:03 +02:00
|
|
|
const adhocMap = {
|
|
|
|
'Amphi Jourdan': 'Amphithéâtre Jourdan',
|
|
|
|
'R2-21 (Jourdan)': 'R2-21',
|
|
|
|
'Salle Jean Ibanes (Jourdan, R1-07)': 'Salle Jean Ibanes (R1-07)',
|
|
|
|
'Salle Madeleine Rebérioux (Jourdan, R2-02)': 'Salle Madeleine Rebérioux (R2-02)',
|
|
|
|
'Salle Marcel Roncayolo (Jourdan, R2-05)': 'Salle Marcel Roncayolo (R2-05)'
|
2022-07-27 21:48:31 +02:00
|
|
|
}
|
2022-07-26 13:07:03 +02:00
|
|
|
|
2022-07-27 21:48:31 +02:00
|
|
|
const correctedLocation = adhocMap[location] || location
|
2022-07-25 22:57:05 +02:00
|
|
|
|
2022-07-26 02:32:26 +02:00
|
|
|
const result = Object.entries(STATIC_LOCATIONS).find(([building, rooms]) =>
|
2022-07-26 13:07:03 +02:00
|
|
|
rooms.includes(correctedLocation)
|
2022-07-26 02:32:26 +02:00
|
|
|
)
|
2022-07-25 22:57:05 +02:00
|
|
|
|
2022-07-26 02:32:26 +02:00
|
|
|
if (result === undefined) return undefined
|
|
|
|
const [building, _] = result
|
2022-07-26 13:07:03 +02:00
|
|
|
return `${building}-${correctedLocation}`
|
2022-07-25 22:57:05 +02:00
|
|
|
}
|
|
|
|
|
2022-02-20 22:29:15 +01:00
|
|
|
function fcEventFromjCalEvent(cal) {
|
|
|
|
return function (evt) {
|
2022-03-06 00:59:11 +01:00
|
|
|
const start = new Date(evt.dtstart)
|
|
|
|
const end = new Date(evt.dtend)
|
2022-02-20 22:29:15 +01:00
|
|
|
const fcEvent = {
|
2022-03-06 13:47:05 +01:00
|
|
|
title: `${cal.short_name ?? cal.name} : ${evt.summary}`,
|
2022-03-01 17:28:32 +01:00
|
|
|
start: evt.dtstart,
|
|
|
|
end: evt.dtend,
|
2022-02-20 22:29:15 +01:00
|
|
|
color: cal.color,
|
2022-03-29 20:39:06 +02:00
|
|
|
textColor: invertColor(cal.color),
|
2022-02-20 22:29:15 +01:00
|
|
|
duration: end - start // in ms
|
2022-03-06 00:59:11 +01:00
|
|
|
}
|
2022-03-06 13:47:05 +01:00
|
|
|
|
|
|
|
fcEvent.calendar = cal.name
|
|
|
|
fcEvent.short_name = evt.summary
|
2022-03-07 10:59:05 +01:00
|
|
|
fcEvent.description = evt.description
|
|
|
|
fcEvent.location = evt.location || cal.default_location
|
2022-02-20 22:29:15 +01:00
|
|
|
|
2022-07-25 22:57:05 +02:00
|
|
|
if (fcEvent.location) {
|
2022-07-26 02:32:26 +02:00
|
|
|
fcEvent.resourceId = findLocationId(fcEvent.location)
|
2022-07-25 22:57:05 +02:00
|
|
|
}
|
|
|
|
|
2022-03-12 16:39:58 +01:00
|
|
|
if (evt.status) {
|
|
|
|
fcEvent.status = evt.status
|
|
|
|
fcEvent.classNames = [`st-${evt.status.toLowerCase()}`]
|
|
|
|
}
|
|
|
|
|
2022-02-20 22:29:15 +01:00
|
|
|
if (evt.rrule) {
|
2022-03-06 00:59:11 +01:00
|
|
|
const { freq, byday, interval } = evt.rrule
|
2022-02-20 22:29:15 +01:00
|
|
|
fcEvent.rrule = {
|
|
|
|
freq,
|
|
|
|
byweekday: byday,
|
2022-03-06 00:59:11 +01:00
|
|
|
dtstart: evt.dtstart
|
|
|
|
}
|
2022-02-21 22:13:44 +01:00
|
|
|
|
|
|
|
if (interval) {
|
2022-03-06 00:59:11 +01:00
|
|
|
fcEvent.rrule.interval = interval
|
2022-02-21 22:13:44 +01:00
|
|
|
}
|
2022-02-20 22:29:15 +01:00
|
|
|
}
|
|
|
|
|
2022-03-06 00:59:11 +01:00
|
|
|
return fcEvent
|
2022-02-20 22:29:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-01 17:28:32 +01:00
|
|
|
function mkEventsFromCalendar(id, cal) {
|
|
|
|
return fetchCalendar(id, cal).then(calendar => {
|
2022-03-06 00:59:11 +01:00
|
|
|
if (calendar[0] !== 'vcalendar') return
|
2022-02-20 22:29:15 +01:00
|
|
|
const cal = new Calendar(id, calendar)
|
|
|
|
return cal.events.map(fcEventFromjCalEvent(cal))
|
2022-03-06 00:59:11 +01:00
|
|
|
})
|
2022-02-20 19:16:33 +01:00
|
|
|
}
|
|
|
|
|
2022-03-06 17:30:42 +01:00
|
|
|
export function mkSource(name) {
|
2022-03-06 19:38:26 +01:00
|
|
|
const calendarId = calendarsByName[name]
|
|
|
|
if (!calendarId) return null
|
|
|
|
const calendar = calendars[calendarId]
|
2022-03-06 17:30:42 +01:00
|
|
|
|
2022-02-20 19:16:33 +01:00
|
|
|
return {
|
2022-03-06 17:30:42 +01:00
|
|
|
id: name,
|
|
|
|
...(calendar?.meta || {}),
|
|
|
|
success: calendarData => {
|
2022-03-06 19:38:26 +01:00
|
|
|
if (calendarData[0] !== 'vcalendar') return
|
|
|
|
const cal = new Calendar(calendarId, calendarData)
|
|
|
|
return cal.events.map(fcEventFromjCalEvent(cal))
|
2022-03-06 17:30:42 +01:00
|
|
|
},
|
|
|
|
failure: error => {
|
2022-03-06 19:38:26 +01:00
|
|
|
console.error(`Fatal error during event source fetching of '${name}': ${error}`)
|
2022-03-06 17:30:42 +01:00
|
|
|
},
|
|
|
|
events: (info, successCallback, failureCallback) => {
|
2022-03-06 19:38:26 +01:00
|
|
|
const { start, end } = info
|
2022-03-06 17:30:42 +01:00
|
|
|
fetchCalendar(calendarId, calendar, {
|
|
|
|
start: start.valueOf() / 1000,
|
|
|
|
end: end.valueOf() / 1000
|
2022-03-06 19:38:26 +01:00
|
|
|
}).then(successCallback, failureCallback)
|
2022-03-29 20:22:05 +02:00
|
|
|
},
|
|
|
|
export_url: mkExportUrl(calendarId, calendar)
|
2022-02-20 19:16:33 +01:00
|
|
|
}
|
|
|
|
}
|