Merge lp:~elachuni/ubuntu-webcatalog/import-partner into lp:ubuntu-webcatalog

Proposed by Anthony Lenton
Status: Merged
Approved by: Danny Tamez
Approved revision: 94
Merged at revision: 96
Proposed branch: lp:~elachuni/ubuntu-webcatalog/import-partner
Merge into: lp:ubuntu-webcatalog
Diff against target: 546 lines (+137/-128)
3 files modified
src/webcatalog/management/commands/import_app_install_data.py (+73/-73)
src/webcatalog/models/applications.py (+1/-1)
src/webcatalog/tests/test_commands.py (+63/-54)
To merge this branch: bzr merge lp:~elachuni/ubuntu-webcatalog/import-partner
Reviewer Review Type Date Requested Status
Danny Tamez (community) Approve
Review via email: mp+100045@code.launchpad.net

Commit message

Made import_app_install_data also sync apps from the partner repo

Description of the change

Overview
========
This branch refactors import_app_install_data, and makes it also sync apps from archive.canonical.com (using metadata from app-install-data-partner).

Details
=======
The biggest refactor change is to the Cache class, that is no longer a context manager. It didn't seem to be necessary, as it was only using the __exit__ method to clean up a temporary directory that shouldn't have been created in the first place.

Besides that, the changes are mainly so that the command also processes app-install-data-partner, like it does app-install-data.

The Application.section field now uses blank=True, because desktop files in app-install-data-partner don't provide the x-appinstall-section field, and the right department can be inferred from the category.

Finally, import_app_install_data.Command.get_screenshot_urls_from_server was patched out during tests so that it doesn't hit the wire at all.

To post a comment you must log in.
Revision history for this message
Danny Tamez (zematynnad) wrote :

Nice. works great.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/management/commands/import_app_install_data.py'
2--- src/webcatalog/management/commands/import_app_install_data.py 2012-03-28 12:45:14 +0000
3+++ src/webcatalog/management/commands/import_app_install_data.py 2012-03-29 22:02:21 +0000
4@@ -51,7 +51,12 @@
5 ]
6
7
8-class Cache(object):
9+class CacheMaker(object):
10+ sources = """
11+deb http://archive.ubuntu.com/ubuntu {distroseries} main universe
12+deb http://archive.ubuntu.com/ubuntu {distroseries}-updates main universe
13+deb http://archive.canonical.com/ubuntu {distroseries} partner
14+"""
15
16 def __init__(self, distroseries):
17 self.distroseries = distroseries
18@@ -61,12 +66,8 @@
19 sources_list = os.path.join(
20 series_cache_location, 'etc', 'apt', 'sources.list')
21 with open(sources_list, "w+") as sources_list_file:
22- sources_list_file.write(
23- 'deb http://archive.ubuntu.com/ubuntu {distroseries}'
24- ' main universe\n'
25- 'deb http://archive.ubuntu.com/ubuntu {distroseries}-updates'
26- ' main universe\n'
27- .format(distroseries=self.distroseries))
28+ sources_list_file.write(self.sources.format(
29+ distroseries=self.distroseries))
30
31 def get_apt_cache(self):
32 # Create on-disk cache for distroseries if it
33@@ -76,20 +77,7 @@
34 if not os.path.exists(series_cache_location):
35 self.create_apt_cache(series_cache_location)
36
37- return apt.Cache(rootdir=series_cache_location)
38-
39- def __enter__(self):
40- tmp_sources_dir = tempfile.mkdtemp()
41- self.tmp_sources_dir = tmp_sources_dir
42- os.makedirs(os.path.join(tmp_sources_dir, 'etc', 'apt'))
43- tmp_sources_list = os.path.join(
44- tmp_sources_dir, 'etc', 'apt', 'sources.list')
45- with open(tmp_sources_list, "w+") as sources_list:
46- sources_list.write(
47- 'deb http://archive.ubuntu.com/ubuntu {0} main\n'.format(
48- self.distroseries))
49-
50- cache = self.get_apt_cache()
51+ cache = apt.Cache(rootdir=series_cache_location)
52 try:
53 # XXX michaeln bug=879268 Lucid doesnt know about translations.
54 # As long as our server is running on Lucid, we need to ensure
55@@ -103,26 +91,39 @@
56 cache.open()
57 return cache
58
59- def __exit__(self, exc_type, exc_value, exc_traceback):
60- shutil.rmtree(self.tmp_sources_dir)
61-
62
63 class Command(LabelCommand):
64
65- help = "Update Application data from app-install-data package."
66+ help = ("Update Application data from apt cache and "
67+ "app-install-data(-partner) packages.")
68 option_list = LabelCommand.option_list + (
69- make_option('--local-app-install-deb',
70+ make_option('--local-app-install-data',
71 action='store',
72- dest='local_app_install_deb',
73+ dest='local_app_install_data',
74 default='',
75 help=('Use a local app-install-data deb package rather than '
76 'downloading from the archive.')),
77+ make_option('--local-app-install-data-partner',
78+ action='store',
79+ dest='local_app_install_data_partner',
80+ default='',
81+ help=('Use a local app-install-data-partner deb package rather '
82+ 'than downloading from the archive.')),
83 )
84 verbosity = 1
85
86+ def set_cache(self, distroseries):
87+ """Set self.cache.
88+
89+ Call set_cache at some point before accessing self.cache.
90+ Calling more than once does no harm.
91+ """
92+ if not hasattr(self, 'cache'):
93+ maker = CacheMaker(distroseries)
94+ self.cache = maker.get_apt_cache()
95+
96 def handle_label(self, distroseries_name, **options):
97 self.verbosity = int(options['verbosity'])
98- data_dir = tempfile.mkdtemp()
99 # First create the apps based on the current cache.
100 distroseries, created = DistroSeries.objects.get_or_create(
101 code_name=distroseries_name)
102@@ -130,32 +131,37 @@
103 self.output(
104 "Created a DistroSeries record called '{0}'.\n".format(
105 distroseries_name), 1)
106- with Cache(distroseries_name) as cache:
107- self.update_from_cache(cache, distroseries_name)
108-
109- # Download app install data when requested.
110- if options['local_app_install_deb'] == '':
111- deb_location = self.get_latest_app_data_for_series(
112- distroseries_name, data_dir, cache)
113- else:
114- deb_location = options['local_app_install_deb']
115-
116- # Extract and parse the deb archive.
117- deb_file = DebFile(deb_location)
118- self.output("Processing application data to update database...\n",
119- 1)
120- deb_file.data.extractall(data_dir)
121- matcher = data_dir + '/usr/share/app-install/desktop/*.desktop'
122- icon_dir = data_dir + '/usr/share/app-install/icons/'
123-
124- for desktop_file in iglob(matcher):
125- if self.verbosity > 1:
126- self.output("Processing {0}\n".format(desktop_file), 1)
127- elif self.verbosity > 0:
128- self.output(".", 1, flush=True)
129- self.process_desktop_file(desktop_file, icon_dir, distroseries)
130-
131- shutil.rmtree(data_dir)
132+ self.update_from_cache(distroseries_name)
133+
134+ for package in ('app-install-data', 'app-install-data-partner', ):
135+ opt_name = 'local_{0}'.format(package.replace('-', '_'))
136+ deb_location = options.get(opt_name, '')
137+ self.update_from_app_data(package, distroseries, deb_location)
138+
139+ def update_from_app_data(self, package_name, distroseries,
140+ deb_location=''):
141+ data_dir = tempfile.mkdtemp()
142+ # Download app install data if necessary
143+ if deb_location == '':
144+ deb_location = self.get_package_data_for_series(package_name,
145+ distroseries.code_name, data_dir)
146+
147+ # Extract and parse the deb archive.
148+ deb_file = DebFile(deb_location)
149+ self.output("Processing {0} to update database...\n".format(
150+ package_name), 1)
151+ deb_file.data.extractall(data_dir)
152+ matcher = data_dir + '/usr/share/app-install/desktop/*.desktop'
153+ icon_dir = data_dir + '/usr/share/app-install/icons/'
154+
155+ for desktop_file in iglob(matcher):
156+ if self.verbosity > 1:
157+ self.output("Processing {0}\n".format(desktop_file), 1)
158+ elif self.verbosity > 0:
159+ self.output(".", 1, flush=True)
160+ self.process_desktop_file(desktop_file, icon_dir, distroseries)
161+
162+ shutil.rmtree(data_dir)
163
164 def get_screenshot_urls_from_server(self, app):
165 url = urljoin(settings.SCREENSHOTS_BASE_URL,
166@@ -167,7 +173,7 @@
167 for entry in data['screenshots']:
168 screenshot_url = entry['large_image_url']
169
170- self.output("Adding {0} to {1}".format(
171+ self.output("Adding {0} to {1}\n".format(
172 screenshot_url,
173 app.package_name), 1)
174 app.applicationmedia_set.get_or_create(
175@@ -178,31 +184,24 @@
176 except (IOError, TypeError):
177 pass
178
179- def get_latest_app_data_for_series(self, distroseries, working_dir,
180- cache=None):
181- # Write a temporary sources.list, update the cache and access:
182- # cache['app-install-data-ubuntu'].candidate.uri
183- install_data_url = self.get_app_data_uri_for_series(distroseries,
184- cache)
185+ def get_package_data_for_series(self, package_name, distroseries,
186+ working_dir):
187+ install_data_url = self.get_package_uri_for_series(
188+ package_name, distroseries)
189 rest, sep, deb_filename = install_data_url.rpartition('/')
190
191- self.output(u"Downloading app-install-data from {0}...\n".format(
192+ self.output(u"Downloading {0} from {1}...\n".format(package_name,
193 install_data_url), 1)
194 deb_location = os.path.join(working_dir, deb_filename)
195 urllib.urlretrieve(install_data_url, deb_location)
196 self.output("Done.\n", 1)
197 return deb_location
198
199- def get_app_data_uri_for_series(self, distroseries, cache=None):
200+ def get_package_uri_for_series(self, package_name, distroseries):
201 self.output("Determining URI for current version of "
202- "app-install-data in {0}.\n".format(distroseries), 1)
203- uri = None
204- if cache == None:
205- with Cache(distroseries) as cache:
206- uri = cache['app-install-data'].candidate.uri
207- else:
208- uri = cache['app-install-data'].candidate.uri
209- return uri
210+ "{0} in {1}.\n".format(package_name, distroseries), 1)
211+ self.set_cache(distroseries)
212+ return self.cache[package_name].candidate.uri
213
214 def process_desktop_file(self, desktop_file_path, icon_dir, distroseries):
215 with open(desktop_file_path, 'r') as desktop_file:
216@@ -262,7 +261,7 @@
217 app.name, icon_path_generic,
218 '|'.join(supported_extensions)).encode('utf-8'), 1)
219
220- def update_from_cache(self, cache, distroseries_name):
221+ def update_from_cache(self, distroseries_name):
222 distroseries, created = DistroSeries.objects.get_or_create(
223 code_name=distroseries_name)
224 if created:
225@@ -272,9 +271,10 @@
226 prefetched_apps = dict((app.package_name, app) for app in
227 Application.objects.filter(distroseries=distroseries))
228
229+ self.set_cache(distroseries_name)
230 # For each package in the apt-cache, we update (or possibly
231 # create) a matching db entry.
232- for package in cache:
233+ for package in self.cache:
234 if self.verbosity > 1:
235 self.output("Updating {0}\n".format(package.name), 1)
236 elif self.verbosity > 0:
237
238=== modified file 'src/webcatalog/models/applications.py'
239--- src/webcatalog/models/applications.py 2012-03-23 20:14:03 +0000
240+++ src/webcatalog/models/applications.py 2012-03-29 22:02:21 +0000
241@@ -78,7 +78,7 @@
242 architectures = models.CharField(max_length=255, blank=True)
243 keywords = models.CharField(max_length=255, blank=True)
244 app_type = models.CharField(max_length=32, blank=True)
245- section = models.CharField(max_length=32)
246+ section = models.CharField(max_length=32, blank=True)
247 categories = models.CharField(max_length=255, blank=True)
248 departments = models.ManyToManyField('Department', blank=True)
249 icon_name = models.CharField(max_length=255, blank=True)
250
251=== modified file 'src/webcatalog/tests/test_commands.py'
252--- src/webcatalog/tests/test_commands.py 2012-03-28 12:44:35 +0000
253+++ src/webcatalog/tests/test_commands.py 2012-03-29 22:02:21 +0000
254@@ -48,7 +48,6 @@
255 ReviewStatsImport,
256 )
257 from webcatalog.management.commands.import_app_install_data import (
258- Cache as CacheContextManager,
259 Command as ImportAppInstallCommand,
260 )
261 from webcatalog.management.commands.import_ratings_stats import (
262@@ -131,6 +130,8 @@
263 def setUp(self):
264 """Create a mock apt-cache so we don't wait for the update."""
265 super(ImportAppInstallTestCase, self).setUp()
266+ self.deb_location = self.factory.get_test_path(
267+ 'app-install-data-test_all.deb')
268
269 # We use a temporary on-disk cache location for our
270 # tests.
271@@ -139,6 +140,11 @@
272 'DISK_APT_CACHE_LOCATION', self.tmp_apt_cache)
273 self.patch_cache_setting.start()
274
275+ self.patch_get_screenshot_urls = patch(
276+ 'webcatalog.management.commands.import_app_install_data.'
277+ 'Command.get_screenshot_urls_from_server')
278+ self.patch_get_screenshot_urls.start()
279+
280 if self.use_mock_apt_cache:
281 # The apt.Cache class is patched to return our own mock which
282 # will be subscriptable with a pre-set dict with mock ock apt
283@@ -150,6 +156,8 @@
284
285 def tearDown(self):
286 self.patch_cache_setting.stop()
287+ if hasattr(self, 'patch_get_screenshot_urls'):
288+ self.patch_get_screenshot_urls.stop()
289 shutil.rmtree(self.tmp_apt_cache)
290 if self.use_mock_apt_cache:
291 self.patch_cache.stop()
292@@ -158,41 +166,41 @@
293 def test_app_data_downloaded_by_default(self):
294 get_data_fn = ('webcatalog.management.commands.'
295 'import_app_install_data.Command.'
296- 'get_latest_app_data_for_series')
297+ 'get_package_data_for_series')
298 with patch(get_data_fn) as mock_get_data_fn:
299- mock_get_data_fn.return_value = self.factory.get_test_path(
300- 'app-install-data-test_all.deb')
301+ mock_get_data_fn.return_value = self.deb_location
302 call_command('import_app_install_data', 'onion', verbosity=0)
303
304- self.assertEqual(1, mock_get_data_fn.call_count)
305- args = mock_get_data_fn.call_args[0]
306- self.assertEqual('onion', args[0])
307- self.assertTrue(args[1].startswith('/tmp/tmp'))
308- if self.use_mock_apt_cache:
309- self.assertEqual(self.mock_cache, args[2])
310+ self.assertEqual(2, mock_get_data_fn.call_count)
311+ for call_args, package_name in zip(mock_get_data_fn.call_args_list,
312+ ('app-install-data', 'app-install-data-partner')):
313+ args = call_args[0]
314+ self.assertEqual(package_name, args[0])
315+ self.assertEqual('onion', args[1])
316+ self.assertTrue(args[2].startswith('/tmp/tmp'))
317
318- def test_get_latest_app_data_for_series(self):
319+ def test_get_package_data_for_series(self):
320 # Requested and downloaded from the server.
321 tmp_dir = tempfile.mkdtemp()
322 get_uri_fn = (
323 'webcatalog.management.commands.import_app_install_data.'
324- 'Command.get_app_data_uri_for_series')
325+ 'Command.get_package_uri_for_series')
326 with patch(get_uri_fn) as mock_get_uri:
327 mock_get_uri.return_value = 'http://example.com/my.deb'
328 with patch('urllib.urlretrieve') as mock_urlretrieve:
329- ImportAppInstallCommand().get_latest_app_data_for_series(
330- 'natty', tmp_dir)
331+ ImportAppInstallCommand().get_package_data_for_series(
332+ 'app-install-data', 'natty', tmp_dir)
333 shutil.rmtree(tmp_dir)
334
335 mock_urlretrieve.assert_called_with(
336 'http://example.com/my.deb', os.path.join(tmp_dir, 'my.deb'))
337
338- def test_local_app_install_deb(self):
339+ def test_local_app_install_data(self):
340 # We don't hit the network when provided a local app-install deb.
341 with patch('urllib.urlretrieve') as mock_urlretrieve:
342 call_command('import_app_install_data', 'natty',
343- local_app_install_deb=self.factory.get_test_path(
344- 'app-install-data-test_all.deb'),
345+ local_app_install_data=self.deb_location,
346+ local_app_install_data_partner=self.deb_location,
347 verbosity=0)
348
349 self.assertEqual(0, mock_urlretrieve.call_count)
350@@ -202,8 +210,8 @@
351
352 call_command(
353 'import_app_install_data', 'natty',
354- local_app_install_deb=self.factory.get_test_path(
355- 'app-install-data-test_all.deb'),
356+ local_app_install_data=self.deb_location,
357+ local_app_install_data_partner=self.deb_location,
358 verbosity=0)
359
360 self.assertEqual(4, Application.objects.count())
361@@ -222,8 +230,8 @@
362
363 call_command(
364 'import_app_install_data', 'natty',
365- local_app_install_deb=self.factory.get_test_path(
366- 'app-install-data-test_all.deb'),
367+ local_app_install_data=self.deb_location,
368+ local_app_install_data_partner=self.deb_location,
369 verbosity=0)
370
371 app_reloaded = Application.objects.get(id=app.id)
372@@ -240,8 +248,8 @@
373
374 call_command(
375 'import_app_install_data', 'oneric',
376- local_app_install_deb=self.factory.get_test_path(
377- 'app-install-data-test_all.deb'),
378+ local_app_install_data=self.deb_location,
379+ local_app_install_data_partner=self.deb_location,
380 verbosity=0)
381
382 app_reloaded = Application.objects.get(id=app.id)
383@@ -252,8 +260,8 @@
384 def test_distroseries_added_to_app(self):
385 call_command(
386 'import_app_install_data', 'natty',
387- local_app_install_deb=self.factory.get_test_path(
388- 'app-install-data-test_all.deb'),
389+ local_app_install_data=self.deb_location,
390+ local_app_install_data_partner=self.deb_location,
391 verbosity=0)
392
393 self.assertEqual(4, Application.objects.count())
394@@ -263,8 +271,8 @@
395 def test_icon_added_to_model(self):
396 call_command(
397 'import_app_install_data', 'natty',
398- local_app_install_deb=self.factory.get_test_path(
399- 'app-install-data-test_all.deb'),
400+ local_app_install_data=self.deb_location,
401+ local_app_install_data_partner=self.deb_location,
402 verbosity=0)
403
404 self.assertEqual(4, Application.objects.count())
405@@ -275,6 +283,8 @@
406
407 def test_screenshots_added_to_the_model(self):
408 fn = 'webcatalog.management.commands.import_app_install_data.urllib'
409+ self.patch_get_screenshot_urls.stop()
410+ del self.patch_get_screenshot_urls
411 with patch(fn) as mock_urllib:
412 mock_response = Mock()
413 mock_response.getcode.return_value = 200
414@@ -285,18 +295,18 @@
415 )
416 mock_urllib.urlopen.return_value = mock_response
417
418- call_command(
419- 'import_app_install_data', 'natty',
420- local_app_install_deb=self.factory.get_test_path(
421- 'app-install-data-test_all.deb'),
422+ call_command('import_app_install_data', 'natty',
423+ local_app_install_data=self.deb_location,
424+ local_app_install_data_partner=self.deb_location,
425 verbosity=0)
426
427 scribus = Application.objects.get(package_name='scribus')
428
429 self.assertEqual(scribus.screenshots, ["http://example.com/1.png"])
430
431- def test_get_app_data_uri_for_series(self):
432- uri = ImportAppInstallCommand().get_app_data_uri_for_series('natty')
433+ def get_package_uri_for_series(self):
434+ uri = ImportAppInstallCommand().get_package_uri_for_series(
435+ 'app-install-data', 'natty')
436
437 self.assertEqual('http://example.com/app-install-1.01.deb', uri)
438
439@@ -305,8 +315,8 @@
440
441 call_command(
442 'import_app_install_data', 'natty',
443- local_app_install_deb=self.factory.get_test_path(
444- 'app-install-data-test_all.deb'),
445+ local_app_install_data=self.deb_location,
446+ local_app_install_data_partner=self.deb_location,
447 verbosity=0)
448
449 self.assertEqual(4, Application.objects.count())
450@@ -325,8 +335,8 @@
451
452 call_command(
453 'import_app_install_data', 'natty',
454- local_app_install_deb=self.factory.get_test_path(
455- 'app-install-data-test_all.deb'),
456+ local_app_install_data=self.deb_location,
457+ local_app_install_data_partner=self.deb_location,
458 verbosity=0)
459
460 app_reloaded = Application.objects.get(id=app.id)
461@@ -335,12 +345,12 @@
462 app_reloaded.comment)
463 self.assertEqual("Scribus description", app_reloaded.description)
464
465- def test_cache_context_manager_creates_sources_list(self):
466+ def test_cache_maker_creates_sources_list(self):
467 # If the on-disk cache doesn't yet exist, it is created.
468 call_command(
469 'import_app_install_data', 'natty',
470- local_app_install_deb=self.factory.get_test_path(
471- 'app-install-data-test_all.deb'),
472+ local_app_install_data=self.deb_location,
473+ local_app_install_data_partner=self.deb_location,
474 verbosity=0)
475
476 natty_cache = os.path.join(self.tmp_apt_cache, 'natty')
477@@ -349,16 +359,17 @@
478 self.assertTrue(os.path.exists(os.path.join(sources_list_path)))
479 with open(sources_list_path) as content:
480 sources_list_content = content.read()
481- self.assertEqual(
482- 'deb http://archive.ubuntu.com/ubuntu natty main universe\n'
483- 'deb http://archive.ubuntu.com/ubuntu '
484- 'natty-updates main universe\n',
485+ self.assertEqual("""
486+deb http://archive.ubuntu.com/ubuntu natty main universe
487+deb http://archive.ubuntu.com/ubuntu natty-updates main universe
488+deb http://archive.canonical.com/ubuntu natty partner
489+""",
490 sources_list_content)
491 if self.use_mock_apt_cache:
492 self.mock_cache_class.assert_called_with(rootdir=natty_cache)
493 self.assertEqual(1, self.mock_cache.update.call_count)
494
495- def test_cache_context_manager_doesnt_overwrite_existing(self):
496+ def test_cache_maker_doesnt_overwrite_existing(self):
497 # If an on-disk cache for the distroseries already exists, a
498 # new one is *not* created.
499 series_cache = os.path.join(self.tmp_apt_cache, 'testwebcatseries')
500@@ -368,12 +379,12 @@
501
502 create_apt_cache_fn = (
503 'webcatalog.management.commands.import_app_install_data.'
504- 'Cache.create_apt_cache')
505+ 'CacheMaker.create_apt_cache')
506 with patch(create_apt_cache_fn) as mock_create_apt_cache:
507 call_command(
508 'import_app_install_data', 'testwebcatseries',
509- local_app_install_deb=self.factory.get_test_path(
510- 'app-install-data-test_all.deb'),
511+ local_app_install_data=self.deb_location,
512+ local_app_install_data_partner=self.deb_location,
513 verbosity=0)
514
515 self.assertEqual(0, mock_create_apt_cache.call_count)
516@@ -385,8 +396,7 @@
517 self.assertEqual(0, DistroSeries.objects.filter(
518 code_name='natty').count())
519
520- with CacheContextManager('natty') as cache:
521- ImportAppInstallCommand().update_from_cache(cache, 'natty')
522+ ImportAppInstallCommand().update_from_cache('natty')
523
524 self.assertEqual(1, DistroSeries.objects.filter(
525 code_name='natty').count())
526@@ -394,8 +404,7 @@
527 def test_apt_cache_apps_used_also(self):
528 self.assertEqual(0, Application.objects.count())
529
530- with CacheContextManager('natty') as cache:
531- ImportAppInstallCommand().update_from_cache(cache, 'natty')
532+ ImportAppInstallCommand().update_from_cache('natty')
533
534 # There are 4 packages in our mock_cache which is patched in the
535 # setUp.
536@@ -415,8 +424,8 @@
537
538 call_command(
539 'import_app_install_data', 'precise',
540- local_app_install_deb=self.factory.get_test_path(
541- 'app-install-data-test_all.deb'),
542+ local_app_install_data=self.deb_location,
543+ local_app_install_data_partner=self.deb_location,
544 verbosity=0)
545
546 firefox = Application.objects.get(package_name='firefox')

Subscribers

People subscribed via source and target branches