Fix issues with multiplatform conversation displays
This commit is contained in:
@@ -1308,7 +1308,7 @@ def _context_base(user, service, identifier, person):
|
||||
).first()
|
||||
or PersonIdentifier.objects.filter(user=user, person=person).first()
|
||||
)
|
||||
if person_identifier is None and identifier:
|
||||
if person_identifier is None and identifier and person is None:
|
||||
person_identifier = PersonIdentifier.objects.filter(
|
||||
user=user,
|
||||
service=service,
|
||||
@@ -1553,10 +1553,34 @@ def _recent_manual_contacts(
|
||||
if not all_rows:
|
||||
return []
|
||||
|
||||
current_service_key = _default_service(current_service)
|
||||
current_identifier_value = str(current_identifier or "").strip()
|
||||
current_person_id = str(current_person.id) if current_person else ""
|
||||
|
||||
row_by_key = {
|
||||
(str(row.get("service") or "").strip().lower(), str(row.get("identifier") or "").strip()): row
|
||||
for row in all_rows
|
||||
}
|
||||
by_person_service = {}
|
||||
person_links = (
|
||||
PersonIdentifier.objects.filter(user=user)
|
||||
.select_related("person")
|
||||
.order_by("person__name", "service", "identifier")
|
||||
)
|
||||
for link in person_links:
|
||||
person_id = str(link.person_id or "")
|
||||
if not person_id:
|
||||
continue
|
||||
service_key = _default_service(link.service)
|
||||
identifier_value = str(link.identifier or "").strip()
|
||||
if not identifier_value:
|
||||
continue
|
||||
by_person_service.setdefault(person_id, {})
|
||||
if service_key not in by_person_service[person_id]:
|
||||
by_person_service[person_id][service_key] = {
|
||||
"identifier": identifier_value,
|
||||
"person_name": str(link.person.name or "").strip() or identifier_value,
|
||||
}
|
||||
ordered_keys = []
|
||||
seen_keys = set()
|
||||
recent_values = (
|
||||
@@ -1582,15 +1606,15 @@ def _recent_manual_contacts(
|
||||
if len(ordered_keys) >= limit:
|
||||
break
|
||||
|
||||
current_key = (_default_service(current_service), str(current_identifier or "").strip())
|
||||
current_key = (current_service_key, current_identifier_value)
|
||||
if current_key[1]:
|
||||
if current_key in ordered_keys:
|
||||
ordered_keys.remove(current_key)
|
||||
ordered_keys.insert(0, current_key)
|
||||
if len(ordered_keys) > limit:
|
||||
ordered_keys = ordered_keys[:limit]
|
||||
|
||||
rows = []
|
||||
seen_people = set()
|
||||
seen_unknown = set()
|
||||
for service_key, identifier_value in ordered_keys:
|
||||
row = dict(row_by_key.get((service_key, identifier_value)) or {})
|
||||
if not row:
|
||||
@@ -1611,13 +1635,86 @@ def _recent_manual_contacts(
|
||||
"linked_person": False,
|
||||
"source": "recent",
|
||||
}
|
||||
row["service_label"] = _service_label(service_key)
|
||||
row["person_id"] = str(row.get("person_id") or "")
|
||||
person_id = row["person_id"]
|
||||
if person_id:
|
||||
if person_id in seen_people:
|
||||
continue
|
||||
seen_people.add(person_id)
|
||||
service_map = dict(by_person_service.get(person_id) or {})
|
||||
if service_key not in service_map and identifier_value:
|
||||
service_map[service_key] = {
|
||||
"identifier": identifier_value,
|
||||
"person_name": str(row.get("person_name") or "").strip()
|
||||
or identifier_value,
|
||||
}
|
||||
|
||||
selected_service = service_key
|
||||
selected_identifier = identifier_value
|
||||
if person_id == current_person_id and current_service_key in service_map:
|
||||
selected_service = current_service_key
|
||||
selected_identifier = str(
|
||||
(service_map.get(current_service_key) or {}).get("identifier") or ""
|
||||
).strip()
|
||||
elif selected_service not in service_map:
|
||||
for fallback_service in ("whatsapp", "signal", "instagram", "xmpp"):
|
||||
if fallback_service in service_map:
|
||||
selected_service = fallback_service
|
||||
selected_identifier = str(
|
||||
(service_map.get(fallback_service) or {}).get("identifier")
|
||||
or ""
|
||||
).strip()
|
||||
break
|
||||
selected_identifier = selected_identifier or identifier_value
|
||||
selected_urls = _compose_urls(
|
||||
selected_service,
|
||||
selected_identifier,
|
||||
person_id,
|
||||
)
|
||||
|
||||
row["service"] = selected_service
|
||||
row["service_label"] = _service_label(selected_service)
|
||||
row["identifier"] = selected_identifier
|
||||
row["compose_url"] = selected_urls["page_url"]
|
||||
row["compose_widget_url"] = selected_urls["widget_url"]
|
||||
row["person_name"] = (
|
||||
str(row.get("linked_person_name") or "").strip()
|
||||
or str(row.get("person_name") or "").strip()
|
||||
or selected_identifier
|
||||
)
|
||||
|
||||
for svc in ("signal", "whatsapp", "instagram", "xmpp"):
|
||||
svc_identifier = str(
|
||||
(service_map.get(svc) or {}).get("identifier") or ""
|
||||
).strip()
|
||||
row[f"{svc}_identifier"] = svc_identifier
|
||||
if svc_identifier:
|
||||
svc_urls = _compose_urls(svc, svc_identifier, person_id)
|
||||
row[f"{svc}_compose_url"] = svc_urls["page_url"]
|
||||
row[f"{svc}_compose_widget_url"] = svc_urls["widget_url"]
|
||||
else:
|
||||
row[f"{svc}_compose_url"] = ""
|
||||
row[f"{svc}_compose_widget_url"] = ""
|
||||
else:
|
||||
unknown_key = (service_key, identifier_value)
|
||||
if unknown_key in seen_unknown:
|
||||
continue
|
||||
seen_unknown.add(unknown_key)
|
||||
row["service_label"] = _service_label(service_key)
|
||||
for svc in ("signal", "whatsapp", "instagram", "xmpp"):
|
||||
row[f"{svc}_identifier"] = identifier_value if svc == service_key else ""
|
||||
row[f"{svc}_compose_url"] = row.get("compose_url") if svc == service_key else ""
|
||||
row[f"{svc}_compose_widget_url"] = (
|
||||
row.get("compose_widget_url") if svc == service_key else ""
|
||||
)
|
||||
|
||||
row["is_active"] = (
|
||||
service_key == _default_service(current_service)
|
||||
and identifier_value == str(current_identifier or "").strip()
|
||||
row.get("service") == current_service_key
|
||||
and str(row.get("identifier") or "").strip() == current_identifier_value
|
||||
)
|
||||
rows.append(row)
|
||||
if len(rows) >= limit:
|
||||
break
|
||||
return rows
|
||||
|
||||
|
||||
@@ -2127,7 +2224,7 @@ class ComposeThread(LoginRequiredMixin, View):
|
||||
session_ids = ComposeHistorySync._session_ids_for_scope(
|
||||
user=request.user,
|
||||
person=base["person"],
|
||||
service=service,
|
||||
service=base["service"],
|
||||
person_identifier=base["person_identifier"],
|
||||
explicit_identifier=base["identifier"],
|
||||
)
|
||||
@@ -2241,15 +2338,14 @@ class ComposeHistorySync(LoginRequiredMixin, View):
|
||||
)
|
||||
variants = cls._identifier_variants(service, explicit_identifier)
|
||||
if variants:
|
||||
identifiers.extend(
|
||||
list(
|
||||
PersonIdentifier.objects.filter(
|
||||
user=user,
|
||||
service=service,
|
||||
identifier__in=variants,
|
||||
)
|
||||
)
|
||||
variant_qs = PersonIdentifier.objects.filter(
|
||||
user=user,
|
||||
service=service,
|
||||
identifier__in=variants,
|
||||
)
|
||||
if person is not None:
|
||||
variant_qs = variant_qs.filter(person=person)
|
||||
identifiers.extend(list(variant_qs))
|
||||
unique_ids = []
|
||||
seen = set()
|
||||
for row in identifiers:
|
||||
|
||||
152
core/views/system.py
Normal file
152
core/views/system.py
Normal file
@@ -0,0 +1,152 @@
|
||||
from django.shortcuts import render
|
||||
from django.views import View
|
||||
|
||||
from core.models import (
|
||||
AIRequest,
|
||||
AIResult,
|
||||
AIResultSignal,
|
||||
Chat,
|
||||
ChatSession,
|
||||
Group,
|
||||
MemoryItem,
|
||||
Message,
|
||||
MessageEvent,
|
||||
PatternArtifactExport,
|
||||
PatternMitigationAutoSettings,
|
||||
PatternMitigationCorrection,
|
||||
PatternMitigationGame,
|
||||
PatternMitigationMessage,
|
||||
PatternMitigationPlan,
|
||||
PatternMitigationRule,
|
||||
Person,
|
||||
PersonIdentifier,
|
||||
Persona,
|
||||
QueuedMessage,
|
||||
WorkspaceConversation,
|
||||
WorkspaceMetricSnapshot,
|
||||
)
|
||||
from core.views.manage.permissions import SuperUserRequiredMixin
|
||||
|
||||
|
||||
class SystemSettings(SuperUserRequiredMixin, View):
|
||||
template_name = "pages/system-settings.html"
|
||||
|
||||
def _counts(self, user):
|
||||
return {
|
||||
"chat_sessions": ChatSession.objects.filter(user=user).count(),
|
||||
"messages": Message.objects.filter(user=user).count(),
|
||||
"queued_messages": QueuedMessage.objects.filter(user=user).count(),
|
||||
"message_events": MessageEvent.objects.filter(user=user).count(),
|
||||
"workspace_conversations": WorkspaceConversation.objects.filter(user=user).count(),
|
||||
"workspace_snapshots": WorkspaceMetricSnapshot.objects.filter(
|
||||
conversation__user=user
|
||||
).count(),
|
||||
"ai_requests": AIRequest.objects.filter(user=user).count(),
|
||||
"ai_results": AIResult.objects.filter(user=user).count(),
|
||||
"ai_result_signals": AIResultSignal.objects.filter(user=user).count(),
|
||||
"memory_items": MemoryItem.objects.filter(user=user).count(),
|
||||
"mitigation_plans": PatternMitigationPlan.objects.filter(user=user).count(),
|
||||
"mitigation_rules": PatternMitigationRule.objects.filter(user=user).count(),
|
||||
"mitigation_games": PatternMitigationGame.objects.filter(user=user).count(),
|
||||
"mitigation_corrections": PatternMitigationCorrection.objects.filter(
|
||||
user=user
|
||||
).count(),
|
||||
"mitigation_messages": PatternMitigationMessage.objects.filter(
|
||||
user=user
|
||||
).count(),
|
||||
"mitigation_auto_settings": PatternMitigationAutoSettings.objects.filter(
|
||||
user=user
|
||||
).count(),
|
||||
"mitigation_exports": PatternArtifactExport.objects.filter(user=user).count(),
|
||||
"osint_people": Person.objects.filter(user=user).count(),
|
||||
"osint_identifiers": PersonIdentifier.objects.filter(user=user).count(),
|
||||
"osint_groups": Group.objects.filter(user=user).count(),
|
||||
"osint_personas": Persona.objects.filter(user=user).count(),
|
||||
}
|
||||
|
||||
def _purge_non_osint(self, user):
|
||||
deleted = 0
|
||||
deleted += PatternArtifactExport.objects.filter(user=user).delete()[0]
|
||||
deleted += PatternMitigationMessage.objects.filter(user=user).delete()[0]
|
||||
deleted += PatternMitigationCorrection.objects.filter(user=user).delete()[0]
|
||||
deleted += PatternMitigationGame.objects.filter(user=user).delete()[0]
|
||||
deleted += PatternMitigationRule.objects.filter(user=user).delete()[0]
|
||||
deleted += PatternMitigationAutoSettings.objects.filter(user=user).delete()[0]
|
||||
deleted += PatternMitigationPlan.objects.filter(user=user).delete()[0]
|
||||
deleted += AIResultSignal.objects.filter(user=user).delete()[0]
|
||||
deleted += AIResult.objects.filter(user=user).delete()[0]
|
||||
deleted += AIRequest.objects.filter(user=user).delete()[0]
|
||||
deleted += MemoryItem.objects.filter(user=user).delete()[0]
|
||||
deleted += WorkspaceMetricSnapshot.objects.filter(conversation__user=user).delete()[0]
|
||||
deleted += MessageEvent.objects.filter(user=user).delete()[0]
|
||||
deleted += Message.objects.filter(user=user).delete()[0]
|
||||
deleted += QueuedMessage.objects.filter(user=user).delete()[0]
|
||||
deleted += WorkspaceConversation.objects.filter(user=user).delete()[0]
|
||||
deleted += ChatSession.objects.filter(user=user).delete()[0]
|
||||
# Chat rows are legacy Signal cache rows and are not user-scoped.
|
||||
deleted += Chat.objects.all().delete()[0]
|
||||
return deleted
|
||||
|
||||
def _purge_osint_people(self, user):
|
||||
return Person.objects.filter(user=user).delete()[0]
|
||||
|
||||
def _purge_osint_identifiers(self, user):
|
||||
return PersonIdentifier.objects.filter(user=user).delete()[0]
|
||||
|
||||
def _purge_osint_groups(self, user):
|
||||
return Group.objects.filter(user=user).delete()[0]
|
||||
|
||||
def _purge_osint_personas(self, user):
|
||||
return Persona.objects.filter(user=user).delete()[0]
|
||||
|
||||
def _handle_action(self, request):
|
||||
action = str(request.POST.get("action") or "").strip().lower()
|
||||
if action == "purge_non_osint":
|
||||
return (
|
||||
"success",
|
||||
f"Purged {self._purge_non_osint(request.user)} non-OSINT row(s).",
|
||||
)
|
||||
if action == "purge_osint_people":
|
||||
return (
|
||||
"warning",
|
||||
f"Purged {self._purge_osint_people(request.user)} OSINT people row(s).",
|
||||
)
|
||||
if action == "purge_osint_identifiers":
|
||||
return (
|
||||
"warning",
|
||||
f"Purged {self._purge_osint_identifiers(request.user)} OSINT identifier row(s).",
|
||||
)
|
||||
if action == "purge_osint_groups":
|
||||
return (
|
||||
"warning",
|
||||
f"Purged {self._purge_osint_groups(request.user)} OSINT group row(s).",
|
||||
)
|
||||
if action == "purge_osint_personas":
|
||||
return (
|
||||
"warning",
|
||||
f"Purged {self._purge_osint_personas(request.user)} OSINT persona row(s).",
|
||||
)
|
||||
return ("danger", "Unknown action.")
|
||||
|
||||
def get(self, request):
|
||||
return render(
|
||||
request,
|
||||
self.template_name,
|
||||
{
|
||||
"counts": self._counts(request.user),
|
||||
"notice_level": "",
|
||||
"notice_message": "",
|
||||
},
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
notice_level, notice_message = self._handle_action(request)
|
||||
return render(
|
||||
request,
|
||||
self.template_name,
|
||||
{
|
||||
"counts": self._counts(request.user),
|
||||
"notice_level": notice_level,
|
||||
"notice_message": notice_message,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user