├── .gitattributes ├── .github └── workflows │ ├── codecov.yaml │ └── unittest.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── full_fred ├── __init__.py ├── _version.py ├── categories.py ├── constants.py ├── fred.py ├── fred_base.py ├── releases.py ├── series.py ├── sources.py ├── tags.py └── tests │ ├── __init__.py │ ├── fred_test_utils.py │ ├── test_api_key_file.py │ ├── test_category_methods.py │ ├── test_fred.py │ ├── test_fred_base_methods.py │ ├── test_release_methods.py │ ├── test_series_methods.py │ ├── test_source_methods.py │ └── test_tag_methods.py ├── pyproject.toml ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py └── versioneer.py /.gitattributes: -------------------------------------------------------------------------------- 1 | full_fred/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yaml: -------------------------------------------------------------------------------- 1 | name: CI with Coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v3 18 | 19 | - name: Set CI environment variable 20 | run: echo "CI=true" >> $GITHUB_ENV 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: "3.11" 26 | 27 | - name: Install Dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install pytest pytest-cov requests pandas 31 | 32 | - name: Export API key 33 | env: 34 | FRED_API_KEY: ${{ secrets.FRED_API_KEY }} 35 | run: echo "FRED_API_KEY=$FRED_API_KEY" >> $GITHUB_ENV 36 | 37 | - name: Run tests with coverage 38 | run: pytest --cov=full_fred --cov-report=xml --cov-report=term 39 | env: 40 | FRED_API_KEY: ${{ secrets.FRED_API_KEY }} 41 | 42 | - name: Upload coverage reports to Codecov 43 | uses: codecov/codecov-action@v5 44 | with: 45 | token: ${{ secrets.CODECOV_TOKEN }} 46 | slug: 7astro7/full_fred -------------------------------------------------------------------------------- /.github/workflows/unittest.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout Code 17 | uses: actions/checkout@v3 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: "3.x" 23 | 24 | - name: Install Dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install -r requirements.txt 28 | 29 | - name: Install pytest 30 | run: pip install pytest 31 | 32 | 33 | - name: Run Unit Tests 34 | run: pytest --junitxml=test-results.xml 35 | 36 | - name: Upload Test Results 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: Test Results 40 | path: test-results.xml 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | .venv 4 | __pycache__ 5 | *.egg-info 6 | example_key.txt 7 | build 8 | dist 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: python 3 | python: 4 | - "3.7" 5 | - "3.8" 6 | install: 7 | - pip install -r requirements.txt 8 | script: 9 | - pytest 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2025 Zachary A. Kraehling 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | include full_fred/_version.py 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![PyPI](https://img.shields.io/pypi/v/full_fred)](https://pypi.org/project/full_fred/) 3 | ![PyPI Downloads](https://img.shields.io/pypi/dm/full_fred) 4 | [![codecov](https://codecov.io/gh/7astro7/full_fred/graph/badge.svg?token=MTM8I9JRZF)](https://codecov.io/gh/7astro7/full_fred) 5 | ![GitHub issues](https://img.shields.io/github/issues/7astro7/full_fred) 6 | ![Unit Tests](https://github.com/7astro7/full_fred/actions/workflows/unittest.yml/badge.svg) 7 | ![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg) 8 | 9 | 10 | 11 | # full_fred 12 | `full_fred` is a Python interface to 13 | [FRED (Federal Reserve Economic Data)](https://fred.stlouisfed.org/) that 14 | prioritizes user preference, flexibility, and speed. `full_fred`'s API translates to Python 15 | [every type of request FRED supports](https://fred.stlouisfed.org/docs/api/fred/): 16 | each query for Categories, Releases, Series, Sources, and Tags 17 | found within FRED's web service has a method associated with it in `full_fred`. 18 | `full_fred` minimizes redundant queries for the sake of users and FRED's servers. 19 | After a request for data is made to FRED web service the retrieved data 20 | is stored in a dictionary, accessible and fungible 21 | 22 | ## Installation 23 | pip install full-fred 24 | 25 | ## Testing 26 | ```full_fred``` requires ```pytest```. Tests can be run with ```FRED_API_KEY``` environment variable set and: 27 | 28 | ```python 29 | pytest 30 | ``` 31 | 32 | ## Usage 33 | 34 | ### API Key 35 | Queries to FRED web service require an API key. FRED has [free API keys available with an account (also free)](https://research.stlouisfed.org/useraccount/apikey) 36 | 37 | You can tell ```full_fred``` about an api key in 2 secure ways: 38 | 1. fred.api_key_file can be set by passing it to the constructor 39 | ```python 40 | In [4]: from full_fred.fred import Fred 41 | 42 | In [5]: fred = Fred('example_key.txt') 43 | 44 | In [6]: fred.get_api_key_file() 45 | Out[6]: 'example_key.txt' 46 | ``` 47 | This will set it too 48 | ```python 49 | In [3]: fred.set_api_key_file('example_key.txt') 50 | Out[3]: True 51 | ``` 52 | If the file assigned to ```api_key_file``` can't be found, ```full_fred``` will say so immediately if api_key_file is set using 53 | the surefire ```fred.set_api_key_file()``` 54 | 55 | 2. FRED_API_KEY Environment Variable 56 | 57 | ```full_fred``` will automatically detect your api key if it's assigned to an environment variable named ```FRED_API_KEY```. 58 | To check that FRED_API_KEY environment variable is detected, you can use 59 | 60 | ```python 61 | In [7]: fred.env_api_key_found() 62 | Out[7]: True 63 | ``` 64 | 65 | ```full_fred``` does not store your api key in an attribute for the sake of security: to send queries to FRED's databases, ```full_fred``` uses the value of 66 | FRED_API_KEY environment variable or the first line of fred.api_key_file 67 | 68 | ### Fetching data 69 | 70 | A pandas DataFrame stores observations when a request for data values is made 71 | 72 | ```python 73 | fred.get_series_df('GDPPOT') 74 | realtime_start realtime_end date value 75 | 0 2021-04-03 2021-04-03 1949-01-01 2103.179936 76 | 1 2021-04-03 2021-04-03 1949-04-01 2130.7327210000003 77 | 2 2021-04-03 2021-04-03 1949-07-01 2159.4478710000003 78 | 3 2021-04-03 2021-04-03 1949-10-01 2186.907265 79 | 4 2021-04-03 2021-04-03 1950-01-01 2216.07306 80 | .. ... ... ... ... 81 | 327 2021-04-03 2021-04-03 2030-10-01 23219.35 82 | 328 2021-04-03 2021-04-03 2031-01-01 23318.31 83 | 329 2021-04-03 2021-04-03 2031-04-01 23417.38 84 | 330 2021-04-03 2021-04-03 2031-07-01 23516.38 85 | 331 2021-04-03 2021-04-03 2031-10-01 23615.28 86 | 87 | [332 rows x 4 columns] 88 | ``` 89 | The fetched data is stored in fred.series_stack (see __Accessing fetched data__ section for more on retrieving queried data) 90 | 91 | ```python 92 | fred.series_stack['get_series_df'] 93 | {'realtime_start': '2021-04-03', 94 | 'realtime_end': '2021-04-03', 95 | 'observation_start': '1600-01-01', 96 | 'observation_end': '9999-12-31', 97 | 'units': 'lin', 98 | 'output_type': 1, 99 | 'file_type': 'json', 100 | 'order_by': 'observation_date', 101 | 'sort_order': 'asc', 102 | 'count': 332, 103 | 'offset': 0, 104 | 'limit': 100000, 105 | 'series_id': 'GDPPOT', 106 | 'df': 107 | realtime_start realtime_end date value 108 | 0 2021-04-03 2021-04-03 1949-01-01 2103.179936 109 | 1 2021-04-03 2021-04-03 1949-04-01 2130.7327210000003 110 | 2 2021-04-03 2021-04-03 1949-07-01 2159.4478710000003 111 | 3 2021-04-03 2021-04-03 1949-10-01 2186.907265 112 | 4 2021-04-03 2021-04-03 1950-01-01 2216.07306 113 | .. ... ... ... ... 114 | 327 2021-04-03 2021-04-03 2030-10-01 23219.35 115 | 328 2021-04-03 2021-04-03 2031-01-01 23318.31 116 | 329 2021-04-03 2021-04-03 2031-04-01 23417.38 117 | 330 2021-04-03 2021-04-03 2031-07-01 23516.38 118 | 331 2021-04-03 2021-04-03 2031-10-01 23615.28 119 | 120 | [332 rows x 4 columns]} 121 | ``` 122 | 123 | 124 | To find a specific category_id or to search FRED categories from 125 | most general to most specific start with the root category 0. 126 | A search along the lines of the following can help to pinpoint different 127 | category_ids: 128 | 129 | ```python 130 | In [4]: fred.get_child_categories(0) 131 | Out[4]: 132 | {'categories': [{'id': 32991, 133 | 'name': 'Money, Banking, & Finance', 134 | 'parent_id': 0}, 135 | {'id': 10, 136 | 'name': 'Population, Employment, & Labor Markets', 137 | 'parent_id': 0}, 138 | {'id': 32992, 'name': 'National Accounts', 'parent_id': 0}, 139 | {'id': 1, 'name': 'Production & Business Activity', 'parent_id': 0}, 140 | {'id': 32455, 'name': 'Prices', 'parent_id': 0}, 141 | {'id': 32263, 'name': 'International Data', 'parent_id': 0}, 142 | {'id': 32213, 'name': 'Greenbook Projections', 'parent_id': 0}, 143 | {'id': 3008, 'name': 'U.S. Regional Data', 'parent_id': 0}, 144 | {'id': 33060, 'name': 'Academic Data', 'parent_id': 0}]} 145 | 146 | In [5]: fred.category_stack['get_child_categories'] 147 | Out[5]: 148 | {'categories': [{'id': 32991, 149 | 'name': 'Money, Banking, & Finance', 150 | 'parent_id': 0}, 151 | {'id': 10, 152 | 'name': 'Population, Employment, & Labor Markets', 153 | 'parent_id': 0}, 154 | {'id': 32992, 'name': 'National Accounts', 'parent_id': 0}, 155 | {'id': 1, 'name': 'Production & Business Activity', 'parent_id': 0}, 156 | {'id': 32455, 'name': 'Prices', 'parent_id': 0}, 157 | {'id': 32263, 'name': 'International Data', 'parent_id': 0}, 158 | {'id': 32213, 'name': 'Greenbook Projections', 'parent_id': 0}, 159 | {'id': 3008, 'name': 'U.S. Regional Data', 'parent_id': 0}, 160 | {'id': 33060, 'name': 'Academic Data', 'parent_id': 0}]} 161 | ``` 162 | 163 | The [whole gamut of requests on FRED web service](https://fred.stlouisfed.org/docs/api/fred/) is implemented. The example below 164 | is one among many other methods in the API, listed in the next section 165 | 166 | ```python 167 | In [1]: from full_fred.fred import Fred 168 | 169 | In [2]: fred = Fred() 170 | 171 | In [3]: fred.get_series_vintagedates('FYFSD', limit = 15) 172 | Out[3]: 173 | {'realtime_start': '1776-07-04', 174 | 'realtime_end': '9999-12-31', 175 | 'order_by': 'vintage_date', 176 | 'sort_order': 'asc', 177 | 'count': 46, 178 | 'offset': 0, 179 | 'limit': 15, 180 | 'vintage_dates': [ 181 | '1998-02-02', 182 | '1998-10-26', 183 | '1999-02-01', 184 | '1999-10-25', 185 | '2000-02-07', 186 | '2000-10-20', 187 | '2001-04-09', 188 | '2001-10-24', 189 | '2002-02-04', 190 | '2002-10-23', 191 | '2003-02-03', 192 | '2003-10-15', 193 | '2004-02-02', 194 | '2004-10-12', 195 | '2005-02-23']} 196 | 197 | In [4]: fred.series_stack['get_series_vintagedates'] 198 | Out[4]: 199 | {'realtime_start': '1776-07-04', 200 | 'realtime_end': '9999-12-31', 201 | 'order_by': 'vintage_date', 202 | 'sort_order': 'asc', 203 | 'count': 46, 204 | 'offset': 0, 205 | 'limit': 15, 206 | 'vintage_dates': [ 207 | '1998-02-02', 208 | '1998-10-26', 209 | '1999-02-01', 210 | '1999-10-25', 211 | '2000-02-07', 212 | '2000-10-20', 213 | '2001-04-09', 214 | '2001-10-24', 215 | '2002-02-04', 216 | '2002-10-23', 217 | '2003-02-03', 218 | '2003-10-15', 219 | '2004-02-02', 220 | '2004-10-12', 221 | '2005-02-23']} 222 | ``` 223 | 224 | ### Accessing fetched data 225 | 226 | There are 5 stacks: 227 | 228 | ```fred.category_stack``` 229 | ```fred.release_stack``` 230 | ```fred.series_stack``` 231 | ```fred.source_stack``` 232 | ```fred.tag_stack``` 233 | 234 | After a method is called the returned data is stored using the method name for its key 235 | 236 | Methods that store data in category stack: 237 | ```python 238 | fred.category_stack["get_a_category"] 239 | fred.category_stack["get_child_categories"] 240 | fred.category_stack["get_related_categories"] 241 | fred.category_stack["get_series_in_a_category"] 242 | fred.category_stack["get_tags_for_a_category"] 243 | fred.category_stack["get_related_tags_for_a_category"] 244 | ``` 245 | 246 | Methods that store data in release stack: 247 | ```python 248 | fred.release_stack["get_a_release"] 249 | fred.release_stack["get_tags_for_a_release"] 250 | fred.release_stack["get_series_on_a_release"] 251 | fred.release_stack["get_sources_for_a_release"] 252 | fred.release_stack["get_related_tags_for_release"] 253 | fred.release_stack["get_release_dates_all_releases"] 254 | fred.release_stack["get_release_tables"] 255 | fred.release_stack["get_release_dates"] 256 | fred.release_stack["get_all_releases"] 257 | ``` 258 | 259 | Methods that store data in series stack: 260 | ```python 261 | fred.series_stack["get_a_series"] 262 | fred.series_stack["get_categories_of_series"] 263 | fred.series_stack["get_series_df"] 264 | fred.series_stack["get_release_for_a_series"] 265 | fred.series_stack["search_for_series"] 266 | fred.series_stack["get_tags_for_series_search"] 267 | fred.series_stack["get_related_tags_for_series_search"] 268 | fred.series_stack["get_tags_for_a_series"] 269 | fred.series_stack["get_series_updates"] 270 | fred.series_stack["get_series_vintagedates"] 271 | ``` 272 | 273 | Methods that store data in source stack: 274 | ```python 275 | fred.source_stack["get_all_sources"] 276 | fred.source_stack["get_releases_for_a_source"] 277 | fred.source_stack["get_a_source"] 278 | ``` 279 | 280 | Methods that store data in tag stack: 281 | ```python 282 | fred.tag_stack["get_all_tags"] 283 | fred.tag_stack["get_related_tags_for_a_tag"] 284 | fred.tag_stack["get_series_matching_tags"] 285 | ``` 286 | 287 | ### full_fred realtime period and observation start/end defaults 288 | By default ```fred.realtime_start``` and ```fred.realtime_end``` are set to None. 289 | realtime_start and realtime_end arguments override ```fred.realtime_start``` and ```fred.realtime_end```. 290 | 291 | ```fred.observation_start``` and ```fred.observation_end``` are also None by default. 292 | observation_start and observation_end arguments override ```fred.observation_start``` and ```fred.observation_end```. 293 | 294 | ## Contributing 295 | The ```full_fred``` project welcomes feature requests, bug reports, bug fixes, documentation improvements, contributions of all kinds. 296 | ```full_fred``` aims to be responsive in integrating patches and listening to your feedback to be a community-driven API. 297 | This project is also new and while ```full_fred``` is still young there's great opportunity to contribute elements that may have disproportionate 298 | impact in the long run 299 | 300 | ## License 301 | Apache v2.0 302 | -------------------------------------------------------------------------------- /full_fred/__init__.py: -------------------------------------------------------------------------------- 1 | from . import _version 2 | 3 | __version__ = _version.get_versions()["version"] 4 | -------------------------------------------------------------------------------- /full_fred/_version.py: -------------------------------------------------------------------------------- 1 | # This file helps to compute a version number in source trees obtained from 2 | # git-archive tarball (such as those provided by githubs download-from-tag 3 | # feature). Distribution tarballs (built by setup.py sdist) and build 4 | # directories (produced by setup.py build) will contain a much shorter file 5 | # that just contains the computed version number. 6 | 7 | # This file is released into the public domain. Generated by 8 | # versioneer-0.21 (https://github.com/python-versioneer/python-versioneer) 9 | 10 | """Git implementation of _version.py.""" 11 | 12 | import errno 13 | import os 14 | import re 15 | import subprocess 16 | import sys 17 | from typing import Callable, Dict 18 | 19 | 20 | def get_keywords(): 21 | """Get the keywords needed to look up the version information.""" 22 | # these strings will be replaced by git during git-archive. 23 | # setup.py/versioneer.py will grep for the variable names, so they must 24 | # each be defined on a line of their own. _version.py will just call 25 | # get_keywords(). 26 | git_refnames = " (HEAD -> master)" 27 | git_full = "7949411ed775ccbef9922572b9e76a4da0238ce4" 28 | git_date = "2025-06-02 10:35:06 -0500" 29 | keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} 30 | return keywords 31 | 32 | 33 | class VersioneerConfig: 34 | """Container for Versioneer configuration parameters.""" 35 | 36 | 37 | def get_config(): 38 | """Create, populate and return the VersioneerConfig() object.""" 39 | # these strings are filled in when 'setup.py versioneer' creates 40 | # _version.py 41 | cfg = VersioneerConfig() 42 | cfg.VCS = "git" 43 | cfg.style = "pep440" 44 | cfg.tag_prefix = "v" 45 | cfg.parentdir_prefix = "full_fred-" 46 | cfg.versionfile_source = "full_fred/_version.py" 47 | cfg.verbose = False 48 | return cfg 49 | 50 | 51 | class NotThisMethod(Exception): 52 | """Exception raised if a method is not valid for the current scenario.""" 53 | 54 | 55 | LONG_VERSION_PY: Dict[str, str] = {} 56 | HANDLERS: Dict[str, Dict[str, Callable]] = {} 57 | 58 | 59 | def register_vcs_handler(vcs, method): # decorator 60 | """Create decorator to mark a method as the handler of a VCS.""" 61 | 62 | def decorate(f): 63 | """Store f in HANDLERS[vcs][method].""" 64 | if vcs not in HANDLERS: 65 | HANDLERS[vcs] = {} 66 | HANDLERS[vcs][method] = f 67 | return f 68 | 69 | return decorate 70 | 71 | 72 | def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): 73 | """Call the given command(s).""" 74 | assert isinstance(commands, list) 75 | process = None 76 | for command in commands: 77 | try: 78 | dispcmd = str([command] + args) 79 | # remember shell=False, so use git.cmd on windows, not just git 80 | process = subprocess.Popen( 81 | [command] + args, 82 | cwd=cwd, 83 | env=env, 84 | stdout=subprocess.PIPE, 85 | stderr=(subprocess.PIPE if hide_stderr else None), 86 | ) 87 | break 88 | except OSError: 89 | e = sys.exc_info()[1] 90 | if e.errno == errno.ENOENT: 91 | continue 92 | if verbose: 93 | print("unable to run %s" % dispcmd) 94 | print(e) 95 | return None, None 96 | else: 97 | if verbose: 98 | print("unable to find command, tried %s" % (commands,)) 99 | return None, None 100 | stdout = process.communicate()[0].strip().decode() 101 | if process.returncode != 0: 102 | if verbose: 103 | print("unable to run %s (error)" % dispcmd) 104 | print("stdout was %s" % stdout) 105 | return None, process.returncode 106 | return stdout, process.returncode 107 | 108 | 109 | def versions_from_parentdir(parentdir_prefix, root, verbose): 110 | """Try to determine the version from the parent directory name. 111 | 112 | Source tarballs conventionally unpack into a directory that includes both 113 | the project name and a version string. We will also support searching up 114 | two directory levels for an appropriately named parent directory 115 | """ 116 | rootdirs = [] 117 | 118 | for _ in range(3): 119 | dirname = os.path.basename(root) 120 | if dirname.startswith(parentdir_prefix): 121 | return { 122 | "version": dirname[len(parentdir_prefix) :], 123 | "full-revisionid": None, 124 | "dirty": False, 125 | "error": None, 126 | "date": None, 127 | } 128 | rootdirs.append(root) 129 | root = os.path.dirname(root) # up a level 130 | 131 | if verbose: 132 | print( 133 | "Tried directories %s but none started with prefix %s" 134 | % (str(rootdirs), parentdir_prefix) 135 | ) 136 | raise NotThisMethod("rootdir doesn't start with parentdir_prefix") 137 | 138 | 139 | @register_vcs_handler("git", "get_keywords") 140 | def git_get_keywords(versionfile_abs): 141 | """Extract version information from the given file.""" 142 | # the code embedded in _version.py can just fetch the value of these 143 | # keywords. When used from setup.py, we don't want to import _version.py, 144 | # so we do it with a regexp instead. This function is not used from 145 | # _version.py. 146 | keywords = {} 147 | try: 148 | with open(versionfile_abs, "r") as fobj: 149 | for line in fobj: 150 | if line.strip().startswith("git_refnames ="): 151 | mo = re.search(r'=\s*"(.*)"', line) 152 | if mo: 153 | keywords["refnames"] = mo.group(1) 154 | if line.strip().startswith("git_full ="): 155 | mo = re.search(r'=\s*"(.*)"', line) 156 | if mo: 157 | keywords["full"] = mo.group(1) 158 | if line.strip().startswith("git_date ="): 159 | mo = re.search(r'=\s*"(.*)"', line) 160 | if mo: 161 | keywords["date"] = mo.group(1) 162 | except OSError: 163 | pass 164 | return keywords 165 | 166 | 167 | @register_vcs_handler("git", "keywords") 168 | def git_versions_from_keywords(keywords, tag_prefix, verbose): 169 | """Get version information from git keywords.""" 170 | if "refnames" not in keywords: 171 | raise NotThisMethod("Short version file found") 172 | date = keywords.get("date") 173 | if date is not None: 174 | # Use only the last line. Previous lines may contain GPG signature 175 | # information. 176 | date = date.splitlines()[-1] 177 | 178 | # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant 179 | # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 180 | # -like" string, which we must then edit to make compliant), because 181 | # it's been around since git-1.5.3, and it's too difficult to 182 | # discover which version we're using, or to work around using an 183 | # older one. 184 | date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 185 | refnames = keywords["refnames"].strip() 186 | if refnames.startswith("$Format"): 187 | if verbose: 188 | print("keywords are unexpanded, not using") 189 | raise NotThisMethod("unexpanded keywords, not a git-archive tarball") 190 | refs = {r.strip() for r in refnames.strip("()").split(",")} 191 | # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of 192 | # just "foo-1.0". If we see a "tag: " prefix, prefer those. 193 | TAG = "tag: " 194 | tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} 195 | if not tags: 196 | # Either we're using git < 1.8.3, or there really are no tags. We use 197 | # a heuristic: assume all version tags have a digit. The old git %d 198 | # expansion behaves like git log --decorate=short and strips out the 199 | # refs/heads/ and refs/tags/ prefixes that would let us distinguish 200 | # between branches and tags. By ignoring refnames without digits, we 201 | # filter out many common branch names like "release" and 202 | # "stabilization", as well as "HEAD" and "master". 203 | tags = {r for r in refs if re.search(r"\d", r)} 204 | if verbose: 205 | print("discarding '%s', no digits" % ",".join(refs - tags)) 206 | if verbose: 207 | print("likely tags: %s" % ",".join(sorted(tags))) 208 | for ref in sorted(tags): 209 | # sorting will prefer e.g. "2.0" over "2.0rc1" 210 | if ref.startswith(tag_prefix): 211 | r = ref[len(tag_prefix) :] 212 | # Filter out refs that exactly match prefix or that don't start 213 | # with a number once the prefix is stripped (mostly a concern 214 | # when prefix is '') 215 | if not re.match(r"\d", r): 216 | continue 217 | if verbose: 218 | print("picking %s" % r) 219 | return { 220 | "version": r, 221 | "full-revisionid": keywords["full"].strip(), 222 | "dirty": False, 223 | "error": None, 224 | "date": date, 225 | } 226 | # no suitable tags, so version is "0+unknown", but full hex is still there 227 | if verbose: 228 | print("no suitable tags, using unknown + full revision id") 229 | return { 230 | "version": "0+unknown", 231 | "full-revisionid": keywords["full"].strip(), 232 | "dirty": False, 233 | "error": "no suitable tags", 234 | "date": None, 235 | } 236 | 237 | 238 | @register_vcs_handler("git", "pieces_from_vcs") 239 | def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): 240 | """Get version from 'git describe' in the root of the source tree. 241 | 242 | This only gets called if the git-archive 'subst' keywords were *not* 243 | expanded, and _version.py hasn't already been rewritten with a short 244 | version string, meaning we're inside a checked out source tree. 245 | """ 246 | GITS = ["git"] 247 | TAG_PREFIX_REGEX = "*" 248 | if sys.platform == "win32": 249 | GITS = ["git.cmd", "git.exe"] 250 | TAG_PREFIX_REGEX = r"\*" 251 | 252 | _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) 253 | if rc != 0: 254 | if verbose: 255 | print("Directory %s not under git control" % root) 256 | raise NotThisMethod("'git rev-parse --git-dir' returned error") 257 | 258 | # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] 259 | # if there isn't one, this yields HEX[-dirty] (no NUM) 260 | describe_out, rc = runner( 261 | GITS, 262 | [ 263 | "describe", 264 | "--tags", 265 | "--dirty", 266 | "--always", 267 | "--long", 268 | "--match", 269 | "%s%s" % (tag_prefix, TAG_PREFIX_REGEX), 270 | ], 271 | cwd=root, 272 | ) 273 | # --long was added in git-1.5.5 274 | if describe_out is None: 275 | raise NotThisMethod("'git describe' failed") 276 | describe_out = describe_out.strip() 277 | full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) 278 | if full_out is None: 279 | raise NotThisMethod("'git rev-parse' failed") 280 | full_out = full_out.strip() 281 | 282 | pieces = {} 283 | pieces["long"] = full_out 284 | pieces["short"] = full_out[:7] # maybe improved later 285 | pieces["error"] = None 286 | 287 | branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) 288 | # --abbrev-ref was added in git-1.6.3 289 | if rc != 0 or branch_name is None: 290 | raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") 291 | branch_name = branch_name.strip() 292 | 293 | if branch_name == "HEAD": 294 | # If we aren't exactly on a branch, pick a branch which represents 295 | # the current commit. If all else fails, we are on a branchless 296 | # commit. 297 | branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) 298 | # --contains was added in git-1.5.4 299 | if rc != 0 or branches is None: 300 | raise NotThisMethod("'git branch --contains' returned error") 301 | branches = branches.split("\n") 302 | 303 | # Remove the first line if we're running detached 304 | if "(" in branches[0]: 305 | branches.pop(0) 306 | 307 | # Strip off the leading "* " from the list of branches. 308 | branches = [branch[2:] for branch in branches] 309 | if "master" in branches: 310 | branch_name = "master" 311 | elif not branches: 312 | branch_name = None 313 | else: 314 | # Pick the first branch that is returned. Good or bad. 315 | branch_name = branches[0] 316 | 317 | pieces["branch"] = branch_name 318 | 319 | # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] 320 | # TAG might have hyphens. 321 | git_describe = describe_out 322 | 323 | # look for -dirty suffix 324 | dirty = git_describe.endswith("-dirty") 325 | pieces["dirty"] = dirty 326 | if dirty: 327 | git_describe = git_describe[: git_describe.rindex("-dirty")] 328 | 329 | # now we have TAG-NUM-gHEX or HEX 330 | 331 | if "-" in git_describe: 332 | # TAG-NUM-gHEX 333 | mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) 334 | if not mo: 335 | # unparsable. Maybe git-describe is misbehaving? 336 | pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out 337 | return pieces 338 | 339 | # tag 340 | full_tag = mo.group(1) 341 | if not full_tag.startswith(tag_prefix): 342 | if verbose: 343 | fmt = "tag '%s' doesn't start with prefix '%s'" 344 | print(fmt % (full_tag, tag_prefix)) 345 | pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( 346 | full_tag, 347 | tag_prefix, 348 | ) 349 | return pieces 350 | pieces["closest-tag"] = full_tag[len(tag_prefix) :] 351 | 352 | # distance: number of commits since tag 353 | pieces["distance"] = int(mo.group(2)) 354 | 355 | # commit: short hex revision ID 356 | pieces["short"] = mo.group(3) 357 | 358 | else: 359 | # HEX: no tags 360 | pieces["closest-tag"] = None 361 | count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) 362 | pieces["distance"] = int(count_out) # total number of commits 363 | 364 | # commit date: see ISO-8601 comment in git_versions_from_keywords() 365 | date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() 366 | # Use only the last line. Previous lines may contain GPG signature 367 | # information. 368 | date = date.splitlines()[-1] 369 | pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 370 | 371 | return pieces 372 | 373 | 374 | def plus_or_dot(pieces): 375 | """Return a + if we don't already have one, else return a .""" 376 | if "+" in pieces.get("closest-tag", ""): 377 | return "." 378 | return "+" 379 | 380 | 381 | def render_pep440(pieces): 382 | """Build up version string, with post-release "local version identifier". 383 | 384 | Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you 385 | get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty 386 | 387 | Exceptions: 388 | 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] 389 | """ 390 | if pieces["closest-tag"]: 391 | rendered = pieces["closest-tag"] 392 | if pieces["distance"] or pieces["dirty"]: 393 | rendered += plus_or_dot(pieces) 394 | rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 395 | if pieces["dirty"]: 396 | rendered += ".dirty" 397 | else: 398 | # exception #1 399 | rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) 400 | if pieces["dirty"]: 401 | rendered += ".dirty" 402 | return rendered 403 | 404 | 405 | def render_pep440_branch(pieces): 406 | """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . 407 | 408 | The ".dev0" means not master branch. Note that .dev0 sorts backwards 409 | (a feature branch will appear "older" than the master branch). 410 | 411 | Exceptions: 412 | 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] 413 | """ 414 | if pieces["closest-tag"]: 415 | rendered = pieces["closest-tag"] 416 | if pieces["distance"] or pieces["dirty"]: 417 | if pieces["branch"] != "master": 418 | rendered += ".dev0" 419 | rendered += plus_or_dot(pieces) 420 | rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 421 | if pieces["dirty"]: 422 | rendered += ".dirty" 423 | else: 424 | # exception #1 425 | rendered = "0" 426 | if pieces["branch"] != "master": 427 | rendered += ".dev0" 428 | rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) 429 | if pieces["dirty"]: 430 | rendered += ".dirty" 431 | return rendered 432 | 433 | 434 | def pep440_split_post(ver): 435 | """Split pep440 version string at the post-release segment. 436 | 437 | Returns the release segments before the post-release and the 438 | post-release version number (or -1 if no post-release segment is present). 439 | """ 440 | vc = str.split(ver, ".post") 441 | return vc[0], int(vc[1] or 0) if len(vc) == 2 else None 442 | 443 | 444 | def render_pep440_pre(pieces): 445 | """TAG[.postN.devDISTANCE] -- No -dirty. 446 | 447 | Exceptions: 448 | 1: no tags. 0.post0.devDISTANCE 449 | """ 450 | if pieces["closest-tag"]: 451 | if pieces["distance"]: 452 | # update the post release segment 453 | tag_version, post_version = pep440_split_post(pieces["closest-tag"]) 454 | rendered = tag_version 455 | if post_version is not None: 456 | rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) 457 | else: 458 | rendered += ".post0.dev%d" % (pieces["distance"]) 459 | else: 460 | # no commits, use the tag as the version 461 | rendered = pieces["closest-tag"] 462 | else: 463 | # exception #1 464 | rendered = "0.post0.dev%d" % pieces["distance"] 465 | return rendered 466 | 467 | 468 | def render_pep440_post(pieces): 469 | """TAG[.postDISTANCE[.dev0]+gHEX] . 470 | 471 | The ".dev0" means dirty. Note that .dev0 sorts backwards 472 | (a dirty tree will appear "older" than the corresponding clean one), 473 | but you shouldn't be releasing software with -dirty anyways. 474 | 475 | Exceptions: 476 | 1: no tags. 0.postDISTANCE[.dev0] 477 | """ 478 | if pieces["closest-tag"]: 479 | rendered = pieces["closest-tag"] 480 | if pieces["distance"] or pieces["dirty"]: 481 | rendered += ".post%d" % pieces["distance"] 482 | if pieces["dirty"]: 483 | rendered += ".dev0" 484 | rendered += plus_or_dot(pieces) 485 | rendered += "g%s" % pieces["short"] 486 | else: 487 | # exception #1 488 | rendered = "0.post%d" % pieces["distance"] 489 | if pieces["dirty"]: 490 | rendered += ".dev0" 491 | rendered += "+g%s" % pieces["short"] 492 | return rendered 493 | 494 | 495 | def render_pep440_post_branch(pieces): 496 | """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . 497 | 498 | The ".dev0" means not master branch. 499 | 500 | Exceptions: 501 | 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] 502 | """ 503 | if pieces["closest-tag"]: 504 | rendered = pieces["closest-tag"] 505 | if pieces["distance"] or pieces["dirty"]: 506 | rendered += ".post%d" % pieces["distance"] 507 | if pieces["branch"] != "master": 508 | rendered += ".dev0" 509 | rendered += plus_or_dot(pieces) 510 | rendered += "g%s" % pieces["short"] 511 | if pieces["dirty"]: 512 | rendered += ".dirty" 513 | else: 514 | # exception #1 515 | rendered = "0.post%d" % pieces["distance"] 516 | if pieces["branch"] != "master": 517 | rendered += ".dev0" 518 | rendered += "+g%s" % pieces["short"] 519 | if pieces["dirty"]: 520 | rendered += ".dirty" 521 | return rendered 522 | 523 | 524 | def render_pep440_old(pieces): 525 | """TAG[.postDISTANCE[.dev0]] . 526 | 527 | The ".dev0" means dirty. 528 | 529 | Exceptions: 530 | 1: no tags. 0.postDISTANCE[.dev0] 531 | """ 532 | if pieces["closest-tag"]: 533 | rendered = pieces["closest-tag"] 534 | if pieces["distance"] or pieces["dirty"]: 535 | rendered += ".post%d" % pieces["distance"] 536 | if pieces["dirty"]: 537 | rendered += ".dev0" 538 | else: 539 | # exception #1 540 | rendered = "0.post%d" % pieces["distance"] 541 | if pieces["dirty"]: 542 | rendered += ".dev0" 543 | return rendered 544 | 545 | 546 | def render_git_describe(pieces): 547 | """TAG[-DISTANCE-gHEX][-dirty]. 548 | 549 | Like 'git describe --tags --dirty --always'. 550 | 551 | Exceptions: 552 | 1: no tags. HEX[-dirty] (note: no 'g' prefix) 553 | """ 554 | if pieces["closest-tag"]: 555 | rendered = pieces["closest-tag"] 556 | if pieces["distance"]: 557 | rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 558 | else: 559 | # exception #1 560 | rendered = pieces["short"] 561 | if pieces["dirty"]: 562 | rendered += "-dirty" 563 | return rendered 564 | 565 | 566 | def render_git_describe_long(pieces): 567 | """TAG-DISTANCE-gHEX[-dirty]. 568 | 569 | Like 'git describe --tags --dirty --always -long'. 570 | The distance/hash is unconditional. 571 | 572 | Exceptions: 573 | 1: no tags. HEX[-dirty] (note: no 'g' prefix) 574 | """ 575 | if pieces["closest-tag"]: 576 | rendered = pieces["closest-tag"] 577 | rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 578 | else: 579 | # exception #1 580 | rendered = pieces["short"] 581 | if pieces["dirty"]: 582 | rendered += "-dirty" 583 | return rendered 584 | 585 | 586 | def render(pieces, style): 587 | """Render the given version pieces into the requested style.""" 588 | if pieces["error"]: 589 | return { 590 | "version": "unknown", 591 | "full-revisionid": pieces.get("long"), 592 | "dirty": None, 593 | "error": pieces["error"], 594 | "date": None, 595 | } 596 | 597 | if not style or style == "default": 598 | style = "pep440" # the default 599 | 600 | if style == "pep440": 601 | rendered = render_pep440(pieces) 602 | elif style == "pep440-branch": 603 | rendered = render_pep440_branch(pieces) 604 | elif style == "pep440-pre": 605 | rendered = render_pep440_pre(pieces) 606 | elif style == "pep440-post": 607 | rendered = render_pep440_post(pieces) 608 | elif style == "pep440-post-branch": 609 | rendered = render_pep440_post_branch(pieces) 610 | elif style == "pep440-old": 611 | rendered = render_pep440_old(pieces) 612 | elif style == "git-describe": 613 | rendered = render_git_describe(pieces) 614 | elif style == "git-describe-long": 615 | rendered = render_git_describe_long(pieces) 616 | else: 617 | raise ValueError("unknown style '%s'" % style) 618 | 619 | return { 620 | "version": rendered, 621 | "full-revisionid": pieces["long"], 622 | "dirty": pieces["dirty"], 623 | "error": None, 624 | "date": pieces.get("date"), 625 | } 626 | 627 | 628 | def get_versions(): 629 | """Get version information or return default if unable to do so.""" 630 | # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have 631 | # __file__, we can work backwards from there to the root. Some 632 | # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which 633 | # case we can only use expanded keywords. 634 | 635 | cfg = get_config() 636 | verbose = cfg.verbose 637 | 638 | try: 639 | return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) 640 | except NotThisMethod: 641 | pass 642 | 643 | try: 644 | root = os.path.realpath(__file__) 645 | # versionfile_source is the relative path from the top of the source 646 | # tree (where the .git directory might live) to this file. Invert 647 | # this to find the root from __file__. 648 | for _ in cfg.versionfile_source.split("/"): 649 | root = os.path.dirname(root) 650 | except NameError: 651 | return { 652 | "version": "0+unknown", 653 | "full-revisionid": None, 654 | "dirty": None, 655 | "error": "unable to find root of source tree", 656 | "date": None, 657 | } 658 | 659 | try: 660 | pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) 661 | return render(pieces, cfg.style) 662 | except NotThisMethod: 663 | pass 664 | 665 | try: 666 | if cfg.parentdir_prefix: 667 | return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) 668 | except NotThisMethod: 669 | pass 670 | 671 | return { 672 | "version": "0+unknown", 673 | "full-revisionid": None, 674 | "dirty": None, 675 | "error": "unable to compute version", 676 | "date": None, 677 | } 678 | -------------------------------------------------------------------------------- /full_fred/categories.py: -------------------------------------------------------------------------------- 1 | from .fred_base import FredBase 2 | 3 | 4 | class Categories(FredBase): 5 | 6 | def __init__(self): 7 | """""" 8 | super().__init__() 9 | self.category_stack = dict() 10 | 11 | def get_a_category( 12 | self, 13 | category_id: int = None, 14 | ) -> dict: 15 | """ 16 | Get a category of FRED data. 17 | 18 | Parameters 19 | ---------- 20 | category_id: int, default None 21 | The ID of the category. 22 | If None, root category_id of 0 is used. 23 | 24 | Returns 25 | ------- 26 | dict 27 | Metadata of category: ID, name, parent_id 28 | 29 | See Also 30 | -------- 31 | fred.get_child_categories: Get specific categories within a category. 32 | fred.get_related_categories: Get similar categories that aren't part of a child-parent link. 33 | 34 | Notes 35 | ----- 36 | FRED web service endpoint: fred/category 37 | https://fred.stlouisfed.org/docs/api/fred/category.html 38 | 39 | Examples 40 | -------- 41 | >>> fred.get_a_category(32991) 42 | {'categories': [{'id': 32991, 43 | 'name': 'Money, Banking, & Finance', 44 | 'parent_id': 0}]} 45 | 46 | >>> fred.get_a_category(0) 47 | >>> fred.category_stack['get_a_category'] 48 | {'categories': [{'id': 0, 'name': 'Categories', 'parent_id': 0}]} 49 | """ 50 | self._viable_api_key() 51 | url_prefix_params = { 52 | "a_url_prefix": "category?category_id=", 53 | "an_int_id": category_id, 54 | } 55 | if url_prefix_params["an_int_id"] is None: 56 | url_prefix_params["an_int_id"] = 0 57 | url = self._append_id_to_url(**url_prefix_params) 58 | self.category_stack["get_a_category"] = self._fetch_data(url) 59 | return self.category_stack["get_a_category"] 60 | 61 | def get_child_categories( 62 | self, 63 | category_id: int, 64 | realtime_start: str = None, 65 | realtime_end: str = None, 66 | ) -> dict: 67 | """ 68 | Get child categories of a category. 69 | 70 | Parameters 71 | ---------- 72 | category_id: int, default None 73 | The ID of the category. 74 | If None, root category_id of 0 is used. 75 | realtime_start: str, default None 76 | The start of the real-time period formatted as "YYYY-MM-DD". 77 | If None, fred.realtime_start is used. 78 | If fred.realtime_start = None, FRED web service will use today's date. 79 | realtime_end: str, default None 80 | The end of the real-time period formatted as "YYYY-MM-DD". 81 | If None, fred.realtime_end is used. 82 | If fred.realtime_end = None, FRED web service will use today's date. 83 | 84 | Returns 85 | ------- 86 | dict 87 | ID, name, parent_id for each child category. 88 | 89 | See Also 90 | -------- 91 | fred.get_related_categories: Get similar categories that aren't part of a child-parent link. 92 | 93 | Notes 94 | ----- 95 | FRED web service endpoint: fred/category/children 96 | https://fred.stlouisfed.org/docs/api/fred/category_children.html 97 | 98 | Examples 99 | -------- 100 | >>> fred.get_child_categories(0) 101 | {'categories': [{'id': 32991, 102 | 'name': 'Money, Banking, & Finance', 103 | 'parent_id': 0}, 104 | {'id': 10, 105 | 'name': 'Population, Employment, & Labor Markets', 106 | 'parent_id': 0}, 107 | {'id': 32992, 'name': 'National Accounts', 'parent_id': 0}, 108 | {'id': 1, 'name': 'Production & Business Activity', 'parent_id': 0}, 109 | {'id': 32455, 'name': 'Prices', 'parent_id': 0}, 110 | {'id': 32263, 'name': 'International Data', 'parent_id': 0}, 111 | {'id': 3008, 'name': 'U.S. Regional Data', 'parent_id': 0}, 112 | {'id': 33060, 'name': 'Academic Data', 'parent_id': 0}]} 113 | """ 114 | self._viable_api_key() 115 | url_prefix_params = { 116 | "a_url_prefix": "category/children?category_id=", 117 | "an_int_id": category_id, 118 | } 119 | url_prefix = self._append_id_to_url(**url_prefix_params) 120 | optional_args = { 121 | "&realtime_start=": realtime_start, 122 | "&realtime_end=": realtime_end, 123 | } 124 | url = self._add_optional_params(url_prefix, optional_args) 125 | self.category_stack["get_child_categories"] = self._fetch_data(url) 126 | return self.category_stack["get_child_categories"] 127 | 128 | def get_related_categories( 129 | self, 130 | category_id: int, 131 | realtime_start: str = None, 132 | realtime_end: str = None, 133 | ): 134 | """ 135 | Get the related categories for a category. FRED web service 136 | defines a related category as a one-way relation between 2 137 | categories where neither of the 2 is the parent category of 138 | the other. According to FRED web service most categories 139 | don't have related categories. 140 | 141 | Parameters 142 | ---------- 143 | category_id: int 144 | The ID of the category. 145 | realtime_start: str, default None 146 | The start of the real-time period formatted as "YYYY-MM-DD". 147 | If None, fred.realtime_start is used. 148 | If fred.realtime_start = None, FRED web service will use today's date. 149 | realtime_end: str, default None 150 | The end of the real-time period formatted as "YYYY-MM-DD". 151 | If None, fred.realtime_end is used. 152 | If fred.realtime_end = None, FRED web service will use today's date. 153 | 154 | Returns 155 | ------- 156 | dict 157 | ID, name, parent_id of related categories. 158 | 159 | See Also 160 | -------- 161 | fred.get_child_categories: Get specific categories within a category. 162 | 163 | Notes 164 | ----- 165 | FRED web service endpoint: fred/category/related 166 | https://fred.stlouisfed.org/docs/api/fred/category_related.html 167 | 168 | Examples 169 | -------- 170 | >>> fred.get_related_categories(32073) 171 | {'categories': [{'id': 149, 'name': 'Arkansas', 'parent_id': 27281}, 172 | {'id': 150, 'name': 'Illinois', 'parent_id': 27281}, 173 | {'id': 151, 'name': 'Indiana', 'parent_id': 27281}, 174 | {'id': 152, 'name': 'Kentucky', 'parent_id': 27281}, 175 | {'id': 153, 'name': 'Mississippi', 'parent_id': 27281}, 176 | {'id': 154, 'name': 'Missouri', 'parent_id': 27281}, 177 | {'id': 193, 'name': 'Tennessee', 'parent_id': 27281}]} 178 | """ 179 | self._viable_api_key() 180 | url_prefix_params = { 181 | "a_url_prefix": "category/related?category_id=", 182 | "an_int_id": category_id, 183 | } 184 | url_prefix = self._append_id_to_url(**url_prefix_params) 185 | optional_args = { 186 | "&realtime_start=": realtime_start, 187 | "&realtime_end=": realtime_end, 188 | } 189 | url = self._add_optional_params(url_prefix, optional_args) 190 | self.category_stack["get_related_categories"] = self._fetch_data(url) 191 | return self.category_stack["get_related_categories"] 192 | 193 | def get_series_in_a_category( 194 | self, 195 | category_id: int, 196 | realtime_start: str = None, 197 | realtime_end: str = None, 198 | limit: int = None, 199 | offset: int = None, 200 | order_by: str = None, 201 | sort_order: str = None, 202 | filter_variable: str = None, 203 | filter_value: str = None, 204 | tag_names: list = None, 205 | exclude_tag_names: list = None, 206 | ): 207 | """ 208 | Get the series that belong to a category (metadata, not dataframes for each series). 209 | 210 | Parameters 211 | ---------- 212 | category_id: int 213 | The ID of the category. 214 | realtime_start: str, default None 215 | The start of the real-time period formatted as "YYYY-MM-DD". 216 | If None, fred.realtime_start is used. 217 | If fred.realtime_start = None, FRED web service will use today's date. 218 | realtime_end: str, default None 219 | The end of the real-time period formatted as "YYYY-MM-DD". 220 | If None, fred.realtime_end is used. 221 | If fred.realtime_end = None, FRED web service will use today's date. 222 | limit: int, default None 223 | The maximum number of results to return. 224 | Values can be in range(1, 1_001). 225 | If None, FRED will use limit = 1_001. 226 | offset: int, default None 227 | If None, offset of 0 is used. 228 | order_by: str, default None 229 | Order results by values of the specified attribute. 230 | Can be one of "series_id", "title", "units", "frequency", 231 | "seasonal_adjustment", "reatime_start", "realtime_end", 232 | "last_updated", "observation_start", "observation_end", 233 | "popularity", "group_popularity" 234 | If None, "series_id" is used. 235 | sort_order: str, default None 236 | Sort results in ascending or descending order for attribute values specified by order_by. 237 | Can be "asc" or "desc". 238 | If None, "asc" is used. 239 | filter_variable: str, default None 240 | The attribute to filter results by. 241 | Can be one of "frequency", "units", "seasonal_adjustment". 242 | If None, no filter variable is used. 243 | filter_value: str, default None 244 | The value of filter_variable to filter results by. 245 | tag_names: list, default None 246 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 247 | each tag must be present in the tag of returned series. 248 | If None, no filtering by tag names is done. 249 | exclude_tag_names: list, default None 250 | list of tag names that series match none of. 251 | If None, no filtering by tag names is done. 252 | 253 | Returns 254 | ------- 255 | dict 256 | Metadata of series that belong to category. 257 | 258 | Notes 259 | ----- 260 | FRED web service endpoint: fred/category/series 261 | https://fred.stlouisfed.org/docs/api/fred/category_series.html 262 | 263 | Examples 264 | -------- 265 | >>> params = { 266 | 'category_id': 125, 267 | 'limit': 3, 268 | 'filter_variable': 'units', 269 | 'order_by': 'units', 270 | 'sort_order': 'desc', 271 | 'offset': 1, 272 | } 273 | >>> fred.get_series_in_a_category(**params) 274 | {'realtime_start': '2021-04-05', 275 | 'realtime_end': '2021-04-05', 276 | 'filter_variable': 'units', 277 | 'filter_value': None, 278 | 'order_by': 'units', 279 | 'sort_order': 'desc', 280 | 'count': 47, 281 | 'offset': 1, 282 | 'limit': 3, 283 | 'seriess': [{'id': 'IEABCSIA', 284 | 'realtime_start': '2021-04-05', 285 | 'realtime_end': '2021-04-05', 286 | 'title': 'Balance on secondary income', 287 | 'observation_start': '1999-01-01', 288 | 'observation_end': '2020-01-01', 289 | 'frequency': 'Annual', 290 | 'frequency_short': 'A', 291 | 'units': 'Millions of Dollars', 292 | 'units_short': 'Mil. of $', 293 | 'seasonal_adjustment': 'Not Seasonally Adjusted', 294 | 'seasonal_adjustment_short': 'NSA', 295 | 'last_updated': '2021-03-23 07:31:14-05', 296 | 'popularity': 3, 297 | 'group_popularity': 4, 298 | 'notes': 'Calculated by subtracting the secondary income (current transfer) payments from the secondary income (current transfer) receipts'}, 299 | {'id': 'IEABCSIN', ....... 300 | """ 301 | self._viable_api_key() 302 | url_prefix_params = { 303 | "a_url_prefix": "category/series?category_id=", 304 | "an_int_id": category_id, 305 | } 306 | url_prefix = self._append_id_to_url(**url_prefix_params) 307 | optional_args = { 308 | "&realtime_start=": realtime_start, 309 | "&realtime_end=": realtime_end, 310 | "&limit=": limit, 311 | "&offset=": offset, 312 | "&order_by=": order_by, 313 | "&sort_order=": sort_order, 314 | "&filter_variable=": filter_variable, 315 | "&filter_value=": filter_value, 316 | "&tag_names=": tag_names, 317 | "&exclude_tag_names=": exclude_tag_names, 318 | } 319 | url = self._add_optional_params(url_prefix, optional_args) 320 | self.category_stack["get_series_in_a_category"] = self._fetch_data(url) 321 | return self.category_stack["get_series_in_a_category"] 322 | 323 | def get_tags_for_a_category( 324 | self, 325 | category_id: int, 326 | realtime_start: str = None, 327 | realtime_end: str = None, 328 | tag_names: list = None, 329 | tag_group_id: str = None, 330 | search_text: str = None, 331 | limit: int = None, 332 | offset: int = None, 333 | order_by: str = None, 334 | sort_order: str = None, 335 | ): 336 | """ 337 | Get the FRED tags for a category. 338 | 339 | Parameters 340 | ---------- 341 | category_id: int 342 | The ID of the category. 343 | realtime_start: str, default None 344 | The start of the real-time period formatted as "YYYY-MM-DD". 345 | If None, fred.realtime_start is used. 346 | If fred.realtime_start = None, FRED web service will use today's date. 347 | realtime_end: str, default None 348 | The end of the real-time period formatted as "YYYY-MM-DD". 349 | If None, fred.realtime_end is used. 350 | If fred.realtime_end = None, FRED web service will use today's date. 351 | tag_names: list, default None 352 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 353 | each tag must be present in the tag of returned series. 354 | If None, no filtering by tag names is done. 355 | tag_group_id: str, default None 356 | A tag group ID to filter tags by type with. 357 | Can be one of 'freq' for frequency, 'gen' for general or concept, 358 | 'geo' for geography, 'geot' for geography type, 'rls' for release, 359 | 'seas' for seasonal adjustment, 'src' for source. 360 | If None, no filtering by tag group is done. 361 | search_text: str, default None 362 | The words to find matching tags with. 363 | If None, no filtering by search words. 364 | limit: int, default None 365 | The maximum number of results to return. 366 | Values can be in range(1, 1_001). 367 | If None, FRED will use limit = 1_001. 368 | offset: int, default None 369 | If None, offset of 0 is used. 370 | order_by: str, default None 371 | Order results by values of the specified attribute. 372 | Can be one of "series_count", "popularity", "created", 373 | "name", "group_id". 374 | If None, "series_count" is used. 375 | sort_order: str, default None 376 | Sort results in ascending or descending order for attribute values specified by order_by. 377 | Can be "asc" or "desc". 378 | If None, "asc" is used. 379 | 380 | Returns 381 | ------- 382 | dict 383 | Metadata of all FRED tags found in a category. 384 | 385 | See Also 386 | -------- 387 | fred.get_related_tags_for_a_category: Find related tags for tags within a FRED category. 388 | 389 | Notes 390 | ----- 391 | FRED web service endpoint: fred/category/tags 392 | https://fred.stlouisfed.org/docs/api/fred/category_tags.html 393 | 394 | Examples 395 | -------- 396 | >>> fred.get_tags_for_a_category(category_id = 125, limit = 3, order_by = 'created') 397 | {'realtime_start': '2021-04-05', 398 | 'realtime_end': '2021-04-05', 399 | 'order_by': 'created', 400 | 'sort_order': 'desc', 401 | 'count': 27, 402 | 'offset': 0, 403 | 'limit': 3, 404 | 'tags': [ 405 | {'name': 'public domain: citation requested', 406 | 'group_id': 'cc', 407 | 'notes': None, 408 | 'created': '2018-12-17 23:33:13-06', 409 | 'popularity': 100, 410 | 'series_count': 42}, 411 | {'name': 'headline figure', ........... 412 | 413 | """ 414 | self._viable_api_key() 415 | url_prefix_params = { 416 | "a_url_prefix": "category/tags?category_id=", 417 | "an_int_id": category_id, 418 | } 419 | url_prefix = self._append_id_to_url(**url_prefix_params) 420 | optional_args = { 421 | "&realtime_start=": realtime_start, 422 | "&realtime_end=": realtime_end, 423 | "&tag_names=": tag_names, 424 | "&tag_group_id=": tag_group_id, 425 | "&search_text=": search_text, 426 | "&limit=": limit, 427 | "&offset=": offset, 428 | "&order_by=": order_by, 429 | "&sort_order=": sort_order, 430 | } 431 | url = self._add_optional_params(url_prefix, optional_args) 432 | self.category_stack["get_tags_for_a_category"] = self._fetch_data(url) 433 | return self.category_stack["get_tags_for_a_category"] 434 | 435 | def get_related_tags_for_a_category( 436 | self, 437 | category_id: int, 438 | tag_names: list, 439 | realtime_start: str = None, 440 | realtime_end: str = None, 441 | exclude_tag_names: list = None, 442 | tag_group_id: str = None, 443 | search_text: str = None, 444 | limit: int = None, 445 | offset: int = None, 446 | order_by: str = None, 447 | sort_order: str = None, 448 | ): 449 | """ 450 | Get the related FRED tags for one or more FRED tags within a category. 451 | 452 | Parameters 453 | ---------- 454 | category_id: int 455 | The ID of the category. 456 | realtime_start: str, default None 457 | The start of the real-time period formatted as "YYYY-MM-DD". 458 | If None, fred.realtime_start is used. 459 | If fred.realtime_start = None, FRED web service will use today's date. 460 | realtime_end: str, default None 461 | The end of the real-time period formatted as "YYYY-MM-DD". 462 | If None, fred.realtime_end is used. 463 | If fred.realtime_end = None, FRED web service will use today's date. 464 | tag_names: list, default None 465 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 466 | each tag must be present in the tag of returned series. 467 | If None, no filtering by tag names is done. 468 | exclude_tag_names: list, default None 469 | list of tag names that series match none of. 470 | If None, no filtering by tag names is done. 471 | tag_group_id: str, default None 472 | A tag group ID to filter tags by type with. 473 | Can be one of 'freq' for frequency, 'gen' for general or concept, 474 | 'geo' for geography, 'geot' for geography type, 'rls' for release, 475 | 'seas' for seasonal adjustment, 'src' for source. 476 | If None, no filtering by tag group is done. 477 | search_text: str, default None 478 | The words to find matching tags with. 479 | If None, no filtering by search words. 480 | limit: int, default None 481 | The maximum number of results to return. 482 | Values can be in range(1, 1_001). 483 | If None, FRED will use limit = 1_001. 484 | offset: int, default None 485 | If None, offset of 0 is used. 486 | order_by: str, default None 487 | Order results by values of the specified attribute. 488 | Can be one of "series_count", "popularity", "created", 489 | "name", "group_id". 490 | If None, "series_count" is used. 491 | sort_order: str, default None 492 | Sort results in ascending or descending order for attribute values specified by order_by. 493 | Can be "asc" or "desc". 494 | If None, "asc" is used. 495 | 496 | Returns 497 | ------- 498 | dict 499 | Metadata for each related tag. 500 | 501 | See Also 502 | -------- 503 | fred.get_tags_for_a_category: Get tags found within a category. 504 | 505 | Notes 506 | ----- 507 | FRED web service endpoint: fred/category/related_tags 508 | https://fred.stlouisfed.org/docs/api/fred/category_related_tags.html 509 | 510 | Examples 511 | -------- 512 | >>> fred.get_related_tags_for_a_category(category_id = 125, tag_names = ('services', 'quarterly',), limit = 3) 513 | {'realtime_start': '2021-04-05', 514 | 'realtime_end': '2021-04-05', 515 | 'order_by': 'series_count', 516 | 'sort_order': 'desc', 517 | 'count': 9, 518 | 'offset': 0, 519 | 'limit': 3, 520 | 'tags': [ 521 | {'name': 'balance', 522 | 'group_id': 'gen', 523 | 'notes': '', 524 | 'created': '2012-02-27 10:18:19-06', 525 | 'popularity': 47, 526 | 'series_count': 10}, 527 | {'name': 'bea', ......... 528 | """ 529 | self._viable_api_key() 530 | url_prefix_params = { 531 | "a_url_prefix": "category/related_tags?category_id=", 532 | "an_int_id": category_id, 533 | } 534 | url_prefix = self._append_id_to_url(**url_prefix_params) 535 | optional_args = { 536 | "&tag_names=": tag_names, # tag_names are required * 537 | "&realtime_start=": realtime_start, 538 | "&realtime_end=": realtime_end, 539 | "&exclude_tag_names=": exclude_tag_names, 540 | "&tag_group_id=": tag_group_id, 541 | "&search_text=": search_text, 542 | "&limit=": limit, 543 | "&offset=": offset, 544 | "&order_by=": order_by, 545 | "&sort_order=": sort_order, 546 | } 547 | url = self._add_optional_params(url_prefix, optional_args) 548 | self.category_stack["get_related_tags_for_a_category"] = self._fetch_data(url) 549 | return self.category_stack["get_related_tags_for_a_category"] 550 | -------------------------------------------------------------------------------- /full_fred/constants.py: -------------------------------------------------------------------------------- 1 | version = "0.0.9a3" 2 | -------------------------------------------------------------------------------- /full_fred/fred.py: -------------------------------------------------------------------------------- 1 | from .tags import Tags 2 | 3 | 4 | class Fred(Tags): 5 | def __init__( 6 | self, 7 | api_key_file: str = None, 8 | observation_start: str = None, 9 | observation_end: str = None, 10 | ): 11 | """ 12 | API Key 13 | ------- 14 | Querying FRED's servers can be done with an API key. To get a new key use https://fred.stlouisfed.org/ -> My Account -> API Keys. 15 | 16 | FRED_API_KEY environment variable 17 | --------------------------------- 18 | Automatically detected and used for queries if no api_key_file is given. To check that Fred detects FRED_API_KEY in environment: 19 | fred.env_api_key_found() 20 | 21 | api_key_file 22 | ------------ 23 | Fred(api_key_file = 'example_key.txt') 24 | fred.set_api_key_file('example_key.txt') 25 | 26 | If an api_key_file is given Fred will ensure the file can be found and will use the string on the file's first line for queries. 27 | To get current api_key_file value: 28 | fred.get_api_key_file() 29 | 30 | In neither case is your api key stored. When a request is made your key will be read from api_key_file or environment. 31 | 32 | Accessing Fetched Data 33 | ---------------------- 34 | When a request to FRED's servers is made, the returned data is available in a dictionary. 35 | category_stack for category requests, tag_stack for tag requests, etc. Each stack is a dictionary with method names for keys and the retrieved 36 | data for values. For example, after calling fred.get_tags(), fred.tag_stack["get_tags"] will return the data FRED web service responded with, 37 | until a new get_tags method invocation is made or you pop "get_tags". 38 | 39 | Setting Realtime, Observation Start/End Defaults 40 | ------------------------- 41 | fred.realtime_start: if set, will be used when realtime_start argument is not given. 42 | fred.realtime_end: if set, will be used when realtime_end argument is not given. 43 | fred.observation_start: if set, will be used when observation_start argument is not given. 44 | fred.observation_end: if set, will be used when observation_end argument is not given. 45 | 46 | All queries with realtime_start as a parameter will use whatever fred.realtime_start is set to if no realtime_start argument is given. If 47 | fred.realtime_start is set to None, FRED web service will determine the default value. In most cases where realtime_start isn't specified FRED 48 | web service will use today's date. Same with fred.realtime_end. 49 | All queries that include observation_start as a parameter will use whatever fred.observation_start is set to if no observation_start argument is given. If 50 | fred.observation_start is set to None, FRED web service will determine the default value. In cases where observation_start isn't specified FRED 51 | web service will use '1776-07-04', and '9999-12-31' for fred.observation_end. 52 | """ 53 | super().__init__() 54 | if api_key_file is not None: 55 | self.set_api_key_file(api_key_file) 56 | else: 57 | self.api_key_file = api_key_file 58 | self.observation_start = observation_start 59 | self.observation_end = observation_end 60 | -------------------------------------------------------------------------------- /full_fred/fred_base.py: -------------------------------------------------------------------------------- 1 | from requests.exceptions import RequestException 2 | import requests 3 | import os 4 | 5 | 6 | class FredBase: 7 | def __init__( 8 | self, 9 | api_key_file: str = None, 10 | ): 11 | """ 12 | FredBase defines methods common to Categories, Releases, 13 | Series, Releases, Sources, Tags classes. 14 | """ 15 | self.realtime_start = None 16 | self.realtime_end = None 17 | self.observation_start = None 18 | self.observation_end = None 19 | self.__url_base = "https://api.stlouisfed.org/fred/" 20 | if api_key_file is not None: 21 | self.set_api_key_file(api_key_file) 22 | else: 23 | self.api_key_file = api_key_file 24 | 25 | def get_api_key_file( 26 | self, 27 | ) -> str: 28 | """ 29 | Return currently assigned api_key_file. 30 | """ 31 | return self.api_key_file 32 | 33 | def set_api_key_file( 34 | self, 35 | api_key_file: str, 36 | ) -> bool: 37 | """ 38 | Return True if api_key_file has been found. 39 | """ 40 | if not os.path.isfile(api_key_file): 41 | e = "Can't find %s on path" % api_key_file 42 | raise FileNotFoundError(e) 43 | self.api_key_file = api_key_file 44 | return True 45 | 46 | def _read_api_key_file( 47 | self, 48 | ) -> str: 49 | """ 50 | Read FRED api key from file. This method exists to minimize the 51 | time that the user's API key is human-readable 52 | """ 53 | try: 54 | with open(self.api_key_file, "r") as key_file: 55 | return key_file.readline().strip() 56 | except FileNotFoundError as e: 57 | print(e) 58 | 59 | def env_api_key_found(self) -> bool: 60 | """ 61 | Indicate whether a FRED_API_KEY environment variable is detected. 62 | """ 63 | if "FRED_API_KEY" in os.environ.keys(): 64 | if os.environ["FRED_API_KEY"] is not None: 65 | return True 66 | return False 67 | 68 | def _add_optional_params( 69 | self, 70 | og_url_string: str, 71 | optional_params: dict, 72 | ) -> str: 73 | """ 74 | Create a parameter string that adds any non-None parameters in optional_params to 75 | og_url_string and return og_url_string with these additions. If all optional_params 76 | are None, return og_url_string 77 | 78 | Parameters 79 | ---------- 80 | og_url_string: str 81 | the string to append new, non-null parameter strings to 82 | 83 | optional_params: dict 84 | a dictionary mapping parameter strings to actual arguments passed by user 85 | for example: 86 | "&tag_group_id=": None 87 | "&limit=": 23 88 | if the value is not None, "&limit=" + str(23) is added to og_url_string 89 | 90 | Returns 91 | ------- 92 | str 93 | og_url_string with any existent k, v pairs concatenated to it. 94 | 95 | Notes 96 | ----- 97 | Not all paramaters passed in optional_params need be optional. Most are. 98 | 99 | If tag_names is a substring of a parameter in optional_params, whitespace is 100 | replaced with "+" so the request URL encodes the whitespace in a standard way. 101 | There's more on this at 102 | https://fred.stlouisfed.org/docs/api/fred/related_tags.html 103 | """ 104 | new_url_string = og_url_string 105 | 106 | # use user-set attribute value if set and argument for it 107 | # isn't passed in optional_params 108 | attribute_map = { 109 | "&observation_start=": self.observation_start, 110 | "&observation_end=": self.observation_end, 111 | "&realtime_start=": self.realtime_start, 112 | "&realtime_end=": self.realtime_end, 113 | } 114 | 115 | for k in optional_params.keys(): 116 | if k in attribute_map.keys(): 117 | if optional_params[k] is None: 118 | if attribute_map[k] is not None: 119 | optional_params[k] = attribute_map[k] 120 | if optional_params[k] is not None: 121 | if k == "&include_release_dates_with_no_data=": 122 | try: 123 | optional_params[k] = str(optional_params[k]).lower() 124 | except TypeError: 125 | e = ( 126 | "Cannot cast include_empty to str, " 127 | "cannot create request url to fetch" 128 | " data" 129 | ) 130 | print(e) 131 | if "tag_names" in k: 132 | tag_names = optional_params[k] 133 | try: 134 | str_names = self._join_strings_by(tag_names, ";") 135 | str_names = str_names.strip().replace(" ", "+") 136 | optional_params[k] = str_names 137 | except TypeError: 138 | e = "Cannot add tag_names to FRED query url" 139 | print(e) 140 | try: 141 | a_parameter_string = k + str(optional_params[k]) 142 | new_url_string += a_parameter_string 143 | except TypeError: 144 | print(k + " " + optional_params[k] + " cannot be cast to str") 145 | return new_url_string 146 | 147 | def _viable_api_key(self) -> str: 148 | """ 149 | Verifies that there's an api key to make a request url with. 150 | Raises error if necessary to allow methods to catch, early in 151 | query process, whether a request for data can be sent to FRED. 152 | If there's a usable key, return which one to use 153 | 154 | Returns 155 | ------- 156 | str 157 | A string indicating where to find user's api key 158 | attribute: user has set self.__api_key attribute 159 | env: it's an environment variable 160 | file: user has specified a file holding the key 161 | """ 162 | if self.api_key_file is None: 163 | if not self.env_api_key_found(): 164 | raise AttributeError("Cannot locate a FRED API key") 165 | return "env" 166 | return "file" 167 | 168 | def _make_request_url( 169 | self, 170 | var_url: str, 171 | ) -> str: 172 | """ 173 | Return the url that can be used to retrieve the desired data given var_url. 174 | """ 175 | key_to_use = self._viable_api_key() 176 | url_base = [ 177 | self.__url_base, 178 | var_url, 179 | "&file_type=json&api_key=", 180 | ] 181 | base = "".join(url_base) 182 | if key_to_use == "env": 183 | try: 184 | return base + os.environ["FRED_API_KEY"] 185 | except KeyError as sans: 186 | print(sans, " no longer found in environment") 187 | if key_to_use == "file": 188 | return base + self._read_api_key_file() 189 | 190 | def _fetch_data( 191 | self, 192 | url_prefix: str, 193 | ) -> dict: 194 | """ 195 | Make request URL, send it to FRED, return JSON upshot 196 | """ 197 | url = self._make_request_url(url_prefix) 198 | json_data = self._get_response(url) 199 | if json_data is None: 200 | # never print api key in message for security 201 | message = "Data could not be retrieved, returning None" 202 | print(message) 203 | return 204 | return json_data 205 | 206 | def _get_response(self, a_url: str) -> dict: 207 | """ 208 | Return a JSON dictionary response with data retrieved from a_url 209 | """ 210 | try: 211 | response = requests.get(a_url) 212 | except RequestException: 213 | return 214 | return response.json() 215 | 216 | def _append_id_to_url( 217 | self, 218 | a_url_prefix: str, 219 | an_int_id: int = None, 220 | a_str_id: str = None, 221 | ) -> str: 222 | """ 223 | Return a_url_prefix with either an_int_id or a_str_id appended to it. 224 | """ 225 | if an_int_id is None and a_str_id is None: 226 | raise ValueError("No id argument given, cannot append to url") 227 | passed_id = an_int_id 228 | new_url_str = a_url_prefix 229 | if passed_id is None: 230 | passed_id = a_str_id 231 | try: 232 | new_url_str += str(passed_id) 233 | except TypeError: 234 | print("Unable to cast id to str, cannot append to url string") 235 | return new_url_str 236 | 237 | def _join_strings_by( 238 | self, 239 | strings: list, 240 | use_str: str, 241 | ) -> str: 242 | """ 243 | Join an iterable of strings using use_str and return the fused string. 244 | """ 245 | if strings is None or use_str is None: 246 | raise TypeError("strings and use_str are both required") 247 | try: 248 | fused_str = use_str.join(strings) 249 | except TypeError: 250 | print("Unable to join strings using %s" % use_str) 251 | return fused_str 252 | -------------------------------------------------------------------------------- /full_fred/releases.py: -------------------------------------------------------------------------------- 1 | from .categories import Categories 2 | 3 | 4 | class Releases(Categories): 5 | def __init__(self): 6 | """""" 7 | super().__init__() 8 | self.release_stack = dict() 9 | 10 | def get_all_releases( 11 | self, 12 | realtime_start: str = None, 13 | realtime_end: str = None, 14 | limit: int = None, 15 | offset: int = None, 16 | order_by: str = None, 17 | sort_order: str = None, 18 | ) -> dict: 19 | """ 20 | Get all releases of economic data. 21 | 22 | Parameters 23 | ---------- 24 | realtime_start: str, default None 25 | The start of the real-time period formatted as "YYYY-MM-DD". 26 | If None, fred.realtime_start is used. 27 | If fred.realtime_start = None, FRED web service will use today's date. 28 | realtime_end: str, default None 29 | The end of the real-time period formatted as "YYYY-MM-DD". 30 | If None, fred.realtime_end is used. 31 | If fred.realtime_end = None, FRED web service will use today's date. 32 | limit: int, default None 33 | The maximum number of results to return. 34 | Values can be in range(1, 1_001). 35 | If None, FRED will use limit = 1_001. 36 | offset: int, default None 37 | If None, offset of 0 is used. 38 | order_by: str, default None 39 | Order results by values of the specified attribute. 40 | Can be one of "release_id", "name", "realtime_start", "realtime_end", 41 | If None, "release_id" is used. 42 | sort_order: str, default None 43 | Sort results in ascending or descending order for attribute values specified by order_by. 44 | Can be "asc" or "desc". 45 | If None, "asc" is used. 46 | 47 | Returns 48 | ------- 49 | dict 50 | ID, name, url, other metadata for all FRED releases. 51 | 52 | See Also 53 | -------- 54 | fred.get_a_release: Get metadata of a specific release of data. 55 | 56 | Notes 57 | ----- 58 | FRED web service endpoint: fred/releases 59 | https://fred.stlouisfed.org/docs/api/fred/releases.html 60 | 61 | Examples 62 | -------- 63 | >>> fred.get_all_releases(limit = 2, sort_order = 'desc', order_by = 'press_release') 64 | {'realtime_start': '2021-04-05', 65 | 'realtime_end': '2021-04-05', 66 | 'order_by': 'press_release', 67 | 'sort_order': 'desc', 68 | 'count': 298, 69 | 'offset': 0, 70 | 'limit': 2, 71 | 'releases': [ 72 | {'id': 9, 73 | 'realtime_start': '2021-04-05', 74 | 'realtime_end': '2021-04-05', 75 | 'name': 'Advance Monthly Sales for Retail and Food Services', 76 | 'press_release': True, 77 | 'link': 'http://www.census.gov/retail/', 78 | 'notes': 'The U.S. Census Bureau conducts ... 79 | {'id': 10, ....... 80 | """ 81 | self._viable_api_key() 82 | url_prefix = "releases?" 83 | optional_args = { 84 | "&realtime_start=": realtime_start, 85 | "&realtime_end=": realtime_end, 86 | "&limit=": limit, 87 | "&offset=": offset, 88 | "&order_by=": order_by, 89 | "&sort_order=": sort_order, 90 | } 91 | url = self._add_optional_params(url_prefix, optional_args) 92 | self.release_stack["get_all_releases"] = self._fetch_data(url) 93 | return self.release_stack["get_all_releases"] 94 | 95 | def get_release_dates_all_releases( 96 | self, 97 | realtime_start: str = None, 98 | realtime_end: str = None, 99 | limit: int = None, 100 | offset: int = None, 101 | order_by: str = None, 102 | sort_order: str = None, 103 | include_empty: bool = None, 104 | ) -> dict: 105 | """ 106 | Get release dates for all releases of economic data. 107 | FRED's data sources publish release dates: release 108 | dates may be published before the data in the release 109 | is available on FRED's servers via this API. 110 | 111 | Parameters 112 | ---------- 113 | realtime_start: str, default None 114 | The start of the real-time period formatted as "YYYY-MM-DD". 115 | If None, fred.realtime_start is used. 116 | If fred.realtime_start = None, FRED web service will use first day of current year. 117 | realtime_end: str, default None 118 | The end of the real-time period formatted as "YYYY-MM-DD". 119 | If None, fred.realtime_end is used. 120 | If fred.realtime_end = None, FRED web service will use "9999-12-31". 121 | limit: int, default None 122 | The maximum number of results to return. 123 | Values can be in range(1, 1_001). 124 | If None, FRED will use limit = 1_001. 125 | offset: int, default None 126 | If None, offset of 0 is used. 127 | order_by: str, default None 128 | Order results by values of the specified attribute. 129 | Can be one of "release_date", "release_id", "release_name", 130 | If None, "release_date" is used. 131 | sort_order: str, default None 132 | Sort results in ascending or descending order for attribute values specified by order_by. 133 | Can be "asc" or "desc". 134 | If None, "asc" is used. 135 | include_empty: bool, default None 136 | Indicates whether to return release dates with no data. 137 | If False, release dates without any data are excluded, namely future release dates. 138 | If None, False is used. 139 | 140 | Returns 141 | ------- 142 | dict 143 | Release_id, release_name, date for all releases 144 | 145 | See Also 146 | -------- 147 | fred.get_a_release: Get metadata of a specific release of data. 148 | 149 | Notes 150 | ----- 151 | FRED web service endpoint: fred/releases 152 | https://fred.stlouisfed.org/docs/api/fred/releases_dates.html 153 | 154 | Examples 155 | -------- 156 | >>> fred.get_release_dates_all_releases(limit = 3, order_by = 'release_name') 157 | {'realtime_start': '2021-01-01', 158 | 'realtime_end': '9999-12-31', 159 | 'order_by': 'release_name', 160 | 'sort_order': 'asc', 161 | 'count': 2288, 162 | 'offset': 0, 163 | 'limit': 3, 164 | 'release_dates': [ 165 | {'release_id': 194, 166 | 'release_name': 'ADP National Employment Report', 167 | 'date': '2021-01-06'}, 168 | {'release_id': 194, ...... 169 | """ 170 | self._viable_api_key() 171 | url_prefix = "releases/dates?" 172 | optional_args = { 173 | "&realtime_start=": realtime_start, 174 | "&realtime_end=": realtime_end, 175 | "&limit=": limit, 176 | "&offset=": offset, 177 | "&order_by=": order_by, 178 | "&sort_order=": sort_order, 179 | "&include_release_dates_with_no_data": include_empty, 180 | } 181 | url = self._add_optional_params(url_prefix, optional_args) 182 | self.release_stack["get_release_dates_all_releases"] = self._fetch_data(url) 183 | return self.release_stack["get_release_dates_all_releases"] 184 | 185 | def get_a_release( 186 | self, 187 | release_id: int, 188 | realtime_start: str = None, 189 | realtime_end: str = None, 190 | ): 191 | """ 192 | Get a release of economic data. 193 | 194 | Parameters 195 | ---------- 196 | release_id: int 197 | The ID of the release. 198 | realtime_start: str, default None 199 | The start of the real-time period formatted as "YYYY-MM-DD". 200 | If None, fred.realtime_start is used. 201 | If fred.realtime_start = None, FRED web service will use today's date. 202 | realtime_end: str, default None 203 | The end of the real-time period formatted as "YYYY-MM-DD". 204 | If None, fred.realtime_end is used. 205 | If fred.realtime_end = None, FRED web service will use today's date. 206 | 207 | Returns 208 | ------- 209 | dict 210 | Release name, id, realtime start and end, url to release, 211 | whether the release is a press_release. 212 | 213 | See Also 214 | -------- 215 | fred.get_all_releases: Get all releases of economic data. 216 | 217 | Notes 218 | ----- 219 | FRED web service endpoint: fred/release 220 | https://fred.stlouisfed.org/docs/api/fred/release.html 221 | 222 | Examples 223 | ----- 224 | >>> fred.get_a_release(53) 225 | {'realtime_start': '2021-04-05', 226 | 'realtime_end': '2021-04-05', 227 | 'releases': [ 228 | {'id': 53, 229 | 'realtime_start': '2021-04-05', 230 | 'realtime_end': '2021-04-05', 231 | 'name': 'Gross Domestic Product', 232 | 'press_release': True, 233 | 'link': 'https://www.bea.gov/data/gdp/gross-domestic-product'}]} 234 | """ 235 | self._viable_api_key() 236 | url_prefix_params = { 237 | "a_url_prefix": "release?release_id=", 238 | "an_int_id": release_id, 239 | } 240 | url_prefix = self._append_id_to_url(**url_prefix_params) 241 | optional_args = { 242 | "&realtime_start=": realtime_start, 243 | "&realtime_end=": realtime_end, 244 | } 245 | url = self._add_optional_params(url_prefix, optional_args) 246 | self.release_stack["get_a_release"] = self._fetch_data(url) 247 | return self.release_stack["get_a_release"] 248 | 249 | def get_release_dates( 250 | self, 251 | release_id: int, 252 | realtime_start: str = None, 253 | realtime_end: str = None, 254 | limit: int = None, 255 | offset: int = None, 256 | sort_order: str = None, 257 | include_empty: bool = None, 258 | ) -> dict: 259 | """ 260 | Get release dates for a release of economic data. 261 | FRED's data sources publish release dates: release 262 | dates may be published before the data in the release 263 | is available on FRED's servers via this API. 264 | 265 | Parameters 266 | ---------- 267 | release_id: int 268 | The ID of the release. 269 | realtime_start: str, default None 270 | The start of the real-time period formatted as "YYYY-MM-DD". 271 | If None, fred.realtime_start is used. 272 | If fred.realtime_start = None, FRED web service will use "1776-07-04". 273 | realtime_end: str, default None 274 | The end of the real-time period formatted as "YYYY-MM-DD". 275 | If None, fred.realtime_end is used. 276 | If fred.realtime_end = None, FRED web service will use "9999-12-31". 277 | limit: int, default None 278 | The maximum number of results to return. 279 | Values can be in range(1, 10_001). 280 | If None, FRED will use limit = 10_001. 281 | offset: int, default None 282 | If None, offset of 0 is used. 283 | sort_order: str, default None 284 | Sort results in ascending or descending order for attribute values specified by order_by. 285 | Can be "asc" or "desc". 286 | If None, "asc" is used. 287 | include_empty: bool, default None 288 | Indicates whether to return release dates with no data. 289 | If False, release dates without any data are excluded, namely future release dates. 290 | If None, False is used. 291 | 292 | Returns 293 | ------- 294 | dict 295 | The release dates for each release of release_id. 296 | 297 | See Also 298 | -------- 299 | get_release_dates_all_releases: Get release dates for all releases of economic data. 300 | 301 | Notes 302 | ----- 303 | FRED web service endpoint: fred/release/dates 304 | https://fred.stlouisfed.org/docs/api/fred/release_dates.html 305 | 306 | Examples 307 | ----- 308 | >>> fred.get_release_dates(release_id=82, limit = 3, sort_order = 'desc') 309 | {'realtime_start': '1776-07-04', 310 | 'realtime_end': '9999-12-31', 311 | 'order_by': 'release_date', 312 | 'sort_order': 'desc', 313 | 'count': 24, 314 | 'offset': 0, 315 | 'limit': 3, 316 | 'release_dates': [ 317 | {'release_id': 82, 'date': '2020-02-20'}, 318 | {'release_id': 82, 'date': '2019-03-19'}, 319 | {'release_id': 82, 'date': '2018-02-12'} 320 | ] 321 | } 322 | """ 323 | self._viable_api_key() 324 | url_prefix_params = { 325 | "a_url_prefix": "release/dates?release_id=", 326 | "an_int_id": release_id, 327 | } 328 | url_prefix = self._append_id_to_url(**url_prefix_params) 329 | optional_args = { 330 | "&realtime_start=": realtime_start, 331 | "&realtime_end=": realtime_end, 332 | "&limit=": limit, 333 | "&offset=": offset, 334 | "&sort_order=": sort_order, 335 | "&include_release_dates_with_no_data=": include_empty, 336 | } 337 | url = self._add_optional_params(url_prefix, optional_args) 338 | self.release_stack["get_release_dates"] = self._fetch_data(url) 339 | return self.release_stack["get_release_dates"] 340 | 341 | def get_series_on_a_release( 342 | self, 343 | release_id: int, 344 | realtime_start: str = None, 345 | realtime_end: str = None, 346 | limit: int = None, 347 | offset: int = None, 348 | order_by: str = None, 349 | sort_order: str = None, 350 | filter_variable: str = None, 351 | filter_value: str = None, 352 | tag_names: list = None, 353 | exclude_tag_names: list = None, 354 | ) -> dict: 355 | """ 356 | Get series on a release of economic data. 357 | 358 | Parameters 359 | ---------- 360 | release_id: int 361 | The ID of the release. 362 | realtime_start: str, default None 363 | The start of the real-time period formatted as "YYYY-MM-DD". 364 | If None, fred.realtime_start is used. 365 | If fred.realtime_start = None, FRED web service will use today's date. 366 | realtime_end: str, default None 367 | The end of the real-time period formatted as "YYYY-MM-DD". 368 | If None, fred.realtime_end is used. 369 | If fred.realtime_end = None, FRED web service will use today's date. 370 | limit: int, default None 371 | The maximum number of results to return. 372 | Values can be in range(1, 1_001). 373 | If None, FRED will use limit = 1_001. 374 | offset: int, default None 375 | If None, offset of 0 is used. 376 | order_by: str, default None 377 | Order results by values of the specified attribute. 378 | Can be one of "series_id", "title", "units", "frequency", 379 | "seasonal_adjustment", "realtime_start", "realtime_end", 380 | "last_updated", "observation_start", "observation_end", 381 | "popularity", "group_popularity". 382 | If None, "series_id" is used. 383 | sort_order: str, default None 384 | Sort results in ascending or descending order for attribute values specified by order_by. 385 | Can be "asc" or "desc". 386 | If None, "asc" is used. 387 | filter_variable: str, default None 388 | The attribute to filter results by. 389 | Can be one of "frequency", "units", "seasonal_adjustment". 390 | If None, no filter is used. 391 | filter_value: str, default None 392 | The value of filter_variable to filter results by. 393 | If None, no filter is used. 394 | tag_names: list, default None 395 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 396 | If None, no filtering by tag names is done. 397 | exclude_tag_names: list, default None 398 | list of tag names that series match none of. 399 | If None, no filtering by excluding tag names is done. 400 | 401 | Returns 402 | ------- 403 | dict 404 | ID, title, units, and other metadata for each series included in 405 | the release. 406 | 407 | See Also 408 | -------- 409 | get_series_df: Get observations of a series in pd.DataFrame form. 410 | 411 | Notes 412 | ----- 413 | FRED web service endpoint: fred/release/series 414 | https://fred.stlouisfed.org/docs/api/fred/release_series.html 415 | 416 | Examples 417 | ----- 418 | >>> fred.get_series_on_a_release(tag_names=('japan',), release_id=51, limit = 3, sort_order='desc', order_by='last_updated') 419 | {'realtime_start': '2021-04-05', 420 | 'realtime_end': '2021-04-05', 421 | 'order_by': 'last_updated', 422 | 'sort_order': 'desc', 423 | 'count': 2, 424 | 'offset': 0, 425 | 'limit': 3, 426 | 'seriess': [ 427 | {'id': 'EXPJP', 428 | 'realtime_start': '2021-04-05', 429 | 'realtime_end': '2021-04-05', 430 | 'title': 'U.S. Exports of Goods by F.A.S. Basis to Japan', 431 | 'observation_start': '1985-01-01', 432 | 'observation_end': '2021-01-01', 433 | 'frequency': 'Monthly', 434 | 'frequency_short': 'M', 435 | 'units': 'Millions of Dollars', 436 | 'units_short': 'Mil. of $', 437 | 'seasonal_adjustment': 'Not Seasonally Adjusted', 438 | 'seasonal_adjustment_short': 'NSA', 439 | 'last_updated': '2021-03-05 07:52:02-06', 440 | 'popularity': 37, 441 | 'group_popularity': 37, 442 | 'notes': 'Free Alongside Ship Basis (f.a.s.)\nFurther information related to the ... 443 | {'id': 'IMPJP', ......... 444 | """ 445 | self._viable_api_key() 446 | url_prefix_params = { 447 | "a_url_prefix": "release/series?release_id=", 448 | "an_int_id": release_id, 449 | } 450 | url_prefix = self._append_id_to_url(**url_prefix_params) 451 | optional_args = { 452 | "&realtime_start=": realtime_start, 453 | "&realtime_end=": realtime_end, 454 | "&limit=": limit, 455 | "&offset=": offset, 456 | "&order_by=": order_by, 457 | "&sort_order=": sort_order, 458 | "&filter_variable=": filter_variable, 459 | "&filter_value=": filter_value, 460 | "&tag_names=": tag_names, 461 | "&exclude_tag_names=": exclude_tag_names, 462 | } 463 | url = self._add_optional_params(url_prefix, optional_args) 464 | self.release_stack["get_series_on_a_release"] = self._fetch_data(url) 465 | return self.release_stack["get_series_on_a_release"] 466 | 467 | def get_sources_for_a_release( 468 | self, 469 | release_id: int, 470 | realtime_start: str = None, 471 | realtime_end: str = None, 472 | ) -> dict: 473 | """ 474 | Get the sources for a release of economic data. 475 | 476 | Parameters 477 | ---------- 478 | release_id: int 479 | The ID of the release. 480 | realtime_start: str, default None 481 | The start of the real-time period formatted as "YYYY-MM-DD". 482 | If None, fred.realtime_start is used. 483 | If fred.realtime_start = None, FRED web service will use today's date. 484 | realtime_end: str, default None 485 | The end of the real-time period formatted as "YYYY-MM-DD". 486 | If None, fred.realtime_end is used. 487 | If fred.realtime_end = None, FRED web service will use today's date. 488 | 489 | Returns 490 | ------- 491 | dict 492 | source_id, name, url, and other metadata about sources of 493 | data found in the release. 494 | 495 | See Also 496 | -------- 497 | fred.get_a_source: Get metadata pertaining to a data source. 498 | 499 | Notes 500 | ----- 501 | FRED web service endpoint: fred/release/sources 502 | https://fred.stlouisfed.org/docs/api/fred/releases_dates.html 503 | 504 | Examples 505 | ----- 506 | >>> fred.get_sources_on_a_release(51) 507 | {'realtime_start': '2021-04-05', 508 | 'realtime_end': '2021-04-05', 509 | 'sources': [ 510 | {'id': 19, 511 | 'realtime_start': '2021-04-05', 512 | 'realtime_end': '2021-04-05', 513 | 'name': 'U.S. Census Bureau', 514 | 'link': 'http://www.census.gov/'}, 515 | {'id': 18, ...... 516 | """ 517 | self._viable_api_key() 518 | url_prefix_params = { 519 | "a_url_prefix": "release/sources?release_id=", 520 | "an_int_id": release_id, 521 | } 522 | url_prefix = self._append_id_to_url(**url_prefix_params) 523 | optional_args = { 524 | "&realtime_start=": realtime_start, 525 | "&realtime_end=": realtime_end, 526 | } 527 | url = self._add_optional_params(url_prefix, optional_args) 528 | self.release_stack["get_sources_for_a_release"] = self._fetch_data(url) 529 | return self.release_stack["get_sources_for_a_release"] 530 | 531 | def get_tags_for_a_release( 532 | self, 533 | release_id: int, 534 | realtime_start: str = None, 535 | realtime_end: str = None, 536 | tag_names: list = None, 537 | tag_group_id: str = None, 538 | search_text: str = None, 539 | limit: int = None, 540 | offset: int = None, 541 | order_by: str = None, 542 | sort_order: str = None, 543 | ) -> dict: 544 | """ 545 | Get the FRED tags for a release. 546 | 547 | Parameters 548 | ---------- 549 | release_id: int 550 | The ID of the release. 551 | realtime_start: str, default None 552 | The start of the real-time period formatted as "YYYY-MM-DD". 553 | If None, fred.realtime_start is used. 554 | If fred.realtime_start = None, FRED web service will use today's date. 555 | realtime_end: str, default None 556 | The end of the real-time period formatted as "YYYY-MM-DD". 557 | If None, fred.realtime_end is used. 558 | If fred.realtime_end = None, FRED web service will use today's date. 559 | tag_names: list, default None 560 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 561 | If None, no filtering by tag names is done. 562 | tag_group_id: str, default None 563 | a tag group id to filter tags by type with 564 | can be one of 'freq' for frequency, 'gen' for general or concept, 565 | 'geo' for geography, 'geot' for geography type, 'rls' for release, 566 | 'seas' for seasonal adjustment, 'src' for source 567 | search_text: str, default None 568 | the words to find matching tags with 569 | if None, no filtering by search words 570 | limit: int, default None 571 | The maximum number of results to return. 572 | Values can be in range(1, 1_001). 573 | If None, FRED will use limit = 1_001. 574 | offset: int, default None 575 | If None, offset of 0 is used. 576 | order_by: str, default None 577 | Order results by values of the specified attribute. 578 | Can be one of "series_count", "popularity", "created", 579 | "name", "group_id". 580 | If None, "series_count" is used. 581 | sort_order: str, default None 582 | Sort results in ascending or descending order for attribute values specified by order_by. 583 | Can be "asc" or "desc". 584 | If None, "asc" is used. 585 | 586 | Returns 587 | ------- 588 | dict 589 | Name, group ID, notes, and other metadata for each tag. 590 | 591 | See Also 592 | -------- 593 | get_related_tags_for_release: Get tags related to those found within a release. 594 | 595 | Notes 596 | ----- 597 | FRED web service endpoint: fred/release/tags 598 | https://fred.stlouisfed.org/docs/api/fred/release_tags.html 599 | 600 | Examples 601 | ----- 602 | >>> fred.get_tags_for_a_release(release_id=86, limit=3, tag_names=('gnp',), sort_order='desc', order_by='created') 603 | {'realtime_start': '2021-04-05', 604 | 'realtime_end': '2021-04-05', 605 | 'order_by': 'created', 606 | 'sort_order': 'desc', 607 | 'count': 44, 608 | 'offset': 0, 609 | 'limit': 3, 610 | 'tags': [ 611 | {'name': 'public domain: citation requested', 612 | 'group_id': 'cc', 613 | 'notes': None, 614 | 'created': '2018-12-17 23:33:13-06', 615 | 'popularity': 100, 616 | 'series_count': 168}, 617 | {'name': 'a2/p2', ..... 618 | """ 619 | self._viable_api_key() 620 | url_prefix_params = { 621 | "a_url_prefix": "release/tags?release_id=", 622 | "an_int_id": release_id, 623 | } 624 | url_prefix = self._append_id_to_url(**url_prefix_params) 625 | optional_args = { 626 | "&realtime_start=": realtime_start, 627 | "&realtime_end=": realtime_end, 628 | "&tag_group_id=": tag_group_id, 629 | "&search_text=": search_text, 630 | "&limit=": limit, 631 | "&offset=": offset, 632 | "&order_by=": order_by, 633 | "&sort_order=": sort_order, 634 | } 635 | url = self._add_optional_params(url_prefix, optional_args) 636 | self.release_stack["get_tags_for_a_release"] = self._fetch_data(url) 637 | return self.release_stack["get_tags_for_a_release"] 638 | 639 | def get_related_tags_for_release( 640 | self, 641 | release_id: int, 642 | tag_names: list, 643 | realtime_start: str = None, 644 | realtime_end: str = None, 645 | exclude_tag_names: list = None, 646 | tag_group_id: str = None, 647 | search_text: str = None, 648 | limit: int = None, 649 | offset: int = None, 650 | order_by: str = None, 651 | sort_order: str = None, 652 | ) -> dict: 653 | """ 654 | Get the related FRED tags for one or more FRED tags within a release. 655 | 656 | Parameters 657 | ---------- 658 | release_id: int 659 | The ID of the release. 660 | tag_names: list, default None 661 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 662 | realtime_start: str, default None 663 | The start of the real-time period formatted as "YYYY-MM-DD". 664 | If None, fred.realtime_start is used. 665 | If fred.realtime_start = None, FRED web service will use today's date. 666 | realtime_end: str, default None 667 | The end of the real-time period formatted as "YYYY-MM-DD". 668 | If None, fred.realtime_end is used. 669 | If fred.realtime_end = None, FRED web service will use today's date. 670 | exclude_tag_names: list, default None 671 | list of tag names that series match none of. 672 | If None, no filtering by excluding tag names is done. 673 | tag_group_id: str, default None 674 | A tag group id to filter tags by type with 675 | can be one of 'freq' for frequency, 'gen' for general or concept, 676 | 'geo' for geography, 'geot' for geography type, 'rls' for release, 677 | 'seas' for seasonal adjustment, 'src' for source 678 | search_text: str, default None 679 | The words to find matching tags with 680 | If None, no filtering by search words 681 | limit: int, default None 682 | The maximum number of results to return. 683 | Values can be in range(1, 1_001). 684 | If None, FRED will use limit = 1_001. 685 | offset: int, default None 686 | If None, offset of 0 is used. 687 | order_by: str, default None 688 | Order results by values of the specified attribute. 689 | Can be one of "series_count", "popularity", "created", 690 | "name", "group_id". 691 | If None, "series_count" is used. 692 | sort_order: str, default None 693 | Sort results in ascending or descending order for attribute values specified by order_by. 694 | Can be "asc" or "desc". 695 | If None, "asc" is used. 696 | 697 | Returns 698 | ------- 699 | dict 700 | Name, group ID, notes, series count, and other metadata for each related tag. 701 | 702 | See Also 703 | -------- 704 | get_tags_for_release: Get the FRED tags found within a release. 705 | 706 | Notes 707 | ----- 708 | FRED web service endpoint: fred/release/related_tags 709 | https://fred.stlouisfed.org/docs/api/fred/release_related_tags.html 710 | 711 | Examples 712 | ----- 713 | >>> fred.get_related_tags_for_release(release_id=86, limit=2, tag_names=('sa', 'foreign',), realtime_end='2013-08-14') 714 | {'realtime_start': '1776-07-04', 715 | 'realtime_end': '2013-08-14', 716 | 'order_by': 'series_count', 717 | 'sort_order': 'desc', 718 | 'count': 9, 719 | 'offset': 0, 720 | 'limit': 2, 721 | 'tags': [ 722 | {'name': 'commercial', 723 | 'group_id': 'gen', 724 | 'notes': '', 725 | 'created': '2012-02-27 10:18:19-06', 726 | 'popularity': 61, 727 | 'series_count': 2}, 728 | {'name': 'commercial paper', ...... 729 | """ 730 | url_prefix_params = { 731 | "a_url_prefix": "release/related_tags?release_id=", 732 | "an_int_id": release_id, 733 | } 734 | url_prefix = self._append_id_to_url(**url_prefix_params) 735 | optional_args_plus_tag_names = { 736 | "&tag_names=": tag_names, 737 | "&realtime_start=": realtime_start, 738 | "&realtime_end=": realtime_end, 739 | "&exclude_tag_names=": exclude_tag_names, 740 | "&tag_group_id=": tag_group_id, 741 | "&search_text=": search_text, 742 | "&limit=": limit, 743 | "&offset=": offset, 744 | "&order_by=": order_by, 745 | "&sort_order=": sort_order, 746 | } 747 | url = self._add_optional_params(url_prefix, optional_args_plus_tag_names) 748 | self.release_stack["get_related_tags_for_release"] = self._fetch_data(url) 749 | return self.release_stack["get_related_tags_for_release"] 750 | 751 | def get_release_tables( 752 | self, 753 | release_id: int, 754 | element_id: int = None, 755 | include_observation_values: bool = None, 756 | observation_date: str = None, 757 | ) -> dict: 758 | """ 759 | Get release tables for a given release. 760 | 761 | Parameters 762 | ---------- 763 | release_id: int 764 | The ID of the release. 765 | element_id: int, default None 766 | The release table element id to retrieve 767 | If None, root (most general) element_id for the release is used. 768 | include_observation_values: bool, default None 769 | Indicates that observations need to be returned. 770 | Observation value and date are only returned for a series 771 | element type. 772 | If None, observations are not returned. 773 | observation_date: str, default None 774 | The observation date to be included with the returned release table. 775 | String formatted as 'YYYY-MM-DD' 776 | If None, '9999-12-31' is used. 777 | 778 | Returns 779 | ------- 780 | dict 781 | Heirarchical metadata for a release. 782 | 783 | Notes 784 | ----- 785 | FRED web service endpoint: fred/release/tables 786 | https://fred.stlouisfed.org/docs/api/fred/release_tables.html 787 | 788 | Examples 789 | -------- 790 | >>> fred.get_release_tables(release_id=53, element_id=12886, include_observation_values=True) 791 | {'name': 'Personal consumption expenditures', 792 | 'element_id': 12886, 793 | 'release_id': '53', 794 | 'elements': 795 | {'12887': {'element_id': 12887, 796 | 'release_id': 53, 797 | 'series_id': 'DGDSRL1A225NBEA', 798 | 'parent_id': 12886, 799 | 'line': '3', 800 | 'type': 'series', 801 | 'name': 'Goods', 802 | 'level': '1', 803 | 'children': [{'element_id': 12888, 804 | 'release_id': 53, 805 | 'series_id': 'DDURRL1A225NBEA', 806 | 'parent_id': 12887, 807 | 'line': '4', 808 | 'type': 'series', 809 | 'name': 'Durable goods', 810 | 'level': '2', 811 | 'children': []}, 812 | {'element_id': 12889, ...... 813 | """ 814 | self._viable_api_key() 815 | url_prefix_params = { 816 | "a_url_prefix": "release/tables?release_id=", 817 | "an_int_id": release_id, 818 | } 819 | url_prefix = self._append_id_to_url(**url_prefix_params) 820 | optional_args_plus_tag_names = { 821 | "&element_id=": element_id, 822 | "&include_observation_values=": include_observation_values, 823 | "&observation_date=": observation_date, 824 | } 825 | url = self._add_optional_params(url_prefix, optional_args_plus_tag_names) 826 | self.release_stack["get_release_tables"] = self._fetch_data(url) 827 | return self.release_stack["get_release_tables"] 828 | -------------------------------------------------------------------------------- /full_fred/sources.py: -------------------------------------------------------------------------------- 1 | from .series import Series 2 | 3 | 4 | class Sources(Series): 5 | def __init__(self): 6 | """ 7 | FRED source = a provider of economic data series such as 8 | Bank of Japan, Chicago Board Options Exchange, etc. 9 | """ 10 | super().__init__() 11 | self.source_stack = dict() 12 | 13 | def get_all_sources( 14 | self, 15 | realtime_start: str = None, 16 | realtime_end: str = None, 17 | limit: int = None, 18 | offset: int = None, 19 | order_by: str = None, 20 | sort_order: str = None, 21 | ) -> dict: 22 | """ 23 | Get all sources of economic data. 24 | 25 | Parameters 26 | ---------- 27 | realtime_start: str, default None 28 | The start of the real-time period formatted as "YYYY-MM-DD". 29 | If None, fred.realtime_start is used. 30 | If fred.realtime_start = None, FRED web service will use today's date. 31 | realtime_end: str, default None 32 | The end of the real-time period formatted as "YYYY-MM-DD". 33 | If None, fred.realtime_end is used. 34 | If fred.realtime_end = None, FRED web service will use today's date. 35 | limit: int, default None 36 | The maximum number of results to return. 37 | Values can be in range(1, 1_001). 38 | If None, FRED will use limit = 1_000. 39 | offset: int, default None 40 | Can be a non-negative int. 41 | If None, offset of 0 is used. 42 | order_by: str, default None 43 | Order results by values of the specified attribute. 44 | Can be one of "source_id", "name", "realtime_start", "realtime_end". 45 | If None, "source_id" is used. 46 | sort_order: str, default None 47 | Sort results in ascending or descending order for attribute values specified by order_by. 48 | Can be "asc" or "desc". 49 | If None, "asc" is used. 50 | 51 | Returns 52 | ------- 53 | dict 54 | Metadata of requested FRED sources. 55 | 56 | See Also 57 | -------- 58 | fred.get_a_source: Get metadata about a source. 59 | 60 | Notes 61 | ----- 62 | FRED web service endpoint: fred/sources 63 | https://fred.stlouisfed.org/docs/api/fred/sources.html 64 | 65 | Examples 66 | -------- 67 | >>> fred.get_all_sources(order_by = 'name', limit = 3, sort_order = 'desc') 68 | {'realtime_start': '2021-04-05', 69 | 'realtime_end': '2021-04-05', 70 | 'order_by': 'name', 71 | 'sort_order': 'desc', 72 | 'count': 103, 73 | 'offset': 0, 74 | 'limit': 3, 75 | 'sources': [ 76 | {'id': 57, 77 | 'realtime_start': '2021-04-05', 78 | 'realtime_end': '2021-04-05', 79 | 'name': 'World Bank', 80 | 'link': 'http://www.worldbank.org/'}, 81 | {'id': 44, .......... 82 | """ 83 | self._viable_api_key() 84 | url_prefix = "sources?" 85 | optional_args = { 86 | "&realtime_start=": realtime_start, 87 | "&realtime_end=": realtime_end, 88 | "&limit=": limit, 89 | "&offset=": offset, 90 | "&order_by=": order_by, 91 | "&sort_order=": sort_order, 92 | } 93 | url = self._add_optional_params(url_prefix, optional_args) 94 | self.source_stack["get_all_sources"] = self._fetch_data(url) 95 | return self.source_stack["get_all_sources"] 96 | 97 | def get_a_source( 98 | self, 99 | source_id: int, 100 | realtime_start: str = None, 101 | realtime_end: str = None, 102 | ) -> dict: 103 | """ 104 | Get a source of economic data. 105 | 106 | Parameters 107 | ---------- 108 | source_id: int 109 | The ID of the series. 110 | realtime_start: str, default None 111 | The start of the real-time period formatted as "YYYY-MM-DD". 112 | If None, fred.realtime_start is used. 113 | If fred.realtime_start = None, FRED web service will use today's date. 114 | realtime_end: str, default None 115 | The end of the real-time period formatted as "YYYY-MM-DD". 116 | If None, fred.realtime_end is used. 117 | If fred.realtime_end = None, FRED web service will use today's date. 118 | 119 | Returns 120 | ------- 121 | dict 122 | Metadata of requested FRED source. 123 | 124 | See Also 125 | -------- 126 | fred.get_all_sources: Get all sources of economic data. 127 | 128 | Notes 129 | ----- 130 | FRED web service endpoint: fred/source 131 | https://fred.stlouisfed.org/docs/api/fred/source.html 132 | 133 | Examples 134 | -------- 135 | >>> fred.get_a_source(1) 136 | {'realtime_start': '2021-04-05', 137 | 'realtime_end': '2021-04-05', 138 | 'sources': [{'id': 1, 139 | 'realtime_start': '2021-04-05', 140 | 'realtime_end': '2021-04-05', 141 | 'name': 'Board of Governors of the Federal Reserve System (US)', 142 | 'link': 'http://www.federalreserve.gov/'}]} 143 | """ 144 | self._viable_api_key() 145 | url_prefix_params = { 146 | "a_url_prefix": "source?source_id=", 147 | "an_int_id": source_id, 148 | } 149 | url_prefix = self._append_id_to_url(**url_prefix_params) 150 | optional_args = { 151 | "&realtime_start=": realtime_start, 152 | "&realtime_end=": realtime_end, 153 | } 154 | url = self._add_optional_params(url_prefix, optional_args) 155 | self.source_stack["get_a_source"] = self._fetch_data(url) 156 | return self.source_stack["get_a_source"] 157 | 158 | def get_releases_for_a_source( 159 | self, 160 | source_id: int, 161 | realtime_start: str = None, 162 | realtime_end: str = None, 163 | limit: int = None, 164 | offset: int = None, 165 | order_by: str = None, 166 | sort_order: str = None, 167 | ): 168 | """ 169 | Get the releases for a source. 170 | 171 | Parameters 172 | ---------- 173 | source_id: int 174 | The ID of the series. 175 | realtime_start: str, default None 176 | The start of the real-time period formatted as "YYYY-MM-DD". 177 | If None, fred.realtime_start is used. 178 | If fred.realtime_start = None, FRED web service will use today's date. 179 | realtime_end: str, default None 180 | The end of the real-time period formatted as "YYYY-MM-DD". 181 | If None, fred.realtime_end is used. 182 | If fred.realtime_end = None, FRED web service will use today's date. 183 | limit: int, default None 184 | The maximum number of results to return. 185 | Values can be in range(1, 1_001). 186 | If None, FRED will use limit = 1_000. 187 | offset: int, default None 188 | Can be a non-negative int. 189 | If None, offset of 0 is used. 190 | order_by: str, default None 191 | Order results by values of the specified attribute. 192 | Can be one of "release_id", "name", "press_release", "realtime_start", "realtime_end". 193 | If None, "release_id" is used. 194 | sort_order: str, default None 195 | Sort results in ascending or descending order for attribute values specified by order_by. 196 | Can be "asc" or "desc". 197 | If None, "asc" is used. 198 | 199 | Returns 200 | ------- 201 | dict 202 | Metadata of each release for a source. 203 | 204 | See Also 205 | -------- 206 | fred.get_series_on_a_release: Get the series within a release of economic data. 207 | 208 | Notes 209 | ----- 210 | FRED web service endpoint: fred/source/releases 211 | https://fred.stlouisfed.org/docs/api/fred/source_releases.html 212 | 213 | Examples 214 | -------- 215 | >>> fred.get_releases_for_a_source(source_id = 1, limit = 3, order_by = 'press_release', sort_order = 'desc') 216 | {'realtime_start': '2021-04-05', 217 | 'realtime_end': '2021-04-05', 218 | 'order_by': 'press_release', 219 | 'sort_order': 'desc', 220 | 'count': 34, 221 | 'offset': 0, 222 | 'limit': 3, 223 | 'releases': [ 224 | {'id': 13, 225 | 'realtime_start': '2021-04-05', 226 | 'realtime_end': '2021-04-05', 227 | 'name': 'G.17 Industrial Production and Capacity Utilization', 228 | 'press_release': True, 229 | 'link': 'http://www.federalreserve.gov/releases/g17/'}, 230 | {'id': 14, ...... 231 | """ 232 | self._viable_api_key() 233 | url_prefix_params = { 234 | "a_url_prefix": "source/releases?source_id=", 235 | "an_int_id": source_id, 236 | } 237 | url_prefix = self._append_id_to_url(**url_prefix_params) 238 | optional_args = { 239 | "&realtime_start=": realtime_start, 240 | "&realtime_end=": realtime_end, 241 | "&limit=": limit, 242 | "&offset=": offset, 243 | "&order_by=": order_by, 244 | "&sort_order=": sort_order, 245 | } 246 | url = self._add_optional_params(url_prefix, optional_args) 247 | self.source_stack["get_releases_for_a_source"] = self._fetch_data(url) 248 | return self.source_stack["get_releases_for_a_source"] 249 | -------------------------------------------------------------------------------- /full_fred/tags.py: -------------------------------------------------------------------------------- 1 | from .sources import Sources 2 | 3 | 4 | class Tags(Sources): 5 | def __init__(self): 6 | """ 7 | FRED tag = an attribute assigned to a series. 8 | Metadata for a tag includes name, group_id, notes, date of creation, popularity, series count. 9 | FRED web service endpoint: fred/tags 10 | https://fred.stlouisfed.org/docs/api/fred/ 11 | """ 12 | super().__init__() 13 | self.tag_stack = dict() 14 | 15 | def get_all_tags( 16 | self, 17 | realtime_start: str = None, 18 | realtime_end: str = None, 19 | tag_names: list = None, 20 | tag_group_id: str = None, 21 | search_text: str = None, 22 | limit: int = None, 23 | offset: int = None, 24 | order_by: str = None, 25 | sort_order: str = None, 26 | ) -> dict: 27 | """ 28 | Get all FRED tags, search for FRED tags, get metadata for FRED tags. 29 | 30 | Parameters 31 | ---------- 32 | realtime_start: str, default None 33 | The start of the real-time period formatted as "YYYY-MM-DD". 34 | If None, fred.realtime_start is used. 35 | If fred.realtime_start = None, FRED web service will use today's date. 36 | realtime_end: str, default None 37 | The end of the real-time period formatted as "YYYY-MM-DD". 38 | If None, fred.realtime_end is used. 39 | If fred.realtime_end = None, FRED web service will use today's date. 40 | tag_names: list, default None 41 | list of tags [str] to include in returned data, excluding any tag not in tag_names; 42 | each tag must be present in the tag of returned series. 43 | If None, no filtering by tag names is done. 44 | tag_group_id: str, default None 45 | a tag group id to filter tags by type with. 46 | Can be one of 'freq' for frequency, 'gen' for general or concept, 47 | 'geo' for geography, 'geot' for geography type, 'rls' for release, 48 | 'seas' for seasonal adjustment, 'src' for source, 'cc' for copyright. 49 | If None, no filtering by tag group is done. 50 | search_text: str, default None 51 | The words to find matching tags with. 52 | If None, no filtering by search words. 53 | limit: int, default None 54 | The maximum number of results to return. 55 | Values can be in range(1, 1_001). 56 | If None, FRED will use limit = 1_000. 57 | offset: int, default None 58 | If None, offset of 0 is used. 59 | order_by: str, default None 60 | Order results by values of the specified attribute. 61 | Can be one of "series_count", "popularity", "created", "name", "group_id". 62 | If None, "series_count" is used. 63 | sort_order: str, default None 64 | Sort results in ascending or descending order for attribute values specified by order_by. 65 | Can be "asc" or "desc". 66 | If None, "asc" is used. 67 | 68 | Returns 69 | ------- 70 | dict 71 | Metadata of requested FRED tags 72 | 73 | See Also 74 | -------- 75 | get_related_tags_for_a_tag: get related tags 76 | 77 | Notes 78 | ----- 79 | FRED web service endpoint: fred/tags 80 | https://fred.stlouisfed.org/docs/api/fred/tags.html 81 | 82 | Examples 83 | -------- 84 | >>> fred.get_all_tags(limit = 2, sort_order = 'desc', tag_names = ('gdp', 'oecd',), order_by = 'name') 85 | {'realtime_start': '2021-04-05', 86 | 'realtime_end': '2021-04-05', 87 | 'order_by': 'name', 88 | 'sort_order': 'desc', 89 | 'count': 2, 90 | 'offset': 0, 91 | 'limit': 2, 92 | 'tags': [ 93 | {'name': 'oecd', 94 | 'group_id': 'src', 95 | 'notes': 'Org. for Economic Co-operation and Development', 96 | 'created': '2012-02-27 10:18:19-06', 97 | 'popularity': 77, 98 | 'series_count': 64090}, 99 | {'name': 'gdp', ........... 100 | """ 101 | self._viable_api_key() 102 | url_prefix = "tags?" 103 | optional_args = { 104 | "&realtime_start=": realtime_start, 105 | "&realtime_end=": realtime_end, 106 | "&tag_names=": tag_names, 107 | "&tag_group_id=": tag_group_id, 108 | "&search_text=": search_text, 109 | "&limit=": limit, 110 | "&offset=": offset, 111 | "&order_by=": order_by, 112 | "&sort_order=": sort_order, 113 | } 114 | url = self._add_optional_params(url_prefix, optional_args) 115 | self.tag_stack["get_all_tags"] = self._fetch_data(url) 116 | return self.tag_stack["get_all_tags"] 117 | 118 | def get_related_tags_for_a_tag( 119 | self, 120 | tag_names: list, 121 | realtime_start: str = None, 122 | realtime_end: str = None, 123 | exclude_tag_names: list = None, 124 | tag_group_id: str = None, 125 | search_text: str = None, 126 | limit: int = None, 127 | offset: int = None, 128 | order_by: str = None, 129 | sort_order: str = None, 130 | ) -> dict: 131 | """ 132 | Get related FRED tags for one or more FRED tags. 133 | 134 | Parameters 135 | ---------- 136 | tag_names: list 137 | list of tags that series match all of. 138 | Each tag must be present in the tag of returned series. 139 | realtime_start: str, default None 140 | The start of the real-time period formatted as "YYYY-MM-DD". 141 | If None, fred.realtime_start is used. 142 | If fred.realtime_start = None, FRED web service will use today's date. 143 | realtime_end: str, default None 144 | The end of the real-time period formatted as "YYYY-MM-DD". 145 | If None, fred.realtime_end is used. 146 | If fred.realtime_end = None, FRED web service will use today's date. 147 | exclude_tag_names: list, default None 148 | A list of tag names that series match none of. 149 | If None, no tag names are excluded. 150 | tag_group_id: str, default None 151 | A tag group id to filter tags by type with. 152 | Can be one of 'freq' for frequency, 'gen' for general or concept, 153 | 'geo' for geography, 'geot' for geography type, 'rls' for release, 154 | 'seas' for seasonal adjustment, 'src' for source. 155 | If None, no filtering by tag group is done. 156 | search_text: str, default None 157 | The words to find matching tags with. 158 | If None, no filtering by search words is done. 159 | limit: int, default None 160 | The maximum number of results to return. 161 | Values can be in range(1, 1_001). 162 | If None, FRED will use limit = 1_000. 163 | offset: int, default None 164 | If None, offset of 0 is used. 165 | order_by: str, default None 166 | Order results by values of the specified attribute. 167 | Can be one of "series_count", "popularity", "created", "name", "group_id". 168 | If None, "series_count" is used. 169 | sort_order: str, default None 170 | Sort results in ascending or descending order for attribute values specified by order_by. 171 | Can be "asc" or "desc". 172 | If None, "asc" is used. 173 | 174 | Returns 175 | ------- 176 | dict 177 | 178 | See Also 179 | -------- 180 | get_all_tags: get all tags in use 181 | 182 | Notes 183 | ----- 184 | FRED web service endpoint: fred/related_tags 185 | https://fred.stlouisfed.org/docs/api/fred/related_tags.html 186 | 187 | Examples 188 | -------- 189 | >>> params = {'tag_names': ('monetary aggregates', 'weekly'), 190 | 'limit': 2, 191 | 'tag_group_id': 'geo', 192 | 'order_by': 'name', 193 | 'sort_order': 'desc',} 194 | >>> fred.get_related_tags_for_a_tag(**params) 195 | {'realtime_start': '2021-04-05', 196 | 'realtime_end': '2021-04-05', 197 | 'order_by': 'name', 198 | 'sort_order': 'desc', 199 | 'count': 1, 200 | 'offset': 0, 201 | 'limit': 2, 202 | 'tags': [ 203 | {'name': 'usa', 204 | 'group_id': 'geo', 205 | 'notes': 'United States of America', 206 | 'created': '2012-02-27 10:18:19-06', 207 | 'popularity': 100, 208 | 'series_count': 14}]} 209 | """ 210 | self._viable_api_key() 211 | fused_tag_names = self._join_strings_by(tag_names, ";") 212 | url_prefix_params = { 213 | "a_url_prefix": "related_tags?tag_names=", 214 | "a_str_id": fused_tag_names, 215 | } 216 | url_prefix = self._append_id_to_url(**url_prefix_params) 217 | optional_args = { 218 | "&realtime_start=": realtime_start, 219 | "&realtime_end=": realtime_end, 220 | "&exclude_tag_names=": exclude_tag_names, 221 | "&tag_group_id=": tag_group_id, 222 | "&search_text=": search_text, 223 | "&limit=": limit, 224 | "&offset=": offset, 225 | "&order_by=": order_by, 226 | "&sort_order=": sort_order, 227 | } 228 | url = self._add_optional_params(url_prefix, optional_args) 229 | self.tag_stack["get_related_tags_for_a_tag"] = self._fetch_data(url) 230 | return self.tag_stack["get_related_tags_for_a_tag"] 231 | 232 | def get_series_matching_tags( 233 | self, 234 | tag_names: list, 235 | exclude_tag_names: list = None, 236 | realtime_start: str = None, 237 | realtime_end: str = None, 238 | limit: int = None, 239 | offset: int = None, 240 | order_by: str = None, 241 | sort_order: str = None, 242 | ) -> dict: 243 | """ 244 | Get the series matching all tags in tag_names parameter and 245 | no tags in the exclude_tag_names parameter. 246 | 247 | Parameters 248 | ---------- 249 | tag_names: list 250 | list of tags that series match all of. 251 | Each tag must be present in the tag of returned series. 252 | exclude_tag_names: list, default None 253 | A list of tag names that series match none of. 254 | If None, no tag names are excluded. 255 | realtime_start: str, default None 256 | The start of the real-time period formatted as "YYYY-MM-DD". 257 | If None, fred.realtime_start is used. 258 | If fred.realtime_start = None, FRED web service will use today's date. 259 | realtime_end: str, default None 260 | The end of the real-time period formatted as "YYYY-MM-DD". 261 | If None, fred.realtime_end is used. 262 | If fred.realtime_end = None, FRED web service will use today's date. 263 | limit: int, default None 264 | The maximum number of results to return. 265 | Values can be in range(1, 1_001). 266 | If None, FRED will use limit = 1_000. 267 | offset: int, default None 268 | If None, offset of 0 is used. 269 | order_by: str, default None 270 | Order results by values of the specified attribute. 271 | Can be one of "series_id", "title", "units", "frequency", 272 | "seasonal_adjustment", "realtime_start", "realtime_end", 273 | "last_updated", "observation_start", "observation_end", 274 | "popularity", "group_popularity". 275 | If None, "series_id" is used. 276 | sort_order: str, default None 277 | Sort results in ascending or descending order for attribute values specified by order_by. 278 | Can be "asc" or "desc". 279 | If None, "asc" is used. 280 | 281 | Returns 282 | ------- 283 | dict 284 | Metadata of series with requested tags. 285 | 286 | See Also 287 | -------- 288 | 289 | Notes 290 | ----- 291 | FRED web service endpoint: fred/tags/series 292 | https://fred.stlouisfed.org/docs/api/fred/tags_series.html 293 | 294 | Examples 295 | -------- 296 | >>> params = { 297 | 'tag_names': ('oecd', 'spain',), 298 | 'limit': 2, 299 | 'order_by': 'seasonal_adjustment', 300 | 'sort_order': 'desc', 301 | } 302 | >>> fred.get_series_matching_tags(**params) 303 | {'realtime_start': '2021-04-05', 304 | 'realtime_end': '2021-04-05', 305 | 'order_by': 'seasonal_adjustment', 306 | 'sort_order': 'desc', 307 | 'count': 1630, 308 | 'offset': 1, 309 | 'limit': 2, 310 | 'seriess': [{'id': 'XTNTVA01ESM667S', 311 | 'realtime_start': '2021-04-05', 312 | 'realtime_end': '2021-04-05', 313 | 'title': 'Net Trade: Value Goods for Spain', 314 | 'observation_start': '1966-01-01', 315 | 'observation_end': '2020-12-01', 316 | 'frequency': 'Monthly', 317 | 'frequency_short': 'M', 318 | 'units': 'US Dollars Monthly Level', 319 | 'units_short': 'US $, Monthly Level', 320 | 'seasonal_adjustment': 'Seasonally Adjusted', 321 | 'seasonal_adjustment_short': 'SA', 322 | 'last_updated': '2021-03-16 17:07:37-05', 323 | 'popularity': 1, 324 | 'group_popularity': 1, 325 | 'notes': 'OECD descriptor ID: XTNTVA01' 326 | {'id': 'XTNTVA01ESQ664S', ........... 327 | """ 328 | self._viable_api_key() 329 | fused_tag_names = self._join_strings_by(tag_names, ";") 330 | url_prefix_params = { 331 | "a_url_prefix": "tags/series?tag_names=", 332 | "a_str_id": fused_tag_names, 333 | } 334 | url_prefix = self._append_id_to_url(**url_prefix_params) 335 | optional_args = { 336 | "&exclude_tag_names=": exclude_tag_names, 337 | "&realtime_start=": realtime_start, 338 | "&realtime_end=": realtime_end, 339 | "&limit=": limit, 340 | "&offset=": offset, 341 | "&order_by=": order_by, 342 | "&sort_order=": sort_order, 343 | } 344 | url = self._add_optional_params(url_prefix, optional_args) 345 | self.tag_stack["get_series_matching_tags"] = self._fetch_data(url) 346 | return self.tag_stack["get_series_matching_tags"] 347 | -------------------------------------------------------------------------------- /full_fred/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/7astro7/full_fred/7949411ed775ccbef9922572b9e76a4da0238ce4/full_fred/tests/__init__.py -------------------------------------------------------------------------------- /full_fred/tests/fred_test_utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | import os 3 | 4 | 5 | def returned_ok( 6 | observed: dict, 7 | expected: dict = None, 8 | check_union: list = None, 9 | ) -> bool: 10 | """ 11 | Parameters 12 | ---------- 13 | observed: dict 14 | A FRED web service response. 15 | expected: dict 16 | expected key, value pairs to check observed key, value pairs against. 17 | check_union: list, default None 18 | iterable of keys; if no element of check_union is present in 19 | observed.keys(), return False. 20 | 21 | Returns 22 | ------- 23 | bool 24 | Whether observed contains expected data. 25 | """ 26 | if not isinstance(observed, dict): 27 | return False 28 | if expected is not None: 29 | for expected_key in expected.keys(): 30 | if not expected_key in observed.keys(): 31 | return False 32 | if expected[expected_key] != observed[expected_key]: 33 | return False 34 | if check_union is None: 35 | return True 36 | for key in check_union: 37 | if key in observed.keys(): 38 | return True 39 | return False 40 | 41 | 42 | def make_time_string( 43 | start: bool = False, 44 | ) -> str: 45 | """ 46 | Method to create start_time, end_time arguments for 47 | test_series_methods.test_get_series_updates_method_works 48 | """ 49 | time_string = datetime.now() - timedelta(days=10) # start_time 50 | if not start: 51 | time_string = datetime.now() - timedelta(days=5) # end_time 52 | return time_string.strftime(format="%Y%m%d%H%M") 53 | 54 | 55 | def api_key_found_in_env() -> bool: 56 | if "FRED_API_KEY" not in os.environ.keys(): 57 | return False 58 | return True 59 | -------------------------------------------------------------------------------- /full_fred/tests/test_api_key_file.py: -------------------------------------------------------------------------------- 1 | from full_fred.fred import Fred 2 | import pytest 3 | import os 4 | from .fred_test_utils import returned_ok 5 | 6 | key_file = "example_key.txt" 7 | test_key_file = True if key_file in os.listdir() else False 8 | 9 | 10 | @pytest.fixture 11 | def fred(): 12 | return Fred(api_key_file=key_file) 13 | 14 | 15 | @pytest.mark.skipif(not test_key_file, reason="file to read api key from is absent") 16 | def test_api_key_file_works_with_get_a_category_method( 17 | fred: Fred, 18 | ) -> bool: 19 | fred.get_a_category(0) 20 | returned_ok_params = { 21 | "observed": fred.category_stack["get_a_category"], 22 | "check_union": ("categories",), 23 | } 24 | return returned_ok(**returned_ok_params) 25 | 26 | 27 | @pytest.fixture 28 | def a_random_api_key_dummy_file() -> str: 29 | return "orange_juice_and_peanuts.txt" 30 | 31 | 32 | def test_api_key_file_get_works_after_constructor( 33 | a_random_api_key_dummy_file: str, 34 | ): 35 | with pytest.raises(FileNotFoundError): 36 | Fred(api_key_file=a_random_api_key_dummy_file) 37 | -------------------------------------------------------------------------------- /full_fred/tests/test_category_methods.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from full_fred.fred import Fred 3 | from .fred_test_utils import ( 4 | returned_ok, 5 | api_key_found_in_env, 6 | ) 7 | 8 | ENV_API_KEY = api_key_found_in_env() 9 | 10 | 11 | @pytest.fixture 12 | def fred() -> Fred: 13 | return Fred() 14 | 15 | 16 | @pytest.fixture 17 | def returned_ok_params() -> dict: 18 | return dict() 19 | 20 | 21 | @pytest.fixture 22 | def get_a_category_method_works( 23 | fred: Fred, 24 | returned_ok_params: dict, 25 | ) -> bool: 26 | fred.get_a_category() # root category of 0 27 | returned_ok_params["observed"] = fred.category_stack["get_a_category"] 28 | returned_ok_params["check_union"] = ("categories",) 29 | return returned_ok(**returned_ok_params) 30 | 31 | 32 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 33 | def test_get_a_category( 34 | get_a_category_method_works: bool, 35 | ): 36 | assert get_a_category_method_works == True 37 | 38 | 39 | @pytest.fixture 40 | def get_child_categories_method_works( 41 | fred: Fred, 42 | returned_ok_params: dict, 43 | ) -> bool: 44 | params = { 45 | "category_id": 13, 46 | } 47 | fred.get_child_categories(**params) 48 | returned_ok_params["observed"] = fred.category_stack["get_child_categories"] 49 | returned_ok_params["check_union"] = ("categories",) 50 | return returned_ok(**returned_ok_params) 51 | 52 | 53 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 54 | def test_get_child_categories( 55 | get_child_categories_method_works: bool, 56 | ): 57 | assert get_child_categories_method_works == True 58 | 59 | 60 | @pytest.fixture 61 | def get_related_categories_method_works( 62 | fred: Fred, 63 | returned_ok_params: dict, 64 | ) -> bool: 65 | params = { 66 | "category_id": 32073, 67 | } 68 | fred.get_related_categories(**params) 69 | returned_ok_params["observed"] = fred.category_stack["get_related_categories"] 70 | returned_ok_params["check_union"] = ("categories",) 71 | return returned_ok(**returned_ok_params) 72 | 73 | 74 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 75 | def test_get_related_categories( 76 | get_related_categories_method_works: bool, 77 | ): 78 | assert get_related_categories_method_works == True 79 | 80 | 81 | @pytest.fixture 82 | def get_series_in_a_category_method_works( 83 | fred: Fred, 84 | returned_ok_params: dict, 85 | ) -> bool: 86 | params = { 87 | "category_id": 125, 88 | "limit": 3, 89 | "realtime_start": "2020-01-01", 90 | "filter_variable": "units", 91 | "order_by": "units", 92 | "sort_order": "desc", 93 | "offset": 1, 94 | } 95 | fred.get_series_in_a_category(**params) 96 | returned_ok_params["observed"] = fred.category_stack["get_series_in_a_category"] 97 | params.pop("category_id") 98 | params.pop("filter_variable") 99 | returned_ok_params["expected"] = params 100 | returned_ok_params["check_union"] = ( 101 | "series", 102 | "seriess", 103 | ) 104 | return returned_ok(**returned_ok_params) 105 | 106 | 107 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 108 | def test_get_series_in_a_category( 109 | get_series_in_a_category_method_works: bool, 110 | ): 111 | assert get_series_in_a_category_method_works == True 112 | 113 | 114 | @pytest.fixture 115 | def get_tags_for_a_category_method_works( 116 | fred: Fred, 117 | returned_ok_params: dict, 118 | ) -> bool: 119 | params = { 120 | "category_id": 125, 121 | "limit": 3, 122 | "realtime_end": "2018-05-22", 123 | "order_by": "created", 124 | "sort_order": "desc", 125 | "offset": 1, 126 | } 127 | fred.get_tags_for_a_category(**params) 128 | returned_ok_params["observed"] = fred.category_stack["get_tags_for_a_category"] 129 | params.pop("category_id") 130 | returned_ok_params["expected"] = params 131 | returned_ok_params["check_union"] = ("tags",) 132 | return returned_ok(**returned_ok_params) 133 | 134 | 135 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 136 | def test_get_tags_for_a_category( 137 | get_tags_for_a_category_method_works: bool, 138 | ): 139 | assert get_tags_for_a_category_method_works == True 140 | 141 | 142 | @pytest.fixture 143 | def get_related_tags_for_a_category_method_works( 144 | fred: Fred, 145 | returned_ok_params: dict, 146 | ) -> bool: 147 | params = { 148 | "category_id": 125, 149 | "tag_names": ("services", "quarterly"), 150 | "limit": 3, 151 | "realtime_end": "2015-09-21", 152 | "order_by": "created", 153 | "sort_order": "desc", 154 | "offset": 1, 155 | } 156 | fred.get_related_tags_for_a_category(**params) 157 | returned_ok_params["observed"] = fred.category_stack[ 158 | "get_related_tags_for_a_category" 159 | ] 160 | params.pop("category_id") 161 | params.pop("tag_names") 162 | returned_ok_params["expected"] = params 163 | returned_ok_params["check_union"] = ("tags",) 164 | return returned_ok(**returned_ok_params) 165 | 166 | 167 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 168 | def test_get_related_tags_for_a_category( 169 | get_related_tags_for_a_category_method_works: bool, 170 | ): 171 | assert get_related_tags_for_a_category_method_works == True 172 | -------------------------------------------------------------------------------- /full_fred/tests/test_fred.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from full_fred.fred import Fred 4 | 5 | 6 | @pytest.fixture 7 | def fred() -> Fred: 8 | return Fred(observation_start="2024-01-01", observation_end="2024-06-30") 9 | 10 | 11 | def test_observation_start_end_assigned(fred: Fred): 12 | assert fred.observation_start == "2024-01-01" 13 | assert fred.observation_end == "2024-06-30" 14 | -------------------------------------------------------------------------------- /full_fred/tests/test_fred_base_methods.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from full_fred.fred_base import FredBase 3 | from .fred_test_utils import api_key_found_in_env 4 | 5 | ENV_API_KEY = api_key_found_in_env() 6 | 7 | 8 | @pytest.fixture 9 | def fredbase() -> FredBase: 10 | return FredBase() 11 | 12 | 13 | @pytest.fixture 14 | def observation_start_attr() -> str: 15 | return "2020-05-10" 16 | 17 | 18 | @pytest.fixture 19 | def observation_end_attr() -> str: 20 | return "2021-05-10" 21 | 22 | 23 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 24 | def test_add_optional_params( 25 | fredbase: FredBase, 26 | observation_end_attr: str, 27 | observation_start_attr: str, 28 | ): 29 | """ 30 | uses optional parameters of series.get_series_df() 31 | """ 32 | 33 | # test append_id_to_url first for specificity 34 | url_prefix_params = { 35 | "a_url_prefix": "series/observations?series_id=", 36 | "a_str_id": "UNRATE", 37 | } 38 | url_prefix = fredbase._append_id_to_url(**url_prefix_params) 39 | assert url_prefix == "series/observations?series_id=UNRATE" 40 | 41 | # set observation_start, observation_end: new_url_str should 42 | # include the values the attributes point to 43 | optional_args = { 44 | "&realtime_start=": None, 45 | "&realtime_end=": None, 46 | "&limit=": None, 47 | "&offset=": None, 48 | "&sort_order=": None, 49 | "&observation_start=": None, 50 | "&observation_end=": None, 51 | "&units=": None, 52 | "&frequency=": None, 53 | "&aggregation_method=": None, 54 | "&output_type=": None, 55 | "&vintage_dates=": None, 56 | } 57 | fredbase.observation_start = observation_start_attr 58 | fredbase.observation_end = observation_end_attr 59 | url_string = fredbase._add_optional_params(url_prefix, optional_args) 60 | assert "observation_start" in url_string 61 | assert "observation_end" in url_string 62 | assert observation_start_attr in url_string 63 | assert observation_end_attr in url_string 64 | -------------------------------------------------------------------------------- /full_fred/tests/test_release_methods.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from full_fred.fred import Fred 3 | from .fred_test_utils import ( 4 | returned_ok, 5 | api_key_found_in_env, 6 | ) 7 | 8 | ENV_API_KEY = api_key_found_in_env() 9 | 10 | 11 | @pytest.fixture 12 | def fred(): 13 | return Fred() 14 | 15 | 16 | @pytest.fixture 17 | def returned_ok_params() -> dict: 18 | return dict() 19 | 20 | 21 | @pytest.fixture 22 | def get_all_releases_method_works( 23 | fred: Fred, 24 | returned_ok_params: dict, 25 | ) -> bool: 26 | params = { 27 | "limit": 2, 28 | "realtime_start": "2018-02-02", 29 | "offset": 1, 30 | "sort_order": "desc", 31 | "order_by": "press_release", 32 | } 33 | fred.get_all_releases(**params) 34 | returned_ok_params["observed"] = fred.release_stack["get_all_releases"] 35 | returned_ok_params["check_union"] = ("releases",) 36 | returned_ok_params["expected"] = params 37 | return returned_ok(**returned_ok_params) 38 | 39 | 40 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 41 | def test_get_all_releases( 42 | get_all_releases_method_works: bool, 43 | ): 44 | assert get_all_releases_method_works == True 45 | 46 | 47 | @pytest.fixture 48 | def get_release_dates_all_releases_method_works( 49 | fred: Fred, 50 | returned_ok_params: dict, 51 | ) -> bool: 52 | params = { 53 | "limit": 2, 54 | "realtime_end": "2018-02-02", 55 | "include_empty": True, 56 | "offset": 1, 57 | "sort_order": "asc", 58 | "order_by": "release_name", 59 | } 60 | fred.get_release_dates_all_releases(**params) 61 | returned_ok_params["observed"] = fred.release_stack[ 62 | "get_release_dates_all_releases" 63 | ] 64 | returned_ok_params["check_union"] = ("release_dates",) 65 | params.pop("include_empty") 66 | returned_ok_params["expected"] = params 67 | return returned_ok(**returned_ok_params) 68 | 69 | 70 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 71 | def test_get_release_dates_all_releases( 72 | get_release_dates_all_releases_method_works: bool, 73 | ): 74 | assert get_release_dates_all_releases_method_works == True 75 | 76 | 77 | @pytest.fixture 78 | def get_a_release_method_works( 79 | fred: Fred, 80 | returned_ok_params: dict, 81 | ) -> bool: 82 | params = { 83 | "release_id": 53, 84 | "realtime_end": "2018-02-02", 85 | } 86 | fred.get_a_release(**params) 87 | returned_ok_params["observed"] = fred.release_stack["get_a_release"] 88 | params.pop("release_id") 89 | returned_ok_params["expected"] = params 90 | returned_ok_params["check_union"] = ("releases",) 91 | return returned_ok(**returned_ok_params) 92 | 93 | 94 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 95 | def test_get_a_release( 96 | get_a_release_method_works: bool, 97 | ): 98 | assert get_a_release_method_works == True 99 | 100 | 101 | @pytest.fixture 102 | def get_release_dates_method_works( 103 | fred: Fred, 104 | returned_ok_params: dict, 105 | ) -> bool: 106 | params = { 107 | "release_id": 82, 108 | "limit": 3, 109 | "realtime_end": "2015-02-02", 110 | "include_empty": True, 111 | "offset": 1, 112 | "sort_order": "desc", 113 | } 114 | fred.get_release_dates(**params) 115 | returned_ok_params["observed"] = fred.release_stack["get_release_dates"] 116 | params.pop("release_id") 117 | params.pop("include_empty") 118 | returned_ok_params["expected"] = params 119 | returned_ok_params["check_union"] = ("release_dates",) 120 | return returned_ok(**returned_ok_params) 121 | 122 | 123 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 124 | def test_get_release_dates( 125 | get_release_dates_method_works: bool, 126 | ): 127 | assert get_release_dates_method_works == True 128 | 129 | 130 | @pytest.fixture 131 | def get_series_on_a_release_method_works( 132 | fred: Fred, 133 | returned_ok_params: dict, 134 | ) -> bool: 135 | params = { 136 | "release_id": 51, 137 | "limit": 3, 138 | "realtime_end": "2020-12-24", 139 | "order_by": "last_updated", 140 | "offset": 1, 141 | "sort_order": "desc", 142 | "tag_names": ("japan",), 143 | } 144 | fred.get_series_on_a_release(**params) 145 | returned_ok_params["observed"] = fred.release_stack["get_series_on_a_release"] 146 | returned_ok_params["check_union"] = ( 147 | "seriess", 148 | "series", 149 | ) 150 | params.pop("release_id") 151 | params.pop("tag_names") 152 | returned_ok_params["expected"] = params 153 | return returned_ok(**returned_ok_params) 154 | 155 | 156 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 157 | def test_get_series_on_a_release( 158 | get_series_on_a_release_method_works: bool, 159 | ): 160 | assert get_series_on_a_release_method_works == True 161 | 162 | 163 | @pytest.fixture 164 | def get_sources_for_a_release_method_works( 165 | fred: Fred, 166 | returned_ok_params: dict, 167 | ) -> bool: 168 | params = { 169 | "release_id": 51, 170 | "realtime_start": "2020-12-24", 171 | } 172 | fred.get_sources_for_a_release(**params) 173 | returned_ok_params["observed"] = fred.release_stack["get_sources_for_a_release"] 174 | params.pop("release_id") 175 | returned_ok_params["expected"] = params 176 | returned_ok_params["check_union"] = ("sources",) 177 | return returned_ok(**returned_ok_params) 178 | 179 | 180 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 181 | def test_get_sources_for_a_release( 182 | get_sources_for_a_release_method_works: bool, 183 | ): 184 | assert get_sources_for_a_release_method_works == True 185 | 186 | 187 | @pytest.fixture 188 | def get_tags_for_a_release_method_works( 189 | fred: Fred, 190 | returned_ok_params: dict, 191 | ) -> bool: 192 | params = { 193 | "release_id": 86, 194 | "limit": 3, 195 | "realtime_start": "2018-10-18", 196 | "tag_names": "gnp", 197 | "offset": 3, 198 | "sort_order": "desc", 199 | "order_by": "created", 200 | } 201 | fred.get_tags_for_a_release(**params) 202 | returned_ok_params["observed"] = fred.release_stack["get_tags_for_a_release"] 203 | returned_ok_params["check_union"] = ("tags",) 204 | params.pop("release_id") 205 | params.pop("tag_names") 206 | returned_ok_params["expected"] = params 207 | return returned_ok(**returned_ok_params) 208 | 209 | 210 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 211 | def test_get_tags_for_a_release( 212 | get_tags_for_a_release_method_works: bool, 213 | ): 214 | assert get_tags_for_a_release_method_works == True 215 | 216 | 217 | @pytest.fixture 218 | def get_related_tags_for_release_method_works( 219 | fred: Fred, 220 | returned_ok_params: dict, 221 | ) -> bool: 222 | params = { 223 | "release_id": 86, 224 | "tag_names": ( 225 | "sa", 226 | "foreign", 227 | ), 228 | "limit": 3, 229 | "offset": 3, 230 | "sort_order": "desc", 231 | "order_by": "created", 232 | "realtime_end": "2013-08-14", 233 | } 234 | fred.get_related_tags_for_release(**params) 235 | returned_ok_params["observed"] = fred.release_stack["get_related_tags_for_release"] 236 | params.pop("release_id") 237 | params.pop("tag_names") 238 | returned_ok_params["expected"] = params 239 | returned_ok_params["check_union"] = ("tags",) 240 | return returned_ok(**returned_ok_params) 241 | 242 | 243 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 244 | def test_get_related_tags_for_release( 245 | get_related_tags_for_release_method_works: bool, 246 | ): 247 | assert get_related_tags_for_release_method_works == True 248 | 249 | 250 | @pytest.fixture 251 | def get_release_tables_method_works( 252 | fred: Fred, 253 | returned_ok_params: dict, 254 | ) -> bool: 255 | params = { 256 | "release_id": 53, 257 | "element_id": 12886, 258 | "include_observation_values": True, 259 | } 260 | fred.get_release_tables(**params) 261 | returned_ok_params["observed"] = fred.release_stack["get_release_tables"] 262 | params.pop("include_observation_values") 263 | returned_ok_params["expected"] = params 264 | str_release_id = str(returned_ok_params["expected"]["release_id"]) 265 | returned_ok_params["expected"]["release_id"] = str_release_id 266 | returned_ok_params["check_union"] = ("elements",) 267 | return returned_ok(**returned_ok_params) 268 | 269 | 270 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 271 | def test_get_release_tables( 272 | get_release_tables_method_works: bool, 273 | ): 274 | assert get_release_tables_method_works == True 275 | -------------------------------------------------------------------------------- /full_fred/tests/test_series_methods.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from full_fred.fred import Fred 3 | from pandas import DataFrame 4 | from .fred_test_utils import ( 5 | returned_ok, 6 | make_time_string, 7 | api_key_found_in_env, 8 | ) 9 | 10 | ENV_API_KEY = api_key_found_in_env() 11 | 12 | 13 | @pytest.fixture 14 | def fred(): 15 | return Fred() 16 | 17 | 18 | @pytest.fixture 19 | def returned_ok_params(): 20 | return dict() 21 | 22 | 23 | @pytest.fixture 24 | def get_a_series_method_works( 25 | fred: Fred, 26 | returned_ok_params: dict, 27 | ) -> bool: 28 | params = { 29 | "series_id": "GNPCA", 30 | "realtime_start": "2007-11-01", 31 | } 32 | fred.get_a_series(**params) 33 | returned_ok_params["observed"] = fred.series_stack["get_a_series"] 34 | params.pop("series_id") 35 | returned_ok_params["expected"] = params 36 | returned_ok_params["check_union"] = ( 37 | "series", 38 | "seriess", 39 | ) 40 | return returned_ok(**returned_ok_params) 41 | 42 | 43 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 44 | def test_get_series( 45 | get_a_series_method_works: bool, 46 | ): 47 | assert get_a_series_method_works == True 48 | 49 | 50 | @pytest.fixture 51 | def get_categories_of_series_method_works( 52 | fred: Fred, 53 | returned_ok_params: dict, 54 | ): 55 | params = { 56 | "series_id": "EXJPUS", 57 | # EXJPUS doesn't exist in ALFRED, forego realtime param 58 | # 'realtime_end': '1995-12-01', 59 | } 60 | fred.get_categories_of_series(**params) 61 | returned_ok_params["observed"] = fred.series_stack["get_categories_of_series"] 62 | params.pop("series_id") 63 | returned_ok_params["expected"] = params 64 | returned_ok_params["check_union"] = ("categories",) 65 | return returned_ok(**returned_ok_params) 66 | 67 | 68 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 69 | def test_get_categories_of_series( 70 | get_categories_of_series_method_works: bool, 71 | ): 72 | assert get_categories_of_series_method_works == True 73 | 74 | 75 | @pytest.fixture 76 | def get_series_df_method_works( 77 | fred: Fred, 78 | returned_ok_params: dict, 79 | ) -> bool: 80 | params = { 81 | "series_id": "GNPCA", 82 | "limit": 10, 83 | "realtime_start": "2003-01-01", 84 | # 'units': 'log', 85 | "sort_order": "desc", 86 | "offset": 1, 87 | "observation_start": "1776-07-04", 88 | "observation_end": "9999-12-31", 89 | } 90 | fred.get_series_df(**params) 91 | returned_ok_params["observed"] = fred.series_stack["get_series_df"] 92 | if not "observation_start" in returned_ok_params["observed"].keys(): 93 | return False 94 | 95 | # 1600-01-01 was returned for 1776-07-04 96 | expected_obs_start = ( 97 | "1776-07-04", 98 | "1600-01-01", 99 | ) 100 | if returned_ok_params["observed"]["observation_start"] not in expected_obs_start: 101 | return False 102 | params.pop("observation_start") 103 | 104 | # series_id is manually added to metadata of DataFrame, 105 | # so series_id in params is retained as an expected key 106 | returned_ok_params["expected"] = params 107 | returned_ok_params["check_union"] = ( 108 | "df", 109 | "observations", 110 | ) 111 | if not returned_ok(**returned_ok_params): 112 | return False 113 | if isinstance(returned_ok_params["observed"]["df"], DataFrame): 114 | return True 115 | return False 116 | 117 | 118 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 119 | def test_get_series_df( 120 | get_series_df_method_works: bool, 121 | ): 122 | assert get_series_df_method_works == True 123 | 124 | 125 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 126 | def test_get_series_df_raises(fred: Fred): 127 | with pytest.raises(KeyError): 128 | fred.get_series_df("GOLDPMGBD229NLBM") 129 | 130 | 131 | @pytest.fixture 132 | def get_release_for_a_series_method_works( 133 | fred: Fred, 134 | returned_ok_params: dict, 135 | ) -> bool: 136 | params = { 137 | "series_id": "IRA", 138 | "realtime_end": "2014-07-04", 139 | } 140 | fred.get_release_for_a_series(**params) 141 | returned_ok_params["observed"] = fred.series_stack["get_release_for_a_series"] 142 | params.pop("series_id") 143 | returned_ok_params["expected"] = params 144 | returned_ok_params["check_union"] = ("releases",) 145 | return returned_ok(**returned_ok_params) 146 | 147 | 148 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 149 | def test_get_release_for_a_series( 150 | get_release_for_a_series_method_works: bool, 151 | ): 152 | assert get_release_for_a_series_method_works == True 153 | 154 | 155 | @pytest.fixture 156 | def search_for_series_method_works( 157 | fred: Fred, 158 | returned_ok_params: dict, 159 | ) -> bool: 160 | params = { 161 | "search_words": ( 162 | "monetary", 163 | "service", 164 | "index", 165 | ), 166 | "limit": 3, 167 | "realtime_end": "2019-06-03", 168 | # 'search_type': 'series_id', 169 | "order_by": "title", 170 | "sort_order": "desc", 171 | "offset": 2, 172 | } 173 | fred.search_for_series(**params) 174 | returned_ok_params["observed"] = fred.series_stack["search_for_series"] 175 | params.pop("search_words") 176 | returned_ok_params["expected"] = params 177 | returned_ok_params["check_union"] = ( 178 | "seriess", 179 | "series", 180 | ) 181 | return returned_ok(**returned_ok_params) 182 | 183 | 184 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 185 | def test_search_for_series( 186 | search_for_series_method_works: bool, 187 | ): 188 | assert search_for_series_method_works == True 189 | 190 | 191 | @pytest.fixture 192 | def get_tags_for_series_search_method_works( 193 | fred: Fred, 194 | returned_ok_params: dict, 195 | ) -> bool: 196 | params = { 197 | "search_words": ( 198 | "monetary", 199 | "service", 200 | "index", 201 | ), 202 | "limit": 3, 203 | "realtime_end": "2019-06-03", 204 | # 'tag_search_words': ('', '',), 205 | "order_by": "created", 206 | "sort_order": "desc", 207 | "offset": 2, 208 | } 209 | fred.get_tags_for_series_search(**params) 210 | returned_ok_params["observed"] = fred.series_stack["get_tags_for_series_search"] 211 | params.pop("search_words") 212 | returned_ok_params["expected"] = params 213 | returned_ok_params["check_union"] = ("tags",) 214 | return returned_ok(**returned_ok_params) 215 | 216 | 217 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 218 | def test_get_tags_for_series_search( 219 | get_tags_for_series_search_method_works: bool, 220 | ): 221 | assert get_tags_for_series_search_method_works == True 222 | 223 | 224 | @pytest.fixture 225 | def get_related_tags_for_series_search_method_works( 226 | fred: Fred, 227 | returned_ok_params: dict, 228 | ) -> bool: 229 | params = { 230 | "search_words": ( 231 | "mortgage", 232 | "rate", 233 | "index", 234 | ), 235 | "tag_names": ( 236 | "30-year", 237 | "frb", 238 | ), 239 | # 'tag_search_words': ('', '',), 240 | "limit": 3, 241 | "realtime_end": "2020-02-13", 242 | "order_by": "name", 243 | "sort_order": "desc", 244 | "offset": 2, 245 | } 246 | fred.get_related_tags_for_series_search(**params) 247 | returned_ok_params["observed"] = fred.series_stack[ 248 | "get_related_tags_for_series_search" 249 | ] 250 | params.pop("search_words") 251 | params.pop("tag_names") 252 | returned_ok_params["expected"] = params 253 | returned_ok_params["check_union"] = ("tags",) 254 | return returned_ok(**returned_ok_params) 255 | 256 | 257 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 258 | def test_get_related_tags_for_series_search( 259 | get_related_tags_for_series_search_method_works: bool, 260 | ): 261 | assert get_related_tags_for_series_search_method_works == True 262 | 263 | 264 | @pytest.fixture 265 | def get_tags_for_a_series_method_works( 266 | fred: Fred, 267 | returned_ok_params: dict, 268 | ) -> bool: 269 | params = { 270 | "series_id": "STLFSI", 271 | "realtime_end": "2019-02-13", 272 | "order_by": "name", 273 | "sort_order": "desc", 274 | } 275 | fred.get_tags_for_a_series(**params) 276 | returned_ok_params["observed"] = fred.series_stack["get_tags_for_a_series"] 277 | params.pop("series_id") 278 | returned_ok_params["expected"] = params 279 | returned_ok_params["check_union"] = ("tags",) 280 | return returned_ok(**returned_ok_params) 281 | 282 | 283 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 284 | def test_get_tags_for_a_series( 285 | get_tags_for_a_series_method_works: bool, 286 | ): 287 | assert get_tags_for_a_series_method_works == True 288 | 289 | 290 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 291 | def test_get_series_updates_raises_type_error( 292 | fred: Fred, 293 | ): 294 | """ 295 | Test TypeError raised when only one of start_time, end_time is 296 | passed 297 | """ 298 | with pytest.raises(TypeError): 299 | fred.get_series_updates(start_time="202103210851") 300 | 301 | 302 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 303 | def test_get_series_updates_raises_value_error( 304 | fred: Fred, 305 | ): 306 | """ 307 | Test ValueError raised when current time - end_time > 2 weeks. 308 | FRED's servers send error code 500 for start dates earlier than 309 | last 2 weeks. if end_date is earlier than last 2 weeks, FRED will 310 | return 500 error code 311 | """ 312 | params = { 313 | "start_time": "202103110851", 314 | "end_time": "202103210851", 315 | } 316 | with pytest.raises(ValueError): 317 | fred.get_series_updates(**params) 318 | 319 | 320 | @pytest.fixture 321 | def get_series_updates_method_works( 322 | fred: Fred, 323 | returned_ok_params: dict, 324 | ) -> bool: 325 | params = { 326 | "limit": 3, 327 | "filter_value": "regional", 328 | "offset": 2, 329 | "start_time": make_time_string(start=True), 330 | "end_time": make_time_string(start=False), 331 | } 332 | fred.get_series_updates(**params) 333 | returned_ok_params["observed"] = fred.series_stack["get_series_updates"] 334 | params.pop("start_time") 335 | params.pop("end_time") 336 | returned_ok_params["expected"] = params 337 | returned_ok_params["check_union"] = ( 338 | "seriess", 339 | "series", 340 | ) 341 | return returned_ok(**returned_ok_params) 342 | 343 | 344 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 345 | def test_get_series_updates( 346 | get_series_updates_method_works: bool, 347 | ): 348 | assert get_series_updates_method_works == True 349 | 350 | 351 | @pytest.fixture 352 | def get_series_vintagedates_method_works( 353 | fred: Fred, 354 | returned_ok_params: dict, 355 | ) -> bool: 356 | params = { 357 | "series_id": "GNPCA", 358 | "limit": 3, 359 | "realtime_start": "1812-06-18", 360 | "sort_order": "desc", 361 | "offset": 2, 362 | } 363 | returned_ok_params["observed"] = fred.get_series_vintagedates(**params) 364 | params.pop("series_id") 365 | returned_ok_params["expected"] = params 366 | returned_ok_params["check_union"] = ("vintage_dates",) 367 | return returned_ok(**returned_ok_params) 368 | 369 | 370 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 371 | def test_get_series_vintagedates( 372 | get_series_vintagedates_method_works: bool, 373 | ): 374 | assert get_series_vintagedates_method_works == True 375 | -------------------------------------------------------------------------------- /full_fred/tests/test_source_methods.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from full_fred.fred import Fred 3 | from .fred_test_utils import ( 4 | returned_ok, 5 | api_key_found_in_env, 6 | ) 7 | 8 | ENV_API_KEY = api_key_found_in_env() 9 | 10 | 11 | @pytest.fixture 12 | def fred() -> Fred: 13 | return Fred() 14 | 15 | 16 | @pytest.fixture 17 | def returned_ok_params() -> dict: 18 | return dict() 19 | 20 | 21 | @pytest.fixture 22 | def get_all_sources_method_works( 23 | fred: Fred, 24 | returned_ok_params: dict, 25 | ) -> bool: 26 | params = { 27 | "limit": 2, 28 | "realtime_start": "2000-01-01", 29 | "sort_order": "desc", 30 | "order_by": "name", 31 | } 32 | fred.get_all_sources(**params) 33 | returned_ok_params["observed"] = fred.source_stack["get_all_sources"] 34 | returned_ok_params["check_union"] = ("sources",) 35 | returned_ok_params["expected"] = params 36 | return returned_ok(**returned_ok_params) 37 | 38 | 39 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 40 | def test_get_all_sources( 41 | get_all_sources_method_works: bool, 42 | ): 43 | assert get_all_sources_method_works == True 44 | 45 | 46 | @pytest.fixture 47 | def get_a_source_method_works( 48 | fred: Fred, 49 | returned_ok_params: dict, 50 | ) -> bool: 51 | params = { 52 | "source_id": 1, 53 | "realtime_end": "2009-04-09", 54 | } 55 | fred.get_a_source(**params) 56 | returned_ok_params["observed"] = fred.source_stack["get_a_source"] 57 | returned_ok_params["check_union"] = ("sources",) 58 | return returned_ok(**returned_ok_params) 59 | 60 | 61 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 62 | def test_get_a_source( 63 | get_a_source_method_works: bool, 64 | ): 65 | assert get_a_source_method_works == True 66 | 67 | 68 | @pytest.fixture 69 | def get_releases_for_a_source_method_works( 70 | fred: Fred, 71 | returned_ok_params: dict, 72 | ) -> bool: 73 | params = { 74 | "source_id": 1, 75 | "limit": 3, 76 | "realtime_start": "2019-08-22", 77 | "order_by": "press_release", 78 | "sort_order": "desc", 79 | } 80 | fred.get_releases_for_a_source(**params) 81 | returned_ok_params["observed"] = fred.source_stack["get_releases_for_a_source"] 82 | params.pop("source_id") 83 | returned_ok_params["expected"] = params 84 | returned_ok_params["check_union"] = ("releases",) 85 | return returned_ok(**returned_ok_params) 86 | 87 | 88 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 89 | def test_get_releases_for_a_source( 90 | get_releases_for_a_source_method_works: bool, 91 | ): 92 | assert get_releases_for_a_source_method_works == True 93 | -------------------------------------------------------------------------------- /full_fred/tests/test_tag_methods.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from full_fred.fred import Fred 3 | from .fred_test_utils import ( 4 | returned_ok, 5 | api_key_found_in_env, 6 | ) 7 | 8 | ENV_API_KEY = api_key_found_in_env() 9 | 10 | 11 | @pytest.fixture 12 | def fred() -> Fred: 13 | return Fred() 14 | 15 | 16 | @pytest.fixture 17 | def returned_ok_params() -> dict: 18 | return dict() 19 | 20 | 21 | @pytest.fixture 22 | def get_all_tags_method_works( 23 | fred: Fred, 24 | returned_ok_params: dict, 25 | ) -> bool: 26 | params = { 27 | "limit": 2, 28 | "realtime_start": "1950-12-19", 29 | "sort_order": "desc", 30 | "tag_names": ( 31 | "gdp", 32 | "oecd", 33 | ), 34 | "order_by": "name", 35 | } 36 | fred.get_all_tags(**params) 37 | returned_ok_params["observed"] = fred.tag_stack["get_all_tags"] 38 | params.pop("tag_names") 39 | returned_ok_params["expected"] = params 40 | returned_ok_params["check_union"] = ("tags",) 41 | return returned_ok(**returned_ok_params) 42 | 43 | 44 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 45 | def test_get_all_tags( 46 | get_all_tags_method_works: bool, 47 | ): 48 | assert get_all_tags_method_works == True 49 | 50 | 51 | @pytest.fixture 52 | def get_related_tags_for_a_tag_method_works( 53 | fred: Fred, 54 | returned_ok_params: dict, 55 | ) -> bool: 56 | params = { 57 | "tag_names": ("monetary aggregates", "weekly"), 58 | "limit": 2, 59 | "tag_group_id": "geo", 60 | "order_by": "name", 61 | "sort_order": "desc", 62 | "search_text": ( 63 | "beans", 64 | "cabbage", 65 | ), 66 | } 67 | fred.get_related_tags_for_a_tag(**params) 68 | returned_ok_params["observed"] = fred.tag_stack["get_related_tags_for_a_tag"] 69 | params.pop("tag_names") 70 | params.pop("tag_group_id") 71 | params.pop("search_text") 72 | returned_ok_params["expected"] = params 73 | returned_ok_params["check_union"] = ("tags",) 74 | return returned_ok(**returned_ok_params) 75 | 76 | 77 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 78 | def test_get_related_tags_for_a_tag( 79 | get_related_tags_for_a_tag_method_works: bool, 80 | ): 81 | assert get_related_tags_for_a_tag_method_works == True 82 | 83 | 84 | @pytest.fixture 85 | def get_series_matching_tags_method_works( 86 | fred: Fred, 87 | returned_ok_params: dict, 88 | ) -> bool: 89 | params = { 90 | "tag_names": ( 91 | "slovenia", 92 | "food", 93 | "oecd", 94 | ), 95 | "limit": 2, 96 | "offset": 1, 97 | "order_by": "seasonal_adjustment", 98 | "sort_order": "desc", 99 | # 'realtime_start': '2000-01-01', 100 | "realtime_end": "2003-01-01", 101 | } 102 | fred.get_series_matching_tags(**params) 103 | returned_ok_params["observed"] = fred.tag_stack["get_series_matching_tags"] 104 | params.pop("tag_names") 105 | returned_ok_params["expected"] = params 106 | returned_ok_params["check_union"] = ( 107 | "series", 108 | "seriess", 109 | ) 110 | return returned_ok(**returned_ok_params) 111 | 112 | 113 | @pytest.mark.skipif(not ENV_API_KEY, reason="Tests need api key") 114 | def test_get_series_matching_tags( 115 | get_series_matching_tags_method_works: bool, 116 | ): 117 | assert get_series_matching_tags_method_works == True 118 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 88 3 | target-version = ['py37', 'py38'] 4 | exclude = ''' 5 | ( 6 | | \.egg 7 | | \.git 8 | | \.tox 9 | | \.venv 10 | | setup.py 11 | | build 12 | | dist 13 | ) 14 | ''' 15 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = full_fred 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | requests 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | 2 | # See the docstring in versioneer.py for instructions. Note that you must 3 | # re-run 'versioneer.py setup' after changing this section, and commit the 4 | # resulting files. 5 | 6 | [build-system] 7 | requires = ["setuptools", "wheel", "versioneer"] 8 | 9 | [versioneer] 10 | VCS = git 11 | style = pep440 12 | versionfile_source = full_fred/_version.py 13 | versionfile_build = full_fred/_version.py 14 | tag_prefix = v 15 | parentdir_prefix = full_fred- 16 | 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from setuptools import setup, find_packages 4 | import versioneer 5 | 6 | with open('README.md', encoding='utf-8') as readme: 7 | readme = readme.read() 8 | 9 | version = versioneer.get_version() 10 | cmdclass = versioneer.get_cmdclass() 11 | 12 | INSTALL_REQUIRES = [ 13 | 'pandas', 14 | 'requests', 15 | 'versioneer', 16 | ] 17 | 18 | setup( 19 | name="full_fred", 20 | packages=find_packages(), 21 | version=version, 22 | cmdclass=cmdclass, 23 | description="Full interface to Federal Reserve Economic Data (FRED)", 24 | author="Zachary A. Kraehling", 25 | author_email="zaknyy@protonmail.com", 26 | long_description=readme, 27 | long_description_content_type="text/markdown", 28 | install_requires=INSTALL_REQUIRES, 29 | url="https://github.com/7astro7/full_fred", 30 | project_urls={ 31 | "Tracker": "https://github.com/7astro7/full_fred/issues", 32 | "Source": "https://github.com/7astro7/full_fred", 33 | }, 34 | test_suite="full_fred.tests", 35 | platforms=["Any"], 36 | classifiers=[ 37 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 38 | "Development Status :: 3 - Alpha", 39 | "Intended Audience :: Science/Research", 40 | "Intended Audience :: Developers", 41 | "Intended Audience :: Financial and Insurance Industry", 42 | "Operating System :: OS Independent", 43 | "Topic :: Software Development :: Libraries :: Python Modules", 44 | "Topic :: Internet :: WWW/HTTP", 45 | "Programming Language :: Python :: 3", 46 | ], 47 | keywords=["economics", "API", "econ", "fred", "financial", "FRED",], 48 | ) 49 | --------------------------------------------------------------------------------