Skip to content

Commit

Permalink
Standardize model __repr__ functions (#1140)
Browse files Browse the repository at this point in the history
## Description of Changes
Addressed inconsistencies with the model `__repr__` functions.

Mostly making this because this PR got a bit out of control:
#1138

## Tests and Linting
- [x] This branch is up-to-date with the `develop` branch.
- [x] `pytest` passes on my local development environment.
- [x] `pre-commit` passes on my local development environment.
  • Loading branch information
michplunkett authored Jan 2, 2025
1 parent 1081bc1 commit a6c54bc
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 34 deletions.
4 changes: 0 additions & 4 deletions OpenOversight/app/main/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import os
import re
import sys
from datetime import datetime
Expand Down Expand Up @@ -124,9 +123,6 @@
)


# Ensure the file is read/write by the creator only
SAVED_UMASK = os.umask(0o077)

sitemap_endpoints = []


Expand Down
45 changes: 30 additions & 15 deletions OpenOversight/app/models/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class Department(BaseModel, TrackUpdates):
__table_args__ = (UniqueConstraint("name", "state", name="departments_name_state"),)

def __repr__(self):
return f"<Department ID {self.id}: {self.name} {self.state}>"
return f"<Department ID: {self.id} : {self.name} : {self.state}>"

def to_custom_dict(self):
return {
Expand Down Expand Up @@ -198,7 +198,7 @@ class Job(BaseModel, TrackUpdates):
)

def __repr__(self):
return f"<Job ID {self.id}: {self.job_title}>"
return f"<Job ID: {self.id} : {self.job_title}>"

def __str__(self):
return self.job_title
Expand All @@ -212,6 +212,9 @@ class Note(BaseModel, TrackUpdates):
officer_id = db.Column(db.Integer, db.ForeignKey("officers.id", ondelete="CASCADE"))
officer = db.relationship("Officer", back_populates="notes")

def __repr__(self):
return f"<Note ID: {self.id} : {self.text_contents}>"


class Description(BaseModel, TrackUpdates):
__tablename__ = "descriptions"
Expand All @@ -221,6 +224,9 @@ class Description(BaseModel, TrackUpdates):
text_contents = db.Column(db.Text())
officer_id = db.Column(db.Integer, db.ForeignKey("officers.id", ondelete="CASCADE"))

def __repr__(self):
return f"<Description ID: {self.id} : {self.text_contents}>"


class Officer(BaseModel, TrackUpdates):
__tablename__ = "officers"
Expand Down Expand Up @@ -279,6 +285,14 @@ class Officer(BaseModel, TrackUpdates):
CheckConstraint("gender in ('M', 'F', 'Other')", name="gender_options"),
)

def __repr__(self):
if self.unique_internal_identifier:
return (
f"<Officer ID: {self.id} : {self.full_name()} "
f"({self.unique_internal_identifier})>"
)
return f"<Officer ID: {self.id} : {self.full_name()}>"

def full_name(self):
if self.middle_initial:
middle_initial = (
Expand Down Expand Up @@ -339,14 +353,6 @@ def currently_on_force(self):
return "Yes" if most_recent.resign_date is None else "No"
return "Uncertain"

def __repr__(self):
if self.unique_internal_identifier:
return (
f"<Officer ID {self.id}: {self.full_name()} "
f"({self.unique_internal_identifier})>"
)
return f"<Officer ID {self.id}: {self.full_name()}>"


class Salary(BaseModel, TrackUpdates):
__tablename__ = "salaries"
Expand All @@ -365,7 +371,7 @@ class Salary(BaseModel, TrackUpdates):
is_fiscal_year = db.Column(db.Boolean, index=False, unique=False, nullable=False)

def __repr__(self):
return f"<Salary: ID {self.officer_id} : {self.salary}"
return f"<Salary ID: {self.officer_id} : {self.salary}>"

@property
def total_pay(self) -> float:
Expand Down Expand Up @@ -406,7 +412,7 @@ class Assignment(BaseModel, TrackUpdates):
resign_date = db.Column(db.Date, index=True, unique=False, nullable=True)

def __repr__(self):
return f"<Assignment: ID {self.officer_id} : {self.star_no}>"
return f"<Assignment ID: {self.officer_id} : {self.star_no}>"

@property
def start_date_or_min(self):
Expand All @@ -433,7 +439,7 @@ class Unit(BaseModel, TrackUpdates):
)

def __repr__(self):
return f"Unit: {self.description}"
return f"<Unit ID: {self.id} : {self.description}>"


class Face(BaseModel, TrackUpdates):
Expand Down Expand Up @@ -485,7 +491,7 @@ class Face(BaseModel, TrackUpdates):
__table_args__ = (UniqueConstraint("officer_id", "img_id", name="unique_faces"),)

def __repr__(self):
return f"<Tag ID {self.id}: {self.officer_id} - {self.img_id}>"
return f"<Tag ID: {self.id} : {self.officer_id} : {self.img_id}>"


class Image(BaseModel, TrackUpdates):
Expand All @@ -512,7 +518,7 @@ class Image(BaseModel, TrackUpdates):
)

def __repr__(self):
return f"<Image ID {self.id}: {self.filepath}>"
return f"<Image ID: {self.id} : {self.filepath}>"


incident_links = db.Table(
Expand Down Expand Up @@ -644,6 +650,9 @@ class LicensePlate(BaseModel, TrackUpdates):
def validate_state(self, key, state):
return state_validator(state)

def __repr__(self):
return f"<LicensePlate ID: {self.id} : {self.state} : {self.number}>"


class Link(BaseModel, TrackUpdates):
__tablename__ = "links"
Expand All @@ -660,6 +669,9 @@ class Link(BaseModel, TrackUpdates):
def validate_url(self, key, url):
return url_validator(url)

def __repr__(self):
return f"<Link ID: {self.id} : {self.title}>"


class Incident(BaseModel, TrackUpdates):
__tablename__ = "incidents"
Expand Down Expand Up @@ -704,6 +716,9 @@ class Incident(BaseModel, TrackUpdates):
"Department", backref=db.backref("incidents", cascade_backrefs=False), lazy=True
)

def __repr__(self):
return f"<Incident ID: {self.id} : {self.report_number}>"


class User(UserMixin, BaseModel):
__tablename__ = "users"
Expand Down
4 changes: 0 additions & 4 deletions OpenOversight/app/utils/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import os


# Cache Key Constants
KEY_DEPT_ALL_ASSIGNMENTS = "all_department_assignments"
KEY_DEPT_ALL_INCIDENTS = "all_department_incidents"
Expand Down Expand Up @@ -44,7 +41,6 @@
ENCODING_UTF_8 = "utf-8"
FILE_TYPE_HTML = "html"
FILE_TYPE_PLAIN = "plain"
SAVED_UMASK = os.umask(0o077) # Ensure the file is read/write by the creator only

# File Name Constants
SERVICE_ACCOUNT_FILE = "service_account_key.json"
Expand Down
53 changes: 43 additions & 10 deletions OpenOversight/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
from OpenOversight.app.models.database import (
Assignment,
Department,
Description,
Face,
Image,
Incident,
Job,
LicensePlate,
Link,
Location,
Note,
Officer,
Salary,
Unit,
Expand All @@ -30,7 +32,7 @@ def test_department_repr(mockdata):
department = Department.query.first()
assert (
repr(department)
== f"<Department ID {department.id}: {department.name} {department.state}>"
== f"<Department ID: {department.id} : {department.name} : {department.state}>"
)


Expand Down Expand Up @@ -93,7 +95,7 @@ def test_officer_repr(session):
).first()

assert (
repr(officer_uii) == f"<Officer ID {officer_uii.id}: "
repr(officer_uii) == f"<Officer ID: {officer_uii.id} : "
f"{officer_uii.first_name} {officer_uii.middle_initial}. {officer_uii.last_name} "
f"({officer_uii.unique_internal_identifier})>"
)
Expand All @@ -107,7 +109,7 @@ def test_officer_repr(session):
).first()

assert (
repr(officer_no_uii) == f"<Officer ID {officer_no_uii.id}: "
repr(officer_no_uii) == f"<Officer ID: {officer_no_uii.id} : "
f"{officer_no_uii.first_name} {officer_no_uii.middle_initial}. "
f"{officer_no_uii.last_name} {officer_no_uii.suffix}>"
)
Expand All @@ -118,7 +120,7 @@ def test_officer_repr(session):

assert (
repr(officer_no_mi)
== f"<Officer ID {officer_no_mi.id}: {officer_no_mi.first_name} "
== f"<Officer ID: {officer_no_mi.id} : {officer_no_mi.first_name} "
f"{officer_no_mi.last_name} {officer_no_mi.suffix} "
f"({officer_no_mi.unique_internal_identifier})>"
)
Expand All @@ -137,28 +139,33 @@ def test_assignment_repr(mockdata):
assignment = Assignment.query.first()
assert (
repr(assignment)
== f"<Assignment: ID {assignment.base_officer.id} : {assignment.star_no}>"
== f"<Assignment ID: {assignment.base_officer.id} : {assignment.star_no}>"
)


def test_incident_repr(mockdata):
incident = Incident.query.first()
assert repr(incident) == f"<Incident ID: {incident.id} : {incident.report_number}>"


def test_job_repr(mockdata):
job = Job.query.first()
assert repr(job) == f"<Job ID {job.id}: {job.job_title}>"
assert repr(job) == f"<Job ID: {job.id} : {job.job_title}>"


def test_image_repr(mockdata):
image = Image.query.first()
assert repr(image) == f"<Image ID {image.id}: {image.filepath}>"
assert repr(image) == f"<Image ID: {image.id} : {image.filepath}>"


def test_face_repr(mockdata):
face = Face.query.first()
assert repr(face) == f"<Tag ID {face.id}: {face.officer_id} - {face.img_id}>"
assert repr(face) == f"<Tag ID: {face.id} : {face.officer_id} : {face.img_id}>"


def test_unit_repr(mockdata):
unit = Unit.query.first()
assert repr(unit) == f"Unit: {unit.description}"
assert repr(unit) == f"<Unit ID: {unit.id} : {unit.description}>"


def test_user_repr(mockdata):
Expand All @@ -168,7 +175,33 @@ def test_user_repr(mockdata):

def test_salary_repr(mockdata):
salary = Salary.query.first()
assert repr(salary) == f"<Salary: ID {salary.officer_id} : {salary.salary}"
assert repr(salary) == f"<Salary ID: {salary.officer_id} : {salary.salary}>"


def test_link_repr(mockdata):
link = Link.query.first()
assert repr(link) == f"<Link ID: {link.id} : {link.title}>"


def test_note_repr(mockdata):
note = Note.query.first()
assert repr(note) == f"<Note ID: {note.id} : {note.text_contents}>"


def test_description_repr(mockdata):
description = Description.query.first()
assert (
repr(description)
== f"<Description ID: {description.id} : {description.text_contents}>"
)


def test_license_plate_repr(mockdata):
license_plate = LicensePlate.query.first()
assert (
repr(license_plate)
== f"<LicensePlate ID: {license_plate.id} : {license_plate.state} : {license_plate.number}>"
)


def test_password_not_printed(mockdata):
Expand Down
2 changes: 1 addition & 1 deletion OpenOversight/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def test_user_cannot_submit_invalid_file_extension(mockdata):

def test_unit_choices(mockdata):
unit_choices_result = [str(x) for x in unit_choices()]
assert "Unit: Bureau of Organized Crime" in unit_choices_result
assert "<Unit ID: 4 : Bureau of Organized Crime>" in unit_choices_result


@upload_s3_patch
Expand Down

0 comments on commit a6c54bc

Please sign in to comment.