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.utils.timezone import utc
13 from django.utils.translation import ugettext_lazy as _
14 from django.core.urlresolvers import reverse
15 from django.contrib.auth.models import User
16 from django.contrib.contenttypes.models import ContentType
17 from django.contrib.contenttypes import generic
18 from django.conf import settings
20 from jsonfield import JSONField
21 from catalogue.models import Book, Tag
24 class Poem(models.Model):
25 slug = models.SlugField(_('slug'), max_length=120, db_index=True)
26 text = models.TextField(_('text'))
27 created_by = models.ForeignKey(User, null=True)
28 created_from = JSONField(_('extra information'), null=True, blank=True)
29 created_at = models.DateTimeField(_('creation date'), auto_now_add=True, editable=False)
30 seen_at = models.DateTimeField(_('last view date'), auto_now_add=True, editable=False)
31 view_count = models.IntegerField(_('view count'), default=1)
34 f = open(settings.LESMIANATOR_PICKLE)
35 global_dictionary = cPickle.load(f)
38 global_dictionary = {}
42 self.seen_at = datetime.utcnow().replace(tzinfo=utc)
45 def __unicode__(self):
46 return "%s (%s...)" % (self.slug, self.text[:20])
49 def choose_letter(word, continuations):
50 if word not in continuations:
53 choices = sum((continuations[word][letter]
54 for letter in continuations[word]))
55 r = randint(0, choices - 1)
57 for letter in continuations[word]:
58 r -= continuations[word][letter]
63 def write(cls, continuations=None, length=3, min_lines=2, maxlen=1000):
64 if continuations is None:
65 continuations = cls.global_dictionary
72 finished_stanza_verses = 0
73 current_stanza_verses = 0
78 # do `min_lines' non-empty verses and then stop,
79 # but let Lesmianator finish his last stanza.
80 while finished_stanza_verses < min_lines and char_count < maxlen:
81 letter = cls.choose_letter(word, continuations)
82 letters.append(letter)
83 word = word[-length + 1:] + letter
88 finished_stanza_verses += current_stanza_verses
89 current_stanza_verses = 0
91 current_stanza_verses += 1
96 return ''.join(letters).strip()
98 def get_absolute_url(self):
99 return reverse('get_poem', kwargs={'poem': self.slug})
102 class Continuations(models.Model):
103 pickle = models.FileField(_('Continuations file'), upload_to='lesmianator')
104 content_type = models.ForeignKey(ContentType)
105 object_id = models.PositiveIntegerField()
106 content_object = generic.GenericForeignKey('content_type', 'object_id')
109 unique_together = (('content_type', 'object_id'), )
111 def __unicode__(self):
112 return "Continuations for: %s" % unicode(self.content_object)
115 def join_conts(a, b):
117 a.setdefault(pre, {})
119 a[pre].setdefault(post, 0)
120 a[pre][post] += b[pre][post]
124 def for_book(cls, book, length=3):
125 # count from this book only
127 wldoc = book.wldocument(parse_dublincore=False)
128 output = wldoc.as_text(('raw-text',)).get_string()
133 for letter in output.decode('utf-8').strip().lower():
134 mydict = conts.setdefault(last_word, {})
135 mydict.setdefault(letter, 0)
137 last_word = last_word[-length+1:] + letter
139 return reduce(cls.join_conts,
140 (cls.get(child) for child in book.children.all().iterator()),
144 def for_set(cls, tag):
145 # book contains its descendants, we don't want them twice
146 books = Book.tagged.with_any((tag,))
147 l_tags = Tag.objects.filter(category='book', slug__in=[book.book_tag_slug() for book in books.iterator()])
148 descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags).iterator()]
150 books = books.exclude(pk__in=descendants_keys)
152 cont_tabs = (cls.get(b) for b in books.iterator())
153 return reduce(cls.join_conts, cont_tabs)
157 object_type = ContentType.objects.get_for_model(sth)
158 should_keys = set([sth.id])
159 if isinstance(sth, Tag):
160 should_keys = set(b.pk for b in Book.tagged.with_any((sth,)).iterator())
162 obj = cls.objects.get(content_type=object_type, object_id=sth.id)
164 raise cls.DoesNotExist
165 f = open(obj.pickle.path)
166 keys, conts = cPickle.load(f)
168 if set(keys) != should_keys:
169 raise cls.DoesNotExist
171 except cls.DoesNotExist:
172 if isinstance(sth, Book):
173 conts = cls.for_book(sth)
174 elif isinstance(sth, Tag):
175 conts = cls.for_set(sth)
177 raise NotImplemented('Lesmianator continuations: only Book and Tag supported')
179 c, created = cls.objects.get_or_create(content_type=object_type, object_id=sth.id)
180 c.pickle.save(sth.slug+'.p', ContentFile(cPickle.dumps((should_keys, conts))))