def test_milestone(self): milestone = Milestone(self.env) milestone.name = 'New target date' milestone.description = 'Lorem ipsum dolor sit amet' milestone.insert() so = self._get_so() self.assertEquals('%s:milestone:New target date' % self.basename, so.doc_id) self.assertEquals('milestone', so.realm), self.assertEquals('New target date', so.id) self.assertTrue('New target date' in so.title) self.assertTrue('Lorem ipsum' in so.title) self.assertTrue('Lorem ipsum' in so.oneline) self.assertTrue('Lorem ipsum' in so.body) milestone.description = 'No latin filler here' milestone.due = datetime(2001, 01, 01, tzinfo=utc) milestone.update() so = self._get_so() self.assertEquals('%s:milestone:New target date' % self.basename, so.doc_id) self.assertEquals('milestone', so.realm), self.assertEquals('New target date', so.id) self.assertEquals(milestone.due, so.changed) self.assertFalse('Lorem ipsum' in so.body) self.assertTrue('No latin filler here' in so.body)
def process_request(self, req): milestone_id = req.args.get('id') req.perm('milestone', milestone_id).require('MILESTONE_VIEW') add_link(req, 'up', req.href.roadmap(), _('Roadmap')) action = req.args.get('action', 'view') try: milestone = Milestone(self.env, milestone_id) except ResourceNotFound: if 'MILESTONE_CREATE' not in req.perm('milestone', milestone_id): raise milestone = Milestone(self.env, None) milestone.name = milestone_id action = 'edit' # rather than 'new' so that it works for POST/save if req.method == 'POST': if req.args.has_key('cancel'): if milestone.exists: req.redirect(req.href.milestone(milestone.name)) else: req.redirect(req.href.roadmap()) elif action == 'edit': return self._do_save(req, milestone) elif action == 'delete': self._do_delete(req, milestone) elif action in ('new', 'edit'): return self._render_editor(req, milestone) elif action == 'delete': return self._render_confirm(req, milestone) if not milestone.name: req.redirect(req.href.roadmap()) return self._render_view(req, milestone)
def change_milestone(self, name_to_change, name=None, description=None): milestone = Milestone(self.env, name_to_change) if name is not None: milestone.name = name if description is not None: milestone.description = description milestone.update() return milestone
def create_milestone(self, name, due=None, duration=20, db=None): """ Creates a milestone with the given name and due date, the latter should be a datetime object """ db, handle_ta = get_db_for_write(self.env, db) # Try to load the milestone first try: m = Milestone(self.env, name=name, db=db) except ResourceNotFound: # than we create it m = Milestone(self.env, db=db) m.name = name if due is not None and isinstance(due, datetime): dueo = due.toordinal() + duration m.due = mktime(datetime.fromordinal(dueo).timetuple()) m.insert() if handle_ta: try: db.commit() # workaround for the fact that trac in 0.11.1 doesn't set exists correctly... m._old_name = m.name except Exception, e: self.env.log.warning(exception_to_unicode(e)) db.rollback()
def _render_milestone_list(self, req): project_id = req.data['project_id'] milestones = Milestone.select(self.env, project_id, include_completed=True) data = { 'milestones': milestones, } return 'mdashboard.html', data, None
def _get_options(self, field_name): """Return a list of options for the given [dynvars] field: [dynvars] myfield.options = value1|value2|value3 If no [dynvars] field is found, a select field is searched and its options returned. For the milestone field, completed milestones are omitted. If no select field is found, then an empty list is returned.""" # look for [dynvars] field for key,val in self.env.config.options('dynvars'): if key == field_name+'.options': return val.split('|') # handle milestone special - skip completed milestones if field_name == 'milestone': return [''] + [m.name for m in Milestone.select(self.env, include_completed=False)] # lookup select field for field in TicketSystem(self.env).get_ticket_fields(): if field['name'] == field_name and 'options' in field: return field['options'] return []
def process_request(self, req): milestone_id = req.args.get('id') req.perm.assert_permission('MILESTONE_VIEW') add_link(req, 'up', req.href.roadmap(), 'Roadmap') db = self.env.get_db_cnx() milestone = Milestone(self.env, milestone_id, db) action = req.args.get('action', 'view') if req.method == 'POST': if req.args.has_key('cancel'): if milestone.exists: req.redirect(req.href.milestone(milestone.name)) else: req.redirect(req.href.roadmap()) elif action == 'edit': self._do_save(req, db, milestone) elif action == 'delete': self._do_delete(req, db, milestone) elif action in ('new', 'edit'): self._render_editor(req, db, milestone) elif action == 'delete': self._render_confirm(req, db, milestone) else: self._render_view(req, db, milestone) if not milestone_id and action != 'new': req.redirect(req.href.roadmap()) add_stylesheet(req, 'common/css/roadmap.css') return 'milestone.cs', None
def _render_editor(self, req, db, milestone): # Suggest a default due time of 18:00 in the user's timezone default_due = datetime.now(req.tz).replace(hour=18, minute=0, second=0, microsecond=0) if default_due <= datetime.now(utc): default_due += timedelta(days=1) data = { 'milestone': milestone, 'datetime_hint': get_datetime_format_hint(), '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, db=db) 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(self.env).add_wiki_toolbars(req) return 'milestone_edit.html', data, None
def _get_options(self, field_name): """Return a list of options for the given [dynvars] field: [dynvars] myfield.options = value1|value2|value3 If no [dynvars] field is found, a select field is searched and its options returned. For the milestone field, completed milestones are omitted. If no select field is found, then an empty list is returned.""" # look for [dynvars] field for key, val in self.env.config.options('dynvars'): if key == field_name + '.options': return val.split('|') # handle milestone special - skip completed milestones if field_name == 'milestone': return [''] + [ m.name for m in Milestone.select(self.env, include_completed=False) ] # lookup select field for field in TicketSystem(self.env).get_ticket_fields(): if field['name'] == field_name and 'options' in field: return field['options'] return []
def test_remove_milestone(self): test_name = sys._getframe().f_code.co_name expected = self.expected_results[test_name] ticket = Ticket(self.env) ticket.populate({'reporter': 'santa', 'summary': 'Summary line', 'description': 'Lorem ipsum dolor sit amet', }) ticket.insert() milestone = Milestone(self.env) milestone.name = 'New target date' milestone.description = 'Lorem ipsum dolor sit amet' milestone.insert() self.assertEqual(2, len(self._get_docs())) rv, output = self._execute('fulltext remove milestone') self.assertEqual(expected, output) self.assertEqual(1, len(self._get_docs()))
def _render_editor(self, req, milestone): # Suggest a default due time of 18:00 in the user's timezone default_due = datetime.now(req.tz).replace(hour=18, minute=0, second=0, microsecond=0) if default_due <= datetime.now(utc): default_due += timedelta(days=1) 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 process(self, commit, status, branch): self.closestatus = status milestones = [ m.name for m in Milestone.select(self.env) if m.name != 'unknown' ] if branch.startswith('fixes/'): branch = branch[6:] milestones = [m for m in milestones if m.startswith(branch)] self.milestone = sorted(milestones)[-1] msg = commit['message'] self.env.log.debug("Processing Commit: %s", msg) msg = "%s \n Branch: %s \n Changeset: %s" % (msg, branch, commit['id']) # author = commit['author']['name'] author = 'Github' timestamp = datetime.now(utc) cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): if (branch == "master") or branch.startswith("fixes/"): tickets.setdefault(tkt_id, []).append(getattr(self, funcname)) # disable this stuff for now, it causes duplicates on merges # proper implementation of this will require tracking commit hashes # else: # tickets.setdefault(tkt_id, []).append(self._cmdRefs) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def _create_milestone(self, name, duedate, description): m = Milestone(self.env) m.name = name m.description = description if duedate: if isinstance(duedate, type("")): m.due = parse_date(duedate) else: m.due = duedate m.insert()
def testMilestoneRenamePropagatesToSprints(self): """Tests that the rename of a Milestone, propagates to the Sprints, this is an AgiloMilestone feature""" m = Milestone(self.env) m.name = 'test_me' m.insert() s = self.teh.create_sprint('my sprint', milestone=m.name) self.assert_equals(m.name, s.milestone) # AT: we need to reload the milestone as there is a problem in trac, # that the insert is not updating the _old_name, making the update # silently fail. I sent a patch for this m = Milestone(self.env, m.name) m.name = 'test_me_not' m.update() smm = SprintModelManager(self.env) smm.get_cache().invalidate() s = smm.get(name=s.name) self.assert_equals(m.name, s.milestone)
def _find_milestones(self): """Return a string list of Milestone names A smarter approach would return a dictionary of milestones: key = name value = tuple (name, due, completed, description) """ return ["%s" % m.name for m in Milestone.select(self.env)]
def _find_milestones(self): """Return a string list of Milestone names A smarter approach would return a dictionary of milestones: key = name value = tuple (name, due, completed, description) """ return [ "%s" % m.name for m in Milestone.select(self.env) ]
def process(self, commit, status, branch): self.closestatus = status milestones = [m.name for m in Milestone.select(self.env) if m.name != "unknown"] if branch.startswith("fixes/"): branch = branch[6:] milestones = [m for m in milestones if m.startswith(branch)] self.milestone = sorted(milestones)[-1] msg = commit["message"] self.env.log.debug("Processing Commit: %s", msg) msg = "%s \n Branch: %s \n Changeset: %s" % (msg, branch, commit["id"]) # author = commit['author']['name'] author = "Github" timestamp = datetime.now(utc) cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), "") self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): if (branch == "master") or branch.startswith("fixes/"): tickets.setdefault(tkt_id, []).append(getattr(self, funcname)) # disable this stuff for now, it causes duplicates on merges # proper implementation of this will require tracking commit hashes # else: # tickets.setdefault(tkt_id, []).append(self._cmdRefs) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change["permanent"]: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def _render_confirm(self, req, db, milestone): req.perm.assert_permission('MILESTONE_DELETE') req.hdf['title'] = 'Jalon %s' % milestone.name req.hdf['milestone'] = milestone_to_hdf(self.env, db, req, milestone) req.hdf['milestone.mode'] = 'delete' for idx,other in enumerate(Milestone.select(self.env, False, db)): if other.name == milestone.name: continue req.hdf['milestones.%d' % idx] = other.name
def test_can_index_delete(self): #arrange self.insert_milestone(self.DUMMY_MILESTONE_NAME) results = self.search_api.query("*") self.assertEqual(1, results.hits) #act Milestone(self.env, self.DUMMY_MILESTONE_NAME).delete() #assert results = self.search_api.query("*") self.print_result(results) self.assertEqual(0, results.hits)
def _render_confirm(self, req, db, milestone): req.perm.assert_permission('MILESTONE_DELETE') req.hdf['title'] = 'Milestone %s' % milestone.name req.hdf['milestone'] = milestone_to_hdf(self.env, db, req, milestone) req.hdf['milestone.mode'] = 'delete' for idx, other in enumerate(Milestone.select(self.env, False, db)): if other.name == milestone.name: continue req.hdf['milestones.%d' % idx] = other.name
def _get_milestone(self, req): """Extract the milestone from the referer url. If not found then return the current milestone.""" path = req.environ.get('HTTP_REFERER','') milestone_re = re.compile(r"/milestone/(?P<milestone>[^?]+)") match = milestone_re.search(path) if match: name = urllib.unquote(match.groupdict()['milestone']) for m in Milestone.select(self.env, include_completed=True): if m.name == name: return m else: raise Exception("Milestone %s not found" % name) else: # milestone not found in url, so assume current milestone for m in Milestone.select(self.env, include_completed=False): return m else: raise Exception("No provided or current milestone") return None
def _get_milestone(self, req): """Extract the milestone from the referer url. If not found then return the current milestone.""" path = req.environ.get('HTTP_REFERER', '') milestone_re = re.compile(r"/milestone/(?P<milestone>[^?]+)") match = milestone_re.search(path) if match: name = urllib.unquote(match.groupdict()['milestone']) for m in Milestone.select(self.env, include_completed=True): if m.name == name: return m else: raise Exception("Milestone %s not found" % name) else: # milestone not found in url, so assume current milestone for m in Milestone.select(self.env, include_completed=False): return m else: raise Exception("No provided or current milestone") return 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 process_request(self, req): req.perm.assert_permission('TICKET_CREATE') db = self.env.get_db_cnx() if req.method == 'POST' and not req.args.has_key('preview'): self._do_create(req, db) ticket = Ticket(self.env, db=db) ticket.populate(req.args) ticket.values.setdefault('reporter', util.get_reporter_id(req)) if ticket.values.has_key('description'): description = wiki_to_html(ticket['description'], self.env, req, db) req.hdf['newticket.description_preview'] = description req.hdf['title'] = 'New Ticket' req.hdf['newticket'] = dict(zip(ticket.values.keys(), [util.escape(value) for value in ticket.values.values()])) field_names = [field['name'] for field in ticket.fields if not field.get('custom')] if 'owner' in field_names: curr_idx = field_names.index('owner') if 'cc' in field_names: insert_idx = field_names.index('cc') else: insert_idx = len(field_names) if curr_idx < insert_idx: ticket.fields.insert(insert_idx, ticket.fields[curr_idx]) del ticket.fields[curr_idx] for field in ticket.fields: name = field['name'] del field['name'] if name in ('summary', 'reporter', 'description', 'type', 'status', 'resolution'): field['skip'] = True elif name == 'owner': field['label'] = 'Assign to' elif name == 'milestone': # Don't make completed milestones available for selection options = field['options'][:] for option in field['options']: milestone = Milestone(self.env, option, db=db) if milestone.is_completed: options.remove(option) field['options'] = [util.escape(option) for option in options] req.hdf['newticket.fields.' + name] = field add_stylesheet(req, 'common/css/ticket.css') return 'newticket.cs', None
def _render_confirm(self, req, db, milestone): req.perm(milestone.resource).require('MILESTONE_DELETE') milestones = [m for m in Milestone.select(self.env, db=db) 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 test_can_save_due_date(self): # Added when Trac 0.12 was released as the format of the timestamp has changed in 0.12 milestone = Milestone(self.env) milestone.name = 'fnord' milestone.insert() # in trac 0.11.1, milestone._old_name is not set to "fnord" at # insert() time, only on init() or update() # so we need to reload the object to be able to run update() on it milestone = Milestone(self.env, name='fnord') expected_time = now().replace(microsecond=0) milestone.due = expected_time milestone.completed = expected_time milestone.update() loaded_milestone = Milestone(self.env, name='fnord') self.assert_equals(expected_time, loaded_milestone.due) self.assert_equals(expected_time, loaded_milestone.completed)
def execute(hdf, txt, env): out = StringIO() out.write('<ul>\n') for milestone in Milestone.select(env, include_completed=False): if milestone.due > 0: date = format_date(milestone.due) else: date = Markup('<i>(later)</i>') out.write(Markup('<li>%s - <a href="%s">%s</a></li>\n', date, env.href.milestone(milestone.name), milestone.name)) out.write('</ul>\n') return out.getvalue()
def execute(hdf, txt, env): out = StringIO() out.write('<ul>\n') for milestone in Milestone.select(env, include_completed=False): if milestone.due > 0: date = format_date(milestone.due) else: date = Markup('<i>(later)</i>') out.write( Markup('<li>%s - <a href="%s">%s</a></li>\n', date, env.href.milestone(milestone.name), milestone.name)) out.write('</ul>\n') return out.getvalue()
def testMilestoneRenamePropagatesToTickets(self): """Tests that the Milestone renaming is propagated to the tickets, this should work out of the box, as it is a Trac feature""" m = Milestone(self.env) m.name = 'test_me' m.insert() t = self.teh.create_ticket(Type.REQUIREMENT, {Key.MILESTONE: m.name}) self.assert_equals(m.name, t[Key.MILESTONE]) # AT: we need to reload the milestone as there is a problem in trac, # that the insert is not updating the _old_name, making the update # silently fail. I sent a patch for this m = Milestone(self.env, m.name) m.name = 'test_me_not' m.update() # test the changes happened in the DB db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT id, milestone FROM ticket WHERE milestone='test_me'") for row in cursor: self.fail("Found one old milestone in ticket: #%s (%s)" % \ (row[0], row[1])) t = self.teh.load_ticket(t) self.assert_equals(m.name, t[Key.MILESTONE])
def process_request(self, req): req.perm.require('MILESTONE_VIEW') show = req.args.getlist('show') if 'all' in show: show = ['completed'] db = self.env.get_db_cnx() milestones = Milestone.select(self.env, 'completed' in show, db) 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, db, milestone.name, '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, db, 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', 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_view(self, req, db, version): db = self.env.get_db_cnx() sql = "SELECT name FROM milestone " \ "INNER JOIN milestone_version ON (name = milestone) " \ "WHERE version = %s " \ "ORDER BY due" cursor = db.cursor() cursor.execute(sql, (version.name,)) milestones = [] tickets = [] milestone_stats = [] for row in cursor: milestone = Milestone(self.env, row[0]) milestones.append(milestone) mtickets = get_tickets_for_milestone(self.env, db, milestone.name, 'owner') mtickets = apply_ticket_permissions(self.env, req, mtickets) tickets += mtickets stat = get_ticket_stats(self.milestone_stats_provider, mtickets) milestone_stats.append(milestone_stats_data(self.env, req, stat, milestone.name)) stats = get_ticket_stats(self.version_stats_provider, tickets) interval_hrefs = version_interval_hrefs(self.env, req, stats, [milestone.name for milestone in milestones]) version.resource = Resource('version', version.name) context = Context.from_request(req, version.resource) version.is_released = version.time and version.time.date() < date.today() version.stats = stats version.interval_hrefs = interval_hrefs version.stats_href = [] # Not implemented yet, see th:#10349 data = { 'context': context, 'version': version, 'attachments': AttachmentModule(self.env).attachment_data(context), 'milestones': milestones, 'milestone_stats': milestone_stats, 'show_milestone_description': self.show_milestone_description # Not implemented yet } add_stylesheet(req, 'extendedversion/css/version.css') add_script(req, 'common/js/folding.js') add_ctxtnav(req, _("Back to Versions"), req.href.versions()) return 'version_view.html', data, None
def expand_macro(self, formatter, name, content): arg, kwarg = parse_args(content) includepattern = kwarg.get('include', '') #excludepattern = kwarg.get('exclude', '') length = int(kwarg.get('max', -1)) ignorenoduedate = kwarg.get('ignore') == 'noduedate' or None if length == -1: length = None out = StringIO() include = re.compile(includepattern) #exclude = re.compile(excludepattern) milestones = [] for milestone in Milestone.select(self.env, include_completed=False): if include.match( milestone.name): # and not exclude.match(milestone.name): milestones.append(milestone) out.write('<ul>\n') for milestone in milestones[0:length]: if milestone.due: #TODO: add one day to tdelta tdelta = (to_timestamp(milestone.due) - to_timestamp(datetime.now(formatter.req.tz))) if tdelta > 0: date = format_date(milestone.due, '%Y-%m-%d', formatter.req.tz) else: date = None elif not ignorenoduedate: date = Markup('<i>(Unspecified)</i>') else: date = None if date: out.write('<li>%s - <a href="%s">%s</a></li>\n' % (date, self.env.href.milestone( milestone.name), milestone.name)) out.write('</ul>\n') return Markup(out.getvalue())
def process_request(self, req): req.perm.require('MILESTONE_VIEW') show = req.args.getlist('show') if 'all' in show: show = ['completed'] db = self.env.get_db_cnx() milestones = Milestone.select(self.env, 'completed' in show, db) 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, db, milestone.name, '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, db, 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', 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 expand_macro(self, formatter, name, content): arg,kwarg = parse_args(content) includepattern = kwarg.get('include', '') #excludepattern = kwarg.get('exclude', '') length = int(kwarg.get('max', -1)) ignorenoduedate = kwarg.get('ignore') == 'noduedate' or None if length==-1: length = None out = StringIO() include = re.compile(includepattern) #exclude = re.compile(excludepattern) milestones = [] for milestone in Milestone.select(self.env, include_completed=False): if include.match(milestone.name): # and not exclude.match(milestone.name): milestones.append(milestone) out.write('<ul>\n') for milestone in milestones[0:length]: if milestone.due: #TODO: add one day to tdelta tdelta = (to_timestamp(milestone.due) - to_timestamp(datetime.now(formatter.req.tz))) if tdelta > 0: date = format_date(milestone.due, '%Y-%m-%d', formatter.req.tz) else: date = None elif not ignorenoduedate: date = Markup('<i>(Unspecified)</i>') else: date = None if date: out.write('<li>%s - <a href="%s">%s</a></li>\n' % (date, self.env.href.milestone(milestone.name), milestone.name)) out.write('</ul>\n') return Markup(out.getvalue())
def _render_link(self, context, name, label, extra=''): try: milestone = Milestone(self.env, name) except TracError: milestone = None # Note: the above should really not be needed, `Milestone.exists` # should simply be false if the milestone doesn't exist in the db # (related to #4130) href = context.href.milestone(name) if milestone and milestone.exists: if 'MILESTONE_VIEW' in context.perm(milestone.resource): closed = 'closed ' if milestone.is_completed else '' return tag.a(label, class_='%smilestone' % closed, href=href + extra) elif 'MILESTONE_CREATE' in context.perm('milestone', name): return tag.a(label, class_='missing milestone', href=href + extra, rel='nofollow') return tag.a(label, class_='missing milestone')
def process_request(self, req, chrome, projects): milestones = [] stats = [] for project in projects: env = project["env"] for m in Milestone.select(env, False, env.get_db_cnx()): req.href = env.href milestone = milestone_to_hdf(env, env.get_db_cnx(), req, m) milestone['project'] = project milestones.append(milestone) milestones.sort(milestone_cmp) idx = 0 for m in milestones: project = m['project'] env = project['env'] milestone_name = unescape(m['name']) # Kludge prefix = 'roadmap.milestones.%d.' % idx tickets = get_tickets_for_milestone(env, env.get_db_cnx(), milestone_name, 'owner') stat = calc_ticket_stats(tickets) stats.append({'stats': stat}) m['queries'] = {} for k, v in get_query_links(env, milestone_name).items(): m['queries'][k] = v m['tickets'] = tickets # for the iCalendar view idx += 1 # make milestone names unique m["name"] = project["name"] + " " + m["name"] data = { 'milestones': milestones, 'milestone_stats': stats } data["chrome.nav.mainnav.roadmap.active"] = 1 data['title'] = _('Roadmap') template_dir = os.path.dirname(__file__) + '/templates/' chrome.populate_data(req, data) output = chrome.render_template(req, template_dir + "roadmap.html", data) req.session.save() req.send(output, 'text/html')
def process_admin_request(self, req, cat, page, path_info): req.perm.assert_permission('RT_ADMIN') update = "" for key in req.args.keys(): if key.startswith("update_"): update = key[len("update_"):] break if update: # update now base_path = self.env.config.get('rtadmin', 'base_path') run_burndown = "python " + os.path.join( base_path, "run_burndown.py %s" % update.encode("utf-8")) os.system(run_burndown) elif req.args.get('save') and req.args.get('sel'): # empty table first RT_Template.deleteAll(self.env) # insert selected milestone into table sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] db = self.env.get_db_cnx() for milestone in sel: RT_Template.insert(self.env, milestone) db.commit() req.redirect(self.env.href.admin(cat, page)) # get all enabled milestones enabledMilestones = RT_Template.getMilestones(self.env) ms = Milestone.select(self.env) ms.sort(cmp=lambda x, y: cmp(x.name, y.name)) req.hdf['milestones'] = [{ 'name': m.name, 'href': self.env.href.admin(cat, page, m.name), 'enabled': m.name in enabledMilestones, 'update': stripMilestoneName(m.name), } for m in ms] return 'admin_relaticket.cs', None
def _render_editor(self, req, db, milestone): if milestone.exists: req.perm.assert_permission('MILESTONE_MODIFY') req.hdf['title'] = u'Jalon %s' % milestone.name req.hdf['milestone.mode'] = 'edit' req.hdf['milestones'] = [m.name for m in Milestone.select(self.env) if m.name != milestone.name] else: req.perm.assert_permission('MILESTONE_CREATE') req.hdf['title'] = u'Nouveau jalon' req.hdf['milestone.mode'] = 'new' from trac.util.datefmt import get_date_format_hint, \ get_datetime_format_hint req.hdf['milestone'] = milestone_to_hdf(self.env, db, req, milestone) req.hdf['milestone.date_hint'] = get_date_format_hint() req.hdf['milestone.datetime_hint'] = get_datetime_format_hint() req.hdf['milestone.datetime_now'] = format_datetime()
def _render_editor(self, req, db, milestone): data = { 'milestone': milestone, 'date_hint': get_date_format_hint(), 'datetime_hint': get_datetime_format_hint(), 'milestone_groups': [], } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') milestones = [m for m in Milestone.select(self.env, db=db) 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') return 'milestone_edit.html', data, None
def process_admin_request(self, req, cat, page, path_info): req.perm.assert_permission('RT_ADMIN') update = "" for key in req.args.keys(): if key.startswith("update_"): update = key[len("update_"):] break if update: # update now base_path = self.env.config.get('rtadmin', 'base_path') run_burndown = "python " + os.path.join(base_path, "run_burndown.py %s" % update.encode("utf-8")) os.system(run_burndown) elif req.args.get('save') and req.args.get('sel'): # empty table first RT_Template.deleteAll(self.env) # insert selected milestone into table sel = req.args.get('sel') sel = isinstance(sel, list) and sel or [sel] db = self.env.get_db_cnx() for milestone in sel: RT_Template.insert(self.env, milestone) db.commit() req.redirect(self.env.href.admin(cat, page)) # get all enabled milestones enabledMilestones = RT_Template.getMilestones(self.env) ms = Milestone.select(self.env) ms.sort(cmp=lambda x,y: cmp(x.name, y.name)) req.hdf['milestones'] = [{'name': m.name, 'href': self.env.href.admin(cat, page, m.name), 'enabled': m.name in enabledMilestones, 'update': stripMilestoneName(m.name), } for m in ms] return 'admin_relaticket.cs', None
def _render_editor(self, req, db, milestone): if milestone.exists: req.perm.assert_permission('MILESTONE_MODIFY') req.hdf['title'] = 'Milestone %s' % milestone.name req.hdf['milestone.mode'] = 'edit' req.hdf['milestones'] = [ m.name for m in Milestone.select(self.env) if m.name != milestone.name ] else: req.perm.assert_permission('MILESTONE_CREATE') req.hdf['title'] = 'New Milestone' req.hdf['milestone.mode'] = 'new' from trac.util.datefmt import get_date_format_hint, \ get_datetime_format_hint req.hdf['milestone'] = milestone_to_hdf(self.env, db, req, milestone) req.hdf['milestone.date_hint'] = get_date_format_hint() req.hdf['milestone.datetime_hint'] = get_datetime_format_hint() req.hdf['milestone.datetime_now'] = format_datetime()
def process_request(self, req): req.perm.assert_permission('ROADMAP_VIEW') req.hdf['title'] = 'Roadmap' showall = req.args.get('show') == 'all' req.hdf['roadmap.showall'] = showall db = self.env.get_db_cnx() milestones = [ milestone_to_hdf(self.env, db, req, m) for m in Milestone.select(self.env, showall, db) ] req.hdf['roadmap.milestones'] = milestones for idx, milestone in enumerate(milestones): milestone_name = unescape(milestone['name']) # Kludge prefix = 'roadmap.milestones.%d.' % idx tickets = get_tickets_for_milestone(self.env, db, milestone_name, 'owner') req.hdf[prefix + 'stats'] = calc_ticket_stats(tickets) for k, v in get_query_links(req, milestone_name).items(): req.hdf[prefix + 'queries.' + k] = v milestone['tickets'] = tickets # for the iCalendar view if req.args.get('format') == 'ics': self.render_ics(req, db, milestones) return add_stylesheet(req, 'common/css/roadmap.css') # FIXME should use the 'webcal:' scheme, probably username = None if req.authname and req.authname != 'anonymous': username = req.authname icshref = req.href.roadmap(show=req.args.get('show'), user=username, format='ics') add_link(req, 'alternate', icshref, 'iCalendar', 'text/calendar', 'ics') return 'roadmap.cs', None
def _get_start_date(self, milestone): """Returns the start date for the given milestone based on the prior milestone as follows - this milestone's start date = * the day after the prior milestone's completed date (if completed) * else the day after the prior milestone's due date (if due) * else today """ prior = None for m in Milestone.select(self.env, include_completed=True): if m.name == milestone.name: break prior = m else: raise Exception("Milestone %s not found" % milestone.name) if prior: if prior.completed: return self._get_day(prior.completed, 'next') if prior.due: return self._get_day(prior.due, 'next') return self._get_day(datetime.utcnow(), 'end')
def process_request(self, req): req.perm.assert_permission('ROADMAP_VIEW') req.hdf['title'] = u'Feuille de route' showall = req.args.get('show') == 'all' req.hdf['roadmap.showall'] = showall db = self.env.get_db_cnx() milestones = [milestone_to_hdf(self.env, db, req, m) for m in Milestone.select(self.env, showall, db)] req.hdf['roadmap.milestones'] = milestones for idx, milestone in enumerate(milestones): milestone_name = unescape(milestone['name']) # Kludge prefix = 'roadmap.milestones.%d.' % idx tickets = get_tickets_for_milestone(self.env, db, milestone_name, 'owner') req.hdf[prefix + 'stats'] = calc_ticket_stats(tickets) for k, v in get_query_links(req, milestone_name).items(): req.hdf[prefix + 'queries.' + k] = v milestone['tickets'] = tickets # for the iCalendar view if req.args.get('format') == 'ics': self.render_ics(req, db, milestones) return add_stylesheet(req, 'common/css/roadmap.css') # FIXME should use the 'webcal:' scheme, probably username = None if req.authname and req.authname != 'anonymous': username = req.authname icshref = req.href.roadmap(show=req.args.get('show'), user=username, format='ics') add_link(req, 'alternate', icshref, 'iCalendar', 'text/calendar', 'ics') return 'roadmap.cs', None
def test_remove_milestone(self): test_name = sys._getframe().f_code.co_name expected = self.expected_results[test_name] ticket = Ticket(self.env) ticket.populate({ 'reporter': 'santa', 'summary': 'Summary line', 'description': 'Lorem ipsum dolor sit amet', }) ticket.insert() milestone = Milestone(self.env) milestone.name = 'New target date' milestone.description = 'Lorem ipsum dolor sit amet' milestone.insert() self.assertEqual(2, len(self._get_docs())) rv, output = self._execute('fulltext remove milestone') self.assertEqual(expected, output) self.assertEqual(1, len(self._get_docs()))
def process_request(self, req): if req.path_info == '/flashgantt/chartxml': req.perm.require('MILESTONE_VIEW') showall = req.args.get('show') == 'all' # Get the current date and time cur_dt = datetime.date.today() # Get the quarter I am in. cur_quarter = self._get_quarter(cur_dt.month) prev_quarter = self._get_prev_quarter(cur_quarter) next_quarter = self._get_next_quarter(cur_quarter) (cq_sd, cq_ed) = self._get_quarter_start_end_dates(cur_dt.year, cur_quarter) (pq_sd, pq_ed) = self._get_prev_quarters_start_end_dates(cur_dt.year, cur_quarter) (nq_sd, nq_ed) = self._get_next_quarters_start_end_dates(cur_dt.year, cur_quarter) min_date = pq_sd.strftime('%d/%m/%Y') max_date = nq_ed.strftime('%d/%m/%Y') quarters = [] quarters.append({'name': 'Q' + str(prev_quarter) + ' ' + str(pq_sd.year), 'start_date': pq_sd.strftime('%d/%m/%Y'), 'end_date': pq_ed.strftime('%d/%m/%Y')}) quarters.append({'name': 'Q' + str(cur_quarter) + ' ' + str(cq_sd.year), 'start_date': cq_sd.strftime('%d/%m/%Y'), 'end_date': cq_ed.strftime('%d/%m/%Y')}) quarters.append({'name': 'Q' + str(next_quarter) + ' ' + str(nq_sd.year), 'start_date': nq_sd.strftime('%d/%m/%Y'), 'end_date': nq_ed.strftime('%d/%m/%Y')}) months = [] for x in range(pq_sd.month, (pq_ed.month + 1)): (cm_sd, cm_ed) = self._get_month_start_end_dates(pq_sd.year, x) months.append({'name': self._get_month_name(x), 'start_date': cm_sd.strftime('%d/%m/%Y'), 'end_date': cm_ed.strftime('%d/%m/%Y')}) for x in range(cq_sd.month, (cq_ed.month + 1)): (cm_sd, cm_ed) = self._get_month_start_end_dates(cq_sd.year, x) months.append({'name': self._get_month_name(x), 'start_date': cm_sd.strftime('%d/%m/%Y'), 'end_date': cm_ed.strftime('%d/%m/%Y')}) for x in range(nq_sd.month, (nq_ed.month + 1)): (cm_sd, cm_ed) = self._get_month_start_end_dates(nq_sd.year, x) months.append({'name': self._get_month_name(x), 'start_date': cm_sd.strftime('%d/%m/%Y'), 'end_date': cm_ed.strftime('%d/%m/%Y')}) milestones = [] db = self.env.get_db_cnx() ms = [m for m in Milestone.select(self.env, showall, db) if 'MILESTONE_VIEW' in req.perm(m.resource)] cnt = 0 for m in ms: cnt = cnt + 1 comp_stat = 0 if (m.is_completed): comp_stat = 1 milestones.append({'name': m.name, 'id': str(cnt), 'start_date': pq_sd.strftime('%d/%m/%Y'), 'due_date': format_date(m.due, format='%d/%m/%Y'), 'completed_date': format_date(m.completed, format='%d/%m/%Y'), 'completed': comp_stat}) data = {'milestones': milestones, 'showall': showall, 'visible_months': months, 'quarters': quarters} # This tuple is for Genshi (template_name, data, content_type) # Without data the trac layout will not appear. return ('chart.xml', data, 'text/xml') else: req.perm.require('MILESTONE_VIEW') showall = req.args.get('show') == 'all' db = self.env.get_db_cnx() milestones = [m for m in Milestone.select(self.env, showall, db) if 'MILESTONE_VIEW' in req.perm(m.resource)] chart_height = 150 + (len(milestones) * 29) if (showall): xmlcharturl = req.href.flashgantt('/chartxml?show=all') else: xmlcharturl = req.href.flashgantt('/chartxml') data = {'milestones': milestones, 'showall': showall, 'xmlcharturl': xmlcharturl, 'chart_height': chart_height} #add_stylesheet(req, 'fg/css/flashgantt.css') # This tuple is for Genshi (template_name, data, content_type) # Without data the trac layout will not appear. return ('flashgantt.html', data, None)
def getMilestones(self, component, req ): milestones = [] for m in Milestone.select(component.env) : milestones.append(m.name) return milestones
def create_milestone(self, name, description = None): milestone = Milestone(self.env) milestone.name = name if description is not None: milestone.description = description return milestone
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 _do_save(self, req, milestone): if milestone.exists: req.perm(milestone.resource).require('MILESTONE_MODIFY') else: req.perm(milestone.resource).require('MILESTONE_CREATE') old_name = milestone.name new_name = req.args.get('name') milestone.description = req.args.get('description', '') if 'due' in req.args: due = req.args.get('duedate', '') milestone.due = user_time(req, parse_date, due, hint='datetime') \ if due else None else: milestone.due = None completed = req.args.get('completeddate', '') retarget_to = req.args.get('target') # Instead of raising one single error, check all the constraints and # let the user fix them by going back to edit mode showing the warnings warnings = [] def warn(msg): add_warning(req, msg) warnings.append(msg) # -- check the name # If the name has changed, check that the milestone doesn't already # exist # FIXME: the whole .exists business needs to be clarified # (#4130) and should behave like a WikiPage does in # this respect. try: new_milestone = Milestone(self.env, new_name) if new_milestone.name == old_name: pass # Creation or no name change elif new_milestone.name: warn( _( 'Milestone "%(name)s" already exists, please ' 'choose another name.', name=new_milestone.name)) else: warn(_('You must provide a name for the milestone.')) except ResourceNotFound: milestone.name = new_name # -- check completed date if 'completed' in req.args: completed = user_time(req, parse_date, completed, hint='datetime') if completed else None if completed and completed > datetime.now(utc): warn(_('Completion date may not be in the future')) else: completed = None milestone.completed = completed if warnings: return self._render_editor(req, milestone) # -- actually save changes if milestone.exists: milestone.update() # eventually retarget opened tickets associated with the milestone if 'retarget' in req.args and completed: self.env.db_transaction( """ UPDATE ticket SET milestone=%s WHERE milestone=%s and status != 'closed' """, (retarget_to, old_name)) self.log.info("Tickets associated with milestone %s " "retargeted to %s" % (old_name, retarget_to)) else: milestone.insert() add_notice(req, _("Your changes have been saved.")) req.redirect(req.href.milestone(milestone.name))
def get_entries_for_index(self): for milestone in Milestone.select(self.env, include_completed=True): yield self.build_doc(milestone)