ctdo-trac/TracRendezVous/tracrendezvous/event/model.py

1112 lines
42 KiB
Python

# -*- 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 "<Event: %d, %s, %s, %s>" % (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