addons-server/apps/stats/models.py

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'