Merge lp:~michael.nelson/ubuntu-webcatalog/1045328-bundle-promotion-apps into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Łukasz Czyżykowski
Approved revision: 174
Merged at revision: 170
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/1045328-bundle-promotion-apps
Merge into: lp:ubuntu-webcatalog
Diff against target: 456 lines (+278/-19)
10 files modified
src/webcatalog/admin.py (+15/-0)
src/webcatalog/migrations/0029_add_application_widget.py (+191/-0)
src/webcatalog/models/__init__.py (+2/-0)
src/webcatalog/models/applications.py (+14/-0)
src/webcatalog/schema.py (+0/-2)
src/webcatalog/templates/webcatalog/application_detail.html (+3/-6)
src/webcatalog/templatetags/webcatalog.py (+7/-0)
src/webcatalog/tests/factory.py (+11/-0)
src/webcatalog/tests/test_views.py (+35/-8)
src/webcatalog/views.py (+0/-3)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/1045328-bundle-promotion-apps
Reviewer Review Type Date Requested Status
Łukasz Czyżykowski (community) Approve
Review via email: mp+123082@code.launchpad.net

Commit message

Add ApplicationWidget model for dynamic campaign widgets.

Description of the change

Overview
========

This branch adds the ApplicationWidget model which can be used to display custom widgets on any application detail page (see bug 1045328).

`fab test`

Notes:
 * I tried a few options for the model-admin, like tabularinline, but raw_id_field was the only way to *not* present the whole list of apps. The good thing is it presents lots of info about each app to decide, the bad thing is it results in a list of ids, which are opaque once set. We may want to investigate further if it is difficult to use.
 * I considered having a widget per application (implemented) and a widget per package_name (what the existing setting did). I went for the DB relationship on application, which is more general (ie. could potentially select only certain distroseries versions of an app to include the widget), but may be more fiddly to use (need to select multiple versions of each app).

To post a comment you must log in.
174. By Michael Nelson

REFACTOR: Use inline for displaying widget on application.

Revision history for this message
Łukasz Czyżykowski (lukasz-czyzykowski) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/webcatalog/admin.py'
--- src/webcatalog/admin.py 2012-07-02 21:25:30 +0000
+++ src/webcatalog/admin.py 2012-09-06 14:09:20 +0000
@@ -23,6 +23,7 @@
23from webcatalog.models import (23from webcatalog.models import (
24 Application,24 Application,
25 ApplicationMedia,25 ApplicationMedia,
26 ApplicationWidget,
26 Department,27 Department,
27 DistroSeries,28 DistroSeries,
28 Exhibit,29 Exhibit,
@@ -36,12 +37,19 @@
36]37]
3738
3839
40class ApplicationWidgetInline(admin.TabularInline):
41 model = ApplicationWidget.applications.through
42
43
39class ApplicationAdmin(admin.ModelAdmin):44class ApplicationAdmin(admin.ModelAdmin):
40 list_display = ('package_name', 'name', 'comment', 'distroseries',45 list_display = ('package_name', 'name', 'comment', 'distroseries',
41 'wilson_score')46 'wilson_score')
42 search_fields = ('package_name', 'name', 'comment')47 search_fields = ('package_name', 'name', 'comment')
43 list_filter = ('distroseries', 'imported_from_sca', 'is_latest',48 list_filter = ('distroseries', 'imported_from_sca', 'is_latest',
44 'departments')49 'departments')
50 inlines = [
51 ApplicationWidgetInline,
52 ]
4553
4654
47class MachineAdmin(admin.ModelAdmin):55class MachineAdmin(admin.ModelAdmin):
@@ -66,8 +74,15 @@
66 def distroseries(self, obj):74 def distroseries(self, obj):
67 return obj.application.distroseries75 return obj.application.distroseries
6876
77
78class ApplicationWidgetAdmin(admin.ModelAdmin):
79 list_display = ('name', )
80 raw_id_fields = ('applications',)
81
82
69admin.site.register(Application, ApplicationAdmin)83admin.site.register(Application, ApplicationAdmin)
70admin.site.register(ApplicationMedia, ApplicationMediaAdmin)84admin.site.register(ApplicationMedia, ApplicationMediaAdmin)
85admin.site.register(ApplicationWidget, ApplicationWidgetAdmin)
71admin.site.register(Department, DepartmentAdmin)86admin.site.register(Department, DepartmentAdmin)
72admin.site.register(DistroSeries)87admin.site.register(DistroSeries)
73admin.site.register(Exhibit, ExhibitAdmin)88admin.site.register(Exhibit, ExhibitAdmin)
7489
=== added file 'src/webcatalog/migrations/0029_add_application_widget.py'
--- src/webcatalog/migrations/0029_add_application_widget.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/migrations/0029_add_application_widget.py 2012-09-06 14:09:20 +0000
@@ -0,0 +1,191 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding model 'ApplicationWidget'
12 db.create_table('webcatalog_applicationwidget', (
13 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 ('name', self.gf('django.db.models.fields.CharField')(max_length=32)),
15 ('template_snippet', self.gf('django.db.models.fields.TextField')()),
16 ))
17 db.send_create_signal('webcatalog', ['ApplicationWidget'])
18
19 # Adding M2M table for field applications on 'ApplicationWidget'
20 db.create_table('webcatalog_applicationwidget_applications', (
21 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
22 ('applicationwidget', models.ForeignKey(orm['webcatalog.applicationwidget'], null=False)),
23 ('application', models.ForeignKey(orm['webcatalog.application'], null=False))
24 ))
25 db.create_unique('webcatalog_applicationwidget_applications', ['applicationwidget_id', 'application_id'])
26
27
28 def backwards(self, orm):
29
30 # Deleting model 'ApplicationWidget'
31 db.delete_table('webcatalog_applicationwidget')
32
33 # Removing M2M table for field applications on 'ApplicationWidget'
34 db.delete_table('webcatalog_applicationwidget_applications')
35
36
37 models = {
38 'auth.group': {
39 'Meta': {'object_name': 'Group'},
40 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
41 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
42 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
43 },
44 'auth.permission': {
45 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
46 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
47 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
48 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
50 },
51 'auth.user': {
52 'Meta': {'object_name': 'User'},
53 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
54 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
55 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
56 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
57 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
58 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
59 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
60 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
61 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
62 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
63 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
64 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
65 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
66 },
67 'contenttypes.contenttype': {
68 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
69 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
70 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
71 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
72 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
73 },
74 'webcatalog.application': {
75 'Meta': {'ordering': "('-wilson_score', 'name')", 'unique_together': "(('distroseries', 'package_name'),)", 'object_name': 'Application'},
76 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
77 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
78 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}),
79 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
80 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
81 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
82 'debtags': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
83 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}),
84 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
85 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}),
86 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
87 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
88 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
89 'imported_from_sca': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
90 'is_latest': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
91 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
92 'license': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
93 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
94 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
95 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
96 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
97 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}),
98 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}),
99 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}),
100 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
101 'section': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
102 'version': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
103 'wilson_score': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
104 },
105 'webcatalog.applicationmedia': {
106 'Meta': {'ordering': "('url',)", 'unique_together': "(('application', 'url'),)", 'object_name': 'ApplicationMedia'},
107 'application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Application']"}),
108 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
109 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
110 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
111 },
112 'webcatalog.applicationwidget': {
113 'Meta': {'object_name': 'ApplicationWidget'},
114 'applications': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Application']", 'symmetrical': 'False'}),
115 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
116 'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
117 'template_snippet': ('django.db.models.fields.TextField', [], {})
118 },
119 'webcatalog.consumer': {
120 'Meta': {'object_name': 'Consumer'},
121 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
122 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
123 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
124 'secret': ('django.db.models.fields.CharField', [], {'default': "'WYDKgmMYvbPrqsjqWjFpMMVGEfHfHS'", 'max_length': '255', 'blank': 'True'}),
125 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
126 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"})
127 },
128 'webcatalog.department': {
129 'Meta': {'object_name': 'Department'},
130 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
131 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
132 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}),
133 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
134 },
135 'webcatalog.distroseries': {
136 'Meta': {'object_name': 'DistroSeries'},
137 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}),
138 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
139 'prerelease': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
140 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'})
141 },
142 'webcatalog.exhibit': {
143 'Meta': {'object_name': 'Exhibit'},
144 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
145 'click_url': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '200'}),
146 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
147 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
148 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}),
149 'html': ('django.db.models.fields.TextField', [], {}),
150 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
151 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
152 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
153 'sca_id': ('django.db.models.fields.IntegerField', [], {}),
154 'weight': ('django.db.models.fields.IntegerField', [], {'default': '0'})
155 },
156 'webcatalog.machine': {
157 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'},
158 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
159 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
160 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}),
161 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
162 'package_list': ('django.db.models.fields.TextField', [], {}),
163 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}),
164 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'})
165 },
166 'webcatalog.nonce': {
167 'Meta': {'unique_together': "(('nonce', 'token', 'consumer'),)", 'object_name': 'Nonce'},
168 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
169 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
170 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
171 'nonce': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
172 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"})
173 },
174 'webcatalog.reviewstatsimport': {
175 'Meta': {'object_name': 'ReviewStatsImport'},
176 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}),
177 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
178 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
179 },
180 'webcatalog.token': {
181 'Meta': {'object_name': 'Token'},
182 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}),
183 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
184 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
185 'token': ('django.db.models.fields.CharField', [], {'default': "'pjfQGToPAEUjSRBWGoACORELAevZrhxtbALJSHxqfaXZAEtIym'", 'max_length': '50', 'primary_key': 'True'}),
186 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'RbVwxdrIRcQnwdlszhxgeoFHoaLpOMUjZebJkAggzeKrAHGcHZ'", 'max_length': '50'}),
187 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
188 }
189 }
190
191 complete_apps = ['webcatalog']
0192
=== modified file 'src/webcatalog/models/__init__.py'
--- src/webcatalog/models/__init__.py 2012-03-19 08:16:51 +0000
+++ src/webcatalog/models/__init__.py 2012-09-06 14:09:20 +0000
@@ -26,6 +26,7 @@
26 'DistroSeries',26 'DistroSeries',
27 'Application',27 'Application',
28 'ApplicationMedia',28 'ApplicationMedia',
29 'ApplicationWidget',
29 'Department',30 'Department',
30 'Exhibit',31 'Exhibit',
31 'ReviewStatsImport',32 'ReviewStatsImport',
@@ -36,6 +37,7 @@
36from .applications import (37from .applications import (
37 Application,38 Application,
38 ApplicationMedia,39 ApplicationMedia,
40 ApplicationWidget,
39 Department,41 Department,
40 DistroSeries,42 DistroSeries,
41 Exhibit,43 Exhibit,
4244
=== modified file 'src/webcatalog/models/applications.py'
--- src/webcatalog/models/applications.py 2012-08-21 13:57:11 +0000
+++ src/webcatalog/models/applications.py 2012-09-06 14:09:20 +0000
@@ -41,6 +41,7 @@
41__all__ = [41__all__ = [
42 'Application',42 'Application',
43 'ApplicationMedia',43 'ApplicationMedia',
44 'ApplicationWidget',
44 'Department',45 'Department',
45 'DistroSeries',46 'DistroSeries',
46 'Exhibit',47 'Exhibit',
@@ -314,6 +315,19 @@
314 return reverse('wc-package-list') + '?' + pkgs315 return reverse('wc-package-list') + '?' + pkgs
315316
316317
318class ApplicationWidget(models.Model):
319 name = models.CharField(max_length=32,
320 help_text="Just a reference for the admin view.")
321 applications = models.ManyToManyField(Application)
322 template_snippet = models.TextField()
323
324 def __unicode__(self):
325 return self.name
326
327 class Meta:
328 app_label = 'webcatalog'
329
330
317# GroupPermissions can't go in fixtures because Permissions are stored as331# GroupPermissions can't go in fixtures because Permissions are stored as
318# model metadata, so our Permission's primary key can change without warning332# model metadata, so our Permission's primary key can change without warning
319def post_syncdb_handler(sender, **kwargs):333def post_syncdb_handler(sender, **kwargs):
320334
=== modified file 'src/webcatalog/schema.py'
--- src/webcatalog/schema.py 2012-08-23 15:12:54 +0000
+++ src/webcatalog/schema.py 2012-09-06 14:09:20 +0000
@@ -66,8 +66,6 @@
66 number_top_rated_apps = schema.IntOption(default=8)66 number_top_rated_apps = schema.IntOption(default=8)
67 screenshots_base_url = schema.StringOption(67 screenshots_base_url = schema.StringOption(
68 default='http://screenshots.ubuntu.com/')68 default='http://screenshots.ubuntu.com/')
69 hib_apps = schema.ListOption(
70 item=schema.StringOption(), default=[])
71 ubuntu_series_for_versions = schema.DictOption(69 ubuntu_series_for_versions = schema.DictOption(
72 item=schema.StringOption())70 item=schema.StringOption())
7371
7472
=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
--- src/webcatalog/templates/webcatalog/application_detail.html 2012-06-28 13:48:45 +0000
+++ src/webcatalog/templates/webcatalog/application_detail.html 2012-09-06 14:09:20 +0000
@@ -109,12 +109,9 @@
109 <p>{{ application.description|htmlize_package_description }}</p>109 <p>{{ application.description|htmlize_package_description }}</p>
110 <div class="install-button">{% install_options application %}</div>110 <div class="install-button">{% install_options application %}</div>
111 </div>111 </div>
112 {% if display_hib_widget %}112 {% for widget in application.applicationwidget_set.all %}
113 <div id="hib-widget" style="width: 652px; padding:24px 16px 8px; clear:both">113 {% render_app_widget widget.template_snippet %}
114 <p>{{ application.name }} is currently also available as part of the <a href="http://www.humblebundle.com/?utm_source=ubuntu">Humble Bundle</a></p>114 {% endfor %}
115 <iframe style="display: block; margin:0 auto; border:none;" src="http://www.humblebundle.com/_widget/html" width="410" height="150"></iframe>
116 </div>
117 {% endif %}
118 <div class="license">115 <div class="license">
119 <table>116 <table>
120 <tr>117 <tr>
121118
=== modified file 'src/webcatalog/templatetags/webcatalog.py'
--- src/webcatalog/templatetags/webcatalog.py 2012-07-02 21:25:30 +0000
+++ src/webcatalog/templatetags/webcatalog.py 2012-09-06 14:09:20 +0000
@@ -32,6 +32,7 @@
32from django.utils.html import escape32from django.utils.html import escape
33from django.utils.safestring import mark_safe33from django.utils.safestring import mark_safe
34from django.utils.translation import ugettext as _34from django.utils.translation import ugettext as _
35from django.template import Template
3536
36from webcatalog.models import Application37from webcatalog.models import Application
37from webcatalog.utilities import UserAgentString38from webcatalog.utilities import UserAgentString
@@ -196,3 +197,9 @@
196197
197 return dict(stars=stars, total=num_ratings,198 return dict(stars=stars, total=num_ratings,
198 STATIC_URL=settings.STATIC_URL, size=size)199 STATIC_URL=settings.STATIC_URL, size=size)
200
201
202@register.simple_tag(takes_context=True)
203def render_app_widget(context, template_snippet):
204 template = Template(template_snippet)
205 return template.render(context)
199206
=== modified file 'src/webcatalog/tests/factory.py'
--- src/webcatalog/tests/factory.py 2012-08-23 10:07:28 +0000
+++ src/webcatalog/tests/factory.py 2012-09-06 14:09:20 +0000
@@ -40,6 +40,7 @@
40from webcatalog.models import (40from webcatalog.models import (
41 Application,41 Application,
42 ApplicationMedia,42 ApplicationMedia,
43 ApplicationWidget,
43 Consumer,44 Consumer,
44 Department,45 Department,
45 DistroSeries,46 DistroSeries,
@@ -250,6 +251,16 @@
250 expire_date=expire_date251 expire_date=expire_date
251 )252 )
252253
254 def make_app_widget(self, applications=None, template_snippet=None):
255 if applications is None:
256 applications = [self.make_application()]
257 if template_snippet is None:
258 template_snippet = u"Promotion for {{ application.name }}"
259
260 widget = ApplicationWidget.objects.create(
261 template_snippet=template_snippet)
262 widget.applications = applications
263
253264
254class TestCaseWithFactory(TestCase):265class TestCaseWithFactory(TestCase):
255266
256267
=== modified file 'src/webcatalog/tests/test_views.py'
--- src/webcatalog/tests/test_views.py 2012-07-02 21:25:30 +0000
+++ src/webcatalog/tests/test_views.py 2012-09-06 14:09:20 +0000
@@ -205,16 +205,43 @@
205 self.assertContains(response, "https://twitter.com/share")205 self.assertContains(response, "https://twitter.com/share")
206206
207 def test_hib_widget_is_present_for_hib_apps(self):207 def test_hib_widget_is_present_for_hib_apps(self):
208 with patch_settings(HIB_APPS=['pkgfoo']):208 app = self.factory.make_application(package_name='pkgfoo',
209 response, app = self.get_app_and_response()209 name=u'A great foo')
210210 self.factory.make_app_widget(
211 self.assertContains(response, '<div id="hib-widget"')211 applications=[app],
212 template_snippet=u"<p>Promotion for {{ application.name }}</p>")
213
214 response = self.get_package_details_response('pkgfoo')
215
216 self.assertContains(response, '<p>Promotion for A great foo</p>')
212217
213 def test_hib_widget_not_present_for_non_hib_apps(self):218 def test_hib_widget_not_present_for_non_hib_apps(self):
214 with patch_settings(HIB_APPS=['something', 'different']):219 self.factory.make_application(package_name='pkgfoo',
215 response, app = self.get_app_and_response()220 name=u'A great foo')
216221 other_app = self.factory.make_application(package_name='pkgbar',
217 self.assertNotContains(response, '<div id="hib-widget"')222 name=u'Another app')
223 self.factory.make_app_widget(
224 applications=[other_app],
225 template_snippet=u"Promotion for {{ application.name }}")
226
227 response = self.get_package_details_response('pkgfoo')
228
229 self.assertNotContains(response, 'Promotion for')
230
231 def test_multiple_promotions_for_app(self):
232 app = self.factory.make_application(package_name='pkgfoo',
233 name=u'A great foo')
234 self.factory.make_app_widget(
235 applications=[app],
236 template_snippet=u"Promotion X for {{ application.name }}")
237 self.factory.make_app_widget(
238 applications=[app],
239 template_snippet=u"Promotion Y for {{ application.name }}")
240
241 response = self.get_package_details_response('pkgfoo')
242
243 self.assertContains(response, 'Promotion X for A great foo')
244 self.assertContains(response, 'Promotion Y for A great foo')
218245
219 def test_twitter_link_does_not_contain_none(self):246 def test_twitter_link_does_not_contain_none(self):
220 app = self.factory.make_application()247 app = self.factory.make_application()
221248
=== modified file 'src/webcatalog/views.py'
--- src/webcatalog/views.py 2012-07-02 21:25:30 +0000
+++ src/webcatalog/views.py 2012-09-06 14:09:20 +0000
@@ -219,8 +219,6 @@
219219
220 debtags = None if not app.debtags else json.loads(app.debtags)220 debtags = None if not app.debtags else json.loads(app.debtags)
221221
222 hib_apps = getattr(settings, 'HIB_APPS', [])
223
224 atts = {222 atts = {
225 'application': app,223 'application': app,
226 'available_distroseries': app.available_distroseries(),224 'available_distroseries': app.available_distroseries(),
@@ -230,7 +228,6 @@
230 reverse('wc-package-detail', args=[package_name])),228 reverse('wc-package-detail', args=[package_name])),
231 'email_form': form,229 'email_form': form,
232 'debtags': debtags,230 'debtags': debtags,
233 'display_hib_widget': app.package_name in hib_apps
234 }231 }
235232
236 return render_to_response(233 return render_to_response(

Subscribers

People subscribed via source and target branches