diff --git a/core/history_views.py b/core/history_views.py new file mode 100644 index 0000000..7576756 --- /dev/null +++ b/core/history_views.py @@ -0,0 +1,150 @@ +from django.shortcuts import render +from django.http import HttpResponse +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from django.conf import settings +from datetime import datetime, timedelta +from loguru import logger +import os + +from .models import ( + ReadingRecord, + InsightRecord, + Summary, + FamilyTask, + TodayPlan, +) +from .views import is_weasyprint_available + + +@login_required +def history_records(request): + """历史记录查询页面""" + logger.info("用户访问历史记录查询页面") + + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') + + if not start_date_str or not end_date_str: + today = timezone.now().date() + start_date = datetime(today.year, 1, 1).date() + end_date = today + else: + start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date() + end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date() + + # 查询各个类型的记录 + reading_records = ReadingRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + insight_records = InsightRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + summary_records = Summary.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + family_tasks = FamilyTask.objects.filter( + created_at__date__range=[start_date, end_date] + ).order_by('-created_at') + + today_plans = TodayPlan.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + context = { + 'start_date': start_date, + 'end_date': end_date, + 'reading_records': reading_records, + 'insight_records': insight_records, + 'summary_records': summary_records, + 'family_tasks': family_tasks, + 'today_plans': today_plans, + } + + return render(request, 'core/history_records.html', context) + + +@login_required +def history_pdf(request): + """导出历史记录PDF""" + if not is_weasyprint_available(): + logger.error("WeasyPrint库不可用,无法生成PDF报告") + return HttpResponse("PDF功能不可用,请检查WeasyPrint库是否正确安装", status=500) + + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') + + if not start_date_str or not end_date_str: + return HttpResponse("请提供开始时间和结束时间", status=400) + + try: + start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date() + end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date() + except ValueError: + return HttpResponse("日期格式错误", status=400) + + if start_date > end_date: + return HttpResponse("开始时间不能晚于结束时间", status=400) + + logger.info(f"用户导出历史记录PDF: {start_date} 至 {end_date}") + + try: + # 查询各个类型的记录 + reading_records = ReadingRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + insight_records = InsightRecord.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + summary_records = Summary.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + family_tasks = FamilyTask.objects.filter( + created_at__date__range=[start_date, end_date] + ).order_by('-created_at') + + today_plans = TodayPlan.objects.filter( + date__range=[start_date, end_date] + ).order_by('-date') + + context = { + 'start_date': start_date, + 'end_date': end_date, + 'reading_records': reading_records, + 'insight_records': insight_records, + 'summary_records': summary_records, + 'family_tasks': family_tasks, + 'today_plans': today_plans, + } + + # 渲染HTML模板 + html_string = render(request, 'core/history_pdf.html', context).content.decode('utf-8') + + # 生成PDF + pdf_file = f"history_{start_date_str}_to_{end_date_str}.pdf" + pdf_path = os.path.join(settings.REPORTS_ROOT, pdf_file) + + # 确保报告目录存在 + os.makedirs(settings.REPORTS_ROOT, exist_ok=True) + + # 动态导入WeasyPrint + from weasyprint import HTML + HTML(string=html_string).write_pdf(pdf_path) + + logger.info(f"历史记录PDF生成成功: {pdf_path}") + + # 返回PDF文件 + with open(pdf_path, 'rb') as f: + response = HttpResponse(f.read(), content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="{pdf_file}"' + return response + + except Exception as e: + logger.error(f"生成历史记录PDF失败: {str(e)}") + return HttpResponse(f"生成PDF失败: {str(e)}", status=500) diff --git a/core/templates/core/history_pdf.html b/core/templates/core/history_pdf.html new file mode 100644 index 0000000..2eb83ac --- /dev/null +++ b/core/templates/core/history_pdf.html @@ -0,0 +1,344 @@ + + +
+ +| 日期 | +类型 | +标题 | +来源 | +进度 | +
|---|---|---|---|---|
| {{ record.date|date:"Y-m-d" }} | +{{ record.type.name }} | +{{ record.title }} | +{{ record.source|default:"-" }} | ++ {% if record.progress %} + + {{ record.progress }}% + {% else %} + - + {% endif %} + | +
来源: {{ record.source }}
+ {% endif %} +| 创建时间 | +类型 | +内容 | +优先级 | +状态 | +截止日期 | +
|---|---|---|---|---|---|
| {{ task.created_at|date:"Y-m-d H:i" }} | +{{ task.type.name }} | +{{ task.content|truncatechars:50 }} | ++ + {{ task.priority.name }} + + | ++ + {{ task.status.name }} + + | +{{ task.deadline|date:"Y-m-d"|default:"-" }} | +
| 日期 | +发言人 | +内容 | +类型 | +优先级 | +状态 | +
|---|---|---|---|---|---|
| {{ plan.date|date:"Y-m-d" }} | +{{ plan.speaker.name }} | +{{ plan.content|truncatechars:50 }} | +{{ plan.type.name }} | ++ + {{ plan.priority.name }} + + | ++ + {{ plan.status.name }} + + | +
+ + {{ start_date|date:"Y年m月d日" }} 至 {{ end_date|date:"Y年m月d日" }} +
+| 日期 | +类型 | +标题 | +来源 | +进度 | +
|---|---|---|---|---|
| {{ record.date|date:"Y-m-d" }} | +{{ record.type.name }} | +{{ record.title }} | +{{ record.source|default:"-" }} | +
+ {% if record.progress %}
+
+
+
+ {{ record.progress }}%
+ {% else %}
+ -
+ {% endif %}
+ |
+
该时间段没有阅读记录
+{{ record.content|linebreaks }}
+ {% if record.file %} + + 查看附件 + + {% endif %} +该时间段没有感悟记录
+{{ record.content|linebreaks }}
+ {% if record.source %} +该时间段没有汇总记录
+| 创建时间 | +类型 | +内容 | +优先级 | +状态 | +截止日期 | +
|---|---|---|---|---|---|
| {{ task.created_at|date:"Y-m-d H:i" }} | +{{ task.type.name }} | +{{ task.content|truncatechars:50 }} | ++ + {{ task.priority.name }} + + | ++ + {{ task.status.name }} + + | ++ {% if task.deadline %} + {% if task.is_overdue %} + {{ task.deadline|date:"Y-m-d" }} (已逾期) + {% else %} + {{ task.deadline|date:"Y-m-d" }} + {% endif %} + {% else %} + - + {% endif %} + | +
该时间段没有家庭事项
+| 日期 | +发言人 | +内容 | +类型 | +优先级 | +状态 | +
|---|---|---|---|---|---|
| {{ plan.date|date:"Y-m-d" }} | +{{ plan.speaker.name }} | +{{ plan.content|truncatechars:50 }} | +{{ plan.type.name }} | ++ + {{ plan.priority.name }} + + | ++ + {{ plan.status.name }} + + | +
该时间段没有今日计划
+