Merge pull request #62 from codersquid/json_endpoint
Adds a schedule_json view
This commit is contained in:
commit
0fb224cbf6
7 changed files with 248 additions and 1 deletions
11
requirements-test.txt
Normal file
11
requirements-test.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
Django==1.4.15
|
||||
Pillow==2.5.3
|
||||
django-discover-runner==1.0
|
||||
django-markitup==2.2.2
|
||||
django-model-utils==1.5.0
|
||||
django-nose==1.2
|
||||
django-reversion==1.8.0
|
||||
django-timezones==0.2
|
||||
factory-boy==2.4.1
|
||||
nose==1.3.4
|
||||
pytz==2014.7
|
|
@ -1,3 +1,5 @@
|
|||
import datetime
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import models
|
||||
|
||||
|
@ -92,6 +94,29 @@ class Slot(models.Model):
|
|||
except ObjectDoesNotExist:
|
||||
return None
|
||||
|
||||
@property
|
||||
def start_datetime(self):
|
||||
return datetime.datetime(
|
||||
self.day.date.year,
|
||||
self.day.date.month,
|
||||
self.day.date.day,
|
||||
self.start.hour,
|
||||
self.start.minute)
|
||||
|
||||
@property
|
||||
def end_datetime(self):
|
||||
return datetime.datetime(
|
||||
self.day.date.year,
|
||||
self.day.date.month,
|
||||
self.day.date.day,
|
||||
self.end.hour,
|
||||
self.end.minute)
|
||||
|
||||
@property
|
||||
def length_in_minutes(self):
|
||||
return int(
|
||||
(self.end_datetime - self.start_datetime).total_seconds() / 60)
|
||||
|
||||
@property
|
||||
def rooms(self):
|
||||
return Room.objects.filter(pk__in=self.slotroom_set.values("room"))
|
||||
|
|
63
symposion/schedule/tests/factories.py
Normal file
63
symposion/schedule/tests/factories.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
import datetime
|
||||
import random
|
||||
|
||||
import factory
|
||||
from factory import fuzzy
|
||||
|
||||
from symposion.schedule.models import Schedule, Day, Slot, SlotKind
|
||||
from symposion.conference.models import Section, Conference
|
||||
|
||||
|
||||
class ConferenceFactory(factory.DjangoModelFactory):
|
||||
title = fuzzy.FuzzyText()
|
||||
start_date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1))
|
||||
end_date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1)
|
||||
+ datetime.timedelta(days=random.randint(1, 10)))
|
||||
# timezone = TimeZoneField("UTC")
|
||||
|
||||
class Meta:
|
||||
model = Conference
|
||||
|
||||
|
||||
class SectionFactory(factory.DjangoModelFactory):
|
||||
conference = factory.SubFactory(ConferenceFactory)
|
||||
name = fuzzy.FuzzyText()
|
||||
slug = fuzzy.FuzzyText()
|
||||
|
||||
class Meta:
|
||||
model = Section
|
||||
|
||||
|
||||
class ScheduleFactory(factory.DjangoModelFactory):
|
||||
section = factory.SubFactory(SectionFactory)
|
||||
published = True
|
||||
hidden = False
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
|
||||
|
||||
class SlotKindFactory(factory.DjangoModelFactory):
|
||||
schedule = factory.SubFactory(ScheduleFactory)
|
||||
label = fuzzy.FuzzyText()
|
||||
|
||||
class Meta:
|
||||
model = SlotKind
|
||||
|
||||
|
||||
class DayFactory(factory.DjangoModelFactory):
|
||||
schedule = factory.SubFactory(ScheduleFactory)
|
||||
date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1))
|
||||
|
||||
class Meta:
|
||||
model = Day
|
||||
|
||||
|
||||
class SlotFactory(factory.DjangoModelFactory):
|
||||
day = factory.SubFactory(DayFactory)
|
||||
kind = factory.SubFactory(SlotKindFactory)
|
||||
start = datetime.time(random.randint(0, 23), random.randint(0, 59))
|
||||
end = datetime.time(random.randint(0, 23), random.randint(0, 59))
|
||||
|
||||
class Meta:
|
||||
model = Slot
|
68
symposion/schedule/tests/runtests.py
Executable file
68
symposion/schedule/tests/runtests.py
Executable file
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# see runtests.py in https://github.com/pydanny/cookiecutter-djangopackage
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
from django.conf import settings
|
||||
|
||||
settings.configure(
|
||||
DEBUG=True,
|
||||
USE_TZ=True,
|
||||
DATABASES={
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
}
|
||||
},
|
||||
ROOT_URLCONF="symposion.schedule.urls",
|
||||
INSTALLED_APPS=[
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sites",
|
||||
|
||||
"markitup",
|
||||
"reversion",
|
||||
|
||||
"symposion",
|
||||
"symposion.conference",
|
||||
"symposion.speakers",
|
||||
"symposion.schedule",
|
||||
"symposion.proposals",
|
||||
|
||||
],
|
||||
SITE_ID=1,
|
||||
NOSE_ARGS=['-s'],
|
||||
|
||||
MARKITUP_FILTER=('django.contrib.markup.templatetags.markup.textile', {}),
|
||||
)
|
||||
|
||||
try:
|
||||
import django
|
||||
setup = django.setup
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
setup()
|
||||
|
||||
from django_nose import NoseTestSuiteRunner
|
||||
except ImportError:
|
||||
raise ImportError("To fix this error, run: pip install -r requirements-test.txt")
|
||||
|
||||
|
||||
def run_tests(*test_args):
|
||||
if not test_args:
|
||||
test_args = ['tests']
|
||||
|
||||
# Run tests
|
||||
test_runner = NoseTestSuiteRunner(verbosity=1)
|
||||
|
||||
failures = test_runner.run_tests(test_args)
|
||||
|
||||
if failures:
|
||||
sys.exit(failures)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests("symposion.schedule.tests.test_views")
|
30
symposion/schedule/tests/test_views.py
Normal file
30
symposion/schedule/tests/test_views.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import json
|
||||
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
|
||||
from . import factories
|
||||
|
||||
|
||||
class ScheduleViewTests(TestCase):
|
||||
|
||||
def test_empty_json(self):
|
||||
c = Client()
|
||||
r = c.get('/conference.json')
|
||||
assert r.status_code == 200
|
||||
|
||||
conference = json.loads(r.content)
|
||||
assert 'schedule' in conference
|
||||
assert len(conference['schedule']) == 0
|
||||
|
||||
def test_populated_empty_presentations(self):
|
||||
|
||||
factories.SlotFactory.create_batch(size=5)
|
||||
|
||||
c = Client()
|
||||
r = c.get('/conference.json')
|
||||
assert r.status_code == 200
|
||||
|
||||
conference = json.loads(r.content)
|
||||
assert 'schedule' in conference
|
||||
assert len(conference['schedule']) == 5
|
|
@ -1,7 +1,6 @@
|
|||
# flake8: noqa
|
||||
from django.conf.urls.defaults import url, patterns
|
||||
|
||||
|
||||
urlpatterns = patterns("symposion.schedule.views",
|
||||
url(r"^$", "schedule_conference", name="schedule_conference"),
|
||||
url(r"^edit/$", "schedule_edit", name="schedule_edit"),
|
||||
|
@ -13,4 +12,5 @@ urlpatterns = patterns("symposion.schedule.views",
|
|||
url(r"^([\w\-]+)/list/$", "schedule_list", name="schedule_list"),
|
||||
url(r"^([\w\-]+)/presentations.csv$", "schedule_list_csv", name="schedule_list_csv"),
|
||||
url(r"^([\w\-]+)/edit/slot/(\d+)/", "schedule_slot_edit", name="schedule_slot_edit"),
|
||||
url(r"^conference.json", "schedule_json", name="schedule_json"),
|
||||
)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.template import loader, Context
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
from symposion.schedule.forms import SlotEditForm
|
||||
from symposion.schedule.models import Schedule, Day, Slot, Presentation
|
||||
|
@ -156,3 +160,49 @@ def schedule_presentation_detail(request, pk):
|
|||
"schedule": schedule,
|
||||
}
|
||||
return render(request, "schedule/presentation_detail.html", ctx)
|
||||
|
||||
|
||||
def schedule_json(request):
|
||||
slots = Slot.objects.filter(
|
||||
day__schedule__published=True,
|
||||
day__schedule__hidden=False
|
||||
).order_by("start")
|
||||
|
||||
protocol = request.META.get('HTTP_X_FORWARDED_PROTO', 'http')
|
||||
data = []
|
||||
for slot in slots:
|
||||
slot_data = {
|
||||
"room": ", ".join(room["name"] for room in slot.rooms.values()),
|
||||
"rooms": [room["name"] for room in slot.rooms.values()],
|
||||
"start": slot.start_datetime.isoformat(),
|
||||
"end": slot.end_datetime.isoformat(),
|
||||
"duration": slot.length_in_minutes,
|
||||
"kind": slot.kind.label,
|
||||
"section": slot.day.schedule.section.slug,
|
||||
}
|
||||
if hasattr(slot.content, "proposal"):
|
||||
slot_data.update({
|
||||
"name": slot.content.title,
|
||||
"authors": [s.name for s in slot.content.speakers()],
|
||||
"contact": [
|
||||
s.email for s in slot.content.speakers()
|
||||
] if request.user.is_staff else ["redacted"],
|
||||
"abstract": slot.content.abstract.raw,
|
||||
"description": slot.content.description.raw,
|
||||
"content_href": "%s://%s%s" % (
|
||||
protocol,
|
||||
Site.objects.get_current().domain,
|
||||
reverse("schedule_presentation_detail", args=[slot.content.pk])
|
||||
),
|
||||
"cancelled": slot.content.cancelled,
|
||||
})
|
||||
else:
|
||||
slot_data.update({
|
||||
"name": slot.content_override.raw if slot.content_override else "Slot",
|
||||
})
|
||||
data.append(slot_data)
|
||||
|
||||
return HttpResponse(
|
||||
json.dumps({'schedule': data}),
|
||||
content_type="application/json"
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue