# -*- coding: utf-8 -*- from datetime import datetime, timedelta from collections import defaultdict from trac.core import * from trac.env import IEnvironmentSetupParticipant from trac.perm import PermissionSystem from trac.db import Table, Column, Index from trac.db.util import sql_escape_percent from trac.util.datefmt import utc, to_timestamp from trac.util.text import to_unicode from trac.util.datefmt import get_timezone, utc, format_time, localtz from trac.util.translation import _ from dateutil.rrule import * from babel.core import Locale from ctdotools.utils import validate_id, gen_wiki_page from tracrendezvous.location.model import ItemLocation __all__ = ['Event', 'EventRRule', 'EventRDate', 'EventModelProvider'] def rrule_to_ical(rrule): ttypes = ("YEARLY","MONTHLY","WEEKLY","DAILY") s = "RRULE:FREQ=%s;INTERVAL=%s%s" % (ttypes[rrule._freq], rrule._interval) if rrule._until: s += ";UNTIL=%s" % rrule._until.strftime("%Y%m%dT%H%M%SZ") elif rrule._count: s += ";COUNT=%s" % rrule._count return s def unfold(d): return map(int, d.split()) def unfold_weekdays(d): return map(lambda s:map(int,s.split(",")), d.split()) class EventRDate(object): def __init__(self, env, erd_id=0, e_id=0, erd_exclude=False, erd_datetime=None): self.env = env self.erd_id = erd_id self.e_id = e_id self.erd_exclude = erd_exclude self.erd_datetime = erd_datetime @staticmethod def fetch_one(env, erd_id): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT * " "FROM event_rdates " "WHERE erd_id=%s", (erd_id,)) row = cursor.fetchone() if not row: return None return EventRDate(env, row[0], row[1], bool(row[2]), datetime.utcfromtimestamp(row[3])) @staticmethod def fetch_by_event(env, e_id): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT * " "FROM event_rdates " "WHERE e_id=%s", (e_id,)) rows = cursor.fetchall() res = list() for row in rows: res.append(EventRDate(env, row[0], row[1], bool(row[2]), datetime.utcfromtimestamp(row[3]))) return res def commit(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("INSERT INTO event_rdates " "(e_id, erd_exclude, erd_datetime) VALUES (%s,%s,%s);", (int(self.e_id), int(self.erd_exclude), to_timestamp(self.erd_datetime))) db.commit() self.erd_id = db.get_last_id(cursor, 'event_rdates') def update(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("UPDATE event_rdates SET " "erd_datetime=%s " "WHERE erd_id=%s;", (to_timestamp(self.erd_datetime), int(self.erd_id))) db.commit() @staticmethod def delete(env, erd_id): db = env.get_db_cnx() cursor = db.cursor() try: cursor.execute("DELETE FROM event_rdates " \ "WHERE erd_id=%s", (erd_id,)) db.commit() except Exception: db.rollback() pass class EventWikiPage(object): def __init__(self, env, ewp_id=0, e_id=0, time_begin=None, wikipage=None): self.env = env self.ewp_id = ewp_id self.e_id = e_id self.time_begin = time_begin self.wikipage = wikipage @staticmethod def fetch_by_event_occurrence(env, e_id, time_begin=None): db = env.get_db_cnx() cursor = db.cursor() if not time_begin: cursor.execute("SELECT * " "FROM event_wikipages " "WHERE e_id=%s", (e_id,)) else: cursor.execute("SELECT * " "FROM event_wikipages " "WHERE e_id=%s and time_begin=%s", (e_id, time_begin)) rows = cursor.fetchall() res = list() for row in rows: res.append(EventWikiPage(env, *row)) return res def commit(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("INSERT INTO event_wikipages " \ "(e_id, time_begin, wikipage) VALUES (%s,%s,%s);", (int(self.e_id), to_timestamp(self.time_begin), self.wikipage)) db.commit() self.ewp_id = db.get_last_id(cursor, 'event_wikipages') def update(self, env): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("UPDATE event_wikipages " \ "(e_id,timestamp,wikipage) VALUES (%s,%s,%s) where ewp_id=%s;", (int(self.e_id), to_timestamp(self.time_begin), self.wikipage, int(self.ewp_id))) db.commit() @staticmethod def delete(env, erd_id): db = env.get_db_cnx() cursor = db.cursor() try: cursor.execute("DELETE FROM event_wikipages " \ "WHERE ewp_id=%s", (ewp_id,)) db.commit() except Exception: db.rollback() pass class EventRRule(object): freq_enum = (YEARLY, MONTHLY, WEEKLY, DAILY) day_enum = (MO, TU, WE, TH, FR, SA, SU) # TODO: porting to babel day_abr_names = (_("MO"), _("TU"), _("WE"), _("TH"), _("FR"), _("SA"), _("SU")) day_names = (_("Monday"), _("Tuesday"), _("Wednesday"), _("Thursday"), _("Friday"), _("Saturday"), _("Sunday")) monthday_names = ["%d." % i for i in xrange(1,32)] + ['last',] + ["%d-last" % i for i in xrange(1,32)] month_names = (_("January"), _("February"), _("March"), _("April"), _("May"), _("June"), _("July"), _("August"), _("September"), _("October"), _("November"), _("December")) monthday_enum = range(1,32) + range(-1,-33,-1) weekday_names = (_("1st"), _("2nd"), _("3rd"), _("4th"), _("5th"), _("last"), _("2-last"), _("3-last"), _("4-last"), _("5-last")) selectkeys = [1, 2, 3, 4, 5, -1, -2, -3, -4, -5, -6] def __init__(self, env, err_id=0, e_id=None, exclude=None, freq=None, interval=None, count=None, until=None, bysetpos=None, bymonth=None, bymonthday=None, byyearday=None, byweeknumber=None, byweekday=set(), byweekdayocc=None): self.env = env self.err_id = err_id self.e_id = e_id self.exclude = exclude self.freq = freq self.interval = interval self.count = count self.until = until self.bysetpos = bysetpos self.bymonth = bymonth self.bymonthday = bymonthday self.byyearday = byyearday self.byweeknumber = byweeknumber self.byweekday = byweekday self.byweekdayocc = byweekdayocc @staticmethod def _fetch_data(env, e_id): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("""SELECT * FROM event_rrules WHERE e_id=%s""", (e_id,)) return cursor.fetchall() @staticmethod def fetch_by_event(env, e_id): '''Returns a list of EventRRule that can be used to display and edit the raw data ''' rows = EventRRule._fetch_data(env, e_id) if not rows: return list() res = list() for row in rows: err_id, e_id, exclude, freq, interval, count, until, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc = row if byweekday: try: byweekday = set(map(int, byweekday.split(" "))) except AttributeError: byweekday = set((byweekday, )) else: byweekday = set() res.append(EventRRule(env, err_id, e_id, exclude, freq, interval, count, until and datetime.utcfromtimestamp(until) or None, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc)) return res @staticmethod def fetch_by_event_rrules(env, e_id, time_begin=None): ''' returns a rruleset that can be used to actually display dates''' rows = EventRRule._fetch_data(env, e_id) if not rows: return rruleset() res = rruleset() for row in rows: res.rrule(EventRRule.to_rrule(row, time_begin)) return res @staticmethod def fetch_by_event_full(env, e_id, time_begin=None): rows = EventRRule._fetch_data(env, e_id) if not rows: return list(), rruleset() lst = list() rs= rruleset() for row in rows: rs.rrule(EventRRule.to_rrule(row, time_begin)) err_id, e_id, exclude, freq, interval, count, until, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc = row if byweekday: try: byweekday = set(map(int, byweekday.split(" "))) except AttributeError: byweekday = set((byweekday, )) else: byweekday = set() lst.append(EventRRule(env, err_id, e_id, exclude, freq, interval, count, until and datetime.utcfromtimestamp(until) or None, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc)) return lst, rs @staticmethod def fetch_by_event_ical(env, e_id, time_begin=None): ''' returns a list of ical formatted strings''' rows = EventRRule._fetch_data(env, e_id) lst = list() for row in rows: lst.append(EventRRule.to_ical(row)) return lst def commit(self, conn=None): db = conn and conn or self.env.get_db_cnx() cursor = db.cursor() try: cursor.execute("INSERT INTO event_rrules (e_id, exclude, freq, interval, count, until, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);", (self.e_id, self.exclude, self.freq, self.interval, self.count, to_timestamp(self.until), self.bysetpos, self.bymonth, self.bymonthday, self.byyearday, self.byweeknumber, self.byweekday and " ".join(map(str, self.byweekday)) or None, self.byweekdayocc)) db.commit() self.err_id = db.get_last_id(cursor, 'event_rrules') except Exception, e: db.rollback() raise @staticmethod def delete(env, e_id): db = env.get_db_cnx() cursor = db.cursor() try: cursor.execute("DELETE FROM event_rrules where e_id=%s", (e_id,)) db.commit() except Exception, e: db.rollback() raise def update(self, conn=None): db = conn and conn or self.env.get_db_cnx() cursor = db.cursor() weekdays = self.byweekday and " ".join(map(str, self.byweekday)) or None cursor.execute("UPDATE event_rrules " \ "SET exclude=%s, " \ "freq=%s, " \ "interval=%s, " \ "count=%s, " \ "until=%s, " \ "bysetpos=%s, " \ "bymonth=%s, " \ "bymonthday=%s, " \ "byyearday=%s, " \ "byweeknumber=%s, " \ "byweekday=%s, " \ "byweekdayocc=%s " \ "WHERE e_id=%s", (self.exclude, self.freq, self.interval, self.count, self.until and to_timestamp(self.until) or None, self.bysetpos, self.bymonth, self.bymonthday, self.byyearday, self.byweeknumber, weekdays, self.byweekdayocc, self.e_id)) if not conn: db.commit() @staticmethod def to_rrule(row, time_begin): (err_id, e_id, exclude, freq, interval, count, until, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc) = row args = {"dtstart" : time_begin, "interval" : interval} if count!=None: args["count"] = count if until: args["until"] = datetime.utcfromtimestamp(until) if bysetpos!=None: args["bysetpos"] = bysetpos if bymonth!=None: args["bymonth"] = bymonth if bymonthday!=None: args["bymonthday"] = bymonthday if byyearday!=None: args["byyearday"] = byyearday if byweeknumber!=None: args["byweekno"] = byweeknumber if byweekday!=None: if byweekdayocc != None: try: byweekdayocc = byweekdayocc.split() except Exception: byweekdayocc = [byweekdayocc,] else: byweekdayocc = [] if byweekday != None: try: byweekday = byweekday.split() except Exception: byweekday = [byweekday,] else: byweekday = [] args["byweekday"] = map( lambda x:EventRRule.day_enum[x[0]](x[1] != None and EventRRule.selectkeys[x[1]] or None), map(None, *[map(int, byweekday), map(int, byweekdayocc)])) return rrule(freq, **args) @staticmethod def to_ical(row): ttypes = ("YEARLY","MONTHLY","WEEKLY","DAILY") (err_id, e_id, exclude, freq, interval, count, until, bysetpos, bymonth, bymonthday, byyearday, byweeknumber, byweekday, byweekdayocc) = row s = ["RRULE:FREQ=%s;INTERVAL=%s" % (ttypes[freq], interval),] if until: s.append("UNTIL=%s" % datetime.utcfromtimestamp(until).strftime("%Y%m%dT%H%M%SZ")) if count!=None: s.append("COUNT=%s" % count) if bysetpos!=None: s.append("BYSETPOS=%s" % bysetpos) if bymonth!=None: s.append("BYMONTH=%s" % bymonth) if bymonthday!=None: s.append("MONTHDAY=%s" % bymonthday) if byyearday!=None: s.append("COUNT=%s" % byyearday) if byweeknumber!=None: s.append("COUNT=%s" % byweeknumber) if byweekday != None: if byweekdayocc != None: try: byweekdayocc = byweekdayocc.split() except Exception: byweekdayocc = [str(byweekdayocc),] else: byweekdayocc = [] if byweekday != None: try: byweekday = byweekday.split() except Exception: byweekday = [str(byweekday),] else: byweekday = [] #data = list() #count = 0 #print "byweekday", byweekday #print "byweekdayocc", byweekdayocc #wi = iter(byweekday) #oi = iter(byweekdayocc) #while 1: #d = wi.next() #print "d", d #try: #d = wi.next() #except StopIteration: #break #try: #o = oi.next() #except StopIteration: #o = None #print "o", o #data.append((d, o)) data = map(None, map(int, byweekday), map(int, byweekdayocc)) s.append("BYDAY=%s" % ",".join( map(lambda x:"%s%s" % (x[1] != None and EventRRule.selectkeys[x[1]] or '', EventRRule.day_enum[x[0]]), data) ) ) return ";".join(s) def explain(self): start = (_(" %d year(s)"), _("monthly"), _("weekly"), _("daily")) freq = (_("year(s)"), _("month(s)"), _("week(s)"), _("day(s)")) expl = [] expl.append("Repeat every %d %s" % (self.interval, freq[self.freq])) if self.byweekday: if self.byweekdayocc != None: try: byweekdayocc = map(int, self.byweekdayocc.split()) except Exception: byweekdayocc = [int(self.byweekdayocc), ] else: byweekdayocc = [] byweekday = self.byweekday if not byweekday: byweekday = [] tmp = map(None, byweekday, byweekdayocc) tpl = _("on %s") res = list() for x0, x1 in tmp: if x1: res.append("%s %s" % (unicode(self.weekday_names[x1]), unicode(self.day_names[x0]))) else: res.append(unicode(self.day_names[x0])) #tmp = ", ".join(res) tmp = tpl % ", ".join( map(lambda x: x[1] and _("%s %s") % (unicode(self.weekday_names[x[1]]), unicode(self.day_names[x[0]])) or unicode(self.day_names[x[0]]), tmp)) expl.append(tmp) elif self.bymonthday: expl.append(_("on day %s") % self.bymonthday) elif self.bymonth: expl.append(_("in %s") % self.month_names[self.bymonth]) elif self.byyearday: expl.append(_("on day %s") % self.byyearday) if self.count and self.count > 0: expl.append(_("for %d times" % self.count)) if self.until: expl.append(_("until %s" % self.until.strftime('%Y-%m-%d'))) #else: #raise NotImplementedError("could not provide a sane explanation of event:\n%s" % self.__str__()) return " ".join(expl) def __str__(self): return "\n".join(("self.e_id %s" % self.e_id, "self.exclude %s" % self.exclude, "self.freq %s" % self.freq, "self.interval %s" % self.interval, "self.count %s" % self.count, "self.until %s" % to_timestamp(self.until), "self.bysetpos %s" % self.bysetpos, "self.bymonth %s" % self.bymonth, "self.bymonthday %s" % self.bymonthday, "self.byyearday %s" % self.byyearday, "self.byweeknumber %s" % self.byweeknumber, "self.byweekday %s" % self.byweekday, "self.byweekdayocc %s" % self.byweekdayocc)) class Event(object): def __init__(self, env, e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id=None, tags=None, attendees=None, is_periodic=False, wikipage=None): """maps an relation of the 'events' table to a python object @type e_id: int @param e_id: primary key @type name: string @param name: name of that event @type author: string @param author: name of events' creator @type time_begin: datetime @param time_begin: begin of the event @type time_end: datetime @param time_end: end of the event @type time_created: datetime @param time_created: creation timestamp of the event @type time_modified: datetime @param time_modified: timestamp of last modification @type location_id: int @param location_id: primary key of the location the event is taking place @type initial_e_id: int @param initial_e_id: gets the same value as e_id, but only if it's not the first occurence of an recurring event @type tags: unicode @param tags: space seperated list of tags @type attendees: unicode @param attendees: space seperated list of tags @type is_periodic: bool @param is_periodic: shows if that event is recurring @type wikipage: unicode @param wikipage: the link as plaintext without 'wiki' prefix, e.g: "events/wikipage-of-that-event", or "foo" """ self.env = env self.e_id = e_id self.name = unicode(name) self.author = unicode(author) self.time_created = time_created self.time_modified = time_modified self.time_begin = time_begin self.time_end = time_end self.location_id = location_id self.initial_e_id = initial_e_id # reference to another object of the same type. if this is not None, its a follow-up event with a wiki page created self.tags = tags # space separated list of strings self.attendees = attendees # space separated list of strings self.is_periodic = is_periodic self.location = ItemLocation.fetch_one(env, location_id) #self.location = 0 self.wikipage = wikipage def periodic(self): return self.is_periodic or self.initial_e_id @staticmethod def fetch_one(env, event_id, show_next=False, show_all=False, days=365): """ returns Event with primary key event_id @type event_id: int @param event_id: primary key of Event @type show_next: bool @param show_next: if True and if Event.is periodic==True then @type show_all: bool @param show_all: if True and if Event.is periodic==True then event gets a new member list 'Event.followups' of followup events between now and now + 1year. """ db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT * " \ "FROM events " \ "WHERE e_id=%s", (event_id,)) row = cursor.fetchone() if not row: return None e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row event = Event(env, e_id, name, author, utc.localize(datetime.utcfromtimestamp(time_created)), utc.localize(datetime.utcfromtimestamp(time_modified)), utc.localize(datetime.utcfromtimestamp(time_begin)), utc.localize(datetime.utcfromtimestamp(time_end)), location_id, initial_e_id, tags, attendees, bool(is_periodic), wikipage) if show_next and is_periodic: rrules = EventRRule.fetch_by_event_rrules(env, e_id, event.time_begin) if rrules: dt = rrules.after(datetime.now(utc), True) if not dt: return event delta = event.time_end - event.time_begin s = Event(env, e_id, name, author, event.time_created, event.time_modified, dt, dt + delta, location_id, e_id, tags, attendees, False, wikipage) return s if show_all and is_periodic: event.followups = [] event.rrules_explained = None ls, rrules = EventRRule.fetch_by_event_full(env, e_id, event.time_begin) if rrules: text = ls[0].explain() event.rrules_explained = text n = datetime.now(utc) e = n + timedelta(days) followups = rrules.between(n, e, True) delta = event.time_end - event.time_begin for i in followups: dt = datetime(i.year, i.month, i.day, event.time_begin.hour, event.time_begin.minute, tzinfo=utc) s = Event(env, e_id, name, author, datetime.utcfromtimestamp(time_created), datetime.utcfromtimestamp(time_modified), dt, dt + delta, location_id, e_id, tags, attendees, False, wikipage) s.rrules_explained = text event.followups.append(s) return event @staticmethod def fetch_all(env): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT * from events;") rows = cursor.fetchall() if not rows: return [] res = [] for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) event = Event(env, e_id, name, author, utc.localize(datetime.utcfromtimestamp(time_created)), utc.localize(datetime.utcfromtimestamp(time_modified)), time_begin, utc.localize(datetime.utcfromtimestamp(time_end)), location_id, initial_e_id, tags, attendees, is_periodic, wikipage) if is_periodic: rrules = EventRRule.fetch_by_event_rrules(env, e_id, event.time_begin) if rrules: dt = rrules.after(datetime.utcnow(), inc=True) if not dt: res.append(event) continue delta = event.time_end - event.time_begin s = Event(env, e_id, name, author, event.time_created, event.time_modified, dt, dt + delta, location_id, e_id, tags, attendees, False, wikipage) res.append(s) else: res.append(event) return res @staticmethod def fetch_all_with_rrule(env): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT * from events;") rows = cursor.fetchall() if not rows: return [] res = [] for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) event = Event(env, e_id, name, author, utc.localize(datetime.utcfromtimestamp(time_created)), utc.localize(datetime.utcfromtimestamp(time_modified)), time_begin, utc.localize(datetime.utcfromtimestamp(time_end)), location_id, initial_e_id, tags, attendees, is_periodic, wikipage) res.append(event) if is_periodic: try: event.rrule = EventRRule.fetch_by_event(env, e_id)[0] except Exception: pass return res @staticmethod def fetch_as_ical(env): db = env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT * from events;") rows = cursor.fetchall() if not rows: return [] res = [] for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) event = Event(env, e_id, name, author, utc.localize(datetime.utcfromtimestamp(time_created)), utc.localize(datetime.utcfromtimestamp(time_modified)), time_begin, utc.localize(datetime.utcfromtimestamp(time_end)), location_id, initial_e_id, tags, attendees, is_periodic, wikipage) event.rrules = EventRRule.fetch_by_event_ical(env, int(e_id)) event.alarms = [ u""" BEGIN:VALARM DESCRIPTION:Gleich beginnt das Event '%s' ACTION:DISPLAY TRIGGER;VALUE=DURATION:-PT10M END:VALARM BEGIN:VALARM DESCRIPTION:In 2 Tagen beginnt das Event '%s' ACTION:DISPLAY TRIGGER;VALUE=DURATION:-P2D END:VALARM""" % (event.name, event.name)] res.append(event) return res @staticmethod def get_recurrency_data(env, e_id): return EventRRule.fetch_by_event_data(env, e_id).extend(EventRDate.fetch_by_event(env, e_id)) @staticmethod def _data_fetch_by_period(env, start_dt, end_dt, is_periodic=False, locations=[]): db = env.get_db_cnx() cursor = db.cursor() if is_periodic: cursor.execute("SELECT * from events where is_periodic=1;") return cursor.fetchall() else: query = "SELECT * from events where is_periodic='0' and (time_begin between %s and %s or time_end between %s and %s)" if locations: query += " and location_id in (%s)" % ",".join(map(sql_escape_percent, locations)) query += ";" s = to_timestamp(start_dt) e = to_timestamp(end_dt) cursor.execute(query, (s, e, s, e)) return cursor.fetchall() @staticmethod def fetch_by_period_dict(env, start_dt, end_dt, locations=[]): """returns a dictionary with date as keys and lists of events as values Some events might be periodic and the actual recurrency ruleset created and processed, so this method is somewhat cpu intensive. To keep apart singular from recurring events check Event.is_periodic """ rows = Event._data_fetch_by_period(env, start_dt, end_dt, locations=locations) res = defaultdict(list) if rows: for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) res[time_begin.date()].append( Event(env, e_id, name, author, utc.localize(datetime.utcfromtimestamp(time_created)), utc.localize(datetime.utcfromtimestamp(time_modified)), time_begin, utc.localize(datetime.utcfromtimestamp(time_end)), location_id, initial_e_id, tags, attendees, False, wikipage)) rows = Event._data_fetch_by_period(env, start_dt, end_dt, is_periodic=True, locations=locations) if not rows: return res for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) time_end = utc.localize(datetime.utcfromtimestamp(time_end)) delta = time_end - time_begin rrules = EventRRule.fetch_by_event_rrules(env, e_id, time_begin) if not rrules: raise ValueError("missing rruleset") try: myrrule = EventRRule.fetch_by_event(env, e_id)[0] except Exception, e: myrrule = None try: excluding = EventRDate.fetch_by_event(env, e_id) except Exception,e: excluding = [] for i in excluding: rrules.exdate(i.erd_datetime.replace(hour=time_begin.hour, minute=time_begin.minute)) followups = rrules.between(start_dt, end_dt, True) for i in followups: dt = datetime(i.year, i.month, i.day, time_begin.hour, time_begin.minute, tzinfo=utc) s = Event(env, e_id, name, author, datetime.utcfromtimestamp(time_created), datetime.utcfromtimestamp(time_modified), dt, dt + delta, location_id, e_id, tags, attendees, False, wikipage) if rrule: s.rrule = myrrule res[i.date()].append(s) return res @staticmethod def fetch_by_period_list(env, start_dt, end_dt, locations=[]): """If you need a list of events between datetime a and datetime b, use this method, which is less expensive than Event.fetch_by_period_dict. """ rows = Event._data_fetch_by_period(env, start_dt, end_dt, locations=locations) res = list() if rows: for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) res.append(Event(env, e_id, name, author, utc.localize(datetime.utcfromtimestamp(time_created)), utc.localize(datetime.utcfromtimestamp(time_modified)), time_begin, utc.localize(datetime.utcfromtimestamp(time_end)), location_id, initial_e_id, tags, attendees, False, wikipage)) rows = Event._data_fetch_by_period(env, start_dt, end_dt, True, locations=locations) if not rows: return res for row in rows: e_id, name, author, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic, wikipage = row time_begin = utc.localize(datetime.utcfromtimestamp(time_begin)) time_end = utc.localize(datetime.utcfromtimestamp(time_end)) time_created = utc.localize(datetime.utcfromtimestamp(time_created)) time_modified = utc.localize(datetime.utcfromtimestamp(time_modified)) delta = time_end - time_begin rrules = EventRRule.fetch_by_event_rrules(env, e_id, time_begin) followups = rrules.between(start_dt, end_dt, inc=True) try: rrule = EventRRule.fetch_by_event(env, e_id)[0] except Exception,e: pass for i in followups: dt = utc.localize(datetime(i.year, i.month, i.day, time_begin.hour, time_begin.minute)) s = Event(env, e_id, name, author, time_created, time_modified, dt, dt + delta, location_id, e_id, tags, attendees, False, wikipage) if rrule: s.rrule = rrule res.append(s) return res def commit(self, conn=None): db = conn and conn or self.env.get_db_cnx() t = utc.localize(datetime.utcnow()) cursor = db.cursor() try: cursor.execute("INSERT INTO events " \ "(name,author,time_created,time_modified,time_begin,time_end,location_id,initial_e_id,tags,attendees,is_periodic, wikipage) " \ "VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);", (self.name, self.author, to_timestamp(self.time_created), to_timestamp(self.time_modified), to_timestamp(self.time_begin), to_timestamp(self.time_end), self.location_id, self.initial_e_id, self.tags, self.attendees, int(self.is_periodic), self.wikipage)) db.commit() self.e_id = db.get_last_id(cursor, 'events') except Exception, e: db.rollback() raise @staticmethod def delete(env, e_id): db = env.get_db_cnx() cursor = db.cursor() try: cursor.execute("DELETE FROM events WHERE e_id = %s", (e_id,)) db.commit() except Exception, e: db.rollback() raise def update(self, conn=None): db = conn and conn or self.env.get_db_cnx() cursor = db.cursor() try: cursor.execute("UPDATE events " \ "SET name =%s, " \ "author=%s, " \ "time_created=%s, " \ "time_modified=%s, " \ "time_begin=%s, " \ "time_end=%s, " \ "initial_e_id=%s, " \ "location_id=%s, " \ "tags=%s, " \ "attendees=%s, " \ "is_periodic=%s, " \ "wikipage=%s " \ "WHERE e_id=%s", (self.name, self.author, to_timestamp(self.time_created), to_timestamp(self.time_modified), to_timestamp(self.time_begin), to_timestamp(self.time_end), self.initial_e_id, self.location_id, self.tags, self.attendees, int(self.is_periodic), self.wikipage, self.e_id)) db.commit() except Exception, e: db.rollback() raise def __str__(self): return "" % (self.e_id, self.name, self.time_begin, self.time_end) class EventModelProvider(Component): implements(IEnvironmentSetupParticipant) SCHEMA = [ Table('events', key='e_id')[ Column('e_id', auto_increment=True), Column('name'), Column('author'), Column('time_created', type='int'), Column('time_modified', type='int'), Column('time_begin', type='int'), Column('time_end', type='int'), Column('location_id', type='int'), Column('initial_e_id', type='int'), Column('tags'), Column('attendees'), Column('is_periodic', type="int"), Column('wikipage'), Index(['name'])], Table('event_wikipages', key='ewp_id')[ Column('ewp_id', auto_increment=True), Column('e_id'), Column('wikipage')], Table('event_rrules', key='err_id')[ Column('err_id', auto_increment=True), Column('e_id', type="int"), Column('exclude', type="int"), Column('freq', type="int"), Column('interval', type="int"), Column('count', type="int"), Column('until', type="int"), Column('bysetpos', type="int"), Column('bymonth', type="int"), Column('bymonthday', type="int"), Column('byyearday', type="int"), Column('byweeknumber'), Column('byweekday'), Column('byweekdayocc')], Table('event_rdates')[ Column('erd_id', auto_increment=True), Column('e_id', type="int"), Column('erd_exclude', type="int"), Column('erd_datetime', type="int")]] #TERMINE_DATA = ( #(u"Offizielles Treffen", #to_timestamp(datetime.now(utc)), #to_timestamp(datetime.now(utc)), #to_timestamp(datetime(2009,5,7,17,0, tzinfo=utc)), #to_timestamp(datetime(2009,5,7,20,0, tzinfo=utc)), #1, #None, #"foo bar", #"heinz horst elke peter", #True), #(u"Topic Treffen", #to_timestamp(datetime.now(utc)), #to_timestamp(datetime.now(utc)), #to_timestamp(datetime(2009,5,8,17,0, tzinfo=utc)), #to_timestamp(datetime(2009,5,8,20,0, tzinfo=utc)), #1, #None, #"foo bar", #"heinz horst elke peter", #True), #(u"Zombies Beamer Action", #to_timestamp(datetime.now(utc)), #to_timestamp(datetime.now(utc)), #to_timestamp(datetime(2009,7,2,17,0, tzinfo=utc)), #to_timestamp(datetime(2009,7,2,23,0, tzinfo=utc)), #1, #None, #"gamez beamer", #"syn knuddel kalle lucifer schnarchnase", #False)) TERMINE_PERIOD_DATA = ( (1,0,3,15,30,None), (2,0,3,30,15,None)) def environment_created(self): self._create_models(self.env.get_db_cnx()) def environment_needs_upgrade(self, db): """First version - nothing to migrate, but possibly to create. """ cursor = db.cursor() try: cursor.execute("select count(*) from events") cursor.fetchone() cursor.execute("select count(*) from event_rrules") cursor.fetchone() cursor.execute("select count(*) from event_rdates") cursor.fetchone() cursor.execute("select count(*) from event_wikipages") cursor.fetchone() return False except: db.rollback() return True def upgrade_environment(self, db): """ nothing to do here for now """ self._create_models(db) def _create_models(self, db): """Called when a new Trac environment is created.""" db_backend = None try: from trac.db import DatabaseManager db_backend, _ = DatabaseManager(self.env)._get_connector() except ImportError: db_backend = self.env.get_db_cnx() try: cursor = db.cursor() for table in self.SCHEMA: try: for stmt in db_backend.to_sql(table): self.env.log.debug(stmt) cursor.execute(stmt) except Exception, e: self.env.log.exception(e) db.commit() #cursor.executemany("""INSERT INTO 'events' # (name, time_created, time_modified, time_begin, time_end, location_id, initial_e_id, tags, attendees, is_periodic) # VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)""", self.TERMINE_DATA) #cursor.executemany("""INSERT INTO 'event_rrules' # (e_id,exclude,freq,interval,count,until) # VALUES(%s,%s,%s,%s,%s,%s)""", self.TERMINE_PERIOD_DATA) #db.commit() #gen_wiki_page(self.env, "hotshelf", "events/1", u"= Offizielles Treffen = \nDiese Page kann mit Ideen und Inhalten des Events/Treffs gefüllt werden", "localhost") #gen_wiki_page(self.env, "hotshelf", "events/2", u"= Topic Treffen = \nDiese Page kann mit Ideen und Inhalten des Events/Treffs gefüllt werden", "localhost") except Exception, e: db.rollback() raise