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.timezone import utc
14 from django.utils.translation import ugettext_lazy as _
15 from django.core.urlresolvers import reverse
16 from django.db.models.signals import m2m_changed
17 from django.contrib.auth.models import User
18 from django.contrib.contenttypes.models import ContentType
19 from django.contrib.contenttypes import generic
20 from django.conf import settings
22 from jsonfield 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.utcnow().replace(tzinfo=utc)
47 def __unicode__(self):
48 return "%s (%s...)" % (self.slug, self.text[:20])
51 def choose_letter(word, continuations):
52 if word not in continuations:
55 choices = sum((continuations[word][letter]
56 for letter in continuations[word]))
57 r = randint(0, choices - 1)
59 for letter in continuations[word]:
60 r -= continuations[word][letter]
65 def write(cls, continuations=None, length=3, min_lines=2, maxlen=1000):
66 if continuations is None:
67 continuations = cls.global_dictionary
74 finished_stanza_verses = 0
75 current_stanza_verses = 0
80 # do `min_lines' non-empty verses and then stop,
81 # but let Lesmianator finish his last stanza.
82 while finished_stanza_verses < min_lines and char_count < maxlen:
83 letter = cls.choose_letter(word, continuations)
84 letters.append(letter)
85 word = word[-length + 1:] + letter
90 finished_stanza_verses += current_stanza_verses
91 current_stanza_verses = 0
93 current_stanza_verses += 1
98 return ''.join(letters).strip()
100 def get_absolute_url(self):
101 return reverse('get_poem', kwargs={'poem': self.slug})
104 class Continuations(models.Model):
105 pickle = models.FileField(_('Continuations file'), upload_to='lesmianator')
106 content_type = models.ForeignKey(ContentType)
107 object_id = models.PositiveIntegerField()
108 content_object = generic.GenericForeignKey('content_type', 'object_id')
111 unique_together = (('content_type', 'object_id'), )
113 def __unicode__(self):
114 return "Continuations for: %s" % unicode(self.content_object)
117 def join_conts(a, b):
119 a.setdefault(pre, {})
121 a[pre].setdefault(post, 0)
122 a[pre][post] += b[pre][post]
126 def for_book(cls, book, length=3):
127 # count from this book only
129 wldoc = book.wldocument(parse_dublincore=False)
130 output = wldoc.as_text(('raw-text',)).get_string()
135 for letter in output.decode('utf-8').strip().lower():
136 mydict = conts.setdefault(last_word, {})
137 mydict.setdefault(letter, 0)
139 last_word = last_word[-length+1:] + letter
141 return reduce(cls.join_conts,
142 (cls.get(child) for child in book.children.all().iterator()),
146 def for_set(cls, tag):
147 # book contains its descendants, we don't want them twice
148 books = Book.tagged.with_any((tag,))
149 l_tags = Tag.objects.filter(category='book', slug__in=[book.book_tag_slug() for book in books.iterator()])
150 descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags).iterator()]
152 books = books.exclude(pk__in=descendants_keys)
154 cont_tabs = (cls.get(b) for b in books.iterator())
155 return reduce(cls.join_conts, cont_tabs)
159 object_type = ContentType.objects.get_for_model(sth)
160 should_keys = set([sth.id])
161 if isinstance(sth, Tag):
162 should_keys = set(b.pk for b in Book.tagged.with_any((sth,)).iterator())
164 obj = cls.objects.get(content_type=object_type, object_id=sth.id)
166 raise cls.DoesNotExist
167 f = open(obj.pickle.path)
168 keys, conts = cPickle.load(f)
170 if set(keys) != should_keys:
171 raise cls.DoesNotExist
173 except cls.DoesNotExist:
174 if isinstance(sth, Book):
175 conts = cls.for_book(sth)
176 elif isinstance(sth, Tag):
177 conts = cls.for_set(sth)
179 raise NotImplemented('Lesmianator continuations: only Book and Tag supported')
181 c, created = cls.objects.get_or_create(content_type=object_type, object_id=sth.id)
182 c.pickle.save(sth.slug+'.p', ContentFile(cPickle.dumps((should_keys, conts))))