diff --git a/fzjgact/db.sqlite3 b/fzjgact/db.sqlite3 index f02ab54..aef337e 100644 Binary files a/fzjgact/db.sqlite3 and b/fzjgact/db.sqlite3 differ diff --git a/fzjgact/huodong/__pycache__/views.cpython-311.pyc b/fzjgact/huodong/__pycache__/views.cpython-311.pyc index 07310f2..8c0caad 100644 Binary files a/fzjgact/huodong/__pycache__/views.cpython-311.pyc and b/fzjgact/huodong/__pycache__/views.cpython-311.pyc differ diff --git a/fzjgact/huodong/templates/branch_all.html b/fzjgact/huodong/templates/branch_all.html index 380735e..63de525 100644 --- a/fzjgact/huodong/templates/branch_all.html +++ b/fzjgact/huodong/templates/branch_all.html @@ -1,292 +1,305 @@ -{% extends 'base.html' %} -{% load custom_filters %} - -{% block content %} -
-
- -
- -
- -
-
-
-
最初活动
-

{{ earliest_act.branch }}{{ earliest_act.name }}

-

- {{ earliest_act.description }} -

-
-
- - - - - {{ earliest_act.start_time|format_chinese_full_date }} - -
-
-
- - -
- - - -
- - -
-
-
-
最后活动
-

{{ latest_act.branch }}{{ latest_act.name }}

-

- {{ latest_act.description }} -

-
-
- - - - - {{ latest_act.start_time|format_chinese_full_date }} -
-
-
-
- - -
-
-
-
- - - - - - - - - - - - {% for item in branches %} - - - - - - - - {% endfor %} - - - - - - - -
- 分支机构名称 -
- 新建-搬迁-装修 -
- 活动的总数量 - - 活动进行中的数量 -
- - {{ item.branch.name }} - - - {{ item.total_count }} - - {{ item.onging_count }} -
- 总计: {{ total_branch_count }} - - {{ total_activities }} - - {{ ongoing_activities_count }} -
- -
- - - -
- -
    - {% for event in ongoing_events %} -
  1. -
    -
    - - - - - 开始时间 {{ event.start_time| date:"Y年m月d日" }} - -
    - - {% if not event.end_time %} - - {{ event.name }}-未完成 - - {% else %} - - {{ event.name }} - - {% endif %} - -
    -
    - {{ event.description }} -
    - - - - - 结束时间 {{ event.end_time| date:"Y年m月d日" }} - -
    -
    - -
  2. - {% endfor %} -
-
- -
- - -
- -
    - {% for huodong in ongoing_activities %} -
  1. -
    -
    - - - - - 开始时间 {{ huodong.start_time| date:"Y年m月d日" }} - -
    - - {% if not huodong.end_time %} - - - {{ huodong.branch }} - - {{ huodong.name }}-未完成 - - {% endif %} - -
    -
    - {{ huodong.description }} -
    -
    -
    -
  2. - {% endfor %} -
-
- -
- -{% for scope, scope_data in grouped_activities.items %} -
- -
    - {% for activity in scope_data.activities %} -
  1. -
    -
    - - - - - 开始时间 {{ activity.start_time| date:"Y年m月d日" }} - -
    - - {% if not activity.end_time %} - - - {{ activity.branch }} - - {{ activity.name }}-未完成 - - {% else %} - - - {{ activity.branch }} - - {{ activity.name }} - - {% endif %} - -
    -
    - {{ activity.description }} -
    - - - 结束时间 {{ activity.end_time| date:"Y年m月d日" }} - -
    -
    -
  2. - {% endfor %} -
-
-
-{% endfor %} - - -
- - -{% for year in historical_years %} -{% with year_data=historical_grouped_activities|get_item:year %} -{% if year_data %} - -
-{% endif %} -{% endwith %} -{% endfor %} - - -{% endblock %} +{% extends 'base.html' %} +{% load custom_filters %} + +{% block content %} +
+
+ +
+ +
+ +
+
+
+
最初活动
+

{{ earliest_act.branch }}{{ earliest_act.name }}

+

+ {{ earliest_act.description }} +

+
+
+ + + + + {{ earliest_act.start_time|format_chinese_full_date }} + +
+
+
+ + +
+ + + +
+ + +
+
+
+
最后活动
+

{{ latest_act.branch }}{{ latest_act.name }}

+

+ {{ latest_act.description }} +

+
+
+ + + + + {{ latest_act.start_time|format_chinese_full_date }} +
+
+
+
+ + +
+
+
+
+ + + + + + + + + + + + {% for item in branches %} + + + + + + + + {% endfor %} + + + + + + + +
+ 分支机构名称 +
+ 新建-搬迁-装修 +
+ 活动的总数量 + + 活动进行中的数量 +
+ + {{ item.branch.name }} + + + {{ item.total_count }} + + {{ item.onging_count }} +
+ 总计: {{ total_branch_count }} + + {{ total_activities }} + + {{ ongoing_activities_count }} +
+ +
+ + + +
+ +
    + {% for event in ongoing_events %} +
  1. +
    +
    + + + + + 开始时间 {{ event.start_time| date:"Y年m月d日" }} + +
    + + {% if not event.end_time %} + + {{ event.name }}-未完成 + + {% else %} + + {{ event.name }} + + {% endif %} + +
    +
    + {{ event.description }} +
    +
    + {% with branch_count=event.branches.count %} + {% if branch_count == all_branch_count %} + 涉及分支机构:全辖分支机构 + {% elif branch_count == 1 %} + 涉及分支机构:{{ event.branches.first.name }} + {% elif branch_count > 1 %} + 涉及分支机构:{{ event.branches.all.0.name }}、{{ event.branches.all.1.name }}等{{ branch_count }}个营业部 + {% else %} + 涉及分支机构:暂无 + {% endif %} + {% endwith %} +
    + + + + + 结束时间 {{ event.end_time| date:"Y年m月d日" }} + +
    +
    + +
  2. + {% endfor %} +
+
+ +
+ + +
+ +
    + {% for huodong in ongoing_activities %} +
  1. +
    +
    + + + + + 开始时间 {{ huodong.start_time| date:"Y年m月d日" }} + +
    + + {% if not huodong.end_time %} + + + {{ huodong.branch }} + + {{ huodong.name }}-未完成 + + {% endif %} + +
    +
    + {{ huodong.description }} +
    +
    +
    +
  2. + {% endfor %} +
+
+ +
+ +{% for scope, scope_data in grouped_activities.items %} +
+ +
    + {% for activity in scope_data.activities %} +
  1. +
    +
    + + + + + 开始时间 {{ activity.start_time| date:"Y年m月d日" }} + +
    + + {% if not activity.end_time %} + + + {{ activity.branch }} + + {{ activity.name }}-未完成 + + {% else %} + + + {{ activity.branch }} + + {{ activity.name }} + + {% endif %} + +
    +
    + {{ activity.description }} +
    + + + 结束时间 {{ activity.end_time| date:"Y年m月d日" }} + +
    +
    +
  2. + {% endfor %} +
+
+
+{% endfor %} + + +
+ + +{% for year in historical_years %} +{% with year_data=historical_grouped_activities|get_item:year %} +{% if year_data %} + +
+{% endif %} +{% endwith %} +{% endfor %} + + +{% endblock %} diff --git a/fzjgact/huodong/templates/branch_detail.html b/fzjgact/huodong/templates/branch_detail.html index 98e42da..2dc67c9 100644 --- a/fzjgact/huodong/templates/branch_detail.html +++ b/fzjgact/huodong/templates/branch_detail.html @@ -1,145 +1,149 @@ -{% extends 'base.html' %} - -{% block content %} - - -

- - - {{ branch.name }} - - -

- -
-

基本信息

-
-
-

信息系统分类: {{ branch.category|default:"暂无" }}

-

联系人: {{ branch.contact_person|default:"暂无" }}

-

联系电话: {{ branch.contact_phone|default:"暂无" }}

-
-
-

地址: {{ branch.address|default:"暂无" }}

-

成立时间: {{ branch.established_date|date:"Y-m-d"|default:"暂无" }}

-
-
-
- - -
-

关联事件

- {% if events %} -
- {% for event in events %} -
-

{{ event.name }}

-

- {{ event.start_time|date:"Y-m-d H:i" }} - {{ event.end_time|date:"Y-m-d H:i"|default:"进行中" }} -

-

{{ event.description|truncatechars:100 }}

-
- {% endfor %} -
- {% else %} -

暂无事件记录

- {% endif %} -
- - -
-

设备间图片

- {% if equipment_images %} -
- {% for image in equipment_images %} -
- 设备间图片 -
-

{{ image.description|default:"无描述" }}

-

{{ image.upload_date|date:"Y-m-d" }}

-
-
- {% endfor %} -
- {% else %} -

暂无设备间图片

- {% endif %} -
- - -
-

公共电子屏

- {% if public_screens %} -
- - - - - - - - - - - {% for screen in public_screens %} - - - - - - - {% endfor %} - -
类型描述最后演练更新时间
{{ screen.get_screen_type_display }}{{ screen.description|default:"无" }}{{ screen.last_drill.date|date:"Y-m-d"|default:"未演练" }}{{ screen.updated_at|date:"Y-m-d H:i" }}
-
- {% else %} -

暂无公共电子屏信息

- {% endif %} -
-{% for scope, activities in grouped_activities.items %} - -
- -
    - {% for huodong in activities %} -
  1. -
    -
    - - - - - 开始时间 {{ huodong.start_time| date:"Y年m月d日" }} - -
    - - {% if not huodong.end_time %} - - {{ huodong.name }}-未完成 - - {% else %} - - {{ huodong.name }} - - {% endif %} - -
    -
    - {{ huodong.description }} -
    - - - 结束时间 {{ huodong.end_time| date:"Y年m月d日" }} - -
    -
    -
  2. - {% endfor %} -
-
-
-{% endfor %} - -{% endblock %} +{% extends 'base.html' %} + +{% block content %} + + +

+ + + {{ branch.name }} + + +

+ +
+

基本信息

+
+
+

信息系统分类: {{ branch.category|default:"暂无" }}

+

联系人: {{ branch.contact_person|default:"暂无" }}

+

联系电话: {{ branch.contact_phone|default:"暂无" }}

+
+
+

地址: {{ branch.address|default:"暂无" }}

+

成立时间: {{ branch.established_date|date:"Y-m-d"|default:"暂无" }}

+
+
+
+ + +
+

关联事件

+ {% if events %} +
+ {% for event in events %} +
+

{{ event.name }}

+

+ {% if event.end_time %} + 开始日期{{ event.start_time|date:"Y年m月d日" }} 结束日期{{ event.end_time|date:"Y年m月d日" }} + {% else %} + 开始日期{{ event.start_time|date:"Y年m月d日" }},现在进行中 + {% endif %} +

+

{{ event.description|truncatechars:100 }}

+
+ {% endfor %} +
+ {% else %} +

暂无事件记录

+ {% endif %} +
+ + +
+

设备间图片

+ {% if equipment_images %} +
+ {% for image in equipment_images %} +
+ 设备间图片 +
+

{{ image.description|default:"无描述" }}

+

{{ image.upload_date|date:"Y-m-d" }}

+
+
+ {% endfor %} +
+ {% else %} +

暂无设备间图片

+ {% endif %} +
+ + +
+

公共电子屏

+ {% if public_screens %} +
+ + + + + + + + + + + {% for screen in public_screens %} + + + + + + + {% endfor %} + +
类型描述最后演练更新时间
{{ screen.get_screen_type_display }}{{ screen.description|default:"无" }}{{ screen.last_drill.date|date:"Y-m-d"|default:"未演练" }}{{ screen.updated_at|date:"Y-m-d H:i" }}
+
+ {% else %} +

暂无公共电子屏信息

+ {% endif %} +
+{% for scope, activities in grouped_activities.items %} + +
+ +
    + {% for huodong in activities %} +
  1. +
    +
    + + + + + 开始时间 {{ huodong.start_time| date:"Y年m月d日" }} + +
    + + {% if not huodong.end_time %} + + {{ huodong.name }}-未完成 + + {% else %} + + {{ huodong.name }} + + {% endif %} + +
    +
    + {{ huodong.description }} +
    + + + 结束时间 {{ huodong.end_time| date:"Y年m月d日" }} + +
    +
    +
  2. + {% endfor %} +
+
+
+{% endfor %} + +{% endblock %} diff --git a/fzjgact/huodong/views.py b/fzjgact/huodong/views.py index 45ce71e..acd232a 100644 --- a/fzjgact/huodong/views.py +++ b/fzjgact/huodong/views.py @@ -1,441 +1,444 @@ -from rest_framework import viewsets -from .models import Branch, Activity, Evaluation, Event, VideoTerminal -from .serializers import BranchSerializer, ActivitySerializer, EvaluationSerializer -from django.shortcuts import render, redirect -from .models import PublicScreen -from collections import defaultdict -from datetime import datetime -from django.db.models import Q -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from .models import Contact -from django.template.defaulttags import register -from django.http import HttpResponse -import openpyxl -from reportlab.lib import colors -from reportlab.lib.pagesizes import letter -from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle - -import os - -@register.filter -def get_item(dictionary, key): - return dictionary.get(key) - - -class BranchViewSet(viewsets.ModelViewSet): - queryset = Branch.objects.all() - serializer_class = BranchSerializer - - -class ActivityViewSet(viewsets.ModelViewSet): - queryset = Activity.objects.all() - serializer_class = ActivitySerializer - - -class EvaluationViewSet(viewsets.ModelViewSet): - queryset = Evaluation.objects.all() - serializer_class = EvaluationSerializer - - -# 在页面上显示id为branch的详细信息 -def branch_detail(request, branch_id): - # 获取分支机构及其所有关联数据 - branch = Branch.objects.get(pk=branch_id) - - # 获取并分组活动 - activities = Activity.objects.filter(branch=branch).order_by('start_time') - grouped_activities = defaultdict(list) - for activity in activities: - grouped_activities[activity.scope].append(activity) - grouped_activities = dict(grouped_activities) - - # 获取其他关联数据 - events = branch.events.all().order_by('-start_time') - equipment_images = branch.equipment_images.all() - public_screens = branch.public_screens.all() - - # 准备上下文数据 - context = { - 'branch': branch, - 'grouped_activities': grouped_activities, - 'events': events, - 'equipment_images': equipment_images, - 'public_screens': public_screens, - } - return render(request, 'branch_detail.html', context) - - -# 在页面上显示所有的branch以及active的数量,首页显示 -def BranchAll(request): - branches = Branch.objects.exclude(activity__isnull=True).order_by('name') - branches_with_counts = [{'branch': branch, - 'total_count': Activity.objects.filter(branch=branch).count(), - 'onging_count': Activity.objects.filter(branch=branch, end_time__isnull=True).count()} - for branch in branches] - # 返回总数 - total_branch_count = len(branches_with_counts) - # 比如说end_time()的year是2024年,但是now()是2025年的2月,fileter出来,但是如果now()是2025年3月,则不显示 - now = datetime.now() - print(f"当前月份是{now.month},年是{now.year}") - if now.month <= 2: - two_months_ago_year = now.year - 1 - activities = Activity.objects.filter( - Q(start_time__year=now.year) | Q(start_time__year=two_months_ago_year)).order_by('branch') - - else: - activities = Activity.objects.filter(start_time__year=now.year).order_by('branch') - # 返回起止时间 - earliest_act = activities.order_by('start_time').first() - print(earliest_act) - latest_act = activities.order_by('start_time').last() - print(latest_act) - # 确保earliest_act和latest_act不为None时才使用 - # 按activities按照scope分组 - grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0}) - for activity in activities: - grouped_activities[activity.scope]['activities'].append(activity) - - # 计算每个scope的分支机构数量 - for scope_data in grouped_activities.values(): - activity_ids = [a.id for a in scope_data['activities']] - scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count() - - # 转换为普通字典以便模板遍历 - grouped_activities = dict(grouped_activities) - # 统计活动中的事件,即end_time为空的活动 - ongoing_activities = Activity.objects.filter(end_time__isnull=True).order_by('branch','start_time') - # 统计活动的总的数量 - total_activities = Activity.objects.count() - - # 统计活动中的事件的总数 - ongoing_activities_count = ongoing_activities.count() - - branch_count = activities.values('branch').distinct().count() - - # 事件的展示 - ongoing_events = Event.objects.all().order_by('start_time') - - # 获取历年活动数据 - historical_years = [] - historical_grouped_activities = {} - - # 获取最早的年份 - earliest_activity = Activity.objects.order_by('start_time').first() - if earliest_activity: - earliest_year = earliest_activity.start_time.year - current_year = datetime.now().year - - # 从去年开始,逐年递减,直到最早的年份 - for year in range(current_year - 1, earliest_year - 1, -1): - # 获取该年份的活动 - year_activities = Activity.objects.filter(start_time__year=year).order_by('branch') - - # 如果该年份有活动数据 - if year_activities.exists(): - historical_years.append(year) - - # 按照scope分组 - year_grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0}) - for activity in year_activities: - year_grouped_activities[activity.scope]['activities'].append(activity) - - # 计算每个scope的分支机构数量 - for scope_data in year_grouped_activities.values(): - activity_ids = [a.id for a in scope_data['activities']] - scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count() - - # 转换为普通字典 - historical_grouped_activities[year] = dict(year_grouped_activities) - - # 把带有活动数量的branches字典列表传递给模板 - context = {'branches': branches_with_counts, - 'grouped_activities': grouped_activities, - 'earliest_act': earliest_act, - 'latest_act': latest_act, - 'total_branch_count':total_branch_count, - 'branch_count': branch_count, - 'ongoing_activities': ongoing_activities, - 'total_activities': total_activities, - 'ongoing_activities_count': ongoing_activities_count, - 'ongoing_events': ongoing_events, - 'historical_years': historical_years, - 'historical_grouped_activities': historical_grouped_activities - } - return render(request, 'branch_all.html', context) - - -# 生成branchinfo的视图 -def Branchinfo(request): - branches = Branch.objects.all - context = {'branches': branches, } - return render(request, 'branch_info.html', context) - - -def Statistics(request): - # 默认筛选条件:2024年、全部分支机构、完成状态(end_time不为空) - selected_year = request.GET.get('year', '2025') - selected_branch = request.GET.get('branch', 'all') # 'all'表示全部分支机构 - selected_status = request.GET.get('status', 'completed') # 'completed'表示完成状态 - - # 构建查询条件 - filters = Q() - # 年份筛选 - if selected_year != 'all': - filters &= Q(start_time__year=selected_year) - # 分支机构筛选 - if selected_branch != 'all': - filters &= Q(branch_id=selected_branch) - # 状态筛选(完成状态:end_time不为空;未完成:end_time为空) - if selected_status == 'completed': - filters &= ~Q(end_time__isnull=True) - else: - filters &= Q(end_time__isnull=True) - - # 获取符合条件的活动 - activities = Activity.objects.filter(filters).order_by('start_time') - - # 获取所有分支机构用于下拉选择 - branches = Branch.objects.all() - - context = { - 'activities': activities, - 'branches': branches, - 'selected_year': selected_year, - 'selected_branch': selected_branch, - 'selected_status': selected_status - } - return render(request, 'statistics.html', context) - - -# 导出分支机构信息为XLS文件 -def export_branches_xls(request): - # 创建一个工作簿 - workbook = openpyxl.Workbook() - worksheet = workbook.active - worksheet.title = "分支机构信息" - - # 添加表头 - headers = ['分支机构名称', '信息系统类别'] - for col_num, header in enumerate(headers, 1): - worksheet.cell(row=1, column=col_num, value=header) - - # 获取所有分支机构数据 - branches = Branch.objects.all() - - # 填充数据 - for row_num, branch in enumerate(branches, 2): - worksheet.cell(row=row_num, column=1, value=branch.name) - worksheet.cell(row=row_num, column=2, value=branch.category) - - # 设置响应 - response = HttpResponse(content_type='application/vnd.ms-excel') - response['Content-Disposition'] = 'attachment; filename="branches_info.xls"' - - # 保存工作簿到响应 - workbook.save(response) - return response - - -# 导出分支机构信息为PDF文件 -def export_branches_pdf(request): - # 创建响应对象 - response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = 'attachment; filename="branches_info.pdf"' - - # 注册中文字体 - from reportlab.pdfbase import pdfmetrics - from reportlab.pdfbase.ttfonts import TTFont - - # 尝试注册系统中文字体,这里使用Windows系统自带的中文字体 - try: - # 尝试注册微软雅黑字体 - pdfmetrics.registerFont(TTFont('MSYaHei', 'C:/Windows/Fonts/msyh.ttc')) - chinese_font = 'MSYaHei' - except: - try: - # 如果微软雅黑不存在,尝试注册宋体 - pdfmetrics.registerFont(TTFont('SimSun', 'C:/Windows/Fonts/simsun.ttc')) - chinese_font = 'SimSun' - except: - # 如果都找不到,使用默认字体(可能仍会有乱码) - chinese_font = 'Helvetica' - - # 创建PDF文档 - doc = SimpleDocTemplate(response, pagesize=letter) - elements = [] - - # 添加标题 - styles = getSampleStyleSheet() - # 复制Title样式并修改字体 - title_style = ParagraphStyle( - 'ChineseTitle', - parent=styles['Title'], - fontName=chinese_font, - fontSize=18, - spaceAfter=30, - alignment=1 # 1表示居中 - ) - title = Paragraph("分支机构信息", title_style) - elements.append(title) - - # 获取所有分支机构数据 - branches = Branch.objects.all() - - # 准备表格数据 - data = [['分支机构名称', '信息系统类别']] # 表头 - for branch in branches: - data.append([branch.name, branch.category]) - - # 创建表格 - table = Table(data) - - # 设置表格样式 - style = TableStyle([ - ('BACKGROUND', (0, 0), (-1, 0), colors.grey), - ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('FONTNAME', (0, 0), (-1, 0), chinese_font), # 使用中文字体 - ('FONTSIZE', (0, 0), (-1, 0), 14), - ('BOTTOMPADDING', (0, 0), (-1, 0), 12), - ('BACKGROUND', (0, 1), (-1, -1), colors.beige), - ('FONTNAME', (0, 1), (-1, -1), chinese_font), # 使用中文字体 - ('GRID', (0, 0), (-1, -1), 1, colors.black) - ]) - table.setStyle(style) - - # 添加表格到文档 - elements.append(table) - - # 构建PDF - doc.build(elements) - - return response - - -def public_screens(request): - branches = Branch.objects.all() - public_screens = PublicScreen.objects.all().order_by('-created_at') - return render(request, 'public_screens.html', { - 'branches': branches, - 'public_screens': public_screens, - }) - - - - -def equipment_images(request): - # 获取搜索参数 - search_query = request.GET.get('search', '') - - # 根据搜索参数筛选分支机构 - if search_query: - branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').filter(name__icontains=search_query) - else: - branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').all() - - # 分页处理,每页显示12个分支机构 - paginator = Paginator(branches_with_images, 12) - page = request.GET.get('page') - - try: - branches = paginator.page(page) - except PageNotAnInteger: - # 如果页码不是整数,返回第一页 - branches = paginator.page(1) - except EmptyPage: - # 如果页码超出范围,返回最后一页 - branches = paginator.page(paginator.num_pages) - - context = { - 'branches': branches, - 'total_count': branches_with_images.count(), - 'search_query': search_query - } - return render(request, 'equipment_images.html', context) - -def contact_list(request): - # 获取筛选参数 - branches_param = request.GET.get('branches') # 支持多机构选择,使用逗号分隔的ID字符串 - category = request.GET.get('category') - contact_name = request.GET.get('contact_name') - - # 构建查询条件 - filters = Q() - - # 分支机构筛选(支持多选) - if branches_param: - # 将逗号分隔的字符串转换为ID列表 - branch_ids = [bid.strip() for bid in branches_param.split(',') if bid.strip().isdigit()] - if branch_ids: - filters &= Q(branch_id__in=branch_ids) - - # 联系人类别筛选 - if category: - filters &= Q(category__contains=category) - - # 联系人姓名筛选 - if contact_name: - filters &= Q(name__icontains=contact_name) - - # 获取筛选后的联系人 - contacts = Contact.objects.filter(filters) - - # 获取所有分支机构和联系人类别用于下拉选择 - branches = Branch.objects.all() - categories = [choice[0] for choice in Contact.CATEGORY_CHOICES] - - context = { - 'contacts': contacts, - 'branches': branches, - 'categories': categories, - 'selected_branches': branches_param, # 传递选中的机构ID字符串 - 'selected_category': category, - 'selected_contact_name': contact_name - } - return render(request, 'contact_list.html', context) - - -def video_terminal_list(request): - # 获取筛选参数 - selected_branch = request.GET.get('branch', '') - selected_type = request.GET.get('type', '') - - # 获取选中项的名称用于前端显示 - selected_branch_name = '' - if selected_branch: - try: - selected_branch_name = Branch.objects.get(id=selected_branch).name - except Branch.DoesNotExist: - pass - - selected_type_name = '' - if selected_type: - for code, name in VideoTerminal.TERMINAL_TYPE_CHOICES: - if code == selected_type: - selected_type_name = name - break - - # 基础查询集 - terminals = VideoTerminal.objects.all().order_by('branch__name', 'terminal_type') - - # 应用筛选条件 - if selected_branch: - terminals = terminals.filter(branch_id=selected_branch) - if selected_type: - terminals = terminals.filter(terminal_type=selected_type) - - # 获取所有分支机构和终端类型用于筛选下拉框 - branches = Branch.objects.all() - terminal_types = VideoTerminal.TERMINAL_TYPES - - context = { - 'terminals': terminals, - 'branches': branches, - 'terminal_types': terminal_types, - 'selected_branch': selected_branch, - 'selected_type': selected_type, - 'selected_branch_name': selected_branch_name, - 'selected_type_name': selected_type_name, - } - return render(request, 'video_terminals.html', context) +from rest_framework import viewsets +from .models import Branch, Activity, Evaluation, Event, VideoTerminal +from .serializers import BranchSerializer, ActivitySerializer, EvaluationSerializer +from django.shortcuts import render, redirect +from .models import PublicScreen +from collections import defaultdict +from datetime import datetime +from django.db.models import Q +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from .models import Contact +from django.template.defaulttags import register +from django.http import HttpResponse +import openpyxl +from reportlab.lib import colors +from reportlab.lib.pagesizes import letter +from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph +from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle + +import os + +@register.filter +def get_item(dictionary, key): + return dictionary.get(key) + + +class BranchViewSet(viewsets.ModelViewSet): + queryset = Branch.objects.all() + serializer_class = BranchSerializer + + +class ActivityViewSet(viewsets.ModelViewSet): + queryset = Activity.objects.all() + serializer_class = ActivitySerializer + + +class EvaluationViewSet(viewsets.ModelViewSet): + queryset = Evaluation.objects.all() + serializer_class = EvaluationSerializer + + +# 在页面上显示id为branch的详细信息 +def branch_detail(request, branch_id): + # 获取分支机构及其所有关联数据 + branch = Branch.objects.get(pk=branch_id) + + # 获取并分组活动 + activities = Activity.objects.filter(branch=branch).order_by('start_time') + grouped_activities = defaultdict(list) + for activity in activities: + grouped_activities[activity.scope].append(activity) + grouped_activities = dict(grouped_activities) + + # 获取其他关联数据 + events = branch.events.all().order_by('-start_time') + equipment_images = branch.equipment_images.all() + public_screens = branch.public_screens.all() + + # 准备上下文数据 + context = { + 'branch': branch, + 'grouped_activities': grouped_activities, + 'events': events, + 'equipment_images': equipment_images, + 'public_screens': public_screens, + } + return render(request, 'branch_detail.html', context) + + +# 在页面上显示所有的branch以及active的数量,首页显示 +def BranchAll(request): + branches = Branch.objects.exclude(activity__isnull=True).order_by('name') + branches_with_counts = [{'branch': branch, + 'total_count': Activity.objects.filter(branch=branch).count(), + 'onging_count': Activity.objects.filter(branch=branch, end_time__isnull=True).count()} + for branch in branches] + # 返回总数 + total_branch_count = len(branches_with_counts) + # 获取所有分支机构总数(包括没有活动的) + all_branch_count = Branch.objects.count() + # 比如说end_time()的year是2024年,但是now()是2025年的2月,fileter出来,但是如果now()是2025年3月,则不显示 + now = datetime.now() + print(f"当前月份是{now.month},年是{now.year}") + if now.month <= 2: + two_months_ago_year = now.year - 1 + activities = Activity.objects.filter( + Q(start_time__year=now.year) | Q(start_time__year=two_months_ago_year)).order_by('branch') + + else: + activities = Activity.objects.filter(start_time__year=now.year).order_by('branch') + # 返回起止时间 + earliest_act = activities.order_by('start_time').first() + print(earliest_act) + latest_act = activities.order_by('start_time').last() + print(latest_act) + # 确保earliest_act和latest_act不为None时才使用 + # 按activities按照scope分组 + grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0}) + for activity in activities: + grouped_activities[activity.scope]['activities'].append(activity) + + # 计算每个scope的分支机构数量 + for scope_data in grouped_activities.values(): + activity_ids = [a.id for a in scope_data['activities']] + scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count() + + # 转换为普通字典以便模板遍历 + grouped_activities = dict(grouped_activities) + # 统计活动中的事件,即end_time为空的活动 + ongoing_activities = Activity.objects.filter(end_time__isnull=True).order_by('branch','start_time') + # 统计活动的总的数量 + total_activities = Activity.objects.count() + + # 统计活动中的事件的总数 + ongoing_activities_count = ongoing_activities.count() + + branch_count = activities.values('branch').distinct().count() + + # 事件的展示 + ongoing_events = Event.objects.all().order_by('start_time') + + # 获取历年活动数据 + historical_years = [] + historical_grouped_activities = {} + + # 获取最早的年份 + earliest_activity = Activity.objects.order_by('start_time').first() + if earliest_activity: + earliest_year = earliest_activity.start_time.year + current_year = datetime.now().year + + # 从去年开始,逐年递减,直到最早的年份 + for year in range(current_year - 1, earliest_year - 1, -1): + # 获取该年份的活动 + year_activities = Activity.objects.filter(start_time__year=year).order_by('branch') + + # 如果该年份有活动数据 + if year_activities.exists(): + historical_years.append(year) + + # 按照scope分组 + year_grouped_activities = defaultdict(lambda: {'activities': [], 'branch_count': 0}) + for activity in year_activities: + year_grouped_activities[activity.scope]['activities'].append(activity) + + # 计算每个scope的分支机构数量 + for scope_data in year_grouped_activities.values(): + activity_ids = [a.id for a in scope_data['activities']] + scope_data['branch_count'] = Activity.objects.filter(id__in=activity_ids).values('branch').distinct().count() + + # 转换为普通字典 + historical_grouped_activities[year] = dict(year_grouped_activities) + + # 把带有活动数量的branches字典列表传递给模板 + context = {'branches': branches_with_counts, + 'grouped_activities': grouped_activities, + 'earliest_act': earliest_act, + 'latest_act': latest_act, + 'total_branch_count':total_branch_count, + 'all_branch_count': all_branch_count, + 'branch_count': branch_count, + 'ongoing_activities': ongoing_activities, + 'total_activities': total_activities, + 'ongoing_activities_count': ongoing_activities_count, + 'ongoing_events': ongoing_events, + 'historical_years': historical_years, + 'historical_grouped_activities': historical_grouped_activities + } + return render(request, 'branch_all.html', context) + + +# 生成branchinfo的视图 +def Branchinfo(request): + branches = Branch.objects.all + context = {'branches': branches, } + return render(request, 'branch_info.html', context) + + +def Statistics(request): + # 默认筛选条件:2024年、全部分支机构、完成状态(end_time不为空) + selected_year = request.GET.get('year', '2025') + selected_branch = request.GET.get('branch', 'all') # 'all'表示全部分支机构 + selected_status = request.GET.get('status', 'completed') # 'completed'表示完成状态 + + # 构建查询条件 + filters = Q() + # 年份筛选 + if selected_year != 'all': + filters &= Q(start_time__year=selected_year) + # 分支机构筛选 + if selected_branch != 'all': + filters &= Q(branch_id=selected_branch) + # 状态筛选(完成状态:end_time不为空;未完成:end_time为空) + if selected_status == 'completed': + filters &= ~Q(end_time__isnull=True) + else: + filters &= Q(end_time__isnull=True) + + # 获取符合条件的活动 + activities = Activity.objects.filter(filters).order_by('start_time') + + # 获取所有分支机构用于下拉选择 + branches = Branch.objects.all() + + context = { + 'activities': activities, + 'branches': branches, + 'selected_year': selected_year, + 'selected_branch': selected_branch, + 'selected_status': selected_status + } + return render(request, 'statistics.html', context) + + +# 导出分支机构信息为XLS文件 +def export_branches_xls(request): + # 创建一个工作簿 + workbook = openpyxl.Workbook() + worksheet = workbook.active + worksheet.title = "分支机构信息" + + # 添加表头 + headers = ['分支机构名称', '信息系统类别'] + for col_num, header in enumerate(headers, 1): + worksheet.cell(row=1, column=col_num, value=header) + + # 获取所有分支机构数据 + branches = Branch.objects.all() + + # 填充数据 + for row_num, branch in enumerate(branches, 2): + worksheet.cell(row=row_num, column=1, value=branch.name) + worksheet.cell(row=row_num, column=2, value=branch.category) + + # 设置响应 + response = HttpResponse(content_type='application/vnd.ms-excel') + response['Content-Disposition'] = 'attachment; filename="branches_info.xls"' + + # 保存工作簿到响应 + workbook.save(response) + return response + + +# 导出分支机构信息为PDF文件 +def export_branches_pdf(request): + # 创建响应对象 + response = HttpResponse(content_type='application/pdf') + response['Content-Disposition'] = 'attachment; filename="branches_info.pdf"' + + # 注册中文字体 + from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase.ttfonts import TTFont + + # 尝试注册系统中文字体,这里使用Windows系统自带的中文字体 + try: + # 尝试注册微软雅黑字体 + pdfmetrics.registerFont(TTFont('MSYaHei', 'C:/Windows/Fonts/msyh.ttc')) + chinese_font = 'MSYaHei' + except: + try: + # 如果微软雅黑不存在,尝试注册宋体 + pdfmetrics.registerFont(TTFont('SimSun', 'C:/Windows/Fonts/simsun.ttc')) + chinese_font = 'SimSun' + except: + # 如果都找不到,使用默认字体(可能仍会有乱码) + chinese_font = 'Helvetica' + + # 创建PDF文档 + doc = SimpleDocTemplate(response, pagesize=letter) + elements = [] + + # 添加标题 + styles = getSampleStyleSheet() + # 复制Title样式并修改字体 + title_style = ParagraphStyle( + 'ChineseTitle', + parent=styles['Title'], + fontName=chinese_font, + fontSize=18, + spaceAfter=30, + alignment=1 # 1表示居中 + ) + title = Paragraph("分支机构信息", title_style) + elements.append(title) + + # 获取所有分支机构数据 + branches = Branch.objects.all() + + # 准备表格数据 + data = [['分支机构名称', '信息系统类别']] # 表头 + for branch in branches: + data.append([branch.name, branch.category]) + + # 创建表格 + table = Table(data) + + # 设置表格样式 + style = TableStyle([ + ('BACKGROUND', (0, 0), (-1, 0), colors.grey), + ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), + ('ALIGN', (0, 0), (-1, -1), 'CENTER'), + ('FONTNAME', (0, 0), (-1, 0), chinese_font), # 使用中文字体 + ('FONTSIZE', (0, 0), (-1, 0), 14), + ('BOTTOMPADDING', (0, 0), (-1, 0), 12), + ('BACKGROUND', (0, 1), (-1, -1), colors.beige), + ('FONTNAME', (0, 1), (-1, -1), chinese_font), # 使用中文字体 + ('GRID', (0, 0), (-1, -1), 1, colors.black) + ]) + table.setStyle(style) + + # 添加表格到文档 + elements.append(table) + + # 构建PDF + doc.build(elements) + + return response + + +def public_screens(request): + branches = Branch.objects.all() + public_screens = PublicScreen.objects.all().order_by('-created_at') + return render(request, 'public_screens.html', { + 'branches': branches, + 'public_screens': public_screens, + }) + + + + +def equipment_images(request): + # 获取搜索参数 + search_query = request.GET.get('search', '') + + # 根据搜索参数筛选分支机构 + if search_query: + branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').filter(name__icontains=search_query) + else: + branches_with_images = Branch.objects.prefetch_related('equipment_images', 'drawings').all() + + # 分页处理,每页显示12个分支机构 + paginator = Paginator(branches_with_images, 12) + page = request.GET.get('page') + + try: + branches = paginator.page(page) + except PageNotAnInteger: + # 如果页码不是整数,返回第一页 + branches = paginator.page(1) + except EmptyPage: + # 如果页码超出范围,返回最后一页 + branches = paginator.page(paginator.num_pages) + + context = { + 'branches': branches, + 'total_count': branches_with_images.count(), + 'search_query': search_query + } + return render(request, 'equipment_images.html', context) + +def contact_list(request): + # 获取筛选参数 + branches_param = request.GET.get('branches') # 支持多机构选择,使用逗号分隔的ID字符串 + category = request.GET.get('category') + contact_name = request.GET.get('contact_name') + + # 构建查询条件 + filters = Q() + + # 分支机构筛选(支持多选) + if branches_param: + # 将逗号分隔的字符串转换为ID列表 + branch_ids = [bid.strip() for bid in branches_param.split(',') if bid.strip().isdigit()] + if branch_ids: + filters &= Q(branch_id__in=branch_ids) + + # 联系人类别筛选 + if category: + filters &= Q(category__contains=category) + + # 联系人姓名筛选 + if contact_name: + filters &= Q(name__icontains=contact_name) + + # 获取筛选后的联系人 + contacts = Contact.objects.filter(filters) + + # 获取所有分支机构和联系人类别用于下拉选择 + branches = Branch.objects.all() + categories = [choice[0] for choice in Contact.CATEGORY_CHOICES] + + context = { + 'contacts': contacts, + 'branches': branches, + 'categories': categories, + 'selected_branches': branches_param, # 传递选中的机构ID字符串 + 'selected_category': category, + 'selected_contact_name': contact_name + } + return render(request, 'contact_list.html', context) + + +def video_terminal_list(request): + # 获取筛选参数 + selected_branch = request.GET.get('branch', '') + selected_type = request.GET.get('type', '') + + # 获取选中项的名称用于前端显示 + selected_branch_name = '' + if selected_branch: + try: + selected_branch_name = Branch.objects.get(id=selected_branch).name + except Branch.DoesNotExist: + pass + + selected_type_name = '' + if selected_type: + for code, name in VideoTerminal.TERMINAL_TYPE_CHOICES: + if code == selected_type: + selected_type_name = name + break + + # 基础查询集 + terminals = VideoTerminal.objects.all().order_by('branch__name', 'terminal_type') + + # 应用筛选条件 + if selected_branch: + terminals = terminals.filter(branch_id=selected_branch) + if selected_type: + terminals = terminals.filter(terminal_type=selected_type) + + # 获取所有分支机构和终端类型用于筛选下拉框 + branches = Branch.objects.all() + terminal_types = VideoTerminal.TERMINAL_TYPES + + context = { + 'terminals': terminals, + 'branches': branches, + 'terminal_types': terminal_types, + 'selected_branch': selected_branch, + 'selected_type': selected_type, + 'selected_branch_name': selected_branch_name, + 'selected_type_name': selected_type_name, + } + return render(request, 'video_terminals.html', context)