Commit 31de1848 authored by c24b's avatar c24b

[API REST] project (JS+/views/api+template overview.html)

parent e94f5c2f
from rest_framework.status import *
from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from gargantext.models import Node, Ngram, NodeNgram, NodeNodeNgram, NodeNode
from gargantext.constants import RESOURCETYPES, NODETYPES, get_resource
from gargantext.util.db import session, delete, func, bulk_insert
from gargantext.util.db_cache import cache, or_
from gargantext.util.validation import validate
from gargantext.models import Node, Ngram, NodeNgram, NodeNodeNgram, NodeNode
from gargantext.constants import RESOURCETYPES, NODETYPES, get_resource
from gargantext.util.http import ValidationException, APIView, JsonHttpResponse, get_parameters
from gargantext.util.files import upload
from gargantext.util.db import session, delete, func, bulk_insert
from gargantext.util.scheduling import scheduled
#import
#NODES format
_user_default_fields =["is_staff","is_superuser","is_active", "username", "email", "first_name", "last_name", "id"]
_api_default_fields = ['id', 'parent_id', 'name', 'typename', 'date']
_doc_default_fields = ['id', 'parent_id', 'name', 'typename', 'date', "hyperdata"]
#_resource_default_fields = [['id', 'parent_id', 'name', 'typename', "hyperdata.method"]
#_corpus_default_fields = ['id', 'parent_id', 'name', 'typename', 'date', "hyperdata","resource"]
def format_parent(node):
'''format the parent'''
try:
#USER
if node.username != "":
return {field: getattr(node, field) for field in _user_default_fields}
except:
#DOC
if node.typename == "DOCUMENT":
return {field: getattr(node, field) for field in _doc_default_fields}
elif node.typename == "CORPUS":
parent = {field: getattr(node, field) for field in _doc_default_fields}
#documents
#parent["documents"] = {"count":node.children("DOCUMENT").count()}
#resources
#parent["resources"] = {"count":node.children("RESOURCE").count()}
#status
#return {field: getattr(node, field) for field in _doc_default_fields}
parent["status_msg"] = status_message
return parent
#PROJECT, RESOURCES?
else:
return {field: getattr(node, field) for field in _api_default_fields}
def format_records(node_list):
'''format the records list'''
if len(node_list) == 0:
return []
node1 = node_list[0]
#USER
if node1.typename == "USER":
return [{field: getattr(node, field) for field in _user_default_fields} for node in node_list]
#DOCUMENT
elif node1.typename == "DOCUMENT":
return [{field: getattr(node, field) for field in _doc_default_fields} for node in node_list]
#CORPUS, PROJECT, RESOURCES?
elif node1.typename == "CORPUS":
records = []
for node in node_list:
#PROJECTS VIEW SHOULD NE BE SO DETAILED
record = {field: getattr(node, field) for field in _doc_default_fields}
record["resources"] = [n.id for n in node.children("RESOURCE")]
record["documents"] = [n.id for n in node.children("DOCUMENT")]
#record["resources"] = format_records([n for n in node.children("RESOURCE")])
#record["documents"] = format_records([n for n in node.children("DOCUMENT")])
status = node.status()
if status is not None and not status['complete']:
if not status['error']:
status_message = '(in progress: %s, %d complete)' % (
status['action'].replace('_', ' '),
status['progress'],
)
else:
status_message = '(aborted: "%s" after %i docs)' % (
status['error'][-1],
status['progress']
)
else:
status_message = ''
record["status"] = status_message
records.append(record)
return records
else:
return [{field: getattr(node, field) for field in _api_default_fields} for node in node_list]
def check_rights(request, node_id):
'''check that the node belong to USER'''
node = session.query(Node).filter(Node.id == node_id).first()
if node is None:
raise APIException("403 Unauthorized")
# return Response({'detail' : "Node #%s not found" %(node_id) },
# status = status.HTTP_404_NOT_FOUND)
elif node.user_id != request.user.id:
#response_data = {"log": "Unauthorized"}
#return JsonHttpResponse(response_data, status=403)
raise APIException("403 Unauthorized")
else:
return node
def format_response(parent, records):
#print(records)
return { "parent": format_parent(parent),
"records": format_records(records),
"count":len(records)
}
from django.core.exceptions import *
from .api import * #APIView, APIException entre autres
from gargantext.util.db import session
from gargantext.models import Node
from gargantext.util.http import *
class CorpusView(APIView):
'''API endpoint that represent a corpus'''
def get(self, request, project_id, corpus_id, view = "DOCUMENT"):
'''GET corpus detail
default view full documents
'''
params = get_parameters(request)
if "view" in params.keys():
filter_view = params["view"].upper()
if view in ["DOCUMENT", "JOURNAL", "TITLE", "ANALYTICS", "RESSOURCE"]:
view = filter_view
project = session.query(Node).filter(Node.id == project_id, Node.typename == "PROJECT").first()
check_rights(request, project.id)
if project is None:
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = status.HTTP_404_NOT_FOUND)
corpus = session.query(Node).filter(Node.id == corpus_id, Node.typename == "CORPUS").first()
if corpus is None:
return Response({'detail' : "CORPUS Node #%s not found" %(corpus_id) },
status = status.HTTP_404_NOT_FOUND)
documents = session.query(Node).filter(Node.parent_id == corpus_id, Node.typename == view).all()
context = format_response(corpus, documents)
return Response(context)
def delete(self, request, project_id, corpus_id):
'''DELETE corpus'''
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>delete")
# project = session.query(Node).filter(Node.id == project_id, Node.typename == "PROJECT").first()
# check_rights(request, project.id)
# if project is None:
# return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
# status = status.HTTP_404_NOT_FOUND)
corpus = session.query(Node).filter(Node.id == corpus_id, Node.typename == "CORPUS").first()
if corpus is None:
return Response({'detail' : "CORPUS Node #%s not found" %(corpus_id) },
status = status.HTTP_404_NOT_FOUND)
documents = session.query(Node).filter(Node.parent_id == corpus_id).all()
session.delete(documents)
session.delete(corpus)
session.commit()
return Response(detail="Deleted corpus #%s" %str(corpus_id), status=HTTP_204_NO_CONTENT)
def put(self, request, project_id, corpus_id, view="DOCUMENT"):
'''UPDATE corpus'''
project = session.query(Node).filter(Node.id == project_id, Node.typename == "PROJECT").first()
project = check_rights(request, project.id)
if project is None:
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = status.HTTP_404_NOT_FOUND)
corpus = session.query(Node).filter(Node.id == corpus_id, Node.typename == "CORPUS").first()
if corpus is None:
return Response({'detail' : "CORPUS Node #%s not found" %(corpus_id) },
status = status.HTTP_404_NOT_FOUND)
#documents = session.query(Node).filter(Node.parent_id == corpus_id, Node.typename= view).all()
for key, val in request.data.items():
if key in ["name", "date", "username", "hyperdata"]:
if key == "username":
#changement de propriétaire
#user = session.query(Node).filter(Node.typename=="USER", Node.username== username).first()
#print(user)
#set(node, user_id, user.id)
pass
elif key == "hyperdata":
#updating some contextualvalues of the corpus
pass
else:
setattr(node, key, val)
session.add(node)
session.commit()
'''#updating children???
'''
return Response({"detail":"Updated corpus #" %str(corpus.id)}, status=HTTP_202_ACCEPTED)
def post(self, request, project_id, corpus_id):
'''ADD a new RESOURCE to CORPUS'''
project = session.query(Node).filter(Node.id == project_id, Node.typename == "PROJECT").first()
check_rights(request, project.id)
if project is None:
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = status.HTTP_404_NOT_FOUND)
corpus = session.query(Node).filter(Node.id == corpus_id, Node.typename == "CORPUS").first()
if corpus is None:
return Response({'detail' : "CORPUS Node #%s not found" %(corpus_id) },
status = status.HTTP_404_NOT_FOUND)
from gargantext.models import Node, Ngram, NodeNgram, NodeNodeNgram, NodeNode from gargantext.models import Node, Ngram, NodeNgram, NodeNodeNgram, NodeNode
from gargantext.constants import NODETYPES, DEFAULT_N_DOCS_HAVING_NGRAM from gargantext.constants import NODETYPES
from gargantext.util.db import session, delete, func, bulk_insert from gargantext.util.db import session, delete, func, bulk_insert
from gargantext.util.db_cache import cache, or_ from gargantext.util.db_cache import cache, or_
from gargantext.util.validation import validate from gargantext.util.validation import validate
...@@ -8,7 +8,7 @@ from gargantext.util.http import ValidationException, APIView \ ...@@ -8,7 +8,7 @@ from gargantext.util.http import ValidationException, APIView \
, get_parameters, JsonHttpResponse, Http404\ , get_parameters, JsonHttpResponse, Http404\
, HttpResponse , HttpResponse
from .api import *
from collections import defaultdict from collections import defaultdict
import csv import csv
...@@ -67,6 +67,48 @@ def _query_nodes(request, node_id=None): ...@@ -67,6 +67,48 @@ def _query_nodes(request, node_id=None):
# return the result! # return the result!
return parameters, query, count return parameters, query, count
class Status(APIView):
'''API endpoint that represent the current status of the node'''
renderer_classes = (JSONRenderer, BrowsableAPIRenderer)
def get(self, request, node_id):
user = cache.User[request.user.id]
check_rights(request, node_id)
node = session.query(Node).filter(Node.id == node_id, Node.user_id== user.id).first()
if node is None:
return Response({"detail":"Node not Found for this user"}, status=HTTP_404_NOT_FOUND)
else:
context = format_response(node, [n for n in node.children()])
try:
context["status"] = node.hyperdata["statuses"]
except KeyError:
context["status"] = None
return Response(context)
def post(self, request, data):
'''create a new status for node'''
raise NotImplementedError
def put(self, request, data):
'''update status for node'''
user = cache.User[request.user.id]
check_rights(request, node_id)
node = session.query(Node).filter(Node.id == node_id).first()
raise NotImplementedError
#return Response({"detail":"Udpated status for NODE #%i " %node.id}, status=HTTP_202_ACCEPTED)
def delete(self, request):
'''delete status for node'''
user = cache.User[request.user.id]
check_rights(request, node_id)
node = session.query(Node).filter(Node.id == node_id).first()
if node is None:
return Response({"detail":"Node not Found"}, status=HTTP_404_NOT_FOUND)
node.hyperdata["status"] = []
session.add(node)
session.commit()
return Response({"detail":"Deleted status for NODE #%i " %node.id}, status=HTTP_204_NO_CONTENT)
class NodeListResource(APIView): class NodeListResource(APIView):
...@@ -143,8 +185,6 @@ class NodeListHaving(APIView): ...@@ -143,8 +185,6 @@ class NodeListHaving(APIView):
Simple implementation: Simple implementation:
Takes IDs of corpus and ngram and returns list of relevent documents in json format Takes IDs of corpus and ngram and returns list of relevent documents in json format
according to TFIDF score (order is decreasing). according to TFIDF score (order is decreasing).
2016-09: add total counts to output json
''' '''
def get(self, request, corpus_id): def get(self, request, corpus_id):
parameters = get_parameters(request) parameters = get_parameters(request)
...@@ -155,7 +195,7 @@ class NodeListHaving(APIView): ...@@ -155,7 +195,7 @@ class NodeListHaving(APIView):
except : except :
raise ValidationException('"ngram_ids" needs integers separated by comma.') raise ValidationException('"ngram_ids" needs integers separated by comma.')
limit = DEFAULT_N_DOCS_HAVING_NGRAM limit=5
nodes_list = [] nodes_list = []
corpus = session.query(Node).filter(Node.id==corpus_id).first() corpus = session.query(Node).filter(Node.id==corpus_id).first()
...@@ -178,18 +218,26 @@ class NodeListHaving(APIView): ...@@ -178,18 +218,26 @@ class NodeListHaving(APIView):
.filter(Node.typename == 'DOCUMENT', Node.parent_id== corpus.id) .filter(Node.typename == 'DOCUMENT', Node.parent_id== corpus.id)
.filter(or_(*[NodeNodeNgram.ngram_id==ngram_id for ngram_id in ngram_ids])) .filter(or_(*[NodeNodeNgram.ngram_id==ngram_id for ngram_id in ngram_ids]))
.group_by(Node) .group_by(Node)
)
# get the total count before applying limit
nodes_count = nodes_query.count()
# now the query with the limit
nodes_results_query = (nodes_query
.order_by(func.sum(NodeNodeNgram.score).desc()) .order_by(func.sum(NodeNodeNgram.score).desc())
.limit(limit) .limit(limit)
) )
# print("\n")
for node, score in nodes_results_query: # print("in TFIDF:")
# print("\tcorpus_id:",corpus_id)
# convert query result to a list of dicts
# if nodes_query is None:
# print("TFIDF error, juste take sums")
# nodes_query = (session
# .query(Node, func.sum(NodeNgram.weight))
# .join(NodeNgram, NodeNgram.node_id == Node.id)
# .filter(Node.parent_id == corpus_id)
# .filter(Node.typename == 'DOCUMENT')
# .filter(or_(*[NodeNgram.ngram_id==ngram_id for ngram_id in ngram_ids]))
# .group_by(Node)
# .order_by(func.sum(NodeNgram.weight).desc())
# .limit(limit)
# )
for node, score in nodes_query:
print(node,score) print(node,score)
print("\t corpus:",corpus_id,"\t",node.name) print("\t corpus:",corpus_id,"\t",node.name)
node_dict = { node_dict = {
...@@ -201,10 +249,7 @@ class NodeListHaving(APIView): ...@@ -201,10 +249,7 @@ class NodeListHaving(APIView):
node_dict[key] = node.hyperdata[key] node_dict[key] = node.hyperdata[key]
nodes_list.append(node_dict) nodes_list.append(node_dict)
return JsonHttpResponse({ return JsonHttpResponse(nodes_list)
'count': nodes_count,
'records': nodes_list
})
...@@ -438,8 +483,7 @@ class CorpusFacet(APIView): ...@@ -438,8 +483,7 @@ class CorpusFacet(APIView):
# check that the hyperfield parameter makes sense # check that the hyperfield parameter makes sense
_facet_available_subfields = [ _facet_available_subfields = [
'journal', 'publication_year', 'rubrique', 'journal', 'publication_year', 'rubrique',
'language_iso2', 'language_iso3', 'language_name', 'language_iso2', 'language_iso3', 'language_name'
'authors'
] ]
parameters = get_parameters(request) parameters = get_parameters(request)
......
from .api import * #notamment APIView, check_rights, format_response
from gargantext.util.http import *
from django.core.exceptions import *
from collections import defaultdict
from gargantext.util.toolchain import *
import copy
from gargantext.util.db import session
class ProjectList(APIView):
'''API endpoint that represent a list of projects owned by a user'''
renderer_classes = (JSONRenderer, BrowsableAPIRenderer)
def get(self, request):
'''GET the projects of a given user'''
user = cache.User[request.user.id]
projects = session.query(Node).filter(Node.typename=="PROJECT", Node.user_id== user.id).all()
if len(projects) == 0:
return Response({"detail":"No projects Found for this user"}, status=HTTP_404_NOT_FOUND)
context = format_response(user, projects)
return Response(context)
def post(self, request):
'''CREATE a new project for a given user'''
user = cache.User[request.user.id]
try:
#corpus name
name = request.data["name"]
except AttributeError:
return Response({"detail":"Invalid POST method: \"name\" field is required "}, status = HTTP_406_NOT_ACCEPTABLE)
if name == "":
return Response({"detail":"Invalid POST method: \"name\" field is empty "}, status = HTTP_406_NOT_ACCEPTABLE)
else:
project = session.query(Node).filter(Node.typename=="PROJECT", Node.name==name).first()
if project is not None:
return Response({"detail":"Project with this name already exists", "url":"/projects/%s" %str(project.id)}, status = HTTP_409_CONFLICT)
else:
new_project = Node(
user_id = request.user.id,
typename = 'PROJECT',
name = name,
)
session.add(new_project)
session.commit()
return Response({"detail": "Created", "url":"/projects/%s" %str(new_project.id)}, status= HTTP_201_CREATED)
def delete(self, request):
''' DELETE the projects of a given user'''
user = cache.User[request.user.id]
projects = session.query(Node).filter(Node.typename=="PROJECT", Node.user_id== user.id).all()
#for project in projects:
# project = check_rights(request, project)
uids = []
for node in projects:
node.delete(synchronize_session=False)
#session.delete(node)
session.commit()
uids.append(node.id)
return Response({"detail":"Deleted %i projects" %len(uids)}, status=HTTP_204_NO_CONTENT)
def put(self, request):
'''UPDATE EVERY projects of a given user'''
user = cache.User[request.user.id]
query = session.query(Node).filter(Node.typename=="PROJECT", Node.user_id== request.user.id).all()
uids = []
for node in query:
for key, val in request.data.items():
#here verify that key is in accepted modified keys
if key in ["name", "date", "username"]:
if key == "username":
#changement de propriétaire
user = session.query(Node).filter(Node.typename=="PROJECT", Node.username== username).first()
set(node, user_id, user.id)
else:
setattr(node, key, val)
#node.name = request.data["name"]
session.add(node)
session.commit()
uids.append(node.id)
return Response({"detail":"Updated %s projects" %len(uids)}, status=HTTP_202_ACCEPTED)
class ProjectView(APIView):
'''API endpoint that represent project detail'''
renderer_classes = (JSONRenderer, BrowsableAPIRenderer)
def get(self, request, project_id):
''' GET /api/projects/<project_id> the list of corpora given a project '''
project = session.query(Node).filter(Node.id == project_id).first()
if project is None:
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = HTTP_404_NOT_FOUND)
check_rights(request, project_id)
corpus_list = project.children('CORPUS', order=True).all()
if len(corpus_list) == 0:
return Response({'detail' : "No corpora found for Project Node #%s" %(project_id) },
status = HTTP_404_NOT_FOUND)
# resource_list = [(n["name"], n["type"], n["id"]) for n in corpus_list[0].children('RESOURCE', order=True).all()]
# print(resource_list)
context = format_response(project, corpus_list)
return Response(context)
def delete(self, request, project_id):
'''DELETE project'''
node = session.query(Node).filter(Node.id == project_id).first()
print("DELETE project")
if node is None:
print("Not Found")
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = HTTP_404_NOT_FOUND)
else:
print(">", node)
check_rights(request, project_id)
session.delete(node)
print("delete")
session.commit()
print("commit")
return Response({"detail": "Successfully deleted Node #%s" %project_id}, 200)
def put(self, request, project_id):
'''UPDATE project '''
project = session.query(Node).filter(Node.id == project_id).first()
if project is None:
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = HTTP_404_NOT_FOUND)
check_rights(request, project_id)
params = get_parameters(request)
print(params)
#u_project = deepcopy(project)
for key, val in params.items():
if len(val) == 0:
return Response({"detail":"Invalid POST method: \"%s\" field is empty " %key}, status = HTTP_406_NOT_ACCEPTABLE)
if key in ["name", "date", "username"]:
if key == "username":
#change ownership
#find user
#user = session.query(Node).filter(Node.username == username, Node.typename="USER").first()
#if user.id
pass
elif key == "name":
other = session.query(Node).filter(Node.name == val ).count()
if other == 0:
setattr(project, key, val)
else:
return Response({"detail":"Project with this name already exists"}, status = HTTP_409_CONFLICT)
else:
setattr(project, key, val)
session.add(project)
session.commit()
return Response({"detail":"Updated PROJECT #%s" %str(project_id)}, status=HTTP_206_PARTIAL_CONTENT)
def post(self, request, project_id):
'''CREATE corpus'''
project = session.query(Node).filter(Node.id == project_id).first()
if project is None:
return Response({'detail' : "PROJECT Node #%s not found" %(project_id) },
status = HTTP_404_NOT_FOUND)
project = check_rights(request, project_id)
#controling form data
if not "name" in request.data.keys():
return Response({'detail' : "CORPUS Node: field name is mandatory" },
status = HTTP_406_NOT_ACCEPTABLE)
if not "source" in request.data.keys():
return Response({'detail' : "CORPUS Node: field source is mandatory"},
status = HTTP_406_NOT_ACCEPTABLE)
corpus_name = request.data["name"]
corpus_source = request.data["source"]
if corpus_name == "":
return Response({'detail' : "CORPUS Node name can't be empty" },
status = HTTP_406_NOT_ACCEPTABLE)
corpus = session.query(Node).filter(Node.name == corpus_name, Node.typename == "CORPUS").first()
if corpus is not None:
return Response({'detail' : "CORPUS Node with name '%s' already exists" %(corpus_name) },
status = HTTP_409_CONFLICT)
if corpus_source == "" or corpus_source == 0 or corpus_source == None:
return Response({'detail' : "CORPUS Node source can't be empty"},status=HTTP_406_NOT_ACCEPTABLE)
params = get_parameters(request)
if "method" not in params.keys():
#if "method" not in request.data.keys():
return Response({'detail' : "CORPUS Node has not 'method' parameter"},
status = HTTP_405_METHOD_NOT_ALLOWED)
#method = request.data["method"]
method = params["method"]
if method not in ["parse", "scan", "copy"]:
return Response({'detail' : "CORPUS Node only parse, scan and copy 'method' are allowed" },
status = HTTP_405_METHOD_NOT_ALLOWED)
if method == "copy":
corpus = session.query(Node).filter(Node.id == corpus_source, Node.typename == "CORPUS").first()
if corpus is None:
return Response({'detail' : "CORPUS Node #%s doesn't exist. Fail to copy" %(str(corpus_source)) },
status = HTTP_404_NOT_FOUND)
else:
#cloned_corpus = {k:v for k,v in corpus if k not in ["user_id", "id", "parent_id"]}
cloned_corpus = copy.deepcopy(corpus)
del cloned_corpus.id
cloned_corpus.parent_id = project_id
cloned_corpus.user_id = request.user.id
for child in corpus.get_children():
#{k:getattr(corpus, k) for k in ["name", "date", "source", "hyperdata"] }
cloned_child = copy.deepcopy(child)
del cloned_child["id"]
cloned_child["parent_id"] = new_corpus.id
cloned_corpus["user_id"] = request.user.id
cloned_corpus.add_child(cloned_child)
session.add(cloned_corpus)
session.commit()
#RESOURCE
source = get_resource(int(corpus_source))
if source is None:
return Response({'detail' : "CORPUS Node sourcetype unknown"},
status = HTTP_406_NOT_ACCEPTABLE)
if method == "parse":
print('PARSING')
if not "file" in request.FILES.keys():
return Response({'detail' : "CORPUS Node need a file to parse" },
status = HTTP_405_METHOD_NOT_ALLOWED)
corpus_file = request.FILES['file']
if "parser" in source.keys():
corpus = project.add_child(
name = request.data["name"],
typename = 'CORPUS',
#path = corpus_file,
)
print("CORPUS #", corpus.id)
session.add(corpus)
session.commit()
resource = Node(
name = source["name"],
typename = 'RESOURCE',
parent_id = corpus.id,
hyperdata = {"type": source["type"],
"method": method,
"file": upload(corpus_file),
"query": None}
)
session.add(resource)
session.commit()
return Response({"detail":"Parsing corpus #%s of type #%s" %(str(corpus.id), resource.name)}, 200)
else:
return Response({"detail":"No Parser found for this corpus #%s of type %s" %(str(corpus.id), resource.name)}, 405)
elif method =="scan":
if "crawler" in source.keys():
if not "query" in request.data.keys():
#corpus_file = request.FILES['file']
return Response({'detail' : "CORPUS Node need a query to scan" },
status = HTTP_405_METHOD_NOT_ALLOWED)
query = request.data['query']
corpus = project.add_child(
name = request.data["name"],
typename = 'CORPUS',
)
resource = Node(
name = source["name"],
typename = 'RESOURCE',
parent_id = corpus.id,
user_id = request.user_id,
hyperdata = {"type": source["type"],
"method": method,
"file": None,
"query": query}
)
session.add(resource)
session.commit()
return Response({'detail': "CORPUS #%s created" %corpus.id}, status = HTTP_201_CREATED)
else:
return Response({'detail' : "CORPUS Node only parse, scan and copy 'method' are allowed" },
status = HTTP_405_METHOD_NOT_ALLOWED)
def old_post(self, request, project_id):
form = self._validate_form(request)
#get params
method = form["method"]
if method in ["parse", "scan", "copy"]:
#Le corpus et la resource n'existent pas
# [HACK]
# creation d'un corpus
corpus = Node( typename = 'CORPUS',
user_id = request.user_id,
parent_id = project.id,
name = form["name"],
)
session.add(corpus)
session.commit()
# creation d'une resource
try:
if method == "parse":
form["file"] = request.FILES['file']
action = getattr(self, "_"+method)
#toutes les actions sauf scan suppriment la resource?
#et remontent l'info dans corpus
if action(corpus, form):
# transferer les infos resource dans le corpus
documents = session.query(Node).filter(Node.typename=="DOCUMENT", Node.user_id== user.id, Node.parent_id==corpus.id).all()
response_data = {
"records": format_records(documents),
"resource": format_records([resource]),
"parent": format_parent(project),
"count":len(documents)
}
return Response(response_data, 200)
else:
raise APIException("Error with ", method)
except Exception as e:
raise APIException(e)
else:
#Le corpus existe et la resource doit être mise à jour
corpus = session.query(Node).filter(Node.typename=="CORPUS", Node.parent_id== project.id, Node.name == form["corpus_name"]).first()
source = get_resource(form["source"])
if corpus is None:
return Response("CORPUS not found", 404)
#[HACK] one corpus one resource by Resourcetype_name
resource = session.query(Node).filter(Node.typename=="RESOURCE",
Node.parent_id== corpus.id,
Node.corpus_name == form["corpus_name"],
Node.name == source["name"]
).first()
action = getattr(self, "_"+method)
if action(resource):
# transferer les infos resource dans le corpus
if method == "fetch":
corpus.sources[resource["name"]].append(resource)
session.delete(resource)
session.add(corpus)
session.commit()
else:
session.add(resource)
session.commit()
return Response({"log": "Created", "uids":[corpus.id]}, 200)
else:
session.delete(resource)
session.delete(corpus)
session.commit()
return Response({"log": method+": Error"}, 500)
def _check_method(self, request):
METHODS = ["scan", "parse", "sample", "fetch", "copy"]
try:
method = get_parameters(request)["method"]
except AttributeError:
raise APIException("Precondition failed : You must specify a method", 412)
if method not in METHODS:
raise APIException("Method not allowed", 405)
else:
return method
def _validate_form(self, request):
'''basic validation of the step given each method
'''
params = {}
method = self._check_method(request)
#parsing a file
if method == "parse":
fields = ['source', 'name', "file"]
#scanning a query => results_nb
elif method == "scan":
fields = ['source', 'name', "query"]
#sampling checking results_nb => ids
#~ elif method == "sample":
#~ fields = ['source', 'name', "results_nb"]
#~ #fetching ids => NewParser
#~ elif method == "fetch":
#~ fields = ['source', 'name', "ids"]
#cloning a corpus_id => Corpus
elif method == "copy":
fields = ['source', 'name', "corpus_id"]
for k in fields:
try:
if request.data[k] != "" or request.data[k] is not None:
params[k] = request.data[k]
else:
raise APIException("Mandatory value %s can't be empty "%str(k), 400)
except AttributeError:
raise APIException("Value %s is mandatory" %str(k), 400)
if len(params) > 0:
params["method"] = method
return params
else:
raise APIException("Form is empty: %s" %str(k), 404)
def _sample(self, resource):
resource = self._find_resource_hyperdata(corpus, form)
crawlbot = eval(resource.crawler)(resource)
records = crawlbot.sample()
#resource.status.insert(0,"sampled")
resource.ids = records
corpus.status(action="sample", progress=1, complete=True)
session.add(corpus)
session.commit()
return Response({"uids": [corpus.id]}, status= HTTP_200_OK)
def _fetch(self, resource):
'''internal method to fetch from a corpus the resource.urls >>> resource._parser(urls)'''
resource = self._find_resource_hyperdata(corpus, form)
resource.status(action="fetch", progress=1, complete=False)
crawlbot = eval(resource.typecrawler)(resource)
#send job to celery
scheduled(crawlbot.fetch())
corpus.status(action="fetch", progress=1, complete=True)
session.add(corpus)
session.commit()
return Response({"uids": [corpus.id]}, 200)
def _copy(self, corpus, form):
#find the target corpus
new_corpus = session.query(Node).filter(Node.typename=="CORPUS", Node.corpus_id == form["corpus_id"]).first()
#get the resource of this corpus and copy it two
new_resource = self._find_resource_hyperdata(new_corpus, form)
#copy new_corpus to previously created corpus
new_resouce.method = "cloned CORPUS #%i" %(new_corpus.id)
new_corpus.id = corpus.id
# change new_corpus ownership
new_corpus.parent_id = corpus.parent_id
new_corpus.user_id = corpus.user_id
#get the documents of the existing corpus
for doc in new_corpus.get_children():
doc.parent_id = new_corpus.parent_id
doc.user_id = new_corpus.id
#store it into corpus
new_doc = corpus.add_child(doc)
for ngrams in doc.get_children():
new_ngrams.parent_id = new_doc.id
new_ngrams.user_id = new_corpus.user_id
#store it into corpus
new_doc.add_child(new_ngrams)
#save the corpus
corpus.status(action="copy", progress=1, complete=True)
session.add(corpus)
session.commit()
return Response({"log": "Corpus created", "uids":[corpus.id]}, 202)
def _scan(self, corpus, form):
'''internal method to scan a query >> add results_nb to resource as a corpus hyperdata'''
resource = self._find_resource_hyperdata(corpus, form)
#corpus_query = check_query(form["query")
ressource.query = form["query"]
corpus.status(action="scan", progress=1, complete=False)
session.add(corpus)
session.commit()
crawlbot = eval(resource.crawler)(corpus.id)
corpus.status(action="scan", progress=2, complete=False)
session.add(corpus)
session.commit()
results_nb = crawlbot.scan_results()
resource.results_nb = results_nb
corpus.status(action="scan", progress=2, complete=True)
code = 200
session.add(corpus)
session.commit()
return Response({"log": "Corpus created", "uids":[corpus.id]}, 200)
def _parse(self, corpus, form):
'''internal method to parse a corpus >> resource >> corpus >> docs
corpus >> resource (method + file params + parser )
^ >> docs (resource.defaultlang <--------| )
| >> ngrams
|------- le tout rappatrié dans corpus
'''
#1. creating a resource
resource = {}
resource = Node(
user_id = corpus.user_id,
parent_id = corpus.id,
typename = "RESOURCE",
#corpus_name = form["name"],
)
resource.method = form["method"]
resource.path = upload(form['file'])
#mapping the default attribute of a given source from constant RESOURCETYPE
for k, v in get_resource(int(form["source"])).items():
setattr(resource, k, v)
resource.status(action="parse", progress=1, complete=False)
session.add(resource)
session.commit()
try:
workflow(resource)
except Exception as e:
print("=======except dans _parse===========")
print(e)
from traceback import print_tb
print_tb(e.__traceback__)
print("====================================")
return True
from django.conf.urls import url from django.conf.urls import url
from . import nodes from . import nodes
from . import projects
from . import corpora
from . import ngrams from . import ngrams
from . import metrics from . import metrics
from . import ngramlists from . import ngramlists
...@@ -10,7 +12,33 @@ from graph.rest import Graph ...@@ -10,7 +12,33 @@ from graph.rest import Graph
urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view() ) urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view() )
, url(r'^nodes/(\d+)$' , nodes.NodeResource.as_view() ) , url(r'^nodes/(\d+)$' , nodes.NodeResource.as_view() )
, url(r'^nodes/(\d+)/having$' , nodes.NodeListHaving.as_view() ) , url(r'^nodes/(\d+)/having$' , nodes.NodeListHaving.as_view() )
, url(r'^nodes/(\d+)/status$' , nodes.Status.as_view() )
#Projects
, url(r'^projects$' , projects.ProjectList.as_view() )
, url(r'^projects/(\d+)$' , projects.ProjectView.as_view() )
#?view=resource
#?view=docs
#Corpora
, url(r'^projects/(\d+)/corpora/(\d+)$' , corpora.CorpusView.as_view() )
#?view=journal
#?view=title
#?view=analytics
#Sources
#, url(r'^projects/(\d+)/corpora/(\d+)/sources$' , corpora.CorpusSources.as_view() )
#, url(r'^projects/(\d+)/corpora/(\d+)/sources/(\d+)$' , corpora.CorpusSourceView.as_view() )
#Facets
, url(r'^projects/(\d+)/corpora/(\d+)/facets$' , nodes.CorpusFacet.as_view() )
#Favorites
, url(r'^projects/(\d+)/corpora/(\d+)/favorites$', nodes.CorpusFavorites.as_view() )
#Metrics
, url(r'^projects/(\d+)/corpora/(\d+)/metrics$', metrics.CorpusMetrics.as_view() )
#GraphExplorer
, url(r'^projects/(\d+)/corpora/(\d+)/explorer$' , Graph.as_view())
# data for graph explorer (json)
# GET /api/projects/43198/corpora/111107/explorer?
# Corresponding view is : /projects/43198/corpora/111107/explorer?
# Parameters (example):
# explorer?field1=ngrams&field2=ngrams&distance=conditional&bridgeness=5&start=1996-6-1&end=2002-10-5
# Ngrams # Ngrams
, url(r'^ngrams/?$' , ngrams.ApiNgrams.as_view() ) , url(r'^ngrams/?$' , ngrams.ApiNgrams.as_view() )
...@@ -63,10 +91,5 @@ urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view() ...@@ -63,10 +91,5 @@ urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view()
, url(r'^ngramlists/maplist$' , ngramlists.MapListGlance.as_view() ) , url(r'^ngramlists/maplist$' , ngramlists.MapListGlance.as_view() )
# fast access to maplist, similarly formatted for termtable # fast access to maplist, similarly formatted for termtable
, url(r'^projects/(\d+)/corpora/(\d+)/explorer$' , Graph.as_view())
# data for graph explorer (json)
# GET /api/projects/43198/corpora/111107/explorer?
# Corresponding view is : /projects/43198/corpora/111107/explorer?
# Parameters (example):
# explorer?field1=ngrams&field2=ngrams&distance=conditional&bridgeness=5&start=1996-6-1&end=2002-10-5
] ]
/////////////////////////////////////
//// AJAX CALLS ////
/////////////////////////////////////
// c24b 01/06/2016
// templating +
// Generic AJAX methods (through API) for project and corpus
// - get (listing)
// - create
// - edit
// - delete
// - update
// - recalculate => API metrics
// utils: templateing cookies, error form, selected checkbox
////////////////////////////////////////////////////////////////////////
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
function loadTpl(tpl_id, record){
//generic function to load a TPL given its tpl_id (#)
var corpus_tpl = $(tpl_id).html();
corpus_tpl = corpus_tpl.replace(/{count}/g, record.count);
corpus_tpl = corpus_tpl.replace(/{name}/g, record.name);
corpus_tpl = corpus_tpl.replace(/{url}/g, record.url);
corpus_tpl = corpus_tpl.replace(/{status}/g, record.status);
corpus_tpl = corpus_tpl.replace(/{id}/g, record.id);
corpus_tpl = corpus_tpl.replace(/{query}/g, record.query);
corpus_tpl = corpus_tpl.replace(/{source}/g, record.source);
return corpus_tpl
};
// FORM STATUSES
function addFormStatus(status, form, msg){
//inform user from error in back throught API
//alert(form)
dismiss = '<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>'
if(status =="error"){
icon = '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> '
msg = dismiss+icon+msg
//form
$(form).addClass("alert-danger")
//msg div
msg_div = $(form).children("div#status-form")
console.log(msg_div)
msg_div.html(msg)
msg_div.collapse("show")
}
else{
$(form).collapse("hide")
window.location.reload()
}
}
function resetStatusForm(form){
$(form).removeClass("alert-danger alert-info alert-success")
$("div#status-form").val("")
$("div#status-form").collapse("hide")
alert("reset")
}
//PAGES STATUSES
function addPageStatus(status, msg){
dismiss = '<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>'
if (status == "error"){
icon = '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> '
msg = dismiss+icon+msg
$('div#status-msg').addClass("alert-danger");
$('div#status-msg').html(msg);
$("div#status").collapse("show");
}
else if (status == "info"){
$('div#status-msg').addClass("alert-info");
icon = '<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> '
msg = dismiss+icon+msg
$('div#status-msg').html(msg);
$("div#status").collapse("show");
$('div#editor').addClass("hidden");
}
else{
window.location.reload();
}
}
function selectedUrls(){
var selected = [];
$('input:checked').each(function() {
selected.push($(this).val());
});
return selected
};
function selectedIds(){
//only used for recalc
var selected = [];
$('input:checked').each(function() {
selected.push($(this).data("id"));
});
return selected
};
//// GET FROM API
function getProjects(){
url_ = "/api/projects"
console.log( ">> get_projects() from api/projects VIEW" );
$.ajax({
type: "GET",
url: url_,
dataType: "json",
success : function(data) {
var _content = "";
var _count = data.count;
for(var i in data.records) {
console.log(data.records[i]);
var record = data.records[i];
record.url = "/projects/"+record.id;
_content += loadTpl("#project_item",record)
};
$("#projects").html( _content );
},
error: function(data) {
console.log(data.status, data.responseJSON);
if (data.status == 404){
msg = 'You have no projects for now. Click <strong>Add project</strong> to create a new one'
addPageStatus("info", msg);
return;
}
else{
msg = data.status+':'+data.responseJSON["detail"]
addPageStatus("error", msg);
return;
}
},
});
};
function getCorpora(){
console.log( ">> get_CORPORA() from api/projects/<pid> VIEW" );
var pathname = window.location.pathname; // Returns path only
//alert(pathname)
url_ = "/api"+pathname
$.ajax({
type: "GET",
url: url_,
success : function(data) {
project = data.parent;
corpus_nb = data.count
corpora = data.records
//manque another request to serve resources
resources = getRessources(pathname);
var _content = "";
corpora.forEach(function(corpus) {
//getCorpus() info
corpus.url = pathname+"/corpora/"+corpus.id
corpus.count = corpus.documents.length
//corpus.resources = getCorpusResource(pathname+"/corpora/"+corpus.id)
_content += loadTpl("#corpus_item", corpus);
//corpus.status = setInterval( getCorpusStatus(pathname+"/corpora/"+corpus.id), 5000 );
//corpus.status = getCorpusStatus()
});
$("div#corpora").html( _content);
},
error: function(data) {
console.log(data.status, data.responseJSON);
if (data.status == 404){
msg = 'You have no corpora for now. Click <strong>Add corpus</strong> to create a new one'
addPageStatus("info", msg);
return;
}
else{
msg = data.status+':'+data.responseJSON["detail"]
addPageStatus("error", msg);
return;
}
},
});
};
function getRessources(){
var pathname = window.location.pathname;
url_ = "/api"+pathname+"/resources"
alert(url_)
}
//// POST TO API
//// PUT AND PATCH TO API
function deleteOne(url){
$.ajax({
url: '/api'+url,
type: 'delete',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(xhr) {
console.log("SUCCESS!");
msg = "Sucessfully deleted"
addPageStatus("success", "#editForm", msg);
},
error: function(xhr) {
console.log("FAIL!");
console.log(xhr);
var status = xhr.status;
var info = xhr.responseJSON["detail"];
var msg = "<strong>ERROR ["+status+"]:</strong>"+ "<p>"+info+"</p>"
addPageStatus("error", msg);
},
});
};
function editOne(url, data){
$.ajax({
url: '/api'+url+"?"+jQuery.param(data),
type: 'PUT',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(response) {
console.log(response);
console.log("EDIT SUCCESS!");
addFormStatus("success", "div#editForm", response["detail"]);
window.location.reload()
},
error: function(xhr) {
console.log("EDIT FAIL!")
var status = xhr.status;
var info = xhr.responseJSON["detail"];
var msg = "<strong>ERROR ["+status+"]:</strong>"+ "<p>"+info+"</p>"
addFormStatus("error", "div#editForm", msg);
},
});
};
function recalculateOne(id){
$.ajax({
url: '/api/metrics/'+id,
type: 'PATCH',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(response) {
console.log("SUCCESS!");
window.location.reload();
},
error: function(xhr) {
console.log("FAIL!")
console.log(result)
var status = xhr.status;
var info = xhr["detail"];
var msg = "<strong>ERROR ["+status+"]:</strong>"+ "<p>"+info+"</p>"
//no form error is sent to page status alert
addPageStatus("error", msg);
},
});
};
/////////////////////////////////////////////////
/// PAGE INTERACTION
////////////////////////////////////////////////
//CONTEXTUAL HELP
$(document).on("hover", "button", function(){
//make tooltip on buttons using title attr
$(this).tooltip();
});
//MULTIPLE SELECTION ACTION
// checkbox with BUTTON #delete, #edit #refresh
//DELETE MULTI
$(document).on("click","#delete", function(){
//alert("Delete");
var selected = selectedUrls();
selected.forEach(function(url) {
deleteOne(url);
});
window.location.reload();
});
//EDIT MULTI
$(document).on("click","#edit", function(){
var selected = selectedUrls();
var statuses = [];
// selected.forEach(function(url) {
// editOne(url, data);
// });
alert("Not Implemented Yet")
});
//RECALC MULTI
$(document).on("click","#recalculate", function(){
alert("Recalculate");
var selected = selectedIds();
selected.forEach(function(id) {
recalculateOne(id);
});
window.location.reload();
});
//UNIQUE action
// button with .delete, .edit, .refresh
// UNIQUE DELETION
$(document).on("click", ".delete", function() {
var url = $( this ).data("url");
alert(url);
deleteOne(url);
alert("delete");
});
//UNIQUE EDITION
$(document).on("click",".edit", function(){
$('#editForm').collapse('show');
var url = $( this ).data("url");
$("#edit-submit").on('click', function(){
//alert(url)
name = $("input#name_p").val()
data = {"name": name}
editOne(url, data);
})
$("#edit-cancel").on('click', function(){
//alert("cancel")
$('input#name_p').val("");
resetStatusForm("#editForm");
})
if ($("#editForm").hasClass("collapse in")){
$("button").on("click", ".edit", function(){
$('input#name_p').val("");
resetStatusForm("#editForm");
})
}
})
//UNIQUE RECALC
$(document).on("click",".refresh", function(){
//alert("refresh")
//console.log( $(this))
var id = $(this).data("id")
//var url = $( this ).data("url")
recalculateOne(id)
window.location.reload();
});
function createProject() {
//alert("Creating a new project");
//simple post: with the name
//onclick inside element because probleme of scope with modal
//we recover the element by hand for the moment
var project_name = $(".popover #inputName").val();
//alert(project_name);
console.log("Create project #"+project_name);
console.log("POST /api/projects");
$.ajax({
url: '/api/projects',
type: 'post',
data: {"name":project_name},
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(response) {
console.log(response.status);
console.log(response["detail"]);
location.reload();
},
error: function(data) {
console.log(data)
status = data.status;
info = data["detail"];
msg = "<strong>ERROR ["+status+"]:</strong>"+ "<p>"+info+"</p>"
addFormStatus("error","div#createForm", msg)
},
})
};
function createCorpus(url, method, form){
console.log(form)
console.log("POST corpus")
$.ajax({
url: '/api/'+url+'?method='+method,
type: 'post',
async: true,
contentType: false, // obligatoire pour de l'upload
processData: false, // obligatoire pour de l'upload
dataType: 'json', // selon le retour attendu
data: form,
cache: false,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(data) {
console.log(data)
status = data.status;
info = data["detail"];
msg = "<strong>OK ["+status+"]:</strong>"+ "<p>"+info+"</p>"
addFormStatus("success", "form#formCorpus", msg)
setTimeout(function(){
$('div#info').slideUp('slow').fadeOut(function() {
window.location.reload(true);
});
}, 6000);
},
error: function(data) {
console.log(data);
console.log(data)
status = data.status;
info = data.responseJSON["detail"];
msg = "<strong>ERROR ["+status+"]:</strong>"+ "<p>"+info+"</p>"
addFormStatus("error","form#formCorpus", msg);
//$(".collapse").collapse("hide");
//_content = '<h2><span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>Error while creating the corpus.</h2>'
//$("div#info").append(_content);
//$("div#info").addClass("alert-danger");
//$("div#info").collapse("show");
},
})
};
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
<script type="text/javascript" src="{% static "lib/jquery/1.11.1/jquery.min.js" %}"></script> <script type="text/javascript" src="{% static "lib/jquery/1.11.1/jquery.min.js" %}"></script>
<script type="text/javascript" src="{% static "lib/gargantext/garganrest.js" %}"></script> <script type="text/javascript" src="{% static "lib/gargantext/garganrest.js" %}"></script>
<link rel="stylesheet" href="{% static "lib/jquery/1.11.2/jquery-ui.css" %}"> <link rel="stylesheet" href="{% static "lib/jquery/1.11.2/jquery-ui.css" %}">
<script type="text/javascript" src="{% static "lib/morris/morris.min.js" %}"></script> <script type="text/javascript" src="{% static "lib/morris/morris.min.js" %}"></script>
<link rel="stylesheet" href="{% static "lib/morris/morris.css" %}"> <link rel="stylesheet" href="{% static "lib/morris/morris.css" %}">
...@@ -36,138 +35,121 @@ ...@@ -36,138 +35,121 @@
Projects Projects
</h1> </h1>
</div> </div>
<div class="col-md-4"></div> <div class="col-md-3"></div>
<div class="col-md-4"> <div class="col-md-5">
<p> <p>
<br> <br>
<button <button id="add" type="button" class="btn btn-primary btn-lg" data-container="body" data-toggle="popover" data-placement="bottom">
type="button"
class="btn btn-primary btn-lg"
data-container="body"
data-toggle="popover"
data-placement="bottom"
>
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
Add a new project Add a new project
</button> </button>
<div id="popover-content" class="hide"> <div id="popover-content" class="hide">
<form enctype='multipart/form-data' action='/projects/' method='post'> <div id="createForm" class="form-group">
{% csrf_token %} {% csrf_token %}
<p> <div id="status-form" class="collapse">
<label for="id_name">Name:</label> </div>
<input id="id_name" maxlength="255" name="name" type="text" /> <div class="row inline">
</p> <label class="col-lg-3" for="inputName" ><span class="pull-right">Name:</span></label>
<input type='submit' class="btn" value='Add this project !'/> <input class="col-lg-8" type="text" id="inputName" class="form-control">
</form> </div>
<div class="row inline">
<div class="col-lg-3"></div>
<button id="createProject" class="btn btn-primary btn-sm col-lg-8 push-left">Add Project</button>
<div class="col-lg-2"></div>
</div> </div>
</p>
</div> </div>
</div> </div>
</p>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<div class="row"> <!-- GENERIC STATUS INFO -->
<div class="container"> <div id="status" class="row col-lg-12 collapse">
<div class="container">
<div class="container">
{% if projects %}
{% for project in projects %}
<!--<div class="col-md-offset-7 col-md-4 content" style="background-color:grey">!-->
<div id="project_{{project.id}}" class="row">
<h3>
<div class="col-md-5 content"> <div id="status-msg" class="alert">
<a
href="/projects/{{ project.id }}">
<span class="glyphicon glyphicon-book" aria-hidden="true"></span>
{{ project.name }}
</a>
</div> </div>
<div class="col-md-3 content"> </div>
<a href="/projects/{{project.id}}" > <!-- CHECKBOX EDITION -->
<button type="button" class="btn btn-default" aria-label="Left Align"> <div class="row" id="editor">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span> <button title="delete selected project" type="button" class="btn btn-danger" id="delete">
<span class="glyphicon glyphicon-trash " aria-hidden="true" ></span>
</button> </button>
</a>
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"
data-content=" <ul>
<!-- <!--
<li>Rename</li> <button title="edit selected project" type="button" class="btn btn-warning" id="edit">
--!> <span class="glyphicon glyphicon-pencil " aria-hidden="true" onclick="editProjects()"></span>
<li onclick=&quot; </button> -->
garganrest.nodes.delete({{project.id}}, function(){$('#project_'+{{project.id}}).remove()}); <!-- <button type="button" class="btn btn-info" id="recalculate">
$(this).parent().parent().remove(); <span class="glyphicon glyphicon-refresh " aria-hidden="true" onclick="recalculateProjects()"></span>
&quot;><a href=&quot;#&quot;>Delete</a>
</li>
</ul>
">
<span class="glyphicon glyphicon-trash pull-right" aria-hidden="true"></span>
</button> </button>
-->
</div> </div>
{% if common_users %}
<!-- <a style="cursor:pointer;"><img class="share_button" data-id="{{ project.id }}" title="Share it!" width="20px" src="{% static "img/share.png" %}"></img></a> --!>
{% endif %}
</h3> <br />
<h4>{{ project.subtitle }}<h4>
</div>
{% endfor %}
{% endif %}
{% if common_projects %}
<br><br><br><br><br><br>
<h3><i> - - Shared projects - -</i></h3>
{% for project in common_projects %}
<!--<div class="col-md-offset-7 col-md-4 content" style="background-color:grey">!-->
<div class="col-md-3 content">
<h3><a href="/projects/{{ project.id }}">{{ project.name }}</a>
<button type="button" class="btn btn-xs btn-default" data-container="body" data-toggle="popover" data-placement="bottom"
data-content='
<ul>
<li> Rename </li>
<li><a href="/projects/{{ project.id }}">Add new corpus</a></li>
<li><a href="/delete/{{ project.id }}">Delete</a></li>
</ul>
'>Manage</button>
</h3>
<h4>{{ project.subtitle }}<h4>
</div>
{% endfor %}
{% endif %}
</div> <div class="row container" id="projects">
</div> <!--here loading projectlist from GET /projects-->
</div>
</div> </div>
<div id="sharemodal" class="modal fade"> </div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"> <script type="html/tpl" id="project_item">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <div id="{url}" class="item row">
<h3 class="modal-title">Share this Corpus with your Groups</h3> <h3>
<div class="col-md-6 content">
<input id="checkbox" type="checkbox" value="{url}" data-id="{id}">
<a href="{url}">
<span class="glyphicon glyphicon-book" aria-hidden="true"></span>
<span class="item_name">{name}</span>
</a>
</div>
</h3>
<div id="project_detail" class="col-md-4 content">
<div id="item-editor">
<!-- GET Project -->
<a href="{url}">
<button type="button" title="see project" class="btn btn-default show pull-right" aria-label="Left Align">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
</button>
</a>
<!-- EDIT PROJECT-->
<button class="btn btn-default edit pull-right" data-url="{url}" data-toggle="collapse" data-target="#editForm" aria-expanded="false" aria-controls="editForm">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</button>
<!-- 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>
</button>
<!-- REFRESH PROJECT -->
<button type="button" class="btn btn-default refresh pull-right" data-id="{id}">
<span class="glyphicon glyphicon-refresh pull-right" aria-hidden="true"></span>
</button>
</div>
<div id="project_status">
<!-- Here add nb of the corpus? -->
</div> </div>
<div class="modal-body form-horizontal">
<h4>List of available groups:</h4>
<div id="groups_list">here show the groups</div>
<div class="modal-footer">
<button id="closesharemodal" type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button id="send_share" type="button" class="btn btn-primary" >Share<span id="simpleloader"></span></button>
</div> </div>
</div> </div>
<div id="editForm" class="collapse col-lg-10 alert" >
<!-- status of the form -->
<div id="status-form" class="collapse">
</div> </div>
<b>Name:</b><input type="text" id="name_p" />
<button id="edit-submit" class="btn btn-success btn-sm"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
<button id="edit-cancel" class="btn btn-danger btn-sm" ><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
</div> </div>
</div> </script>
<style type="text/css"> <style type="text/css">
label { label {
...@@ -181,80 +163,22 @@ ...@@ -181,80 +163,22 @@
} }
</style> </style>
<script type="text/javascript" src="{% static "lib/gargantext/garganrest_projects.js" %}"></script>
<script type="text/javascript"> <script type="text/javascript">
function getCookie(name) { getProjects();
var cookieValue = null; $(document).on("click","#createProject", function(e){
if (document.cookie && document.cookie != '') { //alert("clicked");
var cookies = document.cookie.split(';'); //
for (var i = 0; i < cookies.length; i++) { //resetStatusForm("#createForm");
var cookie = jQuery.trim(cookies[i]); //$('input#inputName').val("");
// Does this cookie string begin with the name we want? createProject();
if (cookie.substring(0, name.length + 1) == (name + '=')) { $('#add').on('hidden.bs.popover', function () {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); // do something…
break; resetStatusForm("#createForm");
} })
}
} })
return cookieValue;
}
var last_project = -1
function get_groups() {
console.log( "IN get_groups()!" )
var url_ = "/get_groups"
$.ajax({
type: "GET",
url: url_,
dataType: "json",
success : function(data, textStatus, jqXHR) {
var _content = ""
for(var i in data) {
var g_id = data[i][0] , g_name=data[i][1]
_content += '<label><input name="groups" data-id="'+g_id+'" type="checkbox" />&nbsp;'+g_name+'</label>'
}
$("#groups_list").html( _content )
},
error: function(exception) {
console.log("exception!:"+exception.status)
}
});
}
if( $(".share_button").length>0 ) {
$(".share_button").click(function(){
last_project = $(this).data("id")
get_groups()
$("#sharemodal").modal("show");
});
$("#send_share").click(function() {
$('input[name=groups]:checked').each(function () {
$("#send_share").attr("disabled","disabled")
console.log( $(this).data("id") );
$("#simpleloader").html('<img width="30px" src="{% static "img/loading-bar.gif" %}"></img>')
$.ajax({
url: "/api/share/"+last_project+"/"+$(this).data("id"),
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function(data) {
console.log("SUCCESS!")
window.location.reload();
},
error: function(result) {
console.log("FAIL!")
console.log(result)
}
});
});
});
}
</script> </script>
{% endblock %} {% 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