From 3283478b42877579d424df8cc48d8b6428b8dd9e Mon Sep 17 00:00:00 2001 From: raiots Date: Sun, 22 Aug 2021 23:24:47 +0800 Subject: [PATCH] update: new query method to calculate workload, the sub_executor_count field in database can be remove now. --- apps/tasks/my_query.py | 1 - apps/tasks/tests.py | 152 +++++++++++++++++++++ apps/tasks/urls.py | 3 +- apps/tasks/views.py | 272 ++++++++++++++++--------------------- templates/tasks/index.html | 26 ++-- 5 files changed, 281 insertions(+), 173 deletions(-) diff --git a/apps/tasks/my_query.py b/apps/tasks/my_query.py index f23059e..da3a34f 100644 --- a/apps/tasks/my_query.py +++ b/apps/tasks/my_query.py @@ -1,6 +1,5 @@ from django.db import connection - def my_annotate(): query1 = ''' CREATE TEMPORARY TABLE work_cal AS diff --git a/apps/tasks/tests.py b/apps/tasks/tests.py index 7ce503c..9448279 100644 --- a/apps/tasks/tests.py +++ b/apps/tasks/tests.py @@ -1,3 +1,155 @@ +from django.core import serializers +from django.core.serializers.json import DjangoJSONEncoder +from django.http import HttpResponse, JsonResponse +from django.shortcuts import render from django.test import TestCase # Create your tests here. +from django.db.models import Sum, F, FloatField, Count, Q + +from django.utils import timezone +from django.views import View + +from apps.users.models import User +from apps.tasks.models import Todo +import pandas as pd +from collections import defaultdict, Counter +from copy import deepcopy + + +class TestView(View): + def get(self, request, year=2021, month=7): + # sub_credit = User.objects.filter(department=request.user.department).annotate( + # pre_credit=Sum( + # (F('sub_executor__predict_work') * (1 - F('sub_executor__evaluate_factor')) / F( + # 'sub_executor__sub_executor_count') + # ), + # filter=Q(sub_executor__deadline__year=year, sub_executor__deadline__month=month), + # output_field=FloatField() + # ), + # real_credit=Sum( + # (F('sub_executor__real_work') * (1 - F('sub_executor__evaluate_factor')) / F( + # 'sub_executor__sub_executor_count') + # ), + # filter=Q(sub_executor__deadline__year=year, sub_executor__deadline__month=month), + # output_field=FloatField() + # ) + # ).values('id', 'real_name', 'pre_credit', 'real_credit') + # sub_credit = [entry for entry in sub_credit] + + # 建立username和真实姓名的对应字典,并在工作量计算完成后插入结果集 + user_info = User.objects.filter(department=request.user.department)\ + .values_list('username', 'real_name') + user_name = {} + for user in user_info: + user_name[user[0]] = user[1] + # print(user_name) + + # 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。 + main_credit = User.objects.filter(department=request.user.department)\ + .annotate(main_count=Count('main_executor'))\ + .order_by('username')\ + .values('username', 'main_executor', 'main_executor__predict_work', 'main_executor__real_work', + 'main_executor__evaluate_factor', 'main_count') # 这里的annotate不知会不会有问题 + + sub_count = Todo.objects.filter(sub_executor__department=request.user.department) \ + .annotate(sub_count=Count('sub_executor')).values('id', 'sub_count') + sub_credit = User.objects.filter(department=request.user.department) \ + .order_by('username') \ + .values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work', + 'sub_executor__evaluate_factor') + + # 构建工作包id对应协办人人数的字典 + sub_exe_count = {} + for i in sub_count: + key = i['id'] + value = i['sub_count'] + sub_exe_count[key] = value + # print(sub_exe_count) + + # 计算每个承办任务的预计、实际工作量,并插入字典 + for i in main_credit: + i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor'] + i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor'] + # print(i) + # print(str(i['sub_executor'])) + + # 将协办任务对应的人数插入,计算每个协办任务的预计、实际工作量,并插入字典 + for i in sub_credit: + sub_todo_id = i['sub_executor'] + i['sub_exe_count'] = sub_exe_count[sub_todo_id] + i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] + i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] + i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数,在累加时导致计算错误 + # print(i) + # print(str(i['sub_executor'])) + + # 用于按用户名合并任务,会对指定字段进行累加,类似GROUP BY(SUM),但会丢失无需计算的部分,因此之前需要单独构建姓名字典 + def solve(dataset, group_by_key, sum_value_keys): + dic = defaultdict(Counter) + for item in dataset: + key = item[group_by_key] + vals = {k: item[k] for k in sum_value_keys} + dic[key].update(vals) + return dic + + main_credit = solve(main_credit, 'username', ['main_pre_cal', 'main_real_cal', 'main_count']) + main_credit = dict(main_credit) + sub_credit = solve(sub_credit, 'username', ['sub_pre_cal', 'sub_real_cal', 'sub_count']) + sub_credit = dict(sub_credit) + total_credit = deepcopy(main_credit) + + # 按用户名合并承办与协办任务字典 + for key in sub_credit.keys(): + if key in total_credit: + total_credit[key].update(sub_credit[key]) + else: + total_credit[key] = sub_credit[key] + + # print(total_credit['admin']['sub_pre_cal']) + # 根据字典内容,计算总工作量 + for key, value in total_credit.items(): + # print(value) + value['pre_cal'] = value['sub_pre_cal'] + value['main_pre_cal'] + value['real_cal'] = value['sub_real_cal'] + value['main_real_cal'] + value['real_name'] = user_name[key] + # total_credit = dict(main_credit.items() + sub_credit.items()) + # for value in sub_credit.values(): + # dict(value) + # print(sub_credit) + # + # new_pair = {} + # for doc, tab in sub_credit.items(): + # new_pair[doc] = {} + # for word, freq in tab.items(): + # new_pair[doc][word] = freq + # print(new_pair) + # return HttpResponse(str(main_credit)+str(sub_credit)) + + current_user = total_credit[request.user.username] + print(current_user) + + # 累加该部门各个用户的工作量,计算部门工作量 + department_cal = {} + temp_pre = [] + depart_pre, depart_real, depart_count = 0, 0, 0 + for username, value in total_credit.items(): + print(username) + print(value['pre_cal']) + depart_pre = depart_pre + value['pre_cal'] + depart_real = depart_real + value['real_cal'] + depart_count = depart_count + value['main_count'] + temp_pre.append(value['pre_cal']) + department_cal['pre_cal'] = depart_pre + department_cal['real_cal'] = depart_real + department_cal['depart_count'] = depart_count + department_cal['pre_avg'] = department_cal['pre_cal'] / len(total_credit) + department_cal['real_avg'] = department_cal['real_cal'] / len(total_credit) + + # 为页面提供日期信息 + date = str(year) + '年' + str(month) + '月' + + # return HttpResponse(str(total_credit) + '\n' + str(department_cal)) + context = {'date': date, 'users_data': total_credit, 'department_cal': department_cal, + 'current_user': current_user} + return render(request, 'tasks/index.html', context) diff --git a/apps/tasks/urls.py b/apps/tasks/urls.py index 406fd54..e3ef483 100644 --- a/apps/tasks/urls.py +++ b/apps/tasks/urls.py @@ -1,10 +1,11 @@ from django.urls import path -from apps.tasks import views +from apps.tasks import views, tests app_name = 'tasks' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('//', views.IndexView.as_view(), name='index_month'), + path('test/', tests.TestView.as_view(), name='index'), path('login/', views.UserLoginView.as_view(), name='login'), path('logout/', views.UserLogoutView.as_view(), name='logout'), path('todolist/', views.TodoListView.as_view(), name='todolist'), diff --git a/apps/tasks/views.py b/apps/tasks/views.py index a4f7285..01d88df 100644 --- a/apps/tasks/views.py +++ b/apps/tasks/views.py @@ -1,4 +1,5 @@ import re +from collections import defaultdict, Counter from copy import deepcopy from django.contrib import auth, messages @@ -28,173 +29,128 @@ class IndexView(View): @method_decorator(login_required) def get(self, request, year=timezone.now().year, month=timezone.now().month): - raw = my_query.my_annotate() - # return HttpResponse(raw) - basic_users = User.objects.filter(department=request.user.department).annotate( - main_executor_count=Count('main_executor', - filter=Q(main_executor__deadline__year=year, main_executor__deadline__month=month) - , distinct=True), - sub_executor_count=Count('sub_executor', - filter=Q(sub_executor__deadline__year=year, sub_executor__deadline__month=month), - distinct=True) - ).values('id', 'real_name', 'main_executor_count', 'sub_executor_count') + # 建立username和真实姓名的对应字典,并在工作量计算完成后插入结果集 + user_info = User.objects.filter(department=request.user.department)\ + .values_list('username', 'real_name') + user_name = {} + for user in user_info: + user_name[user[0]] = user[1] + # print(user_name) - basic_users = [entry for entry in basic_users] - basic_user_cal = {} - for i in basic_users: - basic_user_cal.update({i['id']: i}) - print(basic_user_cal) + # 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。 + main_credit = User.objects.filter(department=request.user.department, + main_executor__deadline__year=year, main_executor__deadline__month=month)\ + .annotate(main_count=Count('main_executor'))\ + .order_by('username')\ + .values('username', 'main_executor', 'main_executor__predict_work', 'main_executor__real_work', + 'main_executor__evaluate_factor', 'main_count') # 这里的annotate不知会不会有问题 - current_user = User.objects.filter(username=request.user.username).annotate( - main_executor_count=Count('main_executor', - filter=Q(main_executor__deadline__year=year, main_executor__deadline__month=month) - , distinct=True), - sub_executor_count=Count('sub_executor', - filter=Q(sub_executor__deadline__year=year, sub_executor__deadline__month=month), - distinct=True), - pre_credit=Sum( - F('main_executor__predict_work') * F('main_executor__evaluate_factor'), - filter=Q(main_executor__deadline__year=year, main_executor__deadline__month=month), - distinct=True, - output_field=FloatField() - ), - real_credit=Sum( - F('main_executor__real_work') * F('main_executor__evaluate_factor'), - filter=Q(main_executor__deadline__year=year, main_executor__deadline__month=month), - distinct=True, - output_field=FloatField() - ) - ) + sub_count = Todo.objects.filter(sub_executor__department=request.user.department, + deadline__year=year, deadline__month=month) \ + .annotate(sub_count=Count('sub_executor')).values('id', 'sub_count') + sub_credit = User.objects.filter(department=request.user.department, + sub_executor__deadline__year=year, sub_executor__deadline__month=month) \ + .order_by('username') \ + .values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work', + 'sub_executor__evaluate_factor') - department_cal = Todo.objects.filter(main_executor__department=request.user.department, - deadline__year=year, deadline__month=month).aggregate(count=Count('id'), - pre_total=Sum('predict_work'), real_total=Sum('real_work'), pre_avg=Sum('predict_work'), real_avg=Sum('real_work')).values() - keys = ['count', 'pre_total', 'real_total', 'pre_avg', 'real_avg'] - temp = [] - for values in department_cal: - print(values) - temp.append(values) - #TODO 解决部门tem无数据报错问题 - try: - tem = [float(tem) for tem in temp] - whole = dict(zip(keys, tem)) - pre_avg = {'pre_avg': float(whole['pre_avg'])/request.user.department.get_user_number} - real_avg = {'real_avg': float(whole['real_avg']) / request.user.department.get_user_number} - whole.update(pre_avg) - whole.update(real_avg) - except: - whole = {} - print('无数据') - # print(whole) + # 构建工作包id对应协办人人数的字典 + sub_exe_count = {} + for i in sub_count: + key = i['id'] + value = i['sub_count'] + sub_exe_count[key] = value + # print(sub_exe_count) + # 计算每个承办任务的预计、实际工作量,并插入字典 + for i in main_credit: + i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor'] + i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor'] + # print(i) + # print(str(i['sub_executor'])) + # 将协办任务对应的人数插入,计算每个协办任务的预计、实际工作量,并插入字典 + for i in sub_credit: + sub_todo_id = i['sub_executor'] + i['sub_exe_count'] = sub_exe_count[sub_todo_id] + i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] + i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] + i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数,在累加时导致计算错误 + # print(i) + # print(str(i['sub_executor'])) + # 用于按用户名合并任务,会对指定字段进行累加,类似GROUP BY(SUM),但会丢失无需计算的部分,因此之前需要单独构建姓名字典 + def solve(dataset, group_by_key, sum_value_keys): + dic = defaultdict(Counter) + for item in dataset: + key = item[group_by_key] + vals = {k: item[k] for k in sum_value_keys} + dic[key].update(vals) + return dic + + main_credit = solve(main_credit, 'username', ['main_pre_cal', 'main_real_cal', 'main_count']) + main_credit = dict(main_credit) + sub_credit = solve(sub_credit, 'username', ['sub_pre_cal', 'sub_real_cal', 'sub_count']) + sub_credit = dict(sub_credit) + total_credit = deepcopy(main_credit) + + # 按用户名合并承办与协办任务字典 + for key in sub_credit.keys(): + if key in total_credit: + total_credit[key].update(sub_credit[key]) + else: + total_credit[key] = sub_credit[key] + + # print(total_credit['admin']['sub_pre_cal']) + # 根据字典内容,计算总工作量 + for key, value in total_credit.items(): + # print(value) + value['pre_cal'] = value['sub_pre_cal'] + value['main_pre_cal'] + value['real_cal'] = value['sub_real_cal'] + value['main_real_cal'] + value['real_name'] = user_name[key] + # total_credit = dict(main_credit.items() + sub_credit.items()) + # for value in sub_credit.values(): + # dict(value) + # print(sub_credit) + # + # new_pair = {} + # for doc, tab in sub_credit.items(): + # new_pair[doc] = {} + # for word, freq in tab.items(): + # new_pair[doc][word] = freq + # print(new_pair) + # return HttpResponse(str(main_credit)+str(sub_credit)) + + # 若total_credit为空,不进行以下操作,避免err + if total_credit: + current_user = total_credit[request.user.username] + + # 累加该部门各个用户的工作量,计算部门工作量 + department_cal = {} + temp_pre = [] + depart_pre, depart_real, depart_count = 0, 0, 0 + for username, value in total_credit.items(): + print(username) + print(value['pre_cal']) + depart_pre = depart_pre + value['pre_cal'] + depart_real = depart_real + value['real_cal'] + depart_count = depart_count + value['main_count'] + temp_pre.append(value['pre_cal']) + department_cal['pre_cal'] = depart_pre + department_cal['real_cal'] = depart_real + department_cal['depart_count'] = depart_count + department_cal['pre_avg'] = department_cal['pre_cal'] / len(total_credit) + department_cal['real_avg'] = department_cal['real_cal'] / len(total_credit) + else: + department_cal, current_user = {}, {} + + # 为页面提供日期信息 date = str(year) + '年' + str(month) + '月' - # points = [] - # point = User.objects.all() - # for i in point: - # points.append(i) - # points = User.objects.annotate(a=F('main_executor__evaluate_factor')) - # points = User.objects.annotate(a=Coalesce(Sum(F('main_executor__predict_work') * F('main_executor__evaluate_factor')) + Sum(F('sub_executor__predict_work') * F('sub_executor__evaluate_factor')), 0)) - # points = User.objects.annotate(a=Sum(F('main_executor__predict_work') * F('main_executor__evaluate_factor')), b=Sum(F('sub_executor__predict_work') * F('sub_executor__evaluate_factor'))) - # points = User.objects.annotate(a=Sum(F('main_executor__main_workload'))) - - main_credit = User.objects.filter(department=request.user.department, ).annotate( - pre_credit=Sum( - F('main_executor__predict_work') * F('main_executor__evaluate_factor'), - filter=Q(main_executor__deadline__year=year, main_executor__deadline__month=month), - distinct=True, - output_field=FloatField() - ), - real_credit=Sum( - F('main_executor__real_work') * F('main_executor__evaluate_factor'), - filter=Q(main_executor__deadline__year=year, main_executor__deadline__month=month), - distinct=True, - output_field=FloatField() - ) - ).values('id', 'real_name', 'pre_credit', 'real_credit') - main_credit = [entry for entry in main_credit] # converts ValuesQuerySet into Python list - - sub_credit = User.objects.filter(department=request.user.department).annotate( - pre_credit=Sum( - (F('sub_executor__predict_work') * (1 - F('sub_executor__evaluate_factor')) / F( - 'sub_executor__sub_executor_count') - ), - filter=Q(sub_executor__deadline__year=year, sub_executor__deadline__month=month), - output_field=FloatField() - ), - real_credit=Sum( - (F('sub_executor__real_work') * (1 - F('sub_executor__evaluate_factor')) / F( - 'sub_executor__sub_executor_count') - ), - filter=Q(sub_executor__deadline__year=year, sub_executor__deadline__month=month), - output_field=FloatField() - ) - ).values('id', 'real_name', 'pre_credit', 'real_credit') - sub_credit = [entry for entry in sub_credit] - - total_credit = main_credit + sub_credit - print(total_credit) - - basic_user_credit = {} - if total_credit: - df = pd.DataFrame(total_credit) - cols = ['id', 'real_name', 'pre_credit', 'real_credit'] - df = df.loc[:, cols] - result = df.groupby(['id', ]).sum() - cal_credit = result.T.to_dict('dict') - # print(cal_credit) - temp_credit = {} - basic_user_credit = {} - for key, values in cal_credit.items(): - # print(values['real_credit']) - temp_credit['pre_credit'] = values['pre_credit'] - temp_credit['real_credit'] = values['real_credit'] - # print(temp_credit) - basic_user_credit.update({key: temp_credit}) - # print(basic_user_credit) - temp_credit = {} - print(basic_user_credit) - else: - print("当前页Index无任何人员具有工作包") - - for key in basic_user_cal: - basic_user_cal[key].update(basic_user_credit[key]) - print(basic_user_cal) - - - - # ids = [] - # person_list = [] - # for person in total_points: - # id = person["id"] - # temp = {} - # if id not in ids: - # ids.append(id) - # temp_list = filter(lambda x: x["id"] == person["id"], total_points) - # for i in temp_list: - # temp.update(i) - # person_list.append(temp) - # else: - # continue - - # for i in main_points: - # for key, value in i.items(): - - # def sum_dict(a, b): - # temp = dict() - # # python3,dict_keys类似set; | 并集 - # for key in a.keys() | b.keys(): - # temp[key] = sum([d.get(key, 0) for d in (a, b)]) - # return temp - # - # def test(): - # # [a,b,c]列表中的参数可以2个也可以多个,自己尝试。 - # return print(reduce(sum_dict, [a, b, c])) - - context = {'users_data': basic_user_cal, 'date': date, 'current_user': current_user, 'department': whole} + # return HttpResponse(str(total_credit) + '\n' + str(department_cal)) + context = {'date': date, 'users_data': total_credit, 'department_cal': department_cal, + 'current_user': current_user} return render(request, 'tasks/index.html', context) diff --git a/templates/tasks/index.html b/templates/tasks/index.html index 67111a2..a69f372 100644 --- a/templates/tasks/index.html +++ b/templates/tasks/index.html @@ -36,7 +36,7 @@
部门总任务数 - {{ department.count|floatformat:'0'|default_if_none:'0' }} + {{ department_cal.depart_count|floatformat:'0'|default_if_none:'0' }}
@@ -50,7 +50,7 @@
部门预计总工作量 - {{ department.pre_total|default_if_none:'0'|floatformat:'1' }} + {{ department_cal.pre_cal|default_if_none:'0'|floatformat:'1' }}
@@ -64,7 +64,7 @@
部门实际总工作量 - {{ department.real_total|default_if_none:'0'|floatformat:'1' }} + {{ department_cal.real_cal|default_if_none:'0'|floatformat:'1' }}
@@ -79,7 +79,7 @@
部门预计平均工作量 - {{ department.pre_avg|default_if_none:'0'|floatformat:'1' }} + {{ department_cal.pre_avg|default_if_none:'0'|floatformat:'1' }}
@@ -93,7 +93,7 @@
部门实际平均工作量 - {{ department.real_avg|default_if_none:'0'|floatformat:'1' }} + {{ department_cal.real_avg|default_if_none:'0'|floatformat:'1' }}
@@ -157,7 +157,7 @@
承办任务数 - {% for user in current_user %}{{ user.main_executor_count }}{% endfor %} + {{ current_user.main_count }}
@@ -173,7 +173,7 @@
协办任务数 - {% for user in current_user %}{{ user.sub_executor_count }}{% endfor %} + {{ current_user.sub_count }}
@@ -187,7 +187,7 @@
预计工作量 - {% for user in current_user %}{{ user.pre_credit|default_if_none:'0' }}{% endfor %} + {{ current_user.pre_cal|floatformat:'1'|default_if_none:'0' }}
@@ -201,7 +201,7 @@
实际工作量 - {% for user in current_user %}{{ user.real_credit|default_if_none:'0' }}{% endfor %} + {{ current_user.real_cal|floatformat:'1'|default_if_none:'0' }}
@@ -376,7 +376,7 @@ pointStrokeColor : '#c1c7d1', pointHighlightFill : '#fff', pointHighlightStroke: 'rgba(220,220,220,1)', - data : [{% for user in users_data.values %}'{{ user.sub_executor_count }}', {% endfor %}] + data : [{% for user in users_data.values %}'{{ user.sub_count }}', {% endfor %}] }, { label : '承办任务', @@ -387,7 +387,7 @@ pointStrokeColor : 'rgba(60,141,188,1)', pointHighlightFill : '#fff', pointHighlightStroke: 'rgba(60,141,188,1)', - data : [{% for user in users_data.values %}'{{ user.main_executor_count }}', {% endfor %}] + data : [{% for user in users_data.values %}'{{ user.main_count }}', {% endfor %}] }, { label : '预计工作量', @@ -398,7 +398,7 @@ pointStrokeColor : 'rgba(60,141,188,1)', pointHighlightFill : '#fff', pointHighlightStroke: 'rgba(60,141,188,1)', - data : [{% for user in users_data.values %}'{{ user.pre_credit|floatformat:'1' }}', {% endfor %}] + data : [{% for user in users_data.values %}'{{ user.pre_cal|floatformat:'1' }}', {% endfor %}] }, { label : '工作量', @@ -409,7 +409,7 @@ pointStrokeColor : 'rgba(60,141,188,1)', pointHighlightFill : '#fff', pointHighlightStroke: 'rgba(60,141,188,1)', - data : [{% for user in users_data.values %}'{{ user.real_credit|floatformat:'1' }}', {% endfor %}] + data : [{% for user in users_data.values %}'{{ user.real_cal|floatformat:'1' }}', {% endfor %}] }, ] }