def test_compound_types(self): self.assertEqual('[1,2,[true,false]]', presentation.to_json([1, 2, [True, False]])) self.assertEqual(r'{"one":1,"other":[null,0],' r'"three":[3,"\u0026\u003c\u003e"],' r'"two":2}', presentation.to_json({"one": 1, "two": 2, "other": [None, 0], "three": [3, "&<>"]}))
def test_compound_types(self): self.assertEqual('[1,2,[true,false]]', presentation.to_json([1, 2, [True, False]])) self.assertEqual(r'{"one":1,"other":[null,0],' r'''"three":[3,"\u0026\u003c\u003e'"],''' r'"two":2,"\u2028\n":"\u2029\r"}', presentation.to_json({"one": 1, "two": 2, "other": [None, 0], "three": [3, "&<>'"], u"\u2028\x0a": u"\u2029\x0d"}))
def test_simple_types(self): self.assertEqual('42', presentation.to_json(42)) self.assertEqual('123.456', presentation.to_json(123.456)) self.assertEqual('true', presentation.to_json(True)) self.assertEqual('false', presentation.to_json(False)) self.assertEqual('null', presentation.to_json(None)) self.assertEqual('"String"', presentation.to_json('String')) self.assertEqual(r'"a \" quote"', presentation.to_json('a " quote')) self.assertEqual('''"a ' single quote"''', presentation.to_json("a ' single quote")) self.assertEqual(r'"\u003cb\u003e\u0026\u003c/b\u003e"', presentation.to_json('<b>&</b>')) self.assertEqual(r'"\n\r\u2028\u2029"', presentation.to_json(u'\x0a\x0d\u2028\u2029'))
def process_request(self, req): q = req.args.get('q') dirname, prefix = posixpath.split(q) prefix = prefix.lower() def kind_order(entry): return (not entry['id'], embedded_numbers(entry['id'])) bm_entries = {'text': _('Favorites'), 'children': [], } result = [] # if no query then we only need to return the favorites. if not q: bm_entries['children'].extend({'id': bm.path, 'text': bm.path, 'is_favorite': True } for bm in VCSFavorite.select_all(self.env) ) bm_entries['children'] = sorted(bm_entries['children'], key=kind_order) result.append(bm_entries) json = to_json(result) req.send(json, 'text/json') return repo_entries = self._get_vcs_folders(req, q, dirname, prefix) bm_entries['children'].extend({'id': bm.path, 'text': bm.path, 'is_favorite': True } for bm in VCSFavorite.select_all_path_begins_with(self.env, q) ) for b_entry in bm_entries['children']: for r_entry in repo_entries['children']: if r_entry['text'] == b_entry['text'] or r_entry['text'] == (b_entry['text'] + '/'): r_entry['is_favorite'] = True break bm_entries['children'] = sorted(bm_entries['children'], key=kind_order) repo_entries['children'] = sorted(repo_entries['children'], key=kind_order) if bm_entries['children']: result.append(bm_entries) if repo_entries['children']: result.append(repo_entries) json = to_json(result) req.send(json, 'text/json')
def write_js(fileobj, catalog, domain, locale): from trac.util.presentation import to_json data = {"domain": domain, "locale": locale} messages = {} for msgid, msgstr in catalog.iteritems(): if isinstance(msgid, (list, tuple)): messages.setdefault(msgid[0], {}) messages[msgid[0]][msgid[1]] = msgstr elif msgid: messages[msgid] = msgstr else: for line in msgstr.splitlines(): line = line.strip() if not line: continue if ":" not in line: continue name, val = line.split(":", 1) name = name.strip().lower() if name == "plural-forms": data["plural_expr"] = pluralexpr(val) break data["messages"] = messages fileobj.write("// Generated messages javascript file " "from compiled MO file\n") fileobj.write("babel.Translations.load(") fileobj.write(to_json(data).encode("utf-8")) fileobj.write(").install();\n")
def write_js(fileobj, catalog, domain, locale): from trac.util.presentation import to_json data = {'domain': domain, 'locale': locale} messages = {} for msgid, msgstr in catalog.iteritems(): if isinstance(msgid, (list, tuple)): messages.setdefault(msgid[0], {}) messages[msgid[0]][msgid[1]] = msgstr elif msgid: messages[msgid] = msgstr else: for line in msgstr.splitlines(): line = line.strip() if not line: continue if ':' not in line: continue name, val = line.split(':', 1) name = name.strip().lower() if name == 'plural-forms': data['plural_expr'] = pluralexpr(val) break data['messages'] = messages fileobj.write('// Generated messages javascript file ' 'from compiled MO file\n') fileobj.write('babel.Translations.load(') fileobj.write(to_json(data).encode('utf-8')) fileobj.write(').install();\n')
def embed_player(self, formatter, url, query, style): query_dict = xform_query(query) set_default_parameters( query_dict, _EMBED_FLOWPLAYER_DEFAULT_PARAMETERS ) player_id = self._generate_player_id() swf = pathjoin(formatter.href.chrome(), EMBED_PATH_FLOWPLAYER['swf']) style.pop('width') # use adaptiveRatio for player-size style.pop('height') # use adaptiveRatio for player-size attrs = { 'id': player_id, 'data-swf': swf, 'style': xform_style(style), } return tag.div( tag.video([ tag.source(type=mimetypes.guess_type(url)[0], src=url), tag.script(""" $(function() { $('#%s').flowplayer(%s); }); """ % (player_id, to_json(query_dict)) ), ]), **attrs )
def process_request(self, req): product = req.args.get('product') fields_to_update = req.args.get('fields_to_update[]'); env = ProductEnvironment(self.env.parent, req.args.get('product')) ticket_fields = TicketSystem(env).get_ticket_fields() data = dict([f['name'], f['options']] for f in ticket_fields if f['type'] == 'select' and f['name'] in fields_to_update) req.send(to_json(data), 'application/json')
def process_request(self, req): if req.path_info.startswith(self.ownurl): req.perm.require('TICKET_VIEW') users = self._session_query(req.args['q'], int(req.args.get('limit', 10))) body = to_json(list(users)).encode('utf8') req.send_response(200) req.send_header('Content-Type', "application/json") req.send_header('Content-Length', len(body)) req.end_headers() req.write(body)
def to_json(value): """Encode `value` to JSON.""" if isinstance(value, basestring): return '"%s"' % javascript_quote(value) elif value is None: return 'null' elif value is False: return 'false' elif value is True: return 'true' elif isinstance(value, (int, long)): return str(value) elif isinstance(value, float): return repr(value) elif isinstance(value, (list, tuple)): return '[%s]' % ','.join(to_json(each) for each in value) elif isinstance(value, dict): return '{%s}' % ','.join('%s:%s' % (to_json(k), to_json(v)) for k, v in sorted(value.iteritems())) else: raise TypeError('Cannot encode type %s' % value.__class__.__name__)
def process_request(self, req): """ If the request is AJAX, inserts a new row into the record table for the authenticated user to show they have seen a particular notification. If the request is a normal GET, try and show the appropriate full screen project message. """ if req.path_info.startswith('/projectmessage'): try: name = req.path_info.split('/projectmessage/')[1] except IndexError: name = None self.log.debug("No project messages to show at " "/projectmessage") if name: try: msg = ProjectMessage(self.env, name) except ResourceNotFound: self.log.debug("No project messages found") else: add_script(req, 'projectmessage/js/project_message.js') data = { 'name': msg['name'], 'message': msg['message'], 'button': msg['button'], } return 'project_message.html', data, None data = {'message': 'No project messages to show.'} return 'project_message.html', data, None elif (req.method == 'POST' and req.path_info.startswith('/ajax/projectmessage')): if req.args.get('name'): new_record = ProjectMessageRecord(self.env) new_record.populate(req) try: new_record.insert() except: self.log.info("Unable to create record that %s agreed " " to %s", new_record['agreed_by'], new_record['message_name']) finally: self.log.debug("Created a new record to show %s agreed " "to %s", new_record['agreed_by'], new_record['message_name']) data = {'success': True} req.send(to_json(data), 'text/json')
def _set_default_query(self, req): """Processes a POST request to save a user based query on the task board. After validating the various values passed in req.args, the session_attribute table is updated and a JSON repsonse returned.""" data = {} status_code = 422 default_milestone = req.args.get('milestone') default_group = req.args.get('group') default_fields = req.args.get('col') default_view = req.args.get('view') if all([default_milestone, default_group, default_fields, default_view]): try: Milestone(self.env, default_milestone) except ResourceNotFound: data['invalid_milestone'] = default_milestone display_fields = default_fields.split(',') for f in chain([default_group], display_fields): if f not in self.valid_display_field_names: data['invalid_field'] = f break if default_view not in ['condensed', 'expanded']: data['invalid_view'] = default_view if not data: req.session.update({ 'taskboard_user_default_milestone': default_milestone, 'taskboard_user_default_group': default_group, 'taskboard_user_default_fields': json.dumps(display_fields), 'taskboard_user_default_view': default_view }) req.session.save() status_code = 200 req.send(to_json(data), 'text/json', status=status_code)
def process_request(self, req): """ Send a JSON response containing workload data to a XMLHttpRequest. Any other type of request raise a TracError. """ if not req.get_header('X-Requested-With') == 'XMLHttpRequest': raise TracError("We only accept XMLHttpRequests to this URL") milestone = self._get_milestone(req.args['id']) if milestone: closed_count = self._limit_user_data(self._get_closed_ticket_count(milestone.name)) logged_count = self._limit_user_data(self._get_hours_logged(milestone.name)) data = { 'closed_tickets': self._limit_user_data(closed_count), 'closed_tickets_other': self._other_user_query_string(closed_count), 'logged_hours': self._limit_user_data(logged_count), 'logged_hours_other': self._other_user_query_string(logged_count), } if milestone.completed: data['milestone_completed'] = True else: ticket_count = self._limit_user_data(self._get_open_ticket_count(milestone.name)) hour_count = self._limit_user_data(self._get_remaining_hours(milestone.name)) data.update({ 'milestone_completed': False, 'remaining_tickets': self._limit_user_data(ticket_count), 'remaing_tickets_other': self._other_user_query_string(ticket_count), 'remaining_hours': self._limit_user_data(hour_count), 'remaining_hours_other': self._other_user_query_string(hour_count), }) else: data = {} req.send(to_json(data, cls=DecimalEncoder), 'text/json')
def process_request(self, req): """ Sets a session attribute to show the user has seen the tortoise svn repo browser dialog. This request will only ever be via a POST with Ajax. As a result, if the request is a GET or does not have a XMLHttpRequest header we redirect the user back to the browser page. Once the new session attribute is set, we send a JSON respsone. We stop showing the dialog box when this attribute is set. """ if (req.method == "POST" and req.get_header('X-Requested-With') == 'XMLHttpRequest' and req.args.get('tortoise-svn-message')): req.session['tortoise_svn_message'] = True self.log.info("Set tortoise_svn_message session attribute " "as True for %s", req.authname) req.session.save() req.send(to_json({"success":True}), 'text/json') else: # if you try to access this page via GET we redirect req.redirect(req.href.browser())
def _enable_autocomplete_for_page(self, req, method, filename, stream, data, inputs): add_stylesheet(req, 'autocomplete/css/autocomplete.css') add_script(req, 'autocomplete/js/jquery.tracautocomplete.js') username_completers = list(self._username_completers(req)) add_script_data(req, {'username_completers': username_completers}) # we could put this into some other URL which the browser could cache? #show users from all groups, shown or not, on the members page add_script_data(req, {'project_users': self._project_users}) controls = (self._split_input(input_) for input_ in inputs) js = ''.join('$("%s").makeAutocompleteSearch("%s", %s);\n' % (selector, method_ or 'select', options if isinstance(options, basestring) else to_json(options)) for selector, method_, options in controls) stream = stream | Transformer('//head').append(tag.script(""" jQuery(document).ready(function($) { %s }); """ % js,type="text/javascript")) return stream
def to_json(data): from trac.util.presentation import to_json text = to_json(data) if isinstance(text, unicode): text = text.encode('utf-8') return text
def _send_json(self, req, data): req.send(to_json(data), 'application/json')
def test_simple_types(self): self.assertEqual('42', presentation.to_json(42)) self.assertEqual('123.456', presentation.to_json(123.456)) self.assertEqual('true', presentation.to_json(True)) self.assertEqual('false', presentation.to_json(False)) self.assertEqual('null', presentation.to_json(None)) self.assertEqual('"String"', presentation.to_json('String')) self.assertEqual('1551895815012345', presentation.to_json(datetime.datetime( 2019, 3, 6, 18, 10, 15, 12345, datefmt.utc))) self.assertEqual('1551895815012345', presentation.to_json(datetime.datetime( 2019, 3, 6, 18, 10, 15, 12345))) self.assertEqual(r'"a \" quote"', presentation.to_json('a " quote')) self.assertEqual('''"a ' single quote"''', presentation.to_json("a ' single quote")) self.assertEqual(r'"\u003cb\u003e\u0026\u003c/b\u003e"', presentation.to_json('<b>&</b>')) self.assertEqual(r'"\n\r\u2028\u2029"', presentation.to_json(u'\x0a\x0d\u2028\u2029'))
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 query_args['col'] = '|'.join([self.totalhours_field, 'owner']) 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]) - float(ticket[self.totalhours_field]) owner = ticket['owner'] sum += estimation if estimations.has_key(owner): estimations[owner] += estimation else: estimations[owner] = estimation except: pass data = [] data.append(['Owner', 'Workload']) for owner, estimation in estimations.iteritems(): estimation = max(0, estimation) label = "%s %g%s" % (obfuscate_email_address(owner), round(estimation, 2), self.estimation_suffix) data.append([label, float(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) element_id = 'chart-%d' % random.randint(0, 0xffffffff) args = { 'containerId': element_id, 'chartType': 'PieChart', 'options': { 'width': int(options['width']), 'height': int(options['height']), 'title': title, 'legend': { 'position': 'labeled' }, 'pieSliceText': 'none', 'tooltip': 'percentage', }, } script = "EstimationCharts.push(function() {\n" script += 'var data=' + to_json(data) + ";\n" script += 'var args=' + to_json(args) + ";\n" script += 'DrawWorkloadChart(data, args);' script += '});' return tag.div(tag.div(id=element_id), tag.script(script))
def process_request(self, req): """Collect the data needed for a burndown chart and pass to JavaScript. If the original request was via AJAX we use to_json, otherwise we return data via add_script_data. Remember that our data reflects how a project looked at the end of a day - so if the ticket_bi_historical table has 20 opens tickets on the 1st December, that were 20 open tickets at the end of that day. """ # Get milestone object and all child milestones # we already know it exists, as we checked in the match_request() milestone = Milestone(self.env, req.args['id']) tree = Milestone.build_tree(self.env) all_milestones = [m.name for m in tree.find(milestone.name).traverse()] # If anyone request burndownchart/milestone_id not via AJAX # and not with a format argument (eg when printing) , we redirect to # milestone/milestonename, as we need to load pre_process_request first XMLHttp = req.get_header('X-Requested-With') == 'XMLHttpRequest' if not XMLHttp and 'format' not in req.args: req.redirect(req.href.milestone(milestone.name)) # Calculate series of dates used to render the burn down charts start = self.get_start_date(req, milestone) end = self.get_end_date(milestone) day_before_start = start - timedelta(days=1) dates = self.dates_inbetween(day_before_start, end) # Open a database connection self.log.debug('Connecting to the database to retrieve chart data') db = self.env.get_read_db() # If no metric data is posted, use the project default self.unit_value metric = req.args.get('metric', self.unit_value) # Remaining Effort (aka burndown) Curve remaining_effort_args = [db, all_milestones, day_before_start, end] burndown_series = [] if metric == 'tickets': burndown_series = self.tickets_open_between_dates(*remaining_effort_args) elif metric == 'hours': burndown_series = self.hours_remaining_between_dates(*remaining_effort_args) elif metric == 'points': burndown_series = self.points_remaining_between_dates(*remaining_effort_args) # If we don't have any burndown data send a message and stop if not burndown_series: data = {'result': False} # For ajax request if XMLHttp: req.send(to_json(data), 'text/json') else: return 'burndown_print.html', data, None # Team Effort Curve team_effort = self.team_effort_curve(db, metric, all_milestones, day_before_start, end, self.dates_as_strings(dates)) # Ideal Curve (unit value doesnt matter) if self.ideal_value == 'fixed': original_estimate = burndown_series[0][1] ideal_data = self.ideal_curve(original_estimate, day_before_start, self.get_due_date(milestone)) data = { 'burndowndata': burndown_series, 'teameffortdata' : team_effort, 'idealcurvedata': ideal_data, 'milestone_name': milestone.name, 'start_date': str(day_before_start), 'effort_units': metric, 'yaxix_label': metric.title(), 'result' : True, } # we need some logic to work out the end date on the xAxis data['due_date'] = (self.get_due_date(milestone)).strftime("%Y-%m-%d") # Ajax request if XMLHttp: kwargs = { 'daysback':0, 'ticket':'on', 'ticket_details': 'on', 'ticket_milestone_%s' % Milestone._hash_name(milestone.name): 'on' } data.update({ 'timeline_url': req.href.timeline(kwargs), 'print_burndown': False, 'render_burndown': True, }) req.send(to_json(data), 'text/json') # Normal request (eg the print friendly page) else: if req.args.get('format') == 'print': # Load the burn down JS file and jqPlot library add_script(req, 'burndown/js/burndown.js') self._add_static_files(req) result = {'data': data, 'print_burndown': True, 'render_burndown': True, 'result': True, } add_script_data(req, result) return 'burndown_print.html', result, None
if not failures: try: files = self.send_as_email(req, sender, recipients, subject, message, mode, *to_send) except Exception, e: files = [] failures.append(str(e)) else: files = [] response = dict(files=files, recipients=[x[1] for x in recipients], failures=failures) if failures != []: msg = ", ".join(failures) add_warning(req, msg) self.log.error('Failures in source sharing: %s', msg) if 'XMLHttpRequest' == req.get_header('X-Requested-With'): req.send(to_json(response), 'text/json') else: add_notice(req, _("Sent %(files)s to %(recipients)s", files=', '.join(files), recipients=', '.join([x[1] for x in recipients]))) req.redirect() def match_request(self, req): return req.path_info == '/share' # IPermissionRequestor methods def get_permission_actions(self): return ['BROWSER_VIEW', 'FILE_VIEW'] #IAutoCompleteUser
def process_request(self, req): req.perm.assert_permission('TICKET_VIEW') # set the default user query if req.path_info == '/taskboard/set-default-query' and req.method == 'POST': self._set_default_query(req) # these headers are only needed when we update tickets via ajax req.send_header("Cache-Control", "no-cache, no-store, must-revalidate") req.send_header("Pragma", "no-cache") req.send_header("Expires", 0) xhr = req.get_header('X-Requested-With') == 'XMLHttpRequest' group_by = req.args.get("group", "status") user_saved_query = False milestones = Milestone.select_names_select2(self.env, include_complete=False) # Try to find a user selected milestone in request - if not found # check session_attribute for a user saved default, and if that is also # not found and fall back on the most upcoming milestone by due date milestone = req.args.get("milestone") milestone_not_found = False if milestone: try: Milestone(self.env, milestone) except ResourceNotFound: milestone_not_found = True milestone = None if not milestone: # try and find a user saved default default_milestone = req.session.get('taskboard_user_default_milestone') if default_milestone: milestone = default_milestone group_by = req.session.get('taskboard_user_default_group') user_saved_query = True # fall back to most imminent milestone by due date elif len(milestones["results"]): milestone = milestones["results"][0]["text"] # Ajax post if req.args.get("ticket") and xhr: result = self.save_change(req, milestone) req.send(to_json(result), 'text/json') else: data = {} constr = {} if milestone: constr['milestone'] = [milestone] # Ajax update: tickets changed between a period if xhr: from_iso = req.args.get("from", "") to_iso = req.args.get("to", "") if from_iso and to_iso: constr['changetime'] = [from_iso + ".." + to_iso] # Get all tickets by milestone and specify ticket fields to retrieve cols = self._get_display_fields(req, user_saved_query) tickets = self._get_permitted_tickets(req, constraints=constr, columns=cols) sorted_cols = sorted([f for f in self.valid_display_fields if f['name'] not in ('summary', 'type')], key=lambda f: f.get('label')) if tickets: s_data = self.get_ticket_data(req, milestone, group_by, tickets) s_data['total_tickets'] = len(tickets) s_data['display_fields'] = cols data['cur_group'] = s_data['groupName'] else: s_data = {} data['cur_group'] = group_by if xhr: if constr.get("changetime"): s_data['otherChanges'] = \ self.all_other_changes(req, tickets, constr['changetime']) req.send(to_json(s_data), 'text/json') else: s_data.update({ 'formToken': req.form_token, 'milestones': milestones, 'milestone': milestone, 'group': group_by, 'default_columns': self.default_display_fields }) data.update({ 'milestone_not_found': milestone_not_found, 'current_milestone': milestone, 'group_by_fields': self.valid_grouping_fields, 'fields': dict((f['name'], f) for f in self.valid_display_fields), 'all_columns': [f['name'] for f in sorted_cols], 'col': cols, 'condensed': self._show_condensed_view(req, user_saved_query) }) add_script(req, 'agiletools/js/update_model.js') add_script(req, 'agiletools/js/taskboard.js') add_script(req, 'common/js/query.js') add_script_data(req, s_data) add_stylesheet(req, 'agiletools/css/taskboard.css') add_stylesheet(req, 'common/css/ticket.css') add_ctxtnav(req, tag.a(tag.i(class_='fa fa-bookmark'), _(" Set as default"), id_='set-default-query', title=_("Make this your default taskboard"))) return "taskboard.html", data, None
def get_templates(self): return {"ticket.html": [('#field-keywords', 'text', '{source: %s}' % to_json( self._current_keywords).encode('utf8'))]}
def expand_macro(self, formatter, name, content, args=None): # Utility methods def lte_ie8(req): user_agent = formatter.req.get_header('user-agent') msie = user_agent.find('MSIE ') return (msie != -1) and user_agent[msie + 5:msie + 6] in ['6', '7', '8'] def after_AS(string): index = string.find(' AS ') return index > 0 and string[index + 4:] or string def before_AS(string): index = string.find(' AS ') return index > 0 and string[:index] or string # add scripts if lte_ie8(formatter.req): add_script(formatter.req, "statushistorychart/js/flot/excanvas.js") add_script(formatter.req, "statushistorychart/js/flot/jquery.flot.js") add_script(formatter.req, "statushistorychart/js/flot/jquery.flot.time.js") add_script(formatter.req, "statushistorychart/js/enabler.js") # from macro parameters query = None query_href = None status_list = ['new', 'assigned', 'accepted', 'closed'] # default if(content): # replace parameters for match in reversed([match for match in re_param.finditer(content)]): param_name = match.group(0) if param_name == '$USER': authname = formatter.req.authname if authname == 'anonymous': authname = 'anonymous||somebody' content = content.replace(param_name, authname) else: param_value = formatter.req.args.get(param_name[1:]) if param_value: content = content.replace(param_name, param_value) # execute query query = Query.from_string(formatter.env, content) field = query and query.id or 'status' # stopgap implementation; I use 'report' for y-axis parameter field = filter(lambda x: x['name'] == field, TicketSystem(self.env).fields) if len(field) <= 0: return tag.div(tag.strong('Error: Macro %s failed' % name), tag.pre("%s is not field name" % query.id), class_="system-message") field = field[0] custom = 'custom' in field and field['custom'] or None status_list = query and query.format and query.format.split('/') or \ 'options' in field and copy.copy(field['options']) or status_list if field['name'] == 'status' and not (query and query.format): def isprime(item): primaries = ['new', 'assigned', 'accepted', 'closed'] return (item in primaries and [primaries.index(item)] or [len(primaries)])[0] status_list.sort(key=isprime) if '*' not in status_list: status_list.append('*') default_status = status_list.index('*') # execute query for ids of ticket if(query and len(query.constraint_cols) > 0): result = query.execute() or [{'id':-1}] # Sentinel for no result cond = "ticket.id in (%s)" % ', '.join([str(t['id']) for t in result]) elif formatter.resource.realm == 'milestone': cond = "ticket.milestone='%s'" % formatter.resource.id elif('query_tickets' in formatter.req.session): # You Feeling Lucky query_tickets = formatter.req.session.get('query_tickets', '') query_href = formatter.req.session.get('query_href', '') tickets = len(query_tickets) and query_tickets.split(' ') or ['-1'] # Sentinel for no result cond = "ticket.id in (%s)" % ', '.join(tickets) else: raise TracError("%sMacro: Empty. There are no content and no context." % name) # execute query for value changes of each ticket join_clause_dummy = '' join_clause = "JOIN ticket_custom ON ticket.id = ticket_custom.ticket and ticket_custom.name = '%s'" cursor = formatter.env.get_read_db().cursor() cursor.execute(""" SELECT id, time, null, %s FROM ticket %s WHERE %s UNION SELECT id, ticket_change.time, oldvalue, newvalue FROM ticket JOIN ticket_change ON ticket.id = ticket_change.ticket WHERE %s AND field='%s' ORDER BY id, time """ % (custom and "'\uFEFF'" or field['name'], # ZERO WIDTH NO-BREAK SPACE; uses for mark of invalid data custom and (join_clause % field['name']) or join_clause_dummy, cond, cond, field['name'])) changes = [row for row in cursor.fetchall()] # transform (ticket, time, status)* ==> (ticket <>- status)* tid = 0 # position of ticket id tickets = {} for change in changes: ticket = tickets.get(change[tid]) # slot is exist, or if ticket is None: ticket = tickets[change[tid]] = [] # create new slot and use it ticket.append(list(change)) # generate status_list splitted, {a, b+c, d} => {a:0, b+c:1, b:1, c:1, d:2} status_list_splitted = {} for index, status in enumerate(map(before_AS, status_list)): status_list_splitted[status] = index if status.find('+') >= 0: for each in status.split('+'): status_list_splitted[each] = index # groupstats = self.compmgr[DefaultTicketGroupStatsProvider] # ticket_groups = groupstats._get_ticket_groups() # for group in ticket_groups: # pass for tid in tickets: if len(tickets[tid]) > 1: tickets[tid][0][3] = tickets[tid][1][2] # override last value with initial value # generate data data = [] # points too_many_tickets = len(tickets) > 200 for no, tid in enumerate(sorted(tickets)): if not too_many_tickets or tickets[tid][-1][3] != 'closed': void, time, void, state = tickets[tid][-1] # @UnusedVariable index = status_list_splitted.get(state, default_status) data.append({'points': {'show': True, 'radius': 8, 'color': no}, 'label': tid, 'data': [[time / 1000, index]]}) # lines for no, tid in enumerate(sorted(tickets)): data.append({'color': no, 'label': tid, 'data': [[time / 1000, status_list_splitted.get(state, default_status)] for void, time, void, state in tickets[tid]]}) # @UnusedVariable from trac import __version__ as VERSION if VERSION[0:1] != '0': # render for trac 1.0 or later add_script_data(formatter.req, {'statushistorychart_yaxis': map(after_AS, status_list)}) add_script_data(formatter.req, {'statushistorychart_data': data}) return tag.a(_("Return to Last Query"), href=query_href) \ + tag.div(id="statushistorychart", style="width: 800px; height: 400px;") else: # if trac < 1.0 or earlier from trac.util.presentation import to_json return tag.script("var statushistorychart_yaxis = %s; var statushistorychart_data = %s" \ % (to_json(map(after_AS, status_list)), to_json(data)), type="text/javascript") \ + tag.a(_("Return to Last Query"), href=query_href) \ + tag.div(id="statushistorychart", style="width: 800px; height: 400px;")
def process_request(self, req): if 'action' in req.args: transform_id = self._generate_running_transformation_id() parameters = {} for k in req.args: if k.startswith("parameter:"): parameter_name = k.split(":",2)[1] parameter_value = req.args[k] parameters[parameter_name] = parameter_value req.perm.require("BUSINESSINTELLIGENCE_TRANSFORMATION_EXECUTE") if req.args['action'] == "execute_async": # execute the transformation thread.start_new_thread(self._do_execute_transformation, (req.args['transform'],), {'transformation_id': transform_id, 'parameters': parameters}) # send transform_id generated via uuid back to JS via JSON # we have to do this after we invoke a new thread as req.send() # returns from this function and stops further flow control req.send(to_json({'transform_id': transform_id}), 'text/json') elif req.args['action'] == "execute_download": filename, stat, filestream = self._do_execute_transformation(req.args['transform'], transformation_id=transform_id, store=False, return_bytes_handle=True, parameters=parameters) req.send_response(200) req.send_header('Content-Type', mimetypes.guess_type(filename)[0] or 'application/octet-stream') req.send_header('Content-Length', stat.st_size) req.send_header('Content-Disposition', content_disposition('attachment', filename)) req.end_headers() while True: bytes = filestream.read(4096) if not bytes: break req.write(bytes) filestream.close() raise RequestDone elif req.args['action'] == "execute": self._do_execute_transformation(req.args['transform'], transformation_id=transform_id, parameters=parameters) elif req.args['action'] == 'check_status': if 'uuid' not in req.args: raise KeyError running_transformations = json.loads(req.args['uuid']) req.send(to_json(self._generate_status_response(running_transformations)), 'text/json') else: add_warning(req, "No valid action found") req.redirect(req.href.businessintelligence()) if req.get_header('X-Requested-With') == 'XMLHttpRequest': req.send_response(200) req.send_header('Content-Length', 0) req.end_headers() return else: if 'returnto' in req.args: req.redirect(req.args['returnto']) else: req.redirect(req.href.businessintelligence()) else: req.perm.require("BUSINESSINTELLIGENCE_TRANSFORMATION_LIST") data = {'transformations': self._list_transformation_files(listall=False)} add_script(req, 'contextmenu/contextmenu.js') add_script(req, 'businessintelligenceplugin/js/business-intelligence.js') add_stylesheet(req, 'common/css/browser.css') add_ctxtnav(req, tag.a(tag.i(class_="fa fa-upload"), ' Upload Transformations', id="uploadbutton")) add_ctxtnav(req, tag.a(tag.i(class_="fa fa-calendar"), ' Schedule Transformations', id="schedulebutton")) add_ctxtnav(req, tag.a(tag.i(class_="fa fa-cog"), ' Running Transformations', id="runningbutton")) return "listtransformations.html", data, None
""" try: tm = self._get_ticket_module() req.perm.require('TICKET_CREATE') summary = req.args.pop('field_summary', '') desc = "" attrs = dict([k[6:], v] for k, v in req.args.iteritems() if k.startswith('field_')) product, tid = self.create(req, summary, desc, attrs, True) except Exception, exc: self.log.exception("BH: Quick create ticket failed %s" % (exc,)) req.send(str(exc), 'plain/text', 500) else: tres = Neighborhood('product', product)('ticket', tid) href = req.href req.send(to_json({'product': product, 'id': tid, 'url': get_resource_url(self.env, tres, href)}), 'application/json') def _get_ticket_module(self): ptm = None if ProductTicketModule is not None: ptm = self.env[ProductTicketModule] tm = self.env[TicketModule] if not (tm is None) ^ (ptm is None): raise TracError('Unable to load TicketModule (disabled)?') if tm is None: tm = ptm return tm # Public API def create(self, req, summary, description, attributes={}, notify=False):
req.perm.require('TICKET_CREATE') summary = req.args.pop('field_summary', '') desc = "" attrs = dict([k[6:], v] for k, v in req.args.iteritems() if k.startswith('field_')) product, tid = self.create(req, summary, desc, attrs, True) except Exception, exc: self.log.exception("BH: Quick create ticket failed %s" % (exc, )) req.send(str(exc), 'plain/text', 500) else: tres = Neighborhood('product', product)('ticket', tid) href = req.href req.send( to_json({ 'product': product, 'id': tid, 'url': get_resource_url(self.env, tres, href) }), 'application/json') def _get_ticket_module(self): ptm = None if ProductTicketModule is not None: ptm = self.env[ProductTicketModule] tm = self.env[TicketModule] if not (tm is None) ^ (ptm is None): raise TracError('Unable to load TicketModule (disabled)?') if tm is None: tm = ptm return tm # Public API
def _json_send(self, req, dictionary): return req.send(to_json(dictionary), 'text/json')