1.1 --- a/imipweb/data.py Mon Apr 06 22:57:23 2015 +0200
1.2 +++ b/imipweb/data.py Tue Apr 07 00:36:47 2015 +0200
1.3 @@ -33,43 +33,48 @@
1.4 intended to represent information obtained from an iCalendar resource.
1.5 """
1.6
1.7 - def __init__(self, start, end, start_attr=None, end_attr=None, form_start=None, form_end=None):
1.8 + def __init__(self, start, end, start_attr=None, end_attr=None, form_start=None, form_end=None, origin=None):
1.9 Period.__init__(self, start, end)
1.10 self.start_attr = start_attr
1.11 self.end_attr = end_attr
1.12 self.form_start = form_start
1.13 self.form_end = form_end
1.14 + self.origin = origin
1.15
1.16 def as_tuple(self):
1.17 - return self.start, self.end, self.start_attr, self.end_attr, self.form_start, self.form_end
1.18 + return self.start, self.end, self.start_attr, self.end_attr, self.form_start, self.form_end, self.origin
1.19
1.20 def __repr__(self):
1.21 - return "EventPeriod(%r, %r, %r, %r, %r, %r)" % self.as_tuple()
1.22 + return "EventPeriod(%r, %r, %r, %r, %r, %r, %r)" % self.as_tuple()
1.23 +
1.24 + def as_event_period(self):
1.25 + return self
1.26 +
1.27 + # Period data methods.
1.28
1.29 def get_start(self):
1.30 return self.start
1.31
1.32 def get_end(self):
1.33 - return self.end
1.34 + return end_date_from_calendar(self.end)
1.35
1.36 - def as_event_period(self):
1.37 - return self
1.38 + # Form data compatibility methods.
1.39
1.40 def get_form_start(self):
1.41 if not self.form_start:
1.42 - self.form_start = self.get_form_date(self.start, self.start_attr)
1.43 + self.form_start = self.get_form_date(self.get_start(), self.start_attr)
1.44 return self.form_start
1.45
1.46 def get_form_end(self):
1.47 if not self.form_end:
1.48 - self.form_end = self.get_form_date(self.end, self.end_attr)
1.49 + self.form_end = self.get_form_date(self.get_end(), self.end_attr)
1.50 return self.form_end
1.51
1.52 def as_form_period(self):
1.53 return FormPeriod(
1.54 - self.get_form_date(self.start, self.start_attr),
1.55 - self.get_form_date(self.end, self.end_attr),
1.56 - isinstance(self.end, datetime) or self.start != self.end - timedelta(1),
1.57 + self.get_form_start(),
1.58 + self.get_form_end(),
1.59 + isinstance(self.end, datetime) or self.get_start() != self.get_end(),
1.60 isinstance(self.start, datetime) or isinstance(self.end, datetime)
1.61 )
1.62
1.63 @@ -83,24 +88,22 @@
1.64 dt, attr
1.65 )
1.66
1.67 -def event_period_from_recurrence_period(period):
1.68 - return EventPeriod(period.start, period.end, period.start_attr, period.end_attr)
1.69 -
1.70 class FormPeriod:
1.71
1.72 "A period whose information originates from a form."
1.73
1.74 - def __init__(self, start, end, end_enabled=True, times_enabled=True):
1.75 + def __init__(self, start, end, end_enabled=True, times_enabled=True, origin=None):
1.76 self.start = start
1.77 self.end = end
1.78 self.end_enabled = end_enabled
1.79 self.times_enabled = times_enabled
1.80 + self.origin = origin
1.81
1.82 def as_tuple(self):
1.83 - return self.start, self.end, self.end_enabled, self.times_enabled
1.84 + return self.start, self.end, self.end_enabled, self.times_enabled, self.origin
1.85
1.86 def __repr__(self):
1.87 - return "FormPeriod(%r, %r, %r, %r)" % self.as_tuple()
1.88 + return "FormPeriod(%r, %r, %r, %r, %r)" % self.as_tuple()
1.89
1.90 def _get_start(self):
1.91 t = self.start.as_datetime_item(self.times_enabled)
1.92 @@ -109,7 +112,7 @@
1.93 else:
1.94 return None
1.95
1.96 - def _get_end(self):
1.97 + def _get_end(self, adjust=False):
1.98
1.99 # Handle specified end datetimes.
1.100
1.101 @@ -117,6 +120,7 @@
1.102 t = self.end.as_datetime_item(self.times_enabled)
1.103 if t:
1.104 dtend, dtend_attr = t
1.105 + dtend = adjust and end_date_to_calendar(dtend) or dtend
1.106 else:
1.107 return None
1.108
1.109 @@ -137,6 +141,29 @@
1.110
1.111 return dtend, dtend_attr
1.112
1.113 + def as_event_period(self, index=None):
1.114 + t = self._get_start()
1.115 + if t:
1.116 + dtstart, dtstart_attr = t
1.117 + else:
1.118 + raise PeriodError(*[index is not None and ("dtstart", index) or "dtstart"])
1.119 +
1.120 + t = self._get_end(adjust=True)
1.121 + if t:
1.122 + dtend, dtend_attr = t
1.123 + else:
1.124 + raise PeriodError(*[index is not None and ("dtend", index) or "dtend"])
1.125 +
1.126 + if dtstart > dtend:
1.127 + raise PeriodError(*[
1.128 + index is not None and ("dtstart", index) or "dtstart",
1.129 + index is not None and ("dtend", index) or "dtend"
1.130 + ])
1.131 +
1.132 + return EventPeriod(dtstart, dtend, dtstart_attr, dtend_attr, self.start, self.end)
1.133 +
1.134 + # Period data methods.
1.135 +
1.136 def get_start(self):
1.137 t = self._get_start()
1.138 if t:
1.139 @@ -153,26 +180,7 @@
1.140 else:
1.141 return None
1.142
1.143 - def as_event_period(self, index=None):
1.144 - t = self._get_start()
1.145 - if t:
1.146 - dtstart, dtstart_attr = t
1.147 - else:
1.148 - raise PeriodError(*[index is not None and ("dtstart", index) or "dtstart"])
1.149 -
1.150 - t = self._get_end()
1.151 - if t:
1.152 - dtend, dtend_attr = t
1.153 - else:
1.154 - raise PeriodError(*[index is not None and ("dtend", index) or "dtend"])
1.155 -
1.156 - if dtstart > dtend:
1.157 - raise PeriodError(*[
1.158 - index is not None and ("dtstart", index) or "dtstart",
1.159 - index is not None and ("dtend", index) or "dtend"
1.160 - ])
1.161 -
1.162 - return EventPeriod(dtstart, dtend, dtstart_attr, dtend_attr, self.start, self.end)
1.163 + # Form data methods.
1.164
1.165 def get_form_start(self):
1.166 return self.start
1.167 @@ -291,4 +299,20 @@
1.168 else:
1.169 return dt
1.170
1.171 +def event_period_from_period(period):
1.172 + if isinstance(period, EventPeriod):
1.173 + return period
1.174 + elif isinstance(period, FormPeriod):
1.175 + return period.as_event_period()
1.176 + else:
1.177 + return EventPeriod(period.start, period.end, period.start_attr, period.end_attr, origin=period.origin)
1.178 +
1.179 +def form_period_from_period(period):
1.180 + if isinstance(period, EventPeriod):
1.181 + return period.as_form_period()
1.182 + elif isinstance(period, FormPeriod):
1.183 + return period
1.184 + else:
1.185 + return event_period_from_period(period).as_form_period()
1.186 +
1.187 # vim: tabstop=4 expandtab shiftwidth=4
2.1 --- a/imipweb/event.py Mon Apr 06 22:57:23 2015 +0200
2.2 +++ b/imipweb/event.py Tue Apr 07 00:36:47 2015 +0200
2.3 @@ -28,8 +28,7 @@
2.4 from imiptools.mail import Messenger
2.5 from imiptools.period import have_conflict
2.6 from imipweb.data import EventPeriod, \
2.7 - end_date_from_calendar, end_date_to_calendar, \
2.8 - event_period_from_recurrence_period, \
2.9 + event_period_from_period, form_period_from_period, \
2.10 FormDate, FormPeriod, PeriodError
2.11 from imipweb.handler import ManagerHandler
2.12 from imipweb.resource import Resource
2.13 @@ -188,9 +187,9 @@
2.14
2.15 "Set in the given 'obj' the given 'period' as the main start and end."
2.16
2.17 - p = period.as_event_period()
2.18 + p = event_period_from_period(period)
2.19 result = self.set_datetime_in_object(p.start, p.start_attr and p.start_attr.get("TZID"), "DTSTART", obj)
2.20 - result = self.set_datetime_in_object(end_date_to_calendar(p.end), p.end_attr and p.end_attr.get("TZID"), "DTEND", obj) or result
2.21 + result = self.set_datetime_in_object(p.end, p.end_attr and p.end_attr.get("TZID"), "DTEND", obj) or result
2.22 return result
2.23
2.24 def set_periods_in_object(self, obj, periods):
2.25 @@ -206,9 +205,9 @@
2.26 del obj["RDATE"]
2.27
2.28 for period in periods:
2.29 - p = period.as_event_period()
2.30 + p = event_period_from_period(period)
2.31 tzid = p.start_attr and p.start_attr.get("TZID") or p.end_attr and p.end_attr.get("TZID")
2.32 - new_rdates.append(get_period_item(p.start, end_date_to_calendar(p.end), tzid))
2.33 + new_rdates.append(get_period_item(p.start, p.end, tzid))
2.34
2.35 obj["RDATE"] = new_rdates
2.36
2.37 @@ -305,7 +304,7 @@
2.38 else:
2.39 dtend, dtend_attr = dtstart, dtstart_attr
2.40
2.41 - return EventPeriod(dtstart, end_date_from_calendar(dtend), dtstart_attr, dtend_attr)
2.42 + return EventPeriod(dtstart, dtend, dtstart_attr, dtend_attr)
2.43
2.44 def get_main_period(self):
2.45
2.46 @@ -332,8 +331,8 @@
2.47 def get_existing_recurrences(self, obj):
2.48 recurrences = []
2.49 for period in obj.get_periods(self.get_tzid(), self.get_window_end()):
2.50 - if period.origin == "RDATE":
2.51 - recurrences.append(event_period_from_recurrence_period(period))
2.52 + if period.origin != "DTSTART":
2.53 + recurrences.append(period)
2.54 return recurrences
2.55
2.56 def get_recurrences(self):
2.57 @@ -708,22 +707,21 @@
2.58 page.a("This event modifies a recurring event.", href=self.link_to(uid))
2.59 page.p.close()
2.60
2.61 - # Obtain the periods associated with the event in the user's time zone.
2.62 + # Obtain the periods associated with the event.
2.63
2.64 - periods = map(event_period_from_recurrence_period, obj.get_periods(self.get_tzid(), self.get_window_end()))
2.65 recurrences = self.get_current_recurrences(obj)
2.66
2.67 - if len(periods) <= 1:
2.68 + if len(recurrences) < 1:
2.69 return
2.70
2.71 recurrenceids = self._get_recurrences(uid)
2.72
2.73 + page.p("This event occurs on the following occasions within the next %d days:" % self.get_window_size())
2.74 +
2.75 # Show each recurrence in a separate table if editable.
2.76
2.77 if self.is_organiser(obj) and recurrences:
2.78
2.79 - page.p("The following occurrences are editable:")
2.80 -
2.81 for index, p in enumerate(recurrences):
2.82
2.83 # Isolate the controls from neighbouring tables.
2.84 @@ -735,6 +733,7 @@
2.85 page.table(cellspacing=5, cellpadding=5, class_="recurrence")
2.86 page.caption("Occurrence")
2.87 page.tbody()
2.88 +
2.89 page.tr()
2.90 error = errors and ("dtstart", index) in errors and " error" or ""
2.91 page.th("Start", class_="objectheading start%s" % error)
2.92 @@ -745,6 +744,7 @@
2.93 page.th("End", class_="objectheading end%s" % error)
2.94 self.show_recurrence_controls(obj, index, p, recurrenceid, recurrenceids, False)
2.95 page.tr.close()
2.96 +
2.97 page.tbody.close()
2.98 page.table.close()
2.99
2.100 @@ -752,26 +752,25 @@
2.101
2.102 # Otherwise, use a compact single table.
2.103
2.104 - page.p("This event occurs on the following occasions within the next %d days:" % self.get_window_size())
2.105 + else:
2.106 + page.table(cellspacing=5, cellpadding=5, class_="recurrence")
2.107 + page.caption("Occurrences")
2.108 + page.thead()
2.109 + page.tr()
2.110 + page.th("Start", class_="objectheading start")
2.111 + page.th("End", class_="objectheading end")
2.112 + page.tr.close()
2.113 + page.thead.close()
2.114 + page.tbody()
2.115
2.116 - page.table(cellspacing=5, cellpadding=5, class_="recurrence")
2.117 - page.caption("Occurrences")
2.118 - page.thead()
2.119 - page.tr()
2.120 - page.th("Start", class_="objectheading start")
2.121 - page.th("End", class_="objectheading end")
2.122 - page.tr.close()
2.123 - page.thead.close()
2.124 - page.tbody()
2.125 + for index, p in enumerate(recurrences):
2.126 + page.tr()
2.127 + self.show_recurrence_label(p, recurrenceid, recurrenceids, True)
2.128 + self.show_recurrence_label(p, recurrenceid, recurrenceids, False)
2.129 + page.tr.close()
2.130
2.131 - for index, p in enumerate(periods):
2.132 - page.tr()
2.133 - self.show_recurrence_label(p, recurrenceid, recurrenceids, True)
2.134 - self.show_recurrence_label(p, recurrenceid, recurrenceids, False)
2.135 - page.tr.close()
2.136 -
2.137 - page.tbody.close()
2.138 - page.table.close()
2.139 + page.tbody.close()
2.140 + page.table.close()
2.141
2.142 def show_conflicting_events(self, uid, obj):
2.143
2.144 @@ -865,7 +864,7 @@
2.145 individual controls for dynamic manipulation.
2.146 """
2.147
2.148 - p = period.as_form_period()
2.149 + p = form_period_from_period(period)
2.150
2.151 page = self.page
2.152 args = self.env.get_args()
2.153 @@ -967,14 +966,11 @@
2.154 page = self.page
2.155 sn = self._suffixed_name
2.156 ssn = self._simple_suffixed_name
2.157 - p = period
2.158 +
2.159 + p = event_period_from_period(period)
2.160
2.161 start_utc = format_datetime(to_timezone(p.get_start(), "UTC"))
2.162 replaced = recurrenceids and start_utc in recurrenceids and "replaced" or ""
2.163 - css = " ".join([
2.164 - replaced,
2.165 - recurrenceid and start_utc == recurrenceid and "affected" or ""
2.166 - ])
2.167
2.168 # Show controls for editing as organiser.
2.169
2.170 @@ -1006,10 +1002,10 @@
2.171 else:
2.172 self.show_recurrence_label(p, recurrenceid, recurrenceids, show_start)
2.173
2.174 - def show_recurrence_label(self, p, recurrenceid, recurrenceids, show_start):
2.175 + def show_recurrence_label(self, period, recurrenceid, recurrenceids, show_start):
2.176
2.177 """
2.178 - Show datetime details for the given period 'p', employing any
2.179 + Show datetime details for the given 'period', employing any
2.180 'recurrenceid' and 'recurrenceids' for the object to configure the
2.181 displayed information.
2.182
2.183 @@ -1019,6 +1015,8 @@
2.184
2.185 page = self.page
2.186
2.187 + p = event_period_from_period(period)
2.188 +
2.189 start_utc = format_datetime(to_timezone(p.get_start(), "UTC"))
2.190 replaced = recurrenceids and start_utc in recurrenceids and "replaced" or ""
2.191 css = " ".join([