Skip to content

Commit

Permalink
refactor: move logic for task schemas into models
Browse files Browse the repository at this point in the history
  • Loading branch information
spwoodcock committed Nov 28, 2023
1 parent f4880dc commit fb69c17
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 176 deletions.
3 changes: 0 additions & 3 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ async def get_projects(
user_id: int,
skip: int = 0,
limit: int = 100,
db_objects: bool = False,
hashtags: List[str] = None,
search: str = None,
):
Expand Down Expand Up @@ -117,8 +116,6 @@ async def get_projects(
.all()
)
project_count = db.query(db_models.DbProject).count()
if db_objects:
return project_count, db_projects
return project_count, await convert_to_app_projects(db_projects)


Expand Down
168 changes: 50 additions & 118 deletions src/backend/app/tasks/tasks_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
# along with FMTM. If not, see <https:#www.gnu.org/licenses/>.
#
import base64
from asyncio import gather
from typing import List

from fastapi import HTTPException
from geoalchemy2.shape import from_shape
Expand All @@ -30,14 +28,12 @@

from ..central import central_crud
from ..db import db_models
from ..db.postgis_utils import geometry_to_geojson
from ..models.enums import (
TaskStatus,
get_action_for_status_change,
verify_valid_status_update,
)
from ..projects import project_crud
from ..tasks import tasks_schemas
from ..users import user_crud


Expand Down Expand Up @@ -86,69 +82,81 @@ async def get_tasks(
)
else:
db_tasks = db.query(db_models.DbTask).offset(skip).limit(limit).all()
return await convert_to_app_tasks(db_tasks)
return db_tasks


async def get_task(db: Session, task_id: int, db_obj: bool = False):
db_task = db.query(db_models.DbTask).filter(db_models.DbTask.id == task_id).first()
if db_obj:
return db_task
return await convert_to_app_task(db_task)
async def get_task(db: Session, task_id: int):
return db.query(db_models.DbTask).filter(db_models.DbTask.id == task_id).first()


async def update_task_status(
db: Session, user_id: int, task_id: int, new_status: TaskStatus
):
log.debug(f"Updating task ID {task_id} to status {new_status}")
if not user_id:
log.error(f"User id is not present: {user_id}")
raise HTTPException(status_code=400, detail="User id required.")

db_user = user_crud.get_user(db, user_id, db_obj=True)
db_user = await user_crud.get_user(db, user_id)
if not db_user:
raise HTTPException(
status_code=400, detail=f"User with id {user_id} does not exist."
)
msg = f"User with id {user_id} does not exist."
log.error(msg)
raise HTTPException(status_code=400, detail=msg)

db_task = await get_task(db, task_id, db_obj=True)
db_task = await get_task(db, task_id)
log.debug(f"Returned task from db: {db_task}")

if db_task:
if db_task.task_status in [
TaskStatus.LOCKED_FOR_MAPPING,
TaskStatus.LOCKED_FOR_VALIDATION,
]:
log.debug(f"Task {task_id} currently locked")
if not (user_id is not db_task.locked_by):
msg = (
f"User {user_id} with username {db_user.username} "
"has not locked this task."
)
log.error(msg)
raise HTTPException(
status_code=401,
detail=f"User {user_id} with username {db_user.username} has not locked this task.",
detail=msg,
)

if verify_valid_status_update(db_task.task_status, new_status):
# update history prior to updating task
update_history = await create_task_history_for_status_change(
db_task, new_status, db_user
if not verify_valid_status_update(db_task.task_status, new_status):
msg = f"{new_status} is not a valid task status"
log.error(msg)
raise HTTPException(
status_code=422,
detail=msg,
)
db.add(update_history)

db_task.task_status = new_status
# update history prior to updating task
update_history = await create_task_history_for_status_change(
db_task, new_status, db_user
)
db.add(update_history)

if new_status in [
TaskStatus.LOCKED_FOR_MAPPING,
TaskStatus.LOCKED_FOR_VALIDATION,
]:
db_task.locked_by = db_user.id
else:
db_task.locked_by = None
db_task.task_status = new_status

if new_status == TaskStatus.MAPPED:
db_task.mapped_by = db_user.id
if new_status == TaskStatus.VALIDATED:
db_task.validated_by = db_user.id
if new_status == TaskStatus.INVALIDATED:
db_task.mapped_by = None
if new_status in [
TaskStatus.LOCKED_FOR_MAPPING,
TaskStatus.LOCKED_FOR_VALIDATION,
]:
db_task.locked_by = db_user.id
else:
db_task.locked_by = None

db.commit()
db.refresh(db_task)
if new_status == TaskStatus.MAPPED:
db_task.mapped_by = db_user.id
if new_status == TaskStatus.VALIDATED:
db_task.validated_by = db_user.id
if new_status == TaskStatus.INVALIDATED:
db_task.mapped_by = None

return await convert_to_app_task(db_task)
db.commit()
db.refresh(db_task)
return db_task

else:
raise HTTPException(
Expand All @@ -165,11 +173,14 @@ async def update_task_status(
async def create_task_history_for_status_change(
db_task: db_models.DbTask, new_status: TaskStatus, db_user: db_models.DbUser
):
msg = f"Status changed from {db_task.task_status.name} to {new_status.name} by: {db_user.username}"
log.info(msg)

new_task_history = db_models.DbTaskHistory(
project_id=db_task.project_id,
task_id=db_task.id,
action=get_action_for_status_change(new_status),
action_text=f"Status changed from {db_task.task_status.name} to {new_status.name} by: {db_user.username}",
action_text=msg,
actioned_by=db_user,
user_id=db_user.id,
)
Expand All @@ -194,85 +205,6 @@ async def create_task_history_for_status_change(
# TODO: write tests for these


async def convert_to_app_history(db_histories: List[db_models.DbTaskHistory]):
if db_histories:
log.debug("Converting DB Histories to App Histories")
app_histories: List[tasks_schemas.TaskHistoryBase] = []
for db_history in db_histories:
log.debug(
f"History ID: {db_history.id} | " "Converting DB History to App History"
)
app_history = db_history
app_history.obj = db_history.action_text
app_histories.append(app_history)
return app_histories
return []


async def convert_to_app_task(db_task: db_models.DbTask):
if db_task:
log.debug("")
log.debug(
f"Project ID {db_task.project_id} | Task ID "
f"{db_task.id} | Converting DB Task to App Task"
)

app_task: tasks_schemas.Task = db_task
app_task.task_status_str = tasks_schemas.TaskStatusOption[
app_task.task_status.name
]

if db_task.outline:
properties = {
"fid": db_task.project_task_index,
"uid": db_task.id,
"name": db_task.project_task_name,
}
log.debug("Converting task outline to geojson")
app_task.outline_geojson = geometry_to_geojson(
db_task.outline, properties, db_task.id
)
# app_task.outline_centroid = get_centroid(db_task.outline)

if db_task.lock_holder:
app_task.locked_by_uid = db_task.lock_holder.id
app_task.locked_by_username = db_task.lock_holder.username
log.debug("Task currently locked by user " f"{app_task.locked_by_username}")

if db_task.qr_code:
log.debug(f"QR code found for task ID {db_task.id}. Converting to base64")
app_task.qr_code_base64 = base64.b64encode(db_task.qr_code.image)
else:
log.warning(f"No QR code found for task ID {db_task.id}")
app_task.qr_code_base64 = ""

if db_task.task_history:
app_task.task_history = convert_to_app_history(db_task.task_history)

return app_task
else:
return None


async def convert_to_app_tasks(
db_tasks: List[db_models.DbTask],
) -> List[tasks_schemas.Task]:
num_tasks = len(db_tasks)
log.debug(f"Number of tasks in project: {num_tasks}")

if db_tasks and num_tasks > 0:
log.debug("Converting DB Tasks to App Tasks")

async def convert_task(task):
return await convert_to_app_task(task)

app_tasks = await gather(*[convert_task(task) for task in db_tasks])
return [task for task in app_tasks if task is not None]
else:
log.debug("No tasks found, skipping DB -> App conversion")
return []


async def get_qr_codes_for_task(
db: Session,
task_id: int,
Expand Down
36 changes: 15 additions & 21 deletions src/backend/app/tasks/tasks_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,19 @@
)


@router.get("/task-list", response_model=List[tasks_schemas.TaskOut])
@router.get("/task-list", response_model=List[tasks_schemas.Task])
async def read_task_list(
project_id: int,
limit: int = 1000,
db: Session = Depends(database.get_db),
):
tasks = await tasks_crud.get_tasks(db, project_id, limit)
if tasks:
return tasks
else:
if not tasks:
raise HTTPException(status_code=404, detail="Tasks not found")
return tasks


@router.get("/", response_model=List[tasks_schemas.TaskOut])
@router.get("/", response_model=List[tasks_schemas.Task])
async def read_tasks(
project_id: int,
user_id: int = None,
Expand All @@ -66,10 +65,9 @@ async def read_tasks(
)

tasks = await tasks_crud.get_tasks(db, project_id, user_id, skip, limit)
if tasks:
return tasks
else:
if not tasks:
raise HTTPException(status_code=404, detail="Tasks not found")
return tasks


@router.get("/point_on_surface")
Expand All @@ -95,40 +93,36 @@ async def get_point_on_surface(project_id: int, db: Session = Depends(database.g
return result_dict_list


@router.post("/near_me", response_model=tasks_schemas.TaskOut)
@router.post("/near_me", response_model=tasks_schemas.Task)
async def get_tasks_near_me(
lat: float, long: float, project_id: int = None, user_id: int = None
):
"""Get tasks near the requesting user."""
return "Coming..."


@router.get("/{task_id}", response_model=tasks_schemas.TaskOut)
@router.get("/{task_id}", response_model=tasks_schemas.Task)
async def read_tasks(task_id: int, db: Session = Depends(database.get_db)):
task = await tasks_crud.get_task(db, task_id)
if task:
return task
else:
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task


@router.post("/{task_id}/new_status/{new_status}", response_model=tasks_schemas.TaskOut)
@router.post("/{task_id}/new_status/{new_status}", response_model=tasks_schemas.Task)
async def update_task_status(
user: user_schemas.User,
task_id: int,
new_status: tasks_schemas.TaskStatusOption,
new_status: TaskStatus,
db: Session = Depends(database.get_db),
):
# TODO verify logged in user
user_id = user.id

task = await tasks_crud.update_task_status(
db, user_id, task_id, TaskStatus[new_status.name]
)
if task:
return task
else:
task = await tasks_crud.update_task_status(db, user_id, task_id, new_status)
if not task:
raise HTTPException(status_code=404, detail="Task status could not be updated.")
return task


@router.post("/task-qr-code/{task_id}")
Expand Down
Loading

0 comments on commit fb69c17

Please sign in to comment.