Skip to content

Commit

Permalink
refactor: renaming for consistency with database (#1114)
Browse files Browse the repository at this point in the history
  • Loading branch information
spwoodcock authored Jan 18, 2024
1 parent 36af459 commit ed95c47
Show file tree
Hide file tree
Showing 43 changed files with 393 additions and 437 deletions.
6 changes: 3 additions & 3 deletions src/backend/app/auth/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def org_admin(
db: Session = Depends(get_db),
user_data: AuthUser = Depends(login_required),
) -> AuthUser:
"""Organization admin with full permission for projects in an organization."""
"""Organisation admin with full permission for projects in an organisation."""
if project and org_id:
log.error("Both org_id and project_id cannot be passed at the same time")
raise HTTPException(
Expand All @@ -89,10 +89,10 @@ async def org_admin(
)

if not org_admin:
log.error(f"User ID {user_id} is not an admin for organization {org_id}")
log.error(f"User ID {user_id} is not an admin for organisation {org_id}")
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="User is not organization admin",
detail="User is not organisation admin",
)

return user_data
Expand Down
6 changes: 3 additions & 3 deletions src/backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
from app.central import central_routes
from app.config import settings
from app.db.database import get_db
from app.organization import organization_routes
from app.organisations import organisation_routes
from app.projects import project_routes
from app.projects.project_crud import read_xlsforms
from app.submission import submission_routes
from app.submissions import submission_routes
from app.tasks import tasks_routes
from app.users import user_routes

Expand Down Expand Up @@ -95,7 +95,7 @@ def get_application() -> FastAPI:
_app.include_router(central_routes.router)
_app.include_router(auth_routes.router)
_app.include_router(submission_routes.router)
_app.include_router(organization_routes.router)
_app.include_router(organisation_routes.router)

return _app

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with FMTM. If not, see <https:#www.gnu.org/licenses/>.
#
"""Logic for organization management."""
"""Logic for organisation management."""

from io import BytesIO

Expand All @@ -27,10 +27,10 @@
from app.config import settings
from app.db import db_models
from app.models.enums import HTTPStatus
from app.organization.organization_deps import (
get_organization_by_name,
from app.organisations.organisation_deps import (
get_organisation_by_name,
)
from app.organization.organization_schemas import OrganisationEdit, OrganisationIn
from app.organisations.organisation_schemas import OrganisationEdit, OrganisationIn
from app.s3 import add_obj_to_bucket


Expand All @@ -50,7 +50,7 @@ async def upload_logo_to_s3(
so it should not matter if a .jpg is renamed .png.
Args:
db_org(db_models.DbOrganisation): The organization database object.
db_org(db_models.DbOrganisation): The organisation database object.
logo_file(UploadFile): The logo image uploaded to FastAPI.
Returns:
Expand All @@ -73,116 +73,116 @@ async def upload_logo_to_s3(
return logo_url


async def create_organization(
async def create_organisation(
db: Session, org_model: OrganisationIn, logo: UploadFile(None)
) -> db_models.DbOrganisation:
"""Creates a new organization with the given name, description, url, type, and logo.
"""Creates a new organisation with the given name, description, url, type, and logo.
Saves the logo file S3 bucket under /{org_id}/logo.png.
Args:
db (Session): database session
org_model (OrganisationIn): Pydantic model for organization input.
logo (UploadFile, optional): logo file of the organization.
org_model (OrganisationIn): Pydantic model for organisation input.
logo (UploadFile, optional): logo file of the organisation.
Defaults to File(...).
Returns:
DbOrganization: SQLAlchemy Organization model.
DbOrganisation: SQLAlchemy Organisation model.
"""
if await get_organization_by_name(db, org_name=org_model.name):
if await get_organisation_by_name(db, org_name=org_model.name):
raise HTTPException(
status_code=HTTPStatus.CONFLICT,
detail=f"Organization already exists with the name {org_model.name}",
detail=f"Organisation already exists with the name {org_model.name}",
)

# Required to check if exists on error
db_organization = None
db_organisation = None

try:
# Create new organization without logo set
db_organization = db_models.DbOrganisation(**org_model.dict())
# Create new organisation without logo set
db_organisation = db_models.DbOrganisation(**org_model.dict())

db.add(db_organization)
db.add(db_organisation)
db.commit()
# Refresh to get the assigned org id
db.refresh(db_organization)
db.refresh(db_organisation)

# Update the logo field in the database with the correct path
if logo:
db_organization.logo = await upload_logo_to_s3(db_organization, logo)
db_organisation.logo = await upload_logo_to_s3(db_organisation, logo)
db.commit()

except Exception as e:
log.exception(e)
log.debug("Rolling back changes to db organization")
log.debug("Rolling back changes to db organisation")
# Rollback any changes
db.rollback()
# Delete the failed organization entry
if db_organization:
log.debug(f"Deleting created organisation ID {db_organization.id}")
db.delete(db_organization)
# Delete the failed organisation entry
if db_organisation:
log.debug(f"Deleting created organisation ID {db_organisation.id}")
db.delete(db_organisation)
db.commit()
raise HTTPException(
status_code=400, detail=f"Error creating organization: {e}"
status_code=400, detail=f"Error creating organisation: {e}"
) from e

return db_organization
return db_organisation


async def update_organization(
async def update_organisation(
db: Session,
organization: db_models.DbOrganisation,
organisation: db_models.DbOrganisation,
values: OrganisationEdit,
logo: UploadFile(None),
) -> db_models.DbOrganisation:
"""Update an existing organisation database entry.
Args:
db (Session): database session
organization (DbOrganisation): Editing database model.
values (OrganisationEdit): Pydantic model for organization edit.
logo (UploadFile, optional): logo file of the organization.
organisation (DbOrganisation): Editing database model.
values (OrganisationEdit): Pydantic model for organisation edit.
logo (UploadFile, optional): logo file of the organisation.
Defaults to File(...).
Returns:
DbOrganization: SQLAlchemy Organization model.
DbOrganisation: SQLAlchemy Organisation model.
"""
if not (updated_fields := values.dict(exclude_none=True)):
raise HTTPException(
status_code=HTTPStatus.UNPROCESSABLE_ENTITY,
detail=f"No values were provided to update organization {organization.id}",
detail=f"No values were provided to update organisation {organisation.id}",
)

update_cmd = (
update(db_models.DbOrganisation)
.where(db_models.DbOrganisation.id == organization.id)
.where(db_models.DbOrganisation.id == organisation.id)
.values(**updated_fields)
)
db.execute(update_cmd)

if logo:
organization.logo = await upload_logo_to_s3(organization, logo)
organisation.logo = await upload_logo_to_s3(organisation, logo)

db.commit()
db.refresh(organization)
db.refresh(organisation)

return organization
return organisation


async def delete_organization(
async def delete_organisation(
db: Session,
organization: db_models.DbOrganisation,
organisation: db_models.DbOrganisation,
) -> Response:
"""Delete an existing organisation database entry.
Args:
db (Session): database session
organization (DbOrganisation): Database model to delete.
organisation (DbOrganisation): Database model to delete.
Returns:
bool: If deletion was successful.
"""
db.delete(organization)
db.delete(organisation)
db.commit()

return Response(status_code=HTTPStatus.NO_CONTENT)
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# along with FMTM. If not, see <https:#www.gnu.org/licenses/>.
#

"""Organization dependencies for use in Depends."""
"""Organisation dependencies for use in Depends."""

from typing import Union

Expand All @@ -31,15 +31,15 @@
from app.models.enums import HTTPStatus


async def get_organization_by_name(db: Session, org_name: str) -> DbOrganisation:
"""Get an organization from the db by name.
async def get_organisation_by_name(db: Session, org_name: str) -> DbOrganisation:
"""Get an organisation from the db by name.
Args:
db (Session): database session
org_name (int): id of the organization
org_name (int): id of the organisation
Returns:
DbOrganisation: organization with the given id
DbOrganisation: organisation with the given id
"""
return (
db.query(DbOrganisation)
Expand All @@ -49,14 +49,14 @@ async def get_organization_by_name(db: Session, org_name: str) -> DbOrganisation


async def get_organisation_by_id(db: Session, org_id: int) -> DbOrganisation:
"""Get an organization from the db by id.
"""Get an organisation from the db by id.
Args:
db (Session): database session
org_id (int): id of the organization
org_id (int): id of the organisation
Returns:
DbOrganisation: organization with the given id
DbOrganisation: organisation with the given id
"""
return db.query(DbOrganisation).filter(DbOrganisation.id == org_id).first()

Expand All @@ -65,7 +65,7 @@ async def org_exists(
org_id: Union[str, int],
db: Session = Depends(get_db),
) -> DbOrganisation:
"""Check if organization name exists, else error.
"""Check if organisation name exists, else error.
The org_id can also be an org name.
"""
Expand All @@ -75,18 +75,18 @@ async def org_exists(
pass

if isinstance(org_id, int):
log.debug(f"Getting organization by id: {org_id}")
db_organization = await get_organisation_by_id(db, org_id)
log.debug(f"Getting organisation by id: {org_id}")
db_organisation = await get_organisation_by_id(db, org_id)

if isinstance(org_id, str):
log.debug(f"Getting organization by name: {org_id}")
db_organization = await get_organization_by_name(db, org_id)
log.debug(f"Getting organisation by name: {org_id}")
db_organisation = await get_organisation_by_name(db, org_id)

if not db_organization:
if not db_organisation:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
detail=f"Organization {org_id} does not exist",
detail=f"Organisation {org_id} does not exist",
)

log.debug(f"Organization match: {db_organization}")
return db_organization
log.debug(f"Organisation match: {db_organisation}")
return db_organisation
87 changes: 87 additions & 0 deletions src/backend/app/organisations/organisation_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Copyright (c) 2022, 2023 Humanitarian OpenStreetMap Team
#
# This file is part of FMTM.
#
# FMTM is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FMTM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with FMTM. If not, see <https:#www.gnu.org/licenses/>.
#
"""Routes for organisation management."""

from fastapi import (
APIRouter,
Depends,
File,
UploadFile,
)
from sqlalchemy.orm import Session

from app.db import database
from app.db.db_models import DbOrganisation
from app.organisations import organisation_crud, organisation_schemas
from app.organisations.organisation_deps import org_exists

router = APIRouter(
prefix="/organisation",
tags=["organisation"],
dependencies=[Depends(database.get_db)],
responses={404: {"description": "Not found"}},
)


@router.get("/", response_model=list[organisation_schemas.OrganisationOut])
def get_organisations(
db: Session = Depends(database.get_db),
) -> list[organisation_schemas.OrganisationOut]:
"""Get a list of all organisations."""
return organisation_crud.get_organisations(db)


@router.get("/{org_id}", response_model=organisation_schemas.OrganisationOut)
async def get_organisation_detail(
organisation: DbOrganisation = Depends(org_exists),
db: Session = Depends(database.get_db),
):
"""Get a specific organisation by id or name."""
return organisation


@router.post("/", response_model=organisation_schemas.OrganisationOut)
async def create_organisation(
org: organisation_schemas.OrganisationIn = Depends(),
logo: UploadFile = File(None),
db: Session = Depends(database.get_db),
) -> organisation_schemas.OrganisationOut:
"""Create an organisation with the given details."""
return await organisation_crud.create_organisation(db, org, logo)


@router.patch("/{org_id}/", response_model=organisation_schemas.OrganisationOut)
async def update_organisation(
new_values: organisation_schemas.OrganisationEdit = Depends(),
logo: UploadFile = File(None),
organisation: DbOrganisation = Depends(org_exists),
db: Session = Depends(database.get_db),
):
"""Partial update for an existing organisation."""
return await organisation_crud.update_organisation(
db, organisation, new_values, logo
)


@router.delete("/{org_id}")
async def delete_organisations(
organisation: DbOrganisation = Depends(org_exists),
db: Session = Depends(database.get_db),
):
"""Delete an organisation."""
return await organisation_crud.delete_organisation(db, organisation)
Loading

0 comments on commit ed95c47

Please sign in to comment.