aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlban Gruin2019-08-30 13:59:13 +0200
committerAlban Gruin2019-09-01 13:28:32 +0200
commit683409424325e7048c802960e81585320904fac1 (patch)
treee752d9d9d4d24a45653af0bdd413f72618734d2b
parent0ad58a9268130e49c017f13beacb618f466a722f (diff)
parenta85f2fb91d1a4a0e31c41c392e404d7ddbf21109 (diff)
Merge branch 'ag/parser-ups-2019' into futur
-rw-r--r--admin.py4
-rw-r--r--management/parsers/ups2018.py10
-rw-r--r--management/parsers/ups2019.py126
-rw-r--r--models.py13
-rw-r--r--templatetags/rooms.py14
-rw-r--r--tests.py34
6 files changed, 178 insertions, 23 deletions
diff --git a/admin.py b/admin.py
index 5350aad..527b1eb 100644
--- a/admin.py
+++ b/admin.py
@@ -38,7 +38,7 @@ class YearAdmin(admin.ModelAdmin):
@admin.register(Source)
class SourceAdmin(admin.ModelAdmin):
- list_display = ("url", "last_update_date",)
+ list_display = ("url", "metadata", "last_update_date",)
@admin.register(Timetable)
@@ -75,7 +75,7 @@ class CourseAdmin(admin.ModelAdmin):
(None, {"fields": ("name", "type", "source", "groups", "rooms",)}),
("Horaires", {"fields": ("begin", "end",)}),
("Remarques", {"fields": ("notes",)}),
- ("Avancé", {"fields": ("celcat_id", "last_update",),
+ ("Avancé", {"fields": ("celcat_id", "last_update", "buggy",),
"classes": ("collapse",)}),)
list_display = ("name", "type", "source", "begin", "end",)
list_filter = ("type", "source__timetables", "groups",)
diff --git a/management/parsers/ups2018.py b/management/parsers/ups2018.py
index afbfc4b..0d6d798 100644
--- a/management/parsers/ups2018.py
+++ b/management/parsers/ups2018.py
@@ -32,6 +32,10 @@ from .abstractparser import AbstractParser, ParserError
VARNAME = "v.events.list = "
+GROUP_PREFIXES = ("L1 ", "L2 ", "L3 ", "L3P ", "M1 ", "M2 ", "DEUST ", "MAG1 ",
+ "1ERE ANNEE ", "2EME ANNEE ", "3EME ANNEE ",
+ "MAT-Agreg Interne ")
+
def find_events_list(soup):
res = []
@@ -123,11 +127,7 @@ class Parser(AbstractParser):
min_i = 1
i = min_i
- while i < len(data) and not data[i].startswith(
- ("L1 ", "L2 ", "L3 ", "L3P ", "M1 ", "M2 ", "DEUST ", "MAG1 ",
- "1ERE ANNEE ", "2EME ANNEE ", "3EME ANNEE ",
- "MAT-Agreg Interne ")
- ):
+ while i < len(data) and not data[i].startswith(GROUP_PREFIXES):
i += 1
groups = data[i]
diff --git a/management/parsers/ups2019.py b/management/parsers/ups2019.py
new file mode 100644
index 0000000..c6bd7e3
--- /dev/null
+++ b/management/parsers/ups2019.py
@@ -0,0 +1,126 @@
+# Copyright (C) 2019 Alban Gruin
+#
+# celcatsanitizer 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.
+#
+# celcatsanitizer 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 celcatsanitizer. If not, see <http://www.gnu.org/licenses/>.
+
+from datetime import date, datetime, timedelta
+from html import unescape
+
+from django.utils import timezone
+
+import requests
+
+from ...models import Course, Group, Module, Room
+from ...utils import get_current_week, get_week
+from .abstractparser import AbstractParser
+from .ups2018 import GROUP_PREFIXES
+
+
+class Parser(AbstractParser):
+ def __get_event(self, event, year, week):
+ if event["allDay"]:
+ return
+
+ begin = timezone.make_aware(
+ datetime.strptime(event["start"], "%Y-%m-%dT%H:%M:%S")
+ )
+ end = timezone.make_aware(
+ datetime.strptime(event["end"], "%Y-%m-%dT%H:%M:%S")
+ )
+
+ if year is not None and week is not None:
+ event_year, event_week, _ = begin.isocalendar()
+ if event_year != year or event_week != week:
+ return
+
+ data = [unescape(st.strip())
+ for st in event["description"].split("<br />")]
+ groups = []
+ rooms = []
+
+ course = Course.objects.create(
+ source=self.source, begin=begin, end=end,
+ celcat_id=event["id"]
+ )
+
+ max_i = len(data)
+
+ if event.get("eventCategory") is not None and \
+ len(event.get("eventCategory", "")) > 0:
+ course.type = event["eventCategory"]
+ max_i -= 1
+
+ if event.get("module", "") is not None and \
+ len(event.get("module", "")) > 0:
+ module, _ = Module.objects.get_or_create(name=event["module"])
+ course.module = module
+
+ i = 0
+ while i < max_i and not data[i].startswith(GROUP_PREFIXES):
+ rooms.append(data[i])
+ i += 1
+ course.rooms.add(*Room.objects.filter(name__in=rooms))
+
+ if len(rooms) != course.rooms.count():
+ print(rooms, course.rooms)
+
+ while i < max_i and data[i].startswith(GROUP_PREFIXES):
+ group, _ = Group.objects.get_or_create(source=self.source,
+ celcat_name=data[i])
+ groups.append(group)
+ i += 1
+ course.groups.add(*groups)
+
+ if i < max_i and course.module is not None and \
+ data[i].startswith(course.module.name):
+ course.name = data[i]
+ i += 1
+
+ course.notes = "\n".join(data[i:max_i]).strip()
+ if "other" in data[i]:
+ print("Warning: \"other\" in notes")
+
+ return course
+
+ def get_events(self, today, year=None, week=None):
+ for event in self.events:
+ course = self.__get_event(event, year, week)
+ if course is not None:
+ yield course
+
+ def get_update_date(self):
+ return
+
+ def get_weeks(self):
+ # FIXME: détection automatique à partir des événements présents
+ beginning, _ = get_week(*get_current_week())
+ self.weeks = {"1": beginning}
+
+ return self.weeks
+
+ def get_source(self):
+ start = date.today()
+ end = start + timedelta(days=365)
+
+ req = requests.post(self.source.url,
+ headers={"User-Agent": self.user_agent},
+ data={"calView": "month",
+ "resType": 103,
+ "federationIds[]": self.source.metadata,
+ "start": start.strftime("%Y-%m-%d"),
+ "end": end.strftime("%Y-%m-%d")})
+ req.encoding = "uft8"
+ req.raise_for_status()
+
+ self.events = req.json()
+ return self.events
diff --git a/models.py b/models.py
index b59f2d8..448b996 100644
--- a/models.py
+++ b/models.py
@@ -48,13 +48,15 @@ class Year(SlugModel):
class Source(models.Model):
- url = models.URLField(max_length=255, verbose_name="URL", unique=True)
+ url = models.URLField(max_length=255, verbose_name="URL")
+ metadata = models.CharField(max_length=256, verbose_name="Métadonnée",
+ blank=True, null=True)
last_update_date = models.DateTimeField(null=True, blank=True,
verbose_name="dernière mise à jour"
" Celcat")
def __str__(self):
- return self.url
+ return "{}, {}".format(self.url, self.metadata)
@property
def formatted_timetables(self):
@@ -62,6 +64,7 @@ class Source(models.Model):
self.timetables.all()])
class Meta:
+ unique_together = (("url", "metadata",),)
verbose_name = "source d’emploi du temps"
verbose_name_plural = "sources d’emploi du temps"
@@ -258,9 +261,12 @@ class Course(models.Model):
last_update = models.DateTimeField(verbose_name="dernière mise à jour",
default=timezone.now)
- celcat_id = models.IntegerField(verbose_name="ID Celcat", null=True)
+ celcat_id = models.CharField(max_length=64, verbose_name="ID Celcat",
+ null=True)
module = models.ForeignKey(Module, on_delete=models.SET_NULL, null=True)
+ buggy = models.BooleanField(verbose_name="Bogué", default=False)
+
def __str__(self):
return self.name
@@ -268,6 +274,7 @@ class Course(models.Model):
if self.type is not None:
self.type = self.type.replace("COURS", "cours")
self.type = self.type.replace("REUNION", "réunion")
+ self.type = self.type.replace("RENCONTRE", "rencontre")
if self.name is not None:
self.name = self.name.split("(")[0].strip()
diff --git a/templatetags/rooms.py b/templatetags/rooms.py
index f0e1b2e..d8b0e23 100644
--- a/templatetags/rooms.py
+++ b/templatetags/rooms.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Alban Gruin
+# Copyright (C) 2017, 2019 Alban Gruin
#
# celcatsanitizer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
@@ -18,11 +18,17 @@ from django import template
register = template.Library()
+def __filter_room_name(name):
+ if '/' in name:
+ return name.split('/')[1].strip()
+ return name
+
+
@register.filter
def format_rooms(rooms):
- amphi_list = [room.name for room in rooms if room.name.startswith("Amphi")]
- room_list = [room.name for room in rooms
- if not room.name.startswith("Amphi")]
+ names = [__filter_room_name(room.name) for room in rooms]
+ amphi_list = [name for name in names if name.startswith("Amphi")]
+ room_list = [name for name in names if not name.startswith("Amphi")]
amphis = ", ".join(amphi_list)
joined = ", ".join(room_list)
diff --git a/tests.py b/tests.py
index b411db3..1e80b48 100644
--- a/tests.py
+++ b/tests.py
@@ -341,6 +341,16 @@ class RoomTestCase(TestCase):
for i in range(5, 7)
]
+ # On n’insère pas ces salles dans la base de données, elles ne
+ # servent que pour le test de formatage.
+ self.formatted_rooms = [
+ Room(name="FSI / {}".format(str(i)))
+ for i in range(5)
+ ] + [
+ Room(name="FSI / Amphi {}".format(str(i)))
+ for i in range(5, 7)
+ ]
+
hours = [({"begin": datetime.time(hour=14, minute=0)},),
({"begin": datetime.time(hour=16, minute=0)},),
({"begin": datetime.time(hour=13, minute=30)},
@@ -364,23 +374,29 @@ class RoomTestCase(TestCase):
course.groups.add(group)
course.rooms.add(room)
- def test_format(self):
- amphis = self.rooms[-2:]
-
+ def __test_format(self, rooms, amphis):
self.assertEqual(format_rooms([]), "")
- self.assertEqual(format_rooms(self.rooms[:1]), "Salle 0")
- self.assertEqual(format_rooms(self.rooms[:2]), "Salles 0, 1")
+ self.assertEqual(format_rooms(rooms[:1]), "Salle 0")
+ self.assertEqual(format_rooms(rooms[:2]), "Salles 0, 1")
self.assertEqual(format_rooms([amphis[0]]), "Amphi 5")
self.assertEqual(format_rooms(amphis), "Amphi 5, Amphi 6")
- self.assertEqual(format_rooms([amphis[0]] + self.rooms[:1]),
+ self.assertEqual(format_rooms([amphis[0]] + rooms[:1]),
"Amphi 5, salle 0")
- self.assertEqual(format_rooms([amphis[0]] + self.rooms[:2]),
+ self.assertEqual(format_rooms([amphis[0]] + rooms[:2]),
"Amphi 5, salles 0, 1")
- self.assertEqual(format_rooms(amphis + self.rooms[:1]),
+ self.assertEqual(format_rooms(amphis + rooms[:1]),
"Amphi 5, Amphi 6, salle 0")
- self.assertEqual(format_rooms(amphis + self.rooms[:2]),
+ self.assertEqual(format_rooms(amphis + rooms[:2]),
"Amphi 5, Amphi 6, salles 0, 1")
+ def test_format(self):
+ amphis = self.rooms[-2:]
+ self.__test_format(self.rooms, amphis)
+
+ def test_reformat(self):
+ amphis = self.formatted_rooms[-2:]
+ self.__test_format(self.formatted_rooms, amphis)
+
def test_qsjps(self):
begin = timezone.make_aware(datetime.datetime.combine(
self.day, datetime.time(hour=15, minute=0)))