def _wiki_link(self, req, args, kwargs, wiki, label, a_class, check=None): """Build links to wiki pages.""" check_sign = None url = self.env.href.wiki(wiki) if WikiSystem(self.env).has_page(wiki.lstrip("/")): a_class += " page" title = _("Go to page %s") % wiki if check and check[0] == "link": chrome_path = "/".join([req.base_path, "chrome"]) ok_img = "wikicalendar/check_ok.png" ok = tag.image(src="/".join([chrome_path, ok_img]), alt="ok", title="ok") nok_img = "wikicalendar/check_nok.png" nok = tag.image(src="/".join([chrome_path, nok_img]), alt="X", title="X") unk_img = "wikicalendar/check_unknown.png" unk = tag.image(src="/".join([chrome_path, unk_img]), alt="?", title="?") result = self._do_check(check[1], wiki) check_sign = result and (result == 1 and ok or nok) or unk else: # The default (empty page) is used, if template name is invalid. url += "?action=edit" # Add page template to create new wiki pages, if specified. template = None if len(args) >= 6 or kwargs.has_key("base"): try: template = kwargs["base"] except KeyError: template = args[5] if template: url += "&template=" + template title = _("Create page %s") % wiki link = tag.a(tag(label), href=url) link(class_=a_class, title_=title) return tag(link, check_sign)
def _gen_wiki_links(self, wiki, label, a_class, url, wiki_page_template, check=None): check_sign = None if WikiSystem(self.env).has_page(wiki.lstrip('/')): a_class += " page" title = _("Go to page %s") % wiki if check and check[0] == 'link': chrome_path = '/'.join([self.ref.req.base_path, 'chrome']) ok_img = 'wikicalendar/check_ok.png' ok = tag.image(src='/'.join([chrome_path, ok_img]), alt='ok', title='ok') nok_img = 'wikicalendar/check_nok.png' nok = tag.image(src='/'.join([chrome_path, nok_img]), alt='X', title='X') unk_img = 'wikicalendar/check_unknown.png' unk = tag.image(src='/'.join([chrome_path, unk_img]), alt='?', title='?') result = self._do_check(check[1], wiki) check_sign = result and (result == 1 and ok or nok) or unk else: url += "?action=edit" # adding template name, if specified if wiki_page_template != "": url += "&template=" + wiki_page_template title = _("Create page %s") % wiki link = tag.a(tag(label), href=url) link(class_=a_class, title_=title) return tag(link, check_sign)
def _wiki_link(self, req, args, kwargs, wiki, label, a_class, check=None): """Build links to wiki pages.""" check_sign = None url = self.env.href.wiki(wiki) if WikiSystem(self.env).has_page(wiki.lstrip('/')): a_class += " page" title = _("Go to page %s") % wiki if check and check[0] == 'link': chrome_path = '/'.join([req.base_path, 'chrome']) ok_img = 'wikicalendar/check_ok.png' ok = tag.image(src='/'.join([chrome_path, ok_img]), alt='ok', title='ok') nok_img = 'wikicalendar/check_nok.png' nok = tag.image(src='/'.join([chrome_path, nok_img]), alt='X', title='X') unk_img = 'wikicalendar/check_unknown.png' unk = tag.image(src='/'.join([chrome_path, unk_img]), alt='?', title='?') result = self._do_check(check[1], wiki) check_sign = result and (result == 1 and ok or nok) or unk else: # The default (empty page) is used, if template name is invalid. url += "?action=edit" # Add page template to create new wiki pages, if specified. template = None if len(args) >= 6 or kwargs.has_key('base'): try: template = kwargs['base'] except KeyError: template = args[5] if template: url += "&template=" + template title = _("Create page %s") % wiki link = tag.a(tag(label), href=url) link(class_=a_class, title_=title) return tag(link, check_sign)
def expand_macro(self, formatter, name, args): """Returns an image that will be displayed in the Wiki content. `name` is the actual name of the macro, `args` is the text enclosed in parenthesis at the call of the macro. """ params = {} args_list, args_dict = parse_args(args) img_url = args_list[0] params['height'] = args_dict.get('height', '') params['width'] = args_dict.get('width', '') params['align'] = args_dict.get('align', 'left') image = tag.image(src=img_url, width=params['width'], eight=params['height'], align=params['align']) link = tag.a(image, href=img_url) return tag.p(link, style="text-align:%s;" % params['align'])
def expand_macro(self, formatter, name, content): # prepare options req = formatter.req options, query_args = parse_options(self.env.get_db_cnx(), content, copy.copy(DEFAULT_OPTIONS)) if not options['startdate']: raise TracError("No start date specified!") # minimum time frame is one day if (options['startdate'] >= options['enddate']): options['enddate'] = options['startdate'] + timedelta(days=1) # calculate data timetable = self._calculate_timetable(options, query_args, req) # remove weekends if not options['weekends']: for date in timetable.keys(): if date.weekday() >= 5: del timetable[date] # scale data xdata, ydata, maxhours = self._scale_data(timetable, options) # build html for google chart api dates = sorted(timetable.keys()) bottomaxis = "0:|" + ("|").join([str(date.day) for date in dates]) + \ "|1:|%s/%s|%s/%s" % (dates[0].month, dates[0].year, dates[ - 1].month, dates[ - 1].year) leftaxis = "2,0,%s" % maxhours # add line for expected progress if options['expected'] == '0': expecteddata = "" else: expecteddata = "|0,100|%s,0" % (round( Decimal(options['expected']) * 100 / maxhours, 2)) # prepare gridlines if options['gridlines'] == '0': gridlinesdata = "100.0,100.0,1,0" # create top and right bounding line by using grid else: gridlinesdata = "%s,%s" % (xdata[1], (round( Decimal(options['gridlines']) * 100 / maxhours, 4))) # mark weekends weekends = [] saturday = None index = 0 halfday = self._round(Decimal("0.5") / (len(dates) - 1)) for date in dates: if date.weekday() == 5: saturday = index if saturday and date.weekday() == 6: weekends.append( "R,%s,0,%s,%s" % (options['wecolor'], self._round((Decimal(xdata[saturday]) / 100) - halfday), self._round((Decimal(xdata[index]) / 100) + halfday))) saturday = None index += 1 # special handling if time period starts with Sundays... if len(dates) > 0 and dates[0].weekday() == 6: weekends.append("R,%s,0,0.0,%s" % (options['wecolor'], halfday)) # or ends with Saturday if len(dates) > 0 and dates[-1].weekday() == 5: weekends.append("R,%s,0,%s,1.0" % (options['wecolor'], Decimal(1) - halfday)) # chart title title = options.get('title', None) if title is None and options.get('milestone'): title = options['milestone'].split('|')[0] chart_args = unicode_urlencode({ 'chs': '%sx%s' % (options['width'], options['height']), 'chf': 'c,s,%s|bg,s,00000000' % options['bgcolor'], 'chd': 't:%s|%s%s' % (",".join(xdata), ",".join(ydata), expecteddata), 'cht': 'lxy', 'chxt': 'x,x,y', 'chxl': bottomaxis, 'chxr': leftaxis, 'chm': "|".join(weekends), 'chg': gridlinesdata, 'chco': '%s,%s' % (options['color'], options['colorexpected']), 'chtt': title }) self.log.debug("BurndownChart data: %s" % repr(chart_args)) if self.serverside_charts: return tag.image( src="%s?data=%s" % (req.href.estimationtools('chart'), unicode_quote(chart_args)), alt="Burndown Chart (server)") else: return tag.image(src="http://chart.googleapis.com/chart?%s" % chart_args, alt="Burndown Chart (client)")
def expand_macro(self, formatter, name, content): req = formatter.req db = self.env.get_db_cnx() # prepare options options, query_args = parse_options(db, content, copy.copy(DEFAULT_OPTIONS)) query_args[self.estimation_field + "!"] = None tickets = execute_query(self.env, req, query_args) sum = 0.0 estimations = {} for ticket in tickets: if ticket['status'] in self.closed_states: continue try: estimation = float(ticket[self.estimation_field]) owner = ticket['owner'] sum += estimation if estimations.has_key(owner): estimations[owner] += estimation else: estimations[owner] = estimation except: pass estimations_string = [] labels = [] for owner, estimation in estimations.iteritems(): # Note: Unconditional obfuscation of owner in case it represents # an email adress, and as the chart API doesn't support SSL # (plain http transfer only, from either client or server). labels.append("%s %g%s" % (obfuscate_email_address(owner), round(estimation, 2), self.estimation_suffix)) estimations_string.append(str(int(estimation))) # Title title = 'Workload' # calculate remaining work time if options.get('today') and options.get('enddate'): currentdate = options['today'] day = timedelta(days=1) days_remaining = 0 while currentdate <= options['enddate']: if currentdate.weekday() < 5: days_remaining += 1 currentdate += day title += ' %g%s (~%s workdays left)' % (round(sum, 2), self.estimation_suffix, days_remaining) chart_args = unicode_urlencode({'chs': '%sx%s' % (options['width'], options['height']), 'chf': 'bg,s,00000000', 'chd': 't:%s' % ",".join(estimations_string), 'cht': 'p3', 'chtt': title, 'chl': "|".join(labels), 'chco': options['color']}) self.log.debug("WorkloadChart data: %s" % repr(chart_args)) if self.serverside_charts: return tag.image(src="%s?data=%s" % (req.href.estimationtools('chart'), unicode_quote(chart_args)), alt="Workload Chart (server)") else: return tag.image(src="http://chart.googleapis.com/chart?%s" % chart_args, alt="Workload Chart (client)")
def expand_macro(self, formatter, name, content): # prepare options req = formatter.req options, query_args = parse_options(self.env.get_db_cnx(), content, copy.copy(DEFAULT_OPTIONS)) if not options['startdate']: raise TracError("No start date specified!") # minimum time frame is one day if (options['startdate'] >= options['enddate']): options['enddate'] = options['startdate'] + timedelta(days=1) # calculate data timetable = self._calculate_timetable(options, query_args, req) # remove weekends if not options['weekends']: for date in timetable.keys(): if date.weekday() >= 5: del timetable[date] # scale data xdata, ydata, maxhours = self._scale_data(timetable, options) # build html for google chart api dates = sorted(timetable.keys()) bottomaxis = "0:|" + ("|").join([str(date.day) for date in dates]) + \ "|1:|%s/%s|%s/%s" % (dates[0].month, dates[0].year, dates[ - 1].month, dates[ - 1].year) leftaxis = "2,0,%s" % maxhours # add line for expected progress if options['expected'] == '0': expecteddata = "" else: expecteddata = "|0,100|%s,0" % (round(Decimal(options['expected']) * 100 / maxhours, 2)) # prepare gridlines if options['gridlines'] == '0': gridlinesdata = "100.0,100.0,1,0" # create top and right bounding line by using grid else: gridlinesdata = "%s,%s" % (xdata[1], (round(Decimal(options['gridlines']) * 100 / maxhours, 4))) # mark weekends weekends = [] saturday = None index = 0 halfday = self._round(Decimal("0.5") / (len(dates) - 1)) for date in dates: if date.weekday() == 5: saturday = index if saturday and date.weekday() == 6: weekends.append("R,%s,0,%s,%s" % (options['wecolor'], self._round((Decimal(xdata[saturday]) / 100) - halfday), self._round((Decimal(xdata[index]) / 100) + halfday))) saturday = None index += 1 # special handling if time period starts with Sundays... if len(dates) > 0 and dates[0].weekday() == 6: weekends.append("R,%s,0,0.0,%s" % (options['wecolor'], halfday)) # or ends with Saturday if len(dates) > 0 and dates[ - 1].weekday() == 5: weekends.append("R,%s,0,%s,1.0" % (options['wecolor'], Decimal(1) - halfday)) # chart title title = options.get('title', None) if title is None and options.get('milestone'): title = options['milestone'].split('|')[0] chart_args = unicode_urlencode( {'chs': '%sx%s' % (options['width'], options['height']), 'chf': 'c,s,%s|bg,s,00000000' % options['bgcolor'], 'chd': 't:%s|%s%s' % (",".join(xdata), ",".join(ydata), expecteddata), 'cht': 'lxy', 'chxt': 'x,x,y', 'chxl': bottomaxis, 'chxr': leftaxis, 'chm': "|".join(weekends), 'chg': gridlinesdata, 'chco': '%s,%s' % (options['color'], options['colorexpected']), 'chtt': title}) self.log.debug("BurndownChart data: %s" % repr(chart_args)) if self.serverside_charts: return tag.image(src="%s?data=%s" % (req.href.estimationtools('chart'), unicode_quote(chart_args)), alt="Burndown Chart (server)") else: return tag.image(src="http://chart.googleapis.com/chart?%s" % chart_args, alt="Burndown Chart (client)")
def expand_macro(self, formatter, name, content): req = formatter.req db = self.env.get_db_cnx() # prepare options options, query_args = parse_options(db, content, copy.copy(DEFAULT_OPTIONS)) query_args[self.estimation_field + "!"] = None tickets = execute_query(self.env, req, query_args) sum = 0.0 estimations = {} for ticket in tickets: if ticket['status'] in self.closed_states: continue try: estimation = float(ticket[self.estimation_field] or 0.0) if options.get('remainingworkload'): completion_cursor = db.cursor() completion_cursor.execute( "SELECT t.value AS totalhours, c.value AS complete, d.value AS due_close FROM ticket tk LEFT JOIN ticket_custom t ON (tk.id = t.ticket AND t.name = 'totalhours') LEFT JOIN ticket_custom c ON (tk.id = c.ticket AND c.name = 'complete') LEFT JOIN ticket_custom d ON (tk.id = d.ticket AND d.name = 'due_close') WHERE tk.id = %s" % ticket['id']) for row in completion_cursor: ticket['totalhours'], ticket['complete'], ticket[ 'due_close'] = row break # skip ticket ticket if due date is later than 'enddate': if options.get('showdueonly'): if not ticket['due_close']: continue # skip tickets with empty ETA when in 'showdueonly' mode due_close = parse_date(ticket['due_close'], ["%Y/%m/%d"]) startdate = options.get('startdate') enddate = options.get('enddate') if startdate and startdate > due_close: continue # skip tickets with ETA in the past if enddate and enddate < due_close: continue # skip tickets with ETA in the future pass totalhours = float(ticket['totalhours'] or 0.0) completed = (float(ticket['complete'] or 0.0) / 100) * estimation completed_hours = min(estimation, max(totalhours, completed)) estimation -= completed_hours pass owner = ticket['owner'] sum += estimation if estimations.has_key(owner): estimations[owner] += estimation else: estimations[owner] = estimation except: raise # Title title = 'Workload' days_remaining = None # calculate remaining work time if options.get('today') and options.get('enddate'): currentdate = options['today'] day = timedelta(days=1) days_remaining = 0 while currentdate <= options['enddate']: if currentdate.weekday() < 5: days_remaining += 1 currentdate += day title += ' %g%s (~%s workdays left)' % (round( sum, 2), self.estimation_suffix, days_remaining) estimations_string = [] labels = [] workhoursperday = max(float(options.get('workhoursperday')), 0.0) chts = '000000' for owner, estimation in estimations.iteritems(): # Note: Unconditional obfuscation of owner in case it represents # an email adress, and as the chart API doesn't support SSL # (plain http transfer only, from either client or server). label = "%s %g%s" % (obfuscate_email_address(owner), round(estimation, 2), self.estimation_suffix) if days_remaining != None: user_remaining_hours = days_remaining * workhoursperday if not user_remaining_hours or (estimation / user_remaining_hours) > 1: label = "%s (~%g hours left)!" % ( label, round(user_remaining_hours, 2) ) # user does not have enough hours left chts = 'FF0000' # set chart title style to red pass pass labels.append(label) estimations_string.append(str(int(estimation))) pass chart_args = unicode_urlencode({ 'chs': '%sx%s' % (options['width'], options['height']), 'chf': 'bg,s,00000000', 'chd': 't:%s' % ",".join(estimations_string), 'cht': 'p3', 'chtt': title, 'chts': chts, 'chl': "|".join(labels), 'chco': options['color'] }) self.log.debug("WorkloadChart data: %s" % repr(chart_args)) if self.serverside_charts: return tag.image( src="%s?data=%s" % (req.href.estimationtools('chart'), unicode_quote(chart_args)), alt="Workload Chart (server)") else: return tag.image(src="https://chart.googleapis.com/chart?%s" % chart_args, alt="Workload Chart (client)")
def expand_macro(self, formatter, name, content): req = formatter.req db = self.env.get_db_cnx() # prepare options options, query_args = parse_options(db, content, copy.copy(DEFAULT_OPTIONS)) query_args[self.estimation_field + "!"] = None tickets = execute_query(self.env, req, query_args) sum = 0.0 estimations = {} for ticket in tickets: if ticket['status'] in self.closed_states: continue try: estimation = float(ticket[self.estimation_field]) owner = ticket['owner'] sum += estimation if estimations.has_key(owner): estimations[owner] += estimation else: estimations[owner] = estimation except: pass estimations_string = [] labels = [] for owner, estimation in estimations.iteritems(): # Note: Unconditional obfuscation of owner in case it represents # an email adress, and as the chart API doesn't support SSL # (plain http transfer only, from either client or server). labels.append("%s %g%s" % (obfuscate_email_address(owner), round( estimation, 2), self.estimation_suffix)) estimations_string.append(str(int(estimation))) # Title title = 'Workload' # calculate remaining work time if options.get('today') and options.get('enddate'): currentdate = options['today'] day = timedelta(days=1) days_remaining = 0 while currentdate <= options['enddate']: if currentdate.weekday() < 5: days_remaining += 1 currentdate += day title += ' %g%s (~%s workdays left)' % (round( sum, 2), self.estimation_suffix, days_remaining) chart_args = unicode_urlencode({ 'chs': '%sx%s' % (options['width'], options['height']), 'chf': 'bg,s,00000000', 'chd': 't:%s' % ",".join(estimations_string), 'cht': 'p3', 'chtt': title, 'chl': "|".join(labels), 'chco': options['color'] }) self.log.debug("WorkloadChart data: %s" % repr(chart_args)) if self.serverside_charts: return tag.image( src="%s?data=%s" % (req.href.estimationtools('chart'), unicode_quote(chart_args)), alt="Workload Chart (server)") else: return tag.image(src="http://chart.googleapis.com/chart?%s" % chart_args, alt="Workload Chart (client)")