Django - logarytmiczna chmura tagów

Data publikacji: 2013-05-22 | Tagi:

Mając już działający system tagów na stronie możemy (i zwykle chcemy) pokusić się o wyświetlenie chmury tagów.

Chmura tagów to zbiór tagów, wyświetlony gdzieś na stronie www, dla którego charakterystyczną cechą jest zróżnicowanie wielkości czcionki w zależności od ilości wystąpień danego tagu. Innymi słowy - im częściej występuje dany tag, tym większa czcionka.

Zwykle pierwsze próby stworzenia takiej chmury prezentują podejście liniowe - czyli wielkość czcionki rośnie liniowo w zależności od częstotliwości występowania.

Jest to jakieś rozwiązanie, ale nie należy do najlepszych, taka chmura nie jest ładna: pojawia się zbyt dużo niewielkich tagów i kilka dużych.

W takim wypadku o wiele lepiej sprawdza się zależność logarytmiczna. Chmura tagów wygląda nieco "soczyściej" i jest większe zrównoważenie różnych wielkości czcionek.

Oto gotowy kod:

# -*- coding: utf-8 -*-
import math
from operator import attrgetter
from pages.models import Page
from django import template
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.db.models import Count
from taggit.models import Tag, TaggedItem

register = template.Library()


T_MAX = getattr(settings, 'TAGCLOUD_MAX', 18.0)
T_MIN = getattr(settings, 'TAGCLOUD_MIN', 9.0)


def prepare_engine(min_count, max_count, min_size, max_size):
    cparameter = (math.log(max_count - (min_count - 1)) / (max_size - min_size or 1) or 1)

    def weight_worker(count):
        return math.log(count - (min_count - 1)) / cparameter + min_size
    return weight_worker


def get_cloud(queryset, limit):
    pages_ct = ContentType.objects.get_for_model(Page)
    tag_ids = set(TaggedItem.objects.filter(content_type=pages_ct, object_id__in=queryset).values_list('tag', flat=True))
    tags = Tag.objects.filter(pk__in=tag_ids).annotate(num_times=Count('taggit_taggeditem_items')).order_by('-num_times')[:limit]
    num_times = tags.values_list('num_times', flat=True)
    if(len(num_times) == 0):
        return ""
    tag_worker = prepare_engine(min(num_times), max(num_times), T_MIN, T_MAX)
    for tag in tags:
        tag.weight = tag_worker(tag.num_times)
    tags = sorted(tags, key=attrgetter('slug'))
    return tags


@register.inclusion_tag('pages/blocks/tag_cloud.html')
def tag_cloud(limit=20):
    pages_queryset = Page.objects.published()
    tags = get_cloud(pages_queryset, limit)
    return {'tags': tags,
            'title': "Najpopularniejsze tagi"}

Analiza nie powinna nastręczać większych problemów.

Template tag tag_cloud pobiera queryset obiektów (w tym przypadku z modelu Page) i oddaje pałeczkę funkcji get_cloud. Ta z kolei, bazując na aplikacji TaggIt, pobiera ograniczoną ilość najczęściej powtarzających się tagów. Następnie dla każdego tagu obliczana jest jego waga na podstawie ilości jego wystąpień, maksymalnej i minimalnej ilości wystąpień danego tagu oraz maksymalnej i minimalnej wysokości czcionki. Do templatetaga tag_cloud zwracana jest już posortowana alfabetycznie lista tagów łącznie z wagą.

Przykładowa templatka renderująca chmurę może wyglądać np tak:

{% load url from future %}
<div class="holder">
    <h2>{{ title }}</h2>
    {% for tag in tags %}
        <a href="{% url "tag" tag.slug %}" rel="tag" title="{{ tag }}"><span style="font-size:{{ tag.weight|floatformat:0 }}pt;">{{ tag }}{% if not forloop.last %}, {% endif %}</span></a>
    {% endfor %} 
</div>


Oceń ten post:
Podziel się:

comments powered by Disqus

IT w obrazkach: