Skip to content

Commit 99240d6

Browse files
Performance fix (#581) (#583)
* Requirements fix (#576) * requirements performance issue fix * commenting sqlalchemy connection pooling * Requirements fix (#578) * requirements performance issue fix * commenting sqlalchemy connection pooling * Requirements fix (#579) * requirements performance issue fix * commenting sqlalchemy connection pooling * Update ops.py * Requirements fix (#580) * requirements performance issue fix * commenting sqlalchemy connection pooling --------- Co-authored-by: saravanpa-aot <Saravan.Azhikoden@gov.bc.ca>
1 parent d405bb2 commit 99240d6

30 files changed

+1270
-201
lines changed

.github/workflows/chromatic.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ on:
66
push:
77
branches:
88
- develop
9+
paths:
10+
- "compliance-web/.storybook/stories/**"
911
# Only run on the main repository, not forks
1012
paths-ignore:
1113
- 'README.md'

compliance-api/.flake8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[flake8]
22
# E203 whitespace before ':' - Black intentionally breaks this rule
3-
extend-ignore = E203,Q000,W503
3+
# D202 No blank lines allowed after function docstring - Black conflicts with this
4+
extend-ignore = E203,Q000,W503,D202
45
max-line-length = 120
56

67
# Additional directories to exclude
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
"""charge recommendation changes
2+
3+
4+
Revision ID: e6d2d06d4b65
5+
Revises: 9b9dd4ac46fc
6+
Create Date: 2025-10-10 13:09:13.177037
7+
8+
"""
9+
from alembic import op
10+
import sqlalchemy as sa
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = 'e6d2d06d4b65'
15+
down_revision = '9b9dd4ac46fc'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
23+
# Create sentence type options table
24+
op.create_table('sentence_type_options',
25+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='The unique identifier of the option'),
26+
sa.Column('name', sa.String(), nullable=True, comment='The name of the option'),
27+
sa.Column('sort_order', sa.Integer(), nullable=True, comment='Order of priority. Mainly used order the options while listing'),
28+
sa.Column('created_date', sa.DateTime(), nullable=False),
29+
sa.Column('updated_date', sa.DateTime(), nullable=True),
30+
sa.Column('created_by', sa.String(length=100), nullable=False),
31+
sa.Column('updated_by', sa.String(length=100), nullable=True),
32+
sa.Column('is_active', sa.Boolean(), server_default='t', nullable=False),
33+
sa.Column('is_deleted', sa.Boolean(), server_default='f', nullable=False),
34+
sa.PrimaryKeyConstraint('id'),
35+
sa.UniqueConstraint('name')
36+
)
37+
38+
# Create sentence type options version table for audit tracking
39+
op.create_table('sentence_type_options_version',
40+
sa.Column('id', sa.Integer(), autoincrement=False, nullable=False, comment='The unique identifier of the option'),
41+
sa.Column('name', sa.String(), autoincrement=False, nullable=True, comment='The name of the option'),
42+
sa.Column('sort_order', sa.Integer(), autoincrement=False, nullable=True, comment='Order of priority. Mainly used order the options while listing'),
43+
sa.Column('created_date', sa.DateTime(), autoincrement=False, nullable=True),
44+
sa.Column('updated_date', sa.DateTime(), autoincrement=False, nullable=True),
45+
sa.Column('created_by', sa.String(length=100), autoincrement=False, nullable=True),
46+
sa.Column('updated_by', sa.String(length=100), autoincrement=False, nullable=True),
47+
sa.Column('is_active', sa.Boolean(), server_default='t', autoincrement=False, nullable=True),
48+
sa.Column('is_deleted', sa.Boolean(), server_default='f', autoincrement=False, nullable=True),
49+
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
50+
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
51+
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
52+
sa.Column('name_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
53+
sa.Column('sort_order_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
54+
sa.Column('created_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
55+
sa.Column('updated_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
56+
sa.Column('created_by_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
57+
sa.Column('updated_by_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
58+
sa.Column('is_active_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
59+
sa.Column('is_deleted_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
60+
sa.PrimaryKeyConstraint('id', 'transaction_id')
61+
)
62+
with op.batch_alter_table('sentence_type_options_version', schema=None) as batch_op:
63+
batch_op.create_index(batch_op.f('ix_sentence_type_options_version_end_transaction_id'), ['end_transaction_id'], unique=False)
64+
batch_op.create_index(batch_op.f('ix_sentence_type_options_version_operation_type'), ['operation_type'], unique=False)
65+
batch_op.create_index(batch_op.f('ix_sentence_type_options_version_transaction_id'), ['transaction_id'], unique=False)
66+
67+
# Insert sentence type options data
68+
sentence_types = [
69+
{'name': 'Fine', 'sort_order': 1},
70+
{'name': 'Creative Sentencing', 'sort_order': 2},
71+
{'name': 'Imprisonment', 'sort_order': 3},
72+
{'name': 'Discharge', 'sort_order': 4}
73+
]
74+
75+
for sentence_type in sentence_types:
76+
op.execute(f"""
77+
INSERT INTO sentence_type_options (name, sort_order, created_date, created_by, is_active, is_deleted)
78+
VALUES ('{sentence_type["name"]}', {sentence_type["sort_order"]}, now() at time zone 'utc', 'system', true, false)
79+
""")
80+
81+
# Create CR sentence type mappings table
82+
op.create_table('cr_sentence_type_mappings',
83+
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='The unique identifier'),
84+
sa.Column('charge_recommendation_id', sa.Integer(), nullable=False, comment='The charge recommendation ID'),
85+
sa.Column('sentence_type_option_id', sa.Integer(), nullable=False, comment='The sentence type option ID'),
86+
sa.Column('is_deleted', sa.Boolean(), server_default='f', nullable=False),
87+
sa.Column('created_date', sa.DateTime(), nullable=False),
88+
sa.Column('updated_date', sa.DateTime(), nullable=True),
89+
sa.Column('created_by', sa.String(length=100), nullable=False),
90+
sa.Column('updated_by', sa.String(length=100), nullable=True),
91+
sa.Column('is_active', sa.Boolean(), server_default='t', nullable=False),
92+
sa.ForeignKeyConstraint(['charge_recommendation_id'], ['charge_recommendations.id'], name='cr_sentence_type_mapping_cr_id_fkey'),
93+
sa.ForeignKeyConstraint(['sentence_type_option_id'], ['sentence_type_options.id'], name='cr_sentence_type_mapping_sentence_type_id_fkey'),
94+
sa.PrimaryKeyConstraint('id')
95+
)
96+
97+
# Create CR sentence type mappings version table for audit tracking
98+
op.create_table('cr_sentence_type_mappings_version',
99+
sa.Column('id', sa.Integer(), autoincrement=False, nullable=False, comment='The unique identifier'),
100+
sa.Column('charge_recommendation_id', sa.Integer(), autoincrement=False, nullable=True, comment='The charge recommendation ID'),
101+
sa.Column('sentence_type_option_id', sa.Integer(), autoincrement=False, nullable=True, comment='The sentence type option ID'),
102+
sa.Column('is_deleted', sa.Boolean(), server_default='f', autoincrement=False, nullable=True),
103+
sa.Column('created_date', sa.DateTime(), autoincrement=False, nullable=True),
104+
sa.Column('updated_date', sa.DateTime(), autoincrement=False, nullable=True),
105+
sa.Column('created_by', sa.String(length=100), autoincrement=False, nullable=True),
106+
sa.Column('updated_by', sa.String(length=100), autoincrement=False, nullable=True),
107+
sa.Column('is_active', sa.Boolean(), server_default='t', autoincrement=False, nullable=True),
108+
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
109+
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
110+
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
111+
sa.Column('charge_recommendation_id_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
112+
sa.Column('sentence_type_option_id_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
113+
sa.Column('is_deleted_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
114+
sa.Column('created_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
115+
sa.Column('updated_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
116+
sa.Column('created_by_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
117+
sa.Column('updated_by_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
118+
sa.Column('is_active_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
119+
sa.PrimaryKeyConstraint('id', 'transaction_id')
120+
)
121+
with op.batch_alter_table('cr_sentence_type_mappings_version', schema=None) as batch_op:
122+
batch_op.create_index(batch_op.f('ix_cr_sentence_type_mappings_version_end_transaction_id'), ['end_transaction_id'], unique=False)
123+
batch_op.create_index(batch_op.f('ix_cr_sentence_type_mappings_version_operation_type'), ['operation_type'], unique=False)
124+
batch_op.create_index(batch_op.f('ix_cr_sentence_type_mappings_version_transaction_id'), ['transaction_id'], unique=False)
125+
126+
# Create new court decision enum
127+
courtdecisionenum = sa.Enum('GUILTY', 'NOT_GUILTY', 'WITHDRAWN', name='courtdecisionenum', create_type=True)
128+
courtdecisionenum.create(op.get_bind())
129+
130+
# Update charge recommendations table
131+
with op.batch_alter_table('charge_recommendations', schema=None) as batch_op:
132+
batch_op.add_column(sa.Column('court_decision', sa.Enum('GUILTY', 'NOT_GUILTY', 'WITHDRAWN', name='courtdecisionenum'), nullable=True, comment='Court decision on the charge recommendation'))
133+
batch_op.add_column(sa.Column('court_decision_date', sa.DateTime(timezone=True), nullable=True, comment='Date when the court decision was made'))
134+
batch_op.drop_column('judgment_date')
135+
batch_op.drop_column('sentence_type')
136+
batch_op.drop_column('judgment')
137+
batch_op.drop_column('court_appearances')
138+
139+
# Update charge recommendations version table
140+
with op.batch_alter_table('charge_recommendations_version', schema=None) as batch_op:
141+
batch_op.add_column(sa.Column('court_decision', sa.Enum('GUILTY', 'NOT_GUILTY', 'WITHDRAWN', name='courtdecisionenum'), autoincrement=False, nullable=True, comment='Court decision on the charge recommendation'))
142+
batch_op.add_column(sa.Column('court_decision_date', sa.DateTime(timezone=True), autoincrement=False, nullable=True, comment='Date when the court decision was made'))
143+
batch_op.add_column(sa.Column('court_decision_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False))
144+
batch_op.add_column(sa.Column('court_decision_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False))
145+
batch_op.drop_column('sentence_type_mod')
146+
batch_op.drop_column('judgment_date')
147+
batch_op.drop_column('sentence_type')
148+
batch_op.drop_column('court_appearances')
149+
batch_op.drop_column('judgment_mod')
150+
batch_op.drop_column('court_appearances_mod')
151+
batch_op.drop_column('judgment')
152+
batch_op.drop_column('judgment_date_mod')
153+
154+
# Drop old judgment enum
155+
op.execute("DROP TYPE IF EXISTS judgmentenum CASCADE")
156+
157+
# ### end Alembic commands ###
158+
159+
160+
def downgrade():
161+
# ### commands auto generated by Alembic - please adjust! ###
162+
163+
# Recreate judgment enum
164+
judgmentenum = sa.Enum('GUILTY', 'NOT_GUILTY', name='judgmentenum', create_type=True)
165+
judgmentenum.create(op.get_bind())
166+
167+
# Update charge recommendations table
168+
with op.batch_alter_table('charge_recommendations', schema=None) as batch_op:
169+
batch_op.add_column(sa.Column('court_appearances', sa.TEXT(), autoincrement=False, nullable=True, comment='Court appearances details'))
170+
batch_op.add_column(sa.Column('judgment', postgresql.ENUM('GUILTY', 'NOT_GUILTY', name='judgmentenum'), autoincrement=False, nullable=True, comment='Judgment on the charge recommendation'))
171+
batch_op.add_column(sa.Column('sentence_type', sa.VARCHAR(), autoincrement=False, nullable=True, comment='Type of sentence'))
172+
batch_op.add_column(sa.Column('judgment_date', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True, comment='Date when the judgment was made'))
173+
batch_op.drop_column('court_decision_date')
174+
batch_op.drop_column('court_decision')
175+
176+
# Update charge recommendations version table
177+
with op.batch_alter_table('charge_recommendations_version', schema=None) as batch_op:
178+
batch_op.add_column(sa.Column('judgment_date_mod', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
179+
batch_op.add_column(sa.Column('judgment', postgresql.ENUM('GUILTY', 'NOT_GUILTY', name='judgmentenum'), autoincrement=False, nullable=True, comment='Judgment on the charge recommendation'))
180+
batch_op.add_column(sa.Column('court_appearances_mod', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
181+
batch_op.add_column(sa.Column('judgment_mod', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
182+
batch_op.add_column(sa.Column('court_appearances', sa.TEXT(), autoincrement=False, nullable=True, comment='Court appearances details'))
183+
batch_op.add_column(sa.Column('sentence_type', sa.VARCHAR(), autoincrement=False, nullable=True, comment='Type of sentence'))
184+
batch_op.add_column(sa.Column('judgment_date', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True, comment='Date when the judgment was made'))
185+
batch_op.add_column(sa.Column('sentence_type_mod', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False))
186+
batch_op.drop_column('court_decision_date_mod')
187+
batch_op.drop_column('court_decision_date')
188+
batch_op.drop_column('court_decision_mod')
189+
batch_op.drop_column('court_decision')
190+
191+
# Drop new tables and enums (drop version tables first, then main tables)
192+
with op.batch_alter_table('cr_sentence_type_mappings_version', schema=None) as batch_op:
193+
batch_op.drop_index(batch_op.f('ix_cr_sentence_type_mappings_version_transaction_id'))
194+
batch_op.drop_index(batch_op.f('ix_cr_sentence_type_mappings_version_operation_type'))
195+
batch_op.drop_index(batch_op.f('ix_cr_sentence_type_mappings_version_end_transaction_id'))
196+
op.drop_table('cr_sentence_type_mappings_version')
197+
198+
with op.batch_alter_table('sentence_type_options_version', schema=None) as batch_op:
199+
batch_op.drop_index(batch_op.f('ix_sentence_type_options_version_transaction_id'))
200+
batch_op.drop_index(batch_op.f('ix_sentence_type_options_version_operation_type'))
201+
batch_op.drop_index(batch_op.f('ix_sentence_type_options_version_end_transaction_id'))
202+
op.drop_table('sentence_type_options_version')
203+
204+
op.drop_table('cr_sentence_type_mappings')
205+
op.drop_table('sentence_type_options')
206+
op.execute("DROP TYPE IF EXISTS courtdecisionenum CASCADE")
207+
208+
# ### end Alembic commands ###

compliance-api/src/compliance_api/models/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
CaseFile, CaseFileInitiationEnum, CaseFileInitiationOption, CaseFileLink, CaseFileOfficer, CaseFileStatusEnum)
2323
from .charge_recommendation import (
2424
ChargeDecisionEnum, ChargeRecommendation, ChargeRecommendationInspectionRequirementMap,
25-
ChargeRecommendationStatusEnum, JudgmentEnum)
25+
ChargeRecommendationStatusEnum, CourtDecisionEnum)
2626
from .complaint import (
2727
Complaint, ComplaintReqOrderDetail, ComplaintRequirementSourceEnum, ComplaintResolution, ComplaintResolutionEnum,
2828
ComplaintSource, ComplaintSourceContact, ComplaintStatusEnum)
2929
from .compliance_finding import ComplianceFindingOption, ComplianceFindingOptionEnum
3030
from .continuation_report import ContinuationReport, ContinuationReportKey
31+
from .cr_sentence_type_mapping import CRSentenceTypeMapping
3132
from .db import db, ma, migrate
3233
from .department_detail import DepartmentDetail
3334
from .document_type import DocumentType
@@ -48,6 +49,7 @@
4849
from .requirement_source import RequirementSource, RequirementSourceEnum
4950
from .restorative_justice import RestorativeJustice, RestorativeJusticeInspectionRequirementMap
5051
from .section import Section
52+
from .sentence_type_option import SentenceTypeOption
5153
from .staff_user import StaffUser
5254
from .topic import Topic
5355
from .unapproved_project import UnapprovedProject

compliance-api/src/compliance_api/models/charge_recommendation.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from sqlalchemy import Boolean, Column, DateTime
66
from sqlalchemy import Enum as SqlEnum
7-
from sqlalchemy import ForeignKey, Index, Integer, String, Text, and_
7+
from sqlalchemy import ForeignKey, Index, Integer, String, and_
88
from sqlalchemy.orm import relationship
99
from sqlalchemy.sql import func
1010

@@ -31,11 +31,12 @@ class ChargeDecisionEnum(Enum):
3131
NOT_PROCEEDING = "Not Proceeding"
3232

3333

34-
class JudgmentEnum(Enum):
35-
"""Enum for Charge Recommendation Judgment."""
34+
class CourtDecisionEnum(Enum):
35+
"""Enum for Charge Recommendation Court Decision."""
3636

3737
GUILTY = "Guilty"
3838
NOT_GUILTY = "Not Guilty"
39+
WITHDRAWN = "Withdrawn"
3940

4041

4142
# Removed SentenceTypeEnum - sentence_type is now a text field
@@ -191,32 +192,21 @@ class ChargeRecommendation(BaseModelVersioned):
191192
nullable=True,
192193
comment="Court file number",
193194
)
194-
court_appearances = Column(
195-
Text,
195+
court_decision = Column(
196+
SqlEnum(CourtDecisionEnum),
196197
nullable=True,
197-
comment="Court appearances details",
198+
comment="Court decision on the charge recommendation",
198199
)
199-
judgment = Column(
200-
SqlEnum(JudgmentEnum),
201-
nullable=True,
202-
comment="Judgment on the charge recommendation",
203-
)
204-
judgment_date = Column(
200+
court_decision_date = Column(
205201
DateTime(timezone=True),
206202
nullable=True,
207-
comment="Date when the judgment was made",
203+
comment="Date when the court decision was made",
208204
)
209205
sentence_date = Column(
210206
DateTime(timezone=True),
211207
nullable=True,
212208
comment="Date when the sentence was given",
213209
)
214-
sentence_type = Column(
215-
String,
216-
nullable=True,
217-
comment="Type of sentence",
218-
)
219-
220210
# Relationships
221211
inspection = relationship("Inspection", foreign_keys=[inspection_id], lazy="joined")
222212
is_deleted = Column(Boolean, default=False, server_default="f", nullable=False)
@@ -231,6 +221,17 @@ class ChargeRecommendation(BaseModelVersioned):
231221
"ChargeRecommendationInspectionRequirementMap.is_deleted == False)"
232222
),
233223
)
224+
sentence_type_mappings = relationship(
225+
"CRSentenceTypeMapping",
226+
back_populates="charge_recommendation",
227+
lazy="select",
228+
primaryjoin=(
229+
"and_(CRSentenceTypeMapping.charge_recommendation_id == "
230+
"ChargeRecommendation.id, "
231+
"CRSentenceTypeMapping.is_active == True, "
232+
"CRSentenceTypeMapping.is_deleted == False)"
233+
),
234+
)
234235

235236
__table_args__ = (
236237
Index(

0 commit comments

Comments
 (0)