Skip to content

Commit 0044af2

Browse files
authored
[COMP-724] Add all sources to requirements grid (#648)
* COMP-724 add all sources to requirements grid * linting
1 parent 73216a0 commit 0044af2

File tree

4 files changed

+90
-11
lines changed

4 files changed

+90
-11
lines changed

compliance-api/src/compliance_api/schemas/inspection_requirement_grid.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ class InspectionRequirementGridItemSchema(Schema):
6969
KeyValueSchema,
7070
metadata={"description": "The project name of the inspection requirement"},
7171
)
72+
requirement_sources = fields.List(
73+
fields.Nested(KeyValueSchema),
74+
metadata={"description": "The list of requirement sources"},
75+
)
7276

7377

7478
class InspectionRequirementFilterSchema(BaseSchema):

compliance-api/src/compliance_api/services/inspection_requirement.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from datetime import datetime
66
from io import BytesIO
7+
import json
78
from typing import List
89

910
import pandas as pd
@@ -461,6 +462,36 @@ def _get_first_requirement_source_sub_query():
461462
)
462463

463464

465+
def _get_all_requirement_sources_sub_query():
466+
"""Get all unique requirement source names for each requirement."""
467+
468+
return (
469+
db.session.query(
470+
InspectionReqSourceDetailModel.requirement_id,
471+
func.array_agg(
472+
func.distinct(
473+
cast(
474+
func.json_build_object(
475+
'id', RequirementSourceOptionModel.id,
476+
'name', RequirementSourceOptionModel.name
477+
), db.Text
478+
)
479+
)
480+
).label("all_sources"),
481+
)
482+
.join(
483+
RequirementSourceOptionModel,
484+
InspectionReqSourceDetailModel.requirement_source_id == RequirementSourceOptionModel.id,
485+
)
486+
.filter(
487+
InspectionReqSourceDetailModel.is_active.is_(True),
488+
InspectionReqSourceDetailModel.is_deleted.is_(False),
489+
)
490+
.group_by(InspectionReqSourceDetailModel.requirement_id)
491+
.subquery("all_requirement_sources")
492+
)
493+
494+
464495
def _get_requirement_order_sub_query():
465496
"""Get requirement order sub query."""
466497
return (
@@ -633,6 +664,7 @@ def _build_inspection_requirements_query(args, enable_pagination=True):
633664
# Get subqueries
634665
subqueries = {
635666
"first_requirement_source": _get_first_requirement_source_sub_query(),
667+
"all_requirement_sources": _get_all_requirement_sources_sub_query(),
636668
"requirement_order": _get_requirement_order_sub_query(),
637669
"requirement_warning_letter": _get_requirement_warning_letter_sub_query(),
638670
"requirement_violation_ticket": _get_requirement_violation_ticket_sub_query(),
@@ -687,6 +719,7 @@ def _build_inspection_requirements_query(args, enable_pagination=True):
687719
models["restorative_justice"].restorative_justice_number.label(
688720
"restorative_justice_number"
689721
),
722+
subqueries["all_requirement_sources"].c.all_sources.label("requirement_sources"),
690723
)
691724
.join(
692725
models["topic"],
@@ -834,6 +867,10 @@ def _build_inspection_requirements_query(args, enable_pagination=True):
834867
models["restorative_justice"].is_active.is_(True),
835868
),
836869
)
870+
.outerjoin(
871+
subqueries["all_requirement_sources"],
872+
models["req"].id == subqueries["all_requirement_sources"].c.requirement_id,
873+
)
837874
.filter(models["req"].is_active.is_(True), models["req"].is_deleted.is_(False))
838875
.order_by(models["req"].id, models["enf_map"].enforcement_action_id)
839876
.options(
@@ -849,7 +886,7 @@ def _build_inspection_requirements_query(args, enable_pagination=True):
849886

850887
# Apply pagination if requested
851888
if enable_pagination:
852-
return _apply_pagination(base_query, args, **models)
889+
return _apply_pagination(base_query, args, subqueries, **models)
853890
return base_query
854891

855892

@@ -1025,12 +1062,13 @@ def _apply_filters(query, args, **kwargs): # pylint: disable=too-many-arguments
10251062
return query
10261063

10271064

1028-
def _apply_pagination(query, args, **kwargs):
1065+
def _apply_pagination(query, args, subqueries, **kwargs):
10291066
"""Apply pagination to the query.
10301067
10311068
Args:
10321069
query: The SQLAlchemy query to paginate
10331070
args: Query arguments containing pagination parameters
1071+
subqueries: Dictionary containing subqueries
10341072
**kwargs: Model aliases
10351073
10361074
Returns:
@@ -1128,6 +1166,8 @@ def _apply_pagination(query, args, **kwargs):
11281166
reference_models["restorative_justice"].restorative_justice_number.label(
11291167
"restorative_justice_number"
11301168
),
1169+
# Include all requirement source IDs
1170+
subqueries["all_requirement_sources"].c.all_sources.label("requirement_sources"),
11311171
# Include enforcement document IDs for distinct key
11321172
reference_models["order"].id.label("order_id"),
11331173
reference_models["warning_letter"].id.label("warning_letter_id"),
@@ -1179,6 +1219,7 @@ def _apply_pagination(query, args, **kwargs):
11791219
subq.c.admin_penalty_number.label("admin_penalty_number"),
11801220
subq.c.charge_rec_number.label("charge_rec_number"),
11811221
subq.c.restorative_justice_number.label("restorative_justice_number"),
1222+
subq.c.requirement_sources.label("requirement_sources"),
11821223
subq.c.order_id.label("order_id"),
11831224
subq.c.warning_letter_id.label("warning_letter_id"),
11841225
subq.c.violation_ticket_id.label("violation_ticket_id"),
@@ -1465,6 +1506,9 @@ def _process_inspection_requirement_query_results(query_results):
14651506
)
14661507
item["enforcement_number"] = _get_enforcement_number_by_type(result)
14671508

1509+
# Add all requirement source names
1510+
item["requirement_sources"] = getattr(result, "requirement_sources", None)
1511+
14681512
processed_requirements.append(item)
14691513
return processed_requirements
14701514

@@ -1514,6 +1558,12 @@ def _convert_enum_string_to_object(enum_string, enum_class_map):
15141558

15151559
def _make_requirement_detail_object(requirements: list):
15161560
"""Make requirement detail object."""
1561+
1562+
def parse_requirement_sources(sources):
1563+
if not sources:
1564+
return []
1565+
return [json.loads(item) for item in sources]
1566+
15171567
requirement_details = []
15181568
for requirement in requirements:
15191569
item = {
@@ -1535,6 +1585,7 @@ def _make_requirement_detail_object(requirements: list):
15351585
"name": requirement["inspection_status"].value,
15361586
},
15371587
"enforcement_number": requirement["enforcement_number"],
1588+
"requirement_sources": parse_requirement_sources(requirement.get("requirement_sources")),
15381589
}
15391590

15401591
# Handle status field - already converted to proper object format in

compliance-web/src/components/App/RequirementsGrid/RequirementsGridUtils.tsx

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,18 @@ export const useConvertFiltersToQueryParams = (
134134
export const useRequirementsGridColumns = (
135135
dataDependencies: RequirementsGridDataDependencies
136136
): MRT_ColumnDef<InspectionRequirementGrid>[] => {
137-
const {
138-
topics,
139-
complianceFindings,
140-
enforcementActions,
141-
requirementSources,
142-
} = dataDependencies;
137+
const RequirementSourceNames = {
138+
SCHEDULE_B: "Schedule B - Table of Conditions",
139+
SCHEDULE_A: "Schedule A - Certified Project Description",
140+
};
141+
142+
const RequirementSourceNameMap: Record<string, string> = {
143+
[RequirementSourceNames.SCHEDULE_B]: "Schedule B",
144+
[RequirementSourceNames.SCHEDULE_A]: "Schedule A",
145+
};
146+
147+
const { topics, complianceFindings, enforcementActions, requirementSources } =
148+
dataDependencies;
143149

144150
return [
145151
{
@@ -186,7 +192,7 @@ export const useRequirementsGridColumns = (
186192
},
187193
{
188194
accessorKey: "enf_stats",
189-
header: "Enf. Status",
195+
header: "Enf. Status",
190196
Cell: ({ row }) => {
191197
const enforcementStatusFlagObj: InspectionMoreDetailsEnforcementAction =
192198
{
@@ -229,7 +235,25 @@ export const useRequirementsGridColumns = (
229235
size: 80,
230236
},
231237
{
232-
accessorFn: (row) => row.requirement_source?.name,
238+
accessorFn: (row) => {
239+
return (
240+
row.requirement_sources
241+
?.map((source) => {
242+
if (source.name === RequirementSourceNames.SCHEDULE_B) {
243+
return RequirementSourceNameMap[
244+
RequirementSourceNames.SCHEDULE_B
245+
];
246+
}
247+
if (source.name === RequirementSourceNames.SCHEDULE_A) {
248+
return RequirementSourceNameMap[
249+
RequirementSourceNames.SCHEDULE_A
250+
];
251+
}
252+
return source.name;
253+
})
254+
.join(", ") ?? ""
255+
);
256+
},
233257
id: "req_src",
234258
header: "Source",
235259
filterVariant: "multi-select",

compliance-web/src/models/InspectionRequirementGrid.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface InspectionRequirementGrid {
1818
date_issued: string;
1919
ir_number: string;
2020
requirement_number: string;
21-
requirement_source: RequirementSource;
21+
requirement_sources: RequirementSource[];
2222
approved_by: StaffUser;
2323
approved_by_id: number;
2424
progress?: Option;

0 commit comments

Comments
 (0)