Display fixes: shelf tags, search results.
[wolnelektury.git] / apps / catalogue / models / tag.py
1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 #
5 from django.conf import settings
6 from django.contrib.auth.models import User
7 from django.db import models
8 from django.db.models import permalink
9 from django.utils.translation import ugettext_lazy as _
10 from newtagging.models import TagBase
11
12
13 # Those are hard-coded here so that makemessages sees them.
14 TAG_CATEGORIES = (
15     ('author', _('author')),
16     ('epoch', _('epoch')),
17     ('kind', _('kind')),
18     ('genre', _('genre')),
19     ('theme', _('theme')),
20     ('set', _('set')),
21     ('thing', _('thing')), # things shown on pictures
22 )
23
24
25 class Tag(TagBase):
26     """A tag attachable to books and fragments (and possibly anything).
27
28     Used to represent searchable metadata (authors, epochs, genres, kinds),
29     fragment themes (motifs) and some book hierarchy related kludges."""
30     name = models.CharField(_('name'), max_length=50, db_index=True)
31     slug = models.SlugField(_('slug'), max_length=120, db_index=True)
32     sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
33     category = models.CharField(_('category'), max_length=50, blank=False, null=False,
34         db_index=True, choices=TAG_CATEGORIES)
35     description = models.TextField(_('description'), blank=True)
36
37     user = models.ForeignKey(User, blank=True, null=True)
38     gazeta_link = models.CharField(blank=True, max_length=240)
39     culturepl_link = models.CharField(blank=True, max_length=240)
40     wiki_link = models.CharField(blank=True, max_length=240)
41
42     created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
43     changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
44
45     class UrlDeprecationWarning(DeprecationWarning):
46         pass
47
48     categories_rev = {
49         'autor': 'author',
50         'epoka': 'epoch',
51         'rodzaj': 'kind',
52         'gatunek': 'genre',
53         'motyw': 'theme',
54         'polka': 'set',
55         'obiekt': 'thing',
56     }
57     categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
58
59     class Meta:
60         ordering = ('sort_key',)
61         verbose_name = _('tag')
62         verbose_name_plural = _('tags')
63         unique_together = (("slug", "category"),)
64         app_label = 'catalogue'
65
66     def __unicode__(self):
67         return self.name
68
69     def __repr__(self):
70         return "Tag(slug=%r)" % self.slug
71
72     @permalink
73     def get_absolute_url(self):
74         return ('catalogue.views.tagged_object_list', [self.url_chunk])
75
76     @classmethod
77     @permalink
78     def create_url(cls, category, slug):
79         return ('catalogue.views.tagged_object_list', [
80                 '/'.join((cls.categories_dict[category], slug))
81             ])
82
83     def has_description(self):
84         return len(self.description) > 0
85     has_description.short_description = _('description')
86     has_description.boolean = True
87
88     @staticmethod
89     def get_tag_list(tags):
90         if isinstance(tags, basestring):
91             real_tags = []
92             ambiguous_slugs = []
93             category = None
94             deprecated = False
95             tags_splitted = tags.split('/')
96             for name in tags_splitted:
97                 if category:
98                     real_tags.append(Tag.objects.get(slug=name, category=category))
99                     category = None
100                 elif name in Tag.categories_rev:
101                     category = Tag.categories_rev[name]
102                 else:
103                     try:
104                         real_tags.append(Tag.objects.get(slug=name))
105                         deprecated = True
106                     except Tag.MultipleObjectsReturned, e:
107                         ambiguous_slugs.append(name)
108
109             if category:
110                 # something strange left off
111                 raise Tag.DoesNotExist()
112             if ambiguous_slugs:
113                 # some tags should be qualified
114                 e = Tag.MultipleObjectsReturned()
115                 e.tags = real_tags
116                 e.ambiguous_slugs = ambiguous_slugs
117                 raise e
118             if deprecated:
119                 e = Tag.UrlDeprecationWarning()
120                 e.tags = real_tags
121                 raise e
122             return real_tags
123         else:
124             return TagBase.get_tag_list(tags)
125
126     @property
127     def url_chunk(self):
128         return '/'.join((Tag.categories_dict[self.category], self.slug))
129
130     @staticmethod
131     def tags_from_info(info):
132         from fnpdjango.utils.text.slughifi import slughifi
133         from sortify import sortify
134         meta_tags = []
135         categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
136         for field_name, category in categories:
137             try:
138                 tag_names = getattr(info, field_name)
139             except:
140                 try:
141                     tag_names = [getattr(info, category)]
142                 except:
143                     # For instance, Pictures do not have 'genre' field.
144                     continue
145             for tag_name in tag_names:
146                 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
147                 tag_sort_key = tag_name
148                 if category == 'author':
149                     tag_sort_key = tag_name.last_name
150                     tag_name = tag_name.readable()
151                 if lang == settings.LANGUAGE_CODE:
152                     # Allow creating new tag, if it's in default language.
153                     tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
154                     if created:
155                         tag_name = unicode(tag_name)
156                         tag.name = tag_name
157                         setattr(tag, "name_%s" % lang, tag_name)
158                         tag.sort_key = sortify(tag_sort_key.lower())
159                         tag.save()
160
161                     meta_tags.append(tag)
162                 else:
163                     # Ignore unknown tags in non-default languages.
164                     try:
165                         tag = Tag.objects.get(category=category, **{"name_%s" % lang: tag_name})
166                     except Tag.DoesNotExist:
167                         pass
168                     else:
169                         meta_tags.append(tag)
170         return meta_tags
171
172
173 # Pickle complains about not having this.
174 TagRelation = Tag.intermediary_table_model