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 gazeta_link = models.CharField(blank=True, max_length=240)
41 wiki_link = models.CharField(blank=True, max_length=240)
43 created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
44 changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
46 class UrlDeprecationWarning(DeprecationWarning):
57 categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
60 ordering = ('sort_key',)
61 verbose_name = _('tag')
62 verbose_name_plural = _('tags')
63 unique_together = (("slug", "category"),)
64 app_label = 'catalogue'
66 def __unicode__(self):
70 return "Tag(slug=%r)" % self.slug
73 def get_absolute_url(self):
74 return ('catalogue.views.tagged_object_list', [self.url_chunk])
77 if self.category == 'book' and (self.gazeta_link or self.wiki_link):
78 raise ValidationError(ugettext(
79 u"Book tags can't have attached links. Set them directly on the book instead of it's tag."))
83 def create_url(cls, category, slug):
84 return ('catalogue.views.tagged_object_list', [
85 '/'.join((cls.categories_dict[category], slug))
88 def has_description(self):
89 return len(self.description) > 0
90 has_description.short_description = _('description')
91 has_description.boolean = True
94 """Returns global book count for book tags, fragment count for themes."""
95 from catalogue.models import Book, Fragment
97 if self.category == 'book':
99 objects = Book.objects.none()
100 elif self.category == 'theme':
101 objects = Fragment.tagged.with_all((self,))
103 objects = Book.tagged.with_all((self,)).order_by()
104 if self.category != 'set':
105 # eliminate descendants
106 l_tags = Tag.objects.filter(slug__in=[book.book_tag_slug() for book in objects.iterator()])
107 descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags).iterator()]
109 objects = objects.exclude(pk__in=descendants_keys)
110 return objects.count()
113 def get_tag_list(tags):
114 if isinstance(tags, basestring):
119 tags_splitted = tags.split('/')
120 for name in tags_splitted:
122 real_tags.append(Tag.objects.get(slug=name, category=category))
124 elif name in Tag.categories_rev:
125 category = Tag.categories_rev[name]
128 real_tags.append(Tag.objects.exclude(category='book').get(slug=name))
130 except Tag.MultipleObjectsReturned, e:
131 ambiguous_slugs.append(name)
134 # something strange left off
135 raise Tag.DoesNotExist()
137 # some tags should be qualified
138 e = Tag.MultipleObjectsReturned()
140 e.ambiguous_slugs = ambiguous_slugs
143 e = Tag.UrlDeprecationWarning()
148 return TagBase.get_tag_list(tags)
152 return '/'.join((Tag.categories_dict[self.category], self.slug))
155 def tags_from_info(info):
156 from fnpdjango.utils.text.slughifi import slughifi
157 from sortify import sortify
159 categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
160 for field_name, category in categories:
162 tag_names = getattr(info, field_name)
165 tag_names = [getattr(info, category)]
167 # For instance, Pictures do not have 'genre' field.
169 for tag_name in tag_names:
170 tag_sort_key = tag_name
171 if category == 'author':
172 tag_sort_key = tag_name.last_name
173 tag_name = tag_name.readable()
174 tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
177 tag.sort_key = sortify(tag_sort_key.lower())
179 meta_tags.append(tag)