feat: admin daily conversation view with date filtering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
from datetime import date
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db.models import Prefetch
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
from openclaw.models import Session, Message, ToolCall
|
||||
|
||||
|
||||
@@ -28,6 +33,73 @@ class ToolCallInline(admin.TabularInline):
|
||||
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)
|
||||
class SessionAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
@@ -51,6 +123,14 @@ class SessionAdmin(admin.ModelAdmin):
|
||||
"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)
|
||||
class MessageAdmin(admin.ModelAdmin):
|
||||
|
||||
43
src/openclaw/templates/admin/openclaw/daily_view.html
Normal file
43
src/openclaw/templates/admin/openclaw/daily_view.html
Normal 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 %}
|
||||
Reference in New Issue
Block a user