Skip to content

[DEV-14570] Implement the spending_by_award filters in Pydantic#4634

Open
aguest-kc wants to merge 19 commits intoqatfrom
ftr/dev-14570-spending-by-award-pydantic-filters
Open

[DEV-14570] Implement the spending_by_award filters in Pydantic#4634
aguest-kc wants to merge 19 commits intoqatfrom
ftr/dev-14570-spending-by-award-pydantic-filters

Conversation

@aguest-kc
Copy link
Copy Markdown
Contributor

@aguest-kc aguest-kc commented Apr 21, 2026

Description:

Use Pydantic to validate the spending_by_award filters instead of TinyShield.

Technical Details:

Requirements for PR Merge:

  1. Unit & integration tests updated
  2. API documentation updated (examples listed below)
    1. API Contracts
    2. API UI
    3. Comments
  3. Jira Ticket(s)
    1. DEV-14570

Explain N/A in above checklist:

@aguest-kc aguest-kc added the needs frontend review [PR] changes an API contract label Apr 22, 2026
@sethstoudenmier sethstoudenmier added the do not merge [PR] shouldn't be merged label Apr 22, 2026
anjenkin
anjenkin previously approved these changes Apr 23, 2026
Copy link
Copy Markdown

@anjenkin anjenkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double checked and the FE isn't using 'program_activity' on the spending_by_award endpoint.

@aguest-kc aguest-kc added in progress [ISSUE] being worked and removed do not merge [PR] shouldn't be merged labels Apr 29, 2026
Copy link
Copy Markdown
Contributor

@zachflanders-frb zachflanders-frb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with some comments to consider.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to consolidate these into a single CodeObject model?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was originally going to combine them, but I kept them separate in case we wanted to do any custom validation for a specific code type in the future.

@classmethod
def validate_zip(cls, v: str) -> str | None:
if v is not None and len(v) != 5:
raise ValueError("ZIP code must be exactly 5 digits")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to check if zip codes are digits as well as length? Could use and not v.isdigit()

Comment on lines +20 to +24
valid_def_codes = list(DisasterEmergencyFundCode.objects.values_list('code', flat=True))

if isinstance(code, str):
code = code.upper()
if code not in valid_def_codes:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of getting all the def codes and serializing them to a list it might be faster to do
DisasterEmergencyFundCode.filter(code=code).exists() and DisasterEmergencyFundCode.filter(code__in=code).exists()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be more efficient. I did it this way to support the existing error message, which lists all of the possible DEF codes.

Comment on lines +165 to +176
naics_codes: NAICSCodeObject | None = None
place_of_performance_locations: list[StandardLocationObject] | None = None
place_of_performance_scope: Literal["domestic", "foreign"] | None = None
program_activities: list[ProgramActivityObject] | None = None
program_numbers: list[str] | None = None
psc_codes: PSCCodeObject | list[str] | None = None
recipient_locations: list[StandardLocationObject] | None = None
recipient_scope: Literal["domestic", "foreign"] | None = None
recipient_search_text: list[str] | None = None
recipient_type_names: list[str] | None = None
set_aside_type_codes: list[str] | None = None
tas_codes: TASCodeObject | None = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we consolidated the CodeObject models (see earlier) we could use that model for the naics codes, psc codes, and tas codes.

filters: Filters
limit: int = 10
order: Literal["asc", "desc"] = "desc"
page: int | None = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does page have to be >= 1? Might be worth a field validation. I feel like I've made the mistake of asking for page 0 before.

@aguest-kc aguest-kc dismissed stale reviews from zachflanders-frb and anjenkin via 967917b April 30, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in progress [ISSUE] being worked needs frontend review [PR] changes an API contract

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants