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(_('category'), max_length=50, blank=False, null=False,
37 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):
162 if not tags: return []
167 tags_splitted = tags.split('/')
168 for name in tags_splitted:
170 real_tags.append(Tag.objects.get(slug=name, category=category))
172 elif name in Tag.categories_rev:
173 category = Tag.categories_rev[name]
176 real_tags.append(Tag.objects.get(slug=name))
178 except Tag.MultipleObjectsReturned, e:
179 ambiguous_slugs.append(name)
182 # something strange left off
183 raise Tag.DoesNotExist()
185 # some tags should be qualified
186 e = Tag.MultipleObjectsReturned()
188 e.ambiguous_slugs = ambiguous_slugs
191 e = Tag.UrlDeprecationWarning()
196 return TagBase.get_tag_list(tags)
200 return '/'.join((Tag.categories_dict[self.category], self.slug))
203 def tags_from_info(info):
204 from fnpdjango.utils.text.slughifi import slughifi
205 from sortify import sortify
207 categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
208 for field_name, category in categories:
210 tag_names = getattr(info, field_name)
213 tag_names = [getattr(info, category)]
215 # For instance, Pictures do not have 'genre' field.
217 for tag_name in tag_names:
218 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
219 tag_sort_key = tag_name
220 if category == 'author':
221 tag_sort_key = tag_name.last_name
222 tag_name = tag_name.readable()
223 if lang == settings.LANGUAGE_CODE:
224 # Allow creating new tag, if it's in default language.
225 tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
227 tag_name = unicode(tag_name)
229 setattr(tag, "name_%s" % lang, tag_name)
230 tag.sort_key = sortify(tag_sort_key.lower())
233 meta_tags.append(tag)
235 # Ignore unknown tags in non-default languages.
237 tag = Tag.objects.get(category=category, **{"name_%s" % lang: tag_name})
238 except Tag.DoesNotExist:
241 meta_tags.append(tag)
245 # Pickle complains about not having this.
246 TagRelation = Tag.intermediary_table_model