Example #1
0
File: api.py Project: kedlaya/lmfdb
def api_query(table, id = None):
    #if censored_table(table):
    #    return flask.abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = ['id'] + fields.split(DELIM)
    else:
        fields = 1.1

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            flask.abort(404)
        else:
            flash_error("offset %s too large, please refine your query.", offset)
            return flask.redirect(url_for(".api_query", table=table))

    # preparing the actual database query q
    try:
        coll = getattr(db, table)
    except AttributeError:
        if format != "html":
            flask.abort(404)
        else:
            flash_error("table %s does not exist", table)
            return flask.redirect(url_for(".index"))
    q = {}

    # if id is set, just go and get it, ignore query parameeters
    if id is not None:
        if offset:
            return flask.abort(404)
        single_object = True
        api_logger.info("API query: id = '%s', fields = '%s'" % (id, fields))
        if re.match(r'^\d+$', id):
            id = int(id)
        data = coll.lucky({'id':id}, projection=fields)
        data = [data] if data else []
    else:
        single_object = False

        for qkey, qval in request.args.iteritems():
            from ast import literal_eval
            try:
                if qkey.startswith("_"):
                    continue
                elif qval.startswith("s"):
                    qval = qval[1:]
                elif qval.startswith("i"):
                    qval = int(qval[1:])
                elif qval.startswith("f"):
                    qval = float(qval[1:])
                elif qval.startswith("o"):
                    qval = ObjectId(qval[1:])
                elif qval.startswith("ls"):      # indicator, that it might be a list of strings
                    qval = qval[2].split(DELIM)
                elif qval.startswith("li"):
                    print qval
                    qval = [int(_) for _ in qval[2:].split(DELIM)]
                    print qval
                elif qval.startswith("lf"):
                    qval = [float(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("py"):     # literal evaluation
                    qval = literal_eval(qval[2:])
                elif qval.startswith("cs"):     # containing string in list
                    qval = { "$contains" : [qval[2:]] }
                elif qval.startswith("ci"):
                    qval = { "$contains" : [int(qval[2:])] }
                elif qval.startswith("cf"):
                    qval = { "contains" : [float(qval[2:])] }
                elif qval.startswith("cpy"):
                    qval = { "$contains" : [literal_eval(qval[3:])] }
            except:
                # no suitable conversion for the value, keep it as string
                pass

            # update the query
            q[qkey] = qval

        # assure that one of the keys of the query is indexed
        # however, this doesn't assure that the query will be fast... 
        #if q != {} and len(set(q.keys()).intersection(collection_indexed_keys(coll))) == 0:
        #    flash_error("no key in the query %s is indexed.", q)
        #    return flask.redirect(url_for(".api_query", table=table))

        # sort = [('fieldname1', 1 (ascending) or -1 (descending)), ...]
        if sortby is not None:
            sort = []
            for key in sortby:
                if key.startswith("-"):
                    sort.append((key[1:], -1))
                else:
                    sort.append((key, 1))
        else:
            sort = None

        # executing the query "q" and replacing the _id in the result list
        api_logger.info("API query: q = '%s', fields = '%s', sort = '%s', offset = %s" % (q, fields, sort, offset))
        try:
            data = list(coll.search(q, projection=fields, sort=sort, limit=100, offset=offset))
        except QueryCanceledError:
            flash_error("Query %s exceeded time limit.", q)
            return flask.redirect(url_for(".api_query", table=table))
        except KeyError, err:
            flash_error("No key %s in table %s", err, table)
            return flask.redirect(url_for(".api_query", table=table))
Example #2
0
def api_query(table, id = None):
    #if censored_table(table):
    #    return abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = ['id'] + fields.split(DELIM)
    else:
        fields = 3

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            return abort(404)
        else:
            flash_error("offset %s too large, please refine your query.", offset)
            return redirect(url_for(".api_query", table=table))

    # preparing the actual database query q
    try:
        coll = getattr(db, table)
    except AttributeError:
        if format != "html":
            return abort(404)
        else:
            flash_error("table %s does not exist", table)
            return redirect(url_for(".index"))
    q = {}

    # if id is set, just go and get it, ignore query parameeters
    if id is not None:
        if offset:
            return abort(404)
        single_object = True
        api_logger.info("API query: id = '%s', fields = '%s'" % (id, fields))
        if re.match(r'^\d+$', id):
            id = int(id)
        else:
            return abort(404, "id '%s' must be an integer" % id)
        data = coll.lucky({'id':id}, projection=fields)
        data = [data] if data else []
    else:
        single_object = False

        for qkey, qval in request.args.items():
            from ast import literal_eval
            try:
                if qkey.startswith("_"):
                    continue
                elif qval.startswith("s"):
                    qval = qval[1:]
                elif qval.startswith("i"):
                    qval = int(qval[1:])
                elif qval.startswith("f"):
                    qval = float(qval[1:])
                elif qval.startswith("ls"):      # indicator, that it might be a list of strings
                    qval = qval[2].split(DELIM)
                elif qval.startswith("li"):
                    qval = [int(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("lf"):
                    qval = [float(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("py"):     # literal evaluation
                    qval = literal_eval(qval[2:])
                elif qval.startswith("cs"):     # containing string in list
                    qval = { "$contains" : [qval[2:]] }
                elif qval.startswith("ci"):
                    qval = { "$contains" : [int(qval[2:])] }
                elif qval.startswith("cf"):
                    qval = { "contains" : [float(qval[2:])] }
                elif qval.startswith("cpy"):
                    qval = { "$contains" : [literal_eval(qval[3:])] }
            except:
                # no suitable conversion for the value, keep it as string
                pass

            # update the query
            q[qkey] = qval

        # assure that one of the keys of the query is indexed
        # however, this doesn't assure that the query will be fast... 
        #if q != {} and len(set(q.keys()).intersection(collection_indexed_keys(coll))) == 0:
        #    flash_error("no key in the query %s is indexed.", q)
        #    return redirect(url_for(".api_query", table=table))

        # sort = [('fieldname1', 1 (ascending) or -1 (descending)), ...]
        if sortby is not None:
            sort = []
            for key in sortby:
                if key.startswith("-"):
                    sort.append((key[1:], -1))
                else:
                    sort.append((key, 1))
        else:
            sort = None

        # executing the query "q" and replacing the _id in the result list
        # So as not to preserve backwards compatibility (see test_api_usage() test)
        if table=='ec_curvedata':
            for oldkey, newkey in zip(['label', 'iso', 'number'], ['Clabel', 'Ciso', 'Cnumber']):
                if oldkey in q:
                    q[newkey] = q[oldkey]
                    q.pop(oldkey)
        try:
            data = list(coll.search(q, projection=fields, sort=sort, limit=100, offset=offset))
        except QueryCanceledError:
            flash_error("Query %s exceeded time limit.", q)
            return redirect(url_for(".api_query", table=table))
        except KeyError as err:
            flash_error("No key %s in table %s", err, table)
            return redirect(url_for(".api_query", table=table))
        except ValueError as err:
            flash_error(str(err))
            return redirect(url_for(".api_query", table=table))

    if single_object and not data:
        if format != 'html':
            return abort(404)
        else:
            flash_error("no document with id %s found in table %s.", id, table)
            return redirect(url_for(".api_query", table=table))

    # fixup data for display and json/yaml encoding
    if 'bytea' in coll.col_type.values():
        for row in data:
            for key, val in row.items():
                if type(val) == buffer:
                    row[key] = "[binary data]"
        #data = [ dict([ (key, val if coll.col_type[key] != 'bytea' else "binary data") for key, val in row.items() ]) for row in data]
    data = Json.prep(data)

    # preparing the datastructure
    start = offset
    next_req = dict(request.args)
    next_req["_offset"] = offset
    url_args = next_req.copy()
    query = url_for(".api_query", table=table, **next_req)
    offset += len(data)
    next_req["_offset"] = offset
    next = url_for(".api_query", table=table, **next_req)

    # the collected result
    data = {
        "table": table,
        "timestamp": datetime.utcnow().isoformat(),
        "data": data,
        "start": start,
        "offset": offset,
        "query": query,
        "next": next,
        "rec_id": 'id' if coll._label_col is None else coll._label_col,
    }

    if format.lower() == "json":
        #return flask.jsonify(**data) # can't handle binary data
        if PY3:
            return current_app.response_class(json.dumps(data, indent=2), mimetype='application/json')
        else:
            return current_app.response_class(json.dumps(data, encoding='ISO-8859-1', indent=2), mimetype='application/json')
    elif format.lower() == "yaml":
        y = yaml.dump(data,
                      default_flow_style=False,
                      canonical=False,
                      allow_unicode=True)
        return Response(y, mimetype='text/plain')
    else:
        # sort displayed records by key (as jsonify and yaml_dump do)
        data["pretty"] = pretty_document
        location = table
        title = "API - " + location
        bc = [("API", url_for(".index")), (table,)]
        query_unquote = unquote(data["query"])
        return render_template("collection.html",
                               title=title,
                               single_object=single_object,
                               query_unquote = query_unquote,
                               url_args = url_args,
                               bread=bc,
                               **data)
Example #3
0
def api_query(db, collection, id=None):
    if censored_db(db) or censored_collection(collection):
        return flask.abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = fields.split(DELIM)

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            flask.abort(404)
        else:
            flash_error("offset %s too large, please refine your query.",
                        offset)
            return flask.redirect(
                url_for(".api_query", db=db, collection=collection))

    # preparing the actual database query q
    C = base.getDBConnection()
    q = {}

    # if id is set, just go and get it, ignore query parameeters
    if id is not None:
        if offset:
            return flask.abort(404)
        single_object = True
        data = []
        api_logger.info("API query: id = '%s', fields = '%s'" % (id, fields))
        # if id looks like an ObjectId, assume it is and try to find it
        if len(id) == 24 and re.match('[0-9a-f]+$', id.strip()):
            data = C[db][collection].find_one({'_id': ObjectId(id)},
                                              projection=fields)
        if not data:
            data = C[db][collection].find_one({'_id': id}, projection=fields)
        data = [data] if data else []
    else:
        single_object = False

        for qkey, qval in request.args.iteritems():
            from ast import literal_eval
            try:
                if qkey.startswith("_"):
                    continue
                elif qval.startswith("s"):
                    qval = qval[1:]
                elif qval.startswith("i"):
                    qval = int(qval[1:])
                elif qval.startswith("f"):
                    qval = float(qval[1:])
                elif qval.startswith("o"):
                    qval = ObjectId(qval[1:])
                elif qval.startswith(
                        "ls"):  # indicator, that it might be a list of strings
                    qval = qval[2:].split(DELIM)
                elif qval.startswith("li"):
                    qval = [int(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("lf"):
                    qval = [float(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("py"):  # literal evaluation
                    qval = literal_eval(qval[2:])
                elif qval.startswith("cs"):  # containing string in list
                    qval = {"$in": [qval[2:]]}
                elif qval.startswith("ci"):
                    qval = {"$in": [int(qval[2:])]}
                elif qval.startswith("cf"):
                    qval = {"$in": [float(qval[2:])]}
                elif qval.startswith("cpy"):
                    qval = {"$in": [literal_eval(qval[3:])]}
            except:
                # no suitable conversion for the value, keep it as string
                pass

            # update the query
            q[qkey] = qval

        # assure that one of the keys of the query is indexed
        # however, this doesn't assure that the query will be fast...
        if q != {} and len(
                set(q.keys()).intersection(
                    collection_indexed_keys(C[db][collection]))) == 0:
            flash_error("no key in the query %s is indexed.", q)
            return flask.redirect(
                url_for(".api_query", db=db, collection=collection))

        # sort = [('fieldname1', ASC/DESC), ...]
        if sortby is not None:
            sort = []
            for key in sortby:
                if key.startswith("-"):
                    sort.append((key[1:], DESC))
                else:
                    sort.append((key, ASC))
        else:
            sort = None

        # executing the query "q" and replacing the _id in the result list
        api_logger.info(
            "API query: q = '%s', fields = '%s', sort = '%s', offset = %s" %
            (q, fields, sort, offset))
        from pymongo.errors import ExecutionTimeout
        try:
            data = list(C[db][collection].find(
                q, projection=fields,
                sort=sort).skip(offset).limit(100).max_time_ms(10000))
        except ExecutionTimeout:
            flash_error("Query %s exceeded time limit.", q)
            return flask.redirect(
                url_for(".api_query", db=db, collection=collection))

    if single_object and not data:
        if format != 'html':
            flask.abort(404)
        else:
            flash_error("no document with id %s found in collection %s.%s.",
                        id, db, collection)
            return flask.redirect(
                url_for(".api_query", db=db, collection=collection))

    # fixup object ids for display and json/yaml encoding
    for document in data:
        oids_to_strings(document)

    # preparing the datastructure
    start = offset
    next_req = dict(request.args)
    next_req["_offset"] = offset
    url_args = next_req.copy()
    query = url_for(".api_query", db=db, collection=collection, **next_req)
    offset += len(data)
    next_req["_offset"] = offset
    next = url_for(".api_query", db=db, collection=collection, **next_req)

    # the collected result
    data = {
        "database": db,
        "collection": collection,
        "timestamp": datetime.utcnow().isoformat(),
        "data": data,
        "start": start,
        "offset": offset,
        "query": query,
        "next": next
    }

    if format.lower() == "json":
        #return flask.jsonify(**data) # can't handle binary data
        return current_app.response_class(json.dumps(
            data, encoding='ISO-8859-1', indent=2, default=json_util.default),
                                          mimetype='application/json')
    elif format.lower() == "yaml":
        y = yaml.dump(data,
                      default_flow_style=False,
                      canonical=False,
                      allow_unicode=True)
        return flask.Response(y, mimetype='text/plain')
    else:
        # sort displayed records by key (as jsonify and yaml_dump do)
        data["pretty"] = pretty_document
        location = "%s/%s" % (db, collection)
        title = "API - " + location
        bc = [("API", url_for(".index")), (location, query)]
        query_unquote = urllib2.unquote(data["query"])
        return render_template("collection.html",
                               title=title,
                               single_object=single_object,
                               query_unquote=query_unquote,
                               url_args=url_args,
                               oid_strip=oid_strip,
                               bread=bc,
                               **data)
Example #4
0
def api_query(table, id=None):
    #if censored_table(table):
    #    return flask.abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = ['id'] + fields.split(DELIM)
    else:
        fields = 3

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            flask.abort(404)
        else:
            flash_error("offset %s too large, please refine your query.",
                        offset)
            return flask.redirect(url_for(".api_query", table=table))

    # preparing the actual database query q
    try:
        coll = getattr(db, table)
    except AttributeError:
        if format != "html":
            flask.abort(404)
        else:
            flash_error("table %s does not exist", table)
            return flask.redirect(url_for(".index"))
    q = {}

    # if id is set, just go and get it, ignore query parameeters
    if id is not None:
        if offset:
            return flask.abort(404)
        single_object = True
        api_logger.info("API query: id = '%s', fields = '%s'" % (id, fields))
        if re.match(r'^\d+$', id):
            id = int(id)
        data = coll.lucky({'id': id}, projection=fields)
        data = [data] if data else []
    else:
        single_object = False

        for qkey, qval in request.args.iteritems():
            from ast import literal_eval
            try:
                if qkey.startswith("_"):
                    continue
                elif qval.startswith("s"):
                    qval = qval[1:]
                elif qval.startswith("i"):
                    qval = int(qval[1:])
                elif qval.startswith("f"):
                    qval = float(qval[1:])
                elif qval.startswith(
                        "ls"):  # indicator, that it might be a list of strings
                    qval = qval[2].split(DELIM)
                elif qval.startswith("li"):
                    print qval
                    qval = [int(_) for _ in qval[2:].split(DELIM)]
                    print qval
                elif qval.startswith("lf"):
                    qval = [float(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("py"):  # literal evaluation
                    qval = literal_eval(qval[2:])
                elif qval.startswith("cs"):  # containing string in list
                    qval = {"$contains": [qval[2:]]}
                elif qval.startswith("ci"):
                    qval = {"$contains": [int(qval[2:])]}
                elif qval.startswith("cf"):
                    qval = {"contains": [float(qval[2:])]}
                elif qval.startswith("cpy"):
                    qval = {"$contains": [literal_eval(qval[3:])]}
            except:
                # no suitable conversion for the value, keep it as string
                pass

            # update the query
            q[qkey] = qval

        # assure that one of the keys of the query is indexed
        # however, this doesn't assure that the query will be fast...
        #if q != {} and len(set(q.keys()).intersection(collection_indexed_keys(coll))) == 0:
        #    flash_error("no key in the query %s is indexed.", q)
        #    return flask.redirect(url_for(".api_query", table=table))

        # sort = [('fieldname1', 1 (ascending) or -1 (descending)), ...]
        if sortby is not None:
            sort = []
            for key in sortby:
                if key.startswith("-"):
                    sort.append((key[1:], -1))
                else:
                    sort.append((key, 1))
        else:
            sort = None

        # executing the query "q" and replacing the _id in the result list
        api_logger.info(
            "API query: q = '%s', fields = '%s', sort = '%s', offset = %s" %
            (q, fields, sort, offset))
        try:
            data = list(
                coll.search(q,
                            projection=fields,
                            sort=sort,
                            limit=100,
                            offset=offset))
        except QueryCanceledError:
            flash_error("Query %s exceeded time limit.", q)
            return flask.redirect(url_for(".api_query", table=table))
        except KeyError, err:
            flash_error("No key %s in table %s", err, table)
            return flask.redirect(url_for(".api_query", table=table))
Example #5
0
def api_query(db, collection, id = None):
    if censored_db(db) or censored_collection(collection):
        return flask.abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = fields.split(DELIM)

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            flask.abort(404)
        else:
            flash_error("offset %s too large, please refine your query.", offset)
            return flask.redirect(url_for(".api_query", db=db, collection=collection))

    # preparing the actual database query q
    C = base.getDBConnection()
    q = {}

    # if id is set, just go and get it, ignore query parameeters
    if id is not None:
        if offset:
            return flask.abort(404)
        single_object = True
        data = []
        api_logger.info("API query: id = '%s', fields = '%s'" % (id, fields))
        # if id looks like an ObjectId, assume it is and try to find it
        if len(id) == 24 and re.match('[0-9a-f]+$', id.strip()):
            data = C[db][collection].find_one({'_id':ObjectId(id)},projection=fields)
        if not data:
            data = C[db][collection].find_one({'_id':id},projection=fields)
        data = [data] if data else []
    else:
        single_object = False

        for qkey, qval in request.args.iteritems():
            from ast import literal_eval
            try:
                if qkey.startswith("_"):
                    continue
                elif qval.startswith("s"):
                    qval = qval[1:]
                elif qval.startswith("i"):
                    qval = int(qval[1:])
                elif qval.startswith("f"):
                    qval = float(qval[1:])
                elif qval.startswith("o"):
                    qval = ObjectId(qval[1:])
                elif qval.startswith("ls"):      # indicator, that it might be a list of strings
                    qval = qval[2:].split(DELIM)
                elif qval.startswith("li"):
                    qval = [int(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("lf"):
                    qval = [float(_) for _ in qval[2:].split(DELIM)]
                elif qval.startswith("py"):     # literal evaluation
                    qval = literal_eval(qval[2:])
                elif qval.startswith("cs"):     # containing string in list
                    qval = { "$in" : [qval[2:]] }
                elif qval.startswith("ci"):
                    qval = { "$in" : [int(qval[2:])] }
                elif qval.startswith("cf"):
                    qval = { "$in" : [float(qval[2:])] }
                elif qval.startswith("cpy"):
                    qval = { "$in" : [literal_eval(qval[3:])] }
            except:
                # no suitable conversion for the value, keep it as string
                pass

            # update the query
            q[qkey] = qval

        # assure that one of the keys of the query is indexed
        # however, this doesn't assure that the query will be fast... 
        if q != {} and len(set(q.keys()).intersection(collection_indexed_keys(C[db][collection]))) == 0:
            flash_error("no key in the query %s is indexed.", q)
            return flask.redirect(url_for(".api_query", db=db, collection=collection))



        # sort = [('fieldname1', ASC/DESC), ...]
        if sortby is not None:
            sort = []
            for key in sortby:
                if key.startswith("-"):
                    sort.append((key[1:], DESC))
                else:
                    sort.append((key, ASC))
        else:
            sort = None

        # executing the query "q" and replacing the _id in the result list
        api_logger.info("API query: q = '%s', fields = '%s', sort = '%s', offset = %s" % (q, fields, sort, offset))
        from pymongo.errors import ExecutionTimeout
        try:
            data = list(C[db][collection].find(q, projection = fields, sort=sort).skip(offset).limit(100).max_time_ms(10000))
        except ExecutionTimeout:
            flash_error("Query %s exceeded time limit.", q)
            return flask.redirect(url_for(".api_query", db=db, collection=collection))
    
    if single_object and not data:
        if format != 'html':
            flask.abort(404)
        else:
            flash_error("no document with id %s found in collection %s.%s.", id, db, collection)
            return flask.redirect(url_for(".api_query", db=db, collection=collection))
    
    # fixup object ids for display and json/yaml encoding
    for document in data:
        oids_to_strings(document)

    # preparing the datastructure
    start = offset
    next_req = dict(request.args)
    next_req["_offset"] = offset
    url_args = next_req.copy()
    query = url_for(".api_query", db=db, collection=collection, **next_req)
    offset += len(data)
    next_req["_offset"] = offset
    next = url_for(".api_query", db=db, collection=collection, **next_req)

    # the collected result
    data = {
        "database": db,
        "collection": collection,
        "timestamp": datetime.utcnow().isoformat(),
        "data": data,
        "start": start,
        "offset": offset,
        "query": query,
        "next": next
    }

    if format.lower() == "json":
        #return flask.jsonify(**data) # can't handle binary data
        return current_app.response_class(json.dumps(data, encoding='ISO-8859-1', indent=2, default=json_util.default), mimetype='application/json')
    elif format.lower() == "yaml":
        y = yaml.dump(data,
                      default_flow_style=False,
                      canonical=False,
                      allow_unicode=True)
        return flask.Response(y, mimetype='text/plain')
    else:
        # sort displayed records by key (as jsonify and yaml_dump do)
        data["pretty"] = pretty_document
        location = "%s/%s" % (db, collection)
        title = "API - " + location
        bc = [("API", url_for(".index")), (location, query)]
        query_unquote = urllib2.unquote(data["query"])
        return render_template("collection.html",
                               title=title,
                               single_object=single_object,
                               query_unquote = query_unquote,
                               url_args = url_args, oid_strip = oid_strip,
                               bread=bc,
                               **data)
Example #6
0
def api_query(db, collection, id = None):
    init_database_info()

    # check what is queried for
    if db not in _databases or collection not in pluck(0, _databases[db]):
        return flask.abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = fields.split(DELIM)

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            flask.abort(404)
        else:
            flask.flash("offset too large, please refine your query.", "error")
            return flask.redirect(url_for(".api_query", db=db, collection=collection))

    # sort = [('fieldname1', ASC/DESC), ...]
    if sortby is not None:
        sort = []
        for key in sortby:
            if key.startswith("-"):
                sort.append((key[1:], DESC))
            else:
                sort.append((key, ASC))
    else:
        sort = None

    # preparing the actual database query q
    C = base.getDBConnection()
    q = {}

    if id is not None:
        if id.startswith('ObjectId('):
            q["_id"] = ObjectId(id[10:-2])
        else:
            q["_id"] = id
        single_object = True
    else:
        single_object = False

    for qkey, qval in request.args.iteritems():
        from ast import literal_eval
        try:
            if qkey.startswith("_"):
                continue
            if qval.startswith("s"):
                qval = qval[1:]
            if qval.startswith("i"):
                qval = int(qval[1:])
            elif qval.startswith("f"):
                qval = float(qval[1:])
            elif qval.startswith("ls"):      # indicator, that it might be a list of strings
                qval = qval[2:].split(DELIM)
            elif qval.startswith("li"):
                qval = [int(_) for _ in qval[2:].split(DELIM)]
            elif qval.startswith("lf"):
                qval = [float(_) for _ in qval[2:].split(DELIM)]
            elif qval.startswith("py"):     # literal evaluation
                qval = literal_eval(qval[2:])
            elif qval.startswith("cs"):     # containing string in list
                qval = { "$in" : [qval[2:]] }
            elif qval.startswith("ci"):
                qval = { "$in" : [int(qval[2:])] }
            elif qval.startswith("cf"):
                qval = { "$in" : [float(qval[2:])] }
            elif qval.startswith("cpy"):
                qval = { "$in" : [literal_eval(qval[3:])] }
        except:
            # no suitable conversion for the value, keep it as string
            pass

        # update the query
        q[qkey] = qval

    # executing the query "q" and replacing the _id in the result list
    api_logger.info("API query: q = '%s', fields = '%s', sort = '%s', offset = %s" % (q, fields, sort, offset))
    data = list(C[db][collection].find(q, projection = fields, sort=sort).skip(offset).limit(100))
    for document in data:
        oid = document["_id"]
        if type(oid) == ObjectId:
            document["_id"] = "ObjectId('%s')" % oid
        elif isinstance(oid, basestring):
            document["_id"] = str(oid)

    # preparing the datastructure
    start = offset
    next_req = dict(request.args)
    next_req["_offset"] = offset
    url_args = next_req.copy()
    query = url_for(".api_query", db=db, collection=collection, **next_req)
    offset += len(data)
    next_req["_offset"] = offset
    next = url_for(".api_query", db=db, collection=collection, **next_req)

    # the collected result
    data = {
        "database": db,
        "collection": collection,
        "timestamp": datetime.utcnow().isoformat(),
        "data": data,
        "start": start,
        "offset": offset,
        "query": query,
        "next": next
    }

    # display of the result (default html)
    if format.lower() == "json":
        return flask.jsonify(**data)
    elif format.lower() == "yaml":
        y = yaml.dump(data,
                      default_flow_style=False,
                      canonical=False,
                      allow_unicode=True)
        return flask.Response(y, mimetype='text/plain')
    else:
        location = "%s/%s" % (db, collection)
        title = "API - " + location
        bc = [("API", url_for(".index")), (location, query)]
        query_unquote = urllib2.unquote(data["query"])
        return render_template("collection.html",
                               title=title,
                               single_object=single_object,
                               query_unquote = query_unquote,
                               url_args = url_args,
                               bread=bc,
                               **data)
Example #7
0
def api_query(db, collection, id = None):
    init_database_info()

    # check what is queried for
    if db not in _databases or collection not in pluck(0, _databases[db]):
        return flask.abort(404)

    # parsing the meta parameters _format and _offset
    format = request.args.get("_format", "html")
    offset = int(request.args.get("_offset", 0))
    DELIM = request.args.get("_delim", ",")
    fields = request.args.get("_fields", None)
    sortby = request.args.get("_sort", None)

    if fields:
        fields = fields.split(DELIM)

    if sortby:
        sortby = sortby.split(DELIM)

    if offset > 10000:
        if format != "html":
            flask.abort(404)
        else:
            flask.flash("offset too large, please refine your query.", "error")
            return flask.redirect(url_for(".api_query", db=db, collection=collection))

    # sort = [('fieldname1', ASC/DESC), ...]
    if sortby is not None:
        sort = []
        for key in sortby:
            if key.startswith("-"):
                sort.append((key[1:], DESC))
            else:
                sort.append((key, ASC))
    else:
        sort = None

    # preparing the actual database query q
    C = base.getDBConnection()
    q = {}

    if id is not None:
        if id.startswith('ObjectId('):
            q["_id"] = ObjectId(id[10:-2])
        else:
            q["_id"] = id
        single_object = True
    else:
        single_object = False

    for qkey, qval in request.args.iteritems():
        from ast import literal_eval
        try:
            if qkey.startswith("_"):
                continue
            if qval.startswith("s"):
                qval = qval[1:]
            if qval.startswith("i"):
                qval = int(qval[1:])
            elif qval.startswith("f"):
                qval = float(qval[1:])
            elif qval.startswith("ls"):      # indicator, that it might be a list of strings
                qval = qval[2:].split(DELIM)
            elif qval.startswith("li"):
                qval = [int(_) for _ in qval[2:].split(DELIM)]
            elif qval.startswith("lf"):
                qval = [float(_) for _ in qval[2:].split(DELIM)]
            elif qval.startswith("py"):     # literal evaluation
                qval = literal_eval(qval[2:])
            elif qval.startswith("cs"):     # containing string in list
                qval = { "$in" : [qval[2:]] }
            elif qval.startswith("ci"):
                qval = { "$in" : [int(qval[2:])] }
            elif qval.startswith("cf"):
                qval = { "$in" : [float(qval[2:])] }
            elif qval.startswith("cpy"):
                qval = { "$in" : [literal_eval(qval[3:])] }
        except:
            # no suitable conversion for the value, keep it as string
            pass

        # update the query
        q[qkey] = qval

    # executing the query "q" and replacing the _id in the result list
    api_logger.info("API query: q = '%s', fields = '%s', sort = '%s', offset = %s" % (q, fields, sort, offset))
    data = list(C[db][collection].find(q, fields = fields, sort=sort).skip(offset).limit(100))
    for document in data:
        oid = document["_id"]
        if type(oid) == ObjectId:
            document["_id"] = "ObjectId('%s')" % oid
        elif isinstance(oid, basestring):
            document["_id"] = str(oid)

    # preparing the datastructure
    start = offset
    next_req = dict(request.args)
    next_req["_offset"] = offset
    url_args = next_req.copy()
    query = url_for(".api_query", db=db, collection=collection, **next_req)
    offset += len(data)
    next_req["_offset"] = offset
    next = url_for(".api_query", db=db, collection=collection, **next_req)

    # the collected result
    data = {
        "database": db,
        "collection": collection,
        "timestamp": datetime.utcnow().isoformat(),
        "data": data,
        "start": start,
        "offset": offset,
        "query": query,
        "next": next
    }

    # display of the result (default html)
    if format.lower() == "json":
        return flask.jsonify(**data)
    elif format.lower() == "yaml":
        y = yaml.dump(data,
                      default_flow_style=False,
                      canonical=False,
                      allow_unicode=True)
        return flask.Response(y, mimetype='text/plain')
    else:
        location = "%s/%s" % (db, collection)
        title = "API - " + location
        bc = [("API", url_for(".index")), (location, query)]
        query_unquote = urllib2.unquote(data["query"])
        return render_template("collection.html",
                               title=title,
                               single_object=single_object,
                               query_unquote = query_unquote,
                               url_args = url_args,
                               bread=bc,
                               **data)