Skip to content

Fix graph visualizations and incident and activity search#176

Merged
tarekio merged 8 commits intomainfrom
fix-viz
Sep 6, 2025
Merged

Fix graph visualizations and incident and activity search#176
tarekio merged 8 commits intomainfrom
fix-viz

Conversation

@tarekio
Copy link
Contributor

@tarekio tarekio commented Sep 4, 2025

Jira Issue

  1. https://syriajustice.atlassian.net/browse/BYNT-1449

Description

[Brief description of changes]

Checklist

  • Tests added/updated
  • Documentation updated (if needed)
  • New strings prepared for translations

API Changes (if applicable)

  • Permissions checked
  • Endpoint tests added

Additional Notes

[Any other relevant information]

Summary by CodeRabbit

  • New Features

    • Re-enabled “Visualize Results” tooltips on Actors, Bulletins, and Incidents pages for clearer guidance.
    • Graph visualizations now display titles for Location nodes, improving readability.
  • Bug Fixes

    • Improved reliability and consistency of incident search and visualization results.
    • Minor UI polish around visualization actions.

@coderabbitai
Copy link

coderabbitai bot commented Sep 4, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Uncomments tooltips around “Visualize Results” in three admin templates. Adjusts incidents search payload from array to object and updates validation model accordingly. Simplifies incident query assembly to a single-pass conditions build. Switches task query execution to return raw Result and adapts graph merge iteration. Adds explicit location node titling.

Changes

Cohort / File(s) Summary of changes
Admin UI tooltips
enferno/admin/templates/admin/actors.html, enferno/admin/templates/admin/bulletins.html, enferno/admin/templates/admin/incidents.html
Re-enable v-tooltip wrappers around Visualize buttons by uncommenting blocks; no logic changes to buttons themselves.
Incidents request shape + tests
enferno/admin/validation/models.py, tests/admin/test_incidents.py
Change IncidentQueryRequestModel.q from list to single object; update test POST body from q: [{}] to q: {}.
Incident search assembly
enferno/utils/search_utils.py
Replace multi-fragment, op-combining incident query build with a single call returning conditions applied via and_(*conditions).
Task query execution + graph merge
enferno/tasks/__init__.py
get_result_set now executes and returns raw DB Result; merge_graphs iterates via result_set.scalars().unique().all() to process ORM items.
Graph node titling for locations
enferno/utils/graph_utils.py
In create_node, set Location node title from instance.title when available, bypassing prior restricted-title gating.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Admin as Admin UI
  participant API as Admin API (/admin/api/incidents)
  participant Val as Validation (IncidentQueryRequestModel)
  participant Search as search_utils.get_query
  participant DB as db.session

  Admin->>API: POST { q: {…}, per_page, include_count }
  API->>Val: Parse body (q as object)
  Val-->>API: Validated request
  API->>Search: Build incident query (single-pass conditions)
  Search-->>API: SQL + conditions
  API->>DB: execute(query)
  DB-->>API: Result (raw)
  API-->>Admin: JSON response
Loading
sequenceDiagram
  autonumber
  actor Admin as Admin UI (Visualize)
  participant Tasks as tasks.get_result_set
  participant DB as db.session
  participant Merge as tasks.merge_graphs
  participant Graph as graph_utils.get_graph_json

  Admin->>Tasks: Request result set
  Tasks->>DB: execute(search_util.get_query())
  DB-->>Tasks: Result
  Tasks-->>Admin: Result (raw)
  Admin->>Merge: Merge graphs with Result
  Merge->>Merge: iterate result.scalars().unique().all()
  Merge->>Graph: get_graph_json(entity_type, item.id)
  Graph-->>Merge: Graph JSON
  Merge-->>Admin: Merged graph
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I twitch my whiskers at tooltips’ light,
Queries hop from lists to single-byte.
Results now raw, we sift and weave,
Locations named—no more naive.
Conditions calm, one pass, all right—
Graphs converge in moonlit night.
Thump-thump: ship it, swift and tight!

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-viz

Comment @coderabbitai help to get the list of available commands and usage tips.

@tarekio tarekio requested a review from level09 September 4, 2025 22:39
@tarekio tarekio self-assigned this Sep 4, 2025
@tarekio tarekio mentioned this pull request Sep 4, 2025
5 tasks
@tarekio
Copy link
Contributor Author

tarekio commented Sep 4, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Sep 4, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
enferno/admin/templates/admin/incidents.html (1)

15-33: Fix invalid Vue binding in tooltip activator (:="props").

:= is not valid. Use v-bind="props" to spread activator bindings; otherwise the template will fail to compile now that the tooltip is enabled. Also disable the button while generating to prevent duplicate jobs.

Apply:

-            <v-tooltip text="{{ _('Visualize Results') }}">
+            <v-tooltip text="{{ _('Visualize Results') }}">
               <template #activator="{ props }">
                 <v-btn
                     elevation="16"
                     color="primary"
-                    :="props"
+                    v-bind="props"
                     @click.stop="visualizeResults"
                     v-if="showVisualize"
                     :loading="visualizeLoading"
+                    :disabled="visualizeLoading"
                     class="mb-5 mr-5 position-absolute "
                     style="right: 30px; bottom: 90px; z-index: 1000;"
                     icon="mdi-graph"
                 >
                 </v-btn>
               </template>
             </v-tooltip>

Note: The same :="props" pattern appears elsewhere in this file; consider correcting globally for consistency.

🧹 Nitpick comments (3)
enferno/utils/graph_utils.py (1)

62-64: Location titles now always visible — confirm authorization intent; simplify type checks

Exposing Location titles regardless of access might be intentional; please confirm it matches the data access policy. Also, prefer class checks over tablename strings.

-        if instance and model.__tablename__ in ["actor", "incident", "bulletin"]:
+        if instance and model in (Actor, Incident, Bulletin):
             if self.user and self.user.can_access(instance):
                 title = instance.title
-        elif instance and model.__tablename__ == "location":
+        elif instance and model is Location:
             title = instance.title
enferno/utils/search_utils.py (1)

119-124: Incident path now consumes a dict — keep deterministic ordering

This simplifies inputs, but you dropped the explicit ordering used inside incident_query’s stmt. Add a default order to keep stable pagination/UX.

-            result = select(Incident)
+            result = select(Incident).order_by(Incident.id.desc())
             if conditions:
                 result = result.where(and_(*conditions))
             return result

Alternatively, just return the stmt from incident_query to reuse its CTE + ordering:

-            _, conditions = self.incident_query(self.search)
-            result = select(Incident).order_by(Incident.id.desc())
-            if conditions:
-                result = result.where(and_(*conditions))
-            return result
+            stmt, _ = self.incident_query(self.search)
+            return stmt
enferno/tasks/__init__.py (1)

1432-1435: Avoid materializing all rows; iterate lazily and harden ID extraction.

.all() forces full materialization and can blow up memory for big graphs. Iterate the ScalarResult directly. Also, the docstring mentions user_id (not in signature) — update it.

Apply:

-    for item in result_set.scalars().unique().all():
+    for item in result_set.scalars().unique():
         current_graph = graph_utils.get_graph_json(entity_type, item.id)
         graph = current_graph if graph is None else graph_utils.merge_graphs(graph, current_graph)

Follow-up (optional): Add a practical upper bound (e.g., 500 items) or truncate with a warning to prevent generating unusably dense graphs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to data retention organization setting

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 524c3e6 and 140cc3a.

📒 Files selected for processing (8)
  • enferno/admin/templates/admin/actors.html (2 hunks)
  • enferno/admin/templates/admin/bulletins.html (2 hunks)
  • enferno/admin/templates/admin/incidents.html (3 hunks)
  • enferno/admin/validation/models.py (1 hunks)
  • enferno/tasks/__init__.py (2 hunks)
  • enferno/utils/graph_utils.py (1 hunks)
  • enferno/utils/search_utils.py (1 hunks)
  • tests/admin/test_incidents.py (1 hunks)
🔇 Additional comments (5)
enferno/admin/templates/admin/actors.html (1)

16-33: Tooltip re-enabled: LGTM

The tooltip wrapper and activator props look correct; no issues spotted.

enferno/admin/templates/admin/bulletins.html (1)

21-37: Tooltip re-enabled: LGTM

Matches Vuetify’s tooltip pattern; no concerns.

enferno/admin/validation/models.py (1)

1505-1505: No action needed — FE callers already send q as an object.

Verified in enferno/admin/templates/admin/incidents.html that requestData.q is set to this.search (an object) before posting to /admin/api/incidents/; no back-compat shim required.

tests/admin/test_incidents.py (1)

75-81: LGTM: payload updated to single-object query.

q: {} matches the new validation model (IncidentQueryRequestModel.q: IncidentQueryModel). Test remains coherent.

enferno/admin/templates/admin/incidents.html (1)

958-964: LGTM: incidents search request now sends q as an object.

Aligns the UI with backend/test changes (q: this.search).

Copy link
Collaborator

@level09 level09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR misses an important refactoring of api_incidents in admin/views

Now that we are passing only a dict (q) we have to adjust the api endpoint,

  • Logging check: if q and q != [{}]: is wrong now

  • Default: q = validated_data.get("q", [{}]) sets list default (wrong).

  • Simple listing detection: q == [{}] or not any(bool(filter_dict) for filter_dict in q ...)

iterates dict keys and mis-detects

it is wrong code that works by accident 😁

@tarekio tarekio changed the title Fix graph visualizations and incident search Fix graph visualizations and incident and activity search Sep 6, 2025
@tarekio tarekio mentioned this pull request Sep 6, 2025
5 tasks
@tarekio
Copy link
Contributor Author

tarekio commented Sep 6, 2025

This PR misses an important refactoring of api_incidents in admin/views

Now that we are passing only a dict (q) we have to adjust the api endpoint,

  • Logging check: if q and q != [{}]: is wrong now
  • Default: q = validated_data.get("q", [{}]) sets list default (wrong).
  • Simple listing detection: q == [{}] or not any(bool(filter_dict) for filter_dict in q ...)

iterates dict keys and mis-detects

it is wrong code that works by accident 😁

You're right here. The whole search q thing is inconsistent. I've cleanup this code and unified the way we send queries to SearchUtilts.

@tarekio tarekio merged commit bf1c433 into main Sep 6, 2025
8 checks passed
@tarekio tarekio deleted the fix-viz branch September 6, 2025 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants