Account lookup summary data (bug 753979)
This commit is contained in:
Родитель
b709bd7802
Коммит
0f4250e638
|
@ -0,0 +1,13 @@
|
|||
from jingo import register
|
||||
import jinja2
|
||||
|
||||
|
||||
@register.filter
|
||||
@jinja2.contextfilter
|
||||
def format_currencies(context, currencies):
|
||||
cs = ', '.join(['%s %.2f' % (code, amount)
|
||||
for code, amount in currencies.items()
|
||||
if amount > 0.0])
|
||||
if cs:
|
||||
cs = '(%s)' % cs
|
||||
return jinja2.Markup(cs)
|
|
@ -0,0 +1,72 @@
|
|||
{% extends 'acct_lookup/base.html' %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="island">
|
||||
{{ _('Summary') }}
|
||||
</section>
|
||||
|
||||
<section class="island c">
|
||||
<h2>{{ account.email }} ({{ account.pk }})</h2>
|
||||
<dl>
|
||||
<dt>{{ _('Display Name') }}:</dt>
|
||||
<dd>{{ account.display_name }}</dd>
|
||||
<dt>{{ _('Username') }}:</dt>
|
||||
<dd>{{ account.username }}</dd>
|
||||
<dt>{{ _('Registered') }}:</dt>
|
||||
<dd>{{ account.created|babel_datetime }}</dd>
|
||||
<dt>UGC:</dt>
|
||||
<dd>?</dd>
|
||||
<dt>{{ _('Groups') }}:</dt>
|
||||
<dd>
|
||||
{% for group in account.groups.all() %}
|
||||
<div>{{ group }}</div>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
</dl>
|
||||
<h2>{{ _('Payments') }}</h2>
|
||||
<dl>
|
||||
<dt>{{ _('PayPal linked') }}:</dt>
|
||||
<dd>TODO</dd>
|
||||
<dt>{{ _('Marketplace Credit') }}:</dt>
|
||||
<dd>TODO</dd>
|
||||
<dt>{{ _('Payments') }}:</dt>
|
||||
<dd>{{ _('apps purchased') }}: {{ app_summary['app_total'] }}
|
||||
{{ app_summary['app_amount']|format_currencies }}</dd>
|
||||
<dd>{{ _('in-app payments') }}: {{ app_summary['inapp_total'] }}
|
||||
{{ app_summary['inapp_amount']|format_currencies }}</dd>
|
||||
<dt>{{ _('Refunds') }}:</dt>
|
||||
<dd>{{ refund_summary['requested'] }} {{ _('requested' ) }}</dd>
|
||||
<dd>{{ refund_summary['approved'] }} {{ _('auto-approved') }}</dd>
|
||||
</dl>
|
||||
<h2>{{ _('Developer Program') }}</h2>
|
||||
<dl>
|
||||
<dt>{{ _('Read agreement') }}:</dt>
|
||||
<dd>
|
||||
{% if account.read_dev_agreement %}
|
||||
{{ _('Yes') }}
|
||||
{% else %}
|
||||
{{ _('No') }}
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{{ _('Address') }}:</dt>
|
||||
<dd>TODO</dd>
|
||||
<dt>{{ _('Submissions') }}:</dt>
|
||||
<dd>
|
||||
{% for addon in user_addons %}
|
||||
<div><a href="{{ addon.get_detail_url() }}">
|
||||
{% if addon.name %}
|
||||
{{ addon.name }}
|
||||
{% else %}
|
||||
{{ _('Unnamed') }}
|
||||
{% endif %}
|
||||
</a>
|
||||
({{ amo.ADDON_TYPE[addon.type] }})</div>
|
||||
{% endfor %}
|
||||
</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
|
@ -1,17 +1,102 @@
|
|||
from decimal import Decimal
|
||||
|
||||
from nose.tools import eq_
|
||||
|
||||
from addons.models import Addon
|
||||
import amo
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.tests import TestCase
|
||||
from amo.tests import TestCase, app_factory
|
||||
from market.models import Refund
|
||||
from stats.models import Contribution
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
class TestViews(TestCase):
|
||||
fixtures = ['base/users.json']
|
||||
fixtures = ['base/users', 'webapps/337141-steamcube']
|
||||
|
||||
def setUp(self):
|
||||
assert self.client.login(username='support-staff@mozilla.com',
|
||||
password='password')
|
||||
self.user_id = 31337 # steamcube
|
||||
self.steamcube = Addon.objects.get(pk=337141)
|
||||
self.otherapp = app_factory(app_slug='otherapp')
|
||||
self.reg_user = UserProfile.objects.get(email='regular@mozilla.com')
|
||||
self.summary_url = reverse('acct_lookup.summary', args=[self.user_id])
|
||||
|
||||
def test_authorization(self):
|
||||
def buy_stuff(self, contrib_type):
|
||||
for i in range(3):
|
||||
if i == 1:
|
||||
curr = 'GBR'
|
||||
else:
|
||||
curr = 'USD'
|
||||
amount = Decimal('2.00')
|
||||
Contribution.objects.create(addon=self.steamcube,
|
||||
type=contrib_type,
|
||||
currency=curr,
|
||||
amount=amount,
|
||||
user_id=self.user_id)
|
||||
|
||||
def summary(self, expected_status=200):
|
||||
res = self.client.get(self.summary_url)
|
||||
eq_(res.status_code, expected_status)
|
||||
return res
|
||||
|
||||
def test_home_auth(self):
|
||||
self.client.logout()
|
||||
res = self.client.get(reverse('acct_lookup.home'))
|
||||
self.assertLoginRedirects(res, reverse('acct_lookup.home'))
|
||||
|
||||
def test_summary_auth(self):
|
||||
self.client.logout()
|
||||
res = self.client.get(self.summary_url)
|
||||
self.assertLoginRedirects(res, self.summary_url)
|
||||
|
||||
def test_home(self):
|
||||
res = self.client.get(reverse('acct_lookup.home'))
|
||||
self.assertNoFormErrors(res)
|
||||
eq_(res.status_code, 200)
|
||||
|
||||
def test_basic_summary(self):
|
||||
res = self.summary()
|
||||
eq_(res.context['account'].pk, self.user_id)
|
||||
|
||||
def test_app_counts(self):
|
||||
self.buy_stuff(amo.CONTRIB_PURCHASE)
|
||||
sm = self.summary().context['app_summary']
|
||||
eq_(sm['app_total'], 3)
|
||||
eq_(sm['app_amount']['USD'], 4.0)
|
||||
eq_(sm['app_amount']['GBR'], 2.0)
|
||||
|
||||
def test_inapp_counts(self):
|
||||
self.buy_stuff(amo.CONTRIB_INAPP)
|
||||
sm = self.summary().context['app_summary']
|
||||
eq_(sm['inapp_total'], 3)
|
||||
eq_(sm['inapp_amount']['USD'], 4.0)
|
||||
eq_(sm['inapp_amount']['GBR'], 2.0)
|
||||
|
||||
def test_requested_refunds(self):
|
||||
contrib = Contribution.objects.create(type=amo.CONTRIB_PURCHASE,
|
||||
user_id=self.user_id,
|
||||
addon=self.steamcube,
|
||||
currency='USD',
|
||||
amount='0.99')
|
||||
Refund.objects.create(contribution=contrib)
|
||||
res = self.summary()
|
||||
eq_(res.context['refund_summary']['requested'], 1)
|
||||
eq_(res.context['refund_summary']['approved'], 0)
|
||||
|
||||
def test_approved_refunds(self):
|
||||
contrib = Contribution.objects.create(type=amo.CONTRIB_PURCHASE,
|
||||
user_id=self.user_id,
|
||||
addon=self.steamcube,
|
||||
currency='USD',
|
||||
amount='0.99')
|
||||
Refund.objects.create(contribution=contrib,
|
||||
status=amo.REFUND_APPROVED_INSTANT)
|
||||
res = self.summary()
|
||||
eq_(res.context['refund_summary']['requested'], 1)
|
||||
eq_(res.context['refund_summary']['approved'], 1)
|
||||
|
||||
def test_app_created(self):
|
||||
res = self.summary()
|
||||
eq_(len(res.context['user_addons']), 1)
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
from django.conf.urls.defaults import patterns, url
|
||||
from django.conf.urls.defaults import patterns, url, include
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url('^$', views.home, name='acct_lookup.home'),
|
||||
# These views all start with user ID.
|
||||
detail_patterns = patterns('',
|
||||
url(r'^summary$', views.summary, name='acct_lookup.summary'),
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.home, name='acct_lookup.home'),
|
||||
(r'''^(?P<user_id>[^/<>"']+)/''', include(detail_patterns)),
|
||||
)
|
||||
|
|
|
@ -1,9 +1,71 @@
|
|||
from django.db import connection
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
import jingo
|
||||
|
||||
import amo
|
||||
from amo.decorators import login_required, permission_required
|
||||
from market.models import Refund
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('AccountLookup', 'View')
|
||||
def home(request):
|
||||
return jingo.render(request, 'acct_lookup/home.html', {})
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('AccountLookup', 'View')
|
||||
def summary(request, user_id):
|
||||
user = get_object_or_404(UserProfile, pk=user_id)
|
||||
app_summary = _app_summary(user.pk)
|
||||
# All refunds that this user has requested (probably as a consumer).
|
||||
req = Refund.objects.filter(contribution__user=user)
|
||||
# All instantly-approved refunds that this user has requested.
|
||||
appr = req.filter(status=amo.REFUND_APPROVED_INSTANT)
|
||||
refund_summary = {'approved': appr.count(),
|
||||
'requested': req.count()}
|
||||
user_addons = user.addons.all().order_by('-created')
|
||||
return jingo.render(request, 'acct_lookup/summary.html',
|
||||
{'account': user,
|
||||
'app_summary': app_summary,
|
||||
'refund_summary': refund_summary,
|
||||
'user_addons': user_addons})
|
||||
|
||||
|
||||
def _app_summary(user_id):
|
||||
sql = """
|
||||
select currency,
|
||||
sum(case when type=%(purchase)s then 1 else 0 end)
|
||||
as app_total,
|
||||
sum(case when type=%(purchase)s then amount else 0.0 end)
|
||||
as app_amount,
|
||||
sum(case when type=%(inapp)s then 1 else 0 end)
|
||||
as inapp_total,
|
||||
sum(case when type=%(inapp)s then amount else 0.0 end)
|
||||
as inapp_amount
|
||||
from stats_contributions
|
||||
where user_id=%(user_id)s
|
||||
group by currency
|
||||
"""
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(sql, {'user_id': user_id,
|
||||
'purchase': amo.CONTRIB_PURCHASE,
|
||||
'inapp': amo.CONTRIB_INAPP})
|
||||
summary = {'app_total': 0,
|
||||
'app_amount': {},
|
||||
'inapp_total': 0,
|
||||
'inapp_amount': {}}
|
||||
cols = [cd[0] for cd in cursor.description]
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
break
|
||||
row = dict(zip(cols, row))
|
||||
for cn in cols:
|
||||
if cn.endswith('total'):
|
||||
summary[cn] += row[cn]
|
||||
elif cn.endswith('amount'):
|
||||
summary[cn][row['currency']] = row[cn]
|
||||
return summary
|
||||
|
|
Загрузка…
Ссылка в новой задаче