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.core.cache import caches
7 from django.contrib.auth.models import User
8 from django.db import models
9 from django.db.models import permalink
10 from django.dispatch import Signal
11 from django.utils.translation import ugettext_lazy as _
12 from newtagging.models import TagBase
13 from ssify import flush_ssi_includes
16 # Those are hard-coded here so that makemessages sees them.
18 ('author', _('author')),
19 ('epoch', _('epoch')),
21 ('genre', _('genre')),
22 ('theme', _('theme')),
24 ('thing', _('thing')), # things shown on pictures
29 """A tag attachable to books and fragments (and possibly anything).
31 Used to represent searchable metadata (authors, epochs, genres, kinds),
32 fragment themes (motifs) and some book hierarchy related kludges."""
33 name = models.CharField(_('name'), max_length=120, db_index=True)
34 slug = models.SlugField(_('slug'), max_length=120, db_index=True)
35 sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
36 category = models.CharField(
37 _('category'), max_length=50, blank=False, null=False, db_index=True, choices=TAG_CATEGORIES)
38 description = models.TextField(_('description'), blank=True)
40 user = models.ForeignKey(User, blank=True, null=True)
41 gazeta_link = models.CharField(blank=True, max_length=240)
42 culturepl_link = models.CharField(blank=True, max_length=240)
43 wiki_link = models.CharField(blank=True, max_length=240)
45 created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
46 changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
48 after_change = Signal(providing_args=['instance', 'languages'])
50 class UrlDeprecationWarning(DeprecationWarning):
62 categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
65 ordering = ('sort_key',)
66 verbose_name = _('tag')
67 verbose_name_plural = _('tags')
68 unique_together = (("slug", "category"),)
69 app_label = 'catalogue'
71 def save(self, *args, **kwargs):
72 flush_cache = flush_all_includes = False
73 if self.pk and self.category != 'set':
74 # Flush the whole views cache.
75 # Seem a little harsh, but changed tag names, descriptions
76 # and links come up at any number of places.
79 # Find in which languages we need to flush related includes.
80 old_self = type(self).objects.get(pk=self.pk)
81 # Category shouldn't normally be changed, but just in case.
82 if self.category != old_self.category:
83 flush_all_includes = True
84 languages_changed = self.languages_changed(old_self)
86 ret = super(Tag, self).save(*args, **kwargs)
89 caches[settings.CACHE_MIDDLEWARE_ALIAS].clear()
90 if flush_all_includes:
94 self.after_change.send(sender=type(self), instance=self, languages=languages_changed)
98 def languages_changed(self, old):
99 all_langs = [lc for (lc, _ln) in settings.LANGUAGES]
100 if (old.category, old.slug) != (self.category, self.slug):
103 for lang in all_langs:
104 name_field = 'name_%s' % lang
105 if getattr(old, name_field) != getattr(self, name_field):
109 def flush_includes(self, languages=True):
112 if languages is True:
113 languages = [lc for (lc, _ln) in settings.LANGUAGES]
115 template % (self.pk, lang)
117 '/api/include/tag/%d.%s.json',
118 '/api/include/tag/%d.%s.xml',
120 for lang in languages
123 '/katalog/%s.json' % lang for lang in languages])
125 def __unicode__(self):
129 return "Tag(slug=%r)" % self.slug
131 def get_initial(self):
132 if self.category == 'author':
133 return self.sort_key[0]
140 def get_absolute_url(self):
141 return 'tagged_object_list', [self.url_chunk]
144 def get_absolute_gallery_url(self):
145 return 'tagged_object_list_gallery', [self.url_chunk]
149 def create_url(cls, category, slug):
150 return ('catalogue.views.tagged_object_list', [
151 '/'.join((cls.categories_dict[category], slug))
154 def has_description(self):
155 return len(self.description) > 0
156 has_description.short_description = _('description')
157 has_description.boolean = True
160 def get_tag_list(tags):
161 if isinstance(tags, basestring):
168 tags_splitted = tags.split('/')
169 for name in tags_splitted:
171 real_tags.append(Tag.objects.get(slug=name, category=category))
173 elif name in Tag.categories_rev:
174 category = Tag.categories_rev[name]
177 real_tags.append(Tag.objects.get(slug=name))
179 except Tag.MultipleObjectsReturned:
180 ambiguous_slugs.append(name)
183 # something strange left off
184 raise Tag.DoesNotExist()
186 # some tags should be qualified
187 e = Tag.MultipleObjectsReturned()
189 e.ambiguous_slugs = ambiguous_slugs
192 e = Tag.UrlDeprecationWarning()
197 return TagBase.get_tag_list(tags)
201 return '/'.join((Tag.categories_dict[self.category], self.slug))
204 def tags_from_info(info):
205 from fnpdjango.utils.text.slughifi import slughifi
206 from sortify import sortify
208 categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
209 for field_name, category in categories:
211 tag_names = getattr(info, field_name)
214 tag_names = [getattr(info, category)]
216 # For instance, Pictures do not have 'genre' field.
218 for tag_name in tag_names:
219 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
220 tag_sort_key = tag_name
221 if category == 'author':
222 tag_sort_key = ' '.join((tag_name.last_name,) + tag_name.first_names)
223 tag_name = tag_name.readable()
224 if lang == settings.LANGUAGE_CODE:
225 # Allow creating new tag, if it's in default language.
226 tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
228 tag_name = unicode(tag_name)
230 setattr(tag, "name_%s" % lang, tag_name)
231 tag.sort_key = sortify(tag_sort_key.lower())
234 meta_tags.append(tag)
236 # Ignore unknown tags in non-default languages.
238 tag = Tag.objects.get(category=category, **{"name_%s" % lang: tag_name})
239 except Tag.DoesNotExist:
242 meta_tags.append(tag)
246 # Pickle complains about not having this.
247 TagRelation = Tag.intermediary_table_model