Skip to content

Commit

Permalink
Merge pull request #398 from RyanPark44/387-feature-convert-the-unit-…
Browse files Browse the repository at this point in the history
…property-into-a-db-model

 [FEAT] Convert the Unit property into a db Unit model
  • Loading branch information
RyanPark44 authored Jun 12, 2024
2 parents b98ed8d + 8ad9a90 commit a743c23
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 8 deletions.
1 change: 1 addition & 0 deletions backend/database/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# Alembic only does a couple models, not all of them.

from .models.agency import *
from .models.unit import *
from .models.attorney import *
from .models.case_document import *
from .models.incident import *
Expand Down
4 changes: 3 additions & 1 deletion backend/database/models/agency.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ..core import db, CrudMixin
from ..core import CrudMixin, db
from enum import Enum
from sqlalchemy.ext.associationproxy import association_proxy

Expand All @@ -22,6 +22,8 @@ class Agency(db.Model, CrudMixin):
jurisdiction = db.Column(db.Enum(Jurisdiction))
# total_officers = db.Column(db.Integer)

units = db.relationship("Unit", back_populates="agency")

officer_association = db.relationship("Employment", back_populates="agency")
officers = association_proxy("officer_association", "officer")

Expand Down
8 changes: 3 additions & 5 deletions backend/database/models/employment.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ class Employment(db.Model, CrudMixin):
id = db.Column(db.Integer, primary_key=True)
officer_id = db.Column(db.Integer, db.ForeignKey("officer.id"))
agency_id = db.Column(db.Integer, db.ForeignKey("agency.id"))
unit_id = db.Column(db.Integer, db.ForeignKey("unit.id"))
earliest_employment = db.Column(db.Text)
latest_employment = db.Column(db.Text)
badge_number = db.Column(db.Text)
unit = db.Column(db.Text)
highest_rank = db.Column(db.Enum(Rank))
currently_employed = db.Column(db.Boolean)

officer = db.relationship("Officer", back_populates="agency_association")
agency = db.relationship("Agency", back_populates="officer_association")
unit = db.relationship("Unit", back_populates="officer_association")

def __repr__(self):
return f"<Employment {self.id}>"
Expand Down Expand Up @@ -65,7 +66,6 @@ def get_highest_rank(records: list[Employment]):

def merge_employment_records(
records: list[Employment],
unit: str = None,
currently_employed: bool = None
):
"""
Expand All @@ -85,17 +85,15 @@ def merge_employment_records(
"""
earliest_employment, latest_employment = get_employment_range(records)
highest_rank = get_highest_rank(records)
if unit is None:
unit = records[0].unit
if currently_employed is None:
currently_employed = records[0].currently_employed
return Employment(
officer_id=records[0].officer_id,
agency_id=records[0].agency_id,
unit_id=records[0].unit_id,
badge_number=records[0].badge_number,
earliest_employment=earliest_employment,
latest_employment=latest_employment,
unit=unit,
highest_rank=highest_rank,
currently_employed=currently_employed,
)
25 changes: 25 additions & 0 deletions backend/database/models/unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from ..core import CrudMixin, db
from sqlalchemy.ext.associationproxy import association_proxy


class Unit(db.Model, CrudMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text)
website_url = db.Column(db.Text)
phone = db.Column(db.Text)
email = db.Column(db.Text)
description = db.Column(db.Text)
address = db.Column(db.Text)
zip = db.Column(db.Text)
agency_url = db.Column(db.Text)
officers_url = db.Column(db.Text)

commander_id = db.Column(db.Integer, db.ForeignKey('officer.id'))
agency_id = db.Column(db.Integer, db.ForeignKey('agency.id'))

agency = db.relationship("Agency", back_populates="units")
officer_association = db.relationship('Employment', back_populates='unit')
officers = association_proxy('officer_association', 'officer')

def __repr__(self):
return f"<Unit {self.name}>"
1 change: 0 additions & 1 deletion backend/routes/agencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ def add_officer_to_agency(agency_id: int):
employment.agency_id = agency_id
employment = merge_employment_records(
employments.all() + [employment],
unit=record.unit,
currently_employed=record.currently_employed
)

Expand Down
1 change: 0 additions & 1 deletion backend/routes/officers.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ def update_employment(officer_id: int):
employment.officer_id = officer_id
employment = merge_employment_records(
employments.all() + [employment],
unit=record.unit,
currently_employed=record.currently_employed
)

Expand Down
51 changes: 51 additions & 0 deletions backend/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .database.models.partner import Partner, PartnerMember, MemberRole
from .database.models.incident import Incident, SourceDetails
from .database.models.agency import Agency, Jurisdiction
from .database.models.unit import Unit
from .database.models.officer import Officer, StateID
from .database.models.employment import Employment
from .database.models.accusation import Accusation
Expand Down Expand Up @@ -122,6 +123,13 @@ def validate(auth=True, **kwargs):
]

_agency_list_attributes = [
'units',
'officer_association',
'officers'
]

_unit_list_attributes = [
'agency',
'officer_association',
'officers'
]
Expand Down Expand Up @@ -179,6 +187,16 @@ def none_to_list(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values


class _UnitMixin(BaseModel):
@root_validator(pre=True)
def none_to_list(cls, values: Dict[str, Any]) -> Dict[str, Any]:
values = {**values} # convert mappings to base dict type.
for i in _unit_list_attributes:
if not values.get(i):
values[i] = []
return values


def schema_create(model_type: DeclarativeMeta, **kwargs) -> ModelMetaclass:
return sqlalchemy_to_pydantic(model_type, exclude="id", **kwargs)

Expand All @@ -187,6 +205,7 @@ def schema_create(model_type: DeclarativeMeta, **kwargs) -> ModelMetaclass:
_BaseCreateIncidentSchema = schema_create(Incident)
_BaseCreateOfficerSchema = schema_create(Officer)
_BaseCreateAgencySchema = schema_create(Agency)
_BaseCreateUnitSchema = schema_create(Unit)
CreateStateIDSchema = schema_create(StateID)
CreateEmploymentSchema = schema_create(Employment)
CreateAccusationSchema = schema_create(Accusation)
Expand Down Expand Up @@ -241,6 +260,20 @@ class CreateAgencySchema(_BaseCreateAgencySchema, _AgencyMixin):
hq_zip: Optional[str]


class CreateUnitSchema(_BaseCreateUnitSchema, _UnitMixin):
name: str
website_url: Optional[str]
phone: Optional[str]
email: Optional[str]
description: Optional[str]
address: Optional[str]
zip: Optional[str]
agency_url: Optional[str]
officers_url: Optional[str]
commander_id: int
agency_id: int


AddMemberSchema = sqlalchemy_to_pydantic(
PartnerMember, exclude=["id", "date_joined", "partner", "user"]
)
Expand All @@ -255,6 +288,7 @@ def schema_get(model_type: DeclarativeMeta, **kwargs) -> ModelMetaclass:
_BaseOfficerSchema = schema_get(Officer)
_BasePartnerMemberSchema = schema_get(PartnerMember)
_BaseAgencySchema = schema_get(Agency)
_BaseUnitSchema = schema_get(Unit)
VictimSchema = schema_get(Victim)
PerpetratorSchema = schema_get(Perpetrator)
TagSchema = schema_get(Tag)
Expand Down Expand Up @@ -293,6 +327,11 @@ class OfficerSchema(_BaseOfficerSchema, _OfficerMixin):


class AgencySchema(_BaseAgencySchema, _AgencyMixin):
units: List[CreateUnitSchema]
officer_association: List[CreateEmploymentSchema]


class UnitSchema(_BaseUnitSchema):
officer_association: List[CreateEmploymentSchema]


Expand Down Expand Up @@ -401,6 +440,18 @@ def agency_orm_to_json(agency: Agency) -> dict:
)


def unit_to_orm(unit: CreateUnitSchema) -> Unit:
"""Convert the JSON unit into an ORM instance"""
orm_attrs = unit.dict()
return Unit(**orm_attrs)


def unit_orm_to_json(unit: Unit) -> dict:
return UnitSchema.from_orm(unit).dict(
exclude_none=True,
)


def employment_to_orm(employment: CreateEmploymentSchema) -> Employment:
"""Convert the JSON employment into an ORM instance"""
orm_attrs = employment.dict()
Expand Down

0 comments on commit a743c23

Please sign in to comment.