Skip to content

Commit

Permalink
Merge pull request #600 from hotosm/feature/update-odk-cred
Browse files Browse the repository at this point in the history
Feature/update odk cred feature api to update odk credentials of existing project
  • Loading branch information
robsavoye authored Jul 19, 2023
2 parents 47545d0 + c20f433 commit 1336ab1
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -1880,3 +1880,15 @@ async def update_project_form(
)

return True

async def update_odk_credentials(project_instance: project_schemas.BETAProjectUpload,
odk_central_cred: project_schemas.ODKCentral,
odkid: int, db: Session):
project_instance.odkid = odkid
project_instance.odk_central_url = odk_central_cred.odk_central_url
project_instance.odk_central_user = odk_central_cred.odk_central_user
project_instance.odk_central_password = odk_central_cred.odk_central_password

db.commit()
db.refresh(project_instance)

47 changes: 46 additions & 1 deletion src/backend/app/projects/project_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import os
import uuid
from typing import List, Optional
import tempfile
import inspect

from fastapi import (
APIRouter,
Expand All @@ -43,6 +45,7 @@
from ..db import database
from . import project_crud, project_schemas
from ..tasks import tasks_crud
from . import utils

router = APIRouter(
prefix="/projects",
Expand Down Expand Up @@ -88,7 +91,7 @@ async def read_project(project_id: int, db: Session = Depends(database.get_db)):
raise HTTPException(status_code=404, detail="Project not found")


@router.post("/delete/{project_id}")
@router.delete("/delete/{project_id}")
async def delete_project(project_id: int, db: Session = Depends(database.get_db)):
"""Delete a project from ODK Central and the local database."""
# FIXME: should check for error
Expand Down Expand Up @@ -147,6 +150,48 @@ async def create_project(
else:
raise HTTPException(status_code=404, detail="Project not found")

@router.post("/update_odk_credentials")
async def update_odk_credentials(
background_task: BackgroundTasks,
odk_central_cred: project_schemas.ODKCentral,
project_id: int,
db: Session = Depends(database.get_db)
):
"""Update odk credential of a project"""
if odk_central_cred.odk_central_url.endswith("/"):
odk_central_cred.odk_central_url = odk_central_cred.odk_central_url[:-1]

project_instance = project_crud.get_project(db, project_id)

if not project_instance:
raise HTTPException(status_code=404, detail="Project not found")

try:
odkproject = central_crud.create_odk_project(
project_instance.project_info[0].name, odk_central_cred
)
logger.debug(f"ODKCentral return after update: {odkproject}")
except Exception as e:
logger.error(e)
raise HTTPException(
status_code=400, detail="Connection failed to central odk. "
) from e

await project_crud.update_odk_credentials(project_instance, odk_central_cred, odkproject["id"], db)

extract_polygon = True if project_instance.data_extract_type == 'polygon' else False
project_id = project_instance.id
contents = project_instance.form_xls if project_instance.form_xls else None


generate_response = await utils.generate_files(background_tasks=background_task,
project_id=project_id,
extract_polygon=extract_polygon,
upload=contents if contents else None, db=db)


return generate_response


@router.put("/{id}", response_model=project_schemas.ProjectOut)
async def update_project(
Expand Down
82 changes: 82 additions & 0 deletions src/backend/app/projects/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import uuid
from typing import Optional

from fastapi import (
BackgroundTasks,
Depends,
File,
Form,
HTTPException,
UploadFile,
)
from fastapi.logger import logger as logger
from sqlalchemy.orm import Session

from ..db import database
from . import project_crud

async def generate_files(
background_tasks: BackgroundTasks,
project_id: int,
extract_polygon: bool = Form(False),
upload: Optional[UploadFile] = File(None),
db: Session = Depends(database.get_db),
):
"""Generate required media files tasks in the project based on the provided params.
Accepts a project ID, category, custom form flag, and an uploaded file as inputs.
The generated files are associated with the project ID and stored in the database.
This function generates qr_code, forms. This utility function also creates an app user for each task and provides the required roles.
Some of the other functionality of this utility includes converting a xls file provided by the user to the xform,
generates osm data extracts and uploads it to the form.
Parameters:
project_id (int): The ID of the project for which files are being generated. This is a required field.
polygon (bool): A boolean flag indicating whether the polygon is extracted or not.
upload (UploadFile): An uploaded file that is used as input for generating the files.
This is not a required field. A file should be provided if user wants to upload a custom xls form.
Returns:
Message (str): A success message containing the project ID.
"""
contents = None
xform_title = None

project = project_crud.get_project(db, project_id)
if not project:
raise HTTPException(
status_code=428, detail=f"Project with id {project_id} does not exist"
)

project.data_extract_type = 'polygon' if extract_polygon else 'centroid'
db.commit()

if upload:
file_ext = 'xls'
contents = upload

# generate a unique task ID using uuid
background_task_id = uuid.uuid4()

# insert task and task ID into database
await project_crud.insert_background_task_into_database(
db, task_id=background_task_id
)

background_tasks.add_task(
project_crud.generate_appuser_files,
db,
project_id,
extract_polygon,
contents,
None,
xform_title,
file_ext if upload else 'xls',
background_task_id,
)

return {"Message": f"{project_id}", "task_id": f"{background_task_id}"}

0 comments on commit 1336ab1

Please sign in to comment.