21eb77746a476079ff229a54ae9f765d330c6bc1
[redakcja.git] / apps / catalogue / models / document.py
1 # -*- coding: utf-8 -*-
2 #
3 # This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later.
4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 #
6 from __future__ import unicode_literals
7
8 from datetime import date
9 from django.conf import settings
10 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
11 from django.db import models
12 from django.template.loader import render_to_string
13 from django.utils.encoding import force_unicode
14 from django.utils.translation import ugettext_lazy as _
15 from dvcs.models import Ref
16 from organizations.models import Organization
17 from catalogue.constants import STAGES
18 from .tag import Tag
19
20
21 class Document(Ref):
22     """ An editable chunk of text."""
23
24     owner_user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
25     owner_organization = models.ForeignKey(Organization, null=True)
26     stage = models.CharField(_('stage'), max_length=128, blank=True, default=STAGES[0][0])
27     assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name='assignments')
28     deleted = models.BooleanField(default=False)
29     tags = models.ManyToManyField(Tag, blank=True)
30     # we need to know if it were ever published (for notifications)
31     published = models.BooleanField(default=False)
32
33     # Where to cache searchable stuff from metadata?
34     # Probably in some kind of search index.
35
36     class Meta:
37         verbose_name = _('document')
38         verbose_name_plural = _('documents')
39
40     def short_html(self):
41         return render_to_string('catalogue/book_list/book.html', {'book': self})
42
43     def meta(self):
44         from lxml import etree
45         metadata = {}
46
47         data = self.materialize()
48         data = data.replace(u'\ufeff', '')
49         # This is bad. The editor shouldn't spew unknown HTML entities.
50         data = data.replace(u' ', u'\u00a0')
51
52         try:
53             t = etree.fromstring(data)
54         except:
55             return {'title': '<<Resource invalid>>'}
56         header = t.find('.//header')
57         if header is None:
58             header = etree.fromstring(data).find('.//{http://nowoczesnapolska.org.pl/sst#}header')
59         metadata['title'] = getattr(header, 'text', ' ') or ' '
60         # print 'meta', d['title']
61
62         m = t.find('metadata')
63         if m is None:
64             m = t.find('{http://nowoczesnapolska.org.pl/sst#}metadata')
65         if m is not None:
66             c = m.find('{http://purl.org/dc/elements/1.1/}relation.coverimage.url')
67             if c is not None:
68                 metadata['cover_url'] = c.text
69             c = m.find('{http://purl.org/dc/elements/1.1/}audience')
70             if c is not None:
71                 metadata['audience'] = c.text
72
73         return metadata
74
75     def can_edit(self, user):
76         if self.owner_user:
77             return self.owner_user == user
78         else:
79             return self.owner_organization.is_member(user)
80
81     def set_stage(self, stage):
82         self.stage = stage
83         plan = self.get_plan()
84         if plan is not None:
85             self.assigned_to = plan.user
86         else:
87             self.assigned_to = None
88         self.save()
89
90     def stage_name(self):
91         return force_unicode(dict(STAGES)[self.stage]) if self.stage else None
92
93     def get_plan(self):
94         try:
95             plan = self.plan_set.get(stage=self.stage)
96         except (ObjectDoesNotExist, MultipleObjectsReturned):
97             return None
98         return plan
99
100     def is_overdue(self):
101         plan = self.get_plan()
102         return plan is not None and plan.deadline and plan.deadline < date.today()