diff options
author | Aamir Khan <syst3m.w0rm@gmail.com> | 2012-07-24 17:00:25 -0400 |
---|---|---|
committer | Aamir Khan <syst3m.w0rm@gmail.com> | 2012-07-24 17:00:25 -0400 |
commit | 9f18a590819a01017c15169d82763680a72848fb (patch) | |
tree | 9c781cd677eeae9b1e50e986647e1929e99bdac7 /hyperkitty/views | |
parent | ae77d9901e2a466622818f95d784fb85b5296727 (diff) | |
download | hyperkitty-9f18a590819a01017c15169d82763680a72848fb.tar.gz hyperkitty-9f18a590819a01017c15169d82763680a72848fb.tar.xz hyperkitty-9f18a590819a01017c15169d82763680a72848fb.zip |
Packaging hyperkitty
Diffstat (limited to 'hyperkitty/views')
-rw-r--r-- | hyperkitty/views/__init__.py | 0 | ||||
-rw-r--r-- | hyperkitty/views/accounts.py | 114 | ||||
-rw-r--r-- | hyperkitty/views/api.py | 27 | ||||
-rw-r--r-- | hyperkitty/views/forms.py | 59 | ||||
-rw-r--r-- | hyperkitty/views/list.py | 291 | ||||
-rw-r--r-- | hyperkitty/views/message.py | 82 | ||||
-rw-r--r-- | hyperkitty/views/pages.py | 38 | ||||
-rw-r--r-- | hyperkitty/views/thread.py | 113 |
8 files changed, 724 insertions, 0 deletions
diff --git a/hyperkitty/views/__init__.py b/hyperkitty/views/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/hyperkitty/views/__init__.py diff --git a/hyperkitty/views/accounts.py b/hyperkitty/views/accounts.py new file mode 100644 index 0000000..3154563 --- /dev/null +++ b/hyperkitty/views/accounts.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 1998-2012 by the Free Software Foundation, Inc. +# +# This file is part of HyperKitty. +# +# HyperKitty is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# HyperKitty is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# HyperKitty. If not, see <http://www.gnu.org/licenses/>. +# +# Author: Aamir Khan <syst3m.w0rm@gmail.com> +# + +import re +import sys + +from django.conf import settings +from django.contrib import messages +from django.contrib.auth import logout, authenticate, login +from django.contrib.auth.decorators import (login_required, + permission_required, + user_passes_test) +from django.contrib.auth.forms import AuthenticationForm +from gsoc.models import UserProfile +from django.contrib.auth.models import User +from django.core.urlresolvers import reverse +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render_to_response, redirect +from django.template import Context, loader, RequestContext +from django.utils.translation import gettext as _ +from urllib2 import HTTPError +from urlparse import urlparse + +from forms import RegistrationForm +from gsoc.utils import log + +def user_logout(request): + logout(request) + return redirect('user_login') + +def user_login(request,template = 'login.html'): + + parse_r = urlparse(request.META.get('HTTP_REFERER', 'index')) + previous = '%s%s' % (parse_r.path, parse_r.query) + + next_var = request.POST.get('next', request.GET.get('next', previous)) + + if request.method == 'POST': + form = AuthenticationForm(request.POST) + user = authenticate(username=request.POST.get('username'), + password=request.POST.get('password')) + + if user is not None: + log('debug', user) + if user.is_active: + login(request,user) + return redirect(next_var) + + else: + form = AuthenticationForm() + return render_to_response(template, {'form': form, 'next' : next_var}, + context_instance=RequestContext(request)) + +@login_required +def user_profile(request, user_email = None): + if not request.user.is_authenticated(): + return redirect('user_login') + # try to render the user profile. + try: + user_profile = request.user.get_profile() + # @TODO: Include the error name e.g, ProfileDoesNotExist? + except: + user_profile = UserProfile.objects.create(user=request.user) + + t = loader.get_template('user_profile.html') + + c = RequestContext(request, { + 'user_profile' : user_profile, + }) + + return HttpResponse(t.render(c)) + + +def user_registration(request): + if request.user.is_authenticated(): + # Already registered, redirect back to index page + return redirect('index') + + if request.POST: + form = RegistrationForm(request.POST) + if form.is_valid(): + # Save the user data. + form.save(form.cleaned_data) + user = authenticate(username=form.cleaned_data['username'], + password=form.cleaned_data['password1']) + + if user is not None: + log('debug', user) + if user.is_active: + login(request,user) + return redirect('index') + else: + form = RegistrationForm() + + return render_to_response('register.html', {'form': form}, context_instance=RequestContext(request)) + diff --git a/hyperkitty/views/api.py b/hyperkitty/views/api.py new file mode 100644 index 0000000..3a764db --- /dev/null +++ b/hyperkitty/views/api.py @@ -0,0 +1,27 @@ +import re +import os +import json +import urllib +import django.utils.simplejson as simplejson + +from calendar import timegm +from datetime import datetime, timedelta + +from urlparse import urljoin +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext, loader +from django.conf import settings +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage +from django.contrib.auth.decorators import (login_required, + permission_required, + user_passes_test) +from gsoc.utils import log + + +def api(request): + t = loader.get_template('api.html') + c = RequestContext(request, { + }) + return HttpResponse(t.render(c)) + + diff --git a/hyperkitty/views/forms.py b/hyperkitty/views/forms.py new file mode 100644 index 0000000..97e5550 --- /dev/null +++ b/hyperkitty/views/forms.py @@ -0,0 +1,59 @@ +from django import forms +from django.core import validators +from django.contrib.auth.models import User + +def isValidUsername(username): + try: + User.objects.get(username=username) + except User.DoesNotExist: + return + raise validators.ValidationError('The username "%s" is already taken.' % username) + +class RegistrationForm(forms.Form): + + username = forms.CharField(label='username', help_text=None, + widget=forms.TextInput( + attrs={'placeholder': 'username...'} + ), required = True, validators=[isValidUsername] + ) + + email = forms.EmailField(required=True) + + password1 = forms.CharField(widget=forms.PasswordInput) + + password2 = forms.CharField(widget=forms.PasswordInput) + + def save(self, new_user_data): + u = User.objects.create_user(new_user_data['username'], + new_user_data['email'], + new_user_data['password1']) + u.is_active = True + u.save() + return u + + +class AddTagForm(forms.Form): + tag = forms.CharField(label='', help_text=None, + widget=forms.TextInput( + attrs={'placeholder': 'Add a tag...'} + ) + ) + from_url = forms.CharField(widget=forms.HiddenInput, required=False) + +class SearchForm(forms.Form): + target = forms.CharField(label='', help_text=None, + widget=forms.Select( + choices=(('Subject', 'Subject'), + ('Content', 'Content'), + ('SubjectContent', 'Subject & Content'), + ('From', 'From')) + ) + ) + + keyword = forms.CharField(max_length=100,label='', help_text=None, + widget=forms.TextInput( + attrs={'placeholder': 'Search this list.'} + ) + ) + + diff --git a/hyperkitty/views/list.py b/hyperkitty/views/list.py new file mode 100644 index 0000000..17da2c6 --- /dev/null +++ b/hyperkitty/views/list.py @@ -0,0 +1,291 @@ +import re +import os +import json +import urllib +import django.utils.simplejson as simplejson + +from calendar import timegm +from datetime import datetime, timedelta + +from urlparse import urljoin +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext, loader +from django.conf import settings +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage +from django.contrib.auth.decorators import (login_required, + permission_required, + user_passes_test) +from kittystore.kittysastore import KittySAStore + +from gsoc.models import Rating +from lib.mockup import * +from forms import * +from gsoc.utils import log + + +STORE = KittySAStore(settings.KITTYSTORE_URL) + + +# @TODO : Move this into settings.py +MONTH_PARTICIPANTS = 284 +MONTH_DISCUSSIONS = 82 + + + +def archives(request, mlist_fqdn, year=None, month=None, day=None): + # No year/month: past 32 days + # year and month: find the 32 days for that month + # @TODO : modify url.py to account for page number + + end_date = None + if year or month or day: + try: + start_day = 1 + end_day = 1 + start_month = int(month) + end_month = int(month) + 1 + start_year = int(year) + end_year = int(year) + if day: + start_day = int(day) + end_day = start_day + 1 + end_month = start_month + if start_month == 12: + end_month = 1 + end_year = start_year + 1 + + begin_date = datetime(start_year, start_month, start_day) + end_date = datetime(end_year, end_month, end_day) + month_string = begin_date.strftime('%B %Y') + except ValueError, err: + print err + logger.error('Wrong format given for the date') + + if not end_date: + today = datetime.utcnow() + begin_date = datetime(today.year, today.month, 1) + end_date = datetime(today.year, today.month+1, 1) + month_string = 'Past thirty days' + list_name = mlist_fqdn.split('@')[0] + + search_form = SearchForm(auto_id=False) + t = loader.get_template('month_view.html') + threads = STORE.get_archives(list_name, start=begin_date, + end=end_date) + + participants = set() + cnt = 0 + for msg in threads: + # Statistics on how many participants and threads this month + participants.add(msg.sender) + msg.participants = STORE.get_thread_participants(list_name, + msg.thread_id) + msg.answers = STORE.get_thread_length(list_name, + msg.thread_id) + threads[cnt] = msg + cnt = cnt + 1 + #print msg + + paginator = Paginator(threads, 10) + pageNo = request.GET.get('page') + + try: + threads = paginator.page(pageNo) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + threads = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + threads = paginator.page(paginator.num_pages) + + + archives_length = STORE.get_archives_length(list_name) + + c = RequestContext(request, { + 'list_name' : list_name, + 'list_address': mlist_fqdn, + 'search_form': search_form, + 'month': month_string, + 'month_participants': len(participants), + 'month_discussions': len(threads), + 'threads': threads, + 'archives_length': archives_length, + }) + return HttpResponse(t.render(c)) + +def list(request, mlist_fqdn=None): + if not mlist_fqdn: + return HttpResponseRedirect('/') + t = loader.get_template('recent_activities.html') + search_form = SearchForm(auto_id=False) + list_name = mlist_fqdn.split('@')[0] + + # Get stats for last 30 days + today = datetime.utcnow() + end_date = datetime(today.year, today.month, today.day) + begin_date = end_date - timedelta(days=32) + + threads = STORE.get_archives(list_name=list_name,start=begin_date, + end=end_date) + + participants = set() + dates = {} + cnt = 0 + for msg in threads: + month = msg.date.month + if month < 10: + month = '0%s' % month + day = msg.date.day + if day < 10: + day = '0%s' % day + key = '%s%s%s' % (msg.date.year, month, day) + if key in dates: + dates[key] = dates[key] + 1 + else: + dates[key] = 1 + # Statistics on how many participants and threads this month + participants.add(msg.sender) + msg.participants = STORE.get_thread_participants(list_name, + msg.thread_id) + msg.answers = STORE.get_thread_length(list_name, + msg.thread_id) + threads[cnt] = msg + cnt = cnt + 1 + + # top threads are the one with the most answers + top_threads = sorted(threads, key=lambda entry: entry.answers, reverse=True) + + # active threads are the ones that have the most recent posting + active_threads = sorted(threads, key=lambda entry: entry.date, reverse=True) + + archives_length = STORE.get_archives_length(list_name) + + # top authors are the ones that have the most kudos. How do we determine + # that? Most likes for their post? + authors = generate_top_author() + authors = sorted(authors, key=lambda author: author.kudos) + authors.reverse() + + # Get the list activity per day + days = dates.keys() + days.sort() + dates_string = ["%s/%s/%s" % (key[0:4], key[4:6], key[6:8]) for key in days] + #print days + #print dates_string + evolution = [dates[key] for key in days] + if not evolution: + evolution.append(0) + + # threads per category is the top thread titles in each category + threads_per_category = generate_thread_per_category() + c = RequestContext(request, { + 'list_name' : list_name, + 'list_address': mlist_fqdn, + 'search_form': search_form, + 'month': 'Recent activity', + 'month_participants': len(participants), + 'month_discussions': len(threads), + 'top_threads': top_threads[:5], + 'most_active_threads': active_threads[:5], + 'top_author': authors, + 'threads_per_category': threads_per_category, + 'archives_length': archives_length, + 'evolution': evolution, + 'dates_string': dates_string, + }) + return HttpResponse(t.render(c)) + + +def _search_results_page(request, mlist_fqdn, threads, search_type, + page=1, num_threads=25, limit=None): + search_form = SearchForm(auto_id=False) + t = loader.get_template('search.html') + list_name = mlist_fqdn.split('@')[0] + res_num = len(threads) + + participants = set() + for msg in threads: + participants.add(msg.sender) + + paginator = Paginator(threads, num_threads) + + #If page request is out of range, deliver last page of results. + try: + threads = paginator.page(page) + except (EmptyPage, InvalidPage): + threads = paginator.page(paginator.num_pages) + + cnt = 0 + for msg in threads.object_list: + msg.email = msg.email.strip() + # Statistics on how many participants and threads this month + participants.add(msg.sender) + if msg.thread_id: + msg.participants = STORE.get_thread_participants(list_name, + msg.thread_id) + msg.answers = STORE.get_thread_length(list_name, + msg.thread_id) + else: + msg.participants = 0 + msg.answers = 0 + threads.object_list[cnt] = msg + cnt = cnt + 1 + + c = RequestContext(request, { + 'list_name' : list_name, + 'list_address': mlist_fqdn, + 'search_form': search_form, + 'month': search_type, + 'month_participants': len(participants), + 'month_discussions': res_num, + 'threads': threads, + 'full_path': request.get_full_path(), + }) + return HttpResponse(t.render(c)) + + +def search(request, mlist_fqdn): + keyword = request.GET.get('keyword') + target = request.GET.get('target') + page = request.GET.get('page') + if keyword and target: + url = '/search/%s/%s/%s/' % (mlist_fqdn, target, keyword) + if page: + url += '%s/' % page + else: + url = '/search/%s' % (mlist_fqdn) + return HttpResponseRedirect(url) + + +def search_keyword(request, mlist_fqdn, target, keyword, page=1): + ## Should we remove the code below? + ## If urls.py does it job we should never need it + if not keyword: + keyword = request.GET.get('keyword') + if not target: + target = request.GET.get('target') + if not target: + target = 'Subject' + regex = '%%%s%%' % keyword + list_name = mlist_fqdn.split('@')[0] + if target.lower() == 'subjectcontent': + threads = STORE.search_content_subject(list_name, keyword) + elif target.lower() == 'subject': + threads = STORE.search_subject(list_name, keyword) + elif target.lower() == 'content': + threads = STORE.search_content(list_name, keyword) + elif target.lower() == 'from': + threads = STORE.search_sender(list_name, keyword) + + return _search_results_page(request, mlist_fqdn, threads, 'Search', page) + + +def search_tag(request, mlist_fqdn, tag=None, page=1): + '''Searches both tag and topic''' + if tag: + query_string = {'Category': tag.capitalize()} + else: + query_string = None + return _search_results_page(request, mlist_fqdn, query_string, + 'Tag search', page, limit=50) + diff --git a/hyperkitty/views/message.py b/hyperkitty/views/message.py new file mode 100644 index 0000000..0b70969 --- /dev/null +++ b/hyperkitty/views/message.py @@ -0,0 +1,82 @@ +import re +import os +import django.utils.simplejson as simplejson + +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext, loader +from django.conf import settings +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage +from django.contrib.auth.decorators import (login_required, + permission_required, + user_passes_test) + +from kittystore.kittysastore import KittySAStore + +from gsoc.models import Rating +from lib.mockup import * +from forms import * +from gsoc.utils import log + +STORE = KittySAStore(settings.KITTYSTORE_URL) + + +def index (request, mlist_fqdn, messageid): + ''' Displays a single message identified by its messageid ''' + list_name = mlist_fqdn.split('@')[0] + + search_form = SearchForm(auto_id=False) + t = loader.get_template('message.html') + message = STORE.get_email(list_name, messageid) + message.email = message.email.strip() + # Extract all the votes for this message + try: + votes = Rating.objects.filter(messageid = messageid) + except Rating.DoesNotExist: + votes = {} + + likes = 0 + dislikes = 0 + + for vote in votes: + if vote.vote == 1: + likes = likes + 1 + elif vote.vote == -1: + dislikes = dislikes + 1 + else: + pass + + message.votes = votes + message.likes = likes + message.dislikes = dislikes + + c = RequestContext(request, { + 'list_name' : list_name, + 'list_address': mlist_fqdn, + 'message': message, + 'messageid' : messageid, + }) + return HttpResponse(t.render(c)) + + + +@login_required +def vote (request, mlist_fqdn): + """ Add a rating to a given message identified by messageid. """ + if not request.user.is_authenticated(): + return redirect('user_login') + + value = request.POST['vote'] + messageid = request.POST['messageid'] + + # Checks if the user has already voted for a this message. If yes modify db entry else create a new one. + try: + v = Rating.objects.get(user = request.user, messageid = messageid, list_address = mlist_fqdn) + except Rating.DoesNotExist: + v = Rating(list_address=mlist_fqdn, messageid = messageid, vote = value) + + v.user = request.user + v.vote = value + v.save() + response_dict = { } + + return HttpResponse(simplejson.dumps(response_dict), mimetype='application/javascript') diff --git a/hyperkitty/views/pages.py b/hyperkitty/views/pages.py new file mode 100644 index 0000000..189d574 --- /dev/null +++ b/hyperkitty/views/pages.py @@ -0,0 +1,38 @@ +#-*- coding: utf-8 -*- + +import re +import os +import json +import urllib +import django.utils.simplejson as simplejson + +from calendar import timegm +from datetime import datetime, timedelta + +from urlparse import urljoin +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext, loader +from django.conf import settings +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage +from django.contrib.auth.decorators import (login_required, + permission_required, + user_passes_test) +from gsoc.models import Rating +from lib.mockup import * +from forms import * +from gsoc.utils import log + +def index(request): + t = loader.get_template('index.html') + search_form = SearchForm(auto_id=False) + + base_url = settings.MAILMAN_API_URL % { + 'username': settings.MAILMAN_USER, 'password': settings.MAILMAN_PASS} + + list_data = ['devel@fp.o', 'packaging@fp.o', 'fr-users@fp.o'] + + c = RequestContext(request, { + 'lists': list_data, + 'search_form': search_form, + }) + return HttpResponse(t.render(c)) diff --git a/hyperkitty/views/thread.py b/hyperkitty/views/thread.py new file mode 100644 index 0000000..06a1bda --- /dev/null +++ b/hyperkitty/views/thread.py @@ -0,0 +1,113 @@ +import django.utils.simplejson as simplejson + +from django.http import HttpResponse, HttpResponseRedirect +from django.template import RequestContext, loader +from django.conf import settings +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, InvalidPage +from django.contrib.auth.decorators import (login_required, + permission_required, + user_passes_test) +from kittystore.kittysastore import KittySAStore + +from gsoc.models import Rating +from lib.mockup import * +from forms import * +from gsoc.utils import log + +STORE = KittySAStore(settings.KITTYSTORE_URL) + + + +def thread_index (request, mlist_fqdn, threadid): + ''' Displays all the email for a given thread identifier ''' + list_name = mlist_fqdn.split('@')[0] + + search_form = SearchForm(auto_id=False) + t = loader.get_template('thread.html') + threads = STORE.get_thread(list_name, threadid) + #prev_thread = mongo.get_thread_name(list_name, int(threadid) - 1) + prev_thread = [] + if len(prev_thread) > 30: + prev_thread = '%s...' % prev_thread[:31] + #next_thread = mongo.get_thread_name(list_name, int(threadid) + 1) + next_thread = [] + if len(next_thread) > 30: + next_thread = '%s...' % next_thread[:31] + + participants = {} + cnt = 0 + + for message in threads: + # @TODO: Move this logic inside KittyStore? + message.email = message.email.strip() + + # Extract all the votes for this message + try: + votes = Rating.objects.filter(messageid = message.message_id) + except Rating.DoesNotExist: + votes = {} + + likes = 0 + dislikes = 0 + + for vote in votes: + if vote.vote == 1: + likes = likes + 1 + elif vote.vote == -1: + dislikes = dislikes + 1 + else: + pass + + message.votes = votes + message.likes = likes + message.dislikes = dislikes + + # Statistics on how many participants and threads this month + participants[message.sender] = {'email': message.email} + cnt = cnt + 1 + + archives_length = STORE.get_archives_length(list_name) + from_url = '/thread/%s/%s/' %(mlist_fqdn, threadid) + tag_form = AddTagForm(initial={'from_url' : from_url}) + + c = RequestContext(request, { + 'list_name' : list_name, + 'list_address': mlist_fqdn, + 'search_form': search_form, + 'addtag_form': tag_form, + 'month': 'Thread', + 'participants': participants, + 'answers': cnt, + 'first_mail': threads[0], + 'threads': threads[1:], + 'next_thread': next_thread, + 'next_thread_id': 0, + 'prev_thread': prev_thread, + 'prev_thread_id': 0, + 'archives_length': archives_length, + }) + return HttpResponse(t.render(c)) + + +@login_required +def add_tag(request, mlist_fqdn, email_id): + """ Add a tag to a given thread. """ + t = loader.get_template('threads/add_tag_form.html') + if request.method == 'POST': + form = AddTagForm(request.POST) + if form.is_valid(): + print "THERE WE ARE" + # TODO: Add the logic to add the tag + if form.data['from_url']: + return HttpResponseRedirect(form.data['from_url']) + else: + return HttpResponseRedirect('/') + else: + form = AddTagForm() + c = RequestContext(request, { + 'list_address': mlist_fqdn, + 'email_id': email_id, + 'addtag_form': form, + }) + return HttpResponse(t.render(c)) + |