Commit 4a5a0cd2 authored by delanoe's avatar delanoe

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

parents 47398dcf b5ff7a7f
#!/bin/bash
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/srv/gargantext_lib/taggers/nlpserver/TurboParser/deps/local/lib:"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/srv/gargantext_lib/taggers/nlpserver:/srv/gargantext_lib/taggers/nlpserver/TurboParser/deps/local/lib:"
if [[ ! "$VIRTUAL_ENV" ]]
then
......
......@@ -27,7 +27,6 @@ def is_stop_word(ngram, stop_words=None):
# , "(.*)(\.)(.*)" trop fort (enlève les sigles !)
, "(.*)(\,)(.*)"
, "(.*)(< ?/?p ?>)(.*)" # marques de paragraphes
, "(.*)(study|elsevier)(.*)"
, "(.*)\b(xx|xi|xv)\b(.*)"
, "(.*)(result)(.*)"
, "(.*)(year|année|nombre|moitié)(.*)"
......@@ -37,6 +36,87 @@ def is_stop_word(ngram, stop_words=None):
, "(.*)(terme)(.*)"
, "(.*)(différent)(.*)"
, "(.*)(travers)(.*)"
# academic stamps
, ".*\belsevier\b.*"
, ".*\bwiley\b.*)"
, ".*\bspringer\b.*"
, ".*university press\b.*"
, ".*\bstudy\b.*"
# academic terms when alone ~~> usually not informative
, "hypothes[ie]s$"
, "analys[ie]s$"
, "bas[ie]s$"
, "online$"
, "importance$"
, "uses?$"
, "cases?$"
, "effects?$"
, "times?$"
, "methods?$"
, "types?$"
, "evidences?$"
, "findings?$"
, "relations?$"
, "terms?$"
, "procedures?$"
, "factors?$"
, "reports?$"
, "changes?$"
, "facts?$"
, "others?$"
, "applications?$"
, "periods?$"
, "investigations?$"
, "orders?$"
, "forms?$"
, "conditions?$"
, "situations?$"
, "papers?$"
, "relationships?$"
, "values?$"
, "areas?$"
, "techniques?$"
, "means?$"
, "conclusions?$"
, "comparisons?$"
, "parts?$"
, "amounts?$"
, "aims?$"
, "lacks?$"
, "issues?$"
, "ways?$"
, "ranges?$"
, "models?$"
, "articles?$"
, "series?$"
, "totals?$"
, "influences?$"
, "journals?$"
, "rules?$"
, "persons?$"
, "abstracts?$"
, "(?:book)? reviews?$"
, "process(?:es)?$"
, "approach(?:es)?$"
, "theor(?:y|ies)?$"
, "methodolog(?:y|ies)?$"
, "similarit(?:y|ies)?$"
, "possibilit(?:y|ies)?$"
, "stud(?:y|ies)?$"
# non-thematic or non-NP expressions
, "none$"
, "other(?: hand)?$"
, "whereas$"
, "usually$"
, "and$"
# , "vol$"
, "eds?$"
, "ltd$"
, "copyright$"
, "e-?mails?$"
, ".*="
, "=.*"
, "further(?:more)?$"
, "(.*)(:|\|)(.*)"
] :
compiled_regexes.append(compile(regex))
......
......@@ -85,8 +85,8 @@ class CSVLists(APIView):
# import the csv
try:
new_lists = import_ngramlists(csv_file)
print("===============================!!!")
print(new_lists)
print("======new_lists=========================!!!")
# print(new_lists) # very long
del csv_file
# merge the new_lists onto those of the target corpus
......
......@@ -38,7 +38,6 @@ def countCooccurrences( corpus_id=None , cooc_id=None
limit :: Int
'''
# FIXME remove the lines below after factorization of parameters
parameters = dict()
parameters['field1'] = field1
......@@ -68,14 +67,20 @@ def countCooccurrences( corpus_id=None , cooc_id=None
cooc_id = coocNode.id
else :
cooc_id = int(cooc_id[0])
# when cooc_id preexisted, but we want to continue (reset = True)
# (to give new contents to this cooc_id)
elif reset:
print("GRAPH #%s ... Counting new cooccurrences data." % cooc_id)
session.query( NodeNgramNgram ).filter( NodeNgramNgram.node_id == cooc_id ).delete()
session.commit()
# when cooc_id preexisted and we just want to load it (reset = False)
else:
print("GRAPH #%s ... Loading cooccurrences computed already." % cooc_id)
cooc = session.query( NodeNgramNgram.ngram1_id, NodeNgramNgram.ngram2_id, NodeNgramNgram.weight ).filter( NodeNgramNgram.node_id == cooc_id ).all()
return(int(cooc_id),WeightedMatrix(cooc))
if reset == True :
session.query( NodeNgramNgram ).filter( NodeNgramNgram.node_id == cooc_id ).delete()
session.commit()
NodeNgramX = aliased(NodeNgram)
......@@ -202,29 +207,29 @@ def countCooccurrences( corpus_id=None , cooc_id=None
#cooc_query = cooc_query.order_by(desc('cooc_score'))
matrix = WeightedMatrix(cooc_query)
print("GRAPH #%s Filtering the matrix with Map and Group Lists." % cooc_id)
cooc = filterMatrix(matrix, mapList_id, groupList_id)
parameters['MapList_id'] = str(mapList_id)
parameters['GroupList_id'] = str(groupList_id)
# TODO factorize savings on db
if save_on_db:
# Saving the cooccurrences
cooc.save(cooc_id)
print("GRAPH #%s ... Node Cooccurrence Matrix saved" % cooc_id)
# Saving the parameters
print("GRAPH #%s ... Parameters saved in Node." % cooc_id)
coocNode = session.query(Node).filter(Node.id==cooc_id).first()
coocNode.hyperdata["parameters"] = dict()
coocNode.hyperdata["parameters"] = parameters
coocNode.save_hyperdata()
session.commit()
#data = cooc2graph(coocNode.id, cooc, distance=distance, bridgeness=bridgeness)
#return data
return(coocNode.id, cooc)
......@@ -51,7 +51,7 @@ def compute_graph( corpus_id=None , cooc_id=None
, mapList_id=mapList_id , groupList_id=groupList_id
, isMonopartite=True , threshold = threshold
, distance=distance , bridgeness=bridgeness
, save_on_db = True
, save_on_db = True , reset = reset
)
print("GRAPH #%d ... Cooccurrences computed." % (cooc_id))
......@@ -73,13 +73,13 @@ def compute_graph( corpus_id=None , cooc_id=None
node.hyperdata[distance] = dict()
node.hyperdata[distance][bridgeness] = data
node.hyperdata[distance]["nodes"] = len(G.nodes())
node.hyperdata[distance]["edges"] = len(G.edges())
node.save_hyperdata()
session.commit()
print("GRAPH #%d ... Notify by email owner of the graph." % cooc_id)
corpus = session.query(Node).filter(Node.id==corpus_id).first()
notify_owner(corpus, cooc_id, distance, bridgeness)
......@@ -99,25 +99,25 @@ def get_graph( request=None , corpus=None
'''
Get_graph : main steps:
0) Check the parameters
get_graph :: GraphParameters -> Either (Dic Nodes Links) (Dic State Length)
where type Length = Int
get_graph first checks the parameters and return either graph data or a dict with
state "type" with an integer to indicate the size of the parameter
get_graph first checks the parameters and return either graph data or a dict with
state "type" with an integer to indicate the size of the parameter
(maybe we could add a String in that step to factor and give here the error message)
1) compute_graph (see function above)
2) return graph
'''
overwrite_node_contents = False
# Case of graph has been computed already
if cooc_id is not None:
print("GRAPH#%d ... Loading data already computed." % int(cooc_id))
node = session.query(Node).filter(Node.id == cooc_id).first()
# Structure of the Node.hyperdata[distance][bridbeness]
# All parameters (but distance and bridgeness)
# are in Node.hyperdata["parameters"]
......@@ -130,6 +130,25 @@ def get_graph( request=None , corpus=None
if graph.get(str(bridgeness), None) is not None:
return graph[str(bridgeness)]
# new graph: we give it an empty node with new id and status
elif saveOnly:
# NB: we do creation already here (instead of same in countCooccurrences)
# to guarantee a unique ref id to the saveOnly graph (async generation)
new_node = corpus.add_child(
typename = "COOCCURRENCES",
name = "GRAPH (in corpus %s)" % corpus.id
)
session.add(new_node)
session.commit()
cooc_id = new_node.id
cooc_name = new_node.name
cooc_date = new_node.date
# and the empty content will need redoing by countCooccurrences
overwrite_node_contents = True
print("GRAPH #%d ... Created new empty data node for saveOnly" % int(cooc_id))
# Case of graph has not been computed already
# First, check the parameters
......@@ -198,10 +217,14 @@ def get_graph( request=None , corpus=None
, mapList_id=mapList_id , groupList_id=groupList_id
, isMonopartite=True , threshold = threshold
, distance=distance , bridgeness=bridgeness
, save_on_db = True
, save_on_db = True , reset=overwrite_node_contents
#, limit=size
)
return {"state" : "saveOnly"}
return {"state" : "saveOnly",
"target_id" : cooc_id,
"target_name": cooc_name,
"target_date": cooc_date}
elif corpus_size > graph_constraints['corpusMax']:
# Then compute cooc asynchronously with celery
......@@ -211,10 +234,10 @@ def get_graph( request=None , corpus=None
, mapList_id=mapList_id , groupList_id=groupList_id
, isMonopartite=True , threshold = threshold
, distance=distance , bridgeness=bridgeness
, save_on_db = True
, save_on_db = True , reset=overwrite_node_contents
#, limit=size
)
# Dict to inform user that corpus maximum is reached
# Dict to inform user that corpus maximum is reached
# then graph is computed asynchronously
return {"state" : "corpusMax", "length" : corpus_size}
......@@ -230,7 +253,7 @@ def get_graph( request=None , corpus=None
, mapList_id=mapList_id , groupList_id=groupList_id
, isMonopartite=True , threshold = threshold
, distance=distance , bridgeness=bridgeness
, save_on_db = True
, save_on_db = True , reset=overwrite_node_contents
#, limit=size
)
......
......@@ -18,6 +18,11 @@ class Graph(APIView):
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():
......@@ -56,7 +61,6 @@ class Graph(APIView):
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 )
......@@ -100,7 +104,7 @@ class Graph(APIView):
, field1=field1 , field2=field2
, mapList_id = mapList_id , groupList_id = groupList_id
, start=start , end=end
, threshold =threshold
, threshold =threshold
, distance=distance , bridgeness=bridgeness
, saveOnly=saveOnly
)
......@@ -127,10 +131,12 @@ class Graph(APIView):
# async data case
link = "http://%s/projects/%d/corpora/%d/myGraphs" % (request.get_host(), corpus.parent_id, corpus.id)
return JsonHttpResponse({
'msg': '''Your graph is saved:
'id': data["target_id"],
'name': data["target_name"],
'date': data["target_date"],
'msg': '''Your graph is being saved:
%s
''' % format_html(link),
''' % format_html(link)
}, status=200)
elif data["state"] == "corpusMin":
......
......@@ -78,6 +78,8 @@ def myGraphs(request, project_id, corpus_id):
#coocs_count[cooc.id] = len(cooc_nodes)
coocs_count[cooc.id] = len([cooc_node for cooc_node in cooc_nodes if cooc_node[1] > 1])
print("coocs_count a posteriori", coocs_count)
return render(
template_name = 'pages/corpora/myGraphs.html',
request = request,
......
......@@ -47,8 +47,9 @@
* - simplify UpdateTable
* - clarify cruds
* - fine-grained "created groups" handling
* - local cache for user params
*
* @version 1.3
* @version 1.4
*
* @requires jquery.dynatable
* @requires d3
......@@ -59,6 +60,7 @@
// GLOBALS <=> INTERACTIVE STATUS etc
// =============================================================================
var corpusId = getIDFromURL("corpora")
// current ngram infos (<-> row data)
// ----------------------------------
......@@ -174,6 +176,34 @@ var tableSpan ;
var corpusesList = {}
// TABLE'S PARAMS' getter
// -----------------------
// Fetch all current user params for sorting, filtering...
// @param aDynatable (eg: MyTable.data('dynatable'))
function getSelectedParams(aDynatable) {
var tbsettings = aDynatable.settings.dataset
var sorting_obj= tbsettings.sorts
var sort_type = null
if (sorting_obj) {
sort_type = Object.keys(sorting_obj).pop()
}
// returns a "picklistParams object"
return {
'search' : tbsettings.queries['search'],
'multiw' : tbsettings.queries['my_termtype_filter'],
'gtlists': tbsettings.queries['my_state_filter'],
'perpp' : tbsettings.perPage,
'sortk' : sort_type,
'sortdirec': sorting_obj ? sorting_obj[sort_type] : null,
'from' : TheBuffer ? TheBuffer[0] : null,
'to' : TheBuffer ? TheBuffer[1] : null
}
}
// =============================================================================
// CACHE MANAGEMENT
// =============================================================================
......@@ -190,18 +220,17 @@ window.onbeforeunload = saveParamsToCache;
// always called at page close/quit
// £TODO use url instead of corpusId+'/terms' as prefix
function saveParamsToCache() {
var corpusId = getIDFromURL("corpora")
var search_filter_status = MyTable.data('dynatable').settings.dataset.queries['search']
var state_filter_status = MyTable.data('dynatable').settings.dataset.queries['my_state_filter']
var type_filter_status = MyTable.data('dynatable').settings.dataset.queries['my_termtype_filter']
// var page_status = document.getElementsByClassName("dynatable-page-link dynatable-active-page")[0].getAttribute("data-dynatable-page")
var per_page_status = MyTable.data('dynatable').settings.dataset.perPage
var params = getSelectedParams(MyTable.data('dynatable'))
// ex: {'name':1} or {'score':-1}
var sorting_obj = MyTable.data('dynatable').settings.dataset.sorts
var sort_type_status = Object.keys(sorting_obj).pop()
var sort_direction_status = sorting_obj[sort_type_status]
var search_filter_status = params.search
var state_filter_status = params.gtlists
var type_filter_status = params.multiw
var per_page_status = params.perpp
var sort_type_status = params.sortk
var sort_direction_status = params.sortdirec
var from_status = params.from
var to_status = params.to
// keys and values are str only so we use path-like keys
if (search_filter_status) {
......@@ -226,6 +255,13 @@ function saveParamsToCache() {
localStorage.removeItem(corpusId+'/terms/type')
}
if (typeof(from_status) != "undefined"
&& typeof(to_status) != "undefined") {
console.log("saving STAT")
localStorage[corpusId+'/terms/fromVal'] = from_status
localStorage[corpusId+'/terms/toVal'] = to_status
}
localStorage[corpusId+'/terms/perPage'] = per_page_status
// localStorage[corpusId+'/terms/page'] = page_status
......@@ -235,40 +271,19 @@ function saveParamsToCache() {
return null;
}
// always called after MyTable init
// called after 1st Ajax and given to table init in Main as filtersParams
function restoreSettingsFromCache() {
var corpusId = getIDFromURL("corpora")
// var had_page = localStorage[corpusId+'/terms/page']
var had_type = localStorage[corpusId+'/terms/type']
var had_state = localStorage[corpusId+'/terms/state']
var had_search = localStorage[corpusId+'/terms/search']
var had_perPage = localStorage[corpusId+'/terms/perPage']
var had_sortType = localStorage[corpusId+'/terms/sortType']
var sortDirection = localStorage[corpusId+'/terms/sortDirection']
// if (had_page) {
// MyTable.data('dynatable').paginationPage.set(had_page);
// }
if (had_type) {
MyTable.data('dynatable').settings.dataset.queries['my_termtype_filter'] = had_type
}
if (had_state) {
MyTable.data('dynatable').settings.dataset.queries['my_state_filter'] = had_state
}
if (had_search) {
MyTable.data('dynatable').settings.dataset.queries['search'] = had_search
// also returns a "picklistParams object"
return {
'search' : localStorage[corpusId+'/terms/search'],
'multiw' : localStorage[corpusId+'/terms/type'],
'gtlists': localStorage[corpusId+'/terms/state'],
'perpp' : localStorage[corpusId+'/terms/perPage'],
'sortk' : localStorage[corpusId+'/terms/sortType'],
'sortdirec':localStorage[corpusId+'/terms/sortDirection'],
'from': localStorage[corpusId+'/terms/fromVal'],
'to': localStorage[corpusId+'/terms/toVal']
}
if (had_perPage) {
MyTable.data('dynatable').paginationPerPage.set(had_perPage)
}
if (had_sortType) {
MyTable.data('dynatable').sorts.clear();
MyTable.data('dynatable').sorts.add(had_sortType, sortDirection)
console.log("added sort",had_sortType)
}
// re-process to makes the changes visible
MyTable.data('dynatable').process()
return null;
}
......@@ -346,54 +361,57 @@ function GetUserPortfolio() {
}
//Getting a corpusB-list and intersecting it with current corpusA-miamlist.
function printCorpuses() {
console.log( "!!!!!!!! in printCorpuses() !!!!!!!! " )
pr(corpusesList)
var selected = $('input[name=optradio]:checked')[0].id.split("_")
var sel_p = selected[0], sel_c=selected[1]
var current_corpus = getIDFromURL("corpora")
var selected_corpus = corpusesList[sel_p]["corpuses"][sel_c]["id"]
pr("current corpus: "+current_corpus)
var the_ids = []
the_ids.push( current_corpus )
the_ids.push( corpusesList[sel_p]["corpuses"][sel_c]["id"] )
$("#closecorpuses").click();
// EXTERNAL CORPUS TO COMPARE:
var whichlist = $('input[name=whichlist]:checked').val()
var url = window.location.origin+"/api/node/"+selected_corpus+"/ngrams/list/"+whichlist//+"?custom"
console.log( url )
GET_( url , function(results, url) {
if(Object.keys( results ).length>0) {
var sub_ngrams_data = {
"ngrams":[],
"scores": $.extend({}, OriginalNG.scores)
}
for(var i in OriginalNG["records"].ngrams) {
if( results[ OriginalNG["records"].ngrams[i].id] ) {
var a_ngram = OriginalNG["records"].ngrams[i]
sub_ngrams_data["records"].push( a_ngram )
}
// if( results[ OriginalNG["records"][i].id] && OriginalNG["records"][i].name.split(" ").length==1 ) {
// if( OriginalNG["map"][ i ] ) {
// var a_ngram = OriginalNG["records"][i]
// // a_ngram["state"] = System[0]["statesD"]["delete"]
// sub_ngrams_data["ngrams"].push( a_ngram )
// }
// }
}
var result = MainTableAndCharts(sub_ngrams_data , OriginalNG.scores.initial , "filter_all")
}
});
}
// function printCorpuses() {
// console.log( "!!!!!!!! in printCorpuses() !!!!!!!! " )
// pr(corpusesList)
//
// var selected = $('input[name=optradio]:checked')[0].id.split("_")
// var sel_p = selected[0], sel_c=selected[1]
//
// var current_corpus = getIDFromURL("corpora")
//
// var selected_corpus = corpusesList[sel_p]["corpuses"][sel_c]["id"]
// pr("current corpus: "+current_corpus)
// var the_ids = []
// the_ids.push( current_corpus )
// the_ids.push( corpusesList[sel_p]["corpuses"][sel_c]["id"] )
//
// $("#closecorpuses").click();
//
// // EXTERNAL CORPUS TO COMPARE:
// var whichlist = $('input[name=whichlist]:checked').val()
// var url = window.location.origin+"/api/node/"+selected_corpus+"/ngrams/list/"+whichlist//+"?custom"
// console.log( url )
//
//
// GET_( url , function(results, url) {
// if(Object.keys( results ).length>0) {
// var sub_ngrams_data = {
// "ngrams":[],
// "scores": $.extend({}, OriginalNG.scores)
// }
// for(var i in OriginalNG["records"].ngrams) {
// if( results[ OriginalNG["records"].ngrams[i].id] ) {
// var a_ngram = OriginalNG["records"].ngrams[i]
// sub_ngrams_data["records"].push( a_ngram )
// }
// // if( results[ OriginalNG["records"][i].id] && OriginalNG["records"][i].name.split(" ").length==1 ) {
// // if( OriginalNG["map"][ i ] ) {
// // var a_ngram = OriginalNG["records"][i]
// // // a_ngram["state"] = System[0]["statesD"]["delete"]
// // sub_ngrams_data["ngrams"].push( a_ngram )
// // }
// // }
// }
// var result = MainTableAndCharts(sub_ngrams_data , OriginalNG.scores.initial , {})
// }
// });
// }
// Updates most global var TheBuffer
// TODO should be distinct from time range (of doc view)
// and adapt more for freq ranges
function Push2Buffer( NewVal ) {
if ( TheBuffer == false) {
if( ! NewVal ) {
......@@ -460,6 +478,7 @@ function Final_UpdateTable( action ) {
}
}
// todo: check if necessary to re-init like this
MyTable = $('#my-ajax-table').dynatable({
dataset: {
records: TimeRange
......@@ -573,13 +592,9 @@ function removeActiveGroupFrameAndUpdate() {
// we also close the open sublists in case some of them don't exist any more
vizopenGroup = {}
// reprocess from current record states
var currentStateFilter = MyTable.data('dynatable').settings.dataset.queries['my_state_filter']
// TODO when several scores, use cache like currentStateFilter
var FirstScore = OriginalNG.scores.initial
// console.log("full re-create table")
MainTableAndCharts(AjaxRecords, FirstScore , currentStateFilter)
// reprocess from current record states and params
var FirstScoreParam = OriginalNG.scores.initial // £TODO use when several scores
MainTableAndCharts(AjaxRecords, FirstScoreParam , getSelectedParams(MyTable.data('dynatable')),"removeActiveGroupFrameAndUpdate")
}
// for click open/close
......@@ -1909,17 +1924,14 @@ function GROUPCRUDS( groupnode_id , send_data, http_method , callback) {
* - AfterAjax for map items
* - ??? for stop items
* 3. Creates the scores distribution chart over table
* 4. Set up Search div
* 4. Set up Search div and initialize user filter statuses
*
* @param ngdata: OriginalNG['records']
* @param initial: initial score type "occs" or "tfidf"
* @param search_filter: value among {0,1,2,'reset'} (see #picklistmenu options)
*
* TODO replace by new @param filters (multiple) for all cached values
* (would allow us to use them directly in settings instead of a posteriori
* with restoreSettingsFromCache)
* @param ngdata: OriginalNG['records']
* @param initial: initial score type "occs" or "tfidf" => £TODO multiscore
* @param filtersParams: contains the option values in a "picklistParams object"
* It can be {} or custom/cached keys for init of filters
*/
function MainTableAndCharts( ngdata , initial , search_filter) {
function MainTableAndCharts( ngdata , initial , filtersParams, callerLabel) {
// debug
// alert("refresh main")
......@@ -1930,8 +1942,10 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
// console.log(ngdata)
// console.log("initial:") //
// console.log(initial)
// console.log("search_filter:") // eg 'filter_all'
// console.log(search_filter)
console.log("filtersParams:") // eg {'lists': filter_all'}
if(typeof(filtersParams) != 'undefined') {console.log(filtersParams)} else {console.log('pas de params')}
console.log("callerLabel:")
if(typeof(callerLabel) != 'undefined') {console.log(callerLabel)} else {console.log('pas de callerLabel')}
// console.log(" = = = = / MainTableAndCharts: = = = = ")
// console.log("")
......@@ -2154,6 +2168,7 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
// .interpolate("monotone")
// .renderDataPoints({radius: 2, fillOpacity: 0.8, strokeOpacity: 0.8})
.brushOn(false)
.rangeChart(volumeChart)
.title(function (d) {
if (isNaN(d.data.value))  {
console.warn(JSON.stringify(d))
......@@ -2173,6 +2188,7 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
LineChart.render()
// selectable barchart
volumeChart.width(800)
.height(100)
.margins({top: 30, right: 50, bottom: 20, left: 40})
......@@ -2188,6 +2204,9 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
.renderlet(function (chart) {
chart.select("g.y").style("display", "none");
})
// ------------------------------------------------------
// main hook between Chart => Buffer => Final_UpdateTable
// ------------------------------------------------------
.on("filtered", function (chart) {
dc.events.trigger(function () {
var chartfilt = chart.filter()
......@@ -2211,8 +2230,13 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
volumeChart.filterAll();
dc.redrawAll();
// expose selection brush
var ourBrush = volumeChart.brush()
// --------------------------
// DYNATABLE initialization
// --------------------------
// writes in the app scope var MyTable
MyTable = []
MyTable = $('#my-ajax-table').dynatable({
dataset: {
......@@ -2267,19 +2291,13 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
}
}
MyTable.data('dynatable').sorts.clear();
MyTable.data('dynatable').sorts.add('score', 1) // 1=DESCENDING,
// MyTable.data('dynatable').process();
MyTable.data('dynatable').paginationPage.set(1);
MyTable.data('dynatable').paginationPerPage.set(50); // default:10
MyTable.data('dynatable').process();
// hook on page change
MyTable.bind('dynatable:page:set', tidyAfterPageSetUpdate)
// hook on any type of update
MyTable.bind('dynatable:afterUpdate', tidyAfterUpdate)
// £TODO multiscore
// // // $("#score_column_id").children()[0].text = FirstScore
// // // // MyTable.data('dynatable').process();
......@@ -2299,7 +2317,7 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
}
}
// second filter named 'my_termtype_filter'
// second filter named 'my_termtype_filter' for gargantext lists (map, )
MyTable.data('dynatable').queries
.functions['my_termtype_filter'] = function(record,selectedValue) {
if (selectedValue == 'reset') {
......@@ -2316,19 +2334,52 @@ function MainTableAndCharts( ngdata , initial , search_filter) {
}
// and set these filters' initial status
MyTable.data('dynatable').settings.dataset.queries['my_state_filter'] = search_filter ;
MyTable.data('dynatable').settings.dataset.queries['my_termtype_filter'] = 'reset' ;
MyTable.data('dynatable').process();
var MyTablesQueries = MyTable.data('dynatable').settings.dataset.queries
MyTablesQueries['my_state_filter'] = filtersParams.gtlists || 'reset' ;
MyTablesQueries['my_termtype_filter'] = filtersParams.multiw || 'reset' ;
// set main text-search value
MyTablesQueries['search'] = filtersParams.search || ''
// £todo factorize with previous with a param search_filters
restoreSettingsFromCache(localStorage)
// set table pagination
MyTable.data('dynatable').paginationPage.set(1);
MyTable.data('dynatable').paginationPerPage.set(filtersParams.perpp || 50) ;
// also set sorts
MyTable.data('dynatable').sorts.clear();
MyTable.data('dynatable').sorts.add( filtersParams.sortk || "score",
filtersParams.sortdirec || 1
// 1=DESCENDING,
)
// go ! ------------------------------
MyTable.data('dynatable').process();
// -----------------------------------
// moves pagination over table
// £TODO pagination copy instead (hard!)
if ( $(".imadiv").length>0 ) return 1;
$('<br><br><div class="imadiv"></div>').insertAfter(".dynatable-per-page")
$(".dynatable-record-count").insertAfter(".imadiv")
$(".dynatable-pagination-links").insertAfter(".imadiv")
// restore chart filters
if (typeof(filtersParams.from) != 'undefined'
&& typeof(filtersParams.to) != 'undefined') {
var fromVal = filtersParams.from
var toVal = filtersParams.to
if (fromVal != oldest || toVal != latest) {
// volumeChart.filterAll() // re-init
placeBrush(ourBrush, fromVal, toVal)
// placeBrush also does volumeChart.filter([fromVal, toVal]) and Push2Buffer
}
}
return "OK"
}
......@@ -2347,6 +2398,24 @@ function doATest() {
console.log("^---------- /TEST -----------^")
}
/**
* placeBrush cf. http://bl.ocks.org/timelyportfolio/5c136de85de1c2abb6fc:
* -----------
* Adds the brush (aka "chart's selection zone") programmatically
* (for instance at initialize if we want to restore previous selection)
*/
function placeBrush(myBrush, min, max) {
// define our brush extent
myBrush.extent([min, max])
// now draw the brush to match our extent
myBrush(d3.select(".brush"));
// now fire the brushstart, brushmove, and brushend events
myBrush.event(d3.select(".brush"))
}
/**
* tidyAfterUpdate:
* -----------
......@@ -2526,6 +2595,7 @@ var final_url = window.location.origin+"/api/ngramlists/family?corpus="+corpus_i
// faster call: just the maplist, will return first
// 2016-05-13: deactivated because it causes a lag before the table is updated
// FIXME: probably because of != nb of records ?
// GET_(prefetch_url, HandleAjax);
// longer call (full list of terms) to return when ready and refresh all data
......@@ -2589,10 +2659,7 @@ function HandleAjax(res, sourceUrl) {
}
// throws errors when ngram in list has no infos
// (can't assign state and copy to AjaxRecords)
// unpack ajax values, read table settings from cache if any, and run Main
function AfterAjax(sourceUrl) {
// -------------------------------------------------------------------
// console.log(JSON.stringify(OriginalNG))
......@@ -2657,7 +2724,7 @@ function AfterAjax(sourceUrl) {
// Building the Score-Selector //OriginalNG["scores"]
var FirstScore = OriginalNG.scores.initial
// TODO scores_div
// TODO scores_div multiscore
// Recreate possible_scores from some constants (tfidf, occs)
// and not from ngrams[0], to keep each ngram's info smaller
......@@ -2670,11 +2737,17 @@ function AfterAjax(sourceUrl) {
// }
// }
// show only map (option = 1) or all terms (option = "reset")
termsfilter = (sourceUrl == final_url) ? "reset" : "1"
// show only map (option = 1, good for waiting if async records)
// or all terms (option = "reset")
// table settings for filters etc.
var configIfAny = {}
configIfAny = restoreSettingsFromCache()
// configIfAny.gtlists = (sourceUrl == final_url) ? "reset" : "1" // condition useful for prefetch
// Initializing the Charts and Table ---------------------------------------
var result = MainTableAndCharts(OriginalNG["records"], FirstScore , termsfilter) ;
var result = MainTableAndCharts(OriginalNG["records"], FirstScore , configIfAny, "AfterAjax") ;
console.log( result ) // OK
// -------------------------------------------------------------------------
......
......@@ -200,8 +200,34 @@ function getRessources(){
//// POST TO API
//// PUT AND PATCH TO API
function deleteOne(url){
function deleteOne(url, thatButton){
// we just show wait image before ajax
var $thatButton = $(thatButton)
var alreadyWaiting = $thatButton.has($('.wait-img-active')).length
if (! alreadyWaiting) {
var previousButtonContent = $thatButton.html()
var availableWidth = $thatButton.width()
var $myWaitImg = $('#wait-img').clone()
$myWaitImg.attr("id", null)
.attr("class","wait-img-active pull-right")
.width(availableWidth)
.css("display", "block") ;
$thatButton.append($myWaitImg)
}
else {
// uncomment if user should stop clicking ;)
// $thatButton.addClass("btn-danger")
// uncomment to prevent a 2nd ajax
return false
}
// now the real ajax
$.ajax({
url: '/api'+url,
type: 'delete',
......@@ -306,7 +332,7 @@ $(document).on('change', 'input[type=checkbox]', function() {
$(document).on("click","#delete", function(){
var selected = selectedUrls();
selected.forEach(function(url) {
deleteOne(url);
deleteOne(url, this);
});
//window.location.reload();
});
......@@ -336,7 +362,7 @@ $(document).on("click","#recalculate", function(){
// UNIQUE DELETION
$(document).on("click", ".delete", function() {
var url = $( this ).data("url");
deleteOne(url);
deleteOne(url, this);
//window.location.reload();
});
......
......@@ -10,6 +10,290 @@
<script src="{% static "lib/jquery/1.11.1/jquery.min.js" %}" type="text/javascript"></script>
<style type="text/css">
.first-graph {
padding-top: 3em;
}
</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, 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)
}
</script>
{% endblock %}
......@@ -18,7 +302,7 @@
<div class="container theme-showcase" role="main">
<h2>My Graphs </h2>
<ol>
<ol id="graph-list">
{% if coocs %}
{% for cooc in coocs %}
<div id="graph_{{cooc.id}}">
......@@ -35,15 +319,15 @@
From: {% if not cooc.hyperdata.parameters.start %} begin of corpus {% else %} {{cooc.hyperdata.parameters.start}} {% endif %}
, To: {% if not cooc.hyperdata.parameters.end %} end of corpus {% else %} {{cooc.hyperdata.parameters.end}} {% endif %}
<br>
<ul>
<li>
<a href="/projects/{{project.id}}/corpora/{{corpus.id}}/explorer?cooc_id={{cooc.id}}&distance=conditional&bridgeness=5">
<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>
{% if cooc.hyperdata.conditional %}
~{{ cooc.hyperdata.conditional.nodes }} nodes,
~{{ cooc.hyperdata.conditional.nodes }} nodes,
~{{ cooc.hyperdata.conditional.edges }} edges
with <b>Conditional</b> distance
{% else %}
Compute this graph with Conditional distance
......@@ -52,12 +336,12 @@
</li>
<li>
<a href="/projects/{{project.id}}/corpora/{{corpus.id}}/explorer?cooc_id={{cooc.id}}&distance=distributional&bridgeness=5">
<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>
{% if cooc.hyperdata.distributional %}
~{{ cooc.hyperdata.distributional.nodes }} nodes,
~{{ cooc.hyperdata.distributional.nodes }} nodes,
~{{ cooc.hyperdata.distributional.edges }} edges
with <b>Distributional</b> distance
{% else %}
Compute this graph with Distributional distance
......@@ -68,7 +352,7 @@
<br>
<!-- <li>{{cooc.id}}</li>
<!-- <li>{{cooc.id}}</li>
<ul>
<li>
......@@ -123,7 +407,7 @@
</button>
</a>
--!>
-->
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"
data-content="
......@@ -176,13 +460,14 @@
<li>Choose a distance</li>
<li>Click on the distance or on MyGraph which is this page</li>
</ol>
<h4>
<span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
Do you want to test ?</h4>
</li>
<a href="/projects/{{project.id}}/corpora/{{ corpus.id }}/explorer?field1=ngrams&amp;field2=ngrams&amp;distance=conditional&amp;bridgeness=5">Compute a new graph with conditional distance!</a>
<btn class="btn btn-info" onclick="goFirstGraph()">
<span style="font-size:120%">Compute a new graph</span> <br/> with conditional distance
</btn>
{% endif %}
</ul>
</ol>
{% endblock %}
......@@ -56,15 +56,15 @@
<div class="clearfix"></div>
</div>
<div class="center">
<button type="submit" class="btn btn-primary btn-rounded">Login</button>
</div>
<div>
<p>
<center>
By submitting I accept the terms of uses [DOC1] [DOC2].
</center>
</p>
<div class="checkbox">
<label>
<input id="terms-accept" type="checkbox" value="" onclick="enableAuth(this)">
I accept the terms of uses [DOC1] [DOC2].
</input>
</label>
</div>
<button id="login-button" type="submit" class="btn btn-primary btn-rounded" disabled>Login</button>
</div>
</form>
</div>
</div>
......@@ -79,6 +79,16 @@
<!-- Bootstrap -->
<script src="{% static "lib/bootstrap/3.2.0/bootstrap.min.js" %}"></script>
<!-- checkbox => submit -->
<script type="text/javascript">
var okButton = document.getElementById('login-button')
function enableAuth(box) {
okButton.disabled = ! box.checked
}
</script>
</body>
......
......@@ -67,7 +67,8 @@
</li>
{% endif %}
</ul>
<ul class="nav pull-right">
<ul class="nav navbar-nav pull-right">
<li class="dropdown">
<a href="#" role="button" class="dropdown-toggle navbar-text" data-toggle="dropdown" title="That is your username">
<i class="icon-user"></i>
......
......@@ -18,6 +18,18 @@
.ui-autocomplete .ui-menu-item {
font-size:x-small;
}
/* for wait gif in buttons */
.wait-img-active {
margin-left: .5em;
}
/* hover red like btn_danger */
.btn.delete:hover {
color: #fff;
background-color: #c9302c;
border-color: #ac2925;
}
</style>
{% endblock %}
......@@ -97,6 +109,7 @@
<!--here loading projectlist from GET /projects-->
</div>
<img id="wait-img" width="90%" style="display:none" src="{% static "img/ajax-loader.gif"%}"></img>
</div>
......@@ -116,10 +129,10 @@
<!-- DELETE PROJECT -->
<button type="button" class="btn btn-default delete pull-right" data-url="{url}" >
<span class="glyphicon glyphicon-trash pull-right" aria-hidden="true"></span>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button>
<!-- EDIT PROJECT-->
<!-- EDIT PROJECT-->
<button class="btn btn-default edit pull-right" data-id="{id}" data-url="{url}" data-toggle="collapse">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</button>
......
......@@ -23,6 +23,11 @@
margin-top: .3em;
color: grey ;
}
ul.inside-popover {
padding: .5em ;
list-style-type: none ;
}
</style>
{% endblock %}
......@@ -111,9 +116,9 @@
<button type="button" class="btn btn-default {% if not state.complete %}hidden{% endif %}" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="focus"
data-content="
<ul>
<ul class=&quot;inside-popover&quot;>
<li
onclick=&quot;updateCorpus({{corpus.id}})&quot;>
onclick=&quot;updateCorpus(event, {{corpus.id}})&quot;>
<a href='#'>Recalculate ngram metrics</a> <br/> (can take a little while)
</li>
</ul>
......@@ -125,19 +130,9 @@
<!-- TODO: delete non seulement si state.complete mais aussi si state.error -->
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"
data-content="
<ul>
<ul id=&quot;{{corpus.id}}_trash_msg&quot; class=&quot;inside-popover&quot;>
<li
onclick=&quot;
trashedIds[{{corpus.id}}] = true ;
garganrest.nodes.delete(
{{corpus.id}},
function(){
$('#corpus_'+{{corpus.id}}).remove()
delete trashedIds[{{corpus.id}}]
}
);
$(this).parent().parent().remove();
&quot;>
onclick=&quot;deleteCorpus(event, {{corpus.id}})&quot;>
<a href='#'>Delete this</a>
</li>
</ul>
......@@ -798,7 +793,36 @@
});
}
function updateCorpus(corpusId) {
function deleteCorpus(e, corpusId) {
// prevents scroll back to top of page
e.preventDefault()
// register pending operation
trashedIds[corpusId] = true ;
// visual loader wheel
var statusDiv = document.getElementById("corpus_"+corpusId+"_status")
statusDiv.innerHTML = '<img width="10%" src="{% static "img/ajax-loader.gif"%}"></img>'
var trashMsgDiv = document.getElementById(corpusId+"_trash_msg")
trashMsgDiv.innerHTML = '<h5>Deleting corpus, please wait</h5>'
// REST and callback
garganrest.nodes.delete(
corpusId,
function(){
$('#corpus_'+corpusId).remove()
delete trashedIds[corpusId]
// remove any popover too
$('.popover').remove();
}
);
}
function updateCorpus(e, corpusId) {
// prevents scroll back to top of page
e.preventDefault()
// show 'waiting'
var statusDiv = document.getElementById("corpus_"+corpusId+"_status")
var previousStatus = statusDiv.innerHTML
......
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