Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion blogs/tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class BlogParserTest(unittest.TestCase):
def setUpClass(cls):
super().setUpClass()
cls.test_file_path = get_test_rss_path()
cls.entries = get_all_entries("file://{}".format(cls.test_file_path))
cls.entries = get_all_entries("file:///{}".format(cls.test_file_path))

def test_entries(self):
self.assertEqual(len(self.entries), 25)
Expand Down
6 changes: 3 additions & 3 deletions events/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import requests

from .models import EventLocation, Event, OccurringRule
from .utils import convert_dt_to_aware
from .utils import extract_date_or_datetime

DATE_RESOLUTION = timedelta(1)
TIME_RESOLUTION = timedelta(0, 0, 1)
Expand All @@ -29,8 +29,8 @@ def import_occurrence(self, event, event_data):
# Django will already convert to datetime by setting the time to 0:00,
# but won't add any timezone information. We will convert them to
# aware datetime objects manually.
dt_start = convert_dt_to_aware(event_data['DTSTART'].dt)
dt_end = convert_dt_to_aware(event_data['DTEND'].dt)
dt_start = extract_date_or_datetime(event_data['DTSTART'].dt)
dt_end = extract_date_or_datetime(event_data['DTEND'].dt)

# Let's mark those occurrences as 'all-day'.
all_day = (
Expand Down
10 changes: 8 additions & 2 deletions events/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,10 @@ def duration(self):

@property
def single_day(self):
return self.dt_start.date() == self.dt_end.date()
return (
self.dt_start.date() == self.dt_end.date() or
self.all_day is True and self.dt_start + datetime.timedelta(days=1) == self.dt_end
)


def duration_default():
Expand Down Expand Up @@ -319,7 +322,10 @@ def dt_end(self):

@property
def single_day(self):
return self.dt_start.date() == self.dt_end.date()
return (
self.dt_start.date() == self.dt_end.date() or
self.all_day is True and self.dt_start + datetime.timedelta(days=1) == self.dt_end
)

def save(self, *args, **kwargs):
self.duration_internal = timedelta_parse(self.duration)
Expand Down
7 changes: 7 additions & 0 deletions events/templatetags/events.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django import template
from django.utils import timezone
from datetime import date, timedelta

from ..models import Event

Expand All @@ -14,3 +15,9 @@ def get_events_upcoming(limit=5, only_featured=False):
if only_featured:
qs.filter(featured=True)
return qs[:limit]


@register.filter_function
def exclude_ending_day(next_event_date):
return next_event_date - timedelta(days=1)

54 changes: 52 additions & 2 deletions events/tests/test_importer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import unittest

from django.test import TestCase
from django.utils.timezone import datetime, make_aware
from django.conf import settings

from events.importer import ICSImporter
Expand All @@ -20,7 +21,7 @@ def setUpClass(cls):

def test_injest(self):
importer = ICSImporter(self.calendar)
with open(EVENTS_CALENDAR) as fh:
with open(EVENTS_CALENDAR, encoding='utf-8') as fh:
ical = fh.read()
importer.import_events_from_text(ical)

Expand Down Expand Up @@ -61,6 +62,9 @@ def test_modified_event(self):
e.description.rendered,
'<a href="https://www.barcamptools.eu/pycamp201604">PythonCamp Cologne 2016</a>'
)
self.assertTrue(e.next_or_previous_time.all_day)
self.assertEqual(e.next_or_previous_time.dt_start, make_aware(datetime(year=2016, month=4, day=2)))
self.assertEqual(e.next_or_previous_time.dt_end, make_aware(datetime(year=2016, month=4, day=4)))

ical = """\
BEGIN:VCALENDAR
Expand Down Expand Up @@ -94,3 +98,49 @@ def test_modified_event(self):
self.assertEqual(e.pk, e2.pk)
self.assertEqual(e2.calendar.url, EVENTS_CALENDAR_URL)
self.assertEqual(e2.description.rendered, 'Python Istanbul')
self.assertTrue(e2.next_or_previous_time.all_day)
self.assertEqual(e2.next_or_previous_time.dt_start, make_aware(datetime(year=2016, month=4, day=2)))
self.assertEqual(e2.next_or_previous_time.dt_end, make_aware(datetime(year=2016, month=4, day=4)))

ical = """\
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//www.marudot.com//iCal Event Maker
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/London
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/London
X-LIC-LOCATION:Europe/London
BEGIN:DAYLIGHT
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
TZNAME:BST
DTSTART:19700329T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
TZNAME:GMT
DTSTART:19701025T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20171005T184245Z
UID:20171005T184245Z-752317732@marudot.com
DTSTART;TZID="Europe/London":20171031T190000
DTEND;TZID="Europe/London":20171031T210000
SUMMARY:Python Sheffield
DESCRIPTION:Monthly Python user group in Sheffield\, United Kingdom http://groups.google.com/group/python-sheffield Twitter: @pysheff
LOCATION:GIST Lab in Sheffield (http://thegisthub.net/groups/gistlab/)
END:VEVENT
END:VCALENDAR
"""
importer.import_events_from_text(ical)

e3 = Event.objects.get(uid='20171005T184245Z-752317732@marudot.com')
self.assertEqual(e3.description.rendered, 'Monthly Python user group in Sheffield, United Kingdom http://groups.google.com/group/python-sheffield Twitter: @pysheff')
self.assertFalse(e3.next_or_previous_time.all_day)
self.assertEqual(e3.next_or_previous_time.dt_start, make_aware(datetime(year=2017, month=10, day=31, hour=19)))
self.assertEqual(e3.next_or_previous_time.dt_end, make_aware(datetime(year=2017, month=10, day=31, hour=21)))
38 changes: 37 additions & 1 deletion events/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ def test_occurring_event(self):
self.assertEqual(self.event.next_time, None)
self.assertTrue(self.event.is_past)

def test_occurring_event_single_day(self):
now = seconds_resolution(timezone.now())

occurring_time_dtstart = now + datetime.timedelta(days=3)
occurring_time_dtend = occurring_time_dtstart + datetime.timedelta(days=1)

ot = OccurringRule.objects.create(
event=self.event,
dt_start=occurring_time_dtstart,
dt_end=occurring_time_dtend,
all_day=True
)

self.assertEqual(self.event.next_time.dt_start, occurring_time_dtstart)
self.assertEqual(self.event.previous_time, None)
self.assertEqual(self.event.next_or_previous_time.dt_start, occurring_time_dtstart)
self.assertTrue(self.event.next_time.single_day)

def test_recurring_event(self):
now = seconds_resolution(timezone.now())

Expand All @@ -62,7 +80,6 @@ def test_recurring_event(self):
self.assertEqual(self.event.next_time.dt_start, recurring_time_dtstart)
self.assertTrue(rt.valid_dt_end())


rt.begin = now - datetime.timedelta(days=5)
rt.finish = now - datetime.timedelta(days=3)
rt.save()
Expand All @@ -71,6 +88,25 @@ def test_recurring_event(self):
self.assertEqual(event.next_time, None)
self.assertEqual(Event.objects.for_datetime().count(), 0)

def test_recurring_event_single_day(self):
now = seconds_resolution(timezone.now())

recurring_time_dtstart = now + datetime.timedelta(days=3)
recurring_time_dtend = recurring_time_dtstart + datetime.timedelta(days=1)

rt = RecurringRule.objects.create(
event=self.event,
begin=recurring_time_dtstart,
finish=recurring_time_dtend,
all_day=True,
duration="1 days"
)

self.assertEqual(self.event.next_time.dt_start, recurring_time_dtstart)
self.assertEqual(self.event.previous_time, None)
self.assertEqual(self.event.next_or_previous_time.dt_start, recurring_time_dtstart)
self.assertTrue(self.event.next_time.single_day)

def test_rrule(self):
now = seconds_resolution(timezone.now())

Expand Down
27 changes: 27 additions & 0 deletions events/tests/test_templatetags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.test import TestCase

import datetime

from ..templatetags.events import exclude_ending_day


class TemplateTagsTests(TestCase):
def test_exclude_ending_day(self):
ending_date = datetime.datetime(year=2017, month=10, day=9, hour=0, minute=0, second=0)

ending_date_after_filter = exclude_ending_day(ending_date)

self.assertEqual(
ending_date_after_filter,
datetime.datetime(year=2017, month=10, day=8)
)

def test_exclude_ending_date_1st_january(self):
ending_date = datetime.datetime(year=2017, month=1, day=1, hour=0, minute=0, second=0)

ending_date_after_filter = exclude_ending_day(ending_date)

self.assertEqual(
ending_date_after_filter,
datetime.datetime(year=2016, month=12, day=31)
)
4 changes: 4 additions & 0 deletions events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def minutes_resolution(dt):
return dt - dt.second * datetime.timedelta(0, 1, 0) - dt.microsecond * datetime.timedelta(0, 0, 1)


def extract_date_or_datetime(dt):
return dt if not isinstance(dt, datetime.datetime) else convert_dt_to_aware(dt)


def date_to_datetime(date, tzinfo=None):
if tzinfo is None:
tzinfo = pytz.UTC
Expand Down
2 changes: 1 addition & 1 deletion peps/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def convert_pep0():
for a Python.org Page returns the core body HTML necessary only
"""
pep0_path = os.path.join(settings.PEP_REPO_PATH, 'pep-0000.html')
pep0_content = open(pep0_path).read()
pep0_content = open(pep0_path, encoding="utf-8 ").read()

soup = BeautifulSoup(pep0_content)

Expand Down
49 changes: 42 additions & 7 deletions templates/events/event_detail.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "base.html" %}

{% load events %}

{% block page_title %}{{ object.title|striptags }} | {{ SITE_INFO.site_name }}{% endblock %}
{% block og_title %}{{ object.title|striptags }}{% endblock %}
Expand All @@ -25,25 +25,60 @@ <h1 class="single-event-title">
{% endif %}

{% with object.next_or_previous_time as next_time %}

{% if next_time.single_day %}
<!-- Single date with start and end times -->
<h3 class="single-event-date">
<time class="single-date" datetime="{{ next_time.dt_start|date:'Y-m-d' }}">{{ next_time.dt_start|date:"d N" }}</time>
<time class="time-start" datetime="{{ next_time.dt_start|date:'Y-m-d H:i' }}">{% if not next_time.all_day %}from {{ next_time.dt_start|date:"fA"|lower }} {{ next_time.dt_start|date:"e" }}{% endif %}</time>
<time class="single-date" datetime="{{ next_time.dt_start|date:'Y-m-d' }}">
{{ next_time.dt_start|date:"d N" }}
</time>
<time class="time-start" datetime="{{ next_time.dt_start|date:'Y-m-d H:i' }}">
{% if not next_time.all_day %}
from {{ next_time.dt_start|date:"fA"|lower }} {{ next_time.dt_start|date:"e" }}
{% endif %}
</time>

{% if next_time.valid_dt_end %}
<time class="time-end" datetime="{{ next_time.dt_end|date:'Y-m-d H:i' }}">{% if not next_time.all_day %}to {{ next_time.dt_end|date:"fA"|lower }} {{ next_time.dt_end|date:"e" }}</time>{% endif %}{% endif %},
<time class="time-end" datetime="{{ next_time.dt_end|date:'Y-m-d H:i' }}">
{% if not next_time.all_day %}
to {{ next_time.dt_end|date:"fA"|lower }} {{ next_time.dt_end|date:"e" }},
{% endif %}
</time>
{% endif %}
<time class="year" datetime="{{ next_time.dt_end|date:'Y' }}">{{ next_time.dt_end|date:"Y" }}</time>
</h3>

{% else %}

<!-- Different start and end dates -->
<h3 class="single-event-date">
<time class="date-start" datetime="{{ next_time.dt_start|date:'c' }}">From {{ next_time.dt_start|date:"d N" }}{% if not next_time.all_day %} at {{ next_time.dt_start|date:"fA"|lower }} {{ next_time.dt_start|date:"e" }}{% endif %}</time>
<time class="date-end" datetime="{{ next_time.dt_end|date:'c' }}">through {{ next_time.dt_end|date:"d N" }}{% if not next_time.all_day %} at {{ next_time.dt_end|date:"fA"|lower }} {{ next_time.dt_end|date:"e" }}{% endif %}</time>,
<time class="year" datetime="{{ next_time.dt_end|date:"Y" }}">{{ next_time.dt_end|date:"Y" }}</time>
<time class="date-start" datetime="{{ next_time.dt_start|date:'c' }}">
From {{ next_time.dt_start|date:"d N" }}
{% if not next_time.all_day %}
at {{ next_time.dt_start|date:"fA"|lower }} {{ next_time.dt_start|date:"e" }}
{% endif %}
</time>
<time class="date-end" datetime="{{ next_time.dt_end|date:'c' }}">
through
{% if not next_time.all_day %}
{{ next_time.dt_end|date:"d N" }} at {{ next_time.dt_end|date:"fA"|lower }} {{ next_time.dt_end|date:"e" }},
{% else %}
{{ next_time.dt_end|exclude_ending_day|date:"d N" }},
{% endif %}
</time>

{% if next_time.all_day %}
<time class="year" datetime="{{ next_time.dt_end|exclude_ending_day|date:"Y" }}">
{{ next_time.dt_end|exclude_ending_day|date:"Y" }}
</time>
{% else %}
<time class="year" datetime="{{ next_time.dt_end|date:"Y" }}">
{{ next_time.dt_end|date:"Y" }}
</time>
{% endif %}
</h3>
{% endif %}

{% endwith %}

<!-- Add a display for Recurrence Rules? -->
Expand Down
53 changes: 50 additions & 3 deletions templates/events/includes/time_tag.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
{% load events %}

{% with dt_start=next_time.dt_start %}

{% if next_time.single_day %}
<time datetime="{{ next_time.dt_start|date:'c' }}">{{ next_time.dt_start|date:"d N" }}<span class="say-no-more"> {{ next_time.dt_start|date:"Y" }}</span>{% if next_time.all_day %} {{ next_time.dt_start|date:"fA"|lower }} {{ next_time.dt_start|date:"e" }}{% if next_time.valid_dt_end %} – {{ next_time.dt_end|date:"fA"|lower }} {{ next_time.dt_end|date:"e" }}{% endif %}{% endif %}</time>

{% with dt_end=next_time.dt_end %}
<time datetime="{{ dt_start|date:'c' }}">
{{ dt_start|date:"d N" }}
<span class="say-no-more">
{{ dt_start|date:"Y" }}
</span>

{% if not next_time.all_day %}
{{ dt_start|date:"fA"|lower }} {{ dt_start|date:"e" }}
{% if next_time.valid_dt_end %}
&ndash; {{ dt_end|date:"fA"|lower }} {{ dt_end|date:"e" }}
{% endif %}
{% endif %}
</time>
{% endwith %}

{% elif next_time.all_day %}

{% with dt_end=next_time.dt_end|exclude_ending_day %}
<time datetime="{{ dt_start|date:'c' }}">
{{ dt_start|date:"d N" }}
{% if next_time.valid_dt_end %}
&ndash; {{ dt_end|date:"d N" }}
{% endif %}
<span class="say-no-more">
{{ dt_end|date:"Y" }}
</span>
</time>
{% endwith %}

{% else %}
<time datetime="{{ next_time.dt_start|date:'c' }}">{{ next_time.dt_start|date:"d N" }}{% if next_time.valid_dt_end %} &ndash; {{ next_time.dt_end|date:"d N" }}{% endif %} <span class="say-no-more"> {{ next_time.dt_end|date:"Y" }}</span></time>
{% endif %}

{% with dt_end=next_time.dt_end %}
<time datetime="{{ dt_start|date:'c' }}">
{{ dt_start|date:"d N" }}
{% if next_time.valid_dt_end %}
&ndash; {{ dt_end|date:"d N" }}
{% endif %}
<span class="say-no-more">
{{ dt_end|date:"Y" }}
</span>
</time>
{% endwith %}

{% endif %}
{% endwith %}
Loading