Merge lp:~michael.nelson/ubuntu-webcatalog/962140-display-video-demos-pt-2 into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Łukasz Czyżykowski
Approved revision: 126
Merged at revision: 117
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/962140-display-video-demos-pt-2
Merge into: lp:ubuntu-webcatalog
Diff against target: 180 lines (+116/-4)
5 files modified
src/webcatalog/models/applications.py (+15/-4)
src/webcatalog/static/css/webcatalog.css (+9/-0)
src/webcatalog/templates/webcatalog/application_detail.html (+8/-0)
src/webcatalog/tests/test_models.py (+73/-0)
src/webcatalog/tests/test_views.py (+11/-0)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/962140-display-video-demos-pt-2
Reviewer Review Type Date Requested Status
Canonical Consumer Applications Hackers Pending
Review via email: mp+104714@code.launchpad.net

Commit message

Add videos to the application detail page.

Description of the change

Overview
========

Adds videos to the application detail page, to look like this:

http://people.canonical.com/~michaeln/tmp/962140-video-iframes.png

Note:
 * The video we have on staging is no longer allowed to be embedded (that's an option set by the author)
 * I've also tweaked the style for the sharing options so that the facebook options are aligned.

`fab test`

To post a comment you must log in.
Revision history for this message
Łukasz Czyżykowski (lukasz-czyzykowski) wrote :

Why using setattr, gettattr, hasattr for media caching instead of just plain dict (self._media_cache = {}). Or other than doing per request cache, maybe it would be better to push that to django cache layer?

126. By Michael Nelson

REFACTOR: update media cache to use a single dict rather than multiple private attributes.

Revision history for this message
Michael Nelson (michael.nelson) wrote :

On Fri, May 4, 2012 at 12:47 PM, Łukasz Czyżykowski
<email address hidden> wrote:
> Why using setattr, gettattr, hasattr for media caching instead of just plain dict (self._media_cache = {}). Or other than doing per request cache, maybe it would be better to push that to django cache layer?

12:02 < noodles> lukasz: +1 for self._media_cache instead, but I think
we'd specifically not want to use the django cache layer as caching
over multiple requests would be bad?
12:02 * noodles updates
12:03 < lukasz> noodles, why?
12:06 < noodles> lukasz: run import_for_pay_apps, then view a specific
app, but it's screenshots haven't updated, or multiple points in the
code need to know about the cache (to clear it during import)
12:06 < noodles> lukasz: I guess the second option isn't so bad.
12:07 < lukasz> noodles, yeah, that would be my only concern, a need
for keeping the cache up to date
12:07 < lukasz> noodles, ok, let's leave it at dict
12:07 < noodles> k
12:12 < noodles> lukasz: pushed with r126

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/webcatalog/models/applications.py'
--- src/webcatalog/models/applications.py 2012-05-03 09:59:18 +0000
+++ src/webcatalog/models/applications.py 2012-05-04 12:14:18 +0000
@@ -114,12 +114,23 @@
114 def __unicode__(self):114 def __unicode__(self):
115 return u"{0} ({1})".format(self.name, self.package_name)115 return u"{0} ({1})".format(self.name, self.package_name)
116116
117 def _get_media(self, media_type):
118 if not hasattr(self, '_media_cache'):
119 self._media_cache = {}
120
121 if not media_type in self._media_cache:
122 medias = self.applicationmedia_set.filter(media_type=media_type)
123 self._media_cache[media_type] = [media.url for media in medias]
124
125 return self._media_cache[media_type]
126
117 @property127 @property
118 def screenshots(self):128 def screenshots(self):
119 if not hasattr(self, '_screenshots'):129 return self._get_media('screenshot')
120 qs = self.applicationmedia_set.filter(media_type='screenshot')130
121 self._screenshots = [am.url for am in qs]131 @property
122 return self._screenshots132 def video_iframe_urls(self):
133 return self._get_media('video')
123134
124 @property135 @property
125 def categories_set(self):136 def categories_set(self):
126137
=== modified file 'src/webcatalog/static/css/webcatalog.css'
--- src/webcatalog/static/css/webcatalog.css 2012-04-19 23:05:19 +0000
+++ src/webcatalog/static/css/webcatalog.css 2012-05-04 12:14:18 +0000
@@ -58,6 +58,9 @@
58.share {58.share {
59 padding: 16px;59 padding: 16px;
60}60}
61.share .fb-like {
62 height: 24px;
63}
61.description {64.description {
62 padding: 42px 16px;65 padding: 42px 16px;
63 font-size: 14px;66 font-size: 14px;
@@ -427,3 +430,9 @@
427 margin: 32px;430 margin: 32px;
428 color: #888;431 color: #888;
429}432}
433
434div.videos iframe {
435 width: 100%;
436 height: 225px;
437 margin: 16px;
438}
430439
=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
--- src/webcatalog/templates/webcatalog/application_detail.html 2012-04-20 18:22:42 +0000
+++ src/webcatalog/templates/webcatalog/application_detail.html 2012-05-04 12:14:18 +0000
@@ -119,6 +119,14 @@
119 {% endif %}119 {% endif %}
120 </table>120 </table>
121 </div>121 </div>
122 {% if application.video_iframe_urls %}
123 <div class="videos">
124 <h3>Videos</h3>
125 {% for iframe_url in application.video_iframe_urls %}
126 <iframe src="{{ iframe_url }}"></iframe>
127 {% endfor %}
128 </div>
129 {% endif %}
122 <div class="share">130 <div class="share">
123 {% include "webcatalog/twitter_share_snippet.html" %}131 {% include "webcatalog/twitter_share_snippet.html" %}
124 {% include "webcatalog/googleplus_share_snippet.html" %}132 {% include "webcatalog/googleplus_share_snippet.html" %}
125133
=== modified file 'src/webcatalog/tests/test_models.py'
--- src/webcatalog/tests/test_models.py 2012-04-05 01:09:37 +0000
+++ src/webcatalog/tests/test_models.py 2012-05-04 12:14:18 +0000
@@ -121,6 +121,79 @@
121 self.assertTrue(icon_url.startswith('/site_media/'))121 self.assertTrue(icon_url.startswith('/site_media/'))
122 self.assertTrue('ubuntu-cof' in icon_url)122 self.assertTrue('ubuntu-cof' in icon_url)
123123
124 def test_screenshots(self):
125 app = self.factory.make_application()
126 app.applicationmedia_set.create(
127 media_type='screenshot',
128 url='http://example.com/screenshot1.png')
129 app.applicationmedia_set.create(
130 media_type='screenshot',
131 url='http://example.com/screenshot2.png')
132 app.applicationmedia_set.create(
133 media_type='video',
134 url='http://example.com/video.m4v')
135
136 urls = app.screenshots
137
138 self.assertEqual([
139 'http://example.com/screenshot1.png',
140 'http://example.com/screenshot2.png',
141 ],
142 urls)
143
144 def test_screenshots_cached(self):
145 # The db isn't hit if _screenshots is already
146 # cached.
147 app = self.factory.make_application()
148 app.applicationmedia_set.create(
149 media_type='screenshot',
150 url='http://example.com/screenshot1.png')
151 app.screenshots
152 app.applicationmedia_set.create(
153 media_type='screenshot',
154 url='http://example.com/screenshot2.png')
155
156 self.assertEqual([
157 'http://example.com/screenshot1.png',
158 ], app.screenshots)
159
160 def test_video_iframes_urls(self):
161 app = self.factory.make_application()
162 app.applicationmedia_set.create(
163 media_type='video',
164 url='http://example.com/video1.m4v')
165 app.applicationmedia_set.create(
166 media_type='video',
167 url='http://example.com/video2.m4v')
168 app.applicationmedia_set.create(
169 media_type='screenshot',
170 url='http://example.com/screenshot.png')
171
172 urls = app.video_iframe_urls
173
174 self.assertEqual([
175 'http://example.com/video1.m4v',
176 'http://example.com/video2.m4v',
177 ],
178 urls)
179
180 def test_video_iframes_urls_cached(self):
181 app = self.factory.make_application()
182 app.applicationmedia_set.create(
183 media_type='video',
184 url='http://example.com/video1.m4v')
185 app.video_iframe_urls
186 app.applicationmedia_set.create(
187 media_type='video',
188 url='http://example.com/video2.m4v')
189
190 urls = app.video_iframe_urls
191
192 self.assertEqual([
193 'http://example.com/video1.m4v',
194 ],
195 urls)
196
124197
125class DepartmentTestCase(TestCaseWithFactory):198class DepartmentTestCase(TestCaseWithFactory):
126 def test_crumbs_no_parent(self):199 def test_crumbs_no_parent(self):
127200
=== modified file 'src/webcatalog/tests/test_views.py'
--- src/webcatalog/tests/test_views.py 2012-04-20 18:22:42 +0000
+++ src/webcatalog/tests/test_views.py 2012-05-04 12:14:18 +0000
@@ -366,6 +366,17 @@
366366
367 self.assertNotContains(response, 'Hardware requirements')367 self.assertNotContains(response, 'Hardware requirements')
368368
369 def test_videos_displayed(self):
370 app = self.factory.make_application()
371 app.applicationmedia_set.create(
372 media_type="video",
373 url="http://example.com/video_iframe.html")
374
375 response = self.client.get(self.get_app_details_url(app))
376
377 self.assertContains(response,
378 '<iframe src="http://example.com/video_iframe.html"')
379
369380
370class ApplicationDetailNoSeriesTestCase(TestCaseWithFactory):381class ApplicationDetailNoSeriesTestCase(TestCaseWithFactory):
371 def test_renders_latest(self):382 def test_renders_latest(self):

Subscribers

People subscribed via source and target branches