def _render_editor(self, req, milestone): data = { 'milestone': milestone, 'datetime_hint': get_datetime_format_hint(req.lc_time), 'default_due': self.get_default_due(req), 'milestone_groups': [], } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') milestones = [m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource)] data['milestone_groups'] = group_milestones(milestones, 'TICKET_ADMIN' in req.perm) data['num_open_tickets'] = milestone \ .get_num_tickets(exclude_closed=True) data['retarget_to'] = self.default_retarget_to else: req.perm(milestone.resource).require('MILESTONE_CREATE') if milestone.name: add_notice(req, _("Milestone %(name)s does not exist. You can" " create it here.", name=milestone.name)) chrome = Chrome(self.env) chrome.add_jquery_ui(req) chrome.add_wiki_toolbars(req) add_stylesheet(req, 'common/css/roadmap.css') return 'milestone_edit.html', data, None
def _go_graph(self, req): #search all artifacts. artifacts = ArtifactManager.find_all(self.env) milestones = [m for m in Milestone.select(self.env) if 'MILESTONE_VIEW' in req.perm(m.resource)] #add artifacts to milestone. for m in milestones: artifacts_of_m = [artifact for artifact in artifacts if artifact.milestone == m.name] setattr(m, 'artifacts', artifacts_of_m) manager = SCMManager(self.env, req) nodes = manager.artifacts_to_nodes(artifacts) roots = manager.get_roots(nodes) gm = GraphManager(self.env) nodes = gm.to_graphnode(roots) gm.to_s(nodes) graph = gm.to_graphviz(nodes, milestones) self.env.log.info('graphviz graph:\n%s' % graph) data = {'milestones':milestones, 'artifacts':artifacts, 'graph':graph} return 'releaseartifactgraph.html', data, None
def select(env, include_completed=True, db=None): if not db: db = env.get_db_cnx() milestones = [ StructuredMilestone(env, milestone) \ for milestone in Milestone.select(env, include_completed, db)] return StructuredMilestone.reorganize(milestones)
def _render_editor(self, req, milestone): # Suggest a default due time of 18:00 in the user's timezone now = datetime.now(req.tz) default_due = datetime(now.year, now.month, now.day, 18) if now.hour > 18: default_due += timedelta(days=1) default_due = to_datetime(default_due, req.tz) data = { 'milestone': milestone, 'datetime_hint': get_datetime_format_hint(req.lc_time), 'default_due': default_due, 'milestone_groups': [], } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') milestones = [m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource)] data['milestone_groups'] = group_milestones(milestones, 'TICKET_ADMIN' in req.perm) else: req.perm(milestone.resource).require('MILESTONE_CREATE') if milestone.name: add_notice(req, _("Milestone %(name)s does not exist. You can" " create it here.", name=milestone.name)) chrome = Chrome(self.env) chrome.add_jquery_ui(req) chrome.add_wiki_toolbars(req) return 'milestone_edit.html', data, None
def _render_editor(self, req, milestone): data = { 'milestone': milestone, 'datetime_hint': get_datetime_format_hint(req.lc_time), 'default_due': self.get_default_due(req), 'milestone_groups': [], } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') milestones = [m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource)] data['milestone_groups'] = \ group_milestones(milestones, 'TICKET_ADMIN' in req.perm) data['num_open_tickets'] = \ get_num_tickets_for_milestone(self.env, milestone, exclude_closed=True) data['retarget_to'] = self.default_retarget_to else: req.perm(milestone.resource).require('MILESTONE_CREATE') if milestone.name: add_notice(req, _("Milestone %(name)s does not exist. You " "can create it here.", name=milestone.name)) chrome = Chrome(self.env) chrome.add_jquery_ui(req) chrome.add_wiki_toolbars(req) add_stylesheet(req, 'common/css/roadmap.css') return 'milestone_edit.html', data
def _render_editor(self, req, milestone): # Suggest a default due time of 18:00 in the user's timezone now = datetime.now(req.tz) default_due = datetime(now.year, now.month, now.day, 18) if now.hour > 18: default_due += timedelta(days=1) default_due = to_datetime(default_due, req.tz) data = { 'milestone': milestone, 'datetime_hint': get_datetime_format_hint(req.lc_time), 'default_due': default_due, 'milestone_groups': [], } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') milestones = [ m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource) ] data['milestone_groups'] = group_milestones( milestones, 'TICKET_ADMIN' in req.perm) else: req.perm(milestone.resource).require('MILESTONE_CREATE') chrome = Chrome(self.env) chrome.add_jquery_ui(req) chrome.add_wiki_toolbars(req) return 'milestone_edit.html', data, None
def _reindex_milestone(self, realm, feedback, finish_fb): resources = Milestone.select(self.env) def check(milestone, check): return True index = self._index_milestone return self._index(realm, resources, check, index, feedback, finish_fb)
def default_retarget_to(self): if self._default_retarget_to and \ not any(self._default_retarget_to == m.name for m in Milestone.select(self.env)): self.log.warn('Milestone "%s" does not exist. Update the ' '"default_retarget_to" option in the [milestone] ' 'section of trac.ini', self._default_retarget_to) return self._default_retarget_to
def _reindex_milestone(self, realm, feedback, finish_fb): resources = Milestone.select(self.env) def check(milestone, check): return True index = self.milestone_created return self._index(realm, resources, check, index, feedback, finish_fb)
def _modify_roadmap_page(self, req, template, data, content_type, is_active): """Insert roadmap.css + products breadcrumb """ add_stylesheet(req, 'dashboard/css/roadmap.css') self._add_products_general_breadcrumb(req, template, data, content_type, is_active) data['milestone_list'] = [m.name for m in Milestone.select(self.env)] req.chrome['ctxtnav'] = []
def test_select_milestones(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('1.0', ), ('2.0', )]) milestones = list(Milestone.select(self.env)) self.assertEqual('1.0', milestones[0].name) assert milestones[0].exists self.assertEqual('2.0', milestones[1].name) assert milestones[1].exists
def test_select_milestones(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('1.0',), ('2.0',)]) milestones = list(Milestone.select(self.env)) self.assertEqual('1.0', milestones[0].name) assert milestones[0].exists self.assertEqual('2.0', milestones[1].name) assert milestones[1].exists
def test_select_milestones(self): cursor = self.db.cursor() cursor.executemany("INSERT INTO milestone (name) VALUES (%s)", [("1.0",), ("2.0",)]) cursor.close() milestones = list(Milestone.select(self.env)) self.assertEqual("1.0", milestones[0].name) assert milestones[0].exists self.assertEqual("2.0", milestones[1].name) assert milestones[1].exists
def test_select_milestones(self): cursor = self.db.cursor() cursor.executemany("INSERT INTO milestone (name) VALUES (%s)", [('1.0', ), ('2.0', )]) cursor.close() milestones = list(Milestone.select(self.env)) self.assertEqual('1.0', milestones[0].name) assert milestones[0].exists self.assertEqual('2.0', milestones[1].name) assert milestones[1].exists
def list_view(self, req, cat, page): data = { 'view': 'list', 'sprints': self.sm.select(), 'format_datetime' : datefmt.format_datetime, 'date_hint' : datefmt.get_date_format_hint(), 'datetime_hint' : datefmt.get_datetime_format_hint(), 'milestones' : [m.name for m in Milestone.select(self.env)], } data.update(req.args) return 'agilo_admin_sprint.html', data
def list_view(self, req, cat, page): data = { 'view': 'list', 'sprints': self.sm.select(), 'format_datetime': datefmt.format_datetime, 'date_hint': datefmt.get_date_format_hint(), 'datetime_hint': datefmt.get_datetime_format_hint(), 'milestones': [m.name for m in Milestone.select(self.env)], } data.update(req.args) return 'agilo_admin_sprint.html', data
def _render_confirm(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') milestones = [m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource)] data = { 'milestone': milestone, 'milestone_groups': group_milestones(milestones, 'TICKET_ADMIN' in req.perm) } return 'milestone_delete.html', data, None
def _render_confirm(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') milestones = [ m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource) ] data = { 'milestone': milestone, 'milestone_groups': group_milestones(milestones, 'TICKET_ADMIN' in req.perm) } return 'milestone_delete.html', data, None
def users(self, req): """hours for all users""" data = {'hours_format': hours_format} ### date data self.date_data(req, data) ### milestone data milestone = req.args.get('milestone') milestones = Milestone.select(self.env) data['milestones'] = milestones ### get the hours #trachours = TracHoursPlugin(self.env) #tickets = trachours.tickets_with_hours() hours = get_all_dict( self.env, """ SELECT * FROM ticket_time WHERE time_started >= %s AND time_started < %s """, *[ int(time.mktime(i.timetuple())) for i in (data['from_date'], data['to_date']) ]) worker_hours = {} for entry in hours: worker = entry['worker'] if worker not in worker_hours: worker_hours[worker] = 0 if milestone and milestone != \ Ticket(self.env, entry['ticket']).values.get('milestone'): continue worker_hours[worker] += entry['seconds_worked'] worker_hours = [(worker, seconds / 3600.) for worker, seconds in sorted(worker_hours.items())] data['worker_hours'] = worker_hours if req.args.get('format') == 'csv': req.send(self.export_csv(req, data)) #add_link(req, 'prev', self.get_href(query, args, context.href), # _('Prev Week')) #add_link(req, 'next', self.get_href(query, args, context.href), # _('Next Week')) #prevnext_nav(req, _('Prev Week'), _('Next Week')) return 'hours_users.html', data, "text/html"
def process_request(self, req): req.perm.require('ROADMAP_VIEW') show = req.args.getlist('show') if 'all' in show: show = ['completed'] milestones = Milestone.select(self.env, 'completed' in show) if 'noduedate' in show: milestones = [ m for m in milestones if m.due is not None or m.completed ] milestones = [ m for m in milestones if 'MILESTONE_VIEW' in req.perm(m.resource) ] stats = [] queries = [] for milestone in milestones: tickets = get_tickets_for_milestone(self.env, milestone=milestone.name, field='owner') tickets = apply_ticket_permissions(self.env, req, tickets) stat = get_ticket_stats(self.stats_provider, tickets) stats.append( milestone_stats_data(self.env, req, stat, milestone.name)) #milestone['tickets'] = tickets # for the iCalendar view if req.args.get('format') == 'ics': self._render_ics(req, milestones) return # FIXME should use the 'webcal:' scheme, probably username = None if req.authname and req.authname != 'anonymous': username = req.authname icshref = req.href.roadmap(show=show, user=username, format='ics') add_link(req, 'alternate', auth_link(req, icshref), _('iCalendar'), 'text/calendar', 'ics') data = { 'milestones': milestones, 'milestone_stats': stats, 'queries': queries, 'show': show, } add_stylesheet(req, 'common/css/roadmap.css') return 'roadmap.html', data, None
def _go_list(self, req): #search all artifacts. artifacts = ArtifactManager.find_all(self.env) milestones = [m for m in Milestone.select(self.env) if 'MILESTONE_VIEW' in req.perm(m.resource)] #add artifacts to milestone. for m in milestones: artifacts_of_m = [artifact for artifact in artifacts if artifact.milestone == m.name] setattr(m, 'artifacts', artifacts_of_m) data = {'milestones':milestones, 'artifacts':artifacts} return 'releaseartifactgraph.html', data, None
def users(self, req): """hours for all users""" data = {'hours_format': hours_format} ### date data self.date_data(req, data) ### milestone data milestone = req.args.get('milestone') milestones = Milestone.select(self.env) data['milestones'] = milestones ### get the hours #trachours = TracHoursPlugin(self.env) #tickets = trachours.tickets_with_hours() hours = get_all_dict(self.env, """ SELECT * FROM ticket_time WHERE time_started >= %s AND time_started < %s """, *[int(time.mktime(i.timetuple())) for i in (data['from_date'], data['to_date'])]) worker_hours = {} for entry in hours: worker = entry['worker'] if worker not in worker_hours: worker_hours[worker] = 0 if milestone and milestone != \ Ticket(self.env, entry['ticket']).values.get('milestone'): continue worker_hours[worker] += entry['seconds_worked'] worker_hours = [(worker, seconds/3600.) for worker, seconds in sorted(worker_hours.items())] data['worker_hours'] = worker_hours if req.args.get('format') == 'csv': req.send(self.export_csv(req, data)) #add_link(req, 'prev', self.get_href(query, args, context.href), # _('Prev Week')) #add_link(req, 'next', self.get_href(query, args, context.href), # _('Next Week')) #prevnext_nav(req, _('Prev Week'), _('Next Week')) return 'hours_users.html', data, "text/html"
def users(self, req): """hours for all users""" data = { 'hours_format' : hours_format } ### date data self.date_data(req, data) ### milestone data milestone = req.args.get('milestone') milestones = Milestone.select(self.env) data['milestones'] = milestones ### get the hours #trachours = TracHoursPlugin(self.env) #tickets = trachours.tickets_with_hours() hours = get_all_dict(self.env, "SELECT * FROM ticket_time WHERE time_started >= %s AND time_started < %s", *[int(time.mktime(i.timetuple())) for i in (data['from_date'], data['to_date'])]) worker_hours = {} for entry in hours: worker = entry['worker'] if worker not in worker_hours: worker_hours[worker] = 0 if milestone: if milestone != Ticket(self.env, entry['ticket']).values.get('milestone'): continue worker_hours[worker] += entry['seconds_worked'] worker_hours = [(worker, seconds/3600.) for worker, seconds in sorted(worker_hours.items())] data['worker_hours'] = worker_hours if req.args.get('format') == 'csv': buffer = StringIO() writer = csv.writer(buffer) format = '%B %d, %Y' title = "Hours for %s" % self.env.project_name writer.writerow([title, req.abs_href()]) writer.writerow([]) writer.writerow(['From', 'To']) writer.writerow([data[i].strftime(format) for i in 'from_date', 'to_date']) if milestone:
def detail_view(self, req, cat, page, name): sprint = self.sm.get(name=name) if not sprint or not sprint.exists: return req.redirect(req.href.admin(cat, page)) data = { 'view': 'detail', 'sprint': sprint, 'teams': self.tm.select(), 'format_datetime': datefmt.format_datetime, 'date_hint': datefmt.get_date_format_hint(), 'datetime_hint': datefmt.get_datetime_format_hint(), 'milestones': [m.name for m in Milestone.select(self.env)], } data.update(req.args) add_script(req, 'common/js/wikitoolbar.js') return 'agilo_admin_sprint.html', data
def _render_confirm(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') milestones = [m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource)] attachments = Attachment.select(self.env, self.realm, milestone.name) data = { 'milestone': milestone, 'milestone_groups': group_milestones(milestones, 'TICKET_ADMIN' in req.perm), 'num_tickets': milestone.get_num_tickets(), 'retarget_to': self.default_retarget_to, 'attachments': list(attachments) } add_stylesheet(req, 'common/css/roadmap.css') return 'milestone_delete.html', data, None
def _render_confirm(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') milestones = [m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource)] attachments = Attachment.select(self.env, self.realm, milestone.name) data = { 'milestone': milestone, 'milestone_groups': group_milestones(milestones, 'TICKET_ADMIN' in req.perm), 'num_tickets': get_num_tickets_for_milestone(self.env, milestone), 'retarget_to': self.default_retarget_to, 'attachments': list(attachments) } add_stylesheet(req, 'common/css/roadmap.css') return 'milestone_delete.html', data
def process_request(self, req): req.perm.require('ROADMAP_VIEW') show = req.args.getlist('show') if 'all' in show: show = ['completed'] milestones = Milestone.select(self.env, 'completed' in show) if 'noduedate' in show: milestones = [m for m in milestones if m.due is not None or m.completed] milestones = [m for m in milestones if 'MILESTONE_VIEW' in req.perm(m.resource)] stats = [] queries = [] for milestone in milestones: tickets = get_tickets_for_milestone( self.env, milestone=milestone.name, field='owner') tickets = apply_ticket_permissions(self.env, req, tickets) stat = get_ticket_stats(self.stats_provider, tickets) stats.append(milestone_stats_data(self.env, req, stat, milestone.name)) #milestone['tickets'] = tickets # for the iCalendar view if req.args.get('format') == 'ics': self._render_ics(req, milestones) return # FIXME should use the 'webcal:' scheme, probably username = None if req.authname and req.authname != 'anonymous': username = req.authname icshref = req.href.roadmap(show=show, user=username, format='ics') add_link(req, 'alternate', auth_link(req, icshref), _('iCalendar'), 'text/calendar', 'ics') data = { 'milestones': milestones, 'milestone_stats': stats, 'queries': queries, 'show': show, } add_stylesheet(req, 'common/css/roadmap.css') return 'roadmap.html', data, None
def _render_editor(self, req, milestone): # Suggest a default due time of 18:00 in the user's timezone now = datetime.now(req.tz) default_due = datetime(now.year, now.month, now.day, 18) if now.hour > 18: default_due += timedelta(days=1) default_due = to_datetime(default_due, req.tz) data = { 'milestone': milestone, 'datetime_hint': get_datetime_format_hint(req.lc_time), 'default_due': default_due, 'milestone_groups': [], } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') milestones = [ m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource) ] num_tickets = self.env.db_query( """ SELECT COUNT(*) FROM ticket WHERE milestone=%s""", (milestone.name, ))[0][0] data['milestone_groups'] = group_milestones( milestones, 'TICKET_ADMIN' in req.perm) data['num_tickets'] = num_tickets data['retarget_to'] = self.default_retarget_to else: req.perm(milestone.resource).require('MILESTONE_CREATE') if milestone.name: add_notice( req, _( "Milestone %(name)s does not exist. You can" " create it here.", name=milestone.name)) chrome = Chrome(self.env) chrome.add_jquery_ui(req) chrome.add_wiki_toolbars(req) add_stylesheet(req, 'common/css/roadmap.css') return 'milestone_edit.html', data, None
def _modify_resource_breadcrumb(self, req, template, data, content_type, is_active): """Provides logic for breadcrumb resource permissions """ if data and ('ticket' in data.keys()) and data['ticket'].exists: data['resourcepath_template'] = 'bh_path_ticket.html' # determine path permissions for resname, permname in [('milestone', 'MILESTONE_VIEW'), ('product', 'PRODUCT_VIEW')]: res = Resource(resname, data['ticket'][resname]) data['path_show_' + resname] = permname in req.perm(res) # add milestone list + current milestone to the breadcrumb data['milestone_list'] = [m.name for m in Milestone.select(self.env)] mname = data['ticket']['milestone'] if mname: data['milestone'] = Milestone(self.env, mname)
def task(add): """the thread task - either we are discovering or adding events""" with lock: env = ProductEnvironment(self.global_env, self.default_product) if add: name = 'milestone_from_' + threading.current_thread().name milestone = Milestone(env) milestone.name = name milestone.insert() else: # collect the names of milestones reported by Milestone and # directly from the db - as sets to ease comparison later results.append({ 'from_t': set([m.name for m in Milestone.select(env)]), 'from_db': set( [v[0] for v in self.env.db_query( "SELECT name FROM milestone")])})
def get_data_burndown(self,printer): self.log.debug("get_data_burndown") database = self.env.get_db_cnx() cursor = database.cursor() data = { 'milestones' : [], 'date_hint': get_date_format_hint(), 'printmilestone': printer, } # get all milestones milestones = [m for m in Milestone.select(self.env, True, database)] # prepare data for each milestone p = re.compile('[0-9]+') for milest in milestones: self.log.debug(milest.completed) tickets = _get_tickets_for_milestone(self.env, database, milest.name, 'history_size') self.log.debug(tickets) tasks = _get_tasks_for_milestone(self, database, milest.name) # calculate totalpoints of sprint totalpoints = 0 for idx, ticket in enumerate(tickets): if ticket['history_size'] and p.match(ticket['history_size']): totalpoints += int(ticket['history_size']) # prepare graph data graphdata = {'points': totalpoints, 'tasks': tasks, 'printer': printer} self.log.debug(graphdata) # if totalpoints and total tasks equal the zero not display burndown chart if totalpoints > 0 and len(tasks) > 0: graph_image_path = _get_graphflash_sprintburndown(self,milest.name,graphdata) else: graph_image_path = None # data the all milestones d = {'milestone' : milest, 'tickets' : tickets, 'tasks': tasks, 'graph': graph_image_path, 'teste': totalpoints} data['milestones'].append(d); return data;
def process_admin_request(self, req, cat, page, path_info): envs = DatamoverSystem(self.env).all_environments() milestones = [m.name for m in Milestone.select(self.env)] if req.method == 'POST': source_type = req.args.get('source') if not source_type or source_type not in ('milestone', 'all'): raise TracError, "Source type not specified or invalid" source = req.args.get(source_type) dest = req.args.get('destination') action = None if 'copy' in req.args.keys(): action = 'copy' elif 'move' in req.args.keys(): action = 'move' else: raise TracError, 'Action not specified or invalid' action_verb = {'copy':'Copied', 'move':'Moved'}[action] milestone_filter = None if source_type == 'milestone': in_milestones = req.args.getlist('milestone') milestone_filter = lambda c: c in in_milestones elif source_type == 'all': milestone_filter = lambda c: True try: sel_milestones = [m for m in milestones if milestone_filter(m)] dest_db = _open_environment(dest).get_db_cnx() for milestone in sel_milestones: copy_milestone(self.env, dest, milestone, dest_db) dest_db.commit() if action == 'move': for milestone in sel_milestones: Milestone(self.env, milestone).delete() req.hdf['datamover.message'] = '%s milestones %s'%(action_verb, ', '.join(sel_milestones)) except TracError, e: req.hdf['datamover.message'] = "An error has occured: \n"+str(e) self.log.warn(req.hdf['datamover.message'], exc_info=True)
def task(add): """the thread task - either we are discovering or adding events""" with lock: env = ProductEnvironment(self.global_env, self.default_product) if add: name = 'milestone_from_' + threading.current_thread().name milestone = Milestone(env) milestone.name = name milestone.insert() else: # collect the names of milestones reported by Milestone and # directly from the db - as sets to ease comparison later results.append({ 'from_t': set([m.name for m in Milestone.select(env)]), 'from_db': set([ v[0] for v in self.env.db_query( "SELECT name FROM milestone") ]) })
def _render_confirm(self, req, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') milestones = [ m for m in Milestone.select(self.env) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource) ] num_tickets = self.env.db_query( """ SELECT COUNT(*) FROM ticket WHERE milestone=%s""", (milestone.name, ))[0][0] data = { 'milestone': milestone, 'milestone_groups': group_milestones(milestones, 'TICKET_ADMIN' in req.perm), 'num_tickets': num_tickets, 'retarget_to': self.default_retarget_to } add_stylesheet(req, 'common/css/roadmap.css') return 'milestone_delete.html', data, None
def expand_macro(self, formatter, name, content): args, kwargs = parse_args(content, strict=False) milestones = filter(lambda m: not args or any(map(lambda a: m.name.startswith(a), args)), Milestone.select(self.env)) req = formatter.req template = "listmilestones.html" data = {'milestones' : milestones} content_type = 'text/html' dispatcher = RequestDispatcher(self.env) response = dispatcher._post_process_request(req, template, data, content_type) # API < 1.1.2 does not return a method. if (len(response) == 3): template, data, content_type = response return Markup(Chrome(self.env).render_template(formatter.req, template, data, content_type=content_type, fragment=True)) template, data, content_type, method = response return Markup(Chrome(self.env).render_template(formatter.req, template, data, content_type=content_type, fragment=True, method=method))
def process_request(self, req): name = req.args.get('name') if not (name == 'query'): id = req.args.get('id') if name == 'ticket': ticket = Ticket(self.env, id) comm_num = 0 attachment_num = len(self.get_attachments('ticket', id)) ticket_log = self.changeLog(id) for log in ticket_log: if log[2] == 'comment' and log[4]: comm_num += 1 data = {'ticket': ticket, 'comm_num': comm_num, 'attachment_num': attachment_num} return 'bh_emb_ticket.html', data, None elif name == 'milestone': ticket_num = len(get_tickets_for_milestone(self.env, milestone=id)) attachment_num = len(self.get_attachments('milestone', id)) data = {'milestone': Milestone(self.env, id), 'product': self.env.product, 'ticket_number': ticket_num, 'attachment_number': attachment_num } return 'bh_emb_milestone.html', data, None elif name == 'products': product = Product(self.env, {'prefix': id}) ticket_num = len(self.get_tickets_for_product(self.env, id)) product_env = ProductEnvironment(self.env, product.prefix) milestone_num = len(Milestone.select(product_env)) version_num = len(Version.select(product_env)) components = component.select(product_env) component_num = 0 for c in components: component_num += 1 data = {'product': product, 'ticket_num': ticket_num, 'owner': product.owner, 'milestone_num': milestone_num, 'version_num': version_num, 'component_num': component_num} return 'bh_emb_product.html', data, None elif name == 'query': qstr = req.query_string qstr = urllib.unquote(qstr).decode('utf8') if qstr=='': qstr = 'status!=closed' qresults = self.query(req, qstr) filters = qresults[0] tickets = qresults[1] data={'tickets': tickets, 'query': qstr, 'filters': filters} return 'bh_emb_query.html', data, None else: msg = "It is not possible to embed this resource." raise ResourceNotFound((msg), ("Invalid resource"))
def _get_milestones(self): return Milestone.select(self.env, include_completed=False)
def _sorted_milestones(self): """ Return a sorted list of active milestones. """ milestones = Milestone.select(self.env, include_completed=False) return [m.name for m in milestones]
def _add_milestones (self, req, ul): for milestone in Milestone.select(self.env, False): label = milestone.name href = '/milestone/' + label self._add_item(req, ul, label, href)
def _render_view(self, req, milestone): milestone_groups = [] available_groups = [] component_group_available = False ticket_fields = TicketSystem(self.env).get_ticket_fields() # collect fields that can be used for grouping for field in ticket_fields: if field['type'] == 'select' and field['name'] != 'milestone' \ or field['name'] in ('owner', 'reporter'): available_groups.append({'name': field['name'], 'label': field['label']}) if field['name'] == 'component': component_group_available = True # determine the field currently used for grouping by = None if component_group_available: by = 'component' elif available_groups: by = available_groups[0]['name'] by = req.args.get('by', by) tickets = get_tickets_for_milestone(self.env, milestone=milestone.name, field=by) tickets = apply_ticket_permissions(self.env, req, tickets) stat = get_ticket_stats(self.stats_provider, tickets) context = web_context(req, milestone.resource) data = { 'context': context, 'milestone': milestone, 'attachments': AttachmentModule(self.env).attachment_data(context), 'available_groups': available_groups, 'grouped_by': by, 'groups': milestone_groups } data.update(milestone_stats_data(self.env, req, stat, milestone.name)) if by: def per_group_stats_data(gstat, group_name): return milestone_stats_data(self.env, req, gstat, milestone.name, by, group_name) milestone_groups.extend( grouped_stats_data(self.env, self.stats_provider, tickets, by, per_group_stats_data)) add_stylesheet(req, 'common/css/roadmap.css') add_script(req, 'common/js/folding.js') def add_milestone_link(rel, milestone): href = req.href.milestone(milestone.name, by=req.args.get('by')) add_link(req, rel, href, _('Milestone "%(name)s"', name=milestone.name)) milestones = [m for m in Milestone.select(self.env) if 'MILESTONE_VIEW' in req.perm(m.resource)] idx = [i for i, m in enumerate(milestones) if m.name == milestone.name] if idx: idx = idx[0] if idx > 0: add_milestone_link('first', milestones[0]) add_milestone_link('prev', milestones[idx - 1]) if idx < len(milestones) - 1: add_milestone_link('next', milestones[idx + 1]) add_milestone_link('last', milestones[-1]) prevnext_nav(req, _('Previous Milestone'), _('Next Milestone'), _('Back to Roadmap')) return 'milestone_view.html', data, None
def users(self, req): """hours for all users""" data = { 'hours_format' : hours_format } ### date data self.date_data(req, data) ### milestone data milestone = req.args.get('milestone') milestones = Milestone.select(self.env) data['milestones'] = milestones ### get the hours #trachours = TracHoursPlugin(self.env) #tickets = trachours.tickets_with_hours() hours = get_all_dict(self.env, "SELECT * FROM ticket_time WHERE time_started >= %s AND time_started < %s", *[int(time.mktime(i.timetuple())) for i in (data['from_date'], data['to_date'])]) worker_hours = {} for entry in hours: worker = entry['worker'] if worker not in worker_hours: worker_hours[worker] = 0 if milestone: if milestone != Ticket(self.env, entry['ticket']).values.get('milestone'): continue worker_hours[worker] += entry['seconds_worked'] worker_hours = [(worker, seconds/3600.) for worker, seconds in sorted(worker_hours.items())] data['worker_hours'] = worker_hours if req.args.get('format') == 'csv': buffer = StringIO() writer = csv.writer(buffer) format = '%B %d, %Y' title = "Hours for %s" % self.env.project_name writer.writerow([title, req.abs_href()]) writer.writerow([]) writer.writerow(['From', 'To']) writer.writerow([data[i].strftime(format) for i in 'from_date', 'to_date']) if milestone: writer.writerow(['Milestone', milestone]) writer.writerow([]) writer.writerow(['Worker', 'Hours']) for worker, hours in worker_hours: writer.writerow([worker, hours]) req.send(buffer.getvalue(), "text/csv") #add_link(req, 'prev', self.get_href(query, args, context.href), _('Prev Week')) #add_link(req, 'next', self.get_href(query, args, context.href), _('Next Week')) #prevnext_nav(req, _('Prev Week'), _('Next Week')) return 'hours_users.html', data, "text/html"
def _render_view(self, req, milestone): milestone_groups = [] available_groups = [] default_group_by_available = False ticket_fields = TicketSystem(self.env).get_ticket_fields() # collect fields that can be used for grouping for field in ticket_fields: if field['type'] == 'select' and field['name'] != 'milestone' \ or field['name'] in ('owner', 'reporter'): available_groups.append({ 'name': field['name'], 'label': field['label'] }) if field['name'] == self.default_group_by: default_group_by_available = True # determine the field currently used for grouping by = None if default_group_by_available: by = self.default_group_by elif available_groups: by = available_groups[0]['name'] by = req.args.get('by', by) tickets = get_tickets_for_milestone(self.env, milestone=milestone.name, field=by) tickets = apply_ticket_permissions(self.env, req, tickets) stat = get_ticket_stats(self.stats_provider, tickets) context = web_context(req, milestone.resource) data = { 'context': context, 'milestone': milestone, 'attachments': AttachmentModule(self.env).attachment_data(context), 'available_groups': available_groups, 'grouped_by': by, 'groups': milestone_groups } data.update(milestone_stats_data(self.env, req, stat, milestone.name)) if by: def per_group_stats_data(gstat, group_name): return milestone_stats_data(self.env, req, gstat, milestone.name, by, group_name) milestone_groups.extend( grouped_stats_data(self.env, self.stats_provider, tickets, by, per_group_stats_data)) add_stylesheet(req, 'common/css/roadmap.css') def add_milestone_link(rel, milestone): href = req.href.milestone(milestone.name, by=req.args.get('by')) add_link(req, rel, href, _('Milestone "%(name)s"', name=milestone.name)) milestones = [ m for m in Milestone.select(self.env) if 'MILESTONE_VIEW' in req.perm(m.resource) ] idx = [i for i, m in enumerate(milestones) if m.name == milestone.name] if idx: idx = idx[0] if idx > 0: add_milestone_link('first', milestones[0]) add_milestone_link('prev', milestones[idx - 1]) if idx < len(milestones) - 1: add_milestone_link('next', milestones[idx + 1]) add_milestone_link('last', milestones[-1]) prevnext_nav(req, _("Previous Milestone"), _("Next Milestone"), _("Back to Roadmap")) return 'milestone_view.html', data
def remove_demo_milestones(self, group, env): for milestone in Milestone.select(env): if milestone.name.startswith('milestone'): milestone.delete()
def process_request(self, req): req.perm.assert_permission('BACKLOG_VIEW') ats = AgileToolsSystem(self.env) if req.get_header('X-Requested-With') == 'XMLHttpRequest': if req.method == "POST": if not req.perm.has_permission("BACKLOG_ADMIN"): return self._json_errors(req, ["BACKLOG_ADMIN permission required"]) str_ticket= req.args.get("ticket") str_relative = req.args.get("relative", 0) direction = req.args.get("relative_direction") milestone = req.args.get("milestone") # Moving a single ticket position (and milestone) if str_ticket: try: int_ticket = int(str_ticket) int_relative = int(str_relative) except (TypeError, ValueError): return self._json_errors(req, ["Invalid arguments"]) try: ticket = Ticket(self.env, int_ticket) except ResourceNotFound: return self._json_errors(req, ["Not a valid ticket"]) response = {} # Change ticket's milestone if milestone is not None: try: self._save_ticket(req, ticket, milestone) ticket = self._get_permitted_tickets(req, constraints={'id': [str(int_ticket)]}) response['tickets'] = self._get_ticket_data(req, ticket) except ValueError as e: return self._json_errors(req, e.message) # Reposition ticket if int_relative: position = ats.position(int_relative, generate=True) if direction == "after": position += 1 ats.move(int_ticket, position, author=req.authname) response['success'] = True return self._json_send(req, response) # Dropping multiple tickets into a milestone elif all (k in req.args for k in ("tickets", "milestone", "changetimes")): changetimes = req.args["changetimes"].split(",") milestone = req.args["milestone"] try: ids = [int(tkt_id) for tkt_id in req.args["tickets"].split(",")] except (ValueError, TypeError): return self._json_errors(req, ["Invalid arguments"]) unique_errors = 0 errors_by_ticket = [] # List of [<ticket_id>, [<error>, ...]] lists if len(ids) == len(changetimes): for i, int_ticket in enumerate(ids): # Valid ticket try: ticket = Ticket(self.env, int_ticket) except ResourceNotFound: errors_by_ticket.append([int_ticket, ["Not a valid ticket"]]) # Can be saved try: self._save_ticket(req, ticket, milestone, ts=changetimes[i]) except ValueError as e: # Quirk: all errors amalgomated into single # we keep track of count at each time so that # we can split the list up to errors by # individual tickets errors_by_ticket.append([int_ticket, e.message[unique_errors:]]) unique_errors = len(e.message) if len(errors_by_ticket) > 5: errors_by_ticket.append("More than 5 tickets failed " "validation, stopping.") break if errors_by_ticket: return self._json_errors(req, errors_by_ticket) else: # Client side makes additional request for all # tickets after this return self._json_send(req, {'success': True}) else: return self._json_errors(req, ["Invalid arguments"]) else: return self._json_errors(req, ["Must provide a ticket"]) else: # TODO make client side compatible with live updates milestone = req.args.get("milestone") from_iso = req.args.get("from") to_iso = req.args.get("to") if milestone is not None: # Requesting an update constr = { 'milestone': [milestone] } if from_iso and to_iso: constr['changetime'] = [from_iso + ".." + to_iso] tickets = self._get_permitted_tickets(req, constraints=constr) formatted = self._get_ticket_data(req, tickets) self._json_send(req, {'tickets': formatted}) else: self._json_errors(req, ["Invalid arguments"]) else: add_script(req, 'agiletools/js/jquery.history.js') add_script(req, "agiletools/js/update_model.js") add_script(req, "agiletools/js/backlog.js") add_stylesheet(req, "agiletools/css/backlog.css") milestones_select2 = Milestone.select_names_select2(self.env, include_complete=False) milestones_select2['results'].insert(0, { "children": [], "text": "Product Backlog", "id": "backlog", "is_backlog": True, }) milestones_flat = [milestone.name for milestone in Milestone.select(self.env, include_completed=False, include_children=True)] script_data = { 'milestones': milestones_select2, 'milestonesFlat': milestones_flat, 'backlogAdmin': req.perm.has_permission("BACKLOG_ADMIN"), } add_script_data(req, script_data) data = {'top_level_milestones': Milestone.select(self.env)} # Just post the basic template, with a list of milestones # The JS will then make a request for tickets in no milestone # and tickets in the most imminent milestone # The client will be able to make subsequent requests to pull # tickets from other milestones and drop tickets into them return "backlog.html", data, None
def _render_view(self, req, db): showall = req.args.get('show') == 'all' showmetrics = req.args.get('showmetrics') == 'true' # Get list of milestone object for the project milestones = list(Milestone.select(self.env, showall, db)) stats = [] queries = [] self.env.log.info("getting milestones statistics") for milestone in milestones: tickets = get_tickets_for_milestone(self.env, db, milestone.name, 'owner') stat = get_ticket_stats(self.stats_provider, tickets) stats.append( milestone_stats_data(self.env, req, stat, milestone.name)) project = { 'name': self.env.project_name, 'description': self.env.project_description } data = { 'context': Context.from_request(req), 'milestones': milestones, 'milestone_stats': stats, 'queries': queries, 'showall': showall, 'showmetrics': showmetrics, 'project': project, 'yui_base_url': self.yui_base_url } self.env.log.info("getting project statistics") # Get project progress stats query = Query.from_string(self.env, 'max=0&order=id') tickets = query.execute(req) proj_stat = get_ticket_stats(self.stats_provider, tickets) data['proj_progress_stat'] = { 'stats': proj_stat, 'stats_href': req.href.query(proj_stat.qry_args), 'interval_hrefs': [ req.href.query(interval['qry_args']) for interval in proj_stat.intervals ] } ticket_ids = [t['id'] for t in tickets] closed_stat = self.stats_provider.get_ticket_resolution_group_stats( ticket_ids) data['proj_closed_stat'] = { 'stats': closed_stat, 'stats_href': req.href.query(closed_stat.qry_args), 'interval_hrefs': [ req.href.query(interval['qry_args']) for interval in closed_stat.intervals ] } tkt_frequency_stats = {} tkt_duration_stats = {} bmi_stats = [] daily_backlog_chart = {} today = to_datetime(None) if showmetrics: self.env.log.info("getting ticket metrics") tkt_group_metrics = TicketGroupMetrics(self.env, ticket_ids) tkt_frequency_stats = tkt_group_metrics.get_frequency_metrics_stats( ) tkt_duration_stats = tkt_group_metrics.get_duration_metrics_stats() #stat for this month first_day = datetime(today.year, today.month, 1, tzinfo=utc) last_day = last_day_of_month(today.year, today.month) bmi_stats.append( tkt_group_metrics.get_bmi_monthly_stats(first_day, last_day)) # stat for last month last_day = first_day - timedelta(days=1) first_day = datetime(last_day.year, last_day.month, 1, tzinfo=utc) bmi_stats.append( tkt_group_metrics.get_bmi_monthly_stats(first_day, last_day)) # get daily backlog history last_day = datetime(today.year, today.month, today.day, tzinfo=utc) first_day = last_day - timedelta(days=DAYS_BACK) self.env.log.info("getting backlog history") backlog_history = tkt_group_metrics.get_daily_backlog_history( first_day, last_day) daily_backlog_chart = tkt_group_metrics.get_daily_backlog_chart( backlog_history) # Get dialy commits history last_day = datetime(today.year, today.month, today.day, tzinfo=utc) first_day = last_day - timedelta(days=DAYS_BACK) changeset_group_stats = ChangesetsStats(self.env, first_day, last_day) commits_by_date = changeset_group_stats.get_commit_by_date() commits_by_date_chart = changeset_group_stats.get_commit_by_date_chart( commits_by_date) data['project_bmi_stats'] = bmi_stats #self.env.log.info(bmi_stats) data['ticket_frequency_stats'] = tkt_frequency_stats data['ticket_duration_stats'] = tkt_duration_stats data['ds_daily_backlog'] = daily_backlog_chart data['ds_commit_by_date'] = commits_by_date_chart add_stylesheet(req, 'pd/css/dashboard.css') add_stylesheet(req, 'common/css/report.css') return ('pdashboard.html', data, None)