imip-extra

Changeset

22:b4cfb993e0c9
2019-08-26 Paul Boddie raw files shortlog changelog graph Added a participant value abstraction, normalising URIs for such properties. Added attendees and organiser methods for convenience. Renamed the rrule method to rule, introducing a separate rrule method for access to the actual property. Tidied up some property accesses, taking advantage of the defaultdict behaviour.
data.py (file)
     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.