diff --git a/.gitignore b/.gitignore index 0698700bd..65df933f0 100644 --- a/.gitignore +++ b/.gitignore @@ -71,4 +71,5 @@ static/swagger-ui/js .pytest_cache/ # Pycharm -.idea \ No newline at end of file +.idea +.vscode/launch.json \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 24c2e4679..96fcad034 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ -apispec==0.39.0 +apispec==4.0.0 certifi==2022.12.7 cfenv==0.5.2 defusedxml==0.6.0 elasticsearch==7.6.0 elasticsearch-dsl==7.3.0 -Flask==2.1.2 +Flask==2.2.5 Flask-Cors==3.0.10 Flask-RESTful==0.3.9 Flask-SQLAlchemy==2.5.1 @@ -19,20 +19,20 @@ prance[osv]==0.22.11.4.0 psycopg2-binary==2.9.1 python-dateutil==2.8.1 python-dotenv>=0.20.0 # Sets variable defaults in .flaskenv file -requests==2.31.0 -requests-aws4auth==1.2.3 +requests==2.25.1 +requests-aws4auth==1.0 sqlalchemy-postgres-copy==0.3.0 SQLAlchemy==1.3.19 ujson==5.4.0 # decoding CSP violation reported vine==5.0.0 # previosly pinned to 1.3.0 to fix amqp dependency, which is a dependency of kombu -webargs==5.5.3 +webargs==7.0.0 werkzeug==2.2.3 # Marshalling -flask-apispec==0.7.0 -git+https://github.com/fecgov/marshmallow-pagination@master -marshmallow==2.16.3 -marshmallow-sqlalchemy==0.15.0 +flask-apispec==0.11.4 +git+https://github.com/fecgov/marshmallow-pagination@feature/upgrade-marshmallow +marshmallow==3.0.0 +marshmallow-sqlalchemy==0.23.1 # Data export smart_open==1.8.0 diff --git a/tests/integration/test_candidates.py b/tests/integration/test_candidates.py index 9808d5d05..c19f08e03 100644 --- a/tests/integration/test_candidates.py +++ b/tests/integration/test_candidates.py @@ -125,19 +125,33 @@ def test_candidate_counts_house(self): ) ) results_tab = self.connection.execute(sql_extract).fetchall() - params = { + candidate_params = { 'election_year': election_year, + 'cycle': election_year, + 'district': '01', + 'state': 'MD', + } + + election_params = { + 'cycle': election_year, + 'election_full': True, + 'district': '01', + 'state': 'MD', + } + + total_params = { 'cycle': election_year, 'election_full': True, 'district': '01', 'state': 'MD', + 'election_year': election_year, } - candidates_api = self._results(rest.api.url_for(CandidateList, **params)) + candidates_api = self._results(rest.api.url_for(CandidateList, **candidate_params)) candidates_totals_api = self._results( - rest.api.url_for(TotalsCandidateView, **params) + rest.api.url_for(TotalsCandidateView, **total_params) ) elections_api = self._results( - rest.api.url_for(ElectionView, office='house', **params) + rest.api.url_for(ElectionView, office='house', **election_params) ) assert ( len(results_tab) diff --git a/tests/integration/test_tsvector_generation.py b/tests/integration/test_tsvector_generation.py index c9a956391..b8764278b 100644 --- a/tests/integration/test_tsvector_generation.py +++ b/tests/integration/test_tsvector_generation.py @@ -477,12 +477,12 @@ def test_accent_insensitive_sched_b(self): connection.execute(insert, data) manage.refresh_materialized(concurrent=False) results = self._results( - api.url_for(ScheduleBView, contributor_employer='ést-lou') + api.url_for(ScheduleBView, recipient_name='ést-lou') ) contbr_employer_list = {r['recipient_name'] for r in results} assert names.issubset(contbr_employer_list) results = self._results( - api.url_for(ScheduleBView, contributor_employer='est lou') + api.url_for(ScheduleBView, recipient_name='est lou') ) contbr_employer_list = {r['recipient_name'] for r in results} assert names.issubset(contbr_employer_list) diff --git a/tests/test_aggregates.py b/tests/test_aggregates.py index 0c6e7f0b8..7a387181c 100644 --- a/tests/test_aggregates.py +++ b/tests/test_aggregates.py @@ -257,7 +257,7 @@ def test_candidate_aggregates_by_committee(self): ) ) assert len(results) == 1 - serialized = schema().dump(aggregates[0]).data + serialized = schema().dump(aggregates[0]) serialized.update( { 'committee_name': self.committee.name, @@ -278,14 +278,13 @@ def test_candidate_aggregates_by_committee_full(self): api.url_for( resource, candidate_id=self.candidate.candidate_id, - committee_id=self.committee.committee_id, cycle=2012, office='president', election_full='true', ) ) assert len(results) == 1 - serialized = schema().dump(aggregates[0]).data + serialized = schema().dump(aggregates[0]) serialized.update( { 'committee_name': self.committee.name, diff --git a/tests/test_committees.py b/tests/test_committees.py index 752348294..32a480546 100644 --- a/tests/test_committees.py +++ b/tests/test_committees.py @@ -297,8 +297,8 @@ def test_committee_sort(self): def test_first_f1_date_sort(self): committees = [ - factories.CommitteeFactory(first_f1_date="2003-10-12"), - factories.CommitteeFactory(first_f1_date="2017-05-14"), + factories.CommitteeFactory(first_f1_date=datetime.date.fromisoformat("2003-10-12")), + factories.CommitteeFactory(first_f1_date=datetime.date.fromisoformat("2017-05-14")), ] committee_ids = [each.committee_id for each in committees] results = self._results(api.url_for(CommitteeList, sort="first_f1_date")) @@ -386,13 +386,12 @@ def _test_committee_date_filters(self, date_field, values, min_date_field, max_d for each in results ) ) - results = self._results( - api.url_for( - CommitteeList, - **{min_date_field: datetime.date.fromisoformat(values[1]), - max_date_field: datetime.date.fromisoformat(values[2])} - ) - ) + results = self._results(api.url_for( + CommitteeList, + **{min_date_field: datetime.date.fromisoformat(values[1]), + max_date_field: datetime.date.fromisoformat(values[2])} + ) + ) self.assertTrue( all( datetime.date.fromisoformat(values[1]).isoformat() @@ -614,22 +613,22 @@ def test_is_active(self): CommitteeHistoryProfileView, committee_id=self.committees[4].committee_id, cycle=2014, - election_full=False, - is_active=False, + election_full=False ) ) assert len(results) == 1 + self.assertFalse(results[0]["is_active"]) results = self._results( api.url_for( CommitteeHistoryProfileView, committee_id=self.committees[2].committee_id, cycle=2014, - election_full=False, - is_active=True, + election_full=False ) ) assert len(results) == 1 + self.assertTrue(results[0]["is_active"]) def test_candidate_cycle(self): results = self._results( @@ -739,8 +738,7 @@ def test_case_insensitivity(self): CommitteeHistoryProfileView, committee_id="id01", cycle=2014, - election_full=False, - is_active=False, + election_full=False ) ) assert len(results) == 1 @@ -750,8 +748,7 @@ def test_case_insensitivity(self): CommitteeHistoryProfileView, candidate_id="h01", cycle=2014, - election_full=False, - is_active=False, + election_full=False ) ) assert len(results) == 1 diff --git a/tests/test_dates.py b/tests/test_dates.py index 483ba3d39..485a29d82 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -30,7 +30,7 @@ def test_reporting_dates_filters(self): factories.ReportDateFactory(update_date=datetime.datetime(2014, 4, 2)) filter_fields = ( - ('due_date', '2014-01-02'), + ('min_due_date', '2014-01-02'), ('report_year', 2015), ('report_type', 'YE'), ('min_create_date', '2014-03-02'), diff --git a/tests/test_itemized.py b/tests/test_itemized.py index e15947b01..b434c08cb 100644 --- a/tests/test_itemized.py +++ b/tests/test_itemized.py @@ -617,7 +617,6 @@ def test_schedule_a_null_pagination_with_null_sort_column_values_descending(self api.url_for( ScheduleAView, sort='-contribution_receipt_date', - sort_reverse_nulls='true', **self.kwargs ) ) @@ -681,7 +680,6 @@ def test_schedule_a_null_pagination_with_null_sort_column_values_ascending(self) api.url_for( ScheduleAView, sort='contribution_receipt_date', - sort_reverse_nulls='true', **self.kwargs ) ) @@ -969,7 +967,6 @@ def test_schedule_b_sort_ignores_nulls_last_parameter(self): api.url_for( ScheduleBView, sort='-disbursement_date', - sort_nulls_last=True, **self.kwargs ) ) @@ -1151,15 +1148,15 @@ def test_filter_sched_e_spender_name_text(self): ), ] results = self._results( - api.url_for(ScheduleEView, q_spender='action', **self.kwargs) + api.url_for(ScheduleEView, q_spender='action') ) self.assertEqual(len(results), 2) results = self._results( - api.url_for(ScheduleEView, q_spender='abc', **self.kwargs) + api.url_for(ScheduleEView, q_spender='abc') ) self.assertEqual(len(results), 1) results = self._results( - api.url_for(ScheduleEView, q_spender='C001', **self.kwargs) + api.url_for(ScheduleEView, q_spender='C001') ) self.assertEqual(len(results), 1) @@ -1196,7 +1193,7 @@ def test_schedule_e_sort_args_descending(self): def test_schedule_e_expenditure_description_field(self): factories.ScheduleEFactory(committee_id='C001', expenditure_description='Advertising Costs') results = self._results( - api.url_for(ScheduleEView, **self.kwargs) + api.url_for(ScheduleEView) ) self.assertEqual(len(results), 1) self.assertEqual(results[0]['expenditure_description'], 'Advertising Costs') @@ -1322,7 +1319,7 @@ def test_filter_sched_e_most_recent(self): factories.ScheduleEFactory(committee_id='C006', filing_form='F3X'), ] results = self._results( - api.url_for(ScheduleEView, most_recent=True, **self.kwargs) + api.url_for(ScheduleEView, most_recent=True) ) # Most recent should include null values self.assertEqual(len(results), 5) @@ -1347,7 +1344,7 @@ def test_filter_sched_e_efile_most_recent(self): factories.EFilingsFactory(file_number=123) db.session.flush() results = self._results( - api.url_for(ScheduleEEfileView, most_recent=True, **self.kwargs) + api.url_for(ScheduleEEfileView, most_recent=True) ) # Most recent should include null values self.assertEqual(len(results), 5) @@ -1396,21 +1393,21 @@ def test_schedule_h4_filter_fulltext_purpose_and(self): for purpose in purposes ] results = self._results( - api.url_for(ScheduleH4View, disbursement_purpose='Test&Test', **self.kwargs) + api.url_for(ScheduleH4View, q_disbursement_purpose='Test&Test') ) self.assertIn(results[0]['disbursement_purpose'], purposes) results = self._results( api.url_for( - ScheduleH4View, disbursement_purpose='Test & Test', **self.kwargs + ScheduleH4View, q_disbursement_purpose='Test & Test' ) ) self.assertIn(results[0]['disbursement_purpose'], purposes) results = self._results( - api.url_for(ScheduleH4View, disbursement_purpose='Test& Test', **self.kwargs) + api.url_for(ScheduleH4View, q_disbursement_purpose='Test& Test') ) self.assertIn(results[0]['disbursement_purpose'], purposes) results = self._results( - api.url_for(ScheduleH4View, disbursement_purpose='Test &Test', **self.kwargs) + api.url_for(ScheduleH4View, q_disbursement_purpose='Test &Test') ) self.assertIn(results[0]['disbursement_purpose'], purposes) @@ -1420,12 +1417,12 @@ def test_schedule_h4_filter_payee_name_text_pass(self): 'Test com', 'Testerosa', 'Test#com', - 'Not.com', + 'Not.com', # this should not be a result 'Test.com and Test.com', ] [factories.ScheduleH4Factory(payee_name=payee) for payee in payee_names] - results = self._results(api.url_for(ScheduleH4View, payee_name='test')) - self.assertEqual(len(results), len(payee_names)) + results = self._results(api.url_for(ScheduleH4View, q_payee_name='test')) + self.assertEqual(len(results), 5) def test_schedule_h4_efile_event_purpose_date_range(self): diff --git a/tests/test_reports.py b/tests/test_reports.py index c96111130..cbf9e4816 100644 --- a/tests/test_reports.py +++ b/tests/test_reports.py @@ -425,7 +425,6 @@ def test_efile_presidential_reports(self): results = self._results( api.url_for( EFilingPresidentialSummaryView, - entity_type='presidential', committee_id=committee_id, ) ) @@ -463,7 +462,6 @@ def test_efile_house_senate_reports(self): results = self._results( api.url_for( EFilingHouseSenateSummaryView, - entity_type='house-senate', committee_id=committee_id, ) ) diff --git a/tests/test_sched_e.py b/tests/test_sched_e.py index 0cceaca9f..94c49c54c 100644 --- a/tests/test_sched_e.py +++ b/tests/test_sched_e.py @@ -11,17 +11,20 @@ class TestScheduleEByCandidateView(ApiBaseTest): def test_fields(self): candidate_id = 'S001' support_oppose_indicator = 'S' - factories.ScheduleEByCandidateFactory(candidate_id=candidate_id, - support_oppose_indicator=support_oppose_indicator, cycle=2018) + factories.ScheduleEByCandidateFactory( + candidate_id=candidate_id, + support_oppose_indicator=support_oppose_indicator, + cycle=2018) factories.CandidateElectionFactory(candidate_id=candidate_id, - cand_election_year=2018) - results = self._results(api.url_for(ScheduleEByCandidateView, candidate_id='S001')) + cand_election_year=2018) + results = self._results(api.url_for(ScheduleEByCandidateView, + candidate_id='S001')) assert len(results) == 1 assert results[0].keys() == ScheduleEByCandidateSchema().fields.keys() def test_sched_e_filter_match(self): factories.CandidateElectionFactory(candidate_id='S002', - cand_election_year=2014) + cand_election_year=2014) factories.ScheduleEByCandidateFactory( total=50000, count=10, @@ -34,17 +37,18 @@ def test_sched_e_filter_match(self): count=5, cycle=2010, candidate_id='S002', - support_oppose_indicator='O', + support_oppose_indicator='S', ), results = self._results( api.url_for(ScheduleEByCandidateView, candidate_id='S002', - cycle=2014, support_oppose_indicator='S') + cycle=2014, support_oppose='S') ) self.assertEqual(len(results), 1) def test_sort_bad_column(self): - response = self.app.get(api.url_for(ScheduleEByCandidateView, sort='candidate')) + response = self.app.get(api.url_for(ScheduleEByCandidateView, + sort='candidate')) self.assertEqual(response.status_code, 422) self.assertIn(b'Cannot sort on value', response.data) @@ -106,7 +110,7 @@ def test_sort_by_candidate_id(self): ), response = self._results( api.url_for(ScheduleEByCandidateView, sort='-candidate_id', - office='senate', state='NY', cycle=2012)) + office='senate', state='NY', cycle=2012)) self.assertEqual(len(response), 3) self.assertEqual(response[0]['candidate_id'], 'S003') self.assertEqual(response[0]['support_oppose_indicator'], 'S') @@ -154,8 +158,11 @@ def test_sort_by_candidate_name_descending(self): support_oppose_indicator='S', ), - response = self._results(api.url_for(ScheduleEByCandidateView, cycle=2010, - office='senate', state='NY', sort='-candidate_name')) + response = self._results(api.url_for(ScheduleEByCandidateView, + cycle=2010, + office='senate', + state='NY', + sort='-candidate_name')) self.assertEqual(len(response), 2) self.assertEqual(response[0]['candidate_name'], 'WARNER, MARK') self.assertEqual(response[1]['candidate_name'], 'BALDWIN, ALISSA') @@ -212,8 +219,11 @@ def test_sort_by_committee_id(self): committee_id='C006', ), - response = self._results(api.url_for(ScheduleEByCandidateView, sort='-committee_id', - office='senate', cycle=2010, state='NY')) + response = self._results(api.url_for(ScheduleEByCandidateView, + sort='-committee_id', + office='senate', + cycle=2010, + state='NY')) self.assertEqual(len(response), 2) self.assertEqual(response[0]['committee_id'], 'C006') self.assertEqual(response[1]['committee_id'], 'C005') @@ -270,8 +280,11 @@ def test_sort_by_committee_name(self): committee_id='C006', ), - response = self._results(api.url_for(ScheduleEByCandidateView, sort='-committee_name', - office='senate', cycle=2010, state='NY')) + response = self._results(api.url_for(ScheduleEByCandidateView, + sort='-committee_name', + office='senate', + cycle=2010, + state='NY')) self.assertEqual(len(response), 2) self.assertEqual(response[0]['committee_name'], 'Warner for America') self.assertEqual(response[1]['committee_name'], 'Ritche for America') diff --git a/tests/test_utils.py b/tests/test_utils.py index a0abf6176..83eff6acf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -309,7 +309,7 @@ def test_hide_null_election(self): class TestArgs(TestCase): def test_currency(self): with rest.app.test_request_context('?dollars=$24.50'): - parsed = flaskparser.parser.parse({'dollars': args.Currency()}, request) + parsed = flaskparser.parser.parse({'dollars': args.Currency()}, request, location='query') self.assertEqual(parsed, {'dollars': 24.50}) diff --git a/webservices/args.py b/webservices/args.py index cd6028f7f..5bff6ad68 100644 --- a/webservices/args.py +++ b/webservices/args.py @@ -1,12 +1,12 @@ import functools import sqlalchemy as sa -from marshmallow.compat import text_type from webargs import fields, validate from webservices import docs from webservices import exceptions from webservices.common.models import db +import datetime def _validate_natural(value): @@ -31,15 +31,15 @@ def __init__(self, places=2, **kwargs): super().__init__(places=places, **kwargs) def _validated(self, value): - if isinstance(value, text_type): + if isinstance(value, str): value = value.lstrip('$').replace(',', '') return super()._validated(value) class IStr(fields.Str): - def _deserialize(self, value, attr, data): - return super()._deserialize(value, attr, data).upper() + def _deserialize(self, value, attr, data, **kwargs): + return super()._deserialize(value, attr, data, **kwargs).upper() class District(fields.Str): @@ -57,10 +57,26 @@ def _validate(self, value): 'District must be a natural number', status_code=422) - def _deserialize(self, value, attr, data): + def _deserialize(self, value, attr, data, **kwargs): return '{0:0>2}'.format(value) +class Date(fields.Str): + + def _validate(self, value): + value = value.strip() + super()._validate(value) + try: + datetime.datetime.strptime(value, '%Y-%m-%d') + except (TypeError, ValueError): + try: + datetime.datetime.strptime(value, '%m/%d/%Y') + except (TypeError, ValueError): + raise exceptions.ApiError( + exceptions.DATE_ERROR, + status_code=422) + + class ImageNumber(fields.Str): def _validate(self, value): @@ -229,10 +245,10 @@ def make_seek_args(field=fields.Int, description=None): 'ao_no': fields.List(IStr, required=False, description=docs.AO_NUMBER), 'ao_name': fields.List(IStr, required=False, description=docs.AO_NAME), - 'ao_min_issue_date': fields.Date(description=docs.AO_MIN_ISSUE_DATE), - 'ao_max_issue_date': fields.Date(description=docs.AO_MAX_ISSUE_DATE), - 'ao_min_request_date': fields.Date(description=docs.AO_MIN_REQUEST_DATE), - 'ao_max_request_date': fields.Date(description=docs.AO_MAX_REQUEST_DATE), + 'ao_min_issue_date': Date(description=docs.AO_MIN_ISSUE_DATE), + 'ao_max_issue_date': Date(description=docs.AO_MAX_ISSUE_DATE), + 'ao_min_request_date': Date(description=docs.AO_MIN_REQUEST_DATE), + 'ao_max_request_date': Date(description=docs.AO_MAX_REQUEST_DATE), 'ao_category': fields.List( IStr(validate=validate.OneOf(['F', 'V', 'D', 'R', 'W', 'C', 'S'])), description=docs.AO_CATEGORY), @@ -248,13 +264,13 @@ def make_seek_args(field=fields.Int, description=None): 'ao_entity_name': fields.List(IStr, required=False, description=docs.AO_ENTITY_NAME), 'case_no': fields.List(IStr, required=False, description=docs.CASE_NO), - 'case_respondents': fields.Str(IStr, required=False, description=docs.CASE_RESPONDONTS), + 'case_respondents': IStr(required=False, description=docs.CASE_RESPONDONTS), 'case_dispositions': fields.List(IStr, required=False, description=docs.CASE_DISPOSTIONS), - 'case_election_cycles': fields.Int(IStr, required=False, description=docs.CASE_ELECTION_CYCLES), - 'case_min_open_date': fields.Date(required=False, description=docs.CASE_MIN_OPEN_DATE), - 'case_max_open_date': fields.Date(required=False, description=docs.CASE_MAX_OPEN_DATE), - 'case_min_close_date': fields.Date(required=False, description=docs.CASE_MIN_CLOSE_DATE), - 'case_max_close_date': fields.Date(required=False, description=docs.CASE_MAX_CLOSE_DATE), + 'case_election_cycles': fields.Int(required=False, description=docs.CASE_ELECTION_CYCLES), + 'case_min_open_date': Date(required=False, description=docs.CASE_MIN_OPEN_DATE), + 'case_max_open_date': Date(required=False, description=docs.CASE_MAX_OPEN_DATE), + 'case_min_close_date': Date(required=False, description=docs.CASE_MIN_CLOSE_DATE), + 'case_max_close_date': Date(required=False, description=docs.CASE_MAX_CLOSE_DATE), 'case_regulatory_citation': fields.List(IStr, required=False, description=docs.REGULATORY_CITATION), 'case_statutory_citation': fields.List(IStr, required=False, description=docs.STATUTORY_CITATION), 'case_citation_require_all': fields.Bool(description=docs.CITATION_REQUIRE_ALL), @@ -269,15 +285,15 @@ def make_seek_args(field=fields.Int, description=None): required=False, validate=validate.OneOf(["archived", "current"]), description=docs.MUR_TYPE), 'af_name': fields.List(IStr, required=False, description=docs.AF_NAME), - 'af_committee_id': fields.Str(IStr, required=False, description=docs.AF_COMMITTEE_ID), - 'af_report_year': fields.Str(IStr, required=False, description=docs.AF_REPORT_YEAR), - 'af_min_rtb_date': fields.Date(required=False, description=docs.AF_MIN_RTB_DATE), - 'af_max_rtb_date': fields.Date(required=False, description=docs.AF_MAX_RTB_DATE), - 'af_rtb_fine_amount': fields.Int(IStr, required=False, description=docs.AF_RTB_FINE_AMOUNT), - 'af_min_fd_date': fields.Date(required=False, description=docs.AF_MIN_FD_DATE), - 'af_max_fd_date': fields.Date(required=False, description=docs.AF_MAX_FD_DATE), - 'af_fd_fine_amount': fields.Int(IStr, required=False, description=docs.AF_FD_FINE_AMOUNT), - 'sort': fields.Str(IStr, required=False, description=docs.SORT), + 'af_committee_id': IStr(required=False, description=docs.AF_COMMITTEE_ID), + 'af_report_year': IStr(required=False, description=docs.AF_REPORT_YEAR), + 'af_min_rtb_date': Date(required=False, description=docs.AF_MIN_RTB_DATE), + 'af_max_rtb_date': Date(required=False, description=docs.AF_MAX_RTB_DATE), + 'af_rtb_fine_amount': fields.Int(required=False, description=docs.AF_RTB_FINE_AMOUNT), + 'af_min_fd_date': Date(required=False, description=docs.AF_MIN_FD_DATE), + 'af_max_fd_date': Date(required=False, description=docs.AF_MAX_FD_DATE), + 'af_fd_fine_amount': fields.Int(required=False, description=docs.AF_FD_FINE_AMOUNT), + 'sort': IStr(required=False, description=docs.SORT), } candidate_detail = { @@ -304,8 +320,8 @@ def make_seek_args(field=fields.Int, description=None): candidate_list = { 'q': fields.List(Keyword, description=docs.CANDIDATE_NAME), 'candidate_id': fields.List(IStr, description=docs.CANDIDATE_ID), - 'min_first_file_date': fields.Date(description=docs.CANDIDATE_MIN_FIRST_FILE_DATE), - 'max_first_file_date': fields.Date(description=docs.CANDIDATE_MAX_FIRST_FILE_DATE), + 'min_first_file_date': Date(description=docs.CANDIDATE_MIN_FIRST_FILE_DATE), + 'max_first_file_date': Date(description=docs.CANDIDATE_MAX_FIRST_FILE_DATE), 'is_active_candidate': fields.Bool(description=docs.ACTIVE_CANDIDATE), } @@ -342,12 +358,12 @@ def make_seek_args(field=fields.Int, description=None): 'candidate_id': fields.List(IStr, description=docs.CANDIDATE_ID), 'state': fields.List(IStr, description=docs.STATE_GENERIC), 'party': fields.List(IStr, description=docs.PARTY), - 'min_first_file_date': fields.Date(description=docs.MIN_FIRST_FILE_DATE), - 'max_first_file_date': fields.Date(description=docs.MAX_FIRST_FILE_DATE), - 'min_first_f1_date': fields.Date(description=docs.MIN_FIRST_F1_DATE), - 'max_first_f1_date': fields.Date(description=docs.MAX_FIRST_F1_DATE), - 'min_last_f1_date': fields.Date(description=docs.MIN_LAST_F1_DATE), - 'max_last_f1_date': fields.Date(description=docs.MAX_LAST_F1_DATE), + 'min_first_file_date': Date(description=docs.MIN_FIRST_FILE_DATE), + 'max_first_file_date': Date(description=docs.MAX_FIRST_FILE_DATE), + 'min_first_f1_date': Date(description=docs.MIN_FIRST_F1_DATE), + 'max_first_f1_date': Date(description=docs.MAX_FIRST_F1_DATE), + 'min_last_f1_date': Date(description=docs.MIN_LAST_F1_DATE), + 'max_last_f1_date': Date(description=docs.MAX_LAST_F1_DATE), 'treasurer_name': fields.List(Keyword, description=docs.TREASURER_NAME), 'sponsor_candidate_id': fields.List(IStr, description=docs.SPONSOR_CANDIDATE_ID), } @@ -371,8 +387,8 @@ def make_seek_args(field=fields.Int, description=None): 'document_type': fields.List(IStr, description=docs.DOC_TYPE), 'beginning_image_number': fields.List(ImageNumber, description=docs.BEGINNING_IMAGE_NUMBER), 'report_year': fields.List(fields.Int, description=docs.REPORT_YEAR), - 'min_receipt_date': fields.Date(description=docs.MIN_RECEIPT_DATE), - 'max_receipt_date': fields.Date(description=docs.MAX_RECEIPT_DATE), + 'min_receipt_date': Date(description=docs.MIN_RECEIPT_DATE), + 'max_receipt_date': Date(description=docs.MAX_RECEIPT_DATE), 'form_type': fields.List(IStr, description=docs.FORM_TYPE), 'state': fields.List(IStr, description=docs.STATE), 'district': fields.List(IStr, description=docs.DISTRICT), @@ -394,8 +410,8 @@ def make_seek_args(field=fields.Int, description=None): efilings = { 'file_number': fields.List(fields.Int, description=docs.FILE_NUMBER), 'committee_id': fields.List(IStr, description=docs.COMMITTEE_ID), - 'min_receipt_date': fields.Date(description=docs.MIN_RECEIPT_DATE), - 'max_receipt_date': fields.Date(description=docs.MAX_RECEIPT_DATE), + 'min_receipt_date': Date(description=docs.MIN_RECEIPT_DATE), + 'max_receipt_date': Date(description=docs.MAX_RECEIPT_DATE), 'q_filer': fields.List(Keyword, description=docs.FILER_NAME_TEXT), } @@ -414,8 +430,8 @@ def make_seek_args(field=fields.Int, description=None): 'max_disbursements_amount': Currency(description=docs.MAX_FILTER), 'min_receipts_amount': Currency(description=docs.MIN_FILTER), 'max_receipts_amount': Currency(description=docs.MAX_FILTER), - 'max_receipt_date': fields.Date(description=docs.MAX_REPORT_RECEIPT_DATE), - 'min_receipt_date': fields.Date(description=docs.MIN_REPORT_RECEIPT_DATE), + 'max_receipt_date': Date(description=docs.MAX_REPORT_RECEIPT_DATE), + 'min_receipt_date': Date(description=docs.MIN_REPORT_RECEIPT_DATE), 'min_cash_on_hand_end_period_amount': Currency(description=docs.MIN_FILTER), 'max_cash_on_hand_end_period_amount': Currency(description=docs.MAX_FILTER), 'min_debts_owed_amount': Currency(description=docs.MIN_FILTER), @@ -488,8 +504,8 @@ def make_seek_args(field=fields.Int, description=None): IStr(validate=validate.OneOf(['', 'C', 'L', 'M', 'T', 'V', 'W'])), description=docs.ORGANIZATION_TYPE, ), - 'min_first_f1_date': fields.Date(description=docs.MIN_FIRST_F1_DATE), - 'max_first_f1_date': fields.Date(description=docs.MAX_FIRST_F1_DATE), + 'min_first_f1_date': Date(description=docs.MIN_FIRST_F1_DATE), + 'max_first_f1_date': Date(description=docs.MAX_FIRST_F1_DATE), } candidate_totals_detail = { @@ -503,10 +519,10 @@ def make_seek_args(field=fields.Int, description=None): 'calendar_category_id': fields.List(fields.Int, description=docs.CATEGORY), 'description': fields.List(Keyword, description=docs.CAL_DESCRIPTION), 'summary': fields.List(Keyword, description=docs.SUMMARY), - 'min_start_date': fields.Date(description=docs.MIN_START_DATE), - 'min_end_date': fields.Date(description=docs.MIN_END_DATE), - 'max_start_date': fields.Date(description=docs.MAX_START_DATE), - 'max_end_date': fields.Date(description=docs.MAX_END_DATE), + 'min_start_date': Date(description=docs.MIN_START_DATE), + 'min_end_date': Date(description=docs.MIN_END_DATE), + 'max_start_date': Date(description=docs.MAX_START_DATE), + 'max_end_date': Date(description=docs.MAX_END_DATE), 'event_id': fields.Int(description=docs.EVENT_ID), } @@ -515,27 +531,27 @@ def make_seek_args(field=fields.Int, description=None): 'election_district': fields.List(fields.Str, description=docs.ELECTION_DISTRICT), 'election_party': fields.List(fields.Str, description=docs.ELECTION_PARTY), 'office_sought': fields.List(fields.Str(validate=validate.OneOf(['H', 'S', 'P'])), description=docs.OFFICE_SOUGHT), - 'min_election_date': fields.Date(description=docs.MIN_ELECTION_DATE), - 'max_election_date': fields.Date(description=docs.MAX_ELECTION_DATE), + 'min_election_date': Date(description=docs.MIN_ELECTION_DATE), + 'max_election_date': Date(description=docs.MAX_ELECTION_DATE), 'election_type_id': fields.List(fields.Str, description=docs.ELECTION_TYPE_ID), - 'min_create_date': fields.Date(description=docs.MIN_CREATE_DATE), - 'max_create_date': fields.Date(description=docs.MAX_CREATE_DATE), - 'min_update_date': fields.Date(description=docs.MIN_UPDATE_DATE), - 'max_update_date': fields.Date(description=docs.MAX_UPDATE_DATE), + 'min_create_date': Date(description=docs.MIN_CREATE_DATE), + 'max_create_date': Date(description=docs.MAX_CREATE_DATE), + 'min_update_date': Date(description=docs.MIN_UPDATE_DATE), + 'max_update_date': Date(description=docs.MAX_UPDATE_DATE), 'election_year': fields.List(fields.Str, description=docs.ELECTION_YEAR), - 'min_primary_general_date': fields.Date(description=docs.MIN_PRIMARY_GENERAL_DATE), - 'max_primary_general_date': fields.Date(description=docs.MAX_PRIMARY_GENERAL_DATE), + 'min_primary_general_date': Date(description=docs.MIN_PRIMARY_GENERAL_DATE), + 'max_primary_general_date': Date(description=docs.MAX_PRIMARY_GENERAL_DATE), } reporting_dates = { - 'min_due_date': fields.Date(description=docs.MIN_DUE_DATE), - 'max_due_date': fields.Date(description=docs.MAX_DUE_DATE), + 'min_due_date': Date(description=docs.MIN_DUE_DATE), + 'max_due_date': Date(description=docs.MAX_DUE_DATE), 'report_year': fields.List(fields.Int, description=docs.REPORT_YEAR), 'report_type': fields.List(fields.Str, description=docs.REPORT_TYPE), - 'min_create_date': fields.Date(description=docs.MIN_CREATE_DATE), - 'max_create_date': fields.Date(description=docs.MAX_CREATE_DATE), - 'min_update_date': fields.Date(description=docs.MIN_UPDATE_DATE), - 'max_update_date': fields.Date(description=docs.MAX_UPDATE_DATE), + 'min_create_date': Date(description=docs.MIN_CREATE_DATE), + 'max_create_date': Date(description=docs.MAX_CREATE_DATE), + 'min_update_date': Date(description=docs.MIN_UPDATE_DATE), + 'max_update_date': Date(description=docs.MAX_UPDATE_DATE), } # ====tag:dates -- endpoints [end]======== @@ -545,8 +561,8 @@ def make_seek_args(field=fields.Int, description=None): 'max_image_number': ImageNumber(description=docs.MAX_IMAGE_NUMBER), 'min_amount': Currency(description='Filter for all amounts greater than a value.'), 'max_amount': Currency(description='Filter for all amounts less than a value.'), - 'min_date': fields.Date(description='Minimum date'), - 'max_date': fields.Date(description='Maximum date'), + 'min_date': Date(description='Minimum date'), + 'max_date': Date(description='Maximum date'), 'line_number': fields.Str(description='Filter for form and line number using the following format: ' '`FORM-LINENUMBER`. For example an argument such as `F3X-16` would filter' ' down to all entries from form `F3X` line number `16`.') @@ -561,7 +577,7 @@ def make_seek_args(field=fields.Int, description=None): 'contributor_zip': fields.List(IStr, description=docs.CONTRIBUTOR_ZIP), 'contributor_employer': fields.List(Keyword, description=docs.CONTRIBUTOR_EMPLOYER), 'contributor_occupation': fields.List(Keyword, description=docs.CONTRIBUTOR_OCCUPATION), - 'last_contribution_receipt_date': fields.Date( + 'last_contribution_receipt_date': Date( missing=None, description='When sorting by `contribution_receipt_date`, this is populated with the \ `contribution_receipt_date` of the last result. However, you will need to pass the index \ @@ -596,8 +612,8 @@ def make_seek_args(field=fields.Int, description=None): IStr(validate=validate.OneOf(['', 'A', 'J', 'P', 'U', 'B', 'D'])), description=docs.DESIGNATION, ), - 'min_load_date': fields.Date(description=docs.MIN_LOAD_DATE), - 'max_load_date': fields.Date(description=docs.MAX_LOAD_DATE), + 'min_load_date': Date(description=docs.MIN_LOAD_DATE), + 'max_load_date': Date(description=docs.MAX_LOAD_DATE), } schedule_a_e_file = { @@ -671,7 +687,7 @@ def make_seek_args(field=fields.Int, description=None): 'disbursement_description': fields.List(Keyword, description=docs.DISBURSEMENT_DESCRIPTION), 'disbursement_purpose_category': fields.List(IStr, description=docs.DISBURSEMENT_PURPOSE_CATEGORY), 'last_disbursement_amount': fields.Float(missing=None, description=docs.LAST_DISBURSEMENT_AMOUNT), - 'last_disbursement_date': fields.Date(missing=None, description=docs.LAST_DISBURSEMENT_DATE), + 'last_disbursement_date': Date(missing=None, description=docs.LAST_DISBURSEMENT_DATE), 'recipient_city': fields.List(IStr, description=docs.RECIPIENT_CITY), 'recipient_committee_id': fields.List(IStr, description=docs.RECIPIENT_COMMITTEE_ID), 'recipient_name': fields.List(Keyword, description=docs.RECIPIENT_NAME), @@ -705,13 +721,13 @@ def make_seek_args(field=fields.Int, description=None): 'image_number': fields.List(ImageNumber, description=docs.IMAGE_NUMBER), 'recipient_city': fields.List(IStr, description='City of recipient'), 'recipient_state': fields.List(IStr, description='State of recipient'), - 'max_date': fields.Date( + 'max_date': Date( missing=None, description='When sorting by `disbursement_date`, this is populated with the \ `disbursement_date` of the last result. However, you will need to pass the index \ of that last result to `last_index` to get the next page.' ), - 'min_date': fields.Date( + 'min_date': Date( missing=None, description='When sorting by `disbursement_date`, this is populated with the \ `disbursement_date` of the last result. However, you will need to pass the index \ @@ -735,8 +751,8 @@ def make_seek_args(field=fields.Int, description=None): 'loan_source_name': fields.List(Keyword, description=docs.LOAN_SOURCE), 'min_payment_to_date': fields.Int(description=docs.MIN_PAYMENT_DATE), 'max_payment_to_date': fields.Int(description=docs.MAX_PAYMENT_DATE), - 'min_incurred_date': fields.Date(missing=None, description=docs.MIN_INCURRED_DATE), - 'max_incurred_date': fields.Date(missing=None, description=docs.MAX_INCURRED_DATE), + 'min_incurred_date': Date(missing=None, description=docs.MIN_INCURRED_DATE), + 'max_incurred_date': Date(missing=None, description=docs.MAX_INCURRED_DATE), } schedule_d = { @@ -755,10 +771,10 @@ def make_seek_args(field=fields.Int, description=None): 'creditor_debtor_name': fields.List(Keyword), 'nature_of_debt': fields.Str(), 'committee_id': fields.List(IStr, description=docs.COMMITTEE_ID), - 'min_coverage_end_date': fields.Date(missing=None, description=docs.MIN_COVERAGE_END_DATE), - 'max_coverage_end_date': fields.Date(missing=None, description=docs.MAX_COVERAGE_END_DATE), - 'min_coverage_start_date': fields.Date(missing=None, description=docs.MIN_COVERAGE_START_DATE), - 'max_coverage_start_date': fields.Date(missing=None, description=docs.MAX_COVERAGE_START_DATE), + 'min_coverage_end_date': Date(missing=None, description=docs.MIN_COVERAGE_END_DATE), + 'max_coverage_end_date': Date(missing=None, description=docs.MAX_COVERAGE_END_DATE), + 'min_coverage_start_date': Date(missing=None, description=docs.MIN_COVERAGE_START_DATE), + 'max_coverage_start_date': Date(missing=None, description=docs.MAX_COVERAGE_START_DATE), 'report_year': fields.List(fields.Int, description=docs.REPORT_YEAR), 'report_type': fields.List(fields.Str, description=docs.REPORT_TYPE) } @@ -810,8 +826,8 @@ def make_seek_args(field=fields.Int, description=None): 'report_year': fields.List(fields.Int, description=docs.REPORT_YEAR), 'min_amount': Currency(description=docs.ELECTIONEERING_MIN_AMOUNT), 'max_amount': Currency(description=docs.ELECTIONEERING_MAX_AMOUNT), - 'min_date': fields.Date(description=docs.ELECTIONEERING_MIN_DATE), - 'max_date': fields.Date(description=docs.ELECTIONEERING_MAX_DATE), + 'min_date': Date(description=docs.ELECTIONEERING_MIN_DATE), + 'max_date': Date(description=docs.ELECTIONEERING_MAX_DATE), 'disbursement_description': fields.List(Keyword, description=docs.DISBURSEMENT_DESCRIPTION), } @@ -922,7 +938,7 @@ def make_seek_args(field=fields.Int, description=None): 'committee_id': fields.List(IStr, description=docs.COMMITTEE_ID), 'candidate_id': fields.List(IStr, description=docs.CANDIDATE_ID), 'filing_form': fields.List(IStr, description=docs.FORM_TYPE), - 'last_expenditure_date': fields.Date( + 'last_expenditure_date': Date( missing=None, description=docs.LAST_EXPENDITURE_DATE), 'last_expenditure_amount': fields.Float( @@ -939,10 +955,10 @@ def make_seek_args(field=fields.Int, description=None): missing=None, description=docs.LAST_SUPPOSE_OPPOSE_INDICATOR), 'is_notice': fields.List(fields.Bool, description=docs.IS_NOTICE), - 'min_dissemination_date': fields.Date(description=docs.DISSEMINATION_MIN_DATE), - 'max_dissemination_date': fields.Date(description=docs.DISSEMINATION_MAX_DATE), - 'min_filing_date': fields.Date(description=docs.MIN_FILED_DATE), - 'max_filing_date': fields.Date(description=docs.MAX_FILED_DATE), + 'min_dissemination_date': Date(description=docs.DISSEMINATION_MIN_DATE), + 'max_dissemination_date': Date(description=docs.DISSEMINATION_MAX_DATE), + 'min_filing_date': Date(description=docs.MIN_FILED_DATE), + 'max_filing_date': Date(description=docs.MAX_FILED_DATE), 'most_recent': fields.Bool(description=docs.MOST_RECENT_IE), 'q_spender': fields.List(Keyword, description=docs.SPENDER_NAME_TEXT), } @@ -956,10 +972,10 @@ def make_seek_args(field=fields.Int, description=None): 'support_oppose_indicator': fields.List( IStr(validate=validate.OneOf(['S', 'O'])), description=docs.SUPPORT_OPPOSE_INDICATOR), - 'min_expenditure_date': fields.Date(description=docs.EXPENDITURE_MIN_DATE), - 'max_expenditure_date': fields.Date(description=docs.EXPENDITURE_MAX_DATE), - 'min_dissemination_date': fields.Date(description=docs.DISSEMINATION_MIN_DATE), - 'max_dissemination_date': fields.Date(description=docs.DISSEMINATION_MAX_DATE), + 'min_expenditure_date': Date(description=docs.EXPENDITURE_MIN_DATE), + 'max_expenditure_date': Date(description=docs.EXPENDITURE_MAX_DATE), + 'min_dissemination_date': Date(description=docs.DISSEMINATION_MIN_DATE), + 'max_dissemination_date': Date(description=docs.DISSEMINATION_MAX_DATE), 'min_expenditure_amount': fields.Integer(description=docs.EXPENDITURE_MIN_AMOUNT), 'max_expenditure_amount': fields.Integer(description=docs.EXPENDITURE_MAX_AMOUNT), 'spender_name': fields.List(IStr, description=docs.COMMITTEE_NAME), @@ -968,8 +984,8 @@ def make_seek_args(field=fields.Int, description=None): 'candidate_office_state': fields.List(IStr, description=docs.STATE), 'candidate_office_district': fields.List(IStr, description=docs.DISTRICT), 'most_recent': fields.Bool(description=docs.MOST_RECENT_IE), - 'min_filed_date': fields.Date(description=docs.FILED_DATE), - 'max_filed_date': fields.Date(description=docs.FILED_DATE), + 'min_filed_date': Date(description=docs.FILED_DATE), + 'max_filed_date': Date(description=docs.FILED_DATE), 'filing_form': fields.List(IStr, description=docs.FORM_TYPE), 'is_notice': fields.Bool(description=docs.IS_NOTICE), } @@ -982,8 +998,8 @@ def make_seek_args(field=fields.Int, description=None): 'name': fields.List(Keyword, description='Name of RAD analyst'), 'email': fields.List(fields.Str, description='Email of RAD analyst'), 'title': fields.List(Keyword, description='Title of RAD analyst'), - 'min_assignment_update_date': fields.Date(description='Filter results for assignment updates made after this date'), - 'max_assignment_update_date': fields.Date(description='Filter results for assignment updates made before this date') + 'min_assignment_update_date': Date(description='Filter results for assignment updates made after this date'), + 'max_assignment_update_date': Date(description='Filter results for assignment updates made before this date') } large_aggregates = {'cycle': fields.Int(required=True, description=docs.RECORD_CYCLE)} @@ -1035,12 +1051,12 @@ def make_seek_args(field=fields.Int, description=None): 'form_type': fields.List(IStr, description=docs.FORM_TYPE), 'amendment_indicator': fields.List(IStr, description=docs.AMENDMENT_INDICATOR), 'status_num': fields.List(fields.Str(validate=validate.OneOf(['0', '1'])), description=docs.STATUS_NUM), - 'min_receipt_date': fields.Date(description=docs.MIN_RECEIPT_DATE), - 'max_receipt_date': fields.Date(description=docs.MAX_RECEIPT_DATE), - 'min_coverage_end_date': fields.Date(description=docs.MIN_COVERAGE_END_DATE), - 'max_coverage_end_date': fields.Date(description=docs.MAX_COVERAGE_END_DATE), - 'min_transaction_data_complete_date': fields.Date(description=docs.MIN_TRANSACTION_DATA_COMPLETE_DATE), - 'max_transaction_data_complete_date': fields.Date(description=docs.MAX_TRANSACTION_DATA_COMPLETE_DATE), + 'min_receipt_date': Date(description=docs.MIN_RECEIPT_DATE), + 'max_receipt_date': Date(description=docs.MAX_RECEIPT_DATE), + 'min_coverage_end_date': Date(description=docs.MIN_COVERAGE_END_DATE), + 'max_coverage_end_date': Date(description=docs.MAX_COVERAGE_END_DATE), + 'min_transaction_data_complete_date': Date(description=docs.MIN_TRANSACTION_DATA_COMPLETE_DATE), + 'max_transaction_data_complete_date': Date(description=docs.MAX_TRANSACTION_DATA_COMPLETE_DATE), } @@ -1097,10 +1113,10 @@ def make_seek_args(field=fields.Int, description=None): 'committee_id': fields.List(IStr, description=docs.COMMITTEE_ID), 'last_payee_name': fields.List(IStr, missing=None, description=docs.LAST_PAYEE_NAME), 'last_disbursement_purpose': fields.List(IStr, missing=None, description=docs.LAST_DISBURSEMENT_PURPOSE), - 'last_event_purpose_date': fields.Date(missing=None, description=docs.LAST_EVENT_DATE), + 'last_event_purpose_date': Date(missing=None, description=docs.LAST_EVENT_DATE), 'last_spender_committee_name': fields.List(IStr, missing=None, description=docs.LAST_COMMITTEE_NAME), - 'min_date': fields.Date(missing=None, description=docs.MIN_EVENT_DATE), - 'max_date': fields.Date(missing=None, description=docs.MAX_EVENT_DATE), + 'min_date': Date(missing=None, description=docs.MIN_EVENT_DATE), + 'max_date': Date(missing=None, description=docs.MAX_EVENT_DATE), 'last_disbursement_amount': fields.Float(missing=None, description=docs.LAST_DISBURSEMENT_AMOUNT), 'min_amount': Currency(description=docs.MIN_FILTER), 'max_amount': Currency(description=docs.MAX_FILTER), @@ -1121,7 +1137,7 @@ def make_seek_args(field=fields.Int, description=None): 'spender_committee_designation': fields.List( IStr(validate=validate.OneOf(['', 'A', 'J', 'P', 'U', 'B', 'D'])), description=docs.DESIGNATION, - ), + ) } schedule_h4_efile = { @@ -1133,9 +1149,9 @@ def make_seek_args(field=fields.Int, description=None): 'payee_state': fields.List(fields.Str, description=docs.PAYEE_STATE), 'committee_id': fields.List(IStr, description=docs.COMMITTEE_ID), 'last_disbursement_purpose': fields.List(IStr, missing=None, description=docs.LAST_DISBURSEMENT_PURPOSE), - 'last_event_purpose_date': fields.Date(missing=None, description=docs.LAST_EVENT_DATE), - 'min_date': fields.Date(missing=None, description=docs.MIN_EVENT_DATE), - 'max_date': fields.Date(missing=None, description=docs.MAX_EVENT_DATE), + 'last_event_purpose_date': Date(missing=None, description=docs.LAST_EVENT_DATE), + 'min_date': Date(missing=None, description=docs.MIN_EVENT_DATE), + 'max_date': Date(missing=None, description=docs.MAX_EVENT_DATE), 'last_disbursement_amount': fields.Float(missing=None, description=docs.LAST_DISBURSEMENT_AMOUNT), 'min_amount': Currency(description=docs.MIN_FILTER), 'max_amount': Currency(description=docs.MAX_FILTER), diff --git a/webservices/common/models/dates.py b/webservices/common/models/dates.py index e04f68b67..8f42093f7 100644 --- a/webservices/common/models/dates.py +++ b/webservices/common/models/dates.py @@ -55,7 +55,7 @@ class ElectionDate(db.Model, FecAppMixin): trc_election_id = db.Column(db.Integer, primary_key=True) election_state = db.Column(db.String, index=True, doc=docs.STATE) - election_district = db.Column(db.Integer, index=True, doc=docs.DISTRICT) + election_district = db.Column(db.String, index=True, doc=docs.DISTRICT) election_party = db.Column(db.String, index=True, doc=docs.PARTY) office_sought = db.Column(db.String, index=True, doc=docs.OFFICE) election_date = db.Column(db.Date, index=True, doc=docs.ELECTION_DATE) diff --git a/webservices/common/models/filings.py b/webservices/common/models/filings.py index 7fc30cbf0..59b20771d 100644 --- a/webservices/common/models/filings.py +++ b/webservices/common/models/filings.py @@ -18,7 +18,7 @@ class Filings(FecFileNumberMixin, CsvMixin, db.Model): sub_id = db.Column(db.BigInteger, index=True, primary_key=True, doc=docs.SUB_ID) coverage_start_date = db.Column(db.Date, index=True, doc=docs.COVERAGE_START_DATE) coverage_end_date = db.Column(db.Date, index=True, doc=docs.COVERAGE_END_DATE) - receipt_date = db.Column(db.Date, index=True, doc=docs.RECEIPT_DATE) + receipt_date = db.Column(db.DateTime, index=True, doc=docs.RECEIPT_DATE) election_year = db.Column(db.Integer, doc=docs.ELECTION_YEAR) form_type = db.Column(db.String, index=True, doc=docs.FORM_TYPE) report_year = db.Column(db.Integer, index=True, doc=docs.REPORT_YEAR) diff --git a/webservices/common/models/itemized.py b/webservices/common/models/itemized.py index 4ced27ddb..59e31d5ee 100644 --- a/webservices/common/models/itemized.py +++ b/webservices/common/models/itemized.py @@ -456,7 +456,7 @@ class ScheduleC(PdfMixin, BaseItemized): due_date_terms = db.Column('due_dt_terms', db.String) interest_rate_terms = db.Column(db.String) secured_ind = db.Column(db.String) - schedule_a_line_number = db.Column('sched_a_line_num', db.Integer) + schedule_a_line_number = db.Column('sched_a_line_num', db.String) personally_funded = db.Column('pers_fund_yes_no', db.String) memo_code = db.Column('memo_cd', db.String) memo_text = db.Column(db.String) diff --git a/webservices/exceptions.py b/webservices/exceptions.py index 46fc11567..fa0132c23 100644 --- a/webservices/exceptions.py +++ b/webservices/exceptions.py @@ -13,6 +13,9 @@ NEXT_IN_CHAIN_DATA_ERROR = """next_in_chain data error, please contact apiinfo@fec.gov.\ """ +DATE_ERROR = """Invalid date. Date must be formatted as MM/DD/YYYY or YYYY-MM-DD.\ +""" + class ApiError(Exception): status_code = 400 diff --git a/webservices/resources/dates.py b/webservices/resources/dates.py index aa0b934b1..a75dda738 100644 --- a/webservices/resources/dates.py +++ b/webservices/resources/dates.py @@ -71,7 +71,6 @@ class CalendarDatesExport(CalendarDatesView): 'ics': (calendar.ICalEventSchema, calendar.render_ical, 'text/calendar'), } - @use_kwargs(args.calendar_dates) @use_kwargs({ 'renderer': fields.Str(missing='ics', validate=validate.OneOf(['ics', 'csv'])), }) @@ -85,7 +84,7 @@ def get(self, **kwargs): schema_type, renderer, mimetype = self.renderers[kwargs['renderer']] schema = schema_type(many=True) return Response( - renderer(schema.dump(query).data, schema), + renderer(schema.dump(query), schema), mimetype=mimetype, ) diff --git a/webservices/resources/reports.py b/webservices/resources/reports.py index 47c90dd23..d4e2279f0 100644 --- a/webservices/resources/reports.py +++ b/webservices/resources/reports.py @@ -154,7 +154,7 @@ def get(self, entity_type=None, **kwargs): validator = args.IndicesValidator(reports_class) validator(kwargs['sort']) page = utils.fetch_page(query, kwargs, model=reports_class, multi=True) - return reports_schema().dump(page).data + return reports_schema().dump(page) def build_query(self, entity_type=None, **kwargs): # For this endpoint we now enforce the enpoint specified to map the right model. @@ -219,7 +219,7 @@ def get(self, committee_id=None, committee_type=None, **kwargs): validator = args.IndicesValidator(reports_class) validator(kwargs['sort']) page = utils.fetch_page(query, kwargs, model=reports_class, multi=True) - return reports_schema().dump(page).data + return reports_schema().dump(page) def build_query(self, committee_id=None, committee_type=None, **kwargs): reports_class, reports_schema = reports_schema_map.get( diff --git a/webservices/resources/totals.py b/webservices/resources/totals.py index da1bd538b..a8bc30486 100644 --- a/webservices/resources/totals.py +++ b/webservices/resources/totals.py @@ -89,7 +89,7 @@ def get(self, committee_id=None, entity_type=None, **kwargs): committee_id=committee_id, entity_type=entity_type, **kwargs ) page = utils.fetch_page(query, kwargs, model=totals_class) - return totals_schema().dump(page).data + return totals_schema().dump(page) def build_query(self, committee_id=None, entity_type=None, **kwargs): totals_class, totals_schema = totals_schema_map.get( @@ -240,7 +240,7 @@ def get(self, committee_id=None, committee_type=None, **kwargs): committee_id=committee_id.upper(), committee_type=committee_type, **kwargs ) page = utils.fetch_page(query, kwargs, model=totals_class) - return totals_schema().dump(page).data + return totals_schema().dump(page) def build_query(self, committee_id=None, committee_type=None, **kwargs): totals_class, totals_schema = totals_schema_map.get( @@ -285,7 +285,7 @@ def get(self, candidate_id, **kwargs): validator = args.IndexValidator(totals_class) validator(kwargs['sort']) page = utils.fetch_page(query, kwargs, model=totals_class) - return totals_schema().dump(page).data + return totals_schema().dump(page) def build_query(self, candidate_id=None, **kwargs): totals_class, totals_schema = candidate_totals_schema_map.get( @@ -348,6 +348,7 @@ def args(self): def index_column(self): return self.model.idx + @doc( tags=['financial'], description=docs.TOTALS_INAUGURAL_DONATIONS ) diff --git a/webservices/rest.py b/webservices/rest.py index 1a387aa1e..9867b1041 100644 --- a/webservices/rest.py +++ b/webservices/rest.py @@ -6,6 +6,7 @@ import http import logging import os +from marshmallow import EXCLUDE import ujson import sqlalchemy as sa import flask_cors as cors @@ -102,8 +103,9 @@ def sqla_conn_string(): class FlaskRestParser(FlaskParser): + DEFAULT_UNKNOWN_BY_LOCATION = {"query": EXCLUDE} - def handle_error(self, error, req, schema, status_code, error_headers): + def handle_error(self, error, req, schema, *, error_status_code, error_headers): message = error.messages status_code = getattr(error, 'status_code', 422) raise exceptions.ApiError(message, status_code) diff --git a/webservices/schemas.py b/webservices/schemas.py index b6a591d07..8d6c68fb9 100644 --- a/webservices/schemas.py +++ b/webservices/schemas.py @@ -4,7 +4,7 @@ from collections import namedtuple import marshmallow as ma -from marshmallow_sqlalchemy import ModelSchema +from marshmallow_sqlalchemy import SQLAlchemySchema, SQLAlchemyAutoSchema from marshmallow_pagination import schemas as paging_schemas from webservices import utils, decoders @@ -14,22 +14,21 @@ from webservices.calendar import format_start_date, format_end_date from marshmallow import post_dump - -spec.definition('OffsetInfo', schema=paging_schemas.OffsetInfoSchema) -spec.definition('SeekInfo', schema=paging_schemas.SeekInfoSchema) +spec.components.schema('OffsetInfo', schema=paging_schemas.OffsetInfoSchema) +spec.components.schema('SeekInfo', schema=paging_schemas.SeekInfoSchema) # A namedtuple used to help capture any additional columns that should be # included with exported data: -# field: the field object definining the relationship on a model, e.g., +# field: the field object definining the relationship on a model,e.g., # models.ScheduleA.committee (an object) -# column: the column object found in the related model, e.g., +# column: the column object found in the related model,e.g., # models.CommitteeHistory.name (an object) # label: the label to use for the column in the query that will appear in the -# header row of the output, e.g., +# header row of the output,e.g., # 'committee_name' (a string) # position: the spot within the list of columns that this should be inserted -# at; defaults to -1 (end of the list), e.g., -# 1 (an integer, in this case the second spot in a list) +# at; defaults to -1 (end of the list),e.g., +# 1 (an integer,in this case the second spot in a list) # Usage: Define a custom attribute in a schema's Meta options object called # 'relationships' and set to a list of one or more relationships. @@ -50,15 +49,33 @@ def __new__(cls, field, column, label, position=-1): ) -class BaseSchema(ModelSchema): +class BaseSchema(SQLAlchemySchema): + class Meta: + sqla_session = models.db.session + load_instance = True + include_relationships = True + include_fk = True - def get_attribute(self, attr, obj, default): + def get_attribute(self, obj, attr, default): if '.' in attr: - return super().get_attribute(attr, obj, default) + return super().get_attribute(obj, attr, default) return getattr(obj, attr, default) -class BaseEfileSchema(BaseSchema): +class BaseAutoSchema(SQLAlchemyAutoSchema): + def get_attribute(self, obj, attr, default): + if '.' in attr: + return super().get_attribute(obj, attr, default) + return getattr(obj, attr, default) + + +class BaseEfileSchema(BaseAutoSchema): + class Meta: + sqla_session = models.db.session + load_instance = True + include_relationships = True + include_fk = True + summary_lines = ma.fields.Method("parse_summary_rows") report_year = ma.fields.Int() pdf_url = ma.fields.Str() @@ -74,7 +91,7 @@ class BaseEfileSchema(BaseSchema): fec_file_id = ma.fields.Str() @post_dump - def extract_summary_rows(self, obj): + def extract_summary_rows(self, obj, **kwargs): if obj.get('summary_lines'): for key, value in obj.get('summary_lines').items(): # may be a way to pull these out using pandas? @@ -84,6 +101,7 @@ def extract_summary_rows(self, obj): obj.pop('summary_lines') if obj.get('amendment'): obj.pop('amendment') + return obj def extract_columns(obj, column_a, column_b, descriptions): @@ -110,10 +128,14 @@ def make_period_string(per_string=None): return per_string -class EFilingF3PSchema(BaseEfileSchema): +class BaseF3PFilingSchema(BaseEfileSchema): + class Meta(BaseEfileSchema.Meta): + model = models.BaseF3PFiling + exclude = ('total_disbursements', 'total_receipts') + treasurer_name = ma.fields.Str() - def parse_summary_rows(self, obj): + def parse_summary_rows(self, obj, **kwargs): line_list = {} state_map = {} @@ -150,73 +172,83 @@ def parse_summary_rows(self, obj): return line_list -class EFilingF3Schema(BaseEfileSchema): +class BaseF3FilingSchema(BaseEfileSchema): + class Meta(BaseEfileSchema.Meta): + model = models.BaseF3Filing + candidate_name = ma.fields.Str() treasurer_name = ma.fields.Str() - def parse_summary_rows(self, obj): + def parse_summary_rows(self, obj, **kwargs): descriptions = decoders.f3_description line_list = extract_columns(obj, decoders.f3_col_a, decoders.f3_col_b, descriptions) # final bit of data cleaning before json marshalling - # If values are None, fall back to 0 to prevent errors - coh_cop_i = line_list.get('coh_cop_i') if line_list.get('coh_cop_i') else 0 - coh_cop_ii = line_list.get('coh_cop_ii') if line_list.get('coh_cop_ii') else 0 - cash = max(coh_cop_i, coh_cop_ii) - line_list["cash_on_hand_end_period"] = cash - # i and ii should always be the same but data can be wrong - line_list.pop('coh_cop_ii') - line_list.pop('coh_cop_i') - coh_bop = line_list.get('coh_bop') if line_list.get('coh_bop') else 0 - cash_on_hand_beginning_period = ( - obj.cash_on_hand_beginning_period - if obj.cash_on_hand_beginning_period - else 0 - ) - cash = max(cash_on_hand_beginning_period, coh_bop) - obj.cash_on_hand_beginning_period = None - line_list.pop('coh_bop') - line_list["cash_on_hand_beginning_period"] = cash - cash = max(line_list.get('total_disbursements_per_i'), line_list.get('total_disbursements_per_ii')) - line_list["total_disbursements_period"] = cash - line_list.pop('total_disbursements_per_i') - line_list.pop('total_disbursements_per_ii') - cash = max(line_list.get('total_receipts_per_i'), line_list.get('ttl_receipts_ii')) - line_list["total_receipts_period"] = cash - line_list.pop('total_receipts_per_i') - line_list.pop('ttl_receipts_ii') + # If values are None,fall back to 0 to prevent errors + if line_list: + coh_cop_i = line_list.get('coh_cop_i') if line_list.get('coh_cop_i') else 0 + coh_cop_ii = line_list.get('coh_cop_ii') if line_list.get('coh_cop_ii') else 0 + cash = max(coh_cop_i, coh_cop_ii) + line_list["cash_on_hand_end_period"] = cash + # i and ii should always be the same but data can be wrong + line_list.pop('coh_cop_ii') + line_list.pop('coh_cop_i') + coh_bop = line_list.get('coh_bop') if line_list.get('coh_bop') else 0 + cash_on_hand_beginning_period = ( + obj.cash_on_hand_beginning_period + if obj.cash_on_hand_beginning_period + else 0 + ) + cash = max(cash_on_hand_beginning_period, coh_bop) + obj.cash_on_hand_beginning_period = None + line_list.pop('coh_bop') + line_list["cash_on_hand_beginning_period"] = cash + cash = max(line_list.get('total_disbursements_per_i'), line_list.get('total_disbursements_per_ii')) + line_list["total_disbursements_period"] = cash + line_list.pop('total_disbursements_per_i') + line_list.pop('total_disbursements_per_ii') + cash = max(line_list.get('total_receipts_per_i'), line_list.get('ttl_receipts_ii')) + line_list["total_receipts_period"] = cash + line_list.pop('total_receipts_per_i') + line_list.pop('ttl_receipts_ii') return line_list -class EFilingF3XSchema(BaseEfileSchema): - def parse_summary_rows(self, obj): +class BaseF3XFilingSchema(BaseEfileSchema): + class Meta(BaseEfileSchema.Meta): + model = models.BaseF3XFiling + + def parse_summary_rows(self, obj, **kwargsj): descriptions = decoders.f3x_description line_list = extract_columns(obj, decoders.f3x_col_a, decoders.f3x_col_b, descriptions) - line_list['cash_on_hand_beginning_calendar_ytd'] = line_list.pop('coh_begin_calendar_yr') - line_list['cash_on_hand_beginning_period'] = line_list.pop('coh_bop') + if line_list: + line_list['cash_on_hand_beginning_calendar_ytd'] = line_list.pop('coh_begin_calendar_yr') + line_list['cash_on_hand_beginning_period'] = line_list.pop('coh_bop') return line_list schema_map = {} -schema_map["BaseF3XFiling"] = EFilingF3XSchema -schema_map["BaseF3Filing"] = EFilingF3Schema -schema_map["BaseF3PFiling"] = EFilingF3PSchema +schema_map["BaseF3XFiling"] = BaseF3XFilingSchema +schema_map["BaseF3Filing"] = BaseF3FilingSchema +schema_map["BaseF3PFiling"] = BaseF3PFilingSchema def register_schema(schema, definition_name=None): definition_name = definition_name or re.sub(r'Schema$', '', schema.__name__) - spec.definition(definition_name, schema=schema) + spec.components.schema(definition_name, schema=schema) -def make_schema(model, class_name=None, fields=None, options=None): +def make_schema(model, class_name=None, fields=None, options=None, BaseSchema=BaseAutoSchema): class_name = class_name or '{0}Schema'.format(model.__name__) Meta = type( 'Meta', - (object, ), + (object,), utils.extend( { 'model': model, 'sqla_session': models.db.session, - 'exclude': ('idx', ), + 'load_instance': True, + 'include_relationships': True, + 'include_fk': True }, options or {}, ) @@ -225,11 +257,10 @@ def make_schema(model, class_name=None, fields=None, options=None): BaseSchema if not schema_map.get(model.__name__) else schema_map.get(model.__name__) - ) return type( class_name, - (mapped_schema, ), + (mapped_schema,), utils.extend({'Meta': Meta}, fields or {}), ) @@ -288,7 +319,8 @@ def augment_itemized_aggregate_models(factory, committee_model, *models, namespa class ApiSchema(ma.Schema): - def _postprocess(self, data, many, obj): + @post_dump + def _postprocess(self, data, **kwargs): ret = {'api_version': __API_VERSION__} ret.update(data) return ret @@ -299,7 +331,7 @@ class BaseSearchSchema(ma.Schema): name = ma.fields.Str() -class CandidateSearchSchema(BaseSearchSchema): +class CandidateSearchBaseSchema(BaseSearchSchema): office_sought = ma.fields.Str() @@ -309,7 +341,7 @@ class CommitteeSearchSchema(BaseSearchSchema): class CandidateSearchListSchema(ApiSchema): results = ma.fields.Nested( - CandidateSearchSchema, + CandidateSearchBaseSchema, ref='#/definitions/CandidateSearch', many=True, ) @@ -323,8 +355,8 @@ class CommitteeSearchListSchema(ApiSchema): ) -register_schema(CandidateSearchSchema) -register_schema(CandidateSearchListSchema) +register_schema(CandidateSearchBaseSchema, 'CandidateSearchBaseSchema') +register_schema(CandidateSearchListSchema, 'CandidateSearchListSchema') register_schema(CommitteeSearchSchema) register_schema(CommitteeSearchListSchema) @@ -358,27 +390,12 @@ class AuditCommitteeSearchListSchema(ApiSchema): register_schema(AuditCommitteeSearchSchema) register_schema(AuditCommitteeSearchListSchema) - -make_efiling_schema = functools.partial( - make_schema, - options={'exclude': ('idx', 'total_disbursements', 'total_receipts')}, - fields={ - 'pdf_url': ma.fields.Str(), - 'report_year': ma.fields.Int(), - } -) - -augment_models( - make_efiling_schema, - models.BaseF3PFiling, - models.BaseF3XFiling, - models.BaseF3Filing, -) +augment_schemas(BaseF3PFilingSchema, BaseF3FilingSchema, BaseF3XFilingSchema) # create pac sponsor candidate schema PacSponsorCandidateschema = make_schema( models.PacSponsorCandidate, - options={'exclude': ('idx', 'committee_id', )}, + options={'exclude': ('idx', 'committee_id',)}, ) # End create pac sponsor candidate schema @@ -392,10 +409,14 @@ class AuditCommitteeSearchListSchema(ApiSchema): CommitteeSchema = make_schema( models.Committee, - options={'exclude': ('idx', 'treasurer_text', )}, fields={ 'sponsor_candidate_list': ma.fields.Nested(PacSponsorCandidateschema, many=True), + 'first_f1_date': ma.fields.Date(), + 'first_file_date': ma.fields.Date(), + 'last_f1_date': ma.fields.Date(), + 'last_file_date': ma.fields.Date() }, + options={'exclude': ('idx', 'treasurer_text',)}, ) CommitteePageSchema = make_page_schema(CommitteeSchema) register_schema(CommitteeSchema) @@ -405,16 +426,16 @@ class AuditCommitteeSearchListSchema(ApiSchema): JFCCommitteeSchema = make_schema( models.JFCCommittee, class_name='JFCCommitteeSchema', - options={'exclude': ('idx', 'committee_id', 'most_recent_filing_flag', )}, -) + options={'exclude': ('idx', 'committee_id', 'most_recent_filing_flag',)}, + ) # End create JFC committee schema make_committees_schema = functools.partial( make_schema, - options={'exclude': ('idx', 'treasurer_text', )}, fields={ 'jfc_committee': ma.fields.Nested(JFCCommitteeSchema, many=True), - } + }, + options={'exclude': ('idx', 'treasurer_text',)}, ) augment_models( @@ -427,26 +448,53 @@ class AuditCommitteeSearchListSchema(ApiSchema): make_candidate_schema = functools.partial( make_schema, - options={'exclude': ('idx', 'principal_committees')}, fields={ 'federal_funds_flag': ma.fields.Boolean(attribute='flags.federal_funds_flag'), 'has_raised_funds': ma.fields.Boolean(attribute='flags.has_raised_funds'), - } + }, + options={'exclude': ('idx', 'principal_committees', 'flags')}, ) augment_models( make_candidate_schema, - models.Candidate, + models.Candidate +) + +candidate_detail_schema = make_schema( models.CandidateDetail, + fields={ + 'federal_funds_flag': ma.fields.Boolean(attribute='flags.federal_funds_flag'), + 'has_raised_funds': ma.fields.Boolean(attribute='flags.has_raised_funds'), + }, + options={'exclude': ('idx',)}, +) +augment_schemas(candidate_detail_schema) + +# built these schemas without make_candidate_schema,as it was filtering out the flags +make_candidate_total_schema = make_schema( + models.CandidateTotal, + fields={ + 'receipts': ma.fields.Float(), + 'disbursements': ma.fields.Float(), + 'individual_itemized_contributions': ma.fields.Float(), + 'transfers_from_other_authorized_committee': ma.fields.Float(), + 'other_political_committee_contributions': ma.fields.Float() + } +) + +augment_schemas(make_candidate_total_schema) + +make_candidate_history_schema = make_schema( + models.CandidateHistory, + options={'exclude': ('idx',)} ) -# built these schemas without make_candidate_schema, as it was filtering out the flags + +augment_schemas(make_candidate_history_schema) + augment_models( make_schema, - models.CandidateHistory, - models.CandidateTotal, models.CandidateFlags - ) @@ -460,20 +508,229 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], CandidateSearchSchema = make_schema( models.Candidate, - options={'exclude': ('idx', 'flags')}, fields={ 'principal_committees': ma.fields.Nested('PrincipalCommitteeSchema', many=True), 'federal_funds_flag': ma.fields.Boolean(attribute='flags.federal_funds_flag'), 'has_raised_funds': ma.fields.Boolean(attribute='flags.has_raised_funds'), }, + options={'exclude': ('idx', 'flags')}, ) CandidateSearchPageSchema = make_page_schema(CandidateSearchSchema) -register_schema(CandidateSearchSchema) -register_schema(CandidateSearchPageSchema) +register_schema(CandidateSearchSchema, 'CandidateSearch') +register_schema(CandidateSearchPageSchema, 'CandidateSearchPage') +committee_fields = { + 'pdf_url': ma.fields.Str(), + 'csv_url': ma.fields.Str(), + 'fec_url': ma.fields.Str(), + 'report_form': ma.fields.Str(), + 'document_description': ma.fields.Str(), + 'committee_type': ma.fields.Str(attribute='committee.committee_type'), + 'beginning_image_number': ma.fields.Str(), + 'end_image_number': ma.fields.Str(), + 'fec_file_id': ma.fields.Str(), + 'total_receipts_period': ma.fields.Float(), + 'total_disbursements_ytd': ma.fields.Float(), + 'total_disbursements_period': ma.fields.Float(), + 'total_contributions_ytd': ma.fields.Float(), + 'total_contributions_period': ma.fields.Float(), + 'refunded_political_party_committee_contributions_ytd': ma.fields.Float(), + 'total_contribution_refunds_period': ma.fields.Float(), + 'total_contribution_refunds_ytd': ma.fields.Float(), + 'refunded_individual_contributions_period': ma.fields.Float(), + 'refunded_individual_contributions_ytd': ma.fields.Float(), + 'refunded_other_political_committee_contributions_period': ma.fields.Float(), + 'refunded_other_political_committee_contributions_ytd': ma.fields.Float(), + 'refunded_political_party_committee_contributions_period': ma.fields.Float(), + 'previous_file_number': ma.fields.Float(), + 'most_recent_file_number': ma.fields.Float(), + 'cash_on_hand_beginning_period': ma.fields.Float(), + 'cash_on_hand_end_period': ma.fields.Float(), + 'debts_owed_by_committee': ma.fields.Float(), + 'debts_owed_to_committee': ma.fields.Float(), + 'other_disbursements_period': ma.fields.Float(), + 'other_disbursements_ytd': ma.fields.Float(), + 'other_political_committee_contributions_period': ma.fields.Float(), + 'other_political_committee_contributions_ytd': ma.fields.Float(), + 'political_party_committee_contributions_period': ma.fields.Float(), + 'political_party_committee_contributions_ytd': ma.fields.Float() + } -make_reports_schema = functools.partial( - make_schema, +committee_reports_presidential_schema = make_schema( + models.CommitteeReportsPresidential, + fields=utils.extend( + committee_fields, + { + 'candidate_contribution_period': ma.fields.Float(), + 'candidate_contribution_ytd': ma.fields.Float(), + 'exempt_legal_accounting_disbursement_period': ma.fields.Float(), + 'exempt_legal_accounting_disbursement_ytd': ma.fields.Float(), + 'expenditure_subject_to_limits': ma.fields.Float(), + 'federal_funds_period': ma.fields.Float(), + 'federal_funds_ytd': ma.fields.Float(), + 'fundraising_disbursements_period': ma.fields.Float(), + 'fundraising_disbursements_ytd': ma.fields.Float(), + 'items_on_hand_liquidated': ma.fields.Float(), + 'loans_received_from_candidate_period': ma.fields.Float(), + 'loans_received_from_candidate_ytd': ma.fields.Float(), + 'offsets_to_fundraising_expenditures_ytd': ma.fields.Float(), + 'offsets_to_fundraising_expenditures_period': ma.fields.Float(), + 'offsets_to_legal_accounting_period': ma.fields.Float(), + 'offsets_to_legal_accounting_ytd': ma.fields.Float(), + 'operating_expenditures_period': ma.fields.Float(), + 'operating_expenditures_ytd': ma.fields.Float(), + 'other_loans_received_period': ma.fields.Float(), + 'other_loans_received_ytd': ma.fields.Float(), + 'other_receipts_period': ma.fields.Float(), + 'other_receipts_ytd': ma.fields.Float(), + 'repayments_loans_made_by_candidate_period': ma.fields.Float(), + 'repayments_loans_made_candidate_ytd': ma.fields.Float(), + 'repayments_other_loans_period': ma.fields.Float(), + 'repayments_other_loans_ytd': ma.fields.Float(), + 'subtotal_summary_period': ma.fields.Float(), + 'total_loan_repayments_made_period': ma.fields.Float(), + 'total_loan_repayments_made_ytd': ma.fields.Float(), + 'total_loans_received_period': ma.fields.Float(), + 'total_loans_received_ytd': ma.fields.Float(), + 'total_offsets_to_operating_expenditures_period': ma.fields.Float(), + 'total_offsets_to_operating_expenditures_ytd': ma.fields.Float(), + 'total_period': ma.fields.Float(), + 'total_ytd': ma.fields.Float(), + 'transfers_from_affiliated_committee_period': ma.fields.Float(), + 'transfers_from_affiliated_committee_ytd': ma.fields.Float(), + 'transfers_to_other_authorized_committee_period': ma.fields.Float(), + 'transfers_to_other_authorized_committee_ytd': ma.fields.Float(), + 'net_contributions_cycle_to_date': ma.fields.Float(), + 'net_operating_expenditures_cycle_to_date': ma.fields.Float() + }), + options={'exclude': ('idx', 'committee', 'filer_name_text')} +) + +augment_schemas(committee_reports_presidential_schema) + +committee_reports_hs_schema = make_schema( + models.CommitteeReportsHouseSenate, + fields=utils.extend( + committee_fields, + { + 'aggregate_amount_personal_contributions_general': ma.fields.Float(), + 'aggregate_contributions_personal_funds_primary': ma.fields.Float(), + 'all_other_loans_period': ma.fields.Float(), + 'all_other_loans_ytd': ma.fields.Float(), + 'candidate_contribution_period': ma.fields.Float(), + 'candidate_contribution_ytd': ma.fields.Float(), + 'gross_receipt_authorized_committee_general': ma.fields.Float(), # missing + 'gross_receipt_authorized_committee_primary': ma.fields.Float(), # missing + 'gross_receipt_minus_personal_contribution_general': ma.fields.Float(), + 'gross_receipt_minus_personal_contributions_primary': ma.fields.Float(), + 'loan_repayments_candidate_loans_period': ma.fields.Float(), + 'loan_repayments_candidate_loans_ytd': ma.fields.Float(), + 'loan_repayments_other_loans_period': ma.fields.Float(), + 'loan_repayments_other_loans_ytd': ma.fields.Float(), + 'loans_made_by_candidate_period': ma.fields.Float(), + 'loans_made_by_candidate_ytd': ma.fields.Float(), + 'net_contributions_ytd': ma.fields.Float(), + 'net_contributions_period': ma.fields.Float(), + 'net_operating_expenditures_period': ma.fields.Float(), + 'net_operating_expenditures_ytd': ma.fields.Float(), + 'operating_expenditures_period': ma.fields.Float(), + 'operating_expenditures_ytd': ma.fields.Float(), + 'other_receipts_period': ma.fields.Float(), + 'other_receipts_ytd': ma.fields.Float(), + 'refunds_total_contributions_col_total_ytd': ma.fields.Float(), + 'subtotal_period': ma.fields.Float(), + 'total_contribution_refunds_col_total_period': ma.fields.Float(), + 'total_contributions_column_total_period': ma.fields.Float(), # missing + 'total_loan_repayments_made_period': ma.fields.Float(), + 'total_loan_repayments_made_ytd': ma.fields.Float(), + 'total_loans_received_period': ma.fields.Float(), + 'total_loans_received_ytd': ma.fields.Float(), + 'total_offsets_to_operating_expenditures_period': ma.fields.Float(), + 'total_offsets_to_operating_expenditures_ytd': ma.fields.Float(), + 'total_operating_expenditures_period': ma.fields.Float(), + 'total_operating_expenditures_ytd': ma.fields.Float(), + 'transfers_from_other_authorized_committee_period': ma.fields.Float(), + 'transfers_from_other_authorized_committee_ytd': ma.fields.Float(), + 'transfers_to_other_authorized_committee_period': ma.fields.Float(), + 'transfers_to_other_authorized_committee_ytd': ma.fields.Float(), + }), + options={'exclude': ('idx', 'committee', 'filer_name_text')} + ) + +augment_schemas(committee_reports_hs_schema) + +committee_reports_pac_schema = make_schema( + models.CommitteeReportsPacParty, + fields=utils.extend( + committee_fields, + { + 'all_loans_received_period': ma.fields.Float(), + 'all_loans_received_ytd': ma.fields.Float(), + 'allocated_federal_election_levin_share_period': ma.fields.Float(), + 'cash_on_hand_beginning_calendar_ytd': ma.fields.Float(), + 'cash_on_hand_close_ytd': ma.fields.Float(), + 'coordinated_expenditures_by_party_committee_period': ma.fields.Float(), + 'coordinated_expenditures_by_party_committee_ytd': ma.fields.Float(), + 'fed_candidate_committee_contribution_refunds_ytd': ma.fields.Float(), + 'fed_candidate_committee_contributions_period': ma.fields.Float(), + 'fed_candidate_committee_contributions_ytd': ma.fields.Float(), + 'fed_candidate_contribution_refunds_period': ma.fields.Float(), + 'independent_expenditures_period': ma.fields.Float(), + 'independent_expenditures_ytd': ma.fields.Float(), + 'loan_repayments_made_period': ma.fields.Float(), + 'loan_repayments_made_ytd': ma.fields.Float(), + 'loan_repayments_received_period': ma.fields.Float(), + 'loan_repayments_received_ytd': ma.fields.Float(), + 'loans_made_period': ma.fields.Float(), + 'loans_made_ytd': ma.fields.Float(), + 'net_contributions_period': ma.fields.Float(), + 'net_contributions_ytd': ma.fields.Float(), + 'net_operating_expenditures_period': ma.fields.Float(), + 'net_operating_expenditures_ytd': ma.fields.Float(), + 'non_allocated_fed_election_activity_period': ma.fields.Float(), + 'non_allocated_fed_election_activity_ytd': ma.fields.Float(), + 'nonfed_share_allocated_disbursements_period': ma.fields.Float(), + 'other_fed_operating_expenditures_period': ma.fields.Float(), + 'other_fed_operating_expenditures_ytd': ma.fields.Float(), + 'other_fed_receipts_period': ma.fields.Float(), + 'other_fed_receipts_ytd': ma.fields.Float(), + 'shared_fed_activity_nonfed_ytd': ma.fields.Float(), + 'shared_fed_activity_period': ma.fields.Float(), + 'shared_fed_activity_ytd': ma.fields.Float(), + 'shared_fed_operating_expenditures_period': ma.fields.Float(), + 'shared_fed_operating_expenditures_ytd': ma.fields.Float(), + 'shared_nonfed_operating_expenditures_period': ma.fields.Float(), + 'shared_nonfed_operating_expenditures_ytd': ma.fields.Float(), + 'subtotal_summary_page_period': ma.fields.Float(), + 'subtotal_summary_ytd': ma.fields.Float(), + 'total_fed_disbursements_period': ma.fields.Float(), + 'total_fed_disbursements_ytd': ma.fields.Float(), + 'total_fed_election_activity_period': ma.fields.Float(), + 'total_fed_election_activity_ytd': ma.fields.Float(), + 'total_fed_operating_expenditures_period': ma.fields.Float(), + 'total_fed_operating_expenditures_ytd': ma.fields.Float(), + 'total_fed_receipts_period': ma.fields.Float(), + 'total_fed_receipts_ytd': ma.fields.Float(), + 'total_nonfed_transfers_period': ma.fields.Float(), + 'total_nonfed_transfers_ytd': ma.fields.Float(), + 'total_operating_expenditures_period': ma.fields.Float(), + 'total_operating_expenditures_ytd': ma.fields.Float(), + 'transfers_from_affiliated_party_period': ma.fields.Float(), + 'transfers_from_affiliated_party_ytd': ma.fields.Float(), + 'transfers_from_nonfed_account_period': ma.fields.Float(), + 'transfers_from_nonfed_account_ytd': ma.fields.Float(), + 'transfers_from_nonfed_levin_period': ma.fields.Float(), + 'transfers_from_nonfed_levin_ytd': ma.fields.Float(), + 'transfers_to_affiliated_committee_period': ma.fields.Float(), + 'transfers_to_affilitated_committees_ytd': ma.fields.Float() + }), + options={'exclude': ('idx', 'committee', 'filer_name_text')} +) + +augment_schemas(committee_reports_pac_schema) + +committee_reports_ie_schema = make_schema( + models.CommitteeReportsIEOnly, fields={ 'pdf_url': ma.fields.Str(), 'csv_url': ma.fields.Str(), @@ -484,17 +741,13 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'beginning_image_number': ma.fields.Str(), 'end_image_number': ma.fields.Str(), 'fec_file_id': ma.fields.Str(), + 'independent_contributions_period': ma.fields.Float(), + 'independent_expenditures_period': ma.fields.Float() }, - options={'exclude': ('idx', 'committee', 'filer_name_text', 'spender_name_text')}, + options={'exclude': ('idx', 'spender_name_text')}, ) -augment_models( - make_reports_schema, - models.CommitteeReportsPresidential, - models.CommitteeReportsHouseSenate, - models.CommitteeReportsPacParty, - models.CommitteeReportsIEOnly, -) +augment_schemas(committee_reports_ie_schema) reports_schemas = ( schemas['CommitteeReportsPresidentialSchema'], @@ -508,68 +761,288 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], entity_fields = { 'pdf_url': ma.fields.Str(), 'report_form': ma.fields.Str(), - 'last_cash_on_hand_end_period': ma.fields.Decimal(places=2), + 'last_cash_on_hand_end_period': ma.fields.Float(), 'last_beginning_image_number': ma.fields.Str(), 'transaction_coverage_date': ma.fields.Date( attribute='transaction_coverage.transaction_coverage_date', default=None), - 'individual_contributions_percent': ma.fields.Decimal(places=2), - 'party_and_other_committee_contributions_percent': ma.fields.Decimal(places=2), - 'contributions_ie_and_party_expenditures_made_percent': ma.fields.Decimal(places=2), - 'operating_expenditures_percent': ma.fields.Decimal(places=2), + 'individual_contributions_percent': ma.fields.Float(), + 'party_and_other_committee_contributions_percent': ma.fields.Float(), + 'contributions_ie_and_party_expenditures_made_percent': ma.fields.Float(), + 'operating_expenditures_percent': ma.fields.Float(), } -# All /totals/entity_type/except 'pac', 'party', 'pac-party' +# All /totals/entity_type/except 'pac','party','pac-party' make_totals_schema = functools.partial( make_schema, - fields=entity_fields, - options={ + fields=utils.extend( + entity_fields, + {'cash_on_hand_beginning_period': ma.fields.Float()}, + {'all_other_loans': ma.fields.Float()}, + {'candidate_contribution': ma.fields.Float()}, + {'disbursements': ma.fields.Float()}, + {'individual_contributions': ma.fields.Float()}, + {'individual_itemized_contributions': ma.fields.Float()}, + {'individual_unitemized_contributions': ma.fields.Float()}, + {'last_cash_on_hand_end_period': ma.fields.Float()}, + {'last_debts_owed_by_committee': ma.fields.Float()}, + {'last_debts_owed_to_committee': ma.fields.Float()}, + {'loan_repayments': ma.fields.Float()}, + {'loan_repayments_candidate_loans': ma.fields.Float()}, + {'loan_repayments_other_loans': ma.fields.Float()}, + {'loans': ma.fields.Float()}, + {'loans_made_by_candidate': ma.fields.Float()}, + {'net_contributions': ma.fields.Float()}, + {'net_operating_expenditures': ma.fields.Float()}, + {'offsets_to_operating_expenditures': ma.fields.Float()}, + {'operating_expenditures': ma.fields.Float()}, + {'other_political_committee_contributions': ma.fields.Float()}, + {'other_receipts': ma.fields.Float()}, + {'political_party_committee_contributions': ma.fields.Float()}, + {'receipts': ma.fields.Float()}, + {'refunded_individual_contributions': ma.fields.Float()}, + {'refunded_other_political_committee_contributions': ma.fields.Float()}, + {'refunded_political_party_committee_contributions': ma.fields.Float()}, + {'transfers_from_other_authorized_committee': ma.fields.Float()}, + {'transfers_to_other_authorized_committee': ma.fields.Float()}, + {'other_disbursements': ma.fields.Float()}, + {'contribution_refunds': ma.fields.Float()}, + {'contributions': ma.fields.Float()}, + ), options={ 'exclude': ( 'transaction_coverage', 'idx', 'treasurer_text', - 'sponsor_candidate_list', - 'sponsor_candidate_ids' ) - }, + } ) augment_models( make_totals_schema, - models.CommitteeTotalsHouseSenate, - models.CommitteeTotalsIEOnly, - models.CommitteeTotalsPerCycle, + models.CommitteeTotalsHouseSenate ) -# /totals/entity_type/ 'pac', 'party', 'pac-party' -make_pac_party_totals_schema = functools.partial( + +make_totals_per_cycle_schema = functools.partial( make_schema, fields=utils.extend( entity_fields, - {'sponsor_candidate_list': ma.fields.Nested(PacSponsorCandidateschema, many=True)} - ), - options={ + {'cash_on_hand_beginning_period': ma.fields.Float()}, + {'all_other_loans': ma.fields.Float()}, + {'candidate_contribution': ma.fields.Float()}, + {'disbursements': ma.fields.Float()}, + {'individual_contributions': ma.fields.Float()}, + {'individual_itemized_contributions': ma.fields.Float()}, + {'individual_unitemized_contributions': ma.fields.Float()}, + {'last_cash_on_hand_end_period': ma.fields.Float()}, + {'last_debts_owed_by_committee': ma.fields.Float()}, + {'last_debts_owed_to_committee': ma.fields.Float()}, + {'loan_repayments': ma.fields.Float()}, + {'loan_repayments_candidate_loans': ma.fields.Float()}, + {'loan_repayments_other_loans': ma.fields.Float()}, + {'loans': ma.fields.Float()}, + {'loans_made_by_candidate': ma.fields.Float()}, + {'net_contributions': ma.fields.Float()}, + {'net_operating_expenditures': ma.fields.Float()}, + {'offsets_to_operating_expenditures': ma.fields.Float()}, + {'operating_expenditures': ma.fields.Float()}, + {'other_political_committee_contributions': ma.fields.Float()}, + {'other_receipts': ma.fields.Float()}, + {'political_party_committee_contributions': ma.fields.Float()}, + {'receipts': ma.fields.Float()}, + {'refunded_individual_contributions': ma.fields.Float()}, + {'refunded_other_political_committee_contributions': ma.fields.Float()}, + {'refunded_political_party_committee_contributions': ma.fields.Float()}, + {'transfers_from_other_authorized_committee': ma.fields.Float()}, + {'transfers_to_other_authorized_committee': ma.fields.Float()}, + {'other_disbursements': ma.fields.Float()}, + {'contribution_refunds': ma.fields.Float()}, + {'contributions': ma.fields.Float()}, + {'exempt_legal_accounting_disbursement': ma.fields.Float()}, + {'federal_funds': ma.fields.Float()}, + {'fundraising_disbursements': ma.fields.Float()}, + {'loan_repayments_made': ma.fields.Float()}, + {'loans_received': ma.fields.Float()}, + {'loans_received_from_candidate': ma.fields.Float()}, + {'offsets_to_fundraising_expenditures': ma.fields.Float()}, + {'offsets_to_legal_accounting': ma.fields.Float()}, + {'other_loans_received': ma.fields.Float()}, + {'repayments_loans_made_by_candidate': ma.fields.Float()}, + {'repayments_other_loans': ma.fields.Float()}, + {'total_offsets_to_operating_expenditures': ma.fields.Float()}, + {'transfers_from_affiliated_committee': ma.fields.Float()} + ), options={ 'exclude': ( 'transaction_coverage', 'idx', 'treasurer_text', ) - }, + } ) + augment_models( - make_pac_party_totals_schema, - models.CommitteeTotalsPacParty, + make_totals_per_cycle_schema, + models.CommitteeTotalsPerCycle ) -make_candidate_totals_schema = functools.partial( +make_committee_totals_ie_only = make_schema( + models.CommitteeTotalsIEOnly, + fields=utils.extend( + entity_fields, + {'total_independent_contributions': ma.fields.Float()}, + {'total_independent_expenditures': ma.fields.Float()} + ), options={ + 'exclude': ( + 'transaction_coverage', + 'idx', + ) + } +) + +augment_schemas(make_committee_totals_ie_only) + +# /totals/entity_type/ 'pac','party','pac-party' +make_pac_party_totals_schema = functools.partial( make_schema, - fields={ - 'last_cash_on_hand_end_period': ma.fields.Decimal(places=2), - 'last_beginning_image_number': ma.fields.Str(), - }, + fields=utils.extend( + entity_fields, + {'sponsor_candidate_list': ma.fields.Nested(PacSponsorCandidateschema, many=True)}, + {'all_loans_received': ma.fields.Float()}, + {'allocated_federal_election_levin_share': ma.fields.Float()}, + {'disbursements': ma.fields.Float()}, + {'convention_exp': ma.fields.Float()}, + {'coordinated_expenditures_by_party_committee': ma.fields.Float()}, + {'exp_prior_years_subject_limits': ma.fields.Float()}, + {'exp_subject_limits': ma.fields.Float()}, + {'fed_candidate_committee_contributions': ma.fields.Float()}, + {'fed_candidate_contribution_refunds': ma.fields.Float()}, + {'fed_disbursements': ma.fields.Float()}, + {'fed_election_activity': ma.fields.Float()}, + {'fed_operating_expenditures': ma.fields.Float()}, + {'fed_receipts': ma.fields.Float()}, + {'independent_expenditures': ma.fields.Float()}, + {'cash_on_hand_beginning_period': ma.fields.Float()}, + {'contribution_refunds': ma.fields.Float()}, + {'individual_contributions': ma.fields.Float()}, + {'individual_itemized_contributions': ma.fields.Float()}, + {'individual_unitemized_contributions': ma.fields.Float()}, + {'itemized_convention_exp': ma.fields.Float()}, + {'itemized_other_disb': ma.fields.Float()}, + {'itemized_other_income': ma.fields.Float()}, + {'itemized_other_refunds': ma.fields.Float()}, + {'itemized_refunds_relating_convention_exp': ma.fields.Float()}, + {'last_debts_owed_by_committee': ma.fields.Float()}, + {'last_debts_owed_to_committee': ma.fields.Float()}, + {'loan_repayments_made': ma.fields.Float()}, + {'loan_repayments_received': ma.fields.Float()}, + {'loans_and_loan_repayments_made': ma.fields.Float()}, + {'loans_and_loan_repayments_received': ma.fields.Float()}, + {'loans_made': ma.fields.Float()}, + {'net_contributions': ma.fields.Float()}, + {'net_operating_expenditures': ma.fields.Float()}, + {'non_allocated_fed_election_activity': ma.fields.Float()}, + {'offsets_to_operating_expenditures': ma.fields.Float()}, + {'operating_expenditures': ma.fields.Float()}, + {'other_disbursements': ma.fields.Float()}, + {'other_fed_operating_expenditures': ma.fields.Float()}, + {'other_fed_receipts': ma.fields.Float()}, + {'other_political_committee_contributions': ma.fields.Float()}, + {'other_refunds': ma.fields.Float()}, + {'political_party_committee_contributions': ma.fields.Float()}, + {'refunded_individual_contributions': ma.fields.Float()}, + {'refunded_other_political_committee_contributions': ma.fields.Float()}, + {'refunded_political_party_committee_contributions': ma.fields.Float()}, + {'refunds_relating_convention_exp': ma.fields.Float()}, + {'shared_fed_activity': ma.fields.Float()}, + {'shared_fed_activity_nonfed': ma.fields.Float()}, + {'shared_fed_operating_expenditures': ma.fields.Float()}, + {'shared_nonfed_operating_expenditures': ma.fields.Float()}, + {'total_exp_subject_limits': ma.fields.Float()}, + {'total_transfers': ma.fields.Float()}, + {'transfers_from_affiliated_party': ma.fields.Float()}, + {'transfers_from_nonfed_account': ma.fields.Float()}, + {'transfers_from_nonfed_levin': ma.fields.Float()}, + {'transfers_to_affiliated_committee': ma.fields.Float()}, + {'unitemized_convention_exp': ma.fields.Float()}, + {'unitemized_other_disb': ma.fields.Float()}, + {'unitemized_other_income': ma.fields.Float()}, + {'unitemized_other_refunds': ma.fields.Float()}, + {'unitemized_refunds_relating_convention_exp': ma.fields.Float()}, + {'contributions': ma.fields.Float()}, + {'federal_funds': ma.fields.Float()}, + {'receipts': ma.fields.Float()} + ), + options={'exclude': ('idx', 'treasurer_text', 'transaction_coverage')} ) augment_models( - make_candidate_totals_schema, + make_pac_party_totals_schema, + models.CommitteeTotalsPacParty, +) + +candidate_committee_fields = { + 'last_cash_on_hand_end_period': ma.fields.Float(), + 'last_beginning_image_number': ma.fields.Str(), + 'receipts': ma.fields.Float(), + 'candidate_contribution': ma.fields.Float(), + 'offsets_to_operating_expenditures': ma.fields.Float(), + 'political_party_committee_contributions': ma.fields.Float(), + 'other_disbursements': ma.fields.Float(), + 'other_political_committee_contributions': ma.fields.Float(), + 'individual_itemized_contributions': ma.fields.Float(), + 'individual_unitemized_contributions': ma.fields.Float(), + 'disbursements': ma.fields.Float(), + 'contributions': ma.fields.Float(), + 'individual_contributions': ma.fields.Float(), + 'contribution_refunds': ma.fields.Float(), + 'operating_expenditures': ma.fields.Float(), + 'refunded_individual_contributions': ma.fields.Float(), + 'refunded_other_political_committee_contributions': ma.fields.Float(), + 'refunded_political_party_committee_contributions': ma.fields.Float(), + 'last_debts_owed_by_committee': ma.fields.Float(), + 'last_debts_owed_to_committee': ma.fields.Float() + +} + +make_candidate_totals_schema = make_schema( models.CandidateCommitteeTotalsPresidential, + fields=utils.extend({ + 'exempt_legal_accounting_disbursement': ma.fields.Float(), + 'federal_funds': ma.fields.Float(), + 'fundraising_disbursements': ma.fields.Float(), + 'loan_repayments_made': ma.fields.Float(), + 'loans_received': ma.fields.Float(), + 'loans_received_from_candidate': ma.fields.Float(), + 'offsets_to_fundraising_expenditures': ma.fields.Float(), + 'offsets_to_legal_accounting': ma.fields.Float(), + 'total_offsets_to_operating_expenditures': ma.fields.Float(), + 'other_loans_received': ma.fields.Float(), + 'other_receipts': ma.fields.Float(), + 'repayments_loans_made_by_candidate': ma.fields.Float(), + 'repayments_other_loans': ma.fields.Float(), + 'transfers_from_affiliated_committee': ma.fields.Float(), + 'transfers_to_other_authorized_committee': ma.fields.Float(), + 'net_operating_expenditures': ma.fields.Float(), + 'net_contributions': ma.fields.Float(), + 'disbursements': ma.fields.Float() + }, candidate_committee_fields) +) +augment_schemas(make_candidate_totals_schema) + +candidate_committee_totals_hs = make_schema( models.CandidateCommitteeTotalsHouseSenate, -) + fields=utils.extend({ + 'all_other_loans': ma.fields.Float(), + 'candidate_contribution': ma.fields.Float(), + 'loan_repayments': ma.fields.Float(), + 'loan_repayments_candidate_loans': ma.fields.Float(), + 'loan_repayments_other_loans': ma.fields.Float(), + 'loans': ma.fields.Float(), + 'loans_made_by_candidate': ma.fields.Float(), + 'other_receipts': ma.fields.Float(), + 'transfers_from_other_authorized_committee': ma.fields.Float(), + 'transfers_to_other_authorized_committee': ma.fields.Float(), + 'net_operating_expenditures': ma.fields.Float(), + 'net_contributions': ma.fields.Float() + }, candidate_committee_fields) +) +augment_schemas(candidate_committee_totals_hs) register_schema(CommitteeReportsSchema) register_schema(CommitteeReportsPageSchema) @@ -593,29 +1066,27 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'memoed_subtotal': ma.fields.Boolean(), 'committee': ma.fields.Nested(schemas['CommitteeHistorySchema']), 'contributor': ma.fields.Nested(schemas['CommitteeHistorySchema']), - 'contribution_receipt_amount': ma.fields.Decimal(places=2), - 'contributor_aggregate_ytd': ma.fields.Decimal(places=2), + 'contribution_receipt_amount': ma.fields.Float(), + 'contributor_aggregate_ytd': ma.fields.Float(), 'image_number': ma.fields.Str(), 'original_sub_id': ma.fields.Str(), 'sub_id': ma.fields.Str(), + 'report_year': ma.fields.Int() }, options={ - 'exclude': ( + 'exclude': ( 'contributor_name_text', 'contributor_employer_text', 'contributor_occupation_text', - 'recipient_street_1', - 'recipient_street_2', - ), - 'relationships': [ + ), + 'relationships': [ Relationship( models.ScheduleA.committee, models.CommitteeHistory.name, 'committee_name', 1 - ), - ], - + ), + ], } ) @@ -629,15 +1100,9 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'sub_id': ma.fields.Str(), 'pdf_url': ma.fields.Str(), 'committee': ma.fields.Nested(schemas['CommitteeHistorySchema']), - - }, - options={ - 'exclude': ( - 'loan_source_name_text', - 'candidate_name_text', - ) }, -) + options={'exclude': ('loan_source_name_text', 'candidate_name_text',)} + ) ScheduleCPageSchema = make_page_schema( ScheduleCSchema, ) @@ -647,10 +1112,10 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], fields={ 'committee_name': ma.fields.Str(), 'recipient_name': ma.fields.Str(), + 'total': ma.fields.Float(), + 'memo_total': ma.fields.Float() }, - options={ - 'exclude': ('idx', 'committee', 'recipient') - }, + options={'exclude': ('idx', 'committee', 'recipient')} ) augment_schemas(ScheduleBByRecipientIDSchema) @@ -659,11 +1124,12 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], ScheduleBByRecipientSchema = make_schema( models.ScheduleBByRecipient, fields={ - 'recipient_disbursement_percent': ma.fields.Decimal(places=2), - }, - options={ - 'exclude': ('idx', 'committee') + 'recipient_disbursement_percent': ma.fields.Float(), + 'committee_total_disbursements': ma.fields.Float(), + 'total': ma.fields.Float(), + 'memo_total': ma.fields.Float() }, + options={'exclude': ('idx', 'committee')} ) ScheduleBByRecipientPageSchema = make_page_schema(ScheduleBByRecipientSchema, page_type=paging_schemas.SeekPageSchema) @@ -688,9 +1154,10 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'candidate_id': ma.fields.Str(), 'committee_name': ma.fields.Str(), 'candidate_name': ma.fields.Str(), + 'total': ma.fields.Float() }, - options={'exclude': ('idx', 'committee', 'candidate')}, -) + options={'exclude': ('idx', 'committee', 'candidate')} + ) ScheduleEByCandidateSchema = make_aggregate_schema(models.ScheduleEByCandidate) augment_schemas(ScheduleEByCandidateSchema) @@ -702,9 +1169,7 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'pdf_url': ma.fields.Str(), 'sub_id': ma.fields.Str(), }, - options={ - 'exclude': ('creditor_debtor_name_text',) - }, + options={'exclude': ('creditor_debtor_name_text',)} ) ScheduleDPageSchema = make_page_schema( ScheduleDSchema @@ -718,10 +1183,8 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'pdf_url': ma.fields.Str(), 'sub_id': ma.fields.Str(), }, - options={'exclude': ('payee_name_text',) - }, - -) + options={'exclude': ('payee_name_text',)} + ) ScheduleFPageSchema = make_page_schema( ScheduleFSchema ) @@ -741,6 +1204,7 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'image_number': ma.fields.Str(), 'original_sub_id': ma.fields.Str(), 'sub_id': ma.fields.Str(), + 'disbursement_amount': ma.fields.Float() }, options={ 'exclude': ( @@ -770,6 +1234,12 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'image_number': ma.fields.Str(), 'original_sub_id': ma.fields.Str(), 'sub_id': ma.fields.Str(), + 'cycle': ma.fields.Int(), + 'disbursement_amount': ma.fields.Float(), + 'federal_share': ma.fields.Float(), + 'nonfederal_share': ma.fields.Float(), + 'event_amount_year_to_date': ma.fields.Float(), + }, options={ 'exclude': ( @@ -795,8 +1265,8 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], fields={ 'memoed_subtotal': ma.fields.Boolean(), 'committee': ma.fields.Nested(schemas['CommitteeHistorySchema']), - 'expenditure_amount': ma.fields.Decimal(places=2), - 'office_total_ytd': ma.fields.Decimal(places=2), + 'expenditure_amount': ma.fields.Float(), + 'office_total_ytd': ma.fields.Float(), 'image_number': ma.fields.Str(), 'original_sub_id': ma.fields.Str(), 'sub_id': ma.fields.Str(), @@ -820,9 +1290,7 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], register_schema(ScheduleESchema) register_schema(ScheduleEPageSchema) -CommunicationCostSchema = make_schema( - models.CommunicationCost, -) +CommunicationCostSchema = make_schema(models.CommunicationCost, fields={'transaction_amount': ma.fields.Float()}) CommunicationCostPageSchema = make_page_schema( CommunicationCostSchema, page_type=paging_schemas.OffsetPageSchema @@ -839,18 +1307,24 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'candidate_name': ma.fields.Str(), 'cycle': ma.fields.Int(), 'count': ma.fields.Int(), - 'total': ma.fields.Decimal(places=2), + 'total': ma.fields.Float(), }, + options={'exclude': ('idx',)} ) CCAggregatesPageSchema = make_page_schema(CCAggregatesSchema) -register_schema(CCAggregatesSchema) -register_schema(CCAggregatesPageSchema) +register_schema(CCAggregatesSchema, 'CCAggregates') +register_schema(CCAggregatesPageSchema, 'CCAggregatesPage') ElectioneeringSchema = make_schema( models.Electioneering, - fields={'election_type': ma.fields.Str()}, - options={'exclude': ('idx', 'purpose_description_text', 'election_type_raw')}, + fields={'election_type': ma.fields.Str(), + 'number_of_candidates': ma.fields.Float(), + 'calculated_candidate_share': ma.fields.Float(), + 'disbursement_amount': ma.fields.Float(), + }, + options={'exclude': ('idx', 'purpose_description_text', 'election_type_raw')} ) + ElectioneeringPageSchema = make_page_schema(ElectioneeringSchema, page_type=paging_schemas.SeekPageSchema) register_schema(ElectioneeringSchema) register_schema(ElectioneeringPageSchema) @@ -864,12 +1338,13 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'candidate_name': ma.fields.Str(), 'cycle': ma.fields.Int(), 'count': ma.fields.Int(), - 'total': ma.fields.Decimal(places=2), + 'total': ma.fields.Float(), }, + options={'exclude': ('idx',)} ) ECAggregatesPageSchema = make_page_schema(ECAggregatesSchema) -register_schema(ECAggregatesSchema) -register_schema(ECAggregatesPageSchema) +register_schema(ECAggregatesSchema, 'ECAggregates') +register_schema(ECAggregatesPageSchema, 'ECAggregatesPage') BaseFilingsSchema = make_schema( models.Filings, @@ -882,22 +1357,46 @@ class CandidateHistoryTotalSchema(schemas['CandidateHistorySchema'], 'sub_id': ma.fields.Str(), 'fec_file_id': ma.fields.Str(), 'report_type_full': ma.fields.Str(), + 'amendment_chain': ma.fields.List(ma.fields.Int()), + 'total_receipts': ma.fields.Float(), + 'total_individual_contributions': ma.fields.Float(), + 'net_donations': ma.fields.Float(), + 'total_disbursements': ma.fields.Float(), + 'total_independent_expenditures': ma.fields.Float(), + 'total_communication_cost': ma.fields.Float(), + 'cash_on_hand_beginning_period': ma.fields.Float(), + 'cash_on_hand_end_period': ma.fields.Float(), + 'debts_owed_by_committee': ma.fields.Float(), + 'debts_owed_to_committee': ma.fields.Float(), + 'house_personal_funds': ma.fields.Float(), + 'senate_personal_funds': ma.fields.Float(), + 'opposition_personal_funds': ma.fields.Float() }, - options={'exclude': ('committee', 'filer_name_text', 'report_type_full_original',)}, + options={'exclude': ('committee', 'filer_name_text', 'report_type_full_original', )} ) class FilingsSchema(BaseFilingsSchema): @post_dump - def remove_fec_url(self, obj): + def remove_fec_url(self, obj, **kwargs): if not obj.get('fec_url'): obj.pop('fec_url') + return obj + augment_schemas(FilingsSchema) EfilingsAmendmentsSchema = make_schema( models.EfilingsAmendments, + fields={ + 'amendment_chain': ma.fields.List(ma.fields.Int()), + 'longest_chain': ma.fields.Float(), + 'most_recent_filing': ma.fields.Float(), + 'depth': ma.fields.Float(), + 'last': ma.fields.Float(), + 'previous_file_number': ma.fields.Float() + } ) augment_schemas(EfilingsAmendmentsSchema) @@ -919,7 +1418,7 @@ def remove_fec_url(self, obj): 'amended_by': ma.fields.Int(), 'fec_file_id': ma.fields.Str(), }, - options={'exclude': ('report', 'amendment', 'superceded')}, + options={'exclude': ('report', 'amendment', 'superceded', 'report_type')} ) augment_schemas(EFilingsSchema) @@ -935,6 +1434,7 @@ def remove_fec_url(self, obj): 'payee_name': ma.fields.Str(), 'report_type': ma.fields.Str(), 'csv_url': ma.fields.Str(), + 'disbursement_amount': ma.fields.Float() }, options={ 'relationships': [ @@ -963,7 +1463,7 @@ def remove_fec_url(self, obj): 'csv_url': ma.fields.Str(), }, options={ - 'exclude': ['cand_fulltxt'], + 'exclude': ('cand_fulltxt',), 'relationships': [ Relationship( models.ScheduleEEfile.committee, @@ -989,12 +1489,11 @@ def remove_fec_url(self, obj): 'payee_name': ma.fields.Str(), 'report_type': ma.fields.Str(), 'csv_url': ma.fields.Str(), + 'disbursement_amount': ma.fields.Float(), + 'fed_share': ma.fields.Float(), + 'event_amount_year_to_date': ma.fields.Float() }, options={ - 'exclude': ( - 'line_num', - 'tran_id', - ), 'relationships': [ Relationship( models.ScheduleH4Efile.committee, @@ -1021,6 +1520,9 @@ def remove_fec_url(self, obj): 'contributor_name': ma.fields.Str(), 'fec_election_type_desc': ma.fields.Str(), 'csv_url': ma.fields.Str(), + 'contributor_aggregate_ytd': ma.fields.Float(), + 'contribution_receipt_amount': ma.fields.Float(), + }, options={ 'exclude': ( @@ -1051,8 +1553,8 @@ def remove_fec_url(self, obj): 'report_type_full': ma.fields.Str(), }, options={'exclude': ('trc_report_due_date_id', 'report')}, + class_name='ReportingDatesSchema' ) -ReportingDatesPageSchema = make_page_schema(ReportingDatesSchema) augment_schemas(ReportingDatesSchema) ElectionDatesSchema = make_schema( @@ -1061,11 +1563,10 @@ def remove_fec_url(self, obj): 'election_type_full': ma.fields.Str(), 'active_election': ma.fields.Boolean(), }, - options={ - 'exclude': ('trc_election_id', 'election_status_id'), - }, + options={'exclude': ('trc_election_id', 'election_status_id')}, + class_name='ElectionDatesSchema' + ) -ElectionDatesPageSchema = make_page_schema(ElectionDatesSchema) augment_schemas(ElectionDatesSchema) CalendarDateSchema = make_schema( @@ -1077,12 +1578,8 @@ def remove_fec_url(self, obj): 'end_date': ma.fields.Function(format_end_date), }, options={ - 'exclude': ( - 'summary_text', 'description_text' - ) - }, + 'exclude': ('summary_text', 'description_text')} ) -CalendarDatePageSchema = make_page_schema(CalendarDateSchema) augment_schemas(CalendarDateSchema) @@ -1101,9 +1598,9 @@ class ElectionSearchSchema(ma.Schema): class ElectionSummarySchema(ApiSchema): count = ma.fields.Int() - receipts = ma.fields.Decimal(places=2) - disbursements = ma.fields.Decimal(places=2) - independent_expenditures = ma.fields.Decimal(places=2) + receipts = ma.fields.Float() + disbursements = ma.fields.Float() + independent_expenditures = ma.fields.Float() register_schema(ElectionSummarySchema) @@ -1117,23 +1614,20 @@ class ElectionSchema(ma.Schema): committee_ids = ma.fields.List(ma.fields.Str) candidate_pcc_id = ma.fields.Str(doc="The candidate's primary campaign committee ID") candidate_pcc_name = ma.fields.Str(doc="The candidate's primary campaign committee name") - total_receipts = ma.fields.Decimal(places=2) - total_disbursements = ma.fields.Decimal(places=2) - cash_on_hand_end_period = ma.fields.Decimal(places=2) + total_receipts = ma.fields.Float() + total_disbursements = ma.fields.Float() + cash_on_hand_end_period = ma.fields.Float() candidate_election_year = ma.fields.Int() coverage_end_date = ma.fields.Date() augment_schemas(ElectionSchema) -ElectionPageSchema = make_page_schema(ElectionSchema) -register_schema(ElectionPageSchema) - class ScheduleABySizeCandidateSchema(ma.Schema): candidate_id = ma.fields.Str() cycle = ma.fields.Int() - total = ma.fields.Decimal(places=2) + total = ma.fields.Float() size = ma.fields.Int() count = ma.fields.Int() @@ -1141,7 +1635,7 @@ class ScheduleABySizeCandidateSchema(ma.Schema): class ScheduleAByStateCandidateSchema(ma.Schema): candidate_id = ma.fields.Str() cycle = ma.fields.Int() - total = ma.fields.Decimal(places=2) + total = ma.fields.Float() state = ma.fields.Str() state_full = ma.fields.Str() count = ma.fields.Int() @@ -1150,16 +1644,16 @@ class ScheduleAByStateCandidateSchema(ma.Schema): class ScheduleAByContributorTypeCandidateSchema(ma.Schema): candidate_id = ma.fields.Str() cycle = ma.fields.Int() - total = ma.fields.Decimal(places=2) + total = ma.fields.Float() individual = ma.fields.Bool() class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): - receipts = ma.fields.Decimal(places=2) - disbursements = ma.fields.Decimal(places=2) - cash_on_hand_end_period = ma.fields.Decimal(places=2) - debts_owed_by_committee = ma.fields.Decimal(places=2) - independent_expenditures = ma.fields.Decimal(places=2) + receipts = ma.fields.Float() + disbursements = ma.fields.Float() + cash_on_hand_end_period = ma.fields.Float() + debts_owed_by_committee = ma.fields.Float() + independent_expenditures = ma.fields.Float() augment_schemas( @@ -1170,7 +1664,11 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): RadAnalystSchema = make_schema( models.RadAnalyst, - options={'exclude': ('idx', 'name_txt')}, + fields={'analyst_id': ma.fields.Float(), + 'analyst_short_id': ma.fields.Float(), + 'telephone_ext': ma.fields.Float() + }, + options={'exclude': ('idx', 'name_txt')} ) RadAnalystPageSchema = make_page_schema(RadAnalystSchema) register_schema(RadAnalystSchema) @@ -1187,6 +1685,7 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): ScheduleAByStateRecipientTotalsSchema = make_schema( models.ScheduleAByStateRecipientTotals, + fields={'total': ma.fields.Float()}, options={'exclude': ('idx',)} ) ScheduleAByStateRecipientTotalsPageSchema = make_page_schema( @@ -1206,7 +1705,8 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): }, options={ 'exclude': ('tier',) - } + }, + BaseSchema=BaseSchema ) AuditPrimaryCategoryPageSchema = make_page_schema(AuditPrimaryCategorySchema) @@ -1224,7 +1724,8 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): }, options={ 'exclude': ('primary_category_id', 'primary_category_name') - } + }, + BaseSchema=BaseSchema ) AuditCategoryRelationPageSchema = make_page_schema(AuditCategoryRelationSchema) @@ -1250,7 +1751,8 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): # 1 # ), # ], - } + }, + BaseSchema=BaseSchema ) AuditCategoryPageSchema = make_page_schema(AuditCategorySchema) @@ -1271,7 +1773,7 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): 'exclude': ( 'primary_category_id', 'audit_case_id', - 'primary_category_name', + 'primary_category_name' ) } ) @@ -1316,14 +1818,12 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): 'audit_id': ma.fields.Integer(), 'candidate_id': ma.fields.Str(), 'candidate_name': ma.fields.Str(), + 'link_to_report': ma.fields.Str(), 'primary_category_list': ma.fields.Nested(AuditCaseCategoryRelationSchema, many=True), }, options={ - 'exclude': ( - 'primary_category_id', - 'sub_category_id', - 'idx', - )} + 'exclude': ('idx',)}, + BaseSchema=BaseSchema ) AuditCasePageSchema = make_page_schema(AuditCaseSchema) register_schema(AuditCaseSchema) @@ -1336,26 +1836,20 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): 'idx', 'sort_order', 'incumbent_id', - 'incumbent_name', - ) - } + 'incumbent_name')} ) ElectionsListPageSchema = make_page_schema(ElectionsListSchema) register_schema(ElectionsListSchema) register_schema(ElectionsListPageSchema) -StateElectionOfficeInfoSchema = make_schema( - models.StateElectionOfficeInfo, -) +StateElectionOfficeInfoSchema = make_schema(models.StateElectionOfficeInfo) StateElectionOfficeInfoPageSchema = make_page_schema(StateElectionOfficeInfoSchema) register_schema(StateElectionOfficeInfoSchema) register_schema(StateElectionOfficeInfoPageSchema) -OperationsLogSchema = make_schema( - models.OperationsLog, -) +OperationsLogSchema = make_schema(models.OperationsLog) OperationsLogPageSchema = make_page_schema(OperationsLogSchema) register_schema(OperationsLogSchema) @@ -1365,11 +1859,11 @@ class TotalsCommitteeSchema(schemas['CommitteeHistorySchema']): class TotalByOfficeSchema(ma.Schema): office = ma.fields.Str() election_year = ma.fields.Int() - total_receipts = ma.fields.Decimal(places=2) - total_disbursements = ma.fields.Decimal(places=2) - total_individual_itemized_contributions = ma.fields.Decimal(places=2) - total_transfers_from_other_authorized_committee = ma.fields.Decimal(places=2) - total_other_political_committee_contributions = ma.fields.Decimal(places=2) + total_receipts = ma.fields.Float() + total_disbursements = ma.fields.Float() + total_individual_itemized_contributions = ma.fields.Float() + total_transfers_from_other_authorized_committee = ma.fields.Float() + total_other_political_committee_contributions = ma.fields.Float() augment_schemas(TotalByOfficeSchema) @@ -1379,8 +1873,8 @@ class TotalByOfficeByPartySchema(ma.Schema): office = ma.fields.Str() party = ma.fields.Str() election_year = ma.fields.Int() - total_receipts = ma.fields.Decimal(places=2) - total_disbursements = ma.fields.Decimal(places=2) + total_receipts = ma.fields.Float() + total_disbursements = ma.fields.Float() augment_schemas(TotalByOfficeByPartySchema) @@ -1390,13 +1884,13 @@ class CandidateTotalAggregateSchema(ma.Schema): election_year = ma.fields.Int() office = ma.fields.Str() party = ma.fields.Str() - total_receipts = ma.fields.Decimal(places=2) - total_disbursements = ma.fields.Decimal(places=2) - total_individual_itemized_contributions = ma.fields.Decimal(places=2) - total_transfers_from_other_authorized_committee = ma.fields.Decimal(places=2) - total_other_political_committee_contributions = ma.fields.Decimal(places=2) - total_cash_on_hand_end_period = ma.fields.Decimal(places=2) - total_debts_owed_by_committee = ma.fields.Decimal(places=2) + total_receipts = ma.fields.Float() + total_disbursements = ma.fields.Float() + total_individual_itemized_contributions = ma.fields.Float() + total_transfers_from_other_authorized_committee = ma.fields.Float() + total_other_political_committee_contributions = ma.fields.Float() + total_cash_on_hand_end_period = ma.fields.Float() + total_debts_owed_by_committee = ma.fields.Float() state = ma.fields.Str() district = ma.fields.Str() district_number = ma.fields.Int() @@ -1409,7 +1903,7 @@ class CandidateTotalAggregateSchema(ma.Schema): class ECTotalsByCandidateSchema(ma.Schema): candidate_id = ma.fields.Str() cycle = ma.fields.Int() - total = ma.fields.Decimal(places=2) + total = ma.fields.Float() augment_schemas(ECTotalsByCandidateSchema) @@ -1419,7 +1913,7 @@ class IETotalsByCandidateSchema(ma.Schema): candidate_id = ma.fields.Str() cycle = ma.fields.Int() support_oppose_indicator = ma.fields.Str() - total = ma.fields.Decimal(places=2) + total = ma.fields.Float() augment_schemas(IETotalsByCandidateSchema) @@ -1429,19 +1923,21 @@ class CCTotalsByCandidateSchema(ma.Schema): candidate_id = ma.fields.Str() cycle = ma.fields.Int() support_oppose_indicator = ma.fields.Str() - total = ma.fields.Decimal(places=2) + total = ma.fields.Float() augment_schemas(CCTotalsByCandidateSchema) # Presidential endpoints -# There may be a more efficient way to do this, but we're going for speed +# There may be a more efficient way to do this,but we're going for speed # By candidate PresidentialByCandidateSchema = make_schema( models.PresidentialByCandidate, - options={'exclude': ('idx',)}, + fields={'net_receipts': ma.fields.Float(), + 'rounded_net_receipts': ma.fields.Float()}, + options={'exclude': ('idx',)} ) PresidentialByCandidatePageSchema = make_page_schema(PresidentialByCandidateSchema) register_schema(PresidentialByCandidateSchema) @@ -1451,7 +1947,30 @@ class CCTotalsByCandidateSchema(ma.Schema): PresidentialSummarySchema = make_schema( models.PresidentialSummary, - options={'exclude': ('idx',)}, + fields={ + 'net_receipts': ma.fields.Float(), + 'rounded_net_receipts': ma.fields.Float(), + 'individual_contributions_less_refunds': ma.fields.Float(), + 'pac_contributions_less_refunds': ma.fields.Float(), + 'party_contributions_less_refunds': ma.fields.Float(), + 'candidate_contributions_less_repayments': ma.fields.Float(), + 'disbursements_less_offsets': ma.fields.Float(), + 'operating_expenditures': ma.fields.Float(), + 'transfers_to_other_authorized_committees': ma.fields.Float(), + 'transfers_from_affiliated_committees': ma.fields.Float(), + 'fundraising_disbursements': ma.fields.Float(), + 'exempt_legal_accounting_disbursement': ma.fields.Float(), + 'total_loan_repayments_made': ma.fields.Float(), + 'repayments_loans_made_by_candidate': ma.fields.Float(), + 'repayments_other_loans': ma.fields.Float(), + 'other_disbursements': ma.fields.Float(), + 'offsets_to_operating_expenditures': ma.fields.Float(), + 'total_contribution_refunds': ma.fields.Float(), + 'debts_owed_by_committee': ma.fields.Float(), + 'federal_funds': ma.fields.Float(), + 'cash_on_hand_end': ma.fields.Float() + }, + options={'exclude': ('idx',)} ) PresidentialSummaryPageSchema = make_page_schema(PresidentialSummarySchema) register_schema(PresidentialSummarySchema) @@ -1461,7 +1980,8 @@ class CCTotalsByCandidateSchema(ma.Schema): PresidentialBySizeSchema = make_schema( models.PresidentialBySize, - options={'exclude': ('idx',)}, + fields={'contribution_receipt_amount': ma.fields.Float()}, + options={'exclude': ('idx',)} ) PresidentialBySizePageSchema = make_page_schema(PresidentialBySizeSchema) register_schema(PresidentialBySizeSchema) @@ -1471,6 +1991,7 @@ class CCTotalsByCandidateSchema(ma.Schema): PresidentialByStateSchema = make_schema( models.PresidentialByState, + fields={'contribution_receipt_amount': ma.fields.Float()}, options={'exclude': ('idx',)}, ) PresidentialByStatePageSchema = make_page_schema(PresidentialByStateSchema) @@ -1481,13 +2002,19 @@ class CCTotalsByCandidateSchema(ma.Schema): PresidentialCoverageSchema = make_schema( models.PresidentialCoverage, - options={'exclude': ('idx',)}, + options={'exclude': ('idx',)} ) PresidentialCoveragePageSchema = make_page_schema(PresidentialCoverageSchema) register_schema(PresidentialCoverageSchema) register_schema(PresidentialCoveragePageSchema) -InauguralDonationsSchema = make_schema(models.InauguralDonations) +InauguralDonationsSchema = make_schema( + models.InauguralDonations, + fields={ + 'total_donation': ma.fields.Float(), + 'cycle': ma.fields.Int() + } +) InauguralDonationsPageSchema = make_page_schema(InauguralDonationsSchema) register_schema(InauguralDonationsSchema) register_schema(InauguralDonationsPageSchema) diff --git a/webservices/spec.py b/webservices/spec.py index 630cd5c41..4a6e22035 100644 --- a/webservices/spec.py +++ b/webservices/spec.py @@ -107,8 +107,8 @@ def format_docstring(docstring): spec = APISpec( title='OpenFEC', version=__API_VERSION__, + openapi_version='2.0', info={'description': format_docstring(docs.API_DESCRIPTION)}, - basePath='/v1', produces=['application/json'], plugins=[MarshmallowPlugin()], securityDefinitions={ diff --git a/webservices/tasks/download.py b/webservices/tasks/download.py index a284032ec..d0b53e92c 100644 --- a/webservices/tasks/download.py +++ b/webservices/tasks/download.py @@ -53,7 +53,7 @@ def parse_kwargs(resource, qs): annotation = resolve_annotations(resource.get, "args", parent=resource) fields = utils.extend(*[option["args"] for option in annotation.options]) with task_utils.get_app().test_request_context(b"?" + qs): - kwargs = flaskparser.parser.parse(fields) + kwargs = flaskparser.parser.parse(fields, location='query') return fields, kwargs diff --git a/webservices/utils.py b/webservices/utils.py index 8c6549500..9e8b9adce 100644 --- a/webservices/utils.py +++ b/webservices/utils.py @@ -34,7 +34,7 @@ logger = logging.getLogger(__name__) -use_kwargs = functools.partial(use_kwargs_original, locations=("query",)) +use_kwargs = functools.partial(use_kwargs_original, location="query") DOCQUERY_URL = 'https://docquery.fec.gov' @@ -44,9 +44,9 @@ class Resource(six.with_metaclass(MethodResourceMeta, restful.Resource)): API_KEY_ARG = fields.Str( - required=True, missing="DEMO_KEY", description=docs.API_KEY_DESCRIPTION, + missing="DEMO_KEY", description=docs.API_KEY_DESCRIPTION, ) -if env.get_credential("PRODUCTION"): +if env.get_credential("PRODUCTION") or env.get_credential("STAGE"): Resource = use_kwargs({"api_key": API_KEY_ARG})(Resource) fec_url_map = {'9': DOCQUERY_URL + '/dcdev/posted/{0}.fec'}