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.
5 from django.conf import settings
6 from django.contrib.auth.models import User
7 from django.core.exceptions import ValidationError
8 from django.db import models
9 from django.db.models import permalink
10 from django.utils.translation import ugettext, ugettext_lazy as _
11 from newtagging.models import TagBase
14 # Those are hard-coded here so that makemessages sees them.
16 ('author', _('author')),
17 ('epoch', _('epoch')),
19 ('genre', _('genre')),
20 ('theme', _('theme')),
23 ('thing', _('thing')), # things shown on pictures
28 """A tag attachable to books and fragments (and possibly anything).
30 Used to represent searchable metadata (authors, epochs, genres, kinds),
31 fragment themes (motifs) and some book hierarchy related kludges."""
32 name = models.CharField(_('name'), max_length=50, db_index=True)
33 slug = models.SlugField(_('slug'), max_length=120, db_index=True)
34 sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
35 category = models.CharField(_('category'), max_length=50, blank=False, null=False,
36 db_index=True, choices=TAG_CATEGORIES)
37 description = models.TextField(_('description'), blank=True)
39 user = models.ForeignKey(User, blank=True, null=True)
40 book_count = models.IntegerField(_('book count'), blank=True, null=True)
41 picture_count = models.IntegerField(_('picture count'), blank=True, null=True)
42 gazeta_link = models.CharField(blank=True, max_length=240)
43 culturepl_link = models.CharField(blank=True, max_length=240)
44 wiki_link = models.CharField(blank=True, max_length=240)
46 created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
47 changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
49 class UrlDeprecationWarning(DeprecationWarning):
61 categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
64 ordering = ('sort_key',)
65 verbose_name = _('tag')
66 verbose_name_plural = _('tags')
67 unique_together = (("slug", "category"),)
68 app_label = 'catalogue'
70 def __unicode__(self):
74 return "Tag(slug=%r)" % self.slug
77 def get_absolute_url(self):
78 return ('catalogue.views.tagged_object_list', [self.url_chunk])
81 if self.category == 'book' and (self.gazeta_link or self.wiki_link):
82 raise ValidationError(ugettext(
83 u"Book tags can't have attached links. Set them directly on the book instead of it's tag."))
87 def create_url(cls, category, slug):
88 return ('catalogue.views.tagged_object_list', [
89 '/'.join((cls.categories_dict[category], slug))
92 def has_description(self):
93 return len(self.description) > 0
94 has_description.short_description = _('description')
95 has_description.boolean = True
98 """Returns global book count for book tags, fragment count for themes."""
99 from catalogue.models import Book, Fragment
101 if self.category == 'book':
103 objects = Book.objects.none()
104 elif self.category == 'theme':
105 objects = Fragment.tagged.with_all((self,))
107 objects = Book.tagged.with_all((self,)).order_by()
108 if self.category != 'set':
109 # eliminate descendants
110 l_tags = Tag.objects.filter(slug__in=[book.book_tag_slug() for book in objects.iterator()])
111 descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags).iterator()]
113 objects = objects.exclude(pk__in=descendants_keys)
114 return objects.count()
116 # I shouldn't break the get_count() api
117 # just to include pictures.
118 def get_picture_count(self):
119 from picture.models import Picture, PictureArea
121 if self.category == 'book':
123 objects = Picture.objects.none()
124 elif self.category == 'theme':
125 objects = PictureArea.tagged.with_all((self,))
126 elif self.category == 'thing':
127 objects = Picture.tagged.with_all((self,))
129 objects = Picture.tagged.with_all((self,)).order_by()
130 return objects.count()
133 def get_tag_list(tags):
134 if isinstance(tags, basestring):
139 tags_splitted = tags.split('/')
140 for name in tags_splitted:
142 real_tags.append(Tag.objects.get(slug=name, category=category))
144 elif name in Tag.categories_rev:
145 category = Tag.categories_rev[name]
148 real_tags.append(Tag.objects.exclude(category='book').get(slug=name))
150 except Tag.MultipleObjectsReturned, e:
151 ambiguous_slugs.append(name)
154 # something strange left off
155 raise Tag.DoesNotExist()
157 # some tags should be qualified
158 e = Tag.MultipleObjectsReturned()
160 e.ambiguous_slugs = ambiguous_slugs
163 e = Tag.UrlDeprecationWarning()
168 return TagBase.get_tag_list(tags)
172 return '/'.join((Tag.categories_dict[self.category], self.slug))
175 def tags_from_info(info):
176 from fnpdjango.utils.text.slughifi import slughifi
177 from sortify import sortify
179 categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
180 for field_name, category in categories:
182 tag_names = getattr(info, field_name)
185 tag_names = [getattr(info, category)]
187 # For instance, Pictures do not have 'genre' field.
189 for tag_name in tag_names:
190 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
191 tag_sort_key = tag_name
192 if category == 'author':
193 tag_sort_key = tag_name.last_name
194 tag_name = tag_name.readable()
195 if lang == settings.LANGUAGE_CODE:
196 # Allow creating new tag, if it's in default language.
197 tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
199 tag_name = unicode(tag_name)
201 setattr(tag, "name_%s" % lang, tag_name)
202 tag.sort_key = sortify(tag_sort_key.lower())
205 meta_tags.append(tag)
207 # Ignore unknown tags in non-default languages.
209 tag = Tag.objects.get(category=category, **{"name_%s" % lang: tag_name})
210 except Tag.DoesNotExist:
213 meta_tags.append(tag)
217 # Pickle complains about not having this.
218 TagRelation = Tag.intermediary_table_model