diff --git a/src/backend/app/auth/roles.py b/src/backend/app/auth/roles.py index 72a0f702d9..b5879b3f1d 100644 --- a/src/backend/app/auth/roles.py +++ b/src/backend/app/auth/roles.py @@ -290,7 +290,9 @@ async def mapper( """A mapper for a specific project.""" # If project is public, skip permission check if project.visibility == ProjectVisibility.PUBLIC: + # FIXME this is a different type than DbUser return user_data + db_user = await check_access( user_data, db, diff --git a/src/backend/app/projects/project_deps.py b/src/backend/app/projects/project_deps.py index efdd6f0b31..6fc80742cf 100644 --- a/src/backend/app/projects/project_deps.py +++ b/src/backend/app/projects/project_deps.py @@ -34,10 +34,11 @@ async def get_project_by_id( db: Session = Depends(get_db), project_id: Optional[int] = None -) -> DbProject: +) -> Optional[DbProject]: """Get a single project by id.""" if not project_id: # Skip if no project id passed + # FIXME why do we need this? return None db_project = db.query(DbProject).filter(DbProject.id == project_id).first() diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index def50ca06e..3b103e7c47 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -323,6 +323,106 @@ async def set_odk_entities_mapping_status( ) +@router.get("/{project_id}/tiles-list/") +async def tiles_list( + project_id: int, + db: Session = Depends(database.get_db), + current_user: AuthUser = Depends(mapper), +): + """Returns the list of tiles for a project. + + Parameters: + project_id: int + db (Session): The database session, provided automatically. + current_user (AuthUser): Check if user is logged in. + + Returns: + Response: List of generated tiles for a project. + """ + return await project_crud.get_mbtiles_list(db, project_id) + + +@router.get("/{project_id}/download-tiles/") +async def download_tiles( + tile_id: int, + db: Session = Depends(database.get_db), + current_user: AuthUser = Depends(mapper), +): + """Download the basemap tile archive for a project.""" + log.debug("Getting tile archive path from DB") + tiles_path = ( + db.query(db_models.DbTilesPath) + .filter(db_models.DbTilesPath.id == str(tile_id)) + .first() + ) + log.info(f"User requested download for tiles: {tiles_path.path}") + + project_id = tiles_path.project_id + project = await project_crud.get_project(db, project_id) + filename = Path(tiles_path.path).name.replace( + f"{project_id}_", f"{project.project_name_prefix}_" + ) + log.debug(f"Sending tile archive to user: {filename}") + + return FileResponse( + tiles_path.path, + headers={"Content-Disposition": f'attachment; filename="{filename}"'}, + ) + + +@router.get("/{project_id}/tiles") +async def generate_project_tiles( + background_tasks: BackgroundTasks, + project_id: int, + source: str = Query( + ..., description="Select a source for tiles", enum=TILES_SOURCE + ), + format: str = Query( + "mbtiles", description="Select an output format", enum=TILES_FORMATS + ), + tms: str = Query( + None, + description="Provide a custom TMS URL, optional", + ), + db: Session = Depends(database.get_db), + current_user: AuthUser = Depends(mapper), +): + """Returns basemap tiles for a project. + + Args: + background_tasks (BackgroundTasks): FastAPI bg tasks, provided automatically. + project_id (int): ID of project to create tiles for. + source (str): Tile source ("esri", "bing", "topo", "google", "oam"). + format (str, optional): Default "mbtiles". Other options: "pmtiles", "sqlite3". + tms (str, optional): Default None. Custom TMS provider URL. + db (Session): The database session, provided automatically. + current_user (AuthUser): Check if user has MAPPER permission. + + Returns: + str: Success message that tile generation started. + """ + # Create task in db and return uuid + log.debug( + "Creating generate_project_tiles background task " + f"for project ID: {project_id}" + ) + background_task_id = await project_crud.insert_background_task_into_database( + db, project_id=project_id + ) + + background_tasks.add_task( + project_crud.get_project_tiles, + db, + project_id, + background_task_id, + source, + format, + tms, + ) + + return {"Message": "Tile generation started"} + + @router.get("/{project_id}", response_model=project_schemas.ReadProject) async def read_project( current_user: AuthUser = Depends(mapper), @@ -1071,106 +1171,6 @@ async def convert_fgb_to_geojson( return Response(content=json.dumps(data_extract_geojson), headers=headers) -@router.get("/tiles/{project_id}") -async def generate_project_tiles( - background_tasks: BackgroundTasks, - project_id: int, - source: str = Query( - ..., description="Select a source for tiles", enum=TILES_SOURCE - ), - format: str = Query( - "mbtiles", description="Select an output format", enum=TILES_FORMATS - ), - tms: str = Query( - None, - description="Provide a custom TMS URL, optional", - ), - db: Session = Depends(database.get_db), - current_user: AuthUser = Depends(mapper), -): - """Returns basemap tiles for a project. - - Args: - background_tasks (BackgroundTasks): FastAPI bg tasks, provided automatically. - project_id (int): ID of project to create tiles for. - source (str): Tile source ("esri", "bing", "topo", "google", "oam"). - format (str, optional): Default "mbtiles". Other options: "pmtiles", "sqlite3". - tms (str, optional): Default None. Custom TMS provider URL. - db (Session): The database session, provided automatically. - current_user (AuthUser): Check if user has MAPPER permission. - - Returns: - str: Success message that tile generation started. - """ - # Create task in db and return uuid - log.debug( - "Creating generate_project_tiles background task " - f"for project ID: {project_id}" - ) - background_task_id = await project_crud.insert_background_task_into_database( - db, project_id=project_id - ) - - background_tasks.add_task( - project_crud.get_project_tiles, - db, - project_id, - background_task_id, - source, - format, - tms, - ) - - return {"Message": "Tile generation started"} - - -@router.get("/tiles_list/{project_id}/") -async def tiles_list( - project_id: int, - db: Session = Depends(database.get_db), - current_user: AuthUser = Depends(mapper), -): - """Returns the list of tiles for a project. - - Parameters: - project_id: int - db (Session): The database session, provided automatically. - current_user (AuthUser): Check if user is logged in. - - Returns: - Response: List of generated tiles for a project. - """ - return await project_crud.get_mbtiles_list(db, project_id) - - -@router.get("/download_tiles/") -async def download_tiles( - tile_id: int, - db: Session = Depends(database.get_db), - current_user: AuthUser = Depends(mapper), -): - """Download the basemap tile archive for a project.""" - log.debug("Getting tile archive path from DB") - tiles_path = ( - db.query(db_models.DbTilesPath) - .filter(db_models.DbTilesPath.id == str(tile_id)) - .first() - ) - log.info(f"User requested download for tiles: {tiles_path.path}") - - project_id = tiles_path.project_id - project = await project_crud.get_project(db, project_id) - filename = Path(tiles_path.path).name.replace( - f"{project_id}_", f"{project.project_name_prefix}_" - ) - log.debug(f"Sending tile archive to user: {filename}") - - return FileResponse( - tiles_path.path, - headers={"Content-Disposition": f'attachment; filename="{filename}"'}, - ) - - @router.get("/boundary_in_osm/{project_id}/") async def download_task_boundary_osm( project_id: int, diff --git a/src/frontend/src/api/Project.js b/src/frontend/src/api/Project.js index db0345c321..66c3c4c16f 100755 --- a/src/frontend/src/api/Project.js +++ b/src/frontend/src/api/Project.js @@ -144,7 +144,7 @@ export const GenerateProjectTiles = (url, payload) => { const generateProjectTiles = async (url, payload) => { try { const response = await CoreModules.axios.get(url); - dispatch(GetTilesList(`${import.meta.env.VITE_API_URL}/projects/tiles_list/${payload}/`)); + dispatch(GetTilesList(`${import.meta.env.VITE_API_URL}/projects/${payload}/tiles-list/`)); dispatch(ProjectActions.SetGenerateProjectTilesLoading(false)); } catch (error) { dispatch(ProjectActions.SetGenerateProjectTilesLoading(false)); diff --git a/src/frontend/src/components/GenerateBasemap.jsx b/src/frontend/src/components/GenerateBasemap.jsx index ea0f6d1b96..f96287e71c 100644 --- a/src/frontend/src/components/GenerateBasemap.jsx +++ b/src/frontend/src/components/GenerateBasemap.jsx @@ -31,12 +31,16 @@ const GenerateBasemap = ({ projectInfo }) => { }); const downloadBasemap = (tileId, toOpfs = false) => { dispatch( - DownloadTile(`${import.meta.env.VITE_API_URL}/projects/download_tiles/?tile_id=${tileId}`, projectInfo, toOpfs), + DownloadTile( + `${import.meta.env.VITE_API_URL}/projects/${id}/download-tiles/?tile_id=${tileId}`, + projectInfo, + toOpfs, + ), ); }; const getTilesList = () => { - dispatch(GetTilesList(`${import.meta.env.VITE_API_URL}/projects/tiles_list/${id}/`)); + dispatch(GetTilesList(`${import.meta.env.VITE_API_URL}/projects/${id}/tiles-list/`)); }; useEffect(() => { @@ -80,7 +84,7 @@ const GenerateBasemap = ({ projectInfo }) => { GenerateProjectTiles( `${ import.meta.env.VITE_API_URL - }/projects/tiles/${id}?source=${selectedTileSource}&format=${selectedOutputFormat}&tms=${tmsUrl}`, + }/projects/${id}/tiles?source=${selectedTileSource}&format=${selectedOutputFormat}&tms=${tmsUrl}`, id, ), );