2024-06-17 23:40:16 +08:00
|
|
|
|
import json
|
2024-06-17 20:40:32 +08:00
|
|
|
|
import urllib.parse
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
from django.contrib.auth.decorators import login_required
|
|
|
|
|
from django.contrib.staticfiles import finders
|
2024-06-17 20:52:07 +08:00
|
|
|
|
from django.http import JsonResponse, FileResponse
|
2024-06-17 15:52:01 +08:00
|
|
|
|
from django.shortcuts import render
|
2024-06-17 20:40:32 +08:00
|
|
|
|
from django.urls import reverse
|
|
|
|
|
from openpyxl.reader.excel import load_workbook
|
|
|
|
|
|
2024-06-17 23:40:16 +08:00
|
|
|
|
from excel_parser.utils import ExcelDataSaver
|
|
|
|
|
|
2024-06-17 20:40:32 +08:00
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
|
def dl_excel_tpl(request, template_name):
|
|
|
|
|
"""
|
|
|
|
|
该视图提供下载指定的Excel模板文件。
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
request: HttpRequest对象。
|
|
|
|
|
template_name: 请求下载的Excel模板文件的名称,期望是一个字符串。
|
|
|
|
|
fields: 包含字段信息的字典,用于生成Excel文件的表头和格式。
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
FileResponse对象,允许用户下载生成的Excel文件。
|
|
|
|
|
如果文件未找到,则返回一个HttpResponseNotFound响应。
|
|
|
|
|
"""
|
|
|
|
|
# 使用finders.find()根据文件名在Django的静态文件目录中查找文件的绝对路径
|
|
|
|
|
file_path = finders.find(f'excels/{template_name}')
|
2024-06-17 15:52:01 +08:00
|
|
|
|
|
2024-06-17 20:40:32 +08:00
|
|
|
|
# 如果文件路径不存在,返回404响应
|
|
|
|
|
if not file_path:
|
|
|
|
|
return JsonResponse("Excel模板文件不存在", status=400)
|
2024-06-17 15:52:01 +08:00
|
|
|
|
|
2024-06-17 20:40:32 +08:00
|
|
|
|
# 创建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
|
2024-06-17 15:52:01 +08:00
|
|
|
|
def common_parse(request):
|
2024-06-17 20:52:07 +08:00
|
|
|
|
"""
|
|
|
|
|
处理上传的 Excel 文件,与模板对比表头并解析数据。
|
|
|
|
|
|
|
|
|
|
如果请求方法为 POST,会执行以下步骤:
|
|
|
|
|
1. 获取上传的 Excel 文件和模板名称。
|
|
|
|
|
2. 加载上传的 Excel 文件并获取其表头。
|
|
|
|
|
3. 找到并加载对应的模板文件,获取模板表头。
|
|
|
|
|
4. 对比上传文件和模板的表头,若不一致则返回错误信息。
|
|
|
|
|
5. 解析上传文件中的数据,处理 datetime 对象并过滤空行。
|
|
|
|
|
6. 将解析后的数据存储在会话中。
|
|
|
|
|
7. 返回预览页面的 URL。
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
request (HttpRequest): Django 的 HttpRequest 对象,包含请求的详细信息。
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
JsonResponse: JSON 响应,包含预览页面的重定向 URL 或错误信息。
|
|
|
|
|
"""
|
2024-06-17 20:40:32 +08:00
|
|
|
|
if request.method == 'POST':
|
2024-06-17 20:52:07 +08:00
|
|
|
|
# 获取上传的 Excel 文件和模板名称
|
2024-06-17 20:40:32 +08:00
|
|
|
|
excel_file = request.FILES['file']
|
|
|
|
|
template_name = request.POST.get('template_name')
|
2024-06-17 23:40:16 +08:00
|
|
|
|
excel_valid_model = request.POST.get('excel_valid_model')
|
2024-06-18 02:48:18 +08:00
|
|
|
|
excel_save_success_direct = request.POST.get('list_url')
|
|
|
|
|
|
2024-06-17 20:40:32 +08:00
|
|
|
|
|
2024-06-17 20:52:07 +08:00
|
|
|
|
# 加载上传的 Excel 文件
|
2024-06-17 20:40:32 +08:00
|
|
|
|
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
|
2024-06-17 23:40:16 +08:00
|
|
|
|
request.session['excel_valid_model'] = excel_valid_model
|
2024-06-18 02:48:18 +08:00
|
|
|
|
request.session['excel_save_success_direct'] = excel_save_success_direct
|
2024-06-17 20:40:32 +08:00
|
|
|
|
|
|
|
|
|
# 返回新的页面 URL
|
|
|
|
|
return JsonResponse({'redirect_url': reverse("excel_preview")})
|
|
|
|
|
else:
|
|
|
|
|
return JsonResponse({"error": "请求方法错误"}, status=400)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
|
def excel_preview(request):
|
2024-06-17 20:52:07 +08:00
|
|
|
|
"""
|
|
|
|
|
预览 Excel 文件的视图函数。该函数从会话中获取列名和表格数据,并将其渲染到 HTML 模板中。
|
|
|
|
|
|
|
|
|
|
预期的会话数据:
|
|
|
|
|
- 'columns': 包含列名的列表。
|
|
|
|
|
- 'table_data': 包含表格数据的列表。
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
request (HttpRequest): Django 的 HttpRequest 对象,包含请求的详细信息。
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
HttpResponse: 渲染 'excel_preview_table.html' 模板,并包含会话中的列名和表格数据。
|
|
|
|
|
"""
|
2024-06-17 23:40:16 +08:00
|
|
|
|
# 从会话中获取数据
|
2024-06-17 20:40:32 +08:00
|
|
|
|
columns = request.session.get('columns', [])
|
|
|
|
|
table_data = request.session.get('table_data', [])
|
2024-06-17 23:40:16 +08:00
|
|
|
|
excel_valid_model = request.session.get('excel_valid_model', '')
|
2024-06-18 02:48:18 +08:00
|
|
|
|
excel_save_success_direct = request.session.get('excel_save_success_direct', '')
|
2024-06-17 23:40:16 +08:00
|
|
|
|
|
|
|
|
|
context = {
|
|
|
|
|
'columns': columns,
|
|
|
|
|
'table_data': table_data,
|
2024-06-18 02:48:18 +08:00
|
|
|
|
'excel_valid_model': excel_valid_model,
|
|
|
|
|
'excel_save_success_direct': excel_save_success_direct
|
2024-06-17 23:40:16 +08:00
|
|
|
|
}
|
2024-06-17 20:40:32 +08:00
|
|
|
|
|
2024-06-17 20:52:07 +08:00
|
|
|
|
# 渲染 HTML 模板 'excel_preview_table.html',并传递列名和表格数据
|
2024-06-17 23:40:16 +08:00
|
|
|
|
return render(request, 'excel_preview_table.html', context)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def common_save(request):
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 如果请求方法是POST
|
2024-06-17 23:40:16 +08:00
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
try:
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 解析请求体中的JSON数据
|
2024-06-17 23:40:16 +08:00
|
|
|
|
body = json.loads(request.body)
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 获取data字段,如果没有则默认为None
|
2024-06-17 23:40:16 +08:00
|
|
|
|
data = body.get('data', None)
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 获取session中的excel_valid_model字段,如果没有则默认为None
|
2024-06-17 23:40:16 +08:00
|
|
|
|
model_name = request.session.get('excel_valid_model', None)
|
|
|
|
|
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 如果data或model_name为空,返回错误响应
|
2024-06-17 23:40:16 +08:00
|
|
|
|
if not data or not model_name:
|
|
|
|
|
return JsonResponse({'status': 'error', 'message': '无效的数据或模型'})
|
|
|
|
|
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 创建一个ExcelDataSaver对象,用于保存数据
|
2024-06-17 23:40:16 +08:00
|
|
|
|
data_saver = ExcelDataSaver(model_name, data)
|
|
|
|
|
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 如果数据保存成功
|
2024-06-17 23:40:16 +08:00
|
|
|
|
if data_saver.save_data():
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 删除会话中的columns、table_data和excel_valid_model字段
|
2024-06-18 02:48:18 +08:00
|
|
|
|
request.session.pop('columns', None)
|
|
|
|
|
request.session.pop('table_data', None)
|
|
|
|
|
request.session.pop('excel_valid_model', None)
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 获取成功保存后的重定向URL
|
2024-06-18 02:48:18 +08:00
|
|
|
|
redirect_url = request.session.pop('excel_save_success_direct', None)
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 返回成功响应
|
2024-06-18 02:48:18 +08:00
|
|
|
|
return JsonResponse({'status': 'success', 'message': '数据已成功保存', 'redirect_url': redirect_url})
|
2024-06-17 23:40:16 +08:00
|
|
|
|
else:
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 返回错误响应,包含错误信息
|
2024-06-17 23:40:16 +08:00
|
|
|
|
return JsonResponse({'status': 'error', 'message': '数据验证失败', 'errors': data_saver.get_errors()})
|
|
|
|
|
|
|
|
|
|
except json.JSONDecodeError:
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 如果解析请求体中的JSON数据失败,返回错误响应
|
2024-06-17 23:40:16 +08:00
|
|
|
|
return JsonResponse({'status': 'error', 'message': '无法解析请求的JSON数据'})
|
|
|
|
|
|
2024-06-18 12:20:06 +08:00
|
|
|
|
# 如果请求方法不是POST,返回错误响应
|
2024-06-17 23:40:16 +08:00
|
|
|
|
return JsonResponse({'status': 'error', 'message': '仅支持POST请求'})
|