Migrate to central appointment database #45
@ -14,9 +14,7 @@ Mit unserer Breitensportgruppe sprechen wie diejenigen an, die zwar (noch) keine
|
||||
|
||||
## Trainingszeit Breitensport
|
||||
|
||||
| Breitensport Standard | Tag | Zeit | Ort |
|
||||
|-----------------------|-------------|----------------|-----------|
|
||||
| Breitensport Standard | Mittwoch | 20:00 - 21:30 | VH Hinten |
|
||||
{{<tsc/calendar/table category="Breitensport">}}
|
||||
|
||||
{{% tsc/link-offers %}}
|
||||
|
||||
|
@ -22,36 +22,15 @@ Und wann bist Du dabei?
|
||||
|
||||
## Trainingszeiten Dance-Styles Adults
|
||||
|
||||
|Dance Styles | Alter | Tag | Zeit | Ort |
|
||||
|--------------------|---------|------------|---------------|-----------|
|
||||
| D-S Adults 0 | ab 18 | Mittwoch | 19:00 - 20:00 | VH Hinten |
|
||||
| D-S Adults 1 | ab 18 | Dienstag | 18:45 - 20:15 | VH Vorn |
|
||||
| D-S Adults 3 | ab 18 | Donnerstag | 19:30 - 20:30 | VH Vorn |
|
||||
| D-S Adults 5 | ab 18 | Montag | 18:15 - 19:15 | VH Vorn |
|
||||
| D-S Contest Gruppe | Anfrage | Montag | 19:15 - 20:45 | VH Vorn |
|
||||
{{< tsc/calendar/table category="DSAdults" showAge="true" >}}
|
||||
|
||||
## Trainingszeiten Dance-Styles Teens
|
||||
|
||||
|Dance-Styles Teens | Jahrgang | Tag | Zeit | Ort |
|
||||
|-------------------|-----------|----------|---------------|------------|
|
||||
| D-S Teens 0 | 2008 - 10 | Mittwoch | 18:00 - 19:00 | VH Vorn |
|
||||
| D-S Teens 1 | 2004 - 07 | Mittwoch | 18:00 - 19:00 | VH Hinten |
|
||||
| D-S Teens 2 | 2006 - 09 | Montag | 17:30 - 18:30 | VH Mitte |
|
||||
{{< tsc/calendar/table category="DSTeens" showAge="true" >}}
|
||||
|
||||
## Trainingszeiten Dance-Styles Kids
|
||||
|
||||
|Dance-Styles Kids | Jahrgang | Tag | Zeit | Ort |
|
||||
|---------------------|-----------|------------|---------------|-----------|
|
||||
| D-S Minis 0 | 2019 - 20 | Freitag | 16:30 - 17:30 | VH Vorn |
|
||||
| D-S Minis 1 | 2020 - 21 | Donnerstag | 16:45 - 17:45 | VH Mitte |
|
||||
| D-S Kids 0 | 2011 - 13 | Dienstag | 17:45 - 18:45 | VH Vorn |
|
||||
| D-S Kids 1 | 2012 - 14 | Donnerstag | 18:30 - 19:30 | VH Vorn |
|
||||
| D-S Kids 2 | 2010 - 12 | Mittwoch | 17:00 - 18:00 | VH Vorn |
|
||||
| D-S Kids 3 | 2014 - 16 | Mittwoch | 16:45 - 17:45 | VH Hinten |
|
||||
| D-S-Kids 4 | 2015 - 17 | Dienstag | 16:30 - 17:30 | VH Vorn |
|
||||
| D-S Kids 5 | 2010 - 13 | Donnerstag | 17:00 - 18:00 | VH Vorn |
|
||||
| D-S-Kids 6 | 2017 - 19 | Montag | 17:00 - 18:00 | VH Vorn |
|
||||
| D-S Kids 7 | 2012 - 15 | Mittwoch | 18:00 - 19:00 | VH Mitte |
|
||||
{{< tsc/calendar/table category="DSKids" showAge="true" >}}
|
||||
|
||||
Interesse an Standard- und Lateintanzkursen für Kinder und Jugendliche? Im Bereich Kinder/Jugend findest du weitere Angebote.
|
||||
|
||||
|
@ -18,8 +18,6 @@ Wer schon Discofox tanzen kann, lernt bestimmt die eine oder andere tolle neue F
|
||||
|
||||
## Trainingszeit Discofox
|
||||
|
||||
|Discofox | Tag | Zeit | Ort |
|
||||
|---|---|---|---|
|
||||
|Discofox | Freitag | 20:30 - 21:30 | VH Vorn |
|
||||
{{< tsc/calendar/table category="Discofox" >}}
|
||||
|
||||
{{% tsc/link-offers %}}
|
||||
|
@ -20,12 +20,7 @@ Für manchen ist es bereits eine gute Erfahrung, um später in einer der größe
|
||||
|
||||
## Trainingszeiten Kinder- / Jugendgruppen, Standard und Latein
|
||||
|
||||
| Kinder | Jahrgang | Tag | Zeit | Ort |
|
||||
|----------------------------------------|-----------|------------|---------------|------------|
|
||||
| Kindertanz 0 | 2018 - 20 | Freitag | 14:30 - 15:30 | VH Hinten |
|
||||
| Kindertanz 1 | 2016 - 18 | Freitag | 15:30 - 16:30 | VH Hinten |
|
||||
| Kindertanz 2 Turnieraufbau Kinder | | Freitag | 16:30 - 19:00 | VH Hinten |
|
||||
| Kindertanz 8 Turnieraufb. Latein Ki/Ju | | Donnerstag | 18:00 - 19:30 | VH Hinten |
|
||||
{{< tsc/calendar/table category="Kinder" showAge="true" >}}
|
||||
|
||||
Weitere spannende Angebote für Kinder und Teens findet ihr bei unseren Dance-Stylern.
|
||||
|
||||
|
@ -17,16 +17,6 @@ Interessiert? Na, dann schauen Sie doch einfach einmal vorbei!
|
||||
|
||||
## Trainingszeiten Tanzkreise
|
||||
|
||||
| Tanzkreise | Tag | Zeit | Ort |
|
||||
|-------------|-------------|---------------|----------|
|
||||
| Tanzkreis 0 | Freitag | 19:00 - 20:30 | VH Vorn |
|
||||
| Tanzkreis 1 | Montag | 20:00 - 21:30 | VH Mitte |
|
||||
| Tanzkreis 2 | Dienstag | 20:00 - 22:00 | VH Mitte |
|
||||
| Tanzkreis 3 | Mittwoch | 19:00 - 20:30 | VH Mitte |
|
||||
| Tanzkreis 4 | Mittwoch | 20:30 - 22:00 | VH Mitte |
|
||||
| Tanzkreis 5 | Donnerstag | 19:30 - 21:00 | VH Mitte |
|
||||
| Tanzkreis 6 Einsteiger | Freitag | 18:30 - 19:00 | VH Mitte |
|
||||
| Tanzkreis 6 | Freitag | 19:00 - 20:30 | VH Mitte |
|
||||
| Tanzkreis 9 | Freitag | 20:30 - 22:00 | VH Mitte |
|
||||
{{< tsc/calendar/table category="Tanzkreise" >}}
|
||||
|
||||
{{% tsc/link-offers %}}
|
||||
|
@ -22,16 +22,9 @@ Darüber hinaus können unsere Tänzer zu fast jeder Zeit noch frei trainieren -
|
||||
[unsere Turnierpaare]({{< relref "paare" >}})
|
||||
|
||||
## Trainingszeiten Turniertanz
|
||||
| Turniertanz | Tag | Zeit | Ort |
|
||||
|---------------------------------------------|---------|-----------------|---------|
|
||||
| Turnieraufbau Latein | Di | 19:00 - 20:00 | VH Neu |
|
||||
| Turnier Standard | Di | 20:00 - 22:00 | VH Neu |
|
||||
| Turnieraufbau Standard | Mi | 20:30 - 22:00 | VH Vorn |
|
||||
| Turnieraufbau Kinder Jugend inkl. Basic | Mi | 16:30 - 18:00 | VH Neu |
|
||||
| Turnieraufbau Kinder Jugend inkl. Basic | Do | 18:00 - 19:30 | VH Neu |
|
||||
| Turnieraufbau Kinder Jugend inkl. Basic | Fr | 16:00 - 17:00 | VH Neu |
|
||||
|
||||
|
||||
{{< tsc/calendar/table category="Turnier" >}}
|
||||
|
||||
{{% tsc/link-offers %}}
|
||||
|
||||
## Sportverwaltung und weitere Links
|
||||
|
@ -11,6 +11,7 @@ aliases:
|
||||
---
|
||||
Hier finden Sie die Übersicht zur aktuellen Belegung unserer Tanzsäle.
|
||||
|
||||
|
||||
## Raumbelegung - außerplanmäßig
|
||||
|
||||
Außerhalb der Trainings- und Kurszeiten können die Räume für spezielle (Gruppen-) Trainings gebucht werden.
|
||||
@ -26,8 +27,9 @@ Weitere Infos zur Reservierung findest du unterhalb der Kalender.
|
||||
|
||||
## Raumbelegung - planmäßig
|
||||
|
||||
{{< tsc/show-calendar 10 22 "Mo" "Di" "Mi" >}}
|
||||
{{< tsc/show-calendar 10 22 "Do" "Fr" >}}
|
||||
{{< tsc/calendar/schedule 10 22 "Mon" "Tue" "Wed" >}}
|
||||
|
||||
{{< tsc/calendar/schedule 14 22 "Thu" "Fri" >}}
|
||||
|
||||
|
||||
## Regeln für die Belegung der Tanzsäle
|
13
content/page/info/raumbelegung/liste.md
Normal file
13
content/page/info/raumbelegung/liste.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: Raumbelegungs Liste
|
||||
date: 2025-01-20T09:02:00
|
||||
draft: false
|
||||
build:
|
||||
list: local
|
||||
# render: never
|
||||
---
|
||||
|
||||
Auf dieser Seite werden die Angebote des Vereins noch einmal zusammen gefasst.
|
||||
Dies dient der internen Kontrolle im Verein.
|
||||
|
||||
{{< tsc/calendar/list >}}
|
@ -1,145 +0,0 @@
|
||||
calendar:
|
||||
vorne:
|
||||
Mo:
|
||||
"17:00":
|
||||
title: DS-Kids 6
|
||||
slots: 4
|
||||
"18:15":
|
||||
title: DS Adults 5
|
||||
slots: 4
|
||||
"19:15":
|
||||
title: DS Contest Gruppe
|
||||
slots: 6
|
||||
Di:
|
||||
"16:30":
|
||||
title: DS Kids 4
|
||||
slots: 4
|
||||
"17:45":
|
||||
title: DS Kids 0
|
||||
slots: 4
|
||||
"18:45":
|
||||
title: DS Adults 1
|
||||
slots: 6
|
||||
Mi:
|
||||
"17:00":
|
||||
title: DS Kids 2
|
||||
slots: 4
|
||||
"18:00":
|
||||
title: DS Teens 0
|
||||
slots: 4
|
||||
"20:30":
|
||||
title: Turnieraufbau Std
|
||||
slots: 6
|
||||
Do:
|
||||
"17:00":
|
||||
title: DS Kids 5
|
||||
slots: 4
|
||||
"18:30":
|
||||
title: DS Kids 1
|
||||
slots: 4
|
||||
"19:30":
|
||||
title: DS Adults 3
|
||||
slots: 4
|
||||
Fr:
|
||||
"16:30":
|
||||
title: DS Minis 0
|
||||
slots: 4
|
||||
"19:00":
|
||||
title: Tanzkreis 0
|
||||
slots: 6
|
||||
"20:30":
|
||||
title: Discofox
|
||||
slots: 4
|
||||
# Sa: {}
|
||||
# So: {}
|
||||
mitte:
|
||||
Mo:
|
||||
"17:30":
|
||||
title: DS Teens 2
|
||||
slots: 4
|
||||
"18:30":
|
||||
title: Ballet
|
||||
slots: 6
|
||||
"20:00":
|
||||
title: Tanzkreis 1
|
||||
slots: 6
|
||||
Di:
|
||||
"20:00":
|
||||
title: Tanzkreis 2
|
||||
slots: 8
|
||||
Mi:
|
||||
"18:00":
|
||||
title: DS Kids 7
|
||||
slots: 4
|
||||
"19:00":
|
||||
title: Tanzkreis 3
|
||||
slots: 6
|
||||
"20:30":
|
||||
title: Tanzkreis 4
|
||||
slots: 6
|
||||
Do:
|
||||
"16:45":
|
||||
title: DS Minis 1
|
||||
slots: 4
|
||||
"19:30":
|
||||
title: Tanzkreis 5
|
||||
slots: 6
|
||||
Fr:
|
||||
"18:30":
|
||||
title: Tanzkreis 6 - Einsteiger
|
||||
slots: 2
|
||||
"19:00":
|
||||
title: Tanzkreis 6
|
||||
slots: 6
|
||||
"20:30":
|
||||
title: Tanzkreis 9
|
||||
slots: 6
|
||||
Sa: {}
|
||||
So: {}
|
||||
hinten:
|
||||
Mo:
|
||||
"16:00":
|
||||
title: Ballet
|
||||
slots: 6
|
||||
# "18:15":
|
||||
# title: DS Adults 5
|
||||
# slots: 4
|
||||
Di:
|
||||
"19:00":
|
||||
title: Turnier Latein
|
||||
slots: 4
|
||||
"20:00":
|
||||
title: Turnier Standard
|
||||
slots: 8
|
||||
Mi:
|
||||
"10:30":
|
||||
title: Ballet
|
||||
slots: 6
|
||||
"16:45":
|
||||
title: DS Kids 3
|
||||
slots: 4
|
||||
"18:00":
|
||||
title: DS Teens 1
|
||||
slots: 4
|
||||
"19:00":
|
||||
title: DS Adults 0
|
||||
slots: 4
|
||||
"20:00":
|
||||
title: Breitensport
|
||||
slots: 6
|
||||
Do:
|
||||
"18:00":
|
||||
title: Kindertanz 8 (Turnieraufbau)
|
||||
slots: 6
|
||||
Fr:
|
||||
"14:30":
|
||||
title: Kindertanz 0
|
||||
slots: 4
|
||||
"15:30":
|
||||
title: Kindertanz 1
|
||||
slots: 4
|
||||
"16:30":
|
||||
title: Kindertanz 2 (Turnieraufbau)
|
||||
slots: 10
|
||||
Sa: {}
|
||||
So: {}
|
7
data/days.yml
Normal file
7
data/days.yml
Normal file
@ -0,0 +1,7 @@
|
||||
Mon: Montag
|
||||
Tue: Dienstag
|
||||
Wed: Mittwoch
|
||||
Thu: Donnerstag
|
||||
Fri: Freitag
|
||||
Sat: Samstag
|
||||
Sun: Sonntag
|
27
data/holidays.yaml
Normal file
27
data/holidays.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
holidays:
|
||||
- from: 2025-01-01
|
||||
to: 2025-01-05
|
||||
- from: 2025-04-14
|
||||
to: 2025-04-27
|
||||
- from: 2025-06-09
|
||||
to: 2025-06-22
|
||||
- from: 2025-07-31
|
||||
to: 2025-09-14
|
||||
- from: 2025-10-27
|
||||
to: 2025-11-02
|
||||
- from: 2025-12-22
|
||||
to: 2026-01-05
|
||||
feasts:
|
||||
- 2025-01-01
|
||||
- 2025-01-06
|
||||
- 2025-04-18
|
||||
- 2025-04-21
|
||||
- 2025-05-01
|
||||
- 2025-05-29
|
||||
- 2025-06-09
|
||||
- 2025-06-19
|
||||
- 2025-10-03
|
||||
- 2025-11-01
|
||||
- 2025-12-25
|
||||
- 2025-12-26
|
||||
|
261
data/schedule.yaml
Normal file
261
data/schedule.yaml
Normal file
@ -0,0 +1,261 @@
|
||||
calendars:
|
||||
vorne:
|
||||
id: "vh-vorne-regeltermine_shared_by_tsc"
|
||||
ignore: false
|
||||
name: VH Vorn
|
||||
schedule:
|
||||
- title: DS Kids 6
|
||||
day: Mon
|
||||
start: '17:00'
|
||||
age: '2017 - 2019'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Adults 5
|
||||
day: Mon
|
||||
start: '18:15'
|
||||
age: 'ab 18'
|
||||
duration: 60
|
||||
class: DSAdults
|
||||
- title: DS Contest Gruppe
|
||||
day: Mon
|
||||
start: '19:15'
|
||||
age: 'auf Anfrage'
|
||||
duration: 90
|
||||
class: DSAdults
|
||||
|
||||
- title: DS Kids 4
|
||||
day: Tue
|
||||
start: '16:30'
|
||||
age: '2015 - 2017'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Kids 0
|
||||
day: Tue
|
||||
start: '17:45'
|
||||
age: '2011 - 2013'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Adults 1
|
||||
day: Tue
|
||||
start: '18:45'
|
||||
age: 'ab 18'
|
||||
duration: 90
|
||||
class: DSAdults
|
||||
|
||||
- title: DS Kids 2
|
||||
day: Wed
|
||||
start: '17:00'
|
||||
age: '2010 - 2012'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Teens 0
|
||||
day: Wed
|
||||
start: '18:00'
|
||||
age: '2008 - 2010'
|
||||
duration: 60
|
||||
class: DSTeens
|
||||
- title: Turnieraufbau Std
|
||||
weight: -1
|
||||
day: Wed
|
||||
start: '20:30'
|
||||
duration: 90
|
||||
class: Turnier
|
||||
|
||||
- title: DS Kids 5
|
||||
day: Thu
|
||||
start: '17:00'
|
||||
age: '2010 - 2013'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Kids 1
|
||||
day: Thu
|
||||
start: '18:30'
|
||||
age: '2012 - 2014'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Adults 3
|
||||
day: Thu
|
||||
start: '19:30'
|
||||
age: 'ab 18'
|
||||
duration: 60
|
||||
class: DSAdults
|
||||
|
||||
- title: DS Minis 0
|
||||
weight: -1
|
||||
day: Fri
|
||||
start: '16:30'
|
||||
age: '2019 - 2020'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: Tanzkreis 0
|
||||
day: Fri
|
||||
start: '19:00'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
- title: Discofox
|
||||
day: Fri
|
||||
start: '20:30'
|
||||
duration: 60
|
||||
class: Discofox
|
||||
|
||||
mitte:
|
||||
id: "vh-mitte-regeltermine_shared_by_tsc"
|
||||
name: VH Mitte
|
||||
ignore: false
|
||||
schedule:
|
||||
- title: DS Teens 2
|
||||
day: Mon
|
||||
start: '17:30'
|
||||
age: '2006 - 2009'
|
||||
duration: 60
|
||||
class: DSTeens
|
||||
- title: Ballet
|
||||
day: Mon
|
||||
start: '18:30'
|
||||
age: ''
|
||||
extern: true
|
||||
duration: 90
|
||||
# class: DSKids
|
||||
- title: Tanzkreis 1
|
||||
day: Mon
|
||||
start: '20:00'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
|
||||
- title: Tanzkreis 2
|
||||
day: Tue
|
||||
start: '20:00'
|
||||
duration: 120
|
||||
class: Tanzkreise
|
||||
|
||||
- title: DS Kids 7
|
||||
day: Wed
|
||||
start: '18:00'
|
||||
age: '2012 - 2015'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: Tanzkreis 3
|
||||
day: Wed
|
||||
start: '19:00'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
- title: Tanzkreis 4
|
||||
day: Wed
|
||||
start: '20:30'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
|
||||
- title: DS Minis 1
|
||||
weight: -1
|
||||
day: Thu
|
||||
start: '16:45'
|
||||
age: '2020 - 2021'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: Tanzkreis 5
|
||||
day: Thu
|
||||
start: '19:30'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
|
||||
- title: Tanzkreis 6
|
||||
subtitle: Einsteiger
|
||||
day: Fri
|
||||
start: '18:30'
|
||||
duration: 30
|
||||
class: Tanzkreise
|
||||
- title: Tanzkreis 6
|
||||
day: Fri
|
||||
start: '19:00'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
- title: Tanzkreis 9
|
||||
day: Fri
|
||||
start: '20:30'
|
||||
duration: 90
|
||||
class: Tanzkreise
|
||||
|
||||
hinten:
|
||||
id: "vh-hinten-regeltermine_shared_by_tsc"
|
||||
name: VH Hinten
|
||||
ignore: false
|
||||
schedule:
|
||||
- title: Ballet
|
||||
day: Mon
|
||||
start: '16:00'
|
||||
age: ''
|
||||
extern: true
|
||||
duration: 90
|
||||
# class: DSKids
|
||||
|
||||
- title: Turnier Latein
|
||||
weight: -1
|
||||
day: Tue
|
||||
start: '19:00'
|
||||
duration: 60
|
||||
class: Turnier
|
||||
- title: Turnier Standard
|
||||
weight: -1
|
||||
day: Tue
|
||||
start: '20:00'
|
||||
duration: 120
|
||||
class: Turnier
|
||||
|
||||
- title: Ballet
|
||||
day: Wed
|
||||
start: '10:30'
|
||||
age: ''
|
||||
extern: true
|
||||
duration: 90
|
||||
# class: DSKids
|
||||
- title: DS Kids 3
|
||||
day: Wed
|
||||
start: '16:45'
|
||||
age: '2014 - 2016'
|
||||
duration: 60
|
||||
class: DSKids
|
||||
- title: DS Teens 1
|
||||
day: Wed
|
||||
start: '18:00'
|
||||
age: '2004 - 2007'
|
||||
duration: 60
|
||||
class: DSTeens
|
||||
- title: DS Adults 0
|
||||
day: Wed
|
||||
start: '19:00'
|
||||
age: 'ab 18'
|
||||
duration: 60
|
||||
class: DSAdults
|
||||
- title: Breitensport
|
||||
subtitle: Standard
|
||||
day: Wed
|
||||
start: '20:00'
|
||||
duration: 90
|
||||
class: Breitensport
|
||||
|
||||
- title: Kindertanz 8
|
||||
subtitle: Turnieraufbau
|
||||
day: Thu
|
||||
start: '18:00'
|
||||
duration: 90
|
||||
class: [Kinder, Turnier]
|
||||
|
||||
- title: Kindertanz 0
|
||||
day: Fri
|
||||
start: '14:30'
|
||||
age: '2018 - 2020'
|
||||
duration: 60
|
||||
class: Kinder
|
||||
- title: Kindertanz 1
|
||||
day: Fri
|
||||
start: '15:30'
|
||||
age: '2016 - 2018'
|
||||
duration: 60
|
||||
class: Kinder
|
||||
- title: Kindertanz 2
|
||||
subtitle: Turnieraufbau
|
||||
day: Fri
|
||||
start: '16:30'
|
||||
age: ''
|
||||
duration: 150
|
||||
class: [Kinder, Turnier]
|
@ -1,5 +1,6 @@
|
||||
baseURL: https://tanzsportclub.vfl-sindelfingen.de/
|
||||
languageCode: de
|
||||
defaultContentLanguage: de
|
||||
title: TSC im VfL Sindelfingen e.V.
|
||||
theme: tsc_vfl
|
||||
relativeUrls: true
|
||||
|
4
scripts/nc-cal-sync/.gitignore
vendored
Normal file
4
scripts/nc-cal-sync/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/venv/
|
||||
__pycache__/
|
||||
|
||||
/login.json
|
14
scripts/nc-cal-sync/Pipfile
Normal file
14
scripts/nc-cal-sync/Pipfile
Normal file
@ -0,0 +1,14 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
caldav = "*"
|
||||
requests = "*"
|
||||
pyyaml = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.12"
|
433
scripts/nc-cal-sync/Pipfile.lock
generated
Normal file
433
scripts/nc-cal-sync/Pipfile.lock
generated
Normal file
@ -0,0 +1,433 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "0976c7c1fc6571d991ea2c8a387c220b8bb9ecb2c86fd86a69214de581ee3c76"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.12"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"caldav": {
|
||||
"hashes": [
|
||||
"sha256:4317131127d8793f740cff2fd256f369321fa49ad750f83d6f31780f7c16c67b",
|
||||
"sha256:e75e84824092e33a9e03ac693de3d01133a3e044fd50a1c542c7f78d1aff0cb2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56",
|
||||
"sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2024.12.14"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537",
|
||||
"sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa",
|
||||
"sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a",
|
||||
"sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294",
|
||||
"sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b",
|
||||
"sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd",
|
||||
"sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601",
|
||||
"sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd",
|
||||
"sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4",
|
||||
"sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d",
|
||||
"sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2",
|
||||
"sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313",
|
||||
"sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd",
|
||||
"sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa",
|
||||
"sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8",
|
||||
"sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1",
|
||||
"sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2",
|
||||
"sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496",
|
||||
"sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d",
|
||||
"sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b",
|
||||
"sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e",
|
||||
"sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a",
|
||||
"sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4",
|
||||
"sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca",
|
||||
"sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78",
|
||||
"sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408",
|
||||
"sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5",
|
||||
"sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3",
|
||||
"sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f",
|
||||
"sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a",
|
||||
"sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765",
|
||||
"sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6",
|
||||
"sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146",
|
||||
"sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6",
|
||||
"sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9",
|
||||
"sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd",
|
||||
"sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c",
|
||||
"sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f",
|
||||
"sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545",
|
||||
"sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176",
|
||||
"sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770",
|
||||
"sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824",
|
||||
"sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f",
|
||||
"sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf",
|
||||
"sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487",
|
||||
"sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d",
|
||||
"sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd",
|
||||
"sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b",
|
||||
"sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534",
|
||||
"sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f",
|
||||
"sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b",
|
||||
"sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9",
|
||||
"sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd",
|
||||
"sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125",
|
||||
"sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9",
|
||||
"sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de",
|
||||
"sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11",
|
||||
"sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d",
|
||||
"sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35",
|
||||
"sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f",
|
||||
"sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda",
|
||||
"sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7",
|
||||
"sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a",
|
||||
"sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971",
|
||||
"sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8",
|
||||
"sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41",
|
||||
"sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d",
|
||||
"sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f",
|
||||
"sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757",
|
||||
"sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a",
|
||||
"sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886",
|
||||
"sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77",
|
||||
"sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76",
|
||||
"sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247",
|
||||
"sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85",
|
||||
"sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb",
|
||||
"sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7",
|
||||
"sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e",
|
||||
"sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6",
|
||||
"sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037",
|
||||
"sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1",
|
||||
"sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e",
|
||||
"sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807",
|
||||
"sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407",
|
||||
"sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c",
|
||||
"sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12",
|
||||
"sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3",
|
||||
"sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089",
|
||||
"sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd",
|
||||
"sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e",
|
||||
"sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00",
|
||||
"sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.4.1"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2",
|
||||
"sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.8"
|
||||
},
|
||||
"icalendar": {
|
||||
"hashes": [
|
||||
"sha256:43c2db8632959d634f4e48f6e6131e706bf2cdddad488cf0b72fda079b796bad",
|
||||
"sha256:46c09b774a6e6948495dafcb166dc15135c8259d0ae25491f154cbc822714b69"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==6.1.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
|
||||
"sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.10"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e",
|
||||
"sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229",
|
||||
"sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3",
|
||||
"sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5",
|
||||
"sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70",
|
||||
"sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15",
|
||||
"sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002",
|
||||
"sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd",
|
||||
"sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22",
|
||||
"sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf",
|
||||
"sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22",
|
||||
"sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832",
|
||||
"sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727",
|
||||
"sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e",
|
||||
"sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30",
|
||||
"sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f",
|
||||
"sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f",
|
||||
"sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51",
|
||||
"sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4",
|
||||
"sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de",
|
||||
"sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875",
|
||||
"sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42",
|
||||
"sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e",
|
||||
"sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6",
|
||||
"sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391",
|
||||
"sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc",
|
||||
"sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b",
|
||||
"sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237",
|
||||
"sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4",
|
||||
"sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86",
|
||||
"sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f",
|
||||
"sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a",
|
||||
"sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8",
|
||||
"sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f",
|
||||
"sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903",
|
||||
"sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03",
|
||||
"sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e",
|
||||
"sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99",
|
||||
"sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7",
|
||||
"sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab",
|
||||
"sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d",
|
||||
"sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22",
|
||||
"sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492",
|
||||
"sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b",
|
||||
"sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3",
|
||||
"sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be",
|
||||
"sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469",
|
||||
"sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f",
|
||||
"sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a",
|
||||
"sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c",
|
||||
"sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a",
|
||||
"sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4",
|
||||
"sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94",
|
||||
"sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442",
|
||||
"sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b",
|
||||
"sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84",
|
||||
"sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c",
|
||||
"sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9",
|
||||
"sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1",
|
||||
"sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be",
|
||||
"sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367",
|
||||
"sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e",
|
||||
"sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21",
|
||||
"sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa",
|
||||
"sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16",
|
||||
"sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d",
|
||||
"sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe",
|
||||
"sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83",
|
||||
"sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba",
|
||||
"sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040",
|
||||
"sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763",
|
||||
"sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8",
|
||||
"sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff",
|
||||
"sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2",
|
||||
"sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a",
|
||||
"sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b",
|
||||
"sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce",
|
||||
"sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c",
|
||||
"sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577",
|
||||
"sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8",
|
||||
"sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71",
|
||||
"sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512",
|
||||
"sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540",
|
||||
"sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f",
|
||||
"sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2",
|
||||
"sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a",
|
||||
"sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce",
|
||||
"sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e",
|
||||
"sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2",
|
||||
"sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27",
|
||||
"sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1",
|
||||
"sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d",
|
||||
"sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1",
|
||||
"sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330",
|
||||
"sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920",
|
||||
"sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99",
|
||||
"sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff",
|
||||
"sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18",
|
||||
"sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff",
|
||||
"sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c",
|
||||
"sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179",
|
||||
"sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080",
|
||||
"sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19",
|
||||
"sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d",
|
||||
"sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70",
|
||||
"sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32",
|
||||
"sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a",
|
||||
"sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2",
|
||||
"sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79",
|
||||
"sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3",
|
||||
"sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5",
|
||||
"sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f",
|
||||
"sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d",
|
||||
"sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3",
|
||||
"sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b",
|
||||
"sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753",
|
||||
"sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9",
|
||||
"sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957",
|
||||
"sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033",
|
||||
"sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb",
|
||||
"sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656",
|
||||
"sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab",
|
||||
"sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b",
|
||||
"sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d",
|
||||
"sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd",
|
||||
"sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859",
|
||||
"sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11",
|
||||
"sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c",
|
||||
"sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a",
|
||||
"sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005",
|
||||
"sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654",
|
||||
"sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80",
|
||||
"sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e",
|
||||
"sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec",
|
||||
"sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7",
|
||||
"sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965",
|
||||
"sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945",
|
||||
"sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==5.3.0"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
|
||||
"sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
|
||||
"version": "==2.9.0.post0"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a",
|
||||
"sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"
|
||||
],
|
||||
"version": "==2024.2"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",
|
||||
"sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48",
|
||||
"sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086",
|
||||
"sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e",
|
||||
"sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133",
|
||||
"sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5",
|
||||
"sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484",
|
||||
"sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee",
|
||||
"sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5",
|
||||
"sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68",
|
||||
"sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a",
|
||||
"sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf",
|
||||
"sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99",
|
||||
"sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8",
|
||||
"sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85",
|
||||
"sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19",
|
||||
"sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc",
|
||||
"sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a",
|
||||
"sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1",
|
||||
"sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317",
|
||||
"sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c",
|
||||
"sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631",
|
||||
"sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d",
|
||||
"sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652",
|
||||
"sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5",
|
||||
"sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e",
|
||||
"sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b",
|
||||
"sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8",
|
||||
"sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476",
|
||||
"sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706",
|
||||
"sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563",
|
||||
"sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237",
|
||||
"sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b",
|
||||
"sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083",
|
||||
"sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180",
|
||||
"sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425",
|
||||
"sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e",
|
||||
"sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f",
|
||||
"sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725",
|
||||
"sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183",
|
||||
"sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab",
|
||||
"sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774",
|
||||
"sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725",
|
||||
"sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
|
||||
"sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5",
|
||||
"sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d",
|
||||
"sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290",
|
||||
"sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44",
|
||||
"sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed",
|
||||
"sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4",
|
||||
"sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba",
|
||||
"sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12",
|
||||
"sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==6.0.2"
|
||||
},
|
||||
"recurring-ical-events": {
|
||||
"hashes": [
|
||||
"sha256:7ea75a560bf0526f7a8294dd93e324cc5379d09d520c48d0ccb4958e591dc2ff",
|
||||
"sha256:a91c2de4350dc5da99ba3bad27232ea63f3ad2e2f22e9466718d770cb9a59317"
|
||||
],
|
||||
"version": "==3.4.0"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
|
||||
"sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.32.3"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
||||
"sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
|
||||
"version": "==1.17.0"
|
||||
},
|
||||
"tzdata": {
|
||||
"hashes": [
|
||||
"sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc",
|
||||
"sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"
|
||||
],
|
||||
"markers": "python_version >= '2'",
|
||||
"version": "==2024.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
|
||||
"sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"vobject": {
|
||||
"hashes": [
|
||||
"sha256:0fbdb982065cf4d1843a5d5950c88510041c6de026bda49c3502721de1c6ac3d",
|
||||
"sha256:ac44e5d7e2079d84c1d52c50a615b9bec4b1ba958608c4c7fe40cbf33247b38e"
|
||||
],
|
||||
"version": "==0.9.9"
|
||||
},
|
||||
"x-wr-timezone": {
|
||||
"hashes": [
|
||||
"sha256:1e475d2dedcd2128550cd88b4b5773a224e936e1c9f22ff50104622180455265",
|
||||
"sha256:37f3927ddc32970c330af97773dc12bf8e0d2deb9ede5c3c676dc13ed62feb11"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==2.0.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
33
scripts/nc-cal-sync/calendar_synchronizer/__init__.py
Normal file
33
scripts/nc-cal-sync/calendar_synchronizer/__init__.py
Normal file
@ -0,0 +1,33 @@
|
||||
from . import cli
|
||||
|
||||
from . import login, sync
|
||||
|
||||
import logging
|
||||
|
||||
_logMap = {
|
||||
0: logging.WARNING,
|
||||
1: logging.INFO,
|
||||
2: logging.DEBUG,
|
||||
3: 5
|
||||
}
|
||||
|
||||
_runMap = {
|
||||
'login': login.run,
|
||||
'sync': sync.run,
|
||||
}
|
||||
|
||||
def main():
|
||||
args = cli.getArgs(
|
||||
loginSpCb=login.buildSubparser,
|
||||
syncSpCb=sync.buildSubparser
|
||||
)
|
||||
logging.basicConfig()
|
||||
_l = logging.getLogger(__name__)
|
||||
_l.setLevel(_logMap.get(args.verbose, logging.DEBUG))
|
||||
|
||||
_l.debug('Parameters %s', args)
|
||||
if args.mode in _runMap:
|
||||
_runMap[args.mode](args)
|
||||
else:
|
||||
_l.error('Unknown mode %s', args.mode)
|
||||
|
3
scripts/nc-cal-sync/calendar_synchronizer/__main__.py
Normal file
3
scripts/nc-cal-sync/calendar_synchronizer/__main__.py
Normal file
@ -0,0 +1,3 @@
|
||||
import calendar_synchronizer
|
||||
|
||||
calendar_synchronizer.main()
|
118
scripts/nc-cal-sync/calendar_synchronizer/cal_helper.py
Normal file
118
scripts/nc-cal-sync/calendar_synchronizer/cal_helper.py
Normal file
@ -0,0 +1,118 @@
|
||||
import datetime as dt
|
||||
import re, logging
|
||||
import zoneinfo
|
||||
|
||||
from .sync import Event
|
||||
|
||||
class IcalHelper:
|
||||
|
||||
_WEEKDAY_MAP = {
|
||||
'Mon': 0,
|
||||
'Tue': 1,
|
||||
'Wed': 2,
|
||||
'Thu': 3,
|
||||
'Fri': 4,
|
||||
'Sat': 5,
|
||||
'Sun': 6
|
||||
}
|
||||
|
||||
tzDE = zoneinfo.ZoneInfo('Europe/Berlin')
|
||||
deltaWeek = dt.timedelta(weeks=1)
|
||||
deltaDay = dt.timedelta(days=1)
|
||||
firstDay = dt.date(year=dt.datetime.now().year, month=1, day=1)
|
||||
|
||||
def __init__(self):
|
||||
self._l = logging.getLogger(__name__)
|
||||
self.reTime = re.compile(r'(\d{1,2}):(\d{2})')
|
||||
|
||||
def getStart(self, event: Event, holidays):
|
||||
firstDay = self._getFirstOccurence(event, holidays)
|
||||
|
||||
if firstDay is None:
|
||||
return None, None
|
||||
return firstDay, dt.datetime.combine(firstDay, self._getTime(event.start), tzinfo=self.tzDE)
|
||||
|
||||
def _getFirstWeekdayInYear(self, weekday):
|
||||
candidate = self.firstDay
|
||||
while candidate.weekday() != self._WEEKDAY_MAP[weekday]:
|
||||
candidate += self.deltaDay
|
||||
|
||||
self._l.log(5, 'First %s in year is %s', weekday, candidate)
|
||||
|
||||
return candidate
|
||||
|
||||
def _getSortedHolidays(self, holidays):
|
||||
return sorted(holidays['holidays'], key=lambda h: h['from'])
|
||||
|
||||
def _getSortedFeasts(self, holidays):
|
||||
return sorted(holidays['feasts'])
|
||||
|
||||
def _isFeast(self, candidate, feasts):
|
||||
return candidate in feasts
|
||||
|
||||
def _isHoliday(self, candidate, holidays):
|
||||
for h in holidays:
|
||||
if h['from'] <= candidate <= h['to']:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _getLastHolidayException(self, holidays):
|
||||
holidayList = list(self._getSortedHolidays(holidays))
|
||||
holidayList.reverse()
|
||||
lastHoliday = holidayList[0]
|
||||
lastHolidayDay = lastHoliday['to']
|
||||
|
||||
feastList = list(self._getSortedFeasts(holidays))
|
||||
feastList.reverse()
|
||||
lastFeast = feastList[0]
|
||||
lastFeastDay = lastFeast
|
||||
|
||||
return max(lastHolidayDay, lastFeastDay)
|
||||
|
||||
def _getFirstOccurence(self, event: Event, holidays):
|
||||
firstWeekday = self._getFirstWeekdayInYear(event.day)
|
||||
|
||||
|
||||
candidate = firstWeekday
|
||||
while candidate.year == firstWeekday.year:
|
||||
if self._isFeast(candidate, holidays['feasts']):
|
||||
self._l.log(5, 'Found feast %s', candidate)
|
||||
candidate += self.deltaWeek
|
||||
continue
|
||||
|
||||
if self._isHoliday(candidate, holidays['holidays']):
|
||||
self._l.log(5, 'Found holiday %s', candidate)
|
||||
candidate += self.deltaWeek
|
||||
continue
|
||||
|
||||
self._l.log(5, 'Found first occurence %s', candidate)
|
||||
return candidate
|
||||
|
||||
return None
|
||||
|
||||
# firstWeekday.tzinfo = dt.timezone.tzname('Europe/Berlin')
|
||||
|
||||
def _getTime(self, startStr):
|
||||
match = self.reTime.match(startStr)
|
||||
return dt.time(int(match.group(1)), int(match.group(2)))
|
||||
|
||||
def getHolidayExceptions(self, event: Event, startDay, holidays):
|
||||
lastHolidayDay = self._getLastHolidayException(holidays)
|
||||
|
||||
pointer = startDay
|
||||
exeptions = []
|
||||
while pointer <= lastHolidayDay:
|
||||
ex = dt.datetime.combine(pointer, self._getTime(event.start), tzinfo=self.tzDE)
|
||||
|
||||
if self._isFeast(pointer, holidays['feasts']):
|
||||
exeptions.append(ex)
|
||||
|
||||
if self._isHoliday(pointer, holidays['holidays']) and not event.external:
|
||||
exeptions.append(ex)
|
||||
|
||||
pointer += self.deltaWeek
|
||||
|
||||
if len(exeptions) > 0:
|
||||
return exeptions
|
||||
return None
|
16
scripts/nc-cal-sync/calendar_synchronizer/cli.py
Normal file
16
scripts/nc-cal-sync/calendar_synchronizer/cli.py
Normal file
@ -0,0 +1,16 @@
|
||||
import argparse
|
||||
|
||||
def getArgs(loginSpCb, syncSpCb):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0, help='Increase the verbosity')
|
||||
|
||||
subparsers = parser.add_subparsers(dest='mode')
|
||||
|
||||
loginSubparser = subparsers.add_parser('login')
|
||||
loginSpCb(loginSubparser)
|
||||
|
||||
syncSubparser = subparsers.add_parser('sync')
|
||||
syncSpCb(syncSubparser)
|
||||
|
||||
# parser.add_argument("url", help="The URL to try and cache")
|
||||
return parser.parse_args()
|
10
scripts/nc-cal-sync/calendar_synchronizer/debug.py
Normal file
10
scripts/nc-cal-sync/calendar_synchronizer/debug.py
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
_listening = False
|
||||
|
||||
def debugger():
|
||||
global _listening
|
||||
if not _listening:
|
||||
import debugpy
|
||||
debugpy.listen(5678)
|
||||
debugpy.wait_for_client()
|
||||
_listening = True
|
62
scripts/nc-cal-sync/calendar_synchronizer/login.py
Normal file
62
scripts/nc-cal-sync/calendar_synchronizer/login.py
Normal file
@ -0,0 +1,62 @@
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
import json
|
||||
|
||||
def buildSubparser(subparser):
|
||||
subparser.add_argument('--url', default='https://cloud.tsc-vfl.de')
|
||||
|
||||
def run(args):
|
||||
l = logging.getLogger(__name__)
|
||||
l.debug('Login to %s', args.url)
|
||||
|
||||
url = f'{args.url}/index.php/login/v2'
|
||||
l.debug('Using login url %s', url)
|
||||
|
||||
startRequest = requests.post(url)
|
||||
startRequest.raise_for_status()
|
||||
|
||||
data = startRequest.json()
|
||||
|
||||
print('You need to login in the browser now to validate the login token.')
|
||||
print('Please visit the following url:')
|
||||
print(data['login'])
|
||||
print()
|
||||
print('After you have logged in, press enter to continue.')
|
||||
|
||||
input()
|
||||
|
||||
l.debug('Logging in')
|
||||
intermediateToken = data['poll']['token']
|
||||
pollUrl = data['poll']['endpoint']
|
||||
|
||||
loginRequest = requests.post(pollUrl, data={'token': intermediateToken})
|
||||
loginRequest.raise_for_status()
|
||||
|
||||
data = loginRequest.json()
|
||||
|
||||
loginName = data['loginName']
|
||||
appPassword = data['appPassword']
|
||||
data = {
|
||||
'loginName': loginName,
|
||||
'appPassword': appPassword,
|
||||
'base': data['server']
|
||||
}
|
||||
|
||||
with open('login.json', 'w') as f:
|
||||
json.dump(data, f)
|
||||
|
||||
l.info('Login successful')
|
||||
|
||||
class LoginData:
|
||||
def __init__(self, loginName, appPassword, base):
|
||||
self.loginName = loginName
|
||||
self.appPassword = appPassword
|
||||
self.base = base
|
||||
|
||||
def loadLoginData():
|
||||
with open('login.json', 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
return LoginData(data['loginName'], data['appPassword'], data['base'])
|
||||
|
324
scripts/nc-cal-sync/calendar_synchronizer/sync.py
Normal file
324
scripts/nc-cal-sync/calendar_synchronizer/sync.py
Normal file
@ -0,0 +1,324 @@
|
||||
import logging, yaml, pprint, json, re
|
||||
import hashlib, uuid
|
||||
import caldav
|
||||
import datetime as dt
|
||||
|
||||
from . import login, debug
|
||||
|
||||
_l = logging.getLogger(__name__)
|
||||
|
||||
def buildSubparser(subparser):
|
||||
subparser.add_argument('--schedule', default='../../data/schedule.yaml')
|
||||
subparser.add_argument('--holidays', default='../../data/holidays.yaml')
|
||||
subparser.add_argument('-n', '--dry', action='store_true')
|
||||
subparser.add_argument('--recreate-all', action='store_true')
|
||||
|
||||
def run(args):
|
||||
loginData, schedule, holidays = _loadRawData(args)
|
||||
packedSchedule = _unpackSchedules(schedule)
|
||||
fixedSchedule = _addHolidayExceptions(packedSchedule, holidays)
|
||||
_synchonizeCalendars(args, fixedSchedule, loginData, holidays)
|
||||
|
||||
def _loadRawData(args):
|
||||
_l.info('Loading data from hard disk')
|
||||
loginData = login.loadLoginData()
|
||||
|
||||
with open(args.schedule, 'r') as f:
|
||||
schedule = yaml.safe_load(f.read())
|
||||
|
||||
_l.log(5, 'Schedule data:\n%s', pprint.pformat(schedule, indent=4, width=100))
|
||||
|
||||
with open(args.holidays, 'r') as f:
|
||||
holidays = yaml.safe_load(f.read())
|
||||
|
||||
_l.log(5, 'Holidays data:\n%s', pprint.pformat(holidays, indent=4, width=100))
|
||||
|
||||
_l.info('Data was read from hard disk')
|
||||
|
||||
return loginData, schedule, holidays
|
||||
|
||||
class Event:
|
||||
def __init__(self, day, start, duration, title):
|
||||
self.day = day
|
||||
self.start = start
|
||||
self.duration = duration
|
||||
self.title = title
|
||||
self.age = None
|
||||
self.external = False
|
||||
self.description = ''
|
||||
self.exceptions = []
|
||||
|
||||
def __repr__(self):
|
||||
wAge = f' ({self.age})' if self.age is not None else ''
|
||||
ext = 'Ext' if self.external else ''
|
||||
return f'{ext}Ev({self.title}{wAge}) [{self.day}, {self.start}, {self.duration}]'
|
||||
|
||||
def getHash(self, holidays):
|
||||
def fixHolidays(holidays):
|
||||
def fixDate(d):
|
||||
return d.strftime('%Y-%m-%d')
|
||||
|
||||
def fixFeast(f):
|
||||
return fixDate(f)
|
||||
|
||||
def fixHoliday(h):
|
||||
return {
|
||||
'from': fixDate(h['from']),
|
||||
'to': fixDate(h['to']),
|
||||
}
|
||||
|
||||
return {
|
||||
**holidays,
|
||||
'feasts': [fixFeast(f) for f in holidays['feasts']],
|
||||
'holidays': [fixHoliday(h) for h in holidays['holidays']],
|
||||
}
|
||||
|
||||
data = {
|
||||
'day': self.day,
|
||||
'start': self.start,
|
||||
'duration': self.duration,
|
||||
'title': self.title,
|
||||
'age': self.age,
|
||||
'external': self.external,
|
||||
'description': self.description,
|
||||
'exceptions': self.exceptions,
|
||||
"holidays": fixHolidays(holidays),
|
||||
}
|
||||
# pprint.pprint(data, indent=4, width=100)
|
||||
hasher = hashlib.sha1()
|
||||
hasher.update(json.dumps(data, sort_keys=True).encode('utf-8'))
|
||||
first = hasher.hexdigest()
|
||||
second = uuid.uuid4().hex
|
||||
return f'{first}___{second}', first
|
||||
|
||||
dDay = dt.timedelta(days=1)
|
||||
|
||||
_MAP_WEEKDAY_ICAL = {
|
||||
'Mon': 'MO',
|
||||
'Tue': 'TU',
|
||||
'Wed': 'WE',
|
||||
'Thu': 'TH',
|
||||
'Fri': 'FR',
|
||||
'Sat': 'SA',
|
||||
'Sun': 'SU',
|
||||
}
|
||||
|
||||
def addToCalendar(self, calendar: caldav.Calendar, holidays):
|
||||
from . import cal_helper
|
||||
|
||||
def getFirstTimeInYear():
|
||||
now = dt.datetime.now()
|
||||
d = dt.datetime(year=now.year, month=1, day=1)
|
||||
pass
|
||||
|
||||
helper = cal_helper.IcalHelper()
|
||||
startDay, start = helper.getStart(self, holidays)
|
||||
|
||||
if start is None:
|
||||
_l.warning('Could not get start time for event %s in the current year', self)
|
||||
return
|
||||
|
||||
uid, uidFirst = self.getHash(holidays)
|
||||
icalData = {
|
||||
'uid': uid,
|
||||
'DTSTART': start,
|
||||
'DTEND': start + dt.timedelta(minutes=self.duration),
|
||||
'SUMMARY': self.title,
|
||||
'RRULE': {
|
||||
'FREQ': 'DAILY',
|
||||
'BYDAY': self._MAP_WEEKDAY_ICAL[self.day]
|
||||
}
|
||||
}
|
||||
# debug.debugger()
|
||||
|
||||
holidayExceptions = helper.getHolidayExceptions(self, startDay, holidays)
|
||||
if holidayExceptions is not None:
|
||||
icalData['EXDATE'] = holidayExceptions
|
||||
|
||||
desc = ''
|
||||
if self.age is not None:
|
||||
desc += f'Jahrgänge: {self.age}'
|
||||
if len(self.description) > 0:
|
||||
desc += '\n\n'
|
||||
desc += self.description
|
||||
if len(desc) > 0:
|
||||
icalData['DESCRIPTION'] = desc
|
||||
|
||||
_l.log(5, 'Adding event\n%s', pprint.pformat(icalData, indent=4, width=100))
|
||||
|
||||
ical = caldav.lib.vcal.create_ical(**icalData)
|
||||
_l.log(5, 'Created event\n%s', pprint.pformat(ical, indent=4, width=100))
|
||||
_l.log(5, 'Created event\n%s', ical)
|
||||
calendar.add_event(**icalData)
|
||||
|
||||
def _unpackSchedules(schedule):
|
||||
def packSingleCalendar(cal):
|
||||
def parseSingleEvent(ev):
|
||||
e = Event(
|
||||
day=ev['day'],
|
||||
start=ev['start'],
|
||||
duration=ev['duration'],
|
||||
title=ev['title']
|
||||
)
|
||||
|
||||
if 'age' in ev:
|
||||
e.age = ev['age']
|
||||
if ev.get('extern', False) or ev.get('external', False):
|
||||
e.external = True
|
||||
if 'desc' in ev:
|
||||
e.description = ev['desc']
|
||||
if 'exceptions' in ev:
|
||||
raise Exception('Not yet implemented to have exceptions')
|
||||
|
||||
return e
|
||||
|
||||
_l.log(5, 'Unpacking calendar %s', cal)
|
||||
ret = { **cal }
|
||||
|
||||
if 'schedule' in ret:
|
||||
ret['schedule'] = [
|
||||
parseSingleEvent(e) for e in ret['schedule']
|
||||
]
|
||||
|
||||
_l.log(5, 'Unpacked calendar %s', ret)
|
||||
return ret
|
||||
|
||||
ret = {}
|
||||
|
||||
for calName in schedule['calendars']:
|
||||
if schedule['calendars'][calName].get('ignore', False):
|
||||
_l.info('Ignoring calendar %s', calName)
|
||||
continue
|
||||
|
||||
ret[calName] = packSingleCalendar(schedule['calendars'][calName])
|
||||
|
||||
_l.log(5, 'Unpacked schedule:\n%s', pprint.pformat(ret, indent=4, width=100))
|
||||
return ret
|
||||
|
||||
def _addHolidayExceptions(schedule, holidays):
|
||||
return schedule
|
||||
|
||||
class CalendarSynchonizer:
|
||||
def __init__(self, args, calId, calName, loginData):
|
||||
self.args = args
|
||||
self.calId = calId
|
||||
self.calName = calName
|
||||
self.loginData = loginData
|
||||
self._calDav = None
|
||||
|
||||
def _getUrl(self):
|
||||
return f'{self.loginData.base}/remote.php/dav/calendars'
|
||||
|
||||
def _getCalDav(self):
|
||||
|
||||
if self._calDav is None:
|
||||
self._calDav = caldav.DAVClient(
|
||||
url=self._getUrl(),
|
||||
username=self.loginData.loginName,
|
||||
password=self.loginData.appPassword
|
||||
)
|
||||
return self._calDav
|
||||
|
||||
def _getCalendar(self):
|
||||
cd = self._getCalDav()
|
||||
return cd.principal().calendar(cal_id=self.calId)
|
||||
|
||||
def synchonize(self, calendars, holidays):
|
||||
downstreamEvents = self._getDownstreamEvents(calendars[self.calName]['schedule'], holidays)
|
||||
_l.debug('Downstream events:\n%s', pprint.pformat(downstreamEvents, indent=4, width=100))
|
||||
|
||||
upstreamEvents = self._getUpstreamEvents()
|
||||
_l.debug('Upstream events:\n%s', pprint.pformat(upstreamEvents, indent=4, width=100))
|
||||
|
||||
newEvents = self._getNewEvents(upstreamEvents, downstreamEvents)
|
||||
_l.log(5, 'New events:\n%s', pprint.pformat(newEvents, indent=4, width=100))
|
||||
deletedEvents = self._getDeletedEvents(upstreamEvents, downstreamEvents)
|
||||
_l.log(5, 'Events marked for deletion:\n%s', pprint.pformat(deletedEvents, indent=4, width=100))
|
||||
|
||||
self._deleteEvents(deletedEvents)
|
||||
self._addEvents(newEvents, holidays)
|
||||
|
||||
def _getUpstreamEvents(self):
|
||||
_l.debug('Fetching upstream calendar entries')
|
||||
cd = self._getCalDav()
|
||||
|
||||
# principal = cd.principal()
|
||||
# _l.log(5, 'Principal %s', principal)
|
||||
|
||||
# calendar = principal.calendar(cal_id=self.calId)
|
||||
# _l.log(5, 'Calendar %s', calendar)
|
||||
|
||||
calendar = self._getCalendar()
|
||||
|
||||
# children = calendar.children()
|
||||
# _l.debug('Getting children for calendar %s:\n%s', self.calId, children)
|
||||
|
||||
events = calendar.events()
|
||||
|
||||
ret = {}
|
||||
|
||||
for e in events:
|
||||
uidRaw = str(e.icalendar_component['uid'])
|
||||
uid = uidRaw.split('___', 1)[0]
|
||||
_l.log(5, 'Event with uid %s was found.', uid)
|
||||
ret[uid] = e
|
||||
|
||||
return ret
|
||||
|
||||
def _getDownstreamEvents(self, schedule, holidays):
|
||||
_l.debug('Preparing local calendar events')
|
||||
|
||||
ret = {}
|
||||
|
||||
for e in schedule:
|
||||
uid, uidFirst = e.getHash(holidays)
|
||||
_l.log(5, 'Event with uid part %s was found.', uidFirst)
|
||||
ret[uidFirst] = e
|
||||
|
||||
return ret
|
||||
|
||||
def _getNewEvents(self, upstreamEvents, downstreamEvents):
|
||||
if self.args.recreate_all:
|
||||
return downstreamEvents.values()
|
||||
|
||||
upstreamUids = set(upstreamEvents.keys())
|
||||
downstreamUids = set(downstreamEvents.keys())
|
||||
newUids = downstreamUids - upstreamUids
|
||||
|
||||
return [downstreamEvents[uid] for uid in newUids]
|
||||
|
||||
def _getDeletedEvents(self, upstreamEvents, downstreamEvents):
|
||||
if self.args.recreate_all:
|
||||
return upstreamEvents.values()
|
||||
|
||||
upstreamUids = set(upstreamEvents.keys())
|
||||
downstreamUids = set(downstreamEvents.keys())
|
||||
toDeleteUids = upstreamUids - downstreamUids
|
||||
|
||||
return [upstreamEvents[uid] for uid in toDeleteUids]
|
||||
|
||||
def _addEvents(self, newEvents, holidays):
|
||||
cal = self._getCalendar()
|
||||
|
||||
for e in newEvents:
|
||||
_l.debug('Adding event %s', e)
|
||||
if self.args.dry:
|
||||
_l.info('Skipping add event %s (dry mode)', e)
|
||||
else:
|
||||
# e.save()
|
||||
e.addToCalendar(cal, holidays)
|
||||
|
||||
def _deleteEvents(self, oldEvents):
|
||||
for e in oldEvents:
|
||||
_l.debug('Deleting event %s', e)
|
||||
if self.args.dry:
|
||||
_l.info('Skipping delete event %s (dry mode)', e)
|
||||
else:
|
||||
e.delete()
|
||||
|
||||
def _synchonizeCalendars(args, calendars, loginData, holidays):
|
||||
for calName in calendars:
|
||||
calendar = calendars[calName]
|
||||
_l.info('Synching calendar %s', calName)
|
||||
calendarSynchonizer = CalendarSynchonizer(args, calendar['id'], calName, loginData)
|
||||
calendarSynchonizer.synchonize(calendars, holidays)
|
23
scripts/nc-cal-sync/helper/convert.sh
Executable file
23
scripts/nc-cal-sync/helper/convert.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "calendars:"
|
||||
for r in vorne mitte hinten
|
||||
do
|
||||
echo " $r:"
|
||||
echo " id: \"$r\""
|
||||
echo " ignore: true"
|
||||
echo " schedule:"
|
||||
echo -n " "
|
||||
(
|
||||
echo '['
|
||||
prefix_comma=''
|
||||
for d in Mo Di Mi Do Fr
|
||||
do
|
||||
echo -n "$prefix_comma"
|
||||
prefix_comma=', '
|
||||
cat ../../data/calendar.yaml | yq -cj '.calendar.'"$r.$d"' | . as $dict | [keys[] | . as $key | $dict[$key] | to_entries | [ .[], {key: "day", value: "'"$d"'"}, {key: "start", value: $key}, {key: "age", value: ""}, {key: "extern", value: false}, {key: "duration", value: (($dict[$key].slots) * 15)} ] | from_entries | del(.slots)]'
|
||||
done
|
||||
echo ']'
|
||||
) | jq -cj '[ .[][] ]'
|
||||
echo
|
||||
done
|
12
scripts/nc-cal-sync/helper/test.yq
Normal file
12
scripts/nc-cal-sync/helper/test.yq
Normal file
@ -0,0 +1,12 @@
|
||||
.calendar.vorne.Mo | . as $dict |
|
||||
keys[] | . as $key |
|
||||
$dict[$key] | to_entries |
|
||||
[
|
||||
.[],
|
||||
{key: "day", value: "Mo"},
|
||||
{key: "start", value: $key},
|
||||
{key: "age", value: ""},
|
||||
{key: "extern", value: false},
|
||||
{key: "duration", value: (($dict[$key].slots) * 15)}
|
||||
] |
|
||||
from_entries | del(.slots)
|
9
themes/tsc_vfl/assets/css/_colors.scss
Normal file
9
themes/tsc_vfl/assets/css/_colors.scss
Normal file
@ -0,0 +1,9 @@
|
||||
$color-red: #cd1013;
|
||||
|
||||
$color-background-mobile-menu: #f5f5f5;
|
||||
$color-background-mobile-menu-header: #e0e0e0;
|
||||
$color-hor-line: #a5a5a5;
|
||||
|
||||
$color-vh-vorne: #ddcb55;
|
||||
$color-vh-mitte: #c98879;
|
||||
$color-vh-hinten: #0082c9;
|
14
themes/tsc_vfl/assets/css/_responsive.scss
Normal file
14
themes/tsc_vfl/assets/css/_responsive.scss
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
@mixin media-large {
|
||||
@media screen and (min-width: 700px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mouse-available {
|
||||
@media screen and (pointer: fine) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
|
112
themes/tsc_vfl/assets/css/_schedule.scss
Normal file
112
themes/tsc_vfl/assets/css/_schedule.scss
Normal file
@ -0,0 +1,112 @@
|
||||
@use './responsive.scss' as r;
|
||||
@use './colors.scss' as *;
|
||||
|
||||
|
||||
.calendar-schedule {
|
||||
$calendar-height-row: 60px;
|
||||
|
||||
$border-style: solid lightgray 1px;
|
||||
|
||||
display: grid;
|
||||
|
||||
font-size: xx-small;
|
||||
|
||||
@include r.media-large {
|
||||
& {
|
||||
font-size: small;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
display: contents;
|
||||
font-weight: bold;
|
||||
div {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.rooms {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
div {
|
||||
flex: 1 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.main-column {
|
||||
border-right: $border-style;
|
||||
}
|
||||
}
|
||||
|
||||
.table-row {
|
||||
height: $calendar-height-row;
|
||||
box-sizing: border-box;
|
||||
|
||||
border-top: $border-style;
|
||||
}
|
||||
|
||||
.times-left {
|
||||
border-right: $border-style;
|
||||
}
|
||||
.times-left, .times-right{
|
||||
width: 100%;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.main-entry {
|
||||
position: relative;
|
||||
|
||||
border-right: $border-style;
|
||||
|
||||
.event {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
|
||||
top: calc($calendar-height-row * var(--minutes) / 60.0);
|
||||
height: calc($calendar-height-row * var(--duration) / 60.0);
|
||||
|
||||
width: 30%;
|
||||
padding: 1px 0;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
|
||||
--fg-color: black;
|
||||
|
||||
&.room-vorne {
|
||||
--bg-color: var(--color-vhvorne);
|
||||
left: 2.5%;
|
||||
}
|
||||
&.room-mitte {
|
||||
--bg-color: var(--color-vhmitte);
|
||||
left: 35%
|
||||
}
|
||||
&.room-hinten {
|
||||
--bg-color: var(--color-vhhinten);
|
||||
--fg-color: white;
|
||||
left: 67.5%;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
|
||||
color: var(--fg-color);
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-grid-2-days {
|
||||
grid-template-columns: auto repeat(2, 1fr) auto;
|
||||
}
|
||||
|
||||
.calendar-grid-3-days {
|
||||
grid-template-columns: auto repeat(3, 1fr) auto;
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
@use 'responsive.scss' as r;
|
||||
@use './schedule.scss';
|
||||
@use './colors.scss' as *;
|
||||
|
||||
/* Variables */
|
||||
$total-width: 95%;
|
||||
$color-red: #cd1013;
|
||||
$color-background-mobile-menu: #f5f5f5;
|
||||
$color-background-mobile-menu-header: #e0e0e0;
|
||||
$color-hor-line: #a5a5a5;
|
||||
$gap-columns-persons: 25px;
|
||||
$left-menu-width: 180px;
|
||||
$color-vh-vorne: #ddcb55;
|
||||
$color-vh-mitte: #c98879;
|
||||
$color-vh-hinten: #0082c9;
|
||||
|
||||
|
||||
/* Mixins */
|
||||
@ -23,17 +20,6 @@ $color-vh-hinten: #0082c9;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin media-large {
|
||||
@media screen and (min-width: 700px) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mouse-available {
|
||||
@media screen and (pointer: fine) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/* main styling */
|
||||
|
||||
@ -78,7 +64,7 @@ h1 {
|
||||
width: $left-menu-width;
|
||||
|
||||
|
||||
@include media-large {
|
||||
@include r.media-large {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@ -138,138 +124,10 @@ h1 {
|
||||
max-width: 100%;
|
||||
hyphens: auto;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
td, th {
|
||||
padding: 5px;
|
||||
border: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color: #dedede;
|
||||
|
||||
&:nth-of-type(2n) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
}
|
||||
|
||||
thead > tr {
|
||||
background-color: $color-red;
|
||||
color: white;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-manual {
|
||||
font-size: xx-small;
|
||||
|
||||
@include media-large {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
tr {
|
||||
height: 20px;
|
||||
|
||||
&.first-min {
|
||||
border-top: solid lightgray 1px;
|
||||
}
|
||||
|
||||
&:nth-of-type(n) {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.time {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.time:last-of-type, .first-col-of-room {
|
||||
border-left: solid lightgray 1px;
|
||||
}
|
||||
|
||||
.day-title {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-block {
|
||||
position: relative;
|
||||
|
||||
.calendar-block-entity {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 1.5px;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
&.height-1 {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&.height-2 {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&.height-3 {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
&.height-4 {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
&.height-5 {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
&.height-6 {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
&.height-7 {
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
&.height-8 {
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
&.height-9 {
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
&.height-10 {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.room-block {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.room-vorne {
|
||||
background-color: var(--color-vhvorne);
|
||||
}
|
||||
|
||||
.room-mitte {
|
||||
background-color: var(--color-vhmitte);
|
||||
}
|
||||
|
||||
.room-hinten {
|
||||
background-color: var(--color-vhhinten);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @include schedule.legacy;
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
margin: 7px 0 7px 15px;
|
||||
@ -308,6 +166,33 @@ h1 {
|
||||
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
td, th {
|
||||
padding: 5px;
|
||||
border: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color: #dedede;
|
||||
|
||||
&:nth-of-type(2n) {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
}
|
||||
|
||||
thead > tr {
|
||||
background-color: $color-red;
|
||||
color: white;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
// @include schedule.legacy;
|
||||
|
||||
#header {
|
||||
border-bottom: 2px solid rgba(173, 173, 173, 50%);
|
||||
margin: 0 auto 30px;
|
||||
@ -342,15 +227,16 @@ h1 {
|
||||
// height: 250px;
|
||||
display: none;
|
||||
|
||||
@include media-large {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
padding: 0 10px;
|
||||
border-right: 20px solid $color-red;
|
||||
border-left: 20px solid $color-red;
|
||||
position: relative;
|
||||
|
||||
@include r.media-large {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
||||
> img {
|
||||
width: calc(100% - 20px);
|
||||
|
||||
@ -406,7 +292,7 @@ h1 {
|
||||
font: 1.5em 'Open Sans Condensed', sans-serif;
|
||||
display: none;
|
||||
|
||||
@include media-large {
|
||||
@include r.media-large {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@ -525,15 +411,15 @@ h1 {
|
||||
.person {
|
||||
width: 100%;
|
||||
|
||||
@include media-large {
|
||||
width: calc(50% - #{$gap-columns-persons} / 2);
|
||||
}
|
||||
|
||||
height: 80px;
|
||||
|
||||
// margin: 10px 25px 10px 0;
|
||||
display: flex;
|
||||
|
||||
@include r.media-large {
|
||||
width: calc(50% - #{$gap-columns-persons} / 2);
|
||||
}
|
||||
|
||||
> .image {
|
||||
flex: 60px 0 0;
|
||||
|
||||
@ -587,7 +473,7 @@ h1 {
|
||||
margin-right: 2px;
|
||||
flex-direction: column;
|
||||
|
||||
@include media-large{
|
||||
@include r.media-large{
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
@ -596,7 +482,7 @@ h1 {
|
||||
display: block;
|
||||
height: 190px;
|
||||
|
||||
@include media-large{
|
||||
@include r.media-large{
|
||||
flex: 33% 1 0;
|
||||
}
|
||||
|
||||
@ -664,12 +550,11 @@ h1 {
|
||||
padding: 30px 5% 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
@include media-large {
|
||||
display: none;
|
||||
}
|
||||
|
||||
border-top: 1px solid $color-hor-line;
|
||||
|
||||
@include r.media-large {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.level-1 {
|
||||
width: 100%;
|
||||
@ -717,7 +602,7 @@ h1 {
|
||||
|
||||
@include menu-style;
|
||||
|
||||
@include media-large {
|
||||
@include r.media-large {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -749,7 +634,7 @@ h1 {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
|
||||
@include media-large {
|
||||
@include r.media-large {
|
||||
&.cols-2 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
@ -788,7 +673,7 @@ h1 {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
@include media-large {
|
||||
@include r.media-large {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
|
||||
@ -852,7 +737,7 @@ h1 {
|
||||
margin: 5px 0;
|
||||
align-items: baseline;
|
||||
|
||||
@include mouse-available {
|
||||
@include r.mouse-available {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -870,7 +755,7 @@ h1 {
|
||||
display: block;
|
||||
padding: 6.5px 0;
|
||||
|
||||
@include mouse-available {
|
||||
@include r.mouse-available {
|
||||
padding: 3px 0;
|
||||
}
|
||||
}
|
||||
@ -921,7 +806,7 @@ table.time {
|
||||
}
|
||||
}
|
||||
|
||||
@include media-large {
|
||||
@include r.media-large {
|
||||
display: table;
|
||||
|
||||
tr {
|
||||
|
@ -11,7 +11,7 @@
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ with .Keywords }}<meta name="keywords" content="{{ delimit . "," "," }}">{{ end }}
|
||||
{{ $options := (dict "targetPath" "main.css" "outputStyle" "compressed" "enableSourceMap" (not hugo.IsProduction)) }}
|
||||
{{ $options := (dict "targetPath" "main.css" "outputStyle" "compressed" "enableSourceMap" (not hugo.IsProduction) "transpiler" "dartsass") }}
|
||||
{{ $scss := resources.Get "css/main.scss" | css.Sass $options }}
|
||||
<link rel="stylesheet" type="text/css" href="{{ $scss.RelPermalink }}" />
|
||||
{{ $title := print .Site.Title " | " .Title }}
|
||||
|
18
themes/tsc_vfl/layouts/partials/tsc/calendar/list.html
Normal file
18
themes/tsc_vfl/layouts/partials/tsc/calendar/list.html
Normal file
@ -0,0 +1,18 @@
|
||||
{{ $calendars := site.Data.schedule.calendars -}}
|
||||
{{- $list := slice -}}
|
||||
{{- range $room, $roomData := $calendars -}}
|
||||
{{- if ($roomData.ignore | default false) -}}{{ continue }}{{- end -}}
|
||||
{{- $roomName := $roomData.name | default $room -}}
|
||||
{{/* warnf "%s " $room */}}
|
||||
{{- range $roomData.schedule -}}
|
||||
{{- $addData := dict "room" $roomName "roomId" $room "weight" 0 -}}
|
||||
{{- $addData = merge $addData . -}}
|
||||
{{/* warnf "%#v" $addData */}}
|
||||
{{- $list = $list | append $addData -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $list = sort $list "start" -}}
|
||||
{{- $list = sort $list "title" -}}
|
||||
{{- $list = sort $list "weight" -}}
|
||||
{{/* warnf "%#v" $list */}}
|
||||
{{- return $list -}}
|
83
themes/tsc_vfl/layouts/shortcodes/tsc/calendar/list.html
Normal file
83
themes/tsc_vfl/layouts/shortcodes/tsc/calendar/list.html
Normal file
@ -0,0 +1,83 @@
|
||||
{{- $list := partialCached "tsc/calendar/list" . -}}
|
||||
{{- $list = sort $list "weight" -}}
|
||||
{{- $list = sort $list "start" -}}
|
||||
{{- $list = sort $list "class" -}}
|
||||
{{- $list = sort $list "title" -}}
|
||||
{{- $cals := apply $list "index" "." "class" -}}
|
||||
{{- $cals = uniq $cals -}}
|
||||
{{/* warnf "%s" $cals */}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppe</th>
|
||||
<th>Kategorie</th>
|
||||
<th>Jahrgang</th>
|
||||
<th>Tag</th>
|
||||
<th>Zeit</th>
|
||||
<th>Ort</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $list -}}
|
||||
<tr>
|
||||
<td>
|
||||
{{ .title }}{{ with .subtitle }} - {{ . }}{{ end }}
|
||||
{{ if (.extern | default false) }}<i>(ext.)</i>{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
{{ with .class }}{{ . }}{{ end }}
|
||||
</td>
|
||||
<td>{{ with .age }}{{ . }}{{ end }}</td>
|
||||
<td>{{ index site.Data.days .day }}</td>
|
||||
<td>
|
||||
{{- $startTimeStr := printf "2025-01-02T%s:00" .start -}}
|
||||
{{- $startTime := time.AsTime $startTimeStr -}}
|
||||
{{- $duration := time.Duration "minute" .duration -}}
|
||||
{{- $endTime := $startTime.Add $duration}}
|
||||
{{/* warnf "Start %s, duration %s, %s" $startTime $duration $endTime */}}
|
||||
{{- $startTime.Format "15:04"}} - {{ $endTime.Format "15:04" }}
|
||||
</td>
|
||||
<td>{{ .room }}</td>
|
||||
</tr>
|
||||
{{- end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ range $cals }}
|
||||
{{- $class := . -}}
|
||||
<h2>Class {{ with . }}{{ . }}{{else}}<i>nil</i>{{end}}</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppe</th>
|
||||
<th>Jahrgang</th>
|
||||
<th>Tag</th>
|
||||
<th>Zeit</th>
|
||||
<th>Ort</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $list -}}
|
||||
{{- if ne .class $class }}{{ continue }}{{ end }}
|
||||
<tr>
|
||||
<td>
|
||||
{{ .title }}{{ with .subtitle }} - {{ . }}{{ end }}
|
||||
{{ if (.extern | default false) }}<i>(ext.)</i>{{ end }}
|
||||
</td>
|
||||
<td>{{ with .age }}{{ . }}{{ end }}</td>
|
||||
<td>{{ index site.Data.days .day }}</td>
|
||||
<td>
|
||||
{{- $startTimeStr := printf "2025-01-02T%s:00" .start -}}
|
||||
{{- $startTime := time.AsTime $startTimeStr -}}
|
||||
{{- $duration := time.Duration "minute" .duration -}}
|
||||
{{- $endTime := $startTime.Add $duration}}
|
||||
{{/* warnf "Start %s, duration %s, %s" $startTime $duration $endTime */}}
|
||||
{{- $startTime.Format "15:04"}} - {{ $endTime.Format "15:04" }}
|
||||
</td>
|
||||
<td>{{ .room }}</td>
|
||||
</tr>
|
||||
{{- end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
51
themes/tsc_vfl/layouts/shortcodes/tsc/calendar/schedule.html
Normal file
51
themes/tsc_vfl/layouts/shortcodes/tsc/calendar/schedule.html
Normal file
@ -0,0 +1,51 @@
|
||||
{{- $start := .Get 0 -}}
|
||||
{{- $end := .Get 1 -}}
|
||||
{{- $days := after 2 .Params -}}
|
||||
{{- $numDays := len $days -}}
|
||||
{{- $calendar := $.Site.Data.calendar.calendar -}}
|
||||
{{- $listSchedule := partialCached "tsc/calendar/list" . }}
|
||||
<div class="calendar-schedule calendar-grid-{{ $numDays }}-days">
|
||||
<div class="header">
|
||||
<div class="times-left"></div>
|
||||
{{ range $days }}
|
||||
<div class="main-column">{{ . }}</div>
|
||||
{{ end }}
|
||||
<div class="times-right"></div>
|
||||
<div class="times-left"></div>
|
||||
{{ range $days }}
|
||||
<div class="rooms main-column">
|
||||
<div>Vorne</div>
|
||||
<div>Mitte</div>
|
||||
<div>Hinten</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="times-right"></div>
|
||||
</div>
|
||||
{{ range (seq $start $end) }}
|
||||
{{- $currentHour := string . -}}
|
||||
{{- $addClass := "" -}}
|
||||
{{- $firstRow := eq . $start -}}
|
||||
{{- if eq . $start }}{{ $addClass = "first-main-row"}}{{ end -}}
|
||||
<div class="table-row times-left">{{ printf "%2d:00" . }}</div>
|
||||
{{- range $id, $day := $days }}
|
||||
<div class="table-row main-entry">
|
||||
{{- range $listSchedule }}
|
||||
{{- if ne .day $day }}{{ continue }}{{ end -}}
|
||||
{{- $sTime := time.AsTime (printf "2025-01-02T%s:00" .start) -}}
|
||||
{{- $evHour := $sTime.Format "15" -}}
|
||||
{{- if ne $currentHour $evHour }}{{ continue }}{{ end -}}
|
||||
{{- $hours := int ($sTime.Format "15") -}}
|
||||
{{- $minutes := int ($sTime.Format "4") -}}
|
||||
{{- $style := printf "--duration: %d; --minutes: %d;" .duration $minutes -}}
|
||||
{{/* warnf "%s" $style */}}
|
||||
<div class="event room-{{ .roomId }}" style="{{ $style | safeCSS }}">
|
||||
<div>
|
||||
{{ .title }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end -}}
|
||||
</div>
|
||||
{{ end -}}
|
||||
<div class="table-row times-right">{{ printf "%2d:00" . }}</div>
|
||||
{{ end }}
|
||||
</div>
|
39
themes/tsc_vfl/layouts/shortcodes/tsc/calendar/table.html
Normal file
39
themes/tsc_vfl/layouts/shortcodes/tsc/calendar/table.html
Normal file
@ -0,0 +1,39 @@
|
||||
{{- $list := partialCached "tsc/calendar/list" . -}}
|
||||
{{- $cat := .Get "category" -}}
|
||||
{{/* warnf "%#v" $cat */}}
|
||||
{{- $showAge := .Get "showAge" | default false -}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppe</th>
|
||||
{{ if $showAge }}<th>Jahrgang</th>{{ end }}
|
||||
<th>Tag</th>
|
||||
<th>Zeit</th>
|
||||
<th>Ort</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $list -}}
|
||||
{{- $cats := slice .class -}}
|
||||
{{- if eq (printf "%T" .class) "[]interface {}" }}{{ $cats = .class }}{{ end -}}
|
||||
{{- if not (in $cats $cat) }}{{ continue }}{{ end -}}
|
||||
{{/* warnf "%T %#v" .class . */}}
|
||||
<tr>
|
||||
<td>
|
||||
{{ .title }}{{ with .subtitle }} - {{ . }}{{ end }}
|
||||
</td>
|
||||
{{ if $showAge }}<td>{{ .age }}</td>{{ end }}
|
||||
<td>{{ index site.Data.days .day }}</td>
|
||||
<td>
|
||||
{{- $startTimeStr := printf "2025-01-02T%s:00" .start -}}
|
||||
{{- $startTime := time.AsTime $startTimeStr -}}
|
||||
{{- $duration := time.Duration "minute" .duration -}}
|
||||
{{- $endTime := $startTime.Add $duration}}
|
||||
{{/* warnf "Start %s, duration %s, %s" $startTime $duration $endTime */}}
|
||||
{{- $startTime.Format "15:04"}} - {{ $endTime.Format "15:04" }}
|
||||
</td>
|
||||
<td>{{ .room }}</td>
|
||||
</tr>
|
||||
{{- end}}
|
||||
</tbody>
|
||||
</table>
|
@ -1,64 +0,0 @@
|
||||
{{ $start := .Get 0 }}
|
||||
{{ $end := .Get 1}}
|
||||
{{ $days := after 2 .Params }}
|
||||
{{ $calendar := $.Site.Data.calendar.calendar }}
|
||||
<table class="calendar-manual">
|
||||
<tr>
|
||||
<th></th>
|
||||
{{ range $days }}
|
||||
<th colspan="3" class="day-title">{{ . }}</th>
|
||||
{{ end }}
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
{{ range $days }}
|
||||
<th class="first-col-of-room">vorne</th>
|
||||
<th>mitte</th>
|
||||
<th>hinten</th>
|
||||
{{ end }}
|
||||
<th></th>
|
||||
</tr>
|
||||
{{ range seq $start $end }}
|
||||
{{ $hour := . }}
|
||||
{{ range seq 0 15 45 }}
|
||||
{{ $firstMin := "" }}
|
||||
{{ if eq . 0 }}
|
||||
{{ $firstMin = "first-min" }}
|
||||
{{ end }}
|
||||
<tr class="{{ $firstMin }}">
|
||||
{{ $time := printf "%2d:%02d" $hour . }}
|
||||
{{ if or (eq . 0) (eq . 30) }}
|
||||
<td rowspan="2" class="time">{{ $time }}</td>
|
||||
{{ end }}
|
||||
{{ range $days }}
|
||||
{{ $day := . }}
|
||||
{{ $firstRoom := true }}
|
||||
{{ range slice "vorne" "mitte" "hinten" }}
|
||||
{{ $room := . }}
|
||||
{{ $addClass := "" }}
|
||||
{{ if $firstRoom }}
|
||||
{{ $addClass = "first-col-of-room" }}
|
||||
{{ $firstRoom = false }}
|
||||
{{ end }}
|
||||
{{ with index (index (index $calendar .) $day) $time }}
|
||||
<td class="calendar-block {{ $addClass }}">
|
||||
<div class="calendar-block-entity height-{{ .slots }}">
|
||||
<div class="room-block room-{{ $room }}">
|
||||
{{ .title }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{{ else }}
|
||||
<td class="{{ $addClass }}"></td>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if or (eq . 0) (eq . 30) }}
|
||||
{{ $time := printf "%2d.%02d" $hour . }}
|
||||
<td rowspan="2" class="time">{{ $time }}</td>
|
||||
{{ end }}
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</table>
|
Loading…
x
Reference in New Issue
Block a user