Implement business plans
This commit is contained in:
249
core/views/automation.py
Normal file
249
core/views/automation.py
Normal file
@@ -0,0 +1,249 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.views import View
|
||||
|
||||
from core.models import (
|
||||
BusinessPlanDocument,
|
||||
BusinessPlanRevision,
|
||||
CommandAction,
|
||||
CommandChannelBinding,
|
||||
CommandProfile,
|
||||
TranslationBridge,
|
||||
TranslationEventLog,
|
||||
)
|
||||
from core.translation.engine import parse_quick_mode_title
|
||||
|
||||
|
||||
class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
template_name = "pages/command-routing.html"
|
||||
|
||||
def _context(self, request):
|
||||
profiles = (
|
||||
CommandProfile.objects.filter(user=request.user)
|
||||
.prefetch_related("channel_bindings", "actions")
|
||||
.order_by("slug")
|
||||
)
|
||||
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):
|
||||
return render(request, self.template_name, self._context(request))
|
||||
|
||||
def post(self, request):
|
||||
action = str(request.POST.get("action") or "").strip()
|
||||
|
||||
if action == "profile_create":
|
||||
slug = str(request.POST.get("slug") or "bp").strip().lower() or "bp"
|
||||
profile, _ = CommandProfile.objects.get_or_create(
|
||||
user=request.user,
|
||||
slug=slug,
|
||||
defaults={
|
||||
"name": str(request.POST.get("name") or "Business Plan").strip()
|
||||
or "Business Plan",
|
||||
"enabled": True,
|
||||
"trigger_token": str(
|
||||
request.POST.get("trigger_token") or "#bp#"
|
||||
).strip()
|
||||
or "#bp#",
|
||||
"template_text": str(request.POST.get("template_text") or ""),
|
||||
},
|
||||
)
|
||||
CommandAction.objects.get_or_create(
|
||||
profile=profile,
|
||||
action_type="extract_bp",
|
||||
defaults={"enabled": True, "position": 0},
|
||||
)
|
||||
CommandAction.objects.get_or_create(
|
||||
profile=profile,
|
||||
action_type="save_document",
|
||||
defaults={"enabled": True, "position": 1},
|
||||
)
|
||||
CommandAction.objects.get_or_create(
|
||||
profile=profile,
|
||||
action_type="post_result",
|
||||
defaults={"enabled": True, "position": 2},
|
||||
)
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "profile_update":
|
||||
profile = get_object_or_404(
|
||||
CommandProfile,
|
||||
id=request.POST.get("profile_id"),
|
||||
user=request.user,
|
||||
)
|
||||
profile.name = str(request.POST.get("name") or profile.name).strip()
|
||||
profile.enabled = bool(request.POST.get("enabled"))
|
||||
profile.trigger_token = (
|
||||
str(request.POST.get("trigger_token") or profile.trigger_token).strip()
|
||||
or "#bp#"
|
||||
)
|
||||
profile.reply_required = bool(request.POST.get("reply_required"))
|
||||
profile.exact_match_only = bool(request.POST.get("exact_match_only"))
|
||||
profile.template_text = str(request.POST.get("template_text") or "")
|
||||
profile.visibility_mode = (
|
||||
str(request.POST.get("visibility_mode") or "status_in_source").strip()
|
||||
or "status_in_source"
|
||||
)
|
||||
profile.save()
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "profile_delete":
|
||||
profile = get_object_or_404(
|
||||
CommandProfile,
|
||||
id=request.POST.get("profile_id"),
|
||||
user=request.user,
|
||||
)
|
||||
profile.delete()
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "binding_create":
|
||||
profile = get_object_or_404(
|
||||
CommandProfile,
|
||||
id=request.POST.get("profile_id"),
|
||||
user=request.user,
|
||||
)
|
||||
CommandChannelBinding.objects.create(
|
||||
profile=profile,
|
||||
direction=str(request.POST.get("direction") or "ingress").strip(),
|
||||
service=str(request.POST.get("service") or "web").strip(),
|
||||
channel_identifier=str(
|
||||
request.POST.get("channel_identifier") or ""
|
||||
).strip(),
|
||||
enabled=bool(request.POST.get("enabled") or "1"),
|
||||
)
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "binding_delete":
|
||||
binding = get_object_or_404(
|
||||
CommandChannelBinding,
|
||||
id=request.POST.get("binding_id"),
|
||||
profile__user=request.user,
|
||||
)
|
||||
binding.delete()
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "action_update":
|
||||
row = get_object_or_404(
|
||||
CommandAction,
|
||||
id=request.POST.get("command_action_id"),
|
||||
profile__user=request.user,
|
||||
)
|
||||
row.enabled = bool(request.POST.get("enabled"))
|
||||
row.position = int(request.POST.get("position") or 0)
|
||||
row.save()
|
||||
return redirect("command_routing")
|
||||
|
||||
if action == "bridge_create":
|
||||
quick_title = str(request.POST.get("quick_mode_title") or "").strip()
|
||||
inferred = parse_quick_mode_title(quick_title)
|
||||
TranslationBridge.objects.create(
|
||||
user=request.user,
|
||||
name=str(request.POST.get("name") or "Translation Bridge").strip()
|
||||
or "Translation Bridge",
|
||||
enabled=bool(request.POST.get("enabled") or "1"),
|
||||
a_service=str(request.POST.get("a_service") or "web").strip(),
|
||||
a_channel_identifier=str(
|
||||
request.POST.get("a_channel_identifier") or ""
|
||||
).strip(),
|
||||
a_language=str(
|
||||
request.POST.get("a_language")
|
||||
or inferred.get("a_language")
|
||||
or "en"
|
||||
).strip(),
|
||||
b_service=str(request.POST.get("b_service") or "web").strip(),
|
||||
b_channel_identifier=str(
|
||||
request.POST.get("b_channel_identifier") or ""
|
||||
).strip(),
|
||||
b_language=str(
|
||||
request.POST.get("b_language")
|
||||
or inferred.get("b_language")
|
||||
or "en"
|
||||
).strip(),
|
||||
direction=str(request.POST.get("direction") or "bidirectional").strip(),
|
||||
quick_mode_title=quick_title,
|
||||
settings={},
|
||||
)
|
||||
return redirect("command_routing")
|
||||
|
||||
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("command_routing")
|
||||
|
||||
|
||||
class BusinessPlanEditor(LoginRequiredMixin, View):
|
||||
template_name = "pages/business-plan-editor.html"
|
||||
|
||||
def get(self, request, doc_id):
|
||||
document = get_object_or_404(BusinessPlanDocument, id=doc_id, user=request.user)
|
||||
revisions = document.revisions.order_by("-created_at")[:100]
|
||||
return render(
|
||||
request,
|
||||
self.template_name,
|
||||
{
|
||||
"document": document,
|
||||
"revisions": revisions,
|
||||
},
|
||||
)
|
||||
|
||||
def post(self, request, doc_id):
|
||||
document = get_object_or_404(BusinessPlanDocument, id=doc_id, user=request.user)
|
||||
document.title = str(request.POST.get("title") or document.title).strip()
|
||||
document.status = str(request.POST.get("status") or document.status).strip()
|
||||
document.content_markdown = str(request.POST.get("content_markdown") or "")
|
||||
document.save()
|
||||
BusinessPlanRevision.objects.create(
|
||||
document=document,
|
||||
editor_user=request.user,
|
||||
content_markdown=document.content_markdown,
|
||||
structured_payload=document.structured_payload or {},
|
||||
)
|
||||
return redirect("business_plan_editor", doc_id=str(document.id))
|
||||
|
||||
|
||||
class TranslationPreview(LoginRequiredMixin, View):
|
||||
def post(self, request):
|
||||
bridge = get_object_or_404(
|
||||
TranslationBridge,
|
||||
id=request.POST.get("bridge_id"),
|
||||
user=request.user,
|
||||
)
|
||||
source = str(request.POST.get("source") or "").strip().lower()
|
||||
text = str(request.POST.get("text") or "")
|
||||
if source == "a":
|
||||
target_language = bridge.b_language
|
||||
else:
|
||||
target_language = bridge.a_language
|
||||
return JsonResponse(
|
||||
{
|
||||
"ok": True,
|
||||
"bridge_id": str(bridge.id),
|
||||
"target_language": target_language,
|
||||
"preview": text,
|
||||
"note": "Preview endpoint is non-mutating; final translation occurs in runtime sync.",
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user