1 # -*- coding: utf-8 -*-
 
   2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 
   3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 
   6 from datetime import datetime
 
   7 from random import randint
 
   8 from StringIO import StringIO
 
  10 from django.core.files.base import ContentFile
 
  11 from django.db import models
 
  12 from django.db.models import permalink
 
  13 from django.utils.translation import ugettext_lazy as _
 
  14 from django.core.urlresolvers import reverse
 
  15 from django.db.models.signals import m2m_changed
 
  16 from django.contrib.auth.models import User
 
  17 from django.contrib.contenttypes.models import ContentType
 
  18 from django.contrib.contenttypes import generic
 
  19 from django.conf import settings
 
  21 from librarian import text
 
  22 from catalogue.fields import JSONField
 
  23 from catalogue.models import Book, Tag
 
  26 class Poem(models.Model):
 
  27     slug = models.SlugField(_('slug'), max_length=120, db_index=True)
 
  28     text = models.TextField(_('text'))
 
  29     created_by = models.ForeignKey(User, null=True)
 
  30     created_from = JSONField(_('extra information'), null=True, blank=True)
 
  31     created_at = models.DateTimeField(_('creation date'), auto_now_add=True, editable=False)
 
  32     seen_at = models.DateTimeField(_('last view date'), auto_now_add=True, editable=False)
 
  33     view_count = models.IntegerField(_('view count'), default=1)
 
  36         f = open(settings.LESMIANATOR_PICKLE)
 
  37         global_dictionary = cPickle.load(f)
 
  40         global_dictionary = {}
 
  44         self.seen_at = datetime.now()
 
  47     def __unicode__(self):
 
  48         return "%s (%s...)" % (self.slug, self.text[:20])
 
  51     def write(cls, continuations=None, length=3, maxlen=1000):
 
  52         def choose_word(word, continuations):
 
  54                 choices = sum((continuations[word][post] for post in continuations[word]))
 
  55                 r = randint(0, choices - 1)
 
  57                 for post in continuations[word]:
 
  58                     r -= continuations[word][post]
 
  65         if continuations is None:
 
  66             continuations = cls.global_dictionary
 
  74         # want at least two lines, but let Lesmianator end his stanzas
 
  75         while (empty < 2 or lines < 2) and maxlen:
 
  76             letter = choose_word(word, continuations)
 
  77             letters.append(letter)
 
  78             word = word[-length+1:] + letter
 
  80                 # count non-empty lines
 
  91         return ''.join(letters).strip()
 
  93     def get_absolute_url(self):
 
  94         return reverse('get_poem', kwargs={'poem': self.slug})
 
  97 class Continuations(models.Model):
 
  98     pickle = models.FileField(_('Continuations file'), upload_to='lesmianator')
 
  99     content_type = models.ForeignKey(ContentType)
 
 100     object_id = models.PositiveIntegerField()
 
 101     content_object = generic.GenericForeignKey('content_type', 'object_id')
 
 103     def __unicode__(self):
 
 104         return "Continuations for: %s" % unicode(self.content_object)
 
 107     def join_conts(a, b):
 
 109             a.setdefault(pre, {})
 
 111                 a[pre].setdefault(post, 0)
 
 112                 a[pre][post] += b[pre][post]
 
 116     def for_book(cls, book, length=3):
 
 117         # count from this book only
 
 118         print 'for_book', book
 
 120         f = open(book.xml_file.path)
 
 121         text.transform(f, output, False, ('raw-text',))
 
 125         for letter in output.getvalue().decode('utf-8').strip().lower():
 
 126             mydict = conts.setdefault(last_word, {})
 
 127             mydict.setdefault(letter, 0)
 
 129             last_word = last_word[-length+1:] + letter
 
 131         return reduce(cls.join_conts, 
 
 132                       (cls.get(child) for child in book.children.all()),
 
 136     def for_set(cls, tag):
 
 137         # book contains its descendants, we don't want them twice
 
 138         books = Book.tagged.with_any((tag,))
 
 139         l_tags = Tag.objects.filter(category='book', slug__in=[book.book_tag_slug() for book in books])
 
 140         descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags)]
 
 142             books = books.exclude(pk__in=descendants_keys)
 
 144         cont_tabs = (cls.get(b) for b in books)
 
 145         return reduce(cls.join_conts, cont_tabs)
 
 149         object_type = ContentType.objects.get_for_model(sth)
 
 150         should_keys = set([sth.id])
 
 151         if isinstance(sth, Tag):
 
 152             should_keys = set(b.pk for b in Book.tagged.with_any((sth,)))
 
 154             obj = cls.objects.get(content_type=object_type, object_id=sth.id)
 
 155             f = open(obj.pickle.path)
 
 156             keys, conts = cPickle.load(f)
 
 158             if set(keys) != should_keys:
 
 160                 raise cls.DoesNotExist
 
 162         except cls.DoesNotExist:
 
 163             if isinstance(sth, Book):
 
 164                 conts = cls.for_book(sth)
 
 165             elif isinstance(sth, Tag):
 
 166                 conts = cls.for_set(sth)
 
 168                 raise NotImplemented('Lesmianator continuations: only Book and Tag supported')
 
 170             c = cls(content_object=sth)
 
 171             c.pickle.save(sth.slug+'.p', ContentFile(cPickle.dumps((should_keys, conts))))