Compare commits

..

No commits in common. "master" and "v0.5" have entirely different histories.
master ... v0.5

65 changed files with 16693 additions and 33100 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
static/** linguist-vendored

View File

@ -1,20 +0,0 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
- 'releases/*'
jobs:
qodana:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@main
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}

View File

@ -1,30 +0,0 @@
name: Django CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
run: |
python manage.py test

5
.gitignore vendored
View File

@ -3,8 +3,3 @@
/packagesdir/
/db.sqlite3
/identifier.sqlite
migrations/
node_modules
.temp
.cache

View File

@ -1,17 +0,0 @@
# 从仓库拉取 带有 python 3.8 的 Linux 环境
FROM python:3.8
# 设置 python 环境变量
ENV PYTHONUNBUFFERED 1
# 创建 code 文件夹并将其设置为工作目录
RUN mkdir /code
WORKDIR /code
# 更新 pip
RUN pip install pip -U
# 将 requirements.txt 复制到容器的 code 目录
ADD requirements.txt /code/
# 安装库
RUN pip install -r requirements.txt -i https://mirrors.ustc.edu.cn/pypi/web/simple
# 将当前目录复制到容器的 code 目录
ADD . /code/

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Raiot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -9,7 +9,7 @@ Author [面壁的雨](https://raiot.me)
到时候会出一个文档的(或许
from 2020-7-13 to 2021-3-31
from 2020-7-13 to 2021-2-2
鬼知道为什么会用这么长时间
@ -18,27 +18,3 @@ from 2020-7-13 to 2021-3-31
#### 注意!
* 此版本仅为beta版本请勿在生产环境使用
### 介绍
这是一个由Django开发的任务分发系统
![QQ20210912003100.png](https://images.everains.com/images/2021/09/12/QQ20210912003100.png)
![QQ20210912003117.png](https://images.everains.com/images/2021/09/12/QQ20210912003117.png)
![QQ20210912003139.png](https://images.everains.com/images/2021/09/12/QQ20210912003139.png)
![QQ20210912003237.png](https://images.everains.com/images/2021/09/12/QQ20210912003237.png)
#### feature
* 员工可以看到自己负责的承办任务和协办任务
* 部门全年任务时间表
* 高定制度的后台管理页面
### Contributors
JetBrains OS License
TasksManager has been developed with PyCharm IDE under JetBrains Open Source License.

View File

@ -10,7 +10,6 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
import time
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
@ -26,7 +25,7 @@ SECRET_KEY = '&!38pk#dv=r!_c(+b&oegc0m(ndzoue+ez*7kvjv2uubuqootp'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['192.168.31.198', '127.0.0.1', '172.20.22.40', '110.42.209.79']
ALLOWED_HOSTS = []
# Application definition
@ -39,7 +38,6 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'import_export',
'apps.users',
'apps.tasks',
]
@ -136,10 +134,7 @@ LOGIN_REDIRECT_URL = '/'
# 自定义用户模型
AUTH_USER_MODEL = 'users.User'
# 它确定库是否会在数据导入中使用数据库事务,以确保安全。
IMPORT_EXPORT_USE_TRANSACTIONS = True
SITE_NAME = '任务管理工具'
# SimpleUI 配置
# 离线模式
@ -150,89 +145,8 @@ SIMPLEUI_HOME_INFO = False
SIMPLEUI_HOME_QUICK = True
SIMPLEUI_HOME_ACTION = True
SIMPLEUI_ANALYSIS = False
SIMPLEUI_HOME_TITLE = SITE_NAME
SIMPLEUI_HOME_TITLE = '任务管理系统'
# SIMPLEUI_HOME_PAGE = 'https://www.baidu.com' # 可用于嵌入其他链接,这里可以直接方便的嵌入报表链接
SIMPLEUI_HOME_ICON = 'el el-icon-platform-eleme'
# ICON 支持element-ui和fontawesome egfa fa-user
# https://zhuanlan.zhihu.com/p/113447102
# 指定simpleui默认的主题,指定一个文件名相对路径就从simpleui的theme目录读取
SIMPLEUI_DEFAULT_THEME = 'ant.design.css'
SIMPLEUI_CONFIG = {
'system_keep': False,
'menu_display': ['任务管理', '系统配置', '关于'], # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
'dynamic': True, # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
'menus': [{
'name': '任务管理',
'icon': 'fas fa-code',
'models': [{
'app': 'tasks',
'name': '年度任务',
'url': 'tasks/task'
}, {
'name': '工作包',
'url': 'tasks/todo?o=3'
}]
}, {
'app': 'admin',
'name': '系统配置',
'icon': 'fas fa-user-shield',
'models': [{
'name': '用户',
'icon': 'fa fa-user',
'url': 'users/user',
}, {
'name': '部门',
'url': 'users/department'
}, {
'name': '任务属性',
'url': 'users/taskproperty',
}, {
'name': '权限组',
'url': 'users/mygroup',
}, {
'name': '评价等级定义',
'url': 'users/qualitymark',
}, {
'name': '评价等级考核系数',
'url': 'users/markvalue'
}]
}, {
# 自2021.02.01+ 支持多级菜单models 为子菜单名
'name': '多级菜单测试',
'icon': 'fa fa-file',
# 二级菜单
'models': [{
'name': 'Baidu',
'icon': 'far fa-surprise',
# 第三级菜单
'models': [
{
'name': '爱奇艺',
'url': 'https://www.iqiyi.com/dianshiju/'
# 第四级就不支持了element只支持了3级
}, {
'name': '百度问答',
'icon': 'far fa-surprise',
'url': 'https://zhidao.baidu.com/'
}
]
}, {
'name': '内网穿透',
'url': 'https://www.wezoz.com',
'icon': 'fab fa-github'
}]
}, {
'name': '动态菜单测试',
'icon': 'fa fa-desktop',
'models': [{
'name': time.time(),
'url': 'http://baidu.com',
'icon': 'far fa-surprise'
}]
}, {
'name': '关于',
'url': '/about'
}]
}

View File

@ -15,13 +15,12 @@ Including another URLconf
"""
from django.contrib import admin
from django.urls import path, include
from TasksManager.settings import SITE_NAME
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('apps.tasks.urls', namespace='tasks')),
]
admin.site.site_header = SITE_NAME
admin.site.site_title = SITE_NAME
admin.site.index_title = SITE_NAME
admin.site.site_header = '任务进度管理系统'
admin.site.site_title = '任务进度管理系统'
admin.site.index_title = u'任务进度管理系统'

View File

@ -1,39 +1,13 @@
from datetime import datetime
import re
from django.contrib import admin
from django.http import JsonResponse
from django.utils.html import format_html
from import_export.admin import ImportExportModelAdmin
from import_export.formats import base_formats
from . import models
from apps.users.models import TaskProperty, User
from .resources import TodoResources, TaskResources
from apps.users.models import TaskProperty
class TodoInline(admin.StackedInline):
# 在Inline中同样筛选仅本部门的承办人、协办人
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'related_task':
kwargs["queryset"] = models.Task.objects.filter(department=request.user.department)
elif db_field.name == 'main_executor':
kwargs["queryset"] = User.objects.filter(department=request.user.department)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'sub_executor':
kwargs["queryset"] = User.objects.filter(department=request.user.department)
return super().formfield_for_manytomany(db_field, request, **kwargs)
model = models.Todo
extra = 0
# classes = ['collapse']
#TODO 选择年度任务时排序 https://www.codenong.com/40740869/
class TaskAdmin(ImportExportModelAdmin):
resource_class = TaskResources
class TaskAdmin(admin.ModelAdmin):
# def formfield_for_manytomany(self, db_field, request, **kwargs):
# if db_field.name == "related_task":
@ -46,36 +20,14 @@ class TaskAdmin(ImportExportModelAdmin):
# pass
# # kwargs["queryset"] = models.Task.objects.get(id=2).related_task
# 所属单位默认为访问用户的部门
def get_changeform_initial_data(self, request):
return {'department': request.user.department}
# 年度任务编辑界面仅显示本部门的任务属性
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'task_property':
kwargs["queryset"] = TaskProperty.objects.filter(own_department=request.user.department)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
# 仅显示当前部门的年度任务,除非为超管
def get_queryset(self, request):
qs = super(TaskAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(department=request.user.department)
def save_model(self, request, obj, form, change):
mvDict = dict(request.POST)
# 解决当工作包协办人数均为0时报错
# print(len(mvDict['related_task-0-sub_executor']))
try:
obj.related_task__sub_executor_count = int(len(mvDict['related_task-0-sub_executor']))
except:
obj.related_task__sub_executor_count = 0
super().save_model(request, obj, form, change)
list_display = (
'task_property', 'task_id', 'task_topic', 'task_origin', 'aim_value', 'deadline', 'duty_group', 'principal',
'leader', 'task_note',
@ -86,291 +38,60 @@ class TaskAdmin(ImportExportModelAdmin):
'fields': (
('task_property', 'task_id', 'task_topic', 'task_origin', 'aim_value', 'deadline', 'duty_group',
'principal', 'leader'),
'task_note', 'department'),
'task_note', 'related_task', 'department'),
}),
)
inlines = [TodoInline]
raw_id_fields = ("principal", "leader",)
list_display_links = ('task_topic',)
# autocomplete_fields = ('related_task',)
# search_fields = ('related_task',)
# 导入导出功能限制
def get_export_formats(self): # 该方法是限制格式为XLS
formats = (
base_formats.XLS,
)
return [f for f in formats if f().can_export()]
def has_import_permission(self, request): # 这是隐藏导入按钮,如果隐藏其他按钮也可以这样操作,
if request.user.is_superuser:
return True
else:
return False
autocomplete_fields = ('related_task',)
search_fields = ('related_task',)
# class TodoAdmin(admin.ModelAdmin):
class TodoAdmin(ImportExportModelAdmin):
resource_class = TodoResources
class TodoAdmin(admin.ModelAdmin):
def sub_executor(self, obj):
return ', '.join([a.real_name for a in obj.sub_executor.all()])
# 工作包页面仅显示所属本部门的年度任务、承办人、协办人
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'related_task':
kwargs["queryset"] = models.Task.objects.filter(department=request.user.department)
elif db_field.name == 'main_executor':
kwargs["queryset"] = User.objects.filter(department=request.user.department)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'sub_executor':
kwargs["queryset"] = User.objects.filter(department=request.user.department)
return super().formfield_for_manytomany(db_field, request, **kwargs)
# 仅显示当前部门的工作包,除非为超管
def get_queryset(self, request):
qs = super(TodoAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(related_task__department=request.user.department)
fieldsets = [
fieldsets = (
(None, {
'fields': [
'related_task', 'todo_topic', 'todo_note', 'deadline', 'duty_group', 'main_executor', 'sub_executor',
'sub_executor_count', 'predict_work', 'evaluate_factor', 'real_work'
],
'description': ''
'fields': (
'todo_topic', 'todo_note', 'deadline', 'duty_group', 'main_executor', 'sub_executor', 'predict_work',
'evaluate_factor',
)
}),
(None, {
'fields': [],
}),
]
('完成情况', {
'fields': (
'real_work', 'complete_note', 'quality_mark', 'maturity'
)
})
)
list_display = (
'todo_topic',
'deadline',
'todo_note',
'task_id',
'task_origin',
'lined_task',
# 'duty_department',
'task_origin',
'duty_department',
'duty_group',
'main_executor',
'list_sub_executor',
'predict_work',
'evaluate_factor',
'maturity',
'real_work',
'complete_note',
'quality_mark',
# 'sub_executor',
)
list_editable = ['quality_mark']
list_filter = ('deadline',)
list_filter = ('deadline', )
list_display_links = ('todo_topic', 'deadline', )
date_hierarchy = 'deadline'
list_per_page = 70 # 目的是取消自动分页好像有bug
# raw_id_fields = ("sub_executor",)
list_per_page = 20
raw_id_fields = ("main_executor", "sub_executor")
search_fields = ('todo_topic',)
ordering = ('related_task', )
readonly_fields = ["attachment"]
# ordering = ('task_id',)
def approval_state(self, obj):
return format_html('<span style="color:{};">{}</span>', 'green', obj.approval)
def task_id(self, obj):
return obj.task_id
task_id.admin_order_field = 'related_task__task_id'
task_id.short_description = '任务编号'
def task_origin(self, obj):
return obj.task_origin
task_origin.short_description = '任务来源'
def duty_department(self, obj):
return obj.duty_group
duty_department.short_description = '责任部门'
def lined_task(self, obj):
return obj.related_task
return obj.lined_task
task_id.admin_order_field = 'task__task_id'
task_id.short_description = '任务编号'
lined_task.admin_order_field = 'task__task_topic'
lined_task.short_description = '任务名称'
# 导入导出功能限制
def get_export_formats(self): # 该方法是限制格式为XLS
formats = (
base_formats.XLS,
)
return [f for f in formats if f().can_export()]
def has_import_permission(self, request): # 这是隐藏导入按钮,如果隐藏其他按钮也可以这样操作,
if request.user.is_superuser:
return True
else:
return False
# 对工作包页面选择其所属的年度任务中,对年度任务进行筛选。条件为:年度任务的完成时间不早于今年或年度任务中有工作包的完成时间晚于今年
def get_form(self, request, obj=None, **kwargs):
form = super(TodoAdmin, self).get_form(request, obj, **kwargs)
query = models.Task.objects.filter(
department=request.user.department, deadline__year__gte=datetime.now().strftime('%Y')).order_by('task_id')\
| models.Task.objects.filter(
department=request.user.department, related_task__deadline__year__gte=datetime.now().strftime('%Y'))\
.order_by('task_id')
form.base_fields['related_task'].queryset = query.distinct()
return form
# def save_model(self, request, obj, form, change):
# # 这一行代码写了一个晚上呜呜! 解决了当保存时,无法从未保存的数据中获取协办人数的问题!
# mvDict = dict(request.POST)
# dicts = request.POST
# print(dicts)
# for key, values in dicts:
# print(key, values)
# obj.user = request.user
# obj.sub_executor_count = int(len(mvDict['sub_executor']))
# super().save_model(request, obj, form, change)
# 增加批量操作按钮
actions = ['bulk_action']
def bulk_action(self, request, queryset):
post = request.POST
# 这里获取到数据后,可以做些业务处理
# post中的_action 是方法名
# post中 _selected 是选中的数据,逗号分割
if not post.get('_selected'):
return JsonResponse(data={
'status': 'error',
'msg': '请先选中数据!'
})
else:
return JsonResponse(data={
'status': 'success',
'msg': '处理成功!'
})
# 显示的文本与django admin一致
bulk_action.short_description = '批量操作'
# icon参考element-ui icon与https://fontawesome.com
bulk_action.icon = 'el-icon-files'
# 指定element-ui的按钮类型参考https://element.eleme.cn/#/zh-CN/component/button
bulk_action.type = 'warning'
# 给按钮追加自定义的颜色
bulk_action.style = 'color:white;'
# 指定为弹出层,这个参数最关键
bulk_action.layer = {
# 弹出层中的输入框配置
# 这里指定对话框的标题
'title': '弹出层输入框',
# 提示信息
'tips': '这个弹出对话框是需要在admin中进行定义数据新增编辑等功能需要自己来实现。',
# 确认按钮显示文本
'confirm_button': '确认提交',
# 取消按钮显示文本
'cancel_button': '取消',
# 弹出层对话框的宽度默认50%
'width': '40%',
# 表单中 label的宽度对应element-ui的 label-width默认80px
'labelWidth': "80px",
'params': [{
# 这里的type 对应el-input的原生input属性默认为input
'type': 'input',
# key 对应post参数中的key
'key': 'name',
# 显示的文本
'label': '名称',
# 为空校验默认为False
'require': True
}, {
'type': 'select',
'key': 'type',
'label': '类型',
'width': '200px',
# size对应elementui的size取值为medium / small / mini
'size': 'small',
# value字段可以指定默认值
'value': '0',
'options': [{
'key': '0',
'label': '收入'
}, {
'key': '1',
'label': '支出'
}]
}, {
'type': 'number',
'key': 'money',
'label': '金额',
# 设置默认值
'value': 1000
}, {
'type': 'date',
'key': 'date',
'label': '日期',
}, {
'type': 'datetime',
'key': 'datetime',
'label': '时间',
}, {
'type': 'rate',
'key': 'star',
'label': '评价等级'
}, {
'type': 'color',
'key': 'color',
'label': '颜色'
}, {
'type': 'slider',
'key': 'slider',
'label': '滑块'
}, {
'type': 'switch',
'key': 'switch',
'label': 'switch开关'
}, {
'type': 'input_number',
'key': 'input_number',
'label': 'input number'
}, {
'type': 'checkbox',
'key': 'checkbox',
# 必须指定默认值
'value': [],
'label': '复选框',
'options': [{
'key': '0',
'label': '收入'
}, {
'key': '1',
'label': '支出'
}, {
'key': '2',
'label': '收益'
}]
}, {
'type': 'radio',
'key': 'radio',
'label': '单选框',
'options': [{
'key': '0',
'label': '收入'
}, {
'key': '1',
'label': '支出'
}, {
'key': '2',
'label': '收益'
}]
}]
}
admin.site.register(models.Task, TaskAdmin)
admin.site.register(models.Todo, TodoAdmin)

View File

@ -1,38 +1,14 @@
from django import forms
from .models import Todo
class LoginForm(forms.Form):
username = forms.CharField(error_messages={'required': '用户名不能为空'})
password = forms.CharField()
remember = forms.BooleanField(required=False)
# TODO 数据不可为空
class TodoForm(forms.ModelForm):
required_css_class = 'required'
# (confused by Form & ModelForm https://stackoverflow.com/questions/2303268/djangos-forms-form-vs-forms-modelform)
# maturity = forms.ChoiceField(widget=forms.Select(attrs={'class': 'form-control'}), choices=(
# ('0%', '0%'),
# ('10%', '10%'),
# ('50%', '50%'),
# ('90%', '90%'),
# ('100%', '100%')
# ))
# real_work = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
# sub_executor = forms.MultipleChoiceField(widget=forms.SelectMultiple(attrs={'class': 'form-control'}))
class Meta:
model = Todo
fields = ['maturity', 'real_work', 'sub_executor', 'evaluate_factor', 'complete_note']
widgets = {'complete_note': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
# 'evaluate_factor': forms.NumberInput(attrs={'class': 'form-control'}),
}
def __init__(self, *args, **kwargs):
super(TodoForm, self).__init__(*args, **kwargs)
# self.fields['sub_executor'].widget.attrs['class'] = 'form-control'
fields = ['maturity', 'real_work', 'sub_executor', 'evaluate_factor', 'complete_note']
for i in fields:
self.fields[i].widget.attrs['class'] = 'form-control'
fields = ['maturity', 'complete_note']
labels ={'text': ''}
widgets = {'row': '3'}

View File

@ -0,0 +1,50 @@
# Generated by Django 3.1.5 on 2021-01-29 13:31
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Task',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_topic', models.CharField(max_length=50, verbose_name='任务名称')),
('task_id', models.CharField(max_length=50, unique=True, verbose_name='任务编号')),
('task_note', models.CharField(max_length=100, verbose_name='任务说明')),
('task_origin', models.CharField(max_length=150, verbose_name='任务来源')),
('aim_value', models.CharField(max_length=50, verbose_name='目标值')),
('deadline', models.DateField(verbose_name='完成时间')),
],
options={
'verbose_name': '年度任务',
'verbose_name_plural': '年度任务',
'ordering': ['deadline'],
},
),
migrations.CreateModel(
name='Todo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('todo_topic', models.CharField(max_length=50, verbose_name='工作事项')),
('todo_note', models.CharField(blank=True, max_length=100, verbose_name='工作要求及交付物')),
('predict_work', models.DecimalField(decimal_places=1, max_digits=5, verbose_name='预计工作量')),
('evaluate_factor', models.DecimalField(decimal_places=1, max_digits=5, verbose_name='折算系数')),
('maturity', models.CharField(blank=True, choices=[('0%', '0%'), ('10%', '10%'), ('20%', '20%'), ('30%', '30%'), ('40%', '40%'), ('50%', '50%'), ('60%', '60%'), ('70%', '70%'), ('80%', '80%'), ('90%', '90%'), ('100%', '100%')], default='0%', max_length=5, verbose_name='成熟度')),
('real_work', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True, verbose_name='实际工作量')),
('complete_note', models.TextField(blank=True, max_length=150, verbose_name='完成情况说明')),
('deadline', models.DateField(verbose_name='完成时间')),
],
options={
'verbose_name': '每月工作事项',
'verbose_name_plural': '每月工作事项',
'ordering': ['deadline'],
},
),
]

View File

@ -0,0 +1,69 @@
# Generated by Django 3.1.5 on 2021-01-29 13:31
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('tasks', '0001_initial'),
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='todo',
name='duty_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.mygroup', verbose_name='承办单位'),
),
migrations.AddField(
model_name='todo',
name='main_executor',
field=models.ManyToManyField(related_name='main_executor', to=settings.AUTH_USER_MODEL, verbose_name='承/督办人'),
),
migrations.AddField(
model_name='todo',
name='quality_mark',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='users.qualitymark', verbose_name='质量评价'),
),
migrations.AddField(
model_name='todo',
name='sub_executor',
field=models.ManyToManyField(blank=True, default='', related_name='sub_executor', to=settings.AUTH_USER_MODEL, verbose_name='协办人'),
),
migrations.AddField(
model_name='task',
name='department',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department', verbose_name='所属单位'),
),
migrations.AddField(
model_name='task',
name='duty_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.mygroup', verbose_name='责任单位'),
),
migrations.AddField(
model_name='task',
name='leader',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leader', to=settings.AUTH_USER_MODEL, verbose_name='主管领导'),
),
migrations.AddField(
model_name='task',
name='principal',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal', to=settings.AUTH_USER_MODEL, verbose_name='负责人'),
),
migrations.AddField(
model_name='task',
name='related_task',
field=models.ManyToManyField(blank=True, to='tasks.Todo', verbose_name='任务节点'),
),
migrations.AddField(
model_name='task',
name='task_property',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.taskproperty', verbose_name='任务属性'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.5 on 2021-01-29 13:35
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0002_auto_20210129_1332'),
('tasks', '0002_auto_20210129_1331'),
]
operations = [
migrations.AlterField(
model_name='task',
name='department',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='department', to='users.department', verbose_name='所属单位'),
),
migrations.AlterField(
model_name='task',
name='duty_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='duty_group', to='users.department', verbose_name='责任单位'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.1.5 on 2021-01-29 14:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0004_auto_20210129_1434'),
('tasks', '0003_auto_20210129_1335'),
]
operations = [
migrations.AlterField(
model_name='todo',
name='duty_group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department', verbose_name='承办单位'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.5 on 2021-01-30 20:21
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0005_auto_20210130_2021'),
('tasks', '0004_auto_20210129_1434'),
]
operations = [
migrations.AlterField(
model_name='task',
name='department',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='department', to='users.department', verbose_name='所属单位'),
),
migrations.AlterField(
model_name='task',
name='duty_group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='duty_group', to='users.department', verbose_name='责任单位'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.5 on 2021-02-01 19:00
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('tasks', '0005_auto_20210130_2021'),
]
operations = [
migrations.RemoveField(
model_name='todo',
name='main_executor',
),
migrations.AddField(
model_name='todo',
name='main_executor',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_executor', to=settings.AUTH_USER_MODEL, verbose_name='承/督办人'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.1.5 on 2021-02-01 19:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0006_auto_20210201_1900'),
('tasks', '0006_auto_20210201_1900'),
]
operations = [
migrations.AlterField(
model_name='todo',
name='quality_mark',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.qualitymark', verbose_name='质量评价'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.1.5 on 2021-02-01 22:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('tasks', '0007_auto_20210201_1901'),
]
operations = [
migrations.AlterModelOptions(
name='todo',
options={'ordering': ['deadline'], 'verbose_name': '工作包', 'verbose_name_plural': '工作包'},
),
]

View File

@ -1,8 +1,4 @@
import time
from django.db import models
from django.http import request
from apps.users.models import User, MyGroup, QualityMark, TaskProperty, Department
@ -16,34 +12,35 @@ class Todo(models.Model):
blank=True,
max_length=100
)
deadline = models.DateField(verbose_name='完成时间')
# related_task = models.ManyToManyField(Task, verbose_name="关联的主任务")
duty_group = models.ForeignKey('users.Department', on_delete=models.CASCADE, verbose_name='承办单位')
main_executor = models.ForeignKey(User, related_name='main_executor', on_delete=models.CASCADE,
verbose_name='承/督办人', blank=True, null=True)
sub_executor = models.ManyToManyField(User, related_name='sub_executor', verbose_name='协办人', blank=True)
sub_executor_count = models.CharField('协办人数', max_length=32)
related_task = models.ForeignKey('Task', related_name='related_task', on_delete=models.CASCADE, verbose_name='年度任务')
predict_work = models.DecimalField('预计工作量', default=0, max_digits=5, decimal_places=1, blank=False)
evaluate_factor = models.DecimalField('折算系数', max_digits=5, decimal_places=1, blank=True, default='1') #Todo 小数点后2位
main_executor = models.ForeignKey(User, related_name='main_executor', on_delete=models.SET_NULL, verbose_name='承/督办人', null=True)
sub_executor = models.ManyToManyField(User, related_name='sub_executor', verbose_name='协办人', default='', blank=True)
predict_work = models.DecimalField('预计工作量', max_digits=5, decimal_places=1)
evaluate_factor = models.DecimalField('折算系数', max_digits=5, decimal_places=1)
maturity = models.CharField(
verbose_name='成熟度',
max_length=5,
choices=(
('0%', '0%'),
('10%', '10%'),
('20%', '20%'),
('30%', '30%'),
('40%', '40%'),
('50%', '50%'),
('60%', '60%'),
('70%', '70%'),
('80%', '80%'),
('90%', '90%'),
('100%', '100%')
),
blank=True,
default='0%',
)
real_work = models.DecimalField('实际工作量', default=0, max_digits=5, decimal_places=1, blank=False)
complete_note = models.TextField('完成情况说明', max_length=500, blank=True)
quality_mark = models.ForeignKey('users.QualityMark', on_delete=models.SET_NULL, blank=True, null=True,
verbose_name='质量评价')
attachment = models.FileField('交付物查看', blank=True)
real_work = models.DecimalField('实际工作量', max_digits=5, decimal_places=1, blank=True, null=True)
complete_note = models.TextField('完成情况说明', max_length=150, blank=True)
quality_mark = models.ForeignKey('users.QualityMark', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='质量评价')
deadline = models.DateField(verbose_name='完成时间')
def __str__(self):
date = str(self.deadline)
@ -55,74 +52,55 @@ class Todo(models.Model):
verbose_name = '工作包'
verbose_name_plural = '工作包'
# def save(self, *args, **kwargs):
# super(Todo, self).save(*args, **kwargs)
# print(request.HttpRequest)
# # 直接保存报错 needs to have a value for field "id" before this many-to-many relationship can be used.
# # self.sub_executor_count = int(self.sub_executor.count())
# # Todo.objects.update(sub_executor_count=self.sub_executor.count())
@property
def lined_task(self):
lined_task = Task.objects.filter(related_task=self)
for task in lined_task:
return task.task_topic
# TODO 不知道有没有不用for循环直接查的
@property
def task_id(self):
return self.related_task.task_id
tasks = Task.objects.filter(related_task=self)
for task in tasks:
return task.task_id
@property
def task_origin(self):
return self.related_task.task_origin
@property
tasks = Task.objects.filter(related_task=self)
for task in tasks:
return task.task_origin
def duty_department(self):
return self.related_task.duty_group
tasks = Task.objects.filter(related_task=self)
for task in tasks:
return task.duty_group
@property
def last_month_list(self):
return self.deadline
@property
def get_total_num(self):
def get_tatal_num(self):
return self.objects.all().count()
@property
def points(self):
return int(self.predict_work * self.evaluate_factor)
@property
def main_workload(self):
return int(self.predict_work) * int(self.evaluate_factor)
@property
def sub_workload(self):
return self.predict_work * (1-int(self.evaluate_factor))/self.sub_executor.count
@classmethod
def sub_member(cls):
return cls.sub_executor.count
def list_sub_executor(self):
return ', '.join([a.real_name for a in self.sub_executor.all()])
list_sub_executor.short_description = '协办人'
class Task(models.Model):
task_topic = models.CharField(
verbose_name='任务名称',
max_length=50
)
task_id = models.CharField(max_length=50, unique=True, verbose_name='编号')
task_id = models.CharField(max_length=50, unique=True, verbose_name='任务编号')
task_note = models.CharField(
verbose_name='任务说明',
max_length=100,
blank=True
max_length=100
)
task_origin = models.CharField(max_length=150, verbose_name='任务来源', blank=True)
task_origin = models.CharField(max_length=150, verbose_name='任务来源')
task_property = models.ForeignKey('users.TaskProperty', on_delete=models.CASCADE, verbose_name='任务属性')
department = models.ForeignKey('users.Department', related_name='department', on_delete=models.SET_NULL, blank=True,
null=True, verbose_name='所属单位')
duty_group = models.ForeignKey('users.Department', related_name='duty_group', on_delete=models.SET_NULL, blank=True,
null=True, verbose_name='责任单位')
principal = models.ForeignKey(User, related_name='principal', verbose_name='负责人', on_delete=models.CASCADE, blank=True, null=True)
leader = models.ForeignKey(User, related_name='leader', verbose_name='主管领导', on_delete=models.CASCADE, blank=True, null=True)
aim_value = models.CharField(max_length=50, verbose_name='目标值', blank=True)
related_task = models.ManyToManyField(Todo, verbose_name='任务节点', blank=True)
department = models.ForeignKey('users.Department', related_name='department', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='所属单位')
duty_group = models.ForeignKey('users.Department', related_name='duty_group', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='责任单位')
principal = models.ForeignKey(User, related_name='principal', verbose_name='负责人', on_delete=models.CASCADE)
leader = models.ForeignKey(User, related_name='leader', verbose_name='主管领导', on_delete=models.CASCADE)
aim_value = models.CharField(max_length=50, verbose_name='目标值')
# start_date = models.DateField(verbose_name='起始日期')
deadline = models.DateField(verbose_name='完成时间')

View File

@ -1,40 +0,0 @@
from django.db import connection
def my_annotate():
query1 = '''
CREATE TEMPORARY TABLE work_cal AS
SELECT tasks_todo_sub_executor.*, tasks_todo.*
FROM tasks_todo_sub_executor
INNER JOIN tasks_todo
ON tasks_todo_sub_executor.todo_id = tasks_todo.id
ORDER BY user_id;
'''
query2 = '''
-- 计算每个工作包协办人数量并插入临时表
UPDATE work_cal SET sub_executor_count = (
SELECT COUNT(*) FROM tasks_todo_sub_executor
WHERE work_cal.todo_id = tasks_todo_sub_executor.todo_id);
'''
query3 = '''
SELECT exe_id, SUM(total_pre_work), SUM(total_real_work) FROM
(
-- 承办任务
SELECT main_executor_id AS exe_id, SUM(predict_work * evaluate_factor) AS total_pre_work,
SUM(real_work * tasks_todo.evaluate_factor) AS total_real_work
FROM tasks_todo
GROUP BY exe_id
UNION ALL
-- 协办任务
SELECT user_id AS exe_id, SUM(predict_work * (1 -evaluate_factor) / sub_executor_count) AS total_pre_work,
SUM(real_work * (1 - evaluate_factor) / sub_executor_count) AS total_real_work
FROM work_cal
GROUP BY exe_id
) AS init
GROUP BY exe_id
'''
cursor = connection.cursor()
cursor.execute(query1)
cursor.execute(query2)
cursor.execute(query3)
raw = cursor.fetchall()
return raw

View File

@ -1,16 +0,0 @@
from import_export import resources
from .models import Todo, Task
class TodoResources(resources.ModelResource):
class Meta:
model = Todo
fields = ('todo_topic', 'todo_note', 'deadline', 'duty_group__name', 'main_executor__real_name',
'sub_executor__real_name', 'sub_executor_count', 'related_task', 'predict_work', 'evaluate_factor',
'maturity', 'real_work', 'complete_note', 'quality_mark')
class TaskResources(resources.ModelResource):
class Meta:
model = Task

View File

@ -7,36 +7,26 @@ register = template.Library()
@register.filter(name='quarter_cate')
def quarter_cate(value, year_quarter):
year_now = datetime.now().strftime('%Y')
def quarter_cate(value, quarter):
month = value.deadline.strftime('%m')
year = value.deadline.strftime('%Y')
month = int(month)
# year = int(year)
# year_now = int(year) 不知道为什么如果转整数会把2021和2022认为相同
# print(quarter)
req_year = str(year_quarter[1])
quarter = int(year_quarter[0])
# 可能造成性能损失,每次数据库会调出符合“当年”的任务或工作包的全部任务下工作包,并逐个判断
if year == req_year:
if quarter == 1 and 1 <= month <= 3:
return str(value) + '&#13;&#10;'
quarter = int(quarter)
elif quarter == 2 and 4 <= month <= 6:
return str(value) + '&#13;&#10;'
if quarter == 1 and 1 <= month <= 3:
return value
elif quarter == 3 and 7 <= month <= 9:
return str(value) + '&#13;&#10;'
elif quarter == 2 and 4 <= month <= 6:
return value
elif quarter == 4 and 10 <= month <= 12:
return str(value) + '&#13;&#10;'
elif quarter == 3 and 7 <= month <= 9:
return value
elif quarter == 4 and 10 <= month <= 12:
return value
else:
return ''
else:
return ''
@register.filter(name='last_month')
def last_month(value):
curent_date = datetime.strptime(value, '%Y年%m月')
@ -44,7 +34,6 @@ def last_month(value):
last_month = last_date.strftime('%Y/%m')
return last_month
@register.filter(name='next_month')
def next_month(value):
curent_date = datetime.strptime(value, '%Y年%m月')
@ -52,28 +41,7 @@ def next_month(value):
next_month = next_date.strftime('%Y/%m')
return next_month
@register.filter(name='this_month')
def this_month(value):
curent_date = datetime.strptime(value, '%Y年%m月')
return curent_date.strftime('%m')
@register.filter(name='last_year')
def last_year(value):
curent_year = value[1]
last_year = curent_year - 1
return last_year
@register.filter(name='next_year')
def next_year(value):
curent_year = value[1]
next_year = curent_year + 1
return next_year
@register.filter(name='this_year')
def this_year(value):
curent_year = value[1]
return curent_year

View File

@ -1,212 +1,3 @@
import decimal
from django.core import serializers
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.functions import TruncMonth
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, ChainMap
from copy import deepcopy
# 用于按用户名合并任务会对指定字段进行累加类似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
def cal_method(main_list, sub_list, user_name):
# print(main_list, sub_list, user_name)
# sub_credit = sub_list
# main_credit = main_list
total_data = []
season = 1
# 分别计算每个季度的工作量、评价
for main_credit, sub_credit in zip(main_list, sub_list):
# TODO 对于完成质量的核算,先对评价求和,再除以已评价的承办任务数
# for i in main_credit:
# print(i['main_executor__quality_mark__mark_value__mark_value'])
# print(main_credit)
quality_dict = {}
for i in main_credit:
quality_dict[i['username']] = []
if i['main_executor__quality_mark__mark_value__mark_value'] != None:
quality_dict[i['username']].append(i['main_executor__quality_mark__mark_value__mark_value'])
for key, value in quality_dict.items():
if value:
quality_dict[key] = sum(value) / len(value)
# print(quality_dict)
main_credit = solve(main_credit, 'username', ['main_pre_cal', 'main_real_cal', 'main_count', 'main_executor__evaluate_factor'])
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]
value['season'] = season
# 由于不计算协办任务quality_dict会有空值情况
try:
value['quality'] = quality_dict[key]
except:
value['quality'] = 0
for value in total_credit.values():
# print(value)
real_cal_season = "real_cal_" + str(season)
value[real_cal_season] = value.pop("real_cal")
quality_season = "quality_" + str(season)
value[quality_season] = value.pop("quality")
# print(total_credit)
total_data.append(total_credit)
# print(season)
season += 1
# new_credit = []
# for item in total_credit:
# for key, value in item.items():
# print(total_data, season)
dd = defaultdict(list)
for d in total_data: # you can list as many input dicts as you want here
for key, value in d.items():
dd[key].append(value)
# print(dd)
return dd
class TestView(View):
def get(self, request, year=2021, month=7):
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]
work_cal = User.objects.filter(department=request.user.department, main_executor__deadline__year=2021) \
.order_by('main_executor__deadline').values('main_executor__todo_topic', 'main_executor__deadline')
# 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。
main_credit = User.objects.filter(department=request.user.department,
main_executor__deadline__year=year) \
.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_executor__maturity', 'main_executor__quality_mark__mark_value__mark_value', 'main_executor__deadline', 'main_count') # 这里的annotate不知会不会有问题
sub_count = Todo.objects.filter(sub_executor__department=request.user.department,
deadline__year=year) \
.annotate(sub_count=Count('sub_executor')).values('id', 'sub_count')
sub_credit = User.objects.filter(department=request.user.department,
sub_executor__deadline__year=year) \
.order_by('username') \
.values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work',
'sub_executor__evaluate_factor', 'sub_executor__maturity', 'sub_executor__quality_mark__mark_value__mark_value', 'sub_executor__deadline')
# 构建工作包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:
# 将成熟度由百分数转小数,以便其后与其他变量计算 eg. 50% -> 0.5
i['main_executor__maturity'] = decimal.Decimal(float(i['main_executor__maturity'].strip('%')) / 100)
# print(i['main_executor__maturity'])
i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
# 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_executor__maturity'] = decimal.Decimal(float(i['sub_executor__maturity'].strip('%')) / 100)
i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数在累加时导致计算错误
# print(i)
# print(str(i['sub_executor']))
main_Q1st, main_Q2nd, main_Q3th, main_Q4th = [], [], [], []
for i in main_credit:
# print(i['main_executor__deadline'].month)
deadline_month = i['main_executor__deadline'].month
if 1 <= deadline_month <= 3:
main_Q1st.append(i)
elif 4 <= deadline_month <= 6:
main_Q2nd.append(i)
elif 7 <= deadline_month <= 9:
main_Q3th.append(i)
elif 10 <= deadline_month <= 12:
main_Q4th.append(i)
# print(Q1st, Q2nd, Q3th, Q4th)
sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th = [], [], [], []
for i in sub_credit:
# print(i['main_executor__deadline'].month)
deadline_month = i['sub_executor__deadline'].month
if 1 <= deadline_month <= 3:
sub_Q1st.append(i)
elif 4 <= deadline_month <= 6:
sub_Q2nd.append(i)
elif 7 <= deadline_month <= 9:
sub_Q3th.append(i)
elif 10 <= deadline_month <= 12:
sub_Q4th.append(i)
main_list = [main_Q1st, main_Q2nd, main_Q3th, main_Q4th]
sub_list = [sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th]
result = cal_method(main_list, sub_list, user_name)
print(result)
stat = {}
for key, value in result.items():
stat[key] = {}
for j in value:
stat[key]['real_name'] = j['real_name']
season = str(j['season'])
real_cal_season = 'real_cal_' + season
quality_season = 'quality_' + season
stat[key][real_cal_season] = j[real_cal_season]
stat[key][quality_season] = j[quality_season]
print(j)
print(stat)
return HttpResponse(stat)
# return render(request, 'tasks/index.html', context)

View File

@ -1,13 +1,9 @@
from django.urls import path, include
# import debug_toolbar
from apps.tasks import views, tests
from TasksManager import settings
from django.urls import path
from apps.tasks import views
app_name = 'tasks'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:year>/<int:month>/', views.IndexView.as_view(), name='index_month'),
path('test/', tests.TestView.as_view(), name='test'),
path('login/', views.UserLoginView.as_view(), name='login'),
path('logout/', views.UserLogoutView.as_view(), name='logout'),
path('todolist/', views.TodoListView.as_view(), name='todolist'),
@ -16,13 +12,4 @@ urlpatterns = [
path('group_todolist/<int:year>/<int:month>/', views.GroupTodoList.as_view(), name='group_todolist_month'),
path('todo/<int:pk>/', views.TodoEntryView.as_view(), name='todo_detail'),
path('tasklist/', views.TaskListView.as_view(), name='tasklist'),
path('tasklist/<int:year>/', views.TaskListView.as_view(), name='tasklist_year'),
path('about/', views.AboutView.as_view(), name='about'),
]
#
# if settings.DEBUG:
# import debug_toolbar
# urlpatterns = [
# path('__debug__/', include(debug_toolbar.urls)),
# ] + urlpatterns

View File

@ -1,363 +1,20 @@
import re
from collections import defaultdict, Counter
from copy import deepcopy
from django.contrib import auth, messages
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
from django.db.models import Sum, F, FloatField, Count, Q
from django.db.models.functions import Coalesce
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.utils.decorators import method_decorator
import django.utils.timezone as timezone
from django.utils.http import url_has_allowed_host_and_scheme
from django.views import View
from django.db import connection
# Create your views here.
from apps.tasks.models import Todo, Task
from apps.tasks.forms import TodoForm
from apps.users.models import User
from . import my_query
from functools import reduce
import pandas as pd
import decimal
class IndexView(View):
@method_decorator(login_required)
def get(self, request, year=None, month=None):
# 搞成了!!! 当月份更改时自动变更首页显示的月份! 其实就是用的上面那个链接的方法,之前不知道为啥没去用
# https://stackoverflow.com/questions/63072235/django-localdate-doesnt-return-correct-date
# https://stackoverflow.com/questions/13225890/django-default-timezone-now-saves-records-using-old-time
# https://stackoverflow.com/questions/38237777/django-timezone-now-vs-timezone-now
if year is None:
year = timezone.now().year
if month is None:
month = timezone.now().month
#TODO 部门不用部门下用户累加
# 建立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,
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不知会不会有问题
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')
# 构建工作包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:
# 暂时解决当该用户当月无任务时total_credit中不包含该用户的用户名导致Key Error
try:
current_user = total_credit[request.user.username]
except:
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)
else:
department_cal, current_user = {}, {}
# 计算实际工作量、完成质量
def cal_method(main_list, sub_list, user_name):
# print(main_list, sub_list, user_name)
# sub_credit = sub_list
# main_credit = main_list
total_data = []
season = 1
# 分别计算每个季度的工作量、评价
for main_credit, sub_credit in zip(main_list, sub_list):
# TODO 对于完成质量的核算,先对评价求和,再除以已评价的承办任务数
# for i in main_credit:
# print(i['main_executor__quality_mark__mark_value__mark_value'])
# print(main_credit)
quality_dict = {}
for i in main_credit:
# 先赋值0避免前端显示个[]
quality_dict[i['username']] = []
if i['main_executor__quality_mark__mark_value__mark_value'] != None:
quality_dict[i['username']].append(i['main_executor__quality_mark__mark_value__mark_value'])
# TEST
for i in sub_credit:
# 先赋值0避免前端显示个[]
quality_dict[i['username']] = []
if i['sub_executor__quality_mark__mark_value__mark_value'] != None:
quality_dict[i['username']].append(i['sub_executor__quality_mark__mark_value__mark_value'])
for key, value in quality_dict.items():
if value:
quality_dict[key] = sum(value) / len(value)
# print(quality_dict)
main_credit = solve(main_credit, 'username',
['main_pre_cal', 'main_real_cal', 'main_count', 'main_executor__evaluate_factor'])
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]
value['season'] = season
# 由于不计算协办任务quality_dict会有空值情况
try:
value['quality'] = quality_dict[key]
except:
value['quality'] = 0
for value in total_credit.values():
# print(value)
real_cal_season = "real_cal_" + str(season)
value[real_cal_season] = value.pop("real_cal")
quality_season = "quality_" + str(season)
value[quality_season] = value.pop("quality")
# print(total_credit)
total_data.append(total_credit)
# print(season)
season += 1
# new_credit = []
# for item in total_credit:
# for key, value in item.items():
# print(total_data, season)
dd = defaultdict(list)
for d in total_data: # you can list as many input dicts as you want here
for key, value in d.items():
dd[key].append(value)
# print(dd)
return dd
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]
work_cal = User.objects.filter(department=request.user.department, main_executor__deadline__year=2021) \
.order_by('main_executor__deadline').values('main_executor__todo_topic', 'main_executor__deadline')
# 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。
main_credit = User.objects.filter(department=request.user.department,
main_executor__deadline__year=year) \
.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_executor__maturity', 'main_executor__quality_mark__mark_value__mark_value', 'main_executor__deadline', 'main_count') # 这里的annotate不知会不会有问题
sub_count = Todo.objects.filter(sub_executor__department=request.user.department,
deadline__year=year) \
.annotate(sub_count=Count('sub_executor')).values('id', 'sub_count')
sub_credit = User.objects.filter(department=request.user.department,
sub_executor__deadline__year=year) \
.order_by('username') \
.values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work',
'sub_executor__evaluate_factor', 'sub_executor__maturity', 'sub_executor__quality_mark__mark_value__mark_value', 'sub_executor__deadline')
# 构建工作包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:
# 将成熟度由百分数转小数,以便其后与其他变量计算 eg. 50% -> 0.5
print('ad', i['main_executor__maturity'])
# 临时补丁,解决用户将成熟度设置为空的问题,后面的协办任务也改了,记得改回去
# TODO 数据库中设置成熟度为非空
try:
i['main_executor__maturity'] = decimal.Decimal(float(i['main_executor__maturity'].strip('%')) / 100)
except:
i['main_executor__maturity'] = decimal.Decimal(float(0) / 100)
# print(i['main_executor__maturity'])
i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
# 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]
try:
i['sub_executor__maturity'] = decimal.Decimal(float(i['sub_executor__maturity'].strip('%')) / 100)
except:
i['sub_executor__maturity'] = decimal.Decimal(float(0) / 100)
i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数在累加时导致计算错误
# print(i)
# print(str(i['sub_executor']))
main_Q1st, main_Q2nd, main_Q3th, main_Q4th = [], [], [], []
for i in main_credit:
# print(i['main_executor__deadline'].month)
deadline_month = i['main_executor__deadline'].month
if 1 <= deadline_month <= 3:
main_Q1st.append(i)
elif 4 <= deadline_month <= 6:
main_Q2nd.append(i)
elif 7 <= deadline_month <= 9:
main_Q3th.append(i)
elif 10 <= deadline_month <= 12:
main_Q4th.append(i)
# print(Q1st, Q2nd, Q3th, Q4th)
sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th = [], [], [], []
for i in sub_credit:
# print(i['main_executor__deadline'].month)
deadline_month = i['sub_executor__deadline'].month
if 1 <= deadline_month <= 3:
sub_Q1st.append(i)
elif 4 <= deadline_month <= 6:
sub_Q2nd.append(i)
elif 7 <= deadline_month <= 9:
sub_Q3th.append(i)
elif 10 <= deadline_month <= 12:
sub_Q4th.append(i)
main_list = [main_Q1st, main_Q2nd, main_Q3th, main_Q4th]
sub_list = [sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th]
stat_result = cal_method(main_list, sub_list, user_name)
stat = {}
for key, value in stat_result.items():
stat[key] = {}
for j in value:
stat[key]['real_name'] = j['real_name']
season = str(j['season'])
real_cal_season = 'real_cal_' + season
quality_season = 'quality_' + season
stat[key][real_cal_season] = j[real_cal_season]
stat[key][quality_season] = j[quality_season]
print(stat_result)
# return HttpResponse(result.items())
# 为页面提供日期信息
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, 'stat': stat}
return render(request, 'tasks/index.html', context)
def get(self, request):
tasks = Task.objects.filter().order_by('task_id')
context = {'tasks': tasks}
return render(request, 'tasks/tasklist.html', context)
class TodoListView(View):
@ -366,36 +23,26 @@ class TodoListView(View):
my_todo = Todo.objects.filter(main_executor=request.user, deadline__year=year, deadline__month=month)
my_sub_todo = Todo.objects.filter(sub_executor=request.user, deadline__year=year, deadline__month=month)
date = str(year) + '' + str(month) + ''
current_path = request.get_full_path()
context = {'my_todo': my_todo, 'my_sub_todo': my_sub_todo, 'date': date, 'current_path': current_path}
context = {'my_todo': my_todo, 'my_sub_todo': my_sub_todo, 'date': date}
return render(request, 'tasks/todolist.html', context)
class GroupTodoList(View):
@method_decorator(login_required)
def get(self, request, year=timezone.now().year, month=timezone.now().month):
group_todo = Todo.objects.filter(main_executor__department=request.user.department, deadline__year=year,
deadline__month=month).order_by('related_task_id', 'deadline')
group_todo = Todo.objects.filter(main_executor__department=request.user.department, deadline__year=year, deadline__month=month)
date = str(year) + '' + str(month) + ''
current_path = request.get_full_path()
context = {'group_todo': group_todo, 'date': date, 'current_path': current_path}
context = {'group_todo': group_todo, 'date': date}
return render(request, 'tasks/group_todolist.html', context)
class TaskListView(View):
@method_decorator(login_required)
def get(self, request, year=timezone.now().year): # TODO 把timezone.now().year写在后面要用year替换的地方是否可以解决
tasks = Task.objects.filter(department=request.user.department, deadline__year=year).order_by('task_id')\
| Task.objects.filter(department=request.user.department, related_task__deadline__year=year).order_by('task_id')
tasks = tasks.distinct()
# tasks = Task.objects.filter(Q(department=request.user.department), Q(deadline__year=year) | Q(related_task__deadline__year=year)).order_by('task_id')
# 使用‘或’,找出工作包/年度任务的截止日期在今年的年度任务。后面还要做一个筛选,以达到只显示本年度的工作包
year_quarter = {'1': [1, year], '2': [2, year], '3': [3, year], '4': [4, year]}
context = {'tasks': tasks, 'year_quarter': year_quarter}
def get(self, request):
tasks = Task.objects.filter().order_by('task_id')
context = {'tasks': tasks}
return render(request, 'tasks/tasklist.html', context)
class UserLoginView(View):
def get(self, request):
return render(request, 'tasks/login.html')
@ -411,7 +58,6 @@ class UserLoginView(View):
else:
return redirect('tasks:index')
class UserLogoutView(View):
def get(self, request):
logout(request)
@ -428,16 +74,7 @@ class TodoEntryView(View):
def post(self, request, pk):
todo_detail = Todo.objects.get(id=pk)
form = TodoForm(instance=todo_detail, data=request.POST)
redirect_to = request.GET.get('next')
if form.is_valid():
form.save()
if url_has_allowed_host_and_scheme(redirect_to, None):
return redirect(redirect_to)
else:
return redirect('tasks:todolist')
return redirect('tasks:index')
# return redirect('tasks:todo_detail', pk=pk)
class AboutView(View):
def get(self, request):
return render(request, 'tasks/about.html')

View File

@ -23,54 +23,23 @@ class MyUserAdmin(UserAdmin):
class MyGroupAdmin(GroupAdmin):
pass
class DepartmentAdmin(admin.ModelAdmin):
pass
class TaskPropertyAdmin(admin.ModelAdmin):
list_display = (
'task_property', 'own_department'
)
def get_queryset(self, request):
qs = super(TaskPropertyAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(own_department=request.user.department)
class QualityMarkAdmin(admin.ModelAdmin):
list_display = (
'mark_name',
'mark_value'
)
def mark_value(self):
return self.mark_value
mark_value.short_description = 'ss'
# # 仅显示当前部门的任务属性,除非为超管
# def get_queryset(self, request):
# qs = super(QualityMarkAdmin, self).get_queryset(request)
# if request.user.is_superuser:
# return qs
# else:
# return qs.filter(department=request.user.department)
class MarkValueAdmin(admin.ModelAdmin):
# def get_queryset(self, request):
# qs = super(MarkValueAdmin, self).get_queryset(request)
# if request.user.is_superuser:
# return qs
# else:
# return qs.filter(department=request.user.department)
pass
admin.site.register(models.User, MyUserAdmin)
admin.site.register(models.MyGroup, MyGroupAdmin)
admin.site.register(models.MarkValue, MarkValueAdmin)
admin.site.register(models.MarkValue)
admin.site.register(models.Department)
admin.site.register(models.QualityMark, QualityMarkAdmin)
admin.site.register(models.TaskProperty, TaskPropertyAdmin)

View File

@ -0,0 +1,97 @@
# Generated by Django 3.1.5 on 2021-01-29 13:31
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='Department',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='部门名称')),
],
options={
'verbose_name': '部门',
},
),
migrations.CreateModel(
name='MarkValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mark_value', models.DecimalField(decimal_places=2, max_digits=3)),
],
),
migrations.CreateModel(
name='MyGroup',
fields=[
('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.group')),
],
options={
'verbose_name': '权限组',
'verbose_name_plural': '权限组',
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
migrations.CreateModel(
name='TaskProperty',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_property', models.CharField(max_length=50, verbose_name='任务属性')),
],
options={
'verbose_name': '任务属性',
'verbose_name_plural': '任务属性',
},
),
migrations.CreateModel(
name='QualityMark',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mark_name', models.CharField(max_length=10, verbose_name='评分')),
('mark_value', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.markvalue')),
],
),
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('real_name', models.CharField(max_length=150, verbose_name='姓名')),
('staff_id', models.CharField(max_length=150, verbose_name='工号')),
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': '用户',
'verbose_name_plural': '用户',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.1.5 on 2021-01-29 13:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='department',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='users.department'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 3.1.5 on 2021-01-29 14:17
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0002_auto_20210129_1332'),
]
operations = [
migrations.AlterModelOptions(
name='department',
options={'verbose_name': '部门', 'verbose_name_plural': '部门'},
),
migrations.AddField(
model_name='taskproperty',
name='own_department',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='users.department'),
preserve_default=False,
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.1.5 on 2021-01-29 14:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0003_auto_20210129_1417'),
]
operations = [
migrations.AlterField(
model_name='taskproperty',
name='own_department',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department', verbose_name='所属部门'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 3.1.5 on 2021-01-30 20:21
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0004_auto_20210129_1434'),
]
operations = [
migrations.AlterField(
model_name='taskproperty',
name='own_department',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.department', verbose_name='所属部门'),
),
migrations.AlterField(
model_name='user',
name='department',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.department'),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 3.1.5 on 2021-02-01 19:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0005_auto_20210130_2021'),
]
operations = [
migrations.AlterModelOptions(
name='markvalue',
options={'verbose_name': '评分值', 'verbose_name_plural': '评分值'},
),
migrations.AlterModelOptions(
name='qualitymark',
options={'verbose_name': '评分名称', 'verbose_name_plural': '评分名称'},
),
migrations.AlterField(
model_name='markvalue',
name='mark_value',
field=models.DecimalField(decimal_places=2, max_digits=3, verbose_name='评分值'),
),
migrations.AlterField(
model_name='qualitymark',
name='mark_name',
field=models.CharField(max_length=10, verbose_name='评分名称'),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 3.1.5 on 2021-02-01 22:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0006_auto_20210201_1900'),
]
operations = [
migrations.AlterModelOptions(
name='markvalue',
options={'verbose_name': '评价等级考核系数', 'verbose_name_plural': '评价等级考核系数'},
),
migrations.AlterModelOptions(
name='qualitymark',
options={'verbose_name': '评价等级定义', 'verbose_name_plural': '评价等级定义'},
),
migrations.AlterField(
model_name='markvalue',
name='mark_value',
field=models.DecimalField(decimal_places=2, max_digits=3, verbose_name='评价等级考核系数'),
),
migrations.AlterField(
model_name='qualitymark',
name='mark_name',
field=models.CharField(max_length=10, verbose_name='评价等级定义'),
),
]

View File

@ -3,13 +3,10 @@ from django.contrib.auth.models import AbstractUser, Group
# Create your models here.
from django.db.models import Avg, Sum, F, Value
class User(AbstractUser):
real_name = models.CharField(max_length=150, verbose_name='姓名')
staff_id = models.CharField(max_length=150, verbose_name='工号')
department = models.ForeignKey('Department', related_name='member', on_delete=models.SET_NULL, null=True, blank=True)
department = models.ForeignKey('Department', on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
verbose_name = '用户'
@ -18,16 +15,6 @@ class User(AbstractUser):
def __str__(self):
return self.real_name
@classmethod
def get_total_point(cls):
return cls.objects.annotate(total=Sum('main_executor__predict_work'))
# def get_total_point(cls):
# return cls.objects.aggregate(total=Sum(F('main_executor__predict_work') * F('main_executor__evaluate_factor') + F('sub_executor__predict_work') * F('sub_executor__evaluate_factor')))['total']
@classmethod
def get_predict_work_count(cls):
return cls.objects.aggregate(total_predict_work=Sum('main_executor__predict_work'))
class MyGroup(Group):
class Meta:
@ -45,10 +32,6 @@ class Department(models.Model):
verbose_name = '部门'
verbose_name_plural = '部门'
@property
def get_user_number(self):
return self.member.count()
class MarkValue(models.Model):
mark_value = models.DecimalField('评价等级考核系数', max_digits=3, decimal_places=2)

View File

@ -1,10 +0,0 @@
version: "3"
services:
app:
restart: always
build: . # '点'代表当前目录
command: "python3 manage.py runserver 0.0.0.0:8000"
volumes:
- .:/code
ports:
- "8000:8000"

View File

@ -1,7 +0,0 @@
import { defineClientConfig } from '@vuepress/client'
export default defineClientConfig({
enhance({ app, router, siteData }) {},
setup() {},
rootComponents: [],
})

View File

@ -1,37 +0,0 @@
// module.exports = {
// lang: 'zh-CN',
// title: 'TasksManager',
// description: '超好用的任务分配管理系统',
// }
//
import {defaultTheme, defineUserConfig} from 'vuepress'
export default defineUserConfig({
lang: 'zh-CN',
title: 'TasksManager',
description: '超好用的任务分配管理系统',
theme: defaultTheme({
navbar: [
{
text: '指南',
link: '/guide/',
},
{
text: '部署',
link: '/install/',
}
],
sidebar: "auto",
repo: 'https://github.com/raiots/TasksManager',
repoLabel: '✨Github',
docsDir: 'docs',
docsBranch: 'master',
lastUpdatedText: "📑 最后更新",
contributorsText: "💕 参与贡献",
editLinkText: "🖊️ 编辑本文",
notFound: ["👻 页面不存在"],
}),
})

View File

@ -1,20 +0,0 @@
---
home: true
heroText: TasksManager
tagline: 超好用的任务分配管理工具❤️
actions:
- text: 项目简介
link: /guide/
type: primary
- text: 快速部署
link: /install/
type: secondary
features:
- title: 任务分发
details: TasksManager 管理员能够以年度任务为主线,向用户发送待办任务
- title: 任务提交
details: 用户可以查看自己的待办任务、并提交完成度与完成情况说明,帮助管理员掌握部门工作情况
- title: 评价考核
details: TasksManager 会对用户完成任务数量与质量进行综合评价,并直观显示于首页
---

View File

@ -1,6 +0,0 @@
---
sidebar: auto
---
TasksManager 是一个由Django开发的任务分发系统

View File

@ -1,81 +0,0 @@
---
sidebar: auto
navbar: True
---
# 部署
你可以使用两种方式部署 TasksManager这里推荐使用 Docker compose 的方式进行部署。
## Docker Compose 部署
> Docker Compose 是一个用于在使用Compose 文件格式定义的 Docker 上运行多容器应用程序的工具。
### 安装
拉取 TaskManager 程序源码
```shell
git clone https://github.com/raiots/TasksManager.git
```
### 启动
使用 cd 命令进入程序文件夹后启动程序:
```shell
docker-compose up -d
```
TasksManager 将会运行在服务器的 8000 端口,在浏览器中打开 http://ip地址:8000 即可访问
## 使用源码手动部署
> 程序使用 Python3.8 开发,请提前配置 Python 环境
### 安装
使用 git 下载 TasksManager 源码:
```shell
git clone https://github.com/raiots/TasksManager.git
cd TasksManager
```
创建并激活 Python 虚拟环境
```shell
python -m venv venv
.\venv\Scripts\activate.sh
```
安装 TasksManager 依赖
```shell
pip install -r requirements.txt
```
### 启动
```shell
python3 manage.py runserver 0.0.0.0:8000
```
TasksManager 将会运行在服务器的 8000 端口,在浏览器中打开 http://ip地址:8000 即可访问
## 使用 Nginx 配置反向代理
```
server
{
listen 443 ssl http2;
server_name your.domain.com ;
location / {
proxy_pass http://127.0.0.1:8000; # 转发规则
proxy_set_header Host $proxy_host; # 修改转发请求头让8000端口的应用可以受到真实的请求
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```

View File

@ -1,9 +0,0 @@
{
"devDependencies": {
"vuepress": "^2.0.0-beta.60"
},
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}

View File

@ -1,21 +1,7 @@
asgiref==3.3.1
defusedxml==0.7.1
diff-match-patch==20200713
Django==3.1.12
django-import-export==2.5.0
django-simpleui==2021.8.1
et-xmlfile==1.0.1
MarkupPy==1.14
numpy==1.20.2
odfpy==1.4.1
openpyxl==3.0.7
pandas==1.2.4
Django==3.1.5
django-simpleui==2021.1.1
python-dateutil==2.8.1
pytz==2020.5
PyYAML==5.4.1
six==1.15.0
sqlparse==0.4.1
tablib==3.0.0
xlrd==2.0.1
xlwt==1.3.0
django-debug-toolbar~=3.2.2

View File

@ -1,40 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nYivN04w.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nRivN04w.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nWivN04w.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* latin-ext */
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nbivN04w.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nVivM.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
BORDER-TOP: 0px solid;
/*width:100%;*/
overflow:hidden;
width:160px;
width:200px;
font-size: 0.5rem;
}
@ -13,8 +13,3 @@
text-align: center;
vertical-align: middle;
}
.col-xs-1-5 {
width: 20%;
float: left;
}

View File

@ -1,234 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ABOUT - Raiot</title>
<link rel="stylesheet" href="{% static 'tasks/nes.min.css' %}">
<style>
@charset "utf-8";
@import url({% static 'tasks/about_font.css' %});
@font-face {
font-family: 'pixel';
src: url('{% static 'tasks/pixel.ttf' %}');
}
body {
padding: 0 2rem;
margin: 0 2rem;
}
#nescss>.container {
max-width: 980px;
margin: 0 auto;
margin-top: 150px;
}
main p {
font-size: 1.2rem;
}
h2 {
font-size: 1.6rem;
}
/* Header */
header {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 9;
border-bottom: 4px solid #D3D3D3;
background-color: white;
}
header>.container {
display: flex;
align-items: baseline;
max-width: 980px;
margin: 0 auto;
padding-top: 1rem;
transition: all 0.2s ease;
}
header>.container>.nav-brand {
margin-right: auto;
}
.nav-brand .brand-logo {
margin-right: 1rem;
}
.nav-brand>a {
color: #212529;
text-decoration: none;
}
/* Main */
.main-content {
margin-bottom: 4rem;
}
/* Footer */
footer {
text-align: center;
margin-bottom: 2rem;
}
footer a {
color: #333;
text-decoration: none;
}
h2>a {
margin-right: 1rem;
}
.topic {
margin-bottom: 3rem;
font-family: 'pixel';
}
/* Containers */
.item.containers>.nes-container {
display: inline-block;
max-width: 400px;
}
/* Topic */
h3.topic-title {
display: flex;
align-items: center;
margin-bottom: 1rem;
}
h3.topic-title>i {
margin-right: 1.5rem;
}
@media screen and (max-width: calc(980px - 4rem)) {
header>.container {
margin: 0 4rem;
}
}
@media screen and (max-width: 768px) {
body {
margin: 2rem 0.5rem;
padding: 0;
}
header>.container {
margin: 0 0.5rem;
}
.github-link {
display: none;
}
.message-list>.message>.nes-balloon {
max-width: 60%;
}
}
</style>
</head>
<body cz-shotcut-listen="true">
<div id="nescss">
<header class="">
<div class="container">
<div class="nav-brand">
<a href="#">
<h1>STAFF</h1>
</a>
<p>©&nbsp;2021 RAIOT</p>
</div>
</div>
</header>
<div class="container">
<main class="main-content">
<section class="topic">
<h2>
<a href="#">#</a>
MIT许可证
</h2>
<p>
特此免费授予任何获得副本的人这个软件和相关的文档文件(“软件”)来处理在软件中没有限制,包括但不限于权利使用,复制,
修改,合并,发布,分发,再许可和/或销售该软件的副本,并允许软件所属的人员提供这样做,但须符合以下条件:
<br>
上述版权声明和本许可声明必须包含在内本软件的副本或实质性部分。
<br>
本软件按“原样”提供,不附有任何形式的明示或暗示保证默示的,包括但不限于对适销性的保证,适用于特定目的和不侵权。在任何
情况下,作者或版权持有者对任何索赔,损坏或其他责任均不负任何责任责任,无论是在合同,民事侵权行为或其他方面,与本软件
或本软件的使用或其他交易有关或与之有关软件。
</p>
</section>
<section class="topic">
<h2>
<a href="#">#</a>
关于我
</h2>
<p>
Django开发的练习之作请不要在意混乱的代码
</p>
</section>
<section class="topic">
<h2>
<a href="#">#</a>
免责声明
</h2>
<p>您当前所访问的站点使用的是下面所提到的开源程序。您能看到这个页面仅代表这个站点使用了本程序,其使用过程中发生的一切问题需要其自行解决,本程序不承担任何责任。</p>
<div class="nes-table-responsive">
<table class="nes-table is-bordered">
<tbody>
<tr>
<td>作者</td>
<td><a href="#">Raiot.Me</a></td>
</tr>
<tr>
<td>维护</td>
<td>
<a href="#">Raiot.Me</a>
</td>
</tr>
<tr>
<td>鸣谢</td>
<td>所有被引用过代码的同学以及所有提交过PR的同学这份About界面框架的作者。当然还有在使用这份程序的你我Ta。</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="topic">
<h2>
<a href="#">#</a>
Credits
</h2>
<div class="lists">
<ul class="nes-list is-circle">
<li>
<a href="#" target="_blank" rel="noopener">
NES.css
</a>
&nbsp;-&nbsp;NES-style CSS Framework
</li>
<li>
<a href="#" target="_blank" rel="noopener">Simple-UI</a>
&nbsp;-&nbsp;Django后台模板
</li>
<li>
<a href="#" target="_blank" rel="noopener">AdminLTE</a>
&nbsp;-&nbsp;Free Bootstrap Admin Template
</li>
</ul>
</div>
</section>
</main>
</div>
</div>
</body>
</html>

View File

@ -9,7 +9,7 @@ scratch. This page gets rid of all links and provides the needed markup only.
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>任务管理工具</title>
<title>任务进度管理系统</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="{% static 'tasks/dist/css/googlefont.css' %}">
@ -32,7 +32,7 @@ scratch. This page gets rid of all links and provides the needed markup only.
<div class="container-xl">
<a href="{% url 'tasks:index' %}" class="navbar-brand">
<img src="{% static 'tasks/dist/img/AdminLTELogo.png' %}" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" style="opacity: .8">
<span class="brand-text font-weight-light">任务管理工具</span>
<span class="brand-text font-weight-light">任务管理系统</span>
</a>
<button class="navbar-toggler order-1" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
@ -53,18 +53,10 @@ scratch. This page gets rid of all links and provides the needed markup only.
<a href="{% url 'tasks:tasklist' %}" class="nav-link">年度任务</a>
</li>
<li class="nav-item">
<a href="/admin/#tasks/task" class="nav-link">编辑任务</a>
</li>
<li class="nav-item dropdown">
<a id="dropdownSubMenu1" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle">管理域风险分析</a>
<ul aria-labelledby="dropdownSubMenu1" class="dropdown-menu border-0 shadow">
<li><a href="{% url 'tasks:todolist' %}" class="dropdown-item" style="font-size: 0.9rem"> 风险事件采集 </a></li>
<li><a href="{% url 'tasks:group_todolist' %}" class="dropdown-item" style="font-size: 0.9rem">风险事件批量采集</a></li>
<li><a href="{% url 'tasks:group_todolist' %}" class="dropdown-item" style="font-size: 0.9rem">可视化分析</a></li>
</ul>
<a href="/admin" class="nav-link">编辑任务</a>
</li>
<li class="nav-item">
<a href="/admin/#users/user" class="nav-link">系统配置</a>
<a href="/admin" class="nav-link">系统配置</a>
</li>
{# <li class="nav-item dropdown">#}
{# <a id="dropdownSubMenu1" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle">统计与分析</a>#}
@ -197,9 +189,7 @@ scratch. This page gets rid of all links and provides the needed markup only.
{# </li>#}
{% if user.is_authenticated %}
<li class="nav-tabs">
<a>欢迎,</a>
<b style="color: cadetblue">{{ user.real_name }}</b>
<a>。 本软件为非涉密应用工具,禁止处理秘密级及以上信息!!!</a>
<a>欢迎,{{ user.real_name }}</a>
</li>
<li class="navbar-tabs">
<a href="{% url 'tasks:logout' %}">&nbsp;登出</a>
@ -312,10 +302,10 @@ scratch. This page gets rid of all links and provides the needed markup only.
<footer class="main-footer">
<!-- To the right -->
<div class="float-right d-none d-sm-inline">
CHRDI Task Manager <a href="{% url 'tasks:about' %}">ver 0.6 - beta</a>
CHRDI Task Manager <a href="#">ver 0.5 - beta</a>
</div>
<!-- Default to the left -->
<strong>Made with ❤ by <a href="{% url 'tasks:about' %}">Raiot</a>.</strong>
<strong>Made with ❤ by <a href="https://raiot.me">Raintony</a>.</strong>
</footer>
</div>
<!-- ./wrapper -->
@ -329,8 +319,5 @@ scratch. This page gets rid of all links and provides the needed markup only.
<script src="{% static 'tasks/plugins/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
<!-- AdminLTE App -->
<script src="{% static 'tasks/dist/js/adminlte.min.js' %}"></script>
{% block script %}
{% endblock %}
</body>
</html>

View File

@ -58,20 +58,18 @@
<table class="table" style="word-break: break-all">
<thead>
<tr>
<th style="width: 16px; text-align:center; vertical-align: middle;">序号</th>
<th style="width: 10px; text-align:center; vertical-align: middle;">#</th>
<th style="width: 280px; text-align:center; vertical-align: middle;">工作事项</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">完成时间</th>
<th style="width: 160px; text-align:center; vertical-align: middle;">工作要求及交付物</th>
<th style="width: 60px; text-align:center; vertical-align: middle;">任务编号</th>
<th style="width: 120px; text-align:center; vertical-align: middle;">任务来源</th>
<th style="width: 120px; text-align:center; vertical-align: middle;">承办单位</th>
<th style="text-align:center; vertical-align: middle;">完成时间</th>
<th style="text-align:center; vertical-align: middle;">工作要求及交付物</th>
<th style="text-align:center; vertical-align: middle; width: 120px">承办单位</th>
<th style="width: 120px;text-align:center; vertical-align: middle;">承/督办人</th>
<th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量(pre)</th>
<th style="width: 60px; text-align:center; vertical-align: middle;">折算系数</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">成熟度</th>
<th style="width: 220px; text-align:center; vertical-align: middle;">完成情况</th>
<th style="text-align:center; vertical-align: middle;">完成情况</th>
<th style="width: 120px; text-align:center; vertical-align: middle;">完成质量</th>
{# <th style="width: 40px">Label</th>#}
</tr>
@ -79,14 +77,12 @@
<tbody>
{% for todo in group_todo %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ todo.id }}</td>
<td>{{ todo.todo_topic }}</td>
<td style="text-align: center">{{ todo.deadline | date:"m月d日" }}</td>
<td style="width: 160px"><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
<td style="text-align: center">{{ todo.task_id }}</td>
<td style="text-align: center">{{ todo.task_origin }}</td>
<td style="text-align: center">{{ todo.duty_group }}</td>
<td style="text-align: center">{{ todo.main_executor}}</td>
<td style="text-align: center">{{ todo.main_executor.all|join:", " }}</td>
<td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>
<td style="text-align:center;">{{ todo.predict_work }}</td>
<td style="text-align:center;">{{ todo.evaluate_factor }}</td>
@ -97,7 +93,7 @@
</div>
<span class="badge bg-gradient-blue">{{ todo.maturity }}</span>
</td>
<td style="text-align: center"><textarea style="overflow:hidden;width:100%;">{{ todo.complete_note }}</textarea></td>
<td style="text-align: center"><textarea style="overflow:hidden; width: 300px">{{ todo.complete_note }}</textarea></td>
<td style="text-align: center">{{ todo.quality_mark|default:'' }}</td>
</tr>
{% endfor %}

View File

@ -1,455 +1 @@
{% extends 'tasks/base.html' %}
{% load static %}
{% block main_content %}
<div class="content-wrapper" style="min-height: 1200.88px;">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>总览 ({{ date }}) </h1>
{# <h1><font color="#FF0000">测试版,请勿用于生产环境!!!</font></h1>#}
</div>
<div class="col-sm-6">
{# <ol class="breadcrumb float-sm-right">#}
{# <li class="breadcrumb-item active"><a href="#">主页</a></li>#}
{# </ol>#}
<br>
{% load taskfilter %}
<ul class="pagination float-md-right">
<li class="page-item"><a class="page-link" href="/{{ date|last_month }}">«</a></li>
<li class="page-item"><a class="page-link"> {{ date|this_month }}月 </a></li>
<li class="page-item"><a class="page-link" href="/{{ date|next_month }}">»</a></li>
</ul>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col">
<div class="info-box">
<span class="info-box-icon bg-danger elevation-1"><i class="fas fa-tasks"></i></span>
<div class="info-box-content">
<span class="info-box-text">部门总任务数</span>
<span class="info-box-number">
{{ department_cal.depart_count|floatformat:'0'|default_if_none:'0' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
<div class="info-box-content">
<span class="info-box-text">部门预计总工作量</span>
<span class="info-box-number">
{{ department_cal.pre_cal|default_if_none:'0'|floatformat:'1' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
<div class="info-box-content">
<span class="info-box-text">部门实际总工作量</span>
<span class="info-box-number">
{{ department_cal.real_cal|default_if_none:'0'|floatformat:'1' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
<div class="info-box-content">
<span class="info-box-text">部门预计平均工作量</span>
<span class="info-box-number">
{{ department_cal.pre_avg|default_if_none:'0'|floatformat:'1' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
<div class="info-box-content">
<span class="info-box-text">部门实际平均工作量</span>
<span class="info-box-number">
{{ department_cal.real_avg|default_if_none:'0'|floatformat:'1' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<!-- fix for small devices only -->
<div class="clearfix hidden-md-up"></div>
{# <div class="col-12 col-sm-6 col-md-3">#}
{# <div class="info-box mb-3">#}
{# <span class="info-box-icon bg-success elevation-1"><i class="fas fa-shopping-cart"></i></span>#}
{##}
{# <div class="info-box-content">#}
{# <span class="info-box-text">Sales</span>#}
{# <span class="info-box-number">760</span>#}
{# </div>#}
{# <!-- /.info-box-content -->#}
{# </div>#}
{# <!-- /.info-box -->#}
{# </div>#}
{# <!-- /.col -->#}
{# <div class="col-12 col-sm-6 col-md-3">#}
{# <div class="info-box mb-3">#}
{# <span class="info-box-icon bg-warning elevation-1"><i class="fas fa-users"></i></span>#}
{##}
{# <div class="info-box-content">#}
{# <span class="info-box-text">New Members</span>#}
{# <span class="info-box-number">2,000</span>#}
{# </div>#}
{# <!-- /.info-box-content -->#}
{# </div>#}
{# <!-- /.info-box -->#}
{# </div>#}
<!-- /.col -->
</div>
<div class="row">
<div class="col">
<div class="info-box">
<span class="info-box-icon bg-info elevation-1"><i class="fas fa-tasks"></i></span>
<div class="info-box-content">
<span class="info-box-text"><b>个人任务数据统计:</b></span>
{# <span class="info-box-number">#}
{# {% for user in current_user %}{{ user.main_executor_count }}{% endfor %}#}
{# <small></small>#}
{# </span>#}
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<div class="col">
<div class="info-box">
{# <span class="info-box-icon bg-info elevation-1"><i class="fas fa-tasks"></i></span>#}
<div class="info-box-content">
<span class="info-box-text">承办任务数</span>
<span class="info-box-number">
{{ current_user.main_count }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
{# <span class="info-box-icon bg-danger elevation-1"><i class="fas fa-tasks"></i></span>#}
<div class="info-box-content">
<span class="info-box-text">协办任务数</span>
<span class="info-box-number">
{{ current_user.sub_count }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
<div class="info-box-content">
<span class="info-box-text">预计工作量</span>
<span class="info-box-number">
{{ current_user.pre_cal|floatformat:'1'|default_if_none:'0' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col">
<div class="info-box mb-3">
<div class="info-box-content">
<span class="info-box-text">实际工作量</span>
<span class="info-box-number">
{{ current_user.real_cal|floatformat:'1'|default_if_none:'0' }}
<small></small>
</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<!-- fix for small devices only -->
<div class="clearfix hidden-md-up"></div>
{# <div class="col-12 col-sm-6 col-md-3">#}
{# <div class="info-box mb-3">#}
{# <span class="info-box-icon bg-success elevation-1"><i class="fas fa-shopping-cart"></i></span>#}
{##}
{# <div class="info-box-content">#}
{# <span class="info-box-text">Sales</span>#}
{# <span class="info-box-number">760</span>#}
{# </div>#}
{# <!-- /.info-box-content -->#}
{# </div>#}
{# <!-- /.info-box -->#}
{# </div>#}
{# <!-- /.col -->#}
{# <div class="col-12 col-sm-6 col-md-3">#}
{# <div class="info-box mb-3">#}
{# <span class="info-box-icon bg-warning elevation-1"><i class="fas fa-users"></i></span>#}
{##}
{# <div class="info-box-content">#}
{# <span class="info-box-text">New Members</span>#}
{# <span class="info-box-number">2,000</span>#}
{# </div>#}
{# <!-- /.info-box-content -->#}
{# </div>#}
{# <!-- /.info-box -->#}
{# </div>#}
<!-- /.col -->
</div>
<div class="row">
<div class="col-md-12">
<!-- BAR CHART -->
<div class="card card-gray">
<div class="card-header">
<h3 class="card-title">本部门工作统计表</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i
class="fas fa-minus"></i>
</button>
<button type="button" class="btn btn-tool" data-card-widget="remove"><i
class="fas fa-times"></i></button>
</div>
</div>
<!-- 工作统计表格 -->
<div class="card-body">
<div class="chart">
<div class="chartjs-size-monitor">
<div class="chartjs-size-monitor-expand">
<div class=""></div>
</div>
<div class="chartjs-size-monitor-shrink">
<div class=""></div>
</div>
</div>
<canvas id="barChart"
style="height: 230px; min-height: 230px; display: block; width: 764px;"
width="764" height="230" class="chartjs-render-monitor"></canvas>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col (MAIN) -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">部门工作统计表</h3>
</div>
{# TODO 更换为绩效考核表 分季度按人显示工作量及完成质量#}
<!-- /.card-header -->
<div class="card-body">
<div id="example2_wrapper" class="dataTables_wrapper dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"></div><div class="col-sm-12 col-md-6"></div></div><div class="row"><div class="col-sm-12"><table id="example2" class="table table-bordered table-hover dataTable" role="grid" aria-describedby="example2_info">
<thead>
<tr role="row">
<th class="sorting_asc" tabindex="0" aria-controls="example2" rowspan="2" colspan="1" aria-sort="ascending" aria-label="Rendering engine: activate to sort column descending">姓名</th>
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">一季度</th>
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">二季度</th>
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">三季度</th>
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">四季度</th>
</tr>
<tr role="row">
<th>实际工作量</th>
<th>完成质量</th>
<th>实际工作量</th>
<th>完成质量</th>
<th>实际工作量</th>
<th>完成质量</th>
<th>实际工作量</th>
<th>完成质量</th>
</tr>
</thead>
<tbody>
{% for user in stat.values %}
<tr role="row" class="odd">
<td class="sorting_1">{{ user.real_name }}</td>
<td>{{ user.real_cal_1|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.quality_1|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.real_cal_2|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.quality_2|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.real_cal_3|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.quality_3|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.real_cal_4|default_if_none:'0'|floatformat:'1' }}</td>
<td>{{ user.quality_4|default_if_none:'0'|floatformat:'1' }}</td>
</tr>
{% endfor %}
</tbody>
{# <tfoot>#}
{# <tr><th rowspan="1" colspan="1">Rendering engine</th><th rowspan="1" colspan="1">Browser</th><th rowspan="1" colspan="1">Platform(s)</th><th rowspan="1" colspan="1">Engine version</th><th rowspan="1" colspan="1">CSS grade</th></tr>#}
{# </tfoot>#}
</table></div></div>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
</div><!-- /.container-fluid -->
</section>
<!-- /.content -->
</div>
{# <div class="content-wrapper">#}
{# {{ main_points }}#}
{# <br>#}
{# {{ sub_points }}#}
{# <br>#}
{# {{ points }}#}
{# {{ points.3.credit }}#}
{# {% for point in points.values %}'{{ point }}', {% endfor %}#}
{# {% for point in points %}#}
{# {{ point }}#}
{# {% endfor %}#}
{##}
{# {% for user in users %}#}
{# {{ user }}#}
{# {{ user.get_predict_work_count.total_predict_work }}#}
{# {{ user.get_total_point }}#}
{# {{ user.main_executor.count }}#}
{# {% for todo in user.main_executor.all %}#}
{# {{ todo }}#}
{# {{ todo.points }}#}
{# {% endfor %}#}
{# </br>#}
{# {% endfor %}#}
{##}
{# </div>#}
{% endblock %}
{% block script %}
<!-- ChartJS -->
<script src="{% static 'tasks/plugins/chart.js/Chart.min.js' %}"></script>
<script>
$(function () {
/* ChartJS
* -------
* Here we will create a few charts using ChartJS
*/
var areaChartData = {
labels : [{% for user in users_data.values %}'{{ user.real_name }}', {% endfor %}],
datasets: [
{
label : '协办任务',
backgroundColor : 'rgba(114,140,212,1)',
borderColor : 'rgba(114,140,212,1)',
pointRadius : false,
pointColor : 'rgba(114,140,212,1)',
pointStrokeColor : '#c1c7d1',
pointHighlightFill : '#fff',
pointHighlightStroke: 'rgba(220,220,220,1)',
data : [{% for user in users_data.values %}'{{ user.sub_count }}', {% endfor %}]
},
{
label : '承办任务',
backgroundColor : 'rgba(23,162,184,0.9)',
borderColor : 'rgba(100,98,204,0.8)',
pointRadius : false,
pointColor : '#3b8bba',
pointStrokeColor : 'rgba(60,141,188,1)',
pointHighlightFill : '#fff',
pointHighlightStroke: 'rgba(60,141,188,1)',
data : [{% for user in users_data.values %}'{{ user.main_count }}', {% endfor %}]
},
{
label : '预计工作量',
backgroundColor : 'rgba(210, 214, 222,1)',
borderColor : 'rgba(210, 214, 222,1)',
pointRadius : false,
pointColor : '#3b8bba',
pointStrokeColor : 'rgba(60,141,188,1)',
pointHighlightFill : '#fff',
pointHighlightStroke: 'rgba(60,141,188,1)',
data : [{% for user in users_data.values %}'{{ user.pre_cal|floatformat:'1' }}', {% endfor %}]
},
{
label : '工作量',
backgroundColor : 'rgba(253,199,101,0.9)',
borderColor : 'rgba(253,199,101,0.8)',
pointRadius : false,
pointColor : '#3b8bba',
pointStrokeColor : 'rgba(60,141,188,1)',
pointHighlightFill : '#fff',
pointHighlightStroke: 'rgba(60,141,188,1)',
data : [{% for user in users_data.values %}'{{ user.real_cal|floatformat:'1' }}', {% endfor %}]
},
]
}
//-------------
//- BAR CHART -
//-------------
var barChartCanvas = $('#barChart').get(0).getContext('2d')
var barChartData = jQuery.extend(true, {}, areaChartData)
var temp0 = areaChartData.datasets[0]
var temp1 = areaChartData.datasets[1]
barChartData.datasets[0] = temp1
barChartData.datasets[1] = temp0
var barChartOptions = {
responsive : true,
maintainAspectRatio : false,
datasetFill : false
}
var barChart = new Chart(barChartCanvas, {
type: 'bar',
data: barChartData,
options: barChartOptions
})
})
</script>
{% endblock %}

View File

@ -5,7 +5,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>任务管理工具</title>
<title>AdminLTE 3 | Log in (v2)</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="{% static 'tasks/dist/css/googlefont.css' %}">
@ -16,12 +16,12 @@
<!-- Theme style -->
<link rel="stylesheet" href="{% static 'tasks/dist/css/adminlte.min.css' %}">
</head>
<body class="hold-transition login-page" style="background-image: url({% static 'tasks/images/peak_background.svg' %})">
<body class="hold-transition login-page">
<div class="login-box">
<!-- /.login-logo -->
<div class="card card-outline card-primary">
<div class="card-header text-center">
<a href="{% url 'tasks:index' %}" class="h1"><b>任务</b>管理系统</a>
<a href="../../index2.html" class="h1"><b>任务</b>管理系统</a>
</div>
<div class="card-body">
<p class="login-box-msg">登录以继续</p>
@ -68,9 +68,9 @@
{% csrf_token %}
</form>
{# <p class="mb-1">#}
{# <a href="#">无法登录?</a>#}
{# </p>#}
<p class="mb-1">
<a href="forgot-password.html">无法登录?</a>
</p>
</div>
<!-- /.card-body -->
</div>

View File

@ -33,26 +33,13 @@
<!-- /.card-header -->
<div class="card-body p-0">
<table class="table table-striped" style="word-break: break-all; overflow: auto" cellspacing="0">
<div class="row">
<div class="col-12" style="background-color: #f2f2f2">
<button type="button" id="unfold" onclick="alert1()" class="btn btn-default float-left" style="display: block;"><i class="far fa-credit-card"></i> 展开</button>
<button type="button" id="fold" onclick="alert1()" class="btn btn-default float-left" style="display: none;"><i class="far fa-credit-card"></i> 收起</button>
<ul class="pagination float-md-right">
{% load taskfilter %}
<li class="page-item"><a class="page-link" href="/tasklist/{{ year_quarter.1|last_year }}">«</a></li>
<li class="page-item"><a class="page-link"> {{ year_quarter.1|this_year }}年 </a></li>
<li class="page-item"><a class="page-link" href="/tasklist/{{ year_quarter.1|next_year }}">»</a></li>
</ul>
</div>
</div>
<thead>
<tr style="background-color: #f2f2f2">
<th style="width: 140px; text-align:center; vertical-align: middle;">任务属性</th>
<th style="width: 60px; text-align:center; vertical-align: middle;">任务编号</th>
<th style="width: 220px; text-align:center; vertical-align: middle;">任务名称</th>
<th style="width: 120px; text-align:center; vertical-align: middle;">任务来源</th>
<th style="width: 120px;text-align:center; vertical-align: middle;">目标值</th>
<th style="width: 100px; vertical-align: middle;">任务属性</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">任务编号</th>
<th style="width: 200px; text-align:center; vertical-align: middle;">任务名称</th>
<th style="text-align:center; vertical-align: middle;">任务来源</th>
<th style="width: 80px;text-align:center; vertical-align: middle;">目标值</th>
<th style="text-align:center; vertical-align: middle; padding: 0" colspan="4">
<table class="table" style="margin: 0;" cellpadding="0">
<tr style="background-color: #f2f2f2"><th colspan="4">任务显性目标及节点</th></tr>
@ -75,40 +62,40 @@
{# </thead>#}
</table>
</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">完成日期</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">完成日期</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">责任单位</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">负责人</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">负责人</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">主管领导</th>
<th style="width: 240px; text-align:center; vertical-align: middle;">任务说明</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">任务说明</th>
{# <th style="width: 40px">Label</th>#}
</tr>
</thead>
<tbody style="font-size: 0.9rem">
{% for task in tasks %}
<tr style="background-color: white">
<td style="text-align:center;">{{ task.task_property }}</td>
<td>{{ task.task_property }}</td>
<td style="text-align:center;">{{ task.task_id }}</td>
<td>{{ task.task_topic }}</td>
<td style="text-align:center;">{{ task.task_origin }}</td>
<td style="width: 160px">{{ task.task_origin }}</td>
<td>{{ task.aim_value }}</td>
{% load taskfilter %}
<td style="background-color: #BFE7A8; padding: 0">
<textarea class="text-none-border" style="background-color: #BFE7A8;">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.1|safe }}{% endfor %}</textarea>
<textarea class="text-none-border" style="background-color: #BFE7A8;">{% for todo in task.related_task.all %}{{ todo|quarter_cate:'1' }}&#13;&#10;{% endfor %}</textarea>
</td>
<td style="background-color: #F5CCB0; padding: 0">
<textarea class="text-none-border" style="background-color: #F5CCB0">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.2|safe }}{% endfor %}</textarea>
<textarea class="text-none-border" style="background-color: #F5CCB0">{% for todo in task.related_task.all %}{{ todo|quarter_cate:'2' }}&#13;&#10;{% endfor %}</textarea>
</td>
<td style="background-color: #F7E4A8; padding: 0">
<textarea class="text-none-border" style="background-color: #F7E4A8">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.3|safe }}{% endfor %}</textarea>
<textarea class="text-none-border" style="background-color: #F7E4A8">{% for todo in task.related_task.all %} {{ todo|quarter_cate:'3' }}&#13;&#10;{% endfor %}</textarea>
</td>
<td style="background-color: #83E1E3; padding: 0">
<textarea class="text-none-border" style="background-color: #83E1E3">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.4|safe }}{% endfor %}</textarea>
<textarea class="text-none-border" style="background-color: #83E1E3">{% for todo in task.related_task.all %} {{ todo|quarter_cate:'4' }}&#13;&#10;{% endfor %}</textarea>
</td>
<td style="text-align:center;">{{ task.deadline | date:"Y.m" }}</td>
<td>{{ task.deadline | date:"Y年m月" }}</td>
<td style="text-align:center;">{{ task.duty_group }}</td>
<td style="text-align:center;">{{ task.principal }}</td>
<td style="text-align:center;">{{ task.leader|default_if_none:"" }}</td>
<td><textarea style="overflow:hidden;width:100%;">{{ task.task_note }}</textarea></td>
<td style="text-align:center;">{{ task.leader }}</td>
<td><textarea>{{ task.task_note }}</textarea></td>
</tr>
{% endfor %}
</tbody>
@ -124,44 +111,4 @@
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<script>
function setHeight(element) {
$(element).css({'height':'auto','overflow-y':'hidden'}).height(element.scrollHeight);
}
$('textarea').each(function () {
setHeight(this);
}).on('input', function () {
setHeight(this);
});
</script>
{% endblock %}
{% block script %}
<script>
$('#unfold').click(function (element){
var unfold = document.getElementById("unfold");
var fold = document.getElementById("fold");
unfold.style.display = "none";
fold.style.display = "block";
$(element).css({'height':'auto','overflow-y':'hidden'}).height(element.scrollHeight);
$('textarea').each(function () {
setHeight(this);
}).on('input', function () {
setHeight(this);
});
})
$('#fold').click(function (element){
var unfold = document.getElementById("unfold");
var fold = document.getElementById("fold");
unfold.style.display = "block";
fold.style.display = "none";
location.reload();
// TODO 暂时无法解决让textarea恢复原本高度的问题使用刷新代替
})
</script>
{% endblock %}

View File

@ -9,7 +9,7 @@
<div class="container">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0"> 我的任务 <small></small></h1>
<h1 class="m-0"> 我的任务 <small>Example 3.0</small></h1>
</div><!-- /.col -->
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
@ -26,40 +26,26 @@
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header bg-gray">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-text-width"></i>
任务详情
</h3>
</div>
<!-- /.card-header -->
<div class="card-body bg-gray-light">
<div class="card-body">
<dl class="row">
<dt class="col-sm-4">工作事项</dt>
<dd class="col-sm-8">{{ todo_detail.todo_topic }}</dd>
<dt class="col-sm-4">完成时间</dt>
<dd class="col-sm-8">{{ todo_detail.deadline }}</dd>
{# <dd class="col-sm-8 offset-sm-4">Donec id elit non mi porta gravida at eget metus.</dd>#}
<dt class="col-sm-4">交付物</dt>
<dd class="col-sm-8">{{ todo_detail.todo_note }}</dd>
<dt class="col-sm-4">任务编号</dt>
<dd class="col-sm-8">{{ todo_detail.task_id }}</dd>
<dt class="col-sm-4">任务来源</dt>
<dd class="col-sm-8">{{ todo_detail.task_origin }}</dd>
{# <dd class="col-sm-8 offset-sm-4">Donec id elit non mi porta gravida at eget metus.</dd>#}
<dt class="col-sm-4">责任单位</dt>
<dd class="col-sm-8">{{ todo_detail.duty_group }}</dd>
<dt class="col-sm-4">承/督办人</dt>
<dd class="col-sm-8">{{ todo_detail.main_executor }}</dd>
<dt class="col-sm-4">协办人</dt>
<dd class="col-sm-8">{{ todo_detail.sub_executor.all|join:', ' }}</dd>
<dt class="col-sm-4">预计工作量</dt>
<dd class="col-sm-8">{{ todo_detail.predict_work }}</dd>
<dt class="col-sm-4">折算系数</dt>
<dd class="col-sm-8">{{ todo_detail.evaluate_factor }}</dd>
<dt class="col-sm-4">实际工作量</dt>
<dd class="col-sm-8">{{ todo_detail.real_work }}</dd>
<dt class="col-sm-4">成熟度</dt>
<dd class="col-sm-8">{{ todo_detail.maturity }}</dd>
<dt class="col-sm-4">责任人</dt>
<dd class="col-sm-8">Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo
sit amet risus.
</dd>
</dl>
</div>
<!-- /.card-body -->
@ -69,40 +55,16 @@
<div class="col-md-6">
<!-- general form elements -->
<div class="card card-primary">
<div class="card-header bg-gray">
<h3 class="card-title">更新工作包</h3>
<div class="card-header">
<h3 class="card-title">Quick Example</h3>
</div>
<!-- /.card-header -->
<!-- form start -->
<form action="" method="post">
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="exampleInputEmail1">成熟度</label>
{{ form.maturity }}
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="exampleInputEmail1">实际工作量</label>
{{ form.real_work }}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="exampleInputEmail">协办人</label>
{{ form.sub_executor }}
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="exampleInputEmail">折算系数</label>
{{ form.evaluate_factor }}
</div>
</div>
<div class="form-group">
<label for="exampleInputEmail1">成熟度</label>
{{ form.maturity }}
</div>
<div class="form-group">
<label for="exampleInputPassword1">完成情况说明</label>

View File

@ -61,17 +61,17 @@
<h3 style="text-align: center; letter-spacing: 30px">我的承办</h3>
<HR width="50%" color=#e2e4e6 SIZE=3>
<tr>
<th style="width: 100px; text-align:center; vertical-align: middle;">序号</th>
<th style="width: 300px; text-align:center; vertical-align: middle;">工作事项</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">完成时间</th>
<th style="width: 240px; text-align:center; vertical-align: middle;">工作要求及交付物</th>
<th style="width: 140px; text-align:center; vertical-align: middle;">承办单位</th>
<th style="width: 200px; text-align:center; vertical-align: middle;">协办人</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">序号</th>
<th style="width: 280px; text-align:center; vertical-align: middle;">工作事项</th>
<th style="text-align:center; vertical-align: middle;">完成时间</th>
<th style="text-align:center; vertical-align: middle;">工作要求及交付物</th>
<th style="text-align:center; vertical-align: middle; width: 120px">承办单位</th>
<th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量(pre)</th>
<th style="width: 60px; text-align:center; vertical-align: middle;">折算系数</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">成熟度</th>
<th style="width: 320px; text-align:center; vertical-align: middle;">完成情况</th>
<th style="text-align:center; vertical-align: middle;">完成情况</th>
<th style="width: 120px; text-align:center; vertical-align: middle;">完成质量</th>
<th style="text-align:center; vertical-align: middle;"></th>
{# <th style="width: 40px">Label</th>#}
@ -80,10 +80,10 @@
<tbody>
{% for todo in my_todo %}
<tr>
<td style="text-align: center">{{ forloop.counter }}</td>
<td style="text-align: center">{{ todo.id }}</td>
<td>{{ todo.todo_topic }}</td>
<td style="text-align: center">{{ todo.deadline | date:"m月d日" }}</td>
<td><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
<td style="width: 160px"><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
<td style="text-align: center">{{ todo.duty_group }}</td>
<td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>
<td style="text-align:center;">{{ todo.predict_work }}</td>
@ -95,9 +95,9 @@
</div>
<span class="badge bg-gradient-blue">{{ todo.maturity }}</span>
</td>
<td style="text-align: center"><textarea style="overflow:hidden; width: 100%" readonly="readonly">{{ todo.complete_note }}</textarea></td>
<td style="text-align: center"><textarea style="overflow:hidden; width: 300px">{{ todo.complete_note }}</textarea></td>
<td style="text-align: center">{{ todo.quality_mark|default:'' }}</td>
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}?next={{ current_path }}">填报</a> </td>
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}">填报</a> </td>
</tr>
{% endfor %}
</tbody>
@ -112,18 +112,18 @@
<h3 style="text-align: center; letter-spacing: 30px">我的协办</h3>
<HR width="50%" color=#e2e4e6 SIZE=3>
<tr>
<th style="width: 70px; text-align:center; vertical-align: middle;">序号</th>
<th style="width: 300px; text-align:center; vertical-align: middle;">工作事项</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">完成时间</th>
<th style="width: 240px; text-align:center; vertical-align: middle;">工作要求及交付物</th>
<th style="width: 140px; text-align:center; vertical-align: middle;">承办单位</th>
<th style="width: 200px;text-align:center; vertical-align: middle;">承/督办人</th>
{# <th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>#}
<th style="width: 80px; text-align:center; vertical-align: middle;">序号</th>
<th style="width: 280px; text-align:center; vertical-align: middle;">工作事项</th>
<th style="text-align:center; vertical-align: middle;">完成时间</th>
<th style="text-align:center; vertical-align: middle;">工作要求及交付物</th>
<th style="text-align:center; vertical-align: middle; width: 120px">承办单位</th>
<th style="width: 120px;text-align:center; vertical-align: middle;">承/督办人</th>
<th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量(pre)</th>
<th style="width: 60px; text-align:center; vertical-align: middle;">折算系数</th>
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量</th>
<th style="width: 100px; text-align:center; vertical-align: middle;">成熟度</th>
<th style="width: 320px; text-align:center; vertical-align: middle;">完成情况</th>
<th style="text-align:center; vertical-align: middle;">完成情况</th>
<th style="width: 120px; text-align:center; vertical-align: middle;">完成质量</th>
<th style="text-align:center; vertical-align: middle;"></th>
{# <th style="width: 40px">Label</th>#}
@ -132,13 +132,13 @@
<tbody>
{% for todo in my_sub_todo %}
<tr>
<td style="text-align: center">{{ forloop.counter }}</td>
<td style="text-align: center">{{ todo.id }}</td>
<td>{{ todo.todo_topic }}</td>
<td style="text-align: center">{{ todo.deadline | date:"m月d日" }}</td>
<td><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
<td style="width: 160px"><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
<td style="text-align: center">{{ todo.duty_group }}</td>
<td style="text-align: center">{{ todo.main_executor }}</td>
{# <td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>#}
<td style="text-align: center">{{ todo.main_executor.all|join:", " }}</td>
<td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>
<td style="text-align:center;">{{ todo.predict_work }}</td>
<td style="text-align:center;">{{ todo.evaluate_factor }}</td>
<td style="text-align:center;">{{ todo.real_work|default:'' }}</td>
@ -148,9 +148,9 @@
</div>
<span class="badge bg-gradient-blue">{{ todo.maturity }}</span>
</td>
<td style="text-align: center"><textarea style="overflow:hidden; width:100%" readonly="readonly">{{ todo.complete_note }}</textarea></td>
<td style="text-align: center"><textarea style="overflow:hidden; width: 300px">{{ todo.complete_note }}</textarea></td>
<td style="text-align: center">{{ todo.quality_mark|default:'' }}</td>
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}?next={{ current_path }}">填报</a> </td>
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}">填报</a> </td>
</tr>
{% endfor %}
</tbody>

1552
yarn.lock

File diff suppressed because it is too large Load Diff