Skip to content

Commit 2898db8

Browse files
matt-bernsteinjomboothricardoantoniocmrobot-ci-heartex
authored
feat: ROOT-43: Prediction results filter (#8112)
Co-authored-by: matt-bernstein <matt-bernstein@users.noreply.github.com> Co-authored-by: Jo Booth <jo.m.booth@gmail.com> Co-authored-by: Ricardo Cabral <ricardo.cabralm@gmail.com> Co-authored-by: robot-ci-heartex <robot-ci-heartex@users.noreply.github.com>
1 parent 40afcf3 commit 2898db8

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Generated by Django 5.1.10 on 2025-08-07 16:14
2+
3+
import logging
4+
5+
from django.conf import settings
6+
from django.db import migrations
7+
8+
from core.redis import start_job_async_or_sync
9+
from core.models import AsyncMigrationStatus
10+
from core.utils.common import btree_gin_migration_operations
11+
12+
logger = logging.getLogger(__name__)
13+
14+
IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE
15+
16+
migration_name = "0056_prediction_result_proj_gin_idx_async"
17+
18+
SQL_CREATE_INDEX = (
19+
"CREATE INDEX CONCURRENTLY IF NOT EXISTS tasks_predictions_result_proj_gin "
20+
"ON prediction USING GIN (project_id, CAST(result AS text) gin_trgm_ops);"
21+
)
22+
23+
SQL_DROP_INDEX = "DROP INDEX CONCURRENTLY IF EXISTS tasks_predictions_result_proj_gin;"
24+
25+
def _forward(migration_name: str):
26+
"""Create the GIN index inside a dedicated job."""
27+
# If the migration has already been executed, do nothing
28+
migration, created = AsyncMigrationStatus.objects.get_or_create(
29+
name=migration_name,
30+
defaults={"status": AsyncMigrationStatus.STATUS_STARTED},
31+
)
32+
if not created:
33+
logger.info("Migration %s already executed", migration_name)
34+
return
35+
36+
logger.info("Starting async migration %s", migration_name)
37+
from django.db import connection
38+
39+
with connection.cursor() as cursor:
40+
cursor.execute(SQL_CREATE_INDEX)
41+
migration.status = AsyncMigrationStatus.STATUS_FINISHED
42+
migration.save()
43+
logger.info("Async migration %s complete", migration_name)
44+
45+
46+
def _backward(migration_name: str):
47+
"""Revert the GIN index creation."""
48+
migration = AsyncMigrationStatus.objects.create(
49+
name=migration_name,
50+
status=AsyncMigrationStatus.STATUS_STARTED,
51+
)
52+
logger.info("Reverting async migration %s", migration_name)
53+
from django.db import connection
54+
55+
with connection.cursor() as cursor:
56+
cursor.execute(SQL_DROP_INDEX)
57+
migration.status = AsyncMigrationStatus.STATUS_FINISHED
58+
migration.save()
59+
logger.info("Revert of async migration %s complete", migration_name)
60+
61+
62+
def forwards(apps, schema_editor):
63+
if IS_SQLITE:
64+
logger.info("SQLite detected; skipping GIN index creation")
65+
return
66+
67+
# Only run on PostgreSQL
68+
if not schema_editor.connection.vendor.startswith("postgres"):
69+
logger.info("Database vendor: %s. Skipping index creation", schema_editor.connection.vendor)
70+
return
71+
72+
start_job_async_or_sync(_forward, migration_name=migration_name)
73+
74+
75+
def backwards(apps, schema_editor):
76+
if IS_SQLITE:
77+
logger.info("SQLite detected; skipping GIN index drop")
78+
return
79+
80+
if not schema_editor.connection.vendor.startswith("postgres"):
81+
logger.info("Database vendor: %s. Skipping index drop", schema_editor.connection.vendor)
82+
return
83+
84+
start_job_async_or_sync(_backward, migration_name=migration_name)
85+
86+
87+
88+
class Migration(migrations.Migration):
89+
atomic = False
90+
91+
dependencies = [
92+
("tasks", "0055_task_proj_octlen_idx_async"),
93+
]
94+
95+
operations = btree_gin_migration_operations(migrations.RunPython(forwards, backwards))

web/libs/datamanager/src/stores/Tabs/tab_column.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,8 @@ export const TabColumn = types
200200

201201
get isAnnotationResultsFilterColumn() {
202202
// these columns are not visible in the column selector, but are used for filtering
203-
return self.id.includes("annotations_results_json.") || self.id.endsWith(":annotations_results_json");
203+
const hidden_column_ids = ["annotations_results_json", "predictions_results_json"];
204+
return hidden_column_ids.some((id) => self.id.includes(`${id}.`) || self.id.endsWith(`:${id}`));
204205
},
205206
}))
206207
.actions((self) => ({

0 commit comments

Comments
 (0)