class NoticeReadTracker(models.Model): """ Save per user notice read tracking """ user = models.ForeignKey(get_user_model_path(), blank=False, null=False) notice = models.ForeignKey(Notice, blank=True, null=True) time_stamp = models.DateTimeField(auto_now=True) objects = NoticeReadTrackerManager() class Meta(object): verbose_name = _('Notice read tracker') verbose_name_plural = _('Notice read trackers') unique_together = ('user', 'notice')
class Profile(NoticeappProfile): """ Profile class that can be used if you doesn't have your site profile. """ user = AutoOneToOneField(get_user_model_path(), related_name='noticeapp_profile', verbose_name=_('User')) class Meta(object): verbose_name = _('Profile') verbose_name_plural = _('Profiles') def get_absolute_url(self): return reverse( 'noticeapp:user', kwargs={'username': getattr(self.user, get_username_field())})
class PollAnswerUser(models.Model): poll_answer = models.ForeignKey(PollAnswer, related_name='users', verbose_name=_('Poll answer')) user = models.ForeignKey(get_user_model_path(), related_name='poll_answers', verbose_name=_('User')) timestamp = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = _('Poll answer user') verbose_name_plural = _('Polls answers users') unique_together = (( 'poll_answer', 'user', ), ) def __str__(self): return '%s - %s' % (self.poll_answer.course, self.user)
# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models from noticeapp.compat import get_image_field_full_name, get_user_model_path, get_user_frozen_models AUTH_USER = get_user_model_path() class Migration(SchemaMigration): def forwards(self, orm): # Deleting field 'Attachment.hash' db.delete_column('noticeapp_attachment', 'hash') # Deleting field 'Attachment.content_type' db.delete_column('noticeapp_attachment', 'content_type') # Deleting field 'Attachment.path' db.delete_column('noticeapp_attachment', 'path') # Deleting field 'Attachment.name' db.delete_column('noticeapp_attachment', 'name') def backwards(self, orm): # Adding field 'Attachment.hash'
# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models from noticeapp.compat import get_image_field_full_name, get_user_model_path, get_user_frozen_models AUTH_USER = get_user_model_path() class Migration(SchemaMigration): def forwards(self, orm): # Adding unique constraint on 'CourseReadTracker', fields ['course', 'user'] db.create_unique('noticeapp_coursereadtracker', ['course_id', 'user_id']) # Adding unique constraint on 'NoticeReadTracker', fields ['user', 'notice'] db.create_unique('noticeapp_noticereadtracker', ['user_id', 'notice_id']) def backwards(self, orm): # Removing unique constraint on 'NoticeReadTracker', fields ['user', 'notice'] db.delete_unique('noticeapp_noticereadtracker', ['user_id', 'notice_id']) # Removing unique constraint on 'CourseReadTracker', fields ['course', 'user'] db.delete_unique('noticeapp_coursereadtracker', ['course_id', 'user_id']) models = { 'auth.group': {
class Notice(models.Model): category = models.ForeignKey(Category, related_name='notices', verbose_name=_('Category')) parent = models.ForeignKey('self', related_name='child_notices', verbose_name=_('Parent notice'), blank=True, null=True) name = models.CharField(_('Name'), max_length=80) position = models.IntegerField(_('Position'), blank=True, default=0) description = models.TextField(_('Description'), blank=True) moderators = models.ManyToManyField(get_user_model_path(), blank=True, null=True, verbose_name=_('Moderators')) updated = models.DateTimeField(_('Updated'), blank=True, null=True) post_count = models.IntegerField(_('Post count'), blank=True, default=0) course_count = models.IntegerField(_('Course count'), blank=True, default=0) hidden = models.BooleanField(_('Hidden'), blank=False, null=False, default=False) readed_by = models.ManyToManyField(get_user_model_path(), through='NoticeReadTracker', related_name='readed_notices') headline = models.TextField(_('Headline'), blank=True, null=True) class Meta(object): ordering = ['position'] verbose_name = _('Notice') verbose_name_plural = _('Notices') def __str__(self): return self.name def update_counters(self): posts = Post.objects.filter(course__notice_id=self.id) self.post_count = posts.count() self.course_count = Course.objects.filter(notice=self).count() try: last_post = posts.order_by('-created', '-id')[0] self.updated = last_post.updated or last_post.created except IndexError: pass self.save() def get_absolute_url(self): return reverse('noticeapp:notice', kwargs={'pk': self.id}) @property def posts(self): return Post.objects.filter(course__notice=self).select_related() @property def last_post(self): try: return self.posts.order_by('-created', '-id')[0] except IndexError: return None def get_parents(self): """ Used in templates for breadcrumb building """ parents = [self.category] parent = self.parent while parent is not None: parents.insert(1, parent) parent = parent.parent return parents
class Post(RenderableItem): course = models.ForeignKey(Course, related_name='posts', verbose_name=_('Course')) user = models.ForeignKey(get_user_model_path(), related_name='posts', verbose_name=_('User')) created = models.DateTimeField(_('Created'), blank=True, db_index=True) updated = models.DateTimeField(_('Updated'), blank=True, null=True) user_ip = models.IPAddressField(_('User IP'), blank=True, default='0.0.0.0') on_moderation = models.BooleanField(_('On moderation'), default=False) class Meta(object): ordering = ['created'] verbose_name = _('Post') verbose_name_plural = _('Posts') def summary(self): limit = 50 tail = len(self.body) > limit and '...' or '' return self.body[:limit] + tail def __str__(self): return self.summary() def save(self, *args, **kwargs): created_at = tznow() if self.created is None: self.created = created_at self.render() new = self.pk is None course_changed = False old_post = None if not new: old_post = Post.objects.get(pk=self.pk) if old_post.course != self.course: course_changed = True super(Post, self).save(*args, **kwargs) # If post is course head and moderated, moderate course too if self.course.head == self and not self.on_moderation and self.course.on_moderation: self.course.on_moderation = False self.course.update_counters() self.course.notice.update_counters() if course_changed: old_post.course.update_counters() old_post.course.notice.update_counters() def get_absolute_url(self): return reverse('noticeapp:post', kwargs={'pk': self.id}) def delete(self, *args, **kwargs): self_id = self.id head_post_id = self.course.posts.order_by('created', 'id')[0].id if self_id == head_post_id: self.course.delete() else: super(Post, self).delete(*args, **kwargs) self.course.update_counters() self.course.notice.update_counters() def get_parents(self): """ Used in templates for breadcrumb building """ return self.course.notice.category, self.course.notice, self.course,
class Course(models.Model): POLL_TYPE_NONE = 0 POLL_TYPE_SINGLE = 1 POLL_TYPE_MULTIPLE = 2 POLL_TYPE_CHOICES = ( (POLL_TYPE_NONE, _('None')), (POLL_TYPE_SINGLE, _('Single answer')), (POLL_TYPE_MULTIPLE, _('Multiple answers')), ) notice = models.ForeignKey(Notice, related_name='courses', verbose_name=_('Notice')) name = models.CharField(_('Subject'), max_length=255) created = models.DateTimeField(_('Created'), null=True) updated = models.DateTimeField(_('Updated'), null=True) user = models.ForeignKey(get_user_model_path(), verbose_name=_('User'), related_name='notice_user') views = models.IntegerField(_('Views count'), blank=True, default=0) sticky = models.BooleanField(_('Sticky'), blank=True, default=False) closed = models.BooleanField(_('Closed'), blank=True, default=False) subscribers = models.ManyToManyField(get_user_model_path(), related_name='subscriptions', verbose_name=_('Subscribers'), blank=True) post_count = models.IntegerField(_('Post count'), blank=True, default=0) readed_by = models.ManyToManyField(get_user_model_path(), through='CourseReadTracker', related_name='readed_courses') on_moderation = models.BooleanField(_('On moderation'), default=False) poll_type = models.IntegerField(_('Poll type'), choices=POLL_TYPE_CHOICES, default=POLL_TYPE_NONE) poll_question = models.TextField(_('Poll question'), blank=True, null=True) class Meta(object): ordering = ['-created'] verbose_name = _('Course') verbose_name_plural = _('Courses') def __str__(self): return self.name @property def head(self): """ Get first post and cache it for request """ if not hasattr(self, "_head"): self._head = self.posts.all().order_by('created', 'id') if not len(self._head): return None return self._head[0] @property def last_post(self): if not getattr(self, '_last_post', None): self._last_post = self.posts.order_by( '-created', '-id').select_related('user')[0] return self._last_post def get_absolute_url(self): return reverse('noticeapp:course', kwargs={'pk': self.id}) def save(self, *args, **kwargs): if self.id is None: self.created = tznow() self.updated = tznow() notice_changed = False old_course = None if self.id is not None: old_course = Course.objects.get(id=self.id) if self.notice != old_course.notice: notice_changed = True super(Course, self).save(*args, **kwargs) if notice_changed: old_course.notice.update_counters() self.notice.update_counters() def delete(self, using=None): super(Course, self).delete(using) self.notice.update_counters() def update_counters(self): self.post_count = self.posts.count() last_post = Post.objects.filter(course_id=self.id).order_by( '-created', '-id')[0] self.updated = last_post.updated or last_post.created self.save() def get_parents(self): """ Used in templates for breadcrumb building """ parents = self.notice.get_parents() parents.append(self.notice) return parents def poll_votes(self): if self.poll_type != self.POLL_TYPE_NONE: return PollAnswerUser.objects.filter( poll_answer__course=self).count() else: return None