Skip to content

Commit 7b2b279

Browse files
authored
Merge pull request #222 from RieCo432/dev
Closes #192
2 parents 95ede50 + dba5c82 commit 7b2b279

27 files changed

+709
-11
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""#192 store closing date instead of closed boolean
2+
3+
Revision ID: 29519339abb4
4+
Revises: b6e19fd0aee6
5+
Create Date: 2025-08-31 13:48:35.120095
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = '29519339abb4'
16+
down_revision: Union[str, None] = 'b6e19fd0aee6'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
# ### commands auto generated by Alembic - please adjust! ###
23+
op.add_column('crimereports', sa.Column('closedon', sa.DateTime(), nullable=True))
24+
op.drop_column('crimereports', 'closed')
25+
# ### end Alembic commands ###
26+
27+
28+
def downgrade() -> None:
29+
# ### commands auto generated by Alembic - please adjust! ###
30+
op.add_column('crimereports', sa.Column('closed', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
31+
op.drop_column('crimereports', 'closedon')
32+
# ### end Alembic commands ###
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""#192 added crimereports table
2+
3+
Revision ID: b6e19fd0aee6
4+
Revises: 2818f7b0c831
5+
Create Date: 2025-08-31 13:03:01.657852
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = 'b6e19fd0aee6'
16+
down_revision: Union[str, None] = '2818f7b0c831'
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
# ### commands auto generated by Alembic - please adjust! ###
23+
op.create_table('crimereports',
24+
sa.Column('id', sa.UUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False),
25+
sa.Column('crimenumber', sa.String(length=40), nullable=False),
26+
sa.Column('contractid', sa.UUID(), nullable=False),
27+
sa.Column('createdon', sa.DateTime(), server_default=sa.text("(current_timestamp at time zone 'utc')"), nullable=False),
28+
sa.Column('closed', sa.Boolean(), server_default=sa.text('FALSE'), nullable=False),
29+
sa.ForeignKeyConstraint(['contractid'], ['contracts.id'], ),
30+
sa.PrimaryKeyConstraint('id')
31+
)
32+
op.create_index(op.f('ix_crimereports_id'), 'crimereports', ['id'], unique=False)
33+
# ### end Alembic commands ###
34+
35+
36+
def downgrade() -> None:
37+
# ### commands auto generated by Alembic - please adjust! ###
38+
op.drop_index(op.f('ix_crimereports_id'), table_name='crimereports')
39+
op.drop_table('crimereports')
40+
# ### end Alembic commands ###

fastapi/app/crud/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
from .expenses import *
1414
from .permissions import *
1515
from .groups import *
16+
from .crimeReports import *

fastapi/app/crud/crimeReports.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import datetime
2+
from typing import List
3+
from uuid import UUID
4+
5+
from sqlalchemy.orm import Session
6+
7+
import app.models as models
8+
import app.schemas as schemas
9+
from sqlalchemy import select, func
10+
11+
12+
def get_crime_report(crime_report_id: UUID, db: Session) -> models.CrimeReport:
13+
return db.scalar(
14+
select(models.CrimeReport)
15+
.where(models.CrimeReport.id == crime_report_id)
16+
)
17+
18+
19+
def get_crime_reports(db: Session, crime_report_id: UUID | None = None, crime_number: str | None = None, contract_id: UUID | None = None) -> List[models.CrimeReport]:
20+
query = select(models.CrimeReport)
21+
22+
if crime_report_id is not None:
23+
query = query.where(models.CrimeReport.id == crime_report_id)
24+
25+
if crime_number is not None:
26+
query = query.where(models.CrimeReport.crimeNumber == crime_number.lower().replace(" ", "").replace("-", ""))
27+
28+
if contract_id is not None:
29+
query = query.where(models.CrimeReport.contractId == contract_id)
30+
31+
return [_ for _ in db.scalars(query)]
32+
33+
34+
def create_crime_report(crime_report_data: schemas.CrimeReport, db: Session) -> models.CrimeReport:
35+
crime_report = models.CrimeReport(
36+
contractId=crime_report_data.contractId,
37+
createdOn=datetime.datetime.utcnow(),
38+
crimeNumber=crime_report_data.crimeNumber.lower().replace(" ", "").replace("-", "")
39+
)
40+
db.add(crime_report)
41+
db.commit()
42+
return crime_report
43+
44+
45+
def close_crime_report(crime_report_id: UUID, db: Session) -> models.CrimeReport:
46+
crime_report = get_crime_report(crime_report_id=crime_report_id, db=db)
47+
crime_report.closedOn = datetime.datetime.utcnow()
48+
db.commit()
49+
return crime_report
50+
51+
52+
def find_crime_reports(db: Session, crime_number: str, max_distance: int = 4) -> List[models.CrimeReport]:
53+
return [_ for _ in db.scalars(
54+
select(models.CrimeReport)
55+
.where(
56+
(models.CrimeReport.crimeNumber.contains(crime_number))
57+
| (func.levenshtein(models.CrimeReport.crimeNumber, crime_number) <= max_distance)
58+
)
59+
)]

fastapi/app/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
app.include_router(routers.expenses)
4848
app.include_router(routers.groups)
4949

50+
app.include_router(routers.crime_reports)
51+
5052

5153

5254
if os.environ["PRODUCTION"] == "true":

fastapi/app/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@
1515
from .groupUser import *
1616
from .groupPermission import *
1717
from .contractDraft import *
18+
from .crimeReports import *

fastapi/app/models/contract.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import app.services as services
1010
from app.database.db import Base
1111

12-
from typing import Self
12+
from typing import Self, List
1313

1414
CONTRACT_EXPIRE_MONTHS = int(os.environ['CONTRACT_EXPIRE_MONTHS'])
1515

@@ -57,6 +57,8 @@ class Contract(Base):
5757
expiryReminderSent: Mapped[bool] = mapped_column("expiryremindersent", Boolean, nullable=False, default=False, server_default=text("FALSE"), quote=False)
5858
returnDetailsSent: Mapped[bool] = mapped_column("returndetailssent", Boolean, nullable=False, default=False, server_default=text("FALSE"), quote=False)
5959

60+
crimeReports: Mapped[List["CrimeReport"]] = relationship("CrimeReport", back_populates="contract")
61+
6062
def __eq__dict(self, other: dict):
6163
return all([
6264
str(self.id) == str(other.get("id")),

fastapi/app/models/crimeReports.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import List
2+
from uuid import uuid4
3+
4+
from datetime import datetime
5+
from sqlalchemy import String, UUID, text, DateTime, ForeignKey, Boolean
6+
from sqlalchemy.orm import Mapped, mapped_column, relationship
7+
8+
from app import services
9+
from app.database.db import Base
10+
11+
12+
class CrimeReport(Base):
13+
__tablename__ = "crimereports"
14+
15+
id: Mapped[UUID] = mapped_column("id", UUID, primary_key=True, nullable=False, default=uuid4, server_default=text("uuid_generate_v4()"), index=True, quote=False)
16+
crimeNumber: Mapped[str] = mapped_column("crimenumber", String(40), nullable=False, quote=False)
17+
contractId: Mapped[UUID] = mapped_column("contractid", ForeignKey("contracts.id"), nullable=False, quote=False)
18+
contract: Mapped["Contract"] = relationship("Contract", back_populates="crimeReports")
19+
createdOn: Mapped[datetime] = mapped_column("createdon", DateTime, nullable=False, default=datetime.utcnow(), server_default=text("(current_timestamp at time zone 'utc')"), quote=False)
20+
closedOn: Mapped[datetime] = mapped_column("closedon", DateTime, nullable=True, quote=False)
21+
22+
23+
def __eq__(self, other: dict):
24+
return all([
25+
str(self.id) == str(other.get("id", None)),
26+
str(self.contractId) == str(other.get("contractId", None)),
27+
str(self.crimeNumber) == str(other.get("crimeNumber", None)),
28+
str(self.createdOn) == str(other.get("createdOn", None))
29+
])
30+
31+
def send_crime_report_added_email(self):
32+
email_html_content = services.email_helpers.build_crime_report_added_email(crime_report=self)
33+
services.email_helpers.send_email(
34+
destination=self.contract.client.emailAddress,
35+
subject="Your Stolen Bike",
36+
content=email_html_content)
37+
38+
def send_crime_report_closed_email(self):
39+
email_html_content = services.email_helpers.build_crime_report_closed_email(crime_report=self)
40+
services.email_helpers.send_email(
41+
destination=self.contract.client.emailAddress,
42+
subject="Your Stolen Bike",
43+
content=email_html_content
44+
)

fastapi/app/routers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@
1717
from .clientsPublic import public_clients
1818
from .appointmentsPublic import appointments_public
1919
from .usersPublic import users_public
20+
from .crimeReports import crime_reports
2021

fastapi/app/routers/clientsMe.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,16 @@ async def get_my_contract(
6868
depositAmountCollected=contract.depositAmountCollected,
6969
conditionOfBike=contract.conditionOfBike,
7070
contractType=contract.contractType,
71-
notes=contract.notes
71+
notes=contract.notes,
72+
crimeReports=[
73+
schemas.CrimeReport(
74+
id=report.id,
75+
crimeNumber=report.crimeNumber,
76+
createdOn=report.createdOn,
77+
closedOn=report.closedOn,
78+
contractId=report.contractId
79+
) for report in contract.crimeReports
80+
]
7281
)
7382

7483

0 commit comments

Comments
 (0)