115 lines
6.0 KiB
Python
115 lines
6.0 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""从新版考核方案 xlsx 生成 9 份绩效导出模版(单 sheet + 占位符)。
|
||
|
||
机制对齐 writeXlsx(IZtCountService.java):遍历单元格做 {key} 子串替换。
|
||
- F2 -> {name}(员工姓名后一格),H2 -> {date}(考核周期后一格)
|
||
- 每个指标行:H 列(本期结果)-> {键名},G 列(绩效数据)-> {detail_键名} 或拆分占位,无则清空
|
||
- 合计行:H 列 -> {total}
|
||
"""
|
||
from copy import copy
|
||
|
||
import openpyxl
|
||
|
||
SRC = r'docs/信息技术部绩效考核标准-新版 - AI下的考核方案.xlsx'
|
||
OUT_DIR = r'codes/zentao/src/main/resources/templates/scope'
|
||
|
||
# (源 sheet 名, 输出文件名, 合计行号, {指标行号: 键名}, {指标行号: G列绩效数据占位})
|
||
SHEETS = [
|
||
('项目经理', '项目经理考核.xlsx', 12,
|
||
{4: 'workloadPrdScore', 5: 'workloadTeamScore', 6: 'versionPlanFinishedRate', 7: 'bugScore',
|
||
8: 'documentQualityScore', 9: 'developFeedbackStory', 10: 'systemStabilityScore',
|
||
11: 'professionalSkillEnhancementScore'},
|
||
{4: '{detail_workloadPrdScore}', 5: '{detail_workloadTeamScore}', 6: '{detail_versionPlanFinishedRate}',
|
||
7: '{detail_bugScore}', 8: '{detail_documentQualityScore}'}),
|
||
('项目经理(王宇航)', '项目经理考核-王宇航.xlsx', 11,
|
||
{4: 'workloadTeamScore', 5: 'versionPlanFinishedRate', 6: 'bugScore', 7: 'documentQualityScore',
|
||
8: 'developFeedbackStory', 9: 'systemStabilityScore', 10: 'professionalSkillEnhancementScore'},
|
||
{4: '{detail_workloadTeamScore}', 5: '{detail_versionPlanFinishedRate}', 6: '{detail_bugScore}',
|
||
7: '{detail_documentQualityScore}'}),
|
||
('产品经理', '产品经理考核.xlsx', 10,
|
||
{4: 'workloadPrdScore', 5: 'workloadTeamScore', 6: 'productProjectOnTimeRateScore', 7: 'productBugRate',
|
||
8: 'productProblemResponse', 9: 'productResponsibilityScore'},
|
||
{4: '{detail_workloadPrdScore}', 5: '{detail_workloadTeamScore}', 6: '{detail_productProjectOnTimeRateScore}',
|
||
7: '{detail_productBugRate}'}),
|
||
('产品助理', '项目助理考核.xlsx', 9,
|
||
{4: 'workloadPrdScore', 5: 'releaseScore', 6: 'productBugRate', 7: 'productProblemResponse',
|
||
8: 'productResponsibilityScore'},
|
||
{4: '{detail_workloadPrdScore}', 6: '{detail_productBugRate}'}),
|
||
('后端开发工程师', '后端开发工程师.xlsx', 12,
|
||
{4: 'punctualityScore', 5: 'bugScore', 6: 'codeQualityScore', 7: 'documentQualityScore', 8: 'saturationScore',
|
||
9: 'workAttitude', 10: 'excellentShare', 11: 'quality'},
|
||
{4: '{totalTask}/{delayTask}/{finishPunctuality}', 5: '{bugCount}/{bugDensity}',
|
||
8: '{workTime}/{examineTime}/{saturation}'}),
|
||
('前端开发工程师', '前端开发工程师.xlsx', 11,
|
||
{4: 'punctualityScore', 5: 'bugScore', 6: 'codeQualityScore', 7: 'saturationScore', 8: 'workAttitude',
|
||
9: 'excellentShare', 10: 'quality'},
|
||
{4: '{totalTask}/{delayTask}/{finishPunctuality}', 5: '{bugCount}/{bugDensity}',
|
||
7: '{workTime}/{examineTime}/{saturation}'}),
|
||
('测试工程师', '测试工程师.xlsx', 11,
|
||
{4: 'punctualityScore', 5: 'documentQualityScore', 6: 'bugFindScore', 7: 'bugScore', 8: 'workAttitude',
|
||
9: 'excellentShare', 10: 'quality'},
|
||
{4: '{totalTask}/{delayTask}/{finishPunctuality}', 6: '{devSlightBug}/{devSeriousBug}/{bugFindRate}',
|
||
7: '{slightBug}/{seriousBug}'}),
|
||
('UI工程师', 'UI工程师.xlsx', 9,
|
||
{4: 'punctualityScore', 5: 'designScore', 6: 'workAttitude', 7: 'uiWorkScore', 8: 'excellentShare'},
|
||
{4: '{totalTask}/{delayTask}/{finishPunctuality}'}),
|
||
('运维工程师', '运维工程师.xlsx', 12,
|
||
{4: 'opsMajorTaskScore', 5: 'opsMonitorScore', 6: 'opsInspectScore', 7: 'opsBackupScore', 8: 'otherOpsScore',
|
||
9: 'systemStabilityScore', 10: 'workAttitude', 11: 'excellentShare'},
|
||
{4: '{detail_opsMajorTaskScore}', 5: '{detail_opsMonitorScore}', 6: '{detail_opsInspectScore}',
|
||
7: '{detail_opsBackupScore}'}),
|
||
]
|
||
|
||
|
||
def copy_sheet(src_ws, dst_ws):
|
||
"""整 sheet 拷贝:值 + 基础样式 + 合并单元格 + 列宽行高。"""
|
||
for row in src_ws.iter_rows():
|
||
for cell in row:
|
||
if cell.value is None and not cell.has_style:
|
||
continue
|
||
nc = dst_ws.cell(row=cell.row, column=cell.column)
|
||
nc.value = cell.value
|
||
if cell.has_style:
|
||
nc.font = copy(cell.font)
|
||
nc.fill = copy(cell.fill)
|
||
nc.border = copy(cell.border)
|
||
nc.alignment = copy(cell.alignment)
|
||
nc.protection = copy(cell.protection)
|
||
nc.number_format = cell.number_format
|
||
for merged in src_ws.merged_cells.ranges:
|
||
dst_ws.merge_cells(str(merged))
|
||
for key, dim in src_ws.column_dimensions.items():
|
||
if dim.width is not None:
|
||
dst_ws.column_dimensions[key].width = dim.width
|
||
for key, dim in src_ws.row_dimensions.items():
|
||
if dim.height is not None:
|
||
dst_ws.row_dimensions[key].height = dim.height
|
||
|
||
|
||
def main():
|
||
src_wb = openpyxl.load_workbook(SRC)
|
||
for sheet_name, out_name, total_row, row_keys, row_details in SHEETS:
|
||
src_ws = src_wb[sheet_name]
|
||
wb = openpyxl.Workbook()
|
||
ws = wb.active
|
||
ws.title = sheet_name
|
||
copy_sheet(src_ws, ws)
|
||
# R2:员工姓名后一格 F2 -> {name},考核周期后一格 H2 -> {date}
|
||
ws.cell(row=2, column=6).value = '{name}'
|
||
ws.cell(row=2, column=8).value = '{date}'
|
||
# 指标行:H 列本期结果 -> {键名};G 列绩效数据 -> 明细占位或清空
|
||
for r, key in row_keys.items():
|
||
ws.cell(row=r, column=8).value = '{%s}' % key
|
||
ws.cell(row=r, column=7).value = row_details.get(r)
|
||
# 合计行结果列 -> {total}
|
||
assert ws.cell(row=total_row, column=1).value == '合计', (sheet_name, total_row)
|
||
ws.cell(row=total_row, column=8).value = '{total}'
|
||
out_path = '%s/%s' % (OUT_DIR, out_name)
|
||
wb.save(out_path)
|
||
print('OK %s <- %s' % (out_name, sheet_name))
|
||
print('done')
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|