Revert "Revert "pass dl-src/user client data to install/contrib/ratings (bug
757266, 763697, 756660)"" and fix migration
This commit is contained in:
Родитель
3bb667dd82
Коммит
24903c76b7
|
@ -46,6 +46,7 @@ class Review(amo.models.ModelBase):
|
|||
editorreview = models.BooleanField(default=False)
|
||||
flag = models.BooleanField(default=False)
|
||||
sandbox = models.BooleanField(default=False)
|
||||
client_data = models.ForeignKey('stats.ClientData', null=True, blank=True)
|
||||
|
||||
# Denormalized fields for easy lookup queries.
|
||||
# TODO: index on addon, user, latest
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from collections import defaultdict
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.encoding import smart_str
|
||||
|
@ -354,7 +355,7 @@ class BaseAjaxSearch(object):
|
|||
|
||||
|
||||
class SearchSuggestionsAjax(BaseAjaxSearch):
|
||||
src = 'ss'
|
||||
src = 'mkt-ss' if settings.MARKETPLACE else 'ss'
|
||||
|
||||
|
||||
class AddonSuggestionsAjax(SearchSuggestionsAjax):
|
||||
|
|
|
@ -122,6 +122,7 @@ class Contribution(amo.models.ModelBase):
|
|||
choices=do_dictsort(amo.PAYPAL_CURRENCIES),
|
||||
default=amo.CURRENCY_DEFAULT)
|
||||
source = models.CharField(max_length=255, null=True)
|
||||
client_data = models.ForeignKey('stats.ClientData', null=True)
|
||||
source_locale = models.CharField(max_length=10, null=True)
|
||||
|
||||
uuid = models.CharField(max_length=255, null=True)
|
||||
|
@ -402,3 +403,20 @@ class GlobalStat(caching.base.CachingMixin, models.Model):
|
|||
db_table = 'global_stats'
|
||||
unique_together = ('name', 'date')
|
||||
get_latest_by = 'date'
|
||||
|
||||
|
||||
class ClientData(models.Model):
|
||||
"""
|
||||
Helps tracks user agent and download source data of installs and purchases.
|
||||
"""
|
||||
download_source = models.ForeignKey('zadmin.DownloadSource', null=True)
|
||||
device_type = models.CharField(max_length=255)
|
||||
user_agent = models.CharField(max_length=255)
|
||||
is_chromeless = models.BooleanField()
|
||||
language = models.CharField(max_length=7)
|
||||
region = models.IntegerField(null=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'client_data'
|
||||
unique_together = ('download_source', 'device_type', 'user_agent',
|
||||
'is_chromeless', 'language', 'region')
|
||||
|
|
|
@ -13,8 +13,10 @@ z.capabilities = {
|
|||
typeof navigator.mozApps.html5Implementation === 'undefined'
|
||||
),
|
||||
'fileAPI': !!window.FileReader,
|
||||
'desktop': window.matchMedia('(max-width: 1024px)').matches,
|
||||
'tablet': window.matchMedia('(max-width: 672px)').matches,
|
||||
'userAgent': navigator.userAgent,
|
||||
'desktop': window.matchMedia('(min-width: 673px)').matches,
|
||||
'tablet': window.matchMedia('(max-width: 672px)').matches &&
|
||||
window.matchMedia('(min-width: 601px)').matches,
|
||||
'mobile': window.matchMedia('(max-width: 600px)').matches,
|
||||
'touch': ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
|
||||
'nativeScroll': (function() {
|
||||
|
@ -22,6 +24,9 @@ z.capabilities = {
|
|||
})(),
|
||||
'performance': !!(window.performance || window.msPerformance || window.webkitPerformance || window.mozPerformance)
|
||||
};
|
||||
z.capabilities.getDeviceType = function() {
|
||||
return this.desktop ? 'desktop' : (this.tablet ? 'tablet' : 'mobile');
|
||||
};
|
||||
|
||||
if (z.capabilities.tablet) {
|
||||
// If we're on tablet, then we're not on desktop.
|
||||
|
@ -34,14 +39,14 @@ if (z.capabilities.mobile) {
|
|||
}
|
||||
|
||||
try {
|
||||
if ('localStorage' in window && window['localStorage'] !== null) {
|
||||
if ('localStorage' in window && window.localStorage !== null) {
|
||||
z.capabilities.localStorage = true;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if ('sessionStorage' in window && window['sessionStorage'] !== null) {
|
||||
if ('sessionStorage' in window && window.sessionStorage !== null) {
|
||||
z.capabilities.sessionStorage = true;
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -49,15 +49,22 @@
|
|||
|
||||
function install(product, receipt) {
|
||||
var data = {};
|
||||
var post_data = {
|
||||
device_type: z.capabilities.getDeviceType()
|
||||
};
|
||||
if (z.capabilities.chromeless) {
|
||||
post_data.chromeless = 1;
|
||||
}
|
||||
|
||||
$(window).trigger('app_install_start', product);
|
||||
$.post(product.recordUrl).success(function(response) {
|
||||
$.post(product.recordUrl, post_data).success(function(response) {
|
||||
if (response.error) {
|
||||
$('#pay-error').show().find('div').text(response.error);
|
||||
installError(product);
|
||||
return;
|
||||
}
|
||||
if (response.receipt) {
|
||||
data['data'] = {'receipts': [response.receipt]};
|
||||
data.data = {'receipts': [response.receipt]};
|
||||
}
|
||||
$.when(apps.install(product, data))
|
||||
.done(installSuccess)
|
||||
|
|
|
@ -7,9 +7,17 @@
|
|||
$def,
|
||||
message = $('#purchased-message'),
|
||||
messageTemplate = template($('#purchased-template').html()),
|
||||
data = {'currency': $('body').data('user').currency},
|
||||
data = {
|
||||
'currency': $('body').data('user').currency,
|
||||
'src': z.getVars().src,
|
||||
'device_type': z.capabilities.getDeviceType()
|
||||
},
|
||||
oneTimePayClicked = false;
|
||||
|
||||
if (z.capabilities.chromeless) {
|
||||
data.chromeless = 1;
|
||||
}
|
||||
|
||||
function beginPurchase(prod) {
|
||||
if (!prod) return;
|
||||
if ($def && $def.state() == 'pending') {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
INSERT INTO download_sources (name, type) VALUES
|
||||
('mkt-home', 'full'),
|
||||
('mkt-featured', 'full'),
|
||||
('mkt-category', 'full'),
|
||||
('mkt-detail', 'full'),
|
||||
('mkt-detail-upsell', 'full'),
|
||||
('mkt-search', 'full'),
|
||||
('mkt-ss', 'full'),
|
||||
('mkt-user-profile', 'full');
|
||||
|
||||
|
||||
CREATE TABLE `client_data` (
|
||||
`id` int(11) unsigned AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
`download_source_id` int(11) unsigned NULL,
|
||||
`device_type` varchar(255) NOT NULL,
|
||||
`user_agent` varchar(255) NOT NULL,
|
||||
`is_chromeless` bool,
|
||||
`language` varchar(7) NOT NULL,
|
||||
`region` int(11) unsigned NULL,
|
||||
UNIQUE (`download_source_id`, `device_type`, `user_agent`, `is_chromeless`, `language`, `region`)
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
|
||||
ALTER TABLE `client_data` ADD CONSTRAINT `download_source_id_refs_id_b71b78fb` FOREIGN KEY (`download_source_id`) REFERENCES `download_sources` (`id`);
|
||||
|
||||
ALTER TABLE users_install ADD COLUMN client_data_id int(11) unsigned;
|
||||
ALTER TABLE `users_install` ADD CONSTRAINT `client_data_id_refs_id_15062d7f` FOREIGN KEY (`client_data_id`) REFERENCES `client_data` (`id`);
|
||||
ALTER TABLE stats_contributions ADD COLUMN client_data_id int(11) unsigned;
|
||||
ALTER TABLE `stats_contributions` ADD CONSTRAINT `client_data_id_refs_id_c8ef1728` FOREIGN KEY (`client_data_id`) REFERENCES `client_data` (`id`);
|
||||
ALTER TABLE reviews ADD COLUMN client_data_id int(11) unsigned;
|
||||
ALTER TABLE `reviews` ADD CONSTRAINT `client_data_id_refs_id_d160c5ba` FOREIGN KEY (`client_data_id`) REFERENCES `client_data` (`id`);
|
||||
|
||||
ALTER TABLE `users_install` ADD CONSTRAINT UNIQUE (`addon_id`, `user_id`, `client_data_id`);
|
|
@ -83,7 +83,7 @@
|
|||
<div id="my-submissions" class="listing c">
|
||||
<h2>{{ _('My Submissions') }}</h2>
|
||||
<ol class="items" start="{{ submissions.start_index() }}">
|
||||
{{ search_results(submissions.object_list, src='search') }}
|
||||
{{ search_results(submissions.object_list, src='mkt-user-profile') }}
|
||||
</ol>
|
||||
{{ submissions|impala_paginator }}
|
||||
</div>
|
||||
|
|
|
@ -197,6 +197,7 @@ JS = {
|
|||
'js/zamboni/truncation.js',
|
||||
'js/common/keys.js',
|
||||
'js/mkt/capabilities.js',
|
||||
'js/impala/serializers.js',
|
||||
'js/mkt/fragments.js',
|
||||
'js/mkt/recaptcha.js',
|
||||
'js/mkt/overlay.js',
|
||||
|
@ -209,7 +210,6 @@ JS = {
|
|||
'js/mkt/apps.js',
|
||||
'js/zamboni/outgoing_links.js',
|
||||
'js/common/upload-image.js',
|
||||
'js/impala/serializers.js',
|
||||
|
||||
# Search suggestions.
|
||||
'js/impala/ajaxcache.js',
|
||||
|
@ -238,6 +238,7 @@ JS = {
|
|||
'js/lib/stick.js',
|
||||
),
|
||||
'mkt/reviewers': (
|
||||
'js/mkt/utils.js',
|
||||
'js/mkt/apps.js',
|
||||
'js/mkt/install.js',
|
||||
'js/mkt/buttons.js',
|
||||
|
|
|
@ -26,18 +26,20 @@ def _categories():
|
|||
|
||||
@register.filter
|
||||
@jinja2.contextfilter
|
||||
def promo_grid(context, products):
|
||||
def promo_grid(context, products, src=''):
|
||||
t = env.get_template('browse/helpers/promo_grid.html')
|
||||
return jinja2.Markup(t.render(request=context['request'],
|
||||
products=products))
|
||||
products=products,
|
||||
src=src))
|
||||
|
||||
|
||||
@register.filter
|
||||
@jinja2.contextfilter
|
||||
def promo_slider(context, products, feature=False):
|
||||
def promo_slider(context, products, feature=False, src=''):
|
||||
t = env.get_template('browse/helpers/promo_slider.html')
|
||||
return jinja2.Markup(t.render(request=context['request'],
|
||||
products=products, feature=feature))
|
||||
products=products, feature=feature,
|
||||
src=src))
|
||||
|
||||
|
||||
@register.function
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<ul class="content">
|
||||
{% for product in products %}
|
||||
<li>
|
||||
{{ market_tile(product) }}
|
||||
{{ market_tile(product, src=src) }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{% for product in products %}
|
||||
{% if not preview or preview.filetype != 'video/webm' %}
|
||||
<li>
|
||||
{{ market_tile(product) }}
|
||||
{{ market_tile(product, src=src) }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</div>
|
||||
</section>
|
||||
<section class="featured full slider">
|
||||
{{ featured|promo_slider(feature=True) }}
|
||||
{{ featured|promo_slider(feature=True, src='mkt-category') }}
|
||||
</section>
|
||||
{% if popular %}
|
||||
<section class="popular subheading full">
|
||||
|
@ -37,7 +37,7 @@
|
|||
</div>
|
||||
</section>
|
||||
<section class="popular grid full">
|
||||
{{ popular|promo_grid }}
|
||||
{{ popular|promo_grid(src='mkt-category') }}
|
||||
</section>
|
||||
{% else %}
|
||||
{{ no_results() }}
|
||||
|
|
|
@ -2,17 +2,13 @@ from nose import SkipTest
|
|||
from nose.tools import eq_
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
import waffle
|
||||
|
||||
import amo
|
||||
import amo.tests
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import urlparams
|
||||
from addons.models import Addon, AddonCategory, Category
|
||||
from bandwagon.models import Collection, CollectionAddon
|
||||
from addons.models import AddonCategory, Category
|
||||
from mkt.webapps.models import Webapp
|
||||
from mkt.zadmin.models import FeaturedApp
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
class BrowseBase(amo.tests.ESTestCase):
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
</section>
|
||||
<section class="featured full slider">
|
||||
{{ featured|promo_slider(feature=True) }}
|
||||
{{ featured|promo_slider(feature=True, src='mkt-home') }}
|
||||
</section>
|
||||
{% if popular %}
|
||||
<section class="popular tabbed narrow full">
|
||||
|
@ -44,7 +44,7 @@
|
|||
</div>
|
||||
</section>
|
||||
<section data-group="popular" data-shown class="popular grid full">
|
||||
{{ popular|promo_grid }}
|
||||
{{ popular|promo_grid(src='mkt-home') }}
|
||||
</section>
|
||||
<section data-group="popular" data-shown class="narrow popular view-more full">
|
||||
<div>
|
||||
|
@ -57,7 +57,7 @@
|
|||
{{ _('New') }}</a></h2>
|
||||
</section>
|
||||
<section data-group="new" class="narrow popular grid full">
|
||||
{{ latest|promo_grid }}
|
||||
{{ latest|promo_grid(src='mkt-home') }}
|
||||
</section>
|
||||
<section data-group="new" class="narrow popular view-more full">
|
||||
<div>
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
from decimal import Decimal
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import fudge
|
||||
from fudge.inspector import arg
|
||||
import mock
|
||||
|
@ -16,10 +18,12 @@ from amo.urlresolvers import reverse
|
|||
from devhub.models import AppLog
|
||||
from market.models import (AddonPremium, AddonPurchase, PreApprovalUser,
|
||||
Price, PriceCurrency)
|
||||
from mkt import regions
|
||||
from mkt.webapps.models import Webapp
|
||||
from paypal import get_preapproval_url, PaypalError, PaypalDataError
|
||||
from stats.models import Contribution
|
||||
from users.models import UserProfile
|
||||
from zadmin.models import DownloadSource
|
||||
|
||||
|
||||
class TestPurchaseEmbedded(amo.tests.TestCase):
|
||||
|
@ -321,6 +325,27 @@ class TestPurchaseEmbedded(amo.tests.TestCase):
|
|||
self.client.post(self.addon.get_purchase_url(),
|
||||
{'result_type': 'json'})
|
||||
|
||||
@mock.patch('paypal.get_paykey')
|
||||
def test_contribution_client_data(self, get_paykey):
|
||||
get_paykey.return_value = ['some-pay-key', '']
|
||||
download_source = DownloadSource.objects.create(name='mkt-home')
|
||||
device_type = 'desktop'
|
||||
user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0)'
|
||||
|
||||
self.client.post_ajax(self.purchase_url,
|
||||
data={'src': download_source.name,
|
||||
'device_type': device_type,
|
||||
'is_chromeless': False},
|
||||
**{'HTTP_USER_AGENT': user_agent})
|
||||
cons = Contribution.objects.filter(type=amo.CONTRIB_PENDING)
|
||||
eq_(cons.count(), 1)
|
||||
eq_(cons[0].client_data.download_source, download_source)
|
||||
eq_(cons[0].client_data.device_type, device_type)
|
||||
eq_(cons[0].client_data.user_agent, user_agent)
|
||||
eq_(cons[0].client_data.is_chromeless, False)
|
||||
eq_(not cons[0].client_data.language, False)
|
||||
eq_(not cons[0].client_data.region, False)
|
||||
|
||||
|
||||
class TestPurchaseDetails(amo.tests.TestCase):
|
||||
fixtures = ['base/users', 'webapps/337141-steamcube']
|
||||
|
|
|
@ -22,9 +22,11 @@ from lib.pay_server import client
|
|||
from market.forms import PriceCurrencyForm
|
||||
from market.models import AddonPurchase
|
||||
import paypal
|
||||
from stats.models import Contribution
|
||||
from stats.models import ClientData, Contribution
|
||||
import mkt
|
||||
from mkt.account.views import preapproval as user_preapproval
|
||||
from mkt.webapps.models import Webapp
|
||||
from zadmin.models import DownloadSource
|
||||
|
||||
log = commonware.log.getLogger('z.purchase')
|
||||
addon_view = addon_view_factory(qs=Webapp.objects.valid)
|
||||
|
@ -40,7 +42,6 @@ def purchase(request, addon):
|
|||
log.debug('Starting purchase of addon: %s by user: %s'
|
||||
% (addon.pk, request.amo_user.pk))
|
||||
amount = addon.premium.get_price()
|
||||
source = request.POST.get('source', '')
|
||||
uuid_ = hashlib.md5(str(uuid.uuid4())).hexdigest()
|
||||
# L10n: {0} is the addon name.
|
||||
contrib_for = (_(u'Mozilla Marketplace purchase of {0}')
|
||||
|
@ -134,10 +135,31 @@ def purchase(request, addon):
|
|||
if paykey:
|
||||
# TODO(solitude): at some point we'll have to see what to do with
|
||||
# contributions.
|
||||
download_source = request.REQUEST.get('src', '')
|
||||
contrib = Contribution(addon_id=addon.id, amount=amount,
|
||||
source=source, source_locale=request.LANG,
|
||||
source=download_source,
|
||||
source_locale=request.LANG,
|
||||
uuid=str(uuid_), type=amo.CONTRIB_PENDING,
|
||||
paykey=paykey, user=request.amo_user)
|
||||
|
||||
# Grab a client data object to hook up with the Contribution object.
|
||||
try:
|
||||
download_source = DownloadSource.objects.get(name=download_source)
|
||||
except DownloadSource.DoesNotExist:
|
||||
download_source = None
|
||||
try:
|
||||
region = request.REGION.id
|
||||
except AttributeError:
|
||||
region = mkt.regions.WORLDWIDE.id
|
||||
client_data, c = ClientData.objects.get_or_create(
|
||||
download_source=download_source,
|
||||
device_type=request.POST.get('device_type', ''),
|
||||
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
||||
is_chromeless=request.POST.get('chromeless', False),
|
||||
language=request.LANG,
|
||||
region=region)
|
||||
contrib.update(client_data=client_data)
|
||||
|
||||
log.debug('Storing contrib for uuid: %s' % uuid_)
|
||||
|
||||
# If this was a pre-approval, it's completed already, we'll
|
||||
|
|
|
@ -7,10 +7,12 @@ import amo
|
|||
from amo.helpers import numberfmt
|
||||
import amo.tests
|
||||
from reviews.models import Review, ReviewFlag
|
||||
from stats.models import ClientData
|
||||
from users.models import UserProfile
|
||||
from zadmin.models import DownloadSource
|
||||
|
||||
from mkt.developers.models import ActivityLog
|
||||
from mkt.webapps.models import Webapp
|
||||
from mkt.webapps.models import Installed, Webapp
|
||||
|
||||
|
||||
class ReviewTest(amo.tests.TestCase):
|
||||
|
@ -262,6 +264,45 @@ class TestCreate(ReviewTest):
|
|||
r = self.client.get(self.add)
|
||||
self.assertLoginRedirects(r, self.add, 302)
|
||||
|
||||
def test_add_client_data(self):
|
||||
self.enable_waffle()
|
||||
client_data = ClientData.objects.create(
|
||||
download_source=DownloadSource.objects.create(name='mkt-test'),
|
||||
device_type='tablet', user_agent='test-agent', is_chromeless=False,
|
||||
language='pt-BR', region=3
|
||||
)
|
||||
client_data_diff_agent = ClientData.objects.create(
|
||||
download_source=DownloadSource.objects.create(name='mkt-test'),
|
||||
device_type='tablet', user_agent='test-agent2',
|
||||
is_chromeless=False, language='pt-BR', region=3
|
||||
)
|
||||
Installed.objects.create(user=self.user, addon=self.webapp,
|
||||
client_data=client_data)
|
||||
Installed.objects.create(user=self.user, addon=self.webapp,
|
||||
client_data=client_data_diff_agent)
|
||||
Review.objects.all().delete()
|
||||
r = self.client.post(self.add,
|
||||
{'body': 'x', 'rating': 4},
|
||||
HTTP_USER_AGENT='test-agent')
|
||||
review = Review.objects.order_by('-created')[0]
|
||||
eq_(review.client_data, client_data)
|
||||
|
||||
def test_add_client_data_no_user_agent_match(self):
|
||||
self.enable_waffle()
|
||||
client_data = ClientData.objects.create(
|
||||
download_source=DownloadSource.objects.create(name='mkt-test'),
|
||||
device_type='tablet', user_agent='test-agent-1',
|
||||
is_chromeless=False, language='pt-BR', region=3
|
||||
)
|
||||
Installed.objects.create(user=self.user, addon=self.webapp,
|
||||
client_data=client_data)
|
||||
Review.objects.all().delete()
|
||||
r = self.client.post(self.add,
|
||||
{'body': 'x', 'rating': 4},
|
||||
HTTP_USER_AGENT='test-agent-2')
|
||||
review = Review.objects.order_by('-created')[0]
|
||||
eq_(review.client_data, client_data)
|
||||
|
||||
|
||||
class TestListing(ReviewTest):
|
||||
|
||||
|
|
|
@ -19,9 +19,11 @@ from reviews.models import Review
|
|||
from reviews.helpers import user_can_delete_review
|
||||
from reviews.tasks import addon_review_aggregates
|
||||
from reviews.views import get_flags
|
||||
from stats.models import ClientData
|
||||
|
||||
from mkt.site import messages
|
||||
from mkt.ratings.forms import ReviewForm
|
||||
from mkt.webapps.models import Installed
|
||||
|
||||
|
||||
log = commonware.log.getLogger('mkt.ratings')
|
||||
|
@ -114,6 +116,26 @@ def add(request, addon):
|
|||
# Don't let app owners review their own apps.
|
||||
return http.HttpResponseForbidden()
|
||||
|
||||
# Get user agent of user submitting review. If there is an install with
|
||||
# logged user agent that matches the current user agent, hook up that
|
||||
# install's client data with the rating. If there aren't any install that
|
||||
# match, use the most recent install. This implies that user must have an
|
||||
# install to submit a review, but not sure if that logic is worked in, so
|
||||
# default client_data to None.
|
||||
client_data = None
|
||||
user_agent = request.META.get('HTTP_USER_AGENT', '')
|
||||
install = (Installed.objects.filter(user=request.user, addon=addon)
|
||||
.order_by('-created'))
|
||||
install_w_user_agent = (install.filter(client_data__user_agent=user_agent)
|
||||
.order_by('-created'))
|
||||
try:
|
||||
if install_w_user_agent:
|
||||
client_data = install_w_user_agent[0].client_data
|
||||
elif install:
|
||||
client_data = install[0].client_data
|
||||
except ClientData.DoesNotExist:
|
||||
client_data = None
|
||||
|
||||
data = request.POST or None
|
||||
|
||||
# Try to get an existing review of the app by this user if we can.
|
||||
|
@ -149,11 +171,11 @@ def add(request, addon):
|
|||
_('Your review was updated successfully!'))
|
||||
else:
|
||||
# If there isn't a review to overwrite, create a new review.
|
||||
review = Review.objects.create(
|
||||
review = Review.objects.create(client_data=client_data,
|
||||
**_review_details(request, addon, form))
|
||||
amo.log(amo.LOG.ADD_REVIEW, addon, review)
|
||||
log.debug('[Review:%s] Created by user %s ' % (review.id,
|
||||
request.user.id))
|
||||
log.debug('[Review:%s] Created by user %s ' %
|
||||
(review.id, request.user.id))
|
||||
messages.success(request,
|
||||
_('Your review was successfully added!'))
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ from amo.urlresolvers import reverse
|
|||
from devhub.models import AppLog
|
||||
from mkt.webapps.models import Webapp
|
||||
from users.models import UserProfile
|
||||
from zadmin.models import DownloadSource
|
||||
|
||||
|
||||
class TestReissue(amo.tests.TestCase):
|
||||
|
@ -173,6 +174,28 @@ class TestInstall(amo.tests.TestCase):
|
|||
content = json.loads(res.content)
|
||||
assert content.get('receipt'), content
|
||||
|
||||
def test_installed_client_data(self):
|
||||
download_source = DownloadSource.objects.create(name='mkt-home')
|
||||
device_type = 'mobile'
|
||||
user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0)'
|
||||
|
||||
self.addon.update(type=amo.ADDON_WEBAPP)
|
||||
res = self.client.post(self.url,
|
||||
data={'device_type': device_type,
|
||||
'is_chromeless': False,
|
||||
'src': download_source.name},
|
||||
HTTP_USER_AGENT=user_agent)
|
||||
|
||||
eq_(res.status_code, 200)
|
||||
eq_(self.user.installed_set.count(), 1)
|
||||
ins = self.user.installed_set.get()
|
||||
eq_(ins.client_data.download_source, download_source)
|
||||
eq_(ins.client_data.device_type, device_type)
|
||||
eq_(ins.client_data.user_agent, user_agent)
|
||||
eq_(ins.client_data.is_chromeless, False)
|
||||
eq_(not ins.client_data.language, False)
|
||||
eq_(not ins.client_data.region, False)
|
||||
|
||||
|
||||
class TestReceiptVerify(amo.tests.TestCase):
|
||||
fixtures = ['base/users']
|
||||
|
|
|
@ -24,7 +24,9 @@ from lib.cef_loggers import receipt_cef
|
|||
import mkt
|
||||
from mkt.webapps.models import Installed, Webapp
|
||||
from services.verify import Verify
|
||||
from stats.models import ClientData
|
||||
from users.models import UserProfile
|
||||
from zadmin.models import DownloadSource
|
||||
|
||||
from .utils import create_receipt
|
||||
|
||||
|
@ -70,8 +72,29 @@ def _record(request, addon):
|
|||
not is_reviewer and not is_dev):
|
||||
return http.HttpResponseForbidden()
|
||||
|
||||
# Log the install.
|
||||
installed, c = Installed.objects.safer_get_or_create(addon=addon,
|
||||
user=request.amo_user)
|
||||
|
||||
# Get download source from GET if it exists, if so get the download
|
||||
# source object if it exists. Then grab a client data object to hook up
|
||||
# with the Installed object.
|
||||
download_source = DownloadSource.objects.filter(
|
||||
name=request.REQUEST.get('src', None))
|
||||
download_source = download_source[0] if download_source else None
|
||||
try:
|
||||
region = request.REGION.id
|
||||
except AttributeError:
|
||||
region = mkt.regions.WORLDWIDE.id
|
||||
client_data, c = ClientData.objects.get_or_create(
|
||||
download_source=download_source,
|
||||
device_type=request.POST.get('device_type', ''),
|
||||
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
||||
is_chromeless=request.POST.get('chromeless', False),
|
||||
language=request.LANG,
|
||||
region=region)
|
||||
installed.update(client_data=client_data)
|
||||
|
||||
# Look up to see if its in the receipt cache and log if we have
|
||||
# to recreate it.
|
||||
receipt = memoize_get('create-receipt', installed.pk)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{% endif %}
|
||||
{% if pager.object_list %}
|
||||
<ol class="items" start="{{ pager.start_index() }}">
|
||||
{{ search_results(pager.object_list, field=query.sort, src='search') }}
|
||||
{{ search_results(pager.object_list, field=query.sort, src='mkt-search') }}
|
||||
</ol>
|
||||
{{ pager|impala_paginator }}
|
||||
{% else %}
|
||||
|
|
|
@ -86,7 +86,7 @@ class TestWebappSearch(PaidAppMixin, SearchBase):
|
|||
a = item.find('h3 a')
|
||||
eq_(a.text(), unicode(self.webapp.name))
|
||||
eq_(a.attr('href'),
|
||||
urlparams(self.webapp.get_url_path(), src='search'))
|
||||
urlparams(self.webapp.get_url_path(), src='mkt-search'))
|
||||
|
||||
def test_results_downloads(self):
|
||||
for sort in ('', 'downloads', 'created'):
|
||||
|
|
|
@ -120,14 +120,15 @@ def product_as_dict_theme(request, product):
|
|||
|
||||
@jinja2.contextfunction
|
||||
@register.function
|
||||
def market_tile(context, product):
|
||||
def market_tile(context, product, src=''):
|
||||
request = context['request']
|
||||
if product.is_webapp():
|
||||
classes = ['product', 'mkt-tile', 'arrow']
|
||||
product_dict = product_as_dict(request, product)
|
||||
data_attrs = {
|
||||
'product': json.dumps(product_dict, cls=JSONEncoder),
|
||||
'manifestUrl': product.manifest_url
|
||||
'manifestUrl': product.manifest_url,
|
||||
'src': src
|
||||
}
|
||||
if product.is_premium() and product.premium:
|
||||
classes.append('premium')
|
||||
|
@ -141,6 +142,7 @@ def market_tile(context, product):
|
|||
product_dict = product_as_dict_theme(request, product)
|
||||
data_attrs = {
|
||||
'product': json.dumps(product_dict, cls=JSONEncoder),
|
||||
'src': src
|
||||
}
|
||||
c = dict(product=product, data_attrs=data_attrs,
|
||||
classes=' '.join(classes))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% set preview = previews[0].image_url if feature else
|
||||
previews[0].thumbnail_url %}
|
||||
{% endif %}
|
||||
<a class="{{ classes }}" href="{{ product.get_url_path() }}" data-prefetch="1"
|
||||
<a class="{{ classes }}" href="{{ product.get_url_path()|urlparams(src=data_attrs.src) }}" data-prefetch="1"
|
||||
{% for k, v in data_attrs.items() -%}
|
||||
data-{{ k }}="{{ v }}" {% endfor %}>
|
||||
<div class="img icon" style="background-image:url({{ product.get_icon_url(64) }})"></div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import caching.base as caching
|
||||
import jinja2
|
||||
|
||||
from jingo import env, register
|
||||
import jinja2
|
||||
|
||||
import amo
|
||||
from addons.models import AddonCategory, Category
|
||||
|
|
|
@ -49,9 +49,8 @@ def _landing(request, category=None):
|
|||
category = get_list_or_404(
|
||||
Category.objects.filter(type=amo.ADDON_PERSONA,
|
||||
slug=category))[0]
|
||||
popular = (Addon.objects
|
||||
popular = (Addon.objects.public()
|
||||
.filter(type=amo.ADDON_PERSONA,
|
||||
status=amo.STATUS_PUBLIC,
|
||||
addoncategory__category__id=category.id)
|
||||
.order_by('-persona__popularity')[:12])
|
||||
|
||||
|
@ -60,8 +59,7 @@ def _landing(request, category=None):
|
|||
ids = AddonCategory.creatured_random(category, request.LANG)
|
||||
featured = manual_order(base, ids, pk_name="addons.id")[:12]
|
||||
else:
|
||||
popular = (Addon.objects.public().filter(type=amo.ADDON_PERSONA,
|
||||
status=amo.STATUS_PUBLIC)
|
||||
popular = (Addon.objects.public().filter(type=amo.ADDON_PERSONA)
|
||||
.order_by('-persona__popularity')[:12])
|
||||
featured = get_featured_personas(request, num_personas=12)
|
||||
|
||||
|
|
|
@ -309,6 +309,7 @@ class Installed(amo.models.ModelBase):
|
|||
addon = models.ForeignKey('addons.Addon', related_name='installed')
|
||||
user = models.ForeignKey('users.UserProfile')
|
||||
uuid = models.CharField(max_length=255, db_index=True, unique=True)
|
||||
client_data = models.ForeignKey('stats.ClientData', null=True)
|
||||
# Because the addon could change between free and premium,
|
||||
# we need to store the state at time of install here.
|
||||
premium_type = models.PositiveIntegerField(
|
||||
|
@ -317,7 +318,7 @@ class Installed(amo.models.ModelBase):
|
|||
|
||||
class Meta:
|
||||
db_table = 'users_install'
|
||||
unique_together = ('addon', 'user')
|
||||
unique_together = ('addon', 'user', 'client_data')
|
||||
|
||||
|
||||
@receiver(models.signals.post_save, sender=Installed)
|
||||
|
|
Загрузка…
Ссылка в новой задаче