Rebuild workspace widgets and behavioral graph views
This commit is contained in:
@@ -40,7 +40,18 @@ from core.models import (
|
||||
WorkspaceConversation,
|
||||
WorkspaceMetricSnapshot,
|
||||
)
|
||||
from core.workspace import DENSITY_POINT_CAPS, downsample_points
|
||||
from core.widget_ids import compose_widget_dom_id
|
||||
from core.workspace import (
|
||||
BEHAVIORAL_GROUPS,
|
||||
BEHAVIORAL_METRIC_MAP,
|
||||
BEHAVIORAL_METRIC_SPECS,
|
||||
DENSITY_POINT_CAPS,
|
||||
build_behavioral_graph_payload,
|
||||
build_behavioral_metric_groups,
|
||||
downsample_points,
|
||||
get_behavioral_metric_graph,
|
||||
sanitize_graph_range,
|
||||
)
|
||||
|
||||
SEND_ENABLED_MODES = {"active", "instant"}
|
||||
OPERATION_LABELS = {
|
||||
@@ -735,6 +746,22 @@ def _compose_widget_url_for_person(user, person, limit=40):
|
||||
return f"{reverse('compose_widget')}?{query}"
|
||||
|
||||
|
||||
def _compose_widget_id_for_person(user, person):
|
||||
preferred_service = _preferred_service_for_person(user, person)
|
||||
identifier_row = _resolve_person_identifier(
|
||||
user=user,
|
||||
person=person,
|
||||
preferred_service=preferred_service,
|
||||
)
|
||||
if identifier_row is None:
|
||||
return ""
|
||||
return compose_widget_dom_id(
|
||||
identifier_row.service,
|
||||
identifier_row.identifier,
|
||||
person.id,
|
||||
)
|
||||
|
||||
|
||||
def _participant_feedback_display(conversation, person):
|
||||
payload = conversation.participant_feedback or {}
|
||||
if not isinstance(payload, dict):
|
||||
@@ -3514,18 +3541,89 @@ def _workspace_nav_urls(person):
|
||||
"ai_workspace_insight_graphs",
|
||||
kwargs={"type": "page", "person_id": person.id},
|
||||
),
|
||||
"graphs_widget_url": reverse(
|
||||
"ai_workspace_insight_graphs",
|
||||
kwargs={"type": "widget", "person_id": person.id},
|
||||
),
|
||||
"information_url": reverse(
|
||||
"ai_workspace_information",
|
||||
kwargs={"type": "page", "person_id": person.id},
|
||||
),
|
||||
"information_widget_url": reverse(
|
||||
"ai_workspace_information",
|
||||
kwargs={"type": "widget", "person_id": person.id},
|
||||
),
|
||||
"help_url": reverse(
|
||||
"ai_workspace_insight_help",
|
||||
kwargs={"type": "page", "person_id": person.id},
|
||||
),
|
||||
"help_widget_url": reverse(
|
||||
"ai_workspace_insight_help",
|
||||
kwargs={"type": "widget", "person_id": person.id},
|
||||
),
|
||||
"workspace_url": f"{reverse('ai_workspace')}?person={person.id}",
|
||||
}
|
||||
|
||||
|
||||
def _behavioral_metric_launch_groups(person):
|
||||
return build_behavioral_metric_groups(
|
||||
lambda spec: {
|
||||
"slug": spec["slug"],
|
||||
"title": spec["menu_title"],
|
||||
"icon": spec["icon"],
|
||||
"widget_url": reverse(
|
||||
"ai_workspace_insight_detail",
|
||||
kwargs={
|
||||
"type": "widget",
|
||||
"person_id": person.id,
|
||||
"metric": spec["slug"],
|
||||
},
|
||||
),
|
||||
"page_url": reverse(
|
||||
"ai_workspace_insight_detail",
|
||||
kwargs={
|
||||
"type": "page",
|
||||
"person_id": person.id,
|
||||
"metric": spec["slug"],
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _render_ai_workspace_widget(
|
||||
request,
|
||||
*,
|
||||
title,
|
||||
unique,
|
||||
window_content,
|
||||
widget_icon,
|
||||
widget_options,
|
||||
**context,
|
||||
):
|
||||
return render(
|
||||
request,
|
||||
"mixins/wm/widget.html",
|
||||
{
|
||||
"title": title,
|
||||
"unique": unique,
|
||||
"window_content": window_content,
|
||||
"widget_icon": widget_icon,
|
||||
"widget_options": widget_options,
|
||||
**context,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _behavioral_range_urls(request):
|
||||
return {
|
||||
"30d": f"{request.path}?range=30d",
|
||||
"90d": f"{request.path}?range=90d",
|
||||
"365d": f"{request.path}?range=365d",
|
||||
"all": f"{request.path}?range=all",
|
||||
}
|
||||
|
||||
|
||||
class AIWorkspace(LoginRequiredMixin, View):
|
||||
template_name = "pages/ai-workspace.html"
|
||||
|
||||
@@ -3641,12 +3739,23 @@ class AIWorkspacePersonWidget(LoginRequiredMixin, View):
|
||||
person,
|
||||
limit=limit,
|
||||
),
|
||||
"compose_widget_id": _compose_widget_id_for_person(request.user, person),
|
||||
"compose_widget_base_url": reverse("compose_widget"),
|
||||
"history_widget_url": (
|
||||
reverse("compose_workspace_history_widget")
|
||||
+ "?"
|
||||
+ urlencode({"person": str(person.id), "limit": limit})
|
||||
),
|
||||
"behavioral_graphs_widget_url": reverse(
|
||||
"ai_workspace_insight_graphs",
|
||||
kwargs={"type": "widget", "person_id": person.id},
|
||||
),
|
||||
"behavioral_graphs_page_url": reverse(
|
||||
"ai_workspace_insight_graphs",
|
||||
kwargs={"type": "page", "person_id": person.id},
|
||||
),
|
||||
"behavioral_graph_groups": _behavioral_metric_launch_groups(person),
|
||||
"behavioral_show_widget_actions": True,
|
||||
"manual_icon_class": "fa-solid fa-paper-plane",
|
||||
"send_target_bundle": _send_target_options_for_person(request.user, person),
|
||||
}
|
||||
@@ -3685,37 +3794,43 @@ class AIWorkspaceInsightDetail(LoginRequiredMixin, View):
|
||||
def get(self, request, type, person_id, metric):
|
||||
if type not in self.allowed_types:
|
||||
return HttpResponseBadRequest("Invalid type specified")
|
||||
spec = INSIGHT_METRICS.get(metric)
|
||||
if spec is None:
|
||||
if str(metric or "").strip() not in BEHAVIORAL_METRIC_MAP:
|
||||
return HttpResponseBadRequest("Unknown insight metric")
|
||||
|
||||
person = get_object_or_404(Person, pk=person_id, user=request.user)
|
||||
conversation = _conversation_for_person(request.user, person)
|
||||
latest_snapshot = conversation.metric_snapshots.first()
|
||||
value = _format_metric_value(conversation, metric, latest_snapshot)
|
||||
group = INSIGHT_GROUPS[spec["group"]]
|
||||
graph_applicable = _metric_supports_history(metric, spec)
|
||||
graph_density = _sanitize_graph_density(request.GET.get("density"))
|
||||
points = []
|
||||
if graph_applicable:
|
||||
points = _history_points(
|
||||
conversation, spec["history_field"], density=graph_density
|
||||
)
|
||||
range_key = sanitize_graph_range(request.GET.get("range"))
|
||||
payload = get_behavioral_metric_graph(
|
||||
user=request.user,
|
||||
person=person,
|
||||
metric_slug=metric,
|
||||
range_key=range_key,
|
||||
density=graph_density,
|
||||
)
|
||||
|
||||
context = {
|
||||
"person": person,
|
||||
"workspace_conversation": conversation,
|
||||
"metric_slug": metric,
|
||||
"metric": spec,
|
||||
"metric_value": value,
|
||||
"metric_psychology_hint": _metric_psychological_read(metric, conversation),
|
||||
"metric_group": group,
|
||||
"graph_points": points,
|
||||
"graph_applicable": graph_applicable,
|
||||
"behavioral_groups": BEHAVIORAL_GROUPS,
|
||||
"metric": payload["metric"],
|
||||
"summary_cards": payload["summary_cards"],
|
||||
"coverage": payload["coverage"],
|
||||
"range_key": range_key,
|
||||
"graph_density": graph_density,
|
||||
"graph_density_caps": DENSITY_POINT_CAPS,
|
||||
"behavioral_graph_groups": _behavioral_metric_launch_groups(person),
|
||||
"behavioral_show_widget_actions": type == "widget",
|
||||
"behavioral_range_urls": _behavioral_range_urls(request),
|
||||
**_workspace_nav_urls(person),
|
||||
}
|
||||
if type == "widget":
|
||||
return _render_ai_workspace_widget(
|
||||
request,
|
||||
title=f"{person.name}: {payload['metric']['title']}",
|
||||
unique=f"ai-behavior-detail-{person.id}-{metric}",
|
||||
window_content="partials/ai-workspace-behavioral-graph-detail.html",
|
||||
widget_icon=payload["metric"]["icon"],
|
||||
widget_options='gs-w="6" gs-h="10" gs-x="6" gs-y="0" gs-min-w="4"',
|
||||
**context,
|
||||
)
|
||||
return render(request, "pages/ai-workspace-insight-detail.html", context)
|
||||
|
||||
|
||||
@@ -3727,17 +3842,37 @@ class AIWorkspaceInsightGraphs(LoginRequiredMixin, View):
|
||||
return HttpResponseBadRequest("Invalid type specified")
|
||||
|
||||
person = get_object_or_404(Person, pk=person_id, user=request.user)
|
||||
conversation = _conversation_for_person(request.user, person)
|
||||
graph_density = _sanitize_graph_density(request.GET.get("density"))
|
||||
graph_cards = _all_graph_payload(conversation, density=graph_density)
|
||||
range_key = sanitize_graph_range(request.GET.get("range"))
|
||||
payload = build_behavioral_graph_payload(
|
||||
user=request.user,
|
||||
person=person,
|
||||
range_key=range_key,
|
||||
density=graph_density,
|
||||
)
|
||||
context = {
|
||||
"person": person,
|
||||
"workspace_conversation": conversation,
|
||||
"graph_cards": graph_cards,
|
||||
"behavioral_groups": BEHAVIORAL_GROUPS,
|
||||
"graph_cards": payload["graphs"],
|
||||
"summary_cards": payload["summary_cards"],
|
||||
"coverage": payload["coverage"],
|
||||
"range_key": range_key,
|
||||
"graph_density": graph_density,
|
||||
"graph_density_caps": DENSITY_POINT_CAPS,
|
||||
"behavioral_graph_groups": _behavioral_metric_launch_groups(person),
|
||||
"behavioral_show_widget_actions": type == "widget",
|
||||
"behavioral_range_urls": _behavioral_range_urls(request),
|
||||
**_workspace_nav_urls(person),
|
||||
}
|
||||
if type == "widget":
|
||||
return _render_ai_workspace_widget(
|
||||
request,
|
||||
title=f"{person.name}: Behavioral Graphs",
|
||||
unique=f"ai-behavior-graphs-{person.id}",
|
||||
window_content="partials/ai-workspace-behavioral-graphs.html",
|
||||
widget_icon="fa-solid fa-chart-line",
|
||||
widget_options='gs-w="8" gs-h="11" gs-x="4" gs-y="0" gs-min-w="4"',
|
||||
**context,
|
||||
)
|
||||
return render(request, "pages/ai-workspace-insight-graphs.html", context)
|
||||
|
||||
|
||||
@@ -3749,39 +3884,38 @@ class AIWorkspaceInformation(LoginRequiredMixin, View):
|
||||
return HttpResponseBadRequest("Invalid type specified")
|
||||
|
||||
person = get_object_or_404(Person, pk=person_id, user=request.user)
|
||||
conversation = _conversation_for_person(request.user, person)
|
||||
latest_snapshot = conversation.metric_snapshots.first()
|
||||
directionality = _commitment_directionality_payload(conversation)
|
||||
graph_density = _sanitize_graph_density(request.GET.get("density"))
|
||||
commitment_graph_cards = [
|
||||
card
|
||||
for card in _all_graph_payload(conversation, density=graph_density)
|
||||
if card["group"] == "commitment"
|
||||
]
|
||||
|
||||
graph_refs = []
|
||||
for ref in directionality.get("graph_refs", []):
|
||||
slug = ref.get("slug")
|
||||
if not slug:
|
||||
continue
|
||||
graph_refs.append(
|
||||
{
|
||||
**ref,
|
||||
"slug": slug,
|
||||
"value": _format_metric_value(conversation, slug, latest_snapshot),
|
||||
}
|
||||
)
|
||||
directionality["graph_refs"] = graph_refs
|
||||
range_key = sanitize_graph_range(request.GET.get("range"))
|
||||
payload = build_behavioral_graph_payload(
|
||||
user=request.user,
|
||||
person=person,
|
||||
range_key=range_key,
|
||||
density=graph_density,
|
||||
)
|
||||
|
||||
context = {
|
||||
"person": person,
|
||||
"workspace_conversation": conversation,
|
||||
"directionality": directionality,
|
||||
"overview_rows": _information_overview_rows(conversation),
|
||||
"commitment_graph_cards": commitment_graph_cards,
|
||||
"behavioral_groups": BEHAVIORAL_GROUPS,
|
||||
"summary_cards": payload["summary_cards"],
|
||||
"graph_cards": payload["graphs"],
|
||||
"coverage": payload["coverage"],
|
||||
"range_key": range_key,
|
||||
"graph_density": graph_density,
|
||||
"behavioral_graph_groups": _behavioral_metric_launch_groups(person),
|
||||
"behavioral_show_widget_actions": type == "widget",
|
||||
"behavioral_range_urls": _behavioral_range_urls(request),
|
||||
**_workspace_nav_urls(person),
|
||||
}
|
||||
if type == "widget":
|
||||
return _render_ai_workspace_widget(
|
||||
request,
|
||||
title=f"{person.name}: MS/PS Information",
|
||||
unique=f"ai-behavior-info-{person.id}",
|
||||
window_content="partials/ai-workspace-behavioral-information.html",
|
||||
widget_icon="fa-solid fa-circle-info",
|
||||
widget_options='gs-w="6" gs-h="10" gs-x="6" gs-y="0" gs-min-w="4"',
|
||||
**context,
|
||||
)
|
||||
return render(request, "pages/ai-workspace-information.html", context)
|
||||
|
||||
|
||||
@@ -3793,33 +3927,37 @@ class AIWorkspaceInsightHelp(LoginRequiredMixin, View):
|
||||
return HttpResponseBadRequest("Invalid type specified")
|
||||
|
||||
person = get_object_or_404(Person, pk=person_id, user=request.user)
|
||||
conversation = _conversation_for_person(request.user, person)
|
||||
latest_snapshot = conversation.metric_snapshots.first()
|
||||
metrics = []
|
||||
for slug, spec in INSIGHT_METRICS.items():
|
||||
metrics.append(
|
||||
{
|
||||
"slug": slug,
|
||||
"title": spec["title"],
|
||||
"group": spec["group"],
|
||||
"group_title": INSIGHT_GROUPS[spec["group"]]["title"],
|
||||
"calculation": spec["calculation"],
|
||||
"psychology": spec["psychology"],
|
||||
"value": _format_metric_value(
|
||||
conversation,
|
||||
slug,
|
||||
latest_snapshot,
|
||||
),
|
||||
}
|
||||
)
|
||||
graph_density = _sanitize_graph_density(request.GET.get("density"))
|
||||
range_key = sanitize_graph_range(request.GET.get("range"))
|
||||
payload = build_behavioral_graph_payload(
|
||||
user=request.user,
|
||||
person=person,
|
||||
range_key=range_key,
|
||||
density=graph_density,
|
||||
)
|
||||
|
||||
context = {
|
||||
"person": person,
|
||||
"workspace_conversation": conversation,
|
||||
"groups": INSIGHT_GROUPS,
|
||||
"metrics": metrics,
|
||||
"groups": BEHAVIORAL_GROUPS,
|
||||
"metrics": payload["graphs"],
|
||||
"summary_cards": payload["summary_cards"],
|
||||
"coverage": payload["coverage"],
|
||||
"range_key": range_key,
|
||||
"behavioral_graph_groups": _behavioral_metric_launch_groups(person),
|
||||
"behavioral_show_widget_actions": type == "widget",
|
||||
"behavioral_range_urls": _behavioral_range_urls(request),
|
||||
**_workspace_nav_urls(person),
|
||||
}
|
||||
if type == "widget":
|
||||
return _render_ai_workspace_widget(
|
||||
request,
|
||||
title=f"{person.name}: MS/PS Help",
|
||||
unique=f"ai-behavior-help-{person.id}",
|
||||
window_content="partials/ai-workspace-behavioral-help.html",
|
||||
widget_icon="fa-solid fa-circle-question",
|
||||
widget_options='gs-w="6" gs-h="10" gs-x="6" gs-y="0" gs-min-w="4"',
|
||||
**context,
|
||||
)
|
||||
return render(request, "pages/ai-workspace-insight-help.html", context)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user