From: Emilie Zawadzki Date: Tue, 17 Jan 2017 18:59:48 +0000 (+0100) Subject: [Timesheet] : export timesheet with leaved days X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=73c1150a6349c2413ea374079e7146c56283b8e0;p=mezzo.git [Timesheet] : export timesheet with leaved days --- diff --git a/app/organization/network/admin.py b/app/organization/network/admin.py index 39c89dd4..7d831112 100644 --- a/app/organization/network/admin.py +++ b/app/organization/network/admin.py @@ -21,6 +21,7 @@ from django.contrib import admin from django import forms +from django.http import HttpResponse from copy import deepcopy from dal import autocomplete from dal_select2_queryset_sequence.views import Select2QuerySetSequenceView @@ -32,7 +33,7 @@ from organization.pages.models import * from organization.core.admin import * from organization.pages.admin import PageImageInline, PageBlockInline, PagePlaylistInline, DynamicContentPageInline, PageRelatedTitleAdmin from organization.shop.models import PageProductList -from django.http import HttpResponse +from organization.network.utils import TimesheetXLS class OrganizationAdminInline(StackedDynamicInlineAdmin): @@ -274,8 +275,6 @@ class TrainingTopicAdmin(BaseTranslationModelAdmin): model = TrainingTopic - - class PersonActivityTimeSheetAdmin(BaseTranslationOrderedModelAdmin): model = PersonActivityTimeSheet search_fields = ['year','activity__person__last_name', "project__title"] diff --git a/app/organization/network/api.py b/app/organization/network/api.py index 268b5c8b..af2773c4 100644 --- a/app/organization/network/api.py +++ b/app/organization/network/api.py @@ -22,7 +22,7 @@ def figgo_request(method): def get_active_persons(): r_p_active = figgo_request('api/users?fields=id,lastname,firstname') r_p_active = r_p_active.json() - return r_p_active['data'] + return r_p_active['data'] if 'data' in r_p_active else {} def get_inactive_persons(): @@ -30,13 +30,14 @@ def get_inactive_persons(): yesterday = yesterday.isoformat() r_p_inactive = figgo_request('api/users?dtContractEnd=until,'+yesterday+',null&fields=id,lastname,firstname') r_p_inactive = r_p_inactive.json() - return r_p_inactive['data'] + return r_p_inactive['data'] if 'data' in r_p_inactive else {} def get_leave_periods(date_from, date_to, person_external_id): leave_periods = figgo_request('api/leaves?date=between,'+str(date_from)+','+str(date_to)+'&fields=owner.name,owner.login,owner.mail,owner.matricule,duration,name,date,status,leaveScope&owner.id='+str(person_external_id)) leave_periods = leave_periods.json() - return leave_periods['data'] + + return leave_periods['data'] if 'data' in leave_periods else {} def get_leave_days(date_from, date_to, person_external_id): diff --git a/app/organization/network/management/commands/export-ircam-timesheet-xls.py b/app/organization/network/management/commands/export-ircam-timesheet-xls.py index 44231eff..93a813aa 100644 --- a/app/organization/network/management/commands/export-ircam-timesheet-xls.py +++ b/app/organization/network/management/commands/export-ircam-timesheet-xls.py @@ -91,6 +91,7 @@ class IrcamTimeSheet(object): def set_work_package(self, person_activity_timesheet): """ set contract id of the project """ + class Command(BaseCommand): help = """Import Person data from IRCAM's legacy XLS management file. python manage.py import-ircam-timesheet-xls -s /srv/backup/TemplateInputTimeSheet2015-16.xlsx @@ -146,6 +147,7 @@ class Command(BaseCommand): # get month month = int(sheet.cell_value(xls.first_month_row, col_index)) self.logger.info('Processing', 'year : ' + str(curr_year) + " | month : " + str(month)) + # calculate the current date curr_date = datetime.date(curr_year, month, 1) diff --git a/app/organization/network/management/commands/import-ircam-project.py b/app/organization/network/management/commands/import-ircam-project.py index 74408344..1ca33511 100644 --- a/app/organization/network/management/commands/import-ircam-project.py +++ b/app/organization/network/management/commands/import-ircam-project.py @@ -94,6 +94,7 @@ class IrcamXLS: class IrcamProjects(object): def __init__(self, project_name): + print("project", project_name) project, is_created = Project.objects.get_or_create(title=project_name) self.project = project self.is_created = is_created diff --git a/app/organization/network/management/commands/import-ircam-timesheet-xls.py b/app/organization/network/management/commands/import-ircam-timesheet-xls.py index 50394f3c..3efed67f 100644 --- a/app/organization/network/management/commands/import-ircam-timesheet-xls.py +++ b/app/organization/network/management/commands/import-ircam-timesheet-xls.py @@ -118,7 +118,7 @@ class IrcamTimeSheet(object): class Command(BaseCommand): help = """Import Person data from IRCAM's legacy XLS management file. - python manage.py import-ircam-timesheet-xls -s /srv/backup/TemplateInputTimeSheet2015-16.xlsx + python manage.py import-ircam-timesheet-xls -s /srv/backup/time_sheet_2015_V3_H2020.xls """ option_list = BaseCommand.option_list + ( @@ -155,10 +155,8 @@ class Command(BaseCommand): for person in persons: period_str = sheet.cell_value(xls.period_row, xls.period_col) periods = findall(r'\d{1,2}/\d{1,2}/\d{4}', period_str) - print("periods", periods, person, period_str) date_from = dateutil.parser.parse(periods[0]) date_to = dateutil.parser.parse(periods[1]) - print("date_from", type(date_from), "date_to", type(date_to)) curr_range_date = datetimerange.DateTimeRange(date_from, date_to) curr_year = date_to.year @@ -213,7 +211,6 @@ class Command(BaseCommand): # processing projects if end_project_list_row == 0: # check if project exists - print("project_id_str", project_id_str, project_row_index, xls.first_project_col - 1) project, is_created = Project.objects.get_or_create(external_id=str(project_id_str)) if is_created: project.title = sheet.cell_value(project_row_index, xls.first_project_col) @@ -233,19 +230,16 @@ class Command(BaseCommand): # processing work package work_package_row_index = project_row_index + 1 - print("WK", sheet.cell_value(work_package_row_index, xls.first_project_col)) while sheet.cell_value(work_package_row_index, xls.first_project_col) != "Date entrée": # get project project_external_id = int(sheet.cell_value(work_package_row_index, xls.first_project_col - 1)) - print("project_external_id", project_external_id) project = Project.objects.get(external_id=str(project_external_id)) # check if project exists if project: # list all work package - print(work_package_row_index, col_index) wk_p_str = sheet.cell_value(work_package_row_index, col_index) if wk_p_str : self.logger.info('Processing', 'work packages : ' + str(wk_p_str) + " | project" + project.external_id + " - " + project.title) @@ -255,7 +249,6 @@ class Command(BaseCommand): elif isinstance(wk_p_str, float): i, d = divmod(wk_p_str, 1) wk_p_list = (int(i), int(d)) - print("wk_p_list", wk_p_list, wk_p_str) # link work packages to timesheet for wk_p_num in wk_p_list: wk_p_num = str(wk_p_num) @@ -288,3 +281,4 @@ class Command(BaseCommand): pat.save() self.logger.info('Processing', '_________________________ Number of record : ' + str(processing_counter) + ' _________________________') + print("Person : " + person.title + " | Number of record : " + str(processing_counter)) diff --git a/app/organization/network/utils.py b/app/organization/network/utils.py index 7b7eb32b..f2e0ef00 100644 --- a/app/organization/network/utils.py +++ b/app/organization/network/utils.py @@ -2,11 +2,12 @@ import pandas as pd import csv from django.http import HttpResponse -from xlwt import Workbook +from xlwt import * import calendar from organization.network.api import * import datetime - +from collections import defaultdict, OrderedDict +from pprint import pprint def get_nb_half_days_by_period(date_from, date_to): @@ -45,22 +46,23 @@ def get_nb_half_days_by_period(date_from, date_to): def get_nb_half_days_by_period_per_month(date_from, date_to): day_list = pd.date_range(date_from, date_to).tolist() - day_dict = { - "monday_am": 0, - "monday_pm": 0, - "tuesday_am": 0, - "tuesday_pm": 0, - "wednesday_am": 0, - "wednesday_pm": 0, - "thursday_am": 0, - "thursday_pm": 0, - "friday_am": 0, - "friday_pm": 0, - } md_dict = {} for i in range(1,13): - md_dict[i] = day_dict + md_dict[i] = { + "monday_am": 0, + "monday_pm": 0, + "tuesday_am": 0, + "tuesday_pm": 0, + "wednesday_am": 0, + "wednesday_pm": 0, + "thursday_am": 0, + "thursday_pm": 0, + "friday_am": 0, + "friday_pm": 0, + } + + # for each day of the period for day in day_list : if day.dayofweek == 0: md_dict[day.month]['monday_am'] += 1 @@ -77,16 +79,23 @@ def get_nb_half_days_by_period_per_month(date_from, date_to): if day.dayofweek == 4: md_dict[day.month]['friday_am'] += 1 md_dict[day.month]['friday_pm'] += 1 + return md_dict +class TimesheetXLS2(object): + + def __init__(self, timesheets): + self.timesheets = timesheets.order_by('activity','project', 'year', 'month') + class TimesheetXLS(object): + t_dict = OrderedDict() first_month_row = 5 first_month_col = 4 last_month_col = first_month_col + 13 - project_margin_row = 6 + project_margin_row = 4 project_first_row = 6 project_first_col = 0 percent_margin = 1 @@ -97,11 +106,14 @@ class TimesheetXLS(object): hours_label = "Productive hours worked on project" hours_label_row = 8 hours_label_col = 0 + wk_margin = 3 wk_label = "Workpackages to which the person has contributed" wk_label_row = 9 wk_label_col = 0 + accounting_margin = 4 accounting_label = "Date of accounting by person working on the action" accounting_label_col = 0 + validation_margin = 5 validation_label_row = 0 validation_label = "Date of validation by the superior" validation_label_col = 0 @@ -117,13 +129,20 @@ class TimesheetXLS(object): grant_row = 1 type_personal_col = 2 type_personal_row = 3 - work_package_margin = 3 - accounting_margin = 3 - validation_margin = 4 + def __init__(self, timesheets): - self.timesheets = timesheets.order_by('activity','project', 'month') + self.timesheets = timesheets.order_by('activity','project', 'year', 'month') self.book = Workbook() + self.grey_pattern = Pattern() + self.grey_pattern.pattern = Pattern.SOLID_PATTERN + self.grey_pattern.pattern_fore_colour = Style.colour_map['gray25'] + self.header_style = XFStyle() + self.header_style.pattern = self.grey_pattern + self.date_style = XFStyle() + self.date_style.num_format_str = 'M/D/YY' + + def init_layout(self, sheet, year): sheet.write(self.title_row, self.title_col, "TIME RECORDING FOR A HORIZON 2020 ACTION") @@ -134,93 +153,123 @@ class TimesheetXLS(object): sheet.write(self.type_personal_row, self.type_personal_col, "Type of personnel :") row = sheet.row(self.first_month_row) for i in range(self.first_month_col, self.last_month_col): - row.write(i, calendar.month_name[i - self.first_month_col] +"-"+ str(year % 100)) - - def export(self): - curr_project = '' - project_row_index = self.project_first_row - percent_label_row_index = self.percent_label_row - hours_label_row_index = self.hours_label_row - wk_label_row_index = self.wk_label_row + row.write(i, calendar.month_name[i - self.first_month_col] +"-"+ str(year % 100), self.header_style) + def format(self): + self.t_dict = OrderedDict() for timesheet in self.timesheets: - print("TIME SHEET", timesheet.id) - try : - self.sheet = self.book.add_sheet(timesheet.activity.person.slug) - self.init_layout(self.sheet, timesheet.year) - - # calculate nb of worked hours + person_slug = timesheet.activity.person.slug + # if new person + if not person_slug in self.t_dict: + self.t_dict[person_slug] = {} + # caculate for each person leaved days in year date_from = datetime.date(timesheet.year, 1, 1) date_to = datetime.date(timesheet.year, 12, 31) nb_half_days = get_nb_half_days_by_period_per_month(date_from, date_to) leave_days = get_leave_days_per_month(date_from, date_to, timesheet.activity.person.external_id) - # substract none worked half days - # print(nb_half_days) - # print("****************************") - # print(leave_days) - # print("****************************") + worked_hours_by_month = {"test" : "1"} + # for each month for m_key, m_val in nb_half_days.items(): - if m_key in leave_days : - for nhd_k, nhd_v in m_val.items(): - if nhd_k in leave_days[m_key]: - print(m_key, nhd_k, leave_days[m_key][nhd_k], nb_half_days[m_key][nhd_k]) - nb_half_days[m_key][nhd_k] = nhd_v - leave_days[m_key][nhd_k] - print(m_key, nhd_k, nb_half_days[m_key][nhd_k]) - print(nb_half_days) - except: - pass - - if curr_project == '': - curr_project = timesheet.project - elif curr_project != timesheet.project: - curr_project = timesheet.project - project_row_index += self.project_margin_row - percent_label_row_index += self.project_margin_row - hours_label_row_index += self.project_margin_row - wk_label_row_index += self.project_margin_row - - # percent - self.sheet.write(project_row_index + self.percent_margin, timesheet.month + self.first_month_col, timesheet.percentage) - - - - # multiplying by nb of theoretical worked hours - # - # theo_worked_hours = - # timesheet.activity.monday_am = - - # multiplying by percent - - # work packages - work_packages = [str(wk.number) for wk in timesheet.work_packages.all()] - work_packages = ",".join(work_packages) - self.sheet.write(project_row_index + self.work_package_margin, timesheet.month + self.first_month_col, work_packages) - - try : - self.sheet.write(project_row_index, self.project_first_col, timesheet.project.__str__()) - self.sheet.write(project_row_index, self.project_first_col + 1, timesheet.project.external_id) - self.sheet.write(percent_label_row_index, self.percent_label_col, self.percent_label) - self.sheet.write(hours_label_row_index, self.hours_label_col, self.hours_label) - self.sheet.write(wk_label_row_index, self.wk_label_col, self.wk_label) - except: - pass - - # check were is the lower cell - if wk_label_row_index > self.validation_label_row: - self.validation_label_row = wk_label_row_index - elif wk_label_row_index == self.validation_label_row: - try : - # accounting date - self.sheet.write(self.validation_label_row + 1, timesheet.month + self.first_month_col, timesheet.accounting) - - # validation date - self.sheet.write(self.validation_label_row + 2, timesheet.month + self.first_month_col, timesheet.validation) - except: - pass + # for each week day + for nhd_k, nhd_v in m_val.items(): + if not m_key in worked_hours_by_month: + worked_hours_by_month[m_key] = 0 + half_day_nb_hours = getattr(timesheet.activity, nhd_k) + if not half_day_nb_hours is None : + # is the person has been present during current m_key month ? + if m_key in leave_days : + if nhd_k in leave_days[m_key]: + # is the person has been present during current half day nhd_d ? + worked_hours_by_month[m_key] += (nb_half_days[m_key][nhd_k] - leave_days[m_key][nhd_k]) * half_day_nb_hours + else : + # if not, count theorical nb oh hours for this half day + worked_hours_by_month[m_key] += nb_half_days[m_key][nhd_k] * half_day_nb_hours + # if not, count theorical nb of hours for whole month + else : + worked_hours_by_month[m_key] += nb_half_days[m_key][nhd_k] * half_day_nb_hours + else : + # missing data... + worked_hours_by_month[m_key] = 0 + + # for each percent time worked on a project... + project_slug = timesheet.project.slug + if not project_slug in self.t_dict[person_slug]: + self.t_dict[person_slug][project_slug] = [] + # ...calculate nb of worked hours proportionally + # the property 'worked_hours' does not exists in the model, it just calculated on the fly + timesheet.worked_hours = worked_hours_by_month[timesheet.month] * timesheet.percentage + self.t_dict[person_slug][project_slug].append(timesheet) + return self.t_dict + + + def export(self): + curr_project = '' + percent_label_row_index = self.percent_label_row + hours_label_row_index = self.hours_label_row + # for each person + for person_k, person_v in self.t_dict.items(): + # for each project + project_row_last = (len(person_v) - 1) * self.project_margin_row + self.project_first_row + for project_k, project_v in person_v.items(): + project_position = list(person_v.keys()).index(project_k) + project_row_index = self.project_margin_row * project_position + self.project_first_row + # for each timesheet + for timesheet in project_v: + try : + self.sheet = self.book.add_sheet(person_k) + self.init_layout(self.sheet, timesheet.year) + except: + pass + + # project name + try : + self.sheet.write(project_row_index, self.project_first_col, timesheet.project.title, self.header_style) + except: + pass + + + # percent + try: + self.sheet.write(project_row_index + self.percent_margin, timesheet.month + self.first_month_col, timesheet.percentage) + except : + pass + + # nb worked hours + try: + self.sheet.write(project_row_index + self.hours_margin, timesheet.month + self.first_month_col, timesheet.worked_hours) + except : + pass + + + # work packages + work_packages = [str(wk.number) for wk in timesheet.work_packages.all()] + work_packages = ",".join(work_packages) + try : + self.sheet.write(project_row_index + self.wk_margin, timesheet.month + self.first_month_col, work_packages) + except: + pass + + try : + self.sheet.write(project_row_index, self.project_first_col + 1, timesheet.project.external_id, self.header_style) + self.sheet.write(project_row_index + self.percent_margin, self.percent_label_col, self.percent_label) + self.sheet.write(project_row_index + self.hours_margin, self.hours_label_col, self.hours_label) + self.sheet.write(project_row_index + self.wk_margin, self.wk_label_col, self.wk_label) + except: + pass + + try : + # accounting date + self.sheet.write(project_row_last + self.accounting_margin, timesheet.month + self.first_month_col, timesheet.accounting, self.date_style) + + # validation date + self.sheet.write(project_row_last + self.validation_margin, timesheet.month + self.first_month_col, timesheet.validation, self.date_style) + except: + pass def write(self): + self.format() self.export() response = HttpResponse(content_type="application/vnd.ms-excel") response['Content-Disposition'] = 'attachment; filename=users.xls' diff --git a/app/organization/network/views.py b/app/organization/network/views.py index 45e2e7e5..41cb3045 100644 --- a/app/organization/network/views.py +++ b/app/organization/network/views.py @@ -31,7 +31,7 @@ from organization.network.models import * from organization.core.views import * from datetime import date from organization.network.forms import * -from organization.network.utils import TimesheetXLS +from organization.network.utils import TimesheetXLS, TimesheetXLS2 class PersonListView(ListView): @@ -175,4 +175,5 @@ class PersonActivityTimeSheetExportView(TimesheetAbstractView, View): def get(self, *args, **kwargs): timesheets = PersonActivityTimeSheet.objects.filter(activity__person__slug__exact=kwargs['slug'], year=kwargs['year']) xls = TimesheetXLS(timesheets) - return xls.write() + response = xls.write() + return response