Merge branch 'img-playground' of ssh://epsilon.fnp/home/rczajka/workspace/redakcja...
[redakcja.git] / apps / catalogue / models / book.py
index 94a9833..90327b2 100755 (executable)
@@ -4,18 +4,14 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.contrib.sites.models import Site
-from django.db import models
+from django.db import models, transaction
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
 from slughifi import slughifi
-from librarian import NoDublinCore, ParseError, ValidationError
-from librarian.dcparser import BookInfo
 
-import apiclient
 from catalogue.helpers import cached_in_field
 from catalogue.models import BookPublishRecord, ChunkPublishRecord
-from catalogue.signals import post_publish
-from catalogue.tasks import refresh_instance
+from catalogue.tasks import refresh_instance, book_content_updated
 from catalogue.xml_tools import compile_text, split_xml
 
 
@@ -36,6 +32,8 @@ class Book(models.Model):
     _single = models.NullBooleanField(editable=False, db_index=True)
     _new_publishable = models.NullBooleanField(editable=False)
     _published = models.NullBooleanField(editable=False)
+    dc_slug = models.CharField(max_length=128, null=True, blank=True,
+            editable=False, db_index=True)
 
     class NoTextError(BaseException):
         pass
@@ -72,6 +70,11 @@ class Book(models.Model):
     def get_absolute_url(self):
         return ("catalogue_book", [self.slug])
 
+    def correct_about(self):
+        return "http://%s%s" % (
+            Site.objects.get_current().domain,
+            self.get_absolute_url()
+        )
 
     # Creating & manipulating
     # =======================
@@ -80,6 +83,7 @@ class Book(models.Model):
         return self.public or request.user.is_authenticated()
 
     @classmethod
+    @transaction.commit_on_success
     def create(cls, creator, text, *args, **kwargs):
         b = cls.objects.create(*args, **kwargs)
         b.chunk_set.all().update(creator=creator)
@@ -91,6 +95,7 @@ class Book(models.Model):
         return self.chunk_set.reverse()[0].split(*args, **kwargs)
 
     @classmethod
+    @transaction.commit_on_success
     def import_xml_text(cls, text=u'', previous_book=None,
                 commit_args=None, **kwargs):
         """Imports a book from XML, splitting it into chunks as necessary."""
@@ -137,6 +142,7 @@ class Book(models.Model):
             i += 1
         return new_slug
 
+    @transaction.commit_on_success
     def append(self, other, slugs=None, titles=None):
         """Add all chunks of another book to self."""
         assert self != other
@@ -184,6 +190,22 @@ class Book(models.Model):
         assert not other.chunk_set.exists()
         other.delete()
 
+    @transaction.commit_on_success
+    def prepend_history(self, other):
+        """Prepend history from all the other book's chunks to own."""
+        assert self != other
+
+        for i in range(len(self), len(other)):
+            title = u"pusta część %d" % i
+            chunk = self.add(slughifi(title), title)
+            chunk.commit('')
+
+        for i in range(len(other)):
+            self[i].prepend_history(other[0])
+
+        assert not other.chunk_set.exists()
+        other.delete()
+
 
     # State & cache
     # =============
@@ -202,6 +224,9 @@ class Book(models.Model):
             raise AssertionError(_('Not all chunks have publishable revisions.'))
         book_xml = self.materialize(changes=changes)
 
+        from librarian.dcparser import BookInfo
+        from librarian import NoDublinCore, ParseError, ValidationError
+
         try:
             bi = BookInfo.from_string(book_xml.encode('utf-8'))
         except ParseError, e:
@@ -211,7 +236,7 @@ class Book(models.Model):
         except ValidationError, e:
             raise AssertionError(_('Invalid Dublin Core') + ': ' + str(e))
 
-        valid_about = "http://%s%s" % (Site.objects.get_current().domain, self.get_absolute_url())
+        valid_about = self.correct_about()
         assert bi.about == valid_about, _("rdf:about is not") + " " + valid_about
 
     def hidden(self):
@@ -248,7 +273,33 @@ class Book(models.Model):
     def short_html(self):
         return render_to_string('catalogue/book_list/book.html', {'book': self})
 
+    def book_info(self, publishable=True):
+        try:
+            book_xml = self.materialize(publishable=publishable)
+        except self.NoTextError:
+            pass
+        else:
+            from librarian.dcparser import BookInfo
+            from librarian import NoDublinCore, ParseError, ValidationError
+            try:
+                return BookInfo.from_string(book_xml.encode('utf-8'))
+            except (self.NoTextError, ParseError, NoDublinCore, ValidationError):
+                return None
+
+    def refresh_dc_cache(self):
+        update = {
+            'dc_slug': None,
+        }
+
+        info = self.book_info()
+        if info is not None:
+            update['dc_slug'] = info.slug
+        Book.objects.filter(pk=self.pk).update(**update)
+
     def touch(self):
+        # this should only really be done when text or publishable status changes
+        book_content_updated.delay(self)
+
         update = {
             "_new_publishable": self.is_new_publishable(),
             "_published": self.is_published(),
@@ -297,6 +348,9 @@ class Book(models.Model):
         """
             Publishes a book on behalf of a (local) user.
         """
+        import apiclient
+        from catalogue.signals import post_publish
+
         self.assert_publishable()
         changes = self.get_current_changes(publishable=True)
         book_xml = self.materialize(changes=changes)