class TimeRange(FilterBase): """ Specifies a time for testing components against. """ serialized_name = "TimeRange" def __init__(self, xml_element): super(TimeRange, self).__init__(xml_element) if xml_element is None: return # One of start or end must be present if "start" not in xml_element.attributes and "end" not in xml_element.attributes: raise ValueError( "One of 'start' or 'end' must be present in CALDAV:time-range") self.start = DateTime.parseText( xml_element.attributes["start"] ) if "start" in xml_element.attributes else None self.end = DateTime.parseText( xml_element.attributes["end"] ) if "end" in xml_element.attributes else None self.tzinfo = None def _deserialize(self, data): """ Convert a JSON compatible serialization of this object into the actual object. """ self.start = DateTime.parseText( data["start"]) if data["start"] else None self.end = DateTime.parseText(data["end"]) if data["end"] else None self.tzinfo = Timezone(tzid=data["tzinfo"]) if data["tzinfo"] else None def serialize(self): """ Create a JSON compatible serialization of this object - will be used in a cross-pod request. """ result = super(TimeRange, self).serialize() result.update({ "start": self.start.getText() if self.start else None, "end": self.end.getText() if self.end else None, "tzinfo": self.tzinfo.getTimezoneID() if self.tzinfo else None, }) return result def settzinfo(self, tzinfo): """ Set the default timezone to use with this query. @param tzinfo: a L{Timezone} to use. """ # Give tzinfo to any TimeRange we have self.tzinfo = tzinfo def valid(self, level=0): """ Indicate whether the time-range is valid (must be date-time in UTC). @return: True if valid, False otherwise """ if self.start is not None and self.start.isDateOnly(): log.info("start attribute in <time-range> is not a date-time: %s" % (self.start, )) return False if self.end is not None and self.end.isDateOnly(): log.info("end attribute in <time-range> is not a date-time: %s" % (self.end, )) return False if self.start is not None and not self.start.utc(): log.info("start attribute in <time-range> is not UTC: %s" % (self.start, )) return False if self.end is not None and not self.end.utc(): log.info("end attribute in <time-range> is not UTC: %s" % (self.end, )) return False # No other tests return True def match(self, property, access=None): """ NB This is only called when doing a time-range match on a property. """ if property is None: return False else: return property.containsTimeRange(self.start, self.end, self.tzinfo) def matchinstance(self, component, instances): """ Test whether this time-range element causes a match to the specified component using the specified set of instances to determine the expanded time ranges. @param component: the L{Component} to test. @param instances: the list of expanded instances. @return: True if the time-range query matches, False otherwise. """ if component is None: return False assert instances is not None or self.end is None, "Failure to expand instance for time-range filter: %r" % ( self, ) # Special case open-ended unbounded if instances is None: if component.getRecurrenceIDUTC() is None: return True else: # See if the overridden component's start is past the start start, _ignore_end = component.getEffectiveStartEnd() if start is None: return True else: return start >= self.start # Handle alarms as a special case alarms = (component.name() == "VALARM") if alarms: testcomponent = component._parent else: testcomponent = component for key in instances: instance = instances[key] # First make sure components match if not testcomponent.same(instance.component): continue if alarms: # Get all the alarm triggers for this instance and test each one triggers = instance.getAlarmTriggers() for trigger in triggers: if timeRangesOverlap(trigger, None, self.start, self.end, self.tzinfo): return True else: # Regular instance overlap test if timeRangesOverlap(instance.start, instance.end, self.start, self.end, self.tzinfo): return True return False
class TimeRange (FilterBase): """ Specifies a time for testing components against. """ serialized_name = "TimeRange" def __init__(self, xml_element): super(TimeRange, self).__init__(xml_element) if xml_element is None: return # One of start or end must be present if "start" not in xml_element.attributes and "end" not in xml_element.attributes: raise ValueError("One of 'start' or 'end' must be present in CALDAV:time-range") self.start = DateTime.parseText(xml_element.attributes["start"]) if "start" in xml_element.attributes else None self.end = DateTime.parseText(xml_element.attributes["end"]) if "end" in xml_element.attributes else None self.tzinfo = None def _deserialize(self, data): """ Convert a JSON compatible serialization of this object into the actual object. """ self.start = DateTime.parseText(data["start"]) if data["start"] else None self.end = DateTime.parseText(data["end"]) if data["end"] else None self.tzinfo = Timezone(tzid=data["tzinfo"]) if data["tzinfo"] else None def serialize(self): """ Create a JSON compatible serialization of this object - will be used in a cross-pod request. """ result = super(TimeRange, self).serialize() result.update({ "start": self.start.getText() if self.start else None, "end": self.end.getText() if self.end else None, "tzinfo": self.tzinfo.getTimezoneID() if self.tzinfo else None, }) return result def settzinfo(self, tzinfo): """ Set the default timezone to use with this query. @param tzinfo: a L{Timezone} to use. """ # Give tzinfo to any TimeRange we have self.tzinfo = tzinfo def valid(self, level=0): """ Indicate whether the time-range is valid (must be date-time in UTC). @return: True if valid, False otherwise """ if self.start is not None and self.start.isDateOnly(): log.info("start attribute in <time-range> is not a date-time: %s" % (self.start,)) return False if self.end is not None and self.end.isDateOnly(): log.info("end attribute in <time-range> is not a date-time: %s" % (self.end,)) return False if self.start is not None and not self.start.utc(): log.info("start attribute in <time-range> is not UTC: %s" % (self.start,)) return False if self.end is not None and not self.end.utc(): log.info("end attribute in <time-range> is not UTC: %s" % (self.end,)) return False # No other tests return True def match(self, property, access=None): """ NB This is only called when doing a time-range match on a property. """ if property is None: return False else: return property.containsTimeRange(self.start, self.end, self.tzinfo) def matchinstance(self, component, instances): """ Test whether this time-range element causes a match to the specified component using the specified set of instances to determine the expanded time ranges. @param component: the L{Component} to test. @param instances: the list of expanded instances. @return: True if the time-range query matches, False otherwise. """ if component is None: return False assert instances is not None or self.end is None, "Failure to expand instance for time-range filter: %r" % (self,) # Special case open-ended unbounded if instances is None: if component.getRecurrenceIDUTC() is None: return True else: # See if the overridden component's start is past the start start, _ignore_end = component.getEffectiveStartEnd() if start is None: return True else: return start >= self.start # Handle alarms as a special case alarms = (component.name() == "VALARM") if alarms: testcomponent = component._parent else: testcomponent = component for key in instances: instance = instances[key] # First make sure components match if not testcomponent.same(instance.component): continue if alarms: # Get all the alarm triggers for this instance and test each one triggers = instance.getAlarmTriggers() for trigger in triggers: if timeRangesOverlap(trigger, None, self.start, self.end, self.tzinfo): return True else: # Regular instance overlap test if timeRangesOverlap(instance.start, instance.end, self.start, self.end, self.tzinfo): return True return False