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 _
13 from newtagging.models import TagBase
14 from ssify import flush_ssi_includes
17 # Those are hard-coded here so that makemessages sees them.
19 ('author', _('author')),
20 ('epoch', _('epoch')),
22 ('genre', _('genre')),
23 ('theme', _('theme')),
25 ('thing', _('thing')), # things shown on pictures
30 """A tag attachable to books and fragments (and possibly anything).
32 Used to represent searchable metadata (authors, epochs, genres, kinds),
33 fragment themes (motifs) and some book hierarchy related kludges."""
34 name = models.CharField(_('name'), max_length=120, db_index=True)
35 slug = models.SlugField(_('slug'), max_length=120, db_index=True)
36 sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
37 category = models.CharField(
38 _('category'), max_length=50, blank=False, null=False, db_index=True, choices=TAG_CATEGORIES)
39 description = models.TextField(_('description'), blank=True)
41 user = models.ForeignKey(User, 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 after_change = Signal(providing_args=['instance', 'languages'])
51 class UrlDeprecationWarning(DeprecationWarning):
52 def __init__(self, tags=None):
53 super(Tag.UrlDeprecationWarning, self).__init__()
65 categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
68 ordering = ('sort_key',)
69 verbose_name = _('tag')
70 verbose_name_plural = _('tags')
71 unique_together = (("slug", "category"),)
72 app_label = 'catalogue'
74 def save(self, *args, **kwargs):
75 flush_cache = flush_all_includes = False
76 if self.pk and self.category != 'set':
77 # Flush the whole views cache.
78 # Seem a little harsh, but changed tag names, descriptions
79 # and links come up at any number of places.
82 # Find in which languages we need to flush related includes.
83 old_self = type(self).objects.get(pk=self.pk)
84 # Category shouldn't normally be changed, but just in case.
85 if self.category != old_self.category:
86 flush_all_includes = True
87 languages_changed = self.languages_changed(old_self)
89 ret = super(Tag, self).save(*args, **kwargs)
92 caches[settings.CACHE_MIDDLEWARE_ALIAS].clear()
93 if flush_all_includes:
97 self.after_change.send(sender=type(self), instance=self, languages=languages_changed)
101 def languages_changed(self, old):
102 all_langs = [lc for (lc, _ln) in settings.LANGUAGES]
103 if (old.category, old.slug) != (self.category, self.slug):
106 for lang in all_langs:
107 name_field = 'name_%s' % lang
108 if getattr(old, name_field) != getattr(self, name_field):
112 def flush_includes(self, languages=True):
115 if languages is True:
116 languages = [lc for (lc, _ln) in settings.LANGUAGES]
118 template % (self.pk, lang)
120 '/api/include/tag/%d.%s.json',
121 '/api/include/tag/%d.%s.xml',
123 for lang in languages
126 '/katalog/%s.json' % lang for lang in languages])
128 def __unicode__(self):
132 return "Tag(slug=%r)" % self.slug
134 def get_initial(self):
135 if self.category == 'author':
136 return self.sort_key[0]
143 def get_absolute_url(self):
144 return 'tagged_object_list', [self.url_chunk]
147 def get_absolute_gallery_url(self):
148 return 'tagged_object_list_gallery', [self.url_chunk]
152 def create_url(cls, category, slug):
153 return ('catalogue.views.tagged_object_list', [
154 '/'.join((cls.categories_dict[category], slug))
157 def has_description(self):
158 return len(self.description) > 0
159 has_description.short_description = _('description')
160 has_description.boolean = True
163 def get_tag_list(tag_str):
164 if isinstance(tag_str, basestring):
171 tags_splitted = tag_str.split('/')
172 for name in tags_splitted:
174 tags.append(Tag.objects.get(slug=name, category=category))
176 elif name in Tag.categories_rev:
177 category = Tag.categories_rev[name]
180 tags.append(Tag.objects.get(slug=name))
182 except Tag.MultipleObjectsReturned:
183 ambiguous_slugs.append(name)
186 # something strange left off
187 raise Tag.DoesNotExist()
189 # some tags should be qualified
190 e = Tag.MultipleObjectsReturned()
192 e.ambiguous_slugs = ambiguous_slugs
195 raise Tag.UrlDeprecationWarning(tags=tags)
198 return TagBase.get_tag_list(tag_str)
202 return '/'.join((Tag.categories_dict[self.category], self.slug))
205 def tags_from_info(info):
206 from fnpdjango.utils.text.slughifi import slughifi
207 from sortify import sortify
209 categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
210 for field_name, category in categories:
212 tag_names = getattr(info, field_name)
215 tag_names = [getattr(info, category)]
217 # For instance, Pictures do not have 'genre' field.
219 for tag_name in tag_names:
220 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
221 tag_sort_key = tag_name
222 if category == 'author':
223 tag_sort_key = ' '.join((tag_name.last_name,) + tag_name.first_names)
224 tag_name = tag_name.readable()
225 if lang == settings.LANGUAGE_CODE:
226 # Allow creating new tag, if it's in default language.
227 tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
229 tag_name = unicode(tag_name)
231 setattr(tag, "name_%s" % lang, tag_name)
232 tag.sort_key = sortify(tag_sort_key.lower())
235 meta_tags.append(tag)
237 # Ignore unknown tags in non-default languages.
239 tag = Tag.objects.get(category=category, **{"name_%s" % lang: tag_name})
240 except Tag.DoesNotExist:
243 meta_tags.append(tag)
247 # Pickle complains about not having this.
248 TagRelation = Tag.intermediary_table_model