Commit 996bf406 authored by Romain Loth's avatar Romain Loth

[FEAT] bouton recalcul de corpus sur la page projet

parent 4283baa5
......@@ -173,5 +173,102 @@ def parse_extract_indexhyperdata(corpus):
@shared_task
def recount(corpus):
"""
Recount essential metrics of the toolchain after group modifications.
==> updates all scores in terms table
==> updates tfidf relationship b/w term and doc
When groups change, the metrics need to be updated because subforms must be
added to their new mainform aggregate values:
- occurrences
- ndocs
- ti_rank
- coocs
- specificity
- tfidf
NB: no new extraction, no list change, just the metrics
"""
# 1) we'll need the new groups and mainlist as basis
group_id = corpus.children("GROUPLIST").first().id
mainlist_id = corpus.children("MAINLIST").first().id
# 2) and we're going to overwrite the previous metric nodes
try:
old_occ_id = corpus.children("OCCURRENCES").first().id
except:
old_occ_id = None
try:
old_tirank_id = corpus.children("TIRANK-GLOBAL").first().id
except:
old_tirank_id = None
try:
old_spec_id = corpus.children("SPECIFICITY").first().id
except:
old_spec_id = None
try:
old_ltfidf_id = corpus.children("TFIDF-CORPUS").first().id
except:
old_ltfidf_id = None
# 3) we redo the required toolchain parts
# -------------------------------------------
# Instantiate status
corpus.status('Recounting mini-workflow', progress=1)
corpus.save_hyperdata()
session.commit()
# -> overwrite occurrences (=> NodeNodeNgram)
occ_id = compute_occs(corpus,
groupings_id = group_id,
overwrite_id=old_occ_id)
print('RECOUNT #%d: [%s] updated occs node #%i' % (corpus.id, t(), occ_id))
# -> write cumulated ti_ranking (tfidf ranking vector) (=> NodeNodeNgram)
tirank_id = compute_ti_ranking(corpus,
groupings_id = group_id,
count_scope="global",
overwrite_id=old_tirank_id)
print('RECOUNT #%d: [%s] updated ti ranking node #%i' % (corpus.id, t(), tirank_id))
# -> write local tfidf similarities to (=> NodeNodeNgram)
ltfidf_id = compute_tfidf_local(corpus,
on_list_id = mainlist_id,
groupings_id = group_id,
overwrite_id = old_ltfidf_id)
print('RECOUNT #%d: [%s] updated localtfidf node #%i' % (corpus.id, t(), ltfidf_id))
# => used for doc <=> ngram association
# ------------
# -> cooccurrences on mainlist: compute + write (=> NodeNgramNgram)
coocs = compute_coocs(corpus,
on_list_id = mainlist_id,
groupings_id = group_id,
just_pass_result = True)
print('RECOUNT #%d: [%s] updated mainlist coocs for specif rank' % (corpus.id, t()))
# -> specificity: compute + write (=> NodeNgram)
spec_id = compute_specificity(corpus,cooc_matrix = coocs, overwrite_id = old_spec_id)
print('RECOUNT #%d: [%s] updated specificity node #%i' % (corpus.id, t(), spec_id))
print('RECOUNT #%d: [%s] FINISHED metric recounts' % (corpus.id, t()))
corpus.status('Recounting mini-workflow', progress=10, complete=True)
corpus.save_hyperdata()
session.commit()
def t():
return datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
from gargantext.util.db_cache import cache
from gargantext.util.http import ValidationException, APIView \
, HttpResponse, JsonHttpResponse
from gargantext.util.toolchain.main import recount
from datetime import datetime
class CorpusMetrics(APIView):
def patch(self, request, corpusnode_id):
"""
PATCH triggers recount of metrics for the specified corpus.
ex PATCH http://localhost:8000/api/metrics/14072
-----
corpus_id
"""
print("==> update metrics request on ", corpusnode_id)
if not request.user.is_authenticated():
# can't use @requires_auth because of positional 'self' within class
return HttpResponse('Unauthorized', status=401)
try:
corpus = cache.Node[int(corpusnode_id)]
except:
corpus = None
if corpus is None:
raise ValidationException("%s is not a valid corpus node id."
% corpusnode_id)
else:
t_before = datetime.now()
# =============
recount(corpus)
# =============
t_after = datetime.now()
return JsonHttpResponse({
'corpus_id' : corpusnode_id,
'took': "%f s." % (t_after - t_before).total_seconds()
})
from django.conf.urls import url
from . import nodes
from . import metrics
from . import ngramlists
from . import analytics
......@@ -19,6 +20,14 @@ urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view()
, url(r'^nodes/(\d+)/favorites$', nodes.CorpusFavorites.as_view() )
# in these two routes the node is supposed to be a *corpus* node
, url(r'^metrics/(\d+)$', metrics.CorpusMetrics.as_view() )
# update all metrics for a corpus
# ex: PUT metrics/123
# \
# corpus id
, url(r'^ngramlists/change$', ngramlists.ListChange.as_view() )
# add or remove ngram from a list
# ex: add <=> PUT ngramlists/change?list=42&ngrams=1,2
......
......@@ -69,10 +69,11 @@ var Resource = function(url_path) {
});
};
// change an item
this.change = this.update = function(item, callback) {
this.change = this.update = function(id, callback) {
$.ajax({
url: url_path + '/' + item.id,
url: url_path + '/' + id,
type: 'PATCH',
success: callback
});
};
......@@ -84,14 +85,18 @@ var Resource = function(url_path) {
$.ajax({
url: url_path + '/' + id,
type: 'DELETE',
success: callback
});
};
// add an item
this.add = this.append = function(value, callback) {
$.ajax({
// todo define id
url: url_path + '/' + id,
type: 'POST',
success: callback
});
};
......@@ -99,12 +104,12 @@ var Resource = function(url_path) {
var GarganRest = function(base_path, path_list) {
var that = this;
$.each(path_list, function(p, path){
$.each(path_list, function(i, path){
that[path] = new Resource(base_path + path);
});
};
garganrest = new GarganRest('/api/', ['nodes']);
garganrest = new GarganRest('/api/', ['nodes', 'metrics']);
// var log = function(result){console.log(result);};
......
......@@ -88,13 +88,29 @@
{{corpus.name}}, {{ corpus.count }} documents {{ corpus.status_message }}
</a>
</div>
<div class="col-md-2 content">
<a href="/projects/{{project.id}}/corpora/{{corpus.id}}">
<div class="col-md-3 content">
<a href="/projects/{{project.id}}/corpora/{{corpus.id}}"
title="View the corpus">
<button type="button" class="btn btn-default" aria-label="Left Align">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
</button>
</a>
<!-- -->
<button type="button" class="btn btn-default yopla" data-container="body" data-toggle="popover" data-placement="bottom" data-trigger="focus"
data-content="
<ul>
<li
onclick=&quot;
garganrest.metrics.update({{corpus.id}}, function(){alert('The corpus ({{corpus.name|escapejs}}) was updated')});
&quot;>
<a href='#'>Recalculate ngram metrics</a> <br/> (can take a little while)
</li>
</ul>
">
<span class="glyphicon glyphicon-dashboard" aria-hidden="true"
title='Recalculate ngram scores and similarities'></span>
</button>
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"
data-content="
<ul>
......@@ -103,14 +119,15 @@
garganrest.nodes.delete({{corpus.id}}, function(){$('#corpus_'+{{corpus.id}}).remove()});
$(this).parent().parent().remove();
&quot;>
<a href=&quot;#&quot;>Delete this</a>
<a href='#'>Delete this</a>
</li>
</ul>
">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
<span class="glyphicon glyphicon-trash" aria-hidden="true"
title='Delete this corpus'></span>
</button>
</div>
<div class="col-md-4 content">
<div class="col-md-3 content">
{% for state in corpus.hyperdata.statuses %}
{% ifequal state.action "Workflow" %}
{% if state.complete %}
......
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