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