Skip to content

Commit

Permalink
Pdct 312 Added endpoint for creating events (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
katybaulch authored Oct 18, 2023
1 parent cd8d332 commit 740548d
Show file tree
Hide file tree
Showing 15 changed files with 406 additions and 21 deletions.
29 changes: 28 additions & 1 deletion app/api/api_v1/routers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import app.service.event as event_service
from app.errors import RepositoryError, ValidationError
from app.model.event import EventReadDTO
from app.model.event import EventCreateDTO, EventReadDTO

event_router = r = APIRouter()

Expand Down Expand Up @@ -89,3 +89,30 @@ async def get_event(import_id: str) -> EventReadDTO:
)

return event


@r.post("/events", response_model=str, status_code=status.HTTP_201_CREATED)
async def create_document(
new_event: EventCreateDTO,
) -> str:
"""
Creates a specific event given the values in EventCreateDTO.
:raises HTTPException: If a validation error occurs, a 400 is
returned.
:raises HTTPException: If an SQL alchemy database error occurs, a
503 is returned.
:return str: returns a the import_id of the event created.
"""
try:
return event_service.create(new_event)

except ValidationError as e:
_LOGGER.error(e.message)
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=e.message)

except RepositoryError as e:
_LOGGER.error(e.message)
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=e.message
)
2 changes: 2 additions & 0 deletions app/model/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class DocumentCreateDTO(BaseModel):
variant_name: str
role: str
type: str

# From PhysicalDocument
title: str
source_url: str
user_language_name: str
Expand Down
30 changes: 28 additions & 2 deletions app/model/event.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
from datetime import datetime
from pydantic import BaseModel
from typing import Optional
from pydantic import BaseModel

from app.clients.db.models.law_policy.family import (
EventStatus,
)


class EventReadDTO(BaseModel):
"""JSON Representation of a Event for reading."""
"""
JSON Representation of the DTO for reading an event.
TODO: Add family document fields here including family title,
family document title, maybe geography etc.?
"""

# From FamilyEvent
import_id: str
event_title: str
date: datetime
event_type_value: str
event_status: EventStatus

# From FamilyDocument
family_import_id: str
family_document_import_id: Optional[str] = None


class EventCreateDTO(BaseModel):
"""
JSON Representation of the DTO for creating an event.
We don't need to specify the import_id because this will be auto-
generated.
"""

# From FamilyEvent
event_title: str
date: datetime
event_type_value: str
event_status: EventStatus

# From FamilyDocument
family_import_id: str
family_document_import_id: Optional[str] = None
68 changes: 61 additions & 7 deletions app/repository/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@
from datetime import datetime
from typing import Optional, Tuple, cast

from sqlalchemy import or_
from sqlalchemy import or_, Column
from sqlalchemy.orm import Query, Session
from sqlalchemy.exc import NoResultFound
from sqlalchemy_utils import escape_like

from app.clients.db.models.app.counters import CountedEntity
from app.clients.db.models.law_policy import (
EventStatus,
FamilyEvent,
Family,
FamilyDocument,
)
from app.model.event import EventReadDTO
from app.errors import ValidationError
from app.model.event import EventCreateDTO, EventReadDTO
from app.repository import family as family_repo
from app.repository.helpers import generate_import_id


_LOGGER = logging.getLogger(__name__)
Expand All @@ -38,9 +42,8 @@ def _get_query(db: Session) -> Query:
)


def _event_to_dto(db: Session, family_event_meta: FamilyEventTuple) -> EventReadDTO:
def _event_to_dto(family_event_meta: FamilyEventTuple) -> EventReadDTO:
family_event = family_event_meta[0]

family_document_import_id = (
None
if family_event.family_document_import_id is None
Expand All @@ -58,6 +61,22 @@ def _event_to_dto(db: Session, family_event_meta: FamilyEventTuple) -> EventRead
)


def _dto_to_event_dict(dto: EventCreateDTO) -> dict:
return {
"family_import_id": dto.family_import_id,
"family_document_import_id": dto.family_document_import_id,
"date": dto.date,
"title": dto.event_title,
"event_type_name": dto.event_type_value,
"status": EventStatus.OK,
}


def _event_from_dto(db: Session, dto: EventCreateDTO):
family_event = FamilyEvent(**_dto_to_event_dict(dto))
return family_event


def all(db: Session) -> list[EventReadDTO]:
"""
Returns all family events.
Expand All @@ -70,7 +89,7 @@ def all(db: Session) -> list[EventReadDTO]:
if not family_event_metas:
return []

result = [_event_to_dto(db, event_meta) for event_meta in family_event_metas]
result = [_event_to_dto(event_meta) for event_meta in family_event_metas]
return result


Expand All @@ -90,7 +109,7 @@ def get(db: Session, import_id: str) -> Optional[EventReadDTO]:
_LOGGER.error(e)
return

return _event_to_dto(db, family_event_meta)
return _event_to_dto(family_event_meta)


def search(db: Session, search_term: str) -> Optional[list[EventReadDTO]]:
Expand All @@ -115,7 +134,42 @@ def search(db: Session, search_term: str) -> Optional[list[EventReadDTO]]:
if not found:
return []

return [_event_to_dto(db, f) for f in found]
return [_event_to_dto(f) for f in found]


def create(db: Session, event: EventCreateDTO) -> str:
"""
Creates a new family event.
:param db Session: The database connection.
:param EventCreateDTO event: The values for the new event.
:return str: The import id of the newly created family event.
"""

try:
new_family_event = _event_from_dto(db, event)

family_import_id = new_family_event.family_import_id

# Generate the import_id for the new event
org = family_repo.get_organisation(db, cast(str, family_import_id))
if org is None:
raise ValidationError(
f"Cannot find counter to generate id for {family_import_id}"
)

org_name = cast(str, org.name)
new_family_event.import_id = cast(
Column, generate_import_id(db, CountedEntity.Event, org_name)
)

db.add(new_family_event)
db.flush()
except Exception:
_LOGGER.exception("Error trying to create Event")
raise

return cast(str, new_family_event.import_id)


def count(db: Session) -> Optional[int]:
Expand Down
29 changes: 27 additions & 2 deletions app/service/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

from pydantic import ConfigDict, validate_call
from sqlalchemy import exc
from sqlalchemy.orm import Session

import app.clients.db.session as db_session
import app.repository.event as event_repo
from app.errors import RepositoryError
from app.model.event import EventReadDTO
import app.service.family as family_service
from app.errors import RepositoryError, ValidationError
from app.model.event import EventCreateDTO, EventReadDTO
from app.service import id


Expand Down Expand Up @@ -73,6 +75,29 @@ def validate_import_id(import_id: str) -> None:
id.validate(import_id)


@db_session.with_transaction(__name__)
@validate_call(config=ConfigDict(arbitrary_types_allowed=True))
def create(event: EventCreateDTO, db: Session = db_session.get_db()) -> str:
"""
Creates a new document with the values passed.
:param documentDTO document: The values for the new document.
:raises RepositoryError: raised on a database error
:raises ValidationError: raised should the import_id be invalid.
:return Optional[documentDTO]: The new created document or
None if unsuccessful.
"""
id.validate(event.family_import_id)

family = family_service.get(event.family_import_id)
if family is None:
raise ValidationError(
f"Could not find family when creating event for {event.family_import_id}"
)

return event_repo.create(db, event)


@validate_call(config=ConfigDict(arbitrary_types_allowed=True))
def count() -> Optional[int]:
"""
Expand Down
8 changes: 8 additions & 0 deletions integration_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
mock_rollback_document_repo,
)
from integration_tests.mocks.rollback_family_repo import mock_rollback_family_repo
from integration_tests.mocks.rollback_event_repo import mock_rollback_event_repo


def get_test_db_url() -> str:
Expand Down Expand Up @@ -160,6 +161,13 @@ def rollback_document_repo(monkeypatch, mocker):
yield document_repo


@pytest.fixture
def rollback_event_repo(monkeypatch, mocker):
"""Mocks the repository for a single test."""
mock_rollback_event_repo(event_repo, monkeypatch, mocker)
yield event_repo


@pytest.fixture
def superuser_header_token() -> Dict[str, str]:
a_token = token_service.encode("[email protected]", True, {})
Expand Down
Loading

0 comments on commit 740548d

Please sign in to comment.