Skip to content

Make tile group dimensions configurable per project type#252

Open
frozenhelium wants to merge 2 commits into
developfrom
fix/update-tile-grouping
Open

Make tile group dimensions configurable per project type#252
frozenhelium wants to merge 2 commits into
developfrom
fix/update-tile-grouping

Conversation

@frozenhelium
Copy link
Copy Markdown
Contributor

@frozenhelium frozenhelium commented May 7, 2026

  • Add min_tile_x_multiplier and min_tile_y_multiplier parameters TileMapServiceBaseProject
    • Defaults (2, 3) preserve the existing find/completeness (6 tasks per screen) layout
    • CompareProject overrides both to 1 so each group is a single tile
  • Add min_tile_x_multiplier and min_tile_y_multiplier parameters to extent_to_groups utility

@frozenhelium frozenhelium changed the title fMake tile group dimensions configurable per project type Make tile group dimensions configurable per project type May 7, 2026
@frozenhelium frozenhelium force-pushed the fix/update-tile-grouping branch from a9ea86f to 2b425d2 Compare May 7, 2026 14:10
@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
129 2 127 0
View the top 2 failed test(s) by shortest run time
apps/project/tests/mutation_test.py::TestProjectTypeMutation::test_project_compare
Stack Traces | 0.754s run time
self = <project.tests.mutation_test.TestProjectTypeMutation testMethod=test_project_compare>
mock_requests = <MagicMock name='delay' id='140240583532160'>

    @patch("apps.project.serializers.process_project_task.delay")
    def test_project_compare(self, mock_requests):  # type: ignore[reportMissingParameterType]
        # Defining user and base project data
        self.force_login(self.user)
        project_data = {
            **self.project_data,
            "projectType": self.genum(ProjectTypeEnum.COMPARE),
            "clientId": str(ULID()),
        }
        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)
        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)
        resp_data = content["data"]["createProjectAsset"]
        assert resp_data["errors"] is None, content
        image_asset = resp_data["result"]
    
        # Updating Project
        # fails as project type specifics has empty object as tile server property
        # project_data = {
        #     "clientId": project_client_id,
        #     "image": image_asset["id"],
        #     "projectTypeSpecifics": {
        #         "find": {
        #             "aoiGeometry": aoi_geometry_asset["id"],
        #             "zoomLevel": 15,
        #             "tileServerProperty": {},
        #             "tileServerBProperty": self.tile_server_property["valid_custom"],
        #         },
        #     },
        # }
        # content = self._update_project_mutation(project_id, project_data, assert_errors=True)
        # assert content["errors"] == [
        #     {
        #         "locations": [{"column": 42, "line": 2}],
        #         "message": "Variable '$data' got invalid value {} at 'data.projectTypeSpecifics.find.tileServerProperty'; Field 'name' of required type 'RasterTileServerNameEnum!' was not provided.",  # noqa: E501
        #     },
        #     {
        #         "locations": [{"column": 42, "line": 2}],
        #         "message": "Variable '$data' got invalid value {'aoiGeometry': '%s', 'zoomLevel': 15, 'tileServerProperty': {}, 'tileServerBProperty': {'name': 'CUSTOM', 'custom': {...}}} at 'data.projectTypeSpecifics.find'; Field 'tileServerBProperty' is not defined by type 'FindProjectPropertyInput'. Did you mean 'tileServerProperty'?"  # noqa: E501
        #         % aoi_geometry_asset["id"],
        #     },
        # ]
    
        # Updating Project
        # fails as project type specifics has partial data
        # project_data = {
        #     "clientId": project_client_id,
        #     "image": image_asset["id"],
        #     "projectTypeSpecifics": {
        #         "compare": {
        #             "aoiGeometry": aoi_geometry_asset["id"],
        #             "zoomLevel": 15,
        #             "tileServerProperty": self.tile_server_property["valid_custom"],
        #         },
        #     },
        # }
        # content = self._update_project_mutation(project_id, project_data, assert_errors=True)
        # assert content["errors"] == [
        #     {
        #         "locations": [{"column": 42, "line": 2}],
        #         "message": "Variable '$data' got invalid value {'aoiGeometry': '%s', 'zoomLevel': 15, 'tileServerProperty': {'name': 'CUSTOM', 'custom': {...}}} at 'data.projectTypeSpecifics.compare'; Field 'tileServerBProperty' of required type 'ProjectRasterTileServerConfigInput!' was not provided."  # noqa: E501
        #         % aoi_geometry_asset["id"],
        #     },
        # ]
    
        # Updating Project
        # fails as project type specifics has one invalid tile server property
        project_data = {
            "clientId": project_client_id,
            "image": image_asset["id"],
            "projectTypeSpecifics": {
                "compare": {
                    "aoiGeometry": aoi_geometry_asset["id"],
                    "zoomLevel": 15,
                    "tileServerProperty": {
                        "name": self.genum(RasterTileServerNameEnum.CUSTOM),
                        "custom": {
                            "url": "https://hi-there",
                            "credits": "My Map",
                        },
                    },
                    "tileServerBProperty": {
                        "name": self.genum(RasterTileServerNameEnum.ESRI),
                        "esri": {
                            "credits": "default credits",
                        },
                    },
                },
            },
        }
        content = self._update_project_mutation(project_id, project_data)
        resp_data = content["data"]["updateProject"]
        assert resp_data["errors"] == [
            {
                "array_errors": None,
                "client_id": project_data["clientId"],
                "field": "nonFieldErrors",
                "messages": "The imagery url 'https://hi-there' must contain {x}, {y} (or {-y}) and {z} or the {quad_key} placeholders.",  # noqa: E501
                "object_errors": None,
                "pydantic_errors": None,
            },
        ], content
    
        # Updating Project
        # fails as project type specifics has one invalid tile server property
        project_data = {
            "clientId": project_client_id,
            "image": image_asset["id"],
            "projectTypeSpecifics": {
                "compare": {
                    "aoiGeometry": aoi_geometry_asset["id"],
                    "zoomLevel": 15,
                    "tileServerProperty": {
                        "name": self.genum(RasterTileServerNameEnum.CUSTOM),
                        "custom": {
                            "url": "https://hi-there/{{x}}/{{y}}/{{z}}",
                            "credits": "My Map",
                        },
                    },
                    "tileServerBProperty": {
                        "name": self.genum(RasterTileServerNameEnum.ESRI_BETA),
                        "esriBeta": {
                            "credits": "default credits",
                        },
                    },
                },
            },
        }
        content = self._update_project_mutation(project_id, project_data)
        resp_data = content["data"]["updateProject"]
        assert resp_data["errors"] == [
            {
                "array_errors": None,
                "client_id": project_client_id,
                "field": "nonFieldErrors",
                "messages": "The imagery url 'https://hi-there/{{x}}/{{y}}/{{z}}' must contain {x}, {y} (or {-y}) and {z} or the {quad_key} placeholders.",  # noqa: E501
                "object_errors": None,
                "pydantic_errors": None,
            },
        ], content
    
        # Updating Project
        project_data = {
            "clientId": project_client_id,
            "image": image_asset["id"],
            "verificationNumber": 10,
            "projectTypeSpecifics": {
                "compare": {
                    "aoiGeometry": aoi_geometry_asset["id"],
                    "zoomLevel": 15,
                    "tileServerProperty": {
                        "name": self.genum(RasterTileServerNameEnum.CUSTOM),
                        "custom": {
                            "url": "https://hi-there/{x}/{y}/{z}",
                            "credits": "My Map",
                        },
                    },
                    "tileServerBProperty": {
                        "name": self.genum(RasterTileServerNameEnum.MAXAR_STANDARD),
                        "maxarStandard": {
                            "credits": "default credits",
                        },
                    },
                },
            },
        }
        content = self._update_project_mutation(project_id, project_data)
        resp_data = content["data"]["updateProject"]
        assert resp_data["errors"] is None, content
    
        latest_project = Project.objects.get(pk=project_id)
        assert latest_project.created_by_id == self.user.pk
        assert latest_project.modified_by_id == self.user.pk
        assert latest_project.image_id == int(image_asset["id"])
        assert latest_project.aoi_geometry_input_asset
        assert latest_project.aoi_geometry_input_asset.id == int(aoi_geometry_asset["id"])
        assert latest_project.project_type_specifics == {
            "aoi_geometry": aoi_geometry_asset["id"],
            "zoom_level": 15,
            "tile_server_property": {
                "name": RasterTileServerNameEnum.CUSTOM.value,
                "custom": {
                    "url": "https://hi-there/{x}/{y}/{z}",
                    "credits": "My Map",
                },
            },
            "tile_server_b_property": {
                "name": RasterTileServerNameEnum.MAXAR_STANDARD.value,
                "maxar_standard": {
                    "credits": "default credits",
                },
            },
        }
        compare_project.CompareProjectProperty.model_validate(
            latest_project.project_type_specifics,
            context={"project_id": latest_project.pk},
        )
    
        # Updating Project:
        # Test project processing
        project_data = {
            "clientId": project_client_id,
            "status": self.genum(Project.Status.READY_TO_PROCESS),
        }
        content = self._update_project_status_mutation(project_id, project_data)
        resp_data = content["data"]["updateProjectStatus"]
        assert resp_data["errors"] is None, content
        assert resp_data["result"]["status"] == self.genum(Project.Status.READY_TO_PROCESS)
        latest_project.refresh_from_db()
        assert latest_project.processing_status is None
    
        mock_requests.assert_called_once()
        mock_requests.assert_has_calls([call(int(project_id))])
    
        process_project_task(int(project_id))
    
        class TaskGroupSpecificsType(typing.TypedDict):
            x_max: int
            x_min: int
            y_max: int
            y_min: int
    
        class TaskGroupType(typing.TypedDict):
            firebase_id: str
            number_of_tasks: int
            required_count: int
            total_area: float
            project_type_specifics: TaskGroupSpecificsType
    
        expected_task_groups: list[TaskGroupType] = [
            {
                "firebase_id": "g101",
                "number_of_tasks": 18,
                "required_count": 10,
                "total_area": 21.010735845202447,
                "project_type_specifics": {
                    "x_max": 24152,
                    "x_min": 24147,
                    "y_max": 13755,
                    "y_min": 13753,
                },
            },
            {
                "firebase_id": "g102",
                "number_of_tasks": 24,
                "required_count": 10,
                "total_area": 28.02915392364502,
                "project_type_specifics": {
                    "x_max": 24153,
                    "x_min": 24146,
                    "y_max": 13758,
                    "y_min": 13756,
                },
            },
            {
                "firebase_id": "g103",
                "number_of_tasks": 24,
                "required_count": 10,
                "total_area": 28.043986769512177,
                "project_type_specifics": {
                    "x_max": 24153,
                    "x_min": 24146,
                    "y_max": 13761,
                    "y_min": 13759,
                },
            },
            {
                "firebase_id": "g104",
                "number_of_tasks": 6,
                "required_count": 10,
                "total_area": 7.014703242812157,
                "project_type_specifics": {
                    "x_max": 24150,
                    "x_min": 24149,
                    "y_max": 13764,
                    "y_min": 13762,
                },
            },
        ]
        expected_last_5_tasks = [
            {
                "firebase_id": "15-24147-13753",
                "project_type_specifics": {
                    "tile_x": 24147,
                    "tile_y": 13753,
                    "url": "https:.../24147/13753/15",
                    "url_b": "https://services.digitalglobe..../tmsaccess/tms/1.0.0/DigitalGlobe%3AImageryTileService@EPSG%3A3857@.../15/24147/13753.jpg?connectId=dummy-maxar-standard",
                },
            },
            {
                "firebase_id": "15-24147-13754",
                "project_type_specifics": {
                    "tile_x": 24147,
                    "tile_y": 13754,
                    "url": "https:.../24147/13754/15",
                    "url_b": "https://services.digitalglobe..../tmsaccess/tms/1.0.0/DigitalGlobe%3AImageryTileService@EPSG%3A3857@.../15/24147/13754.jpg?connectId=dummy-maxar-standard",
                },
            },
            {
                "firebase_id": "15-24147-13755",
                "project_type_specifics": {
                    "tile_x": 24147,
                    "tile_y": 13755,
                    "url": "https:.../24147/13755/15",
                    "url_b": "https://services.digitalglobe..../tmsaccess/tms/1.0.0/DigitalGlobe%3AImageryTileService@EPSG%3A3857@.../15/24147/13755.jpg?connectId=dummy-maxar-standard",
                },
            },
            {
                "firebase_id": "15-24148-13753",
                "project_type_specifics": {
                    "tile_x": 24148,
                    "tile_y": 13753,
                    "url": "https:.../24148/13753/15",
                    "url_b": "https://services.digitalglobe..../tmsaccess/tms/1.0.0/DigitalGlobe%3AImageryTileService@EPSG%3A3857@.../15/24148/13753.jpg?connectId=dummy-maxar-standard",
                },
            },
            {
                "firebase_id": "15-24148-13754",
                "project_type_specifics": {
                    "tile_x": 24148,
                    "tile_y": 13754,
                    "url": "https:.../24148/13754/15",
                    "url_b": "https://services.digitalglobe..../tmsaccess/tms/1.0.0/DigitalGlobe%3AImageryTileService@EPSG%3A3857@.../15/24148/13754.jpg?connectId=dummy-maxar-standard",
                },
            },
        ]
    
        latest_project.refresh_from_db()
        project_task_group_qs = ProjectTaskGroup.objects.filter(project=latest_project)
        project_task_qs = ProjectTask.objects.filter(task_group__project=latest_project)
    
>       assert {
            "required_results": (18 + 24 + 24 + 6) * 10,
            "tasks_groups_count": project_task_group_qs.count(),
            "tasks_groups": list(
                project_task_group_qs.order_by("id").values(
                    "firebase_id",
                    "number_of_tasks",
                    "required_count",
                    "total_area",
                    "project_type_specifics",
                ),
            ),
            "tasks_count": project_task_qs.count(),
            "tasks": list(
                project_task_qs.order_by("id").values(
                    "firebase_id",
                    "project_type_specifics",
                )[:5],
            ),
            "status": latest_project.status,
            "processing_status": latest_project.processing_status,
        } == {
            "required_results": latest_project.required_results,
            "tasks_groups_count": len(expected_task_groups),
            "tasks_groups": expected_task_groups,
            "tasks_count": 72,
            "tasks": expected_last_5_tasks,
            "status": Project.Status.PROCESSED,
            "processing_status": Project.ProcessingStatus.COMPLETED,
        }
E       AssertionError: assert equals failed
E         {                                {                               
E           'processing_status': ProjectP    'processing_status': ProjectP 
E         rocessingStatusEnum.COMPLETED,   rocessingStatusEnum.COMPLETED,  
E           'required_results': #x00^72#x010,         'required_results': #x00^53#x010,      
E           'status': ProjectStatusEnum.P    'status': ProjectStatusEnum.P 
E         ROCESSED,                        ROCESSED,                       
E           'tasks': [                       'tasks': [                    
E                                          #x00+    {#x01                           
E                                          #x00+      'firebase_id': '15-24147-#x01 
E                                          #x00+13753',#x01                         
E                                          #x00+      'project_type_specifics':#x01 
E                                          #x00+ {#x01                              
E                                          #x00+        'tile_x': 24147,#x01        
E                                          #x00+        'tile_y': 13753,#x01        
E                                          #x00+        'url': 'https://hi-ther#x01 
E                                          #x00+.../24147/13753/15',#x01              
E                                          #x00+        'url_b': 'https://servi#x01 
E                                          #x00+ces.digitalglobe.com/earthservi#x01 
E                                          #x00+.../tmsaccess/tms/1.0.0/DigitalG#x01 
E                                          #x00+lobe%3AImageryTileService@EPSG%#x01 
E                                          #x00+3A3857@.../15/24147/13753.jpg?c#x01 
E                                          #x00+onnectId=dummy-maxar-standard',#x01 
E                                          #x00+      },#x01                        
E                                          #x00+    },#x01                          
E                                          #x00+    {#x01                           
E                                          #x00+      'firebase_id': '15-24147-#x01 
E                                          #x00+13754',#x01                         
E                                          #x00+      'project_type_specifics':#x01 
E                                          #x00+ {#x01                              
E                                          #x00+        'tile_x': 24147,#x01        
E                                          #x00+        'tile_y': 13754,#x01        
E                                          #x00+        'url': 'https://hi-ther#x01 
E                                          #x00+.../24147/13754/15',#x01              
E                                          #x00+        'url_b': 'https://servi#x01 
E                                          #x00+ces.digitalglobe.com/earthservi#x01 
E                                          #x00+.../tmsaccess/tms/1.0.0/DigitalG#x01 
E                                          #x00+lobe%3AImageryTileService@EPSG%#x01 
E                                          #x00+3A3857@.../15/24147/13754.jpg?c#x01 
E                                          #x00+onnectId=dummy-maxar-standard',#x01 
E                                          #x00+      },#x01                        
E                                          #x00+    },#x01                          
E                                          #x00+    {#x01                           
E                                          #x00+      'firebase_id': '15-24147-#x01 
E                                          #x00+13755',#x01                         
E                                          #x00+      'project_type_specifics':#x01 
E                                          #x00+ {#x01                              
E                                          #x00+        'tile_x': 24147,#x01        
E                                          #x00+        'tile_y': 13755,#x01        
E                                          #x00+        'url': 'https://hi-ther#x01 
E                                          #x00+.../24147/13755/15',#x01              
E                                          #x00+        'url_b': 'https://servi#x01 
E                                          #x00+ces.digitalglobe.com/earthservi#x01 
E                                          #x00+.../tmsaccess/tms/1.0.0/DigitalG#x01 
E                                          #x00+lobe%3AImageryTileService@EPSG%#x01 
E                                          #x00+3A3857@.../15/24147/13755.jpg?c#x01 
E                                          #x00+onnectId=dummy-maxar-standard',#x01 
E                                          #x00+      },#x01                        
E                                          #x00+    },#x01                          
E                                          #x00+    {#x01                           
E                                          #x00+      'firebase_id': '15-24148-#x01 
E                                          #x00+13753',#x01                         
E                                          #x00+      'project_type_specifics':#x01 
E                                          #x00+ {#x01                              
E                                          #x00+        'tile_x': 24148,#x01        
E                                          #x00+        'tile_y': 13753,#x01        
E                                          #x00+        'url': 'https://hi-ther#x01 
E                                          #x00+.../24148/13753/15',#x01              
E                                          #x00+        'url_b': 'https://servi#x01 
E                                          #x00+ces.digitalglobe.com/earthservi#x01 
E                                          #x00+.../tmsaccess/tms/1.0.0/DigitalG#x01 
E                                          #x00+lobe%3AImageryTileService@EPSG%#x01 
E                                          #x00+3A3857@.../15/24148/13753.jpg?c#x01 
E                                          #x00+onnectId=dummy-maxar-standard',#x01 
E                                          #x00+      },#x01                        
E                                          #x00+    },#x01                          
E             {                                {                           
E               'firebase_id': '15-24148-        'firebase_id': '15-24148- 
E         13754',                          13754',                         
E               'project_type_specifics':        'project_type_specifics': 
E          {                                {                              
E                 'tile_x': 24148,                 'tile_x': 24148,        
E                 'tile_y': 13754,                 'tile_y': 13754,        
E                 'url': 'https://hi-ther          'url': 'https://hi-ther 
E         .../24148/13754/15',               .../24148/13754/15',              
E                 'url_b': 'https://servi          'url_b': 'https://servi 
E         ces.digitalglobe.com/earthservi  ces.digitalglobe.com/earthservi 
E         .../tmsaccess/tms/1.0.0/DigitalG  .../tmsaccess/tms/1.0.0/DigitalG 
E         lobe%3AImageryTileService@EPSG%  lobe%3AImageryTileService@EPSG% 
E         3A3857@.../15/24148/13754.jpg?c  3A3857@.../15/24148/13754.jpg?c 
E         onnectId=dummy-maxar-standard',  onnectId=dummy-maxar-standard', 
E               },                               },                        
E             },                               },                          
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': '15-24149-#x01                                  
E         #x00-13754',#x01                                                          
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'tile_x': 24149,#x01                                         
E         #x00-        'tile_y': 13754,#x01                                         
E         #x00-        'url': 'https://hi-ther#x01                                  
E         #.../24149/13754/15',#x01                                               
E         #x00-        'url_b': 'https://servi#x01                                  
E         #x00-ces.digitalglobe.com/earthservi#x01                                  
E         #x00-.../tmsaccess/tms/1.0.0/DigitalG#x01                                  
E         #x00-lobe%3AImageryTileService@EPSG%#x01                                  
E         #x00-3A3857@.../15/24149/13754.jpg?c#x01                                  
E         #x00-onnectId=dummy-maxar-standard',#x01                                  
E         #x00-      },#x01                                                         
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': '15-24150-#x01                                  
E         #x00-13754',#x01                                                          
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'tile_x': 24150,#x01                                         
E         #x00-        'tile_y': 13754,#x01                                         
E         #x00-        'url': 'https://hi-ther#x01                                  
E         #.../24150/13754/15',#x01                                               
E         #x00-        'url_b': 'https://servi#x01                                  
E         #x00-ces.digitalglobe.com/earthservi#x01                                  
E         #x00-.../tmsaccess/tms/1.0.0/DigitalG#x01                                  
E         #x00-lobe%3AImageryTileService@EPSG%#x01                                  
E         #x00-3A3857@.../15/24150/13754.jpg?c#x01                                  
E         #x00-onnectId=dummy-maxar-standard',#x01                                  
E         #x00-      },#x01                                                         
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': '15-24151-#x01                                  
E         #x00-13754',#x01                                                          
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'tile_x': 24151,#x01                                         
E         #x00-        'tile_y': 13754,#x01                                         
E         #x00-        'url': 'https://hi-ther#x01                                  
E         #.../24151/13754/15',#x01                                               
E         #x00-        'url_b': 'https://servi#x01                                  
E         #x00-ces.digitalglobe.com/earthservi#x01                                  
E         #x00-.../tmsaccess/tms/1.0.0/DigitalG#x01                                  
E         #x00-lobe%3AImageryTileService@EPSG%#x01                                  
E         #x00-3A3857@.../15/24151/13754.jpg?c#x01                                  
E         #x00-onnectId=dummy-maxar-standard',#x01                                  
E         #x00-      },#x01                                                         
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': '15-24152-#x01                                  
E         #x00-13754',#x01                                                          
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'tile_x': 24152,#x01                                         
E         #x00-        'tile_y': 13754,#x01                                         
E         #x00-        'url': 'https://hi-ther#x01                                  
E         #.../24152/13754/15',#x01                                               
E         #x00-        'url_b': 'https://servi#x01                                  
E         #x00-ces.digitalglobe.com/earthservi#x01                                  
E         #x00-.../tmsaccess/tms/1.0.0/DigitalG#x01                                  
E         #x00-lobe%3AImageryTileService@EPSG%#x01                                  
E         #x00-3A3857@.../15/24152/13754.jpg?c#x01                                  
E         #x00-onnectId=dummy-maxar-standard',#x01                                  
E         #x00-      },#x01                                                         
E         #x00-    },#x01                                                           
E           ],                               ],                            
E           'tasks_count': #x00^53#x01,               'tasks_count': #x00^72#x01,            
E           'tasks_groups': [                'tasks_groups': [             
E             {                                {                           
E               'firebase_id': 'g101',           'firebase_id': 'g101',    
E               'number_of_tasks': #x00^5#x01,            'number_of_tasks': #x00^18#x01,    
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'x_max': 24152,#x01                                          
E         #x00-        'x_min': 24148,#x01                                          
E         #x00-        'y_max': 13754,#x01                                          
E         #x00-        'y_min': 13754,#x01                                          
E         #x00-      },#x01                                                         
E         #x00-      'required_count': 10,#x01                                      
E         #x00-      'total_area': 5.836315563#x01                                  
E         #x00-40456,#x01                                                           
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': 'g102',#x01                                     
E         #x00-      'number_of_tasks': 6,#x01                                      
E               'project_type_specifics':        'project_type_specifics': 
E          {                                {                              
E                 'x_max': 24152,                  'x_max': 24152,         
E                 'x_min': 24147,                  'x_min': 24147,         
E                 'y_max': 13755,                  'y_max': 13755,         
E                 'y_min': 1375#x00^5#x01,                  'y_min': 1375#x00^3#x01,         
E               },                               },                        
E               'required_count': 10,            'required_count': 10,     
E         #x00-      'total_area': 7.004815481#x01  #x00+      'total_area': 21.01073584#x01 
E         #x00-5750125,#x01                         #x00+5202447,#x01                        
E             },                               },                          
E             {                                {                           
E               'firebase_id': 'g10#x00^3#x01',           'firebase_id': 'g10#x00^2#x01',    
E               'number_of_tasks': #x00^7#x01,            'number_of_tasks': #x00^24#x01,    
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'x_max': 24152,#x01                                          
E         #x00-        'x_min': 24146,#x01                                          
E         #x00-        'y_max': 13756,#x01                                          
E         #x00-        'y_min': 13756,#x01                                          
E         #x00-      },#x01                                                         
E         #x00-      'required_count': 10,#x01                                      
E         #x00-      'total_area': 8.173727454#x01                                  
E         #x00-215049,#x01                                                          
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': 'g104',#x01                                     
E         #x00-      'number_of_tasks': 7,#x01                                      
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'x_max': 24152,#x01                                          
E         #x00-        'x_min': 24146,#x01                                          
E         #x00-        'y_max': 13757,#x01                                          
E         #x00-        'y_min': 13757,#x01                                          
E         #x00-      },#x01                                                         
E         #x00-      'required_count': 10,#x01                                      
E         #x00-      'total_area': 8.175169965#x01                                  
E         #x00-969086,#x01                                                          
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': 'g105',#x01                                     
E         #x00-      'number_of_tasks': 8,#x01                                      
E               'project_type_specifics':        'project_type_specifics': 
E          {                                {                              
E                 'x_max': 24153,                  'x_max': 24153,         
E                 'x_min': 24146,                  'x_min': 24146,         
E                 'y_max': 13758,                  'y_max': 13758,         
E                 'y_min': 1375#x00^8#x01,                  'y_min': 1375#x00^6#x01,         
E               },                               },                        
E               'required_count': 10,            'required_count': 10,     
E         #x00-      'total_area': 9.344699729#x01  #x00+      'total_area': 28.02915392#x01 
E         #x00-148864,#x01                          #x00+364502,#x01                         
E             },                               },                          
E             {                                {                           
E               'firebase_id': 'g10#x00^6#x01',           'firebase_id': 'g10#x00^3#x01',    
E               'number_of_tasks': #x00^8#x01,            'number_of_tasks': #x00^24#x01,    
E               'project_type_specifics':        'project_type_specifics': 
E          {                                {                              
E                 'x_max': 24153,                  'x_max': 24153,         
E                 'x_min': 24146,                  'x_min': 24146,         
E                 'y_max': 137#x00^59#x01,                  'y_max': 137#x00^61#x01,         
E                 'y_min': 13759,                  'y_min': 13759,         
E               },                               },                        
E               'required_count': 10,            'required_count': 10,     
E         #x00-      'total_area': 9.346347823#x01  #x00+      'total_area': 28.04398676#x01 
E         #x00-39859,#x01                           #x00+9512177,#x01                        
E             },                               },                          
E             {                                {                           
E               'firebase_id': 'g10#x00^7#x01',           'firebase_id': 'g10#x00^4#x01',    
E               'number_of_tasks': #x00^5#x01,            'number_of_tasks': #x00^6#x01,     
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'x_max': 24152,#x01                                          
E         #x00-        'x_min': 24148,#x01                                          
E         #x00-        'y_max': 13760,#x01                                          
E         #x00-        'y_min': 13760,#x01                                          
E         #x00-      },#x01                                                         
E         #x00-      'required_count': 10,#x01                                      
E         #x00-      'total_area': 5.842497294#x01                                  
E         #x00-938564,#x01                                                          
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': 'g108',#x01                                     
E         #x00-      'number_of_tasks': 5,#x01                                      
E         #x00-      'project_type_specifics':#x01                                  
E         #x00- {#x01                                                               
E         #x00-        'x_max': 24152,#x01                                          
E         #x00-        'x_min': 24148,#x01                                          
E         #x00-        'y_max': 13761,#x01                                          
E         #x00-        'y_min': 13761,#x01                                          
E         #x00-      },#x01                                                         
E         #x00-      'required_count': 10,#x01                                      
E         #x00-      'total_area': 5.843527046#x01                                  
E         #x00-382428,#x01                                                          
E         #x00-    },#x01                                                           
E         #x00-    {#x01                                                            
E         #x00-      'firebase_id': 'g109',#x01                                     
E         #x00-      'number_of_tasks': 2,#x01                                      
E               'project_type_specifics':        'project_type_specifics': 
E          {                                {                              
E                 'x_max': 24150,                  'x_max': 24150,         
E                 'x_min': 24149,                  'x_min': 24149,         
E                 'y_max': 1376#x00^2#x01,                  'y_max': 1376#x00^4#x01,         
E                 'y_min': 13762,                  'y_min': 13762,         
E               },                               },                        
E               'required_count': 10,            'required_count': 10,     
E         #x00-      'total_area': 2.337822657#x01  #x00+      'total_area': 7.014703242#x01 
E         #x00-541275,#x01                          #x00+812157,#x01                         
E             },                               },                          
E           ],                               ],                            
E           'tasks_groups_count': #x00^9#x01,         'tasks_groups_count': #x00^4#x01,      
E         }                                }

.../project/tests/mutation_test.py:1494: AssertionError
apps/project/tests/e2e_create_project_tile_map_service_test.py::TestTileMapServiceProjectE2E::test_compare_project_e2e
Stack Traces | 2.44s run time
self = <project.tests.e2e_create_project_tile_map_service_test.TestTileMapServiceProjectE2E testMethod=test_compare_project_e2e>

    def test_compare_project_e2e(self):
        with create_override():
>           self._test_project(
                "compare",
                ".../projects/compare/project_data.json5",
            )

.../project/tests/e2e_create_project_tile_map_service_test.py:367: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <project.tests.e2e_create_project_tile_map_service_test.TestTileMapServiceProjectE2E testMethod=test_compare_project_e2e>
projectKey = 'compare'
filename = '.../projects/compare/project_data.json5'

    def _test_project(self, projectKey: str, filename: str):
        # Load test data file
        full_path = Path(Config.BASE_DIR, filename)
        with full_path.open("r", encoding="utf-8") as f:
            test_data = json5.load(f)
    
        # Create contributor user and login
        contributor_user = ContributorUserFactory.create(
            username="Ram Bahadur",
            firebase_id=test_data["contributor_user_firebase_id"],
        )
        user = UserFactory.create(
            contributor_user=contributor_user,
        )
    
        self.force_login(user)
    
        # Define full path for image and AOI files
        image_filename = Path(Config.BASE_DIR) / test_data["assets"]["image"]
        aoi_geometry_filename = Path(Config.BASE_DIR) / test_data["assets"]["aoi"]
    
        # Create an organization
        create_organization_data = test_data["create_organization"]
        with self.captureOnCommitCallbacks(execute=True):
            organization_content = self.query_check(
                self.Mutation.CREATE_ORGANIZATION,
                variables={"data": create_organization_data},
            )
    
        organization_response = organization_content["data"]["createOrganization"]
        assert organization_response is not None, "Organization create response is None"
        assert organization_response["ok"]
    
        organization_id = organization_response["result"]["id"]
        organization_fb_id = organization_response["result"]["firebaseId"]
    
        organization_fb_ref = self.firebase_helper.ref(f"/v2/organisations/{organization_fb_id}")
        organization_fb_data = organization_fb_ref.get()
        assert organization_fb_data is not None, "Organization in firebase is None"
    
        # Create project
        create_project_data = test_data["create_project"]
        create_project_data["requestingOrganization"] = organization_id
    
        with self.captureOnCommitCallbacks(execute=True):
            project_content = self.query_check(
                self.Mutation.CREATE_PROJECT,
                variables={"data": create_project_data},
            )
    
        project_response = project_content["data"]["createProject"]
        assert project_response is not None, "Project create response is None"
        assert project_response["ok"], project_response["errors"]
    
        project_id = project_response["result"]["id"]
        project_fb_id = project_response["result"]["firebaseId"]
        project_client_id = create_project_data["clientId"]
    
        # Create Image Asset for cover image
        image_asset_data = {
            "clientId": project_client_id,
            "inputType": "COVER_IMAGE",
            "project": project_id,
        }
        with image_filename.open("rb") as img_file:
            image_content = self.query_check(
                self.Mutation.UPLOAD_PROJECT_ASSET,
                variables={"data": image_asset_data},
                files={"imageFile": img_file},
                map={"imageFile": ["variables.data.file"]},
            )
        image_response = image_content["data"]["createProjectAsset"]
        assert image_response is not None, "Image create response is None"
        assert image_response["ok"]
        image_id = image_response["result"]["id"]
    
        # Create GeoJSON Asset for AOI Geometry
        aoi_asset_data = {
            "clientId": str(ULID()),
            "inputType": "AOI_GEOMETRY",
            "project": project_id,
        }
        with aoi_geometry_filename.open("rb") as geo_file:
            aoi_content = self.query_check(
                self.Mutation.UPLOAD_PROJECT_ASSET,
                variables={"data": aoi_asset_data},
                files={"geoFile": geo_file},
                map={"geoFile": ["variables.data.file"]},
            )
        aoi_response = aoi_content["data"]["createProjectAsset"]
        assert aoi_response is not None, "AOI create response is None"
        assert aoi_response["ok"]
        aoi_id = aoi_response["result"]["id"]
    
        # Update project
        update_project_data = test_data["update_project"]
        update_project_data["image"] = image_id
        update_project_data["projectTypeSpecifics"][projectKey]["aoiGeometry"] = aoi_id
        update_project_data["requestingOrganization"] = organization_id
        with self.captureOnCommitCallbacks(execute=True):
            update_content = self.query_check(
                self.Mutation.UPDATE_PROJECT,
                variables={"pk": project_id, "data": update_project_data},
            )
        update_response = update_content["data"]["updateProject"]
        assert update_response["ok"], update_response["errors"]
        assert update_response is not None, "Project update response is None"
    
        # Process project
        process_project_data = {
            "clientId": project_client_id,
            "status": "READY_TO_PROCESS",
        }
        with self.captureOnCommitCallbacks(execute=True):
            process_project_content = self.query_check(
                self.Mutation.UPDATE_PROJECT_STATUS,
                variables={"pk": project_id, "data": process_project_data},
            )
        process_project_response = process_project_content["data"]["updateProjectStatus"]
        assert process_project_response is not None, "Project ready to process response is None"
        assert process_project_response["ok"], process_project_response["errors"]
        assert process_project_response["result"]["status"] == "READY_TO_PROCESS", "Project should be ready to process"
    
        # Create tutorial from above project
        create_tutorial_data = test_data["create_tutorial"]
        create_tutorial_data["project"] = project_id
        with self.captureOnCommitCallbacks(execute=True):
            tutorial_content = self.query_check(
                self.Mutation.CREATE_TUTORIAL,
                variables={"data": create_tutorial_data},
            )
    
        tutorial_response = tutorial_content["data"]["createTutorial"]
        assert tutorial_response is not None, "Tutorial create response is None"
        assert tutorial_response["ok"]
    
        tutorial_id = tutorial_response["result"]["id"]
        tutorial_fb_id = tutorial_response["result"]["firebaseId"]
        tutorial_client_id = create_tutorial_data["clientId"]
    
        # Update Tutorial
        with self.captureOnCommitCallbacks(execute=True):
            update_tutorial_content = self.query_check(
                query=self.Mutation.UPDATE_TUTORIAL,
                variables={
                    "data": test_data["update_tutorial"],
                    "pk": tutorial_id,
                },
            )
        update_tutorial_response = update_tutorial_content["data"]["updateTutorial"]
        assert update_tutorial_response is not None, "Tutorial update response is None"
        assert update_tutorial_response["ok"], update_tutorial_response["errors"]
        assert update_tutorial_response is not None, "Tutorial update response is None"
    
        # Publish tutorial
        publish_tutorial_data = {
            "clientId": tutorial_client_id,
            "status": "READY_TO_PUBLISH",
        }
        with self.captureOnCommitCallbacks(execute=True):
            publish_tutorial_content = self.query_check(
                self.Mutation.UPDATE_TUTORIAL_STATUS,
                variables={"pk": tutorial_id, "data": publish_tutorial_data},
            )
        publish_tutorial_response = publish_tutorial_content["data"]["updateTutorialStatus"]
        assert publish_tutorial_response["ok"], publish_tutorial_response["errors"]
        assert publish_tutorial_response is not None, "Processed tutorial publish response is None"
        assert publish_tutorial_response["result"]["status"] == "READY_TO_PUBLISH", "tutorial should be ready to published"
    
        tutorial_fb_ref = self.firebase_helper.ref(f"/v2/projects/{tutorial_fb_id}")
        tutorial_fb_data = tutorial_fb_ref.get()
    
        # Check tutorial in firebase
        assert tutorial_fb_data is not None, "Tutorial in firebase is None"
        assert isinstance(tutorial_fb_data, dict), "Tutorial in firebase should be a dictionary"
    
        filtered_tutorial_actual = tutorial_fb_data
        filtered_tutorial_expected = test_data["expected_tutorial_data"]
        assert filtered_tutorial_actual == filtered_tutorial_expected, "Difference found for tutorial data in firebase."
    
        # Check tutorial groups in firebase
        tutorial_groups_fb_ref = self.firebase_helper.ref(f"/v2/groups/{tutorial_fb_id}/")
        tutorial_groups_fb_data = tutorial_groups_fb_ref.get()
    
        filtered_group_actual = tutorial_groups_fb_data
        filtered_group_expected = test_data["expected_tutorial_groups_data"]
        assert filtered_group_actual == filtered_group_expected, "Difference found for tutorial group data in firebase."
    
        # Check tutorial tasks in firebase
        tutorial_tasks_ref = self.firebase_helper.ref(f"/v2/tasks/{tutorial_fb_id}/")
        tutorial_task_fb_data = tutorial_tasks_ref.get()
    
        sanitized_tasks_actual = tutorial_task_fb_data
        sanitized_tasks_expected = test_data["expected_tutorial_tasks_data"]
    
        assert sanitized_tasks_actual == sanitized_tasks_expected, (
            "Differences found between expected and actual tasks on tutorial in firebase."
        )
    
        # Update processed project: attach tutorial, organization
        update_processed_project_data = test_data["update_processed_project"]
        update_processed_project_data["tutorial"] = tutorial_id
        update_processed_project_data["requestingOrganization"] = organization_id
        with self.captureOnCommitCallbacks(execute=True):
            update_processed_project_content = self.query_check(
                self.Mutation.UPDATE_PROCESSED_PROJECT,
                variables={"pk": project_id, "data": update_processed_project_data},
            )
        update_processed_response = update_processed_project_content["data"]["updateProcessedProject"]
        assert update_processed_response["ok"], update_processed_response["errors"]
        assert update_processed_response is not None, "Processed project update response is None"
    
        # Publish project
        publish_project_data = {
            "clientId": project_client_id,
            "status": "READY_TO_PUBLISH",
        }
        with self.captureOnCommitCallbacks(execute=True):
            publish_project_content = self.query_check(
                self.Mutation.UPDATE_PROJECT_STATUS,
                variables={"pk": project_id, "data": publish_project_data},
            )
        publish_project_response = publish_project_content["data"]["updateProjectStatus"]
        assert publish_project_response["ok"], publish_project_response["errors"]
        assert publish_project_response is not None, "Processed project publish response is None"
        assert publish_project_response["result"]["status"] == "READY_TO_PUBLISH", "Project should be ready to publish"
    
        project_fb_ref = self.firebase_helper.ref(f"/v2/projects/{project_fb_id}")
        project_fb_data = project_fb_ref.get()
    
        # Check project in firebase
        assert project_fb_data is not None, "Project in firebase is None"
        assert isinstance(project_fb_data, dict), "Project in firebase should be a dictionary"
        assert project_fb_data["created"] is not None, "Field 'created' should be defined"
        assert datetime.fromisoformat(project_fb_data["created"]), "Field 'created' should be a timestamp"
    
        ignored_project_keys = {"created"}
        filtered_project_actual = remove_object_keys(project_fb_data, ignored_project_keys)
        filtered_project_expected = remove_object_keys(test_data["expected_project_data"], ignored_project_keys)
>       assert filtered_project_actual == filtered_project_expected, "Difference found for project data in firebase."
E       AssertionError: Difference found for project data in firebase.
E       assert equals failed
E           'projectRegion': 'southeeaste    'projectRegion': 'southeeaste 
E         rn peninsula of Mexico',         rn peninsula of Mexico',        
E           'projectTopic': 'Forest Chang    'projectTopic': 'Forest Chang 
E         e',                              e',                             
E           'projectTopicKey': 'compare d    'projectTopicKey': 'compare d 
E         ates - forest change - southeea  ates - forest change - southeea 
E         stern peninsula of mexico (1) t  stern peninsula of mexico (1) t 
E         ogglecorp (test)',               ogglecorp (test)',              
E           'projectType': 3,                'projectType': 3,             
E           'requestingOrganisation': 'To    'requestingOrganisation': 'To 
E         gglecorp (test)',                gglecorp (test)',               
E           'requiredResults': 1#x00^47#x01,          'requiredResults': #x00+2#x011#x00^6#x01,       
E           'resultCount': 0,                'resultCount': 0,             
E           'status': 'active',              'status': 'active',           
E           'tileServer': {                  'tileServer': {               
E             'apiKey': 'dummy-bing',          'apiKey': 'dummy-bing',     
E             'credits': '© 2019 Microsof      'credits': '© 2019 Microsof 
E         t Corporation, Earthstar Geogra  t Corporation, Earthstar Geogra 
E         phics SIO',                      phics SIO',

.../project/tests/e2e_create_project_tile_map_service_test.py:613: 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.

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.

1 participant