adds a schedule json endpoint. based on @taavi's PR #45 with some changes from the @pyohio/pyohio repo
This commit is contained in:
parent
c4db94b7e5
commit
51709c6eaf
6 changed files with 201 additions and 35 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
|
62
symposion/schedule/tests/factories.py
Normal file
62
symposion/schedule/tests/factories.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
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")
|
31
symposion/schedule/tests/test_views.py
Normal file
31
symposion/schedule/tests/test_views.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
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,9 +1,5 @@
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
from django.conf.urls.defaults import url, patterns
|
from django.conf.urls.defaults import url, patterns
|
||||||
from django.views.decorators.cache import cache_page
|
|
||||||
|
|
||||||
from symposion.schedule.views import schedule_json
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns("symposion.schedule.views",
|
urlpatterns = patterns("symposion.schedule.views",
|
||||||
url(r"^$", "schedule_conference", name="schedule_conference"),
|
url(r"^$", "schedule_conference", name="schedule_conference"),
|
||||||
|
@ -16,5 +12,5 @@ urlpatterns = patterns("symposion.schedule.views",
|
||||||
url(r"^([\w\-]+)/list/$", "schedule_list", name="schedule_list"),
|
url(r"^([\w\-]+)/list/$", "schedule_list", name="schedule_list"),
|
||||||
url(r"^([\w\-]+)/presentations.csv$", "schedule_list_csv", name="schedule_list_csv"),
|
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"^([\w\-]+)/edit/slot/(\d+)/", "schedule_slot_edit", name="schedule_slot_edit"),
|
||||||
url(r"^conference.json", cache_page(300)(schedule_json), name="schedule_json"),
|
url(r"^conference.json", "schedule_json", name="schedule_json"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.template import loader, Context
|
from django.template import loader, Context
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
from symposion.schedule.forms import SlotEditForm
|
from symposion.schedule.forms import SlotEditForm
|
||||||
from symposion.schedule.models import Schedule, Day, Slot, Presentation
|
from symposion.schedule.models import Schedule, Day, Slot, Presentation
|
||||||
|
@ -159,46 +164,39 @@ def schedule_presentation_detail(request, pk):
|
||||||
|
|
||||||
|
|
||||||
def schedule_json(request):
|
def schedule_json(request):
|
||||||
everything = bool(request.GET.get('everything'))
|
slots = Slot.objects.filter(day__schedule__published=True, day__schedule__hidden=False).order_by("start")
|
||||||
slots = Slot.objects.all().order_by("start")
|
|
||||||
|
protocol = request.META.get('HTTP_X_FORWARDED_PROTO', 'http')
|
||||||
data = []
|
data = []
|
||||||
for slot in slots:
|
for slot in slots:
|
||||||
if slot.content:
|
slot_data = {
|
||||||
slot_data = {
|
"room": ", ".join(room["name"] for room in slot.rooms.values()),
|
||||||
|
"rooms": [room["name"] for room in slot.rooms.values()],
|
||||||
|
"start": datetime.combine(slot.day.date, slot.start).isoformat(),
|
||||||
|
"end": datetime.combine(slot.day.date, slot.end).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,
|
"name": slot.content.title,
|
||||||
"room": ", ".join(room["name"] for room in slot.rooms.values()),
|
|
||||||
"start": slot.start_datetime.isoformat(),
|
|
||||||
"end": slot.end_datetime.isoformat(),
|
|
||||||
"duration": slot.length_in_minutes,
|
|
||||||
"authors": [s.name for s in slot.content.speakers()],
|
"authors": [s.name for s in slot.content.speakers()],
|
||||||
"released": slot.content.proposal.recording_release,
|
"contact": [
|
||||||
# You may wish to change this...
|
s.email for s in slot.content.speakers()
|
||||||
"license": "All Rights Reserved",
|
] if request.user.is_staff else ["redacted"],
|
||||||
"contact":
|
|
||||||
[s.email for s in slot.content.speakers()]
|
|
||||||
if request.user.is_staff
|
|
||||||
else ["redacted"],
|
|
||||||
"abstract": slot.content.abstract.raw,
|
"abstract": slot.content.abstract.raw,
|
||||||
"description": slot.content.description.raw,
|
"description": slot.content.description.raw,
|
||||||
"conf_key": slot.content.pk,
|
"content_href": "%s://%s%s" % (
|
||||||
"conf_url": "https://%s%s" % (
|
protocol,
|
||||||
Site.objects.get_current().domain,
|
Site.objects.get_current().domain,
|
||||||
reverse("schedule_presentation_detail", args=[slot.content.pk])
|
reverse("schedule_presentation_detail", args=[slot.content.pk])
|
||||||
),
|
),
|
||||||
"kind": slot.kind.label,
|
})
|
||||||
"tags": "",
|
|
||||||
}
|
|
||||||
elif everything:
|
|
||||||
slot_data = {
|
|
||||||
"room": ", ".join(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,
|
|
||||||
"title": slot.content_override.raw,
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
continue
|
slot_data.update({
|
||||||
|
"name": slot.content_override.raw if slot.content_override else "Slot",
|
||||||
|
})
|
||||||
data.append(slot_data)
|
data.append(slot_data)
|
||||||
|
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
|
|
Loading…
Reference in a new issue