fix for when there are no students
[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             for i, lesson in enumerate(clessons):
113                 prefix = 'Pelny kurs/%d %s/%d %s/' % (c, section.slug, i, lesson.slug)
114                 lesson.add_to_zip(zipf, student, prefix)
115         for i, lesson in enumerate(lessons['project']):
116             prefix = 'Projekty/%d %s/' % (i, lesson.slug)
117             lesson.add_to_zip(zipf, student, prefix)
118         # Add all appendix lessons, from all levels.
119         for lesson in Lesson.objects.filter(type='appendix'):
120             prefix = '%s/' % lesson.slug
121             lesson.add_to_zip(zipf, student, prefix)
122         zipf.close()
123
124         fieldname = "student_package" if student else "package"
125         getattr(self, fieldname).save(None, ContentFile(buff.getvalue()))
126
127     def build_packages(self):
128         self.build_package(False)
129         self.build_package(True)
130
131
132 add_translatable(Level, {
133     'name': models.CharField(_('name'), max_length=255, default=''),
134     'group': models.CharField(_('group'), max_length=255, default='')
135 })
136
137
138 class CompetenceLevel(models.Model):
139     competence = models.ForeignKey(Competence)
140     level = models.ForeignKey(Level)
141
142     class Meta:
143         ordering = ['competence', 'level']
144         verbose_name = _('competence on level')
145         verbose_name_plural = _('competences on levels')
146
147     def __unicode__(self):
148         return u"%s/%s" % (self.competence, self.level)
149
150     def get_absolute_url(self):
151         return "%s?c=%d&level=%s&d=1" % (reverse("curriculum"), self.competence.pk, self.level.slug)
152
153 add_translatable(CompetenceLevel, {
154     'description': models.TextField(_('description'), default='')
155 })
156
157
158 class CurriculumLevel(models.Model):
159     title = models.CharField(max_length=16, db_index=True)
160
161     class Meta:
162         verbose_name = _("curriculum level")
163         verbose_name_plural = _("curriculum levels")
164
165     def __unicode__(self):
166         return self.title
167
168
169 class CurriculumCourse(models.Model):
170     title = models.CharField(max_length=255)
171     accusative = models.CharField(max_length=255)
172     slug = models.CharField(max_length=255, db_index=True)
173
174     class Meta:
175         verbose_name = _("curriculum course")
176         verbose_name_plural = _("curriculum courses")
177         ordering = ['slug']
178
179     def __unicode__(self):
180         return self.title
181
182
183 class Curriculum(models.Model):
184     """Official curriculum."""
185     TYPES = {'c': u'Cele kształcenia', 't': u'Treści nauczania'}
186
187     identifier = models.CharField(max_length=255, db_index=True)
188     title = models.CharField(max_length=255)
189     course = models.ForeignKey(CurriculumCourse)
190     level = models.ForeignKey(CurriculumLevel)
191     type = models.CharField(max_length=16, choices=TYPES.items())
192
193     class Meta:
194         verbose_name = _("curriculum item")
195         verbose_name_plural = _("curriculum items")
196
197     def __unicode__(self):
198         return self.identifier
199
200     @classmethod
201     def from_text(cls, identifier, title):
202         m = re.match(r"^\d+/(?P<level>[^/]+)/(?P<course>[^/]+)/"
203                      r"(?P<type>(?:%s))[^/]+(?P<roz>/roz)?" % "|".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