def test_ActualHours(self): """ """ task = self.portal.project.iteration.story.task booking = task.booking ann = IActualHours(booking) self.assertEqual(ann.actual_time, 3.25) task.invokeFactory('Booking', id='booking2', hours=0, minutes=0) ann = IActualHours(task.booking2) self.assertEqual(ann.actual_time, 0.0) task.invokeFactory('Booking', id='booking3', hours=0, minutes=45) ann = IActualHours(task.booking3) self.assertEqual(ann.actual_time, 0.75) # The following two have a weird number of minutes, but if # they pass, that is fine. task.invokeFactory('Booking', id='booking4', hours=4, minutes=60) ann = IActualHours(task.booking4) self.assertEqual(ann.actual_time, 5.0) task.invokeFactory('Booking', id='booking5', hours=4, minutes=75) ann = IActualHours(task.booking5) self.assertEqual(ann.actual_time, 5.25)
def recalculate_actual(self, action, data): """Recalculate the actual hours. Can be used in case the totals are wrong for some reason. """ context = aq_inner(self.context) cat = getToolByName(context, 'portal_catalog') for portal_type in ('Booking', 'Task', 'PoiTask', 'Story', 'Iteration'): brains = cat(portal_type=portal_type) for brain in brains: obj = brain.getObject() anno = IActualHours(obj) anno.recalc()
def main(self): """Get a dict with info from this Booking. """ context = aq_inner(self.context) anno = IActualHours(context, None) if anno is not None: actual = anno.actual_time else: # What the??? actual = -99.0 ploneview = context.restrictedTraverse('@@plone') # Webintelligenttext for the description desc = context.Description() pt = getToolByName(context, 'portal_transforms') desc = pt('web_intelligent_plain_text_to_html', desc) returnvalue = dict( title=context.title_or_id(), description=desc, actual=formatTime(actual), booking_date=ploneview.toLocalizedTime(context.getBookingDate()), billable=context.getBillable(), creator=context.Creator(), # base_view of a booking gets redirected to the task view, # which we do not want here. url=context.absolute_url() + '/base_edit', ) return returnvalue
def assertAnnotationGeneralBrainHoursEquality(self, obj, value, portal_type): ann = IActualHours(obj) self.assertEqual(ann.actual_time, value) catalog = self.portal.portal_catalog brains = catalog(portal_type=portal_type, path='/'.join(obj.getPhysicalPath())) self.assertEqual(brains[0]['actual_time'], value)
def actual_budget_left(self): context = self.context project = aq_parent(aq_inner(self.context)) hours_left = project.getBudgetHours() if not hours_left: return None contentfilter = dict(portal_type='Iteration') iteration_brains = project.getFolderContents(contentfilter) for brain in iteration_brains: iteration = brain.getObject() hours_left -= IActualHours(brain.getObject()).actual_time return hours_left
def main(self): """Get a dict with info from this Context. """ context = aq_inner(self.context) anno = IActualHours(context, None) if anno is not None: actual = anno.actual_time else: # Should not happen (tm). actual = -99.0 est = IEstimate(context, None) if est is not None: estimate = est.estimate else: # Should not happen (tm). estimate = -99.0 # Size estimate. We may want to do this smarter. filter = dict(portal_type='Story') items = context.getFolderContents(filter) size_estimate = sum([ item.size_estimate for item in items if item.size_estimate is not None ]) review_state = self.workflow.getInfoFor(context, 'review_state') if review_state in ['completed', 'invoiced', 'own-account']: budget_left = None else: budget_left = self.actual_budget_left() if budget_left is not None: budget_left = formatTime(budget_left) ploneview = context.restrictedTraverse('@@plone') if hasattr(context, 'getManHours'): manhours = context.getManHours() else: manhours = None returnvalue = dict( title=context.Title(), description=context.Description(), man_hours=manhours, start_date=ploneview.toLocalizedTime(context.getStartDate()), end_date=ploneview.toLocalizedTime(context.getEndDate()), estimate=formatTime(estimate), size_estimate=size_estimate, actual=formatTime(actual), difference=formatTime(estimate - actual), review_state=review_state, budget_left=budget_left, ) return returnvalue
def __init__(self, context, request): super(ChartView, self).__init__(context, request) self.table = [] self.total_iterations = 0 self.project = aq_inner(self.context).getProject() for it in self.get_iterations_generator(): self.total_iterations += 1 estim_total = int(self.get_total_estimate_iteration(it) + 0.5) estim_total_adapt = int((IEstimate(it).estimate / 8.0) + 0.5) work_total = int((IActualHours(it).actual_time / 8.0) + 0.5) self.table.append({ 'label': it.title_or_id(), 'estimate_stories': estim_total, 'estimate_tasks': estim_total_adapt, 'worked': work_total })
def totals(self): """Get a dict with totals for this Story. """ context = aq_inner(self.context) anno = IActualHours(context, None) if anno is not None: actual = anno.actual_time else: # Should not happen (tm). actual = -99.0 est = IEstimate(context, None) if est is not None: estimate = est.estimate else: # Should not happen (tm). estimate = -99.0 totals = dict( estimate=formatTime(estimate), actual=formatTime(actual), difference=formatTime(estimate - actual), ) return totals
def main(self): """Get a dict with info from this Task. """ context = aq_inner(self.context) anno = IActualHours(context, None) if anno is not None: actual = anno.actual_time else: # Should not happen (tm). actual = -99.0 est = IEstimate(context, None) if est is not None: estimate = est.estimate else: # Should not happen (tm). estimate = -99.0 pas_member = queryMultiAdapter((context, self.request), name='pas_member') if pas_member is None: # Plone 3, Poi 1.2 nice_namer = context.poi_niceName else: # Plone 4 nice_namer = lambda x: pas_member.info(x).get('name_or_id') # Get info for previous and next links. We only want tasks # here, not for example images. story = aq_parent(context) tasks = story.getFolderContents() num_tasks = len(tasks) pos = story.getObjectPosition(context.id) next = None next_pos = pos + 1 while next_pos < num_tasks: if tasks[next_pos].portal_type in ('Task', 'PoiTask'): next = tasks[next_pos] break next_pos += 1 prev = None prev_pos = pos - 1 while prev_pos >= 0: if tasks[prev_pos].portal_type in ('Task', 'PoiTask'): prev = tasks[prev_pos] break prev_pos -= 1 returnvalue = dict( title=context.Title(), description=context.Description(), cooked_body=context.CookedBody(), estimate=formatTime(estimate), actual=formatTime(actual), difference=formatTime(estimate - actual), review_state=self.workflow.getInfoFor(context, 'review_state'), assignees=[{ 'niceName': nice_namer(x), 'username': x, 'active': True } for x in context.getAssignees()], prev=prev, next=next, ) return returnvalue