class ReportTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env) def test_sub_var_no_quotes(self): req = Mock(hdf=dict()) sql, args = self.report_module.sql_sub_vars(req, "$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], args) def test_sub_var_quotes(self): req = Mock(hdf=dict()) sql, args = self.report_module.sql_sub_vars(req, "'$VAR'", {'VAR': 'value'}) self.assertEqual("''||%s||''", sql) self.assertEqual(['value'], args) def test_sub_var_mysql(self): req = Mock(hdf=dict()) env = EnvironmentStub() env.db = MockMySQLConnection() sql, args = ReportModule(env).sql_sub_vars(req, "'$VAR'", {'VAR': 'value'}) self.assertEqual("concat('', %s, '')", sql) self.assertEqual(['value'], args)
def expand_macro(self, formatter, name, args): req = formatter.req chrome = Chrome(self.env) report = ReportModule(self.env) comma_splitter = re.compile(r'(?<!\\),') kwargs = {} for arg in comma_splitter.split(args): arg = arg.replace(r'\,', ',') m = re.match(r'\s*[^=]+=', arg) if m: kw = arg[:m.end() - 1].strip() value = arg[m.end():] if re.match(r'^\$[A-Z]*$', value): value = req.args.get(value[1:]) kwargs[kw] = value if value!= None else '' else: if re.match(r'^\$[A-Z]*$', arg): arg = req.args.get(arg[1:]) id = int(arg) req.args = kwargs req.args['page'] = '1' template, data, content_type = report._render_view(req, id) add_stylesheet(req, 'common/css/report.css') fullpath = '' if pkg_resources.resource_exists('wikireport', 'WikiReport.html'): fullpath = pkg_resources.resource_filename('wikireport', 'WikiReport.html') else: filepath = os.path.dirname(os.path.abspath( __file__ )) fullpath = os.path.join(filepath, 'WikiReport.html') return chrome.render_template(req, fullpath, data, None, fragment=True)
def pre_process_request(self, req, handler): rmodule = ReportModule(self.env) #report's match request. if it's gonna be true then we'll stick in our translator, #but only if there's a report id (i.e. it's actually a report page) if rmodule.match_request(req) and req.args.get('id', -1) != -1 and req.args.get('action', 'view') == 'view': href = '' params = rmodule.get_var_args(req) if params: href = '&' + unicode_urlencode(params) add_link(req, 'alternate', '?format=rss&changes=true' + href, _('Changes RSS Feed'), 'application/xhtml+xml', 'rss') return handler
def pre_process_request(self, req, handler): """Called after initial handler selection, and can be used to change the selected handler or redirect request. Always returns the request handler, even if unchanged. """ rmodule = ReportModule(self.env) #report's match request. if it's gonna be true then we'll stick in our translator, #but only if there's a report id (i.e. it's actually a report page) if rmodule.match_request(req) and 'id' in req.args and req.args.get('action', 'view') == 'view': add_link(req, 'alternate', '?format=rss&detailed=true' , _('Detailed RSS Feed'), 'application/xhtml+xml', 'rss') return handler
def test_report(self): mod = ExcelReportModule(self.env) req = MockRequest(self.env, path_info='/report/1', args={'id': '1', 'format': 'xls'}) report_mod = ReportModule(self.env) self.assertTrue(report_mod.match_request(req)) template, data, content_type = report_mod.process_request(req) self.assertEqual('report_view.html', template) try: mod.post_process_request(req, template, data, content_type) self.fail('not raising RequestDone') except RequestDone: content = req.response_sent.getvalue() self.assertEqual(self._magic_number, content[:8]) self.assertEqual(self._mimetype, req.headers_sent['Content-Type'])
class ReportTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env) def _make_environ(self, scheme='http', server_name='example.org', server_port=80, method='GET', script_name='/trac', **kwargs): environ = {'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''), 'REQUEST_METHOD': method, 'SERVER_NAME': server_name, 'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name} environ.update(kwargs) return environ def test_sub_var_no_quotes(self): sql, args = self.report_module.sql_sub_vars("$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], args) def test_sub_var_quotes(self): sql, args = self.report_module.sql_sub_vars("'$VAR'", {'VAR': 'value'}) self.assertEqual(self.env.get_db_cnx().concat("''", '%s', "''"), sql) self.assertEqual(['value'], args) # Probably not needed anymore def test_sub_var_mysql(self): env = EnvironmentStub() env.db = MockMySQLConnection() # ditto sql, args = ReportModule(env).sql_sub_vars("'$VAR'", {'VAR': 'value'}) self.assertEqual("concat('', %s, '')", sql) self.assertEqual(['value'], args) def test_csv_escape(self): buf = StringIO() def start_response(status, headers): return buf.write environ = self._make_environ() req = Request(environ, start_response) cols = ['TEST_COL'] rows = [('value, needs escaped',)] try: self.report_module._send_csv(req, cols, rows) except RequestDone: pass self.assertEqual('TEST_COL\r\n"value, needs escaped"\r\n', buf.getvalue())
def setUp(self): self.report_module = ReportModule(self.env) self.query_module = QueryModule(self.env) self.chrome_module = Chrome(self.env) self.perm_sys = PermissionSystem(self.env) if self.env.is_component_enabled(ReportModule): self.perm_sys.grant_permission('has_report_view', 'REPORT_VIEW') self.perm_sys.grant_permission('has_both', 'REPORT_VIEW') self.perm_sys.grant_permission('has_ticket_view', 'TICKET_VIEW') self.perm_sys.grant_permission('has_both', 'TICKET_VIEW') self.tickets_link = lambda href: '<a href="%s">View Tickets</a>' \ % href
def pre_process_request(self, req, handler): if handler is not ReportModule(self.env): return handler url = req.path_info user = req.authname find = url.rfind('/') report_id = url[find + 1:] try: report_id = int(report_id) except: return handler if self._has_permission(user, report_id): return handler else: raise TracError( 'You don\'t have the permission to access this report', 'No Permission')
def post_process_request(self, req, template, data, content_type): """Inject dashboard helpers in data. """ if data is not None: data['bhdb'] = DashboardChrome(self.env) if isinstance(req.perm.env, ProductEnvironment) \ and not 'resourcepath_template' in data \ and 'product_list' in data: data['resourcepath_template'] = 'bh_path_general.html' for item in req.chrome['nav'].get('mainnav', []): self.log.debug('%s' % (item, )) if item['name'] == 'tickets': item['label'] = tag.a(_(self.mainnav_label), href=req.href.dashboard()) if item['active'] and \ not ReportModule(self.env).match_request(req): add_ctxtnav(req, _('Reports'), href=req.href.report()) break return template, data, content_type
def get_navigation_items(self, req): # Don't allow this to be exposed if 'DO_PRIVATETICKETS_FILTER' in req.args.keys(): del req.args['DO_PRIVATETICKETS_FILTER'] # Various ways to allow access if not req.perm.has_permission('TICKET_VIEW') and \ (req.perm.has_permission('TICKET_VIEW_REPORTER') or \ req.perm.has_permission('TICKET_VIEW_OWNER') or \ req.perm.has_permission('TICKET_VIEW_CC') or \ req.perm.has_permission('TICKET_VIEW_REPORTER_GROUP') or \ req.perm.has_permission('TICKET_VIEW_OWNER_GROUP') or \ req.perm.has_permission('TICKET_VIEW_CC_GROUP')): if TicketModule(self.env).match_request(req): if PrivateTicketsSystem(self.env).check_ticket_access( req, req.args['id']): self._grant_view(req) elif AttachmentModule(self.env).match_request(req): if req.args['type'] == 'ticket' and PrivateTicketsSystem( self.env).check_ticket_access( req, req.args['path'].split('/')[0]): self._grant_view(req) elif QueryModule(self.env).match_request(req): req.args['DO_PRIVATETICKETS_FILTER'] = 'query' self._grant_view(req) # Further filtering in query.py elif SearchModule(self.env).match_request(req): if 'ticket' in req.args.keys(): req.args['pticket'] = req.args['ticket'] del req.args['ticket'] elif ReportModule(self.env).match_request(req): self._grant_view(req) # So they can see the query page link if req.args.get('id'): req.args['DO_PRIVATETICKETS_FILTER'] = 'report' # NOTE: Send this back here because the button would be hidden otherwise. <NPK t:1129> if not self.env.is_component_enabled( ReportModule) or not req.perm.has_permission( 'REPORT_VIEW'): return [('mainnav', 'tickets', html.A('View Tickets', href=req.href.query()))] return []
def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env)
class ReportTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env) def tearDown(self): self.env.reset_db() def _make_environ(self, scheme='http', server_name='example.org', server_port=80, method='GET', script_name='/trac', **kwargs): environ = {'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''), 'REQUEST_METHOD': method, 'SERVER_NAME': server_name, 'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name} environ.update(kwargs) return environ def test_sub_var_no_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_digits_underscore(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$_VAR, $VAR2, $2VAR", {'_VAR': 'value1', 'VAR2': 'value2'}) self.assertEqual("%s, %s, $2VAR", sql) self.assertEqual(['value1', 'value2'], values) self.assertEqual([], missing_args) def test_sub_var_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "'$VAR'", {'VAR': 'value'}) self.assertEqual(self.env.get_read_db().concat("''", '%s', "''"), sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_missing_args(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR, $PARAM, $MISSING", {'VAR': 'value'}) self.assertEqual("%s, %s, %s", sql) self.assertEqual(['value', '', ''], values) self.assertEqual(['PARAM', 'MISSING'], missing_args) def test_csv_escape(self): buf = StringIO() def start_response(status, headers): return buf.write environ = self._make_environ() req = Request(environ, start_response) cols = ['TEST_COL', 'TEST_ZERO'] rows = [('value, needs escaped', 0)] try: self.report_module._send_csv(req, cols, rows) except RequestDone: pass self.assertEqual('\xef\xbb\xbfTEST_COL,TEST_ZERO\r\n"value, needs escaped",0\r\n', buf.getvalue()) def test_saved_custom_query_redirect(self): query = u'query:?type=résumé' db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("INSERT INTO report (title,query,description) " "VALUES (%s,%s,%s)", ('redirect', query, '')) id = db.get_last_id(cursor, 'report') db.commit() headers_sent = {} def start_response(status, headers): headers_sent.update(dict(headers)) environ = self._make_environ() req = Request(environ, start_response) req.authname = 'anonymous' req.session = Mock(save=lambda: None) self.assertRaises(RequestDone, self.report_module._render_view, req, id) self.assertEqual('http://example.org/trac/query?' + \ 'type=r%C3%A9sum%C3%A9&report=' + str(id), headers_sent['Location'])
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 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)
class ReportModuleTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.report_module = ReportModule(self.env) def tearDown(self): self.env.reset_db() def _insert_report(self, title, query, description): with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( """ INSERT INTO report (title,query,description) VALUES (%s,%s,%s) """, (title, query, description)) return db.get_last_id(cursor, 'report') def test_create_report(self): req = MockRequest(self.env, method='POST', args={ 'action': 'new', 'title': "New Report", 'query': "SELECT 1", 'description': "The description" }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertTrue(Report(self.env, 9).exists) self.assertIn("The report has been created.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report/9', req.headers_sent['Location']) def test_create_report_cancel(self): req = MockRequest(self.env, method='POST', args={ 'action': 'new', 'cancel': True, 'title': "New Report", 'query': "SELECT 1", 'description': "The description" }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertRaises(ResourceNotFound, Report, self.env, 9) self.assertNotIn("The report has been created.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report', req.headers_sent['Location']) def test_delete_report(self): req = MockRequest(self.env, method='POST', args={ 'action': 'delete', 'id': '1' }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertRaises(ResourceNotFound, Report, self.env, 1) self.assertIn("The report {1} has been deleted.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report', req.headers_sent['Location']) def test_delete_report_cancel(self): req = MockRequest(self.env, method='POST', args={ 'action': 'delete', 'cancel': True, 'id': '1' }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertTrue(Report(self.env, 1).exists) self.assertNotIn("The report {1} has been deleted.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report/1', req.headers_sent['Location']) def test_update_report(self): report = Report(self.env) report.query = "SELECT 1" report.insert() req = MockRequest(self.env, method='POST', args={ 'action': 'edit', 'id': '1', 'title': "New Report edited", 'query': "SELECT 2", 'description': "The description edited" }) self.assertRaises(RequestDone, self.report_module.process_request, req) report = Report(self.env, 1) self.assertEqual("New Report edited", report.title) self.assertEqual("SELECT 2", report.query) self.assertEqual("The description edited", report.description) self.assertIn("Your changes have been saved.", req.chrome['notices']) def test_update_report_cancel(self): report = Report(self.env) report.query = "SELECT 1" report.insert() req = MockRequest(self.env, method='POST', args={ 'action': 'edit', 'cancel': True, 'id': '1', 'title': "New Report edited", 'query': "SELECT 2", 'description': "The description edited" }) self.assertRaises(RequestDone, self.report_module.process_request, req) report = Report(self.env, 1) self.assertEqual("Active Tickets", report.title) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", report.description) self.assertIn("SELECT p.value AS __color__", report.query) self.assertNotIn("Your changes have been saved.", req.chrome['notices']) def test_render_confirm_delete(self): req = MockRequest(self.env, method='GET', args={ 'action': 'delete', 'id': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual("Delete Report {1} Active Tickets", data['title']) self.assertEqual('delete', data['action']) self.assertEqual(1, data['report']['id']) self.assertEqual('Active Tickets', data['report']['title']) def test_render_editor(self): req = MockRequest(self.env, method='GET', args={ 'action': 'edit', 'id': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual(1, data['report']['id']) self.assertEqual("Active Tickets", data['report']['title']) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", data['report']['description']) self.assertIn("SELECT p.value AS __color__", data['report']['sql']) def test_render_list(self): req = MockRequest(self.env, method='GET', args={ 'action': 'view', 'id': '-1' }) data = self.report_module.process_request(req)[1] self.assertEqual('report', data['sort']) self.assertEqual(1, data['asc']) self.assertEqual(8, len(data['reports'])) self.assertEqual(1, data['reports'][0][0]) self.assertEqual('Active Tickets', data['reports'][0][1]) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", data['reports'][0][2]) self.assertTrue('Active Reports', data['reports'][0][3]) self.assertTrue('Active Reports', data['reports'][0][4]) def test_render_view(self): req = MockRequest(self.env, method='GET', args={ 'action': 'view', 'id': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual(1, data['report']['id']) self.assertEqual("{1} Active Tickets", data['title']) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", data['description']) def test_render_empty_report(self): """Empty report raises a TracError.""" rid = self._insert_report('Empty report', '', '') req = MockRequest(self.env, method='GET', args={ 'action': 'view', 'id': rid }) data = self.report_module.process_request(req)[1] self.assertEqual("Report failed: Report {%d} has no SQL query." % rid, data['message']) def test_render_view_sort_order_preserved_on_update(self): """Sort column and order are preserved on update.""" req = MockRequest(self.env, method='GET', args={ 'action': 'view', 'id': 1, 'sort': 'summary', 'asc': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual(1, data['asc']) self.assertEqual('summary', data['sort']) def test_sub_var_no_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_digits_underscore(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"$_VAR, $VAR2, $2VAR", { '_VAR': 'value1', 'VAR2': 'value2' }) self.assertEqual("%s, %s, $2VAR", sql) self.assertEqual(['value1', 'value2'], values) self.assertEqual([], missing_args) def test_sub_var_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"'$VAR'", {'VAR': 'value'}) with self.env.db_query as db: concatenated = db.concat("''", '%s', "''") self.assertEqual(concatenated, sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_missing_args(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"$VAR, $PARAM, $MISSING", {'VAR': 'value'}) self.assertEqual("%s, %s, %s", sql) self.assertEqual(['value', '', ''], values) self.assertEqual(['PARAM', 'MISSING'], missing_args) def test_csv_escape(self): req = MockRequest(self.env) cols = ['TEST_COL', 'TEST_ZERO'] rows = [('value, needs escaped', 0)] self.assertRaises(RequestDone, self.report_module._send_csv, req, cols, rows) self.assertEqual( '\xef\xbb\xbfTEST_COL,TEST_ZERO\r\n"' 'value, needs escaped",0\r\n', req.response_sent.getvalue()) def test_saved_custom_query_redirect(self): query = u'query:?type=résumé' rid = self._insert_report('redirect', query, '') req = MockRequest(self.env) self.assertRaises(RequestDone, self.report_module._render_view, req, rid) self.assertEqual( 'http://example.org/trac.cgi/query?' + 'type=r%C3%A9sum%C3%A9&report=' + str(rid), req.headers_sent['Location']) def test_quoted_id_with_var(self): req = MockRequest(self.env) name = """%s"`'%%%?""" with self.env.db_query as db: sql = u'SELECT 1 AS %s, $USER AS user' % db.quote(name) rv = self.report_module.execute_paginated_report( req, 1, sql, {'USER': '******'}) self.assertEqual(5, len(rv), repr(rv)) cols, results, num_items, missing_args, limit_offset = rv self.assertEqual([name, 'user'], cols) self.assertEqual([(1, 'joe')], results) self.assertEqual([], missing_args) self.assertIsNone(limit_offset) def test_value_error_from_get_var_args(self): req = MockRequest(self.env, path_info='/report/1') def get_var_args(req): raise ValueError('<error>') self.report_module.get_var_args = get_var_args self.assertTrue(self.report_module.match_request(req)) data = self.report_module.process_request(req)[1] self.assertEqual('Report failed: <error>', data['message']) def test_title_and_description_with_sub_vars(self): with self.env.db_transaction: id_ = self._insert_report( 'Tickets on $M for $USER', 'SELECT * FROM ticket WHERE milestone=$M AND owner=$USER', 'Show tickets on $M for $USER') for milestone in ('milestone1', 'milestone2'): insert_ticket(self.env, status='new', summary='Test 1', owner='joe', milestone=milestone) req = MockRequest(self.env, path_info='/report/%d' % id_, authname='joe', args={'M': 'milestone2'}) self.assertTrue(self.report_module.match_request(req)) data = self.report_module.process_request(req)[1] self.assertEqual('{%d} Tickets on milestone2 for joe' % id_, data['title']) self.assertEqual('Show tickets on milestone2 for joe', data['description']) self.assertIsNone(data['message']) self.assertEqual(1, data['numrows']) def test_title_and_description_with_sub_vars_in_sql(self): with self.env.db_transaction: id_ = self._insert_report( 'Tickets on $M for $USER', '-- M=milestone1\r\n' 'SELECT * FROM ticket WHERE milestone=$M AND owner=$USER\r\n', 'Show tickets on $M for $USER') for milestone in ('milestone1', 'milestone2'): insert_ticket(self.env, status='new', summary='Test 1', owner='joe', milestone=milestone) req = MockRequest(self.env, path_info='/report/%d' % id_, authname='joe') self.assertTrue(self.report_module.match_request(req)) data = self.report_module.process_request(req)[1] self.assertEqual('{%d} Tickets on milestone1 for joe' % id_, data['title']) self.assertEqual('Show tickets on milestone1 for joe', data['description']) self.assertIsNone(data['message']) self.assertEqual(1, data['numrows']) def test_clear(self): req = MockRequest(self.env, method='GET', path_info='/report', args={'action': 'clear'}) req.session['query_href'] = req.href.query() req.session['query_tickets'] = '42' self.assertTrue(self.report_module.match_request(req)) self.assertEqual('report_list.html', self.report_module.process_request(req)[0]) self.assertIn('query_href', req.session) self.assertIn('query_tickets', req.session) req = MockRequest(self.env, method='POST', path_info='/report', args={'action': 'clear'}) self.assertTrue(self.report_module.match_request(req)) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertNotIn('query_href', req.session) self.assertNotIn('query_tickets', req.session) def test_valid_html_for_report(self): req = MockRequest( self.env, method='POST', path_info='/report', args={ 'action': 'new', 'title': '#13046', 'query': "SELECT '#13046' AS foo_, 42 AS bar, 'blah' AS _baz_", 'description': '' }) self.assertTrue(self.report_module.match_request(req)) self.assertRaises(RequestDone, self.report_module.process_request, req) req = MockRequest(self.env, method='GET', path_info='/report/9') self.assertTrue(self.report_module.match_request(req)) rv = self.report_module.process_request(req) rendered = Chrome(self.env).render_template(req, rv[0], rv[1], { 'fragment': False, 'iterable': False }) self.assertRegexpMatches( rendered, r'<tr[^>]*>\s*' r'<td class="fullrow foo" colspan="100">' r'\s*#13046\s*<hr />\s*</td>\s*</tr>') self.assertRegexpMatches( rendered, r'<tr[^>]*>\s*' r'<td class="fullrow bar" colspan="100">' r'\s*42\s*<hr />\s*</td>\s*</tr>') self.assertRegexpMatches( rendered, r'<tr[^>]*>\s*' r'<td class="fullrow baz" colspan="100">' r'\s*blah\s*<hr />\s*</td>\s*</tr>') if genshi: XML(rendered) # validates as XML def test_timestamp_columns(self): req = MockRequest( self.env, method='POST', path_info='/report', args={ 'action': 'new', 'title': '#13134', 'query': "SELECT %d AS time, %d AS created, %d AS datetime" % ((1 * 86400 + 42) * 1000000, (2 * 86400 + 43) * 1000000, (3 * 86400 + 44) * 1000000), 'description': '' }) self.assertTrue(self.report_module.match_request(req)) self.assertRaises(RequestDone, self.report_module.process_request, req) req = MockRequest(self.env, method='GET', path_info='/report/9') self.assertTrue(self.report_module.match_request(req)) rv = self.report_module.process_request(req) rendered = Chrome(self.env).render_template(req, rv[0], rv[1], { 'fragment': False, 'iterable': False }) self.assertRegexpMatches( rendered, r'<td class="date">\s*(12:00:42 AM|00:00:42)\s*</td>') self.assertRegexpMatches( rendered, r'<td class="date">\s*(Jan 3, 1970|01/03/70)\s*</td>') self.assertRegexpMatches( rendered, r'<td class="date">\s*(Jan 4, 1970, 12:00:44 AM|' r'01/04/70 00:00:44)\s*</td>')
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)
class ReportTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env) def tearDown(self): self.env.reset_db() def _make_environ(self, scheme='http', server_name='example.org', server_port=80, method='GET', script_name='/trac', **kwargs): environ = {'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''), 'REQUEST_METHOD': method, 'SERVER_NAME': server_name, 'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name} environ.update(kwargs) return environ def test_sub_var_no_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_digits_underscore(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$_VAR, $VAR2, $2VAR", {'_VAR': 'value1', 'VAR2': 'value2'}) self.assertEqual("%s, %s, $2VAR", sql) self.assertEqual(['value1', 'value2'], values) self.assertEqual([], missing_args) def test_sub_var_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "'$VAR'", {'VAR': 'value'}) with self.env.db_query as db: concatenated = db.concat("''", '%s', "''") self.assertEqual(concatenated, sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_missing_args(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR, $PARAM, $MISSING", {'VAR': 'value'}) self.assertEqual("%s, %s, %s", sql) self.assertEqual(['value', '', ''], values) self.assertEqual(['PARAM', 'MISSING'], missing_args) def test_csv_escape(self): buf = StringIO() def start_response(status, headers): return buf.write environ = self._make_environ() req = Request(environ, start_response) cols = ['TEST_COL', 'TEST_ZERO'] rows = [('value, needs escaped', 0)] try: self.report_module._send_csv(req, cols, rows) except RequestDone: pass self.assertEqual('\xef\xbb\xbfTEST_COL,TEST_ZERO\r\n"value, needs escaped",0\r\n', buf.getvalue()) def test_saved_custom_query_redirect(self): query = u'query:?type=résumé' with self.env.db_transaction as db: cursor = db.cursor() cursor.execute("INSERT INTO report (title,query,description) " "VALUES (%s,%s,%s)", ('redirect', query, '')) id = db.get_last_id(cursor, 'report') headers_sent = {} def start_response(status, headers): headers_sent.update(dict(headers)) environ = self._make_environ() req = Request(environ, start_response) req.authname = 'anonymous' req.session = Mock(save=lambda: None) self.assertRaises(RequestDone, self.report_module._render_view, req, id) self.assertEqual('http://example.org/trac/query?' + \ 'type=r%C3%A9sum%C3%A9&report=' + str(id), headers_sent['Location']) def test_quoted_id_with_var(self): req = Mock(base_path='', chrome={}, args={}, session={}, abs_href=Href('/'), href=Href('/'), locale='', perm=MockPerm(), authname=None, tz=None) name = """%s"`'%%%?""" with self.env.db_query as db: sql = 'SELECT 1 AS %s, $USER AS user' % db.quote(name) rv = self.report_module.execute_paginated_report(req, 1, sql, {'USER': '******'}) self.assertEqual(5, len(rv), repr(rv)) cols, results, num_items, missing_args, limit_offset = rv self.assertEqual([name, 'user'], cols) self.assertEqual([(1, 'joe')], results) self.assertEqual([], missing_args) self.assertEqual(None, limit_offset)
class ReportTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env) def _make_environ(self, scheme='http', server_name='example.org', server_port=80, method='GET', script_name='/trac', **kwargs): environ = { 'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''), 'REQUEST_METHOD': method, 'SERVER_NAME': server_name, 'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name } environ.update(kwargs) return environ def test_sub_var_no_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_digits_underscore(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$_VAR, $VAR2, $2VAR", { '_VAR': 'value1', 'VAR2': 'value2' }) self.assertEqual("%s, %s, $2VAR", sql) self.assertEqual(['value1', 'value2'], values) self.assertEqual([], missing_args) def test_sub_var_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "'$VAR'", {'VAR': 'value'}) self.assertEqual(self.env.get_db_cnx().concat("''", '%s', "''"), sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) # Probably not needed anymore def test_sub_var_mysql(self): env = EnvironmentStub() env.db = MockMySQLConnection() # ditto sql, values, missing_args = ReportModule(env).sql_sub_vars( "'$VAR'", {'VAR': 'value'}) self.assertEqual("concat('', %s, '')", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_missing_args(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR, $PARAM, $MISSING", {'VAR': 'value'}) self.assertEqual("%s, %s, %s", sql) self.assertEqual(['value', '', ''], values) self.assertEqual(['PARAM', 'MISSING'], missing_args) def test_csv_escape(self): buf = StringIO() def start_response(status, headers): return buf.write environ = self._make_environ() req = Request(environ, start_response) cols = ['TEST_COL', 'TEST_ZERO'] rows = [('value, needs escaped', 0)] try: self.report_module._send_csv(req, cols, rows) except RequestDone: pass self.assertEqual('TEST_COL,TEST_ZERO\r\n"value, needs escaped",0\r\n', buf.getvalue())
class ReportTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.report_module = ReportModule(self.env) def tearDown(self): self.env.reset_db() def _make_environ(self, scheme='http', server_name='example.org', server_port=80, method='GET', script_name='/trac', **kwargs): environ = { 'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''), 'REQUEST_METHOD': method, 'SERVER_NAME': server_name, 'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name } environ.update(kwargs) return environ def test_sub_var_no_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_digits_underscore(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$_VAR, $VAR2, $2VAR", { '_VAR': 'value1', 'VAR2': 'value2' }) self.assertEqual("%s, %s, $2VAR", sql) self.assertEqual(['value1', 'value2'], values) self.assertEqual([], missing_args) def test_sub_var_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( "'$VAR'", {'VAR': 'value'}) with self.env.db_query as db: concatenated = db.concat("''", '%s', "''") self.assertEqual(concatenated, sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_missing_args(self): sql, values, missing_args = self.report_module.sql_sub_vars( "$VAR, $PARAM, $MISSING", {'VAR': 'value'}) self.assertEqual("%s, %s, %s", sql) self.assertEqual(['value', '', ''], values) self.assertEqual(['PARAM', 'MISSING'], missing_args) def test_csv_escape(self): buf = StringIO() def start_response(status, headers): return buf.write environ = self._make_environ() req = Request(environ, start_response) cols = ['TEST_COL', 'TEST_ZERO'] rows = [('value, needs escaped', 0)] try: self.report_module._send_csv(req, cols, rows) except RequestDone: pass self.assertEqual( '\xef\xbb\xbfTEST_COL,TEST_ZERO\r\n"value, needs escaped",0\r\n', buf.getvalue()) def test_saved_custom_query_redirect(self): query = u'query:?type=résumé' with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( "INSERT INTO report (title,query,description) " "VALUES (%s,%s,%s)", ('redirect', query, '')) id = db.get_last_id(cursor, 'report') headers_sent = {} def start_response(status, headers): headers_sent.update(dict(headers)) environ = self._make_environ() req = Request(environ, start_response) req.authname = 'anonymous' req.session = Mock(save=lambda: None) self.assertRaises(RequestDone, self.report_module._render_view, req, id) self.assertEqual('http://example.org/trac/query?' + \ 'type=r%C3%A9sum%C3%A9&report=' + str(id), headers_sent['Location']) def test_quoted_id_with_var(self): req = Mock(base_path='', chrome={}, args={}, session={}, abs_href=Href('/'), href=Href('/'), locale='', perm=MockPerm(), authname=None, tz=None) name = """%s"`'%%%?""" with self.env.db_query as db: sql = 'SELECT 1 AS %s, $USER AS user' % db.quote(name) rv = self.report_module.execute_paginated_report( req, 1, sql, {'USER': '******'}) self.assertEqual(5, len(rv), repr(rv)) cols, results, num_items, missing_args, limit_offset = rv self.assertEqual([name, 'user'], cols) self.assertEqual([(1, 'joe')], results) self.assertEqual([], missing_args) self.assertEqual(None, limit_offset)
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 setUp(self): self.env = EnvironmentStub(default_data=True) self.report_module = ReportModule(self.env)
class ReportModuleTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.report_module = ReportModule(self.env) def tearDown(self): self.env.reset_db() def test_create_report(self): req = MockRequest(self.env, method='POST', args={ 'action': 'new', 'title': "New Report", 'query': "SELECT 1", 'description': "The description" }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertTrue(Report(self.env, 9).exists) self.assertIn("The report has been created.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report/9', req.headers_sent['Location']) def test_create_report_cancel(self): req = MockRequest(self.env, method='POST', args={ 'action': 'new', 'cancel': True, 'title': "New Report", 'query': "SELECT 1", 'description': "The description" }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertRaises(ResourceNotFound, Report, self.env, 9) self.assertNotIn("The report has been created.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report', req.headers_sent['Location']) def test_delete_report(self): req = MockRequest(self.env, method='POST', args={ 'action': 'delete', 'id': '1' }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertRaises(ResourceNotFound, Report, self.env, 1) self.assertIn("The report {1} has been deleted.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report', req.headers_sent['Location']) def test_delete_report_cancel(self): req = MockRequest(self.env, method='POST', args={ 'action': 'delete', 'cancel': True, 'id': '1' }) self.assertRaises(RequestDone, self.report_module.process_request, req) self.assertTrue(Report(self.env, 1).exists) self.assertNotIn("The report {1} has been deleted.", req.chrome['notices']) self.assertEqual('http://example.org/trac.cgi/report/1', req.headers_sent['Location']) def test_update_report(self): Report(self.env).insert() req = MockRequest(self.env, method='POST', args={ 'action': 'edit', 'id': '1', 'title': "New Report edited", 'query': "SELECT 2", 'description': "The description edited" }) self.assertRaises(RequestDone, self.report_module.process_request, req) report = Report(self.env, 1) self.assertEqual("New Report edited", report.title) self.assertEqual("SELECT 2", report.query) self.assertEqual("The description edited", report.description) self.assertIn("Your changes have been saved.", req.chrome['notices']) def test_update_report_cancel(self): Report(self.env).insert() req = MockRequest(self.env, method='POST', args={ 'action': 'edit', 'cancel': True, 'id': '1', 'title': "New Report edited", 'query': "SELECT 2", 'description': "The description edited" }) self.assertRaises(RequestDone, self.report_module.process_request, req) report = Report(self.env, 1) self.assertEqual("Active Tickets", report.title) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", report.description) self.assertIn("SELECT p.value AS __color__", report.query) self.assertNotIn("Your changes have been saved.", req.chrome['notices']) def test_render_confirm_delete(self): req = MockRequest(self.env, method='GET', args={ 'action': 'delete', 'id': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual("Delete Report {1} Active Tickets", data['title']) self.assertEqual('delete', data['action']) self.assertEqual(1, data['report']['id']) self.assertEqual('Active Tickets', data['report']['title']) def test_render_editor(self): req = MockRequest(self.env, method='GET', args={ 'action': 'edit', 'id': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual(1, data['report']['id']) self.assertEqual("Active Tickets", data['report']['title']) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", data['report']['description']) self.assertIn("SELECT p.value AS __color__", data['report']['sql']) def test_render_list(self): req = MockRequest(self.env, method='GET', args={ 'action': 'view', 'id': '-1' }) data = self.report_module.process_request(req)[1] self.assertEqual('report', data['sort']) self.assertEqual(1, data['asc']) self.assertEqual(8, len(data['reports'])) self.assertEqual(1, data['reports'][0][0]) self.assertEqual('Active Tickets', data['reports'][0][1]) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", data['reports'][0][2]) self.assertTrue('Active Reports', data['reports'][0][3]) self.assertTrue('Active Reports', data['reports'][0][4]) def test_render_view(self): req = MockRequest(self.env, method='GET', args={ 'action': 'view', 'id': '1' }) data = self.report_module.process_request(req)[1] self.assertEqual(1, data['report']['id']) self.assertEqual("{1} Active Tickets", data['title']) self.assertEqual( " * List all active tickets by priority.\n" " * Color each row based on priority.\n", data['description']) def test_sub_var_no_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"$VAR", {'VAR': 'value'}) self.assertEqual("%s", sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_digits_underscore(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"$_VAR, $VAR2, $2VAR", { '_VAR': 'value1', 'VAR2': 'value2' }) self.assertEqual("%s, %s, $2VAR", sql) self.assertEqual(['value1', 'value2'], values) self.assertEqual([], missing_args) def test_sub_var_quotes(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"'$VAR'", {'VAR': 'value'}) with self.env.db_query as db: concatenated = db.concat("''", '%s', "''") self.assertEqual(concatenated, sql) self.assertEqual(['value'], values) self.assertEqual([], missing_args) def test_sub_var_missing_args(self): sql, values, missing_args = self.report_module.sql_sub_vars( u"$VAR, $PARAM, $MISSING", {'VAR': 'value'}) self.assertEqual("%s, %s, %s", sql) self.assertEqual(['value', '', ''], values) self.assertEqual(['PARAM', 'MISSING'], missing_args) def test_csv_escape(self): req = MockRequest(self.env) cols = ['TEST_COL', 'TEST_ZERO'] rows = [('value, needs escaped', 0)] self.assertRaises(RequestDone, self.report_module._send_csv, req, cols, rows) self.assertEqual( '\xef\xbb\xbfTEST_COL,TEST_ZERO\r\n"' 'value, needs escaped",0\r\n', req.response_sent.getvalue()) def test_saved_custom_query_redirect(self): query = u'query:?type=résumé' with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( "INSERT INTO report (title,query,description) " "VALUES (%s,%s,%s)", ('redirect', query, '')) id = db.get_last_id(cursor, 'report') req = MockRequest(self.env) self.assertRaises(RequestDone, self.report_module._render_view, req, id) self.assertEqual( 'http://example.org/trac.cgi/query?' + 'type=r%C3%A9sum%C3%A9&report=' + str(id), req.headers_sent['Location']) def test_quoted_id_with_var(self): req = MockRequest(self.env) name = """%s"`'%%%?""" with self.env.db_query as db: sql = u'SELECT 1 AS %s, $USER AS user' % db.quote(name) rv = self.report_module.execute_paginated_report( req, 1, sql, {'USER': '******'}) self.assertEqual(5, len(rv), repr(rv)) cols, results, num_items, missing_args, limit_offset = rv self.assertEqual([name, 'user'], cols) self.assertEqual([(1, 'joe')], results) self.assertEqual([], missing_args) self.assertEqual(None, limit_offset)
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 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 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)