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.contrib.auth.models import User
6 from django.core.exceptions import ValidationError
7 from django.db import models
8 from django.db.models import permalink
9 from django.utils.translation import ugettext, ugettext_lazy as _
10 from newtagging.models import TagBase
13 # Those are hard-coded here so that makemessages sees them.
15 ('author', _('author')),
16 ('epoch', _('epoch')),
18 ('genre', _('genre')),
19 ('theme', _('theme')),
22 ('thing', _('thing')), # things shown on pictures
27 """A tag attachable to books and fragments (and possibly anything).
29 Used to represent searchable metadata (authors, epochs, genres, kinds),
30 fragment themes (motifs) and some book hierarchy related kludges."""
31 name = models.CharField(_('name'), max_length=50, db_index=True)
32 slug = models.SlugField(_('slug'), max_length=120, db_index=True)
33 sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
34 category = models.CharField(_('category'), max_length=50, blank=False, null=False,
35 db_index=True, choices=TAG_CATEGORIES)
36 description = models.TextField(_('description'), blank=True)
38 user = models.ForeignKey(User, blank=True, null=True)
39 book_count = models.IntegerField(_('book count'), blank=True, null=True)
40 picture_count = models.IntegerField(_('picture count'), blank=True, null=True)
41 gazeta_link = models.CharField(blank=True, max_length=240)
42 wiki_link = models.CharField(blank=True, max_length=240)
44 created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
45 changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
47 class UrlDeprecationWarning(DeprecationWarning):
59 categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
62 ordering = ('sort_key',)
63 verbose_name = _('tag')
64 verbose_name_plural = _('tags')
65 unique_together = (("slug", "category"),)
66 app_label = 'catalogue'
68 def __unicode__(self):
72 return "Tag(slug=%r)" % self.slug
75 def get_absolute_url(self):
76 return ('catalogue.views.tagged_object_list', [self.url_chunk])
79 if self.category == 'book' and (self.gazeta_link or self.wiki_link):
80 raise ValidationError(ugettext(
81 u"Book tags can't have attached links. Set them directly on the book instead of it's tag."))
85 def create_url(cls, category, slug):
86 return ('catalogue.views.tagged_object_list', [
87 '/'.join((cls.categories_dict[category], slug))
90 def has_description(self):
91 return len(self.description) > 0
92 has_description.short_description = _('description')
93 has_description.boolean = True
96 """Returns global book count for book tags, fragment count for themes."""
97 from catalogue.models import Book, Fragment
99 if self.category == 'book':
101 objects = Book.objects.none()
102 elif self.category == 'theme':
103 objects = Fragment.tagged.with_all((self,))
105 objects = Book.tagged.with_all((self,)).order_by()
106 if self.category != 'set':
107 # eliminate descendants
108 l_tags = Tag.objects.filter(slug__in=[book.book_tag_slug() for book in objects.iterator()])
109 descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags).iterator()]
111 objects = objects.exclude(pk__in=descendants_keys)
112 return objects.count()
114 # I shouldn't break the get_count() api
115 # just to include pictures.
116 def get_picture_count(self):
117 from picture.models import Picture
119 if self.category == 'book':
121 objects = Book.objects.none()
122 elif self.category == 'theme':
123 objects = Picture.tagged.with_all((self,))
124 elif self.category == 'thing':
125 objects = Picture.tagged.with_all((self,))
127 objects = Picture.tagged.with_all((self,)).order_by()
128 return objects.count()
131 def get_tag_list(tags):
132 if isinstance(tags, basestring):
137 tags_splitted = tags.split('/')
138 for name in tags_splitted:
140 real_tags.append(Tag.objects.get(slug=name, category=category))
142 elif name in Tag.categories_rev:
143 category = Tag.categories_rev[name]
146 real_tags.append(Tag.objects.exclude(category='book').get(slug=name))
148 except Tag.MultipleObjectsReturned, e:
149 ambiguous_slugs.append(name)
152 # something strange left off
153 raise Tag.DoesNotExist()
155 # some tags should be qualified
156 e = Tag.MultipleObjectsReturned()
158 e.ambiguous_slugs = ambiguous_slugs
161 e = Tag.UrlDeprecationWarning()
166 return TagBase.get_tag_list(tags)
170 return '/'.join((Tag.categories_dict[self.category], self.slug))
173 def tags_from_info(info):
174 from fnpdjango.utils.text.slughifi import slughifi
175 from sortify import sortify
177 categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
178 for field_name, category in categories:
180 tag_names = getattr(info, field_name)
183 tag_names = [getattr(info, category)]
185 # For instance, Pictures do not have 'genre' field.
187 for tag_name in tag_names:
188 tag_sort_key = tag_name
189 if category == 'author':
190 tag_sort_key = tag_name.last_name
191 tag_name = tag_name.readable()
192 tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
195 tag.sort_key = sortify(tag_sort_key.lower())
197 meta_tags.append(tag)