+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Changing field 'Tag.book_count'
+        db.alter_column('catalogue_tag', 'book_count', self.gf('django.db.models.fields.IntegerField')(null=True))
+
+        if not db.dry_run:
+            from django.contrib.contenttypes.models import ContentType
+            from simplejson import loads, dumps
+
+            manager = orm.TagRelation.objects
+
+            def type_id(model):
+                return ContentType.objects.get_for_model(model).pk
+
+            def tagged_with_any(model, tags):
+                object_ids = {}
+                for relation in manager.filter(content_type=type_id(model), tag__in=tags):
+                    object_ids[relation.object_id] = 1
+                return model.objects.filter(pk__in=object_ids.keys())
+
+            def get_tags(instance):
+                return [relation.tag for relation in manager.filter(
+                            content_type=type_id(type(instance)), object_id=instance.pk).select_related()]
+
+            def refresh_book_count(tag):
+                if tag.category == 'theme':
+                    objects = tagged_with_any(orm.Fragment, [tag]).only()
+                else:
+                    objects = tagged_with_any(orm.Book, [tag]).only('slug')
+                    if tag.category != 'set':
+                        # eliminate descendants
+                        l_tags = orm.Tag.objects.filter(slug__in=['l-'+book.slug for book in objects])
+                        descendants_keys = [book.pk for book in tagged_with_any(orm.Book, l_tags)]
+                        if descendants_keys:
+                            objects = objects.exclude(pk__in=descendants_keys)
+                tag.book_count = objects.count()
+                tag.save()
+
+            def refresh_tag_counter(book):
+                tags = {}
+                for child in book.children.all().order_by():
+                    for tag_pk, value in tag_counter(child).iteritems():
+                        tags[tag_pk] = tags.get(tag_pk, 0) + value
+                for tag in [tag for tag in get_tags(book) if tag.category not in ('book', 'theme', 'set')]:
+                    tags[tag.pk] = 1
+                book._tag_counter = dumps(tags)
+                book.save()
+                return tags
+
+            def tag_counter(book):
+                if book._tag_counter is None:
+                    return refresh_tag_counter(book)
+                return dict((int(k), v) for k, v in loads(book._tag_counter).iteritems())
+
+            def theme_counter(book):
+                if book._theme_counter is None:
+                    tags = {}
+                    l_tag = orm.Tag.objects.get(slug='l-'+book.slug)
+                    for fragment in tagged_with_any(orm.Fragment, [l_tag]):
+                        for tag in [tag for tag in get_tags(fragment) if tag.category=='theme']:
+                            tags[tag.pk] = tags.get(tag.pk, 0) + 1
+                    book._theme_counter = dumps(tags)
+                    book.save()
+
+
+            # remove orphaned relations
+            book_type_id = type_id(orm.Book)
+            book_ids = [b.pk for b in orm.Book.objects.all().only()]
+            manager.filter(content_type=book_type_id).exclude(object_id__in=book_ids).delete()
+            del book_ids
+
+            fragment_type_id = type_id(orm.Fragment)
+            fragment_ids = [b.pk for b in orm.Fragment.objects.all().only()]
+            manager.filter(content_type=fragment_type_id).exclude(object_id__in=fragment_ids).delete()
+            del fragment_ids
+
+            tag_ids = [t.pk for t in orm.Tag.objects.all().only()]
+            manager.exclude(tag__in=tag_ids).delete()
+            del tag_ids
+
+            # remove theme tags for books
+            manager.filter(content_type=book_type_id).filter(tag__category='theme').delete()
+
+            # reset count fields
+            for tag in orm.Tag.objects.exclude(category__in=('book', 'set')).iterator():
+                refresh_book_count(tag)
+            for book in orm.Book.objects.all().iterator():
+                theme_counter(book)
+            for book in orm.Book.objects.filter(parent=None).iterator():
+                tag_counter(book)
+
+    def backwards(self, orm):
+
+        # Changing field 'Tag.book_count'
+        db.alter_column('catalogue_tag', 'book_count', self.gf('django.db.models.fields.IntegerField')())
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'catalogue.book': {
+            'Meta': {'object_name': 'Book'},
+            '_short_html': ('django.db.models.fields.TextField', [], {}),
+            '_short_html_de': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_en': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_es': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_fr': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_lt': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_pl': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_ru': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_uk': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_tag_counter': ('catalogue.fields.JSONField', [], {'null': 'True'}),
+            '_theme_counter': ('catalogue.fields.JSONField', [], {'null': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'epub_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'extra_info': ('catalogue.fields.JSONField', [], {}),
+            'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}),
+            'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'odt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}),
+            'parent_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'txt_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
+            'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}),
+            'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        'catalogue.bookstub': {
+            'Meta': {'object_name': 'BookStub'},
+            'author': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'pd': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'translator': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'translator_death': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        'catalogue.fragment': {
+            'Meta': {'object_name': 'Fragment'},
+            '_short_html': ('django.db.models.fields.TextField', [], {}),
+            '_short_html_de': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_en': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_es': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_fr': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_lt': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_pl': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_ru': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            '_short_html_uk': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}),
+            'anchor': ('django.db.models.fields.CharField', [], {'max_length': '120'}),
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fragments'", 'to': "orm['catalogue.Book']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'short_text': ('django.db.models.fields.TextField', [], {}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        'catalogue.tag': {
+            'Meta': {'unique_together': "(('slug', 'category'),)", 'object_name': 'Tag'},
+            'book_count': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'death': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'main_page': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}),
+            'sort_key': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'db_index': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'})
+        },
+        'catalogue.tagrelation': {
+            'Meta': {'unique_together': "(('tag', 'content_type', 'object_id'),)", 'object_name': 'TagRelation', 'db_table': "'catalogue_tag_relation'"},
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'to': "orm['catalogue.Tag']"})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        }
+    }
+
+    complete_apps = ['catalogue']