예제 #1
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)
예제 #2
0
파일: api.py 프로젝트: davidfarmer/lmfdb
    if single_object and not data:
        if format != 'html':
            flask.abort(404)
        else:
            flash_error("no document with id %s found in table %s.", id, table)
            return flask.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.iteritems():
                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.iteritems() ]) 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(),
예제 #3
0
def datapage(labels, tables, title, bread, label_cols=None, sorts=None):
    """
    INPUT:

    - ``labels`` -- a string giving a label used in the tables (e.g. '11.a1' for an elliptic curve), or a list of strings (one per table)
    - ``tables`` -- a search table or list of search tables (as strings)
    - ``title`` -- title for the page
    - ``bread`` -- bread for the page
    - ``label_cols`` -- a list of column names of the same length; defaults to using ``label`` everywhere
    - ``sorts`` -- lists for sorting each table; defaults to None
    """
    format = request.args.get("_format", "html")
    if not isinstance(tables, list):
        tables = [tables]
    if not isinstance(labels, list):
        labels = [labels for table in tables]
    if label_cols is None:
        label_cols = ["label" for table in tables]
    if sorts is None:
        sorts = [None for table in tables]
    assert len(labels) == len(tables) == len(label_cols)

    def apierror(msg, flash_extras=[], code=404, table=False):
        if format == "html":
            flash_error(msg, *flash_extras)
            if table:
                return redirect(url_for("API.api_query", table=table))
            else:
                return redirect(url_for("API.index"))
        else:
            return abort(code, msg % tuple(flash_extras))
    data = []
    search_schema = {}
    extra_schema = {}
    for label, table, col, sort in zip(labels, tables, label_cols, sorts):
        q = {col: label}
        coll = db[table]
        try:
            data.append(list(coll.search(q, projection=3, sort=sort)))
        except QueryCanceledError:
            return apierror("Query %s exceeded time limit.", [q], code=500, table=table)
        except KeyError as err:
            return apierror("No key %s in table %s", [err, table], table=table)
        except Exception as err:
            return apierror(str(err), table=table)
        search_schema[table] = [(col, coll.col_type[col])
                                for col in sorted(coll.search_cols)]
        extra_schema[table] = [(col, coll.col_type[col])
                               for col in sorted(coll.extra_cols)]
    data = Json.prep(data)

    # the collected result
    data = {
        "labels": labels,
        "tables": tables,
        "label_cols": label_cols,
        "timestamp": datetime.utcnow().isoformat(),
        "data": data,
    }
    if format.lower() == "json":
        return current_app.response_class(json.dumps(data, 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:
        return render_template("apidata.html",
                               title=title,
                               search_schema=search_schema,
                               extra_schema=extra_schema,
                               bread=bread,
                               pretty=pretty_document,
                               **data)
예제 #4
0
파일: api.py 프로젝트: ngonhi/lmfdb-1
    if single_object and not data:
        if format != 'html':
            flask.abort(404)
        else:
            flash_error("no document with id %s found in table %s.", id, table)
            return flask.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.iteritems():
                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.iteritems() ]) 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(),