Coverage for src/usaspending/models/subtier_agency.py: 100%
49 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-03 17:15 -0700
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-03 17:15 -0700
1"""SubTier Agency model for USASpending sub-agency data."""
3from __future__ import annotations
5from typing import Optional, List, TYPE_CHECKING, Dict, Any
6from ..utils.formatter import to_float, to_int, contracts_titlecase
7from .base_model import BaseModel
9if TYPE_CHECKING:
10 from ..client import USASpending
13class SubTierAgency(BaseModel):
14 """Rich wrapper around a USAspending subtier agency record.
16 This model represents a subtier agency with its essential properties,
17 including nested office information.
18 """
20 def __init__(self, data: Dict[str, Any], client: Optional["USASpending"] = None):
21 """Initialize SubTierAgency model.
23 Args:
24 data: Raw sub-agency data from API
25 client: USASpending client instance
26 """
27 # Check if this data includes office_agency_name (from award context)
28 office_agency_name = data.get("office_agency_name")
29 if office_agency_name and "children" not in data:
30 # Create a synthetic child office from office_agency_name
31 office_child = {
32 "name": contracts_titlecase(office_agency_name)
33 }
34 # Create a copy of data with the office child
35 data = data.copy()
36 data["children"] = [office_child]
38 super().__init__(data)
39 self._client = client
41 @property
42 def name(self) -> Optional[str]:
43 """Name of the subtier agency."""
44 return contracts_titlecase(self.get_value("name"))
46 @property
47 def code(self) -> Optional[str]:
48 """Code of the subtier agency."""
49 return self.get_value("code")
51 @property
52 def abbreviation(self) -> Optional[str]:
53 """Abbreviation of the subtier agency."""
54 return self.get_value("abbreviation")
56 @property
57 def total_obligations(self) -> Optional[float]:
58 """Total obligations for this subtier agency."""
59 obligations = self.get_value("total_obligations")
60 return to_float(obligations)
62 @property
63 def transaction_count(self) -> Optional[int]:
64 """Number of transactions for this subtier agency."""
65 count = self.get_value("transaction_count")
66 return to_int(count)
68 @property
69 def new_award_count(self) -> Optional[int]:
70 """Number of new awards for this subtier agency."""
71 count = self.get_value("new_award_count")
72 return to_int(count)
74 @property
75 def offices(self) -> List["SubTierAgency"]:
76 """List of offices under this subtier agency.
78 Returns:
79 List of SubTierAgency instances representing offices
80 """
81 children_data = self.get_value("children", default=[])
82 if not isinstance(children_data, list):
83 return []
85 offices = []
86 for office_data in children_data:
87 if isinstance(office_data, dict):
88 office = SubTierAgency(office_data, self._client)
89 offices.append(office)
91 return offices
93 def __repr__(self) -> str:
94 """String representation of SubTierAgency."""
95 name = self.name or "?"
96 code = self.code or "?"
97 return f"<SubTierAgency {code}: {name}>"