From 1c0db6b8e3b262b9cdba6a32e71c9bbac8b5f7cc Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Mon, 15 Jul 2013 12:02:04 +0200 Subject: Cleanups in the lib/__init__.py file --- hyperkitty/lib/__init__.py | 204 ----------------------------------------- hyperkitty/lib/posting.py | 66 +++++++++++++ hyperkitty/lib/view_helpers.py | 171 ++++++++++++++++++++++++++++++++++ hyperkitty/tests/test_forms.py | 20 ---- hyperkitty/tests/test_lib.py | 2 +- hyperkitty/views/accounts.py | 3 +- hyperkitty/views/list.py | 7 +- hyperkitty/views/message.py | 4 +- hyperkitty/views/search.py | 3 +- hyperkitty/views/thread.py | 4 +- 10 files changed, 251 insertions(+), 233 deletions(-) create mode 100644 hyperkitty/lib/posting.py create mode 100644 hyperkitty/lib/view_helpers.py delete mode 100644 hyperkitty/tests/test_forms.py (limited to 'hyperkitty') diff --git a/hyperkitty/lib/__init__.py b/hyperkitty/lib/__init__.py index 9bcded3..1970917 100644 --- a/hyperkitty/lib/__init__.py +++ b/hyperkitty/lib/__init__.py @@ -19,59 +19,6 @@ # Author: Aurelien Bompard # -import urllib -from hashlib import md5 -import datetime - -from django.core.exceptions import SuspiciousOperation -from django.core.mail import EmailMessage -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.utils.timezone import utc -from mailmanclient import MailmanConnectionError - -from hyperkitty.lib import mailman -from hyperkitty.models import ThreadCategory, LastView -from hyperkitty.views.forms import CategoryForm - - -FLASH_MESSAGES = { - "updated-ok": ("success", "The profile was successfully updated."), - "sent-ok": ("success", "The message has been sent successfully."), -} - - -def gravatar_url(email): - '''Return a gravatar url for an email address''' - size = 64 - default = "http://fedoraproject.org/static/images/" + \ - "fedora_infinity_%ix%i.png" % (size, size) - query_string = urllib.urlencode({'s': size, 'd': default}) - identifier = md5(email).hexdigest() - return 'http://www.gravatar.com/avatar/%s?%s' % (identifier, query_string) - - -def get_months(store, list_name): - """ Return a dictionnary of years, months for which there are - potentially archives available for a given list (based on the - oldest post on the list). - - :arg list_name, name of the mailing list in which this email - should be searched. - """ - date_first = store.get_start_date(list_name) - if not date_first: - return {} - archives = {} - now = datetime.datetime.now() - year = date_first.year - month = date_first.month - while year < now.year: - archives[year] = range(1, 13)[(month -1):] - year = year + 1 - month = 1 - archives[now.year] = range(1, 13)[:now.month] - return archives - def get_store(request): return request.environ["kittystore.store"] @@ -83,154 +30,3 @@ def stripped_subject(mlist, subject): if subject.lower().startswith(mlist.subject_prefix.lower()): subject = subject[len(mlist.subject_prefix) : ] return subject - - -def get_display_dates(year, month, day): - if day is None: - start_day = 1 - else: - start_day = int(day) - begin_date = datetime.datetime(int(year), int(month), start_day) - - if day is None: - end_date = begin_date + datetime.timedelta(days=32) - end_date = end_date.replace(day=1) - else: - end_date = begin_date + datetime.timedelta(days=1) - - return begin_date, end_date - - -def daterange(start_date, end_date): - for n in range(int((end_date - start_date).days)): - yield start_date + datetime.timedelta(n) - - -class PostingFailed(Exception): pass - -def post_to_list(request, mlist, subject, message, headers={}, - attachments=None): - if not mlist: - # Make sure the list exists to avoid posting to any email addess - raise SuspiciousOperation("I don't know this mailing-list") - # Check that the user is subscribed - try: - mailman.subscribe(mlist.name, request.user) - except MailmanConnectionError: - raise PostingFailed("Can't connect to Mailman's REST server, " - "your message has not been sent.") - # send the message - headers["User-Agent"] = "HyperKitty on %s" % request.build_absolute_uri("/") - if not request.user.first_name and not request.user.last_name: - from_email = request.user.email - else: - from_email = '"%s %s" <%s>' % (request.user.first_name, - request.user.last_name, - request.user.email) - msg = EmailMessage( - subject=subject, - body=message, - from_email=from_email, - to=[mlist.name], - headers=headers, - ) - # Attachments - if attachments: - if not isinstance(attachments, list): - attachments = [attachments] - for attach in attachments: - msg.attach(attach.name, attach.read()) - msg.send() - - -def paginate(objects, page_num, max_page_range=10, paginator=None): - try: - page_num = int(page_num) - except (TypeError, ValueError): - page_num = 1 - if paginator is None: - paginator = Paginator(objects, 10) # else use the provided instance - try: - objects = paginator.page(page_num) - except PageNotAnInteger: - # If page is not an integer, deliver first page. - objects = paginator.page(1) - except EmptyPage: - # If page is out of range (e.g. 9999), deliver last page of results. - objects = paginator.page(paginator.num_pages) - # Calculate the displayed page range - if paginator.num_pages > max_page_range: - objects.page_range = [ 1 ] - subrange_lower = page_num - (max_page_range / 2 - 2) - if subrange_lower > 3: - objects.page_range.append("...") - else: - subrange_lower = 2 - objects.page_range.extend(range(subrange_lower, page_num)) - if page_num != 1 and page_num != 100: - objects.page_range.append(page_num) - subrange_upper = page_num + (max_page_range / 2 - 2) - if subrange_upper >= paginator.num_pages - 2: - subrange_upper = paginator.num_pages - 1 - objects.page_range.extend(range(page_num+1, subrange_upper+1)) - if subrange_upper < paginator.num_pages - 2: - objects.page_range.append("...") - objects.page_range.append(paginator.num_pages) - else: - objects.page_range = [ p+1 for p in range(paginator.num_pages) ] - return objects - - -def get_category_widget(request=None, current_category=None): - """ - Returns the category form and the applicable category object (or None if no - category is set for this thread). - - If current_category is not provided or None, try to deduce it from the POST - request. - if request is not provided or None, don't return the category form, return - None instead. - """ - categories = [ (c.name, c.name.upper()) - for c in ThreadCategory.objects.all() ] \ - + [("", "no category")] - - if request: - if request.method == "POST": - category_form = CategoryForm(request.POST) - else: - category_form = CategoryForm(initial={"category": current_category or ""}) - category_form["category"].field.choices = categories - else: - category_form = None - if request and request.method == "POST" and category_form.is_valid(): - # is_valid() must be called after the choices have been set - current_category = category_form.cleaned_data["category"] - - if not current_category: - category = None - else: - try: - category = ThreadCategory.objects.get(name=current_category) - except ThreadCategory.DoesNotExist: - category = None - return category, category_form - - -def is_thread_unread(request, mlist_name, thread): - """Returns True or False if the thread is unread or not.""" - unread = False - if request.user.is_authenticated(): - try: - last_view_obj = LastView.objects.get( - list_address=mlist_name, - threadid=thread.thread_id, - user=request.user) - except LastView.DoesNotExist: - unread = True - else: - if thread.date_active.replace(tzinfo=utc) \ - > last_view_obj.view_date: - unread = True - return unread - diff --git a/hyperkitty/lib/posting.py b/hyperkitty/lib/posting.py new file mode 100644 index 0000000..306806e --- /dev/null +++ b/hyperkitty/lib/posting.py @@ -0,0 +1,66 @@ +#-*- 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 . +# +# Author: Aurelien Bompard +# + + +from django.core.exceptions import SuspiciousOperation +from django.core.mail import EmailMessage +from mailmanclient import MailmanConnectionError + +from hyperkitty.lib import mailman + + +class PostingFailed(Exception): + pass + + +def post_to_list(request, mlist, subject, message, headers={}, + attachments=None): + if not mlist: + # Make sure the list exists to avoid posting to any email addess + raise SuspiciousOperation("I don't know this mailing-list") + # Check that the user is subscribed + try: + mailman.subscribe(mlist.name, request.user) + except MailmanConnectionError: + raise PostingFailed("Can't connect to Mailman's REST server, " + "your message has not been sent.") + # send the message + headers["User-Agent"] = "HyperKitty on %s" % request.build_absolute_uri("/") + if not request.user.first_name and not request.user.last_name: + from_email = request.user.email + else: + from_email = '"%s %s" <%s>' % (request.user.first_name, + request.user.last_name, + request.user.email) + msg = EmailMessage( + subject=subject, + body=message, + from_email=from_email, + to=[mlist.name], + headers=headers, + ) + # Attachments + if attachments: + if not isinstance(attachments, list): + attachments = [attachments] + for attach in attachments: + msg.attach(attach.name, attach.read()) + msg.send() diff --git a/hyperkitty/lib/view_helpers.py b/hyperkitty/lib/view_helpers.py new file mode 100644 index 0000000..a27baaf --- /dev/null +++ b/hyperkitty/lib/view_helpers.py @@ -0,0 +1,171 @@ +#-*- 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 . +# +# Author: Aurelien Bompard +# + + +import datetime + +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.utils.timezone import utc + +from hyperkitty.models import ThreadCategory, LastView +from hyperkitty.views.forms import CategoryForm + + +FLASH_MESSAGES = { + "updated-ok": ("success", "The profile was successfully updated."), + "sent-ok": ("success", "The message has been sent successfully."), +} + + +def get_months(store, list_name): + """ Return a dictionnary of years, months for which there are + potentially archives available for a given list (based on the + oldest post on the list). + + :arg list_name, name of the mailing list in which this email + should be searched. + """ + date_first = store.get_start_date(list_name) + if not date_first: + return {} + archives = {} + now = datetime.datetime.now() + year = date_first.year + month = date_first.month + while year < now.year: + archives[year] = range(1, 13)[(month -1):] + year = year + 1 + month = 1 + archives[now.year] = range(1, 13)[:now.month] + return archives + + +def get_display_dates(year, month, day): + if day is None: + start_day = 1 + else: + start_day = int(day) + begin_date = datetime.datetime(int(year), int(month), start_day) + + if day is None: + end_date = begin_date + datetime.timedelta(days=32) + end_date = end_date.replace(day=1) + else: + end_date = begin_date + datetime.timedelta(days=1) + + return begin_date, end_date + + +def daterange(start_date, end_date): + for n in range(int((end_date - start_date).days)): + yield start_date + datetime.timedelta(n) + + +def paginate(objects, page_num, max_page_range=10, paginator=None): + try: + page_num = int(page_num) + except (TypeError, ValueError): + page_num = 1 + if paginator is None: + paginator = Paginator(objects, 10) # else use the provided instance + try: + objects = paginator.page(page_num) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + objects = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + objects = paginator.page(paginator.num_pages) + # Calculate the displayed page range + if paginator.num_pages > max_page_range: + objects.page_range = [ 1 ] + subrange_lower = page_num - (max_page_range / 2 - 2) + if subrange_lower > 3: + objects.page_range.append("...") + else: + subrange_lower = 2 + objects.page_range.extend(range(subrange_lower, page_num)) + if page_num != 1 and page_num != 100: + objects.page_range.append(page_num) + subrange_upper = page_num + (max_page_range / 2 - 2) + if subrange_upper >= paginator.num_pages - 2: + subrange_upper = paginator.num_pages - 1 + objects.page_range.extend(range(page_num+1, subrange_upper+1)) + if subrange_upper < paginator.num_pages - 2: + objects.page_range.append("...") + objects.page_range.append(paginator.num_pages) + else: + objects.page_range = [ p+1 for p in range(paginator.num_pages) ] + return objects + + +def get_category_widget(request=None, current_category=None): + """ + Returns the category form and the applicable category object (or None if no + category is set for this thread). + + If current_category is not provided or None, try to deduce it from the POST + request. + if request is not provided or None, don't return the category form, return + None instead. + """ + categories = [ (c.name, c.name.upper()) + for c in ThreadCategory.objects.all() ] \ + + [("", "no category")] + + if request: + if request.method == "POST": + category_form = CategoryForm(request.POST) + else: + category_form = CategoryForm(initial={"category": current_category or ""}) + category_form["category"].field.choices = categories + else: + category_form = None + if request and request.method == "POST" and category_form.is_valid(): + # is_valid() must be called after the choices have been set + current_category = category_form.cleaned_data["category"] + + if not current_category: + category = None + else: + try: + category = ThreadCategory.objects.get(name=current_category) + except ThreadCategory.DoesNotExist: + category = None + return category, category_form + + +def is_thread_unread(request, mlist_name, thread): + """Returns True or False if the thread is unread or not.""" + unread = False + if request.user.is_authenticated(): + try: + last_view_obj = LastView.objects.get( + list_address=mlist_name, + threadid=thread.thread_id, + user=request.user) + except LastView.DoesNotExist: + unread = True + else: + if thread.date_active.replace(tzinfo=utc) \ + > last_view_obj.view_date: + unread = True + return unread diff --git a/hyperkitty/tests/test_forms.py b/hyperkitty/tests/test_forms.py deleted file mode 100644 index 9a056cd..0000000 --- a/hyperkitty/tests/test_forms.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- 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 . -# -# Author: Aamir Khan -# diff --git a/hyperkitty/tests/test_lib.py b/hyperkitty/tests/test_lib.py index b24f5c4..4b9b778 100644 --- a/hyperkitty/tests/test_lib.py +++ b/hyperkitty/tests/test_lib.py @@ -23,7 +23,7 @@ import datetime from django.test import TestCase -from hyperkitty.lib import get_display_dates, paginate +from hyperkitty.lib.view_helpers import get_display_dates, paginate class GetDisplayDatesTestCase(TestCase): diff --git a/hyperkitty/views/accounts.py b/hyperkitty/views/accounts.py index 3a91c3d..373390b 100644 --- a/hyperkitty/views/accounts.py +++ b/hyperkitty/views/accounts.py @@ -36,7 +36,8 @@ from social_auth.backends import SocialAuthBackend from hyperkitty.models import UserProfile, Rating, Favorite, LastView from hyperkitty.views.forms import RegistrationForm, UserProfileForm -from hyperkitty.lib import get_store, FLASH_MESSAGES, paginate +from hyperkitty.lib import get_store +from hyperkitty.lib.view_helpers import FLASH_MESSAGES, paginate logger = logging.getLogger(__name__) diff --git a/hyperkitty/views/list.py b/hyperkitty/views/list.py index 4857c27..89c113c 100644 --- a/hyperkitty/views/list.py +++ b/hyperkitty/views/list.py @@ -32,9 +32,10 @@ from django.utils.timezone import utc from django.http import Http404 from hyperkitty.models import Tag, Favorite -from hyperkitty.lib import get_months, get_store, get_display_dates, daterange -from hyperkitty.lib import FLASH_MESSAGES, paginate, get_category_widget -from hyperkitty.lib import is_thread_unread +from hyperkitty.lib import get_store +from hyperkitty.lib.view_helpers import FLASH_MESSAGES, paginate, \ + get_category_widget, get_months, get_display_dates, daterange, \ + is_thread_unread from hyperkitty.lib.voting import get_votes, set_message_votes, set_thread_votes diff --git a/hyperkitty/views/message.py b/hyperkitty/views/message.py index 66cc314..6e3a640 100644 --- a/hyperkitty/views/message.py +++ b/hyperkitty/views/message.py @@ -31,7 +31,9 @@ from django.core.exceptions import SuspiciousOperation from django.template import RequestContext, loader from django.contrib.auth.decorators import login_required -from hyperkitty.lib import get_store, get_months, post_to_list, PostingFailed +from hyperkitty.lib import get_store +from hyperkitty.lib.view_helpers import get_months +from hyperkitty.lib.posting import post_to_list, PostingFailed from hyperkitty.lib.voting import set_message_votes from hyperkitty.models import Rating from forms import ReplyForm, PostForm diff --git a/hyperkitty/views/search.py b/hyperkitty/views/search.py index f143876..dbef37d 100644 --- a/hyperkitty/views/search.py +++ b/hyperkitty/views/search.py @@ -24,7 +24,8 @@ from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger, Page from hyperkitty.models import Tag -from hyperkitty.lib import get_store, paginate +from hyperkitty.lib import get_store +from hyperkitty.lib.view_helpers import paginate from hyperkitty.lib.voting import get_votes, set_message_votes from .list import _thread_list diff --git a/hyperkitty/views/thread.py b/hyperkitty/views/thread.py index ecfd6eb..1098302 100644 --- a/hyperkitty/views/thread.py +++ b/hyperkitty/views/thread.py @@ -35,8 +35,8 @@ import robot_detection from hyperkitty.models import Tag, Favorite, LastView, ThreadCategory from hyperkitty.views.forms import AddTagForm, ReplyForm, CategoryForm -from hyperkitty.lib import get_months, get_store, stripped_subject -from hyperkitty.lib import get_category_widget +from hyperkitty.lib import get_store, stripped_subject +from hyperkitty.lib.view_helpers import get_months, get_category_widget from hyperkitty.lib.voting import set_message_votes -- cgit