def build_report(self, data): def converter(time=None): #converts the specified format if type(time) is datetime.timedelta: return round(float(time.seconds) / 60 / 60, 2) if type(time) is datetime.time: return round(float(time.hour * 60 + time.minute) / 60, 2) if type(time) is float: hh = abs(int(time)) mm = abs(int((time - int(time))*60)) return datetime.timedelta(hours=hh, minutes=mm, seconds=00) # Initial values project = data['project'] start_date = data['start_date'] or datetime.datetime(2000, 1, 1) # we don't have projects before 2000 end_date = data['end_date'] or TODAY is_fixed = data['is_fixed'] total_time = 0 actual_overtime = 0 time_saved = 0 ticket_url_list = [] user_url_list = [] # Initial table table_data = [(force_unicode(_('Task')), force_unicode(_('Overtime')), force_unicode(_('Description')), force_unicode(_('User')))] # Check parameters if is_fixed: tickets = Ticket.objects.filter(project=project, status='closed', time__range=(start_date, end_date)) else: tickets = Ticket.objects.filter(project=project, time__range=(start_date, end_date)) if data['hrs_cp'] is None: try: project_estimate = converter(project.last_estimate_values['project_time_per_cp']) except TypeError: project_estimate = 2 else: project_estimate = data['hrs_cp'] time_sheet_entries = TimesheetEntry.objects.filter(project=project, billable=True) # Fill table data for ticket in tickets: #Get ticket overtime ticket_time = ticket.get_billable_time() ticket_overtime = converter(project_estimate) * ticket.estimatedhours - converter(ticket_time) actual_overtime += ticket_overtime #summarizes the positive and negative values #show tickets only with overtime if ticket_overtime < 0: total_time += ticket_overtime #summarizes just negative values #Get ticket url ticket_url = '`#%d`_' % (ticket.number) ticket_url_list.append('.. _`#%d`: %s' % (ticket.number, ticket.trac_url)) #Get users who worked on the task user_list = str() time_sheet = time_sheet_entries.filter(ticket_number=ticket.number) for entries in time_sheet: user_data = '`%s`_' % (entries.timesheet.user) user_url = '.. _`%s`: %s' % (entries.timesheet.user, entries.timesheet.user.get_profile().get_absolute_url()) if user_data not in user_list: user_list += ' ' + user_data if user_url not in user_url_list: user_url_list.append(user_url) table_data.append((ticket_url, timedelta_to_str(converter(ticket_overtime)), ticket.summary, user_list)) else: time_saved += ticket_overtime #summarizes just positive values #Fill totals row total_time = converter(total_time) actual_overtime = converter(actual_overtime) time_saved = converter(time_saved) table_data.append(('', '', '', '')) table_data.append(('Saved time:', timedelta_to_str(time_saved), '', '')) table_data.append(('Total overtime:', timedelta_to_str(total_time), '', '')) table_data.append(('Actual overtime:', timedelta_to_str(actual_overtime), '', '')) # Create table table = Table(table_data).create_table() #Generate Context context = Context({'end_date': end_date, 'start_date': start_date, 'table': table, 'cp': ('Given hrs/cp: %s' % str(project_estimate)), 'ticket_url_list': ticket_url_list, 'user_url_list': user_url_list, }) try: subject = render_to_string(OVERTIME_TEMPLATE_SUBJECT, context) # If some error raised - generate fallback subject except: subject = force_unicode(_('Overtime report from %s to %s') % \ (date_filter(start_date), date_filter(end_date))) else: subject = u''.join(subject.splitlines()) try: body = render_to_string(OVERTIME_TEMPLATE_BODY, context) except: body = _('== Warning ==\n\n' \ 'Cannot successfully generate time report cause of ' \ 'error in rendering body template.\n') body = force_unicode(body) message = Message() message.project = project message.subject = subject message.tags = OVERTIME_TAG message.body = body return message