Пример #1
0
 def get(self, **kwargs):
     """Get itemized resources. If multiple values are passed for `committee_id`,
     create a subquery for each and combine with `UNION ALL`. This is necessary
     to avoid slow queries when one or more relevant committees has many
     records.
     """
     if kwargs.get("last_index"):
         if all(
             kwargs.get("last_{}".format(option)) is None
             for option in self.sort_options
         ) and not kwargs.get("sort_null_only"):
             raise exceptions.ApiError(
                 "When paginating through results, both values from the \
                 previous page's `last_indexes` object are needed. For more information, \
                 see https://api.open.fec.gov/developers/. Please add one of the following \
                 filters to your query: `sort_null_only`=True, {}".format(
                     ", ".join("`last_" + option + "`" for option in self.sort_options)
                 ),
                 status_code=422,
             )
     committee_ids = kwargs.get('committee_id', [])
     if len(committee_ids) > 10:
         raise exceptions.ApiError(
             'Can only specify up to ten values for "committee_id".',
             status_code=422,
         )
     if len(committee_ids) > 1:
         query, count = self.join_committee_queries(kwargs)
         return utils.fetch_seek_page(query, kwargs, self.index_column, count=count)
     query = self.build_query(**kwargs)
     count, _ = counts.get_count(query, models.db.session)
     return utils.fetch_seek_page(query, kwargs, self.index_column, count=count, cap=self.cap)
Пример #2
0
 def _validate(self, value):
     super()._validate(value)
     try:
         value = int(value)
     except (TypeError, ValueError):
         raise exceptions.ApiError('District must be a number',
                                   status_code=422)
     if value < 0:
         raise exceptions.ApiError('District must be a natural number',
                                   status_code=422)
Пример #3
0
 def build_query(self, **kwargs):
     secondary_index_options = [
         'committee_id',
         'contributor_id',
         'contributor_name',
         'contributor_city',
         'contributor_zip',
         'contributor_employer',
         'contributor_occupation',
         'image_number',
     ]
     two_year_transaction_periods = set(
         kwargs.get('two_year_transaction_period', [])
     )
     if len(two_year_transaction_periods) != 1:
         if not any(kwargs.get(field) for field in secondary_index_options):
             raise exceptions.ApiError(
                 "Please choose a single `two_year_transaction_period` or \
                 add one of the following filters to your query: `{}`".format(
                     "`, `".join(secondary_index_options)
                 ),
                 status_code=400,
             )
     query = super().build_query(**kwargs)
     query = filters.filter_contributor_type(query, self.model.entity_type, kwargs)
     zip_list = []
     if kwargs.get('contributor_zip'):
         for value in kwargs['contributor_zip']:
             if re.search('[^a-zA-Z0-9-\s]', value):  # noqa
                 raise exceptions.ApiError(
                     'Invalid zip code. It can not have special character',
                     status_code=400,
                 )
             else:
                 zip_list.append(value[:5])
         contributor_zip_list = {'contributor_zip': zip_list}
         query = filters.filter_multi_start_with(
             query, contributor_zip_list, self.filter_multi_start_with_fields
         )
     if kwargs.get('sub_id'):
         query = query.filter_by(sub_id=int(kwargs.get('sub_id')))
     if kwargs.get('line_number'):
         # line_number is a composite value of 'filing_form-line_number'
         if len(kwargs.get('line_number').split('-')) == 2:
             form, line_no = kwargs.get('line_number').split('-')
             query = query.filter_by(filing_form=form.upper())
             query = query.filter_by(line_number=line_no)
         else:
             raise exceptions.ApiError(
                 exceptions.LINE_NUMBER_ERROR, status_code=400,
             )
     return query
Пример #4
0
 def build_query(self, **kwargs):
     query = super().build_query(**kwargs)
     query = filters.filter_contributor_type(query, self.model.entity_type,
                                             kwargs)
     zip_list = []
     if kwargs.get('contributor_zip'):
         for value in kwargs['contributor_zip']:
             if re.search('[^a-zA-Z0-9-\s]', value):
                 raise exceptions.ApiError(
                     'Invalid zip code. It can not have special character',
                     status_code=400,
                 )
             else:
                 zip_list.append(value[:5])
         contributor_zip_list = {'contributor_zip': zip_list}
         query = filters.filter_multi_start_with(
             query, contributor_zip_list,
             self.filter_multi_start_with_fields)
     if kwargs.get('sub_id'):
         query = query.filter_by(sub_id=int(kwargs.get('sub_id')))
     if kwargs.get('line_number'):
         if len(kwargs.get('line_number').split('-')) == 2:
             form, line_no = kwargs.get('line_number').split('-')
             query = query.filter_by(filing_form=form.upper())
             query = query.filter_by(line_number=line_no)
     return query
Пример #5
0
    def build_query(self, **kwargs):
        query = super().build_query(**kwargs)

        if {'receipts', '-receipts'}.intersection(kwargs.get(
                'sort', [])) and 'q' not in kwargs:
            raise exceptions.ApiError(
                'Cannot sort on receipts when parameter "q" is not set',
                status_code=422,
            )

        if kwargs.get('q'):
            query = utils.search_text(
                query.join(
                    models.CandidateSearch,
                    models.Candidate.candidate_id == models.CandidateSearch.id,
                ),
                models.CandidateSearch.fulltxt,
                kwargs['q'],
            ).distinct()

        if kwargs.get('name'):
            query = query.filter(
                models.Candidate.name.ilike('%{}%'.format(kwargs['name'])))

        # TODO(jmcarp) Reintroduce year filter pending accurate `load_date` and `expire_date` values
        if kwargs.get('cycle'):
            query = query.filter(
                models.Candidate.cycles.overlap(kwargs['cycle']))

        return query
Пример #6
0
 def get(self, **kwargs):
     """Get itemized resources. If multiple values are passed for `committee_id`,
     create a subquery for each and combine with `UNION ALL`. This is necessary
     to avoid slow queries when one or more relevant committees has many
     records.
     """
     committee_ids = kwargs.get('committee_id', [])
     if len(committee_ids) > 10:
         raise exceptions.ApiError(
             'Can only specify up to ten values for "committee_id".',
             status_code=422,
         )
     if len(committee_ids) > 1:
         query, count = self.join_committee_queries(kwargs)
         return utils.fetch_seek_page(query,
                                      kwargs,
                                      self.index_column,
                                      count=count)
     query = self.build_query(**kwargs)
     count, _ = counts.get_count(query, models.db.session)
     return utils.fetch_seek_page(query,
                                  kwargs,
                                  self.index_column,
                                  count=count,
                                  cap=self.cap)
Пример #7
0
    def build_query(self, **kwargs):
        query = super().build_query(**kwargs)

        if {'receipts', '-receipts'}.intersection(kwargs.get(
                'sort', [])) and 'q' not in kwargs:
            raise exceptions.ApiError(
                'Cannot sort on receipts when parameter "q" is not set',
                status_code=422,
            )

        if kwargs.get('candidate_id'):
            query = query.filter(
                models.Committee.candidate_ids.overlap(kwargs['candidate_id']))

        if kwargs.get('q'):
            query = query.join(
                models.CommitteeSearch,
                models.Committee.committee_id == models.CommitteeSearch.id,
            ).distinct()

        if kwargs.get('year'):
            query = filter_year(models.Committee, query, kwargs['year'])

        if kwargs.get('cycle'):
            query = query.filter(
                models.Committee.cycles.overlap(kwargs['cycle']))

        return query
Пример #8
0
 def __call__(self, value):
     for sort_column in value:
         if sort_column.lstrip('-') not in self.values:
             raise exceptions.ApiError(
                 'Cannot sort on value "{0}"'.format(sort_column),
                 status_code=422,
             )
Пример #9
0
    def build_query(self, candidate_id=None, **kwargs):
        totals_class, totals_schema = candidate_totals_schema_map.get(
            self._resolve_committee_type(candidate_id=candidate_id, **kwargs),
            default_schemas,
        )
        query = totals_class.query
        query = query.filter(totals_class.candidate_id == candidate_id)

        if 'full_election' in kwargs.keys():
            # full_election is replaced by election_full.
            raise exceptions.ApiError(
                exceptions.FULL_ELECTION_ERROR,
                status_code=400,
            )

        if kwargs.get('election_full') is None:
            # not pass election_full
            if kwargs.get('cycle'):
                # only filter by cycle
                query = query.filter(totals_class.cycle.in_(kwargs['cycle']))
        else:
            # pass election_full (true or false)
            query = query.filter(
                totals_class.election_full == kwargs['election_full'])
            if kwargs.get('cycle'):
                if kwargs.get('election_full'):
                    # if election_full = true, filter by candidate_election_year = cycle
                    query = query.filter(
                        totals_class.candidate_election_year.in_(
                            kwargs['cycle']))
                else:
                    # if election_full = false, filter by cycle = cycle
                    query = query.filter(
                        totals_class.cycle.in_(kwargs['cycle']))
        return query, totals_class, totals_schema
Пример #10
0
def filter_election(query,
                    kwargs,
                    candidate_column,
                    cycle_column=None,
                    year_column=None):
    if not kwargs['office']:
        return query
    if isinstance(kwargs['cycle'], list):
        if len(kwargs['cycle']) != 1:
            raise exceptions.ApiError(
                'Must include exactly one argument "cycle"',
                status_code=422,
            )
        kwargs['cycle'] = kwargs['cycle'][0]
    utils.check_election_arguments(kwargs)
    query = query.join(
        models.CandidateHistory,
        candidate_column == models.CandidateHistory.candidate_id,
    ).filter(
        models.CandidateHistory.two_year_period == kwargs['cycle'],
        models.CandidateHistory.office == kwargs['office'][0].upper(),
    )
    if cycle_column:
        query = query.filter(cycle_column == kwargs['cycle'])
    elif year_column:
        query = query.filter(
            year_column.in_(kwargs['cycle'], kwargs['cycle'] - 1))
    else:
        raise ValueError('Must provide `cycle_column` or `year_column`')
    if kwargs['state']:
        query = query.filter(models.CandidateHistory.state == kwargs['state'])
    if kwargs['district']:
        query = query.filter(
            models.CandidateHistory.district == kwargs['district'])
    return query
Пример #11
0
def check_cap(kwargs, cap):
    if cap:
        if not kwargs.get('per_page') or kwargs['per_page'] > cap:
            raise exceptions.ApiError(
                'Parameter "per_page" must be between 1 and {}'.format(cap),
                status_code=422,
            )
Пример #12
0
def check_cap(kwargs, cap):
    if cap:
        if not kwargs.get('per_page'):
            raise exceptions.ApiError(
                'Parameter "per_page" must be > 0'.format(cap),
                status_code=422,
            )
Пример #13
0
def check_election_arguments(kwargs):
    for arg in office_args_required:
        if kwargs.get(arg) is None:
            raise exceptions.ApiError(
                'Required parameter "{0}" not found.'.format(arg),
                status_code=422,
            )
    conditional_args = office_args_map.get(kwargs['office'], [])
    for arg in conditional_args:
        if kwargs.get(arg) is None:
            raise exceptions.ApiError(
                'Must include argument "{0}" with office type "{1}"'.format(
                    arg,
                    kwargs['office'],
                ),
                status_code=422,
            )
Пример #14
0
def handle_exception(exception):
    wrapped = ResponseException(str(exception), ErrorCode.INTERNAL_ERROR,
                                type(exception))
    app.logger.error(
        'An API error occurred with the status code of {status} ({exception}).'
        .format(status=wrapped.status, exception=wrapped.wrappedException))
    raise exceptions.ApiError('Could not process the request',
                              status_code=http.client.NOT_FOUND)
Пример #15
0
 def __call__(self, value):
     if value.lstrip('-') not in self.values:
         raise exceptions.ApiError(
             'Cannot sort on value "{0}". Instead choose one of: "{1}"'.format(
                 value, '", "'.join(self.values)
             ),
             status_code=422,
         )
Пример #16
0
def get_cycle(kwargs):
    if isinstance(kwargs['cycle'], list):
        if len(kwargs['cycle']) != 1:
            raise exceptions.ApiError(
                'Must include exactly one argument "cycle"',
                status_code=422,
            )
        return kwargs['cycle'][0]
    return kwargs['cycle']
Пример #17
0
 def get(self, **kwargs):
     if len(kwargs['committee_id']) > 5:
         raise exceptions.ApiError(
             'Can only specify up to five values for "committee_id".',
             status_code=422,
         )
     if len(kwargs['committee_id']) > 1:
         query, count = self.join_committee_queries(kwargs)
         return utils.fetch_seek_page(query,
                                      kwargs,
                                      self.index_column,
                                      count=count)
     return super(ScheduleAView, self).get(**kwargs)
Пример #18
0
def handle_exception(exception):
    wrapped = ResponseException(str(exception), ErrorCode.INTERNAL_ERROR,
                                type(exception))

    logger.info(
        'An API error occurred with the status code of {status} ({exception}).'
        .format(status=wrapped.status, exception=wrapped.wrappedException))

    if is_retrievable_from_cache(wrapped.status, request.path):
        logger.info('Attempting to retrieving the cached request from S3...')

        # Retrieve the information needed to construct a URL for the S3 bucket
        # where the cached API responses live.
        formatted_url = utils.format_url(request.url)
        s3_bucket = utils.get_bucket()
        bucket_region = env.get_credential('region')
        cached_url = "http://s3-{0}.amazonaws.com/{1}/cached-calls/{2}".format(
            bucket_region, s3_bucket.name, formatted_url)

        # Attempt to retrieve the cached data from S3.
        cached_data = utils.get_cached_request(cached_url)

        # If the cached data was returned, we can return that to the client.
        # Otherwise, log the error and raise an API error.
        if cached_data is not None:
            logger.info('Successfully retrieved cached request from S3.')
            return cached_data
        else:
            logger.error(
                'An error occured while retrieving the cached file from S3.')
            raise exceptions.ApiError(
                'The requested URL could not be found.'.format(request.url),
                status_code=http.client.NOT_FOUND)
    else:
        raise exceptions.ApiError(
            'The requested URL could not be found.'.format(request.url),
            status_code=http.client.NOT_FOUND)
Пример #19
0
 def build_query(self, **kwargs):
     query = super(ScheduleH4View, self).build_query(**kwargs)
     query = query.options(sa.orm.joinedload(models.ScheduleH4.committee))
     if kwargs.get('sub_id'):
         query = query.filter_by(sub_id=int(kwargs.get('sub_id')))
     if kwargs.get('line_number'):
         # line number is a composite value of 'filing_form-line_number'
         if len(kwargs.get('line_number').split('-')) == 2:
             form, line_no = kwargs.get('line_number').split('-')
             query = query.filter_by(filing_form=form.upper())
             query = query.filter_by(line_number=line_no)
         else:
             raise exceptions.ApiError(
                 exceptions.LINE_NUMBER_ERROR,
                 status_code=400,
             )
     return query
Пример #20
0
 def post(self, path, filename=None, **kwargs):
     parts = request.path.split('/')
     parts.remove('download')
     path = '/'.join(parts)
     cached_file = get_cached_file(path, request.query_string, filename=filename)
     if cached_file:
         return {
             'status': 'complete',
             'url': cached_file,
         }
     resource = download.call_resource(path, request.query_string)
     if resource['count'] > MAX_RECORDS:
         raise exceptions.ApiError(
             'Cannot request downloads with more than {} records'.format(MAX_RECORDS),
             status_code=http.client.FORBIDDEN,
         )
     download.export_query.delay(path, request.query_string)
     return {'status': 'queued'}
Пример #21
0
 def build_query(self, committee_id=None, **kwargs):
     query = super().build_query(committee_id=committee_id, **kwargs)
     election_full = kwargs.get('election_full')
     if election_full and not (kwargs.get('candidate_id')
                               or kwargs.get('office')):
         raise exceptions.ApiError(
             'Must include "candidate_id" or "office" argument(s)',
             status_code=422,
         )
     cycle_column = (models.CandidateElection.cand_election_year
                     if election_full else self.model.cycle)
     query = filters.filter_election(query, kwargs, self.model.candidate_id,
                                     cycle_column)
     query = query.filter(
         cycle_column.in_(kwargs['cycle']) if kwargs.get('cycle') else True)
     if election_full:
         query = self.aggregate_cycles(query, cycle_column)
     return self.join_entity_names(query)
Пример #22
0
    def build_query(self, **kwargs):
        if kwargs.get('name'):
            kwargs['q'] = kwargs['name']

        query = super().build_query(**kwargs)

        if {'receipts', '-receipts'}.intersection(kwargs.get(
                'sort', [])) and 'q' not in kwargs:
            raise exceptions.ApiError(
                'Cannot sort on receipts when parameter "q" is not set',
                status_code=422,
            )

        if 'has_raised_funds' in kwargs:
            query = query.filter(
                models.Candidate.flags.has(
                    models.CandidateFlags.has_raised_funds ==
                    kwargs['has_raised_funds']))
        if 'federal_funds_flag' in kwargs:
            query = query.filter(
                models.Candidate.flags.has(
                    models.CandidateFlags.federal_funds_flag ==
                    kwargs['federal_funds_flag']))

        if kwargs.get('q'):
            query = query.join(
                models.CandidateSearch,
                models.Candidate.candidate_id == models.CandidateSearch.id,
            ).distinct()

        if kwargs.get('cycle'):
            query = query.filter(
                models.Candidate.cycles.overlap(kwargs['cycle']))
        if kwargs.get('election_year'):
            query = query.filter(
                models.Candidate.election_years.overlap(
                    kwargs['election_year']))

        return query
Пример #23
0
    def build_query(self, **kwargs):
        query = super().build_query(**kwargs)
        query = filters.filter_contributor_type(query, self.model.entity_type,
                                                kwargs)

        if kwargs.get('contributor_zip'):
            for value in kwargs['contributor_zip']:
                if re.search('^-?\d{5}$', value) is None:
                    raise exceptions.ApiError(
                        'Invalid Zip code. It must be 5 digits',
                        status_code=400,
                    )
            query = filters.filter_multi_start_with(
                query, kwargs, self.filter_multi_start_with_fields)

        if kwargs.get('sub_id'):
            query = query.filter_by(sub_id=int(kwargs.get('sub_id')))
        if kwargs.get('line_number'):
            if len(kwargs.get('line_number').split('-')) == 2:
                form, line_no = kwargs.get('line_number').split('-')
                query = query.filter_by(filing_form=form.upper())
                query = query.filter_by(line_number=line_no)
        return query
Пример #24
0
def _validate_natural(value):
    if value <= 0:
        raise exceptions.ApiError(
            'Must be greater than zero',
            status_code=422
        )
Пример #25
0
 def handle_error(self, error):
     message = error.messages
     status_code = getattr(error, 'status_code', 422)
     raise exceptions.ApiError(message, status_code)
Пример #26
0
    def build_query(self, **kwargs):
        if kwargs.get('name'):
            kwargs['q'] = kwargs['name']

        query = super().build_query(**kwargs)
        candidate_detail = models.Candidate

        if {'receipts', '-receipts'}.intersection(kwargs.get(
                'sort', [])) and 'q' not in kwargs:
            raise exceptions.ApiError(
                'Cannot sort on receipts when parameter "q" is not set',
                status_code=422,
            )

        if 'has_raised_funds' in kwargs:
            query = query.filter(
                candidate_detail.flags.has(
                    models.CandidateFlags.has_raised_funds ==
                    kwargs['has_raised_funds']))
        if 'federal_funds_flag' in kwargs:
            query = query.filter(
                candidate_detail.flags.has(
                    models.CandidateFlags.federal_funds_flag ==
                    kwargs['federal_funds_flag']))

        if kwargs.get('q'):
            query = query.join(
                models.CandidateSearch,
                candidate_detail.candidate_id == models.CandidateSearch.id,
            ).distinct()

        if kwargs.get('cycle'):
            query = query.filter(
                candidate_detail.cycles.overlap(kwargs['cycle']))
        if kwargs.get('election_year'):
            query = query.filter(
                candidate_detail.election_years.overlap(
                    kwargs['election_year']))
        if 'is_active_candidate' in kwargs and kwargs.get(
                'is_active_candidate'):
            # load active candidates only if True
            if kwargs.get('election_year'):
                query = query.filter(
                    sa.or_(
                        ~(candidate_detail.inactive_election_years.contains(
                            kwargs['election_year'])),
                        candidate_detail.inactive_election_years.is_(None),
                    ))
            else:
                query = query.filter(
                    candidate_detail.candidate_inactive == False)  # noqa
        elif 'is_active_candidate' in kwargs and not kwargs.get(
                'is_active_candidate'):
            # load inactive candidates only if False
            if kwargs.get('election_year'):
                query = query.filter(
                    candidate_detail.inactive_election_years.overlap(
                        kwargs['election_year']))
            else:
                query = query.filter(
                    candidate_detail.candidate_inactive == True  # noqa
                )
        else:
            # load all candidates
            pass
        return query
Пример #27
0
 def handle_error(self, error):
     logger.error(error)
     message = text_type(error)
     status_code = getattr(error, 'status_code', 400)
     payload = getattr(error, 'data', {})
     raise exceptions.ApiError(message, status_code, payload)
Пример #28
0
 def __call__(self, value):
     if value.lstrip('-') not in self.values:
         raise exceptions.ApiError(
             'Cannot sort on value "{0}"'.format(value), status_code=422)
Пример #29
0
 def handle_error(self, error, req, schema, status_code, error_headers):
     message = error.messages
     status_code = getattr(error, 'status_code', 422)
     raise exceptions.ApiError(message, status_code)
Пример #30
0
def _validate_natural(value):
    if value < 0:
        raise exceptions.ApiError('Must be a natural number', status_code=422)