Skip to content

Commit 76cb50c

Browse files
committed
feat(frontend,backed): adds review maps shows all images; review map show rejection_reason
1 parent 2cfbfa0 commit 76cb50c

File tree

8 files changed

+536
-264
lines changed

8 files changed

+536
-264
lines changed

src/backend/app/images/image_classification.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ async def get_batch_map_data(
10271027
) -> dict:
10281028
"""Get map data for batch review visualization.
10291029
1030-
Returns task geometries and image point locations as GeoJSON.
1030+
Returns task geometries and all images as GeoJSON (with/without GPS coordinates).
10311031
"""
10321032
# Get all task IDs that have images in this batch
10331033
task_ids_query = """
@@ -1075,47 +1075,68 @@ async def get_batch_map_data(
10751075
}
10761076
)
10771077

1078-
# Get image locations as GeoJSON points
1079-
images_query = """
1078+
# Get all images with or without GPS data
1079+
all_images_query = """
10801080
SELECT
10811081
id,
10821082
filename,
10831083
status,
1084+
rejection_reason,
10841085
task_id,
10851086
ST_X(location::geometry) as longitude,
10861087
ST_Y(location::geometry) as latitude
10871088
FROM project_images
10881089
WHERE batch_id = %(batch_id)s
10891090
AND project_id = %(project_id)s
1090-
AND location IS NOT NULL
1091+
ORDER BY uploaded_at DESC
10911092
"""
10921093

10931094
async with db.cursor(row_factory=dict_row) as cur:
10941095
await cur.execute(
1095-
images_query,
1096+
all_images_query,
10961097
{"batch_id": str(batch_id), "project_id": str(project_id)},
10971098
)
1098-
images = await cur.fetchall()
1099+
all_images = await cur.fetchall()
1100+
1101+
# Build GeoJSON features for each image
1102+
images_features = []
1103+
located_count = 0
1104+
unlocated_count = 0
1105+
1106+
for img in all_images:
1107+
properties = {
1108+
"id": str(img["id"]),
1109+
"filename": img["filename"],
1110+
"status": img["status"],
1111+
"task_id": str(img["task_id"]) if img["task_id"] else None,
1112+
"rejection_reason": img["rejection_reason"],
1113+
}
10991114

1100-
images_geojson = {
1101-
"type": "FeatureCollection",
1102-
"features": [
1103-
{
1115+
# Add Point geometry if GPS data exists
1116+
if img["longitude"] is not None and img["latitude"] is not None:
1117+
feature = {
11041118
"type": "Feature",
11051119
"geometry": {
11061120
"type": "Point",
11071121
"coordinates": [img["longitude"], img["latitude"]],
11081122
},
1109-
"properties": {
1110-
"id": str(img["id"]),
1111-
"filename": img["filename"],
1112-
"status": img["status"],
1113-
"task_id": str(img["task_id"]) if img["task_id"] else None,
1114-
},
1123+
"properties": properties,
11151124
}
1116-
for img in images
1117-
if img["longitude"] is not None and img["latitude"] is not None
1118-
],
1125+
located_count += 1
1126+
else:
1127+
# Add feature with null geometry for images without GPS
1128+
feature = {
1129+
"type": "Feature",
1130+
"geometry": None,
1131+
"properties": properties,
1132+
}
1133+
unlocated_count += 1
1134+
1135+
images_features.append(feature)
1136+
1137+
images_geojson = {
1138+
"type": "FeatureCollection",
1139+
"features": images_features,
11191140
}
11201141

11211142
return {
@@ -1124,6 +1145,8 @@ async def get_batch_map_data(
11241145
"images": images_geojson,
11251146
"total_tasks": len(tasks_geojson["features"]),
11261147
"total_images": len(images_geojson["features"]),
1148+
"total_images_with_gps": located_count,
1149+
"total_images_without_gps": unlocated_count,
11271150
}
11281151

11291152
@staticmethod
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""add image classification fields
2+
3+
Revision ID: add_image_classification
4+
Revises: 001_project_images, 7389d0d528c3
5+
Create Date: 2025-01-06
6+
7+
"""
8+
9+
from alembic import op
10+
import sqlalchemy as sa
11+
from sqlalchemy.dialects import postgresql
12+
13+
revision = "add_image_classification"
14+
down_revision = ("001_project_images", "7389d0d528c3")
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# Add batch_id column
21+
try:
22+
op.add_column(
23+
"project_images",
24+
sa.Column("batch_id", postgresql.UUID(as_uuid=True), nullable=True),
25+
)
26+
except Exception:
27+
# Column might already exist
28+
pass
29+
30+
# Add rejection_reason column
31+
try:
32+
op.add_column(
33+
"project_images", sa.Column("rejection_reason", sa.Text(), nullable=True)
34+
)
35+
except Exception:
36+
pass
37+
38+
# Add sharpness_score column
39+
try:
40+
op.add_column(
41+
"project_images", sa.Column("sharpness_score", sa.Float(), nullable=True)
42+
)
43+
except Exception:
44+
pass
45+
46+
# Create indexes
47+
op.execute(
48+
"CREATE INDEX IF NOT EXISTS idx_project_images_batch_id ON project_images (batch_id)"
49+
)
50+
op.execute(
51+
"CREATE INDEX IF NOT EXISTS idx_project_images_batch_status ON project_images (batch_id, status)"
52+
)
53+
54+
55+
def downgrade():
56+
op.drop_index("idx_project_images_batch_status", table_name="project_images")
57+
op.drop_index("idx_project_images_batch_id", table_name="project_images")
58+
op.drop_column("project_images", "sharpness_score")
59+
op.drop_column("project_images", "rejection_reason")
60+
op.drop_column("project_images", "batch_id")

src/backend/app/migrations/versions/add_image_classification_fields.py

Lines changed: 0 additions & 196 deletions
This file was deleted.

0 commit comments

Comments
 (0)