From 524a7d83c93ff4b671fe128efbb592b3a11f9507 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Tue, 18 Sep 2012 03:53:16 -0400 Subject: broken commit, start for the group messaging app --- group_messaging/views.py | 148 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 group_messaging/views.py (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py new file mode 100644 index 00000000..d24833f2 --- /dev/null +++ b/group_messaging/views.py @@ -0,0 +1,148 @@ +"""semi-views for the `group_messaging` application +These are not really views - rather context generator +functions, to be used separately, when needed. + +For example, some other application can call these +in order to render messages within the page. + +Notice that :mod:`urls` module decorates all these functions +and turns them into complete views +""" +import functools +from django.contrib.auth.models import Group +from django.core.exceptions import PermissionDenied +from django.forms import IntegerField +from django.http import HttpResponse +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.views.decorators.http import require_GET +from django.views.decorators.http import require_POST +from django.utils import simplejson +from group_messages.models import Message +from group_messages.models import MessageMemo +from group_messages.models import SenderList +from group_messages.models import get_personal_group_by_id + +class InboxView(object): + """custom class-based view + to be used for pjax use and for generation + of content in the traditional way""" + template_name = None #this needs to be set + + def get(self, request, *args, **kwargs): + context = self.get_context(request, *args, **kwargs) + #todo: load template with Coffin and render it + return HttpResponse(json, mimetype='application/json') + + def get_context(self, request, *args, **kwargs): + """Should return the context dictionary""" + return {} + + def as_pjax(self): + """returns the view function - for the urls.py""" + def view_function(request, *args, **kwargs): + """the actual view function""" + if request.user.is_anonymous(): + raise PermissionDenied() + if request.is_ajax() is False: + raise PermissionDenied() + + if request.method == 'GET': + return self.get(request, *args, **kwargs) + elif request.method == 'POST': + return self.post(request, *args, **kwargs) + else: + raise NotImplementedError + return view_function + + +def require_login(view_func): + @functools.wraps(view_func) + def wrapped(request, *args, **kwargs): + if request.user.is_authenticated(): + return view_func(request, *args, **kwargs) + else: + raise PermissionDenied() + return wrapped + + +def ajax(view_func): + @functools.wraps(view_func) + def wrapped(request, *args, **kwargs): + if request.is_ajax(): + result = view_func(request, *args, **kwargs) + json = simplejson.dumps(result) + return HttpResponse(json, mimetype='application/json') + else: + raise PermissionDenied() + + +class NewThread(InboxView): + template_name = 'new_thread.html' + + def post(self, request): + recipient_id = IntegerField().clean(request.POST['recipient_id']) + recipient = get_personal_group_by_id(recipient_id) + message = Message.objects.create_thread( + sender=request.user, + recipients=[recipient], + text=request.POST['text'] + ) + return {'message_id': message.id} + + +class NewResponse(InboxView): + def get(self, request): + raise PermissionDenied() + + def post(self, request): + parent_id = IntegerField().clean(request.POST['parent_id']) + parent = Message.objects.get(id=parent_id) + message = Message.objects.create_response( + sender=request.user, + text=request.POST['text'], + parent=parent + ) + +class ThreadsList(InboxView): + """shows list of threads for a given user""" + template_name = 'threads_list.html' + + def get_context(self, request): + """returns thread list data""" + if request.method != 'GET': + raise PermissionDenied() + + threads = Message.objects.get_threads_for_user(request.user) + threads = threads.values('id', 'headline', 'is_read') + return {'threads': threads} + + +class SendersList(InboxView): + """shows list of senders for a user""" + template_name = 'senders_list.html' + + def get_context(request): + """get data about senders for the user""" + if request.method != 'GET': + raise PermissionDenied() + + senders = SenderList.objects.get_senders_for_user(request.user) + senders = senders.values('id', 'username') + return {'senders': senders} + + +class ThreadDetails(InboxView): + """shows entire thread in the unfolded form""" + template_name = 'thread_details.html' + + def get_context(request): + """shows individual thread""" + if request.method != 'GET': + raise PermissionDenied() + + thread_id = IntegerField().clean(request.GET['thread_id']) + #todo: assert that current thread is the root + messages = Message.objects.filter(root__id=thread_id) + messages = messages.values('html') + return {'messages': messages} -- cgit v1.2.3-1-g7c22 From 8adb7e1b244d07e27d040d14e1cfd5e5280ce40b Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Wed, 19 Sep 2012 01:58:49 -0400 Subject: started building front end for private messaging --- group_messaging/views.py | 135 ++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 66 deletions(-) (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py index d24833f2..c2e9af93 100644 --- a/group_messaging/views.py +++ b/group_messaging/views.py @@ -8,92 +8,98 @@ in order to render messages within the page. Notice that :mod:`urls` module decorates all these functions and turns them into complete views """ -import functools -from django.contrib.auth.models import Group -from django.core.exceptions import PermissionDenied +from coffin.template.loader import get_template from django.forms import IntegerField from django.http import HttpResponse -from django.shortcuts import render_to_response -from django.template import RequestContext -from django.views.decorators.http import require_GET -from django.views.decorators.http import require_POST +from django.http import HttpResponseNotAllowed +from django.http import HttpResponseForbidden from django.utils import simplejson -from group_messages.models import Message -from group_messages.models import MessageMemo -from group_messages.models import SenderList -from group_messages.models import get_personal_group_by_id +from group_messaging.models import Message +from group_messaging.models import SenderList +from group_messaging.models import get_personal_group_by_user_id class InboxView(object): """custom class-based view to be used for pjax use and for generation - of content in the traditional way""" - template_name = None #this needs to be set + of content in the traditional way, where + the only the :method:`get_context` would be used. + """ + template_name = None #used only for the "GET" method + http_method_names = ('GET', 'POST') + + def render_to_response(self, context, template_name=None): + """like a django's shortcut, except will use + template_name from self, if `template_name` is not given. + Also, response is packaged as json with an html fragment + for the pjax consumption + """ + if template_name is None: + template_name = self.template_name + template = get_template(self.template_name) + html = template.render(context) + json = simplejson.dumps({'html': html}) + return HttpResponse(json, mimetype='application/json') + def get(self, request, *args, **kwargs): + """view function for the "GET" method""" context = self.get_context(request, *args, **kwargs) - #todo: load template with Coffin and render it - return HttpResponse(json, mimetype='application/json') + return self.render_to_response(context) + + def post(self, request, *args, **kwargs): + """view function for the "POST" method""" + pass + + def dispatch(self, request, *args, **kwargs): + """checks that the current request method is allowed + and calls the corresponding view function""" + if request.method not in self.http_method_names: + return HttpResponseNotAllowed() + view_func = getattr(self, request.method.lower()) + return view_func(request, *args, **kwargs) def get_context(self, request, *args, **kwargs): - """Should return the context dictionary""" + """Returns the context dictionary for the "get" + method only""" return {} - def as_pjax(self): + def as_view(self): """returns the view function - for the urls.py""" def view_function(request, *args, **kwargs): """the actual view function""" - if request.user.is_anonymous(): - raise PermissionDenied() - if request.is_ajax() is False: - raise PermissionDenied() - - if request.method == 'GET': - return self.get(request, *args, **kwargs) - elif request.method == 'POST': - return self.post(request, *args, **kwargs) + if request.user.is_authenticated() and request.is_ajax(): + view_method = getattr(self, request.method.lower()) + return view_method(request, *args, **kwargs) else: - raise NotImplementedError - return view_function - - -def require_login(view_func): - @functools.wraps(view_func) - def wrapped(request, *args, **kwargs): - if request.user.is_authenticated(): - return view_func(request, *args, **kwargs) - else: - raise PermissionDenied() - return wrapped + return HttpResponseForbidden() - -def ajax(view_func): - @functools.wraps(view_func) - def wrapped(request, *args, **kwargs): - if request.is_ajax(): - result = view_func(request, *args, **kwargs) - json = simplejson.dumps(result) - return HttpResponse(json, mimetype='application/json') - else: - raise PermissionDenied() + return view_function class NewThread(InboxView): - template_name = 'new_thread.html' + """view for creation of new thread""" + template_name = 'create_thread.html'# contains new thread form + http_method_list = ('GET', 'POST') def post(self, request): + """creates a new thread on behalf of the user + response is blank, because on the client side we just + need to go back to the thread listing view whose + content should be cached in the client' + """ recipient_id = IntegerField().clean(request.POST['recipient_id']) - recipient = get_personal_group_by_id(recipient_id) - message = Message.objects.create_thread( + recipient = get_personal_group_by_user_id(recipient_id) + Message.objects.create_thread( sender=request.user, recipients=[recipient], text=request.POST['text'] ) - return {'message_id': message.id} + return HttpResponse('', mimetype='application/json') class NewResponse(InboxView): - def get(self, request): - raise PermissionDenied() + """view to create a new response""" + http_method_list = ('POST',) def post(self, request): parent_id = IntegerField().clean(request.POST['parent_id']) @@ -103,30 +109,29 @@ class NewResponse(InboxView): text=request.POST['text'], parent=parent ) + return self.render_to_response( + {'message': message}, template_name='stored_message.htmtl' + ) class ThreadsList(InboxView): """shows list of threads for a given user""" template_name = 'threads_list.html' + http_method_list = ('GET',) def get_context(self, request): """returns thread list data""" - if request.method != 'GET': - raise PermissionDenied() - threads = Message.objects.get_threads_for_user(request.user) - threads = threads.values('id', 'headline', 'is_read') + threads = threads.values('id', 'headline') return {'threads': threads} class SendersList(InboxView): """shows list of senders for a user""" template_name = 'senders_list.html' + http_method_names = ('GET',) - def get_context(request): + def get_context(self, request): """get data about senders for the user""" - if request.method != 'GET': - raise PermissionDenied() - senders = SenderList.objects.get_senders_for_user(request.user) senders = senders.values('id', 'username') return {'senders': senders} @@ -135,12 +140,10 @@ class SendersList(InboxView): class ThreadDetails(InboxView): """shows entire thread in the unfolded form""" template_name = 'thread_details.html' + http_method_names = ('GET',) - def get_context(request): + def get_context(self, request): """shows individual thread""" - if request.method != 'GET': - raise PermissionDenied() - thread_id = IntegerField().clean(request.GET['thread_id']) #todo: assert that current thread is the root messages = Message.objects.filter(root__id=thread_id) -- cgit v1.2.3-1-g7c22 From 7681d6d236524608b58a96fc846fab0f6fff864f Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Thu, 20 Sep 2012 03:55:41 -0400 Subject: close to posting the new thread --- group_messaging/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py index c2e9af93..9d324d62 100644 --- a/group_messaging/views.py +++ b/group_messaging/views.py @@ -87,8 +87,9 @@ class NewThread(InboxView): need to go back to the thread listing view whose content should be cached in the client' """ - recipient_id = IntegerField().clean(request.POST['recipient_id']) - recipient = get_personal_group_by_user_id(recipient_id) + username = IntegerField().clean(request.POST['to_username']) + user = User.objects.get(username=username) + recipient = get_personal_group_by_user_id(user.id) Message.objects.create_thread( sender=request.user, recipients=[recipient], -- cgit v1.2.3-1-g7c22 From a202578af77b8ecf66cb58a7f535481818db91e5 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Fri, 21 Sep 2012 01:45:45 -0400 Subject: made possibility to display threads as new/seen --- group_messaging/views.py | 72 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 13 deletions(-) (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py index 9d324d62..8c06ff99 100644 --- a/group_messaging/views.py +++ b/group_messaging/views.py @@ -8,7 +8,10 @@ in order to render messages within the page. Notice that :mod:`urls` module decorates all these functions and turns them into complete views """ +import copy +import datetime from coffin.template.loader import get_template +from django.contrib.auth.models import User from django.forms import IntegerField from django.http import HttpResponse from django.http import HttpResponseNotAllowed @@ -16,7 +19,9 @@ from django.http import HttpResponseForbidden from django.utils import simplejson from group_messaging.models import Message from group_messaging.models import SenderList +from group_messaging.models import LastVisitTime from group_messaging.models import get_personal_group_by_user_id +from group_messaging.models import get_personal_groups_for_users class InboxView(object): """custom class-based view @@ -78,8 +83,7 @@ class InboxView(object): class NewThread(InboxView): """view for creation of new thread""" - template_name = 'create_thread.html'# contains new thread form - http_method_list = ('GET', 'POST') + http_method_list = ('POST',) def post(self, request): """creates a new thread on behalf of the user @@ -87,15 +91,29 @@ class NewThread(InboxView): need to go back to the thread listing view whose content should be cached in the client' """ - username = IntegerField().clean(request.POST['to_username']) - user = User.objects.get(username=username) - recipient = get_personal_group_by_user_id(user.id) - Message.objects.create_thread( - sender=request.user, - recipients=[recipient], - text=request.POST['text'] - ) - return HttpResponse('', mimetype='application/json') + usernames = request.POST['to_usernames'] + usernames = map(lambda v: v.strip(), usernames.split(',')) + users = User.objects.filter(username__in=usernames) + + missing = copy.copy(usernames) + for user in users: + if user.username in missing: + missing.remove(user.username) + + result = dict() + if missing: + result['success'] = False + result['missing_users'] = missing + else: + recipients = get_personal_groups_for_users(users) + message = Message.objects.create_thread( + sender=request.user, + recipients=recipients, + text=request.POST['text'] + ) + result['success'] = True + result['message_id'] = message.id + return HttpResponse(simplejson.dumps(result), mimetype='application/json') class NewResponse(InboxView): @@ -121,9 +139,37 @@ class ThreadsList(InboxView): def get_context(self, request): """returns thread list data""" + #get threads and the last visit time threads = Message.objects.get_threads_for_user(request.user) - threads = threads.values('id', 'headline') - return {'threads': threads} + try: + last_visit = LastVisitTime.objects.get(user=request.user) + except LastVisitTime.DoesNotExist: + timestamp = datetime.datetime(2010, 3, 24)#day of askbot + last_visit = LastVisitTime(user=request.user, at=timestamp) + + + #for each thread we need to know if there is something + #unread for the user - to mark "new" threads as bold + threads_data = dict() + for thread in threads: + thread_data = dict() + #determine status + status = 'seen' + if thread.last_active_at > last_visit.at: + status = 'new' + thread_data['status'] = status + #determine the senders info + senders_names = thread.senders_info.split(',') + if request.user.username in senders_names: + senders_names.remove(request.user.username) + thread_data['senders_info'] = ', '.join(senders_names) + threads_data[thread.id] = thread_data + + #after we have all the data - update the last visit time + last_visit.at = datetime.datetime.now() + last_visit.save() + + return {'threads': threads, 'threads_data': threads_data} class SendersList(InboxView): -- cgit v1.2.3-1-g7c22 From 27c52cc8c52157d539bb3c761a53d1b6192c8b64 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 24 Sep 2012 21:43:36 -0400 Subject: hopefully fixed a corner case bug in the login system --- group_messaging/views.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py index 8c06ff99..6511fe6e 100644 --- a/group_messaging/views.py +++ b/group_messaging/views.py @@ -141,12 +141,6 @@ class ThreadsList(InboxView): """returns thread list data""" #get threads and the last visit time threads = Message.objects.get_threads_for_user(request.user) - try: - last_visit = LastVisitTime.objects.get(user=request.user) - except LastVisitTime.DoesNotExist: - timestamp = datetime.datetime(2010, 3, 24)#day of askbot - last_visit = LastVisitTime(user=request.user, at=timestamp) - #for each thread we need to know if there is something #unread for the user - to mark "new" threads as bold @@ -154,21 +148,26 @@ class ThreadsList(InboxView): for thread in threads: thread_data = dict() #determine status - status = 'seen' - if thread.last_active_at > last_visit.at: - status = 'new' - thread_data['status'] = status + thread_data['status'] = 'new' #determine the senders info senders_names = thread.senders_info.split(',') if request.user.username in senders_names: senders_names.remove(request.user.username) thread_data['senders_info'] = ', '.join(senders_names) threads_data[thread.id] = thread_data + threads_data[thread] = thread + + last_visit_times = LastVisitTime.objects.filter( + user=request.user, + message__in=threads + ) + for last_visit in last_visit_times: + thread_data = threads_data[last_visit.thread_id] + if thread_data['thread'].last_active_at <= last_visit.at: + thread_data['status'] = 'seen' #after we have all the data - update the last visit time - last_visit.at = datetime.datetime.now() - last_visit.save() - + last_visit_times.update(at=datetime.datetime.now()) return {'threads': threads, 'threads_data': threads_data} -- cgit v1.2.3-1-g7c22 From 74c55ff1382cc11eaa822bb0bf011ddd85797403 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Tue, 25 Sep 2012 04:43:25 -0400 Subject: private message navigation starts to work --- group_messaging/views.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py index 6511fe6e..0ea710db 100644 --- a/group_messaging/views.py +++ b/group_messaging/views.py @@ -42,7 +42,7 @@ class InboxView(object): template_name = self.template_name template = get_template(self.template_name) html = template.render(context) - json = simplejson.dumps({'html': html}) + json = simplejson.dumps({'html': html, 'success': True}) return HttpResponse(json, mimetype='application/json') @@ -134,7 +134,7 @@ class NewResponse(InboxView): class ThreadsList(InboxView): """shows list of threads for a given user""" - template_name = 'threads_list.html' + template_name = 'group_messaging/threads_list.html' http_method_list = ('GET',) def get_context(self, request): @@ -142,6 +142,10 @@ class ThreadsList(InboxView): #get threads and the last visit time threads = Message.objects.get_threads_for_user(request.user) + sender_id = IntegerField().clean(request.GET.get('sender_id', '-1')) + if sender_id != -1: + threads = threads.filter(sender__id=sender_id) + #for each thread we need to know if there is something #unread for the user - to mark "new" threads as bold threads_data = dict() @@ -154,15 +158,15 @@ class ThreadsList(InboxView): if request.user.username in senders_names: senders_names.remove(request.user.username) thread_data['senders_info'] = ', '.join(senders_names) + thread_data['thread'] = thread threads_data[thread.id] = thread_data - threads_data[thread] = thread last_visit_times = LastVisitTime.objects.filter( user=request.user, message__in=threads ) for last_visit in last_visit_times: - thread_data = threads_data[last_visit.thread_id] + thread_data = threads_data[last_visit.message_id] if thread_data['thread'].last_active_at <= last_visit.at: thread_data['status'] = 'seen' @@ -173,7 +177,7 @@ class ThreadsList(InboxView): class SendersList(InboxView): """shows list of senders for a user""" - template_name = 'senders_list.html' + template_name = 'group_messaging/senders_list.html' http_method_names = ('GET',) def get_context(self, request): @@ -185,13 +189,19 @@ class SendersList(InboxView): class ThreadDetails(InboxView): """shows entire thread in the unfolded form""" - template_name = 'thread_details.html' + template_name = 'group_messaging/thread_details.html' http_method_names = ('GET',) - def get_context(self, request): + def get_context(self, request, thread_id=None): """shows individual thread""" - thread_id = IntegerField().clean(request.GET['thread_id']) #todo: assert that current thread is the root - messages = Message.objects.filter(root__id=thread_id) - messages = messages.values('html') - return {'messages': messages} + root = Message.objects.get(id=thread_id) + responses = Message.objects.filter(root__id=thread_id) + last_visit, created = LastVisitTime.objects.get_or_create( + message=root, + user=request.user + ) + if created is False: + last_visit.at = datetime.datetime.now() + last_visit.save() + return {'root_message': root, 'responses': responses, 'request': request} -- cgit v1.2.3-1-g7c22 From 23ac9ee8e92ef6b8429b239e4229c39dc7bb4ac5 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Thu, 27 Sep 2012 03:40:08 -0400 Subject: made personal messages answerable --- group_messaging/views.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'group_messaging/views.py') diff --git a/group_messaging/views.py b/group_messaging/views.py index 0ea710db..289961ff 100644 --- a/group_messaging/views.py +++ b/group_messaging/views.py @@ -40,7 +40,7 @@ class InboxView(object): """ if template_name is None: template_name = self.template_name - template = get_template(self.template_name) + template = get_template(template_name) html = template.render(context) json = simplejson.dumps({'html': html, 'success': True}) return HttpResponse(json, mimetype='application/json') @@ -116,7 +116,7 @@ class NewThread(InboxView): return HttpResponse(simplejson.dumps(result), mimetype='application/json') -class NewResponse(InboxView): +class PostReply(InboxView): """view to create a new response""" http_method_list = ('POST',) @@ -128,8 +128,15 @@ class NewResponse(InboxView): text=request.POST['text'], parent=parent ) + last_visit = LastVisitTime.objects.get( + message=message.root, + user=request.user + ) + last_visit.at = datetime.datetime.now() + last_visit.save() return self.render_to_response( - {'message': message}, template_name='stored_message.htmtl' + {'post': message, 'user': request.user}, + template_name='group_messaging/stored_message.html' ) class ThreadsList(InboxView): -- cgit v1.2.3-1-g7c22