# -*- 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 # Author: Aurelien Bompard # import datetime from collections import namedtuple, defaultdict from django.shortcuts import redirect, render from django.conf import settings from django.core.urlresolvers import reverse from django.utils import formats from django.utils.dateformat import format as date_format from django.utils.timezone import utc from django.http import Http404 from hyperkitty.models import Tag, Favorite, LastView 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.voting import get_votes, set_message_votes, set_thread_votes if settings.USE_MOCKUPS: from hyperkitty.lib.mockup import generate_top_author, generate_thread_per_category Thread = namedtuple('Thread', [ "thread_id", "subject", "participants", "length", "date_active", "likes", "dislikes", "likestatus", "category", ]) def archives(request, mlist_fqdn, year=None, month=None, day=None): if year is None and month is None: today = datetime.date.today() return redirect(reverse( 'archives_with_month', kwargs={ "mlist_fqdn": mlist_fqdn, 'year': today.year, 'month': today.month})) begin_date, end_date = get_display_dates(year, month, day) store = get_store(request) mlist = store.get_list(mlist_fqdn) threads = store.get_threads(mlist_fqdn, start=begin_date, end=end_date) if day is None: list_title = date_format(begin_date, "F Y") no_results_text = "for this month" else: #list_title = date_format(begin_date, settings.DATE_FORMAT) list_title = formats.date_format(begin_date) # works with i18n no_results_text = "for this day" extra_context = { 'month': begin_date, 'month_num': begin_date.month, "list_title": list_title.capitalize(), "no_results_text": no_results_text, } return _thread_list(request, mlist, threads, extra_context=extra_context) def _thread_list(request, mlist, threads, template_name='thread_list.html', extra_context={}): if mlist is None: raise Http404("No archived mailing-list by that name.") store = get_store(request) participants = set() for thread in threads: participants.update(thread.participants) # Votes set_thread_votes(thread, request.user) # Favorites thread.favorite = False if request.user.is_authenticated(): try: Favorite.objects.get(list_address=mlist.name, threadid=thread.thread_id, user=request.user) except Favorite.DoesNotExist: pass else: thread.favorite = True # Tags try: thread.tags = Tag.objects.filter(threadid=thread.thread_id, list_address=mlist.name) except Tag.DoesNotExist: thread.tags = [] # Category thread.category_hk, thread.category_form = \ get_category_widget(request, thread.category) # Unread status thread.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: thread.unread = True else: if thread.date_active.replace(tzinfo=utc) \ > last_view_obj.view_date: thread.unread = True threads = paginate(threads, request.GET.get('page')) flash_messages = [] flash_msg = request.GET.get("msg") if flash_msg: flash_msg = { "type": FLASH_MESSAGES[flash_msg][0], "msg": FLASH_MESSAGES[flash_msg][1] } flash_messages.append(flash_msg) context = { 'mlist' : mlist, 'threads': threads, 'participants': len(participants), 'months_list': get_months(store, mlist.name), 'flash_messages': flash_messages, } context.update(extra_context) return render(request, template_name, context) def overview(request, mlist_fqdn=None): if not mlist_fqdn: return redirect('/') # Get stats for last 30 days today = datetime.datetime.utcnow() # the upper boundary is excluded in the search, add one day end_date = today + datetime.timedelta(days=1) begin_date = end_date - datetime.timedelta(days=32) store = get_store(request) mlist = store.get_list(mlist_fqdn) if mlist is None: raise Http404("No archived mailing-list by that name.") threads_result = store.get_threads( list_name=mlist.name, start=begin_date, end=end_date) threads = [] participants = set() for thread_obj in threads_result: # Votes set_thread_votes(thread_obj, request.user) thread = Thread(thread_obj.thread_id, thread_obj.subject, thread_obj.participants, len(thread_obj), thread_obj.date_active.replace(tzinfo=utc), thread_obj.likes, thread_obj.dislikes, thread_obj.likestatus, get_category_widget(None, thread_obj.category)[0], ) # Statistics on how many participants and threads this month participants.update(thread.participants) threads.append(thread) # top threads are the one with the most answers top_threads = sorted(threads, key=lambda t: t.length, reverse=True) # active threads are the ones that have the most recent posting active_threads = sorted(threads, key=lambda t: t.date_active, reverse=True) # top authors are the ones that have the most kudos. How do we determine # that? Most likes for their post? if settings.USE_MOCKUPS: authors = generate_top_author() authors = sorted(authors, key=lambda author: author.kudos) authors.reverse() else: authors = [] # Top posters top_posters = [] for poster in store.get_top_participants(list_name=mlist.name, start=begin_date, end=end_date, limit=5): top_posters.append({"name": poster[0], "email": poster[1], "count": poster[2]}) # Popular threads pop_threads = sorted([ t for t in threads if t.likes - t.dislikes > 0 ], key=lambda t: t.likes - t.dislikes, reverse=True)[:5] # List activity # Use get_messages and not get_threads to count the emails, because # recently active threads include messages from before the start date emails_in_month = store.get_messages(list_name=mlist.name, start=begin_date, end=end_date) dates = defaultdict(lambda: 0) # no activity by default # populate with all days before adding data. for single_date in daterange(begin_date, end_date): dates[single_date.strftime("%Y-%m-%d")] = 0 for email in emails_in_month: date_str = email.date.strftime("%Y-%m-%d") dates[date_str] = dates[date_str] + 1 days = dates.keys() days.sort() evolution = [dates[d] for d in days] if not evolution: evolution.append(0) archives_baseurl = reverse("archives_latest", kwargs={'mlist_fqdn': mlist.name}) archives_baseurl = archives_baseurl.rpartition("/")[0] # Threads by category threads_by_category = {} for thread in active_threads: if not thread.category: continue if len(threads_by_category.setdefault(thread.category, [])) > 5: continue threads_by_category[thread.category].append(thread) context = { 'mlist' : mlist, 'top_threads': top_threads[:5], 'most_active_threads': active_threads[:5], 'top_author': authors, 'top_posters': top_posters, 'pop_threads': pop_threads, 'threads_by_category': threads_by_category, 'months_list': get_months(store, mlist.name), 'evolution': evolution, 'days': days, 'archives_baseurl': archives_baseurl, } return render(request, "overview.html", context)