# -*- coding: utf-8 -*- import re, calendar, time from datetime import date, timedelta from genshi.builder import tag from trac.core import Component, implements, TracError from trac.web import IRequestHandler from trac.web.chrome import INavigationContributor, ITemplateProvider from trac.util.datefmt import to_datetime from trac.config import BoolOption from ganttcalendar.translation import _ month_tbl = { 1: 'January', 2: 'February', 3: 'March', 4: 'April', 5: 'May', 6: 'June', 7: 'July', 8: 'August', 9: 'September', 10: 'October', 11: 'November', 12: 'December' } def _to_float(value, default=None): try: return float(value) except: return default class TicketCalendarPlugin(Component): implements(INavigationContributor, IRequestHandler, ITemplateProvider) show_weekly_view = BoolOption('ganttcalendar', 'show_weekly_view', 'false', """Set weekly view as default in calendar. (default: false)""") # INavigationContributor methods def get_active_navigation_item(self, req): return 'ticketcalendar' def get_navigation_items(self, req): if req.perm.has_permission('TICKET_VIEW'): yield ('mainnav', 'ticketcalendar',tag.a(_('Calendar'), href=req.href.ticketcalendar())) # IRequestHandler methods def match_request(self, req): return re.match(r'/ticketcalendar(?:_trac)?(?:/.*)?$', req.path_info) def calendarRange(self, y, m, wk): calendar.setfirstweekday(wk) li = calendar.monthcalendar(y,m) days = li[0].count(0) firstDay = date(y,m,1) - timedelta(days) days = li[-1].count(0) lastDay = date(y,m,max(li[-1])) + timedelta(days) return firstDay, lastDay def process_request(self, req): req.perm.assert_permission('TICKET_VIEW') req.perm.require('TICKET_VIEW') self.log.debug("process_request " + str(globals().get('__file__'))) year = req.args.get('year') month = req.args.get('month') day = req.args.get('day') or '1' weekly_view = int(req.args.get('weekly') or self.show_weekly_view) show_my_ticket = req.args.get('show_my_ticket') show_closed_ticket = req.args.get('show_closed_ticket') selected_milestone = req.args.get('selected_milestone') # first_day= 0: sunday (default) 1: monday 2: tuesday 3: wednesday 4: thursday 5: friday 6: saturday first_day = self.config['ganttcalendar'].getint('first_day', default=0) weekdays = [6, 0, 1, 2, 3, 4, 5] first_wkday = weekdays[first_day % 7] # first_wkday= 0: monday 1: tuesday 2: wednesday 3: thursday 4: friday 5: saturday 6: sunday (default) dateFormat = str(self.config['ganttcalendar'].get('format', default='%Y/%m/%d')) or '%Y/%m/%d' if year and month: cday = date(int(year),int(month),int(day)) else: cday = date.today() show_closed_ticket = 'on' first, last = self.calendarRange(cday.year, cday.month, first_wkday) if weekly_view: first = first + timedelta(weeks=(cday-first).days/7) last = first + timedelta(days=6) prev = first - timedelta(weeks=1) next = first + timedelta(weeks=1) else: prev = cday.replace(day=1).__add__(timedelta(days=-1)).replace(day=1) next = cday.replace(day=1).__add__(timedelta(days=32)).replace(day=1) # process ticket db = self.env.get_db_cnx() cursor = db.cursor(); my_ticket_sql = "" self.log.debug("myticket") self.log.debug(show_my_ticket) if show_my_ticket=="on": my_ticket_sql = "AND owner = '" + req.authname + "'" closed_ticket_sql = "" if show_closed_ticket != 'on': closed_ticket_sql = "AND status <> 'closed'" selected_milestone_sql = "" if selected_milestone != None and selected_milestone != "": selected_milestone_sql = "AND milestone = '" + selected_milestone + "'" sql = ("SELECT id, type, summary, owner, description, status, resolution, priority, a.value, c.value, cmp.value, est.value, tot.value from ticket t " "JOIN ticket_custom a ON a.ticket = t.id AND a.name = 'due_assign' " "JOIN ticket_custom c ON c.ticket = t.id AND c.name = 'due_close' " "JOIN ticket_custom cmp ON cmp.ticket = t.id AND cmp.name = 'complete' " "LEFT OUTER JOIN ticket_custom est ON est.ticket = t.id AND est.name = 'estimatedhours' " "LEFT OUTER JOIN ticket_custom tot ON tot.ticket = t.id AND tot.name = 'totalhours' " "WHERE ((a.value >= '%s' AND a.value <= '%s' ) " "OR (c.value >= '%s' AND c.value <= '%s')) %s %s %s" % (first.strftime(dateFormat), last.strftime(dateFormat), first.strftime(dateFormat), last.strftime(dateFormat), my_ticket_sql, closed_ticket_sql, selected_milestone_sql)) self.log.debug(sql) cursor.execute(sql) sum_estimatedhours = 0.0 sum_totalhours = 0.0 sum_est_isNone = True tickets=[] for id, type, summary, owner, description, status, resolution, priority, due_assign, due_close, complete, estimatedhours, totalhours in cursor: due_assign_date = None due_close_date = None try: t = time.strptime(due_assign, dateFormat) due_assign_date = date(t[0],t[1],t[2]) except ( TracError, ValueError, TypeError): pass try: t = time.strptime(due_close, dateFormat) due_close_date = date(t[0],t[1],t[2]) except ( TracError, ValueError, TypeError): pass if complete != None and len(complete)>1 and complete[len(complete)-1]=='%': complete = complete[0:len(complete)-1] try: if int(complete) >100: complete = "100" except: complete = "0" complete = int(complete) if (due_assign_date and due_close_date) \ and (due_assign_date > due_close_date): continue # time tracking if estimatedhours != None: estimatedhours = _to_float(estimatedhours, default=0.0) sum_estimatedhours += estimatedhours sum_est_isNone = False if totalhours != None: totalhours = _to_float(totalhours, default=0.0) sum_totalhours += totalhours else: totalhours = 0.0 ticket = {'id':id, 'type':type, 'summary':summary, 'owner':owner, 'description': description, 'status':status, 'resolution':resolution, 'priority':priority, 'due_assign':due_assign_date, 'due_close':due_close_date, 'complete': complete, 'estimatedhours':estimatedhours, 'totalhours':totalhours} tickets.append(ticket) # time tracking if sum_est_isNone: sum_estimatedhours = None # get roadmap sql = ("SELECT name, due, completed, description FROM milestone") self.log.debug(sql) cursor.execute(sql) milestones = [{}] for name, due, completed, description in cursor: milestone = {'name':name, 'completed':completed != 0,'description':description} if due!=0: milestone['due']= to_datetime(due, req.tz).date() milestones.append(milestone) holidays = {} sql = "SELECT date,description from holiday" try: cursor.execute(sql) for hol_date,hol_desc in cursor: holidays[hol_date]= hol_desc except: pass #days days={} for d in range((last-first).days+1): mday= first + timedelta(d) mday_str= mday.isoformat() days[mday]={} #day kind days[mday]['kind']= 'active' if mday_str in holidays.keys(): days[mday]['holiday_desc']= holidays[mday_str] days[mday]['kind']= 'holiday' if mday == date.today(): days[mday]['kind']= 'today' elif mday.weekday() in (5,6): days[mday]['kind']= 'holiday' #ticket days[mday]['ticket']=[] for t in range(len(tickets)): if mday == tickets[t].get('due_assign') == tickets[t].get('due_close'): days[mday]['ticket'].append({'img':'bw','num':t}) elif mday == tickets[t].get('due_assign'): days[mday]['ticket'].append({'img':'from','num':t}) elif mday == tickets[t].get('due_close'): days[mday]['ticket'].append({'img':'to','num':t}) #milestone days[mday]['milestone']=[] for m in range(len(milestones)): if mday== milestones[m].get('due'): days[mday]['milestone'].append(m) data = {'current':cday, 'prev':prev, 'next':next, 'weekly':weekly_view, 'first':first, 'last':last, 'tickets':tickets, 'milestones':milestones,'days':days, 'sum_estimatedhours':sum_estimatedhours, 'sum_totalhours':sum_totalhours, 'show_my_ticket': show_my_ticket, 'show_closed_ticket': show_closed_ticket, 'selected_milestone': selected_milestone, '_':_,'dateFormat':dateFormat, 'holidays':holidays, 'month_tbl': month_tbl} return 'calendar.html', data, None def get_templates_dirs(self): from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): from pkg_resources import resource_filename return [('tc', resource_filename(__name__, 'htdocs'))]