zinnia.templatetags.zinnia_tags
Covered: 189 lines
Missed: 5 lines
Skipped 59 lines
Percent: 97 %
  1
"""Template tags and filters for Zinnia"""
  2
try:
  3
    from hashlib import md5
  4
except ImportError:
  5
    from md5 import new as md5
  7
from random import sample
  8
from urllib import urlencode
  9
from datetime import datetime
 11
from django.db import connection
 12
from django.template import Library
 13
from django.contrib.comments.models import Comment
 14
from django.contrib.contenttypes.models import ContentType
 15
from django.utils.encoding import smart_unicode
 17
from zinnia.models import Entry
 18
from zinnia.models import Author
 19
from zinnia.models import Category
 20
from zinnia.comparison import VectorBuilder
 21
from zinnia.comparison import pearson_score
 22
from zinnia.templatetags.zbreadcrumbs import retrieve_breadcrumbs
 24
register = Library()
 26
VECTORS = None
 27
VECTORS_FACTORY = lambda: VectorBuilder({'queryset': Entry.published.all(),
 28
                                         'fields': ['title', 'excerpt',
 29
                                                    'content']})
 30
CACHE_ENTRIES_RELATED = {}
 33
@register.inclusion_tag('zinnia/tags/dummy.html')
 34
def get_categories(template='zinnia/tags/categories.html'):
 35
    """Return the categories"""
 36
    return {'template': template,
 37
            'categories': Category.tree.all()}
 40
@register.inclusion_tag('zinnia/tags/dummy.html')
 41
def get_authors(template='zinnia/tags/authors.html'):
 42
    """Return the published authors"""
 43
    return {'template': template,
 44
            'authors': Author.published.all()}
 47
@register.inclusion_tag('zinnia/tags/dummy.html')
 48
def get_recent_entries(number=5, template='zinnia/tags/recent_entries.html'):
 49
    """Return the most recent entries"""
 50
    return {'template': template,
 51
            'entries': Entry.published.all()[:number]}
 54
@register.inclusion_tag('zinnia/tags/dummy.html')
 55
def get_featured_entries(number=5, template='zinnia/tags/featured_entries.html'):
 56
    """Return the featured entries"""
 57
    return {'template': template,
 58
            'entries': Entry.published.filter(featured=True)[:number]}
 61
@register.inclusion_tag('zinnia/tags/dummy.html')
 62
def get_random_entries(number=5, template='zinnia/tags/random_entries.html'):
 63
    """Return random entries"""
 64
    entries = Entry.published.all()
 65
    if number > len(entries):
 66
        number = len(entries)
 67
    return {'template': template,
 68
            'entries': sample(entries, number)}
 71
@register.inclusion_tag('zinnia/tags/dummy.html')
 72
def get_popular_entries(number=5, template='zinnia/tags/popular_entries.html'):
 73
    """Return popular  entries"""
 74
    ctype = ContentType.objects.get_for_model(Entry)
 75
    query = """SELECT object_pk, COUNT(*) AS score
 76
    FROM %s
 77
    WHERE content_type_id = %%s
 78
    AND is_public = '1'
 79
    GROUP BY object_pk
 80
    ORDER BY score DESC""" % Comment._meta.db_table
 82
    cursor = connection.cursor()
 83
    cursor.execute(query, [ctype.id])
 84
    object_ids = [int(row[0]) for row in cursor.fetchall()]
 88
    object_dict = Entry.published.in_bulk(object_ids)
 90
    return {'template': template,
 91
            'entries': [object_dict[object_id]
 92
                        for object_id in object_ids
 93
                        if object_id in object_dict][:number]}
 96
@register.inclusion_tag('zinnia/tags/dummy.html', takes_context=True)
 97
def get_similar_entries(context, number=5,
 98
                        template='zinnia/tags/similar_entries.html',
 99
                        flush=False):
100
    """Return similar entries"""
101
    global VECTORS
102
    global CACHE_ENTRIES_RELATED
104
    if VECTORS is None or flush:
105
        VECTORS = VECTORS_FACTORY()
106
        CACHE_ENTRIES_RELATED = {}
108
    def compute_related(object_id, dataset):
109
        """Compute related entries to an entry with a dataset"""
110
        object_vector = None
111
        for entry, e_vector in dataset.items():
112
            if entry.pk == object_id:
113
                object_vector = e_vector
115
        if not object_vector:
116
            return []
118
        entry_related = {}
119
        for entry, e_vector in dataset.items():
120
            if entry.pk != object_id:
121
                score = pearson_score(object_vector, e_vector)
122
                if score:
123
                    entry_related[entry] = score
125
        related = sorted(entry_related.items(), key=lambda(k, v): (v, k))
126
        return [rel[0] for rel in related]
128
    object_id = context['object'].pk
129
    columns, dataset = VECTORS()
130
    key = '%s-%s' % (object_id, VECTORS.key)
131
    if not key in CACHE_ENTRIES_RELATED.keys():
132
        CACHE_ENTRIES_RELATED[key] = compute_related(object_id, dataset)
134
    entries = CACHE_ENTRIES_RELATED[key][:number]
135
    return {'template': template,
136
            'entries': entries}
139
@register.inclusion_tag('zinnia/tags/dummy.html')
140
def get_archives_entries(template='zinnia/tags/archives_entries.html'):
141
    """Return archives entries"""
142
    return {'template': template,
143
            'archives': Entry.published.dates('creation_date', 'month',
144
                                              order='DESC')}
147
@register.inclusion_tag('zinnia/tags/dummy.html')
148
def get_archives_entries_tree(
149
    template='zinnia/tags/archives_entries_tree.html'):
150
    """Return archives entries as a Tree"""
151
    return {'template': template,
152
            'archives': Entry.published.dates('creation_date', 'day',
153
                                              order='ASC')}
156
@register.inclusion_tag('zinnia/tags/dummy.html', takes_context=True)
157
def get_calendar_entries(context, year=None, month=None,
158
                         template='zinnia/tags/calendar.html'):
159
    """Return an HTML calendar of entries"""
160
    if not year or not month:
161
        date_month = context.get('month') or context.get('day') or \
162
                     getattr(context.get('object'), 'creation_date', None) or \
163
                     datetime.today()
164
        year, month = date_month.timetuple()[:2]
166
    try:
167
        from zinnia.templatetags.zcalendar import ZinniaCalendar
168
    except ImportError:
169
        return {'calendar':
170
                '<p class="notice">Calendar is unavailable for Python<2.5.</p>'}
172
    calendar = ZinniaCalendar()
173
    current_month = datetime(year, month, 1)
175
    dates = list(Entry.published.dates('creation_date', 'month'))
177
    if not current_month in dates:
178
        dates.append(current_month)
179
        dates.sort()
180
    index = dates.index(current_month)
182
    previous_month = index > 0 and dates[index - 1] or None
183
    next_month = index != len(dates) - 1 and dates[index + 1] or None
185
    return {'template': template,
186
            'next_month': next_month,
187
            'previous_month': previous_month,
188
            'calendar': calendar.formatmonth(year, month)}
191
@register.inclusion_tag('zinnia/tags/dummy.html')
192
def get_recent_comments(number=5, template='zinnia/tags/recent_comments.html'):
193
    """Return the most recent comments"""
195
    entry_published_pks = map(smart_unicode,
196
                              Entry.published.values_list('id', flat=True))
197
    content_type = ContentType.objects.get_for_model(Entry)
199
    comments = Comment.objects.filter(
200
        content_type=content_type,
201
        object_pk__in=entry_published_pks,
202
        flags__flag=None, is_public=True).order_by(
203
        '-submit_date')[:number]
205
    return {'template': template,
206
            'comments': comments}
209
@register.inclusion_tag('zinnia/tags/dummy.html')
210
def get_recent_linkbacks(number=5,
211
                         template='zinnia/tags/recent_linkbacks.html'):
212
    """Return the most recent linkbacks"""
213
    entry_published_pks = map(smart_unicode,
214
                              Entry.published.values_list('id', flat=True))
215
    content_type = ContentType.objects.get_for_model(Entry)
217
    linkbacks = Comment.objects.filter(
218
        content_type=content_type,
219
        object_pk__in=entry_published_pks,
220
        flags__flag__in=['pingback', 'trackback'],
221
        is_public=True).order_by(
222
        '-submit_date')[:number]
224
    return {'template': template,
225
            'linkbacks': linkbacks}
228
@register.inclusion_tag('zinnia/tags/dummy.html', takes_context=True)
229
def zinnia_breadcrumbs(context, separator='/', root_name='Blog',
230
                       template='zinnia/tags/breadcrumbs.html',):
231
    """Return a breadcrumb for the application"""
232
    path = context['request'].path
233
    page_object = context.get('object') or context.get('category') or \
234
                  context.get('tag') or context.get('author')
235
    breadcrumbs = retrieve_breadcrumbs(path, page_object, root_name)
237
    return {'template': template,
238
            'separator': separator,
239
            'breadcrumbs': breadcrumbs}
242
@register.simple_tag
243
def get_gravatar(email, size=80, rating='g', default=None):
244
    """Return url for a Gravatar"""
245
    url = 'http://www.gravatar.com/avatar/%s.jpg' % \
246
          md5(email.strip().lower()).hexdigest()
247
    options = {'s': size, 'r': rating}
248
    if default:
249
        options['d'] = default
251
    url = '%s?%s' % (url, urlencode(options))
252
    return url.replace('&', '&amp;')