class Action(models.Model): points_tried = models.PositiveIntegerField(default=5) points_incomplete = models.PositiveIntegerField(default=0) points_correct = models.PositiveIntegerField(default=10) points_correct_at_first_try = models.PositiveIntegerField(default=12) activity = models.ForeignKey(models.Page) name = models.CharField(_('name'), default="Action", max_length=200) short_description = delegate_to('activity') long_description = delegate_to('activity') #def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) #if self.activity is not None: # if self.activity is None: # self.activity = self.activity.specific def clean(self): super().clean() self.activity = self.activity.specific if not isinstance(self.activity, Activity): raise ValidationError({'activity': _('Page is not an activity!')}) #def get_absolute_url(self): #return "badge/" % self.pk # return reverse('action', args=[str(self.pk)]) class Meta(): verbose_name = _('action') verbose_name_plural = _('actions') title = property(lambda x: x.name) def __str__(self): return self.name
class LessonPage(models.CodeschoolPage): """ A single lesson in an ordered list. """ class Meta: verbose_name = _('Lesson') verbose_name_plural = _('Lessons') body = models.StreamField([ ('paragraph', blocks.RichTextBlock()), ], blank=True, null=True) description = delegate_to('lesson') date = delegate_to('lesson') calendar = property(lambda x: x.get_parent()) # Wagtail admin parent_page_types = ['cs_core.CalendarPage'] subpage_types = [] content_panels = models.CodeschoolPage.content_panels + [ panels.StreamFieldPanel('body'), ]
class Calendar(models.Page): """ A page that gathers a list of lessons in the course. """ @property def course(self): return self.get_parent() weekly_lessons = delegate_to('course') def __init__(self, *args, **kwargs): if not args: kwargs.setdefault('title', __('Calendar')) kwargs.setdefault('slug', 'calendar') super().__init__(*args, **kwargs) def add_lesson(self, lesson, copy=True): """ Register a new lesson in the course. If `copy=True` (default), register a copy. """ if copy: lesson = lesson.copy() lesson.move(self) lesson.save() def new_lesson(self, *args, **kwargs): """ Create a new lesson instance by calling the Lesson constructor with the given arguments and add it to the course. """ kwargs['parent_node'] = self return LessonInfo.objects.create(*args, **kwargs) # Wagtail admin parent_page_types = ['courses.Course'] subpage_types = ['courses.Lesson'] content_panels = models.Page.content_panels + [ panels.InlinePanel( 'info', label=_('Lessons'), help_text=_('List of lessons for this course.'), ), ]
class Lesson(models.Page): """ A single lesson in an ordered list. """ class Meta: verbose_name = _('Lesson') verbose_name_plural = _('Lessons') body = models.StreamField([ ('paragraph', blocks.RichTextBlock()), ], blank=True, null=True) date = delegate_to('lesson') calendar = property(lambda x: x.get_parent()) def save(self, *args, **kwargs): lesson = getattr(self, '_created_for_lesson', None) if self.pk is None and lesson is None: calendar = lesson.calendar ordering = calendar.info.values_list('sort_order', flat=True) calendar.lessons.add( Lesson( title=self.title, page=self, sort_order=max(ordering) + 1, )) calendar.save() # Wagtail admin parent_page_types = ['courses.Calendar'] subpage_types = [] content_panels = models.Page.content_panels + [ panels.StreamFieldPanel('body'), ]
class Course(models.RoutablePageMixin, models.CodeschoolPage): """ One specific occurrence of a course for a given teacher in a given period. """ class Meta: parent_init_attribute = 'discipline' teachers = models.ManyToManyField( models.User, related_name='courses_as_teacher', blank=True, ) students = models.ManyToManyField( models.User, related_name='courses_as_student', blank=True, ) staff = models.ManyToManyField( models.User, related_name='courses_as_staff_p', blank=True, ) weekly_lessons = models.BooleanField( _('weekly lessons'), default=False, help_text=_( 'If true, the lesson spans a whole week. Othewise, each lesson ' 'would correspond to a single day/time slot.'), ) accept_subscriptions = models.BooleanField( _('accept subscriptions'), default=True, help_text=_('Set it to false to prevent new student subscriptions.'), ) is_public = models.BooleanField( _('is it public?'), default=False, help_text=_( 'If true, all students will be able to see the contents of the ' 'course. Most activities will not be available to non-subscribed ' 'students.'), ) subscription_passphrase = models.CharField( _('subscription passphrase'), max_length=140, help_text=_( 'A passphrase/word that students must enter to subscribe in the ' 'course. Leave empty if no passphrase should be necessary.'), blank=True, ) objects = PageManager.from_queryset(CourseQueryset)() short_description = delegate_to('discipline', True) long_description = delegate_to('discipline', True) short_description_html = delegate_to('discipline', True) long_description_html = delegate_to('discipline', True) lessons = property(lambda x: x.calendar_page.lessons) @property def calendar_page(self): content_type = models.ContentType.objects.get(app_label='cs_core', model='calendarpage') return apps.get_model('cs_core', 'CalendarPage').objects.get( depth=self.depth + 1, path__startswith=self.path, content_type_id=content_type, ) @property def questions_page(self): content_type = models.ContentType.objects.get(app_label='cs_questions', model='questionlist') return apps.get_model('cs_questions', 'QuestionList').objects.get( depth=self.depth + 1, path__startswith=self.path, content_type_id=content_type, ) @property def gradables_page(self): content_type = models.ContentType.objects.get(app_label='cs_core', model='gradablespage') return apps.get_model('cs_core', 'GradablesPage').objects.get( depth=self.depth + 1, path__startswith=self.path, content_type_id=content_type, ) @property def discipline(self): return self.get_parent().specific @discipline.setter def discipline(self, value): self.set_parent(value) @property def questions(self): return self.questions_page.questions def add_question(self, question, copy=True): """ Register a new question to the course. If `copy=True` (default), register a copy. """ self.questions.add_question(question, copy) def new_question(self, cls, *args, **kwargs): """ Create a new question instance by calling the cls with the given arguments and add it to the course. """ self.questions.new_question(cls, *args, **kwargs) def add_lesson(self, lesson, copy=True): """ Register a new lesson in the course. If `copy=True` (default), register a copy. """ self.lessons.add_lesson(lesson, copy) def new_lesson(self, *args, **kwargs): """ Create a new lesson instance by calling the Lesson constructor with the given arguments and add it to the course. """ self.lessons.new_lesson(*args, **kwargs) def register_student(self, student): """ Register a new student in the course. """ self.students.add(student) self.update_friendship_status(student) def update_friendship_status(self, student=None): """ Recompute the friendship status for a single student by marking it as a colleague of all participants in the course.. If no student is given, update the status of all enrolled students. """ update = self._update_friendship_status if student is None: for student in self.students.all(): update(student) else: update(student) def _update_friendship_status(self, student): # Worker function for update_friendship_status colleague_status = FriendshipStatus.STATUS_COLLEAGUE for colleague in self.students.all(): if colleague != student: try: FriendshipStatus.objects.get(owner=student, other=colleague) except FriendshipStatus.DoesNotExist: FriendshipStatus.objects.create(owner=student, other=colleague, status=colleague_status) def get_absolute_url(self): return url_reverse('course-detail', args=(self.pk, )) def get_user_role(self, user): """Return a string describing the most privileged role the user as in the course. The possible values are: teacher: Owns the course and can do any kind of administrative tasks in the course. staff: Teacher assistants. May have some privileges granted by the teacher. student: Enrolled students. visitor: Have no relation to the course. If course is marked as public, visitors can access the course contents. """ if user == self.teacher: return 'teacher' if user in self.staff.all(): return 'staff' if user in self.students.all(): return 'student' return 'visitor' def get_user_activities(self, user): """ Return a sequence of all activities that are still open for the user. """ activities = self.activities.filter(status=Activity.STATUS_OPEN) return activities.select_subclasses() def activity_duration(self): """ Return the default duration (in minutes) for an activity starting from now. """ return 120 def next_time_slot(self): """Return the start and end times for the next class in the course. If a time slot is currently open, return it.""" now = timezone.now() return now, now + timezone.timedelta(self.activity_duration()) def next_date(self, date=None): """Return the date of the next available time slot.""" def can_view(self, user): return user != annonymous_user() def can_edit(self, user): return user in self.teachers.all() or user == self.owner def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context['activities'] = self.questions return context # Wagtail admin parent_page_types = ['cs_core.Discipline'] subpage_types = [] content_panels = Page.content_panels + [ panels.InlinePanel( 'time_slots', label=_('Time slots'), help_text=_('Define when the weekly classes take place.'), ), ] settings_panels = Page.settings_panels + [ panels.MultiFieldPanel([ panels.FieldPanel('weekly_lessons'), panels.FieldPanel('is_public'), ], heading=_('Options')), panels.MultiFieldPanel([ panels.FieldPanel('accept_subscriptions'), panels.FieldPanel('subscription_passphrase'), ], heading=_('Subscription')), ]
class UserMixin: # Delegate profile properties about_me = delegate_to('profile') date_of_birth = delegate_to('profile') gender = delegate_to('profile') nickname = delegate_to('profile') phone = delegate_to('profile') school_id = delegate_to('profile') website = delegate_to('profile') mugshot = delegate_to('profile') @property def profile(self): try: return self._profile except Profile.DoesNotExist: return Profile.objects.create(user=self) @property def is_student(self): return ... @property def is_teacher(self): return ... def _filtered(self, status): pks = (self.related_users.filter(status=status).values_list('other', flat=True)) return models.User.objects.filter(pk__in=pks).order_by( 'first_name', 'username') @property def friends(self): """A queryset with all the users's friends.""" return self._filtered('friend') @property def unfriends(self): """A queryset with all users the that were un-friended by the current user.""" return self._filtered('unfriend') @property def friends_pending(self): """A queryset with users that have a pending friendship request waiting to be accepted or rejected.""" return self._filtered('pending') def _multi_select(self, field): courses = list(self.enrolled_courses.all()) if not courses: return [] first, *tail = courses users = getattr(first, field).all() for course in tail: users |= getattr(course, field).all() users = users.exclude(pk=self.pk) return users.order_by('first_name', 'username').distinct() @property def colleagues(self): """A queryset of all user's colleagues.""" return self._multi_select('students') @property def staff_contacts(self): """A queryset of all staff members to the user's courses.""" return self._multi_select('staff') @property def teacher_contacts(self): """A queryset of all teachers in the user's enrolled courses.""" pks = self.enrolled_courses.values_list('teacher', flat=True) return models.User.objects.filter(pk__in=pks).distinct() def get_absolute_url(self): return self.profile.url
class Profile(UserenaBaseProfile, models.CodeschoolPage): """ Social information about users. """ class Meta: permissions = ( ('student', _('Can access/modify data visible to student\'s')), ('teacher', _('Can access/modify data visible only to Teacher\'s')), ) username = delegate_to('user', True) first_name = delegate_to('user') last_name = delegate_to('user') email = delegate_to('user') @property def short_description(self): return '%s (id: %s)' % (self.get_full_name_or_username(), self.school_id) @property def age(self): if self.date_of_birth is None: return None today = timezone.now().date() return int(round((today - self.date_of_birth).years)) user = models.OneToOneField( models.User, unique=True, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('user'), related_name='_profile', ) school_id = models.CharField( _('school id'), help_text=_('Identification number in your school issued id card.'), max_length=50, blank=True, null=True) nickname = models.CharField(max_length=50, blank=True, null=True) phone = models.CharField(max_length=20, blank=True, null=True) gender = models.SmallIntegerField(_('gender'), choices=[(0, _('male')), (1, _('female'))], blank=True, null=True) date_of_birth = models.DateField(_('date of birth'), blank=True, null=True) website = models.URLField(blank=True, null=True) about_me = models.RichTextField(blank=True, null=True) objects = ProfileManager.from_queryset(models.PageManager)() def __init__(self, *args, **kwargs): if 'user' in kwargs and 'id' not in kwargs: kwargs.setdefault('parent_page', profile_root()) super().__init__(*args, **kwargs) if self.pk is None and self.user is not None: user = self.user self.title = self.title or __("%(name)s's profile") % { 'name': user.get_full_name() or user.username } self.slug = self.slug or user.username.replace('.', '-') def __str__(self): if self.user is None: return __('Unbound profile') full_name = self.user.get_full_name() or self.user.username return __('%(name)s\'s profile') % {'name': full_name} def get_full_name_or_username(self): name = self.user.get_full_name() if name: return name else: return self.user.username # Wagtail admin parent_page_types = ['cs_core.ProfileRoot'] content_panels = models.CodeschoolPage.content_panels + [ panels.MultiFieldPanel([ panels.FieldPanel('school_id'), ], heading='Required information'), panels.MultiFieldPanel([ panels.FieldPanel('nickname'), panels.FieldPanel('phone'), panels.FieldPanel('gender'), panels.FieldPanel('date_of_birth'), ], heading=_('Personal Info')), panels.MultiFieldPanel([ panels.FieldPanel('website'), ], heading=_('Web presence')), panels.RichTextFieldPanel('about_me'), ]
class A: bar = delegate_to('foo')