feat: admin daily conversation view with date filtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-05 14:57:17 +08:00
parent a20eed9387
commit 9fa698b3ea
2 changed files with 123 additions and 0 deletions

View File

@@ -1,4 +1,9 @@
from datetime import date
from django.contrib import admin from django.contrib import admin
from django.db.models import Prefetch
from django.template.response import TemplateResponse
from openclaw.models import Session, Message, ToolCall from openclaw.models import Session, Message, ToolCall
@@ -28,6 +33,73 @@ class ToolCallInline(admin.TabularInline):
return False return False
def daily_conversation_view(request):
"""Admin standalone view for date-range conversation browsing."""
start_str = request.GET.get("start")
end_str = request.GET.get("end")
agent_filter = request.GET.get("agent", "")
start_date = start_str if start_str else date.today().isoformat()
end_date = end_str if end_str else date.today().isoformat()
agents = list(
Session.objects.values_list("agent_name", flat=True)
.distinct()
.order_by("agent_name")
)
sessions_qs = Session.objects.filter(
start_time__date__gte=start_date,
start_time__date__lte=end_date,
).order_by("start_time")
if agent_filter:
sessions_qs = sessions_qs.filter(agent_name=agent_filter)
messages_prefetch = Prefetch(
"messages",
queryset=Message.objects.order_by("seq"),
)
sessions_qs = sessions_qs.prefetch_related(messages_prefetch)
role_labels = {
"user": "User",
"assistant": "Assistant",
"toolResult": "Tool Result",
}
session_list = []
for session in sessions_qs:
messages = []
for msg in session.messages.all():
messages.append({
"timestamp": msg.timestamp,
"role": msg.role,
"content_text": msg.content_text,
"tool_name": msg.tool_name,
"get_role_label": role_labels.get(msg.role, msg.role),
})
session_list.append({
"session_id": session.session_id,
"agent_name": session.agent_name,
"model_id": session.model_id,
"total_tokens": session.total_tokens,
"start_time": session.start_time,
"messages": messages,
})
context = {
**admin.site.each_context(request),
"start_date": start_date,
"end_date": end_date,
"selected_agent": agent_filter,
"agents": agents,
"sessions": session_list,
"title": "Daily Conversation View",
}
return TemplateResponse(request, "admin/openclaw/daily_view.html", context)
@admin.register(Session) @admin.register(Session)
class SessionAdmin(admin.ModelAdmin): class SessionAdmin(admin.ModelAdmin):
list_display = ( list_display = (
@@ -51,6 +123,14 @@ class SessionAdmin(admin.ModelAdmin):
"pushed_at", "pushed_at",
) )
def get_urls(self):
from django.urls import path
urls = super().get_urls()
custom_urls = [
path("daily/", admin.site.admin_view(daily_conversation_view), name="openclaw_daily"),
]
return custom_urls + urls
@admin.register(Message) @admin.register(Message)
class MessageAdmin(admin.ModelAdmin): class MessageAdmin(admin.ModelAdmin):

View File

@@ -0,0 +1,43 @@
{% extends "admin/base_site.html" %}
{% block content %}
<h1>Daily Conversation View</h1>
<form method="get">
<label>Start date: <input type="date" name="start" value="{{ start_date }}" /></label>
<label>End date: <input type="date" name="end" value="{{ end_date }}" /></label>
<label>Agent:
<select name="agent">
<option value="">All</option>
{% for agent in agents %}
<option value="{{ agent }}" {% if agent == selected_agent %}selected{% endif %}>{{ agent }}</option>
{% endfor %}
</select>
</label>
<button type="submit">Search</button>
</form>
{% if sessions %}
{% for session in sessions %}
<div class="session-block" style="margin-top: 2em; border: 1px solid #ddd; padding: 1em;">
<h2>Session: {{ session.session_id }} ({{ session.agent_name }})</h2>
<p>Model: {{ session.model_id or 'N/A' }} | Tokens: {{ session.total_tokens }} |
Start: {{ session.start_time|default:"N/A" }}</p>
{% for msg in session.messages %}
<div class="message" data-role="{{ msg.role }}" style="padding: 0.5em; margin: 0.3em 0; border-left: 3px solid {% if msg.role == 'user' %}#4CAF50{% elif msg.role == 'assistant' %}#2196F3{% else %}#FF9800{% endif %};">
<strong>{{ msg.timestamp|date:"H:i" }} {{ msg.get_role_label }}</strong>
{% if msg.role == 'toolResult' %}
{% if msg.tool_name %}<em>[Tool: {{ msg.tool_name }}]</em>{% endif %}
{% endif %}
<details>
<summary>Content</summary>
<pre style="white-space: pre-wrap; word-break: break-word;">{{ msg.content_text|default:"(empty)" }}</pre>
</details>
</div>
{% empty %}
<p>No messages.</p>
{% endfor %}
</div>
{% endfor %}
{% endif %}
{% endblock %}