moved django_comment_client into edx
This commit is contained in:
@@ -1 +0,0 @@
|
||||
/Users/dementrock/coding/cs_comments_client_python/djangoapp
|
||||
0
lms/djangoapps/django_comment_client/__init__.py
Normal file
0
lms/djangoapps/django_comment_client/__init__.py
Normal file
17
lms/djangoapps/django_comment_client/base/urls.py
Normal file
17
lms/djangoapps/django_comment_client/base/urls.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.conf.urls.defaults import url, patterns
|
||||
import django_comment_client.base.views
|
||||
|
||||
urlpatterns = patterns('django_comment_client.base.views',
|
||||
url(r'(?P<commentable_id>[\w\-]+)/threads/create$', 'create_thread', name='create_thread'),
|
||||
url(r'threads/(?P<thread_id>[\w\-]+)/update$', 'update_thread', name='update_thread'),
|
||||
url(r'threads/(?P<thread_id>[\w\-]+)/reply$', 'create_comment', name='create_comment'),
|
||||
url(r'threads/(?P<thread_id>[\w\-]+)/delete', 'delete_thread', name='delete_thread'),
|
||||
url(r'comments/(?P<comment_id>[\w\-]+)/update$', 'update_comment', name='update_comment'),
|
||||
url(r'comments/(?P<comment_id>[\w\-]+)/endorse$', 'endorse_comment', name='endorse_comment'),
|
||||
url(r'comments/(?P<comment_id>[\w\-]+)/reply$', 'create_sub_comment', name='create_sub_comment'),
|
||||
url(r'comments/(?P<comment_id>[\w\-]+)/delete$', 'delete_comment', name='delete_comment'),
|
||||
url(r'comments/(?P<comment_id>[\w\-]+)/upvote$', 'vote_for_comment', {'value': 'up'}, name='upvote_comment'),
|
||||
url(r'comments/(?P<comment_id>[\w\-]+)/downvote$', 'vote_for_comment', {'value': 'down'}, name='downvote_comment'),
|
||||
url(r'threads/(?P<thread_id>[\w\-]+)/upvote$', 'vote_for_thread', {'value': 'up'}, name='upvote_thread'),
|
||||
url(r'threads/(?P<thread_id>[\w\-]+)/downvote$', 'vote_for_thread', {'value': 'down'}, name='downvote_thread'),
|
||||
)
|
||||
141
lms/djangoapps/django_comment_client/base/views.py
Normal file
141
lms/djangoapps/django_comment_client/base/views.py
Normal file
@@ -0,0 +1,141 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_POST, require_GET
|
||||
from django.http import HttpResponse
|
||||
from django.utils import simplejson
|
||||
|
||||
import comment_client
|
||||
|
||||
class JsonResponse(HttpResponse):
|
||||
def __init__(self, data=None):
|
||||
content = simplejson.dumps(data,
|
||||
indent=2,
|
||||
ensure_ascii=False)
|
||||
super(JsonResponse, self).__init__(content,
|
||||
mimetype='application/json; charset=utf8')
|
||||
|
||||
class JsonError(HttpResponse):
|
||||
def __init__(self, status, error_message=""):
|
||||
content = simplejson.dumps({'errors': error_message},
|
||||
indent=2,
|
||||
ensure_ascii=False)
|
||||
super(JsonError, self).__init__(content,
|
||||
status=status,
|
||||
mimetype='application/json; charset=utf8')
|
||||
|
||||
def thread_author_only(fn):
|
||||
def verified_fn(request, *args, **kwargs):
|
||||
thread_id = args.get('thread_id', False) or \
|
||||
kwargs.get('thread_id', False)
|
||||
thread = comment_client.get_thread(thread_id)
|
||||
if request.user.id == thread['user_id']:
|
||||
return fn(request, *args, **kwargs)
|
||||
else:
|
||||
return JsonError(400, "unauthorized")
|
||||
return verified_fn
|
||||
|
||||
def comment_author_only(fn):
|
||||
def verified_fn(request, *args, **kwargs):
|
||||
comment_id = args.get('comment_id', False) or \
|
||||
kwargs.get('comment_id', False)
|
||||
comment = comment_client.get_comment(comment_id)
|
||||
if request.user.id == comment['user_id']:
|
||||
return fn(request, *args, **kwargs)
|
||||
else:
|
||||
return JsonError(400, "unauthorized")
|
||||
return verified_fn
|
||||
|
||||
def instructor_only(fn): #TODO add instructor verification
|
||||
return fn
|
||||
|
||||
def extract(dic, keys):
|
||||
return {k: dic[k] for k in keys}
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def create_thread(request, commentable_id):
|
||||
attributes = extract(request.POST, ['body', 'title'])
|
||||
attributes['user_id'] = request.user.id
|
||||
attributes['course_id'] = "1" # TODO either remove this or pass this parameter somehow
|
||||
response = comment_client.create_thread(commentable_id, attributes)
|
||||
return JsonResponse(response)
|
||||
|
||||
@thread_author_only
|
||||
@login_required
|
||||
@require_POST
|
||||
def update_thread(request, thread_id):
|
||||
attributes = extract(request.POST, ['body', 'title'])
|
||||
response = comment_client.update_thread(thread_id, attributes)
|
||||
return JsonResponse(response)
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def create_comment(request, thread_id):
|
||||
attributes = extract(request.POST, ['body'])
|
||||
attributes['user_id'] = request.user.id
|
||||
attributes['course_id'] = "1" # TODO either remove this or pass this parameter somehow
|
||||
response = comment_client.create_comment(thread_id, attributes)
|
||||
return JsonResponse(response)
|
||||
|
||||
@thread_author_only
|
||||
@login_required
|
||||
@require_POST
|
||||
def delete_thread(request, thread_id):
|
||||
response = comment_client.delete_thread(thread_id)
|
||||
return JsonResponse(response)
|
||||
|
||||
@thread_author_only
|
||||
@login_required
|
||||
@require_POST
|
||||
def update_comment(request, comment_id):
|
||||
attributes = extract(request.POST, ['body'])
|
||||
response = comment_client.update_comment(comment_id, attributes)
|
||||
return JsonResponse(response)
|
||||
|
||||
@instructor_only
|
||||
@login_required
|
||||
@require_POST
|
||||
def endorse_comment(request, comment_id):
|
||||
attributes = extract(request.POST, ['endorsed'])
|
||||
response = comment_client.update_comment(comment_id, attributes)
|
||||
return JsonResponse(response)
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def create_sub_comment(request, comment_id):
|
||||
attributes = extract(request.POST, ['body'])
|
||||
attributes['user_id'] = request.user.id
|
||||
attributes['course_id'] = "1" # TODO either remove this or pass this parameter somehow
|
||||
response = comment_client.create_sub_comment(comment_id, attributes)
|
||||
return JsonResponse(response)
|
||||
|
||||
@comment_author_only
|
||||
@login_required
|
||||
@require_POST
|
||||
def delete_comment(request, comment_id):
|
||||
response = comment_client.delete_comment(comment_id)
|
||||
return JsonResponse(response)
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def vote_for_comment(request, comment_id, value):
|
||||
user_id = request.user.id
|
||||
response = comment_client.vote_for_comment(comment_id, user_id, value)
|
||||
return JsonResponse(response)
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def vote_for_thread(request, thread_id, value):
|
||||
user_id = request.user.id
|
||||
response = comment_client.vote_for_thread(thread_id, user_id, value)
|
||||
return JsonResponse(response)
|
||||
|
||||
#undo vote: disabled for now
|
||||
|
||||
|
||||
@login_required
|
||||
@require_GET
|
||||
def search(request):
|
||||
text = request.GET.get('text', None)
|
||||
commentable_id = request.GET.get('commentable_id', None)
|
||||
response = comment_client.search(text, commentable_id)
|
||||
return JsonResponse(response)
|
||||
8
lms/djangoapps/django_comment_client/forum/urls.py
Normal file
8
lms/djangoapps/django_comment_client/forum/urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.conf.urls.defaults import url, patterns
|
||||
import django_comment_client.forum.views
|
||||
|
||||
urlpatterns = patterns('django_comment_client.forum.views',
|
||||
url(r'search$', 'search', name='search'),
|
||||
url(r'threads/(?P<thread_id>\w+)$', 'single_thread', name='single_thread'),
|
||||
url(r'(?P<course_id>[\w\.\-]+)/(?P<discussion_id>\w+)$', 'forum_form_discussion', name='forum_form_discussion'),
|
||||
)
|
||||
168
lms/djangoapps/django_comment_client/forum/views.py
Normal file
168
lms/djangoapps/django_comment_client/forum/views.py
Normal file
@@ -0,0 +1,168 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.http import HttpResponse
|
||||
from django.utils import simplejson
|
||||
from django.core.context_processors import csrf
|
||||
|
||||
from mitxmako.shortcuts import render_to_response, render_to_string
|
||||
from courseware.courses import check_course
|
||||
from courseware.models import StudentModuleCache
|
||||
from courseware.module_render import get_module, get_section
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from importlib import import_module
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import comment_client
|
||||
import dateutil
|
||||
from dateutil.tz import tzlocal
|
||||
from datehelper import time_ago_in_words
|
||||
|
||||
import operator
|
||||
import itertools
|
||||
|
||||
_FULLMODULES = None
|
||||
_DISCUSSIONINFO = None
|
||||
|
||||
def get_full_modules():
|
||||
global _FULLMODULES
|
||||
if not _FULLMODULES:
|
||||
class_path = settings.MODULESTORE['default']['ENGINE']
|
||||
module_path, _, class_name = class_path.rpartition('.')
|
||||
class_ = getattr(import_module(module_path), class_name)
|
||||
modulestore = class_(eager=True, **settings.MODULESTORE['default']['OPTIONS'])
|
||||
_FULLMODULES = modulestore.modules
|
||||
return _FULLMODULES
|
||||
|
||||
def get_categorized_discussion_info(request, user, course, course_name, url_course_id):
|
||||
"""
|
||||
return a dict of the form {category: modules}
|
||||
"""
|
||||
global _DISCUSSIONINFO
|
||||
if not _DISCUSSIONINFO:
|
||||
|
||||
_is_course_discussion = lambda x: x[0].dict()['category'] == 'discussion' \
|
||||
and x[0].dict()['course'] == course_name
|
||||
|
||||
_get_module_descriptor = operator.itemgetter(1)
|
||||
|
||||
def _get_module(module_descriptor):
|
||||
print module_descriptor
|
||||
module = get_module(user, request, module_descriptor.location, student_module_cache)[0]
|
||||
return module
|
||||
|
||||
def _extract_info(module):
|
||||
return {
|
||||
'title': module.title,
|
||||
'discussion_id': module.discussion_id,
|
||||
'category': module.category,
|
||||
}
|
||||
|
||||
discussion_module_descriptors = map(_get_module_descriptor,
|
||||
filter(_is_course_discussion,
|
||||
get_full_modules().items()))
|
||||
|
||||
student_module_cache = StudentModuleCache(user, course)
|
||||
|
||||
discussion_info = map(_extract_info, map(_get_module, discussion_module_descriptors))
|
||||
|
||||
_DISCUSSIONINFO = dict((category, list(l)) \
|
||||
for category, l in itertools.groupby(discussion_info, operator.itemgetter('category')))
|
||||
|
||||
_DISCUSSIONINFO['General'] = [{
|
||||
'title': 'General discussion',
|
||||
'discussion_id': url_course_id,
|
||||
'category': 'General',
|
||||
}]
|
||||
|
||||
return _DISCUSSIONINFO
|
||||
|
||||
def render_accordion(request, course, discussion_info, discussion_id):
|
||||
context = dict([
|
||||
('course', course),
|
||||
('discussion_info', discussion_info),
|
||||
('active', discussion_id), # TODO change this later
|
||||
('csrf', csrf(request)['csrf_token'])])
|
||||
|
||||
return render_to_string('discussion/accordion.html', context)
|
||||
|
||||
def render_discussion(request, threads, discussion_id=None, search_text=''):
|
||||
context = {
|
||||
'threads': threads,
|
||||
'time_ago_in_words': time_ago_in_words,
|
||||
'parse': dateutil.parser.parse,
|
||||
'discussion_id': discussion_id,
|
||||
'search_bar': render_search_bar(request, discussion_id, text=search_text),
|
||||
}
|
||||
return render_to_string('discussion/inline.html', context)
|
||||
|
||||
def render_search_bar(request, discussion_id=None, text=''):
|
||||
if not discussion_id:
|
||||
return ''
|
||||
context = {
|
||||
'discussion_id': discussion_id,
|
||||
'text': text,
|
||||
}
|
||||
return render_to_string('discussion/search_bar.html', context)
|
||||
|
||||
def forum_form_discussion(request, course_id, discussion_id):
|
||||
|
||||
course_id = course_id.replace('-', '/')
|
||||
course = check_course(course_id)
|
||||
|
||||
_, course_name, _ = course_id.split('/')
|
||||
|
||||
url_course_id = course_id.replace('/', '_').replace('.', '_')
|
||||
|
||||
discussion_info = get_categorized_discussion_info(request, request.user, course, course_name, url_course_id)
|
||||
|
||||
search_text = request.GET.get('text', '')
|
||||
|
||||
if len(search_text) > 0:
|
||||
threads = comment_client.search(search_text, discussion_id)
|
||||
else:
|
||||
threads = comment_client.get_threads(discussion_id, recursive=False)
|
||||
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'COURSE_TITLE': course.title,
|
||||
'course': course,
|
||||
'init': '',
|
||||
'content': render_discussion(request, threads, discussion_id, search_text),
|
||||
'accordion': render_accordion(request, course, discussion_info, discussion_id),
|
||||
}
|
||||
|
||||
return render_to_response('discussion/index.html', context)
|
||||
|
||||
def render_single_thread(request, thread_id):
|
||||
context = {
|
||||
'thread': comment_client.get_thread(thread_id, recursive=True),
|
||||
'time_ago_in_words': time_ago_in_words,
|
||||
'parse': dateutil.parser.parse,
|
||||
}
|
||||
return render_to_string('discussion/single_thread.html', context)
|
||||
|
||||
def single_thread(request, thread_id):
|
||||
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'init': '',
|
||||
'content': render_single_thread(request, thread_id),
|
||||
'accordion': '',
|
||||
}
|
||||
|
||||
return render_to_response('discussion/index.html', context)
|
||||
|
||||
def search(request):
|
||||
text = request.GET.get('text', None)
|
||||
threads = comment_client.search(text)
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'init': '',
|
||||
'content': render_discussion(request, threads, search_text=text),
|
||||
'accordion': '',
|
||||
}
|
||||
|
||||
return render_to_response('discussion/index.html', context)
|
||||
6
lms/djangoapps/django_comment_client/urls.py
Normal file
6
lms/djangoapps/django_comment_client/urls.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.conf.urls.defaults import url, patterns, include
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'forum/', include('django_comment_client.forum.urls')),
|
||||
url(r'', include('django_comment_client.base.urls')),
|
||||
)
|
||||
Reference in New Issue
Block a user