From ccfa43645eca2955f476fbfc8ca8d1357c737d59 Mon Sep 17 00:00:00 2001 From: bruce Date: Mon, 8 Jun 2026 22:25:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(dashboard):=20=E5=A2=9E=E5=8A=A0=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E5=B7=A5=E4=BD=9C=E5=8F=B0=E5=B9=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/urls.py | 5 +- review_agent/views.py | 166 +++++++++++++++++- static/css/login.css | 127 ++++++++++++++ templates/attachment_manager.html | 6 +- templates/home.html | 8 +- templates/knowledge_base.html | 6 +- templates/workbench.html | 173 +++++++++++++++++++ tests/test_application_form_fill_frontend.py | 2 +- tests/test_file_summary_frontend.py | 10 +- tests/test_home_dashboard.py | 146 ++++++++++++++++ tests/test_regulatory_frontend.py | 8 +- 11 files changed, 632 insertions(+), 25 deletions(-) create mode 100644 templates/workbench.html create mode 100644 tests/test_home_dashboard.py diff --git a/config/urls.py b/config/urls.py index caf51ba..3de58ba 100644 --- a/config/urls.py +++ b/config/urls.py @@ -2,10 +2,11 @@ from django.contrib import admin from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView from django.urls import include, path -from review_agent.views import attachment_manager, knowledge_base_manager, stream_chat, workspace +from review_agent.views import attachment_manager, home_dashboard, knowledge_base_manager, stream_chat, workspace urlpatterns = [ - path("", workspace, name="home"), + path("", home_dashboard, name="home"), + path("chat/", workspace, name="chat"), path("knowledge-base/", knowledge_base_manager, name="knowledge_base_manager"), path("attachments/", attachment_manager, name="attachment_manager"), path("", include("review_agent.urls")), diff --git a/review_agent/views.py b/review_agent/views.py index f629cfb..6297a1d 100644 --- a/review_agent/views.py +++ b/review_agent/views.py @@ -1,9 +1,10 @@ from django.contrib.auth.decorators import login_required -from django.db.models import Count, Q +from django.db.models import Count, Q, Sum import json from django.http import HttpRequest, HttpResponse, JsonResponse, StreamingHttpResponse from django.shortcuts import redirect, render +from django.utils.http import urlencode from django.views.decorators.http import require_http_methods from .services import ( @@ -28,6 +29,29 @@ from .models import KnowledgeBaseDocument from .regulatory_review.services.info_extract import ensure_regulatory_condition_candidates +@login_required +@require_http_methods(["GET"]) +def home_dashboard(request: HttpRequest) -> HttpResponse: + """Renders the data-first home dashboard for the current user.""" + + if request.GET.get("conversation"): + query = {"conversation": request.GET["conversation"]} + search = (request.GET.get("q") or "").strip() + if search: + query["q"] = search + return redirect(f"/chat/?{urlencode(query)}") + + context = build_home_dashboard_context(request.user) + return render( + request, + "workbench.html", + { + "page_title": "首页", + "dashboard": context, + }, + ) + + @login_required @require_http_methods(["GET", "POST"]) def workspace(request: HttpRequest) -> HttpResponse: @@ -39,7 +63,7 @@ def workspace(request: HttpRequest) -> HttpResponse: if action == "new_conversation": conversation = create_conversation(request.user) - return redirect(f"/?conversation={conversation.pk}") + return redirect(f"/chat/?conversation={conversation.pk}") if action == "send_message": content = (request.POST.get("prompt") or "").strip() @@ -47,7 +71,7 @@ def workspace(request: HttpRequest) -> HttpResponse: conversation = create_conversation(request.user) if content: send_message(conversation, content) - return redirect(f"/?conversation={conversation.pk}") + return redirect(f"/chat/?conversation={conversation.pk}") search = (request.GET.get("q") or "").strip() conversations = list_conversations(request.user, search) @@ -325,3 +349,139 @@ def _format_form_fill_label(batch: ApplicationFormFillBatch) -> str: if batch.risk_notes: parts.append(f"提示 {len(batch.risk_notes)}") return " · ".join(parts) + + +def build_home_dashboard_context(user) -> dict[str, object]: + conversations = Conversation.objects.filter(user=user) + active_attachments = FileAttachment.objects.filter(user=user).exclude( + upload_status=FileAttachment.UploadStatus.DELETED + ) + active_knowledge_documents = KnowledgeBaseDocument.objects.filter(user=user).exclude( + status=KnowledgeBaseDocument.Status.DELETED + ) + knowledge_context = build_knowledge_base_context_for_user(user) + builtin_source_count = int(knowledge_context.get("source_count") or 0) + collection_chunk_count = int((knowledge_context.get("collection") or {}).get("count") or 0) + managed_document_count = active_knowledge_documents.count() + file_batches = FileSummaryBatch.objects.filter(user=user).select_related("conversation") + regulatory_batches = RegulatoryReviewBatch.objects.filter(user=user).select_related("conversation") + form_fill_batches = ApplicationFormFillBatch.objects.filter(user=user, is_deleted=False).select_related("conversation") + + batch_status_counts = _build_batch_status_counts(file_batches, regulatory_batches, form_fill_batches) + total_batches = file_batches.count() + regulatory_batches.count() + form_fill_batches.count() + successful_batches = batch_status_counts["success"] + handled_batches = successful_batches + batch_status_counts["failed"] + recent_records = _build_recent_dashboard_records( + conversations.order_by("-updated_at", "-id")[:8], + file_batches.order_by("-created_at", "-id")[:8], + regulatory_batches.order_by("-created_at", "-id")[:8], + form_fill_batches.order_by("-created_at", "-id")[:8], + ) + + return { + "metrics": { + "conversation_count": conversations.count(), + "recent_conversation_count": conversations.filter(messages__isnull=False).distinct().count(), + "attachment_count": active_attachments.count(), + "active_attachment_count": active_attachments.filter(is_active=True).count(), + "knowledge_document_count": managed_document_count + builtin_source_count, + "running_batch_count": batch_status_counts["running"], + "handled_batch_count": handled_batches, + "success_batch_count": successful_batches, + "waiting_batch_count": batch_status_counts["waiting"], + "failed_batch_count": batch_status_counts["failed"], + "total_batch_count": total_batches, + }, + "knowledge": { + "document_count": managed_document_count, + "builtin_source_count": builtin_source_count, + "total_material_count": managed_document_count + builtin_source_count, + "active_document_count": active_knowledge_documents.filter(is_active=True).count(), + "indexed_document_count": active_knowledge_documents.filter(indexed_chunk_count__gt=0).count(), + "managed_chunk_count": active_knowledge_documents.aggregate(total=Sum("indexed_chunk_count"))["total"] or 0, + "chunk_count": collection_chunk_count, + }, + "attachments": { + "attachment_count": active_attachments.count(), + "active_attachment_count": active_attachments.filter(is_active=True).count(), + "recent_attachment_count": active_attachments.order_by("-created_at", "-id")[:5].count(), + "conversation_count": active_attachments.values("conversation_id").distinct().count(), + }, + "workflow": { + "file_summary_count": file_batches.count(), + "regulatory_review_count": regulatory_batches.count(), + "application_form_fill_count": form_fill_batches.count(), + **batch_status_counts, + }, + "recent_records": recent_records, + } + + +def _build_batch_status_counts(file_batches, regulatory_batches, form_fill_batches) -> dict[str, int]: + running_statuses = { + FileSummaryBatch.Status.PENDING, + FileSummaryBatch.Status.RUNNING, + ApplicationFormFillBatch.Status.PENDING, + ApplicationFormFillBatch.Status.RUNNING, + RegulatoryReviewBatch.Status.PENDING, + RegulatoryReviewBatch.Status.RUNNING, + } + waiting_statuses = { + ApplicationFormFillBatch.Status.WAITING_USER, + RegulatoryReviewBatch.Status.WAITING_USER, + } + success_statuses = { + FileSummaryBatch.Status.SUCCESS, + RegulatoryReviewBatch.Status.SUCCESS, + ApplicationFormFillBatch.Status.SUCCESS, + ApplicationFormFillBatch.Status.PARTIAL_SUCCESS, + } + failed_statuses = { + FileSummaryBatch.Status.FAILED, + RegulatoryReviewBatch.Status.FAILED, + ApplicationFormFillBatch.Status.FAILED, + } + statuses = [ + *file_batches.values_list("status", flat=True), + *regulatory_batches.values_list("status", flat=True), + *form_fill_batches.values_list("status", flat=True), + ] + return { + "running": sum(1 for status in statuses if status in running_statuses), + "waiting": sum(1 for status in statuses if status in waiting_statuses), + "success": sum(1 for status in statuses if status in success_statuses), + "failed": sum(1 for status in statuses if status in failed_statuses), + } + + +def _build_recent_dashboard_records(conversations, file_batches, regulatory_batches, form_fill_batches) -> list[dict[str, object]]: + records = [] + for conversation in conversations: + records.append( + { + "type": "对话", + "title": conversation.title or "新对话", + "status": "已更新", + "updated_at": conversation.updated_at, + "url": f"/chat/?conversation={conversation.pk}", + } + ) + for batch in file_batches: + records.append(_batch_record(batch, "文件汇总")) + for batch in regulatory_batches: + status = batch.status + risk_label = _format_risk_label(batch.risk_summary or {}) + records.append(_batch_record(batch, "法规核查", status_label=risk_label or status)) + for batch in form_fill_batches: + records.append(_batch_record(batch, "申报填表")) + return sorted(records, key=lambda item: item["updated_at"], reverse=True)[:8] + + +def _batch_record(batch, record_type: str, status_label: str | None = None) -> dict[str, object]: + return { + "type": record_type, + "title": batch.batch_no, + "status": status_label or batch.status, + "updated_at": batch.created_at, + "url": f"/chat/?conversation={batch.conversation_id}", + } diff --git a/static/css/login.css b/static/css/login.css index e21ca9b..b7fa671 100644 --- a/static/css/login.css +++ b/static/css/login.css @@ -1478,6 +1478,116 @@ input:focus { background: #eaf2ff; } +.dashboard-page { + display: grid; + align-content: start; + gap: 12px; + height: calc(100vh - 60px); + overflow-y: auto; + padding: 16px 24px 20px; + background: var(--bg); +} + +.dashboard-hero, +.metric-grid, +.dashboard-split, +.dashboard-panel { + width: min(1440px, 100%); + margin: 0 auto; +} + +.dashboard-hero { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 0; +} + +.dashboard-hero h1 { + margin: 2px 0; + font-size: 22px; +} + +.dashboard-hero p { + margin: 0; + color: var(--muted); + font-size: 13px; +} + +.dashboard-primary-action { + background: #ffffff; +} + +.metric-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px; +} + +.metric-card { + display: grid; + gap: 8px; + min-height: 104px; + padding: 14px; + border: 1px solid var(--line); + border-radius: 8px; + background: #ffffff; +} + +.metric-card span, +.metric-card em { + color: var(--muted); + font-size: 12px; + font-style: normal; + font-weight: 700; +} + +.metric-card strong { + color: var(--text); + font-size: 30px; + line-height: 1; +} + +.dashboard-split { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.dashboard-stat-list { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + margin: 0; +} + +.dashboard-stat-list div { + display: grid; + gap: 6px; + padding: 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #f8fafc; +} + +.dashboard-stat-list dt { + color: var(--muted); + font-size: 12px; + font-weight: 700; +} + +.dashboard-stat-list dd { + margin: 0; + color: var(--text); + font-size: 22px; + font-weight: 800; +} + +.recent-activity-table td { + height: 44px; +} + .table-empty, .attachment-manager-empty { color: var(--muted); @@ -2293,6 +2403,23 @@ input:focus { grid-template-columns: 1fr; gap: 4px; } + + .dashboard-page { + height: auto; + min-height: calc(100vh - 60px); + padding: 12px; + } + + .dashboard-hero { + align-items: stretch; + flex-direction: column; + } + + .metric-grid, + .dashboard-split, + .dashboard-stat-list { + grid-template-columns: 1fr; + } } @keyframes pulse-caret { diff --git a/templates/attachment_manager.html b/templates/attachment_manager.html index 5b7fd7c..581eb24 100644 --- a/templates/attachment_manager.html +++ b/templates/attachment_manager.html @@ -9,8 +9,8 @@
@@ -52,7 +52,7 @@ {% endfor %} {% if selected_conversation %} - 返回对话 + 返回对话 {% endif %}
diff --git a/templates/home.html b/templates/home.html index 82d1c7c..467b64b 100644 --- a/templates/home.html +++ b/templates/home.html @@ -9,8 +9,8 @@
-
+ {% csrf_token %} diff --git a/templates/knowledge_base.html b/templates/knowledge_base.html index efc87cd..c899103 100644 --- a/templates/knowledge_base.html +++ b/templates/knowledge_base.html @@ -9,8 +9,8 @@
{{ knowledge_base.status.label }} - 返回对话 + 返回对话
diff --git a/templates/workbench.html b/templates/workbench.html new file mode 100644 index 0000000..e8a323e --- /dev/null +++ b/templates/workbench.html @@ -0,0 +1,173 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}首页 - DEMO-AGENT V2{% endblock %} +{% block body_class %}app-body{% endblock %} + +{% block content %} +
+
+ + +
+
+ +
+
+
+ +
+
+
+

首页

+

注册资料审核工作台

+

当前账号资料、知识库、附件与审核处理数据总览。

+
+ 进入审核智能体 +
+ +
+
+ 对话总数 + {{ dashboard.metrics.conversation_count }} + 已处理 {{ dashboard.metrics.recent_conversation_count }} +
+
+ 附件总数 + {{ dashboard.metrics.attachment_count }} + 启用 {{ dashboard.metrics.active_attachment_count }} +
+
+ 知识库材料 + {{ dashboard.metrics.knowledge_document_count }} + 管理 {{ dashboard.knowledge.document_count }} · 内置 {{ dashboard.knowledge.builtin_source_count }} +
+
+ 执行中批次 + {{ dashboard.metrics.running_batch_count }} + 总批次 {{ dashboard.metrics.total_batch_count }} +
+
+ 已处理批次 + {{ dashboard.metrics.handled_batch_count }} + 成功 {{ dashboard.metrics.success_batch_count }} +
+
+ 等待确认 + {{ dashboard.metrics.waiting_batch_count }} + 需人工处理 +
+
+ 失败批次 + {{ dashboard.metrics.failed_batch_count }} + 需排查 +
+
+ 申报填表 + {{ dashboard.workflow.application_form_fill_count }} + 自动填表批次 +
+
+ +
+
+
+

知识库概览

+
+
+
+
管理文档
+
{{ dashboard.knowledge.document_count }}
+
+
+
内置材料
+
{{ dashboard.knowledge.builtin_source_count }}
+
+
+
已索引
+
{{ dashboard.knowledge.indexed_document_count }}
+
+
+
向量片段
+
{{ dashboard.knowledge.chunk_count }}
+
+
+
+ +
+
+

附件与文档概览

+
+
+
+
附件总数
+
{{ dashboard.attachments.attachment_count }}
+
+
+
启用附件
+
{{ dashboard.attachments.active_attachment_count }}
+
+
+
最近上传
+
{{ dashboard.attachments.recent_attachment_count }}
+
+
+
关联对话
+
{{ dashboard.attachments.conversation_count }}
+
+
+
+
+ +
+
+

最近处理记录

+ 最近 8 条 +
+
+ + + + + + + + + + + + {% for record in dashboard.recent_records %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
类型名称或批次号状态更新时间入口
{{ record.type }}{{ record.title }}{{ record.status }}{{ record.updated_at|date:"Y-m-d H:i" }} + 查看 +
暂无处理记录
+
+
+
+
+{% endblock %} diff --git a/tests/test_application_form_fill_frontend.py b/tests/test_application_form_fill_frontend.py index 7df21f6..55563f7 100644 --- a/tests/test_application_form_fill_frontend.py +++ b/tests/test_application_form_fill_frontend.py @@ -31,7 +31,7 @@ def test_workspace_renders_application_form_fill_workflow_card(client, django_us ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") content = response.content.decode("utf-8") assert "AFF-CARD" in content diff --git a/tests/test_file_summary_frontend.py b/tests/test_file_summary_frontend.py index 27e9675..15289ac 100644 --- a/tests/test_file_summary_frontend.py +++ b/tests/test_file_summary_frontend.py @@ -17,7 +17,7 @@ def test_workspace_renders_summary_panel(client, django_user_model): ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") assert response.status_code == 200 content = response.content.decode("utf-8") @@ -37,7 +37,7 @@ def test_workspace_links_to_attachment_manager(client, django_user_model): conversation = Conversation.objects.create(user=user, title="会话") client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") assert response.status_code == 200 content = response.content.decode("utf-8") @@ -85,7 +85,7 @@ def test_attachment_manager_selects_conversation_and_lists_attachments(client, d assert "编辑" in content assert "删除" in content assert "attachment-manager-split" in content - assert reverse("home") + f"?conversation={conversation.pk}" in content + assert reverse("chat") + f"?conversation={conversation.pk}" in content def test_attachment_manager_uses_compact_admin_layout(client, django_user_model): @@ -142,7 +142,7 @@ def test_workspace_renders_workflow_history_as_batch_carousel(client, django_use ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") assert response.status_code == 200 content = response.content.decode("utf-8") @@ -265,7 +265,7 @@ def test_workspace_tool_buttons_fill_default_prompts(client, django_user_model): conversation = Conversation.objects.create(user=user, title="会话") client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") content = response.content.decode("utf-8") script = open("static/js/app.js", encoding="utf-8").read() diff --git a/tests/test_home_dashboard.py b/tests/test_home_dashboard.py new file mode 100644 index 0000000..499d8e6 --- /dev/null +++ b/tests/test_home_dashboard.py @@ -0,0 +1,146 @@ +import pytest +from django.urls import reverse + +from review_agent.models import ( + ApplicationFormFillBatch, + Conversation, + FileAttachment, + FileSummaryBatch, + KnowledgeBaseDocument, + RegulatoryReviewBatch, +) + + +pytestmark = pytest.mark.django_db + + +def test_home_dashboard_renders_current_user_metrics(client, django_user_model): + user = django_user_model.objects.create_user(username="owner", password="pass") + other = django_user_model.objects.create_user(username="other", password="pass") + conversation = Conversation.objects.create(user=user, title="注册资料会话") + other_conversation = Conversation.objects.create(user=other, title="其他用户会话") + FileAttachment.objects.create( + conversation=conversation, + user=user, + original_name="active.docx", + storage_path="x/active.docx", + file_size=128, + is_active=True, + ) + FileAttachment.objects.create( + conversation=conversation, + user=user, + original_name="deleted.docx", + storage_path="x/deleted.docx", + file_size=128, + is_active=False, + upload_status=FileAttachment.UploadStatus.DELETED, + ) + FileAttachment.objects.create( + conversation=other_conversation, + user=other, + original_name="other.docx", + storage_path="x/other.docx", + file_size=128, + ) + KnowledgeBaseDocument.objects.create( + user=user, + display_name="法规资料", + original_name="rule.md", + storage_path="kb/rule.md", + file_size=64, + is_active=True, + indexed_chunk_count=3, + ) + KnowledgeBaseDocument.objects.create( + user=user, + display_name="删除资料", + original_name="deleted.md", + storage_path="kb/deleted.md", + file_size=64, + status=KnowledgeBaseDocument.Status.DELETED, + is_active=False, + indexed_chunk_count=5, + ) + KnowledgeBaseDocument.objects.create( + user=other, + display_name="其他资料", + original_name="other.md", + storage_path="kb/other.md", + file_size=64, + indexed_chunk_count=9, + ) + summary = FileSummaryBatch.objects.create( + conversation=conversation, + user=user, + batch_no="FS-RUN", + status=FileSummaryBatch.Status.RUNNING, + ) + RegulatoryReviewBatch.objects.create( + conversation=conversation, + user=user, + source_summary_batch=summary, + batch_no="RR-WAIT", + status=RegulatoryReviewBatch.Status.WAITING_USER, + risk_summary={"high": 2}, + ) + ApplicationFormFillBatch.objects.create( + conversation=conversation, + user=user, + source_summary_batch=summary, + batch_no="AFF-OK", + status=ApplicationFormFillBatch.Status.SUCCESS, + ) + FileSummaryBatch.objects.create( + conversation=other_conversation, + user=other, + batch_no="FS-OTHER", + status=FileSummaryBatch.Status.FAILED, + ) + client.force_login(user) + + response = client.get(reverse("home")) + + assert response.status_code == 200 + content = response.content.decode("utf-8") + assert "注册资料审核工作台" in content + assert "当前账号资料、知识库、附件与审核处理数据总览" in content + assert "工作流流程" not in content + assert "对话总数" in content + assert "附件总数" in content + assert "知识库材料" in content + assert "内置材料" in content + assert f"管理 {1} · 内置" in content + assert "向量片段" in content + assert "FS-RUN" in content + assert "RR-WAIT" in content + assert "AFF-OK" in content + assert "FS-OTHER" not in content + assert "其他用户会话" not in content + assert f'href="{reverse("chat")}?conversation={conversation.pk}"' in content + + +def test_chat_route_renders_review_agent_workspace(client, django_user_model): + user = django_user_model.objects.create_user(username="owner", password="pass") + conversation = Conversation.objects.create(user=user, title="审核会话") + client.force_login(user) + + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") + + assert response.status_code == 200 + content = response.content.decode("utf-8") + assert "审核智能体" in content + assert 'id="summaryPanel"' in content + assert f'action="{reverse("chat")}"' in content + assert f'href="{reverse("chat")}?conversation={conversation.pk}"' in content + + +def test_legacy_home_conversation_redirects_to_chat(client, django_user_model): + user = django_user_model.objects.create_user(username="owner", password="pass") + conversation = Conversation.objects.create(user=user, title="旧入口会话") + client.force_login(user) + + response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + + assert response.status_code == 302 + assert response["Location"] == f"{reverse('chat')}?conversation={conversation.pk}" diff --git a/tests/test_regulatory_frontend.py b/tests/test_regulatory_frontend.py index de59447..9964434 100644 --- a/tests/test_regulatory_frontend.py +++ b/tests/test_regulatory_frontend.py @@ -44,7 +44,7 @@ def test_workspace_renders_regulatory_workflow_card(client, django_user_model): ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") content = response.content.decode("utf-8") assert "RR-CARD" in content @@ -97,7 +97,7 @@ def test_workspace_renders_condition_confirmation_form(client, django_user_model ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") content = response.content.decode("utf-8") assert "适用条件确认" in content @@ -152,7 +152,7 @@ def test_workspace_refreshes_incomplete_condition_confirmation_candidates(client ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") content = response.content.decode("utf-8") assert "体外诊断试剂" in content @@ -193,7 +193,7 @@ def test_workspace_renders_rectification_actions_and_summaries(client, tmp_path, ) client.force_login(user) - response = client.get(f"{reverse('home')}?conversation={conversation.pk}") + response = client.get(f"{reverse('chat')}?conversation={conversation.pk}") content = response.content.decode("utf-8") assert "data-rectification-action=\"full-review\"" in content