#!/usr/bin/python3 import sys, os, re, datetime eventTypes = {'APPOINTMENT' : {'hasTime': 1, 'numDates' : 2}, 'UNTIMED_EVENT' : {'hasTime' : 0, 'numDates' : 1}} monthToIndex = {'jan' : 1, 'feb' : 2, 'mar' : 3, 'apr' : 4, 'may' : 5, 'jun' : 6, 'jul' : 7, 'aug' : 8, 'sep' : 9, 'oct' : 10, 'nov' : 11, 'dec' : 12} dayToIndex = {'sun' : 0, 'mon' : 1, 'tue' : 2, 'wed' : 3, 'thu' : 4, 'fri' : 5, 'sat' : 6} indexToDay = {0: 'sun', 1: 'mon', 2: 'tue', 3: 'wed', 4: 'thu', 5: 'fri', 6: 'sat'} entries = [] class Repeat: def __init__(self): self.startDate = None self.endDate = None self.repeatType = '' self.repeatDays = [] def __repr__(self): string = "Repeats from %s to %s" % (self.startDate, self.endDate) if (self.repeatType == 'weekly'): string = string + " on" for day in self.repeatDays: string = string + " %s" % indexToDay[day] return string def containsDate(self, date): if (date < self.startDate or self.endDate < date): return 0 if (self.repeatType == 'daily'): return 1 elif (self.repeatType == 'weekly'): return ((date.isoweekday() % 7) in self.repeatDays) else: print("Unknown repeat type: %s" % self.repeatType) return 0 class DatebookEntry: def __init__(self): self.entryType = '' self.hasTime = 0 self.startDate = None self.endDate = None self.title = '' self.category = None self.repeat = None def __repr__(self): string = "%s (%s" % (self.title, self.startDate) if (self.endDate): string = string + " to %s" % self.endDate string = string + ")" if (self.category): string = string + " - %s" % self.category if (self.repeat): string = string + " %s" % self.repeat return string def containsDate(self, date): if (self.repeat): return self.repeat.containsDate(date) else: startDate = self.getStartDate() endDate = self.getEndDate() return (startDate == date or endDate == date) # TODO - make static? def getDateFromDateOrDatetime(self, dordt): toReturn = dordt # If we're a datetime, get a date if (isinstance(dordt, datetime.datetime)): toReturn = dordt.date() return toReturn def getStartDate(self): return self.getDateFromDateOrDatetime(self.startDate) def getEndDate(self): return self.getDateFromDateOrDatetime(self.endDate) dateRE = re.compile(r'^(\d+)\s+(\w+)\s+(\d+)(?:\s+(\d+):(\d+))?$') categoryRe = re.compile(r'^category=\d+\s+\((.*?)\)$', re.IGNORECASE) def parseDate(text, hasTime): dateMatch = dateRE.match(text) if (dateMatch): if (hasTime): return datetime.datetime(int(dateMatch.group(3)), monthToIndex[dateMatch.group(2).lower()], int(dateMatch.group(1)), int(dateMatch.group(4)), int(dateMatch.group(5))) else: return datetime.date(int(dateMatch.group(3)), monthToIndex[dateMatch.group(2).lower()], int(dateMatch.group(1))) else: return None def parseLines(lines): inEntry = 0 curEntry = None for line in lines: if (not inEntry): for eventType in eventTypes: if (line.startswith(eventType)): inEntry = 1 # Find all <> pairs after the first. lessThanIndex = line.find('<') dates = [] for i in range(0, eventTypes[eventType]['numDates']): lessThanIndex = line.find('<', lessThanIndex + 1) greaterThanIndex = line.find('>', lessThanIndex) dates.append(parseDate(line[lessThanIndex+1:greaterThanIndex], eventTypes[eventType]['hasTime'])) #print "got event with dates: " #for date in dates: #print str(date) curEntry = DatebookEntry() curEntry.entryType = eventType curEntry.hasTime = len(dates) - 1 curEntry.startDate = dates[0] if (curEntry.hasTime): curEntry.endDate = dates[1] #print repr(curEntry) else: if (line == '' or line == '\n'): #print "Adding %s" % repr(curEntry) entries.append(curEntry) curEntry = None inEntry = 0 else: if (line.startswith('attributes=')): # We don't care about attributes pass elif (line.startswith('category=')): categoryMatch = categoryRe.match(line) if (categoryMatch): curEntry.category = categoryMatch.group(1) else: print("ERROR - couldn't parse category line: %s" % line) elif (line.startswith('REPEAT from')): repeatRe = re.compile("^REPEAT from <(.*?)> until <(.*?)>:\s*(.*)$") repeatMatch = repeatRe.match(line) if (repeatMatch): startRepeatDate = parseDate(repeatMatch.group(1), 0) endRepeatDate = parseDate(repeatMatch.group(2), 0) intervalRe = re.compile(r"^<(.*?)>(?: on <(.*?)>)?$") intervalMatch = intervalRe.match(repeatMatch.group(3)) if (intervalMatch): repeat = Repeat() repeat.startDate = startRepeatDate repeat.endDate = endRepeatDate repeat.repeatType = intervalMatch.group(1).lower() if (repeat.repeatType == 'weekly'): repeat.repeatDays = [dayToIndex[x] for x in intervalMatch.group(2).lower().split(',')] # TODO - add other cases curEntry.repeat = repeat else: print("ERROR - couldn't parse interval line: %s" % repeatMatch.group(3)) else: print("ERROR - couldn't parse repeat line: %s" % line) else: curEntry.title = line[0:-1] def getEntries(fileName, date, numDays): datebookFile = open(fileName, 'r') lines = datebookFile.readlines() datebookFile.close() parseLines(lines) curDate = date toReturn = {} for i in range(0, numDays): entriesToday = [e for e in entries if e.containsDate(curDate)] if (entriesToday): toReturn[curDate] = entriesToday curDate = curDate + datetime.timedelta(1) return toReturn def main(): if (len(sys.argv) > 1): datebookFileName = sys.argv[1] else: datebookFileName = 'datebook.txt' entries = getEntries(datebookFileName, datetime.date.today(), 40) for date in entries: print("Entries for %s:" % date) for entry in entries[date]: print(" %s" % entry) if (__name__ == '__main__'): main()