XH_Digital_Management/excel_parser/views.py

233 lines
9.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import urllib.parse
from datetime import datetime
from io import BytesIO
from django.contrib.auth.decorators import login_required
from django.contrib.staticfiles import finders
from django.http import JsonResponse, FileResponse
from django.shortcuts import render
from django.urls import reverse
from openpyxl.reader.excel import load_workbook
from openpyxl.workbook import Workbook
from application.hrm_mgnt.models import EmployeeInformation
from excel_parser.utils import ExcelDataSaver
@login_required
def generate_employee_performance_excel(request, template_name):
"""
生成员工绩效情况的Excel文件。
参数:
request: HttpRequest对象。
template_name: 请求下载的Excel模板文件的名称期望是一个字符串。
返回:
FileResponse对象允许用户下载生成的Excel文件。
"""
# 创建一个新的 Excel 工作簿
wb = Workbook()
ws = wb.active
ws.title = '员工绩效情况'
# 添加表头
headers = ['员工', '一级部门', '年份', '年度绩效']
ws.append(headers)
# 查询在职员工信息
employees = EmployeeInformation.objects.filter(status='在职')
# 填充数据
for employee in employees:
row = [employee.name, employee.primary_department, '', '']
ws.append(row)
# 保存 Excel 文件到内存中
output = BytesIO()
wb.save(output)
output.seek(0)
# 创建文件响应
response = FileResponse(output, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = f'attachment; filename="{urllib.parse.quote(template_name)}"'
return response
@login_required
def dl_excel_tpl(request, template_name):
"""
该视图提供下载指定的Excel模板文件。
参数:
request: HttpRequest对象。
template_name: 请求下载的Excel模板文件的名称期望是一个字符串。
fields: 包含字段信息的字典用于生成Excel文件的表头和格式。
返回:
FileResponse对象允许用户下载生成的Excel文件。
如果文件未找到则返回一个HttpResponseNotFound响应。
"""
if template_name == '人力资源管理-员工绩效-Excel上传模板.xlsx':
return generate_employee_performance_excel(request, template_name)
else:
# 使用finders.find()根据文件名在Django的静态文件目录中查找文件的绝对路径
file_path = finders.find(f'excels/{template_name}')
# 如果文件路径不存在返回404响应
if not file_path:
return JsonResponse("Excel模板文件不存在", status=400)
# 创建FileResponse对象设置为以附件形式下载设置MIME类型为Excel文件
response = FileResponse(open(file_path, 'rb'), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
# 设置Content-Disposition头告诉浏览器这是一个需要下载的文件
# 使用urllib.parse.quote对文件名进行URL编码以确保文件名中包含的特殊字符不会引起问题
response['Content-Disposition'] = f'attachment; filename="{urllib.parse.quote(template_name)}"'
return response
@login_required
def common_parse(request):
"""
处理上传的 Excel 文件,与模板对比表头并解析数据。
如果请求方法为 POST会执行以下步骤
1. 获取上传的 Excel 文件和模板名称。
2. 加载上传的 Excel 文件并获取其表头。
3. 找到并加载对应的模板文件,获取模板表头。
4. 对比上传文件和模板的表头,若不一致则返回错误信息。
5. 解析上传文件中的数据,处理 datetime 对象并过滤空行。
6. 将解析后的数据存储在会话中。
7. 返回预览页面的 URL。
参数:
request (HttpRequest): Django 的 HttpRequest 对象,包含请求的详细信息。
返回:
JsonResponse: JSON 响应,包含预览页面的重定向 URL 或错误信息。
"""
if request.method == 'POST':
# 获取上传的 Excel 文件和模板名称
excel_file = request.FILES['file']
template_name = request.POST.get('template_name')
excel_valid_model = request.POST.get('excel_valid_model')
excel_save_success_direct = request.POST.get('list_url')
# 加载上传的 Excel 文件
wb = load_workbook(excel_file)
sheet = wb.active
# 获取上传文件中的列名
uploaded_columns = [cell.value for cell in sheet[1]]
# 获取模板中的列名
template_path = finders.find(f'excels/{template_name}')
if not template_path:
return JsonResponse({'error': "Excel模板文件不存在"}, status=400)
template_wb = load_workbook(template_path)
template_sheet = template_wb.active
template_columns = [cell.value for cell in template_sheet[1]]
# 对比表头
if uploaded_columns != template_columns:
return JsonResponse({'error': "请使用填报模板上传"}, status=400)
# 获取数据并处理 datetime 对象
data = []
for row in sheet.iter_rows(min_row=2, values_only=True):
row_data = []
for cell in row:
if isinstance(cell, datetime):
row_data.append(cell.strftime('%Y-%m-%d'))
else:
row_data.append(cell)
# 检查行是否有非空单元格
if any(cell is not None and cell != '' for cell in row_data):
data.append(row_data)
# 存储解析数据到 session 中
request.session['columns'] = uploaded_columns
request.session['table_data'] = data
request.session['excel_valid_model'] = excel_valid_model
request.session['excel_save_success_direct'] = excel_save_success_direct
# 返回新的页面 URL
return JsonResponse({'redirect_url': reverse("excel_preview")})
else:
return JsonResponse({"error": "请求方法错误"}, status=400)
@login_required
def excel_preview(request):
"""
预览 Excel 文件的视图函数。该函数从会话中获取列名和表格数据,并将其渲染到 HTML 模板中。
预期的会话数据:
- 'columns': 包含列名的列表。
- 'table_data': 包含表格数据的列表。
参数:
request (HttpRequest): Django 的 HttpRequest 对象,包含请求的详细信息。
返回:
HttpResponse: 渲染 'excel_preview_table.html' 模板,并包含会话中的列名和表格数据。
"""
# 从会话中获取数据
columns = request.session.get('columns', [])
table_data = request.session.get('table_data', [])
excel_valid_model = request.session.get('excel_valid_model', '')
excel_save_success_direct = request.session.get('excel_save_success_direct', '')
context = {
'columns': columns,
'table_data': table_data,
'excel_valid_model': excel_valid_model,
'excel_save_success_direct': excel_save_success_direct
}
# 渲染 HTML 模板 'excel_preview_table.html',并传递列名和表格数据
return render(request, 'excel_preview_table.html', context)
def common_save(request):
# 如果请求方法是POST
if request.method == 'POST':
try:
# 解析请求体中的JSON数据
body = json.loads(request.body)
# 获取data字段如果没有则默认为None
data = body.get('data', None)
# 获取session中的excel_valid_model字段如果没有则默认为None
model_name = request.session.get('excel_valid_model', None)
# 如果data或model_name为空返回错误响应
if not data or not model_name:
return JsonResponse({'status': 'error', 'message': '无效的数据或模型'})
# 创建一个ExcelDataSaver对象用于保存数据
data_saver = ExcelDataSaver(model_name, data)
# 如果数据保存成功
if data_saver.save_data():
# 删除会话中的columns、table_data和excel_valid_model字段
request.session.pop('columns', None)
request.session.pop('table_data', None)
request.session.pop('excel_valid_model', None)
# 获取成功保存后的重定向URL
redirect_url = request.session.pop('excel_save_success_direct', None)
# 返回成功响应
return JsonResponse({'status': 'success', 'message': '数据已成功保存', 'redirect_url': redirect_url})
else:
# 返回错误响应,包含错误信息
return JsonResponse({'status': 'error', 'message': '数据验证失败', 'errors': data_saver.get_errors()})
except json.JSONDecodeError:
# 如果解析请求体中的JSON数据失败返回错误响应
return JsonResponse({'status': 'error', 'message': '无法解析请求的JSON数据'})
# 如果请求方法不是POST返回错误响应
return JsonResponse({'status': 'error', 'message': '仅支持POST请求'})