generated from container/tmpl
			
				 5 changed files with 599 additions and 0 deletions
			
			
		| @ -0,0 +1,21 @@ | |||
| ## 配置文件 | |||
| `config.ini`为项目配置文件。 | |||
| - `[clickhouse]`配置节为 ClickHouse 数据库的配置信息: | |||
|     - `port`为数据库的`TCP`端口。 | |||
|     - `password`为数据库密码,如果没有密码,则不需要配置任何值。 | |||
| - `[qiniu]`配置节为七牛云的配置信息: | |||
|     - `access-key`和`secret-key`为七牛云密钥。 | |||
|     - `bucket`为七牛云存储空间。 | |||
|     - `domain`为存储空间对应的外链域名。 | |||
| 
 | |||
| ## requirements.txt | |||
| 项目所需的依赖库。 | |||
| ``` | |||
| pip freeze > requirements.txt | |||
| ``` | |||
| 
 | |||
| ## 打包`.exe`文件 | |||
| 使用`--distpath`选项指定输出目录的路径。例如,将`.exe`文件输出至当前文件夹下: | |||
| ``` | |||
| (venv) E:\WorkSpace\FS-Git\fs-pep-stats-report>pyinstaller -F main.py --distpath=.  | |||
| ``` | |||
| @ -0,0 +1,184 @@ | |||
| def querystring_user_procinst_duration(start, end, except_senior, department): | |||
|     filter_user = "and u.name not in ('刘文峰', '金亮', '姜珍', '余莎莎', '张阳根', '唐国华', '刘国勇', '刘会连', '肖琥')" if except_senior else "" | |||
|     filter_department = "and department_name='{}'".format(department) if department else "" | |||
|     return ''' | |||
| select department_name, | |||
|        user_name, | |||
|        procinst_id, | |||
|        sum(duration_in_minutes) as procinst_duration_in_minutes | |||
| from ( | |||
|     select distinct d.id as department_id, | |||
|            d.name as department_name, | |||
|            u.id as user_id, | |||
|            u.name as user_name, | |||
|            wp.name as process_name, | |||
|            wpa.procinst_id as procinst_id, | |||
|            wpa.task_id as taskinst_id, | |||
|            wpa.task_name as task_name, | |||
|            wpa.start_time as start_time, | |||
|            wpa.end_time as end_time, | |||
|            wpa.total_minutes as duration_in_minutes | |||
|     from workflow_process_achievements as wpa | |||
|     inner join workflow_process_history as wph on wpa.procinst_id=wph.procinst_id | |||
|     inner join workflow_process_version as wpv on wph.version_id=wpv.id | |||
|     inner join workflow_process as wp on wpv.process_id=wp.id | |||
|     inner join user as u on wpa.deal_user_id=u.id | |||
|     inner join department_user as du on u.id=du."user" | |||
|     inner join department as d on du.department=d.id | |||
|     where wpa.end_time >='{}' and wpa.end_time < '{}' and wp.deleted=false and wp.is_enable=true and d.delete=0 and u.delete=0 and u.state=1 and u.active_status=1 | |||
|       {} {} | |||
| ) as r | |||
| group by department_name, user_name, procinst_id | |||
|     '''.format(start, end, filter_user, filter_department) | |||
| 
 | |||
| 
 | |||
| def querystring_senior_procinst_duration(start, end): | |||
|     return ''' | |||
| SELECT user_name, | |||
|      ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user | |||
| from ( | |||
| select user_name, | |||
|        procinst_id, | |||
|        sum(duration_in_minutes) as procinst_duration_in_minutes | |||
| from ( | |||
|   select distinct u.id as user_id, | |||
|          u.name as user_name, | |||
|          wp.name as process_name, | |||
|          wpa.procinst_id as procinst_id, | |||
|          wpa.task_id as taskinst_id, | |||
|          wpa.task_name as task_name, | |||
|          wpa.start_time as start_time, | |||
|          wpa.end_time as end_time, | |||
|          wpa.total_minutes as duration_in_minutes | |||
|   from workflow_process_achievements as wpa | |||
|   inner join workflow_process_history as wph on wpa.procinst_id=wph.procinst_id | |||
|   inner join workflow_process_version as wpv on wph.version_id=wpv.id | |||
|   inner join workflow_process as wp on wpv.process_id=wp.id | |||
|   inner join user as u on wpa.deal_user_id=u.id | |||
|   where wpa.end_time >='{}' and wpa.end_time < '{}' and wp.deleted=false and wp.is_enable=true and u.delete=0 and u.state=1 and u.active_status=1 | |||
|     and u.name in ('刘文峰', '金亮', '姜珍', '余莎莎', '张阳根', '唐国华', '刘国勇', '刘会连', '肖琥') | |||
| ) as r | |||
| group by user_name, procinst_id | |||
| ) as r2 | |||
| GROUP by r2.user_name | |||
|     '''.format(start, end) | |||
| 
 | |||
| 
 | |||
| # 按月统计项企平台使用人数 | |||
| def querystring_user_count(start, end): | |||
|     return ''' | |||
| select count(distinct deal_user_id) as pep_using_user_count  | |||
| from workflow_process_achievements as wpa | |||
| inner join user as u on wpa.deal_user_id=u.id | |||
| where wpa.end_time >='{}' and wpa.end_time < '{}' and u.delete=0 and u.state=1 and u.active_status=1 | |||
|     '''.format(start, end) | |||
| 
 | |||
| 
 | |||
| # 个人处理流程平均耗时 | |||
| def querystring_procinst_duration_by_company(start, end): | |||
|     return ''' | |||
| SELECT ROUND(AVG(procinst_duration_in_minutes_by_user) /60, 2) as procinst_duration_in_hours from ( | |||
|   SELECT user_name, | |||
|          ROUND(AVG(procinst_duration_in_minutes), 2) as procinst_duration_in_minutes_by_user | |||
|   from ({}) as r2 | |||
|   GROUP by r2.user_name | |||
| ) as r3 | |||
|     '''.format(querystring_user_procinst_duration(start, end, False, '')) | |||
| 
 | |||
| 
 | |||
| # 部门数(高于公司平均数) | |||
| def querystring_department_count_gt_avg(start, end, avg): | |||
|     return ''' | |||
| SELECT count(*) from ( | |||
|   SELECT department_name, | |||
|          ROUND(AVG(procinst_duration_in_hours_by_user), 2) as procinst_duration_in_hours_by_department | |||
|   from ( | |||
|     SELECT department_name, | |||
|            user_name, | |||
|            ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user | |||
|     from ({}) as r2 | |||
|     GROUP by r2.department_name, r2.user_name | |||
|   ) as r3 | |||
|   GROUP by r3.department_name | |||
| ) as r4 | |||
| WHERE procinst_duration_in_hours_by_department > {} | |||
|     '''.format(querystring_user_procinst_duration(start, end, True, ''), avg) | |||
| 
 | |||
| 
 | |||
| # 用户数(高于公司平均数) | |||
| def querystring_user_count_gt_avg(start, end, avg): | |||
|     return ''' | |||
| SELECT count(*) from ( | |||
|   SELECT user_name, | |||
|          ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user | |||
|   from ({}) as r2 | |||
|   group by r2.user_name | |||
| ) as r3 | |||
| WHERE r3.procinst_duration_in_hours_by_user > {} | |||
|     '''.format(querystring_user_procinst_duration(start, end, False, ''), avg) | |||
| 
 | |||
| 
 | |||
| # 部门处理流程平均耗时 | |||
| def querystring_procinst_duration_by_department(start, end): | |||
|     return """ | |||
| SELECT department_name, | |||
|        ROUND(AVG(procinst_duration_in_hours_by_user), 2) as procinst_duration_in_hours_by_department | |||
| from ( | |||
|   SELECT department_name, | |||
|          user_name, | |||
|          ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user | |||
|   from ({}) as r2 | |||
|   GROUP by r2.department_name, r2.user_name | |||
| ) as r3 | |||
| GROUP by r3.department_name | |||
| order by procinst_duration_in_hours_by_department DESC | |||
|     """.format(querystring_user_procinst_duration(start, end, True, '')) | |||
| 
 | |||
| 
 | |||
| # 部门处理流程平均耗时(高管) | |||
| def querystring_procinst_duration_by_senior(start, end): | |||
|     return ''' | |||
| SELECT ROUND(AVG(procinst_duration_in_hours_by_user), 2) as procinst_duration_in_hours_by_department | |||
| from ({}) as r3 | |||
|     '''.format(querystring_senior_procinst_duration(start, end)) | |||
| 
 | |||
| 
 | |||
| # 用户单流程处理耗时(高管) | |||
| def querystring_procinst_duration_by_user_senior(start, end): | |||
|     return ''' | |||
| {} | |||
| order by procinst_duration_in_hours_by_user DESC | |||
|     '''.format(querystring_senior_procinst_duration(start, end)) | |||
| 
 | |||
| 
 | |||
| # 各部门耗时较长用户(高管) | |||
| def querystring_user_gt_senior_avg(start, end, avg): | |||
|     return ''' | |||
| SELECT * from ({}) as r3 | |||
| WHERE r3.procinst_duration_in_hours_by_user > {} | |||
| order by procinst_duration_in_hours_by_user DESC | |||
|     '''.format(querystring_senior_procinst_duration(start, end), avg) | |||
| 
 | |||
| 
 | |||
| # 各部门耗时较长用户 | |||
| def querystring_user_gt_department_avg(start, end, department, avg): | |||
|     return """ | |||
| SELECT * from ( | |||
|   SELECT user_name, | |||
|          ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user | |||
|   from ({}) as r2 | |||
|   group by r2.user_name | |||
| ) as r3 | |||
| WHERE r3.procinst_duration_in_hours_by_user > {} | |||
| order by procinst_duration_in_hours_by_user desc | |||
|     """.format(querystring_user_procinst_duration(start, end, True, department), avg) | |||
| 
 | |||
| 
 | |||
| # 按部门统计用户单流程处理耗时 | |||
| def querystring_procinst_duration_by_user(start, end, department): | |||
|     return """ | |||
| SELECT user_name, | |||
|      ROUND(AVG(procinst_duration_in_minutes) /60, 2) as procinst_duration_in_hours_by_user | |||
| from ({}) as r2 | |||
| group by r2.user_name | |||
|     """.format(querystring_user_procinst_duration(start, end, True, department)) | |||
| @ -0,0 +1,12 @@ | |||
| [clickhouse] | |||
| host = 10.8.30.161 | |||
| port = 30900 | |||
| username = default | |||
| password = | |||
| database = pepca8 | |||
| 
 | |||
| [qiniu] | |||
| access-key=5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu | |||
| secret-key=w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5 | |||
| bucket=pep-resource | |||
| domain=https://pepsource.anxinyun.cn | |||
| @ -0,0 +1,346 @@ | |||
| import os | |||
| import datetime | |||
| import configparser | |||
| from clickhouse_driver import Client | |||
| from docx import Document | |||
| from docx.shared import Inches, Pt, RGBColor | |||
| from docx.enum.text import WD_PARAGRAPH_ALIGNMENT, WD_LINE_SPACING | |||
| from docx.oxml.ns import qn | |||
| import matplotlib.pyplot as plt | |||
| from app import db_helper | |||
| import qiniu | |||
| import logging | |||
| 
 | |||
| REPORT_DIR = 'output' | |||
| LOG_DIR = 'log' | |||
| 
 | |||
| 
 | |||
| def get_report_year_month(): | |||
|     # 获取上月的最后一天 | |||
|     last_day = datetime.datetime.now().replace(day=1) - datetime.timedelta(days=1) | |||
|     return last_day.year, last_day.month | |||
| 
 | |||
| 
 | |||
| def get_report_start_end(): | |||
|     # 获取当前日期 | |||
|     now = datetime.datetime.now() | |||
|     # 获取本月第一天 | |||
|     this_month_start = datetime.datetime(now.year, now.month, 1) | |||
|     # 获取上个月最后一天 | |||
|     last_month_end = this_month_start - datetime.timedelta(days=1) | |||
|     # 获取上个月第一天 | |||
|     last_month_start = datetime.datetime(last_month_end.year, last_month_end.month, 1) | |||
|     return last_month_start, this_month_start | |||
| 
 | |||
| 
 | |||
| def set_paragraph_space(p): | |||
|     # 设置间距 | |||
|     p.paragraph_format.space_before = 0  # 段前 | |||
|     p.paragraph_format.space_after = 0  # 段后 | |||
|     p.paragraph_format.line_spacing = Pt(20)  # 设置段落行距为20磅 | |||
| 
 | |||
| 
 | |||
| def set_paragraph_format(p): | |||
|     set_paragraph_space(p) | |||
|     # 设置首行缩进2个字符 | |||
|     p.paragraph_format.first_line_indent = Inches(0.3) | |||
| 
 | |||
| 
 | |||
| def set_heading_format(h, r): | |||
|     set_paragraph_space(h) | |||
|     r.font.name = 'Times New Roman' | |||
|     r._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') | |||
|     r.font.size = Pt(10.5)  # 五号 | |||
|     r.font.color.rgb = RGBColor(0, 0, 0) | |||
|     r.bold = True | |||
| 
 | |||
| 
 | |||
| def set_title_format(h, r): | |||
|     h.paragraph_format.space_before = Pt(12)  # 段前1行 | |||
|     h.paragraph_format.space_after = Pt(12)  # 段后1行 | |||
|     h.paragraph_format.line_spacing = Pt(20)  # 设置段落行距为20磅 | |||
|     h.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |||
|     r.font.name = u'Times New Roman' | |||
|     r._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') | |||
|     r.font.size = Pt(10.5)  # 五号 | |||
|     r.font.color.rgb = RGBColor(0, 0, 0) | |||
|     r.bold = True | |||
| 
 | |||
| 
 | |||
| def set_global_normal_style(doc): | |||
|     style = doc.styles['Normal'] | |||
|     style.font.name = 'Times New Roman'  # 英文、数字字体 | |||
|     style._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')  # 中文字体 | |||
|     style.font.size = Pt(10.5)  # 五号 | |||
|     style.paragraph_format.space_before = 0  # 段前 | |||
|     style.paragraph_format.space_after = 0  # 段后 | |||
|     style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.SINGLE  # 单倍行距 | |||
| 
 | |||
| 
 | |||
| def create_bar_chart(doc, x, y, mean, title, filename): | |||
|     plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体 | |||
|     plt.rcParams["axes.unicode_minus"] = False  # 正常显示负号 | |||
| 
 | |||
|     if title == '部门流程处理平均耗时': | |||
|         plt.figure(figsize=(12, 6), layout='tight') | |||
|         plt.margins(x=0.01) | |||
|     else: | |||
|         plt.figure(figsize=(6.4, 3.4), layout='tight') | |||
| 
 | |||
|     # 绘制柱状图 | |||
|     plt.bar(x, y, color=(31 / 255, 168 / 255, 201 / 255)) | |||
|     # 绘制均值线 | |||
|     plt.axhline(mean, linestyle='dashed', color='#FF8C00') | |||
|     # 添加标题 | |||
|     plt.title(title) | |||
|     # 设置x轴标签旋转角度 | |||
|     plt.xticks(rotation=-30, ha='left') | |||
|     # 添加图例 | |||
|     plt.legend(['均值:{}'.format(mean)], loc='upper right') | |||
|     # 保存图形 | |||
|     plt.savefig('{}/{}'.format(REPORT_DIR, filename)) | |||
|     plt.close() | |||
|     # 插入图形到 Word 文档中 | |||
|     p = doc.add_paragraph() | |||
|     p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |||
|     r = p.add_run() | |||
|     r.add_picture('{}/{}'.format(REPORT_DIR, filename), width=Inches(6)) | |||
| 
 | |||
| 
 | |||
| def add_section(doc, rank, department, d_elapse): | |||
|     h = doc.add_heading(level=2) | |||
|     r = h.add_run('{}. {}'.format(rank, department)) | |||
|     set_heading_format(h, r) | |||
| 
 | |||
|     try: | |||
|         qs = db_helper.querystring_user_gt_senior_avg( | |||
|             start_time, end_time, d_elapse | |||
|         ) if department == '高管' else db_helper.querystring_user_gt_department_avg( | |||
|             start_time, end_time, department, d_elapse) | |||
|         user_gt_department_avg = client.execute(qs) | |||
| 
 | |||
|         qs = db_helper.querystring_procinst_duration_by_user_senior( | |||
|             start_time, end_time | |||
|         ) if department == '高管' else db_helper.querystring_procinst_duration_by_user( | |||
|             start_time, end_time, department) | |||
|         procinst_duration_by_user = client.execute(qs) | |||
|     except Exception as error: | |||
|         print('数据库查询错误:', error) | |||
|         logging.error('数据库查询错误:{}'.format(error)) | |||
|         raise RuntimeError('数据库查询错误:', error) | |||
| 
 | |||
|     users = [] | |||
|     for user, _ in user_gt_department_avg: | |||
|         users.append(user) | |||
| 
 | |||
|     p = doc.add_paragraph() | |||
|     p.add_run('{}{}月份个人处理流程平均耗时时长在公司排名为'.format(department, last_month)) | |||
|     p.add_run('{}'.format(rank)).underline = True | |||
|     if len(procinst_duration_by_user) == 1: | |||
|         p.add_run(',{}公司平均水平。'.format( | |||
|             '高于' if d_elapse > procinst_duration_by_company else | |||
|             '低于' if d_elapse < procinst_duration_by_company else '等于')) | |||
|     else: | |||
|         p.add_run(',其中耗时较长的有') | |||
|         p.add_run('{}'.format('、'.join(users))).underline = True | |||
|         p.add_run(',{}({}小时/流程)。' | |||
|                   .format('高于公司平均水平和部门平均水平' if d_elapse >= procinst_duration_by_company else '低于公司平均水平但高于部门平均水平', | |||
|                           d_elapse)) | |||
|     set_paragraph_format(p) | |||
| 
 | |||
|     x = [] | |||
|     y = [] | |||
|     for user, elapse in procinst_duration_by_user: | |||
|         x.append(user) | |||
|         y.append(elapse) | |||
|     create_bar_chart(doc, x, y, d_elapse, "个人流程处理平均耗时", "{}-流程处理平均耗时.png".format(department)) | |||
|     caption = doc.add_paragraph('图 2-{} {}个人处理流程平均耗时'.format(rank, department)) | |||
|     caption.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |||
| 
 | |||
| 
 | |||
| def add_chapter_2(doc): | |||
|     h = doc.add_heading(level=1) | |||
|     r = h.add_run('二、各部门成员详情') | |||
|     set_heading_format(h, r) | |||
| 
 | |||
|     departments_excluded = ['汇派-质量部', '汇派-生产部', '汇派-计划部', '汇派-人事部', '汇派-采购部', '党建工会', '工程项目中心', '北京技术中心'] | |||
| 
 | |||
|     for index, (department, elapse) in enumerate(procinst_duration_by_department): | |||
|         if department not in departments_excluded: | |||
|             add_section(doc, index + 1, department, elapse) | |||
| 
 | |||
| 
 | |||
| def add_chapter_1(doc): | |||
|     h = doc.add_heading(level=1) | |||
|     r = h.add_run('一、公司各部门横向比较情况') | |||
|     set_heading_format(h, r) | |||
| 
 | |||
|     try: | |||
|         qs = db_helper.querystring_user_count(start_time, end_time) | |||
|         user_count = client.execute(qs)[0][0] | |||
| 
 | |||
|         qs = db_helper.querystring_department_count_gt_avg(start_time, end_time, procinst_duration_by_company) | |||
|         department_count_gt_avg = client.execute(qs)[0][0] | |||
| 
 | |||
|         qs = db_helper.querystring_user_count_gt_avg(start_time, end_time, procinst_duration_by_company) | |||
|         user_count_gt_avg = client.execute(qs)[0][0] | |||
|     except Exception as error: | |||
|         print('数据库查询错误:', error) | |||
|         logging.error('数据库查询错误:{}'.format(error)) | |||
|         raise RuntimeError('数据库查询错误:', error) | |||
| 
 | |||
|     if procinst_duration_by_senior > procinst_duration_by_company: | |||
|         department_count_gt_avg += 1 | |||
| 
 | |||
|     p = doc.add_paragraph() | |||
|     p.add_run('{}年{}月,公司共计'.format(last_year, last_month)) | |||
|     p.add_run('{}'.format(user_count)).underline = True | |||
|     p.add_run('人使用项企平台,个人处理流程平均耗时') | |||
|     p.add_run('{}'.format(procinst_duration_by_company)).underline = True | |||
|     p.add_run('小时/流程,其中,平均单一流程处理耗时高于公司平均数的有') | |||
|     p.add_run('{}'.format(department_count_gt_avg)).underline = True | |||
|     p.add_run('个部门,') | |||
|     p.add_run('{}'.format(user_count_gt_avg)).underline = True | |||
|     p.add_run('人。') | |||
|     set_paragraph_format(p) | |||
| 
 | |||
|     # 准备数据 | |||
|     x = [] | |||
|     y = [] | |||
|     for department, elapse in procinst_duration_by_department: | |||
|         x.append(department) | |||
|         y.append(elapse) | |||
| 
 | |||
|     # 创建柱状图并保存为文件 | |||
|     create_bar_chart(doc, x, y, procinst_duration_by_company, "部门流程处理平均耗时", "部门流程处理平均耗时.png") | |||
|     caption = doc.add_paragraph('图 1 部门流程处理平均耗时') | |||
|     caption.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER | |||
| 
 | |||
| 
 | |||
| def upload_to_qiniu(filename): | |||
|     logging.info('上传报表至七牛云...') | |||
| 
 | |||
|     # 获取 qiniu 配置参数 | |||
|     qn_cfg = config['qiniu'] | |||
|     access_key = qn_cfg['access-key'] | |||
|     secret_key = qn_cfg['secret-key'] | |||
|     bucket_name = qn_cfg['bucket'] | |||
| 
 | |||
|     q = qiniu.Auth(access_key, secret_key) | |||
|     bucket = qiniu.BucketManager(q) | |||
| 
 | |||
|     key = 'pep-stats-report/{}'.format(filename) | |||
|     localfile = '{}/{}'.format(REPORT_DIR, filename) | |||
| 
 | |||
|     ret, info = bucket.stat(bucket_name, key) | |||
|     if info.status_code == 200: | |||
|         print('文件已存在,删除文件...') | |||
|         ret, info = bucket.delete(bucket_name, key) | |||
|         if info.status_code == 200: | |||
|             print('删除成功') | |||
|         else: | |||
|             print('删除失败') | |||
| 
 | |||
|     print('上传文件...') | |||
|     token = q.upload_token(bucket_name, key) | |||
|     ret, info = qiniu.put_file(token, key, localfile) | |||
|     if info.status_code == 200: | |||
|         print('上传成功') | |||
|         logging.info('上传成功') | |||
|         # 获取文件访问 URL | |||
|         url = '{}/{}?attname='.format(qn_cfg['domain'], ret['key']) | |||
|         print('文件访问 URL:', url) | |||
|         logging.info('文件访问 URL:{}'.format(url)) | |||
|     else: | |||
|         print('上传失败,错误信息:', info.error) | |||
|         logging.error('上传失败,错误信息:{}'.format(error)) | |||
| 
 | |||
| 
 | |||
| def generate_word_report(): | |||
|     # 创建一个新的Word文档 | |||
|     doc = Document() | |||
|     set_global_normal_style(doc) | |||
| 
 | |||
|     # 添加标题 | |||
|     h = doc.add_heading(level=0) | |||
|     r = h.add_run('项企流程效能分析结果公示') | |||
|     set_title_format(h, r) | |||
| 
 | |||
|     # 添加段落 | |||
|     p1 = doc.add_paragraph( | |||
|         '为了提升公司整体工作效率,确保跨部门工作高效推进,充分发挥我司自研平台的优势,现基于项企系统数据,利用Superset工具打造了项企流程效能分析平台,能够对我司项企平台的数据进行分析,进而通过量化数据发现我司办公流程流转中的问题,' \ | |||
|         '现将{}年{}月公司员工处理流程平均响应时长(响应时长为从流程流转到当前节点到当前节点处理完成的耗时,单位:小时)情况结果进行公示如下:' | |||
|             .format(last_year, last_month)) | |||
|     set_paragraph_format(p1) | |||
| 
 | |||
|     # 一、公司各部门横向比较情况 | |||
|     add_chapter_1(doc) | |||
| 
 | |||
|     # 二、各部门成员详情 | |||
|     add_chapter_2(doc) | |||
| 
 | |||
|     # 保存文档 | |||
|     report_path = '{}/项企流程效能分析结果公示(全员)-{}年{}月.docx'.format(REPORT_DIR, last_year, last_month) | |||
|     doc.save(report_path) | |||
|     logging.info('本地报表创建成功:{}'.format(report_path)) | |||
| 
 | |||
|     # 上传七牛云 | |||
|     upload_to_qiniu('项企流程效能分析结果公示(全员)-{}年{}月.docx'.format(last_year, last_month)) | |||
| 
 | |||
| 
 | |||
| def create_clickhouse_client(): | |||
|     # 获取 clickhouse 配置参数 | |||
|     ch_cfg = config['clickhouse'] | |||
|     host = ch_cfg['host'] | |||
|     port = ch_cfg.getint('port') | |||
|     username = ch_cfg['username'] | |||
|     password = ch_cfg['password'] | |||
|     database = ch_cfg['database'] | |||
|     # 创建 ClickHouse 客户端对象 | |||
|     return Client(host=host, port=port, user=username, password=password, database=database) | |||
| 
 | |||
| 
 | |||
| def try_create_dir(dir_name): | |||
|     # 判断目录是否存在,如果不存在,则创建目录 | |||
|     if not os.path.exists(dir_name): | |||
|         print(f"目录'{dir_name}'不存在,即将创建...") | |||
|         os.makedirs(dir_name) | |||
|         print(f"目录'{dir_name}'创建成功!") | |||
| 
 | |||
| 
 | |||
| if __name__ == '__main__': | |||
|     try: | |||
|         try_create_dir(REPORT_DIR) | |||
|         try_create_dir(LOG_DIR) | |||
|         logging.basicConfig(filename='{}/runtime.log'.format(LOG_DIR), level=logging.INFO, | |||
|                             format='%(asctime)s.%(msecs)03d - %(levelname)s: %(message)s', | |||
|                             datefmt='%Y-%m-%d %H:%M:%S') | |||
| 
 | |||
|         config = configparser.ConfigParser() | |||
|         config.read('config.ini') | |||
| 
 | |||
|         client = create_clickhouse_client() | |||
| 
 | |||
|         last_year, last_month = get_report_year_month() | |||
|         start_time, end_time = get_report_start_end() | |||
| 
 | |||
|         qs1 = db_helper.querystring_procinst_duration_by_company(start_time, end_time) | |||
|         procinst_duration_by_company = client.execute(qs1)[0][0] | |||
| 
 | |||
|         qs2 = db_helper.querystring_procinst_duration_by_department(start_time, end_time) | |||
|         procinst_duration_by_department = client.execute(qs2) | |||
| 
 | |||
|         qs3 = db_helper.querystring_procinst_duration_by_senior(start_time, end_time) | |||
|         procinst_duration_by_senior = client.execute(qs3)[0][0] | |||
| 
 | |||
|         for index, (department, elapse) in enumerate(procinst_duration_by_department): | |||
|             if procinst_duration_by_senior >= elapse: | |||
|                 procinst_duration_by_department.insert(index, ('高管', procinst_duration_by_senior)) | |||
|                 break | |||
| 
 | |||
|         generate_word_report()  # 生成报表文件 | |||
|     except Exception as error: | |||
|         print('程序运行出错:', error) | |||
|         logging.error('程序运行出错:{}'.format(error)) | |||
| 
 | |||
|     # input("请按任意键退出...") | |||
| @ -0,0 +1,36 @@ | |||
| altgraph==0.17.3 | |||
| APScheduler==3.10.1 | |||
| backports.zoneinfo==0.2.1 | |||
| certifi==2022.12.7 | |||
| charset-normalizer==3.1.0 | |||
| clickhouse-driver==0.2.5 | |||
| contourpy==1.0.7 | |||
| cycler==0.11.0 | |||
| decorator==5.1.1 | |||
| fonttools==4.39.3 | |||
| idna==3.4 | |||
| importlib-resources==5.12.0 | |||
| kiwisolver==1.4.4 | |||
| logging==0.4.9.6 | |||
| lxml==4.9.2 | |||
| matplotlib==3.7.1 | |||
| numpy==1.24.2 | |||
| packaging==23.0 | |||
| pefile==2023.2.7 | |||
| Pillow==9.5.0 | |||
| pyinstaller==5.10.1 | |||
| pyinstaller-hooks-contrib==2023.2 | |||
| pyparsing==3.0.9 | |||
| python-dateutil==2.8.2 | |||
| python-docx==0.8.11 | |||
| pytz==2023.3 | |||
| pytz-deprecation-shim==0.1.0.post0 | |||
| pywin32-ctypes==0.2.0 | |||
| qiniu==7.10.0 | |||
| requests==2.28.2 | |||
| self==2020.12.3 | |||
| six==1.16.0 | |||
| tzdata==2023.3 | |||
| tzlocal==4.3 | |||
| urllib3==1.26.15 | |||
| zipp==3.15.0 | |||
					Loading…
					
					
				
		Reference in new issue