Skip to content

Feature/panoramax#236

Open
ofr1tz wants to merge 27 commits intodevelopfrom
feature/panoramax
Open

Feature/panoramax#236
ofr1tz wants to merge 27 commits intodevelopfrom
feature/panoramax

Conversation

@ofr1tz
Copy link

@ofr1tz ofr1tz commented Feb 23, 2026

Depends on

Changes

This enables the MapSwipe backend to handle View Streets projects with different image providers (currently Mapillary and Panoramax).

  • Adds image provider to the View Streets project specifics
  • Adds logic to download and process Panoramax metadata
  • Maps Panoramax metadata attribute names on the respective Mapillary attribute names for filtering
  • Changes max length of task ID to accommodate Panoramax image IDs as task IDs

This PR doesn't introduce any:

  • temporary files, auto-generated files or secret keys
  • n+1 queries
  • flake8 issues
  • print
  • typos
  • unwanted comments

This PR contains valid:

  • tests
  • permission checks (tests here too)
  • translations

@codecov
Copy link

codecov bot commented Feb 24, 2026

❌ 35 Tests Failed:

Tests completed Failed Passed Skipped
132 35 97 0
View the top 3 failed test(s) by shortest run time
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_coordinate_download_with_failures
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_coordinate_download_with_failures>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_create_tiles_with_empty_geometry
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_create_tiles_with_empty_geometry>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_create_tiles_with_empty_polygon
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_create_tiles_with_empty_polygon>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_create_tiles_with_multipolygon
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_create_tiles_with_multipolygon>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_create_tiles_with_valid_polygon
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_create_tiles_with_valid_polygon>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_download_and_process_tile_panoramax
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_download_and_process_tile_panoramax>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_download_and_process_tile_returns_none
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_download_and_process_tile_returns_none>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_download_and_process_tile_spatial_filtering
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_download_and_process_tile_spatial_filtering>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_download_and_process_tile_success
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_download_and_process_tile_success>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_creator_id
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_creator_id>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_default
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_default>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_missing_columns
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_missing_columns>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_no_rows_after_filter
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_no_rows_after_filter>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_organization_id
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_organization_id>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_pano_false
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_pano_false>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_pano_true
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_pano_true>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_results_no_creator_id
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_results_no_creator_id>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_time_no_data
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_time_no_data>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_time_range
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_time_range>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_within_time_range
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_within_time_range>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_filter_without_end_time
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_filter_without_end_time>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_geojson_to_polygon_contribution_geojson
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_geojson_to_polygon_contribution_geojson>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_geojson_to_polygon_empty_feature_collection
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_geojson_to_polygon_empty_feature_collection>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_geojson_to_polygon_feature_collection_with_multiple_polygons
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_geojson_to_polygon_feature_collection_with_multiple_polygons>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_geojson_to_polygon_non_polygon_geometry_in_feature_collection
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_geojson_to_polygon_non_polygon_geometry_in_feature_collection>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_geojson_to_polygon_single_feature_multipolygon
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_geojson_to_polygon_single_feature_multipolygon>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_geojson_to_polygon_single_feature_polygon
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_geojson_to_polygon_single_feature_polygon>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_get_image_metadata
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_get_image_metadata>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_get_image_metadata_drop_duplicates
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_get_image_metadata_drop_duplicates>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_get_image_metadata_size_restriction
Stack Traces | 0.003s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_get_image_metadata_size_restriction>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_download_and_process_tile_failure
Stack Traces | 0.004s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_download_and_process_tile_failure>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_get_image_metadata_empty_response
Stack Traces | 0.004s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_get_image_metadata_empty_response>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
project_types/street/tests/api_calls_test.py::TestTileGroupingFunctions::test_coordinate_download_no_tiles
Stack Traces | 0.01s run time
self = <project_types.street.tests.api_calls_test.TestTileGroupingFunctions testMethod=test_coordinate_download_no_tiles>

    @typing.override
    def setUp(self):
        self.level = 14
        self.test_polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
        self.empty_polygon = Polygon()
        self.empty_geometry = GeometryCollection()
        self.row = pd.Series({"x": 1, "y": 1, "z": self.level})
>       self.provider = StreetImageProvider(
            name=StreetImageProviderNameEnum.MAPILLARY,
            url=Config.MAPILLARY_API_LINK,
        )
E       pydantic_core._pydantic_core.ValidationError: 1 validation error for StreetImageProvider
E       url
E         Value error, url field is only allowed for PANORAMAX_CUSTOM provider [type=value_error, input_value='https://tiles.mapillary....mly1_computed_public/2/', input_type=str]
E           For further information visit https://errors.pydantic.dev/2.10/v/value_error

.../street/tests/api_calls_test.py:56: ValidationError
apps/project/tests/mutation_test.py::TestProjectTypeMutation::test_project_street
Stack Traces | 0.102s run time
self = <project.tests.mutation_test.TestProjectTypeMutation testMethod=test_project_street>
mock_requests = <MagicMock name='apply_async' id='140272640345824'>

    @patch("apps.project.serializers.process_project_task.apply_async")
    def test_project_street(self, mock_requests):  # type: ignore[reportMissingParameterType]
        self.force_login(self.user)
        project_data = {
            **self.project_data,
            "clientId": str(ULID()),
            "projectType": self.genum(ProjectTypeEnum.STREET),
        }
        content = self._create_project_mutation(project_data)
        resp_data = content["data"]["createProject"]
        assert resp_data["errors"] is None, content
    
        project_id = resp_data["result"]["id"]
        project_client_id = resp_data["result"]["clientId"]
    
        # Creating AOI Project Asset
        project_asset_data = {
            "project": project_id,
            "clientId": str(ULID()),
        }
    
        content = self._create_project_aoi_asset(project_asset_data, assert_errors=True)
        resp_data = content["data"]["createProjectAsset"]
        assert resp_data["errors"] is None, content
        aoi_geometry_asset = resp_data["result"]
    
        # Creating Project Image Asset
        project_asset_data = {
            "project": project_id,
            "clientId": str(ULID()),
        }
        content = self._create_project_image_asset(project_asset_data, assert_errors=True)
        resp_data = content["data"]["createProjectAsset"]
        assert resp_data["errors"] is None, content
        image_asset = resp_data["result"]
    
        # Updating Project
        project_data = {
            "clientId": project_client_id,
            "image": image_asset["id"],
            "verificationNumber": 10,
            "projectTypeSpecifics": {
                "street": {
                    "aoiGeometry": aoi_geometry_asset["id"],
                    "customOptions": {
                        "clientId": str(ULID()),
                        "description": "Street project description",
                        "icon": self.genum(IconEnum.ADD_OUTLINE),
                        "iconColor": "#FF0000",
                        "title": "Street Project Title",
                        "value": 1,
                        "subOptions": [
                            {
                                "clientId": str(ULID()),
                                "value": 1,
                                "description": "Street sub option description",
                            },
                        ],
                    },
                    "mapillaryImageFilters": {
                        "isPano": True,
                        "creatorId": None,
                        "organizationId": None,
                        "startTime": None,
                        "endTime": None,
                        "randomizeOrder": False,
                        "samplingThreshold": None,
                    },
                },
            },
        }
>       content = self._update_project_mutation(project_id, project_data)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.../project/tests/mutation_test.py:1666: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../project/tests/mutation_test.py:1115: in _update_project_mutation
    return update_project_query(
.../project/tests/mutation_test.py:101: in update_project_query
    return query_check_func(
main/tests/base_test.py:126: in query_check
    self.assertResponseNoErrors(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <project.tests.mutation_test.TestProjectTypeMutation testMethod=test_project_street>
resp = <HttpResponse status_code=200, "application/json">, msg = None

    def assertResponseNoErrors(self, resp: typing.Any, msg=None):  # type: ignore[reportMissingParameterType]
        """Assert that the call went through correctly. 200 means the syntax is ok,
        if there are no `errors`, the call was fine.
    
        :resp HttpResponse: Response
        """
        content = resp.json()
        assert resp.status_code == 200, msg or content
>       assert "errors" not in list(content.keys()), msg or content
E       AssertionError: {'data': None, 'errors': [{'locations': [{'column': 42, 'line': 2}], 'message': "Variable '$data' got invalid value {'isPano': True, 'creatorId': None, 'organizationId': None, 'startTime': None, 'endTime': None, 'randomizeOrder': False, 'samplingThreshold': None} at 'data.projectTypeSpecifics.street.mapillaryImageFilters'; Field 'isPano' is not defined by type 'StreetMapillaryImageFiltersInput'."}]}
E       assert 'errors' not in ['data', 'errors']
E        +  where ['data', 'errors'] = list(dict_keys(['data', 'errors']))
E        +    where dict_keys(['data', 'errors']) = <built-in method keys of dict object at 0x7f93e86b8b40>()
E        +      where <built-in method keys of dict object at 0x7f93e86b8b40> = {'data': None, 'errors': [{'locations': [{'column': 42, 'line': 2}], 'message': "Variable '$data' got invalid value {'isPano': True, 'creatorId': None, 'organizationId': None, 'startTime': None, 'endTime': None, 'randomizeOrder': False, 'samplingThreshold': None} at 'data.projectTypeSpecifics.street.mapillaryImageFilters'; Field 'isPano' is not defined by type 'StreetMapillaryImageFiltersInput'."}]}.keys

main/tests/base_test.py:143: AssertionError
apps/project/tests/e2e_create_street_project_test.py::TestStreetProjectE2E::test_street_project_e2e
Stack Traces | 2.59s run time
self = <project.tests.e2e_create_street_project_test.TestStreetProjectE2E testMethod=test_street_project_e2e>

    @pytest.mark.vcr(".../projects/street/cassette")
    def test_street_project_e2e(self):
        # TODO(susilnem): Add more test with filters
        with create_override():
>           self._test_project(
                ".../projects/street/project_data.json5",
            )

.../project/tests/e2e_create_street_project_test.py:305: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../project/tests/e2e_create_street_project_test.py:411: in _test_project
    update_content = self.query_check(
main/tests/base_test.py:126: in query_check
    self.assertResponseNoErrors(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <project.tests.e2e_create_street_project_test.TestStreetProjectE2E testMethod=test_street_project_e2e>
resp = <HttpResponse status_code=200, "application/json">, msg = None

    def assertResponseNoErrors(self, resp: typing.Any, msg=None):  # type: ignore[reportMissingParameterType]
        """Assert that the call went through correctly. 200 means the syntax is ok,
        if there are no `errors`, the call was fine.
    
        :resp HttpResponse: Response
        """
        content = resp.json()
        assert resp.status_code == 200, msg or content
>       assert "errors" not in list(content.keys()), msg or content
E       AssertionError: {'data': None, 'errors': [{'locations': [{'column': 42, 'line': 2}], 'message': "Variable '$data' got invalid value {'isPano': None, 'creatorId': None, 'organizationId': None, 'startTime': None, 'endTime': None, 'randomizeOrder': False, 'samplingThreshold': None} at 'data.projectTypeSpecifics.street.mapillaryImageFilters'; Field 'isPano' is not defined by type 'StreetMapillaryImageFiltersInput'."}]}
E       assert 'errors' not in ['data', 'errors']
E        +  where ['data', 'errors'] = list(dict_keys(['data', 'errors']))
E        +    where dict_keys(['data', 'errors']) = <built-in method keys of dict object at 0x7f93e86cc040>()
E        +      where <built-in method keys of dict object at 0x7f93e86cc040> = {'data': None, 'errors': [{'locations': [{'column': 42, 'line': 2}], 'message': "Variable '$data' got invalid value {'isPano': None, 'creatorId': None, 'organizationId': None, 'startTime': None, 'endTime': None, 'randomizeOrder': False, 'samplingThreshold': None} at 'data.projectTypeSpecifics.street.mapillaryImageFilters'; Field 'isPano' is not defined by type 'StreetMapillaryImageFiltersInput'."}]}.keys

main/tests/base_test.py:143: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@ofr1tz ofr1tz marked this pull request as ready for review February 24, 2026 17:42
@frozenhelium frozenhelium requested a review from susilnem March 6, 2026 08:09
return firebase_models.FbMappingTaskStreetCreateOnlyInput(
# XXX: converting this to int for backwards compatibility
taskId=int(task.firebase_id),
taskId=task.firebase_id,
Copy link
Member

Choose a reason for hiding this comment

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

Is there any reason to remove the casting of firebase_id?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, we use the street-level image ID as the MapSwipe task ID so that we can easily link MapSwipe results back to the original images without having to add an additional task attribute.

However, whereas Mapillary has integer image identifiers, Panoramax uses UUID v4 for identifiers.

Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be in street/project.py?

Copy link
Author

Choose a reason for hiding this comment

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

This is consistent with raster_tile_server and vector_tile_server also in utils/geo/


# Type hints
id: int
id: int | str
Copy link
Member

Choose a reason for hiding this comment

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

Any reason on changing the type?

Copy link
Author

Choose a reason for hiding this comment

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

Same as above: task id == Panoramax image ID (UUID v4)

@ofr1tz ofr1tz force-pushed the feature/panoramax branch from 71352ae to f15f817 Compare March 19, 2026 15:50
@ofr1tz ofr1tz requested a review from susilnem March 23, 2026 16:06
@ofr1tz ofr1tz force-pushed the feature/panoramax branch 2 times, most recently from a2dad0a to f15f817 Compare March 23, 2026 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants