diff bio/models.py @ 581:ee87ea74d46b

For Django 1.4, rearranged project structure for new manage.py.
author Brian Neal <bgneal@gmail.com>
date Sat, 05 May 2012 17:10:48 -0500
parents gpp/bio/models.py@98b373ca09f3
children 678a1a2ef55a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bio/models.py	Sat May 05 17:10:48 2012 -0500
@@ -0,0 +1,216 @@
+"""
+Contains models for the bio application.
+I would have picked profile for this application, but that is already taken, apparently.
+"""
+import datetime
+import os.path
+
+from django.db import models
+from django.contrib.auth.models import User
+from django.conf import settings
+from django.core.cache import cache
+from django.template.loader import render_to_string
+
+from core.markup import SiteMarkup
+
+
+# These are the secondary user status enumeration values. 
+(STA_ACTIVE,        # User is a full member in good standing.
+ STA_RESIGNED,      # User has voluntarily asked to be removed.
+ STA_REMOVED,       # User was removed for bad behavior.
+ STA_SUSPENDED,     # User is temporarily suspended; e.g. a stranger tripped
+                    # the spam filter.
+ STA_SPAMMER,       # User has been removed for spamming.
+ STA_STRANGER,      # New member, isn't fully trusted yet. Their comments and
+                    # forum posts are scanned for spam. They can have their
+                    # accounts deactivated by moderators for spamming.
+ ) = range(6)
+
+USER_STATUS_CHOICES = (
+    (STA_ACTIVE, "Active"),
+    (STA_RESIGNED, "Resigned"),
+    (STA_REMOVED, "Removed"),
+    (STA_SUSPENDED, "Suspended"),
+    (STA_SPAMMER, "Spammer"),
+    (STA_STRANGER, "Stranger")
+)
+
+
+class Badge(models.Model):
+    """This model represents badges that users can earn."""
+    image = models.ImageField(upload_to='badges')
+    name = models.CharField(max_length=64)
+    description = models.TextField(blank=True)
+    order = models.IntegerField()
+    numeric_id = models.IntegerField(db_index=True)
+
+    class Meta:
+        ordering = ('order', )
+
+    def __unicode__(self):
+        return self.name
+
+    def get_absolute_url(self):
+        return self.image.url
+
+    def html(self):
+        """Returns a HTML img tag representation of the badge."""
+        if self.image:
+            return u'<img src="%s" alt="%s" title="%s" />' % (
+                    self.get_absolute_url(), self.name, self.name)
+        return u''
+    html.allow_tags = True
+
+
+def avatar_file_path(instance, filename):
+    ext = os.path.splitext(filename)[1]
+    if not ext:
+        ext = '.jpg'
+    avatar_name = instance.user.username + ext
+    return os.path.join(settings.AVATAR_DIR, 'users', avatar_name)
+
+
+class UserProfile(models.Model):
+    """model to represent additional information about users"""
+
+    user = models.ForeignKey(User, unique=True)
+    location = models.CharField(max_length=128, blank=True)
+    birthday = models.DateField(blank=True, null=True,
+            help_text='Optional; the year is not shown to others')
+    occupation = models.CharField(max_length=128, blank=True)
+    interests = models.CharField(max_length=255, blank=True)
+    profile_text = models.TextField(blank=True)
+    profile_html = models.TextField(blank=True)
+    hide_email = models.BooleanField(default=True)
+    signature = models.TextField(blank=True)
+    signature_html = models.TextField(blank=True)
+    avatar = models.ImageField(upload_to=avatar_file_path, blank=True)
+    time_zone = models.CharField(max_length=64, blank=True,
+            default='US/Pacific')
+    use_24_time = models.BooleanField(default=False)
+    forum_post_count = models.IntegerField(default=0)
+    status = models.IntegerField(default=STA_STRANGER,
+            choices=USER_STATUS_CHOICES)
+    status_date = models.DateTimeField(auto_now_add=True)
+    badges = models.ManyToManyField(Badge, through="BadgeOwnership")
+    update_date = models.DateTimeField(db_index=True, blank=True)
+    auto_favorite = models.BooleanField(default=False)
+    auto_subscribe = models.BooleanField(default=False)
+
+    def __unicode__(self):
+        return self.user.username
+
+    class Meta:
+        ordering = ('user__username', )
+
+    def save(self, *args, **kwargs):
+        """
+        Custom profile save() function.
+        If content_update is True (default), then it is assumed that major
+        fields are being updated and that the profile_content_update signal
+        should be signalled. When content_update is False, the update_date is
+        not updated, expensive markup conversions are not performed, and the
+        signal is not signalled. This is useful for updating the
+        forum_post_count, for example.
+
+        """
+        content_update = kwargs.pop('content_update', True)
+
+        if content_update:
+            self.update_date = datetime.datetime.now()
+            sm = SiteMarkup()
+            self.profile_html = sm.convert(self.profile_text)
+            self.signature_html = sm.convert(self.signature)
+            cache.delete('avatar_' + self.user.username)
+
+        super(UserProfile, self).save(*args, **kwargs)
+
+        if content_update:
+            notify_profile_content_update(self)
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ('bio-view_profile', (), {'username': self.user.username})
+
+    def badge_ownership(self):
+        return BadgeOwnership.objects.filter(profile=self).select_related('badge')
+
+    def is_stranger(self):
+        """Returns True if this user profile status is STA_STRANGER."""
+        return self.status == STA_STRANGER
+
+    def user_is_active(self):
+        """Returns the profile's user is_active status. This function exists
+        for the admin.
+        """
+        return self.user.is_active
+    user_is_active.boolean = True
+    user_is_active.short_description = "Is Active"
+
+    def reset_text_fields(self):
+        """
+        Reset profile text fields to empty defaults.
+        This function is useful when a spammer is identified.
+
+        """
+        self.location = ''
+        self.occupation = ''
+        self.interests = ''
+        self.profile_text = ''
+        self.signature = ''
+
+    def search_title(self):
+        full_name = self.user.get_full_name()
+        if full_name:
+            return u"%s (%s)" % (self.user.username, full_name)
+        return self.user.username
+
+    def search_summary(self):
+        text = render_to_string('search/indexes/bio/userprofile_text.txt',
+                {'object': self});
+        return text
+
+
+class UserProfileFlag(models.Model):
+    """This model represents a user flagging a profile as inappropriate."""
+    user = models.ForeignKey(User)
+    profile = models.ForeignKey(UserProfile)
+    flag_date = models.DateTimeField(auto_now_add=True)
+
+    def __unicode__(self):
+        return u"%s's profile flagged by %s" % (self.profile.user.username,
+                self.user.username)
+
+    class Meta:
+        ordering = ('flag_date', )
+
+    def get_profile_url(self):
+        return '<a href="%s">Profile</a>' % self.profile.get_absolute_url()
+    get_profile_url.allow_tags = True
+
+
+class BadgeOwnership(models.Model):
+    """This model represents the ownership of badges by users."""
+    profile = models.ForeignKey(UserProfile)
+    badge = models.ForeignKey(Badge)
+    count = models.IntegerField(default=1)
+
+    class Meta:
+        verbose_name_plural = "badge ownership"
+        ordering = ('badge__order', )
+
+    def __unicode__(self):
+        if self.count == 1:
+            return u"%s owns 1 %s" % (self.profile.user.username,
+                    self.badge.name)
+        else:
+            return u"%s owns %d %s badges" % (self.profile.user.username,
+                    self.count, self.badge.name)
+
+    def badge_count_str(self):
+        if self.count == 1:
+            return u"1 %s" % self.badge.name
+        return u"%d %ss" % (self.count, self.badge.name)
+
+# Put down here to avoid a circular import
+from bio.signals import notify_profile_content_update