예제 #1
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)
예제 #2
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)
예제 #3
0
def list_resources():
    # http://en.wikipedia.org/wiki/HATEOAS
    children = []
    for rule in current_app.url_map.iter_rules():
        # e.g, `api.list_resources`
        if rule.endpoint.startswith(core_bp.name):

            # don't add current endpoint as child
            if rule.endpoint != request.url_rule.endpoint:
                children.append(rule_link(rule))

    return create_response({'_links': {
                            'self': rule_link(request.url_rule),
                            'child': children}})
예제 #4
0
def categorize_birth_year():
    data = request.json
    if data:
        values = data.get('values')
        years = [value for value in values if value.get('label') == 'born']
        if years:
            year = years[0]['value']
            try:
                year = int(year)
            except BaseException:
                year = int(year.split(' ')[0])
            age = datetime.datetime.utcnow().year - year
            if age <= 13:
                category = 'child'
            elif 14 < age < 18:
                category = 'youth'
            elif age >= 18:
                category = 'adult'
            else:
                abort(400)

            return create_response({
                'age': age,
                'age_category': category,
                '_links': {
                    'self': rule_link(request.url_rule)
                }
            })
    abort(400)
예제 #5
0
def nominatum():
    """ Look up a given place name on OSM via
        http://wiki.openstreetmap.org/wiki/Nominatim

        Nominatim does not do any fuzzy matching, so
        not so helpful for user-provided spellings.
    """
    data = request.values

    if data:
        payload = {'format': 'json'}
        payload['q'] = data['query']
        country = data.get('country')
        if country:
            payload['countrycodes'] = country

        result = requests.get(NOMINATUM_URL, params=payload)
        results = result.json()

        matches = list()
        for match in results:
            if match['type'] == 'village':
                if match['class'] == 'place':
                    matches.append(match)
        return create_response({
            'matches': matches,
            '_links': {
                'self': rule_link(request.url_rule)
            }
        })

    abort(400)
예제 #6
0
def calculate_last_menses():
    data = request.json
    if data:
        values = data.get('values')
        weeks = [
            value for value in values
            if value.get('label') == 'weeks_since_last_menses'
        ]
        if weeks:
            weeks = weeks[0]['value']
            try:
                weeks = int(weeks)
            except BaseException:
                weeks = int(weeks.split(' ')[0])
            days_since_menses = weeks * 7
            last_menses_date = datetime.datetime.utcnow() - \
                datetime.timedelta(days=days_since_menses)
            expected_delivery_date = last_menses_date + \
                datetime.timedelta(days=280)
            return create_response({
                'days_since_menses': days_since_menses,
                'last_menses': last_menses_date,
                'expected_delivery_date': expected_delivery_date,
                '_links': {
                    'self': rule_link(request.url_rule)
                }
            })
    abort(400)
예제 #7
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)
예제 #8
0
def shipment_received():
    """ Called when shipment has been received by end user """
    shipment = _update_shipment_status(request, SHIPMENT_RECEIVED_FIELDS)
    if shipment:
        return create_response({
            'shipment': shipment,
            '_links': {
                'self': rule_link(request.url_rule)
            }
        })
    abort(400)
예제 #9
0
def notify_death():
    data = request.json
    if data:
        data.update({'type': 'death'})
        g.db.save_doc(data)
        docid = data['_id']

        return create_response({
            'id': docid,
            '_links': {
                'self': rule_link(request.url_rule)
            }
        })
    abort(400)
예제 #10
0
def update_shipment():
    """ Called when shipment status is updated by end user """
    shipment = _update_shipment_status(request, SHIPMENT_UPDATE_FIELDS)

    # TODO if we have a revised date estimate,
    # then schedule flow for after revised date

    if shipment:
        return create_response({
            'shipment': shipment,
            '_links': {
                'self': rule_link(request.url_rule)
            }
        })
    abort(400)
예제 #11
0
def expected_shipments_for_contact():
    if request.json is not None:
        data = request.json
    else:
        data = request.values

    if data:
        phone = _format_phone(data.get('phone'))
        if phone:
            shipments_doc = get_or_create_shipments_doc(phone)
            shipments_doc = g.db.open_doc('shipments-%s' % phone)
            shipments = shipments_doc.get('shipments')
            if shipments:
                return create_response({
                    'shipment': shipments.pop(),
                    '_links': {
                        'self': rule_link(request.url_rule)
                    }
                })
    abort(400)
예제 #12
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)