|
27 | 27 | from main.tests import TestCase |
28 | 28 | from project_types.street import project as street_project |
29 | 29 | from project_types.tile_map_service.compare import project as compare_project |
| 30 | +from project_types.tile_map_service.locate import project as locate_project |
| 31 | +from project_types.tile_map_service.locate.project import SubGridSizeEnum |
30 | 32 | from utils.geo.raster_tile_server.config import RasterTileServerNameEnum |
31 | 33 |
|
32 | 34 | BASE_DIR = Path(__file__).resolve().parent |
@@ -1692,3 +1694,302 @@ def test_project_street(self, mock_requests): # type: ignore[reportMissingParam |
1692 | 1694 | assert resp_data["result"]["status"] == self.genum(Project.Status.READY_TO_PROCESS) |
1693 | 1695 |
|
1694 | 1696 | mock_requests.assert_called_once() |
| 1697 | + |
| 1698 | + @patch("apps.project.serializers.process_project_task.delay") |
| 1699 | + def test_project_locate(self, mock_requests): # type: ignore[reportMissingParameterType] |
| 1700 | + self.force_login(self.user) |
| 1701 | + project_data = { |
| 1702 | + **self.project_data, |
| 1703 | + "projectType": self.genum(ProjectTypeEnum.LOCATE), |
| 1704 | + "clientId": str(ULID()), |
| 1705 | + } |
| 1706 | + content = self._create_project_mutation(project_data) |
| 1707 | + resp_data = content["data"]["createProject"] |
| 1708 | + assert resp_data["errors"] is None, content |
| 1709 | + |
| 1710 | + project_id = resp_data["result"]["id"] |
| 1711 | + project_client_id = resp_data["result"]["clientId"] |
| 1712 | + |
| 1713 | + # Creating AOI Project Asset |
| 1714 | + project_asset_data = { |
| 1715 | + "project": project_id, |
| 1716 | + "clientId": str(ULID()), |
| 1717 | + } |
| 1718 | + content = self._create_project_aoi_asset(project_asset_data) |
| 1719 | + resp_data = content["data"]["createProjectAsset"] |
| 1720 | + assert resp_data["errors"] is None, content |
| 1721 | + aoi_geometry_asset = resp_data["result"] |
| 1722 | + |
| 1723 | + # Creating Project Image Asset |
| 1724 | + project_asset_data = { |
| 1725 | + "project": project_id, |
| 1726 | + "clientId": str(ULID()), |
| 1727 | + } |
| 1728 | + content = self._create_project_image_asset(project_asset_data) |
| 1729 | + resp_data = content["data"]["createProjectAsset"] |
| 1730 | + assert resp_data["errors"] is None, content |
| 1731 | + image_asset = resp_data["result"] |
| 1732 | + |
| 1733 | + # Updating Project |
| 1734 | + project_data = { |
| 1735 | + "clientId": project_client_id, |
| 1736 | + "image": image_asset["id"], |
| 1737 | + "verificationNumber": 10, |
| 1738 | + "projectTypeSpecifics": { |
| 1739 | + "locate": { |
| 1740 | + "aoiGeometry": aoi_geometry_asset["id"], |
| 1741 | + "zoomLevel": 15, |
| 1742 | + "tileServerProperty": { |
| 1743 | + "name": self.genum(RasterTileServerNameEnum.CUSTOM), |
| 1744 | + "custom": { |
| 1745 | + "url": "https://hi-there/{x}/{y}/{z}", |
| 1746 | + "credits": "My Map", |
| 1747 | + }, |
| 1748 | + }, |
| 1749 | + "subGridSize": self.genum(SubGridSizeEnum.SIZE_2X2), |
| 1750 | + }, |
| 1751 | + }, |
| 1752 | + } |
| 1753 | + content = self._update_project_mutation(project_id, project_data) |
| 1754 | + resp_data = content["data"]["updateProject"] |
| 1755 | + assert resp_data["errors"] is None, content |
| 1756 | + |
| 1757 | + latest_project = Project.objects.get(pk=project_id) |
| 1758 | + assert latest_project.created_by_id == self.user.pk |
| 1759 | + assert latest_project.modified_by_id == self.user.pk |
| 1760 | + assert latest_project.image_id == int(image_asset["id"]) |
| 1761 | + assert latest_project.aoi_geometry_input_asset |
| 1762 | + assert latest_project.aoi_geometry_input_asset.id == int(aoi_geometry_asset["id"]) |
| 1763 | + assert latest_project.project_type_specifics == { |
| 1764 | + "aoi_geometry": aoi_geometry_asset["id"], |
| 1765 | + "zoom_level": 15, |
| 1766 | + "tile_server_property": { |
| 1767 | + "name": RasterTileServerNameEnum.CUSTOM.value, |
| 1768 | + "custom": { |
| 1769 | + "url": "https://hi-there/{x}/{y}/{z}", |
| 1770 | + "credits": "My Map", |
| 1771 | + }, |
| 1772 | + }, |
| 1773 | + "sub_grid_size": SubGridSizeEnum.SIZE_2X2.value, |
| 1774 | + } |
| 1775 | + locate_project.LocateProjectProperty.model_validate( |
| 1776 | + latest_project.project_type_specifics, |
| 1777 | + context={"project_id": latest_project.pk}, |
| 1778 | + ) |
| 1779 | + |
| 1780 | + # Updating Project: |
| 1781 | + # Test project processing |
| 1782 | + project_data = { |
| 1783 | + "clientId": project_client_id, |
| 1784 | + "status": self.genum(Project.Status.READY_TO_PROCESS), |
| 1785 | + } |
| 1786 | + content = self._update_project_status_mutation(project_id, project_data) |
| 1787 | + resp_data = content["data"]["updateProjectStatus"] |
| 1788 | + assert resp_data["errors"] is None, content |
| 1789 | + assert resp_data["result"]["status"] == self.genum(Project.Status.READY_TO_PROCESS) |
| 1790 | + latest_project.refresh_from_db() |
| 1791 | + assert latest_project.processing_status is None |
| 1792 | + |
| 1793 | + mock_requests.assert_called_once() |
| 1794 | + mock_requests.assert_has_calls([call(int(project_id))]) |
| 1795 | + |
| 1796 | + process_project_task(int(project_id)) |
| 1797 | + |
| 1798 | + latest_project.refresh_from_db() |
| 1799 | + |
| 1800 | + project_task_group_qs = ProjectTaskGroup.objects.filter(project=latest_project) |
| 1801 | + project_task_qs = ProjectTask.objects.filter(task_group__project=latest_project) |
| 1802 | + |
| 1803 | + class TaskGroupSpecificsType(typing.TypedDict): |
| 1804 | + x_max: int |
| 1805 | + x_min: int |
| 1806 | + y_max: int |
| 1807 | + y_min: int |
| 1808 | + |
| 1809 | + class TaskGroupType(typing.TypedDict): |
| 1810 | + firebase_id: str |
| 1811 | + number_of_tasks: int |
| 1812 | + required_count: int |
| 1813 | + total_area: float |
| 1814 | + project_type_specifics: TaskGroupSpecificsType |
| 1815 | + |
| 1816 | + expected_tasks_groups: list[TaskGroupType] = [ |
| 1817 | + { |
| 1818 | + "firebase_id": "g101", |
| 1819 | + "number_of_tasks": 18, |
| 1820 | + "project_type_specifics": { |
| 1821 | + "x_max": 24152, |
| 1822 | + "x_min": 24147, |
| 1823 | + "y_max": 13755, |
| 1824 | + "y_min": 13753, |
| 1825 | + }, |
| 1826 | + "required_count": 10, |
| 1827 | + "total_area": 21.010735845202447, |
| 1828 | + }, |
| 1829 | + { |
| 1830 | + "firebase_id": "g102", |
| 1831 | + "number_of_tasks": 24, |
| 1832 | + "project_type_specifics": { |
| 1833 | + "x_max": 24153, |
| 1834 | + "x_min": 24146, |
| 1835 | + "y_max": 13758, |
| 1836 | + "y_min": 13756, |
| 1837 | + }, |
| 1838 | + "required_count": 10, |
| 1839 | + "total_area": 28.02915392364502, |
| 1840 | + }, |
| 1841 | + { |
| 1842 | + "firebase_id": "g103", |
| 1843 | + "number_of_tasks": 24, |
| 1844 | + "project_type_specifics": { |
| 1845 | + "x_max": 24153, |
| 1846 | + "x_min": 24146, |
| 1847 | + "y_max": 13761, |
| 1848 | + "y_min": 13759, |
| 1849 | + }, |
| 1850 | + "required_count": 10, |
| 1851 | + "total_area": 28.043986769512177, |
| 1852 | + }, |
| 1853 | + { |
| 1854 | + "firebase_id": "g104", |
| 1855 | + "number_of_tasks": 6, |
| 1856 | + "project_type_specifics": { |
| 1857 | + "x_max": 24150, |
| 1858 | + "x_min": 24149, |
| 1859 | + "y_max": 13764, |
| 1860 | + "y_min": 13762, |
| 1861 | + }, |
| 1862 | + "required_count": 10, |
| 1863 | + "total_area": 7.014703242812157, |
| 1864 | + }, |
| 1865 | + ] |
| 1866 | + |
| 1867 | + expected_last_5_tasks = [ |
| 1868 | + { |
| 1869 | + "firebase_id": "15-24147-13753", |
| 1870 | + "project_type_specifics": { |
| 1871 | + "tile_x": 24147, |
| 1872 | + "tile_y": 13753, |
| 1873 | + "url": "https://hi-there/24147/13753/15", |
| 1874 | + }, |
| 1875 | + }, |
| 1876 | + { |
| 1877 | + "firebase_id": "15-24147-13754", |
| 1878 | + "project_type_specifics": { |
| 1879 | + "tile_x": 24147, |
| 1880 | + "tile_y": 13754, |
| 1881 | + "url": "https://hi-there/24147/13754/15", |
| 1882 | + }, |
| 1883 | + }, |
| 1884 | + { |
| 1885 | + "firebase_id": "15-24147-13755", |
| 1886 | + "project_type_specifics": { |
| 1887 | + "tile_x": 24147, |
| 1888 | + "tile_y": 13755, |
| 1889 | + "url": "https://hi-there/24147/13755/15", |
| 1890 | + }, |
| 1891 | + }, |
| 1892 | + { |
| 1893 | + "firebase_id": "15-24148-13753", |
| 1894 | + "project_type_specifics": { |
| 1895 | + "tile_x": 24148, |
| 1896 | + "tile_y": 13753, |
| 1897 | + "url": "https://hi-there/24148/13753/15", |
| 1898 | + }, |
| 1899 | + }, |
| 1900 | + { |
| 1901 | + "firebase_id": "15-24148-13754", |
| 1902 | + "project_type_specifics": { |
| 1903 | + "tile_x": 24148, |
| 1904 | + "tile_y": 13754, |
| 1905 | + "url": "https://hi-there/24148/13754/15", |
| 1906 | + }, |
| 1907 | + }, |
| 1908 | + ] |
| 1909 | + |
| 1910 | + assert { |
| 1911 | + "required_results": (18 + 24 + 24 + 6) * 10, |
| 1912 | + "tasks_groups_count": project_task_group_qs.count(), |
| 1913 | + "tasks_groups": list( |
| 1914 | + project_task_group_qs.order_by("id").values( |
| 1915 | + "firebase_id", |
| 1916 | + "number_of_tasks", |
| 1917 | + "required_count", |
| 1918 | + "total_area", |
| 1919 | + "project_type_specifics", |
| 1920 | + ), |
| 1921 | + ), |
| 1922 | + "tasks_count": project_task_qs.count(), |
| 1923 | + "tasks": list( |
| 1924 | + project_task_qs.order_by("id").values( |
| 1925 | + "firebase_id", |
| 1926 | + "project_type_specifics", |
| 1927 | + )[:5], |
| 1928 | + ), |
| 1929 | + "status": latest_project.status, |
| 1930 | + "processing_status": latest_project.processing_status, |
| 1931 | + } == { |
| 1932 | + "required_results": latest_project.required_results, |
| 1933 | + "tasks_groups_count": len(expected_tasks_groups), |
| 1934 | + "tasks_groups": expected_tasks_groups, |
| 1935 | + "tasks_count": 72, |
| 1936 | + "tasks": expected_last_5_tasks, |
| 1937 | + "status": Project.Status.PROCESSED, |
| 1938 | + "processing_status": Project.ProcessingStatus.COMPLETED, |
| 1939 | + } |
| 1940 | + |
| 1941 | + # Updating Processed Project: |
| 1942 | + # Attaching tutorial |
| 1943 | + locate_tutorial = TutorialFactory.create( |
| 1944 | + **self.user_resource_kwargs, |
| 1945 | + project=latest_project, |
| 1946 | + ) |
| 1947 | + project_data = { |
| 1948 | + "clientId": project_client_id, |
| 1949 | + "tutorial": str(locate_tutorial.id), |
| 1950 | + } |
| 1951 | + content = self._update_processed_project_mutation(project_id, project_data) |
| 1952 | + resp_data = content["data"]["updateProcessedProject"] |
| 1953 | + assert resp_data["errors"] is None, content |
| 1954 | + assert resp_data["result"]["tutorialId"] == str(locate_tutorial.id) |
| 1955 | + |
| 1956 | + # project is not sync to firebase |
| 1957 | + project_ref = self.firebase_helper.ref( |
| 1958 | + Config.FirebaseKeys.project(latest_project.firebase_id), |
| 1959 | + ) |
| 1960 | + fb_project: typing.Any = project_ref.get() |
| 1961 | + assert fb_project is None |
| 1962 | + |
| 1963 | + # Updating Processed Project: |
| 1964 | + # Publishing project |
| 1965 | + project_data = { |
| 1966 | + "clientId": project_client_id, |
| 1967 | + "status": self.genum(Project.Status.READY_TO_PUBLISH), |
| 1968 | + } |
| 1969 | + content = self._update_project_status_mutation(project_id, project_data) |
| 1970 | + resp_data = content["data"]["updateProjectStatus"] |
| 1971 | + assert resp_data["errors"] is None, content |
| 1972 | + assert resp_data["result"]["status"] == self.genum(Project.Status.READY_TO_PUBLISH) |
| 1973 | + |
| 1974 | + latest_project.refresh_from_db() |
| 1975 | + assert latest_project.processing_status == Project.ProcessingStatus.COMPLETED |
| 1976 | + |
| 1977 | + # project is sync to firebase after publish |
| 1978 | + fb_project: typing.Any = project_ref.get() |
| 1979 | + assert fb_project is not None |
| 1980 | + |
| 1981 | + # Updating Processed Project after publishing |
| 1982 | + project_data = { |
| 1983 | + "clientId": project_client_id, |
| 1984 | + "maxTasksPerUser": 1000, |
| 1985 | + } |
| 1986 | + content = self._update_processed_project_mutation(project_id, project_data) |
| 1987 | + resp_data = content["data"]["updateProcessedProject"] |
| 1988 | + assert resp_data["errors"] is None, content |
| 1989 | + assert resp_data["result"]["maxTasksPerUser"] == 1000 |
| 1990 | + project_ref = self.firebase_helper.ref( |
| 1991 | + Config.FirebaseKeys.project(latest_project.firebase_id), |
| 1992 | + ) |
| 1993 | + fb_project: typing.Any = project_ref.get() |
| 1994 | + assert fb_project is not None |
| 1995 | + assert fb_project["maxTasksPerUser"] == 1000 |
0 commit comments