add school holidays connector (#63013)

This commit is contained in:
Valentin Deniaud 2022-03-22 16:30:55 +01:00
parent 90f158a89f
commit 250afb34f8
12 changed files with 1180 additions and 1 deletions

1
debian/control vendored
View File

@ -38,6 +38,7 @@ Depends: ${python3:Depends},
python3-feedparser,
python3-pdfrw,
python3-httplib2,
python3-vobject,
python3-xmlschema
Description: Uniform access to multiple data sources and services (Python module)

View File

View File

@ -0,0 +1,32 @@
# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2022 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import ugettext_lazy as _
from passerelle.forms import GenericConnectorForm
class HolidaysConnectorForm(GenericConnectorForm):
def __init__(self, *args, **kwargs):
from .models import HOLIDAYS_CHOICES
super().__init__(*args, **kwargs)
self.fields['holidays'] = forms.MultipleChoiceField(
choices=HOLIDAYS_CHOICES,
widget=forms.CheckboxSelectMultiple,
initial=[x[0] for x in HOLIDAYS_CHOICES],
label=_('Holidays'),
)

View File

@ -0,0 +1,65 @@
# Generated by Django 2.2.19 on 2022-03-23 12:09
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('base', '0029_auto_20210202_1627'),
]
operations = [
migrations.CreateModel(
name='Holidays',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('title', models.CharField(max_length=50, verbose_name='Title')),
('slug', models.SlugField(unique=True, verbose_name='Identifier')),
('description', models.TextField(verbose_name='Description')),
(
'zone',
models.CharField(
choices=[('a', 'A'), ('b', 'B'), ('c', 'C')], max_length=16, verbose_name='Zone'
),
),
(
'holidays',
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
('winter_holidays', 'Vacances dHiver'),
('spring_holidays', 'Vacances de Pâques'),
('summer_holidays', 'Vacances dÉté'),
('all_saints_holidays', 'Vacances de la Toussaint'),
('christmas_holidays', 'Vacances de Noël'),
('fathers_day', 'Fête des Pères'),
('mothers_day', 'Fête des Mères'),
],
max_length=32,
),
size=None,
verbose_name='Holidays',
),
),
(
'users',
models.ManyToManyField(
blank=True,
related_name='_holidays_users_+',
related_query_name='+',
to='base.ApiUser',
),
),
],
options={
'verbose_name': 'School holidays',
},
),
]

View File

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2022 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import requests
import vobject
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.http import HttpResponse
from django.utils.translation import ugettext_lazy as _
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
from .forms import HolidaysConnectorForm
ZONE_CHOICES = (
('a', 'A'),
('b', 'B'),
('c', 'C'),
)
HOLIDAYS_LABELS = {
'winter_holidays': "Vacances dHiver",
'spring_holidays': 'Vacances de Pâques',
'summer_holidays': "Vacances dÉté",
'all_saints_holidays': 'Vacances de la Toussaint',
'christmas_holidays': 'Vacances de Noël',
}
HOLIDAYS_CHOICES = [(x[0], x[1]) for x in HOLIDAYS_LABELS.items()]
ZONE_URLS = {
'a': 'https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-A.ics',
'b': 'https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-B.ics',
'c': 'https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-C.ics',
}
HOLIDAYS_MAPPING = {
"Vacances d'Hiver": 'winter_holidays',
'Vacances de Printemps': 'spring_holidays',
"Vacances d'Été (Élèves)": 'summer_holidays',
"Vacances d'Été": 'summer_holidays',
'Vacances de la Toussaint': 'all_saints_holidays',
'Vacances de Noël': 'christmas_holidays',
}
class Holidays(BaseResource):
zone = models.CharField(_('Zone'), max_length=16, choices=ZONE_CHOICES)
holidays = ArrayField(
models.CharField(max_length=32, choices=HOLIDAYS_CHOICES),
verbose_name=_('Holidays'),
blank=False,
null=False,
)
category = _('Misc')
manager_form_base_class = HolidaysConnectorForm
class Meta:
verbose_name = _('School holidays')
def get_holidays_display(self):
return ', '.join(HOLIDAYS_LABELS[holiday] for holiday in self.holidays)
@endpoint(name='holidays.ics', description=_('Get holidays ICS.'))
def holidays_ics(self, request):
try:
response = requests.get(ZONE_URLS[self.zone])
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise APIError('Error while getting ICS file: %s' % e)
try:
parsed = vobject.readOne(response.text)
except vobject.base.ParseError as e:
raise APIError('Invalid ICS file: %s' % e)
if not parsed.contents.get('vevent'):
raise APIError('ICS file does not contain events.')
cal = vobject.iCalendar()
for vevent in parsed.contents.get('vevent'):
description = vevent.contents['description'][0].value
holiday_id = HOLIDAYS_MAPPING.get(description)
if holiday_id not in self.holidays:
continue
new_vevent = cal.add('vevent')
new_vevent.add('summary').value = HOLIDAYS_LABELS[holiday_id]
new_vevent.add('uid').value = '%s-%s' % (holiday_id, vevent.dtstart.value.year)
new_vevent.add('categories').value = [holiday_id]
new_vevent.add('dtstart').value = vevent.dtstart.value
new_vevent.add('dtend').value = vevent.dtend.value
return HttpResponse(cal.serialize(), content_type='text/calendar')

View File

@ -146,6 +146,7 @@ INSTALLED_APPS = (
'passerelle.apps.franceconnect_data',
'passerelle.apps.gdc',
'passerelle.apps.gesbac',
'passerelle.apps.holidays',
'passerelle.apps.jsondatastore',
'passerelle.apps.maelis',
'passerelle.apps.mdel',

View File

@ -125,7 +125,7 @@ urlpatterns += [
r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/$', GenericConnectorView.as_view(), name='view-connector'
),
url(
r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/(?P<endpoint>[\w,-]+)(?:/(?P<rest>.*))?$',
r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/(?P<endpoint>[\w,-.]+)(?:/(?P<rest>.*))?$',
GenericEndpointView.as_view(),
name='generic-endpoint',
),

View File

@ -163,6 +163,7 @@ setup(
'httplib2',
'xmlschema',
'pytz',
'vobject',
],
cmdclass={
'build': build,

View File

@ -0,0 +1,416 @@
BEGIN:VCALENDAR
PRODID:-//MENJS//Calendrier//FR
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:Calendrier scolaire - Zone A
X-WR-TIMEZONE:Europe/Paris
X-WR-CALDESC:Congés scolaires de la zone A depuis l'année 2017-2018
BEGIN:VTIMEZONE
TZID:Europe/Paris
X-LIC-LOCATION:Europe/Paris
BEGIN:DAYLIGHT
DTSTART:19700329T020000
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
TZNAME:CET
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19701025T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20171021
DTEND;VALUE=DATE:20171106
SUMMARY:Vacances de la Toussaint
UID:20220301T030012Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20171223
DTEND;VALUE=DATE:20180108
SUMMARY:Vacances de Noël
UID:20220301T030013Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180210
DTEND;VALUE=DATE:20180226
SUMMARY:Vacances d'Hiver
UID:20220301T030014Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180407
DTEND;VALUE=DATE:20180423
SUMMARY:Vacances de Printemps
UID:20220301T030015Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180707
DTEND;VALUE=DATE:20180831
SUMMARY:Vacances d'Été
UID:20220301T030016Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180707
DTEND;VALUE=DATE:20180903
SUMMARY:Vacances d'Été
UID:20220301T030017Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20181020
DTEND;VALUE=DATE:20181105
SUMMARY:Vacances de la Toussaint
UID:20220301T030018Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20181222
DTEND;VALUE=DATE:20190107
SUMMARY:Vacances de Noël
UID:20220301T030019Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190216
DTEND;VALUE=DATE:20190304
SUMMARY:Vacances d'Hiver
UID:20220301T030020Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190413
DTEND;VALUE=DATE:20190429
SUMMARY:Vacances de Printemps
UID:20220301T030021Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190529
DTEND;VALUE=DATE:20190603
SUMMARY:Pont de l'Ascension
UID:20220301T030022Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190706
DTEND;VALUE=DATE:20190830
SUMMARY:Vacances d'Été
UID:20220301T030023Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190706
DTEND;VALUE=DATE:20190902
SUMMARY:Vacances d'Été
UID:20220301T030024Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20191019
DTEND;VALUE=DATE:20191104
SUMMARY:Vacances de la Toussaint
UID:20220301T030025Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20191221
DTEND;VALUE=DATE:20200106
SUMMARY:Vacances de Noël
UID:20220301T030026Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200222
DTEND;VALUE=DATE:20200309
SUMMARY:Vacances d'Hiver
UID:20220301T030027Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200418
DTEND;VALUE=DATE:20200504
SUMMARY:Vacances de Printemps
UID:20220301T030028Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200520
DTEND;VALUE=DATE:20200525
SUMMARY:Pont de l'Ascension
UID:20220301T030029Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200704
DTEND;VALUE=DATE:20200901
SUMMARY:Vacances d'Été
UID:20220301T030030Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20201017
DTEND;VALUE=DATE:20201102
SUMMARY:Vacances de la Toussaint
UID:20220301T030031Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20201219
DTEND;VALUE=DATE:20210104
SUMMARY:Vacances de Noël
UID:20220301T030032Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210206
DTEND;VALUE=DATE:20210222
SUMMARY:Vacances d'Hiver
UID:20220301T030033Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210410
DTEND;VALUE=DATE:20210426
SUMMARY:Vacances de Printemps
UID:20220301T030034Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210513
DTEND;VALUE=DATE:20210517
SUMMARY:Pont de l'Ascension
UID:20220301T030035Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210706
DTEND;VALUE=DATE:20210901
SUMMARY:Vacances d'Été
UID:20220301T030036Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210706
DTEND;VALUE=DATE:20210902
SUMMARY:Vacances d'Été
UID:20220301T030037Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20211023
DTEND;VALUE=DATE:20211108
SUMMARY:Vacances de la Toussaint
UID:20220301T030038Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20211218
DTEND;VALUE=DATE:20220103
SUMMARY:Vacances de Noël
UID:20220301T030039Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220212
DTEND;VALUE=DATE:20220228
SUMMARY:Vacances d'Hiver
UID:20220301T030040Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220416
DTEND;VALUE=DATE:20220502
SUMMARY:Vacances de Printemps
UID:20220301T030041Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220526
DTEND;VALUE=DATE:20220528
SUMMARY:Pont de l'Ascension
UID:20220301T030042Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220707
DTEND;VALUE=DATE:20220831
SUMMARY:Vacances d'Été
UID:20220301T030043Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220707
DTEND;VALUE=DATE:20220901
SUMMARY:Vacances d'Été
UID:20220301T030044Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20221022
DTEND;VALUE=DATE:20221107
SUMMARY:Vacances de la Toussaint
UID:20220301T030045Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20221217
DTEND;VALUE=DATE:20230103
SUMMARY:Vacances de Noël
UID:20220301T030046Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230204
DTEND;VALUE=DATE:20230220
SUMMARY:Vacances d'Hiver
UID:20220301T030047Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230408
DTEND;VALUE=DATE:20230424
SUMMARY:Vacances de Printemps
UID:20220301T030048Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230518
DTEND;VALUE=DATE:20230522
SUMMARY:Pont de l'Ascension
UID:20220301T030049Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230708
DTEND;VALUE=DATE:20230708
SUMMARY:Début des Vacances d'Été
UID:20220301T030050Z-Zone-A@data.education.gouv.fr
LOCATION:
DESCRIPTION:Début des Vacances d'Été
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

View File

@ -0,0 +1,416 @@
BEGIN:VCALENDAR
PRODID:-//MENJS//Calendrier//FR
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:Calendrier scolaire - Zone B
X-WR-TIMEZONE:Europe/Paris
X-WR-CALDESC:Congés scolaires de la zone B depuis l'année 2017-2018
BEGIN:VTIMEZONE
TZID:Europe/Paris
X-LIC-LOCATION:Europe/Paris
BEGIN:DAYLIGHT
DTSTART:19700329T020000
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYDAY=-1SU
TZNAME:CET
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19701025T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20171021
DTEND;VALUE=DATE:20171106
SUMMARY:Vacances de la Toussaint
UID:20220301T030051Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20171223
DTEND;VALUE=DATE:20180108
SUMMARY:Vacances de Noël
UID:20220301T030052Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180224
DTEND;VALUE=DATE:20180312
SUMMARY:Vacances d'Hiver
UID:20220301T030053Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180421
DTEND;VALUE=DATE:20180507
SUMMARY:Vacances de Printemps
UID:20220301T030054Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180707
DTEND;VALUE=DATE:20180831
SUMMARY:Vacances d'Été
UID:20220301T030055Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20180707
DTEND;VALUE=DATE:20180903
SUMMARY:Vacances d'Été
UID:20220301T030056Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20181020
DTEND;VALUE=DATE:20181105
SUMMARY:Vacances de la Toussaint
UID:20220301T030057Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20181222
DTEND;VALUE=DATE:20190107
SUMMARY:Vacances de Noël
UID:20220301T030058Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190209
DTEND;VALUE=DATE:20190225
SUMMARY:Vacances d'Hiver
UID:20220301T030059Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190406
DTEND;VALUE=DATE:20190423
SUMMARY:Vacances de Printemps
UID:20220301T030100Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190529
DTEND;VALUE=DATE:20190603
SUMMARY:Pont de l'Ascension
UID:20220301T030101Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190706
DTEND;VALUE=DATE:20190830
SUMMARY:Vacances d'Été
UID:20220301T030102Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20190706
DTEND;VALUE=DATE:20190902
SUMMARY:Vacances d'Été
UID:20220301T030103Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20191019
DTEND;VALUE=DATE:20191104
SUMMARY:Vacances de la Toussaint
UID:20220301T030104Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20191221
DTEND;VALUE=DATE:20200106
SUMMARY:Vacances de Noël
UID:20220301T030105Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200215
DTEND;VALUE=DATE:20200302
SUMMARY:Vacances d'Hiver
UID:20220301T030106Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200411
DTEND;VALUE=DATE:20200427
SUMMARY:Vacances de Printemps
UID:20220301T030107Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200520
DTEND;VALUE=DATE:20200525
SUMMARY:Pont de l'Ascension
UID:20220301T030108Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20200704
DTEND;VALUE=DATE:20200901
SUMMARY:Vacances d'Été
UID:20220301T030109Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20201017
DTEND;VALUE=DATE:20201102
SUMMARY:Vacances de la Toussaint
UID:20220301T030110Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20201219
DTEND;VALUE=DATE:20210104
SUMMARY:Vacances de Noël
UID:20220301T030111Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210220
DTEND;VALUE=DATE:20210308
SUMMARY:Vacances d'Hiver
UID:20220301T030112Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210410
DTEND;VALUE=DATE:20210426
SUMMARY:Vacances de Printemps
UID:20220301T030113Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210513
DTEND;VALUE=DATE:20210517
SUMMARY:Pont de l'Ascension
UID:20220301T030114Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210706
DTEND;VALUE=DATE:20210901
SUMMARY:Vacances d'Été
UID:20220301T030115Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20210706
DTEND;VALUE=DATE:20210902
SUMMARY:Vacances d'Été
UID:20220301T030116Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20211023
DTEND;VALUE=DATE:20211108
SUMMARY:Vacances de la Toussaint
UID:20220301T030117Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20211218
DTEND;VALUE=DATE:20220103
SUMMARY:Vacances de Noël
UID:20220301T030118Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220205
DTEND;VALUE=DATE:20220221
SUMMARY:Vacances d'Hiver
UID:20220301T030119Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220409
DTEND;VALUE=DATE:20220425
SUMMARY:Vacances de Printemps
UID:20220301T030120Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220526
DTEND;VALUE=DATE:20220528
SUMMARY:Pont de l'Ascension
UID:20220301T030121Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220707
DTEND;VALUE=DATE:20220831
SUMMARY:Vacances d'Été
UID:20220301T030122Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Enseignants)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20220707
DTEND;VALUE=DATE:20220901
SUMMARY:Vacances d'Été
UID:20220301T030123Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Été (Élèves)
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20221022
DTEND;VALUE=DATE:20221107
SUMMARY:Vacances de la Toussaint
UID:20220301T030124Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de la Toussaint
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20221217
DTEND;VALUE=DATE:20230103
SUMMARY:Vacances de Noël
UID:20220301T030125Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Noël
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230211
DTEND;VALUE=DATE:20230227
SUMMARY:Vacances d'Hiver
UID:20220301T030126Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances d'Hiver
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230415
DTEND;VALUE=DATE:20230502
SUMMARY:Vacances de Printemps
UID:20220301T030127Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Vacances de Printemps
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230518
DTEND;VALUE=DATE:20230522
SUMMARY:Pont de l'Ascension
UID:20220301T030128Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Pont de l'Ascension
TRANSP:TRANSPARENT
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20220301T030012Z
DTSTART;VALUE=DATE:20230708
DTEND;VALUE=DATE:20230708
SUMMARY:Début des Vacances d'Été
UID:20220301T030129Z-Zone-B@data.education.gouv.fr
LOCATION:
DESCRIPTION:Début des Vacances d'Été
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

133
tests/test_holidays.py Normal file
View File

@ -0,0 +1,133 @@
import os
import pytest
import vobject
from httmock import HTTMock, with_httmock
from passerelle.apps.holidays.models import Holidays
from .test_manager import login
pytestmark = pytest.mark.django_db
with open(os.path.join(os.path.dirname(__file__), 'data/holidays/Zone-A.ics')) as fd:
RESPONSE_ZONE_A = fd.read()
with open(os.path.join(os.path.dirname(__file__), 'data/holidays/Zone-B.ics')) as fd:
RESPONSE_ZONE_B = fd.read()
def ics_data(url, request):
if url.path == 'https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-A.ics':
return {'content': RESPONSE_ZONE_A, 'request': request, 'status_code': 200}
if url.path == 'https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-B.ics':
return {'content': RESPONSE_ZONE_B, 'request': request, 'status_code': 200}
@with_httmock(ics_data)
def test_holidays_ics(app):
connector = Holidays.objects.create(slug='test', zone='a', holidays=[])
resp = app.get('/holidays/test/holidays.ics')
calendar = vobject.readOne(resp.text)
assert not 'vevent' in calendar.contents
connector.holidays.append('winter_holidays')
connector.save()
resp = app.get('/holidays/test/holidays.ics')
calendar = vobject.readOne(resp.text)
first_event = calendar.contents['vevent'][0]
assert first_event.uid.value == 'winter_holidays-2018'
assert first_event.summary.value == 'Vacances dHiver'
assert first_event.categories.value == ['winter_holidays']
assert str(first_event.dtstart.value) == '2018-02-10'
assert str(first_event.dtend.value) == '2018-02-26'
for i, event in enumerate(calendar.contents['vevent'][1:], start=2019):
assert event.uid.value == 'winter_holidays-%s' % i
assert event.summary.value == 'Vacances dHiver'
assert event.categories.value == ['winter_holidays']
connector.zone = 'b'
connector.save()
resp = app.get('/holidays/test/holidays.ics')
calendar = vobject.readOne(resp.text)
first_event = calendar.contents['vevent'][0]
assert first_event.uid.value == 'winter_holidays-2018'
assert str(first_event.dtstart.value) == '2018-02-24'
assert str(first_event.dtend.value) == '2018-03-12'
connector.holidays.append('summer_holidays')
connector.save()
resp = app.get('/holidays/test/holidays.ics')
calendar = vobject.readOne(resp.text)
assert calendar.contents['vevent'][0].uid.value == 'winter_holidays-2018'
first_summer_event = next(x for x in calendar.contents['vevent'] if x.uid.value.startswith('summer'))
assert first_summer_event.uid.value == 'summer_holidays-2018'
assert first_summer_event.summary.value == 'Vacances dÉté'
assert first_summer_event.categories.value == ['summer_holidays']
assert str(first_summer_event.dtstart.value) == '2018-07-07'
assert str(first_summer_event.dtend.value) == '2018-09-03'
connector.holidays = [
'winter_holidays',
'spring_holidays',
'summer_holidays',
'all_saints_holidays',
'christmas_holidays',
]
connector.save()
resp = app.get('/holidays/test/holidays.ics')
calendar = vobject.readOne(resp.text)
holiday_ids = {x.categories.value[0] for x in calendar.contents['vevent']}
assert all(holiday_id in holiday_ids for holiday_id in connector.holidays)
def test_holidays_ics_invalid_responses(app):
Holidays.objects.create(slug='test', zone='a', holidays=[])
def server_error(url, request):
return {'content': 'error', 'status_code': 500}
with HTTMock(server_error):
resp = app.get('/holidays/test/holidays.ics')
assert resp.json['err'] == 1
assert 'Error while getting ICS file' in resp.json['err_desc']
def invalid_file(url, request):
return {'content': 'invalid', 'status_code': 200}
with HTTMock(invalid_file):
resp = app.get('/holidays/test/holidays.ics')
assert resp.json['err'] == 1
assert 'Invalid ICS file' in resp.json['err_desc']
def empty_file(url, request):
return {
'content': 'BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//PYVOBJECT//NONSGML Version 1//EN\r\nEND:VCALENDAR\r\n',
'status_code': 200,
}
with HTTMock(empty_file):
resp = app.get('/holidays/test/holidays.ics')
assert resp.json['err'] == 1
assert 'ICS file does not contain events' in resp.json['err_desc']
def test_holidays_manager(app, admin_user):
login(app)
resp = app.get('/manage/holidays/add')
resp.form['title'] = 'Holidays'
resp.form['slug'] = 'test'
resp.form['description'] = 'test'
resp.form['zone'] = 'c'
resp.form['holidays'] = ['summer_holidays', 'christmas_holidays']
resp = resp.form.submit().follow()
assert 'Holidays:\n Vacances dÉté, Vacances de Noël' in resp.text
assert 'Zone:\n C' in resp.text