diff --git a/src/camps/models.py b/src/camps/models.py index 2982dfec..f868fd64 100644 --- a/src/camps/models.py +++ b/src/camps/models.py @@ -1,7 +1,7 @@ import datetime from django.db import models from utils.models import UUIDModel, CreatedUpdatedModel -from program.models import EventType +from program.models import EventType, EventLocation from django.contrib.postgres.fields import DateTimeRangeField from psycopg2.extras import DateTimeTZRange from django.core.exceptions import ValidationError @@ -72,6 +72,11 @@ class Camp(CreatedUpdatedModel, UUIDModel): # return all event types with at least one event in this camp return EventType.objects.filter(event__instances__isnull=False, event__camp=self).distinct() + @property + def event_locations(self): + ''' Return all event locations with at least one event in this camp''' + return EventLocation.objects.filter(eventinstances__isnull=False, camp=self).distinct() + @property def logo_small(self): return 'img/%(slug)s/logo/%(slug)s-logo-small.png' % {'slug': self.slug} diff --git a/src/program/migrations/0019_auto_20170205_1940.py b/src/program/migrations/0019_auto_20170205_1940.py new file mode 100644 index 00000000..3d4ce550 --- /dev/null +++ b/src/program/migrations/0019_auto_20170205_1940.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-05 18:40 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0019_auto_20170131_1849'), + ('program', '0018_eventtype_notifications'), + ] + + operations = [ + migrations.CreateModel( + name='EventLocation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=100, unique=True)), + ('slug', models.SlugField()), + ('icon', models.URLField()), + ('camp', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='eventlocations', to='camps.Camp')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='eventinstance', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='eventinstances', to='program.EventLocation'), + ), + ] diff --git a/src/program/migrations/0020_auto_20170205_1940.py b/src/program/migrations/0020_auto_20170205_1940.py new file mode 100644 index 00000000..8d196893 --- /dev/null +++ b/src/program/migrations/0020_auto_20170205_1940.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-05 18:40 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('program', '0019_auto_20170205_1940'), + ] + + operations = [ + migrations.AlterField( + model_name='eventinstance', + name='location', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='eventinstances', to='program.EventLocation'), + ), + ] diff --git a/src/program/migrations/0021_auto_20170205_2130.py b/src/program/migrations/0021_auto_20170205_2130.py new file mode 100644 index 00000000..d85af26c --- /dev/null +++ b/src/program/migrations/0021_auto_20170205_2130.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-02-05 20:30 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('camps', '0019_auto_20170131_1849'), + ('program', '0020_auto_20170205_1940'), + ] + + operations = [ + migrations.AlterField( + model_name='eventlocation', + name='icon', + field=models.CharField(max_length=100), + ), + migrations.AlterField( + model_name='eventlocation', + name='name', + field=models.CharField(max_length=100), + ), + migrations.AlterUniqueTogether( + name='eventlocation', + unique_together=set([('camp', 'slug'), ('camp', 'name')]), + ), + ] diff --git a/src/program/models.py b/src/program/models.py index 1cb0bfcf..3697a787 100644 --- a/src/program/models.py +++ b/src/program/models.py @@ -1,4 +1,3 @@ - from django.contrib.postgres.fields import DateTimeRangeField from django.db import models from django.utils.text import slugify @@ -8,6 +7,20 @@ from django.core.exceptions import ValidationError from datetime import timedelta +class EventLocation(CreatedUpdatedModel): + """ The places where stuff happens """ + name = models.CharField(max_length=100) + slug = models.SlugField() + icon = models.CharField(max_length=100) + camp = models.ForeignKey('camps.Camp', null=True, related_name="eventlocations") + + def __str__(self): + return self.name + + class Meta: + unique_together = (('camp', 'slug'), ('camp', 'name')) + + class EventType(CreatedUpdatedModel): """ Every event needs to have a type. """ name = models.CharField(max_length=100, unique=True) @@ -52,6 +65,7 @@ class EventInstance(CreatedUpdatedModel): event = models.ForeignKey('program.event', related_name='instances') when = DateTimeRangeField() notifications_sent = models.BooleanField(default=False) + location = models.ForeignKey('program.EventLocation', related_name='eventinstances') class Meta: ordering = ['when'] @@ -61,17 +75,11 @@ class EventInstance(CreatedUpdatedModel): def __clean__(self): errors = [] - if self.when.lower > self.when.upper: - errors.append(ValidationError({'when', "Start should be earlier than finish"})) - - if self.when.lower.time().minute != 0 and self.when.lower.time().minute != 30: - errors.append(ValidationError({'when', "Start time minute should be 0 or 30."})) - - if self.when.upper.time().minute != 0 and self.when.upper.time().minute != 30: - errors.append(ValidationError({'when', "End time minute should be 0 or 30."})) + if self.location.camp != self.event.camp: + errors.append(ValidationError({'location', "Error: This location belongs to a different camp"})) if errors: - raise ValidationError(errors) + raise ValidationError(errors) @property def schedule_date(self): @@ -79,7 +87,8 @@ class EventInstance(CreatedUpdatedModel): Returns the schedule date of this eventinstance. Schedule date is determined by substracting settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS from the eventinstance start time. This means that if an event is scheduled for 00:30 wednesday evening (technically thursday) then the date - after substracting 5 hours would be wednesdays date, not thursdays. + after substracting 5 hours would be wednesdays date, not thursdays + (given settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS=5) """ return (self.when.lower-timedelta(hours=settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS)).date() diff --git a/src/program/templates/program_base.html b/src/program/templates/program_base.html index a223c3bf..c3cd2f10 100644 --- a/src/program/templates/program_base.html +++ b/src/program/templates/program_base.html @@ -1,49 +1,71 @@ {% extends 'schedule_base.html' %} +{% load dateutils %} {% block schedule_content %} -
-
- Overview - {% for day in camp.camp_days %} - {% with day.lower.date|date:"m" as month_padded %} - {% with day.lower.date|date:"d" as day_padded %} - - {{ day.lower.date|date:"l" }} - - {% endwith %} +
+ +
-
-

- -

-
- - {% if not urlyear %} - - {% if eventtype %}All{% else %}All{% endif %} - - {% else %} - - {% if eventtype %}All{% else %}All{% endif %} - - {% endif %} - - {% for event_type in camp.event_types %} +
+ + +
+ +
+ +
-

diff --git a/src/program/templates/program_overview.html b/src/program/templates/program_overview.html index 67fbb038..862234f3 100644 --- a/src/program/templates/program_overview.html +++ b/src/program/templates/program_overview.html @@ -4,22 +4,17 @@ {% for day in camp.camp_days %} {{ day.lower.date|date:"D d/m" }}
- {% for event in camp.events.all %} - {% for eventinstance in event.instances.all %} + {% for eventinstance in eventinstances %} {% if eventinstance.schedule_date == day.lower.date %} - {% if not eventtype or eventtype == eventinstance.event.event_type %} {{ eventinstance.when.lower|date:"H:i" }} - {{ eventinstance.when.upper|date:"H:i" }}
- {{ event.title }} + {{ eventinstance.event.title }}
- {% if event.speakers.exists %}by {{ event.speakers_list }}{% endif %}
{% endif %} - {% endif %} - {% endfor %} {% endfor %}

diff --git a/src/program/views.py b/src/program/views.py index cdfd9fad..9dd8358e 100644 --- a/src/program/views.py +++ b/src/program/views.py @@ -30,8 +30,7 @@ class ProgramOverviewView(CampViewMixin, ListView): model = models.Event template_name = 'program_overview.html' - def dispatch(self, *args, **kwargs): - """ If an event type has been supplied check if it is valid """ + def get_context_data(self, *args, **kwargs): if 'type' in self.request.GET: try: eventtype = models.EventType.objects.get( @@ -39,12 +38,27 @@ class ProgramOverviewView(CampViewMixin, ListView): ) except models.EventType.DoesNotExist: raise Http404 - return super(ProgramOverviewView, self).dispatch(*args, **kwargs) - def get_context_data(self, *args, **kwargs): + if 'location' in self.request.GET: + try: + eventlocation = models.EventLocation.objects.get( + slug=self.request.GET['location'], + camp=self.camp, + ) + except models.EventLocation.DoesNotExist: + raise Http404 + context = super(ProgramOverviewView, self).get_context_data(**kwargs) + eventinstances = models.EventInstance.objects.filter(event__in=self.camp.events.all()) + if 'type' in self.request.GET: - context['eventtype'] = models.EventType.objects.get(slug=self.request.GET['type']) + context['eventtype'] = eventtype + eventinstances = eventinstances.filter(event__event_type=eventtype) + + if 'location' in self.request.GET: + context['location'] = eventlocation + eventinstances = eventinstances.filter(location=eventlocation) + context['eventinstances'] = eventinstances return context @@ -76,7 +90,15 @@ class ProgramDayView(CampViewMixin, TemplateView): ) if ei.event.event_type != eventtype: skip.append(ei.id) - context['eventinstances'] = eventinstances.exclude(id__in=skip).order_by('event__event_type') + eventinstances = eventinstances.exclude(id__in=skip).order_by('event__event_type') + if 'location' in self.request.GET: + eventlocation = models.EventLocation.objects.get( + camp=self.camp, + slug=self.request.GET['location'] + ) + eventinstances = eventinstances.filter(location=eventlocation) + + context['eventinstances'] = eventinstances start = when + datetime.timedelta(hours=settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS) timeslots = [] @@ -94,6 +116,8 @@ class ProgramDayView(CampViewMixin, TemplateView): if 'type' in self.request.GET: context['eventtype'] = models.EventType.objects.get(slug=self.request.GET['type']) + if 'location' in self.request.GET: + context['location'] = models.EventLocation.objects.get(camp=self.camp, slug=self.request.GET['location']) return context diff --git a/src/utils/management/commands/bootstrap-devsite.py b/src/utils/management/commands/bootstrap-devsite.py index 76dba98e..2e2788d3 100644 --- a/src/utils/management/commands/bootstrap-devsite.py +++ b/src/utils/management/commands/bootstrap-devsite.py @@ -6,7 +6,7 @@ from news.models import NewsItem from shop.models import ProductCategory, Product from info.models import InfoCategory, InfoItem from villages.models import Village -from program.models import EventType, Event, EventInstance, Speaker +from program.models import EventType, Event, EventInstance, Speaker, EventLocation from django.contrib.auth.models import User @@ -133,7 +133,7 @@ class Command(BaseCommand): name='Slacking Off', slug='slacking-off', color='#0000ff', - light_text=False + light_text=True ) self.output("creating productcategories...") @@ -154,6 +154,32 @@ class Command(BaseCommand): for camp in [camp1, camp2, camp3]: year = camp.camp.lower.year + self.output('Creating eventlocations for {}...'.format(year)) + loc1 = EventLocation.objects.create( + name='Speakers Tent', + slug='speakers-tent', + icon='speakertent.png', + camp=camp + ) + loc2 = EventLocation.objects.create( + name='Workshop rooms', + slug='workshop-rooms', + icon='workshop.png', + camp=camp + ) + loc3 = EventLocation.objects.create( + name='Bar Area', + slug='bar-area', + icon='bararea.png', + camp=camp + ) + loc4 = EventLocation.objects.create( + name='Food Area', + slug='food-area', + icon='foodarea.png', + camp=camp + ) + self.output('Creating news for {}...'.format(year)) NewsItem.objects.create( title='Welcome to {}'.format(camp.title), @@ -648,6 +674,7 @@ programming for a danish startup. self.output("creating eventinstances for {}...".format(year)) EventInstance.objects.create( event=ev3, + location=loc1, when=( timezone.datetime(year, 8, 27, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 27, 13, 0, tzinfo=timezone.utc), @@ -655,6 +682,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev1, + location=loc1, when=( timezone.datetime(year, 8, 28, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 28, 13, 0, tzinfo=timezone.utc), @@ -662,6 +690,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev2, + location=loc1, when=( timezone.datetime(year, 8, 29, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 29, 13, 0, tzinfo=timezone.utc), @@ -669,6 +698,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 8, 27, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 28, 5, 0, tzinfo=timezone.utc), @@ -676,6 +706,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 8, 28, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 29, 5, 0, tzinfo=timezone.utc), @@ -683,6 +714,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 8, 29, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 30, 5, 0, tzinfo=timezone.utc), @@ -690,6 +722,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 8, 30, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 31, 5, 0, tzinfo=timezone.utc), @@ -697,6 +730,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 8, 31, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 1, 5, 0, tzinfo=timezone.utc), @@ -704,6 +738,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 9, 1, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 2, 5, 0, tzinfo=timezone.utc), @@ -711,6 +746,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev4, + location=loc3, when=( timezone.datetime(year, 9, 2, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 3, 5, 0, tzinfo=timezone.utc), @@ -718,6 +754,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev5, + location=loc1, when=( timezone.datetime(year, 8, 28, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 28, 13, 0, tzinfo=timezone.utc), @@ -725,6 +762,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev6, + location=loc1, when=( timezone.datetime(year, 8, 29, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 29, 13, 0, tzinfo=timezone.utc), @@ -732,6 +770,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev9, + location=loc1, when=( timezone.datetime(year, 8, 30, 11, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 30, 11, 30, tzinfo=timezone.utc), @@ -739,6 +778,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev10, + location=loc1, when=( timezone.datetime(year, 8, 30, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 30, 13, 0, tzinfo=timezone.utc), @@ -746,6 +786,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev12, + location=loc1, when=( timezone.datetime(year, 8, 30, 9, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 30, 11, 30, tzinfo=timezone.utc), @@ -753,6 +794,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev11, + location=loc1, when=( timezone.datetime(year, 8, 31, 14, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 31, 16, 0, tzinfo=timezone.utc), @@ -760,6 +802,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev18, + location=loc1, when=( timezone.datetime(year, 9, 2, 14, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 2, 15, 0, tzinfo=timezone.utc), @@ -767,6 +810,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev17, + location=loc1, when=( timezone.datetime(year, 9, 2, 16, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 2, 17, 0, tzinfo=timezone.utc), @@ -774,6 +818,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev15, + location=loc1, when=( timezone.datetime(year, 9, 1, 15, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 1, 16, 0, tzinfo=timezone.utc), @@ -781,6 +826,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev14, + location=loc1, when=( timezone.datetime(year, 8, 31, 21, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 31, 22, 0, tzinfo=timezone.utc), @@ -788,6 +834,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev16, + location=loc1, when=( timezone.datetime(year, 9, 1, 14, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 1, 15, 0, tzinfo=timezone.utc), @@ -795,6 +842,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev13, + location=loc1, when=( timezone.datetime(year, 8, 31, 17, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 31, 18, 0, tzinfo=timezone.utc), @@ -802,6 +850,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev19, + location=loc1, when=( timezone.datetime(year, 8, 30, 22, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 30, 23, 0, tzinfo=timezone.utc), @@ -809,6 +858,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev19, + location=loc1, when=( timezone.datetime(year, 8, 29, 22, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 29, 23, 0, tzinfo=timezone.utc), @@ -816,6 +866,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev19, + location=loc1, when=( timezone.datetime(year, 8, 28, 22, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 28, 23, 0, tzinfo=timezone.utc), @@ -823,6 +874,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev19, + location=loc1, when=( timezone.datetime(year, 8, 31, 22, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 31, 23, 0, tzinfo=timezone.utc), @@ -830,6 +882,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev19, + location=loc1, when=( timezone.datetime(year, 9, 1, 22, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 1, 23, 0, tzinfo=timezone.utc), @@ -837,6 +890,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev20, + location=loc1, when=( timezone.datetime(year, 9, 2, 20, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 2, 22, 0, tzinfo=timezone.utc), @@ -844,6 +898,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev21, + location=loc1, when=( timezone.datetime(year, 8, 28, 12, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 28, 13, 0, tzinfo=timezone.utc), @@ -851,6 +906,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev22, + location=loc1, when=( timezone.datetime(year, 8, 28, 18, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 28, 19, 0, tzinfo=timezone.utc), @@ -858,6 +914,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev23, + location=loc1, when=( timezone.datetime(year, 8, 29, 9, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 29, 11, 30, tzinfo=timezone.utc), @@ -865,6 +922,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev24, + location=loc1, when=( timezone.datetime(year, 8, 29, 20, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 29, 22, 0, tzinfo=timezone.utc), @@ -872,6 +930,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev25, + location=loc1, when=( timezone.datetime(year, 9, 1, 17, 0, tzinfo=timezone.utc), timezone.datetime(year, 9, 1, 18, 0, tzinfo=timezone.utc), @@ -879,6 +938,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev26, + location=loc1, when=( timezone.datetime(year, 8, 30, 11, 0, tzinfo=timezone.utc), timezone.datetime(year, 8, 30, 12, 0, tzinfo=timezone.utc), @@ -886,6 +946,7 @@ programming for a danish startup. ) EventInstance.objects.create( event=ev26, + location=loc1, when=( timezone.datetime(year, 9, 1, 11, 45, tzinfo=timezone.utc), timezone.datetime(year, 9, 1, 12, 30, tzinfo=timezone.utc), diff --git a/src/utils/templatetags/dateutils.py b/src/utils/templatetags/dateutils.py new file mode 100644 index 00000000..54b4c307 --- /dev/null +++ b/src/utils/templatetags/dateutils.py @@ -0,0 +1,8 @@ +from django import template +from django.utils.dateparse import parse_date +register = template.Library() + +@register.simple_tag +def get_weekday(year, month, day): + return parse_date("%(year)s-%(month)s-%(day)s" % {'year': year, 'month': month, 'day': day}).strftime("%A") +