314fa1f63fcb82d4a3cc8492cb4e480168aa44b8
[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             # ugly fix
122             if self.slug in ('przedszkole', 'sp1-3', 'sp4-6'):
123                 if lesson.slug == 'slowniczek':
124                     continue
125             else:
126                 if lesson.slug == 'slowniczek-sp':
127                     continue
128             prefix = '%s/' % lesson.slug
129             lesson.add_to_zip(zipf, student, prefix)
130         zipf.close()
131
132         fieldname = "student_package" if student else "package"
133         getattr(self, fieldname).save(None, ContentFile(buff.getvalue()))
134
135     def build_packages(self):
136         self.build_package(False)
137         self.build_package(True)
138
139
140 add_translatable(Level, {
141     'name': models.CharField(_('name'), max_length=255, default=''),
142     'group': models.CharField(_('group'), max_length=255, default='')
143 })
144
145
146 class CompetenceLevel(models.Model):
147     competence = models.ForeignKey(Competence)
148     level = models.ForeignKey(Level)
149
150     class Meta:
151         ordering = ['competence', 'level']
152         verbose_name = _('competence on level')
153         verbose_name_plural = _('competences on levels')
154
155     def __unicode__(self):
156         return u"%s/%s" % (self.competence, self.level)
157
158     def get_absolute_url(self):
159         return "%s?c=%d&level=%s&d=1" % (reverse("curriculum"), self.competence.pk, self.level.slug)
160
161 add_translatable(CompetenceLevel, {
162     'description': models.TextField(_('description'), default='')
163 })
164
165
166 class CurriculumLevel(models.Model):
167     title = models.CharField(max_length=16, db_index=True)
168
169     class Meta:
170         verbose_name = _("curriculum level")
171         verbose_name_plural = _("curriculum levels")
172
173     def __unicode__(self):
174         return self.title
175
176
177 class CurriculumCourse(models.Model):
178     title = models.CharField(max_length=255)
179     accusative = models.CharField(max_length=255)
180     slug = models.CharField(max_length=255, db_index=True)
181
182     class Meta:
183         verbose_name = _("curriculum course")
184         verbose_name_plural = _("curriculum courses")
185         ordering = ['slug']
186
187     def __unicode__(self):
188         return self.title
189
190
191 class Curriculum(models.Model):
192     """Official curriculum."""
193     TYPES = {'c': u'Cele kształcenia', 't': u'Treści nauczania'}
194
195     identifier = models.CharField(max_length=255, db_index=True, unique=True)
196     title = models.CharField(max_length=255)
197     course = models.ForeignKey(CurriculumCourse)
198     level = models.ForeignKey(CurriculumLevel)
199     type = models.CharField(max_length=16, choices=TYPES.items())
200
201     class Meta:
202         ordering = ['identifier']
203         verbose_name = _("curriculum item")
204         verbose_name_plural = _("curriculum items")
205
206     def __unicode__(self):
207         return self.identifier
208
209     @classmethod
210     def from_text(cls, identifier, title):
211         m = re.match(r"^\d+/(?P<level>[^/]+)/(?P<course>[^/]+)/"
212                      r"(?P<type>(?:%s))[^/]+(?P<roz>/roz)?" % "|".join(cls.TYPES), identifier)
213         assert m is not None, "Curriculum identifier doesn't match template."
214         level, created = CurriculumLevel.objects.get_or_create(
215                                        title=m.group('level'))
216         def_title = m.group('course').title()
217         course, created = CurriculumCourse.objects.get_or_create(
218                                         slug=m.group('course').lower(),
219                                         defaults={
220                                             'title': def_title,
221                                             'accusative': def_title,
222                                         })
223         type_ = m.group('type')
224         if m.group('roz'):
225             title += " (zakres rozszerzony)"
226
227         try:
228             curr = cls.objects.get(identifier=identifier)
229         except cls.DoesNotExist:
230             curr = cls(identifier=identifier)
231         curr.title = title
232         curr.course = course
233         curr.level = level
234         curr.type = type_
235         curr.save()
236         return curr