def setUp(self): self.env = EnvironmentStub(default_data=True) self.req = Mock(base_path='', chrome={}, args={}, session={}, abs_href=Href('/'), href=Href('/'), locale='', perm=MockPerm(), authname=None, tz=None) self.report_module = ReportModule(self.env)
def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env)
def setUp(self): self.env = EnvironmentStub(default_data=True) self.report_module = ReportModule(self.env)
def test_invalid_post_request_raises_exception(self): req = MockRequest(self.env, method='POST', action=None) self.assertRaises(HTTPBadRequest, ReportModule(self.env).process_request, req)
def test_asc_argument_is_invalid(self): """Invalid value for `asc` argument is coerced to default.""" req = MockRequest(self.env, args={'asc': '--'}) self.assertRaises(HTTPBadRequest, ReportModule(self.env).process_request, req)
def run0(req, env, db, content): args = parse(content or '') items = [] summary = None ticket = default_ticket_field summaries = {} inline = False nosort = False inline_total = False title = "Tickets" styles = default_styles.copy() args_re = re.compile("^(?:" + string.join(args_pat, "|") + ")$") # process options first for arg in args: match = args_re.match(arg) if not match: env.log.debug('TicketBox: unknown arg: %s' % arg) continue elif match.group('title'): title = match.group('title')[1:-1] elif match.group('width'): styles['width'] = match.group('width') elif match.group('keyword'): kw = match.group('keyword').lower() kwarg = unquote(match.group('kwarg') or '') if kw == 'summary': summary = kwarg or default_summary_field elif kw == 'ticket': ticket = kwarg or default_ticket_field elif kw == 'inline': inline = True elif kw == 'nosort': nosort = True elif kw == 'nowrap': styles['white-space'] = 'nowrap' elif kw == 'inline_total': inline_total = True elif kw in styles and kwarg: styles[kw] = kwarg # pick up ticket numbers and report numbers for arg in args: sql = None params = [] match = args_re.match(arg) id_name = ticket sidx = iidx = -1 if not match: continue elif match.group('tktnum'): items.append(int(match.group('tktnum'))) elif match.group('query'): if not has_query: raise Exception('You cannot use trac query for this version of trac') q = Query.from_string(env, match.group('query')) sql, params = call(q.get_sql, locals()) id_name = 'id' elif match.group('rptnum') or match.group('rptnum2'): num = match.group('rptnum') or match.group('rptnum2') #env.log.debug('dynamic variables = %s' % dv) curs = db.cursor() try: curs.execute('SELECT %s FROM report WHERE id=%s' % (report_query_field, num)) rows = curs.fetchall() if len(rows) == 0: raise Exception("No such report: %s" % num) sql = rows[0][0] finally: curs.close() if sql: sql = sql.strip() if has_query and sql.lower().startswith("query:"): if sql.lower().startswith('query:?'): raise Exception('URL style of query string is not supported.') q = Query.from_string(env, sql[6:]) sql, params = call(q.get_sql, locals()) id_name = 'id' if sql: if not params: # handle dynamic variables # NOTE: sql_sub_vars() takes different arguments in # several trac versions. # For 0.10 or before, arguments are (req, query, args) # For 0.10.x, arguments are (req, query, args, db) # For 0.11 or later, arguments are (query, args, db) dv = ReportModule(env).get_var_args(req) sql, params = call(ReportModule(env).sql_sub_vars, locals()) try: #env.log.debug('sql = %s' % sql) curs = db.cursor() curs.execute(sql, params) rows = curs.fetchall() if rows: descriptions = [desc[0] for desc in curs.description] try: iidx = descriptions.index(id_name) except: raise Exception('No such column for ticket number: %r' % id_name ) if summary: try: sidx = descriptions.index(summary) except: raise Exception('No such column for summary: %r' % summary) for row in rows: items.append(row[iidx]) if summary and 0 <= sidx: summaries[row[iidx]] = row[sidx] finally: curs.close() if summary: # get summary text for id in items: if summaries.get(id): continue tkt = Ticket(env, tkt_id=id) if not tkt: continue summaries[id] = tkt['summary'] items = uniq(items) if not nosort: items.sort() html = '' if inline_total: # return simple text of total count to be placed inline. return '<span class="ticketbox"><span id="total">%s</span></span>' \ % len(items) if ver < [0, 11]: fargs = dict(db=db) else: fargs = dict(db=db, req=req) if summary: format = '%(summary)s (%(id)s)' sep = '<br/>' else: format = '%(id)s' sep = ', ' lines = [] for n in items: kwds = dict(id="#%d" % n) if summary: kwds['summary'] = summaries[n] lines.append(wiki_to_oneliner(format % kwds, env, **fargs)) html = sep.join(lines) if html: try: title = title % len(items) # process %d in title except: pass if inline: for key in ['float', 'width']: del styles[key] style = ';'.join(["%s:%s" % (k,v) for k,v in styles.items() if v]) return '<fieldset class="ticketbox" style="%s"><legend>%s</legend>%s</fieldset>' % \ (style, title, html) else: return ''
def render_macro(self, req, name, txt): env = self.env hdf = req.hdf # get trac version ver = [int(x) for x in version.split(".")] ## default style values styles = { "float": "right", "background": "#f7f7f0", "width": "25%", } inline_styles = { "background": "#f7f7f0", } args_pat = [ r"#?(?P<tktnum>\d+)", r"{(?P<rptnum>\d+)}", r"\[report:(?P<rptnum2>\d+)(?P<dv>\?.*)?\]", r"(?P<width>\d+(pt|px|%))", r"(?P<jourshomme>jourshomme)", r"(?P<table_report>table_report)", r"(?P<ticket_report>ticket_report)", r"(?P<do_not_group_none>do_not_group_none)", r"(?P<summary>summary)", r"(?P<inline>inline)", r"(?P<title1>'.*')", r'(?P<title2>".*")' ] ##def execute(hdf, txt, env): if not txt: txt = '' items = [] long_items = {} show_summary = False inline = False title = "Tickets" table_report_option = False ticket_report_option = False do_not_group_none = True theader = '' args_re = re.compile("^(?:" + string.join(args_pat, "|") + ")$") for arg in [string.strip(s) for s in txt.split(',')]: match = args_re.match(arg) if not match: env.log.debug('TicketBox: unknown arg: %s' % arg) continue elif match.group('title1'): title = match.group('title1')[1:-1] elif match.group('title2'): title = match.group('title2')[1:-1] elif match.group('width'): styles['width'] = match.group('width') elif match.group('tktnum'): items.append(int(match.group('tktnum'))) elif match.group('summary'): show_summary = True elif match.group('table_report'): table_report_option = True elif match.group('ticket_report'): ticket_report_option = True elif match.group('do_not_group_none'): do_not_group_none = False elif match.group('inline'): inline = True for arg in [string.strip(s) for s in txt.split(',')]: match = args_re.match(arg) if match.group('rptnum') or match.group('rptnum2'): num = match.group('rptnum') or match.group('rptnum2') dv = {} # username, do not override if specified if not dv.has_key('USER'): dv['USER'] = hdf.getValue('trac.authname', 'anonymous') if match.group('dv'): for expr in string.split(match.group('dv')[1:], '&'): k, v = string.split(expr, '=') dv[k] = v #env.log.debug('dynamic variables = %s' % dv) db = env.get_db_cnx() curs = db.cursor() try: curs.execute('SELECT query FROM report WHERE id=%s' % num) (query, ) = curs.fetchone() # replace dynamic variables with sql_sub_vars() # NOTE: sql_sub_vars() takes different arguments in # several trac versions. # For 0.10 or before, arguments are (req, query, args) # For 0.10.x, arguments are (req, query, args, db) # For 0.11 or later, arguments are (query, args, db) if ver <= [0, 10]: args = (req, query, dv) # for 0.10 or before elif ver < [0, 11]: args = (req, query, dv, db) # for 0.10.x else: args = (query, dv, db) # for 0.11 or later query, dv = ReportModule(env).sql_sub_vars(*args) #env.log.debug('query = %s' % query) curs.execute(query, dv) rows = curs.fetchall() if rows: descriptions = [desc[0] for desc in curs.description] env.log.debug('TEST %s' % descriptions) for descriptions_item in descriptions: theader = theader + '<td> %s </td>' % descriptions_item if do_not_group_none == False: idx = descriptions.index('milestone') else: idx = descriptions.index('ticket') for row in rows: items.append(row[idx]) if table_report_option: table_field = '' for descriptions_item in descriptions: if descriptions_item == 'ticket': table_field = table_field + '<td> %s </td>' % wiki_to_oneliner( "#%s" % row[descriptions.index( descriptions_item)], env, env.get_db_cnx()) else: table_field = table_field + '<td> %s </td>' % row[ descriptions.index( descriptions_item)] long_items[row[idx]] = table_field elif ticket_report_option: table_field = '' for descriptions_item in descriptions: if descriptions_item == 'ticket': table_field = table_field + '<li>' + descriptions_item + ': %s' % wiki_to_oneliner( "#%s" % row[descriptions.index( descriptions_item)], env, env.get_db_cnx()) + '</li>' elif descriptions_item != 'description' and descriptions_item != 'summary': table_field = table_field + '<li>' + descriptions_item + ': %s, ' % row[ descriptions.index( descriptions_item)] + '</li>' long_items[row[idx]] = '<b>%s</b><br/>' % row[ descriptions.index( 'summary' )] + '<ul>' + table_field + ' </ul>' + wiki_to_html( row[descriptions.index( "description")] or '', env, req, env.get_db_cnx()) else: summ = descriptions.index('summary') long_items[row[idx]] = row[summ] finally: if not hasattr(env, 'get_cnx_pool'): # without db connection pool, we should close db. curs.close() db.close() y = [] for i in items: if not y.count(i): y.append(i) items = y items.sort() html = '' tfooter = '' if show_summary: html = string.join([ wiki_to_oneliner("%s (#%d)" % (v, k), env, env.get_db_cnx()) for k, v in long_items.iteritems() ], "<br>") elif table_report_option: for k, v in long_items.iteritems(): if "#%s" % k == "#None" and do_not_group_none and do_not_group_none: tfooter = "<tfoot>%s</tfoot>" % v else: html = html + "<tr>%s</tr>" % v elif ticket_report_option: for k, v in long_items.iteritems(): if "#%s" % k != "#None": html = html + "%s" % v else: html = wiki_to_oneliner( string.join(["#%s" % c for c in items], ", "), env, env.get_db_cnx()) if html != '': try: title = title % len(items) # process %d in title except: pass if inline: style = string.join([ "%s:%s" % (k, v) for k, v in inline_styles.items() if v <> "" ], "; ") else: style = string.join( ["%s:%s" % (k, v) for k, v in styles.items() if v <> ""], "; ") if table_report_option: return '<table border=1><caption>' + title + '</caption><thead>' + theader + '</thead><tbody>' + html + '</tbody>' + tfooter + '</table>' if ticket_report_option: return html else: return '<fieldset class="ticketbox" style="%s"><legend>%s</legend>%s</fieldset>' % \ (style, title, html) else: return ''
def get_report_data(self, req, id): db = self.env.get_db_cnx() _template, data, _content_type = ReportModule(self.env)._render_view( req, db, id) return data
def post_process_request(self, req, template, data, content_type): if req.path_info.startswith('/report'): data['main_reports'] = [int(id) for id in self.main_reports] data['available_reports'] = self.available_reports(req) if data and data.get('row_groups'): id = req.args.get('id') action = req.args.get('action', 'view') header_groups = data.get('header_groups') row_groups = data.get('row_groups') if id and action == 'view': req.session['last_used_report'] = id if header_groups and len(header_groups) > 0: all_col_names = [ col['col'] for header_group in header_groups for col in header_group ] data['column_classes'] = dict( [ (key, value.get('classname')) \ for key, value in self.fields_config().items() \ if key in all_col_names]) if self._are_all_mandatory_fields_found(all_col_names): id_col = all_col_names[0] args = ReportModule(self.env).get_var_args(req) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT query FROM report WHERE id=%s", (id, )) sql, = cursor.fetchone() sql, args = ReportModule(self.env).sql_sub_vars( sql, args, db) cursor.execute("SELECT DISTINCT __group__, __group_preset__, count("+id_col+") as q, count(*) as fq "+\ "FROM (%s) as group_config GROUP BY __group__, __group_preset__" % sql, args) exec_groups = [(group, preset, quantity, full_quantity) for group, preset, quantity, full_quantity in cursor] data['exec_groups'] = req.args[ 'exec_groups'] = exec_groups paginator = data['paginator'] range_start, range_end = paginator.span num_rows = 0 num_none_filtered = 0 for x, y, quantity, full_quantity in exec_groups: num_rows = num_rows + quantity num_none_filtered = num_none_filtered + full_quantity delta = full_quantity - quantity if range_start and delta and range_start > num_none_filtered: range_start = range_start - delta range_end = range_end - delta paginator.num_items = num_rows num_filtered_on_page = 0 for i, (value_for_group, row_group) in enumerate(row_groups): filtered_row_group = [ row for row in row_group if (('id' not in row) or row['id']) ] row_groups[i] = (value_for_group, filtered_row_group) range_end = range_end - (len(row_group) - len(filtered_row_group)) if range_end > paginator.num_items: range_end = paginator.num_items paginator.span = range_start, range_end return (template, data, content_type)