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.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_cache import cache, or_
from gargantext.util.validation import validate
......@@ -8,7 +8,7 @@ from gargantext.util.http import ValidationException, APIView \
, get_parameters, JsonHttpResponse, Http404\
, HttpResponse
from .api import *
from collections import defaultdict
import csv
......@@ -67,6 +67,48 @@ def _query_nodes(request, node_id=None):
# return the result!
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):
......@@ -143,8 +185,6 @@ class NodeListHaving(APIView):
Simple implementation:
Takes IDs of corpus and ngram and returns list of relevent documents in json format
according to TFIDF score (order is decreasing).
2016-09: add total counts to output json
'''
def get(self, request, corpus_id):
parameters = get_parameters(request)
......@@ -155,7 +195,7 @@ class NodeListHaving(APIView):
except :
raise ValidationException('"ngram_ids" needs integers separated by comma.')
limit = DEFAULT_N_DOCS_HAVING_NGRAM
limit=5
nodes_list = []
corpus = session.query(Node).filter(Node.id==corpus_id).first()
......@@ -178,18 +218,26 @@ class NodeListHaving(APIView):
.filter(Node.typename == 'DOCUMENT', Node.parent_id== corpus.id)
.filter(or_(*[NodeNodeNgram.ngram_id==ngram_id for ngram_id in ngram_ids]))
.group_by(Node)
.order_by(func.sum(NodeNodeNgram.score).desc())
.limit(limit)
)
# 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())
.limit(limit)
)
for node, score in nodes_results_query:
# print("\n")
# 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("\t corpus:",corpus_id,"\t",node.name)
node_dict = {
......@@ -201,10 +249,7 @@ class NodeListHaving(APIView):
node_dict[key] = node.hyperdata[key]
nodes_list.append(node_dict)
return JsonHttpResponse({
'count': nodes_count,
'records': nodes_list
})
return JsonHttpResponse(nodes_list)
......@@ -438,8 +483,7 @@ class CorpusFacet(APIView):
# check that the hyperfield parameter makes sense
_facet_available_subfields = [
'journal', 'publication_year', 'rubrique',
'language_iso2', 'language_iso3', 'language_name',
'authors'
'language_iso2', 'language_iso3', 'language_name'
]
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 . import nodes
from . import projects
from . import corpora
from . import ngrams
from . import metrics
from . import ngramlists
......@@ -10,7 +12,33 @@ from graph.rest import Graph
urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view() )
, url(r'^nodes/(\d+)$' , nodes.NodeResource.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
, url(r'^ngrams/?$' , ngrams.ApiNgrams.as_view() )
......@@ -63,10 +91,5 @@ urlpatterns = [ url(r'^nodes$' , nodes.NodeListResource.as_view()
, url(r'^ngramlists/maplist$' , ngramlists.MapListGlance.as_view() )
# 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 @@
<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>
<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>
<link rel="stylesheet" href="{% static "lib/morris/morris.css" %}">
......@@ -36,138 +35,121 @@
Projects
</h1>
</div>
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="col-md-3"></div>
<div class="col-md-5">
<p>
<br>
<button
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>
<button id="add" 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>
Add a new project
</button>
<div id="popover-content" class="hide">
<form enctype='multipart/form-data' action='/projects/' method='post'>
{% csrf_token %}
<p>
<label for="id_name">Name:</label>
<input id="id_name" maxlength="255" name="name" type="text" />
</p>
<input type='submit' class="btn" value='Add this project !'/>
</form>
</div>
</p>
</div>
</button>
<div id="popover-content" class="hide">
<div id="createForm" class="form-group">
{% csrf_token %}
<div id="status-form" class="collapse">
</div>
<div class="row inline">
<label class="col-lg-3" for="inputName" ><span class="pull-right">Name:</span></label>
<input class="col-lg-8" type="text" id="inputName" class="form-control">
</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>
</div>
</p>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="container">
<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>
<!-- GENERIC STATUS INFO -->
<div id="status" class="row col-lg-12 collapse">
<div class="col-md-5 content">
<a
href="/projects/{{ project.id }}">
<span class="glyphicon glyphicon-book" aria-hidden="true"></span>
{{ project.name }}
</a>
</div>
<div id="status-msg" class="alert">
</div>
<div class="col-md-3 content">
<a href="/projects/{{project.id}}" >
<button type="button" class="btn btn-default" aria-label="Left Align">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
</button>
</a>
</div>
<!-- CHECKBOX EDITION -->
<div class="row" id="editor">
<button title="delete selected project" type="button" class="btn btn-danger" id="delete">
<span class="glyphicon glyphicon-trash " aria-hidden="true" ></span>
</button>
<!--
<button title="edit selected project" type="button" class="btn btn-warning" id="edit">
<span class="glyphicon glyphicon-pencil " aria-hidden="true" onclick="editProjects()"></span>
</button> -->
<!-- <button type="button" class="btn btn-info" id="recalculate">
<span class="glyphicon glyphicon-refresh " aria-hidden="true" onclick="recalculateProjects()"></span>
</button>
-->
<button type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="bottom"
data-content=" <ul>
<!--
<li>Rename</li>
--!>
<li onclick=&quot;
garganrest.nodes.delete({{project.id}}, function(){$('#project_'+{{project.id}}).remove()});
$(this).parent().parent().remove();
&quot;><a href=&quot;#&quot;>Delete</a>
</li>
</ul>
">
<span class="glyphicon glyphicon-trash pull-right" aria-hidden="true"></span>
</button>
</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 %}
</div>
</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>
</div>
<div class="row container" id="projects">
<!--here loading projectlist from GET /projects-->
</div>
<div id="sharemodal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
</div>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 class="modal-title">Share this Corpus with your Groups</h3>
<script type="html/tpl" id="project_item">
<div id="{url}" class="item row">
<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 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>
<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>
</script>
<style type="text/css">
label {
......@@ -181,80 +163,22 @@
}
</style>
<script type="text/javascript" src="{% static "lib/gargantext/garganrest_projects.js" %}"></script>
<script type="text/javascript">
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;
}
getProjects();
$(document).on("click","#createProject", function(e){
//alert("clicked");
//
//resetStatusForm("#createForm");
//$('input#inputName').val("");
createProject();
$('#add').on('hidden.bs.popover', function () {
// do something…
resetStatusForm("#createForm");
})
})
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>
{% 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