Commit 48a58dad authored by delanoe's avatar delanoe

Merge remote-tracking branch 'origin/romain-list' into testing-merge

parents e09c6a94 c2ce1582
......@@ -11,7 +11,7 @@ from gargantext.util.group_tools import query_groups, group_union
from gargantext.util.db import session, desc, func, \
bulk_insert_ifnotexists
from gargantext.models import Ngram, NodeNgram, NodeNodeNgram, \
NodeNgramNgram
NodeNgramNgram, Node
from gargantext.util.lists import UnweightedList, Translations
......@@ -327,7 +327,7 @@ def export_ngramlists(node,fname=None,delimiter=DEFAULT_CSV_DELIM,titles=True):
def import_ngramlists(fname, delimiter=DEFAULT_CSV_DELIM,
def import_ngramlists(the_file, delimiter=DEFAULT_CSV_DELIM,
group_delimiter=DEFAULT_CSV_DELIM_GROUP):
'''
This function reads a CSV of an ngrams table for a Corpus,
......@@ -385,7 +385,7 @@ def import_ngramlists(fname, delimiter=DEFAULT_CSV_DELIM,
-------
3 x UnweightedList + 1 x Translations
@param fname a local filename or a filehandle-like
@param the_file a local filename or file contents or a filehandle-like
@param delimiter a character used as separator in the CSV
@param group_delimiter a character used as grouped subforms separator
(in the last column)
......@@ -418,21 +418,27 @@ def import_ngramlists(fname, delimiter=DEFAULT_CSV_DELIM,
# =============== READ CSV ===============
if isinstance(fname, str):
fh = open(fname, "r")
elif callable(getattr(fname, "read", None)):
fh = fname
if isinstance(the_file, list):
fname = 'imported_file'
contents = the_file
else:
raise TypeError("IMPORT: fname argument has unknown type %s" % type(fh))
if isinstance(the_file, str):
fh = open(the_file, "r")
fname = the_file
elif callable(getattr(the_file, "read", None)):
fh = the_file
fname = the_file
else:
raise TypeError("IMPORT: the_file argument has unknown type %s" % type(the_file))
# reading all directly b/c csv.reader takes only lines or a real fh in bytes
# and we usually have a "false" fh (uploadedfile.InMemoryUploadedFile) in strings
# (but we checked its size before!)
contents = fh.read().decode("UTF-8").split("\n")
# reading all directly b/c csv.reader takes only lines or a real fh in bytes
# and we usually have a "false" fh (uploadedfile.InMemoryUploadedFile) in strings
# (but we checked its size before!)
contents = fh.read().decode("UTF-8").split("\n")
# end of CSV read
fh.close()
# end of CSV read
fh.close()
# <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
......@@ -609,7 +615,6 @@ def import_ngramlists(fname, delimiter=DEFAULT_CSV_DELIM,
return result
def merge_ngramlists(new_lists={}, onto_corpus=None, del_originals=[]):
"""
Integrates an external terms table to the current one:
......@@ -832,3 +837,16 @@ def merge_ngramlists(new_lists={}, onto_corpus=None, del_originals=[]):
# return a log
return("\n".join(my_log))
def import_and_merge_ngramlists(file_contents, onto_corpus_id):
"""
A single function to run import_ngramlists and merge_ngramlists together
"""
new_lists = import_ngramlists(file_contents)
corpus_node = session.query(Node).filter(Node.id == onto_corpus_id).first()
# merge the new_lists onto those of the target corpus
log_msg = merge_ngramlists(new_lists, onto_corpus=corpus_node)
return log_msg
......@@ -12,12 +12,13 @@ from gargantext.util.http import APIView, get_parameters, JsonHttpResponse,\
from gargantext.util.db import session, aliased, bulk_insert
from gargantext.util.db_cache import cache
from sqlalchemy import tuple_
from gargantext.models import Ngram, NodeNgram, NodeNodeNgram, NodeNgramNgram
from gargantext.models import Ngram, NodeNgram, NodeNodeNgram, NodeNgramNgram, Node
from gargantext.util.lists import UnweightedList, Translations
# useful subroutines
from gargantext.util.ngramlists_tools import query_list, export_ngramlists, \
import_ngramlists, merge_ngramlists
import_ngramlists, merge_ngramlists, \
import_and_merge_ngramlists
from gargantext.util.group_tools import query_grouped_ngrams
......@@ -82,23 +83,22 @@ class CSVLists(APIView):
# ----------------------
csv_file = request.data['csvfile']
# import the csv
try:
new_lists = import_ngramlists(csv_file)
print("======new_lists=========================!!!")
# print(new_lists) # very long
del csv_file
csv_contents = csv_file.read().decode("UTF-8").split("\n")
csv_file.close()
del csv_file
# merge the new_lists onto those of the target corpus
log_msg = merge_ngramlists(new_lists, onto_corpus=corpus_node)
return JsonHttpResponse({
'log': log_msg,
}, 200)
# import the csv
# try:
log_msg = import_and_merge_ngramlists(csv_contents,
onto_corpus_id = corpus_node.id)
return JsonHttpResponse({
'log': log_msg,
}, 200)
except Exception as e:
return JsonHttpResponse({
'err': str(e),
}, 400)
# except Exception as e:
# return JsonHttpResponse({
# 'err': str(e),
# }, 400)
def patch(self,request):
"""
......@@ -361,6 +361,7 @@ class ListChange(APIView):
1) Checks current user authentication to prevent remote DB manipulation
2) Prepares self.list_objects from params
"""
if not request.user.is_authenticated():
raise Http404()
# can't use return in initial() (although 401 maybe better than 404)
......@@ -368,8 +369,20 @@ class ListChange(APIView):
# get validated params
self.params = get_parameters(request)
(self.base_list, self.change_list) = ListChange._validate(self.params)
if not len(self.change_list.items):
payload_ngrams = request.data['ngrams']
# print("no change_list in params but we got:", payload_ngrams)
# change_list can be in payload too
change_ngram_ids = [int(n) for n in payload_ngrams.split(',')]
if (not len(change_ngram_ids)):
raise ValidationException('The "ngrams" parameter requires one or more ngram_ids separated by comma')
else:
self.change_list = UnweightedList(change_ngram_ids)
def put(self, request):
"""
Adds one or more ngrams to a list.
......@@ -406,6 +419,7 @@ class ListChange(APIView):
'count_removed': len(self.base_list.items) - len(new_list.items),
}, 200)
@staticmethod
def _validate(params):
"""
......@@ -420,9 +434,9 @@ class ListChange(APIView):
if 'list' not in params:
raise ValidationException('The route /api/ngramlists/change requires a "list" \
parameter, for instance /api/ngramlists/change?list_id=42')
if 'ngrams' not in params:
raise ValidationException('The route /api/ngramlists/change requires an "ngrams"\
parameter, for instance /api/ngramlists/change?ngrams=1,2,3,4')
# if 'ngrams' not in params:
# raise ValidationException('The route /api/ngramlists/change requires an "ngrams"\
# parameter, for instance /api/ngramlists/change?ngrams=1,2,3,4')
# 2 x retrieval => 2 x UnweightedLists
# ------------------------------------
......@@ -430,17 +444,18 @@ class ListChange(APIView):
try:
base_list_id = int(params['list'])
# UnweightedList retrieved by id
base_list = UnweightedList(base_list_id)
except:
raise ValidationException('The "list" parameter requires an existing list id.')
base_list = UnweightedList(base_list_id)
change_ngram_ids = []
try:
change_ngram_ids = [int(n) for n in params['ngrams'].split(',')]
# UnweightedList created from items
change_list = UnweightedList(change_ngram_ids)
except:
raise ValidationException('The "ngrams" parameter requires one or more ngram_ids separated by comma')
# ngrams no longer mandatory inline, see payload check afterwards
pass
change_list = UnweightedList(change_ngram_ids)
return(base_list, change_list)
......
......@@ -1818,14 +1818,25 @@ function SaveLocalChanges() {
// For list modifications (add/delete), all http-requests
function CRUD( list_id , ngram_ids , http_method , callback) {
// ngramlists/change?node_id=42&ngram_ids=1,2
var the_url = window.location.origin+"/api/ngramlists/change?list="+list_id+"&ngrams="+ngram_ids.join(",");
// var the_url = window.location.origin+"/api/ngramlists/change?list="+list_id+"&ngrams="+ngram_ids.join(",");
var the_url = window.location.origin+"/api/ngramlists/change?list="+list_id;
// debug
// console.log(" ajax target: " + the_url + " (" + http_method + ")")
// 2016-10-05 pass PUT and DELETE ngrams in payload as if it was POSTs
// (to avoid too long urls that trigger Bad Gateway in production)
var myNgramsData = new FormData();
myNgramsData.append("ngrams", ngram_ids.join(","))
if(ngram_ids.length>0) {
$.ajax({
method: http_method,
async: true,
contentType: false,
processData: false,
data: myNgramsData,
url: the_url,
// data: args, // currently all data explicitly in the url (like a GET)
beforeSend: function(xhr) {
......
......@@ -16,316 +16,6 @@
}
</style>
<script type="text/javascript">
// initial vars
var projectId = "{{project.id | escapejs}}"
var corpusId = "{{corpus.id | escapejs }}"
/**
* Some html block templates to render responses after the ajax of goFirstGraph
*
* TODO use template_literals returned by lazy function or any other better templating
*/
var processingHtml='\
<div class="progress">\
<div class=" progress-bar progress-bar-striped active"\
role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 70%">\
<span>\
Processing\
</span>\
</div>\
</div>'
var finishedHtmlTemplate='\
<br>\
From: begin of corpus\
, To: end of corpus\
<br>\
<ul>\
<li>\
<a href="/projects/%%project_id%%/corpora/%%corpus_id%%/explorer?cooc_id=%%cooc_id%%&amp;distance=conditional&amp;bridgeness=5">\
<span class="glyphicon glyphicon-eye-open" style="font-size:150%"></span>\
~%%nb_nodes%% nodes,\
~%%nb_edges%% edges\
with <b>Conditional</b> distance\
</a>\
</li>\
<li>\
<a href="/projects/%%project_id%%/corpora/%%corpus_id%%/explorer?cooc_id=%%cooc_id%%&amp;distance=distributional&amp;bridgeness=5">\
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>\
Compute this graph with Distributional distance\
</a>\
</li>\
</ul>\
<br>'
var baseSkeletonTemplate='\
<div id="graph_%%cooc_id%%" class="first-graph">\
<div class="row">\
<div class="col-md-1 content"></div>\
<div class="col-md-5 content">\
<li>\
<h4 title="%%cooc_id%%">%%cooc_name%%</h4>\
%%cooc_date%%\
\
%%HERE_RESPONSE_DEPENDANT%%\
\
</li>\
</div>\
<div class="col-md-3 content">\
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"\
data-content="\
<ul>\
<li\
onclick=&quot;\
garganrest.nodes.delete(%%cooc_id%%, function(){$(\'#graph_\'+%%cooc_id%%).remove()});\
$(this).parent().parent().remove();\
&quot;>\
<a href=\'#\'>Delete this</a>\
</li>\
</ul>\
">\
<span class="glyphicon glyphicon-trash" aria-hidden="true"\
title=\'Delete this graph\'></span>\
</button>\
</div>\
</div>\
</div>'
/**
* function showNewCoocDiv(status_code, cooc_id, cooc_name, cooc_date)
*
* (uses the templates to make a new cooc html appear)
*
* @param 'progress_status' || 'finished_status'
* @param projectId
* @param corpusId
* @param coocId
* @param coocName
* @param coocDate
* @param nNodes (optional <=> if finished_status)
* @param nEdges (optional <=> if finished_status)
*/
function showNewCoocDiv(statusCode,
coocId, coocName, coocDate,
nbNodes, nbEdges) {
var resultHtml = baseSkeletonTemplate
// initial if
switch (statusCode) {
case "progress_status":
resultHtml = resultHtml.replace(/%%HERE_RESPONSE_DEPENDANT%%/,
processingHtml
)
break;
case "finished_status":
resultHtml = resultHtml.replace(/%%HERE_RESPONSE_DEPENDANT%%/,
finishedHtmlTemplate
)
break;
default:
console.warning("showNewCoocDiv: can't show div (Unknown statusCode", statusCode,")");
return false
}
// also replace template variables (thx c24b!)
resultHtml = resultHtml.replace(/%%project_id%%/g, projectId);
resultHtml = resultHtml.replace(/%%corpus_id%%/g, corpusId);
resultHtml = resultHtml.replace(/%%cooc_id%%/g, coocId);
resultHtml = resultHtml.replace(/%%cooc_name%%/g, coocName);
resultHtml = resultHtml.replace(/%%cooc_date%%/g, formatDateLikeDjango(coocDate));
if (typeof nbEdges != 'undefined' && typeof nbNodes != 'undefined') {
resultHtml = resultHtml.replace(/%%nb_nodes%%/g, nbNodes);
resultHtml = resultHtml.replace(/%%nb_edges%%/g, nbEdges);
}
// what do we do with those results ?
switch (statusCode) {
case "progress_status":
// render the result in DOM
$('#graph-list').append(resultHtml)
return null
case "finished_status":
// replace the previous results
var previousDiv = document.getElementById('graph_'+coocId)
previousDiv.innerHTML = resultHtml
return true
}
}
/**
* function goFirstGraph()
*
* 1) run a "save new graph" ajax on graph api
* 2) retrieve the new cooc_id in immediate response
* 3) monitor status of the async generation
*/
function goFirstGraph() {
// ajax config vars
var graphApi = "/api/projects/{{project.id}}/corpora/{{ corpus.id }}/explorer"
var graphParams = "saveOnly=True&distance=conditional&bridgeness=5"
// vars we'll get at creation steps (1: graph init, 2: graph finish)
var coocId = null // 1 & 2
var coocName = null // 1 & 2
var coocDate = null // 1 & 2
var nbNodes = null // 2
var nbEdges = null // 2
// run the "save new graph" ajax
// -----------------------------
// cf. data["state"] == "saveOnly"
$.ajax({
method: "GET",
url: graphApi + '?' + graphParams,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(data){
// console.log("data", data)
// 1 - retrieve the new coocId etc
if (data.id && data.name && data.date) {
coocId = data['id']
coocName = data['name']
coocDate = data['date']
}
// 2 - show the node with basic info and progressbar
showNewCoocDiv("progress_status",
coocId, coocName, coocDate)
// 3 - run status updating
// (it will call next step of
// showNewCoocDiv when ready)
keepCheckingGraphStatus(coocId)
},
error: function(result) {
console.log("result", result)
}
});
}
/**
* function keepCheckingGraphStatus(coocId)
*
* 1) keeps checking an API of cooc status
* 2) triggers showNewCoocDiv('finished_status') if ready
* or abandons after 5 attempts
*/
var nChecks = 0
var currentJob = null
var graphReadyFlag = false
function keepCheckingGraphStatus(coocNodeId) {
console.log("checking status", nChecks)
nChecks ++
// var the_status_url = "/api/nodes/"+coocNodeId+"/status?format=json"
var the_url_to_check = "/api/nodes/"+coocNodeId+"?fields[]=hyperdata&fields[]=name&fields[]=date"
// we get all hyperdata instead of just statuses because
// we'll need hyperdata.conditional.nodes and edges
// remote call
$.ajax({
type: 'GET',
url: the_url_to_check,
success: function(data) {
// TODO hyperdata would contains statuses too
// var statuses = data['hyperdata']['statuses']
var coocNodeName = data['name']
var coocNodeDate = data['date']
// test if ready like this for the moment
if (typeof data['hyperdata']['conditional'] != "undefined") {
console.log("GRAPH is READY", coocNodeId)
graphReadyFlag = true
var nbNodes = data['hyperdata']['conditional']['nodes']
var nbEdges = data['hyperdata']['conditional']['edges']
// console.warn("running callback for graph id:" + coocNodeId)
showNewCoocDiv("finished_status", coocNodeId,
coocNodeName, coocNodeDate,
nbNodes, nbEdges)
}
// stopping conditions
if (graphReadyFlag || nChecks > 5) {
// we abandon after 5 checks
console.warn("stopping status checks for graph:",
coocNodeId)
nChecks = 0
return null
}
// scheduled recursion
else {
console.log("GRAPH not ready yet...", coocNodeId)
// decreasing intervals (preserving DB while "loosing interest")
var nextTime = nChecks * 3000
// schedule next check
currentJob = setTimeout(function(){keepCheckingGraphStatus(coocNodeId)}, nextTime)
console.log("next status check in", nextTime/1000, "s" )
return false
}
},
error: function(data, s) {
console.warn("status GET: ajax err (s="+s+")")
console.log(data)
},
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
}
})
}
function stopCheckingGraphStatus() {
clearTimeout(currentJob)
}
// raw DB format: "2016-10-04T15:00:35Z" (ISOString)
// -----------------------
//
// but django default format: Oct. 4, 2016, 3:00 p.m.
// ------------------------
// cf. docs.djangoproject.com/en/dev/ref/settings/#date-format
//
// POSSIBLE: remove UTC from django and here (current timezone more practical)
function formatDateLikeDjango(isoDateTimeStr) {
asDate = new Date(isoDateTimeStr)
// ex: "Oct 4, 2016"
var newDateStr = asDate.toLocaleDateString(
'en-US',
{ 'year': 'numeric',
'month': 'short',
'day': 'numeric' })
// ex: 3:00 pm
var newTimeStr = asDate.toLocaleTimeString(
'en-US',
{ 'hour12': true,
'timeZone': 'UTC',
'hour': '2-digit',
'minute':'numeric'})
.toLowerCase()
// ex Oct 4, 2016, 3:00 pm => close enough !
return newDateStr + ', ' + newTimeStr
}
</script>
{% endblock %}
......@@ -337,7 +27,7 @@
<ol id="graph-list">
{% if coocs %}
{% for cooc in coocs %}
<div id="graph_{{cooc.id}}">
<div id="graph_{{cooc.id}}" class="graph-elt">
<div class="row">
<div class="col-md-1 content"></div>
......@@ -352,7 +42,7 @@
, To: {% if not cooc.hyperdata.parameters.end %} end of corpus {% else %} {{cooc.hyperdata.parameters.end}} {% endif %}
<br>
<ul>
<ul id="graph_{{cooc.id}}_finished">
<li>
<a href="/projects/{{project.id}}/corpora/{{corpus.id}}/explorer?cooc_id={{cooc.id}}&amp;distance=conditional&amp;bridgeness=5">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
......@@ -420,7 +110,7 @@
<div class=" progress-bar progress-bar-striped active"
role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 70%">
<span>
Processing (wait and reload the page)
Processing (please wait)
</span>
</div>
</div>
......@@ -445,10 +135,7 @@
data-content="
<ul>
<li
onclick=&quot;
garganrest.nodes.delete({{cooc.id}}, function(){$('#graph_'+{{cooc.id}}).remove()});
$(this).parent().parent().remove();
&quot;>
onclick=&quot;deleteGraph(event, {{cooc.id}})&quot;>
<a href='#'>Delete this</a>
</li>
</ul>
......@@ -504,4 +191,391 @@
{% endif %}
</ol>
<script type="text/javascript">
// initial vars
var projectId = "{{project.id | escapejs}}"
var corpusId = "{{corpus.id | escapejs }}"
/**
* Some html block templates to render responses after the ajax of goFirstGraph
*
* TODO use template_literals returned by lazy function or any other better templating
*/
var processingHtml='\
<div class="progress">\
<div class=" progress-bar progress-bar-striped active"\
role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 70%">\
<span>\
Processing\
</span>\
</div>\
</div>'
var finishedHtmlTemplate='\
<br>\
From: begin of corpus\
, To: end of corpus\
<br>\
<ul id="graph_%%cooc_id%%_finished">\
<li>\
<a href="/projects/%%project_id%%/corpora/%%corpus_id%%/explorer?cooc_id=%%cooc_id%%&amp;distance=conditional&amp;bridgeness=5">\
<span class="glyphicon glyphicon-eye-open" style="font-size:150%"></span>\
~%%nb_nodes%% nodes,\
~%%nb_edges%% edges\
with <b>Conditional</b> distance\
</a>\
</li>\
<li>\
<a href="/projects/%%project_id%%/corpora/%%corpus_id%%/explorer?cooc_id=%%cooc_id%%&amp;distance=distributional&amp;bridgeness=5">\
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>\
Compute this graph with Distributional distance\
</a>\
</li>\
</ul>\
<br>'
var baseSkeletonTemplate='\
<div id="graph_%%cooc_id%%" class="graph-elt first-graph">\
<div class="row">\
<div class="col-md-1 content"></div>\
<div class="col-md-5 content">\
<li>\
<h4 title="%%cooc_id%%">%%cooc_name%%</h4>\
%%cooc_date%%\
\
%%HERE_RESPONSE_DEPENDANT%%\
\
</li>\
</div>\
<div class="col-md-3 content">\
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"\
data-content="\
<ul>\
<li\
onclick=&quot;\
garganrest.nodes.delete(%%cooc_id%%, function(){$(\'#graph_\'+%%cooc_id%%).remove()});\
$(this).parent().parent().remove();\
&quot;>\
<a href=\'#\'>Delete this</a>\
</li>\
</ul>\
">\
<span class="glyphicon glyphicon-trash" aria-hidden="true"\
title=\'Delete this graph\'></span>\
</button>\
</div>\
</div>\
</div>'
// -----------------------------------------------------
// for testing on client side which need refresh
// -----------------------------------------------------
function testActiveGraphs() {
var activeGraphIds = []
for (var i in graphIds) {
var grid = graphIds[i]
if ((! document.getElementById('graph_'+grid+'_finished'))
&& (! trashedIds[grid])) {
activeGraphIds.push(grid)
}
}
return activeGraphIds
}
/**
* function showNewCoocDiv(status_code, cooc_id, cooc_name, cooc_date)
*
* (uses the templates to make a new cooc html appear)
*
* @param 'progress_status' || 'finished_status'
* @param projectId
* @param corpusId
* @param coocId
* @param coocName
* @param coocDate
* @param nNodes (optional <=> if finished_status)
* @param nEdges (optional <=> if finished_status)
*/
function showNewCoocDiv(statusCode,
coocId, coocName, coocDate,
nbNodes, nbEdges) {
var resultHtml = baseSkeletonTemplate
// initial if
switch (statusCode) {
case "progress_status":
resultHtml = resultHtml.replace(/%%HERE_RESPONSE_DEPENDANT%%/,
processingHtml
)
break;
case "finished_status":
resultHtml = resultHtml.replace(/%%HERE_RESPONSE_DEPENDANT%%/,
finishedHtmlTemplate
)
break;
default:
console.warning("showNewCoocDiv: can't show div (Unknown statusCode", statusCode,")");
return false
}
// also replace template variables (thx c24b!)
resultHtml = resultHtml.replace(/%%project_id%%/g, projectId);
resultHtml = resultHtml.replace(/%%corpus_id%%/g, corpusId);
resultHtml = resultHtml.replace(/%%cooc_id%%/g, coocId);
resultHtml = resultHtml.replace(/%%cooc_name%%/g, coocName);
resultHtml = resultHtml.replace(/%%cooc_date%%/g, formatDateLikeDjango(coocDate));
if (typeof nbEdges != 'undefined' && typeof nbNodes != 'undefined') {
resultHtml = resultHtml.replace(/%%nb_nodes%%/g, nbNodes);
resultHtml = resultHtml.replace(/%%nb_edges%%/g, nbEdges);
}
// what do we do with those results ?
switch (statusCode) {
case "progress_status":
// render the result in DOM
$('#graph-list').append(resultHtml)
return null
case "finished_status":
// replace the previous results
var previousDiv = document.getElementById('graph_'+coocId)
previousDiv.innerHTML = resultHtml
return true
}
}
/**
* function goFirstGraph()
*
* 1) run a "save new graph" ajax on graph api
* 2) retrieve the new cooc_id in immediate response
* 3) monitor status of the async generation
*/
function goFirstGraph() {
// ajax config vars
var graphApi = "/api/projects/{{project.id}}/corpora/{{ corpus.id }}/explorer"
var graphParams = "saveOnly=True&distance=conditional&bridgeness=5"
// vars we'll get at creation steps (1: graph init, 2: graph finish)
var coocId = null // 1 & 2
var coocName = null // 1 & 2
var coocDate = null // 1 & 2
var nbNodes = null // 2
var nbEdges = null // 2
// run the "save new graph" ajax
// -----------------------------
// cf. data["state"] == "saveOnly"
$.ajax({
method: "GET",
url: graphApi + '?' + graphParams,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(data){
// console.log("data", data)
// 1 - retrieve the new coocId etc
if (data.id && data.name && data.date) {
coocId = data['id']
coocName = data['name']
coocDate = data['date']
}
// 2 - show the node with basic info and progressbar
showNewCoocDiv("progress_status",
coocId, coocName, coocDate)
// 3 - run status updating
// (it will call next step of
// showNewCoocDiv when ready)
console.log("goFirstGraph => keepCheckingGraphStatus on:", coocId)
keepCheckingGraphStatus(coocId)
},
error: function(result) {
console.log("result", result)
}
});
}
/**
* function keepCheckingGraphStatus(coocId)
*
* 1) keeps checking an API of cooc status
* 2) triggers showNewCoocDiv('finished_status') if ready
* or abandons after 5 attempts
*/
var currentJob = null
function keepCheckingGraphStatus(coocNodeId, nChecksArg) {
var graphReadyFlag = false
var nChecks = 0
if (typeof nChecksArg != 'undefined') {
nChecks = nChecksArg
}
console.log("checking status", nChecks)
nChecks ++
// var the_status_url = "/api/nodes/"+coocNodeId+"/status?format=json"
var the_url_to_check = "/api/nodes/"+coocNodeId+"?fields[]=hyperdata&fields[]=name&fields[]=date"
// we get all hyperdata instead of just statuses because
// we'll need hyperdata.conditional.nodes and edges
// remote call
$.ajax({
type: 'GET',
url: the_url_to_check,
success: function(data) {
// TODO hyperdata would contains statuses too
// var statuses = data['hyperdata']['statuses']
var coocNodeName = data['name']
var coocNodeDate = data['date']
// test if ready like this for the moment
if (typeof data['hyperdata']['conditional'] != "undefined") {
console.log("GRAPH is READY", coocNodeId)
graphReadyFlag = true
var nbNodes = data['hyperdata']['conditional']['nodes']
var nbEdges = data['hyperdata']['conditional']['edges']
// console.warn("running callback for graph id:" + coocNodeId)
showNewCoocDiv("finished_status", coocNodeId,
coocNodeName, coocNodeDate,
nbNodes, nbEdges)
}
// stopping conditions
if (graphReadyFlag || nChecks > 5) {
// we abandon after 5 checks
console.warn("stopping status checks for graph:",
coocNodeId)
return null
}
// scheduled recursion
else {
console.log("GRAPH not ready yet...", coocNodeId)
// decreasing intervals (preserving DB while "loosing interest")
var nextTime = nChecks * 3000
// schedule next check
currentJob = setTimeout(function(){keepCheckingGraphStatus(coocNodeId, nChecks)}, nextTime)
console.log("next status check in", nextTime/1000, "s" )
return false
}
},
error: function(data, s) {
console.warn("status GET: ajax err (s="+s+")")
console.log(data)
},
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
}
})
}
function stopCheckingGraphStatus() {
clearTimeout(currentJob)
}
// raw DB format: "2016-10-04T15:00:35Z" (ISOString)
// -----------------------
//
// but django default format: Oct. 4, 2016, 3:00 p.m.
// ------------------------
// cf. docs.djangoproject.com/en/dev/ref/settings/#date-format
//
// POSSIBLE: remove UTC from django and here (current timezone more practical)
function formatDateLikeDjango(isoDateTimeStr) {
asDate = new Date(isoDateTimeStr)
// ex: "Oct 4, 2016"
var newDateStr = asDate.toLocaleDateString(
'en-US',
{ 'year': 'numeric',
'month': 'short',
'day': 'numeric' })
// ex: 3:00 pm
var newTimeStr = asDate.toLocaleTimeString(
'en-US',
{ 'hour12': true,
'timeZone': 'UTC',
'hour': '2-digit',
'minute':'numeric'})
.toLowerCase()
// ex Oct 4, 2016, 3:00 pm => close enough !
return newDateStr + ', ' + newTimeStr
}
function deleteGraph(e, graphId) {
// prevents scroll back to top of page
e.preventDefault()
// register pending operation
trashedIds[graphId] = true ;
// POSSIBLE visual loader wheel
// REST and callback
garganrest.nodes.delete(
graphId,
function(){
$('#graph_'+graphId).remove()
delete trashedIds[graphId]
// remove any popover too
$('.popover').remove();
}
);
}
// main
// all graph ids
var graphDivs = document.getElementsByClassName('graph-elt')
var graphIds = []
// for graph ids whose delete is pending
var trashedIds = {}
for (var i = 0 ; i < graphDivs.length ; i++) {
// ex: graph_48
divId = graphDivs[i].id
if (divId) {
var graphId = divId.match(/[0-9]+$/).pop()
graphIds.push(graphId)
}
}
var activeGraphIds = testActiveGraphs()
if (activeGraphIds.length) {
// initial checks if page reloaded with active corpora
for (var i in activeGraphIds) {
// !careful with closure, async function & loop on i
// cf stackoverflow.com/a/21819961/2489184
(function(i) {
var myCoocId = activeGraphIds[i]
keepCheckingGraphStatus(activeGraphIds, 0)
})(i)
}
}
console.warn("hello", activeGraphIds)
</script>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment