1
"""Template tags and filters for Zinnia"""
3
from hashlib import md5
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
27
VECTORS_FACTORY = lambda: VectorBuilder({'queryset': Entry.published.all(),
28
'fields': ['title', 'excerpt',
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):
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
77
WHERE content_type_id = %%s
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()]
86
# Use ``in_bulk`` here instead of an ``id__in`` filter, because ``id__in``
87
# would clobber the ordering.
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',
100
"""Return similar entries"""
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"""
111
for entry, e_vector in dataset.items():
112
if entry.pk == object_id:
113
object_vector = e_vector
115
if not object_vector:
119
for entry, e_vector in dataset.items():
120
if entry.pk != object_id:
121
score = pearson_score(object_vector, e_vector)
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,
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',
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',
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 \
164
year, month = date_month.timetuple()[:2]
167
from zinnia.templatetags.zcalendar import ZinniaCalendar
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)
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"""
194
# Using map(smart_unicode... fix bug related to issue #8554
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}
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}
249
options['d'] = default
251
url = '%s?%s' % (url, urlencode(options))
252
return url.replace('&', '&')