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

1"""SubTier Agency model for USASpending sub-agency data.""" 

2 

3from __future__ import annotations 

4 

5from typing import Optional, List, TYPE_CHECKING, Dict, Any 

6from ..utils.formatter import to_float, to_int, contracts_titlecase 

7from .base_model import BaseModel 

8 

9if TYPE_CHECKING: 

10 from ..client import USASpending 

11 

12 

13class SubTierAgency(BaseModel): 

14 """Rich wrapper around a USAspending subtier agency record. 

15  

16 This model represents a subtier agency with its essential properties, 

17 including nested office information. 

18 """ 

19 

20 def __init__(self, data: Dict[str, Any], client: Optional["USASpending"] = None): 

21 """Initialize SubTierAgency model. 

22 

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] 

37 

38 super().__init__(data) 

39 self._client = client 

40 

41 @property 

42 def name(self) -> Optional[str]: 

43 """Name of the subtier agency.""" 

44 return contracts_titlecase(self.get_value("name")) 

45 

46 @property 

47 def code(self) -> Optional[str]: 

48 """Code of the subtier agency.""" 

49 return self.get_value("code") 

50 

51 @property 

52 def abbreviation(self) -> Optional[str]: 

53 """Abbreviation of the subtier agency.""" 

54 return self.get_value("abbreviation") 

55 

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) 

61 

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) 

67 

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) 

73 

74 @property 

75 def offices(self) -> List["SubTierAgency"]: 

76 """List of offices under this subtier agency. 

77  

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 [] 

84 

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) 

90 

91 return offices 

92 

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}>" 

98