290 строки
9.0 KiB
Python
290 строки
9.0 KiB
Python
import datetime
|
|
|
|
from django.conf import settings
|
|
from django.db import models
|
|
from django.template import Context, loader
|
|
|
|
import caching.base
|
|
import tower
|
|
from tower import ugettext as _
|
|
|
|
import amo
|
|
from amo.models import ModelBase
|
|
from amo.fields import DecimalCharField
|
|
from amo.utils import send_mail
|
|
|
|
from .db import StatsDictField, StatsManager
|
|
|
|
|
|
class AddonCollectionCount(caching.base.CachingMixin, models.Model):
|
|
addon = models.ForeignKey('addons.Addon')
|
|
collection = models.ForeignKey('bandwagon.Collection')
|
|
count = models.PositiveIntegerField()
|
|
date = models.DateField()
|
|
|
|
class Meta:
|
|
db_table = 'stats_addons_collections_counts'
|
|
|
|
|
|
class CollectionCount(caching.base.CachingMixin, models.Model):
|
|
collection = models.ForeignKey('bandwagon.Collection')
|
|
count = models.PositiveIntegerField()
|
|
date = models.DateField()
|
|
|
|
objects = models.Manager()
|
|
stats = StatsManager('date')
|
|
|
|
class Meta:
|
|
db_table = 'stats_collections_counts'
|
|
|
|
|
|
class CollectionStats(caching.base.CachingMixin, models.Model):
|
|
"""In the running for worst-named model ever."""
|
|
collection = models.ForeignKey('bandwagon.Collection')
|
|
name = models.CharField(max_length=255, null=True)
|
|
count = models.PositiveIntegerField()
|
|
date = models.DateField()
|
|
|
|
class Meta:
|
|
db_table = 'stats_collections'
|
|
|
|
|
|
class DownloadCount(caching.base.CachingMixin, models.Model):
|
|
addon = models.ForeignKey('addons.Addon')
|
|
count = models.PositiveIntegerField()
|
|
date = models.DateField()
|
|
|
|
# Leave this out of queries if you can.
|
|
sources = StatsDictField(db_column='src', null=True)
|
|
|
|
objects = models.Manager()
|
|
stats = StatsManager('date')
|
|
|
|
class Meta:
|
|
db_table = 'download_counts'
|
|
|
|
def flush_urls(self):
|
|
return ['*/addon/%d/statistics/downloads*' % self.addon_id, ]
|
|
|
|
|
|
class UpdateCount(caching.base.CachingMixin, models.Model):
|
|
addon = models.ForeignKey('addons.Addon')
|
|
count = models.PositiveIntegerField()
|
|
date = models.DateField()
|
|
|
|
# Leave these out of queries if you can.
|
|
versions = StatsDictField(db_column='version', null=True)
|
|
statuses = StatsDictField(db_column='status', null=True)
|
|
applications = StatsDictField(db_column='application', null=True)
|
|
oses = StatsDictField(db_column='os', null=True)
|
|
locales = StatsDictField(db_column='locale', null=True)
|
|
|
|
objects = models.Manager()
|
|
stats = StatsManager('date')
|
|
|
|
class Meta:
|
|
db_table = 'update_counts'
|
|
|
|
def flush_urls(self):
|
|
return ['*/addon/%d/statistics/usage*' % self.addon_id, ]
|
|
|
|
|
|
class AddonShareCount(caching.base.CachingMixin, models.Model):
|
|
addon = models.ForeignKey('addons.Addon')
|
|
count = models.PositiveIntegerField()
|
|
service = models.CharField(max_length=255, null=True)
|
|
date = models.DateField()
|
|
|
|
objects = models.Manager()
|
|
stats = StatsManager('date')
|
|
|
|
class Meta:
|
|
db_table = 'stats_share_counts'
|
|
|
|
|
|
class AddonShareCountTotal(caching.base.CachingMixin, models.Model):
|
|
addon = models.ForeignKey('addons.Addon')
|
|
count = models.PositiveIntegerField()
|
|
service = models.CharField(max_length=255, null=True)
|
|
|
|
objects = caching.base.CachingManager()
|
|
stats = caching.base.CachingManager()
|
|
|
|
class Meta:
|
|
db_table = 'stats_share_counts_totals'
|
|
|
|
|
|
# stats_collections_share_counts exists too, but we don't touch it.
|
|
class CollectionShareCountTotal(caching.base.CachingMixin, models.Model):
|
|
collection = models.ForeignKey('bandwagon.Collection')
|
|
count = models.PositiveIntegerField()
|
|
service = models.CharField(max_length=255, null=True)
|
|
|
|
objects = caching.base.CachingManager()
|
|
|
|
class Meta:
|
|
db_table = 'stats_collections_share_counts_totals'
|
|
|
|
|
|
class ContributionError(Exception):
|
|
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __str__(self):
|
|
return repr(self.value)
|
|
|
|
|
|
class Contribution(caching.base.CachingMixin, models.Model):
|
|
addon = models.ForeignKey('addons.Addon')
|
|
charity = models.ForeignKey('addons.Charity', null=True)
|
|
amount = DecimalCharField(max_digits=9, decimal_places=2,
|
|
nullify_invalid=True, null=True)
|
|
source = models.CharField(max_length=255, null=True)
|
|
source_locale = models.CharField(max_length=10, null=True)
|
|
annoying = models.PositiveIntegerField(
|
|
choices=amo.CONTRIB_CHOICES, default=0)
|
|
created = models.DateTimeField(auto_now_add=True)
|
|
uuid = models.CharField(max_length=255, null=True)
|
|
is_suggested = models.BooleanField()
|
|
suggested_amount = DecimalCharField(max_digits=254, decimal_places=2,
|
|
nullify_invalid=True, null=True)
|
|
comment = models.CharField(max_length=255)
|
|
transaction_id = models.CharField(max_length=255, null=True)
|
|
post_data = StatsDictField(null=True)
|
|
|
|
objects = models.Manager()
|
|
stats = StatsManager('created')
|
|
|
|
class Meta:
|
|
db_table = 'stats_contributions'
|
|
|
|
def __unicode__(self):
|
|
return u'%s: %s' % (self.addon.name, self.amount)
|
|
|
|
def flush_urls(self):
|
|
return ['*/addon/%d/statistics/contributions*' % self.addon_id, ]
|
|
|
|
@property
|
|
def date(self):
|
|
try:
|
|
return datetime.date(self.created.year,
|
|
self.created.month, self.created.day)
|
|
except AttributeError:
|
|
# created may be None
|
|
return None
|
|
|
|
@property
|
|
def contributor(self):
|
|
try:
|
|
return u'%s %s' % (self.post_data['first_name'],
|
|
self.post_data['last_name'])
|
|
except (TypeError, KeyError):
|
|
# post_data may be None or missing a key
|
|
return None
|
|
|
|
@property
|
|
def email(self):
|
|
try:
|
|
return self.post_data['payer_email']
|
|
except (TypeError, KeyError):
|
|
# post_data may be None or missing a key
|
|
return None
|
|
|
|
def mail_thankyou(self, request=None):
|
|
"""
|
|
Mail a thankyou note for a completed contribution.
|
|
|
|
Raises a ``ContributionError`` exception when the contribution
|
|
is not complete or email addresses are not found.
|
|
"""
|
|
|
|
# Setup l10n before loading addon.
|
|
if self.source_locale:
|
|
lang = self.source_locale
|
|
else:
|
|
lang = self.addon.default_locale
|
|
tower.activate(lang)
|
|
|
|
# Thankyous must be enabled.
|
|
if not self.addon.enable_thankyou:
|
|
# Not an error condition, just return.
|
|
return
|
|
|
|
# Contribution must be complete.
|
|
if not self.transaction_id:
|
|
raise ContributionError('Transaction not complete')
|
|
|
|
# Send from support_email, developer's email, or default.
|
|
from_email = settings.DEFAULT_FROM_EMAIL
|
|
if self.addon.support_email:
|
|
from_email = str(self.addon.support_email)
|
|
else:
|
|
try:
|
|
author = self.addon.listed_authors[0]
|
|
if author.email and not author.emailhidden:
|
|
from_email = author.email
|
|
except (IndexError, TypeError):
|
|
# This shouldn't happen, but the default set above is still ok.
|
|
pass
|
|
|
|
# We need the contributor's email.
|
|
to_email = self.post_data['payer_email']
|
|
if not to_email:
|
|
raise ContributionError('Empty payer email')
|
|
|
|
# Make sure the url uses the right language.
|
|
# Setting a prefixer would be nicer, but that requires a request.
|
|
url_parts = self.addon.meet_the_dev_url().split('/')
|
|
url_parts[1] = lang
|
|
|
|
# Buildup the email components.
|
|
t = loader.get_template('stats/contribution-thankyou-email.ltxt')
|
|
c = {
|
|
'thankyou_note': self.addon.thankyou_note,
|
|
'addon_name': self.addon.name,
|
|
'learn_url': '%s%s?src=emailinfo' % (settings.SITE_URL,
|
|
'/'.join(url_parts)),
|
|
'domain': settings.DOMAIN,
|
|
}
|
|
body = t.render(Context(c))
|
|
subject = _('Thanks for contributing to {addon_name}').format(
|
|
addon_name=self.addon.name)
|
|
|
|
# Send the email
|
|
if send_mail(subject, body, from_email, [to_email],
|
|
fail_silently=True):
|
|
# Clear out contributor identifying information.
|
|
del(self.post_data['payer_email'])
|
|
self.save()
|
|
|
|
@staticmethod
|
|
def post_save(sender, instance, **kwargs):
|
|
from . import tasks
|
|
tasks.addon_total_contributions.delay(instance.addon_id)
|
|
|
|
|
|
models.signals.post_save.connect(Contribution.post_save, sender=Contribution)
|
|
|
|
|
|
class SubscriptionEvent(ModelBase):
|
|
"""Save subscription info for future processing."""
|
|
post_data = StatsDictField()
|
|
|
|
class Meta:
|
|
db_table = 'subscription_events'
|
|
|
|
|
|
class GlobalStat(caching.base.CachingMixin, models.Model):
|
|
name = models.CharField(max_length=255)
|
|
count = models.IntegerField()
|
|
date = models.DateField()
|
|
|
|
objects = caching.base.CachingManager()
|
|
stats = caching.base.CachingManager()
|
|
|
|
class Meta:
|
|
db_table = 'global_stats'
|
|
unique_together = ('name', 'date')
|
|
get_latest_by = 'date'
|