Commit bb6fc256 authored by Romain Loth's avatar Romain Loth

[FIX] reconnect the context menu 'add ngram' in annotations view

parent b4f866ce
......@@ -133,8 +133,6 @@
background: white;
font-size: 0.8em;
font-weight: 600;
-webkit-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
-moz-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
......
......@@ -26,10 +26,107 @@
// ex: projects/1/corpora/2/documents/9/
// ex: projects/1/corpora/2/documents/9/focus=2677 (to highlight ngram 2677 more)
var path = window.location.pathname.match(/\/projects\/(.*)\/corpora\/(.*)\/documents\/(.*)\/(?:focus=([0-9,]+))?/);
// shared vars -------------------
$rootScope.projectId = path[1];
$rootScope.corpusId = path[2];
$rootScope.docId = path[3];
$rootScope.focusNgram = path[4];
// -------------------------------
// shared toolbox (functions useful for several modules) -------------------
$rootScope.mafonction = function (bidule) {console.warn(bidule)}
// chained recursion to do several AJAX actions and then a callback (eg refresh)
$rootScope.makeChainedCalls =
function (i, listOfCalls, finalCallback, lastCache) {
var callDetails = listOfCalls[i]
console.log(">> calling ajax call ("+(i+1)+"/"+listOfCalls.length+")")
// each callDetails object describes the Ajax call
// and passes the required functions and arguments
// via 3 properties: service, action, params
// ex: callDetails = {
// 'service' : MainApiChangeNgramHttpService,
// 'action' : 'delete'
// 'params' : { 'listId': ..., 'ngramIdList':...}
// there is an optional 4th slot: the dataPropertiesToCache directive
//
// 'dataPropertiesToCache' : ['id'] <== means that on call success
// we will store data.id into
// cache.id for next calls
// }
var service = callDetails['service']
var params = callDetails['params']
var action = callDetails['action']
// cache if we need to store properties of data response for next calls
var cache = {}
if (lastCache) cache = lastCache
// and interpolation of params with this current cache
for (var key in params) {
var val = params[key]
if (typeof val == "object" && val["fromCache"]) {
var propToRead = val["fromCache"]
// console.log("reading from cache: response data property "
// +propToRead+" ("+cache[propToRead]+")")
params[key] = cache[propToRead]
}
}
// Now we run the call
// ex:
// service action
// vvvvv vvvv
// MainApiChangeNgramHttpService["delete"](
// params >>> {'listId': listId, 'ngramIdList': ngramId},
// onsuccess(), onfailure() )
service[action](
params,
// on success
function(data) {
// console.log("SUCCESS:" + action)
// console.log("listOfCalls.length:" + listOfCalls.length)
// console.log("i+1:" + i+1)
// case NEXT
// ----
// when chained actions
if (listOfCalls.length > i+1) {
// if we need to store anything it's the right moment
for (var k in callDetails['dataPropertiesToCache']) {
var prop = callDetails['dataPropertiesToCache'][k]
// console.log("storing in cache: response data property "
// +prop+" ("+data[prop]+")")
cache[prop] = data[prop]
}
// ======= recursive call for next action in list ================
$rootScope.makeChainedCalls(i+1, listOfCalls, finalCallback, cache)
// ================================================================
}
// case LAST
// ------
// when last action
else {
console.log(">> calling refresh")
finalCallback()
}
},
// on error
function(data) {
console.error("unable to call ajax no "+i+" with service "+service.name+
" (http "+action+" with args "+JSON.stringify(params)+")");
}
);
}
// -------------------------------------------------------------------------
// debug
// console.log("==> $rootScope <==")
......
......@@ -31,9 +31,10 @@
$scope.keyword = keyword;
}
// this onClick only works for existing annotations
$scope.onClick = function(e) {
$rootScope.$emit("positionAnnotationMenu", e.pageX, e.pageY);
$rootScope.$emit("toggleAnnotationMenu", $scope.keyword);
$rootScope.$emit("positionAnnotationMenu", e.pageX, e.pageY);
// $rootScope.$emit("toggleAnnotationMenu", {'uuid':42,'list_id':1,'text':'gotcha'});
// console.log("EMIT toggleAnnotationMenu with \$scope.keyword: '" + $scope.keyword.text +"'")
e.stopPropagation();
......@@ -44,8 +45,9 @@
* Controls the menu over the current mouse selection
*/
annotationsAppHighlight.controller('TextSelectionMenuController',
['$scope', '$rootScope', '$element', '$timeout', 'MainApiChangeNgramHttpService', 'NgramListHttpService',
function ($scope, $rootScope, $element, $timeout, MainApiChangeNgramHttpService, NgramListHttpService) {
['$scope', '$rootScope', '$element', '$timeout', 'MainApiChangeNgramHttpService', 'MainApiAddNgramHttpService', 'NgramListHttpService',
function ($scope, $rootScope, $element, $timeout, MainApiChangeNgramHttpService, MainApiAddNgramHttpService, NgramListHttpService) {
/*
* Universal text selection
*/
......@@ -106,7 +108,12 @@
// console.log("toggleMenu with \$scope.selection_text: '" + JSON.stringify($scope.selection_text) +"'") ;
if (angular.isObject(annotation) && !$element.hasClass('menu-is-opened')) {
// existing ngram
var ngramId = annotation.uuid
var mainformId = annotation.group
var targetId = mainformId ? mainformId : ngramId
// Context menu proposes 2 things for each item of list A
// - adding/moving to other lists B or C
......@@ -122,38 +129,45 @@
// otherwise the menu for mainlist items can hide the menu for map items
if ($rootScope.lists[annotation.list_id] == "MAPLIST") {
console.log($scope)
$scope.menuItems.push({
// "tgtListName" is just used to render the GUI explanation
'tgtListName': 'STOPLIST',
// crudActions is an array of rest/DB actions
// crudCalls is an array of rest/DB actions
// (consequences of the intention)
'crudActions':[
["delete", maplist_id],
["delete", mainlist_id],
["put", stoplist_id]
'crudCalls':[
{'service': MainApiChangeNgramHttpService, 'action': 'delete',
'params' : {'listId':maplist_id, 'ngramIdList': targetId} },
{'service': MainApiChangeNgramHttpService, 'action': 'delete',
'params' : {'listId':mainlist_id, 'ngramIdList': targetId} },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':stoplist_id, 'ngramIdList': targetId} }
]
});
$scope.menuItems.push({
'tgtListName': 'MAINLIST',
'crudActions':[
["delete", maplist_id]
]
'crudCalls':[
{'service': MainApiChangeNgramHttpService, 'action': 'delete',
'params' : {'listId':maplist_id, 'ngramIdList': targetId} }
]
});
}
else if ($rootScope.lists[annotation.list_id] == "MAINLIST") {
$scope.menuItems.push({
'tgtListName': "STOPLIST",
'crudActions':[
["delete", mainlist_id],
["put", stoplist_id]
'crudCalls':[
{'service': MainApiChangeNgramHttpService, 'action': 'delete',
'params' : {'listId':mainlist_id, 'ngramIdList': targetId} },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':stoplist_id, 'ngramIdList': targetId} }
]
});
$scope.menuItems.push({
'tgtListName': "MAPLIST",
'crudActions':[
["put", maplist_id]
'crudCalls':[
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':maplist_id, 'ngramIdList': targetId} }
]
});
}
......@@ -161,46 +175,79 @@
else if ($rootScope.lists[annotation.list_id] == "STOPLIST") {
$scope.menuItems.push({
'tgtListName': "MAINLIST",
'crudActions':[
["delete", stoplist_id],
["put", mainlist_id]
'crudCalls':[
{'service': MainApiChangeNgramHttpService, 'action': 'delete',
'params' : {'listId':stoplist_id, 'ngramIdList': targetId} },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':mainlist_id, 'ngramIdList': targetId} }
]
});
$scope.menuItems.push({
'tgtListName': "MAPLIST",
'crudActions':[
["delete", stoplist_id],
["put", mainlist_id],
["put", maplist_id]
'crudCalls':[
{'service': MainApiChangeNgramHttpService, 'action': 'delete',
'params' : {'listId':stoplist_id, 'ngramIdList': targetId} },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':mainlist_id, 'ngramIdList': targetId} },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':maplist_id, 'ngramIdList': targetId} }
]
});
}
// show the menu
$element.fadeIn(100);
$element.fadeIn(50);
$element.addClass('menu-is-opened');
}
// -------8<------ "add" actions for non-existing ngram ------
// else if (annotation.trim() !== "" && !$element.hasClass('menu-is-opened')) {
// // new ngram
// $scope.menuItems = [
// {
// 'action': 'post',
// 'listId': miamlist_id,
// 'verb': 'Add to',
// 'listName': $rootScope.lists[miamlist_id]
// }
// ];
// // show the menu
// $element.fadeIn(100);
// $element.addClass('menu-is-opened');
// }
// -------8<--------------------------------------------------
// "add" actions for non-existing ngram
else if (annotation.trim() !== "" && !$element.hasClass('menu-is-opened')) {
var newNgramText = annotation.trim()
// new ngram (first call creates then like previous case for list)
$scope.menuItems.push({
'comment' : "Create and add to STOPLIST",
'tgtListName': "STOPLIST",
'crudCalls':[
{'service': MainApiAddNgramHttpService, 'action': 'put',
'params' : {'ngramStr':newNgramText, corpusId: $rootScope.corpusId},
'dataPropertiesToCache': ['id'] },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':stoplist_id, 'ngramIdList': {'fromCache': 'id'} } }
]
}) ;
$scope.menuItems.push({
'comment' : "Create and add to MAINLIST",
'tgtListName': "MAINLIST",
'crudCalls':[
{'service': MainApiAddNgramHttpService, 'action': 'put',
'params' : {'ngramStr':newNgramText, corpusId: $rootScope.corpusId},
'dataPropertiesToCache': ['id'] },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':mainlist_id, 'ngramIdList': {'fromCache': 'id'} } }
]
}) ;
$scope.menuItems.push({
'comment' : "Create and add to MAPLIST",
'tgtListName': "MAPLIST",
'crudCalls':[
{'service': MainApiAddNgramHttpService, 'action': 'put',
'params' : {'ngramStr':newNgramText, corpusId: $rootScope.corpusId},
'dataPropertiesToCache': ['id'] },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':mainlist_id, 'ngramIdList': {'fromCache': 'id'} } },
{'service': MainApiChangeNgramHttpService, 'action': 'put',
'params' : {'listId':maplist_id, 'ngramIdList': {'fromCache': 'id'} } }
]
}) ;
// show the menu
$element.fadeIn(50);
$element.addClass('menu-is-opened');
}
else {
$scope.menuItems = [];
// close the menu
$element.fadeOut(100);
$scope.menuItems = [];
$element.fadeOut(50);
$element.removeClass('menu-is-opened');
}
});
......@@ -229,8 +276,9 @@
/*
* Finish positioning the menu then display the menu
*/
$(".text-container").mouseup(function(){
$(".text-container").mouseup(function(e){
$(".text-container").unbind("mousemove", positionMenu);
$rootScope.$emit("positionAnnotationMenu", e.pageX, e.pageY);
toggleSelectionHighlight(selection.toString().trim());
toggleMenu(null, selection.toString().trim());
});
......@@ -257,108 +305,28 @@
* (1 intention => list of actions => MainApiChangeNgramHttpService CRUDs)
* post/delete
*/
$scope.onMenuClick = function($event, crudActions) {
$scope.onMenuClick = function($event, crudCalls) {
// console.warn('in onMenuClick')
// console.warn('item.crudActions')
// console.warn(crudActions)
if (angular.isObject($scope.selection_text)) {
var ngramId = $scope.selection_text.uuid
var mainformId = $scope.selection_text.group
var ngramText = $scope.selection_text.text
var lastCallback = function() {
// Refresh the annotationss
NgramListHttpService.get(
{'corpusId': $rootScope.corpusId,
'docId': $rootScope.docId},
function(data) {
$rootScope.annotations = data[$rootScope.corpusId.toString()][$rootScope.docId.toString()];
$rootScope.refreshDisplay();
},
function(data) {
console.error("unable to get the list of ngrams");
}
);
}
// chained recursion to do several actions then callback (eg refresh)
function makeChainedCalls (i, listOfActions, finalCallback) {
// each action couple has 2 elts
var action = listOfActions[i][0]
var listId = listOfActions[i][1]
console.log("===>"+action+"<===")
MainApiChangeNgramHttpService[action](
{'listId': listId,
'ngramIdList': mainformId ? mainformId : ngramId},
// on success
function(data) {
// case NEXT
// ----
// when chained actions
if (listOfActions.length > i+1) {
console.log("calling next action ("+(i+1)+")")
// ==============================================
makeChainedCalls(i+1, listOfActions, finalCallback)
// ==============================================
}
// case LAST
// ------
// when last action
else {
finalCallback()
}
},
// on error
function(data) {
console.error("unable to edit the Ngram \""+ngramText+"\""
+"(ngramId "+ngramId+")"+"at crud no "+i
+" ("+action+" on list "+listId+")");
}
);
}
// run the loop by calling the initial recursion step
makeChainedCalls(0, crudActions, lastCallback)
// console.warn('item.crudCalls', crudCalls)
var lastCallback = function() {
// Refresh the annotationss
NgramListHttpService.get(
{'corpusId': $rootScope.corpusId,
'docId': $rootScope.docId},
function(data) {
$rootScope.annotations = data[$rootScope.corpusId.toString()][$rootScope.docId.toString()];
$rootScope.refreshDisplay();
},
function(data) {
console.error("unable to refresh the list of ngrams");
}
);
}
// TODO: first action creates then like previous case
// else if ($scope.selection_text.trim() !== "") {
// // new annotation from selection
// NgramHttpService.post(
// {
// 'listId': listId,
// 'ngramId': 'create'
// },
// {
// 'text': $scope.selection_text.trim()
// }, function(data) {
// // Refresh the annotationss
// NgramListHttpService.get(
// {
// 'corpusId': $rootScope.corpusId,
// 'docId': $rootScope.docId
// },
// function(data) {
// $rootScope.annotations = data[$rootScope.corpusId.toString()][$rootScope.docId.toString()];
// $rootScope.refreshDisplay();
// },
// function(data) {
// console.error("unable to get the list of ngrams");
// }
// );
// }, function(data) {
// console.error("unable to edit the Ngram " + $scope.selection_text);
// }
// );
// }
// run the loop by calling the initial recursion step
$rootScope.makeChainedCalls(0, crudCalls, lastCallback)
// syntax: (step_to_run, list_of_steps, lastCallback)
// hide the highlighted text and the menu element
$(".text-panel").removeClass("selection");
......
......@@ -86,48 +86,31 @@
);
});
/*
* NgramHttpService: Create, modify or delete 1 Ngram
* =================
*
* TODO add a create case separately and then remove service
*
* NB : replaced by external api: (MainApiChangeNgramHttpService)
* api/ngramlists/change?list=LISTID&ngrams=ID1,ID2..
*
* old logic:
* ----------
* if new ngram
* -> ngram_id will be "create"
* -> route: annotations/lists/@node_id/ngrams/create
* -> will land on views.NgramCreate
*
* else:
* -> ngram_id is a real ngram id
* -> route: annotations/lists/@node_id/ngrams/@ngram_id
* -> will land on views.NgramEdit
* MainApiAddNgramHttpService: Create and index a new ngram
* ===========================
* route: PUT api/ngrams?text=mynewngramstring&corpus=corpus_id
* ------
*
*/
// http.factory('NgramHttpService', function ($resource) {
// return $resource(
// window.ANNOTATION_API_URL + 'lists/:listId/ngrams/:ngramId',
// {
// listId: '@listId',
// ngramId: '@id'
// },
// {
// post: {
// method: 'POST',
// params: {'listId': '@listId', 'ngramId': '@ngramId'}
// },
// delete: {
// method: 'DELETE',
// params: {'listId': '@listId', 'ngramId': '@ngramId'}
// }
// }
// );
// });
http.factory('MainApiAddNgramHttpService', function($resource) {
return $resource(
// adding explicit "http://" b/c this a cross origin request
'http://' + window.GARG_ROOT_URL
+ "/api/ngrams?text=:ngramStr&corpus=:corpusId",
{
ngramStr: '@ngramStr',
corpusId: '@corpusId'
},
{
put: {
method: 'PUT',
params: {listId: '@listId', ngramIdList: '@ngramIdList'}
}
}
);
});
/*
* MainApiChangeNgramHttpService: Add/remove ngrams from lists
......
......@@ -142,12 +142,16 @@
}
};
});
/*
* new NGram from the user input
*/
annotationsAppNgramList.controller('NgramInputController',
['$scope', '$rootScope', '$element', 'NgramListHttpService',
function ($scope, $rootScope, $element, NgramListHttpService) {
'MainApiChangeNgramHttpService', 'MainApiAddNgramHttpService',
function ($scope, $rootScope, $element, NgramListHttpService,
MainApiChangeNgramHttpService, MainApiAddNgramHttpService) {
/*
* Add a new NGram from the user input in the extra-text list
*/
......@@ -158,11 +162,14 @@
var value = angular.element(inputEltId).val().trim();
if (value === "") return;
// £TEST locally check if already in annotations NodeNgrams ------
// locally check if already in annotations NodeNgrams ------------
// $rootScope.annotations = array of ngram objects like:
// {"list_id":805,"occs":2,"uuid":9386,"text":"petit échantillon"}
// TODO £NEW : lookup obj[list_id][term_text] = {terminfo}
// // $rootScope.lookup =
console.log('looking for "' + value + '" in list:' + listId)
var already_in_list = false ;
angular.forEach($rootScope.annotations, function(annot,i) {
......@@ -177,49 +184,65 @@
if (already_in_list) { return ; }
// ---------------------------------------------------------------
// will check if there's a preexisting ngramId for this value
// TODO: reconnect separately from list addition
// TODO: if maplist => also add to miam
// NgramHttpService.post(
// {
// 'listId': listId,
// 'ngramId': 'create'
// },
// {
// 'text': value
// },
// function(data) {
// console.warn("refresh attempt");
// // on success
// if (data) {
// angular.element(inputEltId).val("");
// // Refresh the annotationss
// NgramListHttpService.get(
// {
// 'corpusId': $rootScope.corpusId,
// 'docId': $rootScope.docId
// },
// function(data) {
// $rootScope.annotations = data[$rootScope.corpusId.toString()][$rootScope.docId.toString()];
//
// // TODO £NEW : lookup obj[list_id][term_text] = {terminfo}
// // $rootScope.lookup =
//
//
// $rootScope.refreshDisplay();
// },
// function(data) {
// console.error("unable to get the list of ngrams");
// }
// );
// }
// }, function(data) {
// // on error
// angular.element(inputEltId).parent().addClass("has-error");
// console.error("error adding Ngram "+ value);
// }
// );
};
// AddNgram
// --------
// creation will return an ngramId
// (checks if there's a preexisting ngramId for this value
// otherwise creates a new one and indexes the ngram in corpus)
MainApiAddNgramHttpService.put(
{
// text <=> str to create the new ngram
'text': value,
'corpusId': $rootScope.corpusId
},
// on AddNgram success
function(data) {
var newNgramId = data.id
console.log("OK created new ngram for '"+value+"' with id: "+newNgramId)
// ChangeNgram
// -----------
// add to listId after creation
// TODO: if maplist => also add to miam
MainApiChangeNgramHttpService["put"](
{
'listId': listId,
'ngramIdList': newNgramId
},
// on ChangeNgram success
function(data) {
// Refresh the annotations (was broken: TODO FIX)
console.warn("refresh attempt");
angular.element(inputEltId).val(""); // what for ???
NgramListHttpService.get(
{
'corpusId': $rootScope.corpusId,
'docId': $rootScope.docId
},
// on refresh success
function(data) {
$rootScope.annotations = data[$rootScope.corpusId.toString()][$rootScope.docId.toString()];
$rootScope.refreshDisplay();
},
// on refresh error
function(data) {
console.error("unable to get the list of ngrams");
}
);
},
// on ChangeNgram error
function(data) {
console.error("unable to edit the Ngram"+ngramId+") on list "+listId+")");
}
);
},
// on AddNgram error
function(data) {
angular.element(inputEltId).parent().addClass("has-error");
console.error("error adding Ngram "+ value);
}
);
}; // onListSubmit
}]);
})(window);
......@@ -121,7 +121,10 @@
<!-- this menu is over the text on mouse selection -->
<div ng-controller="TextSelectionMenuController" id="selection" class="selection-menu">
<ul class="noselection">
<li ng-repeat="item in menuItems" class="{[{item.tgtListName}]}" ng-click="onMenuClick($event, item.crudActions)">Move to {[{item.tgtListName}]}</li>
<li ng-repeat="item in menuItems"
class="{[{item.tgtListName}]}"
ng-click="onMenuClick($event, item.crudCalls)"
>{[{item.comment ? item.comment : 'Move to ' + item.tgtListName}]}</li>
</ul>
</div>
</div>
......
......@@ -18,5 +18,5 @@ urlpatterns = [
# 2016-03-24: refactoring, deactivated NgramEdit and NgramCreate
# 2016-05-27: removed NgramEdit: replaced the local httpservice by api/ngramlists
# url(r'^lists/(?P<list_id>[0-9]+)/ngrams/create$', views.NgramCreate.as_view()), #
# 2016-07-21: removed NgramCreate: replaced the local httpservice by api/ngrams (put)
]
......@@ -162,55 +162,7 @@ class NgramList(APIView):
# 2016-03-24: refactoring, deactivated NgramEdit and NgramCreate
# 2016-05-27: removed NgramEdit: replaced the local httpservice by api/ngramlists
# ------------------------------------
#
# class NgramCreate(APIView):
# """
# Create a new Ngram in one list
# """
# renderer_classes = (JSONRenderer,)
# authentication_classes = (SessionAuthentication, BasicAuthentication)
#
# def post(self, request, list_id):
# """
# create NGram in a given list
#
# example: request.data = {'text': 'phylogeny'}
# """
# # implicit global session
# list_id = int(list_id)
# # format the ngram's text
# ngram_text = request.data.get('text', None)
# if ngram_text is None:
# raise APIException("Could not create a new Ngram without one \
# text key in the json body")
#
# ngram_text = ngram_text.strip().lower()
# ngram_text = ' '.join(ngram_text.split())
# # check if the ngram exists with the same terms
# ngram = session.query(Ngram).filter(Ngram.terms == ngram_text).first()
# if ngram is None:
# ngram = Ngram(n=len(ngram_text.split()), terms=ngram_text)
# else:
# # make sure the n value is correct
# ngram.n = len(ngram_text.split())
#
# session.add(ngram)
# session.commit()
# ngram_id = ngram.id
# # create the new node_ngram relation
# # TODO check existing Node_Ngram ?
# # £TODO ici indexation
# node_ngram = NodeNgram(node_id=list_id, ngram_id=ngram_id, weight=1.0)
# session.add(node_ngram)
# session.commit()
#
# # return the response
# return Response({
# 'uuid': ngram_id,
# 'text': ngram_text,
# 'list_id': list_id,
# })
# 2016-07-21: removed NgramCreate: replaced the local httpservice by api/ngrams (put)
class Document(APIView):
"""
......
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