annotate bio/models.py @ 989:2908859c2fe4

Smilies now use relative links. This is for upcoming switch to SSL. Currently we do not need absolute URLs for smilies. If this changes we can add it later.
author Brian Neal <bgneal@gmail.com>
date Thu, 29 Oct 2015 20:54:34 -0500
parents 0b6bf9c5a982
children eeaf387803c6
rev   line source
gremmie@1 1 """
gremmie@1 2 Contains models for the bio application.
gremmie@1 3 I would have picked profile for this application, but that is already taken, apparently.
gremmie@1 4 """
bgneal@277 5 import datetime
gremmie@1 6 import os.path
gremmie@1 7
gremmie@1 8 from django.db import models
bgneal@259 9 from django.contrib.auth.models import User
gremmie@1 10 from django.conf import settings
bgneal@54 11 from django.core.cache import cache
bgneal@277 12 from django.template.loader import render_to_string
gremmie@1 13
bgneal@128 14 from core.markup import SiteMarkup
bgneal@609 15 import bio.flags
bgneal@919 16 from bio.signals import notify_profile_content_update
bgneal@124 17
bgneal@562 18
bgneal@215 19 # These are the secondary user status enumeration values.
bgneal@215 20 (STA_ACTIVE, # User is a full member in good standing.
bgneal@215 21 STA_RESIGNED, # User has voluntarily asked to be removed.
bgneal@215 22 STA_REMOVED, # User was removed for bad behavior.
bgneal@215 23 STA_SUSPENDED, # User is temporarily suspended; e.g. a stranger tripped
bgneal@215 24 # the spam filter.
bgneal@215 25 STA_SPAMMER, # User has been removed for spamming.
bgneal@215 26 STA_STRANGER, # New member, isn't fully trusted yet. Their comments and
bgneal@215 27 # forum posts are scanned for spam. They can have their
bgneal@215 28 # accounts deactivated by moderators for spamming.
bgneal@215 29 ) = range(6)
bgneal@147 30
bgneal@147 31 USER_STATUS_CHOICES = (
bgneal@147 32 (STA_ACTIVE, "Active"),
bgneal@147 33 (STA_RESIGNED, "Resigned"),
bgneal@147 34 (STA_REMOVED, "Removed"),
bgneal@147 35 (STA_SUSPENDED, "Suspended"),
bgneal@147 36 (STA_SPAMMER, "Spammer"),
bgneal@215 37 (STA_STRANGER, "Stranger")
bgneal@147 38 )
bgneal@147 39
bgneal@204 40
bgneal@204 41 class Badge(models.Model):
bgneal@204 42 """This model represents badges that users can earn."""
bgneal@204 43 image = models.ImageField(upload_to='badges')
bgneal@204 44 name = models.CharField(max_length=64)
bgneal@204 45 description = models.TextField(blank=True)
bgneal@204 46 order = models.IntegerField()
bgneal@204 47 numeric_id = models.IntegerField(db_index=True)
bgneal@204 48
bgneal@204 49 class Meta:
bgneal@204 50 ordering = ('order', )
bgneal@204 51
bgneal@204 52 def __unicode__(self):
bgneal@204 53 return self.name
bgneal@204 54
bgneal@204 55 def get_absolute_url(self):
bgneal@204 56 return self.image.url
bgneal@204 57
bgneal@204 58 def html(self):
bgneal@204 59 """Returns a HTML img tag representation of the badge."""
bgneal@204 60 if self.image:
bgneal@204 61 return u'<img src="%s" alt="%s" title="%s" />' % (
bgneal@204 62 self.get_absolute_url(), self.name, self.name)
bgneal@204 63 return u''
bgneal@204 64 html.allow_tags = True
bgneal@204 65
bgneal@204 66
gremmie@1 67 def avatar_file_path(instance, filename):
bgneal@666 68 # The avatar's name is the username plus the current time plus the file
bgneal@666 69 # extension. The time is used to bust browser caches.
bgneal@666 70 # Get extension, if it exists:
bgneal@560 71 ext = os.path.splitext(filename)[1]
bgneal@560 72 if not ext:
bgneal@560 73 ext = '.jpg'
bgneal@666 74 avatar_name = "{}{}{}".format(instance.user.username,
bgneal@666 75 datetime.datetime.utcnow().strftime('%S%f'), ext)
bgneal@560 76 return os.path.join(settings.AVATAR_DIR, 'users', avatar_name)
gremmie@1 77
gremmie@1 78
gremmie@1 79 class UserProfile(models.Model):
gremmie@1 80 """model to represent additional information about users"""
gremmie@1 81
bgneal@789 82 user = models.OneToOneField(User, related_name='profile')
gremmie@1 83 location = models.CharField(max_length=128, blank=True)
bgneal@609 84 country = models.CharField(max_length=2, blank=True, default='',
bgneal@609 85 choices=bio.flags.FLAG_CHOICES,
bgneal@609 86 help_text='Optional')
gremmie@1 87 birthday = models.DateField(blank=True, null=True,
gremmie@1 88 help_text='Optional; the year is not shown to others')
gremmie@1 89 occupation = models.CharField(max_length=128, blank=True)
gremmie@1 90 interests = models.CharField(max_length=255, blank=True)
gremmie@1 91 profile_text = models.TextField(blank=True)
gremmie@1 92 profile_html = models.TextField(blank=True)
gremmie@1 93 hide_email = models.BooleanField(default=True)
gremmie@1 94 signature = models.TextField(blank=True)
gremmie@1 95 signature_html = models.TextField(blank=True)
gremmie@1 96 avatar = models.ImageField(upload_to=avatar_file_path, blank=True)
bgneal@70 97 time_zone = models.CharField(max_length=64, blank=True,
bgneal@70 98 default='US/Pacific')
bgneal@120 99 use_24_time = models.BooleanField(default=False)
bgneal@96 100 forum_post_count = models.IntegerField(default=0)
bgneal@215 101 status = models.IntegerField(default=STA_STRANGER,
bgneal@147 102 choices=USER_STATUS_CHOICES)
bgneal@147 103 status_date = models.DateTimeField(auto_now_add=True)
bgneal@204 104 badges = models.ManyToManyField(Badge, through="BadgeOwnership")
bgneal@277 105 update_date = models.DateTimeField(db_index=True, blank=True)
bgneal@390 106 auto_favorite = models.BooleanField(default=False)
bgneal@390 107 auto_subscribe = models.BooleanField(default=False)
gremmie@1 108
gremmie@1 109 def __unicode__(self):
gremmie@1 110 return self.user.username
gremmie@1 111
gremmie@1 112 class Meta:
gremmie@1 113 ordering = ('user__username', )
gremmie@1 114
gremmie@1 115 def save(self, *args, **kwargs):
bgneal@562 116 """
bgneal@562 117 Custom profile save() function.
bgneal@562 118 If content_update is True (default), then it is assumed that major
bgneal@562 119 fields are being updated and that the profile_content_update signal
bgneal@562 120 should be signalled. When content_update is False, the update_date is
bgneal@562 121 not updated, expensive markup conversions are not performed, and the
bgneal@562 122 signal is not signalled. This is useful for updating the
bgneal@562 123 forum_post_count, for example.
bgneal@562 124
bgneal@562 125 """
bgneal@562 126 content_update = kwargs.pop('content_update', True)
bgneal@562 127
bgneal@562 128 if content_update:
bgneal@562 129 self.update_date = datetime.datetime.now()
bgneal@562 130 sm = SiteMarkup()
bgneal@562 131 self.profile_html = sm.convert(self.profile_text)
bgneal@562 132 self.signature_html = sm.convert(self.signature)
bgneal@562 133 cache.delete('avatar_' + self.user.username)
bgneal@562 134
gremmie@1 135 super(UserProfile, self).save(*args, **kwargs)
bgneal@562 136
bgneal@562 137 if content_update:
bgneal@562 138 notify_profile_content_update(self)
bgneal@138 139
bgneal@138 140 @models.permalink
bgneal@138 141 def get_absolute_url(self):
bgneal@138 142 return ('bio-view_profile', (), {'username': self.user.username})
bgneal@138 143
bgneal@204 144 def badge_ownership(self):
bgneal@329 145 return BadgeOwnership.objects.filter(profile=self).select_related('badge')
bgneal@204 146
bgneal@215 147 def is_stranger(self):
bgneal@215 148 """Returns True if this user profile status is STA_STRANGER."""
bgneal@215 149 return self.status == STA_STRANGER
bgneal@215 150
bgneal@215 151 def user_is_active(self):
bgneal@215 152 """Returns the profile's user is_active status. This function exists
bgneal@215 153 for the admin.
bgneal@215 154 """
bgneal@215 155 return self.user.is_active
bgneal@215 156 user_is_active.boolean = True
bgneal@215 157 user_is_active.short_description = "Is Active"
bgneal@215 158
bgneal@625 159 def reset(self):
bgneal@363 160 """
bgneal@625 161 Reset profile fields to empty defaults.
bgneal@363 162 This function is useful when a spammer is identified.
bgneal@363 163
bgneal@363 164 """
bgneal@363 165 self.location = ''
bgneal@625 166 self.country = ''
bgneal@625 167 self.birthday = None
bgneal@363 168 self.occupation = ''
bgneal@363 169 self.interests = ''
bgneal@363 170 self.profile_text = ''
bgneal@363 171 self.signature = ''
bgneal@363 172
bgneal@223 173 def search_title(self):
bgneal@223 174 full_name = self.user.get_full_name()
bgneal@223 175 if full_name:
bgneal@223 176 return u"%s (%s)" % (self.user.username, full_name)
bgneal@223 177 return self.user.username
bgneal@223 178
bgneal@223 179 def search_summary(self):
bgneal@277 180 text = render_to_string('search/indexes/bio/userprofile_text.txt',
bgneal@277 181 {'object': self});
bgneal@277 182 return text
bgneal@223 183
bgneal@138 184
bgneal@138 185 class UserProfileFlag(models.Model):
bgneal@138 186 """This model represents a user flagging a profile as inappropriate."""
bgneal@259 187 user = models.ForeignKey(User)
bgneal@138 188 profile = models.ForeignKey(UserProfile)
bgneal@138 189 flag_date = models.DateTimeField(auto_now_add=True)
bgneal@138 190
bgneal@138 191 def __unicode__(self):
bgneal@138 192 return u"%s's profile flagged by %s" % (self.profile.user.username,
bgneal@138 193 self.user.username)
bgneal@138 194
bgneal@138 195 class Meta:
bgneal@138 196 ordering = ('flag_date', )
bgneal@138 197
bgneal@138 198 def get_profile_url(self):
bgneal@138 199 return '<a href="%s">Profile</a>' % self.profile.get_absolute_url()
bgneal@138 200 get_profile_url.allow_tags = True
bgneal@204 201
bgneal@204 202
bgneal@204 203 class BadgeOwnership(models.Model):
bgneal@204 204 """This model represents the ownership of badges by users."""
bgneal@204 205 profile = models.ForeignKey(UserProfile)
bgneal@204 206 badge = models.ForeignKey(Badge)
bgneal@204 207 count = models.IntegerField(default=1)
bgneal@204 208
bgneal@204 209 class Meta:
bgneal@204 210 verbose_name_plural = "badge ownership"
bgneal@204 211 ordering = ('badge__order', )
bgneal@204 212
bgneal@204 213 def __unicode__(self):
bgneal@204 214 if self.count == 1:
bgneal@204 215 return u"%s owns 1 %s" % (self.profile.user.username,
bgneal@204 216 self.badge.name)
bgneal@204 217 else:
bgneal@204 218 return u"%s owns %d %s badges" % (self.profile.user.username,
bgneal@204 219 self.count, self.badge.name)
bgneal@204 220
bgneal@204 221 def badge_count_str(self):
bgneal@204 222 if self.count == 1:
bgneal@204 223 return u"1 %s" % self.badge.name
bgneal@204 224 return u"%d %ss" % (self.count, self.badge.name)