1.1 --- a/data.py Sun Aug 25 23:07:17 2019 +0200
1.2 +++ b/data.py Mon Aug 26 00:09:37 2019 +0200
1.3 @@ -27,6 +27,16 @@
1.4 import vCalendar
1.5 import vRecurrence
1.6
1.7 +
1.8 +
1.9 +# Property details.
1.10 +
1.11 +PARTICIPANT_PROPERTIES = "ATTENDEE", "ORGANIZER"
1.12 +
1.13 +
1.14 +
1.15 +# Abstractions.
1.16 +
1.17 class Object:
1.18
1.19 "A calendar object abstraction."
1.20 @@ -49,7 +59,7 @@
1.21 if name in vCalendar.SECTION_TYPES:
1.22 self.objects.append(Object(name, value))
1.23 else:
1.24 - self.values[name].append(Value(value, attrs))
1.25 + self.values[name].append(make_value(name, attrs, value))
1.26
1.27 # Related objects.
1.28
1.29 @@ -127,6 +137,9 @@
1.30 # Mapping interface for values.
1.31
1.32 def __getitem__(self, key):
1.33 +
1.34 + "Return the value for 'key' or the default empty list."
1.35 +
1.36 return self.values[key]
1.37
1.38 def get(self, key, default=None):
1.39 @@ -153,19 +166,33 @@
1.40 return parse_datetime(self.get_value(key))
1.41
1.42 def datetimes(self, key):
1.43 - return map(parse_datetime, self.get(key, []))
1.44 + return map(parse_datetime, self[key])
1.45
1.46 def duration(self):
1.47 return dates.parse.duration(self.get_value("DURATION"))
1.48
1.49 # Specific property methods.
1.50
1.51 + def attendees(self):
1.52 +
1.53 + "Return all attendees."
1.54 +
1.55 + return self["ATTENDEE"]
1.56 +
1.57 def exdates(self):
1.58
1.59 "Return all excluded recurrences."
1.60
1.61 return self.datetimes("EXDATE")
1.62
1.63 + def organiser(self):
1.64 +
1.65 + "Return any organiser."
1.66 +
1.67 + return self.get_value("ORGANIZER")
1.68 +
1.69 + organizer = organiser
1.70 +
1.71 def rdates(self):
1.72
1.73 "Return all non-rule recurrence periods."
1.74 @@ -173,7 +200,7 @@
1.75 duration = self.period().duration()
1.76 l = []
1.77
1.78 - for value in self.get("RDATE", []):
1.79 + for value in self["RDATE"]:
1.80 l.append(parse_period(value, duration, RecurrencePeriod))
1.81
1.82 return l
1.83 @@ -186,11 +213,9 @@
1.84
1.85 def rrule(self):
1.86
1.87 - "Return an object providing access to the recurrence rule."
1.88 + "Return details of the recurrence rule."
1.89
1.90 - start = self.datetime("DTSTART")
1.91 - rule = self.get_value("RRULE")
1.92 - return start and rule and vRecurrence.get_rule(start, rule.value)
1.93 + return self.get_value("RRULE")
1.94
1.95 def uid(self):
1.96
1.97 @@ -236,7 +261,7 @@
1.98
1.99 # Either add rule-based periods (including the main period).
1.100
1.101 - from_rule = self.rrule()
1.102 + from_rule = self.rule()
1.103 if from_rule:
1.104 it = from_rule.select(main.start, window.end)
1.105 iters.append(RulePeriodIterator(it, main))
1.106 @@ -277,6 +302,42 @@
1.107
1.108 return ifilter(window.wraps, periods)
1.109
1.110 + def rule(self):
1.111 +
1.112 + "Return an object providing access to the recurrence rule."
1.113 +
1.114 + start = self.datetime("DTSTART")
1.115 + rule = self.rrule()
1.116 + return start and rule and vRecurrence.get_rule(start, rule.value)
1.117 +
1.118 +
1.119 +
1.120 +# Value abstractions.
1.121 +
1.122 +class Value:
1.123 +
1.124 + "A value plus attributes abstraction."
1.125 +
1.126 + def __init__(self, value, attrs=None):
1.127 + self.value = value
1.128 + self.attrs = attrs
1.129 +
1.130 + def __repr__(self):
1.131 + return "Value(%r, %r)" % (self.value, self.attrs)
1.132 +
1.133 +class Participant(Value):
1.134 +
1.135 + "An abstraction for participant values."
1.136 +
1.137 + def __init__(self, value, attrs=None):
1.138 + Value.__init__(self, uri(value), attrs)
1.139 +
1.140 + def __repr__(self):
1.141 + return "Participant(%r, %r)" % (self.value, self.attrs)
1.142 +
1.143 + def address(self):
1.144 + return self.value.split(":", 1)[1]
1.145 +
1.146
1.147
1.148 # Utility classes.
1.149 @@ -351,17 +412,6 @@
1.150 end = start + self.main.duration()
1.151 return RulePeriod(start, end)
1.152
1.153 -class Value:
1.154 -
1.155 - "A value plus attributes abstraction."
1.156 -
1.157 - def __init__(self, value, attrs=None):
1.158 - self.value = value
1.159 - self.attrs = attrs
1.160 -
1.161 - def __repr__(self):
1.162 - return "Value(%r, %r)" % (self.value, self.attrs)
1.163 -
1.164
1.165
1.166 # Utility functions.
1.167 @@ -384,6 +434,31 @@
1.168 def copy(obj):
1.169 return obj.copy()
1.170
1.171 +def make_value(name, attrs, value):
1.172 +
1.173 + "Return a value appropriate for 'name' containing 'attrs' and 'value."
1.174 +
1.175 + if name in PARTICIPANT_PROPERTIES:
1.176 + return Participant(value, attrs)
1.177 + else:
1.178 + return Value(value, attrs)
1.179 +
1.180 +def uri(value):
1.181 +
1.182 + """
1.183 + Return 'value' to indicate a mailto: URI, with the protocol potentially
1.184 + being in upper case and being normalised to lower case in the result, for
1.185 + both existing mailto: URI values and non-URI values. Other URI values are
1.186 + returned unchanged.
1.187 + """
1.188 +
1.189 + if value.lower().startswith("mailto:"):
1.190 + return "mailto:%s" % value[7:]
1.191 + elif ":" not in value:
1.192 + return "mailto:%s" % value
1.193 + else:
1.194 + return value
1.195 +
1.196
1.197
1.198 # Parsing functions.