Let the annoying commence.
[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(upload_to=lambda i, f: "curriculum/pack/edukacjamedialna_%s.zip" % i.slug,
77         null=True, blank=True, max_length=255, storage=bofh_storage)
78     student_package = models.FileField(upload_to=lambda i, f: "curriculum/pack/edukacjamedialna_%s_uczen.zip" % i.slug,
79         null=True, blank=True, max_length=255, storage=bofh_storage)
80
81     class Meta:
82         ordering = ['order']
83         verbose_name = _('educational level')
84         verbose_name_plural = _('educational levels')
85
86     def __unicode__(self):
87         return self.name
88
89     def length_course(self):
90         return self.lesson_set.filter(type='course').count()
91
92     def length_synthetic(self):
93         return self.lesson_set.filter(type='synthetic').count()
94
95     def build_package(self, student):
96         from StringIO import StringIO
97         import zipfile
98         from django.core.files.base import ContentFile
99         from catalogue.templatetags.catalogue_tags import level_box
100         from catalogue.models import Lesson
101
102         buff = StringIO()
103         zipf = zipfile.ZipFile(buff, 'w', zipfile.ZIP_STORED)
104
105         lessons = level_box(self)['lessons']
106         for i, lesson in enumerate(lessons['synthetic']):
107             prefix = 'Skrocony kurs/%d %s/' % (i, lesson.slug)
108             lesson.add_to_zip(zipf, student, prefix)
109         for c, (section, clessons) in enumerate(lessons['course'].items()):
110             for i, lesson in enumerate(clessons):
111                 prefix = 'Pelny kurs/%d %s/%d %s/' % (c, section.slug, i, lesson.slug)
112                 lesson.add_to_zip(zipf, student, prefix)
113         for i, lesson in enumerate(lessons['project']):
114             prefix = 'Projekty/%d %s/' % (i, lesson.slug)
115             lesson.add_to_zip(zipf, student, prefix)
116         # Add all appendix lessons, from all levels.
117         for lesson in Lesson.objects.exclude(type__in=('synthetic', 'course', 'project')):
118             prefix = '%s/' % lesson.slug
119             lesson.add_to_zip(zipf, student, prefix)
120         zipf.close()
121
122         fieldname = "student_package" if student else "package"
123         getattr(self, fieldname).save(None, ContentFile(buff.getvalue()))
124
125     def build_packages(self):
126         self.build_package(False)
127         self.build_package(True)
128
129
130
131 add_translatable(Level, {
132     'name': models.CharField(_('name'), max_length=255, default = ''),
133     'group': models.CharField(_('group'), max_length=255, default = '')
134 })
135
136
137 class CompetenceLevel(models.Model):
138     competence = models.ForeignKey(Competence)
139     level = models.ForeignKey(Level)
140
141     class Meta:
142         ordering = ['competence', 'level']
143         verbose_name = _('competence on level')
144         verbose_name_plural = _('competences on levels')
145
146     def __unicode__(self):
147         return u"%s/%s" % (self.competence, self.level)
148
149     def get_absolute_url(self):
150         return "%s?c=%d&level=%s&d=1" % (reverse("curriculum"), self.competence.pk, self.level.slug)
151
152 add_translatable(CompetenceLevel, {
153     'description': models.TextField(_('description'), default = '')
154 })
155
156
157 class CurriculumLevel(models.Model):
158     title = models.CharField(max_length=16, db_index=True)
159
160     class Meta:
161         verbose_name = _("curriculum level")
162         verbose_name_plural = _("curriculum levels")
163
164     def __unicode__(self):
165         return self.title
166
167
168 class CurriculumCourse(models.Model):
169     title = models.CharField(max_length=255)
170     accusative = models.CharField(max_length=255)
171     slug = models.CharField(max_length=255, db_index=True)
172
173     class Meta:
174         verbose_name = _("curriculum course")
175         verbose_name_plural = _("curriculum courses")
176         ordering = ['slug']
177
178     def __unicode__(self):
179         return self.title
180
181
182 class Curriculum(models.Model):
183     """Official curriculum."""
184     TYPES = {'c': u'Cele kształcenia', 't': u'Treści nauczania'}
185
186     identifier = models.CharField(max_length=255, db_index=True)
187     title = models.CharField(max_length=255)
188     course = models.ForeignKey(CurriculumCourse)
189     level = models.ForeignKey(CurriculumLevel)
190     type = models.CharField(max_length=16, choices=TYPES.items())
191
192     class Meta:
193         verbose_name = _("curriculum item")
194         verbose_name_plural = _("curriculum items")
195
196     def __unicode__(self):
197         return self.identifier
198
199     @classmethod
200     def from_text(cls, identifier, title):
201         m = re.match(r"^\d+/(?P<level>[^/]+)/(?P<course>[^/]+)/"
202                      "(?P<type>(?:%s))[^/]+(?P<roz>/roz)?" %
203                         "|".join(cls.TYPES), identifier)
204         assert m is not None, "Curriculum identifier doesn't match template."
205         level, created = CurriculumLevel.objects.get_or_create(
206                                        title=m.group('level'))
207         def_title = m.group('course').title()
208         course, created = CurriculumCourse.objects.get_or_create(
209                                         slug=m.group('course').lower(),
210                                         defaults={
211                                             'title': def_title,
212                                             'accusative': def_title,
213                                         })
214         type_ = m.group('type')
215         if m.group('roz'):
216             title += " (zakres rozszerzony)"
217
218         try:
219             curr = cls.objects.get(identifier=identifier)
220         except cls.DoesNotExist:
221             curr = cls(identifier=identifier)
222         curr.title = title
223         curr.course = course
224         curr.level = level
225         curr.type = type_
226         curr.save()
227         return curr
228