Ejemplo n.º 1
0
    def setUp(self):
        self.engine = Engine("op1", "op2")
        self.engine.op1.setup(in_name="in", out_name="middle", required=False)
        self.engine.op2.setup(in_name="middle", out_name="out")

        self.engine.op1.set(OptProductEx())
        foisdouze = OptProductEx("foisdouze")
        foisdouze.force_option_value("factor", 12)
        self.engine.op2.set(foisdouze, OptProductEx())

        egn_view = EngineView(self.engine, name="my_egn")
        egn_view.add_input("in", Numeric(vtype=int, min=-5, max=5))
        egn_view.add_input("middle", Numeric(vtype=int))
        print(self.engine.needed_inputs())
        egn_view.add_output("in")
        egn_view.add_output("middle")
        egn_view.add_output("out")

        api = ReliureAPI()
        api.register_view(egn_view)

        app = Flask(__name__)
        app.config['TESTING'] = True
        app.register_blueprint(api, url_prefix="/api")
        self.app = app.test_client()
Ejemplo n.º 2
0
    def setUp(self):
        comp_view = ComponentView(OptProductEx())
        comp_view.add_input("number", Numeric())
        comp_view.add_output("value", Numeric())
        comp_view.play_route("n/<number>")

        api = ReliureAPI()
        api.register_view(comp_view)

        app = Flask(__name__)
        app.config['TESTING'] = True
        app.register_blueprint(api, url_prefix="/api")
        self.app = app.test_client()
Ejemplo n.º 3
0
def explore_api(engines, graphdb):
    #explor_api = explor.explore_api("xplor", graphdb, engines)
    api = ReliureAPI("xplor", expose_route=False)

    # prox search returns graph only
    view = EngineView(explore_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", graph2dict, id_attribute='uuid')

    api.register_view(view, url_prefix="explore")

    # prox expand returns [(node,score), ...]
    view = EngineView(engines.expand_prox_engine(graphdb))
    view.set_input_type(NodeExpandQuery())
    view.add_output("scores", lambda x: x)

    api.register_view(view, url_prefix="expand_px")

    # additive search
    view = EngineView(engines.additive_nodes_engine(graphdb))
    view.set_input_type(AdditiveNodes())
    view.add_output("graph", graph2dict, id_attribute='uuid')

    api.register_view(view, url_prefix="additive_nodes")

    #layout
    api = layout_api(engines, api)
    #clustering
    api = clustering_api(engines, api)

    return api
Ejemplo n.º 4
0
def clustering_api(engines, api=None, optionables=None, prefix="clustering"):
        
    def clustering_engine(optionables):
        """ Return a default engine over a lexical graph
        """
        # setup
        engine = Engine("gbuilder", "clustering", "labelling")
        engine.gbuilder.setup(in_name="request", out_name="graph", hidden=True)
        engine.clustering.setup(in_name="graph", out_name="clusters")
        engine.labelling.setup(in_name="clusters", out_name="clusters", hidden=True)

        engine.gbuilder.set(engines.edge_subgraph) 
        engine.clustering.set(*optionables)

        ## Labelling
        from cello.clustering.labelling.model import Label
        from cello.clustering.labelling.basic import VertexAsLabel, TypeFalseLabel, normalize_score_max

        def _labelling(graph, cluster, vtx):
            return  Label(vtx["uuid"], score=1, role="default")
        
        labelling = VertexAsLabel( _labelling ) | normalize_score_max
        engine.labelling.set(labelling)

        return engine
        
    if api is None:
        api = ReliureAPI(name,expose_route = False)
        
    ## Clustering
    from cello.graphs.transform import EdgeAttr
    from cello.clustering.common import Infomap, Walktrap
    # weighted
    walktrap = Walktrap(weighted=True)
    walktrap.name = "Walktrap"
    infomap = Infomap(weighted=True) 
    infomap.name = "Infomap"

    DEFAULTS = [walktrap, infomap]

    if optionables == None : optionables = DEFAULTS
    
    view = EngineView(clustering_engine(optionables))
    view.set_input_type(EdgeList())
    view.add_output("clusters", export_clustering,  vertex_id_attr='uuid')

    api.register_view(view, url_prefix=prefix)
    return api
Ejemplo n.º 5
0
def chat_api(name, db):
    """ user authentification api """
    from app import login_manager

    api = ReliureAPI(name)

    @api.route("/about", methods=['GET', 'POST'])
    def about():
        return jsonify(infos)

    @api.route("/talk", methods=['GET', 'POST'])
    @login_required
    def talk(graph, message, tags):
        """
            send text to graph chan
        """
        return jsonify({'message': "bla"})

    @api.route("/read", methods=['GET', 'POST'])
    @login_required
    def read(graph, tags, **kwargs):
        """
            read text from graph chan
            
            graph: namespace
            kwargs :
                count: int max 50
                before: msgid
                after: msgid
        """
        return jsonify({'message': "read"})

    return api
Ejemplo n.º 6
0
    def setUp(self):
        self.engine = Engine("op1", "op2")
        self.engine.op1.setup(in_name="in", out_name="middle")
        self.engine.op2.setup(in_name="middle", out_name="out")

        self.engine.op1.set(OptProductEx())

        foisdouze = OptProductEx("foisdouze")
        foisdouze.force_option_value("factor", 12)
        foisquatre = OptProductEx("foisquatre")
        foisquatre.force_option_value("factor", 4)
        self.engine.op2.set(foisdouze, foisquatre)

        op2_view = EngineView(self.engine.op2, name="op2")
        op2_view.set_input_type(Numeric(vtype=int))
        op2_view.add_output("out")

        api = ReliureAPI()
        api.register_view(op2_view)

        app = Flask(__name__)
        app.config['TESTING'] = True
        app.register_blueprint(api, url_prefix="/api")
        self.app = app.test_client()
Ejemplo n.º 7
0
def clustering_api(graphdb,
                   engines,
                   api=None,
                   optionables=None,
                   prefix="clustering"):
    def clustering_engine(optionables):
        """ Return a default engine over a lexical graph
        """
        # setup
        engine = Engine("gbuilder", "clustering")
        engine.gbuilder.setup(in_name="request", out_name="graph", hidden=True)
        engine.clustering.setup(in_name="graph", out_name="clusters")

        engine.gbuilder.set(engines.edge_subgraph)
        engine.clustering.set(*optionables)

        return engine

    if api is None:
        api = ReliureAPI(name, expose_route=False)

    ## Clustering
    from cello.graphs.transform import EdgeAttr
    from cello.clustering.common import Infomap, Walktrap
    # weighted
    walktrap = Walktrap(weighted=True)
    walktrap.name = "Walktrap"
    infomap = Infomap(weighted=True)
    infomap.name = "Infomap"

    DEFAULTS = [walktrap, infomap]

    if optionables == None: optionables = DEFAULTS

    from pdgapi.explor import EdgeList
    view = EngineView(clustering_engine(optionables))
    view.set_input_type(EdgeList())
    view.add_output("clusters", export_clustering, vertex_id_attr='uuid')
    api.register_view(view, url_prefix=prefix)

    # cluster labels
    view = EngineView(clusters_labels_engine(graphdb))
    view.set_input_type(Clusters())
    view.add_output("labels", lambda e: e)
    api.register_view(view, url_prefix="labels")

    return api
Ejemplo n.º 8
0
def explore_api(engines, graphdb):
    #explor_api = explor.explore_api("xplor", graphdb, engines)
    api = ReliureAPI("xplor", expose_route=False)

    # import pad
    view = EngineView(import_calc_engine(graphdb))
    view.set_input_type(AdditiveNodes())
    view.add_output("graph", export_graph, id_attribute='uuid')
    api.register_view(view, url_prefix="import")

    # istex search
    view = EngineView(search_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="search")

    # graph exploration, reset global
    view = EngineView(graph_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="graph")

    # prox expand returns [(node,score), ...]
    view = EngineView(expand_prox_engine(graphdb))
    view.set_input_type(NodeExpandQuery())
    view.add_output("scores", lambda x: x)

    api.register_view(view, url_prefix="expand_px")

    # additive search
    view = EngineView(engines.additive_nodes_engine(graphdb))
    view.set_input_type(AdditiveNodes())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="additive_nodes")

    # export pad
    view = EngineView(export_calc_engine(graphdb))
    view.set_input_type(AdditiveNodes())
    view.add_output("url", lambda e: e)
    api.register_view(view, url_prefix="export")

    return api
Ejemplo n.º 9
0
def layout_api(engines, api=None, optionables=None, prefix="layout"):
        
    def export_layout(graph, layout):
        uuids = graph.vs['uuid']
        coords = { uuid: layout[i] for i,uuid in enumerate(uuids)  }
        return {
            "desc"  : str(layout),
            "coords": coords
        }


    def layout_engine(layouts):
        """ Return a default engine over a lexical graph 
        """
        # setup
        engine = Engine("gbuilder", "layout", "export")
        engine.gbuilder.setup(in_name="request", out_name="graph", hidden=True)
        engine.layout.setup(in_name="graph", out_name="layout")
        engine.export.setup(in_name=["graph", "layout"], out_name="layout", hidden=True)
        
        engine.gbuilder.set(engines.edge_subgraph) 

        for k,v in layouts:
            v.name = k
            
        layouts = [ l for n,l in layouts ]        
        engine.layout.set( *layouts )        
        engine.export.set( export_layout )

        return engine

    from cello.layout.simple import KamadaKawaiLayout, GridLayout, FruchtermanReingoldLayout
    from cello.layout.proxlayout import ProxLayoutPCA, ProxLayoutRandomProj, ProxLayoutMDS, ProxMDSSugiyamaLayout
    from cello.layout.transform import Shaker
    from cello.layout.transform import ByConnectedComponent
    from cello.layout.simple import DrlLayout

    LAYOUTS = [

        ("2D_Force_directed" , FruchtermanReingoldLayout(dim=2, weighted=True) ),
        ("3D_Force_directed" , FruchtermanReingoldLayout(dim=3, weighted=True) ),

        ("2D_KamadaKawai" , KamadaKawaiLayout(dim=2) ),
        ("3D_KamadaKawai" , KamadaKawaiLayout(dim=3) ),
        
        ("3DMds"         , ProxLayoutMDS(dim=3) | Shaker(kelastic=.9) ),
        ("2DMds"         , ProxLayoutMDS(dim=2 ) | Shaker(kelastic=.9) ),
        
        ("3DPca"         , ProxLayoutPCA(dim=3, ) | Shaker(kelastic=.9) ),
        ("3DPcaWeighted" , ProxLayoutPCA(dim=3, weighted=True) | Shaker(kelastic=.9) ),
        ("2DRandomProj"  , ProxLayoutRandomProj(dim=2) ),
        #("3DRandomProj"  , ProxLayoutRandomProj(dim=3) ),
        ("3DOrdered"     , ProxMDSSugiyamaLayout(dim=3) | Shaker(kelastic=0.9) ),
        # 2D
        ("2DPca"         , ProxLayoutPCA(dim=2) | Shaker(kelastic=1.8) ),
        # tree
        ("DrlLayout" , DrlLayout(dim=2) | Shaker(kelastic=0.8) ),
    ]

    
    if api is None:
        api = ReliureAPI(name,expose_route = False)
    if optionables == None : optionables = LAYOUTS
    
    view = EngineView(layout_engine(optionables))
    view.set_input_type(EdgeList())
    view.add_output("layout", lambda x:x)

    api.register_view(view, url_prefix=prefix)
    return api
Ejemplo n.º 10
0
def explore_api(name, graphdb, engines):
    """ API over tmuse elastic search
    """
    api = ReliureAPI(name,expose_route=True)

    # starred 
    view = EngineView(engines.starred_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="starred")

    # prox search returns graph only
    view = EngineView(engines.explore_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="explore")

    # prox expand returns [(node,score), ...]
    view = EngineView(engines.expand_prox_engine(graphdb))
    view.set_input_type(NodeExpandQuery())
    view.add_output("scores", lambda x:x)

    api.register_view(view, url_prefix="expand_px")


    # additive search
    view = EngineView(engines.additive_nodes_engine(graphdb))
    view.set_input_type(AdditiveNodes())
    view.add_output("graph", export_graph, id_attribute='uuid'  )

    api.register_view(view, url_prefix="additive_nodes")

    import random
    import json
    import pickle
    from flask import request, jsonify
    from flask import Response, make_response
    

    @api.route("/<string:gid>.json", methods=['GET'])
    @api.route("/starred/<string:gid>.json", methods=['GET'])
    def _json_dump(gid):
        dumps = lambda g : json.dumps( export_graph(g, id_attribute='uuid') )
        return stargraph_dump(gid, dumps, 'json')

    @api.route("/<string:gid>.pickle", methods=['GET'])
    @api.route("/starred/<string:gid>.pickle", methods=['GET'])
    def _pickle_dump(gid):
        return stargraph_dump(gid, pickle.dumps, 'pickle')

    def stargraph_dump(gid, dumps, content_type):
        """ returns igraph pickled/jsonified starred graph  """

        engine = engines.starred_engine(graphdb)
        
        meta = graphdb.get_graph_metadata(gid)
        graph = engine.play({'graph':gid})['graph']

        for k,v in meta.iteritems():
            graph[k] = v

        response = make_response(dumps(graph))
        response.headers['Content-Type'] = 'application/%s' % content_type
        response.headers['Content-Disposition'] = 'inline; filename=%s.%s' % (gid, content_type)
        return response
        

    @api.route("/<string:gid>/random")
    def random_node(gid):

        return jsonify({ 'gid': gid})

        # Debug views
    @api.route("/<string:gid>/extraction/<string:text>", methods=['GET'])
    @api.route("/<string:gid>/extraction", methods=['POST'])
    def _extract(gid, text=None):
        """
            POST /<string:graph>/extraction {
                gid: graph,
                uuids : [uuid, uuid]
            }
        """

        if request.method == "GET":
            labels = text.split(',')
            nodes = [ graphdb.get_node_by_name(gid, label) for label in labels ]
            p0_uuids = [ node['uuid'] for node in nodes ]
        elif request.method == "POST":
            assert graph == request.json.get('gid', None)
            p0_uuids = request.json.get('uuids')

        prox = graphdb.proxemie(gid, p0_uuids, limit=50, n_step=3)

        return jsonify({ 'gid': gid, 'nodes': p0_uuids , 'extraction':prox})






    return api
Ejemplo n.º 11
0
def explore_api(engines, graphdb):
    #explor_api = explor.explore_api("xplor", graphdb, engines)
    api = ReliureAPI("xplor", expose_route=False)

    # starred
    view = EngineView(starred_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="starred")

    # prox search returns graph only
    view = EngineView(explore_engine(graphdb))
    view.set_input_type(ComplexQuery())
    view.add_output("request", ComplexQuery())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="explore")

    # prox expand returns [(node,score), ...]

    view = EngineView(expand_prox_engine(graphdb))
    view.set_input_type(NodeExpandQuery())
    view.add_output("scores", lambda x: x)

    api.register_view(view, url_prefix="expand_px")

    # additive search
    view = EngineView(engines.additive_nodes_engine(graphdb))
    view.set_input_type(AdditiveNodes())
    view.add_output("graph", export_graph, id_attribute='uuid')

    api.register_view(view, url_prefix="additive_nodes")

    @api.route("/starred/<string:gid>.json", methods=['GET'])
    def g_json_dump(gid):
        graph = graphdb.get_graph(gid)
        g = starred(graph, limit=100, prune=True)
        g = export_graph(g, id_attribute='uuid')
        return jsonify(g)

    @api.route("/<string:gid>.json", methods=['GET'])
    def _json_dump(gid):
        dumps = lambda g: json.dumps(export_graph(g, id_attribute='uuid'))
        return stargraph_dump(gid, dumps, 'json')

    @api.route("/<string:gid>.pickle", methods=['GET'])
    def _pickle_dump(gid):
        return stargraph_dump(gid, pickle.dumps, 'pickle')

    def stargraph_dump(gid, dumps, content_type):
        """ returns igraph pickled/jsonified starred graph  """

        engine = explore_engine(graphdb)

        meta = graphdb.get_graph_metadata(gid)
        graph = engine.play({'graph': gid})['graph']

        for k, v in meta.iteritems():
            graph[k] = v

        response = make_response(dumps(graph))
        response.headers['Content-Type'] = 'application/%s' % content_type
        response.headers['Content-Disposition'] = 'inline; filename=%s.%s' % (
            gid, content_type)
        return response

    return api
Ejemplo n.º 12
0
for gname, config in graph_config.iteritems():

    graph = igraph.read(config.pop("path"))
    graph['vertices_color'] = config.pop("vertices_color")
    graphs.add(gname)
    engine = lexical_graph_engine(graph)

    view = EngineView(engine)
    view.set_input_type(Text())
    view.add_output("query", lambda x : x.encode('utf8'))
    view.add_output("graph", export_graph)
    view.add_output("layout", export_layout)
    view.add_output("clusters", export_clustering)

    api = ReliureAPI(name=gname )
    api.register_view(view,  url_prefix="api"  )

    app.register_blueprint(api,  url_prefix="/graph/%s" % (gname) )




# === Routes ===

@app.route("/")
def index():
    #TODO: better index page ?
    return "<a href='www.kodexlab.com'>www.kodexlab.com</a>"

# main entry HTML entry points
Ejemplo n.º 13
0
def graphedit_api(name, app, graphdb, login_manager, socketio):
    """ graph  api """
    api = ReliureAPI(name, expose_route=False)

    infos = {
        "desc": "graph api",
        "version": "0.1dev",
        "name": name,
        "db": repr(graphdb)
    }
    print(infos)

    def make_pad_url(name):
        return ""

    def broadcast(*args):
        if socketio: socketio.broadcast(*args)

    def broadcast_multi(messages):
        if socketio: socketio.broadcast_multi(messages)

    """ Graph """

    @api.route("/list", methods=['GET'])
    @api.route("/list/page/<int:page>", methods=['GET'])
    def http_list(page=1, order_by="name"):
        offset = 30
        data = graphdb.list_graphs(page,
                                   offset=offset,
                                   order_by=order_by,
                                   meta=False,
                                   reverse=False,
                                   root_page_url=url_for("%s.http_list" %
                                                         api.name))

        return jsonify(data)

    @api.route("/create", methods=['POST'])
    @api.route("/g/<string:gid>", methods=['POST'])
    @api.route("/g/<string:gid>", methods=['PUT'])
    @login_required
    def edit_graph(gid=None):
        """ create new graph  """
        username = current_user.username
        form = request.json

        if form is None:
            form = request.form

        keys = ['description', 'tags', 'image']

        properties = {k: form.get(k, "") for k in keys}

        data = {'graph': gid, 'username': username}

        if request.method == "POST":
            if gid == None: gid = form['name']

            g = graphdb.create_graph(username, gid, properties)
            properties['pad_url'] = make_pad_url(name)
            data['graph'] = gid
            data['status'] = 'created'
            broadcast(0, 'new graph', data)

        if request.method == "PUT" and gid is not None:
            graphdb.update_graph(username, gid, properties)
            data['status'] = 'edited'
            data['properties'] = properties
            broadcast(gid, 'edit graph', data)

        return jsonify(graphdb.get_graph(gid))

    @api.route("/g/<string:gid>", methods=['GET'])
    def get_graph_metadata(gid):
        """ get  graph metadata  """

        data = {gid: graphdb.get_graph_metadata(gid)}
        if request.args.get('edgetypes', "") == "false":
            data[gid].pop("edgetypes")

        if request.args.get('nodetypes', "") == "false":
            data[gid].pop("nodetypes")

        return jsonify(data)

    #@api.route("/g/<string:gid>/drop", methods=['GET'])
    @api.route("/g/<string:gid>", methods=['DELETE'])
    @login_required
    def delete_graph(gid):
        """ get  graph metadata  """

        username = current_user.username if current_user.is_authenticated else ""
        graph = graphdb.get_graph(gid)

        if graph and app.config.get(
                "ALLOW_OWNER_DELETE_GRAPH",
                False) and graph['meta']['owner'] == username:

            graphdb.destroy_graph(gid)

            data = {'graph': gid, 'status': 'deleted'}

            return jsonify(data)

        else:
            return current_app.login_manager.unauthorized()

    """ Subgraph """

    @api.route("/g/<string:gid>/subgraph", methods=['POST'])
    def extract_subgraph(gid):
        """
        returns json subgraph in a list of triples. (src, props, target)
        input: json post request
                {
                    nodes : [ uuid, .. ]
                }

        output:
                {   graph : gid,
                    subgraph : [ ( uuid_source, {edge properties}, uuid_target ) ]
                }
        """

        uuids = request.json.get('nodes')
        subgraph = graphdb.get_subgraph(gid, uuids)

        data = {
            "graph": gid,
            "subgraph": subgraph,
        }
        return jsonify(data)

    """ Completion """

    @api.route("/g/<string:gid>/complete/<string:obj_type>/<string:prefix>",
               methods=['GET'])
    @api.route("/g/<string:gid>/complete", methods=['POST'])
    def complete_label(gid, obj_type=None, prefix=None):

        start = 0

        if request.method == 'GET':
            start = request.args.get('start', start)

        if request.method == 'POST':
            obj_type = request.json.get('obj_type')
            prefix = request.json.get('prefix')
            start = request.json.get('start')

        start = int(start)
        complete = graphdb.complete_label(gid, obj_type, prefix, start, 100)
        data = {
            "graph": gid,
            "obj_type": obj_type,
            "prefix": prefix,
            "complete": complete,
            "start": start,
            "count": len(complete),
        }

        return jsonify(data)

    """ Completion by uuid"""

    @api.route("/g/<string:gid>/complete/uuid/<string:uuid>", methods=['GET'])
    @api.route("/g/<string:gid>/complete/uuid", methods=['POST'])
    def complete_uuid(gid, uuid=None):

        if request.method == 'POST':
            uuid = request.json.get('uuid', uuid)

        node = _get_node(gid, uuid)
        data = {
            "graph": gid,
            "uuid": uuid,
            "obj_type": "node",
            "complete": node,
            "count": len(node),
        }

        return jsonify(data)

    """ Schema """

    @api.route("/g/<string:gid>/schema", methods=['GET', 'POST'])
    def get_schema(gid):
        """ Get graph schema """

        data = {
            'graph': gid,
            'schema': {
                "nodetypes": graphdb.get_node_types(gid),
                "edgetypes": graphdb.get_edge_types(gid),
            }
        }

        return jsonify(data)

    @api.route("/g/<string:gid>/schema", methods=['POST'])
    @login_required
    def set_schema(gid):
        """ Edit node data """
        data = {'graph': gid, 'status': 'changed'}

        raise GraphError('Set schema Not implemented')

        return jsonify(data)

    #  NodeTypes
    @api.route("/g/<string:gid>/nodetypes", methods=['GET'])
    def get_node_types(gid):
        return jsonify({
            "graph": gid,
            "nodetypes": graphdb.get_node_types(gid),
        })

    @api.route("/g/<string:gid>/nodetype/<string:uuid>", methods=['GET'])
    def get_nodetype(gid, uuid):
        #username = current_user.username

        nodetype = graphdb.get_node_type(gid, uuid)
        nodetype.update({'graph': gid})

        return jsonify(nodetype)

    @api.route("/g/<string:gid>/nodetype", methods=['POST'])
    @api.route("/g/<string:gid>/nodetype/<string:uuid>", methods=['PUT'])
    @login_required
    def edit_nodetype(gid, uuid=None):
        """ Edit node data
        POST to create a new node type
        PUT to update existing node type !!! append properties only
        {
            name : <str> node type name ,
            desc : <str> node type description ,
            properties : {
                name : json serialized  cello type,
                name : ...
            },
            material: {}
        }
        """
        username = current_user.username

        data = request.json
        name = data.pop('name', "")
        description = data.pop('description', "")
        properties = data.pop('properties', {})

        resp = {'graph': gid, 'username': username}
        # TODO check properties type
        if name == "": raise ValueError("NodeType should have a name.")
        for k, v in properties.iteritems():
            if k == "": raise ValueError("Properties should have a name.")

        # creation
        if request.method == "POST" and uuid is None:

            nodetype = graphdb.create_node_type(username, gid, name,
                                                properties, description)
            nodetype.update({
                'graph': gid,
                'status': 'created',
                'username': username,
            })

            broadcast(uuid, 'new nodetype', nodetype)

            return jsonify(nodetype)

        # update
        elif request.method == "PUT" and uuid is not None:

            nodetype = graphdb.update_nodetype(uuid, properties, description)
            nodetype.update({
                'graph': gid,
                'status': 'edited',
                'username': username,
            })

            broadcast(uuid, 'edit nodetype', nodetype)

            return jsonify(nodetype)

        else:
            return 404

    """ Edge types """

    @api.route("/g/<string:gid>/edgetype/<string:uuid>", methods=['GET'])
    def get_edgetype(gid, uuid):
        #username = current_user.username

        resp = {
            'graph': gid,
        }
        edgetype = graphdb.get_edge_type(uuid)

        return jsonify(edgetype)

    @api.route("/g/<string:gid>/edgetypes", methods=['GET'])
    def get_edgetypes(gid):
        edgetypes = graphdb.get_edge_types(gid)
        return jsonify({
            "graph": gid,
            "edgetypes": edgetypes,
        })

    @api.route("/g/<string:gid>/edgetype", methods=['POST'])
    @api.route("/g/<string:gid>/edgetype/<string:uuid>",
               methods=['POST', 'PUT'])
    @login_required
    def edit_edge_type(gid, uuid=None):
        """ Edit node data """
        username = current_user.username

        data = request.json
        name = data.pop('name', "")
        description = data.pop('description', "")
        properties = data.pop('properties', {})

        if name == "": raise ValueError("EdgeType should have a name.")
        for k, v in properties.iteritems():
            if k == "": raise ValueError("Properties should have a name.")

        # creation
        if request.method == "POST" and uuid is None:

            edgetype = graphdb.create_edge_type(username, gid, name,
                                                properties, description)
            edgetype.update({
                'graph': gid,
                'status': 'edited',
                'username': username,
            })
            broadcast(uuid, 'new edgetype', edgetype)

            return jsonify(edgetype)

        # update
        elif request.method == "PUT" and uuid is not None:

            #props = { x['name']: x['otype'] for x in properties }
            edgetype = graphdb.update_edgetype(uuid, properties, description)
            edgetype.update({
                'graph': gid,
                'status': 'edited',
                'username': username,
            })

            broadcast(uuid, 'edit edgetype', edgetype)

            return jsonify(edgetype)

        else:
            return 404

    """ Node """

    @api.route("/g/<string:gid>/nodes", methods=['POST'])
    @login_required
    def create_nodes(gid):
        """ batch insert nodes """
        username = current_user.username

        #print request.args
        #print request.values
        #print request.data
        #print request.stream.read()
        #print request.get_json(force=True, silent=False)

        data = request.json

        nodes = data['nodes']
        res = []

        # validate nodes properties

        for node in nodes:
            pass

        uuids = graphdb.batch_create_nodes(username, gid, nodes)

        #assert len(uuids) == len(nodes)

        # post to Notifications Server
        messages = [{
            "username": username,
            "action": "new node",
            "graph": gid,
            "uuid": uuid,
            "properties": nodes[i]['properties'],
            "status": "created"
        } for i, uuid in uuids]

        broadcast_multi(messages)

        return jsonify({
            "action": "new node",
            'graph': gid,
            'username': username,
            'results': uuids
        })

    @api.route("/g/<string:gid>/nodes/find", methods=['POST'])
    @api.route("/g/<string:gid>/nodes/find/<string:node_type>",
               methods=['GET'])
    def find_nodes(gid, node_type=None):
        """ Get node data
        
        """

        start = 0
        size = 100
        properties = {}

        if request.method == "GET":
            start = int(request.args.get('start', start))
        if request.method == "POST":
            form = request.json
            start = form.get('start', 0)
            size = min(form.get('size', 10), 100)
            node_type = form.get('nodetype')
            properties = form.get('properties')

        nodes = graphdb.find_nodes(gid, node_type, properties, start, size)

        data = {
            'graph': gid,
            'nodetype': node_type,  # uuid
            'start': start,
            'size': size,
            'properties': properties,
            #'uuids' : []
            'nodes': nodes,
            'count': len(nodes)
        }

        return jsonify(data)

    @api.route("/g/<string:gid>/node/<string:uuid>", methods=['GET'])
    def get_node_by_id(gid, uuid):
        return _get_node(gid, uuid)

    @api.route("/g/<string:gid>/node/<string:uuid>/by_name", methods=['GET'])
    def get_node_by_name(gid, uuid):
        return _get_node(gid, uuid, by_name=True)

    def _get_node(gid, uuid, by_name=False):
        """ Get node data """

        # TODO check node is in graph

        if by_name:
            node = graphdb.get_node_by_name(gid, uuid)
        else:
            node = graphdb.get_node(gid, uuid)

        node.update({
            'graph': gid,
            'label' if by_name else 'uuid': uuid,
            'status': 'read'
        })

        return jsonify(node)

    @api.route("/g/<string:gid>/node/<string:uuid>/neighbors",
               methods=['GET', 'POST'])
    def node_neigbhors(gid, uuid):
        """ Function doc
        :param gid: <str> graph
        :param uuid: <uuid> node uuid
        :returns : neighbors list starting from `start` with a size of `size`
        """
        SIZE = int(request.args.get('size', 100))

        if request.method == 'GET':
            start = int(request.args.get('start', 0))
            mode = request.args.get('mode', 'ALL')

        elif request.method == 'POST':
            start = request.json.get('start', 0)
            mode = request.json.get('mode', 'ALL')

        neighbors = graphdb.get_graph_neighbors(gid,
                                                uuid,
                                                filter_edges=None,
                                                filter_nodes=None,
                                                filter_properties=None,
                                                mode=mode,
                                                start=start,
                                                size=SIZE)

        data = {
            'graph':
            gid,
            'node':
            uuid,
            'start':
            start,
            'mode':
            mode,
            'neighbors':
            neighbors,
            'size':
            SIZE,
            'length':
            len(neighbors),
            'count':
            graphdb.count_neighbors(gid,
                                    uuid,
                                    filter_edges=None,
                                    filter_nodes=None,
                                    filter_properties=None,
                                    mode=mode)
        }
        return jsonify(data)

    @api.route("/g/<string:gid>/node/<string:uuid>/neighbors/count",
               methods=['GET', 'POST'])
    def count_node_neigbhors(gid, uuid):
        """ Function doc
        :param gid: <str> graph
        :param uuid: <uuid> node uuid
        :returns : neighbors count
        """
        if request.method == 'GET':
            pass
        elif request.method == 'POST':
            pass

        # TODO parse args for filter

        edges = graphdb.count_neighbors(uuid,
                                        filter_edges=None,
                                        filter_nodes=None,
                                        filter_properties=None,
                                        mode='ALL')

        data = {'graph': gid, 'node': uuid, 'neighbors': edges}
        return jsonify(data)

    @api.route("/g/<string:gid>/node", methods=['POST'])
    @api.route("/g/<string:gid>/node/<string:uuid>", methods=['PUT'])
    @login_required
    def edit_node(gid, uuid=None):
        """ Edit node data """
        data = request.json

        if request.method == "POST":
            if uuid is not None:
                raise ApiError(
                    'POST method is not allowed with a uuid, use PUT to edit ')

        if request.method == "PUT":
            if uuid is None:
                raise ApiError(
                    'PUT method expect a uuid, use POST to create a node ')

        resp = post_node(current_user.username, gid, uuid, data)

        return jsonify(resp)

    def post_node(username, gid, uuid, data):

        # ::: TODO :::
        # check node uuid is in graph
        # check node_type
        # check properties key & values
        # check update/create node

        resp = {
            'graph': gid,
            'uuid': uuid,
            'username': current_user.username,
        }
        # creation

        nt_uuid = data.pop('nodetype')
        props = data.pop('properties', {})
        label = props.get('label', "")

        nodetype = graphdb.get_node_type(gid, nt_uuid)

        if nodetype is None:
            raise ApiError("Nodetype '%s' is unknown" % nt_uuid)

        # TODO extract label pattern like %name

        if uuid is None and request.method == "POST":

            uuid = graphdb.create_node(username, gid, nt_uuid, props)
            resp['uuid'] = uuid
            resp['status'] = "created"
            resp['label'] = label

            broadcast(gid, "new node", resp)
            return resp

        # edition
        elif uuid and request.method == "PUT":
            # TODO:: check that node belongs to this graph
            graphdb.change_node_properties(username, uuid, props)

            resp['status'] = "edited"
            resp['label'] = label

            broadcast(uuid, "edit node", resp)
            return resp
        # error
        else:
            return 404

    @api.route("/g/<string:gid>/nodes", methods=['PUT'])
    @login_required
    def edit_nodes(gid):

        return 500, "unimplemented"

    @api.route("/g/<string:gid>/node/<string:uuid>", methods=['DELETE'])
    @login_required
    def delete_node(gid, uuid):
        """ Delete
         """
        node = graphdb.get_node(gid, uuid)

        deleted = graphdb.delete_node(current_user.username, gid, uuid)

        data = {
            'graph': gid,
            'uuid': uuid,
            'username': current_user.username,
            'label': node['properties']['label'],
            'status': "deleted",
        }

        broadcast(uuid, 'delete node', data)

        return jsonify(data)

    # ~~~ Stars ~~~

    def _set_nodes_starred(gid, nodes, starred):

        graphdb.set_nodes_starred(gid, nodes, starred)

        data = {
            'graph': gid,
            'nodes': nodes,
            'count': len(nodes),
            'star': starred
        }
        return data

    @login_required
    @api.route("/g/<string:gid>/nodes/star", methods=['POST'])
    def star_nodes(gid):
        nodes = request.json.get('nodes', [])
        return jsonify(_set_nodes_starred(gid, nodes, True))

    @login_required
    @api.route("/g/<string:gid>/nodes/unstar", methods=['POST'])
    def unstar_nodes(gid):
        nodes = request.json.get('nodes', [])
        return jsonify(_set_nodes_starred(gid, nodes, False))

    @api.route("/g/<string:gid>/node/<string:uuid>/star",
               methods=['GET', 'POST'])
    @login_required
    def star_node(gid, uuid):
        return jsonify(_set_nodes_starred(gid, [uuid], True))

    @api.route("/g/<string:gid>/node/<string:uuid>/unstar",
               methods=['GET', 'POST'])
    @login_required
    def unstar_node(gid, uuid):
        return jsonify(_set_nodes_starred(gid, [uuid], False))

    @api.route("/g/<string:gid>/upvote", methods=['GET'])
    @login_required
    def user_upvote_graph(gid):

        username = current_user.username if current_user.is_authenticated else ""
        return jsonify(
            graphdb.toggle_user_updownvote_graph(username, gid, 'up'))

    @api.route("/g/<string:gid>/downvote", methods=['GET'])
    @login_required
    def user_downvote_graph(gid):

        username = current_user.username if current_user.is_authenticated else ""
        return jsonify(
            graphdb.toggle_user_updownvote_graph(username, gid, 'down'))

    @api.route("/g/<string:gid>/vote", methods=['GET'])
    @login_required
    def get_user_graph_vote(gid):

        username = current_user.username if current_user.is_authenticated else ""
        return jsonify(graphdb.get_user_updownvote_graph(username, gid))

    """ Edges """

    @api.route("/g/<string:gid>/edge/<string:uuid>", methods=['GET'])
    def get_edge(gid, uuid):
        """ Get edge data """

        # TODO check edge is in graph

        edge = graphdb.get_edge(uuid)

        edge.update({
            'graph': gid,
        })

        return jsonify(edge)

    @api.route("/g/<string:gid>/edges", methods=['POST'])
    @login_required
    def edit_edges(gid):

        messages = []
        data = request.json

        edges = data['edges']
        uuids = graphdb.batch_create_edges(current_user.username, gid, edges)

        for i, uuid in uuids:

            edge = edges[i]
            edge['uuid'] = uuid

            edge.update({
                "graph": gid,
                "username": current_user.username,
                "status": "created",
            })

            edge.update({
                "action": "new edge",
            })
            messages.append(dict(edge))

            edge.update({
                "action": "new edge from",
            })
            messages.append(dict(edge))

            edge.update({
                "action": "new edge to",
            })
            messages.append(dict(edge))

        broadcast_multi(messages)

        return jsonify({
            'graph': gid,
            'username': current_user.username,
            'status': 'created',
            'results': uuids
        })

    @api.route("/g/<string:gid>/edges/find", methods=['POST'])
    def find_edges(gid):
        """ Get node data """
        form = request.json

        start = form.get('start', 0)
        size = form.get('size', 100)
        edge_type = form.get('edge_type')
        properties = form.get('properties')

        edges = graphdb.find_edges(gid, edge_type, properties, start, size)

        data = {
            'graph': gid,
            'edge_type': edge_type,
            'start': start,
            'size': size,
            'properties': properties,
            'edges': edges
        }

        return jsonify(data)

    @api.route("/g/<string:gid>/edge", methods=['POST'])
    @api.route("/g/<string:gid>/edge/<string:uuid>", methods=['PUT'])
    @login_required
    def edit_edge(gid, uuid=None):
        """ Edit edge data
        One can update properties for edges not source or target
        """
        username = current_user.username
        data = request.json
        edge = {}

        edgetype = data.pop('edgetype')
        props = data.pop('properties', {})
        #label  = props.pop('label', "")

        # ::: TODO :::
        # check edge_type
        # check properties key & values
        # check update/create edge

        # creation
        if uuid is None and request.method == "POST":
            # TODO has any ?
            source = data.pop('source')
            target = data.pop('target')

            assert source and target, "Wrong source or target for an edge (%s,%s) " % (
                source, target)

            uuid = graphdb.create_edge(username, gid, edgetype, props, source,
                                       target)

            edge = graphdb.get_edge(uuid)

            edge.update({
                'graph': gid,
                'username': username,
                'status': 'created',
            })

            broadcast(uuid, 'new edge', edge)

        elif uuid and request.method == "PUT":
            # edition
            graphdb.change_edge_properties(username, uuid, props)

            edge = graphdb.get_edge(uuid)

            edge.update({
                'graph': gid,
                'username': username,
                'status': 'edited',
            })

            broadcast(uuid, 'edit edge', edge)

        else:
            return 404

        return jsonify(edge)

    @api.route("/g/<string:gid>/edge/<string:uuid>", methods=['DELETE'])
    @login_required
    def delete_edge(gid, uuid):
        """ Delete edge """

        # TODO : mark edge deleted
        graphdb.delete_edge(current_user.username, gid, uuid)

        data = {
            'graph': gid,
            'uuid': uuid,
            'properties': {},
            'username': current_user.username,
            'status': "deleted",
        }

        broadcast(uuid, 'delete edge', data)

        return jsonify(data)

    return api
Ejemplo n.º 14
0
def users_api(name):
    """ user authentification api """
    from app import login_manager

    from reliure.web import ReliureAPI

    api = ReliureAPI(name, expose_route=False)

    PADAGRAPH_HOST = app.config["PADAGRAPH_HOST"]

    @api.route("/about", methods=['GET', 'POST'])
    def about():
        return jsonify(infos)

    # === auth ====

    @api.route("/authenticate", methods=['GET', 'POST'])
    def auth():
        user = authenticate_user(request)

        if user:
            return jsonify({
                'logged': True,
                'user': user.as_dict(),
                'token': user.get_auth_token(),
            })

        return "login failed", 401

    @api.route("/me", methods=['GET'])
    @login_required
    def me():
        user = current_user
        return jsonify(user.as_dict())

    @api.route("/me/generate_auth_token", methods=['GET'])
    @login_required
    def generate_auth_token():
        user = current_user
        return jsonify({
            'user': user.as_dict(),
            'token': user.get_auth_token(),
        })

    @api.route("/login", methods=['GET', 'POST'])
    def login():
        logged = False

        print ">>>>>>> login"

        user = authenticate_user(request)

        print request, user

        if user:
            logged = True
            login_user(user)
            url = request.args.get('redirect', None)
            if url:
                return redirect(url)

        return jsonify({
            'logged': logged,
            'username': user.username if logged else ""
        })

    @api.route("/logout", methods=['GET', 'POST'])
    def logout():
        print "req.cookie", request.cookies
        try:
            logout_user()
        finally:
            pass

        if request.method == "GET":
            resp = redirect('/')
        else:
            resp = jsonify({'logged': False})

        resp.set_cookie('session', '', expires=0)
        resp.set_cookie('gggg', 'bla', expires=23330)
        return resp

    # === create account ===

    @api.route("/invitation", methods=['POST'])
    def invite(invitation=None):

        # validate token
        form = InvitationForm()

        if form.validate():

            send_invitation_link(form.email.data)

            return render_template('create_account.html',
                                   step="invitation-send")

        return redirect('/?invalid=1')

    @api.route("/create-account", methods=['POST', 'GET'])
    @api.route("/create-account/<string:invitation>", methods=['GET'])
    def create_account(invitation=None):

        step = "create"
        has_error = False

        if USER_ACCOUNT_LIMIT > 0:
            if graphdb.get_users_count() >= USER_ACCOUNT_LIMIT:
                raise UserAccountLimitException()

        form = CreateUserForm()

        if request.method == "GET":
            form.invitation.data = invitation

            if not form.validate():
                print form.invitation.errors
                if len(form.invitation.errors):
                    return render_template('create_account.html',
                                           step="invitation-invalid")

        elif request.method == "POST":
            if form.validate():

                # create user
                username = form.username.data
                email = form.email.data
                password = form.password.data

                login = PdgUser.create(username, email, password)

                send_activation_link(email)

                return render_template('create_account.html',
                                       step="validate",
                                       form=form)

            else:
                has_error = True

                if len(form.invitation.errors):

                    return render_template('create_account.html',
                                           step="invitation-invalid")

        return render_template('create_account.html',
                               step=step,
                               form=form,
                               has_error=has_error)

    @api.route("/resend-validation/<string:token>", methods=['GET'])
    def resend_validation(token=None):

        # validate token
        try:

            seed, email = serialiser.loads(token)
            if seed != "activation":
                raise ValueError()

            user = PdgUser.get_by_email(email)
            send_activation_link(email)
            return render_template('create_account.html',
                                   step="validation-resent",
                                   username=user.username,
                                   email=email)

        except:
            return render_template('create_account.html', step="token-invalid")

    @api.route("/activate-account/<string:token>", methods=['GET'])
    def activate_account(token=None):

        # validate token
        try:

            seed, email = serialiser.loads(token)

            if seed != "activation":
                raise ValueError("wrong token")

            user = PdgUser.activate(email)
            return render_template('create_account.html',
                                   step="active",
                                   username=user.username)

        except:
            return render_template('create_account.html', step="token-invalid")

    # === / password recovery ===

    @api.route("/password-recovery", methods=['GET', 'POST'])
    def get_recovery():

        form = RecoveryEmailForm()

        if request.method == "POST" and form.validate():

            # send email recovery
            try:
                email = form.email.data
                if PdgUser.has_email(email):
                    user = PdgUser.get_by_email(email)
                    send_password_recovery_link(email, user.password)
                return render_template('account-recovery.html',
                                       step="email-sent",
                                       email=email)
            except:
                raise
                return render_template('account-recovery.html',
                                       step="token-invalid")

        return render_template('account-recovery.html', step="email-form")

    @api.route("/change-password", methods=['POST'])
    @api.route("/change-password/<string:token>", methods=['GET'])
    def post_recovery(token=None):
        form = RecoveryPasswordForm()

        if request.method == "POST":
            token = form.token.data

        try:
            # validate 24h token

            seed, email, pwd = serialiser.loads(token,
                                                max_age=RECOVERY_TOKEN_MAX_AGE)

            if seed != "recovery":
                raise ValueError("wrong token, %s" % seed)

            user = PdgUser.get_by_email(email)
            if not user.verify_password(pwd, hashed=True):
                raise ValueError("wrong token, %s" % seed)

            if request.method == "POST":
                if form.validate():
                    PdgUser.change_password(email, form.password.data)
                    return render_template('account-recovery.html',
                                           step="password-changed")

                return render_template('account-recovery.html',
                                       step="password-form",
                                       token=token,
                                       email=email,
                                       has_error=True,
                                       errors=form.errors)
        except:
            return render_template('account-recovery.html',
                                   step="token-invalid")

        return render_template('account-recovery.html',
                               step="password-form",
                               token=token,
                               has_error=False)

    @api.route("/u/<string:uid>", methods=['GET'])
    @login_required
    def user(uid):
        """ Get public info for user <user> """
        user = User.get(uid)
        return jsonify({uuid: user.as_dict()})

    @api.route("/count", methods=['GET'])
    #@login_required
    def users_count():
        """ Get users count """
        print "count"
        count = graphdb.get_users_count()
        return jsonify({"count": count})

    return api
Ejemplo n.º 15
0
def TmuseApi(name,
             host='localhost:9200',
             index_name='tmuse',
             doc_type='graph',
             retry=5):
    """ API over tmuse elastic search
    """
    esindex = EsIndex(index_name, doc_type=doc_type, host=host)
    print "# TmuseApi", host, doc_type, index_name

    # let es start
    for i in range(retry):
        if not esindex._es.ping():
            print "waiting for es to start"
            time.sleep(i)
    assert esindex._es.ping(), "impossible to reach ES server"

    # build the API from this engine
    print "api name", name
    api = ReliureAPI(name)

    # Main api entry point: tmuse engine (subgraph)
    view = EngineView(engine(esindex))
    view.set_input_type(ComplexQuery())
    view.add_output("query", ComplexQuery())
    view.add_output("graph", export_graph)
    view.add_output("layout", export_layout)
    view.add_output("clusters", export_clustering)
    # add a simple play route
    view.play_route("<query>")
    api.register_view(view, url_prefix="subgraph")

    # Add auto completion View
    completion = TmuseEsComplete(index=esindex, size=20)
    # TODO suggestion rerank
    # completion |= rerank
    completion_view = ComponentView(completion)
    completion_view.add_input("lang", Text(default=u"*"))
    completion_view.add_input("pos", Text(default=u"*"))
    completion_view.add_input("form")
    completion_view.add_output("response")
    completion_view.play_route("<lang>.<pos>.<form>")
    api.register_view(completion_view, url_prefix="complete")

    import random

    @api.route("/random")
    @api.route("/random/<string:pos>")
    def random_node(pos=None, retry=5, count=0):
        if pos not in ALL_POS:
            pos = random.sample(ALL_POS, 1)[0]

        graph = "jdm.%s.flat" % pos
        docs = tmuse.random_node(esindex, graph)

        doc = docs[0] if len(docs) else dict()

        return jsonify({'pos': pos, 'doc': doc})

    # Debug views
    @api.route("/_extract/<string:graph>/<string:text>")
    def _extract(graph, text):
        query = QueryUnit(graph=graph, form=text)
        es_res = tmuse.extract(esindex, query)
        return jsonify({'res': es_res})

    @api.route("/_prox/<string:graph>/<string:text>")
    def _prox(graph, text):
        es_res = proxlist(esindex, graph, text, 100)
        return jsonify({'res': es_res})

    return api