Implement periodic import of shifts
parent
38a72a97be
commit
3dd3c028e1
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.4 on 2022-04-23 00:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("app", "0005_alter_helper_phone"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="shift",
|
||||
name="deleted",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -0,0 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from .models import Calendar
|
||||
|
||||
|
||||
@admin.register(Calendar)
|
||||
class CalendarAdmin(admin.ModelAdmin):
|
||||
list_display = ("url", "has_errors")
|
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ImporterConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "shiftregister.importer"
|
@ -0,0 +1,83 @@
|
||||
from datetime import timezone
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from icalendar import Calendar
|
||||
from .models import Event, Room, Shift
|
||||
import requests
|
||||
|
||||
|
||||
def import_calendar(calendar):
|
||||
try:
|
||||
r = requests.get(calendar.url)
|
||||
r.raise_for_status()
|
||||
except:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
return False
|
||||
|
||||
if not r.headers["content-type"].startswith("text/calendar"):
|
||||
return False
|
||||
|
||||
try:
|
||||
cal = Calendar.from_ical(r.text)
|
||||
|
||||
rooms = {}
|
||||
events = {}
|
||||
for event in cal.walk("vevent"):
|
||||
uid = event.decoded("uid").decode()
|
||||
room = (
|
||||
event.decoded("location", None) or event.decoded("summary")
|
||||
).decode()
|
||||
start = event.decoded("dtstart").astimezone(timezone.utc)
|
||||
end = event.decoded("dtend").astimezone(timezone.utc)
|
||||
|
||||
if not uid or not room:
|
||||
return False
|
||||
|
||||
rooms[room] = None
|
||||
events[uid] = (
|
||||
room,
|
||||
{
|
||||
"start_at": start,
|
||||
"duration": end - start,
|
||||
"uuid": uid,
|
||||
"calendar": calendar,
|
||||
},
|
||||
)
|
||||
|
||||
with transaction.atomic():
|
||||
for r in Room.objects.filter(name__in=rooms):
|
||||
rooms[r.name] = r
|
||||
for room, r in rooms.items():
|
||||
if r == None:
|
||||
rooms[room] = Room(
|
||||
name=room, required_helpers=0
|
||||
) # required_helpers=0 ensures a shift in a new room is not displayed until the correct number of required helpers is set
|
||||
rooms[room].save()
|
||||
|
||||
for e in Event.objects.filter(calendar=calendar, uuid__in=events):
|
||||
uuid = str(e.uuid)
|
||||
room, event = events[uuid]
|
||||
|
||||
e.room = rooms[room]
|
||||
e.start_at = event["start_at"]
|
||||
e.duration = event["duration"]
|
||||
e.save()
|
||||
|
||||
events[uuid] = (room, e)
|
||||
for event in events:
|
||||
room, e = events[event]
|
||||
if not isinstance(e, Event):
|
||||
Event(room=rooms[room], **e).save()
|
||||
|
||||
for event in Event.objects.filter(calendar=calendar).exclude(
|
||||
uuid__in=events
|
||||
):
|
||||
event.deleted = True
|
||||
event.save()
|
||||
except:
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
return False
|
||||
|
||||
return True
|
@ -0,0 +1,49 @@
|
||||
# Generated by Django 4.0.4 on 2022-04-23 00:42
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("app", "0006_shift_deleted"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Calendar",
|
||||
fields=[
|
||||
("url", models.URLField(primary_key=True, serialize=False)),
|
||||
("has_errors", models.BooleanField(default=False, editable=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Event",
|
||||
fields=[
|
||||
(
|
||||
"shift_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
to="app.shift",
|
||||
),
|
||||
),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
(
|
||||
"calendar",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="importer.calendar",
|
||||
),
|
||||
),
|
||||
],
|
||||
bases=("app.shift",),
|
||||
),
|
||||
]
|
@ -0,0 +1,12 @@
|
||||
from django.db import models
|
||||
from shiftregister.app.models import *
|
||||
|
||||
|
||||
class Calendar(models.Model):
|
||||
url = models.URLField(primary_key=True)
|
||||
has_errors = models.BooleanField(default=False, editable=False)
|
||||
|
||||
|
||||
class Event(Shift):
|
||||
uuid = models.UUIDField(primary_key=True, editable=False)
|
||||
calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE)
|
@ -0,0 +1,10 @@
|
||||
from celery import shared_task
|
||||
from .importer import import_calendar
|
||||
from .models import Calendar
|
||||
|
||||
|
||||
@shared_task
|
||||
def import_shifts():
|
||||
for calendar in Calendar.objects.all():
|
||||
calendar.has_errors = not import_calendar(calendar)
|
||||
calendar.save()
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
Loading…
Reference in New Issue