From 44c159a587a078d1a9d3283ce8bf4ca231a61b5f Mon Sep 17 00:00:00 2001
From: Mark Steward
Date: Sun, 27 Mar 2016 01:17:22 +0000
Subject: [PATCH] Fix override attribute and default description
Rework constructor of PSEvents so that it always take a date, and
the override attribute is accurate. This allows us to compare two
PSEvents for equality and use .override in templates.
Fixes #8, #15 and #22
---
ps_data.py | 78 ++++++++++++++++++++++++++++++-----------
templates/event.html | 2 ++
templates/homepage.html | 2 ++
tests/test_ps_data.py | 6 ++++
4 files changed, 67 insertions(+), 21 deletions(-)
diff --git a/ps_data.py b/ps_data.py
index 5171a3990..99510fdec 100644
--- a/ps_data.py
+++ b/ps_data.py
@@ -16,28 +16,48 @@
PS_STARTS = datetime.time(18, 0, 0)
PS_ENDS = datetime.time(23, 30, 0)
PS_TIMEZONE = 'Europe/London'
-PS_DESCRIPTION = 'We\'ll meet in the upstairs room as usual.'
class PSEvent(object):
- def __init__(self, data={}, date=None, override=False):
+ """
+ Represents a meeting on a particular date.
+ For a scheduled event, construct with a date from the algorithm.
+ Otherwise override the calendar by passing any date and event_data.
+
+ >>> ps_100 = PSEvent(datetime.date(2014, 3, 13))
+ >>> ps_100
+
+
+ >>> ps_100 == PSEvent('2014-03-13')
+ True
+
+ >>> ps_100_named = PSEvent('2014-03-13', {'name': "PS 100"})
+ >>> ps_100_named == ps_100
+ False
+ >>> ps_100_named
+
+ """
+ def __init__(self, date, event_data=None):
+ if isinstance(date, basestring):
+ self.date = datetime.datetime.strptime(date, '%Y-%m-%d').date()
+ else:
+ self.date = date
+
self.starts = PS_STARTS
self.ends = PS_ENDS
self.location = PS_LOCATION
self.address = PS_ADDRESS
self.name = None
- self.description = PS_DESCRIPTION
self.cancelled = False
- self.override = override # used for merging iters
- if date is not None:
- data['date'] = date
+ if event_data is None:
+ self.override = False
- for k, v in data.items():
- if k == 'date' and isinstance(v, basestring):
- v = datetime.datetime.strptime(v, '%Y-%m-%d')
- if k in ('starts', 'ends') and isinstance(v, basestring):
- v = datetime.datetime.strptime(v, '%H:%M').time()
- setattr(self, k, v)
+ else:
+ self.override = True
+ for k, v in event_data.items():
+ if k in ('starts', 'ends') and isinstance(v, basestring):
+ v = datetime.datetime.strptime(v, '%H:%M').time()
+ setattr(self, k, v)
self.tzinfo = pytz.timezone(PS_TIMEZONE)
@@ -46,8 +66,19 @@ def __init__(self, data={}, date=None, override=False):
self.start_dt = combine_tz(self.date, self.starts, self.tzinfo)
self.end_dt = combine_tz(self.date, self.ends, self.tzinfo)
+ def __eq__(self, other):
+ if self.override or other.override:
+ return set(self.__dict__.items()) == set(other.__dict__.items())
+ return self.date == other.date
+
def __lt__(self, other):
- return self.date.date() < other.date.date()
+ return self.date < other.date
+
+ def __repr__(self):
+ datestr = self.date.strftime('%Y-%m-%d')
+ if self.override:
+ return '' % (datestr, self.name)
+ return '' % datestr
@property
def title(self):
@@ -92,28 +123,33 @@ def get_ps_event_by_number(number):
date = the_algorithm.ps_date_from_offset(number)
stringdate = date.strftime('%Y-%m-%d')
event_data = load_ps_data().get(stringdate, {})
- return PSEvent(event_data, date=date)
+ if event_data:
+ return PSEvent(date, event_data)
+
+ return PSEvent(date)
def get_ps_event_by_slug(slug):
for stringdate, event in load_ps_data().items():
- event = PSEvent(event, date=stringdate)
+ event = PSEvent(stringdate, event)
if event.slug == slug:
return event
def gen_events(start=None, end=None):
gen = the_algorithm.gen_ps_dates(start)
- event = PSEvent(date=gen.next())
+ # gen_ps_dates actually returns datetimes
+ event = PSEvent(gen.next().date())
while not end or event.end_dt < end:
yield event
- event = PSEvent(date=gen.next())
+ event = PSEvent(gen.next().date())
def get_manual_ps_events(start=None, end=None):
for stringdate, event in load_ps_data().items():
- event = PSEvent(event, date=stringdate, override=True)
+ event = PSEvent(stringdate, event)
if start and event.end_dt < start:
continue
- if not end or event.end_dt < end:
- yield event
+ if end and event.end_dt >= end:
+ continue
+ yield event
# heapq.merge is not stable, however the merge guaranteed overrides will be sequential
def merge_event_iters(one, two):
@@ -121,7 +157,7 @@ def merge_event_iters(one, two):
previous = None
for event in events:
if previous:
- if previous.date.date() == event.date.date():
+ if previous.date == event.date:
if event.override:
previous = event
continue
diff --git a/templates/event.html b/templates/event.html
index aa087babd..79be0a72d 100644
--- a/templates/event.html
+++ b/templates/event.html
@@ -11,6 +11,8 @@ {{event.title}}
{{ event.description|safe }}
+ {% elif not event.override and not event.in_the_past %}
+ We'll meet in the upstairs room as usual.
{% endif %}
{% endif %}
diff --git a/templates/homepage.html b/templates/homepage.html
index 9816a3339..b61fcdf7e 100644
--- a/templates/homepage.html
+++ b/templates/homepage.html
@@ -23,6 +23,8 @@
{% if event.description %}
{{ event.description }}
+ {% elif not event.override and not event.in_the_past %}
+ We'll meet in the upstairs room as usual.
{% endif %}
{% endif %}
diff --git a/tests/test_ps_data.py b/tests/test_ps_data.py
index 2d9adbd69..08420b65f 100644
--- a/tests/test_ps_data.py
+++ b/tests/test_ps_data.py
@@ -1,4 +1,5 @@
import unittest
+import doctest
import ps_data
from test_util import parse_datestr, is_algorithmic_ps_date, utc_datetime
from datetime import datetime, timedelta
@@ -216,3 +217,8 @@ def get_prev_evs(start):
check_event_queries_for_starts(begin_dst)
check_event_queries_for_starts(end_dst)
+def test_doctests():
+ results = doctest.testmod(ps_data)
+ if results.failed:
+ raise Exception(results)
+