2012-03-31 15:45:24 +00:00
# -*- coding: utf-8 -*-
import re , math
from datetime import datetime , timedelta , time
from os import mkdir
from os . path import join
from re import match , sub
from collections import defaultdict
from sqlite3 import IntegrityError
from operator import attrgetter
from trac . config import *
from trac . mimeview . api import Mimeview , IContentConverter , Context
from trac . core import Component , implements , TracError
from trac . resource import IResourceManager , Resource , get_resource_url
from trac . perm import PermissionError , IPermissionRequestor
from trac . search import ISearchSource , search_to_sql , shorten_result
from trac . util import get_reporter_id , Ranges
from trac . util . text import to_unicode
from trac . util . html import html
from trac . util . datefmt import get_timezone , utc , format_time , localtz
#from trac.util.translation import _, add_domain
from trac . web . chrome import INavigationContributor , ITemplateProvider , add_stylesheet , add_warning , add_notice , add_ctxtnav , add_script , add_link , Chrome
from trac . web import IRequestHandler
from trac . wiki import IWikiSyntaxProvider
2012-04-25 06:35:33 +00:00
from trac . wiki . web_ui import WikiModule
2012-03-31 15:45:24 +00:00
from dateutil import rrule
from genshi . builder import tag
from genshi . template import TemplateLoader , NewTextTemplate
from tractags . api import TagSystem , ITagProvider
from tracrendezvous . location . model import ItemLocation
from tracrendezvous . event . model import Event , EventRRule , EventRDate
#from tracrendezvous.event.parse_ical import parse_ical
2012-04-24 10:25:53 +00:00
from ctdotools . utils import gen_wiki_page , validate_id , time_parse , datetime_parse , date_parse , get_tz
2012-03-31 15:45:24 +00:00
from trac . util . translation import domain_functions
add_domain , _ , tag_ = domain_functions ( ' tracrendezvous ' , ( ' add_domain ' , ' _ ' , ' tag_ ' ) )
import tracrendezvous
__all__ = [ ' EventModule ' , ' EventTagProvider ' ]
class EventTagProvider ( Component ) :
""" A tag provider using Events tag field as sources of tags.
"""
implements ( ITagProvider )
# ITagProvider methods
def get_taggable_realm ( self ) :
return ' event '
def get_tagged_resources ( self , req , tags ) :
split_into_tags = TagSystem ( self . env ) . split_into_tags
db = self . env . get_db_cnx ( )
cursor = db . cursor ( )
args = [ ]
sql = " SELECT * FROM (SELECT e_id, tags, COALESCE(tags, ' ' ) as fields FROM events) "
constraints = [ ]
if tags :
constraints . append (
" ( " + ' OR ' . join ( [ " fields LIKE %s " for t in tags ] ) + " ) " )
args + = [ ' % ' + t + ' % ' for t in tags ]
else :
constraints . append ( " fields != ' ' " )
if constraints :
sql + = " WHERE %s " % " AND " . join ( constraints )
sql + = " ORDER BY e_id "
self . env . log . debug ( sql )
cursor . execute ( sql , args )
for row in cursor :
id , ttags = row [ 0 ] , ' ' . join ( [ f for f in row [ 1 : - 1 ] if f ] )
event_tags = split_into_tags ( ttags )
tags = set ( [ to_unicode ( x ) for x in tags ] )
if ( not tags or event_tags . intersection ( tags ) ) :
yield Resource ( ' event ' , id ) , event_tags
def get_resource_tags ( self , req , resource ) :
if ' EVENTS_VIEW ' not in req . perm ( resource ) :
return
event = Event . fetch_one ( self . env , resource . id )
tags = self . __event_tags ( event )
return tags
def set_resource_tags ( self , req , resource , tags ) :
req . perm . require ( ' EVENTS_MODIFY ' , resource )
split_into_tags = TagSystem ( self . env ) . split_into_tags
event = Event . fetch_one ( self . env , resource . id )
all_ = self . __event_tags ( event )
tags . difference_update ( all_ . difference ( event . tags ) )
event . tags = u ' ' . join ( sorted ( map ( to_unicode , tags ) ) )
event . update ( )
def remove_resource_tags ( self , req , resource ) :
req . perm . require ( ' EVENTS_MODIFY ' , resource )
event = Event . fetch_one ( self . env , resource . id )
event . tags = None
event . update ( )
# Private methods
def __event_tags ( self , rendezvous ) :
return TagSystem ( self . env ) . split_into_tags ( rendezvous . tags )
class EventModule ( Component ) :
''' The web ui frontend for the event management system '''
Option ( " event " , " upcoming_name " , _ ( u " Upcoming Events " ) )
Option ( " event " , " upcoming_desc " , _ ( u " Hier erfährst Du direkt, was in den nächsten 6 Monaten im Chaostreff los ist. " ) )
Option ( " event " , " wiki_content " , _ ( u " Feel free to fill this outer space with content descibing the actual event.[[BR]]If the event is recurrent, describe the overall topics or similarities here and put the date dependant stuff into the wikipages you find via the links on the right side. " ) )
implements ( IRequestHandler ,
IResourceManager ,
IWikiSyntaxProvider ,
IContentConverter ,
IPermissionRequestor ,
INavigationContributor ,
ISearchSource ,
ITemplateProvider )
m1 = re . compile ( r ' ^/event/( \ d+)$ ' )
m2 = re . compile ( r ' ^/event/edit/( \ d+)$ ' )
m3 = re . compile ( r ' ^/event/recurrency/( \ d+)$ ' )
m4 = re . compile ( r ' ^/event/by-day/( \ d {4} )-( \ d { 1,2})-( \ d { 1,2})$ ' )
m5 = re . compile ( r ' ^/event/new/( \ d {4} )-( \ d { 1,2})-( \ d { 1,2})$ ' )
m6 = re . compile ( r ' ^/event/createpage/( \ d+)/( \ d {4} )-( \ d { 1,2})-( \ d { 1,2})$ ' )
def __init__ ( self , parent = None ) :
from pkg_resources import resource_filename
locale_dir = resource_filename ( tracrendezvous . __name__ , ' locale ' )
add_domain ( self . env . path , locale_dir )
# INavigationContributor methods
def get_active_navigation_item ( self , req ) :
return ' event '
def get_navigation_items ( self , req ) :
if " EVENTS_VIEW " in req . perm :
yield ( ' mainnav ' , ' event ' , html . A ( _ ( ' Upcoming Events ' ) , href = req . href . event ( " upcoming " ) ) )
def get_permission_actions ( self ) :
''' returns all permissions this component provides '''
return [ ' EVENTS_ADD ' , ' EVENTS_DELETE ' , ' EVENTS_MODIFY ' , ' EVENTS_VIEW ' ,
( ' EVENTS_ADMIN ' ,
( ' EVENTS_ADD ' , ' EVENTS_DELETE ' , ' EVENTS_MODIFY ' , ' EVENTS_VIEW ' ) ) ]
def match_request ( self , req ) :
self . env . log . debug ( " EventModule.match_request %s \n " % req )
key = req . path_info
simple_paths = ( " /event/upcoming " , ' /event ' , " /event/new " )
if key in simple_paths :
return True
m = self . m1 . match ( key )
if m :
req . args [ " e_id " ] = int ( m . group ( 1 ) )
return True
m = self . m2 . match ( key )
if m :
req . args [ " e_id " ] = int ( m . group ( 1 ) )
return True
m = self . m3 . match ( key )
if m :
req . args [ " e_id " ] = int ( m . group ( 1 ) )
return True
m = self . m4 . match ( key )
if m :
req . args [ ' arg_date ' ] = datetime ( int ( m . group ( 1 ) ) , int ( m . group ( 2 ) ) , int ( m . group ( 3 ) ) , 17 , tzinfo = utc )
return True
m = self . m5 . match ( key )
if m :
req . args [ ' arg_date ' ] = datetime ( int ( m . group ( 1 ) ) , int ( m . group ( 2 ) ) , int ( m . group ( 3 ) ) , 17 , tzinfo = utc )
return True
m = self . m6 . match ( key )
if m :
req . args [ " e_id " ] = int ( m . group ( 1 ) )
req . args [ ' arg_date ' ] = datetime ( int ( m . group ( 2 ) ) , int ( m . group ( 3 ) ) , int ( m . group ( 4 ) ) , tzinfo = utc )
return True
return False
# ITemplateProvider methods
def get_templates_dirs ( self ) :
from pkg_resources import resource_filename
return [ resource_filename ( __name__ , ' templates ' ) ]
def get_htdocs_dirs ( self ) :
""" Return a list of directories with static resources (such as style
sheets , images , etc . )
Each item in the list must be a ` ( prefix , abspath ) ` tuple . The
` prefix ` part defines the path in the URL that requests to these
resources are prefixed with .
The ` abspath ` is the absolute path to the directory containing the
resources on the local file system .
"""
from pkg_resources import resource_filename
return [ ( ' hw ' , resource_filename ( __name__ , ' htdocs ' ) ) ,
( ' parent ' , resource_filename ( tracrendezvous . __name__ , ' htdocs ' ) ) ]
def process_request ( self , req ) :
query = req . path_info
if " /event/upcoming " == query :
return self . __display_upcoming_events ( req )
elif " /event/by-day " in query :
return self . __display_events_by_day ( req )
elif " /event/new " in query :
return self . __process_event ( req )
elif " /event/recurrency " in query :
return self . __process_recurrency ( req )
elif " /event/edit " in query :
return self . __process_event ( req )
elif " /event/createpage " in query :
return self . __create_wiki_page ( req )
elif " /event " in query :
return self . __display_events ( req )
else :
return self . __display_upcoming_events ( req )
# IContentConverter methods
def get_supported_conversions ( self ) :
#yield ('rss', _('RSS Feed'), 'xml',
#'tracrendezvous.Event', 'application/rss+xml', 8)
yield ( ' ical ' , _ ( ' Ical Feed ' ) , ' ics ' ,
' tracrendezvous.Event ' , ' text/calendar ' , 8 )
def convert_content ( self , req , mimetype , cal , key ) :
#if key == 'rss':
#return self._export_upcoming_events_rss(req, ticket)
if key == ' ical ' :
return self . __export_upcoming_events_ical ( req , cal )
# IResourceManager methods
def get_resource_realms ( self ) :
yield ' event '
def get_resource_description ( self , resource , format = None , context = None ,
* * kwargs ) :
if format == ' compact ' :
return ' Event # %s ' % resource . id
from tracrendezvous . event . model import Event
event = Event . fetch_one ( self . env , resource . id )
# TODO: really UTC?
return " Event # %d - %s ( %s UTC - %s UTC) " % ( event . e_id , event . name , event . time_begin , event . time_end )
# ISearchSource methods
def get_search_filters ( self , req ) :
if ' EVENTS_VIEW ' in req . perm :
yield ( ' event ' , _ ( ' Events ' ) )
def get_search_results ( self , req , terms , filters ) :
if not ' event ' in filters :
return
db = self . env . get_db_cnx ( )
sql_query , args = search_to_sql ( db , [ ' e1.name ' , ' e1.author ' ] ,
terms )
cursor = db . cursor ( )
cursor . execute ( " SELECT e1.e_id, e1.name, e1.author, e1.time_modified "
" FROM events e1 "
" WHERE " + sql_query , args )
event_realm = Resource ( ' event ' )
for e_id , name , author , ts in cursor :
event = event_realm ( id = e_id )
yield ( get_resource_url ( self . env , event , req . href ) ,
name ,
2012-04-25 05:30:22 +00:00
datetime . utcfromtimestamp ( ts ) , author ,
2012-03-31 15:45:24 +00:00
_ ( " Click the link " ) )
# IWikiSyntaxProvider methods
def get_wiki_syntax ( self ) :
return [ ]
def get_link_resolvers ( self ) :
yield ( ' event ' , self . _format_event_link )
# private methods
def __process_recurrency ( self , req ) :
req . perm . require ( " EVENTS_MODIFY " )
e_id = req . args [ " e_id " ]
add_ctxtnav ( req , _ ( ' Back to ' ) + _ ( ' Overview ' ) , req . href . event ( ) )
add_ctxtnav ( req , _ ( ' Back to ' ) + _ ( ' Event ' ) + ' # %s ' % e_id , req . href . event ( ' edit ' , e_id ) )
add_stylesheet ( req , ' hw/css/ui.all.css ' )
add_stylesheet ( req , ' hw/css/event.css ' )
add_script ( req , ' hw/scripts/jquery-ui-1.6.custom.min.js ' )
if req . session . has_key ( " edited " ) :
#add_notice(req, tag.p(_("Recurrency rule created successfully. Back to "), tag.a(_("Event"), href=req.href.event(event.e_id))))
del req . session [ " edited " ]
req . session . save ( )
if req . session . has_key ( " added " ) :
#add_notice(req, tag.p(_("Recurrency rule created successfully. Back to "), tag.a(_("Event"), href=req.href.event(event.e_id))))
del req . session [ " added " ]
req . session . save ( )
if req . session . has_key ( " event_added " ) :
add_notice ( req , tag . p ( _ ( " Event created successfully. " ) + _ ( " Back to " ) , tag . a ( _ ( " Event " ) , href = req . href . event ( event . e_id ) ) ) )
del req . session [ " event_added " ]
req . session . save ( )
if req . session . has_key ( " deleted " ) :
#add_notice(req, tag.p(_("Recurrency rule deleted successfully. Back to "), tag.a(_("Event"), href=req.href.event(event.e_id))))
del req . session [ " deleted " ]
req . session . save ( )
session_tzname , selected_tz = get_tz ( req . session . get ( ' tz ' , self . env . config . get ( " trac " , " default_timezone " ) or None ) )
event = Event . fetch_one ( self . env , e_id )
if not event :
raise TracError ( _ ( " Event not found " ) )
try :
period = EventRRule . fetch_by_event ( self . env , e_id ) [ 0 ]
exists = True
except IndexError :
period = EventRRule ( self . env , e_id = event . e_id )
exists = False
freq = 0
if req . method == " POST " :
if req . args . has_key ( " save " ) :
is_valid = True
is_periodic = req . args . has_key ( " is_periodic " )
if event . is_periodic and not is_periodic :
EventRRule . delete ( self . env , event . e_id )
EventRDate . delete ( self . env , event . e_id )
event . is_periodic = False
event . update ( )
req . redirect ( req . href . event ( " recurrency " , event . e_id ) )
elif not event . is_periodic and is_periodic :
event . is_periodic = True
event . update ( )
def get_bymonth ( req , name , period ) :
try :
period . bymonth = int ( req . args [ name ] )
if not ( 0 < = period . bymonth < 12 ) :
raise Exception ( )
except Exception :
add_warning ( req , _ ( " Wrong Value for ' month ' . " ) )
raise
def get_bymonthday ( req , name , period ) :
try :
period . bymonthday = int ( req . args [ name ] )
if not ( - 33 < = period . bymonthday < 32 ) :
raise Exception ( )
except Exception , e :
add_warning ( req , _ ( " Wrong Value for ' monthday ' . " ) )
raise
def get_byweekday ( req , name , period ) :
try :
period . byweekday = set ( ( int ( req . args [ name ] ) , ) )
if filter ( lambda x : not ( 0 < = x < 7 ) , period . byweekday ) :
raise Exception ( )
except Exception , e :
add_warning ( req , _ ( " Wrong Value for ' weekday ' . " ) )
raise
def get_byweekdayocc ( req , name , period ) :
byweekdayocc = req . args [ name ]
if byweekdayocc :
try :
period . byweekdayocc = int ( byweekdayocc )
except Exception , e :
add_warning ( req , _ ( " Wrong Value for ' weekday occurence ' . " ) )
raise
else :
period . byweekdayocc = None
def get_interval ( req , name , period ) :
try :
period . interval = int ( req . args [ name ] )
if not ( 0 < = period . interval < 1000 ) :
raise Exception ( )
except Exception , e :
add_warning ( req , _ ( " Wrong Value for interval " ) )
raise
# std parameters
count = req . args [ " count " ]
if count :
try :
period . count = int ( count )
except Exception , e :
is_valid = False
add_warning ( req , _ ( " Wrong Value for count " ) )
else :
period . count = None
until_date = req . args [ " until_date " ]
if until_date :
try :
until = date_parse ( until_date )
period . until = datetime . combine ( until , time ( 12 , tzinfo = utc ) )
except Exception , e :
add_warning ( req , _ ( " Wrong Value for until_date or until_time " ) )
raise
else :
period . until = None
# selective parameters
try :
freq = int ( req . args [ " freq " ] )
except KeyError :
add_warning ( req , _ ( " Please select one of the recurrency types " ) )
is_valid = False
if freq == 0 :
period . freq = rrule . YEARLY
period . byweekday = list ( )
period . byweekdayocc = None
period . byyearday = None
try :
get_interval ( req , " yearinterval " , period )
get_bymonthday ( req , " monthday-yearly-0 " , period )
get_bymonth ( req , " monthname-yearly-0 " , period )
except Exception :
is_valid = False
elif freq == 1 :
period . freq = rrule . YEARLY
get_interval ( req , " yearinterval " , period )
period . bymonthday = None
period . byyearday = None
try :
get_byweekdayocc ( req , " dayocc-yearly-1 " , period )
get_byweekday ( req , " weekday-yearly-1 " , period )
get_bymonth ( req , " monthname-yearly-1 " , period )
except Exception :
is_valid = False
elif freq == 2 :
period . freq = rrule . YEARLY
try :
get_interval ( req , " yearinterval " , period )
except Exception :
is_valid = False
period . bymonthday = None
period . bymonth = None
period . byweekday = list ( )
period . byweekdayocc = None
try :
period . byyearday = int ( req . args [ " yearday " ] )
if not ( 0 < = period . byyearday < 366 ) :
raise Exception ( )
except Exception , e :
add_warning ( req , _ ( " Wrong Value for ' weekday ' . " ) )
is_valid = False
elif freq == 3 :
period . freq = rrule . MONTHLY
try :
get_interval ( req , " monthinterval " , period )
get_bymonthday ( req , " monthday-monthly-0 " , period )
except Exception :
is_valid = False
period . byweekday = None
period . byweekdayocc = None
period . byyearday = None
period . bymonth = None
elif freq == 4 :
period . freq = rrule . MONTHLY
period . byyearday = None
period . bymonth = None
period . bymonthday = None
try :
get_interval ( req , " monthinterval " , period )
get_byweekdayocc ( req , " dayocc-monthly-1 " , period )
get_byweekday ( req , " weekday-monthly-1 " , period )
except Exception :
is_valid = False
elif freq == 5 :
period . freq = rrule . WEEKLY
try :
get_interval ( req , " weekinterval " , period )
except Exception :
is_valid = False
period . byweekday = list ( )
period . byweekdayocc = None
period . byyearday = None
period . bymonth = None
period . bymonthday = None
try :
weekdays = set ( map ( int , req . args [ " weekday-weekly " ] ) )
if any ( map ( lambda x : not ( 0 < = x < 7 ) , weekdays ) ) :
is_valid = False
if weekdays != period . byweekday :
period . byweekday = weekdays
except KeyError :
add_warning ( req , _ ( " Any days selected. " ) )
is_valid = False
elif freq == 6 :
period . freq = rrule . DAILY
try :
get_interval ( req , " dayinterval " , period )
except Exception :
is_valid = False
period . byweekday = None
period . byweekdayocc = None
period . byyearday = None
period . bymonth = None
period . bymonthday = None
if is_valid :
if exists :
period . update ( )
else :
period . commit ( )
req . redirect ( req . href . event ( " recurrency " , e_id ) )
elif req . args . has_key ( " exception-add " ) :
try :
exception = datetime_parse ( " %s 12:00 " % req . args [ " exception-name " ] , selected_tz )
exception = exception . replace ( hour = event . time_begin . hour , minute = event . time_begin . minute )
except ValueError :
add_warning ( req , _ ( " Wrong recurrency exception date format " ) )
req . redirect ( req . href . event ( " recurrency " , e_id ) )
rdate = EventRDate ( self . env , 0 , event . e_id , True , exception )
rdate . commit ( )
req . session [ " added " ] = True
req . redirect ( req . href . event ( " recurrency " , e_id ) )
elif req . args . has_key ( " exception-edit " ) :
for k in req . args . iterkeys ( ) :
try :
marker , erd_id = k . split ( " : " , 1 )
except Exception , e :
continue
try :
erd_id = int ( erd_id )
validate_id ( erd_id )
except ValueError , e :
continue
if marker != " exception " :
continue
rdate = EventRDate . fetch_one ( self . env , erd_id )
try :
exception = datetime_parse ( " %s 12:00 " % req . args [ " exception-name " ] , selected_tz )
exception = exception . replace ( hour = event . time_begin . hour , minute = event . time_begin . minute )
except KeyError :
add_warning ( req , _ ( " You have to specify a valid recurrency exception date for that operation! " ) )
req . redirect ( req . href . event ( " recurrency " , e_id ) )
if exception :
rdate . erd_datetime = exception
rdate . update ( )
req . session [ " edited " ] = True
req . redirect ( req . href . event ( " recurrency " , e_id ) )
req . redirect ( req . href . event ( " recurrency " , e_id ) )
elif req . args . has_key ( " exception-delete " ) :
for k in req . args . iterkeys ( ) :
try :
marker , erd_id = k . split ( " : " , 1 )
except Exception , e :
continue
try :
erd_id = int ( erd_id )
validate_id ( erd_id )
except ValueError , e :
continue
if marker != " exception " :
continue
EventRDate . delete ( self . env , erd_id )
req . session [ " deleted " ] = True
req . redirect ( req . href . event ( " recurrency " , e_id ) )
else :
if period . bymonthday != None and period . bymonth != None :
freq = 0
elif period . byweekday and period . bymonth != None :
freq = 1
elif period . byyearday != None :
freq = 2
elif period . bymonthday != None :
freq = 3
elif period . byweekdayocc != None and period . byweekday :
freq = 4
elif period . freq == rrule . WEEKLY :
freq = 5
elif period . freq == rrule . DAILY :
freq = 6
exceptions = EventRDate . fetch_by_event ( self . env , event . e_id )
return " recur_edit.html " , {
" event " : event ,
" freq " : freq ,
" period " : period ,
" session_tzname " : session_tzname ,
" selected_tz " : selected_tz ,
" exceptions " : exceptions ,
" default_location " : self . config . getint ( " location " , " default_location " ) ,
" locations " : ItemLocation . fetch_all ( self . env ) ,
" month_names " : EventRRule . month_names ,
" day_names " : EventRRule . day_names ,
" weekday_names " : EventRRule . weekday_names ,
" monthday_names " : EventRRule . monthday_names } , None
def __process_event ( self , req ) :
''' process add, change and delete rendezvous '''
if ' EVENTS_ADD ' not in req . perm and ' EVENTS_MODIFY ' not in req . perm and ' EVENTS_DELETE ' not in req . perm :
raise PermissionError ( )
add_stylesheet ( req , ' hw/css/event.css ' )
add_stylesheet ( req , ' hw/css/ui.all.css ' )
add_script ( req , ' hw/scripts/jquery-ui-1.6.custom.min.js ' )
# set information after redirects
if req . session . has_key ( " edited " ) :
add_notice ( req , tag . p ( _ ( " Event edited successfully. Back to " ) , tag . a ( _ ( " Overview " ) , href = req . href . event ( ) ) ) )
del req . session [ " edited " ]
req . session . save ( )
if req . session . has_key ( " added " ) :
add_notice ( req , tag . p ( _ ( " Event created successfully. Back to " ) , tag . a ( _ ( " Overview " ) , href = req . href . event ( ) ) ) )
del req . session [ " added " ]
req . session . save ( )
if req . session . has_key ( " deleted " ) :
add_notice ( req , tag . p ( _ ( " Event deleted successfully. Back to " ) , tag . a ( _ ( " Overview " ) , href = req . href . event ( ) ) ) )
del req . session [ " deleted " ]
req . session . save ( )
myenv = self . env
2012-04-24 10:25:53 +00:00
date_now = utc . localize ( datetime . utcnow ( ) )
2012-03-31 15:45:24 +00:00
session_tzname , selected_tz = get_tz ( req . session . get ( ' tz ' , self . config . get ( " trac " , " default_timezone " ) or None ) )
if req . args . has_key ( " e_id " ) :
event = Event . fetch_one ( myenv , req . args [ " e_id " ] )
if not event :
raise TracError ( _ ( " Event not found " ) )
add_ctxtnav ( req , _ ( ' Events Overview ' ) , req . href . event ( ) )
add_ctxtnav ( req , _ ( ' Upcoming Events ' ) , req . href . event ( " upcoming " ) )
add_ctxtnav ( req , _ ( ' Add New Event ' ) , req . href . event ( " new " ) )
else :
try :
pi = req . path_info
crap , new_date = pi . split ( " new/ " , 1 )
year , month , day = re . match ( " ( \ d {4} )-( \ d { 1,2})-( \ d { 1,2}) " , new_date ) . groups ( )
2012-04-24 10:25:53 +00:00
new_date = utc . localize ( datetime ( int ( year ) , int ( month ) , int ( day ) , 17 , tzinfo = selected_tz ) )
2012-03-31 15:45:24 +00:00
except ValueError :
new_date = date_now
2012-04-24 10:25:53 +00:00
event = Event ( myenv , 0 , " " , req . authname , date_now , date_now , new_date , selected_tz . normalize ( new_date + timedelta ( 0 , 10800 ) ) , 1 )
2012-03-31 15:45:24 +00:00
add_ctxtnav ( req , _ ( ' Events Overview ' ) , req . href . event ( ) )
add_ctxtnav ( req , _ ( ' Upcoming Events ' ) , req . href . event ( " upcoming " ) )
if req . method == " POST " :
if req . args . has_key ( " delete " ) :
req . perm . require ( " EVENTS_DELETE " )
EventRRule . delete ( myenv , event . e_id )
EventRDate . delete ( myenv , event . e_id )
Event . delete ( myenv , event . e_id )
db = myenv . get_db_cnx ( )
cursor = db . cursor ( )
# TODO: that is hackish. find out how to use trac arg formatting
# of "select * from foo where bar like 'baz%';"
like = " delete from wiki where name like ' events/ %s / %% ' " % int ( event . e_id )
cursor . execute ( like )
db . commit ( )
req . session [ " deleted " ] = True
req . redirect ( req . href . event ( " new " ) )
is_valid = True
event . name = req . args . get ( " name " , None )
try :
event . old_time_begin = event . time_begin
event . time_begin = datetime_parse ( " %s %s " % ( req . args [ " date_begin " ] , req . args [ " time_begin " ] ) , selected_tz )
2012-04-24 10:25:53 +00:00
print " time_begin " , event . time_begin , req . args [ " time_begin " ]
2012-03-31 15:45:24 +00:00
except Exception :
add_warning ( req , _ ( " Wrong format in ' Date begin ' . " ) )
is_valid = False
try :
event . old_time_end = event . time_end
event . time_end = datetime_parse ( " %s %s " % ( req . args [ " date_end " ] , req . args [ " time_end " ] ) , selected_tz )
2012-04-24 10:25:53 +00:00
print " time_end " , event . time_end , req . args [ " time_end " ]
2012-03-31 15:45:24 +00:00
except Exception , e :
add_warning ( req , _ ( " Wrong format in ' Date end ' . " ) )
is_valid = False
try :
event . location_id = int ( req . args [ " location_id " ] )
location = ItemLocation . fetch_one ( myenv , event . location_id )
except Exception , e :
add_warning ( req , _ ( " Could not find location " ) )
is_valid = False
tags = req . args . get ( " tags " , None )
if tags :
event . tags = sub ( " [,;.:] " , " " , tags )
attendees = req . args . get ( " attendees " , None )
if attendees :
attendees = sub ( " [,;.:] " , " " , attendees )
if is_valid :
if not req . args . has_key ( " e_id " ) :
req . perm . require ( " EVENTS_ADD " )
try :
self . __validate_event ( event )
event . commit ( )
except Exception , e :
add_warning ( req , str ( e ) )
raise
event . wikipage = " events/ %d " % event . e_id
event . update ( )
wikicontent = u " = %s = \n [[EventHeader( %d )]] \n \n %s " % ( event . name , event . e_id , self . config . get ( " event " , " wiki_content " ) )
try :
gen_wiki_page ( myenv , req . authname , event . wikipage , wikicontent , req . remote_addr )
except IntegrityError , e :
add_warning ( req , _ ( " Wikipage already exists. " ) )
if req . args . has_key ( " show_recurrency " ) :
req . redirect ( req . href . event ( " recurrency " , event . e_id ) )
req . session [ " event_added " ] = True
req . session . save ( )
if req . args . has_key ( " show_recurrency " ) :
req . session [ " added " ] = True
req . session . save ( )
req . redirect ( req . href . event ( " edit " , event . e_id ) )
else :
req . perm . require ( " EVENTS_MODIFY " )
try :
self . __validate_event ( event )
except Exception , e :
add_warning ( req , str ( e ) )
raise
else :
2012-04-24 10:25:53 +00:00
event . time_modified = utc . localize ( datetime . utcnow ( ) )
2012-03-31 15:45:24 +00:00
event . update ( )
req . session [ " edited " ] = True
req . session . save ( )
req . redirect ( req . href . event ( " edit " , event . e_id ) )
2012-04-24 10:25:53 +00:00
2012-03-31 15:45:24 +00:00
return " event_edit.html " , { " event " : event ,
" session_tzname " : session_tzname ,
" selected_tz " : selected_tz ,
" default_location " : self . config . getint ( " location " , " default_location " ) ,
" locations " : ItemLocation . fetch_all ( myenv ) } , None
def __display_events ( self , req ) :
''' process add,change,delete actions '''
add_stylesheet ( req , ' hw/css/event.css ' )
if " EVENTS_ADD " in req . perm :
add_ctxtnav ( req , _ ( ' Add New Event ' ) , req . href . event ( " new " ) )
add_ctxtnav ( req , _ ( ' Upcoming Events ' ) , req . href . event ( " upcoming " ) )
if req . args . has_key ( " e_id " ) :
events = [ Event . fetch_one ( self . env , req . args [ " e_id " ] , show_next = True ) ]
title = _ ( " Event Details " )
else :
events = Event . fetch_all_with_rrule ( self . env )
events = sorted ( events , key = attrgetter ( " time_begin " ) )
title = _ ( " Event Overview " )
session_tzname , selected_tz = get_tz ( req . session . get ( ' tz ' , self . env . config . get ( " trac " , " default_timezone " ) or None ) )
2012-04-25 06:35:33 +00:00
return " event_list.html " , { " wiki_on " : self . env . is_component_enabled ( WikiModule ) , " events " : events , " title " : title , " session_tzname " : session_tzname , " selected_tz " : selected_tz } , None
2012-03-31 15:45:24 +00:00
def __display_upcoming_events ( self , req ) :
if req . args . get ( " format " ) == ' ical ' :
events = Event . fetch_as_ical ( self . env )
Mimeview ( self . env ) . send_converted ( req , ' tracrendezvous.Event ' , events , " ical " , _ ( " CTDO: Upcoming Events " ) )
add_stylesheet ( req , ' hw/css/event.css ' )
if req . locale is not None :
add_script ( req , ' parent/tracrendezvous/ %s .js ' % req . locale )
if " EVENTS_ADD " in req . perm :
add_ctxtnav ( req , _ ( ' Add New Event ' ) , req . href . event ( " new " ) )
add_ctxtnav ( req , _ ( ' Events Overview ' ) , req . href . event ( ) )
for conversion in Mimeview ( self . env ) . get_supported_conversions ( " tracrendezvous.Event " ) :
conversion_href = req . href . event ( " upcoming " , format = conversion [ 0 ] )
add_link ( req , ' alternate ' , conversion_href , conversion [ 1 ] , conversion [ 3 ] , conversion [ 0 ] )
session_tzname , selected_tz = get_tz ( req . session . get ( ' tz ' , self . env . config . get ( " trac " , " default_timezone " ) or None ) )
2012-04-25 06:35:33 +00:00
n = utc . localize ( datetime . utcnow ( ) )
2012-03-31 15:45:24 +00:00
n = n . replace ( hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
end = n + timedelta ( 183 )
end = end . replace ( hour = 23 , minute = 0 , second = 59 , microsecond = 999 )
table , headers = self . __get_upcoming_table ( n , end )
2012-04-25 06:35:33 +00:00
return " events.html " , { " wiki_on " : self . env . is_component_enabled ( WikiModule ) , " table " : table , " headers " : headers , " session_tzname " : session_tzname , " format " : " %a , %d . % m. % Y " , " selected_tz " : selected_tz , " title " : _ ( " Upcoming Events for " ) , " title2 " : " %s - %s " % ( n . strftime ( ' % A, %d . % m. % Y % H: % M ' ) , end . strftime ( ' % A, %d . % m. % Y % H: % M ' ) ) , " now " : n , " end " : end } , None
2012-03-31 15:45:24 +00:00
def __display_events_by_day ( self , req ) :
add_stylesheet ( req , ' hw/css/event.css ' )
add_ctxtnav ( req , _ ( ' Back to Upcoming Events ' ) , req . href . event ( " upcoming " ) )
session_tzname , selected_tz = get_tz ( req . session . get ( ' tz ' , self . env . config . get ( " trac " , " default_timezone " ) or None ) )
# do datetime calc only in utc !!!
n = title = req . args [ " arg_date " ]
n = datetime ( n . year , n . month , n . day , tzinfo = utc )
#n = local_to_utc(n, selected_tz)
e = n + timedelta ( 1 )
if " EVENTS_ADD " in req . perm :
add_ctxtnav ( req , _ ( ' Add New Event ' ) , req . href . event ( " new " , title . strftime ( " % Y- % m- %d " ) ) )
table , headers = self . __get_day_table ( n , e )
return " event_details.html " , { " table " : table , " headers " : headers , " format " : " % H: % M " , " session_tzname " : session_tzname , " selected_tz " : selected_tz , " title " : _ ( " Details of %s " ) % ( title . strftime ( ' % A, %d . % m. % Y ' ) , ) , " now " : n , " end " : e } , None
def __export_upcoming_events_rss ( self , req , foo ) :
now = datetime . now ( utc )
now . replace ( hour = 0 , second = 0 , microsecond = 0 )
end = now + timedelta ( 183 )
end . replace ( hour = 23 , second = 59 , microsecond = 999 )
events = Event . fetch_all ( self . env )
data = { " events " : sorted ( events , key = attrgetter ( " time_begin " ) ) , " today " : datetime . now ( utc ) }
output = Chrome ( self . env ) . render_template ( req , ' upcoming_events.rss ' , data ,
' application/rss+xml ' )
return output , ' application/rss+xml '
def __export_upcoming_events_ical ( self , req , events ) :
data = { " stamp " : datetime . now ( utc ) . strftime ( " % Y % m %d T % H % M % SZ " ) ,
" events " : sorted ( events , key = attrgetter ( " time_begin " ) ) ,
" today " : datetime . now ( utc ) ,
" calname " : self . env . config . get ( " events " , " upcoming_name " ) ,
" caldesc " : self . env . config . get ( " events " , " upcoming_desc " ) }
ch = Chrome ( self . env )
ds = ch . get_all_templates_dirs ( )
data = ch . populate_data ( req , data )
templates = TemplateLoader ( ds , auto_reload = self . env . config . getbool ( ' trac ' , ' auto_reload ' ) , variable_lookup = ' lenient ' )
template = templates . load ( " ical.txt " , cls = NewTextTemplate )
try :
stream = template . generate ( * * data )
output = stream . render ( )
return output , ' text/calendar '
except Exception , e :
# give some hints when hitting a Genshi unicode error
if isinstance ( e , UnicodeError ) :
pos = self . _stream_location ( stream )
if pos :
location = " ' %s ' , line %s , char %s " % pos
else :
location = _ ( " (unknown template location) " )
raise TracError ( _ ( " Genshi %(error)s error while rendering "
" template %(location)s " ,
error = e . __class__ . __name__ ,
location = location ) )
raise
def __validate_event ( self , event ) :
if not event . name :
raise TypeError ( _ ( " EventValidationError: Title is empty " ) )
if event . time_end < = event . time_begin :
raise TypeError ( _ ( " EventValidationError: end time before or equal begin time " ) )
def __get_upcoming_table ( self , timerange_begin , timerange_end ) :
# ongoing events goes in here referenced by column index
res = Event . fetch_by_period_dict ( self . env , timerange_begin , timerange_end )
ongoing = { } # column index -> event
table = [ ]
timerange_begin = timerange_begin . date ( )
timerange_end = timerange_end . date ( )
dt = timedelta ( 1 )
column_count = 0
def get_next ( d ) :
count = 1
while 1 :
if not d . has_key ( count ) :
return count
count + = 1
while timerange_begin < timerange_end :
row = { 0 : timerange_begin }
done = [ ]
for key , event in ongoing . iteritems ( ) :
if event . time_end . date ( ) < timerange_begin :
done . append ( key )
for i in done :
del ongoing [ i ]
for key in ongoing . iterkeys ( ) :
row [ key ] = False
events = res [ timerange_begin ]
for event in events :
dt2 = event . time_end - event . time_begin
dt2_days = dt2 . days + 1
if dt2 . seconds > 43200 :
dt2_days + = 1
slot = get_next ( ongoing )
event . rowspan = dt2_days
ongoing [ slot ] = event
row [ slot ] = [ event , ]
column_count = max ( column_count , len ( ongoing ) )
table . append ( row )
timerange_begin + = dt
table2 = [ ]
mbase = [ True for x in xrange ( column_count + 1 ) ]
for row in table :
myrow = mbase [ : ]
for key , value in row . iteritems ( ) :
myrow [ key ] = value
table2 . append ( myrow )
mbase = [ " " for x in xrange ( column_count + 1 ) ]
mbase [ 0 ] = _ ( " date " )
return table2 , mbase
def __create_wiki_page ( self , req ) :
''' Programatically create a new wiki page tailored to a given occurence of an event
- one date of a recurrence set .
'''
req . perm . require ( " WIKI_CREATE " )
dt = req . args [ " arg_date " ]
e_id = req . args [ " e_id " ]
wikipage = " events/ %d / %s " % ( e_id , dt . strftime ( " % Y- % m- %d " ) )
event = Event . fetch_one ( self . env , e_id )
wikicontent = u " = %s = \n [[EventHeader( %d )]] \n \n %s " % ( event . name , event . e_id , self . env . config . get ( " event " , " wiki_content " , " " ) )
try :
gen_wiki_page ( self . env , req . authname , wikipage , wikicontent , req . remote_addr )
except IntegrityError , e :
pass
req . redirect ( req . href . wiki ( wikipage ) )
def __get_day_table ( self , timerange_begin , timerange_end , dt = timedelta ( 0 , 1800 ) ) :
''' Calculates and arranges events during a day period which can be defined
by start datetime ' timerange_begin ' and end datetime ' timerange_end ' .
The resolution of the " table " prepared for html or other UIs can be
controlled by timedelta ' dt ' .
The first column contains the time slots , additional columns contain
destinct locations .
It returns a row list of cells . '''
class UList ( list ) :
def ___init__ ( self ) :
list . __init__ ( self )
self . rowspan = None
events = Event . fetch_by_period_list ( self . env , timerange_begin , timerange_end )
if not events :
return None , None
timerange_begin = min ( events , key = lambda x : x . time_begin ) . time_begin
timerange_end = max ( events , key = lambda x : x . time_end ) . time_end
# now we calcutate the value of previous "slot" beginning as datetime to beautify
# the slot time beginnings. Try to uncomment the next few lines and reload to see
# what happens to the table without;-)
dts = dt . days * 86400 + dt . seconds
tbs = ( ( timerange_begin . hour * 3600 + timerange_begin . minute * 60 ) / dts ) * dts
minutes , seconds = divmod ( tbs , 60 )
hours , minutes = divmod ( minutes , 60 )
timerange_begin = timerange_begin . replace ( hour = hours , minute = minutes )
#timerange_end += dt # we want one slot after the last event ends
column_count = 1 # column 0 is always timerange_begin
locmap = dict ( ) # location_id -> column index
ongoing = dict ( ) # column index of actual used column -> eventlist
locs = dict ( ) # column index -> location name
slotcount = 0
table = [ ]
done = [ ]
while timerange_begin < timerange_end :
row = { 0 : timerange_begin }
for k , e in done :
events . remove ( e )
done = [ ]
slot_end = timerange_begin + dt
for event in events :
if event . time_begin > = timerange_begin and event . time_begin < slot_end :
if not locmap . has_key ( event . location_id ) :
index = column_count
column_count + = 1
locmap [ event . location_id ] = index
locs [ event . location_id ] = event . location . name
else :
index = locmap [ event . location_id ]
if not hasattr ( event , " rowspan_begin " ) :
# event starts in that timeslot
event . rowspan_begin = slotcount
if not row . has_key ( index ) :
row [ index ] = UList ( )
if ongoing . has_key ( index ) :
ongoing [ index ] [ 0 ] + = 1
refc , event_list = ongoing [ index ]
else :
event_list = row [ index ]
ongoing [ index ] = [ 1 , event_list ]
event_list . append ( event )
if event . time_end < = slot_end :
# event ends
index = locmap [ event . location_id ]
done . append ( ( locmap [ event . location_id ] , event ) )
event . rowspan_end = slotcount + 1
refc , el = ongoing [ index ]
if refc < = 1 :
del ongoing [ index ]
else :
refc - = 1
ongoing [ index ] = [ refc , el ]
if hasattr ( event , " rowspan_begin " ) and event . time_begin < timerange_begin and event . time_end > timerange_begin :
# marking cell as occupied due to longer lasting event than time slot length
index = locmap [ event . location_id ]
row [ index ] = False
table . append ( row )
timerange_begin + = dt
slotcount + = 1
final_table = [ ]
header = [ True for x in xrange ( column_count ) ]
for row in table :
trow = header [ : ]
for index , item in row . iteritems ( ) :
trow [ index ] = item
if item and isinstance ( item , UList ) :
rowspan_begin = min ( item , key = lambda x : x . rowspan_begin ) . rowspan_begin
rowspan_end = max ( item , key = lambda x : x . rowspan_end ) . rowspan_end
item . rowspan = rowspan_end - rowspan_begin
final_table . append ( trow )
for location_id , index in locmap . iteritems ( ) :
header [ index ] = locs [ location_id ]
header [ 0 ] = _ ( " Start " )
return final_table , header
def _format_event_link ( self , formatter , ns , target , label , fullmatch = None ) :
link , params , fragment = formatter . split_link ( target )
r = Ranges ( link )
if len ( r ) == 1 :
num = r . a
validate_id ( num )
cursor = formatter . db . cursor ( )
cursor . execute ( " SELECT name,time_begin,time_end "
" FROM events WHERE e_id= %s " , ( num , ) )
session_tzname , selected_tz = get_tz ( formatter . req . session . get ( " tz " , self . env . config . get ( " trac " , " default_timezone " ) or None ) )
for name , time_begin , time_end in cursor :
2012-04-25 05:30:22 +00:00
time_begin = utc . localize ( datetime . utcfromtimestamp ( time_begin ) ) . astimezone ( selected_tz )
time_end = utc . localize ( datetime . datetime . utcfromtimestamp ( time_end ) ) . astimezone ( selected_tz )
2012-03-31 15:45:24 +00:00
title = " %s ( %s - %s %s ) " % ( name ,
time_begin . strftime ( ' %d . % m. % Y % H: % M ' ) ,
time_end . strftime ( ' %d . % m. % Y % H: % M ' ) , time_begin . tzinfo . tzname ( None ) )
if label == link :
label = title
href = formatter . href . event ( num )
return tag . a ( label , title = title , href = href )
return tag . a ( label , class_ = ' missing event ' )