Almost ready.
[wolnelektury.git] / src / catalogue / models / tag.py
index 830f29f..3c2a536 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
@@ -9,13 +8,12 @@ from django.core.cache import caches
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
-from django.db.models import permalink
 from django.db.models.query import Prefetch
 from django.dispatch import Signal
 from django.db.models.query import Prefetch
 from django.dispatch import Signal
+from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
 from django.utils.translation import ugettext_lazy as _
 
-from newtagging.models import TagBase
-from ssify import flush_ssi_includes
+from newtagging.models import TagManager, TaggedItemManager
 
 
 # Those are hard-coded here so that makemessages sees them.
 
 
 # Those are hard-coded here so that makemessages sees them.
@@ -32,23 +30,25 @@ TAG_CATEGORIES = (
 
 class TagRelation(models.Model):
 
 
 class TagRelation(models.Model):
 
-    tag = models.ForeignKey('Tag', verbose_name=_('tag'), related_name='items')
-    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
+    tag = models.ForeignKey('Tag', models.CASCADE, verbose_name=_('tag'), related_name='items')
+    content_type = models.ForeignKey(ContentType, models.CASCADE, verbose_name=_('content type'))
     object_id = models.PositiveIntegerField(_('object id'), db_index=True)
     content_object = GenericForeignKey('content_type', 'object_id')
 
     object_id = models.PositiveIntegerField(_('object id'), db_index=True)
     content_object = GenericForeignKey('content_type', 'object_id')
 
+    objects = TaggedItemManager()
+
     class Meta:
         db_table = 'catalogue_tag_relation'
         unique_together = (('tag', 'content_type', 'object_id'),)
 
     class Meta:
         db_table = 'catalogue_tag_relation'
         unique_together = (('tag', 'content_type', 'object_id'),)
 
-    def __unicode__(self):
+    def __str__(self):
         try:
         try:
-            return u'%s [%s]' % (self.content_type.get_object_for_this_type(pk=self.object_id), self.tag)
+            return '%s [%s]' % (self.content_type.get_object_for_this_type(pk=self.object_id), self.tag)
         except ObjectDoesNotExist:
         except ObjectDoesNotExist:
-            return u'<deleted> [%s]' % self.tag
+            return '<deleted> [%s]' % self.tag
 
 
 
 
-class Tag(TagBase):
+class Tag(models.Model):
     """A tag attachable to books and fragments (and possibly anything).
 
     Used to represent searchable metadata (authors, epochs, genres, kinds),
     """A tag attachable to books and fragments (and possibly anything).
 
     Used to represent searchable metadata (authors, epochs, genres, kinds),
@@ -63,17 +63,20 @@ class Tag(TagBase):
     for_books = models.BooleanField(default=False)
     for_pictures = models.BooleanField(default=False)
 
     for_books = models.BooleanField(default=False)
     for_pictures = models.BooleanField(default=False)
 
-    user = models.ForeignKey(User, blank=True, null=True)
+    user = models.ForeignKey(User, models.CASCADE, blank=True, null=True)
     gazeta_link = models.CharField(blank=True, max_length=240)
     culturepl_link = models.CharField(blank=True, max_length=240)
     wiki_link = models.CharField(blank=True, max_length=240)
     gazeta_link = models.CharField(blank=True, max_length=240)
     culturepl_link = models.CharField(blank=True, max_length=240)
     wiki_link = models.CharField(blank=True, max_length=240)
+    photo = models.FileField(blank=True, null=True, upload_to='catalogue/tag/')
+    photo_attribution = models.CharField(max_length=255, blank=True)
 
     created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
     changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
 
 
     created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
     changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
 
-    after_change = Signal(providing_args=['instance', 'languages'])
+    after_change = Signal(providing_args=['instance'])
 
     intermediary_table_model = TagRelation
 
     intermediary_table_model = TagRelation
+    objects = TagManager()
 
     class UrlDeprecationWarning(DeprecationWarning):
         def __init__(self, tags=None):
 
     class UrlDeprecationWarning(DeprecationWarning):
         def __init__(self, tags=None):
@@ -89,7 +92,7 @@ class Tag(TagBase):
         'polka': 'set',
         'obiekt': 'thing',
     }
         'polka': 'set',
         'obiekt': 'thing',
     }
-    categories_dict = dict((item[::-1] for item in categories_rev.iteritems()))
+    categories_dict = dict((item[::-1] for item in categories_rev.items()))
 
     class Meta:
         ordering = ('sort_key',)
 
     class Meta:
         ordering = ('sort_key',)
@@ -99,60 +102,13 @@ class Tag(TagBase):
         app_label = 'catalogue'
 
     def save(self, *args, **kwargs):
         app_label = 'catalogue'
 
     def save(self, *args, **kwargs):
-        flush_cache = flush_all_includes = False
-        if self.pk and self.category != 'set':
-            # Flush the whole views cache.
-            # Seem a little harsh, but changed tag names, descriptions
-            # and links come up at any number of places.
-            flush_cache = True
-
-            # Find in which languages we need to flush related includes.
-            old_self = type(self).objects.get(pk=self.pk)
-            # Category shouldn't normally be changed, but just in case.
-            if self.category != old_self.category:
-                flush_all_includes = True
-            languages_changed = self.languages_changed(old_self)
-
+        existing = self.pk and self.category != 'set'
         ret = super(Tag, self).save(*args, **kwargs)
         ret = super(Tag, self).save(*args, **kwargs)
-
-        if flush_cache:
-            caches[settings.CACHE_MIDDLEWARE_ALIAS].clear()
-            if flush_all_includes:
-                flush_ssi_includes()
-            else:
-                self.flush_includes()
-            self.after_change.send(sender=type(self), instance=self, languages=languages_changed)
-
+        if existing:
+            self.after_change.send(sender=type(self), instance=self)
         return ret
 
         return ret
 
-    def languages_changed(self, old):
-        all_langs = [lc for (lc, _ln) in settings.LANGUAGES]
-        if (old.category, old.slug) != (self.category, self.slug):
-            return all_langs
-        languages = set()
-        for lang in all_langs:
-            name_field = 'name_%s' % lang
-            if getattr(old, name_field) != getattr(self, name_field):
-                languages.add(lang)
-        return languages
-
-    def flush_includes(self, languages=True):
-        if not languages:
-            return
-        if languages is True:
-            languages = [lc for (lc, _ln) in settings.LANGUAGES]
-        flush_ssi_includes([
-            template % (self.pk, lang)
-            for template in [
-                '/api/include/tag/%d.%s.json',
-                '/api/include/tag/%d.%s.xml',
-                ]
-            for lang in languages
-            ])
-        flush_ssi_includes([
-            '/katalog/%s.json' % lang for lang in languages])
-
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     def __repr__(self):
         return self.name
 
     def __repr__(self):
@@ -166,20 +122,15 @@ class Tag(TagBase):
         else:
             return ''
 
         else:
             return ''
 
-    @permalink
+    @property
+    def category_plural(self):
+        return self.category + 's'
+
     def get_absolute_url(self):
     def get_absolute_url(self):
-        return 'tagged_object_list', [self.url_chunk]
+        return reverse('tagged_object_list', args=[self.url_chunk])
 
 
-    @permalink
     def get_absolute_gallery_url(self):
     def get_absolute_gallery_url(self):
-        return 'tagged_object_list_gallery', [self.url_chunk]
-
-    @classmethod
-    @permalink
-    def create_url(cls, category, slug):
-        return ('catalogue.views.tagged_object_list', [
-            '/'.join((cls.categories_dict[category], slug))
-        ])
+        return reverse('tagged_object_list_gallery', args=[self.url_chunk])
 
     def has_description(self):
         return len(self.description) > 0
 
     def has_description(self):
         return len(self.description) > 0
@@ -188,41 +139,38 @@ class Tag(TagBase):
 
     @staticmethod
     def get_tag_list(tag_str):
 
     @staticmethod
     def get_tag_list(tag_str):
-        if isinstance(tag_str, basestring):
-            if not tag_str:
-                return []
-            tags = []
-            ambiguous_slugs = []
-            category = None
-            deprecated = False
-            tags_splitted = tag_str.split('/')
-            for name in tags_splitted:
-                if category:
-                    tags.append(Tag.objects.get(slug=name, category=category))
-                    category = None
-                elif name in Tag.categories_rev:
-                    category = Tag.categories_rev[name]
-                else:
-                    try:
-                        tags.append(Tag.objects.get(slug=name))
-                        deprecated = True
-                    except Tag.MultipleObjectsReturned:
-                        ambiguous_slugs.append(name)
-
+        if not tag_str:
+            return []
+        tags = []
+        ambiguous_slugs = []
+        category = None
+        deprecated = False
+        tags_splitted = tag_str.split('/')
+        for name in tags_splitted:
             if category:
             if category:
-                # something strange left off
-                raise Tag.DoesNotExist()
-            if ambiguous_slugs:
-                # some tags should be qualified
-                e = Tag.MultipleObjectsReturned()
-                e.tags = tags
-                e.ambiguous_slugs = ambiguous_slugs
-                raise e
-            if deprecated:
-                raise Tag.UrlDeprecationWarning(tags=tags)
-            return tags
-        else:
-            return TagBase.get_tag_list(tag_str)
+                tags.append(Tag.objects.get(slug=name, category=category))
+                category = None
+            elif name in Tag.categories_rev:
+                category = Tag.categories_rev[name]
+            else:
+                try:
+                    tags.append(Tag.objects.get(slug=name))
+                    deprecated = True
+                except Tag.MultipleObjectsReturned:
+                    ambiguous_slugs.append(name)
+
+        if category:
+            # something strange left off
+            raise Tag.DoesNotExist()
+        if ambiguous_slugs:
+            # some tags should be qualified
+            e = Tag.MultipleObjectsReturned()
+            e.tags = tags
+            e.ambiguous_slugs = ambiguous_slugs
+            raise e
+        if deprecated:
+            raise Tag.UrlDeprecationWarning(tags=tags)
+        return tags
 
     @property
     def url_chunk(self):
 
     @property
     def url_chunk(self):
@@ -230,14 +178,14 @@ class Tag(TagBase):
 
     @staticmethod
     def tags_from_info(info):
 
     @staticmethod
     def tags_from_info(info):
-        from fnpdjango.utils.text.slughifi import slughifi
+        from slugify import slugify
         from sortify import sortify
         meta_tags = []
         categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
         for field_name, category in categories:
             try:
                 tag_names = getattr(info, field_name)
         from sortify import sortify
         meta_tags = []
         categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
         for field_name, category in categories:
             try:
                 tag_names = getattr(info, field_name)
-            except KeyError:
+            except (AttributeError, KeyError):  # TODO: shouldn't be KeyError here at all.
                 try:
                     tag_names = [getattr(info, category)]
                 except KeyError:
                 try:
                     tag_names = [getattr(info, category)]
                 except KeyError:
@@ -251,9 +199,9 @@ class Tag(TagBase):
                     tag_name = tag_name.readable()
                 if lang == settings.LANGUAGE_CODE:
                     # Allow creating new tag, if it's in default language.
                     tag_name = tag_name.readable()
                 if lang == settings.LANGUAGE_CODE:
                     # Allow creating new tag, if it's in default language.
-                    tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
+                    tag, created = Tag.objects.get_or_create(slug=slugify(tag_name), category=category)
                     if created:
                     if created:
-                        tag_name = unicode(tag_name)
+                        tag_name = str(tag_name)
                         tag.name = tag_name
                         setattr(tag, "name_%s" % lang, tag_name)
                         tag.sort_key = sortify(tag_sort_key.lower())
                         tag.name = tag_name
                         setattr(tag, "name_%s" % lang, tag_name)
                         tag.sort_key = sortify(tag_sort_key.lower())
@@ -271,6 +219,9 @@ class Tag(TagBase):
         return meta_tags
 
 
         return meta_tags
 
 
+TagRelation.tag_model = Tag
+
+
 def prefetch_relations(objects, category, only_name=True):
     queryset = TagRelation.objects.filter(tag__category=category).select_related('tag')
     if only_name:
 def prefetch_relations(objects, category, only_name=True):
     queryset = TagRelation.objects.filter(tag__category=category).select_related('tag')
     if only_name: