diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 00000000..2ebbf3b6 --- /dev/null +++ b/requirements-test.txt @@ -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 diff --git a/symposion/schedule/tests/factories.py b/symposion/schedule/tests/factories.py new file mode 100644 index 00000000..f4304e9a --- /dev/null +++ b/symposion/schedule/tests/factories.py @@ -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 diff --git a/symposion/schedule/tests/runtests.py b/symposion/schedule/tests/runtests.py new file mode 100755 index 00000000..dcb46455 --- /dev/null +++ b/symposion/schedule/tests/runtests.py @@ -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") diff --git a/symposion/schedule/tests/test_views.py b/symposion/schedule/tests/test_views.py new file mode 100644 index 00000000..04e04efd --- /dev/null +++ b/symposion/schedule/tests/test_views.py @@ -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 diff --git a/symposion/schedule/urls.py b/symposion/schedule/urls.py index c25a84ba..94acd3e1 100644 --- a/symposion/schedule/urls.py +++ b/symposion/schedule/urls.py @@ -1,9 +1,5 @@ # flake8: noqa 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", 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\-]+)/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", cache_page(300)(schedule_json), name="schedule_json"), + url(r"^conference.json", "schedule_json", name="schedule_json"), ) diff --git a/symposion/schedule/views.py b/symposion/schedule/views.py index a4d65b65..aa043994 100644 --- a/symposion/schedule/views.py +++ b/symposion/schedule/views.py @@ -1,8 +1,13 @@ +from datetime import datetime +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 @@ -159,46 +164,39 @@ def schedule_presentation_detail(request, pk): def schedule_json(request): - everything = bool(request.GET.get('everything')) - slots = Slot.objects.all().order_by("start") + 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: - 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, - "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()], - "released": slot.content.proposal.recording_release, - # You may wish to change this... - "license": "All Rights Reserved", - "contact": - [s.email for s in slot.content.speakers()] - 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, "description": slot.content.description.raw, - "conf_key": slot.content.pk, - "conf_url": "https://%s%s" % ( + "content_href": "%s://%s%s" % ( + protocol, Site.objects.get_current().domain, 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: - continue + slot_data.update({ + "name": slot.content_override.raw if slot.content_override else "Slot", + }) data.append(slot_data) return HttpResponse(