usaspending-orm 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. usaspending_orm-0.6.0/LICENSE +18 -0
  2. usaspending_orm-0.6.0/PKG-INFO +324 -0
  3. usaspending_orm-0.6.0/README.md +297 -0
  4. usaspending_orm-0.6.0/pyproject.toml +53 -0
  5. usaspending_orm-0.6.0/src/usaspending/__init__.py +144 -0
  6. usaspending_orm-0.6.0/src/usaspending/cli/__init__.py +2 -0
  7. usaspending_orm-0.6.0/src/usaspending/cli/download_award.py +113 -0
  8. usaspending_orm-0.6.0/src/usaspending/client.py +550 -0
  9. usaspending_orm-0.6.0/src/usaspending/config.py +119 -0
  10. usaspending_orm-0.6.0/src/usaspending/download/__init__.py +8 -0
  11. usaspending_orm-0.6.0/src/usaspending/download/job.py +219 -0
  12. usaspending_orm-0.6.0/src/usaspending/download/manager.py +140 -0
  13. usaspending_orm-0.6.0/src/usaspending/exceptions.py +71 -0
  14. usaspending_orm-0.6.0/src/usaspending/logging_config.py +310 -0
  15. usaspending_orm-0.6.0/src/usaspending/models/__init__.py +86 -0
  16. usaspending_orm-0.6.0/src/usaspending/models/agency.py +631 -0
  17. usaspending_orm-0.6.0/src/usaspending/models/award.py +933 -0
  18. usaspending_orm-0.6.0/src/usaspending/models/award_factory.py +72 -0
  19. usaspending_orm-0.6.0/src/usaspending/models/award_types.py +114 -0
  20. usaspending_orm-0.6.0/src/usaspending/models/base_model.py +214 -0
  21. usaspending_orm-0.6.0/src/usaspending/models/contract.py +147 -0
  22. usaspending_orm-0.6.0/src/usaspending/models/district_spending.py +70 -0
  23. usaspending_orm-0.6.0/src/usaspending/models/download.py +74 -0
  24. usaspending_orm-0.6.0/src/usaspending/models/funding.py +182 -0
  25. usaspending_orm-0.6.0/src/usaspending/models/grant.py +147 -0
  26. usaspending_orm-0.6.0/src/usaspending/models/idv.py +165 -0
  27. usaspending_orm-0.6.0/src/usaspending/models/lazy_record.py +82 -0
  28. usaspending_orm-0.6.0/src/usaspending/models/loan.py +85 -0
  29. usaspending_orm-0.6.0/src/usaspending/models/location.py +125 -0
  30. usaspending_orm-0.6.0/src/usaspending/models/period_of_performance.py +43 -0
  31. usaspending_orm-0.6.0/src/usaspending/models/recipient.py +204 -0
  32. usaspending_orm-0.6.0/src/usaspending/models/recipient_business_categories.py +364 -0
  33. usaspending_orm-0.6.0/src/usaspending/models/recipient_spending.py +58 -0
  34. usaspending_orm-0.6.0/src/usaspending/models/spending.py +70 -0
  35. usaspending_orm-0.6.0/src/usaspending/models/state_spending.py +43 -0
  36. usaspending_orm-0.6.0/src/usaspending/models/subaward.py +270 -0
  37. usaspending_orm-0.6.0/src/usaspending/models/subtier_agency.py +98 -0
  38. usaspending_orm-0.6.0/src/usaspending/models/transaction.py +89 -0
  39. usaspending_orm-0.6.0/src/usaspending/queries/__init__.py +87 -0
  40. usaspending_orm-0.6.0/src/usaspending/queries/agencies_search.py +196 -0
  41. usaspending_orm-0.6.0/src/usaspending/queries/agency_award_summary.py +134 -0
  42. usaspending_orm-0.6.0/src/usaspending/queries/agency_query.py +102 -0
  43. usaspending_orm-0.6.0/src/usaspending/queries/award_query.py +40 -0
  44. usaspending_orm-0.6.0/src/usaspending/queries/awarding_agencies_search.py +16 -0
  45. usaspending_orm-0.6.0/src/usaspending/queries/awards_search.py +602 -0
  46. usaspending_orm-0.6.0/src/usaspending/queries/filters.py +437 -0
  47. usaspending_orm-0.6.0/src/usaspending/queries/funding_agencies_search.py +20 -0
  48. usaspending_orm-0.6.0/src/usaspending/queries/funding_search.py +180 -0
  49. usaspending_orm-0.6.0/src/usaspending/queries/query_builder.py +1430 -0
  50. usaspending_orm-0.6.0/src/usaspending/queries/recipient_query.py +66 -0
  51. usaspending_orm-0.6.0/src/usaspending/queries/recipients_search.py +167 -0
  52. usaspending_orm-0.6.0/src/usaspending/queries/single_resource_base.py +83 -0
  53. usaspending_orm-0.6.0/src/usaspending/queries/spending_search.py +261 -0
  54. usaspending_orm-0.6.0/src/usaspending/queries/sub_agency_query.py +177 -0
  55. usaspending_orm-0.6.0/src/usaspending/queries/subawards_search.py +196 -0
  56. usaspending_orm-0.6.0/src/usaspending/queries/transactions_search.py +213 -0
  57. usaspending_orm-0.6.0/src/usaspending/resources/__init__.py +17 -0
  58. usaspending_orm-0.6.0/src/usaspending/resources/agency_resource.py +105 -0
  59. usaspending_orm-0.6.0/src/usaspending/resources/award_resource.py +119 -0
  60. usaspending_orm-0.6.0/src/usaspending/resources/base_resource.py +31 -0
  61. usaspending_orm-0.6.0/src/usaspending/resources/download_resource.py +102 -0
  62. usaspending_orm-0.6.0/src/usaspending/resources/funding_resource.py +41 -0
  63. usaspending_orm-0.6.0/src/usaspending/resources/recipients_resource.py +104 -0
  64. usaspending_orm-0.6.0/src/usaspending/resources/spending_resource.py +45 -0
  65. usaspending_orm-0.6.0/src/usaspending/resources/subawards_resource.py +68 -0
  66. usaspending_orm-0.6.0/src/usaspending/resources/transactions_resource.py +39 -0
  67. usaspending_orm-0.6.0/src/usaspending/utils/__init__.py +6 -0
  68. usaspending_orm-0.6.0/src/usaspending/utils/formatter.py +550 -0
  69. usaspending_orm-0.6.0/src/usaspending/utils/rate_limit.py +135 -0
  70. usaspending_orm-0.6.0/src/usaspending/utils/retry.py +257 -0
  71. usaspending_orm-0.6.0/src/usaspending/utils/special_cases.yaml +155 -0
@@ -0,0 +1,18 @@
1
+ Copyright 2025 Casey Dreier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the “Software”), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
15
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
16
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,324 @@
1
+ Metadata-Version: 2.3
2
+ Name: usaspending-orm
3
+ Version: 0.6.0
4
+ Summary: A Python ORM wrapper for the USAspending.gov API with intuitive queries, automatic pagination, and smart caching
5
+ License: MIT
6
+ Keywords: usaspending,federal-spending,government-data,api-wrapper,orm,contracts,grants
7
+ Author: Casey Dreier
8
+ Author-email: casey.dreier@planetary.org
9
+ Requires-Python: >=3.9
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Dist: cachier (>=4.1.0,<5.0.0)
19
+ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
20
+ Requires-Dist: requests (>=2.32.4,<3.0.0)
21
+ Requires-Dist: titlecase (>=2.4.1,<3.0.0)
22
+ Project-URL: Documentation, https://github.com/planetary-society/usaspending-orm/blob/main/README.md
23
+ Project-URL: Homepage, https://github.com/planetary-society/usaspending-orm
24
+ Project-URL: Issues, https://github.com/planetary-society/usaspending-orm/issues
25
+ Project-URL: Repository, https://github.com/planetary-society/usaspending-orm
26
+ Description-Content-Type: text/markdown
27
+
28
+ # USASpending ORM
29
+
30
+ A python library that provides an object-relational mapping layer to the USAspending.gov API.
31
+
32
+ ## Why This Library?
33
+
34
+ [USASpending.gov](https://usaspending.gov) is the official federal database for tracking U.S. government spending, and provides extensive data on federal contracts, grants, and other awards since 2007-10-01, enabling citizens to track how federal money is spent.
35
+
36
+ The platform has a [comprehensive API](https://api.usaspending.gov) for querying the data, but the API is complex and can be cumbersome to work with. This library provides an abstraction layer and other quality-of-life improvements to enable rapid development of applications that consume USASpending data.
37
+
38
+ ## Key Features
39
+
40
+ **🔗 ORM-Style Chained Interface** - Access related data through object associations (e.g., `award.recipient.location.city`) inspired by ActiveRecord and SQLAlchemy. Navigate related data without manual API calls.
41
+
42
+ **🔎 Comprehensive Award Queries** - Build complex searches with chainable filters for agencies, award types, fiscal years, and more.
43
+
44
+ **⚡️ Smart Caching & Rate Limiting** - Optional file-based caching to dramatically improve performance for repeated queries. Automatic rate limiting and retry logic handles API throttle limits during bulk operations.
45
+
46
+ **🛡️ Data Normalization and Type Casting** - Consistent field naming across resources, with lazy-loading for nested data and automatic type conversion.
47
+
48
+ **🥩 Raw API Output Preserved** - Access the original API JSON response via the `.raw` property on any resource object when you need the underlying data structure.
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install usaspending-orm
54
+ ```
55
+
56
+ Requires Python 3.9 or higher. No API key required.
57
+
58
+ ## Usage
59
+
60
+ The library provides a `USASpendingClient` class that manages the connection to the USASpending API and provides access to various resources such as awards, recipients, agencies, etc. This can be used as a context manager to ensure proper session management
61
+ or can be instantiated directly.
62
+
63
+ #### Load the client
64
+ ```python
65
+ from usaspending import USASpendingClient
66
+ ```
67
+
68
+ #### Then load a specific award by its Award ID
69
+ ```python
70
+ with USASpendingClient() as client:
71
+ award = client.awards.find_by_award_id("80GSFC18C0008")
72
+ ```
73
+
74
+ #### Access related Award properties via chained object associations
75
+ ```python
76
+ with USASpendingClient() as client:
77
+ award = client.awards.find_by_award_id("80GSFC18C0008")
78
+ award.recipient.location.full_address # -> 105 Jessup Hall, Iowa City, IA, 52242, United States
79
+ award.subawards.count() # -> 100
80
+ award.subawards[2].recipient.place_of_performance.district # -> AL-03
81
+ ```
82
+
83
+ #### Searching for Awards
84
+
85
+ You can query awards data using the `search()` method on the `client.awards` object:
86
+
87
+ ```python
88
+
89
+ with USASpendingClient() as client:
90
+ awards_query = client.awards.search()
91
+ ```
92
+
93
+ Search parameters are outlined in the [spending_by_award](https://github.com/fedspendingtransparency/usaspending-api/raw/refs/heads/master/usaspending_api/api_contracts/contracts/v2/search/spending_by_award.md) endpoint of the USASpending API. Every search parameter is applied via a matching "snake_case" method name. These methods can be chained together to build complex queries.
94
+
95
+ ``` python
96
+ awards_query = client.awards.search() \
97
+ .agencies({"name":"National Aeronautics and Space Administration", "type":"awarding", "tier":"toptier"}) \
98
+ .grants() \
99
+ .keywords("Perseverance","Mars")
100
+ ```
101
+
102
+ This returns a query object that can be further refined or executed to return results.
103
+ The methods `.all()`, `.first()`, `.count()` will trigger a query to the API, as will iterating over the query object.
104
+
105
+ ### Example: Searching for NASA Contracts to SpaceX in 2023
106
+
107
+ ```python
108
+
109
+ with USASpendingClient() as client:
110
+
111
+ # Create query object with chained filters
112
+ awards_query = client.awards.search() \
113
+ .agency("National Aeronautics and Space Administration") \
114
+ .recipient_search_text("Space Exploration Technologies") \
115
+ .contracts() \
116
+ .fiscal_year(2023) \
117
+ .order_by("Award Amount", "desc")
118
+
119
+ # -> <AwardQuery ...> object, no API call made yet
120
+
121
+ # Return results count without fetching all records
122
+ count = awards_query.count() # -> 8
123
+
124
+ # Fetch first result (query executes here)
125
+ top_spacex_award = awards_query.first()
126
+
127
+ # Returned value is an Award object with all properties mapped
128
+ # and properly typed.
129
+ top_spacex_award.total_obligation # -> Decimal('3029850123.69')
130
+ top_spacex_award.category # -> "contract"
131
+ top_spacex_award.description # -> "The Commercial Crew Program (CCP) contract ...."
132
+
133
+ # Helper methods provide easy access to common fields without having to account for
134
+ # inconsistent naming or nested structures in the raw API response
135
+ top_spacex_award.award_identifier # -> "80GSFC18C0008"
136
+ top_spacex_award.start_date # -> datetime.date(2016, 12, 30)
137
+ top_spacex_award.end_date # -> datetime.date(2023,12,31)
138
+
139
+ # The resulting object provides a normalized interface to the full Award record,
140
+ # and provides access to related data via chained associations
141
+
142
+ # Recipient information
143
+ top_spacex_award.recipient.name # -> "Space Exploration Technologies Corp."
144
+ top_spacex_award.recipient.location.city # -> "Hawthorne"
145
+
146
+ # Award Transactions
147
+ last_transaction = top_spacex_award.transactions.order_by("action_date", "desc").first()
148
+ last_transaction.action_date # -> datetime.date(2025, 10, 08)
149
+ last_transaction.action_type_description # -> "SUPPLEMENTAL AGREEMENT FOR WORK WITHIN SCOPE"
150
+
151
+ ```
152
+
153
+ ## Configuration
154
+
155
+ ### Session Management and Lazy-Loading
156
+
157
+ The library uses lazy-loading to avoid unnecessary API calls. Missing award and Recipient properties will trigger an API call to fetch the missing data. This means models require an active client session to load missing data on demand.
158
+
159
+ #### Session Lifecycle
160
+
161
+ Use the client as a context manager (recommended) or explicitly call `close()`:
162
+
163
+ ```python
164
+ with USASpendingClient() as client:
165
+ awards = client.awards.search().agencies("NASA").all()
166
+ for award in awards:
167
+ # Access lazy-loaded properties inside the context
168
+ print(f"{award.recipient.name}: ${award.total_obligation:,.2f}")
169
+ print(f"Subawards: {award.subaward_count}")
170
+ # Session automatically closed here
171
+
172
+ # Or explicitly manage session
173
+ client = USASpendingClient()
174
+ awards = client.awards.search().agencies("NASA").all()
175
+ client.close()
176
+ ```
177
+
178
+ Accessing related properties after the client session closes raises a `DetachedInstanceError`:
179
+
180
+ ```python
181
+ # This will raise DetachedInstanceError
182
+ with USASpendingClient() as client:
183
+ awards = client.awards.search().all()
184
+ # Client is closed here
185
+
186
+ # This will raise DetachedInstanceError
187
+ print(awards[0].transactions.count())
188
+ ```
189
+
190
+ You can also reattach objects to a new session if needed:
191
+
192
+ ```python
193
+ # Create objects in one session
194
+ with USASpendingClient() as client:
195
+ award = client.awards.find_by_award_id("80GSFC18C0008")
196
+
197
+ # Reattach to a new session to access related properties
198
+ with USASpendingClient() as new_client:
199
+ award.reattach(new_client)
200
+ print(f"Subawards: {award.subawards.count()}") # Works!
201
+
202
+ # Recursive reattach for nested objects
203
+ award.reattach(new_client, recursive=True)
204
+ print(f"Recipient: {award.recipient.name}") # Recipient also reattached
205
+ ```
206
+
207
+ ### Performance & Caching
208
+
209
+ By default, caching is **disabled**. However, enabling caching can dramatically improve performance and reduce API load for repeated queries, especially during development or when working with large datasets.
210
+
211
+ To enable caching, load the configuration module and set `cache_enabled=True` before creating a client instance:
212
+
213
+ ```python
214
+ from usaspending import config as usaspending_config, USASpendingClient
215
+
216
+ # Enable with defaults (1 week TTL, file-based storage)
217
+ usaspending_config.configure(cache_enabled=True)
218
+
219
+ with USASpendingClient() as client:
220
+ # All queries will now be cached
221
+ awards = client.awards.search().agencies("NASA").all()
222
+ ```
223
+
224
+ The library defaults to file-based caching with a 1-week TTL, but you can customize these settings as needed:
225
+
226
+ ```python
227
+ usaspending_config.configure(
228
+ cache_enabled=True, # Enable caching
229
+ cache_ttl=86400, # Cache for 1 day (default: 1 week)
230
+ cache_backend="memory", # "file" or "memory" (default: "file")
231
+ )
232
+ ```
233
+
234
+ **File-based caching** (default):
235
+ - Persists between Python sessions
236
+ - Stored in `~/.cache/usaspending` directory
237
+ - Uses pickle for serialization
238
+ - Best for production and development workflows
239
+
240
+ **Memory-based caching**:
241
+ - Faster access, no disk I/O
242
+ - Cleared when Python process ends
243
+ - Best for single-session data exploration
244
+ - Enable with `cache_backend="memory"`
245
+
246
+ ### Customizable Settings
247
+
248
+ The library applies some sensible defaults that work for most use cases:
249
+
250
+ - Rate limiting: 1000 calls per 5 minutes (respecting USASpending API limits)
251
+ - Caching: disabled by default (see Performance & Caching section above to enable)
252
+
253
+ Customize these settings before creating a client instance if needed:
254
+
255
+ ```python
256
+ from usaspending import config as usaspending_config
257
+
258
+ # Configure settings before creating the client
259
+ usaspending_config.configure(
260
+ # Cache settings (caching is disabled by default)
261
+ cache_enabled=True, # Enable caching
262
+ # Set file-cache directory (default: ~/.usaspending_cache)
263
+ cache_dir="/tmp/usaspending_cache",
264
+ # Set cache expiration time (default 1 week)
265
+ cache_ttl=86400,
266
+ # Set cache backend to be in-memory "memory" or "file" for file-based caching via pickle (default: "file")
267
+ cache_backend="memory",
268
+
269
+ # Set rate limiting parameters
270
+ # Set number of calls allowed within the rate limit period (default: 1000)
271
+ rate_limit_calls=500,
272
+ # Rate limit period (in seconds, default: 300)
273
+ rate_limit_period=60,
274
+
275
+ # Set HTTP request parameters (default: max_retries=3, timeout=30)
276
+ # Set number of retries for failed requests (default: 3)
277
+ max_retries=5,
278
+ # Set delay between retries in seconds (default: 1.0)
279
+ retry_delay=10.0,
280
+ # Set exponential backoff factor for retries (default: 2.0)
281
+ retry_backoff=2.0,
282
+ # Set request timeout in seconds (default: 30)
283
+ timeout=60 # Longer timeout for slow connections (default: 30)
284
+ )
285
+
286
+ ```
287
+
288
+ ### Logging Configuration
289
+
290
+ The library provides detailed logging, which you can configure in your application:
291
+
292
+ ```python
293
+ import logging
294
+ from usaspending import USASpendingClient
295
+
296
+ # Configure root logger (affects all loggers)
297
+ logging.basicConfig(
298
+ level=logging.DEBUG,
299
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
300
+ )
301
+
302
+ ```
303
+
304
+ ## Project Status
305
+
306
+ USASpending Python Wrapper is under active development. The API is stabilizing but may change as we refine the abstractions based on real-world usage. We welcome feedback on the interface design and feature priorities.
307
+
308
+ ## Contributing
309
+
310
+ We welcome contributions to improve and expand the implementation and functionality.
311
+
312
+ ## About The Planetary Society
313
+
314
+ This library was initially developed to serve the needs of The Planetary Society's Space Policy and Advocacy team in tracking and analyzing NASA contract data, and is in-use in our internal and external data tools.
315
+
316
+ We have open-sourced the project to enable others to better use USASpending data.
317
+
318
+ [The Planetary Society](planetary.org) is an independent nonprofit organization that empowers the world's citizens to advance space science and exploration. The organization is supported by individuals across the world, and does not accept government grants nor does it have major aerospace donations.
319
+
320
+ Please consider supporting our work by [becoming a member](https://www.planetary.org/join).
321
+
322
+ ## License
323
+
324
+ MIT License
@@ -0,0 +1,297 @@
1
+ # USASpending ORM
2
+
3
+ A python library that provides an object-relational mapping layer to the USAspending.gov API.
4
+
5
+ ## Why This Library?
6
+
7
+ [USASpending.gov](https://usaspending.gov) is the official federal database for tracking U.S. government spending, and provides extensive data on federal contracts, grants, and other awards since 2007-10-01, enabling citizens to track how federal money is spent.
8
+
9
+ The platform has a [comprehensive API](https://api.usaspending.gov) for querying the data, but the API is complex and can be cumbersome to work with. This library provides an abstraction layer and other quality-of-life improvements to enable rapid development of applications that consume USASpending data.
10
+
11
+ ## Key Features
12
+
13
+ **🔗 ORM-Style Chained Interface** - Access related data through object associations (e.g., `award.recipient.location.city`) inspired by ActiveRecord and SQLAlchemy. Navigate related data without manual API calls.
14
+
15
+ **🔎 Comprehensive Award Queries** - Build complex searches with chainable filters for agencies, award types, fiscal years, and more.
16
+
17
+ **⚡️ Smart Caching & Rate Limiting** - Optional file-based caching to dramatically improve performance for repeated queries. Automatic rate limiting and retry logic handles API throttle limits during bulk operations.
18
+
19
+ **🛡️ Data Normalization and Type Casting** - Consistent field naming across resources, with lazy-loading for nested data and automatic type conversion.
20
+
21
+ **🥩 Raw API Output Preserved** - Access the original API JSON response via the `.raw` property on any resource object when you need the underlying data structure.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install usaspending-orm
27
+ ```
28
+
29
+ Requires Python 3.9 or higher. No API key required.
30
+
31
+ ## Usage
32
+
33
+ The library provides a `USASpendingClient` class that manages the connection to the USASpending API and provides access to various resources such as awards, recipients, agencies, etc. This can be used as a context manager to ensure proper session management
34
+ or can be instantiated directly.
35
+
36
+ #### Load the client
37
+ ```python
38
+ from usaspending import USASpendingClient
39
+ ```
40
+
41
+ #### Then load a specific award by its Award ID
42
+ ```python
43
+ with USASpendingClient() as client:
44
+ award = client.awards.find_by_award_id("80GSFC18C0008")
45
+ ```
46
+
47
+ #### Access related Award properties via chained object associations
48
+ ```python
49
+ with USASpendingClient() as client:
50
+ award = client.awards.find_by_award_id("80GSFC18C0008")
51
+ award.recipient.location.full_address # -> 105 Jessup Hall, Iowa City, IA, 52242, United States
52
+ award.subawards.count() # -> 100
53
+ award.subawards[2].recipient.place_of_performance.district # -> AL-03
54
+ ```
55
+
56
+ #### Searching for Awards
57
+
58
+ You can query awards data using the `search()` method on the `client.awards` object:
59
+
60
+ ```python
61
+
62
+ with USASpendingClient() as client:
63
+ awards_query = client.awards.search()
64
+ ```
65
+
66
+ Search parameters are outlined in the [spending_by_award](https://github.com/fedspendingtransparency/usaspending-api/raw/refs/heads/master/usaspending_api/api_contracts/contracts/v2/search/spending_by_award.md) endpoint of the USASpending API. Every search parameter is applied via a matching "snake_case" method name. These methods can be chained together to build complex queries.
67
+
68
+ ``` python
69
+ awards_query = client.awards.search() \
70
+ .agencies({"name":"National Aeronautics and Space Administration", "type":"awarding", "tier":"toptier"}) \
71
+ .grants() \
72
+ .keywords("Perseverance","Mars")
73
+ ```
74
+
75
+ This returns a query object that can be further refined or executed to return results.
76
+ The methods `.all()`, `.first()`, `.count()` will trigger a query to the API, as will iterating over the query object.
77
+
78
+ ### Example: Searching for NASA Contracts to SpaceX in 2023
79
+
80
+ ```python
81
+
82
+ with USASpendingClient() as client:
83
+
84
+ # Create query object with chained filters
85
+ awards_query = client.awards.search() \
86
+ .agency("National Aeronautics and Space Administration") \
87
+ .recipient_search_text("Space Exploration Technologies") \
88
+ .contracts() \
89
+ .fiscal_year(2023) \
90
+ .order_by("Award Amount", "desc")
91
+
92
+ # -> <AwardQuery ...> object, no API call made yet
93
+
94
+ # Return results count without fetching all records
95
+ count = awards_query.count() # -> 8
96
+
97
+ # Fetch first result (query executes here)
98
+ top_spacex_award = awards_query.first()
99
+
100
+ # Returned value is an Award object with all properties mapped
101
+ # and properly typed.
102
+ top_spacex_award.total_obligation # -> Decimal('3029850123.69')
103
+ top_spacex_award.category # -> "contract"
104
+ top_spacex_award.description # -> "The Commercial Crew Program (CCP) contract ...."
105
+
106
+ # Helper methods provide easy access to common fields without having to account for
107
+ # inconsistent naming or nested structures in the raw API response
108
+ top_spacex_award.award_identifier # -> "80GSFC18C0008"
109
+ top_spacex_award.start_date # -> datetime.date(2016, 12, 30)
110
+ top_spacex_award.end_date # -> datetime.date(2023,12,31)
111
+
112
+ # The resulting object provides a normalized interface to the full Award record,
113
+ # and provides access to related data via chained associations
114
+
115
+ # Recipient information
116
+ top_spacex_award.recipient.name # -> "Space Exploration Technologies Corp."
117
+ top_spacex_award.recipient.location.city # -> "Hawthorne"
118
+
119
+ # Award Transactions
120
+ last_transaction = top_spacex_award.transactions.order_by("action_date", "desc").first()
121
+ last_transaction.action_date # -> datetime.date(2025, 10, 08)
122
+ last_transaction.action_type_description # -> "SUPPLEMENTAL AGREEMENT FOR WORK WITHIN SCOPE"
123
+
124
+ ```
125
+
126
+ ## Configuration
127
+
128
+ ### Session Management and Lazy-Loading
129
+
130
+ The library uses lazy-loading to avoid unnecessary API calls. Missing award and Recipient properties will trigger an API call to fetch the missing data. This means models require an active client session to load missing data on demand.
131
+
132
+ #### Session Lifecycle
133
+
134
+ Use the client as a context manager (recommended) or explicitly call `close()`:
135
+
136
+ ```python
137
+ with USASpendingClient() as client:
138
+ awards = client.awards.search().agencies("NASA").all()
139
+ for award in awards:
140
+ # Access lazy-loaded properties inside the context
141
+ print(f"{award.recipient.name}: ${award.total_obligation:,.2f}")
142
+ print(f"Subawards: {award.subaward_count}")
143
+ # Session automatically closed here
144
+
145
+ # Or explicitly manage session
146
+ client = USASpendingClient()
147
+ awards = client.awards.search().agencies("NASA").all()
148
+ client.close()
149
+ ```
150
+
151
+ Accessing related properties after the client session closes raises a `DetachedInstanceError`:
152
+
153
+ ```python
154
+ # This will raise DetachedInstanceError
155
+ with USASpendingClient() as client:
156
+ awards = client.awards.search().all()
157
+ # Client is closed here
158
+
159
+ # This will raise DetachedInstanceError
160
+ print(awards[0].transactions.count())
161
+ ```
162
+
163
+ You can also reattach objects to a new session if needed:
164
+
165
+ ```python
166
+ # Create objects in one session
167
+ with USASpendingClient() as client:
168
+ award = client.awards.find_by_award_id("80GSFC18C0008")
169
+
170
+ # Reattach to a new session to access related properties
171
+ with USASpendingClient() as new_client:
172
+ award.reattach(new_client)
173
+ print(f"Subawards: {award.subawards.count()}") # Works!
174
+
175
+ # Recursive reattach for nested objects
176
+ award.reattach(new_client, recursive=True)
177
+ print(f"Recipient: {award.recipient.name}") # Recipient also reattached
178
+ ```
179
+
180
+ ### Performance & Caching
181
+
182
+ By default, caching is **disabled**. However, enabling caching can dramatically improve performance and reduce API load for repeated queries, especially during development or when working with large datasets.
183
+
184
+ To enable caching, load the configuration module and set `cache_enabled=True` before creating a client instance:
185
+
186
+ ```python
187
+ from usaspending import config as usaspending_config, USASpendingClient
188
+
189
+ # Enable with defaults (1 week TTL, file-based storage)
190
+ usaspending_config.configure(cache_enabled=True)
191
+
192
+ with USASpendingClient() as client:
193
+ # All queries will now be cached
194
+ awards = client.awards.search().agencies("NASA").all()
195
+ ```
196
+
197
+ The library defaults to file-based caching with a 1-week TTL, but you can customize these settings as needed:
198
+
199
+ ```python
200
+ usaspending_config.configure(
201
+ cache_enabled=True, # Enable caching
202
+ cache_ttl=86400, # Cache for 1 day (default: 1 week)
203
+ cache_backend="memory", # "file" or "memory" (default: "file")
204
+ )
205
+ ```
206
+
207
+ **File-based caching** (default):
208
+ - Persists between Python sessions
209
+ - Stored in `~/.cache/usaspending` directory
210
+ - Uses pickle for serialization
211
+ - Best for production and development workflows
212
+
213
+ **Memory-based caching**:
214
+ - Faster access, no disk I/O
215
+ - Cleared when Python process ends
216
+ - Best for single-session data exploration
217
+ - Enable with `cache_backend="memory"`
218
+
219
+ ### Customizable Settings
220
+
221
+ The library applies some sensible defaults that work for most use cases:
222
+
223
+ - Rate limiting: 1000 calls per 5 minutes (respecting USASpending API limits)
224
+ - Caching: disabled by default (see Performance & Caching section above to enable)
225
+
226
+ Customize these settings before creating a client instance if needed:
227
+
228
+ ```python
229
+ from usaspending import config as usaspending_config
230
+
231
+ # Configure settings before creating the client
232
+ usaspending_config.configure(
233
+ # Cache settings (caching is disabled by default)
234
+ cache_enabled=True, # Enable caching
235
+ # Set file-cache directory (default: ~/.usaspending_cache)
236
+ cache_dir="/tmp/usaspending_cache",
237
+ # Set cache expiration time (default 1 week)
238
+ cache_ttl=86400,
239
+ # Set cache backend to be in-memory "memory" or "file" for file-based caching via pickle (default: "file")
240
+ cache_backend="memory",
241
+
242
+ # Set rate limiting parameters
243
+ # Set number of calls allowed within the rate limit period (default: 1000)
244
+ rate_limit_calls=500,
245
+ # Rate limit period (in seconds, default: 300)
246
+ rate_limit_period=60,
247
+
248
+ # Set HTTP request parameters (default: max_retries=3, timeout=30)
249
+ # Set number of retries for failed requests (default: 3)
250
+ max_retries=5,
251
+ # Set delay between retries in seconds (default: 1.0)
252
+ retry_delay=10.0,
253
+ # Set exponential backoff factor for retries (default: 2.0)
254
+ retry_backoff=2.0,
255
+ # Set request timeout in seconds (default: 30)
256
+ timeout=60 # Longer timeout for slow connections (default: 30)
257
+ )
258
+
259
+ ```
260
+
261
+ ### Logging Configuration
262
+
263
+ The library provides detailed logging, which you can configure in your application:
264
+
265
+ ```python
266
+ import logging
267
+ from usaspending import USASpendingClient
268
+
269
+ # Configure root logger (affects all loggers)
270
+ logging.basicConfig(
271
+ level=logging.DEBUG,
272
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
273
+ )
274
+
275
+ ```
276
+
277
+ ## Project Status
278
+
279
+ USASpending Python Wrapper is under active development. The API is stabilizing but may change as we refine the abstractions based on real-world usage. We welcome feedback on the interface design and feature priorities.
280
+
281
+ ## Contributing
282
+
283
+ We welcome contributions to improve and expand the implementation and functionality.
284
+
285
+ ## About The Planetary Society
286
+
287
+ This library was initially developed to serve the needs of The Planetary Society's Space Policy and Advocacy team in tracking and analyzing NASA contract data, and is in-use in our internal and external data tools.
288
+
289
+ We have open-sourced the project to enable others to better use USASpending data.
290
+
291
+ [The Planetary Society](planetary.org) is an independent nonprofit organization that empowers the world's citizens to advance space science and exploration. The organization is supported by individuals across the world, and does not accept government grants nor does it have major aerospace donations.
292
+
293
+ Please consider supporting our work by [becoming a member](https://www.planetary.org/join).
294
+
295
+ ## License
296
+
297
+ MIT License
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "usaspending-orm"
3
+ version = "0.6.0"
4
+ description = "A Python ORM wrapper for the USAspending.gov API with intuitive queries, automatic pagination, and smart caching"
5
+ authors = [
6
+ {name = "Casey Dreier", email = "casey.dreier@planetary.org"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ keywords = [
13
+ "usaspending", "federal-spending", "government-data",
14
+ "api-wrapper", "orm", "contracts", "grants"
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+ dependencies = [
27
+ "requests>=2.32.4,<3.0.0",
28
+ "cachier>=4.1.0,<5.0.0",
29
+ "titlecase>=2.4.1,<3.0.0",
30
+ "pyyaml>=6.0.2,<7.0.0"
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/planetary-society/usaspending-orm"
35
+ Repository = "https://github.com/planetary-society/usaspending-orm"
36
+ Issues = "https://github.com/planetary-society/usaspending-orm/issues"
37
+ Documentation = "https://github.com/planetary-society/usaspending-orm/blob/main/README.md"
38
+
39
+ [tool.poetry]
40
+ packages = [{include = "usaspending", from = "src"}]
41
+
42
+
43
+ [tool.poetry.group.test.dependencies]
44
+ pytest = "^8.4.1"
45
+ requests-mock = "^1.12.1"
46
+ ruff = "^0.12.7"
47
+
48
+ [build-system]
49
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
50
+ build-backend = "poetry.core.masonry.api"
51
+
52
+ [tool.pytest.ini_options]
53
+ pythonpath = "src"