Improve search
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db import transaction
|
||||
from django.db.models import Avg, Count, Q, Sum
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.utils import timezone
|
||||
from django.views import View
|
||||
|
||||
from core.models import (
|
||||
AIRunLog,
|
||||
BusinessPlanDocument,
|
||||
BusinessPlanRevision,
|
||||
CommandAction,
|
||||
@@ -20,6 +26,15 @@ from core.translation.engine import parse_quick_mode_title
|
||||
class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
template_name = "pages/command-routing.html"
|
||||
|
||||
@staticmethod
|
||||
def _normalize_action_positions(profile):
|
||||
rows = list(profile.actions.order_by("position", "id"))
|
||||
for idx, row in enumerate(rows):
|
||||
if row.position != idx:
|
||||
row.position = idx
|
||||
row.save(update_fields=["position", "updated_at"])
|
||||
return rows
|
||||
|
||||
def _context(self, request):
|
||||
profiles = (
|
||||
CommandProfile.objects.filter(user=request.user)
|
||||
@@ -29,21 +44,12 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
documents = BusinessPlanDocument.objects.filter(user=request.user).order_by(
|
||||
"-updated_at"
|
||||
)[:30]
|
||||
bridges = TranslationBridge.objects.filter(user=request.user).order_by("-id")
|
||||
events = (
|
||||
TranslationEventLog.objects.filter(bridge__user=request.user)
|
||||
.select_related("bridge")
|
||||
.order_by("-created_at")[:50]
|
||||
)
|
||||
return {
|
||||
"profiles": profiles,
|
||||
"documents": documents,
|
||||
"bridges": bridges,
|
||||
"events": events,
|
||||
"channel_services": ("web", "xmpp", "signal", "whatsapp"),
|
||||
"directions": ("ingress", "egress", "scratchpad_mirror"),
|
||||
"action_types": ("extract_bp", "post_result", "save_document"),
|
||||
"bridge_directions": ("a_to_b", "b_to_a", "bidirectional"),
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
@@ -149,10 +155,66 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
profile__user=request.user,
|
||||
)
|
||||
row.enabled = bool(request.POST.get("enabled"))
|
||||
row.position = int(request.POST.get("position") or 0)
|
||||
row.save()
|
||||
if request.POST.get("position") not in (None, ""):
|
||||
row.position = int(request.POST.get("position") or 0)
|
||||
row.save(update_fields=["enabled", "position", "updated_at"])
|
||||
else:
|
||||
row.save(update_fields=["enabled", "updated_at"])
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "action_move":
|
||||
row = get_object_or_404(
|
||||
CommandAction,
|
||||
id=request.POST.get("command_action_id"),
|
||||
profile__user=request.user,
|
||||
)
|
||||
direction = str(request.POST.get("direction") or "").strip().lower()
|
||||
if direction not in {"up", "down"}:
|
||||
return redirect("command_routing")
|
||||
with transaction.atomic():
|
||||
ordered = self._normalize_action_positions(row.profile)
|
||||
action_ids = [entry.id for entry in ordered]
|
||||
try:
|
||||
idx = action_ids.index(row.id)
|
||||
except ValueError:
|
||||
return redirect("command_routing")
|
||||
target_idx = idx - 1 if direction == "up" else idx + 1
|
||||
if target_idx < 0 or target_idx >= len(ordered):
|
||||
return redirect("command_routing")
|
||||
other = ordered[target_idx]
|
||||
current_pos = ordered[idx].position
|
||||
ordered[idx].position = other.position
|
||||
other.position = current_pos
|
||||
ordered[idx].save(update_fields=["position", "updated_at"])
|
||||
other.save(update_fields=["position", "updated_at"])
|
||||
return redirect("command_routing")
|
||||
|
||||
return redirect("command_routing")
|
||||
|
||||
|
||||
class TranslationSettings(LoginRequiredMixin, View):
|
||||
template_name = "pages/translation-settings.html"
|
||||
|
||||
def _context(self, request):
|
||||
bridges = TranslationBridge.objects.filter(user=request.user).order_by("-id")
|
||||
events = (
|
||||
TranslationEventLog.objects.filter(bridge__user=request.user)
|
||||
.select_related("bridge")
|
||||
.order_by("-created_at")[:50]
|
||||
)
|
||||
return {
|
||||
"bridges": bridges,
|
||||
"events": events,
|
||||
"channel_services": ("web", "xmpp", "signal", "whatsapp"),
|
||||
"bridge_directions": ("a_to_b", "b_to_a", "bidirectional"),
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
return render(request, self.template_name, self._context(request))
|
||||
|
||||
def post(self, request):
|
||||
action = str(request.POST.get("action") or "").strip()
|
||||
|
||||
if action == "bridge_create":
|
||||
quick_title = str(request.POST.get("quick_mode_title") or "").strip()
|
||||
inferred = parse_quick_mode_title(quick_title)
|
||||
@@ -183,16 +245,84 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
quick_mode_title=quick_title,
|
||||
settings={},
|
||||
)
|
||||
return redirect("command_routing")
|
||||
return redirect("translation_settings")
|
||||
|
||||
if action == "bridge_delete":
|
||||
bridge = get_object_or_404(
|
||||
TranslationBridge, id=request.POST.get("bridge_id"), user=request.user
|
||||
)
|
||||
bridge.delete()
|
||||
return redirect("command_routing")
|
||||
return redirect("translation_settings")
|
||||
|
||||
return redirect("command_routing")
|
||||
return redirect("translation_settings")
|
||||
|
||||
|
||||
class AIExecutionLogSettings(LoginRequiredMixin, View):
|
||||
template_name = "pages/ai-execution-log.html"
|
||||
|
||||
def _context(self, request):
|
||||
now = timezone.now()
|
||||
runs_qs = AIRunLog.objects.filter(user=request.user)
|
||||
runs = runs_qs.order_by("-started_at")[:300]
|
||||
last_24h = runs_qs.filter(started_at__gte=now - timedelta(hours=24))
|
||||
last_7d = runs_qs.filter(started_at__gte=now - timedelta(days=7))
|
||||
|
||||
total_runs = runs_qs.count()
|
||||
total_ok = runs_qs.filter(status="ok").count()
|
||||
total_failed = runs_qs.filter(status="failed").count()
|
||||
avg_ms = runs_qs.aggregate(v=Avg("duration_ms")).get("v") or 0
|
||||
success_rate = (float(total_ok) / float(total_runs) * 100.0) if total_runs else 0.0
|
||||
|
||||
usage_totals = runs_qs.aggregate(
|
||||
prompt_chars_total=Sum("prompt_chars"),
|
||||
response_chars_total=Sum("response_chars"),
|
||||
avg_prompt_chars=Avg("prompt_chars"),
|
||||
avg_response_chars=Avg("response_chars"),
|
||||
)
|
||||
|
||||
stats = {
|
||||
"total_runs": total_runs,
|
||||
"total_ok": total_ok,
|
||||
"total_failed": total_failed,
|
||||
"last_24h_runs": last_24h.count(),
|
||||
"last_24h_failed": last_24h.filter(status="failed").count(),
|
||||
"last_7d_runs": last_7d.count(),
|
||||
"avg_duration_ms": int(avg_ms),
|
||||
"success_rate": round(success_rate, 1),
|
||||
"total_prompt_chars": int(usage_totals.get("prompt_chars_total") or 0),
|
||||
"total_response_chars": int(usage_totals.get("response_chars_total") or 0),
|
||||
"avg_prompt_chars": int(usage_totals.get("avg_prompt_chars") or 0),
|
||||
"avg_response_chars": int(usage_totals.get("avg_response_chars") or 0),
|
||||
}
|
||||
|
||||
operation_breakdown = (
|
||||
runs_qs.values("operation")
|
||||
.annotate(
|
||||
total=Count("id"),
|
||||
failed=Count("id", filter=Q(status="failed")),
|
||||
ok=Count("id", filter=Q(status="ok")),
|
||||
)
|
||||
.order_by("-total", "operation")[:20]
|
||||
)
|
||||
model_breakdown = (
|
||||
runs_qs.values("model")
|
||||
.annotate(
|
||||
total=Count("id"),
|
||||
failed=Count("id", filter=Q(status="failed")),
|
||||
ok=Count("id", filter=Q(status="ok")),
|
||||
)
|
||||
.order_by("-total", "model")[:20]
|
||||
)
|
||||
|
||||
return {
|
||||
"stats": stats,
|
||||
"runs": runs,
|
||||
"operation_breakdown": operation_breakdown,
|
||||
"model_breakdown": model_breakdown,
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
return render(request, self.template_name, self._context(request))
|
||||
|
||||
|
||||
class BusinessPlanEditor(LoginRequiredMixin, View):
|
||||
|
||||
Reference in New Issue
Block a user