make dvcs models abstract,
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 1 Jul 2011 13:28:22 +0000 (15:28 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 1 Jul 2011 13:28:59 +0000 (15:28 +0200)
refactor chunk list rendering

25 files changed:
apps/dvcs/admin.py [deleted file]
apps/dvcs/fixtures/stages.json [deleted file]
apps/dvcs/migrations/0001_initial.py [deleted file]
apps/dvcs/migrations/__init__.py [deleted file]
apps/dvcs/models.py
apps/wiki/admin.py
apps/wiki/fixtures/stages.json [new file with mode: 0644]
apps/wiki/forms.py
apps/wiki/helpers.py
apps/wiki/migrations/0003_add_dvcs.py [new file with mode: 0644]
apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py [deleted file]
apps/wiki/models.py
apps/wiki/templates/wiki/book_append_to.html
apps/wiki/templates/wiki/book_detail.html
apps/wiki/templates/wiki/book_edit.html
apps/wiki/templates/wiki/chunk_add.html
apps/wiki/templates/wiki/chunk_edit.html
apps/wiki/templates/wiki/document_list.html
apps/wiki/templates/wiki/pubmark_dialog.html
apps/wiki/templates/wiki/revert_dialog.html
apps/wiki/templates/wiki/save_dialog.html
apps/wiki/templates/wiki/tag_dialog.html
apps/wiki/templatetags/wiki.py
apps/wiki/views.py
redakcja/settings/common.py

diff --git a/apps/dvcs/admin.py b/apps/dvcs/admin.py
deleted file mode 100644 (file)
index f6a1ab3..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.contrib import admin
-
-from dvcs import models
-
-
-class TagAdmin(admin.ModelAdmin):
-    list_display = ['name', 'slug', 'ordering']
-
-admin.site.register(models.Tag, TagAdmin)
-admin.site.register(models.Document)
-admin.site.register(models.Change)
diff --git a/apps/dvcs/fixtures/stages.json b/apps/dvcs/fixtures/stages.json
deleted file mode 100644 (file)
index fc05d57..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-[
-    {
-        "pk": 1, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 1, 
-            "name": "Autokorekta", 
-            "slug": "first_correction"
-        }
-    }, 
-    {
-        "pk": 2, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 2, 
-            "name": "Tagowanie", 
-            "slug": "tagging"
-        }
-    }, 
-    {
-        "pk": 3, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 3, 
-            "name": "Korekta", 
-            "slug": "proofreading"
-        }
-    }, 
-    {
-        "pk": 4, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 4, 
-            "name": "Sprawdzenie przypis\u00f3w \u017ar\u00f3d\u0142a", 
-            "slug": "annotation-proofreading"
-        }
-    }, 
-    {
-        "pk": 5, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 5, 
-            "name": "Uwsp\u00f3\u0142cze\u015bnienie", 
-            "slug": "modernisation"
-        }
-    }, 
-    {
-        "pk": 6, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 6, 
-            "name": "Przypisy", 
-            "slug": "annotations"
-        }
-    }, 
-    {
-        "pk": 7, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 7, 
-            "name": "Motywy", 
-            "slug": "themes"
-        }
-    }, 
-    {
-        "pk": 8, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 8, 
-            "name": "Ostateczna redakcja literacka", 
-            "slug": "editor-proofreading"
-        }
-    }, 
-    {
-        "pk": 9, 
-        "model": "dvcs.tag", 
-        "fields": {
-            "ordering": 9, 
-            "name": "Ostateczna redakcja techniczna", 
-            "slug": "technical-editor-proofreading"
-        }
-    }
-]
diff --git a/apps/dvcs/migrations/0001_initial.py b/apps/dvcs/migrations/0001_initial.py
deleted file mode 100644 (file)
index bf8d3e2..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-# 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):
-        
-        # Adding model 'Tag'
-        db.create_table('dvcs_tag', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=64, unique=True, null=True, blank=True)),
-            ('ordering', self.gf('django.db.models.fields.IntegerField')()),
-        ))
-        db.send_create_signal('dvcs', ['Tag'])
-
-        # Adding model 'Change'
-        db.create_table('dvcs_change', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
-            ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
-            ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
-            ('patch', self.gf('django.db.models.fields.TextField')(blank=True)),
-            ('tree', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Document'])),
-            ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
-            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['dvcs.Change'])),
-            ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['dvcs.Change'])),
-            ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
-            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
-            ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('dvcs', ['Change'])
-
-        # Adding unique constraint on 'Change', fields ['tree', 'revision']
-        db.create_unique('dvcs_change', ['tree_id', 'revision'])
-
-        # Adding M2M table for field tags on 'Change'
-        db.create_table('dvcs_change_tags', (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            ('change', models.ForeignKey(orm['dvcs.change'], null=False)),
-            ('tag', models.ForeignKey(orm['dvcs.tag'], null=False))
-        ))
-        db.create_unique('dvcs_change_tags', ['change_id', 'tag_id'])
-
-        # Adding model 'Document'
-        db.create_table('dvcs_document', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_documents', null=True, to=orm['auth.User'])),
-            ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['dvcs.Change'], null=True, blank=True)),
-            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
-            ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Tag'], null=True, blank=True)),
-        ))
-        db.send_create_signal('dvcs', ['Document'])
-
-        if not db.dry_run:
-            from django.core.management import call_command
-            call_command("loaddata", "stages.json")
-
-
-    def backwards(self, orm):
-        
-        # Removing unique constraint on 'Change', fields ['tree', 'revision']
-        db.delete_unique('dvcs_change', ['tree_id', 'revision'])
-
-        # Deleting model 'Tag'
-        db.delete_table('dvcs_tag')
-
-        # Deleting model 'Change'
-        db.delete_table('dvcs_change')
-
-        # Removing M2M table for field tags on 'Change'
-        db.delete_table('dvcs_change_tags')
-
-        # Deleting model 'Document'
-        db.delete_table('dvcs_document')
-
-
-    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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", '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'})
-        },
-        'dvcs.change': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
-            'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dvcs.Tag']", 'symmetrical': 'False'}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
-        },
-        'dvcs.document': {
-            'Meta': {'object_name': 'Document'},
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Tag']", 'null': 'True', 'blank': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
-        },
-        'dvcs.tag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'Tag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        }
-    }
-
-    complete_apps = ['dvcs']
diff --git a/apps/dvcs/migrations/__init__.py b/apps/dvcs/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
index 86f66bc..5449240 100644 (file)
@@ -1,6 +1,7 @@
 from datetime import datetime
 
 from django.db import models
 from datetime import datetime
 
 from django.db import models
+from django.db.models.base import ModelBase
 from django.contrib.auth.models import User
 from django.utils.translation import ugettext_lazy as _
 from mercurial import mdiff, simplemerge
 from django.contrib.auth.models import User
 from django.utils.translation import ugettext_lazy as _
 from mercurial import mdiff, simplemerge
@@ -20,6 +21,7 @@ class Tag(models.Model):
     _object_cache = {}
 
     class Meta:
     _object_cache = {}
 
     class Meta:
+        abstract = True
         ordering = ['ordering']
 
     def __unicode__(self):
         ordering = ['ordering']
 
     def __unicode__(self):
@@ -63,7 +65,6 @@ class Change(models.Model):
     author_name = models.CharField(max_length=128, null=True, blank=True)
     author_email = models.CharField(max_length=128, null=True, blank=True)
     patch = models.TextField(blank=True)
     author_name = models.CharField(max_length=128, null=True, blank=True)
     author_email = models.CharField(max_length=128, null=True, blank=True)
     patch = models.TextField(blank=True)
-    tree = models.ForeignKey('Document')
     revision = models.IntegerField(db_index=True)
 
     parent = models.ForeignKey('self',
     revision = models.IntegerField(db_index=True)
 
     parent = models.ForeignKey('self',
@@ -79,9 +80,8 @@ class Change(models.Model):
                         default=datetime.now)
     publishable = models.BooleanField(default=False)
 
                         default=datetime.now)
     publishable = models.BooleanField(default=False)
 
-    tags = models.ManyToManyField(Tag)
-
     class Meta:
     class Meta:
+        abstract = True
         ordering = ('created_at',)
         unique_together = ['tree', 'revision']
 
         ordering = ('created_at',)
         unique_together = ['tree', 'revision']
 
@@ -122,8 +122,7 @@ class Change(models.Model):
         if self.parent is None and self.merge_parent is not None:
             return self.apply_to(self.merge_parent.materialize())
 
         if self.parent is None and self.merge_parent is not None:
             return self.apply_to(self.merge_parent.materialize())
 
-        changes = Change.objects.exclude(parent=None).filter(
-                        tree=self.tree,
+        changes = self.tree.change_set.exclude(parent=None).filter(
                         revision__lte=self.revision).order_by('revision')
         text = u''
         for change in changes:
                         revision__lte=self.revision).order_by('revision')
         text = u''
         for change in changes:
@@ -183,19 +182,60 @@ class Change(models.Model):
         self.tree.commit(text=self.materialize(), **kwargs)
 
 
         self.tree.commit(text=self.materialize(), **kwargs)
 
 
+def create_tag_model(model):
+    name = model.__name__ + 'Tag'
+    attrs = {
+        '__module__': model.__module__,
+    }
+    return type(name, (Tag,), attrs)
+
+
+def create_change_model(model):
+    name = model.__name__ + 'Change'
+
+    attrs = {
+        '__module__': model.__module__,
+        'tree': models.ForeignKey(model, related_name='change_set'),
+        'tags': models.ManyToManyField(model.tag_model, related_name='change_set'),
+    }
+    return type(name, (Change,), attrs)
+
+
+
+class DocumentMeta(ModelBase):
+    "Metaclass for Document models."
+    def __new__(cls, name, bases, attrs):
+        model = super(DocumentMeta, cls).__new__(cls, name, bases, attrs)
+        if not model._meta.abstract:
+            # create a real Tag object and `stage' fk
+            model.tag_model = create_tag_model(model)
+            models.ForeignKey(model.tag_model, 
+                null=True, blank=True).contribute_to_class(model, 'stage')
+
+            # create real Change model and `head' fk
+            model.change_model = create_change_model(model)
+            models.ForeignKey(model.change_model,
+                    null=True, blank=True, default=None,
+                    help_text=_("This document's current head."),
+                    editable=False).contribute_to_class(model, 'head')
+
+        return model
+
+
+
 class Document(models.Model):
     """
         File in repository.        
     """
 class Document(models.Model):
     """
         File in repository.        
     """
+    __metaclass__ = DocumentMeta
+
     creator = models.ForeignKey(User, null=True, blank=True, editable=False,
                 related_name="created_documents")
     creator = models.ForeignKey(User, null=True, blank=True, editable=False,
                 related_name="created_documents")
-    head = models.ForeignKey(Change,
-                    null=True, blank=True, default=None,
-                    help_text=_("This document's current head."),
-                    editable=False)
 
     user = models.ForeignKey(User, null=True, blank=True)
 
     user = models.ForeignKey(User, null=True, blank=True)
-    stage = models.ForeignKey(Tag, null=True, blank=True)
+
+    class Meta:
+        abstract = True
 
     def __unicode__(self):
         return u"{0}, HEAD: {1}".format(self.id, self.head_id)
 
     def __unicode__(self):
         return u"{0}, HEAD: {1}".format(self.id, self.head_id)
@@ -222,7 +262,7 @@ class Document(models.Model):
         else:
             parent = kwargs['parent']
             if not isinstance(parent, Change):
         else:
             parent = kwargs['parent']
             if not isinstance(parent, Change):
-                parent = Change.objects.get(pk=kwargs['parent'])
+                parent = self.change_set.objects.get(pk=kwargs['parent'])
 
         if 'patch' not in kwargs:
             if 'text' not in kwargs:
 
         if 'patch' not in kwargs:
             if 'text' not in kwargs:
@@ -289,7 +329,7 @@ class Document(models.Model):
         if not isinstance(instance, Document):
             return
         if created:
         if not isinstance(instance, Document):
             return
         if created:
-            instance.head = Change.objects.create(
+            instance.head = instance.change_model.objects.create(
                     revision=-1,
                     author=instance.creator,
                     patch=Change.make_patch('', ''),
                     revision=-1,
                     author=instance.creator,
                     patch=Change.make_patch('', ''),
index 9725d0b..b742f70 100644 (file)
@@ -9,3 +9,5 @@ class BookAdmin(admin.ModelAdmin):
 admin.site.register(models.Book, BookAdmin)
 admin.site.register(models.Chunk)
 admin.site.register(models.Theme)
 admin.site.register(models.Book, BookAdmin)
 admin.site.register(models.Chunk)
 admin.site.register(models.Theme)
+
+admin.site.register(models.Chunk.tag_model)
diff --git a/apps/wiki/fixtures/stages.json b/apps/wiki/fixtures/stages.json
new file mode 100644 (file)
index 0000000..992ebc7
--- /dev/null
@@ -0,0 +1,83 @@
+[
+    {
+        "pk": 1, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 1, 
+            "name": "Autokorekta", 
+            "slug": "first_correction"
+        }
+    }, 
+    {
+        "pk": 2, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 2, 
+            "name": "Tagowanie", 
+            "slug": "tagging"
+        }
+    }, 
+    {
+        "pk": 3, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 3, 
+            "name": "Korekta", 
+            "slug": "proofreading"
+        }
+    }, 
+    {
+        "pk": 4, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 4, 
+            "name": "Sprawdzenie przypis\u00f3w \u017ar\u00f3d\u0142a", 
+            "slug": "annotation-proofreading"
+        }
+    }, 
+    {
+        "pk": 5, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 5, 
+            "name": "Uwsp\u00f3\u0142cze\u015bnienie", 
+            "slug": "modernisation"
+        }
+    }, 
+    {
+        "pk": 6, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 6, 
+            "name": "Przypisy", 
+            "slug": "annotations"
+        }
+    }, 
+    {
+        "pk": 7, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 7, 
+            "name": "Motywy", 
+            "slug": "themes"
+        }
+    }, 
+    {
+        "pk": 8, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 8, 
+            "name": "Ostateczna redakcja literacka", 
+            "slug": "editor-proofreading"
+        }
+    }, 
+    {
+        "pk": 9, 
+        "model": "wiki.chunktag", 
+        "fields": {
+            "ordering": 9, 
+            "name": "Ostateczna redakcja techniczna", 
+            "slug": "technical-editor-proofreading"
+        }
+    }
+]
index e153f7e..6b10909 100644 (file)
@@ -8,7 +8,6 @@ from django.db.models import Count
 from django import forms
 from django.utils.translation import ugettext_lazy as _
 
 from django import forms
 from django.utils.translation import ugettext_lazy as _
 
-from dvcs.models import Tag
 from wiki.constants import MASTERS
 from wiki.models import Book, Chunk
 
 from wiki.constants import MASTERS
 from wiki.models import Book, Chunk
 
@@ -18,7 +17,7 @@ class DocumentTagForm(forms.Form):
     """
 
     id = forms.CharField(widget=forms.HiddenInput)
     """
 
     id = forms.CharField(widget=forms.HiddenInput)
-    tag = forms.ModelChoiceField(queryset=Tag.objects.all())
+    tag = forms.ModelChoiceField(queryset=Chunk.tag_model.objects.all())
     revision = forms.IntegerField(widget=forms.HiddenInput)
 
 
     revision = forms.IntegerField(widget=forms.HiddenInput)
 
 
@@ -114,7 +113,7 @@ class DocumentTextSaveForm(forms.Form):
     )
 
     stage_completed = forms.ModelChoiceField(
     )
 
     stage_completed = forms.ModelChoiceField(
-        queryset=Tag.objects.all(),
+        queryset=Chunk.tag_model.objects.all(),
         required=False,
         label=_(u"Completed"),
         help_text=_(u"If you completed a life cycle stage, select it."),
         required=False,
         label=_(u"Completed"),
         help_text=_(u"If you completed a life cycle stage, select it."),
@@ -157,7 +156,7 @@ class ChunkForm(forms.ModelForm):
         Form used for editing a chunk.
     """
     user = forms.ModelChoiceField(queryset=
         Form used for editing a chunk.
     """
     user = forms.ModelChoiceField(queryset=
-        User.objects.annotate(count=Count('document')).
+        User.objects.annotate(count=Count('chunk')).
         order_by('-count', 'last_name', 'first_name'))
 
 
         order_by('-count', 'last_name', 'first_name'))
 
 
index fe4b3b8..253f129 100644 (file)
@@ -1,4 +1,5 @@
 from django import http
 from django import http
+from django.db.models import Count
 from django.utils import simplejson as json
 from django.utils.functional import Promise
 from datetime import datetime
 from django.utils import simplejson as json
 from django.utils.functional import Promise
 from datetime import datetime
@@ -144,27 +145,50 @@ def active_tab(tab):
     return wrapper
 
 
     return wrapper
 
 
-class BookChunks(object):
-    """
-        Yields the chunks of a book.
-    """
+class ChunksList(object):
+    def __init__(self, chunk_qs):
+        self.chunk_qs = chunk_qs.annotate(
+            book_length=Count('book__chunk')).select_related(
+            'book', 'stage__name',
+            'user')
 
 
-    def __init__(self, book):
-        self.book = book
+        self.book_ids = [x['book_id'] for x in chunk_qs.values('book_id')]
+
+    def __getitem__(self, key):
+        if isinstance(key, slice):
+            return self.get_slice(key)
+        elif isinstance(key, int):
+            return self.get_slice(slice(key, key+1))[0]
+        else:
+            raise TypeError('Unsupported list index. Must be a slice or an int.')
+
+    def __len__(self):
+        return len(self.book_ids)
+
+    def get_slice(self, slice_):
+        book_ids = self.book_ids[slice_]
+        chunk_qs = self.chunk_qs.filter(book__in=book_ids)
 
 
-    @property
-    def chunks(self):
-        return self.book.chunk_set.all()
+        chunks_list = []
+        book = None
+        for chunk in chunk_qs:
+            if chunk.book != book:
+                book = chunk.book
+                chunks_list.append(ChoiceChunks(book, [chunk], chunk.book_length))
+            else:
+                chunks_list[-1].chunks.append(chunk)
+        return chunks_list
 
 
 
 
-class ChoiceChunks(BookChunks):
+class ChoiceChunks(object):
     """
         Associates the given chunks iterable for a book.
     """
 
     chunks = None
 
     """
         Associates the given chunks iterable for a book.
     """
 
     chunks = None
 
-    def __init__(self, book, chunks):
+    def __init__(self, book, chunks, book_length):
         self.book = book
         self.chunks = chunks
         self.book = book
         self.chunks = chunks
+        self.book_length = book_length
 
 
diff --git a/apps/wiki/migrations/0003_add_dvcs.py b/apps/wiki/migrations/0003_add_dvcs.py
new file mode 100644 (file)
index 0000000..a301abf
--- /dev/null
@@ -0,0 +1,371 @@
+# encoding: utf-8
+import datetime
+import os.path
+import cPickle
+import re
+import urllib
+
+from django.conf import settings
+from django.db import models
+from mercurial import mdiff, hg, ui
+from south.db import db
+from south.v2 import SchemaMigration
+
+from slughifi import slughifi
+
+META_REGEX = re.compile(r'\s*<!--\s(.*?)-->', re.DOTALL | re.MULTILINE)
+STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
+AUTHOR_RE = re.compile(r'\s*(.*?)\s*<(.*)>\s*')
+
+
+def urlunquote(url):
+    """Unqotes URL
+
+    # >>> urlunquote('Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84')
+    # u'Za\u017c\xf3\u0142\u0107_g\u0119\u015bl\u0105 ja\u017a\u0144'
+    """
+    return unicode(urllib.unquote(url), 'utf-8', 'ignore')
+
+
+def split_name(name):
+    parts = name.split('__')
+    return parts
+
+
+def file_to_title(fname):
+    """ Returns a title-like version of a filename. """
+    parts = (p.replace('_', ' ').title() for p in fname.split('__'))
+    return ' / '.join(parts)
+
+
+def make_patch(src, dst):
+    if isinstance(src, unicode):
+        src = src.encode('utf-8')
+    if isinstance(dst, unicode):
+        dst = dst.encode('utf-8')
+    return cPickle.dumps(mdiff.textdiff(src, dst))
+
+
+def plain_text(text):
+    return re.sub(META_REGEX, '', text, 1)
+
+
+def gallery(slug, text):
+    result = {}
+
+    m = re.match(META_REGEX, text)
+    if m:
+        for line in m.group(1).split('\n'):
+            try:
+                k, v = line.split(':', 1)
+                result[k.strip()] = v.strip()
+            except ValueError:
+                continue
+
+    gallery = result.get('gallery', slughifi(slug))
+
+    if gallery.startswith('/'):
+        gallery = os.path.basename(gallery)
+
+    return gallery
+
+
+def migrate_file_from_hg(orm, fname, entry):
+    fname = urlunquote(fname)
+    print fname
+    if fname.endswith('.xml'):
+        fname = fname[:-4]
+    title = file_to_title(fname)
+    fname = slughifi(fname)
+    # create all the needed objects
+    # what if it already exists?
+    book = orm.Book.objects.create(
+        title=title,
+        slug=fname)
+    chunk = orm.Chunk.objects.create(
+        book=book,
+        number=1,
+        slug='1')
+    head = orm['wiki.ChunkChange'].objects.create(
+        tree=chunk,
+        revision=-1,
+        patch=make_patch('', ''),
+        created_at=datetime.datetime.fromtimestamp(entry.filectx(0).date()[0]),
+        description=''
+        )
+    chunk.head = head
+    try:
+        chunk.stage = orm['wiki.ChunkTag'].objects.order_by('ordering')[0]
+    except IndexError:
+        chunk.stage = None
+    old_data = ''
+
+    maxrev = entry.filerev()
+    gallery_link = None
+    
+    for rev in xrange(maxrev + 1):
+        fctx = entry.filectx(rev)
+        data = fctx.data()
+        gallery_link = gallery(fname, data)
+        data = plain_text(data)
+
+        # get tags from description
+        description = fctx.description().decode("utf-8", 'replace')
+        tags = STAGE_TAGS_RE.findall(description)
+        tags = [orm['wiki.ChunkTag'].objects.get(slug=slug.strip()) for slug in tags]
+
+        if tags:
+            max_ordering = max(tags, key=lambda x: x.ordering).ordering
+            try:
+                chunk.stage = orm['wiki.ChunkTag'].objects.filter(ordering__gt=max_ordering).order_by('ordering')[0]
+            except IndexError:
+                chunk.stage = None
+
+        description = STAGE_TAGS_RE.sub('', description)
+
+        author = author_name = author_email = None
+        author_desc = fctx.user().decode("utf-8", 'replace')
+        m = AUTHOR_RE.match(author_desc)
+        if m:
+            try:
+                author = orm['auth.User'].objects.get(username=m.group(1), email=m.group(2))
+            except orm['auth.User'].DoesNotExist:
+                author_name = m.group(1)
+                author_email = m.group(2)
+        else:
+            author_name = author_desc
+
+        head = orm['wiki.ChunkChange'].objects.create(
+            tree=chunk,
+            revision=rev + 1,
+            patch=make_patch(old_data, data),
+            created_at=datetime.datetime.fromtimestamp(fctx.date()[0]),
+            description=description,
+            author=author,
+            author_name=author_name,
+            author_email=author_email,
+            parent=chunk.head
+            )
+        head.tags = tags
+        chunk.head = head
+        old_data = data
+
+    chunk.save()
+    if gallery_link:
+        book.gallery = gallery_link
+        book.save()
+
+
+def migrate_from_hg(orm):
+    try:
+        hg_path = settings.WIKI_REPOSITORY_PATH
+    except:
+        pass
+
+    print 'migrate from', hg_path
+    repo = hg.repository(ui.ui(), hg_path)
+    tip = repo['tip']
+    for fname in tip:
+        if fname.startswith('.'):
+            continue
+        migrate_file_from_hg(orm, fname, tip[fname])
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'Book'
+        db.create_table('wiki_book', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=128, db_index=True)),
+            ('gallery', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['wiki.Book'])),
+            ('parent_number', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)),
+            ('last_published', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+            ('_list_html', self.gf('django.db.models.fields.TextField')(null=True)),
+        ))
+        db.send_create_signal('wiki', ['Book'])
+
+        # Adding model 'Chunk'
+        db.create_table('wiki_chunk', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_documents', null=True, to=orm['auth.User'])),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+            ('book', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Book'])),
+            ('number', self.gf('django.db.models.fields.IntegerField')()),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
+            ('comment', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+            ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.ChunkTag'], null=True, blank=True)),
+            ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['wiki.ChunkChange'], null=True, blank=True)),
+        ))
+        db.send_create_signal('wiki', ['Chunk'])
+
+        # Adding unique constraint on 'Chunk', fields ['book', 'number']
+        db.create_unique('wiki_chunk', ['book_id', 'number'])
+
+        # Adding unique constraint on 'Chunk', fields ['book', 'slug']
+        db.create_unique('wiki_chunk', ['book_id', 'slug'])
+
+        # Adding model 'ChunkTag'
+        db.create_table('wiki_chunktag', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=64, unique=True, null=True, blank=True)),
+            ('ordering', self.gf('django.db.models.fields.IntegerField')()),
+        ))
+        db.send_create_signal('wiki', ['ChunkTag'])
+
+        # Adding model 'ChunkChange'
+        db.create_table('wiki_chunkchange', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+            ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+            ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+            ('patch', self.gf('django.db.models.fields.TextField')(blank=True)),
+            ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['wiki.ChunkChange'])),
+            ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['wiki.ChunkChange'])),
+            ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
+            ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('tree', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Chunk'])),
+        ))
+        db.send_create_signal('wiki', ['ChunkChange'])
+
+        # Adding unique constraint on 'ChunkChange', fields ['tree', 'revision']
+        db.create_unique('wiki_chunkchange', ['tree_id', 'revision'])
+
+        # Adding M2M table for field tags on 'ChunkChange'
+        db.create_table('wiki_chunkchange_tags', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('chunkchange', models.ForeignKey(orm['wiki.chunkchange'], null=False)),
+            ('chunktag', models.ForeignKey(orm['wiki.chunktag'], null=False))
+        ))
+        db.create_unique('wiki_chunkchange_tags', ['chunkchange_id', 'chunktag_id'])
+
+        if not db.dry_run:
+            from django.core.management import call_command
+            call_command("loaddata", "stages.json")
+
+            migrate_from_hg(orm)
+
+    def backwards(self, orm):
+        
+        # Removing unique constraint on 'ChunkChange', fields ['tree', 'revision']
+        db.delete_unique('wiki_chunkchange', ['tree_id', 'revision'])
+
+        # Removing unique constraint on 'Chunk', fields ['book', 'slug']
+        db.delete_unique('wiki_chunk', ['book_id', 'slug'])
+
+        # Removing unique constraint on 'Chunk', fields ['book', 'number']
+        db.delete_unique('wiki_chunk', ['book_id', 'number'])
+
+        # Deleting model 'Book'
+        db.delete_table('wiki_book')
+
+        # Deleting model 'Chunk'
+        db.delete_table('wiki_chunk')
+
+        # Deleting model 'ChunkTag'
+        db.delete_table('wiki_chunktag')
+
+        # Deleting model 'ChunkChange'
+        db.delete_table('wiki_chunkchange')
+
+        # Removing M2M table for field tags on 'ChunkChange'
+        db.delete_table('wiki_chunkchange_tags')
+
+
+    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']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            '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']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", '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'})
+        },
+        'wiki.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            '_list_html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['wiki.Book']"}),
+            'parent_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'wiki.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Book']"}),
+            'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_documents'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['wiki.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+        },
+        'wiki.chunkchange': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['wiki.ChunkChange']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['wiki.ChunkChange']"}),
+            'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['wiki.ChunkTag']", 'symmetrical': 'False'}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Chunk']"})
+        },
+        'wiki.chunktag': {
+            'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'ordering': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+        },
+        'wiki.theme': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Theme'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
+        }
+    }
+
+    complete_apps = ['wiki']
diff --git a/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py b/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py
deleted file mode 100644 (file)
index deb649f..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-# encoding: utf-8
-import datetime
-import os.path
-import cPickle
-import re
-import urllib
-
-from django.conf import settings
-from django.db import models
-from mercurial import mdiff, hg, ui
-from south.db import db
-from south.v2 import SchemaMigration
-
-from slughifi import slughifi
-
-META_REGEX = re.compile(r'\s*<!--\s(.*?)-->', re.DOTALL | re.MULTILINE)
-STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
-AUTHOR_RE = re.compile(r'\s*(.*?)\s*<(.*)>\s*')
-
-
-def urlunquote(url):
-    """Unqotes URL
-
-    # >>> urlunquote('Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84')
-    # u'Za\u017c\xf3\u0142\u0107_g\u0119\u015bl\u0105 ja\u017a\u0144'
-    """
-    return unicode(urllib.unquote(url), 'utf-8', 'ignore')
-
-
-def split_name(name):
-    parts = name.split('__')
-    return parts
-
-
-def file_to_title(fname):
-    """ Returns a title-like version of a filename. """
-    parts = (p.replace('_', ' ').title() for p in fname.split('__'))
-    return ' / '.join(parts)
-
-
-def make_patch(src, dst):
-    if isinstance(src, unicode):
-        src = src.encode('utf-8')
-    if isinstance(dst, unicode):
-        dst = dst.encode('utf-8')
-    return cPickle.dumps(mdiff.textdiff(src, dst))
-
-
-def plain_text(text):
-    return re.sub(META_REGEX, '', text, 1)
-
-
-def gallery(slug, text):
-    result = {}
-
-    m = re.match(META_REGEX, text)
-    if m:
-        for line in m.group(1).split('\n'):
-            try:
-                k, v = line.split(':', 1)
-                result[k.strip()] = v.strip()
-            except ValueError:
-                continue
-
-    gallery = result.get('gallery', slughifi(slug))
-
-    if gallery.startswith('/'):
-        gallery = os.path.basename(gallery)
-
-    return gallery
-
-
-def migrate_file_from_hg(orm, fname, entry):
-    fname = urlunquote(fname)
-    print fname
-    if fname.endswith('.xml'):
-        fname = fname[:-4]
-    title = file_to_title(fname)
-    fname = slughifi(fname)
-    # create all the needed objects
-    # what if it already exists?
-    book = orm.Book.objects.create(
-        title=title,
-        slug=fname)
-    chunk = orm.Chunk.objects.create(
-        book=book,
-        number=1,
-        slug='1')
-    head = orm['dvcs.Change'].objects.create(
-        tree=chunk,
-        revision=-1,
-        patch=make_patch('', ''),
-        created_at=datetime.datetime.fromtimestamp(entry.filectx(0).date()[0]),
-        description=''
-        )
-    chunk.head = head
-    try:
-        chunk.stage = orm['dvcs.Tag'].objects.order_by('ordering')[0]
-    except IndexError:
-        chunk.stage = None
-    old_data = ''
-
-    maxrev = entry.filerev()
-    gallery_link = None
-    
-    for rev in xrange(maxrev + 1):
-        fctx = entry.filectx(rev)
-        data = fctx.data()
-        gallery_link = gallery(fname, data)
-        data = plain_text(data)
-
-        # get tags from description
-        description = fctx.description().decode("utf-8", 'replace')
-        tags = STAGE_TAGS_RE.findall(description)
-        tags = [orm['dvcs.Tag'].objects.get(slug=slug.strip()) for slug in tags]
-
-        if tags:
-            max_ordering = max(tags, key=lambda x: x.ordering).ordering
-            try:
-                chunk.stage = orm['dvcs.Tag'].objects.filter(ordering__gt=max_ordering).order_by('ordering')[0]
-            except IndexError:
-                chunk.stage = None
-
-        description = STAGE_TAGS_RE.sub('', description)
-
-        author = author_name = author_email = None
-        author_desc = fctx.user().decode("utf-8", 'replace')
-        m = AUTHOR_RE.match(author_desc)
-        if m:
-            try:
-                author = orm['auth.User'].objects.get(username=m.group(1), email=m.group(2))
-            except orm['auth.User'].DoesNotExist:
-                author_name = m.group(1)
-                author_email = m.group(2)
-        else:
-            author_name = author_desc
-
-        head = orm['dvcs.Change'].objects.create(
-            tree=chunk,
-            revision=rev + 1,
-            patch=make_patch(old_data, data),
-            created_at=datetime.datetime.fromtimestamp(fctx.date()[0]),
-            description=description,
-            author=author,
-            author_name=author_name,
-            author_email=author_email,
-            parent=chunk.head
-            )
-        head.tags = tags
-        chunk.head = head
-        old_data = data
-
-    chunk.save()
-    if gallery_link:
-        book.gallery = gallery_link
-        book.save()
-
-
-def migrate_from_hg(orm):
-    try:
-        hg_path = settings.WIKI_REPOSITORY_PATH
-    except:
-        pass
-
-    print 'migrate from', hg_path
-    repo = hg.repository(ui.ui(), hg_path)
-    tip = repo['tip']
-    for fname in tip:
-        if fname.startswith('.'):
-            continue
-        migrate_file_from_hg(orm, fname, tip[fname])
-
-
-class Migration(SchemaMigration):
-
-    depends_on = [
-        ('dvcs', '0001_initial'),
-    ]
-
-    def forwards(self, orm):
-        
-        # Adding model 'Book'
-        db.create_table('wiki_book', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=128, db_index=True)),
-            ('gallery', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
-            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['wiki.Book'])),
-            ('parent_number', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)),
-            ('last_published', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-            ('_list_html', self.gf('django.db.models.fields.TextField')(null=True)),
-        ))
-        db.send_create_signal('wiki', ['Book'])
-
-        # Adding model 'Chunk'
-        db.create_table('wiki_chunk', (
-            ('document_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['dvcs.Document'], unique=True, primary_key=True)),
-            ('book', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Book'])),
-            ('number', self.gf('django.db.models.fields.IntegerField')()),
-            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
-            ('comment', self.gf('django.db.models.fields.CharField')(max_length=255)),
-        ))
-        db.send_create_signal('wiki', ['Chunk'])
-
-        # Adding unique constraint on 'Chunk', fields ['book', 'number']
-        db.create_unique('wiki_chunk', ['book_id', 'number'])
-
-        # Adding unique constraint on 'Chunk', fields ['book', 'slug']
-        db.create_unique('wiki_chunk', ['book_id', 'slug'])
-
-        if not db.dry_run:
-            migrate_from_hg(orm)
-
-    def backwards(self, orm):
-        
-        # Removing unique constraint on 'Chunk', fields ['book', 'slug']
-        db.delete_unique('wiki_chunk', ['book_id', 'slug'])
-
-        # Removing unique constraint on 'Chunk', fields ['book', 'number']
-        db.delete_unique('wiki_chunk', ['book_id', 'number'])
-
-        # Deleting model 'Book'
-        db.delete_table('wiki_book')
-
-        # Deleting model 'Chunk'
-        db.delete_table('wiki_chunk')
-
-
-    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']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'codename')", '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            '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']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        'contenttypes.contenttype': {
-            'Meta': {'ordering': "('name',)", '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'})
-        },
-        'dvcs.change': {
-            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
-            'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
-            'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
-            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dvcs.Tag']", 'symmetrical': 'False'}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
-        },
-        'dvcs.document': {
-            'Meta': {'object_name': 'Document'},
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Tag']", 'null': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
-        },
-        'dvcs.tag': {
-            'Meta': {'ordering': "['ordering']", 'object_name': 'Tag'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
-            'ordering': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
-        },
-        'wiki.book': {
-            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
-            '_list_html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'last_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['wiki.Book']"}),
-            'parent_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
-        },
-        'wiki.chunk': {
-            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk', '_ormbases': ['dvcs.Document']},
-            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Book']"}),
-            'comment': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'document_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['dvcs.Document']", 'unique': 'True', 'primary_key': 'True'}),
-            'number': ('django.db.models.fields.IntegerField', [], {}),
-            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
-        },
-        'wiki.theme': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Theme'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['wiki']
index 28fdab8..d35b3c4 100644 (file)
@@ -176,13 +176,12 @@ class Chunk(dvcs_models.Document):
         else:
             return cls.objects.get(book__slug=slug, slug=chunk)
 
         else:
             return cls.objects.get(book__slug=slug, slug=chunk)
 
-    def pretty_name(self):
+    def pretty_name(self, book_length=None):
         title = self.book.title
         if self.comment:
             title += ", %s" % self.comment
         title = self.book.title
         if self.comment:
             title += ", %s" % self.comment
-        count = len(self.book)
-        if count > 1:
-            title += " (%d/%d)" % (self.number, len(self.book))
+        if book_length > 1:
+            title += " (%d/%d)" % (self.number, book_length)
         return title
 
     def split(self, slug, comment='', creator=None):
         return title
 
     def split(self, slug, comment='', creator=None):
index 0350aa6..da6594d 100755 (executable)
@@ -3,6 +3,7 @@
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
+    {% csrf_token %}
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Append book" %}</button></p>
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Append book" %}</button></p>
index 84d2097..5ea0e3d 100755 (executable)
@@ -54,6 +54,7 @@
     {% if need_fixing %}
         <tr><td></td><td>
             <form method="POST" action="">
     {% if need_fixing %}
         <tr><td></td><td>
             <form method="POST" action="">
+                {% csrf_token %}
                 {% if choose_master %}
                     {{ form.master }}
                 {% endif %}
                 {% if choose_master %}
                     {{ form.master }}
                 {% endif %}
@@ -89,7 +90,7 @@
         <button id="publish-button" type="submit">
             <span>{% trans "Publish" %}</span></button>
         <img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" />
         <button id="publish-button" type="submit">
             <span>{% trans "Publish" %}</span></button>
         <img src="{{ STATIC_URL }}img/angel-right.png" style="vertical-align: middle" />
-        </form></form></p>
+        </form></p>
 {% else %}
     {% trans "This book cannot be published yet" %}
 {% endif %}
 {% else %}
     {% trans "This book cannot be published yet" %}
 {% endif %}
index d5f527d..8bc7ea7 100755 (executable)
@@ -3,6 +3,7 @@
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
+    {% csrf_token %}
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Save" %}</button></p>
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Save" %}</button></p>
index 2b8939e..836c34d 100755 (executable)
@@ -3,6 +3,7 @@
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
+    {% csrf_token %}
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Add chunk" %}</button></p>
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Add chunk" %}</button></p>
index d5f527d..8bc7ea7 100755 (executable)
@@ -3,6 +3,7 @@
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
 
 {% block leftcolumn %}
        <form enctype="multipart/form-data" method="POST" action="">
+    {% csrf_token %}
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Save" %}</button></p>
        {{ form.as_p }}
 
        <p><button type="submit">{% trans "Save" %}</button></p>
index 2a0a5ef..434324c 100644 (file)
@@ -47,17 +47,19 @@ $(function() {
         {% endif %}
        {% for item in books %}
             {% with item.book as book %}
         {% endif %}
        {% for item in books %}
             {% with item.book as book %}
-            
-            {% ifequal book.chunk_set.count 1 %}
+
+            {% ifequal item.book_length 1 %}
+                {% with item.chunks.0 as chunk %}
                 <tr>
                     <td><a target="_blank" href="{% url wiki_book book.slug %}">[B]</a></td>
                 <tr>
                     <td><a target="_blank" href="{% url wiki_book book.slug %}">[B]</a></td>
-                    <td><a href="{% url wiki_chunk_edit book.slug book.0.slug %}">[c]</a></td>
+                    <td><a href="{% url wiki_chunk_edit book.slug chunk.slug %}">[c]</a></td>
                     <td><a target="_blank"
                                 href="{% url wiki_editor book.slug %}">
                                 {{ book.title }}</a></td>
                     <td><a target="_blank"
                                 href="{% url wiki_editor book.slug %}">
                                 {{ book.title }}</a></td>
-                    <td>({{ book.0.stage }})</td>
-                    <td>{% if book.0.user %}<a href="{% url wiki_user book.0.user.username %}">{{ book.0.user.first_name }} {{ book.0.user.last_name }}</a>{% endif %}</td>
+                    <td>({{ chunk.stage }})</td>
+                    <td>{% if chunk.user %}<a href="{% url wiki_user chunk.user.username %}">{{ chunk.user.first_name }} {{ chunk.user.last_name }}</a>{% endif %}</td>
                 </tr>
                 </tr>
+                {% endwith %}
             {% else %}
                 <tr>
                     <td><a target="_blank" href="{% url wiki_book book.slug %}">[B]</a></td>
             {% else %}
                 <tr>
                     <td><a target="_blank" href="{% url wiki_book book.slug %}">[B]</a></td>
index 93a9856..a70a0c3 100755 (executable)
@@ -1,6 +1,7 @@
 {% load i18n %}
 <div id="pubmark_dialog" class="dialog" data-ui-jsclass="PubmarkDialog">
        <form method="POST" action="#">
 {% load i18n %}
 <div id="pubmark_dialog" class="dialog" data-ui-jsclass="PubmarkDialog">
        <form method="POST" action="#">
+    {% csrf_token %}
                {% for field in forms.pubmark.visible_fields %}
                <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
                <p>{{ field.help_text }}</p>
                {% for field in forms.pubmark.visible_fields %}
                <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
                <p>{{ field.help_text }}</p>
index 7e5089f..c2fc155 100644 (file)
@@ -1,6 +1,7 @@
 {% load i18n %}
 <div id="revert_dialog" class="dialog" data-ui-jsclass="RevertDialog">
        <form method="POST" action="">
 {% load i18n %}
 <div id="revert_dialog" class="dialog" data-ui-jsclass="RevertDialog">
        <form method="POST" action="">
+    {% csrf_token %}
        <p>{{ forms.text_revert.comment.label }}</p>
        <p class="help_text">
                {{ forms.text_revert.comment.help_text}}
        <p>{{ forms.text_revert.comment.label }}</p>
        <p class="help_text">
                {{ forms.text_revert.comment.help_text}}
index b2f53ba..c34eb9e 100644 (file)
@@ -1,6 +1,7 @@
 {% load i18n %}
 <div id="save_dialog" class="dialog" data-ui-jsclass="SaveDialog">
        <form method="POST" action="">
 {% load i18n %}
 <div id="save_dialog" class="dialog" data-ui-jsclass="SaveDialog">
        <form method="POST" action="">
+    {% csrf_token %}
        <p>{{ forms.text_save.comment.label }}</p>
        <p class="help_text">
                {{ forms.text_save.comment.help_text}}
        <p>{{ forms.text_save.comment.label }}</p>
        <p class="help_text">
                {{ forms.text_save.comment.help_text}}
index bc601cb..aa4b242 100644 (file)
@@ -1,6 +1,7 @@
 {% load i18n %}
 <div id="add_tag_dialog" class="dialog" data-ui-jsclass="AddTagDialog">
        <form method="POST" action="#">
 {% load i18n %}
 <div id="add_tag_dialog" class="dialog" data-ui-jsclass="AddTagDialog">
        <form method="POST" action="#">
+    {% csrf_token %}
                {% for field in forms.add_tag.visible_fields %}
                <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
                <p>{{ field.help_text }}</p>
                {% for field in forms.add_tag.visible_fields %}
                <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
                <p>{{ field.help_text }}</p>
index 337afa6..db2eca3 100644 (file)
@@ -1,13 +1,13 @@
 from __future__ import absolute_import
 
 from __future__ import absolute_import
 
+from django.db.models import Count
 from django.core.urlresolvers import reverse
 from django.contrib.comments.models import Comment
 from django.template.defaultfilters import stringfilter
 from django import template
 from django.utils.translation import ugettext as _
 
 from django.core.urlresolvers import reverse
 from django.contrib.comments.models import Comment
 from django.template.defaultfilters import stringfilter
 from django import template
 from django.utils.translation import ugettext as _
 
-from wiki.models import Book
-from dvcs.models import Change
+from wiki.models import Book, Chunk
 
 register = template.Library()
 
 
 register = template.Library()
 
@@ -63,11 +63,14 @@ class WallItem(object):
 
 
 def changes_wall(max_len):
 
 
 def changes_wall(max_len):
-    qs = Change.objects.filter(revision__gt=-1).order_by('-created_at').select_related()
+    qs = Chunk.change_model.objects.filter(revision__gt=-1).order_by('-created_at')
+    qs = qs.defer('patch')
+    qs = qs.select_related('author', 'tree')
+    #qs = qs.annotate(book_length=Count('chunk__book__chunk'))
     qs = qs[:max_len]
     for item in qs:
         tag = 'stage' if item.tags.count() else 'change'
     qs = qs[:max_len]
     for item in qs:
         tag = 'stage' if item.tags.count() else 'change'
-        chunk = item.tree.chunk
+        chunk = item.tree
         w  = WallItem(tag)
         w.title = chunk.pretty_name()
         w.summary = item.description
         w  = WallItem(tag)
         w.title = chunk.pretty_name()
         w.summary = item.description
index 7bff1a0..59ba984 100644 (file)
@@ -47,8 +47,12 @@ MAX_LAST_DOCS = 10
 @active_tab('all')
 @never_cache
 def document_list(request):
 @active_tab('all')
 @never_cache
 def document_list(request):
+    chunks_list = helpers.ChunksList(Chunk.objects.order_by(
+        'book__title', 'book', 'number'))
+
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
-        'books': [helpers.BookChunks(b) for b in Book.objects.all()],
+        'books': chunks_list,
+        #'books': [helpers.BookChunks(b) for b in Book.objects.all().select_related()],
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
@@ -57,18 +61,11 @@ def document_list(request):
 @active_tab('unassigned')
 @never_cache
 def unassigned(request):
 @active_tab('unassigned')
 @never_cache
 def unassigned(request):
-    chunks = Chunk.objects.filter(user=None).order_by('book__title', 'book', 'number')
-    books = []
-    book = None
-    for chunk in chunks:
-        if chunk.book != book:
-            book = chunk.book
-            books.append(helpers.ChoiceChunks(book, [chunk]))
-        else:
-            books[-1].chunks.append(chunk)
+    chunks_list = helpers.ChunksList(Chunk.objects.filter(
+        user=None).order_by('book__title', 'book__id', 'number'))
 
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
 
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
-        'books': books,
+        'books': chunks_list,
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
@@ -84,18 +81,11 @@ def user(request, username=None):
     else:
         user = get_object_or_404(User, username=username)
 
     else:
         user = get_object_or_404(User, username=username)
 
-    chunks = Chunk.objects.filter(user=user).order_by('book__title', 'number')
-    books = []
-    book = None
-    for chunk in chunks:
-        if chunk.book != book:
-            book = chunk.book
-            books.append(helpers.ChoiceChunks(book, [chunk]))
-        else:
-            books[-1].chunks.append(chunk)
+    chunks_list = helpers.ChunksList(Chunk.objects.filter(
+        user=user).order_by('book__title', 'book', 'number'))
 
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
 
     return direct_to_template(request, 'wiki/document_list.html', extra_context={
-        'books': books,
+        'books': chunks_list,
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
                         key=lambda x: x[1]['time'], reverse=True),
     })
@@ -105,7 +95,7 @@ my = login_required(active_tab('my')(user))
 @active_tab('users')
 def users(request):
     return direct_to_template(request, 'wiki/user_list.html', extra_context={
 @active_tab('users')
 def users(request):
     return direct_to_template(request, 'wiki/user_list.html', extra_context={
-        'users': User.objects.all().annotate(count=Count('document')).order_by(
+        'users': User.objects.all().annotate(count=Count('chunk')).order_by(
             '-count', 'last_name', 'first_name'),
     })
 
             '-count', 'last_name', 'first_name'),
     })
 
index f55a877..7c40abe 100644 (file)
@@ -61,13 +61,12 @@ SESSION_COOKIE_NAME = "redakcja_sessionid"
 
 # List of callables that know how to import templates from various sources.
 TEMPLATE_LOADERS = (
 
 # List of callables that know how to import templates from various sources.
 TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.load_template_source',
-    'django.template.loaders.app_directories.load_template_source',
-#     'django.template.loaders.eggs.load_template_source',
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
 )
 
 TEMPLATE_CONTEXT_PROCESSORS = (
 )
 
 TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.core.context_processors.auth",
+    "django.contrib.auth.context_processors.auth",
     "django.core.context_processors.debug",
     "django.core.context_processors.i18n",
     "redakcja.context_processors.settings", # this is instead of media
     "django.core.context_processors.debug",
     "django.core.context_processors.i18n",
     "redakcja.context_processors.settings", # this is instead of media