Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d090333
feature(street): add image provider in schema
ofr1tz Nov 11, 2025
db6bd92
feat(street): add panoramax as image provider
ofr1tz Nov 13, 2025
a66f006
feat(street): update firebase submodule pointer
ofr1tz Nov 18, 2025
8504d29
feat(street): update schema for image provider selection
ofr1tz Feb 11, 2026
48ab199
feat(street): extend firebase_id char length to 36 to accommodate pan…
ofr1tz Feb 17, 2026
ce75f12
feat(street): use safe image provider api url
ofr1tz Feb 17, 2026
8c961dd
feat(street): update submodule pointer
ofr1tz Feb 17, 2026
7175ac1
feat(street): update submodule pointer
ofr1tz Feb 17, 2026
db831b6
feat(street): add image provider to firebase project type specifics
ofr1tz Feb 17, 2026
7207042
feat(street): add image provider to firebase tutorial
ofr1tz Feb 18, 2026
8a776df
feat(street): Correctly convert Mapillary and Panoramax timestamps, e…
ofr1tz Feb 19, 2026
228bf9d
fix(street): format and typechecks
ofr1tz Feb 24, 2026
38c2276
feat(street): submodule pointer for updated test assets
ofr1tz Feb 24, 2026
583097f
feat(street): fix tests
ofr1tz Feb 24, 2026
c447378
feat(street): improve cov
ofr1tz Feb 24, 2026
0250e18
feat(streets): format
ofr1tz Feb 24, 2026
6718dbb
feat(street): fix test
ofr1tz Feb 24, 2026
a9fac16
feat(street): update submodule pointers
ofr1tz Feb 24, 2026
9474466
feat(street): add panoramax custom to enum, use pano_only instead is_…
ofr1tz Mar 19, 2026
24eed04
fix(street): formatting
ofr1tz Mar 19, 2026
a3ce485
fix(street): ruff errors
ofr1tz Mar 19, 2026
1e67c2b
fix(street): adjust tests
ofr1tz Mar 19, 2026
0951e4d
feat(street): remove url from provider if Mapillary
ofr1tz Mar 19, 2026
b2b8177
Update utils/spatial_sampling.py
ofr1tz Mar 23, 2026
ea49f01
refactor(street): move tile level logic to StreetImageProvider and ma…
ofr1tz Mar 23, 2026
2fde083
feat(street): update graphql schema
ofr1tz Mar 23, 2026
9c432d1
test(street): update test assets to reflect changes (default image pr…
ofr1tz Mar 23, 2026
ef2d2e9
fix(street): Use assert_never, cast firebase task id to int when prov…
ofr1tz Apr 2, 2026
39561ac
fix(street): update firebase submodule pointer
ofr1tz Apr 2, 2026
4e771c7
fix(street): update e2e test, firebase submodule pointer
ofr1tz Apr 2, 2026
78d5b8e
feat(street): remove Mapillary from names
ofr1tz Apr 28, 2026
7071a59
feat(street): mandatory image provider
ofr1tz Apr 28, 2026
3bdd7c0
fix(street): format image provider backfill migration, adapt tests to…
ofr1tz Apr 28, 2026
2196cc0
feat(street): migrate isPano to panoOnly
ofr1tz Apr 28, 2026
5a5553c
fix(street): always call Mapillary API at MAPILLARY_API_LINK, fix whi…
ofr1tz Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/common/migrations/0003_alter_announcement_firebase_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.2.5 on 2026-02-17 12:03

import ulid
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('common', '0002_initial'),
]

operations = [
migrations.AlterField(
model_name='announcement',
name='firebase_id',
field=models.CharField(default=ulid.ULID, max_length=36, unique=True),
),
]
3 changes: 2 additions & 1 deletion apps/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ class FirebasePushResource(Model):
# NOTE: We should not directly use old_id. This is ID reference to old system
old_id = models.CharField[str | None, str | None](max_length=30, db_index=True, null=True, blank=True)

firebase_id = models.CharField[str, str](max_length=30, unique=True, default=ULID)
# NOTE: Panoramax uses UUIDv4 (length 36) for identifiers, which we use as firebase_id for street project tasks
firebase_id = models.CharField[str, str](max_length=36, unique=True, default=ULID)

firebase_push_status: int | None = IntegerChoicesField( # type: ignore[reportAssignmentType]
choices_enum=FirebasePushStatusEnum,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.2.5 on 2026-02-17 12:03

import ulid
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('contributor', '0002_initial'),
]

operations = [
migrations.AlterField(
model_name='contributorteam',
name='firebase_id',
field=models.CharField(default=ulid.ULID, max_length=36, unique=True),
),
migrations.AlterField(
model_name='contributorusergroup',
name='firebase_id',
field=models.CharField(default=ulid.ULID, max_length=36, unique=True),
),
]
9 changes: 7 additions & 2 deletions apps/project/graphql/inputs/project_types/street.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import strawberry

from project_types.street import project as street_project
from utils.geo.street_image_provider.models import StreetImageProvider


@strawberry.experimental.pydantic.input(model=street_project.StreetMapillaryImageFilters, all_fields=True)
class StreetMapillaryImageFiltersInput: ...
@strawberry.experimental.pydantic.input(model=street_project.StreetImageFilters, all_fields=True)
class StreetImageFiltersInput: ...


@strawberry.experimental.pydantic.input(model=StreetImageProvider, all_fields=True)
class StreetImageProviderInput: ...


@strawberry.experimental.pydantic.input(model=street_project.StreetProjectProperty, all_fields=True)
Expand Down
9 changes: 7 additions & 2 deletions apps/project/graphql/types/project_types/street.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import strawberry

from project_types.street import project as street_project
from utils.geo.street_image_provider.models import StreetImageProvider as StreetImageProviderModel


@strawberry.experimental.pydantic.type(model=street_project.StreetMapillaryImageFilters, all_fields=True)
class StreetMapillaryImageFilters: ...
@strawberry.experimental.pydantic.type(model=street_project.StreetImageFilters, all_fields=True)
class StreetImageFilters: ...


@strawberry.experimental.pydantic.type(model=StreetImageProviderModel, all_fields=True)
class StreetImageProvider: ...


@strawberry.experimental.pydantic.type(model=street_project.StreetProjectProperty, all_fields=True)
Expand Down
18 changes: 18 additions & 0 deletions apps/project/migrations/0011_alter_projecttask_firebase_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.5 on 2025-11-12 15:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('project', '0010_alter_projecttask_unique_together'),
]

operations = [
migrations.AlterField(
model_name='projecttask',
name='firebase_id',
field=models.CharField(max_length=36),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.2.5 on 2026-02-17 12:03

import ulid
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('project', '0011_alter_projecttask_firebase_id'),
]

operations = [
migrations.AlterField(
model_name='organization',
name='firebase_id',
field=models.CharField(default=ulid.ULID, max_length=36, unique=True),
),
migrations.AlterField(
model_name='project',
name='firebase_id',
field=models.CharField(default=ulid.ULID, max_length=36, unique=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 5.2.5 on 2026-04-28 14:28

from django.db import migrations

def backfill_image_provider(apps, schema_editor):
Project = apps.get_model('project', 'Project')

for project in Project._default_manager.all():
if project.project_type != 7: # STREET type
continue

if project.project_type_specifics is None:
project.project_type_specifics = {}

if 'imageProvider' not in project.project_type_specifics:
project.project_type_specifics['imageProvider'] = {
'name': 'MAPILLARY'
}
project.save(update_fields=['project_type_specifics'])

def reverse_backfill(apps, schema_editor):
pass

class Migration(migrations.Migration):

dependencies = [
('project', '0012_alter_organization_firebase_id_and_more'),
]

operations = [
migrations.RunPython(backfill_image_provider, reverse_backfill),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Generated by Django 5.2.5 on 2026-04-28 14:59

import logging

from django.db import migrations

logger = logging.getLogger(__name__)


def migrate_is_pano_to_pano_only(apps, schema_editor):
"""
Migrate isPano field to panoOnly in mapillaryImageFilters
for all STREET-type projects to conform with updated schema.
"""
Project = apps.get_model('project', 'Project')

migrated_count = 0

for project in Project._default_manager.all():
if project.project_type != 7: # STREET type
continue

if project.project_type_specifics is None:
continue

if 'mapillaryImageFilters' in project.project_type_specifics:
filters = project.project_type_specifics['mapillaryImageFilters']
if isinstance(filters, dict) and 'isPano' in filters:
logger.info(
f"Migrating isPano to panoOnly for project {project.id} ({project.name})"
)
filters['panoOnly'] = filters.pop('isPano')
project.save(update_fields=['project_type_specifics'])
migrated_count += 1

logger.info(f"Field migration completed: {migrated_count} projects migrated isPano to panoOnly")


def reverse_pano_migration(apps, schema_editor):
"""
Revert panoOnly back to isPano for compatibility.
"""
Project = apps.get_model('project', 'Project')

reverted_count = 0

for project in Project._default_manager.all():
if project.project_type != 7: # STREET type
continue

if project.project_type_specifics is None:
continue

if 'mapillaryImageFilters' in project.project_type_specifics:
filters = project.project_type_specifics['mapillaryImageFilters']
if isinstance(filters, dict) and 'panoOnly' in filters:
logger.info(
f"Reverting panoOnly to isPano for project {project.id} ({project.name})"
)
filters['isPano'] = filters.pop('panoOnly')
project.save(update_fields=['project_type_specifics'])
reverted_count += 1

logger.info(f"Reverse migration completed: {reverted_count} projects reverted panoOnly to isPano")


class Migration(migrations.Migration):

dependencies = [
('project', '0013_backfill_image_provider_for_street_projects'),
]

operations = [
migrations.RunPython(migrate_is_pano_to_pano_only, reverse_pano_migration),
]
2 changes: 1 addition & 1 deletion apps/project/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ class ProjectTask(FirebasePushResource):
in the context of a larger mapping or data collection project.
"""

firebase_id = models.CharField[str, str](max_length=30)
firebase_id = models.CharField[str, str](max_length=36)

task_group: ProjectTaskGroup = models.ForeignKey[ProjectTaskGroup, ProjectTaskGroup]( # type: ignore[reportAssignmentType]
ProjectTaskGroup,
Expand Down
6 changes: 6 additions & 0 deletions apps/project/tests/e2e_create_street_project_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,12 @@ def _test_project(self, filename: str):
sanitized_tasks_actual = project_tasks_fb_data
sanitized_tasks_expected = test_data["expected_project_tasks_data"]

for tasks in sanitized_tasks_expected.values():
for task in tasks:
task_id = task.get("taskId")
if isinstance(task_id, str) and task_id.isdigit():
task["taskId"] = int(task_id)

assert sanitized_tasks_actual == sanitized_tasks_expected, (
"Differences found between expected and actual tasks on project in firebase."
)
Expand Down
5 changes: 4 additions & 1 deletion apps/project/tests/mutation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1652,14 +1652,17 @@ def test_project_street(self, mock_requests): # type: ignore[reportMissingParam
],
},
"mapillaryImageFilters": {
"isPano": True,
"panoOnly": True,
"creatorId": None,
"organizationId": None,
"startTime": None,
"endTime": None,
"randomizeOrder": False,
"samplingThreshold": None,
},
"imageProvider": {
"name": "MAPILLARY",
},
},
},
}
Expand Down
1 change: 1 addition & 0 deletions main/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Config:
# NOTE: We get mapillary data from mapillary
MAPILLARY_API_LINK = "https://tiles.mapillary.com/maps/vtp/mly1_computed_public/2/"
MAPILLARY_API_KEY = typing.cast("str", settings.MAPILLARY_API_KEY)
PANORAMAX_API_LINK = "https://api.panoramax.xyz/"

FIREBASE_HELPER = typing.cast("FirebaseHelper", settings.FIREBASE_HELPER)
FIREBASE_EMULATOR_USE = typing.cast("str | None", settings.FIREBASE_EMULATOR_USE)
Expand Down
2 changes: 2 additions & 0 deletions main/graphql/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
from project_types.validate.project import ValidateObjectSourceTypeEnum
from project_types.validate_image.project import ValidateImageSourceTypeEnum
from utils.geo.raster_tile_server.config import RasterTileServerNameEnum
from utils.geo.street_image_provider.models import StreetImageProviderNameEnum
from utils.geo.vector_tile_server.config import VectorTileServerNameEnum

ENUM_TO_STRAWBERRY_ENUMS: list[type] = [
RasterTileServerNameEnum,
StreetImageProviderNameEnum,
VectorTileServerNameEnum,
ValidateObjectSourceTypeEnum,
ValidateImageSourceTypeEnum,
Expand Down
Loading
Loading