1.1 --- a/RecurrenceSupport.py Sat Jul 13 23:53:29 2013 +0200
1.2 +++ b/RecurrenceSupport.py Sun Jul 14 21:11:12 2013 +0200
1.3 @@ -11,20 +11,23 @@
1.4
1.5 recurrence = <specific-recurrence> | <repeating-recurrence>
1.6
1.7 - specific-recurrence = ( ( the <qualifier> <interval> ) | <datetime> | <month> | <year> )
1.8 + specific-recurrence = ( ( the <qualifier> <interval> ) | <concrete-datetime> )
1.9 [ in <specific-recurrence> ]
1.10
1.11 repeating-recurrence = every [ <qualifier> ] <interval>
1.12 [ from <specific-recurrence> ]
1.13 [ until <specific-recurrence> ]
1.14
1.15 + concrete-datetime = <datetime> | <month> | <year>
1.16 +
1.17 Constraints:
1.18
1.19 repeating-recurrence: if <qualifier> is not "single":
1.20 from and/or until must be specified
1.21 """
1.22
1.23 -from DateSupport import weekday_labels, month_labels
1.24 +from DateSupport import weekday_labels, weekday_labels_verbose, month_labels, \
1.25 + getDate, getMonth
1.26
1.27 qualifiers = {
1.28 "2nd" : 2,
1.29 @@ -97,6 +100,9 @@
1.30 for day in weekday_labels:
1.31 intervals[day] = intervals["day"]
1.32
1.33 +for day in weekday_labels_verbose:
1.34 + intervals[day] = intervals["day"]
1.35 +
1.36 for month in month_labels:
1.37 intervals[month] = intervals["month"]
1.38
1.39 @@ -133,11 +139,14 @@
1.40 if t != token:
1.41 raise ParseError, self.tokens
1.42
1.43 - def have(self, token):
1.44 + def have(self, token=None):
1.45 if self.end:
1.46 return False
1.47 t = self.tokens[-1]
1.48 - return t == token
1.49 + if token:
1.50 + return t == token
1.51 + else:
1.52 + return t
1.53
1.54 def _next(self):
1.55 t = self.iterator.next()
1.56 @@ -145,6 +154,9 @@
1.57 return t
1.58
1.59 class Selector:
1.60 +
1.61 + "A selector of datetime occurrences at a particular interval resolution."
1.62 +
1.63 def __init__(self, qualified_by=None):
1.64 self.recurrence_type = None
1.65 self.qualifier = None
1.66 @@ -162,6 +174,13 @@
1.67 self.interval = interval
1.68 self.interval_level = interval_level
1.69
1.70 + def add_datetime(self, interval, interval_level, value):
1.71 + self.recurrence_type = "the"
1.72 + self.qualifier = str(value)
1.73 + self.qualifier_level = value
1.74 + self.interval = interval
1.75 + self.interval_level = interval_level
1.76 +
1.77 def set_from(self, from_datetime):
1.78 self.from_datetime = from_datetime
1.79
1.80 @@ -169,8 +188,10 @@
1.81 self.until_datetime = until_datetime
1.82
1.83 def __str__(self):
1.84 - return "%s %s %s%s%s%s" % (
1.85 - self.recurrence_type, self.qualifier, self.interval,
1.86 + return "%s%s%s%s%s%s" % (
1.87 + self.recurrence_type or "",
1.88 + self.qualifier and " %s" % self.qualifier or "",
1.89 + self.interval and " %s" % self.interval or "",
1.90 self.from_datetime and " from {%s}" % self.from_datetime or "",
1.91 self.until_datetime and " until {%s}" % self.until_datetime or "",
1.92 self.qualified_by and ", selecting %s" % self.qualified_by or "")
1.93 @@ -182,6 +203,10 @@
1.94 self.until_datetime and ", until_datetime=%r" % self.until_datetime or "",
1.95 self.qualified_by and ", qualified_by=%r" % self.qualified_by or "")
1.96
1.97 + def select(self):
1.98 +
1.99 + "Select occurrences using this object's criteria."
1.100 +
1.101 # Parsing functions.
1.102
1.103 def getRecurrence(s):
1.104 @@ -225,7 +250,48 @@
1.105 elif words.have("the"):
1.106 return parseSpecificRecurrence(words, current)
1.107 else:
1.108 - raise ParseError, words.tokens
1.109 + return parseConcreteDateTime(words, current)
1.110 +
1.111 +def parseConcreteDateTime(words, current):
1.112 +
1.113 + """
1.114 + Using the incoming 'words' and given the 'current' selector, parse and
1.115 + return a datetime acting as a specific recurrence.
1.116 + """
1.117 +
1.118 + word = words.have()
1.119 +
1.120 + # Detect dates.
1.121 +
1.122 + date = getDate(word)
1.123 + if date:
1.124 + current.add_datetime("day", intervals["day"], date)
1.125 + words.want()
1.126 + return current
1.127 +
1.128 + # Detect months.
1.129 +
1.130 + month = getMonth(word)
1.131 + if month:
1.132 + current.add_datetime("month", intervals["month"], month)
1.133 + words.want()
1.134 + return current
1.135 +
1.136 + # Detect years.
1.137 +
1.138 + if word.isdigit():
1.139 + current.add_datetime("year", intervals["year"], int(word))
1.140 + words.want()
1.141 + return current
1.142 +
1.143 + # Detect month labels.
1.144 +
1.145 + elif word in month_labels:
1.146 + current.add_datetime("month", intervals["month"], word)
1.147 + words.want()
1.148 + return current
1.149 +
1.150 + raise ParseError, words.tokens
1.151
1.152 def parseSpecificRecurrence(words, current):
1.153
1.154 @@ -271,11 +337,17 @@
1.155 """
1.156
1.157 qualifier = words.next()
1.158 +
1.159 + # Handle intervals without qualifiers.
1.160 +
1.161 if intervals.has_key(qualifier):
1.162 interval = qualifier
1.163 interval_level = intervals.get(interval)
1.164 qualifier = "single"
1.165 qualifier_level = isRepeatingQualifier(qualifier)
1.166 +
1.167 + # Handle qualified intervals.
1.168 +
1.169 else:
1.170 qualifier_level = isRepeatingQualifier(qualifier)
1.171 if not qualifier_level:
1.172 @@ -312,15 +384,21 @@
1.173 from_datetime = until_datetime = None
1.174
1.175 if words.have("from"):
1.176 - words.need("the")
1.177 from_datetime = Selector()
1.178 - from_datetime = parseSpecificRecurrence(words, from_datetime)
1.179 + words.next()
1.180 + if words.have("the"):
1.181 + from_datetime = parseSpecificRecurrence(words, from_datetime)
1.182 + else:
1.183 + from_datetime = parseConcreteDateTime(words, from_datetime)
1.184 current.set_from(from_datetime)
1.185
1.186 if words.have("until"):
1.187 - words.need("the")
1.188 until_datetime = Selector()
1.189 - until_datetime = parseSpecificRecurrence(words, until_datetime)
1.190 + words.next()
1.191 + if words.have("the"):
1.192 + until_datetime = parseSpecificRecurrence(words, until_datetime)
1.193 + else:
1.194 + until_datetime = parseConcreteDateTime(words, until_datetime)
1.195 current.set_until(until_datetime)
1.196
1.197 # Where the selector refers to a interval repeating at a frequency greater