# HG changeset patch # User Paul Boddie # Date 1373829072 -7200 # Node ID 9fc4e33b0b611865de674a5dd5b55091cf4da77b # Parent eba69418c451d9a0442a79da84f746a322e99323 Added support for concrete datetime specifiers. diff -r eba69418c451 -r 9fc4e33b0b61 RecurrenceSupport.py --- a/RecurrenceSupport.py Sat Jul 13 23:53:29 2013 +0200 +++ b/RecurrenceSupport.py Sun Jul 14 21:11:12 2013 +0200 @@ -11,20 +11,23 @@ recurrence = | - specific-recurrence = ( ( the ) | | | ) + specific-recurrence = ( ( the ) | ) [ in ] repeating-recurrence = every [ ] [ from ] [ until ] + concrete-datetime = | | + Constraints: repeating-recurrence: if is not "single": from and/or until must be specified """ -from DateSupport import weekday_labels, month_labels +from DateSupport import weekday_labels, weekday_labels_verbose, month_labels, \ + getDate, getMonth qualifiers = { "2nd" : 2, @@ -97,6 +100,9 @@ for day in weekday_labels: intervals[day] = intervals["day"] +for day in weekday_labels_verbose: + intervals[day] = intervals["day"] + for month in month_labels: intervals[month] = intervals["month"] @@ -133,11 +139,14 @@ if t != token: raise ParseError, self.tokens - def have(self, token): + def have(self, token=None): if self.end: return False t = self.tokens[-1] - return t == token + if token: + return t == token + else: + return t def _next(self): t = self.iterator.next() @@ -145,6 +154,9 @@ return t class Selector: + + "A selector of datetime occurrences at a particular interval resolution." + def __init__(self, qualified_by=None): self.recurrence_type = None self.qualifier = None @@ -162,6 +174,13 @@ self.interval = interval self.interval_level = interval_level + def add_datetime(self, interval, interval_level, value): + self.recurrence_type = "the" + self.qualifier = str(value) + self.qualifier_level = value + self.interval = interval + self.interval_level = interval_level + def set_from(self, from_datetime): self.from_datetime = from_datetime @@ -169,8 +188,10 @@ self.until_datetime = until_datetime def __str__(self): - return "%s %s %s%s%s%s" % ( - self.recurrence_type, self.qualifier, self.interval, + return "%s%s%s%s%s%s" % ( + self.recurrence_type or "", + self.qualifier and " %s" % self.qualifier or "", + self.interval and " %s" % self.interval or "", self.from_datetime and " from {%s}" % self.from_datetime or "", self.until_datetime and " until {%s}" % self.until_datetime or "", self.qualified_by and ", selecting %s" % self.qualified_by or "") @@ -182,6 +203,10 @@ self.until_datetime and ", until_datetime=%r" % self.until_datetime or "", self.qualified_by and ", qualified_by=%r" % self.qualified_by or "") + def select(self): + + "Select occurrences using this object's criteria." + # Parsing functions. def getRecurrence(s): @@ -225,7 +250,48 @@ elif words.have("the"): return parseSpecificRecurrence(words, current) else: - raise ParseError, words.tokens + return parseConcreteDateTime(words, current) + +def parseConcreteDateTime(words, current): + + """ + Using the incoming 'words' and given the 'current' selector, parse and + return a datetime acting as a specific recurrence. + """ + + word = words.have() + + # Detect dates. + + date = getDate(word) + if date: + current.add_datetime("day", intervals["day"], date) + words.want() + return current + + # Detect months. + + month = getMonth(word) + if month: + current.add_datetime("month", intervals["month"], month) + words.want() + return current + + # Detect years. + + if word.isdigit(): + current.add_datetime("year", intervals["year"], int(word)) + words.want() + return current + + # Detect month labels. + + elif word in month_labels: + current.add_datetime("month", intervals["month"], word) + words.want() + return current + + raise ParseError, words.tokens def parseSpecificRecurrence(words, current): @@ -271,11 +337,17 @@ """ qualifier = words.next() + + # Handle intervals without qualifiers. + if intervals.has_key(qualifier): interval = qualifier interval_level = intervals.get(interval) qualifier = "single" qualifier_level = isRepeatingQualifier(qualifier) + + # Handle qualified intervals. + else: qualifier_level = isRepeatingQualifier(qualifier) if not qualifier_level: @@ -312,15 +384,21 @@ from_datetime = until_datetime = None if words.have("from"): - words.need("the") from_datetime = Selector() - from_datetime = parseSpecificRecurrence(words, from_datetime) + words.next() + if words.have("the"): + from_datetime = parseSpecificRecurrence(words, from_datetime) + else: + from_datetime = parseConcreteDateTime(words, from_datetime) current.set_from(from_datetime) if words.have("until"): - words.need("the") until_datetime = Selector() - until_datetime = parseSpecificRecurrence(words, until_datetime) + words.next() + if words.have("the"): + until_datetime = parseSpecificRecurrence(words, until_datetime) + else: + until_datetime = parseConcreteDateTime(words, until_datetime) current.set_until(until_datetime) # Where the selector refers to a interval repeating at a frequency greater diff -r eba69418c451 -r 9fc4e33b0b61 tests/test_recurrence.py --- a/tests/test_recurrence.py Sat Jul 13 23:53:29 2013 +0200 +++ b/tests/test_recurrence.py Sun Jul 14 21:11:12 2013 +0200 @@ -4,6 +4,24 @@ # Good recurrences. +s = "every single day of 2013" +print s +print "->", getRecurrence(s) +s = "every Tuesday of 2013" +print s +print "->", getRecurrence(s) +s = "every Wednesday of every March" +print s +print "->", getRecurrence(s) +s = "every single day of every other February from 2013" +print s +print "->", getRecurrence(s) +s = "every single day of every other February from 2013-02-01" +print s +print "->", getRecurrence(s) +s = "every other day from 2013-02-01" +print s +print "->", getRecurrence(s) s = "every single day of the second month" print s print "->", getRecurrence(s)