Esempio n. 1
0
def nomenklatura_update_entity_attributes():
    data = request.values

    if data:
        if 'entity' not in data:
            raise exceptions.APIError(message='Missing field: entity',
                                      field='entity',
                                      resource=rule_link(request.url_rule))
        if 'attributes' not in data:
            raise exceptions.APIError(message='Missing field: attributes',
                                      field='attributes',
                                      resource=rule_link(request.url_rule))

        # rapidpro doesnt send json post data. instead we'll have a form field
        # with a json string inside rather than a dict from request.json
        if not isinstance(data['attributes'], dict):
            try:
                # request.values is a CombinedMultiDict, so convert to dict
                data = data.to_dict()
                data['attributes'] = json.loads(data['attributes'])
            except JSONDecodeError:
                # we didn't get json or anything json-like, so abort
                abort(400)

        payload = {'format': 'json'}
        payload['api_key'] = NOMENKLATURA_API_KEY

        result = requests.get(_url_for_entity(data['entity']), json=payload)
        results = result.json()

        # update endpoint requires name
        payload['name'] = results['name']
        updated_attributes = results['attributes']
        updated_attributes.update(data['attributes'])

        # attribute values for nomenklatura must be strings (no ints!)
        # as nomenklatura uses postgres' hstore to store attribute kv pairs
        # TODO this smells dodgy
        payload['attributes'] = {
            str(k): str(v)
            for k, v in updated_attributes.items()
        }

        result = requests.post(_url_for_entity(data['entity']),
                               headers=NOMENKLATURA_HEADERS,
                               json=payload)

        # TODO pass on a better error message from nomenklatura if non200
        if result.status_code == requests.codes.ok:
            return create_response({
                'entity': result.json(),
                '_links': {
                    'self': rule_link(request.url_rule)
                }
            })
    abort(400)
Esempio n. 2
0
def serialize_data_to_response(data=None, serializer_slug=None):
    """ given an API response serializer/format type, serializes
        data (a mapping like dict) into the given format.
        serializer_slug can be either the format name (e.g., 'json')
        or the response content type describing that format
        (e.g., 'application/json').
        Returns a flask Response object containing serialized data
        and the corresponding content_type
    """
    if serializer_slug is None:
        serializer_slug = registry.default.content_type

    try:
        assert (serializer_slug in SERIALIZER_FORMATS
                or serializer_slug in SERIALIZER_TYPES)
    except AssertionError:
        raise exceptions.APIError(code=422,
                                  message='invalid serialization format',
                                  field=serializer_slug,
                                  resource='serializer')

    if serializer_slug in SERIALIZER_FORMATS:
        return registry.get(serializer_slug).to_response(data)
    else:
        return registry.get(SERIALIZER_TYPES_MAP[serializer_slug])\
            .to_response(data)
Esempio n. 3
0
def nomenklatura_create_entity():
    data = request.values

    if data:
        if 'name' not in data:
            raise exceptions.APIError(message='Missing field: name',
                                      field='name',
                                      resource=rule_link(request.url_rule))

        if 'attributes' in data:
            # rapidpro doesnt send json post data. instead we'll have a form field
            # with a json string inside rather than a dict from request.json
            if not isinstance(data['attributes'], dict):
                try:
                    # request.values is a CombinedMultiDict, so convert to dict
                    data = data.to_dict()
                    data['attributes'] = json.loads(data['attributes'])
                except ValueError:
                    # we didn't get json or anything json-like, so abort
                    abort(400)

        payload = {'format': 'json'}
        payload['api_key'] = NOMENKLATURA_API_KEY
        # nomenklatura complains if we don't include `attributes`
        payload['attributes'] = {}

        # attribute values for nomenklatura must be strings (no ints!)
        # as nomenklatura uses postgres' hstore to store attribute kv pairs
        # TODO this smells dodgy
        if 'attributes' in data:
            payload['attributes'] = {
                str(k): str(v)
                for k, v in data['attributes'].items()
            }

        payload['dataset'] = data['dataset']
        payload['name'] = data['name']
        if 'description' in data:
            payload['description'] = data['description']

        result = requests.post(NOMENKLATURA_URL + 'entities',
                               headers=NOMENKLATURA_HEADERS,
                               json=payload)

        if result.json().get('errors') is not None:
            abort(
                make_response(jsonify(message=result.json().get('errors')),
                              400))

        # TODO pass on a better error message from nomenklatura if non200
        if result.status_code == requests.codes.ok:
            return create_response({
                'entity': result.json(),
                '_links': {
                    'self': rule_link(request.url_rule)
                }
            })
    abort(400)
Esempio n. 4
0
def nomenklatura_retrieve_entity_attributes():

    data = request.values

    if data:
        if 'entity' not in data:
            raise exceptions.APIError(message='Missing field: entity',
                                      field='entity',
                                      resource=rule_link(request.url_rule))

        payload = {'format': 'json'}
        payload['api_key'] = NOMENKLATURA_API_KEY

        result = requests.get(_url_for_entity(data['entity']), json=payload)
        results = result.json()

        if 'attribute' in data:
            query = data['attribute']
            if (results.get('attributes')
                    is not None) and (query in results['attributes'].keys()):
                return create_response({
                    'entity': results,
                    query: results['attributes'][query],
                    '_links': {
                        'self': rule_link(request.url_rule)
                    }
                })
            else:
                return create_response({
                    'entity': results,
                    query: None,
                    '_links': {
                        'self': rule_link(request.url_rule)
                    }
                })

        return create_response({
            'entity': results,
            '_links': {
                'self': rule_link(request.url_rule)
            }
        })

    abort(400)
Esempio n. 5
0
def nomenklatura():
    # TODO better logging
    log = dict({'timestamp': datetime.datetime.utcnow().isoformat()})

    data = request.values

    if data:
        try:
            # `data` is likely werkzeug's ImmutableDict inside a
            # CombinedMultiDict so try to cast with `to_dict`
            log.update({'request_data': data.to_dict()})
        except AttributeError:
            # `data` can also be a vanilla dict
            log.update({'request_data': data})

        if 'query' not in data:
            raise exceptions.APIError(message='Missing field: query',
                                      field='query',
                                      resource=rule_link(request.url_rule))
        if 'dataset' not in data:
            raise exceptions.APIError(message='Missing field: dataset',
                                      field='dataset',
                                      resource=rule_link(request.url_rule))

        payload = {'format': 'json'}

        # titlecase the query for better chance of exact match
        query = data['query'].title()
        payload['query'] = query
        payload['api_key'] = NOMENKLATURA_API_KEY

        log.update({'nomenklatura_payload': payload})
        result = requests.get(_url_for_dataset(data['dataset']),
                              params=payload)
        try:
            results = result.json()
        except JSONDecodeError:
            results = []

        matches = list()
        if len(results) > 0:
            for match in results['result']:
                if match['match'] is True:
                    matches.append(match)
                else:
                    if match['score'] >= 50:
                        matches.append(match)

        log.update({'nomenklatura_response': results})
        log.update({'matches': matches})
        g.db.save_doc(log)

        if len(matches) < 1:
            # attempt to match each token of query
            match = _try_harder(data, query, 1)
            if match is None:
                # attempt to match each bigram of query
                match = _try_harder(data, query, 2)
                if match is None:
                    return create_response({
                        'message':
                        _localized_fail(data.get('lang', 'eng')) %
                        _format_type(data['dataset']),
                        'match':
                        None,
                        '_links': {
                            'self': rule_link(request.url_rule)
                        }
                    })
            if match is not None:
                # dataset_name = match[0]['type'][0]['name']
                return create_response({
                    'message': _localized_success(data.get('lang', 'eng')) % {
                        'loc_type': _format_type(data['dataset']),
                        'match': match[0]['name']
                    },
                    'match': match[0]['name'],
                    '_links': {
                        'self': rule_link(request.url_rule)
                    }
                })

        else:
            # dataset_name = matches[0]['type'][0]['name']
            return create_response({
                'message': _localized_success(data.get('lang', 'eng')) % {
                    'loc_type': _format_type(data['dataset']),
                    'match': matches[0]['name']
                },
                'match': matches[0]['name'],
                '_links': {
                    'self': rule_link(request.url_rule)
                }
            })

    abort(400)