republish lessons - don't stop on exceptions
[edumed.git] / curriculum / models.py
1 # -*- coding: utf-8
2 import re
3 from django.core.urlresolvers import reverse
4 from django.db import models
5 from django.utils.translation import ugettext_lazy as _, get_language
6 from fnpdjango.storage import BofhFileSystemStorage
7 from fnpdjango.utils.models.translation import add_translatable
8
9 bofh_storage = BofhFileSystemStorage()
10
11
12 class Section(models.Model):
13     slug = models.SlugField(_('slug'))
14     order = models.IntegerField(_('order'))
15
16     class Meta:
17         ordering = ['order']
18         verbose_name = _('section')
19         verbose_name_plural = _('sections')
20
21     def __unicode__(self):
22         return self.name
23
24     def get_absolute_url(self):
25         return "%s?s=%d" % (reverse("curriculum"), self.pk)
26
27     def url_for_level(self, level):
28         return "%s?s=%d&level=%s&d=1" % (reverse("curriculum"), self.pk, level.slug)
29
30 add_translatable(Section, {
31     'name': models.CharField(_('name'), max_length=255, default='')
32 })
33
34
35 class Competence(models.Model):
36     section = models.ForeignKey(Section)
37     slug = models.SlugField(_('slug'))
38     order = models.IntegerField(_('order'))
39
40     class Meta:
41         ordering = ['section', 'order']
42         verbose_name = _('competence')
43         verbose_name_plural = _('competences')
44
45     def __unicode__(self):
46         return self.name
47
48     def get_absolute_url(self):
49         return "%s?c=%d" % (reverse("curriculum"), self.pk)
50
51     def for_level(self, level):
52         return self.competencelevel_set.get(level=level)
53
54     def url_for_level(self, level):
55         return self.for_level(level).get_absolute_url()
56
57     @classmethod
58     def from_text(cls, text):
59         """Tries to return a Competence or a Section."""
60         parts = re.split(ur'[-\u2013]', text, 1)
61         lookup_field_name = 'name_%s__iexact' % get_language()
62         if len(parts) == 1:
63             return Section.objects.get(**{lookup_field_name: text.strip()})
64         else:
65             return cls.objects.get(**{lookup_field_name: parts[1].strip()})
66
67 add_translatable(Competence, {
68     'name': models.CharField(_('name'), max_length=255, default='')
69 })
70
71
72 class Level(models.Model):
73     slug = models.CharField(_('slug'), max_length=255, unique=True)
74     meta_name = models.CharField(_('meta name'), max_length=255, unique=True)
75     order = models.IntegerField(_('order'))
76     package = models.FileField(
77         upload_to=lambda i, f: "curriculum/pack/edukacjamedialna_%s.zip" % i.slug,
78         null=True, blank=True, max_length=255, storage=bofh_storage)
79     student_package = models.FileField(
80         upload_to=lambda i, f: "curriculum/pack/edukacjamedialna_%s_uczen.zip" % i.slug,
81         null=True, blank=True, max_length=255, storage=bofh_storage)
82
83     class Meta:
84         ordering = ['order']
85         verbose_name = _('educational level')
86         verbose_name_plural = _('educational levels')
87
88     def __unicode__(self):
89         return self.name
90
91     def length_course(self):
92         return self.lesson_set.filter(type='course').count()
93
94     def length_synthetic(self):
95         return self.lesson_set.filter(type='synthetic').count()
96
97     def build_package(self, student):
98         from StringIO import StringIO
99         import zipfile
100         from django.core.files.base import ContentFile
101         from catalogue.templatetags.catalogue_tags import level_box
102         from catalogue.models import Lesson
103
104         buff = StringIO()
105         zipf = zipfile.ZipFile(buff, 'w', zipfile.ZIP_STORED)
106
107         lessons = level_box(self)['lessons']
108         for i, lesson in enumerate(lessons['synthetic']):
109             prefix = 'Skrocony kurs/%d %s/' % (i, lesson.slug)
110             lesson.add_to_zip(zipf, student, prefix)
111         for c, (section, clessons) in enumerate(lessons['course'].items()):
112             assert section, clessons
113             for i, lesson in enumerate(clessons):
114                 prefix = 'Pelny kurs/%d %s/%d %s/' % (c, section.slug, i, lesson.slug)
115                 lesson.add_to_zip(zipf, student, prefix)
116         for i, lesson in enumerate(lessons['project']):
117             prefix = 'Projekty/%d %s/' % (i, lesson.slug)
118             lesson.add_to_zip(zipf, student, prefix)
119         # Add all appendix lessons, from all levels.
120         for lesson in Lesson.objects.filter(type='appendix'):
121             prefix = '%s/' % lesson.slug
122             lesson.add_to_zip(zipf, student, prefix)
123         zipf.close()
124
125         fieldname = "student_package" if student else "package"
126         getattr(self, fieldname).save(None, ContentFile(buff.getvalue()))
127
128     def build_packages(self):
129         self.build_package(False)
130         self.build_package(True)
131
132
133 add_translatable(Level, {
134     'name': models.CharField(_('name'), max_length=255, default=''),
135     'group': models.CharField(_('group'), max_length=255, default='')
136 })
137
138
139 class CompetenceLevel(models.Model):
140     competence = models.ForeignKey(Competence)
141     level = models.ForeignKey(Level)
142
143     class Meta:
144         ordering = ['competence', 'level']
145         verbose_name = _('competence on level')
146         verbose_name_plural = _('competences on levels')
147
148     def __unicode__(self):
149         return u"%s/%s" % (self.competence, self.level)
150
151     def get_absolute_url(self):
152         return "%s?c=%d&level=%s&d=1" % (reverse("curriculum"), self.competence.pk, self.level.slug)
153
154 add_translatable(CompetenceLevel, {
155     'description': models.TextField(_('description'), default='')
156 })
157
158
159 class CurriculumLevel(models.Model):
160     title = models.CharField(max_length=16, db_index=True)
161
162     class Meta:
163         verbose_name = _("curriculum level")
164         verbose_name_plural = _("curriculum levels")
165
166     def __unicode__(self):
167         return self.title
168
169
170 class CurriculumCourse(models.Model):
171     title = models.CharField(max_length=255)
172     accusative = models.CharField(max_length=255)
173     slug = models.CharField(max_length=255, db_index=True)
174
175     class Meta:
176         verbose_name = _("curriculum course")
177         verbose_name_plural = _("curriculum courses")
178         ordering = ['slug']
179
180     def __unicode__(self):
181         return self.title
182
183
184 class Curriculum(models.Model):
185     """Official curriculum."""
186     TYPES = {'c': u'Cele kształcenia', 't': u'Treści nauczania'}
187
188     identifier = models.CharField(max_length=255, db_index=True)
189     title = models.CharField(max_length=255)
190     course = models.ForeignKey(CurriculumCourse)
191     level = models.ForeignKey(CurriculumLevel)
192     type = models.CharField(max_length=16, choices=TYPES.items())
193
194     class Meta:
195         verbose_name = _("curriculum item")
196         verbose_name_plural = _("curriculum items")
197
198     def __unicode__(self):
199         return self.identifier
200
201     @classmethod
202     def from_text(cls, identifier, title):
203         m = re.match(r"^\d+/(?P<level>[^/]+)/(?P<course>[^/]+)/"
204                      r"(?P<type>(?:%s))[^/]+(?P<roz>/roz)?" % "|".join(cls.TYPES), identifier)
205         assert m is not None, "Curriculum identifier doesn't match template."
206         level, created = CurriculumLevel.objects.get_or_create(
207                                        title=m.group('level'))
208         def_title = m.group('course').title()
209         course, created = CurriculumCourse.objects.get_or_create(
210                                         slug=m.group('course').lower(),
211                                         defaults={
212                                             'title': def_title,
213                                             'accusative': def_title,
214                                         })
215         type_ = m.group('type')
216         if m.group('roz'):
217             title += " (zakres rozszerzony)"
218
219         try:
220             curr = cls.objects.get(identifier=identifier)
221         except cls.DoesNotExist:
222             curr = cls(identifier=identifier)
223         curr.title = title
224         curr.course = course
225         curr.level = level
226         curr.type = type_
227         curr.save()
228         return curr