from gargantext.util.db import session from gargantext.models.nodes import Node from graph.graph import get_graph from graph.utils import compress_graph, format_html from gargantext.util.http import APIView, APIException\ , JsonHttpResponse, requires_auth from gargantext.constants import graph_constraints from traceback import format_tb class Graph(APIView): ''' REST part for graphs. ''' def get(self, request, project_id, corpus_id): ''' Graph.get :: Get graph data as REST api. Get all the parameters first graph?field1=ngrams&field2=ngrams& graph?field1=ngrams&field2=ngrams&start=''&end='' NB save new graph mode (option saveOnly=True without a cooc_id) can return the new cooc id in the json before counting + filling data in async ''' if not request.user.is_authenticated(): # can't use @requires_auth because of positional 'self' within class return HttpResponse('Unauthorized', status=401) # Get the node we are working with corpus = session.query(Node).filter(Node.id==corpus_id).first() # TODO Parameters to save in hyperdata of the Node Cooc # WARNING: we could factorize the parameters as dict but ... # ... it causes a bug in asynchronous function ! # Check celery upgrades before. # Example (for the future): # parameters = dict() # parameters['field1'] = field1 # parameters['field2'] = field2 # Get all the parameters in the URL cooc_id = request.GET.get ('cooc_id' , None ) saveOnly = request.GET.get ('saveOnly' , None ) field1 = str(request.GET.get ('field1' , 'ngrams' )) field2 = str(request.GET.get ('field2' , 'ngrams' )) start = request.GET.get ('start' , None ) end = request.GET.get ('end' , None ) mapList_id = int(request.GET.get ('mapList' , 0 )) groupList_id = int(request.GET.get ('groupList' , 0 )) threshold = int(request.GET.get ('threshold' , 1 )) bridgeness = int(request.GET.get ('bridgeness', -1 )) format_ = str(request.GET.get ('format' , 'json' )) type_ = str(request.GET.get ('type' , 'node_link' )) distance = str(request.GET.get ('distance' , 'conditional')) # Get default map List of corpus if mapList_id == 0 : mapList_id = ( session.query ( Node.id ) .filter( Node.typename == "MAPLIST" , Node.parent_id == corpus.id ) .first() ) mapList_id = mapList_id[0] if mapList_id == None : raise ValueError("MAPLIST node needed for cooccurrences") # Get default value if no group list if groupList_id == 0 : groupList_id = ( session.query ( Node.id ) .filter( Node.typename == "GROUPLIST" , Node.parent_id == corpus.id ) .first() ) groupList_id = groupList_id[0] if groupList_id == None : raise ValueError("GROUPLIST node needed for cooccurrences") # Declare accepted fields accepted_field1 = ['ngrams', 'journal', 'source', 'authors'] accepted_field2 = ['ngrams', ] options = ['start', 'end', 'threshold', 'distance', 'cooc_id' ] try: # Check if parameters are accepted if (field1 in accepted_field1) and (field2 in accepted_field2): data = get_graph( corpus=corpus, cooc_id = cooc_id , field1=field1 , field2=field2 , mapList_id = mapList_id , groupList_id = groupList_id , start=start , end=end , threshold =threshold , distance=distance , bridgeness=bridgeness , saveOnly=saveOnly ) # data :: Either (Dic Nodes Links) (Dic State Length) # data_test :: Either String Bool data_test = data.get("state", True) if data_test is True: # normal case -------------------------------- if format_ == 'json': return JsonHttpResponse( compress_graph(data), status=200 ) # -------------------------------------------- else: # All other cases (more probable are higher in the if list) if data["state"] == "saveOnly": # async data case link = "http://%s/projects/%d/corpora/%d/myGraphs" % (request.get_host(), corpus.parent_id, corpus.id) return JsonHttpResponse({ 'id': data["target_id"], 'name': data["target_name"], 'date': data["target_date"], 'msg': '''Your graph is being saved: %s ''' % format_html(link) }, status=200) elif data["state"] == "corpusMin": # async data case link = "http://%s/projects/%d/" % (request.get_host(), corpus.parent_id) return JsonHttpResponse({ 'msg': '''Problem: your corpus is too small (only %d documents). Solution: Add more documents (more than %d documents) in order to get a graph. You can manage your corpus here: %s ''' % ( data["length"] , graph_constraints['corpusMin'] , format_html(link) ), }, status=400) elif data["state"] == "mapListError": # async data case link = 'http://%s/projects/%d/corpora/%d/terms' % (request.get_host(), corpus.parent_id, corpus.id) return JsonHttpResponse({ 'msg': '''Problem: your map list is too small (currently %d terms). Solution: Add some terms (more than %d terms) in order to get a graph. You can manage your map terms here: %s ''' % ( data["length"] , graph_constraints['mapList'] , format_html(link) ), }, status=400) elif data["state"] == "corpusMax": # async data case link = 'http://%s/projects/%d/corpora/%d/myGraphs' % (request.get_host(), corpus.parent_id, corpus.id) return JsonHttpResponse({ 'msg': '''Warning: Async graph generation since your corpus is big (about %d documents). Wait a while and discover your graph very soon. Click on the link below and see your current graph processing on top of the list: %s ''' % (data["length"], format_html(link)), }, status=200) else : return JsonHttpResponse({ 'msg': '''Programming error.''', }, status=400) elif len(data["nodes"]) < 2 and len(data["links"]) < 2: # empty data case return JsonHttpResponse({ 'msg': '''Empty graph warning No cooccurences found in this corpus for the words of this maplist (maybe add more terms to the maplist or increase the size of your corpus ?)''', }, status=400) else: # parameters error case return JsonHttpResponse({ 'msg': '''Usage warning Please choose only one field from each range: - "field1": %s - "field2": %s - "options": %s''' % (accepted_field1, accepted_field2, options) }, status=400) # for any other errors that we forgot to test except Exception as error: print(error) return JsonHttpResponse({ 'msg' : 'Unknown error (showing the trace):\n%s' % "\n".join(format_tb(error.__traceback__)) }, status=400)