annotate downloads/models.py @ 661:15dbe0ccda95

Prevent exceptions when viewing downloads in the admin when the file doesn't exist on the filesystem. This is usually seen in development but can also happen in production if the file is missing.
author Brian Neal <bgneal@gmail.com>
date Tue, 14 May 2013 21:02:47 -0500
parents ee87ea74d46b
children 3e1905e523be
rev   line source
gremmie@1 1 """
gremmie@1 2 Models for the downloads application.
gremmie@1 3 """
gremmie@1 4 import os
gremmie@1 5
gremmie@1 6 import datetime
gremmie@1 7 from django.db import models
gremmie@1 8 from django.contrib.auth.models import User
gremmie@1 9 from django.template.defaultfilters import filesizeformat
gremmie@1 10
bgneal@128 11 from core.markup import site_markup
bgneal@124 12
gremmie@1 13
gremmie@1 14 class Category(models.Model):
gremmie@1 15 """Downloads belong to categories."""
gremmie@1 16 title = models.CharField(max_length=64)
bgneal@241 17 slug = models.SlugField(max_length=64)
gremmie@1 18 description = models.TextField(blank=True)
bgneal@192 19 count = models.IntegerField(default=0, blank=True)
gremmie@1 20
gremmie@1 21 class Meta:
gremmie@1 22 verbose_name_plural = 'Categories'
gremmie@1 23 ordering = ('title', )
gremmie@1 24
gremmie@1 25 def __unicode__(self):
gremmie@1 26 return self.title
gremmie@1 27
gremmie@1 28
gremmie@1 29 def download_path(instance, filename):
gremmie@1 30 """
gremmie@1 31 Creates a path for a download. Uses the current date to avoid filename
gremmie@1 32 clashes. Uses the current microsecond also to make the directory name
gremmie@1 33 harder to guess.
gremmie@1 34 """
gremmie@1 35 now = datetime.datetime.now()
gremmie@1 36 parts = ['downloads']
bgneal@210 37 parts.extend([str(p) for p in (now.year, now.month, now.day)])
bgneal@210 38 parts.append(hex((now.hour * 3600 + now.minute * 60 + now.second) * 1000 + (
bgneal@210 39 now.microsecond / 1000))[2:])
gremmie@1 40 parts.append(filename)
gremmie@1 41 return os.path.join(*parts)
gremmie@1 42
gremmie@1 43
gremmie@1 44 class PublicDownloadManager(models.Manager):
gremmie@1 45 """The manager for all public downloads."""
gremmie@1 46 def get_query_set(self):
bgneal@190 47 return super(PublicDownloadManager, self).get_query_set().filter(
bgneal@190 48 is_public=True).select_related()
gremmie@1 49
gremmie@1 50
bgneal@204 51 class DownloadBase(models.Model):
bgneal@204 52 """Abstract model to collect common download fields."""
gremmie@1 53 title = models.CharField(max_length=128)
gremmie@1 54 category = models.ForeignKey(Category)
gremmie@1 55 description = models.TextField()
gremmie@1 56 html = models.TextField(blank=True)
gremmie@1 57 file = models.FileField(upload_to=download_path)
gremmie@1 58 user = models.ForeignKey(User)
bgneal@277 59 date_added = models.DateTimeField(db_index=True)
gremmie@1 60 ip_address = models.IPAddressField('IP Address')
bgneal@277 61 update_date = models.DateTimeField(db_index=True, blank=True)
bgneal@204 62
bgneal@204 63 class Meta:
bgneal@204 64 abstract = True
bgneal@204 65
bgneal@204 66 def size(self):
bgneal@661 67 try:
bgneal@661 68 return filesizeformat(self.file.size)
bgneal@661 69 except OSError:
bgneal@661 70 return '?'
bgneal@204 71
bgneal@204 72
bgneal@204 73 class PendingDownload(DownloadBase):
bgneal@204 74 """This model represents pending downloads created by users. These pending
bgneal@204 75 downloads must be approved by an admin before they turn into "real"
bgneal@204 76 Downloads and are visible on site.
bgneal@204 77 """
bgneal@204 78 class Meta:
bgneal@204 79 ordering = ('date_added', )
bgneal@204 80
bgneal@204 81 def __unicode__(self):
bgneal@204 82 return self.title
bgneal@204 83
bgneal@204 84 def save(self, *args, **kwargs):
bgneal@204 85 if not self.pk:
bgneal@204 86 self.date_added = datetime.datetime.now()
bgneal@277 87 self.update_date = self.date_added
bgneal@277 88 else:
bgneal@277 89 self.update_date = datetime.datetime.now()
bgneal@277 90
bgneal@204 91 self.html = site_markup(self.description)
bgneal@204 92 super(PendingDownload, self).save(*args, **kwargs)
bgneal@204 93
bgneal@204 94
bgneal@204 95 class Download(DownloadBase):
bgneal@204 96 """Model to represent a download."""
gremmie@1 97 hits = models.IntegerField(default=0)
gremmie@1 98 average_score = models.FloatField(default=0.0)
gremmie@1 99 total_votes = models.IntegerField(default=0)
gremmie@1 100 is_public = models.BooleanField(default=False, db_index=True)
gremmie@1 101
gremmie@1 102 # Managers:
gremmie@1 103 objects = models.Manager()
gremmie@1 104 public_objects = PublicDownloadManager()
gremmie@1 105
gremmie@1 106 def __unicode__(self):
gremmie@1 107 return self.title
gremmie@1 108
bgneal@14 109 @models.permalink
bgneal@14 110 def get_absolute_url(self):
bgneal@23 111 return ('downloads-details', [str(self.id)])
bgneal@14 112
bgneal@182 113 def save(self, *args, **kwargs):
bgneal@277 114 if not self.pk:
bgneal@277 115 self.date_added = datetime.datetime.now()
bgneal@277 116 self.update_date = self.date_added
bgneal@277 117 else:
bgneal@277 118 self.update_date = datetime.datetime.now()
bgneal@277 119
bgneal@128 120 self.html = site_markup(self.description)
bgneal@182 121 super(Download, self).save(*args, **kwargs)
gremmie@1 122
gremmie@1 123 def vote(self, vote_value):
gremmie@1 124 """receives a vote_value and updates internal score accordingly"""
gremmie@1 125 total_score = self.average_score * self.total_votes
gremmie@1 126 total_score += vote_value
gremmie@1 127 self.total_votes += 1
gremmie@1 128 self.average_score = total_score / self.total_votes
gremmie@1 129 return self.average_score
gremmie@1 130
bgneal@221 131 def search_title(self):
bgneal@221 132 return self.title
bgneal@221 133
bgneal@221 134 def search_summary(self):
bgneal@221 135 return self.description
bgneal@221 136
gremmie@1 137
gremmie@1 138 class AllowedExtensionManager(models.Manager):
gremmie@1 139 def get_extension_list(self):
gremmie@1 140 return self.values_list('extension', flat=True)
gremmie@1 141
gremmie@1 142
gremmie@1 143 class AllowedExtension(models.Model):
gremmie@1 144 """Model to represent the list of allowed file extensions."""
bgneal@206 145 extension = models.CharField(max_length=8, help_text="e.g. .txt")
gremmie@1 146
gremmie@1 147 objects = AllowedExtensionManager()
gremmie@1 148
gremmie@1 149 def __unicode__(self):
gremmie@1 150 return self.extension
gremmie@1 151
gremmie@1 152 class Meta:
gremmie@1 153 ordering = ('extension', )
gremmie@1 154
gremmie@1 155
gremmie@1 156 class VoteRecord(models.Model):
gremmie@1 157 """Model to record the date that a user voted on a download."""
gremmie@1 158 download = models.ForeignKey(Download)
gremmie@1 159 user = models.ForeignKey(User)
gremmie@1 160 vote_date = models.DateTimeField(auto_now_add=True)
gremmie@1 161
gremmie@1 162 def __unicode__(self):
bgneal@192 163 return u"%s voted on '%s' on %s" % (
bgneal@412 164 self.user.username,
bgneal@412 165 self.download.title,
gremmie@1 166 self.vote_date.strftime('%b %d, %Y %H:%M:%S'))
gremmie@1 167
gremmie@1 168 class Meta:
gremmie@1 169 ordering = ('-vote_date', )