def setUp(self): self.super() self.start = normalize_date(now()) duration = timedelta(days=20) self.end = normalize_date(self.start + duration) self.manager = SprintModelManager(self.env) self.tmm = TeamMemberModelManager(self.env)
def create_emtpy_sprint(env): assert sprint_name != None, 'Please configure a sprint name.' sprint = SprintModelManager(env).create(name=sprint_name, start=now(), duration=14) assert sprint is not None t_manager = AgiloTicketModelManager(env) for t_id, t_type, t_status in sprint._fetch_tickets(): ticket = t_manager.create(tkt_id=t_id, t_type=t_type) if ticket[Key.SPRINT] == sprint.name: ticket[Key.SPRINT] = None t_manager.save(ticket, None, 'reset sprint field for performance measurement')
def _ensure_milestone_fits_to_sprint(self, ticket, sprint_name): milestone_name = self._milestone_name_from_ticket(ticket) pulled_task_into_milestone = milestone_name is not None and Key.MILESTONE in ticket._old moved_task_between_milestones = (Key.SPRINT in ticket._old and ticket._old[Key.SPRINT] != sprint_name) milestone_did_change = pulled_task_into_milestone or moved_task_between_milestones if not milestone_did_change: return # Now checks if the milestone is compatible with the current set sprint # otherwise reset the sprint value sp_manager = SprintModelManager(self.env) sprints = [s.name for s in sp_manager.select(criteria={'milestone': milestone_name})] if not ticket[Key.SPRINT] in sprints: # The milestone is changed, and the sprint not, probably is an high #level re-planning, so reset the Sprint. ticket[Key.SPRINT] = None
class SprintModule(Component): """Module to handle Sprint objects""" implements(ITemplateProvider, ITimelineEventProvider, IPermissionRequestor) def __init__(self, *args, **kwargs): """Sets a SprintModelManager""" super(SprintModule, self).__init__(*args, **kwargs) self.sp_manager = SprintModelManager(self.env) self.tm_manager = TeamModelManager(self.env) #============================================================================= # IPermissionRequestor methods #============================================================================= def get_permission_actions(self): actions = [Action.SPRINT_VIEW, Action.SPRINT_EDIT] return actions + [(Action.SPRINT_ADMIN, actions)] #============================================================================= # ITemplateProvider methods #============================================================================= def get_htdocs_dirs(self): return [] def get_templates_dirs(self): return [resource_filename('agilo.scrum.sprint', 'templates')] #========================================================================== # ITimelineEventProvider methods #========================================================================== def get_timeline_filters(self, req): if Action.SPRINT_VIEW in req.perm: yield (Key.SPRINT, 'Sprints') def get_timeline_events(self, req, start, stop, filters): if Key.SPRINT not in filters: return # prepare select criteria criteria = {'start': '>=%d' % to_timestamp(start), 'start': '<=%d' % to_timestamp(stop)} for sprint in self.sp_manager.select(criteria=criteria): # the first value of the data tuple tells if we're showing # a start or an end date (True=start, False=end), see next function if sprint.is_currently_running: yield(Key.SPRINT, sprint.start, '', (True, sprint)) if sprint.is_closed: yield(Key.SPRINT, sprint.end, '', (False, sprint)) def render_timeline_event(self, context, field, event): (start, sprint) = event[3] if field == 'url': return context.href.sprint(sprint.name) elif field == 'title': if start: return tag('Sprint ', tag.em(sprint.name), ' started') return tag('Sprint ', tag.em(sprint.name), ' finished') elif field == 'description': return format_to(self.env, None, context(resource=sprint), sprint.description)
def _ensure_milestone_fits_to_sprint(self, ticket, sprint_name): milestone_name = self._milestone_name_from_ticket(ticket) pulled_task_into_milestone = milestone_name is not None and Key.MILESTONE in ticket._old moved_task_between_milestones = ( Key.SPRINT in ticket._old and ticket._old[Key.SPRINT] != sprint_name) milestone_did_change = pulled_task_into_milestone or moved_task_between_milestones if not milestone_did_change: return # Now checks if the milestone is compatible with the current set sprint # otherwise reset the sprint value sp_manager = SprintModelManager(self.env) sprints = [ s.name for s in sp_manager.select(criteria={'milestone': milestone_name}) ] if not ticket[Key.SPRINT] in sprints: # The milestone is changed, and the sprint not, probably is an high #level re-planning, so reset the Sprint. ticket[Key.SPRINT] = None
def __init__(self, env, default_capacity=[6, 6, 6, 6, 6, 0, 0], **kwargs): """ Initialize the TeamMember object wrapping the default capacity into a list of value """ days = dict() for i, day in self.DAYS.items(): days['ts_%s' % day] = default_capacity[i] kwargs.update(days) super(TeamMember, self).__init__(env, **kwargs) # Instance of Sprint Model Manager from agilo.scrum.sprint import SprintModelManager self.sp_manager = SprintModelManager(self.env)
def validate(self, ticket): """Validate the ticket against the defined rules""" debug(self, "Called validate(%s)..." % ticket) if ticket is not None and isinstance(ticket, AgiloTicket) and \ Key.REMAINING_TIME in ticket.fields_for_type: sprint_name = ticket[Key.SPRINT] if sprint_name not in (None, ''): sprint = SprintModelManager(self.env).get(name=sprint_name) if sprint and sprint.team is not None: owner = ticket[Key.OWNER] self.check_team_membership(ticket, sprint, owner, is_owner=True) for r in ticket.get_resource_list(): self.check_team_membership(ticket, sprint, r)
class MetricsChartWidget(FlotChartWidget): """This widget generates HTML and JS code so that Flot can generate a chart for the team metrics (per sprint) combining some metrics.""" default_width = 400 default_height = 200 def __init__(self, env, **kwargs): from agilo.scrum.sprint import SprintModelManager template_filename = 'scrum/metrics/templates/agilo_metrics_chart.html' self._define_chart_resources(env, template_filename, kwargs) super(MetricsChartWidget, self).__init__(env, template_filename, **kwargs) self.sp_manager = SprintModelManager(env) def _get_metrics_data(self, metric_names, sprint_names, metrics_by_sprint): metrics = [] for label in metric_names: metric_values = [] for i, sprint_name in enumerate(sprint_names): m = metrics_by_sprint[sprint_name] value = m[label] if value != None: metric_values.append((i, value)) metrics.append((get_label(label), metric_values)) return metrics def _make_title(self, metric_names): titles = [] for name in metric_names: titles.append(get_label(name)) return ' / '.join(titles) def fill(self, team_name, metric_names): # Charts are not allowed to import the model classes globally from agilo.scrum.metrics import TeamMetrics sprints = self.sp_manager.select( criteria={'team': team_name}, order_by=['%s' % Key.START]) sprint_names = [sprint.name for sprint in sprints] metrics_by_sprint = dict([(s.name, TeamMetrics(self.env, sprint=s)) \ for s in sprints]) metrics = self._get_metrics_data(metric_names, sprint_names, metrics_by_sprint) title = self._make_title(metric_names) self.data.update( dict(team_name=team_name, sprint_names=sprint_names, metrics=metrics, title=title))
class SprintModule(Component): """Module to handle Sprint objects""" implements(ITemplateProvider, ITimelineEventProvider, IPermissionRequestor) def __init__(self, *args, **kwargs): """Sets a SprintModelManager""" super(SprintModule, self).__init__(*args, **kwargs) self.sp_manager = SprintModelManager(self.env) self.tm_manager = TeamModelManager(self.env) #============================================================================= # IPermissionRequestor methods #============================================================================= def get_permission_actions(self): actions = [Action.SPRINT_VIEW, Action.SPRINT_EDIT] return actions + [(Action.SPRINT_ADMIN, actions)] #============================================================================= # ITemplateProvider methods #============================================================================= def get_htdocs_dirs(self): return [] def get_templates_dirs(self): return [resource_filename('agilo.scrum.sprint', 'templates')] #========================================================================== # ITimelineEventProvider methods #========================================================================== def get_timeline_filters(self, req): if Action.SPRINT_VIEW in req.perm: yield (Key.SPRINT, 'Sprints') def get_timeline_events(self, req, start, stop, filters): if Key.SPRINT not in filters: return # prepare select criteria criteria = { 'start': '>=%d' % to_timestamp(start), 'start': '<=%d' % to_timestamp(stop) } for sprint in self.sp_manager.select(criteria=criteria): # the first value of the data tuple tells if we're showing # a start or an end date (True=start, False=end), see next function if sprint.is_currently_running: yield (Key.SPRINT, sprint.start, '', (True, sprint)) if sprint.is_closed: yield (Key.SPRINT, sprint.end, '', (False, sprint)) def render_timeline_event(self, context, field, event): (start, sprint) = event[3] if field == 'url': return context.href.sprint(sprint.name) elif field == 'title': if start: return tag('Sprint ', tag.em(sprint.name), ' started') return tag('Sprint ', tag.em(sprint.name), ' finished') elif field == 'description': return format_to(self.env, None, context(resource=sprint), sprint.description)
def __init__(self, *args, **kwargs): """Sets a SprintModelManager""" super(SprintModule, self).__init__(*args, **kwargs) self.sp_manager = SprintModelManager(self.env) self.tm_manager = TeamModelManager(self.env)
class TestSprint(AgiloTestCase): """ Tests the Sprint object """ def setUp(self): self.super() self.start = normalize_date(now()) duration = timedelta(days=20) self.end = normalize_date(self.start + duration) self.manager = SprintModelManager(self.env) self.tmm = TeamMemberModelManager(self.env) def testSprintCreationWithDates(self): """Tests the Sprint creation with the start and end date""" sprint = self.manager.create(name="Test Sprint", start=self.start, end=self.end) self.assert_equals( sprint.duration, count_working_days(self.start.date(), self.end.date())) # Now save the sprint and reload it from the DB # AT: With the manager the sprint is already saved # self.manager.save(sprint) sprint = self.manager.get(name="Test Sprint") self.assert_equals( sprint.duration, count_working_days(self.start.date(), self.end.date())) self.assert_equals(self.end, sprint.end) self.assert_equals(self.start, sprint.start) # Now add a description and test the reload sprint.description = 'This is a test Sprint' self.manager.save(sprint) # clear the cache so we are sure it will be reloaded from the DB self.manager.get_cache().invalidate() sprint = self.manager.get(name="Test Sprint") self.assert_equals(sprint.description, 'This is a test Sprint') def testSprintUpdateDuration(self): """ Tests the Sprint update of the duration in case start, or end, or duration are changed """ sprint = self.manager.create(name="Test Sprint", start=self.start, end=self.end) self.assert_equals( sprint.duration, count_working_days(sprint.start.date(), sprint.end.date())) # Now change the start date +1 sprint.start = sprint.start + timedelta(days=1) self.assert_equals( sprint.duration, count_working_days(sprint.start.date(), sprint.end.date())) # Now move the end -1 sprint.end = sprint.end - timedelta(days=1) self.assert_equals( sprint.duration, count_working_days(sprint.start.date(), sprint.end.date())) def testSprintCreateWithStartDuration(self): """ Test the creation of a sprint setting the start date and the duration in working days """ sprint = self.teh.create_sprint(name="Test Sprint", start=self.start, end=self.end) another_sprint = self.teh.create_sprint(name="Another Sprint", start=self.start, duration=sprint.duration) # Check they have the same end date... self.assert_equals(sprint.end.date(), another_sprint.end.date()) # Now, save and load and check that the data are set correctly # FIXME: where is the check mentioned in the above comment? # Now change the end and see if the duration change as well another_sprint.end = another_sprint.end + timedelta(days=1) self.assert_true(another_sprint.duration > sprint.duration) def testSprintSelect(self): """Test the Sprint select function""" self.manager.create(name="Test Sprint 1", start=self.start, end=self.end) self.manager.create(name="Test Sprint 2", start=self.start, end=self.end) self.manager.create(name="Test Sprint 3", start=parse_date("2008-06-10"), end=parse_date("2008-06-30")) # Now test the select sprints = self.manager.select( criteria={'end': '> %d' % to_timestamp(now())}) self.assert_equals(len(sprints), 2) sprints = self.manager.select() self.assert_equals(len(sprints), 3) def testDeleteSprint(self): """Tests the deletion of a Sprint""" sprint1 = self.manager.create(name="Test Sprint 1", start=self.start, end=self.end) self.assert_true(sprint1.exists) self.assert_true(self.manager.delete(sprint1)) # Now make sure is not there anymore self.manager.get(name="Test Sprint 1") def testRenameSprint(self): """Tests the Sprint rename""" name = 'Test sprint name' sprint = self.manager.create(name=name, start=self.start, end=self.end) self.assert_true(sprint.exists) self.assert_equals(sprint.name, name) # create a ticket for this sprint t = self.teh.create_ticket(Type.USER_STORY, props={Key.SPRINT: name}) # reload ticket self.assert_equals(t[Key.SPRINT], name) # create a metrics object team = self.teh.create_team(name='Testteam') metrics = TeamMetrics(self.env, sprint, team) metrics['test'] = 1.0 metrics.save() # Rename the sprint new_name = 'New sprint name' sprint.name = new_name self.assert_true(self.manager.save(sprint)) self.assert_equals(sprint.name, new_name) # Remove the sprint from the cache and reload it again self.manager.get_cache().invalidate(model_instance=sprint) # check new name after reload sprint = self.manager.get(name=new_name) self.assert_equals(sprint.name, new_name) t = self.teh.load_ticket(t_id=t.id) # sprint in ticket and metrics should be renamed as well self.assert_equals(t[Key.SPRINT], new_name) metrics = TeamMetrics(self.env, sprint, team) self.assert_equals(metrics.sprint, sprint) self.assert_equals(metrics['test'], 1.0) # Rename the sprint with some not allowed characters new_name = "That's my sprint!" sprint.name = new_name self.assert_true(sprint.save()) self.assert_equals(sprint.name, new_name) # check new name after reload sprint = Sprint(self.env, name=new_name) self.assert_equals(sprint.name, new_name) def testAssignTeamToSprint(self): """Tests the assignment of a team to a Sprint""" team = self.teh.create_team(name="The Team") self.teh.create_member(name="Team Member 1", team=team) self.teh.create_member(name="Team Member 2", team=team) # Now create a Sprint s = self.manager.create(name="Test S", team=team) self.assert_equals(team.name, s.team.name) for i, member in enumerate(team.members): self.assert_equals(member.name, s.team.members[i].name) def testSprintClosedAndIsCurrentlyRunning(self): """Tests the is_closed and is_started""" start = now() - timedelta(days=3) # no risk to get a weekend s = self.teh.create_sprint("Test", start=start) self.assert_true(s.is_currently_running) self.assert_false(s.is_closed) s.start += timedelta( days=5 ) # Move 5 to make sure that we will overcome also a normalization over a weekend self.assert_false(s.is_currently_running, "%s <= %s < %s" % \ (s.start, start, s.end)) self.assert_false(s.is_closed) # check functions for an old, closed sprint s.start = parse_date("2008-01-01") s.end = parse_date("2008-01-31") self.assert_false(s.is_currently_running) self.assert_true(s.is_closed) def testSprintIsNotCurrentlyRunningAfterSprintEnd(self): today = now() sprint_start = today - timedelta(days=20) sprint = self.teh.create_sprint("Test", start=sprint_start) sprint.end = today - timedelta(days=10) # Sprint ended ten days before today self.assert_false(sprint.is_currently_running) self.assert_true(sprint.is_closed) def testLoadSprintWhichHasNoStartDate(self): self.manager.create(name='foo') sprint = self.manager.get(name='foo') self.assert_true(sprint.exists) self.assert_equals('foo', sprint.name) def testCanDisableSprintStartDateNormalization(self): config = AgiloConfig(self.env).get_section(AgiloConfig.AGILO_GENERAL) option_name = 'sprints_can_start_or_end_on_weekends' self.assert_false(config.get_bool(option_name)) config.change_option(option_name, True, save=True) self.assert_true( AgiloConfig(self.env).sprints_can_start_or_end_on_weekends) start = datetime(2009, 05, 30, tzinfo=utc) sprint = self.teh.create_sprint('Foo', start=start) self.assert_equals(start, sprint.start) self.assert_equals(utc, sprint.start.tzinfo) def testManagerAttributeIsNotSerialized(self): sprint = self.teh.create_sprint('Foo Sprint') sprint_dict = sprint.as_dict() self.assert_false('manager' in sprint_dict) def test_can_build_resource(self): sprint = self.teh.create_sprint('Foo Sprint') self.assert_equals(Resource(Realm.SPRINT, sprint.name), sprint.resource()) def test_can_build_by_duration_without_normalization(self): self.teh.disable_sprint_date_normalization() some_friday = datetime(day=30, month=4, year=2010) self.assert_equals(4, some_friday.weekday()) sprint = self.teh.create_sprint('Foo Sprint', start=some_friday, duration=4) sprint_length = (sprint.end - sprint.start).days self.assert_equals(3, sprint_length)
def __init__(self, *args, **kwargs): """Set managers instance for other persistent objects""" super(Team, self).__init__(*args, **kwargs) from agilo.scrum.sprint import SprintModelManager self.sp_manager = SprintModelManager(self.env)
class TestSprint(AgiloTestCase): """ Tests the Sprint object """ def setUp(self): self.super() self.start = normalize_date(now()) duration = timedelta(days=20) self.end = normalize_date(self.start + duration) self.manager = SprintModelManager(self.env) self.tmm = TeamMemberModelManager(self.env) def testSprintCreationWithDates(self): """Tests the Sprint creation with the start and end date""" sprint = self.manager.create(name="Test Sprint", start=self.start, end=self.end) self.assert_equals(sprint.duration, count_working_days(self.start.date(), self.end.date())) # Now save the sprint and reload it from the DB # AT: With the manager the sprint is already saved # self.manager.save(sprint) sprint = self.manager.get(name="Test Sprint") self.assert_equals(sprint.duration, count_working_days(self.start.date(), self.end.date())) self.assert_equals(self.end, sprint.end) self.assert_equals(self.start, sprint.start) # Now add a description and test the reload sprint.description = 'This is a test Sprint' self.manager.save(sprint) # clear the cache so we are sure it will be reloaded from the DB self.manager.get_cache().invalidate() sprint = self.manager.get(name="Test Sprint") self.assert_equals(sprint.description, 'This is a test Sprint') def testSprintUpdateDuration(self): """ Tests the Sprint update of the duration in case start, or end, or duration are changed """ sprint = self.manager.create(name="Test Sprint", start=self.start, end=self.end) self.assert_equals(sprint.duration, count_working_days(sprint.start.date(), sprint.end.date())) # Now change the start date +1 sprint.start = sprint.start + timedelta(days=1) self.assert_equals(sprint.duration, count_working_days(sprint.start.date(), sprint.end.date())) # Now move the end -1 sprint.end = sprint.end - timedelta(days=1) self.assert_equals(sprint.duration, count_working_days(sprint.start.date(), sprint.end.date())) def testSprintCreateWithStartDuration(self): """ Test the creation of a sprint setting the start date and the duration in working days """ sprint = self.teh.create_sprint(name="Test Sprint", start=self.start, end=self.end) another_sprint = self.teh.create_sprint(name="Another Sprint", start=self.start, duration=sprint.duration) # Check they have the same end date... self.assert_equals(sprint.end.date(), another_sprint.end.date()) # Now, save and load and check that the data are set correctly # FIXME: where is the check mentioned in the above comment? # Now change the end and see if the duration change as well another_sprint.end = another_sprint.end + timedelta(days=1) self.assert_true(another_sprint.duration > sprint.duration) def testSprintSelect(self): """Test the Sprint select function""" self.manager.create(name="Test Sprint 1", start=self.start, end=self.end) self.manager.create(name="Test Sprint 2", start=self.start, end=self.end) self.manager.create(name="Test Sprint 3", start=parse_date("2008-06-10"), end=parse_date("2008-06-30")) # Now test the select sprints = self.manager.select(criteria={'end': '> %d' % to_timestamp(now())}) self.assert_equals(len(sprints), 2) sprints = self.manager.select() self.assert_equals(len(sprints), 3) def testDeleteSprint(self): """Tests the deletion of a Sprint""" sprint1 = self.manager.create(name="Test Sprint 1", start=self.start, end=self.end) self.assert_true(sprint1.exists) self.assert_true(self.manager.delete(sprint1)) # Now make sure is not there anymore self.manager.get(name="Test Sprint 1") def testRenameSprint(self): """Tests the Sprint rename""" name = 'Test sprint name' sprint = self.manager.create(name=name, start=self.start, end=self.end) self.assert_true(sprint.exists) self.assert_equals(sprint.name, name) # create a ticket for this sprint t = self.teh.create_ticket(Type.USER_STORY, props={Key.SPRINT: name}) # reload ticket self.assert_equals(t[Key.SPRINT], name) # create a metrics object team = self.teh.create_team(name='Testteam') metrics = TeamMetrics(self.env, sprint, team) metrics['test'] = 1.0 metrics.save() # Rename the sprint new_name = 'New sprint name' sprint.name = new_name self.assert_true(self.manager.save(sprint)) self.assert_equals(sprint.name, new_name) # Remove the sprint from the cache and reload it again self.manager.get_cache().invalidate(model_instance=sprint) # check new name after reload sprint = self.manager.get(name=new_name) self.assert_equals(sprint.name, new_name) t = self.teh.load_ticket(t_id=t.id) # sprint in ticket and metrics should be renamed as well self.assert_equals(t[Key.SPRINT], new_name) metrics = TeamMetrics(self.env, sprint, team) self.assert_equals(metrics.sprint, sprint) self.assert_equals(metrics['test'], 1.0) # Rename the sprint with some not allowed characters new_name = "That's my sprint!" sprint.name = new_name self.assert_true(sprint.save()) self.assert_equals(sprint.name, new_name) # check new name after reload sprint = Sprint(self.env, name=new_name) self.assert_equals(sprint.name, new_name) def testAssignTeamToSprint(self): """Tests the assignment of a team to a Sprint""" team = self.teh.create_team(name="The Team") self.teh.create_member(name="Team Member 1", team=team) self.teh.create_member(name="Team Member 2", team=team) # Now create a Sprint s = self.manager.create(name="Test S", team=team) self.assert_equals(team.name, s.team.name) for i, member in enumerate(team.members): self.assert_equals(member.name, s.team.members[i].name) def testSprintClosedAndIsCurrentlyRunning(self): """Tests the is_closed and is_started""" start = now() - timedelta(days=3) # no risk to get a weekend s = self.teh.create_sprint("Test", start=start) self.assert_true(s.is_currently_running) self.assert_false(s.is_closed) s.start += timedelta(days=5) # Move 5 to make sure that we will overcome also a normalization over a weekend self.assert_false(s.is_currently_running, "%s <= %s < %s" % \ (s.start, start, s.end)) self.assert_false(s.is_closed) # check functions for an old, closed sprint s.start = parse_date("2008-01-01") s.end = parse_date("2008-01-31") self.assert_false(s.is_currently_running) self.assert_true(s.is_closed) def testSprintIsNotCurrentlyRunningAfterSprintEnd(self): today = now() sprint_start = today - timedelta(days=20) sprint = self.teh.create_sprint("Test", start=sprint_start) sprint.end = today - timedelta(days=10) # Sprint ended ten days before today self.assert_false(sprint.is_currently_running) self.assert_true(sprint.is_closed) def testLoadSprintWhichHasNoStartDate(self): self.manager.create(name='foo') sprint = self.manager.get(name='foo') self.assert_true(sprint.exists) self.assert_equals('foo', sprint.name) def testCanDisableSprintStartDateNormalization(self): config = AgiloConfig(self.env).get_section(AgiloConfig.AGILO_GENERAL) option_name = 'sprints_can_start_or_end_on_weekends' self.assert_false(config.get_bool(option_name)) config.change_option(option_name, True, save=True) self.assert_true(AgiloConfig(self.env).sprints_can_start_or_end_on_weekends) start = datetime(2009, 05, 30, tzinfo=utc) sprint = self.teh.create_sprint('Foo', start=start) self.assert_equals(start, sprint.start) self.assert_equals(utc, sprint.start.tzinfo) def testManagerAttributeIsNotSerialized(self): sprint = self.teh.create_sprint('Foo Sprint') sprint_dict = sprint.as_dict() self.assert_false('manager' in sprint_dict) def test_can_build_resource(self): sprint = self.teh.create_sprint('Foo Sprint') self.assert_equals(Resource(Realm.SPRINT, sprint.name), sprint.resource()) def test_can_build_by_duration_without_normalization(self): self.teh.disable_sprint_date_normalization() some_friday = datetime(day=30, month=4, year=2010) self.assert_equals(4, some_friday.weekday()) sprint = self.teh.create_sprint('Foo Sprint', start=some_friday, duration=4) sprint_length = (sprint.end - sprint.start).days self.assert_equals(3, sprint_length)
def __init__(self): self.sp_manager = SprintModelManager(self.env) self.c_manager = ContingentModelManager(self.env)
def __init__(self, env, **kwargs): from agilo.scrum.sprint import SprintModelManager template_filename = 'scrum/metrics/templates/agilo_metrics_chart.html' self._define_chart_resources(env, template_filename, kwargs) super(MetricsChartWidget, self).__init__(env, template_filename, **kwargs) self.sp_manager = SprintModelManager(env)
def _sprint(self, sprint_name): return SprintModelManager(self.env).get(name=sprint_name)