diff --git a/graphs/models.py b/graphs/models.py
index d3e70bb2..b7104834 100644
--- a/graphs/models.py
+++ b/graphs/models.py
@@ -10,10 +10,10 @@
ex. 'id' for user table would be 'user_id'
'''
-from sqlalchemy import Column, Integer, String, ForeignKey, Table, Index, ForeignKeyConstraint
+from sqlalchemy import Column, Integer, String, ForeignKey, Table, Index, ForeignKeyConstraint, Boolean, UniqueConstraint
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
-from sqlalchemy.types import TIMESTAMP
+from sqlalchemy.types import TIMESTAMP, Boolean
from django.db import models
from django.conf import settings
from sqlalchemy import create_engine
@@ -316,6 +316,33 @@ class Edge(Base):
ForeignKeyConstraint([user_id, graph_id, tail_node_id], [Node.user_id, Node.graph_id, Node.node_id], ondelete="CASCADE", onupdate="CASCADE"), {})
#no relationship specified
+class ShareGraphEvent(Base):
+ __tablename__ = 'share_graph_event'
+ # unique id for each share graph event
+ id = Column(Integer, autoincrement=True, primary_key=True)
+ # id of the graph shared
+ graph_id = Column(String, nullable=False)
+ # id of the owner of the graph which is shared
+ owner_id = Column(String, nullable=False)
+ # id of the group the graph is shared in
+ group_id = Column(String, ForeignKey('group.group_id', ondelete="CASCADE", onupdate="CASCADE"), nullable=False)
+ # id of the member of the group.
+ # Hence there can be multiple share graph events if a owner shares a grap
+ # with a group. A share graph event will be created for all the memebers
+ # of the group except the owner of the graph (the one who shared it).
+ member_id = Column(String, ForeignKey('user.user_id', ondelete="CASCADE", onupdate="CASCADE"), nullable=False)
+ # timestamp at which the share graph event occured
+ share_time = Column(TIMESTAMP, nullable = False)
+ # Boolean value to track if notifications is read or not.
+ # if True then the notification is active, i.e not read
+ is_active = Column(Boolean, nullable=False)
+ # We use ForeignKeyConstraint for graph_id and owner_id
+ # because this is the only to define a composite foreign key
+ __table_args__ = (
+ UniqueConstraint('graph_id', 'owner_id', 'group_id', 'member_id'),
+ ForeignKeyConstraint([graph_id, owner_id], [Graph.graph_id, Graph.user_id], ondelete="CASCADE", onupdate="CASCADE"),
+ )
+
#Create indices
Index('graph_public_idx', Graph.public)
Index('graph_owner_idx', Graph.user_id)
diff --git a/graphs/static/graphs/css/notifications.css b/graphs/static/graphs/css/notifications.css
new file mode 100644
index 00000000..e418b71b
--- /dev/null
+++ b/graphs/static/graphs/css/notifications.css
@@ -0,0 +1,3 @@
+.notification_read {
+ background-color: #bdbdbd;
+}
\ No newline at end of file
diff --git a/graphs/static/graphs/js/notifications.js b/graphs/static/graphs/js/notifications.js
new file mode 100644
index 00000000..78fd63a5
--- /dev/null
+++ b/graphs/static/graphs/js/notifications.js
@@ -0,0 +1,97 @@
+$(document).ready(function () {
+ /**
+ * Mark notification as read through the UI.
+ */
+
+
+ // Method to mark as read individual notifications
+ $(".read_notification").click(function(e){
+ // get the user id of the logged in user
+ var uid = $(this).attr('uid');
+ // get a list of notification ids, in this case it will be a
+ // list of length 1 as it a single notification
+ var notification_ids = [$(this).attr('notification_id')];
+ // Call function read_notification which sends a POST request to mark
+ // notification as read
+ read_notifications(uid, notification_ids, function(error, data){
+ if (error) {
+ return alert(error);
+ } else {
+ // If no error is caught that manipulate the DOM for notification
+ manipulate_dom(notification_ids);
+ }
+ });
+ });
+
+ // Method to mark all notification of a group as read for the user
+ $(".read_all_group_notifications").click(function(e){
+ // get the user id of the logged in user
+ var uid = $(this).attr('uid');
+ // get the group id of the notifications that need to be marked as read
+ var group_id = $(this).attr('group_id');
+ // create a list of notification ids which are present in the group
+ var notification_ids = [];
+ $.each($('.notifications.' + group_id), function( index, notification ) {
+ notification_ids.push($(notification).attr('id'));
+ });
+ // Call function read_notifications which sends a POST request to mark
+ // notifications as read
+ read_notifications(uid, notification_ids, function(error, data){
+ if (error) {
+ return alert(error);
+ } else {
+ // If no error is caught that manipulate the DOM for notification
+ manipulate_dom(notification_ids);
+ }
+ });
+ });
+
+ // Method to mark all notifications as read for the user
+ $(".read_all_notifications").click(function(e){
+ // get the user id of the logged in user
+ var uid = $(this).attr('uid');
+ // create a list of notification ids which are present in the group
+ var notification_ids = [];
+ $.each($('.notifications'), function( index, notification ) {
+ notification_ids.push($(notification).attr('id'));
+ });
+ // Call function read_notification which sends a POST request to mark
+ // notifications as read
+ read_notifications(uid, notification_ids, function(error, data){
+ if (error) {
+ return alert(error);
+ } else {
+ // If no error is caught that manipulate the DOM for notification
+ manipulate_dom(notification_ids);
+ $
+ }
+ });
+ });
+
+ // send a POST request to the view method mark_notificartions_as_read_api
+ // with a list of notification_ids
+ var read_notifications = function(uid, notification_ids, callback) {
+ console.log(notification_ids);
+ $.post('/javascript/' + uid + '/mark_notifications_as_read_api/', {
+ 'notification_ids[]': [notification_ids]
+ }, function (data) {
+ if (data.Error) {
+ callback(data.Error, null);
+ } else {
+ callback(null, data);
+ }
+ });
+ };
+
+ // grey out notification row and remove the tick mark for notification row
+ var manipulate_dom = function(notification_ids){
+ $.each($(notification_ids), function(index, element){
+ $('.remove_read'+element).remove();
+ $('.notification_event'+element).addClass('notification_read');
+ });
+ };
+
+ // helper function for tooltip, used to display text when cursors hovers on
+ // the read tick marks.
+ $('[data-toggle="tooltip"]').tooltip();
+});
\ No newline at end of file
diff --git a/graphs/templates/base.html b/graphs/templates/base.html
index a022f40d..ec7488be 100644
--- a/graphs/templates/base.html
+++ b/graphs/templates/base.html
@@ -109,7 +109,10 @@
+
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/graphs/urls.py b/graphs/urls.py
index 7aee8be0..7ef4021a 100644
--- a/graphs/urls.py
+++ b/graphs/urls.py
@@ -22,6 +22,9 @@
url(r'^graphs/public/$', views.public_graphs, name='public_graphs'),
url(r'^graphs/upload/$', views.upload_graph_through_ui, name='upload_graph_through_ui'),
+ # notifications page
+ url(r'^(?P\b[A-Z0-9a-z._%+-]+@[A-Z0-9a-z.-]+\.[A-Za-z]{2,4}\b)/notifications/$', views.notifications, name='notifications'),
+
# view graph page. This contains regular expression to catch url in the form of the following:
# /graphs/email_address/graph_id/
# regex from http://www.regular-expressions.info/email.html
@@ -77,6 +80,8 @@
url(r'^resetPassword/$', views.resetPassword, name='resetPassword'),
url(r'^launchTask/$', views.launchTask, name='launchTask'),
url(r'^retrieveTaskCode/$', views.retrieveTaskCode, name='retrieveTaskCode'),
+ url(r'^javascript/(?P\b[A-Z0-9a-z._%+-]+@[A-Z0-9a-z.-]+\.[A-Za-z]{2,4}\b)/mark_notifications_as_read_api/$', views.mark_notifications_as_read_api, name='mark_notifications_as_read_api'),
+
#REST API
@@ -90,6 +95,7 @@
url(r'^api/users/(?P.+)/graph/makeGraphPrivate/(?P.+)/$', views.make_graph_private, name='make_graph_private'),
url(r'^api/users/(?P.+)/graphs/$', views.view_all_graphs_for_user, name='view_all_graphs_for_user'),
+
# Group REST API endpoints
url(r'^api/groups/get/(?P.+)/(?P.+)/$', views.get_group, name='get_group'),
url(r'^api/groups/get/$', views.get_groups, name='get_groups'),
diff --git a/graphs/util/db.py b/graphs/util/db.py
index 1d11ab9c..264d7e2d 100644
--- a/graphs/util/db.py
+++ b/graphs/util/db.py
@@ -3637,7 +3637,11 @@ def share_graph_with_group(owner, graph, groupId, groupOwner):
# If they're an owner or a group member, they can add graph to the group
if group_owner != None or group_member != None:
new_shared_graph = models.GroupToGraph(group_id = groupId, group_owner = groupOwner, user_id = owner, graph_id = graph, modified = graph_exists.modified)
-
+ members = get_group_members(groupOwner, groupId)
+ for member in members:
+ # TODO: use the current db session instead of creating a new db
+ # session
+ add_share_graph_event(graph, owner, groupId, member.user_id)
db_session.add(new_shared_graph)
db_session.commit()
else:
@@ -4731,3 +4735,144 @@ def constructResponse(statusCode, message, error):
response['Error'] = error
return response
+
+
+def add_share_graph_event(graph_id, owner_id, group_id, member_id):
+ '''
+ Add a new share graph event to the table.
+ After sharing the graph with a group this function will create
+ a share graph event for all the users in that group
+
+ @param graph_id: id of the graph shared
+ @param owner_id: owner of the graph which is shared
+ @param group_id: id of the grop
+ @param member_id: id of the member the graph is shared
+ '''
+ # Create database connection
+ db_session = data_connection.new_session()
+
+ # Get the current time
+ cur_time = datetime.now()
+
+ new_event = models.ShareGraphEvent(graph_id=graph_id, owner_id=owner_id, group_id=group_id, member_id=member_id, share_time=cur_time, is_active=True)
+ db_session.add(new_event)
+ db_session.commit()
+ db_session.close()
+
+
+# admin function
+def update_share_graph_event(event_id, active, member_id):
+ '''
+ Update the share graph event. Change its active state.
+ If active is True then the notification is not read/clicked.
+
+ @param event_id: id of the share graph event
+ @param active: Boolean value, update the state of event
+ @param member_id: id of the user, the logged in user.
+ '''
+ db_session = data_connection.new_session()
+ event = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.id == event_id).filter(models.ShareGraphEvent.member_id == member_id).one()
+ event.is_active = active
+ db_session.commit()
+ db_session.close()
+
+
+# admin function
+def delete_share_graph_event(event_id, member_id):
+ '''
+ Delete the share graph event from the table for the member
+
+ :param event_id: id of the share graph event
+ :param member_id: id of the member
+
+ '''
+ db_session = data_connection.new_session()
+ event = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.id == event_id).filter(models.ShareGraphEvent.member_id == member_id).one()
+ db_session.delete(event)
+ db_session.commit()
+ db_session.close()
+
+
+def get_share_graph_events_by_member_id(member_id, all_notifications=1):
+ """
+ Return a list of share graph events for a user for a given userid of a member of any group and given status of the notification.
+
+ If no results are found the method will raise NoResultFound exception.
+
+ :param member_id: id of the user
+ :param is_active: 1 if we want only the list of unread share graph events else 0 to get all share graph events
+ :return: List of share graph events
+
+ """
+ # Create database connection
+ db_session = data_connection.new_session()
+ if all_notifications == 1:
+ events = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.member_id == member_id).all()
+ else:
+ events = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.member_id == member_id).filter(models.ShareGraphEvent.is_active == 1).all()
+ db_session.close()
+ return events
+
+
+def get_share_graph_event_by_id(event_id, member_id):
+ '''
+ Query database to find share graph event by event_id
+
+ :param event_id: id of the event
+ :param member_id: id of the logged in user
+
+ :return: event with id event_id
+ '''
+ db_session = data_connection.new_session()
+ event = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.id == event_id).filter(models.ShareGraphEvent.member_id == member_id).one()
+ db_session.close()
+ return event
+
+
+def get_all_share_graph_event():
+ '''
+ Query database to find all the share graph events
+ and return a list of events
+
+ :return: list of events
+ '''
+ db_session = data_connection.new_session()
+ events = db_session.query(models.ShareGraphEvent).all()
+ db_session.close()
+ return events
+
+
+def set_share_graph_events_inactive(event_ids, member_id):
+ '''
+ Set all events in the list event_ids as inactive
+
+ :param events_id: list of event ids
+ :param member_id: id of the logged in user
+ '''
+ db_session = data_connection.new_session()
+ for event_id in event_ids:
+ db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.member_id == member_id).filter(models.ShareGraphEvent.id == event_id).update({"is_active": 0})
+ db_session.commit()
+ db_session.close()
+
+
+def get_share_graph_event_by_member_id_and_group_id(member_id, group_id, all_notifications=1):
+ '''
+ Query database to find share graph events with a specific member_id
+ ,group_id and all_notifications and return a list. If all_notifications
+ is `1` then all notifications are returned irrespective of `is_active`
+ field (i.e. read or unread), else (all_notifications=0) only unread
+ events are returned.
+
+ :param member_id: id of the logged in user
+ :param group_id: id of thr group
+ :param all_notifications: boolean value to display all notifications
+ :return: list of events
+ '''
+ db_session = data_connection.new_session()
+ if all_notifications == 1:
+ events = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.member_id == member_id).filter(models.ShareGraphEvent.group_id == group_id).all()
+ else:
+ events = db_session.query(models.ShareGraphEvent).filter(models.ShareGraphEvent.member_id == member_id).filter(models.ShareGraphEvent.group_id == group_id).filter(models.ShareGraphEvent.is_active == 1).all()
+ db_session.close()
+ return events
diff --git a/graphs/util/db_test.py b/graphs/util/db_test.py
new file mode 100644
index 00000000..1f0d0c03
--- /dev/null
+++ b/graphs/util/db_test.py
@@ -0,0 +1,12 @@
+from graphs.models import share_graph_event
+from django.test import TestCase
+from db import (get_all_share_graph_event, get_share_graph_event_by_id,
+ get_share_graph_events_by_member_id, delete_share_graph_event,
+ update_share_graph_event, add_share_graph_event)
+
+
+class share_graph_event_test(TestCase):
+ def basic_test(self):
+ share_event = add_share_graph_event()
+ # add tests after adding logic
+
diff --git a/graphs/views.py b/graphs/views.py
index 01ff87ea..21097e49 100644
--- a/graphs/views.py
+++ b/graphs/views.py
@@ -1,3 +1,5 @@
+from sqlalchemy.orm.exc import NoResultFound
+
from django.shortcuts import render, redirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponse, HttpResponseRedirect, Http404
@@ -23,112 +25,118 @@
URL_PATH = settings.URL_PATH
+
##### VIEWS #####
def image(request):
- name = request.GET.get('name', '')
+ name = request.GET.get('name', '')
+
+ if len(name) > 0:
+ return HttpResponseRedirect(URL_PATH + 'static/images/' + name + '.png')
+ else:
+ return HttpResponse(json.dumps(db.throwError(404, "Image not found!")), content_type="application/json")
- if len(name) > 0:
- return HttpResponseRedirect(URL_PATH + 'static/images/' + name + '.png')
- else:
- return HttpResponse(json.dumps(db.throwError(404, "Image not found!")), content_type="application/json")
def saveFeedback(request):
+ if request.POST:
+ feedback = request.POST["feedback"]
+ graph_id = request.POST["graph_id"]
+ user_id = request.POST["user_id"]
+ layout_owner = request.POST["layout_owner"]
+ layout_name = request.POST["layout_name"]
- if request.POST:
- feedback = request.POST["feedback"]
- graph_id = request.POST["graph_id"]
- user_id = request.POST["user_id"]
- layout_owner = request.POST["layout_owner"]
- layout_name = request.POST["layout_name"]
+ error = db.saveFeedback(feedback, graph_id, user_id, layout_owner, layout_name)
- error = db.saveFeedback(feedback, graph_id, user_id, layout_owner, layout_name)
+ if error != None:
+ return HttpResponse(json.dumps(db.throwError(500, error)), content_type="application/json")
+ else:
+ return HttpResponse(json.dumps(db.sendMessage(200, "Feedback saved!")), content_type="application/json")
- if error != None:
- return HttpResponse(json.dumps(db.throwError(500, error)), content_type="application/json")
- else:
- return HttpResponse(json.dumps(db.sendMessage(200, "Feedback saved!")), content_type="application/json")
def getFeedback(request):
+ if request.POST:
+ graph_id = request.POST["graph_id"]
+ user_id = request.POST["user_id"]
+ layout_owner = request.POST["layout_owner"]
+ layout_name = request.POST["layout_name"]
- if request.POST:
- graph_id = request.POST["graph_id"]
- user_id = request.POST["user_id"]
- layout_owner = request.POST["layout_owner"]
- layout_name = request.POST["layout_name"]
+ results = db.getFeedback(graph_id, user_id, layout_owner, layout_name)
- results = db.getFeedback(graph_id, user_id, layout_owner, layout_name)
+ if len(results) > 0:
+ return HttpResponse(json.dumps(db.sendMessage(200, results)), content_type="application/json")
+ else:
+ return HttpResponse(json.dumps(db.throwError(500, "No feedback entered for this task!")),
+ content_type="application/json")
- if len(results) > 0:
- return HttpResponse(json.dumps(db.sendMessage(200, results)), content_type="application/json")
- else:
- return HttpResponse(json.dumps(db.throwError(500, "No feedback entered for this task!")), content_type="application/json")
def index(request):
- '''
+ '''
Render the main page
:param request: HTTP GET Request
'''
- # If there is a POST request made to the main page (graphspace.org/index or graphspace.org/),
- # that means that the user is trying to log on to GraphSpace.
- # If they try to log on, we first check to see if their password needs to be reset (for whatever reason).
- # The password_reset table contains all the users whose passwords need to be updated.
- # Once the user has updated their password, their name is removed from the password_reset table
+ # If there is a POST request made to the main page (graphspace.org/index or graphspace.org/),
+ # that means that the user is trying to log on to GraphSpace.
+ # If they try to log on, we first check to see if their password needs to be reset (for whatever reason).
+ # The password_reset table contains all the users whose passwords need to be updated.
+ # Once the user has updated their password, their name is removed from the password_reset table
+
+ if request.method == 'POST' and db.need_to_reset_password(request.POST['user_id']) != None:
+ context = {}
+
+ # Forcibly clearing an existing user session (essentially logging user out)
+ request.session['uid'] = None
+
+ # Email the user the link to reset their password
+ result = db.sendForgotEmail(request.POST['user_id'])
+
+ # Any and all errors are thrown via "Error" key in context. This will
+ # be displayed to the user on the front end through a message.
+ context['Error'] = "Need to reset your password! An email has been sent to " + request.POST[
+ 'user_id'] + ' with instructions to reset your password!'
+ return HttpResponse(json.dumps(db.throwError(400, context['Error'])), content_type="application/json");
+
+ # Action to login the user to GraphSpace
+ context = login(request)
+
+ if context['Error'] == None:
+ return render(request, 'graphs/index.html', context)
+ elif db.need_to_reset_password(request.POST['user_id']) != None:
+ context = {}
+ context['Error'] = "Invalid password. Perhaps you need to reset your password!"
+ # Any and all errors are thrown via "Error" key in context. This will
+ # be displayed to the user on the front end through a message.
+ return HttpResponse(json.dumps(db.throwError(400, context['Error'])), content_type="application/json");
+ else:
+ # If there is a problem, throw error and the reason why there was a problem
+ return HttpResponse(json.dumps(db.throwError(400, context['Error'])), content_type="application/json");
- if request.method == 'POST' and db.need_to_reset_password(request.POST['user_id']) != None:
- context = {}
-
- # Forcibly clearing an existing user session (essentially logging user out)
- request.session['uid'] = None
-
- # Email the user the link to reset their password
- result = db.sendForgotEmail(request.POST['user_id'])
-
- # Any and all errors are thrown via "Error" key in context. This will
- # be displayed to the user on the front end through a message.
- context['Error'] = "Need to reset your password! An email has been sent to " + request.POST['user_id'] + ' with instructions to reset your password!'
- return HttpResponse(json.dumps(db.throwError(400, context['Error'])), content_type="application/json");
-
- # Action to login the user to GraphSpace
- context = login(request)
-
- if context['Error'] == None:
- return render(request, 'graphs/index.html', context)
- elif db.need_to_reset_password(request.POST['user_id']) != None:
- context = {}
- context['Error'] = "Invalid password. Perhaps you need to reset your password!"
- # Any and all errors are thrown via "Error" key in context. This will
- # be displayed to the user on the front end through a message.
- return HttpResponse(json.dumps(db.throwError(400, context['Error'])), content_type="application/json");
- else:
- # If there is a problem, throw error and the reason why there was a problem
- return HttpResponse(json.dumps(db.throwError(400, context['Error'])), content_type="application/json");
def logout(request):
- '''
+ '''
Log the user out and display logout page.
:param request: HTTP GET Request
'''
- # Clears all context
- context = {}
-
- # Deletes the "Uid" key from the session
- # currently being tracked by Django.
- try:
- del request.session['uid']
- except KeyError:
- # TODO: should something be done here?
- pass
+ # Clears all context
+ context = {}
+
+ # Deletes the "Uid" key from the session
+ # currently being tracked by Django.
+ try:
+ del request.session['uid']
+ except KeyError:
+ # TODO: should something be done here?
+ pass
+
+ # redirect to the main page after logout.
+ return HttpResponseRedirect('/index/')
- # redirect to the main page after logout.
- return HttpResponseRedirect('/index/')
def download(request):
- '''
+ '''
Download the graph as an image.
Used for when user requests to download PNG of graph.
@@ -136,47 +144,51 @@ def download(request):
'''
- # Only respond if it is a POST request.
- # It will contain the image to be downloaded by the user
- if request.POST:
- if request.POST['image']:
- response = HttpResponse(request.POST['image'], content_type='application/octet-stream')
- response['Content-Disposition'] = 'attachment; filename="foo.png"'
- return response
+ # Only respond if it is a POST request.
+ # It will contain the image to be downloaded by the user
+ if request.POST:
+ if request.POST['image']:
+ response = HttpResponse(request.POST['image'], content_type='application/octet-stream')
+ response['Content-Disposition'] = 'attachment; filename="foo.png"'
+ return response
+
+ else:
+ # redirect to the main page
+ return HttpResponseRedirect('/index/')
- else:
- # redirect to the main page
- return HttpResponseRedirect('/index/')
def graphs(request):
- '''
+ '''
Render the My Graphs page
:param request: HTTP GET Request
'''
- return _graphs_page(request, 'my graphs')
-
+ return _graphs_page(request, 'my graphs')
+
+
def shared_graphs(request):
- '''
+ '''
Render the graphs/shared/ page showing all graphs that are shared with a user
:param request: HTTP GET Request
'''
-
- return _graphs_page(request, 'shared')
+
+ return _graphs_page(request, 'shared')
+
def public_graphs(request):
- '''
+ '''
Render the graphs/public/ page showing all graphs that are public
:param request: HTTP GET Request
'''
- return _graphs_page(request, 'public')
+ return _graphs_page(request, 'public')
+
def _graphs_page(request, view_type):
- '''
+ '''
wrapper view for the following pages:
graphs/
graphs/shared/
@@ -185,203 +197,392 @@ def _graphs_page(request, view_type):
:param request: HTTP GET Request
:param view_type: Type of view for graph (Ex: my graphs, shared, public)
'''
- # context of the view to be passed in for rendering
- context = {}
-
- # List of graphs that will be returned by the request
- graph_list = None
-
- # handle login
- context = login(request)
-
- # Send view_type to front end to tell the user (through button color) where they are
- # The view_type refers to which category of graphs are being viewed (public, shared, my graphs)
- context['view_type'] = view_type
-
- # If there is an error, display the error
- if context['Error']:
- return render(request, 'graphs/error.html', context)
+ # context of the view to be passed in for rendering
+ context = {}
+
+ # List of graphs that will be returned by the request
+ graph_list = None
- # Checks to see if a user is currently logged on
- uid = request.session['uid']
+ # handle login
+ context = login(request)
- # Placeholder to keep track of
- # whether we are partially searching or
- # exact searching
- search_type = None
+ # Send view_type to front end to tell the user (through button color) where they are
+ # The view_type refers to which category of graphs are being viewed (public, shared, my graphs)
+ context['view_type'] = view_type
- # Partial search may be thought of as "contains" matching
- # Exact search may be though of as "identical" matching
- if 'partial_search' in request.GET:
- search_type = 'partial_search'
- elif 'full_search' in request.GET:
- search_type = 'full_search'
+ # If there is an error, display the error
+ if context['Error']:
+ return render(request, 'graphs/error.html', context)
- # Set all information abouut graphs to the front-end
- # Information of graphs consists of all data for an individual graph
- # as well as any search queries and tag queries being performed
- context = db.get_graphs_for_view_type(context, view_type, uid, request)
+ # Checks to see if a user is currently logged on
+ uid = request.session['uid']
- # Holds the amount of times a tag appears for a graph
- all_tags = {}
+ # Placeholder to keep track of
+ # whether we are partially searching or
+ # exact searching
+ search_type = None
- # Goes through all the graphs that are currently on a page
- if context['graph_list'] != None:
- pager_context = pager(request, context['graph_list'])
- if type(pager_context) is dict:
- context.update(pager_context)
- for i in xrange(len(context['current_page'].object_list)):
- graph = list(context['current_page'][i])
- # Get all the tags associated with current graphs and populate the
- # tags accordion
- graph_tags = []
+ # Partial search may be thought of as "contains" matching
+ # Exact search may be though of as "identical" matching
+ if 'partial_search' in request.GET:
+ search_type = 'partial_search'
+ elif 'full_search' in request.GET:
+ search_type = 'full_search'
+
+ # Set all information abouut graphs to the front-end
+ # Information of graphs consists of all data for an individual graph
+ # as well as any search queries and tag queries being performed
+ context = db.get_graphs_for_view_type(context, view_type, uid, request)
+
+ # Holds the amount of times a tag appears for a graph
+ all_tags = {}
+
+ # Goes through all the graphs that are currently on a page
+ if context['graph_list'] != None:
+ pager_context = pager(request, context['graph_list'])
+ if type(pager_context) is dict:
+ context.update(pager_context)
+ for i in xrange(len(context['current_page'].object_list)):
+ graph = list(context['current_page'][i])
+ # Get all the tags associated with current graphs and populate the
+ # tags accordion
+ graph_tags = []
+
+ if request.GET.get(search_type):
+ user_id = graph[5]
+ graph_id = graph[0]
+ graph_tags = db.get_all_tags_for_graph(graph_id, user_id)
+ graph[1] = graph_tags
+ graph.append(db.get_visibility_of_graph(user_id, graph_id))
+ else:
+ user_id = graph[2]
+ graph_id = graph[0]
+ graph_tags = db.get_all_tags_for_graph(graph_id, user_id)
+ graph.insert(1, graph_tags)
+ graph.append(db.get_visibility_of_graph(user_id, graph_id))
+
+ context['current_page'].object_list[i] = graph
+
+ # reset the search form
+ context['search_form'] = SearchForm(placeholder='Search...')
+
+ # Checks to see if there are any tags that the user wants to search for
+ request_tags = request.GET.get('tags') or request.GET.get('tag') or None
+
+ # If there are no graphs returned by the query, then display message on
+ # how to add graphs
+ if len(context['graph_list']) == 0:
+ context = constructGraphMessage(context, view_type, request.GET.get(search_type), request_tags)
+
+ recent_graphs = context['graph_list']
+
+ recent_graphs.sort(key=lambda r: r[2], reverse=True)
+
+ if len(recent_graphs) > 250:
+ recent_graphs = recent_graphs[:250]
+
+ graph_tags = []
+
+ for graph in recent_graphs:
+
+ if request.GET.get(search_type):
+ graph_tags = db.get_all_tags_for_graph(graph[0], graph[5])
+ else:
+ graph_tags = db.get_all_tags_for_graph(graph[0], graph[2])
+
+ for tag in graph_tags:
+ if len(tag) > 0:
+ if tag in all_tags:
+ all_tags[tag] += 1
+ else:
+ all_tags[tag] = 1
+
+ sorted_tags = sorted(all_tags.items(), key=operator.itemgetter(1), reverse=True)[:10]
+
+ all_tags_refined = [i[0] for i in sorted_tags]
+
+ # Populates tags search bar with most used tags of last 250 graphs
+ context['all_tags'] = all_tags_refined # list(set(all_tags))[:10]
+
+ # indicator to include css/js footer for side menu support etc.
+ context['footer'] = True
+
+ return render(request, 'graphs/graphs.html', context)
+
+
+# Controller method for notifications.
+def get_notifications_for_user(user_id, group_id=None, all_notifications=1):
+ '''
+ Controller method to get notifications for a user.
+ This method returns a list of events of a user according to the parameters
+ provided. If a group_id is present then only the notifications for the specific
+ group are returned. If all_notifications is `1` then all notifications irrespective
+ of there `is_active` i.e. read field are returned otherwise (all_notifications=0)
+ only unread notifications are returned.
+
+ :param user_id: user id of logged in user
+ :param group_id: id of requested group (default=None)
+ :param all_notifications: value of query string parameter (default=1)
+ :return: list of events
+ '''
+
+ try:
+ if group_id == None:
+ events = db.get_share_graph_events_by_member_id(user_id, all_notifications)
+ else:
+ events = db.get_share_graph_event_by_member_id_and_group_id(user_id, group_id, all_notifications)
+ except NoResultFound:
+ events = list()
+ except:
+ raise Exception('Database Error: Error while fetching notifications')
+
+ return events
+
+
+def get_notifications_group_stats_for_user(user_id):
+ '''
+ Controller method to get statistics for groups and notifications.
+ These stats are displayed in the left column of the notificaitons.html
+ page. We calculate the number of all notifications,
+ active notifications (i.e. Unread) and groups the user is a part of.
+
+ :param user_id:
+ :return: a three tuple of number of active notifications, all notificaitons
+ and groups of user.
+ '''
+ try:
+ events = db.get_share_graph_events_by_member_id(user_id, all_notifications=0)
+ all_events = db.get_share_graph_events_by_member_id(user_id, all_notifications=1)
+ groups_of_user = [group['groupId'] for group in db.groups_for_user(user_id)]
+
+ except:
+ raise Exception('Database Error: Error while fetching notifications')
+
+ return len(events), len(all_events), groups_of_user
+
+
+# View method for notifications
+def notifications(request, uid):
+ '''
+ View method to display notifications for a user.
+ Use uid to check if user is currently logged in the session
+ and query string parameters `group_id` and `all` to display
+ group specific notifications and all notifications for a user
+
+ :param request:
+ :param uid: user_id of the user
+
+ :return: Render HTML page for notifications or HTML page for
+ errors if any errors occurs.
+ '''
+
+ # handle login
+ context = login(request)
+ # Checks to see if a user is currently logged in
+ uid = request.session['uid']
+ # Query string parameter for group_id
+ group_id = request.GET.get('group_id')
+ if request.GET.get('all'):
+ all_notifications = int(request.GET.get('all'))
+ else:
+ all_notifications = 0
+
+ if uid is None:
+ context['Error'] = "Please log in to view notifications."
+ return render(request, 'graphs/error.html', context)
+
+ try:
+ notifications = get_notifications_for_user(uid, group_id, all_notifications=all_notifications)
+
+ # Create a grouped notification dictonary which is keyed by the group_id
+ # and has all the notifications for that group as a list in the values field.
+ context['grouped_notifications'] = dict()
+ for notification in notifications:
+ if notification.group_id in context['grouped_notifications']:
+ context['grouped_notifications'][notification.group_id].append(notification)
+ else:
+ context['grouped_notifications'][notification.group_id] = [notification]
+
+ context['group'] = group_id
+
+ # Create context for left column stats which display Unread notifications, all
+ # notifications and groups with number of notifications.
+ notification_stats = get_notifications_group_stats_for_user(uid)
+
+ # Create a dictionary to store the groups and the number
+ # of notifications for each group.
+ # Instead of using grouped_notifications we need to create a new variable
+ # because grouped_notification will only have the notifications for a
+ # particular group when we click on notifications of a group, and we'll lose
+ # all the information about other groups. Hence only the particular group will
+ # be dispalyed in the left column when we iterate through the grouped_notifcation
+ # variable.
+ group_count = {}
+ for group in notification_stats[2]:
+ if group in context['grouped_notifications']:
+ group_count[group] = len(context['grouped_notifications'][group])
+ else:
+ group_count[group] = 0
+
+ context['num_active_notifications'], context['num_notifications'] = notification_stats[0], notification_stats[1]
+ context['groups_of_user'] = group_count
+
+ return render(request, 'graphs/notifications.html', context)
+
+ except Exception, e:
+ # Better way of handling errors? As we don't have any GraphSpace
+ # specific excpections I am just using the generic SQLalchemy ORM
+ # generic exception NoResultFound.
+ context['Error'] = str(e)
+ return render(request, 'graphs/error.html', context)
+
+
+def mark_notifications_as_read_api(request, uid):
+ '''
+ view method to mark notifications as read.
+ It marks notifications as read and returns a HTTP response
+ depending on actions.
+ 401 - if the user is not logged in
+ 200 - Success
+ 500 - If any database error or any other exception is recorded
+
+ :param request: request session variable
+ :param uid: user_id of the logged in user
+ :return: HttpResponse
+ '''
+ if request.method == 'POST':
+ notification_ids = request.POST.get('notification_ids[]') if 'notification_ids[]' in request.POST else []
+ if uid == None:
+ return HttpResponse(
+ json.dumps(db.throwError(401, "You are not allowed to update this share event."), indent=4,
+ separators=(',', ': ')), content_type="application/json")
+ else:
+ try:
+ status = mark_notifications_as_read(notification_ids, uid)
+ return HttpResponse(
+ json.dumps(db.sendMessage(200, "Successfully updated share event(s) owned by " + uid + '.'), indent=4,
+ separators=(',', ': ')), content_type="application/json")
+ except Exception, e:
+ # Better way of handling errors? As we don't have any GraphSpace
+ # specific excpections I am just using the generic SQLalchemy ORM
+ # generic exception NoResultFound.
+ return HttpResponse(
+ json.dumps(db.throwError(500, str(e)), indent=4, separators=(',', ': ')),
+ content_type="application/json")
+
+
+def mark_notifications_as_read(notification_ids, uid):
+ '''
+ Controller method to mark a list of notifications
+ as read. Interacts with the database layer and updates
+ the is_active field for the list of share graph events
+
+ It raises an error if any exception is raised by the
+ database layer.
+
+ :param notification_ids: list of notification ids
+ :param uid: user_id of the logged in user
+ :return: 1 if successful
+ '''
+ try:
+ db.set_share_graph_events_inactive(notification_ids, uid)
+ return 1
+ except:
+ raise Exception('Database Error: Error setting notifications as read.')
- if request.GET.get(search_type):
- user_id = graph[5]
- graph_id = graph[0]
- graph_tags = db.get_all_tags_for_graph(graph_id, user_id)
- graph[1] = graph_tags
- graph.append(db.get_visibility_of_graph(user_id, graph_id))
- else:
- user_id = graph[2]
- graph_id = graph[0]
- graph_tags = db.get_all_tags_for_graph(graph_id, user_id)
- graph.insert(1, graph_tags)
- graph.append(db.get_visibility_of_graph(user_id, graph_id))
- context['current_page'].object_list[i] = graph
-
- # reset the search form
- context['search_form'] = SearchForm(placeholder='Search...')
-
- # Checks to see if there are any tags that the user wants to search for
- request_tags = request.GET.get('tags') or request.GET.get('tag') or None
-
- # If there are no graphs returned by the query, then display message on
- # how to add graphs
- if len(context['graph_list']) == 0:
- context = constructGraphMessage(context, view_type, request.GET.get(search_type), request_tags)
-
- recent_graphs = context['graph_list']
-
- recent_graphs.sort(key=lambda r: r[2], reverse=True)
-
- if len(recent_graphs) > 250:
- recent_graphs = recent_graphs[:250]
-
- graph_tags = []
+def upload_graph_through_ui(request):
+ if request.method == 'POST':
+ login_form = LoginForm()
+ register_form = RegisterForm()
- for graph in recent_graphs:
+ upload_json = True
- if request.GET.get(search_type):
- graph_tags = db.get_all_tags_for_graph(graph[0], graph[5])
- else:
- graph_tags = db.get_all_tags_for_graph(graph[0], graph[2])
+ title_of_graph = None
- for tag in graph_tags:
- if len(tag) > 0:
- if tag in all_tags:
- all_tags[tag] += 1
- else:
- all_tags[tag] = 1
+ if 'title' in request.POST:
+ title_of_graph = request.POST['title']
- sorted_tags = sorted(all_tags.items(), key=operator.itemgetter(1), reverse = True)[:10]
+ if str(request.FILES['graphname'])[-4:] != "json":
+ upload_json = None
- all_tags_refined = [i[0] for i in sorted_tags]
+ if request.POST['email'] == 'Public User':
+ # assign random id generator
+ if upload_json:
+ result = db.uploadJSONFile(None, request.FILES['graphname'].read(), title_of_graph)
+ else:
+ result = db.uploadCyjsFile(None, request.FILES['graphname'].read(), title_of_graph)
- # Populates tags search bar with most used tags of last 250 graphs
- context['all_tags'] = all_tags_refined #list(set(all_tags))[:10]
+ if 'Error' not in result:
+ context = {'login_form': login_form, 'register_form': register_form, 'Success': result['Success']}
+ else:
+ context = {'login_form': login_form, 'register_form': register_form, 'Error': result['Error']}
+ return render(request, 'graphs/upload_graph.html', context)
+ else:
- # indicator to include css/js footer for side menu support etc.
- context['footer'] = True
+ if upload_json:
+ result = db.uploadJSONFile(request.POST['email'], request.FILES['graphname'].read(), title_of_graph)
+ else:
+ result = db.uploadCyjsFile(request.POST['email'], request.FILES['graphname'].read(), title_of_graph)
- return render(request, 'graphs/graphs.html', context)
+ if 'Error' not in result:
+ context = {'login_form': login_form, 'uid': request.POST['email'], 'register_form': register_form,
+ 'Success': result['Success']}
+ else:
+ context = {'login_form': login_form, 'uid': request.POST['email'], 'register_form': register_form,
+ 'Error': result['Error']}
-def upload_graph_through_ui(request):
+ return render(request, 'graphs/upload_graph.html', context)
+ else:
+ context = login(request)
+ return render(request, 'graphs/upload_graph.html', context)
- if request.method == 'POST':
- login_form = LoginForm()
- register_form = RegisterForm()
-
- upload_json = True
-
- title_of_graph = None
-
- if 'title' in request.POST:
- title_of_graph = request.POST['title']
-
- if str(request.FILES['graphname'])[-4:] != "json":
- upload_json = None
-
- if request.POST['email'] == 'Public User':
- # assign random id generator
- if upload_json:
- result = db.uploadJSONFile(None, request.FILES['graphname'].read(), title_of_graph)
- else:
- result = db.uploadCyjsFile(None, request.FILES['graphname'].read(), title_of_graph)
-
- if 'Error' not in result:
- context = {'login_form': login_form, 'register_form': register_form, 'Success': result['Success']}
- else:
- context = {'login_form': login_form, 'register_form': register_form, 'Error': result['Error']}
- return render(request, 'graphs/upload_graph.html', context)
- else:
-
- if upload_json:
- result = db.uploadJSONFile(request.POST['email'], request.FILES['graphname'].read(), title_of_graph)
- else:
- result = db.uploadCyjsFile(request.POST['email'], request.FILES['graphname'].read(), title_of_graph)
-
- if 'Error' not in result:
- context = {'login_form': login_form, 'uid': request.POST['email'], 'register_form': register_form, 'Success': result['Success']}
- else:
- context = {'login_form': login_form, 'uid': request.POST['email'], 'register_form': register_form, 'Error': result['Error']}
-
- return render(request, 'graphs/upload_graph.html', context)
- else:
- context = login(request)
- return render(request, 'graphs/upload_graph.html', context)
def save_layout(request, uid, gid):
- '''
+ '''
Saves a layout for a graph.
:param HTTP POST Request
'''
- graph_owner = uid
- if request.POST:
- if uid == None:
- return HttpResponse(json.dumps(db.throwError(500, "Must be signed in to save a layout!")), content_type="application/json")
+ graph_owner = uid
+ if request.POST:
+ if uid == None:
+ return HttpResponse(json.dumps(db.throwError(500, "Must be signed in to save a layout!")),
+ content_type="application/json")
- result = db.save_layout(gid, graph_owner, request.POST['layout_name'], request.POST['loggedIn'], request.POST['points'], request.POST['public'], request.POST['unlisted'])
- if result == None:
- return HttpResponse(json.dumps(db.sendMessage(200, "Layout saved!")), content_type="application/json")
-
- return HttpResponse(json.dumps(db.throwError(400, result)), content_type="application/json")
+ result = db.save_layout(gid, graph_owner, request.POST['layout_name'], request.POST['loggedIn'],
+ request.POST['points'], request.POST['public'], request.POST['unlisted'])
+ if result == None:
+ return HttpResponse(json.dumps(db.sendMessage(200, "Layout saved!")), content_type="application/json")
+
+ return HttpResponse(json.dumps(db.throwError(400, result)), content_type="application/json")
+
+ else:
+ context = {"Error": "This route only accepts POST requests."}
+ return render(request, 'graphs/error.html', context)
- else:
- context = {"Error": "This route only accepts POST requests."}
- return render(request, 'graphs/error.html', context)
def update_layout(request, uid, gid):
- '''
+ '''
Updates a layout for a graph.
:param HTTP POST Request
'''
- if gid[len(gid) - 1] == '/':
- gid = gid[:len(gid) - 1]
+ if gid[len(gid) - 1] == '/':
+ gid = gid[:len(gid) - 1]
+
+ error = db.update_layout(gid, uid, request.POST['layout_name'], request.POST['loggedIn'], request.POST['points'],
+ request.POST['public'], request.POST['unlisted'], request.POST['originalLayout'])
+ if error == None:
+ return HttpResponse(json.dumps(db.sendMessage(200, "Layout updated!")), content_type="application/json")
+
+ return HttpResponse(json.dumps(db.throwError(400, error)), content_type="application/json");
- error = db.update_layout(gid, uid, request.POST['layout_name'], request.POST['loggedIn'], request.POST['points'], request.POST['public'], request.POST['unlisted'], request.POST['originalLayout'])
- if error == None:
- return HttpResponse(json.dumps(db.sendMessage(200, "Layout updated!")), content_type="application/json")
-
- return HttpResponse(json.dumps(db.throwError(400, error)), content_type="application/json");
def design_graph(request, uid, gid):
- '''
+ '''
View a graph with CytoscapeJS along with tool pallete
to help researcher layout of a graph.
@@ -390,183 +591,189 @@ def design_graph(request, uid, gid):
:param gid: Graph id of the graph to view
'''
- # Context contains all the elements we want to render on the web
- # page. We fill in the various elements of context before calling
- # the render() function.
- #handle login
- # context = login(request)
- context = {
- "uid": request.session['uid'],
- "Error": None
- }
-
- if gid[len(gid) - 1] == '/':
- gid = gid[:len(gid) - 1]
-
- #TODO: Create trigger to delete older tasks (3 days)
-
- # if the graph is public, or if a user is a member
- # of the group where this graph is shared
- # or if he owns this graph, then allow him to view it
- # otherwise do not allow it
- if db.is_public_graph(uid, gid) or 'Public_User_' in uid:
- graph_to_view = db.get_all_info_for_graph(uid, gid)
- elif request.session['uid'] == None:
- context['Error'] = "You are not authorized to view this graph, create an account and contact graph's owner for permission to see this graph."
- return render(request, 'graphs/error.html', context)
- else:
- # If the user is member of group where this graph is shared
- user_is_member = db.can_see_shared_graph(context['uid'], uid, gid)
-
- # if user is owner of graph or a member of group that shares graph
- if request.session['uid'] == uid or user_is_member == True:
- graph_info = db.getGraphInfo(uid, gid)
- if graph_info != None:
- graph_to_view = graph_info
- else:
- context['Error'] = "Graph: " + gid + " does not exist for " + uid + ". Upload a graph with this name into GraphSpace in order to see it."
- return render(request, 'graphs/error.html', context)
- else:
- context['Error'] = "You are not authorized to view this graph, please contact graph's owner for permission."
- return render(request, 'graphs/error.html', context)
-
- # Get correct layout for the graph to view
- context = db.set_layout_context(request, context, uid, gid)
-
- if context['Error']:
- return render(request, 'graphs/error.html', context)
-
- # Convert JSON for CytoscapeJS, if needed
- context['graph'] = db.retrieve_cytoscape_json(graph_to_view[0])
- context['draw_graph'] = True
-
- # TODO: This will eventually get deleted
- json_data = json.loads(context['graph'])
-
- # id of the owner of this graph
- context['owner'] = uid
-
- # graph id
- context['graph_id'] = gid
-
- # Don't display the task_view
- context["task_view"] = False
- context["approve_view"] = False
- context["researcher_view"] = False
- context["designer_view"] = True
-
- return render(request, 'graphs/view_graph.html', context)
+ # Context contains all the elements we want to render on the web
+ # page. We fill in the various elements of context before calling
+ # the render() function.
+ # handle login
+ # context = login(request)
+ context = {
+ "uid": request.session['uid'],
+ "Error": None
+ }
+
+ if gid[len(gid) - 1] == '/':
+ gid = gid[:len(gid) - 1]
+
+ # TODO: Create trigger to delete older tasks (3 days)
+
+ # if the graph is public, or if a user is a member
+ # of the group where this graph is shared
+ # or if he owns this graph, then allow him to view it
+ # otherwise do not allow it
+ if db.is_public_graph(uid, gid) or 'Public_User_' in uid:
+ graph_to_view = db.get_all_info_for_graph(uid, gid)
+ elif request.session['uid'] == None:
+ context[
+ 'Error'] = "You are not authorized to view this graph, create an account and contact graph's owner for permission to see this graph."
+ return render(request, 'graphs/error.html', context)
+ else:
+ # If the user is member of group where this graph is shared
+ user_is_member = db.can_see_shared_graph(context['uid'], uid, gid)
+
+ # if user is owner of graph or a member of group that shares graph
+ if request.session['uid'] == uid or user_is_member == True:
+ graph_info = db.getGraphInfo(uid, gid)
+ if graph_info != None:
+ graph_to_view = graph_info
+ else:
+ context[
+ 'Error'] = "Graph: " + gid + " does not exist for " + uid + ". Upload a graph with this name into GraphSpace in order to see it."
+ return render(request, 'graphs/error.html', context)
+ else:
+ context['Error'] = "You are not authorized to view this graph, please contact graph's owner for permission."
+ return render(request, 'graphs/error.html', context)
+
+ # Get correct layout for the graph to view
+ context = db.set_layout_context(request, context, uid, gid)
+
+ if context['Error']:
+ return render(request, 'graphs/error.html', context)
+
+ # Convert JSON for CytoscapeJS, if needed
+ context['graph'] = db.retrieve_cytoscape_json(graph_to_view[0])
+ context['draw_graph'] = True
+
+ # TODO: This will eventually get deleted
+ json_data = json.loads(context['graph'])
+
+ # id of the owner of this graph
+ context['owner'] = uid
+
+ # graph id
+ context['graph_id'] = gid
+
+ # Don't display the task_view
+ context["task_view"] = False
+ context["approve_view"] = False
+ context["researcher_view"] = False
+ context["designer_view"] = True
+
+ return render(request, 'graphs/view_graph.html', context)
+
def view_graph(request, uid, gid):
- '''
+ '''
View a graph with CytoscapeJS.
:param request: HTTP GET Request
:param uid: Owner of the graph to view
:param gid: Graph id of the graph to view
'''
- # Context contains all the elements we want to render on the web
- # page. We fill in the various elements of context before calling
- # the render() function.
- #handle login
- context = login(request)
-
- if gid[len(gid) - 1] == '/':
- gid = gid[:len(gid) - 1]
-
- #TODO: Create trigger to delete older tasks (3 days)
-
- # if the graph is public, or if a user is a member
- # of the group where this graph is shared
- # or if he owns this graph, then allow him to view it
- # otherwise do not allow it
- if db.is_public_graph(uid, gid) or 'Public_User_' in uid:
- graph_to_view = db.get_all_info_for_graph(uid, gid)
- elif request.session['uid'] == None:
- context['Error'] = "You are not authorized to view this graph, create an account and contact graph's owner for permission to see this graph."
- return render(request, 'graphs/error.html', context)
- else:
- # If the user is member of group where this graph is shared
- user_is_member = db.can_see_shared_graph(context['uid'], uid, gid)
-
- # if user is owner of graph or a member of group that shares graph
- if request.session['uid'] == uid or user_is_member == True:
- graph_info = db.getGraphInfo(uid, gid)
- if graph_info != None:
- graph_to_view = graph_info
- else:
- context['Error'] = "Graph: " + gid + " does not exist for " + uid + ". Upload a graph with this name into GraphSpace in order to see it."
- return render(request, 'graphs/error.html', context)
- else:
- context['Error'] = "You are not authorized to view this graph, please contact graph's owner for permission."
- return render(request, 'graphs/error.html', context)
-
- # Get correct layout for the graph to view
- context = db.set_layout_context(request, context, uid, gid)
-
- if context['Error']:
- return render(request, 'graphs/error.html', context)
-
- # Convert JSON for CytoscapeJS, if needed
- context['graph'] = db.retrieve_cytoscape_json(graph_to_view[0])
- context['draw_graph'] = True
-
- # Get all the groups that are shared for this graph
- shared_groups = db.get_all_groups_for_this_graph(uid, graph_to_view[2])
-
- format_shared_groups = []
- for shared_group in shared_groups:
- format_shared_groups.append((shared_group.group_id, shared_group.owner_id))
-
- context['shared_groups'] = format_shared_groups
-
- if graph_to_view[1] == 1:
- context['shared'] = 'Publicly Shared'
- else:
- context['shared'] = 'Privately Shared'
-
- # TODO: This will eventually get deleted
- json_data = json.loads(context['graph'])
- #add sidebar information to the context for display
- if 'description' in json_data['metadata']:
- context['description'] = json_data['metadata']['description'] + "