Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a comment/discussion system in GraphSpace #426

Open
wants to merge 96 commits into
base: comment-system
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
c1c914e
Architecture.png
melvin15may Jun 10, 2017
a66e227
Add files via upload
melvin15may Jun 24, 2017
9d209ac
Added screen-casts for group notifications
melvin15may Jul 2, 2017
3aa78a2
Delete add_new_member.webm
melvin15may Jul 2, 2017
b5ea257
Delete group-notification-tab.webm
melvin15may Jul 2, 2017
fc92a9a
Delete individual-notification.webm
melvin15may Jul 2, 2017
3917fef
Delete mark-all-as-read-notification.webm
melvin15may Jul 2, 2017
1c86293
Delete share-unshare-graph.webm
melvin15may Jul 2, 2017
b78cf01
Add files via upload
melvin15may Jul 2, 2017
7125307
Delete share-layout.webm
melvin15may Jul 2, 2017
f1a7c6f
Delete graph_notification.gif
melvin15may Jul 2, 2017
a1dc11d
Delete layout_notification.gif
melvin15may Jul 2, 2017
0a3804e
Delete group_notification.gif
melvin15may Jul 2, 2017
b016885
Delete group_update_notification.gif
melvin15may Jul 2, 2017
e55794d
Delete notification_page.gif
melvin15may Jul 2, 2017
b1afe02
Add files via upload
melvin15may Jul 2, 2017
d07fe12
Add files via upload
melvin15may Jul 3, 2017
01fa25f
Merge remote-tracking branch 'upstream/master'
melvin15may Aug 14, 2017
f655860
started implementing comments system
bruce-wayne99 Jun 8, 2018
8cbc3cc
added models for comments, comment to node and comment to edge relati…
bruce-wayne99 Jun 9, 2018
71608b7
removed comments folder
bruce-wayne99 Jun 10, 2018
0d4493e
added views and controllers for creating comments
bruce-wayne99 Jun 10, 2018
f0b5d51
update dal.py and models.py
bruce-wayne99 Jun 10, 2018
6639114
added errorcode for creating comment
bruce-wayne99 Jun 10, 2018
ab79a78
removed comments related code from graphs
bruce-wayne99 Jun 16, 2018
87dd719
added comment-system application
bruce-wayne99 Jun 16, 2018
b308319
added views and routes to display and upload comments for testing
bruce-wayne99 Jun 16, 2018
bc2eda9
added asgi for sockets
bruce-wayne99 Jun 16, 2018
163e5c2
django channels added for real-time functionality
bruce-wayne99 Jun 16, 2018
c51eae5
added sockets for frontend and signals
bruce-wayne99 Jun 16, 2018
3f41a4a
added functionality to get edgeId/nodeId based on edge/node name
bruce-wayne99 Jun 21, 2018
db394d8
added ajax handlers to create comments by ajax request
bruce-wayne99 Jun 21, 2018
52b5bb0
added panel for adding comments, viewing comments
bruce-wayne99 Jul 13, 2018
fd0df77
update signals to handle delete,add,edit comments
bruce-wayne99 Jul 13, 2018
e6a5c7e
update controllers,dal,models to handle edit,delete,resolve,add,reply…
bruce-wayne99 Jul 13, 2018
903387c
added views to handle crud operations on comments
bruce-wayne99 Jul 13, 2018
e0a1276
added event listeners to comments for CRUD operations
bruce-wayne99 Jul 13, 2018
5b830aa
updated socket handler functions
bruce-wayne99 Jul 13, 2018
f572c90
added functionality for displaying updated time of comment in real-time
bruce-wayne99 Jul 18, 2018
58f2e66
added functionality for showing the graph associated with a comment
bruce-wayne99 Jul 18, 2018
5156c2a
fixed real-time functionality to highlight graph elements on hovering…
bruce-wayne99 Jul 19, 2018
e8f7940
added permissions for CRUD on comments
bruce-wayne99 Jul 19, 2018
3b3714e
added functionality frontend to highlight graph elements
bruce-wayne99 Jul 19, 2018
3574323
fixed bug real-time functionality
bruce-wayne99 Jul 19, 2018
e50f8d8
added implementation for visualizing comments based on selected graph
bruce-wayne99 Jul 20, 2018
58cc0dc
added functionality for comments dropdown
bruce-wayne99 Jul 21, 2018
dcfc9f3
fixed filtering comments to handle resolved comments
bruce-wayne99 Jul 21, 2018
38b59b7
added backend functionality to handle replies to resolved comments
bruce-wayne99 Jul 24, 2018
a500e07
added errorcodes on trying to reply to resolved comments
bruce-wayne99 Jul 24, 2018
7b48c78
added frontend and socket functionality to handle resolved comments
bruce-wayne99 Jul 24, 2018
e45ec93
did code clean up to remove unnecessary id's for elements
bruce-wayne99 Jul 24, 2018
1fba768
added backend implementation for pinning comments
bruce-wayne99 Jul 27, 2018
cd09b6f
added error codes to handle errors for pinning comments
bruce-wayne99 Jul 27, 2018
62d6c9e
Added frontend code for pinning comments and pagination
bruce-wayne99 Jul 27, 2018
d94f4f3
added real-time functionality to handle pin/unpin/resolve/reopen comm…
bruce-wayne99 Jul 27, 2018
dbd89e3
update graph_page.js, graphspace.css base.html for displaying resolve…
bruce-wayne99 Jul 31, 2018
e37760f
update graphspace.css, graph_page.js for pagination
bruce-wayne99 Aug 1, 2018
591b7b0
added real-time functionality for pagination
bruce-wayne99 Aug 1, 2018
ee2d892
added resolved checkbox for comments
bruce-wayne99 Aug 1, 2018
d025f3f
added functionality to allow owner of the graph to delete comments, a…
bruce-wayne99 Aug 8, 2018
d420f3b
Merge pull request #1 from bruce-wayne99/summer-work
bruce-wayne99 Aug 8, 2018
e4d9c1c
added code documentation
bruce-wayne99 Sep 11, 2018
6f14c60
Merge pull request #2 from bruce-wayne99/summer-work
bruce-wayne99 Sep 11, 2018
03f6fd0
removed upload comment method
bruce-wayne99 Mar 9, 2019
18c1650
changed port for redis server
bruce-wayne99 Mar 17, 2019
01f12c9
added docker configuration files
bruce-wayne99 Mar 17, 2019
bb36d69
update requirements.txt
bruce-wayne99 Mar 17, 2019
57fc838
added code for running the server
bruce-wayne99 Mar 17, 2019
75d3396
added changes for displaying ports
bruce-wayne99 Mar 17, 2019
a77a8d7
changed requirements file and fixed bugs in Dockerfile
bruce-wayne99 Mar 17, 2019
dbe0d2d
changed forwared docker ports
bruce-wayne99 Mar 18, 2019
3a42dcf
fixed bug in docker_command file
bruce-wayne99 Mar 18, 2019
bece9ef
Merge branch 'comment-system' of github.com:bruce-wayne99/GraphSpace …
yash170106065 May 7, 2020
1776df3
solve the merge conflicts
yash170106065 May 16, 2020
a75406b
created discussion app
yash170106065 Jun 8, 2020
e722c76
created table for discussions and updated base.py
yash170106065 Jun 8, 2020
ded5cc3
created add and view discussions on bootstrap table functionality (UI…
yash170106065 Jun 10, 2020
d1ad9fe
add urls and functions for discussion page
yash170106065 Jun 10, 2020
a9b0d7a
designed discussion page UI and backend for adding and view comments
yash170106065 Jun 16, 2020
71d1a2a
modified models for unique discussions in a group
yash170106065 Jun 28, 2020
54be9ec
adding functionality views.py for deleting editing discussions
yash170106065 Jun 28, 2020
5682340
functionality for search close and open discussions
yash170106065 Jun 28, 2020
dfa8fab
adding authorization and error codes for discussions and its function…
yash170106065 Jun 28, 2020
25ba9aa
added delete discussions modal
yash170106065 Jun 28, 2020
6edabc2
created a functionality for read-more and solved the bug for timeago …
yash170106065 Jun 28, 2020
559a9d5
designed the discussion page
yash170106065 Jun 28, 2020
f2d9d53
modified the bootstrap discussion table for discussions
yash170106065 Jun 28, 2020
1cf9dcc
updating the stylesheet for discussion page and table
yash170106065 Jun 28, 2020
b911da2
adding event listeners and CRUD functionalities for discussion page a…
yash170106065 Jun 28, 2020
3fcda4b
added real-time websockets functionalities for discussions
yash170106065 Jun 28, 2020
e24e689
modified comments and code
yash170106065 Jun 30, 2020
c9c1fc8
modified id and classes names and case
yash170106065 Jul 2, 2020
b3f07e8
changed the models and improve the functionalities with docstrings
yash170106065 Jul 29, 2020
2abca88
added the reaction system in discussions
yash170106065 Aug 11, 2020
4447c34
updated the comment system and resolve bugs
yash170106065 Aug 25, 2020
9335334
Resolve edit comment bug
yash170106065 Aug 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
3 changes: 3 additions & 0 deletions applications/discussions/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
7 changes: 7 additions & 0 deletions applications/discussions/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from __future__ import unicode_literals

from django.apps import AppConfig


class DiscussionsConfig(AppConfig):
name = 'discussions'
83 changes: 83 additions & 0 deletions applications/discussions/controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from sqlalchemy.exc import IntegrityError
import applications.discussions.dal as db
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
from graphspace.exceptions import ErrorCodes, BadRequest
from graphspace.wrappers import atomic_transaction


@atomic_transaction
def add_discussion(request, message=None, topic=None, group_id=None, is_resolved=0, owner_email=None,
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
parent_discussion_id=None):
# Construct new discussion to add to database
discussion = db.add_discussion(request.db_session, message=message, topic=topic,
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
owner_email=owner_email,
group_id=group_id, is_resolved=is_resolved,
parent_discussion_id=parent_discussion_id)
return discussion


def search_discussions_by_group_id(request, group_id=None, topic=None, limit=20, offset=0, order='desc', sort='created_at'):
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
if group_id is None:
raise Exception("Atleast one group id is required.")

if sort == 'topic':
sort_attr = db.Discussion.topic
else:
sort_attr = db.Discussion.created_at

if order == 'desc':
orber_by = db.asc(sort_attr)
else:
orber_by = db.desc(sort_attr)

total, discussions = db.get_discussions_by_group_id(request.db_session,
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
group_id=group_id,
topic=topic,
limit=limit,
offset=offset,
order_by=orber_by)

return total, discussions



def get_discussion_by_id(request, discussion_id):
return db.get_discussion(request.db_session, id=discussion_id)


def search_comments_by_discussion_id(request, group_id=None, discussion_id=None):
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
if group_id is None:
raise Exception("Atleast one group id is required.")
if discussion_id is None:
raise Exception("Atleast one discussion id is required.")
return db.get_comments_by_discussion_id(request.db_session, group_id=group_id, discussion_id=discussion_id)


def is_user_authorized_to_delete_discussion(request, username, discussion_id):
is_authorized = False

discussion = db.get_discussion(request.db_session, discussion_id)

if discussion is not None:
if discussion.owner_email == username:
is_authorized = True

return is_authorized


def delete_discussion_by_id(request, discussion_id):
db.delete_discussion(request.db_session, id=discussion_id)
return


def update_discussion(request, discussion_id, message, is_resolved):
discussion = {}
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
if message is not None:
discussion['message'] = message
return db.update_discussion(request.db_session, id=discussion_id, updated_discussion=discussion)
if is_resolved is not None:
if is_resolved == u'1':
discussion['is_resolved'] = is_resolved
return db.resolve_discussion(request.db_session, id=discussion_id, updated_discussion=discussion)
if is_resolved == u'0':
discussion['is_resolved'] = is_resolved
return db.reopen_discussion(request.db_session, id=discussion_id, updated_discussion=discussion)
126 changes: 126 additions & 0 deletions applications/discussions/dal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from sqlalchemy import and_, or_, desc, asc, event
from sqlalchemy.orm import joinedload, subqueryload
from graphspace.wrappers import with_session
from applications.discussions.models import *
from applications.users.dal import *
from applications.users.models import *
import graphspace.signals as socket
from graphspace.database import *

@with_session
def add_discussion(db_session, message, topic, group_id, owner_email=None, is_resolved=0, parent_discussion_id=None):
discussion = Discussion(owner_email=owner_email, group_id=group_id,
is_resolved=is_resolved, parent_discussion_id=parent_discussion_id, message=message, topic=topic)
group = get_group(db_session, group_id)
group.group_discussions.append(discussion)
db_session.add(discussion)
return discussion


@with_session
def get_discussions_by_group_id(db_session, group_id, topic, limit, offset, order_by=desc(Discussion.created_at)):

query = db_session.query(Discussion).filter(Discussion.group_id == group_id)
query = query.filter(Discussion.parent_discussion_id == None)
if order_by is not None:
query = query.order_by(order_by)
if topic is not None:
query1 = query.filter(Discussion.topic.ilike(topic))
query2 = query.filter(Discussion.owner_email.ilike(topic))
query3 = query.filter(Discussion.message.ilike(topic))
query4 = query1.union(query2)
query = query3.union(query4)
total = query.count()

if offset is not None and limit is not None:
query = query.limit(limit).offset(offset)

return total, query.all()

@with_session
def get_discussion(db_session, id):
"""
Get discussion by discussion id.
:param db_session: Database session.
:param id: Unique ID of the discussion
:return: Discussion if id exists else None
"""
return db_session.query(Discussion).filter(Discussion.id == id).one_or_none()
def get_comments_by_discussion_id(db_session, group_id, discussion_id):
query = db_session.query(Discussion).filter(Discussion.group_id == group_id)
query = query.filter(Discussion.parent_discussion_id == discussion_id)
return query.count(), query.all()

@with_session
def delete_discussion(db_session, id):
"""
Delete discussion from Discussion table.
:param db_session: Database session.
:param id: Unique ID of the discussion
:return: discussion
"""
discussion = db_session.query(Discussion).filter(Discussion.id == id).one_or_none()
query = db_session.query(Discussion).filter(Discussion.parent_discussion_id == id).all()
db_session.delete(discussion)
for ele in query:
db_session.delete(ele)
return discussion

@with_session
def update_discussion(db_session, id, updated_discussion):
"""
Update discussion row entry.
:param db_session: Database session.
:param id: Unique ID of the discussion
:param updated_discussion: Updated discussion row entry
:return: Discussion if id exists else None
"""
discussion = db_session.query(Discussion).filter(Discussion.id == id).one_or_none()
for (key, value) in updated_discussion.items():
setattr(discussion, key, value)
send_discussion(discussion, event="edited")
return discussion

@with_session
def resolve_discussion(db_session, id, updated_discussion):
"""
Update discussion row entry.
:param db_session: Database session.
:param id: Unique ID of the discussion
:param updated_discussion: Updated discussion row entry
:return: Discussion if id exists else None
"""
discussion = db_session.query(Discussion).filter(Discussion.id == id).one_or_none()
for (key, value) in updated_discussion.items():
setattr(discussion, key, value)
send_discussion(discussion, event="resolve")
return discussion

@with_session
def reopen_discussion(db_session, id, updated_discussion):
"""
Update discussion row entry.
:param db_session: Database session.
:param id: Unique ID of the discussion
:param updated_discussion: Updated discussion row entry
:return: Discussion if id exists else None
"""
discussion = db_session.query(Discussion).filter(Discussion.id == id).one_or_none()
for (key, value) in updated_discussion.items():
setattr(discussion, key, value)
send_discussion(discussion, event="reopen")
return discussion


@event.listens_for(Discussion, 'after_insert')
def update_listener(mapper, connection, discussion):
send_discussion(discussion, event="insert")

@event.listens_for(Discussion, 'after_delete')
def delete_listener(mapper, connection, discussion):
send_discussion(discussion, event="delete")

def send_discussion(discussion, event):
users_list = get_users_by_group(Database().session(), discussion.group_id)
socket.send_discussion(discussion=discussion, type="private", users=users_list, event=event)

Empty file.
51 changes: 51 additions & 0 deletions applications/discussions/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from __future__ import unicode_literals

from applications.users.models import *
from django.conf import settings
from graphspace.mixins import *
import json
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
from sqlalchemy import ForeignKeyConstraint, text, Enum, Boolean
from sqlalchemy import String, ForeignKey, UniqueConstraint
Base = settings.BASE


# ================== Table Definitions =================== #

class Discussion(IDMixin, TimeStampMixin, Base):
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
__tablename__ = 'discussion'

message = Column(String, nullable=False)
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
is_resolved = Column(Integer, nullable=False, default=0)
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
topic = Column(String, nullable=True)

owner_email = Column(String, ForeignKey('user.email', ondelete="CASCADE", onupdate="CASCADE"), nullable=False)
owner = relationship("User", back_populates="owned_discussions", uselist=False)

group_id = Column(Integer, ForeignKey('group.id', ondelete="CASCADE", onupdate="CASCADE"), nullable=False)
group = relationship("Group", back_populates="group_discussions", uselist=False)

parent_discussion_id = Column(Integer, ForeignKey('discussion.id', ondelete="CASCADE", onupdate="CASCADE"),
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
nullable=True)
yash170106065 marked this conversation as resolved.
Show resolved Hide resolved
constraints = (UniqueConstraint('topic', 'group_id', name='_discussion_uc_topic_group_id'),)
indices = ()

@declared_attr
def __table_args__(cls):
args = cls.constraints + cls.indices
return args

def serialize(cls, **kwargs):
return {
'id': cls.id,
'owner_email': cls.owner_email,
'message': cls.message,
'topic': cls.topic,
'is_resolved': cls.is_resolved,
'group_id': cls.group_id,
'parent_discussion_id': cls.parent_discussion_id,
'group_owner_email': cls.group.owner_email,
'created_at': cls.created_at.isoformat(),
'updated_at': cls.updated_at.isoformat()
}


6 changes: 5 additions & 1 deletion applications/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from applications.graphs.models import *
from applications.comments.models import *
from applications.discussions.models import *


Base = settings.BASE
Expand All @@ -31,7 +32,7 @@ class User(IDMixin, TimeStampMixin, Base):
owned_graphs = relationship("Graph", back_populates="owner", cascade="all, delete-orphan")
owned_layouts = relationship("Layout", back_populates="owner", cascade="all, delete-orphan")
owned_comments = relationship("Comment", back_populates="owner", cascade="all, delete-orphan")

owned_discussions = relationship("Discussion", back_populates="owner", cascade="all, delete-orphan")

member_groups = association_proxy('user_groups', 'group')

Expand Down Expand Up @@ -86,6 +87,8 @@ class Group(IDMixin, TimeStampMixin, Base):
invite_code = Column(String, nullable=False)

owner = relationship("User", back_populates="owned_groups", uselist=False)
group_discussions = relationship("Discussion", back_populates="group", cascade="all, delete-orphan")

members = association_proxy('member_users', 'user')
graphs = association_proxy('shared_graphs', 'graph')

Expand All @@ -106,6 +109,7 @@ def serialize(cls, **kwargs):
'description': cls.description,
'total_graphs': len(cls.graphs),
'total_members': len(cls.members),
'group_discussions': len(cls.group_discussions),
'created_at': cls.created_at.isoformat(),
'updated_at': cls.updated_at.isoformat()
}
Expand Down
4 changes: 4 additions & 0 deletions applications/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

url(r'^groups/$', views.groups_page, name='groups'),
url(r'^groups/(?P<group_id>[^/]+)$', views.group_page, name='group'),
url(r'^groups/(?P<group_id>[^/]+)/discussions/(?P<discussion_id>[^/]+)$', views.discussion_page, name='discussion'),

url(r'^groups/(?P<group_id>[^/]+)/join/$', views.join_group_page, name='signup_by_invitation'),

Expand All @@ -22,6 +23,9 @@
# Group Graphs
url(r'^ajax/groups/(?P<group_id>[^/]+)/graphs$', views.group_graphs_ajax_api, name='group_graphs_ajax_api'),
url(r'^ajax/groups/(?P<group_id>[^/]+)/graphs/(?P<graph_id>[^/]+)$', views.group_graphs_ajax_api, name='group_graphs_ajax_api'),
# Group Discussions
url(r'^ajax/groups/(?P<group_id>[^/]+)/discussions$', views.group_discussions_ajax_api, name='group_discussions_ajax_api'),
url(r'^ajax/groups/(?P<group_id>[^/]+)/discussions/(?P<discussion_id>[^/]+)$', views.discussion_comments_ajax_api, name='discussion_comments_ajax_api'),

# REST APIs Endpoints

Expand Down
Loading