Merge lp:~michael.nelson/ubuntu-webcatalog/form-for-import-data into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Merged at revision: 6
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/form-for-import-data
Merge into: lp:ubuntu-webcatalog
Diff against target: 332 lines (+212/-32)
6 files modified
src/webcatalog/forms.py (+66/-0)
src/webcatalog/management/commands/import_app_install_data.py (+15/-17)
src/webcatalog/models.py (+14/-13)
src/webcatalog/tests/__init__.py (+2/-1)
src/webcatalog/tests/factory.py (+1/-1)
src/webcatalog/tests/test_forms.py (+114/-0)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/form-for-import-data
Reviewer Review Type Date Requested Status
Anthony Lenton (community) Approve
Review via email: mp+57342@code.launchpad.net

Description of the change

Overview
========

This branch adds the remaining fields which the USC client parses from the desktop files.

Most of these fields are parsed from the desktop files in app-install-data-ubuntu so that they can override the package values where appropriate.

I've also added simplified the importer by included a form for data validation, and ensured the form class can create instances based on the desktop file.

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

MimeType is not required, more helpful error when certain desktop files are ignored.

11. By Michael Nelson

Fixed the --local-app-install-deb option.

12. By Michael Nelson

Unicode strings when writing to stdout..

Revision history for this message
Anthony Lenton (elachuni) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'src/webcatalog/forms.py'
--- src/webcatalog/forms.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/forms.py 2011-04-12 15:56:34 +0000
@@ -0,0 +1,66 @@
1# -*- coding: utf-8 -*-
2# This file is part of the Ubuntu Web Catalog
3# Copyright (C) 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as
7# published by the Free Software Foundation, either version 3 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Forms used by the web catalog."""
19
20from __future__ import (
21 absolute_import,
22 with_statement,
23 )
24
25from ConfigParser import ConfigParser
26from StringIO import StringIO
27
28from django import forms
29
30from webcatalog.models import Application
31
32__metaclass__ = type
33__all__ = [
34 'ApplicationForm',
35 'desktop_field_mappings',
36 ]
37
38desktop_field_mappings = {
39 'x-appinstall-package': 'package_name',
40 'x-appinstall-popcon': 'popcon',
41 'x-appinstall-channel': 'channel',
42 'x-appinstall-screenshot-url': 'screenshot_url',
43 'x-appinstall-architectures': 'architectures',
44 'x-appinstall-keywords': 'keywords',
45 'x-appinstall-description': 'description',
46 'x-appinstall-section': 'section',
47 'type': 'app_type',
48 }
49
50
51class ApplicationForm(forms.ModelForm):
52
53 class Meta:
54 model = Application
55
56 @classmethod
57 def get_form_from_desktop_data(cls, str_data):
58 parser = ConfigParser()
59 parser.readfp(StringIO(str_data))
60 data = dict(parser.items('Desktop Entry'))
61 for desktop_key, app_key in desktop_field_mappings.items():
62 if data.has_key(desktop_key):
63 data[app_key] = data[desktop_key]
64 del(data[desktop_key])
65
66 return cls(data=data)
067
=== modified file 'src/webcatalog/management/commands/import_app_install_data.py'
--- src/webcatalog/management/commands/import_app_install_data.py 2011-04-11 14:08:41 +0000
+++ src/webcatalog/management/commands/import_app_install_data.py 2011-04-12 15:56:34 +0000
@@ -25,13 +25,11 @@
25import tempfile25import tempfile
26import urllib26import urllib
27from apt_inst import DebFile27from apt_inst import DebFile
28from ConfigParser import ConfigParser
29from optparse import make_option28from optparse import make_option
30from StringIO import StringIO
3129
32from django.core.management.base import BaseCommand30from django.core.management.base import BaseCommand
3331
34from webcatalog.models import Application32from webcatalog.forms import ApplicationForm
3533
36__metaclass__ = type34__metaclass__ = type
37__all__ = [35__all__ = [
@@ -43,7 +41,7 @@
43 help = "Update Application data from app-install-data package."41 help = "Update Application data from app-install-data package."
44 option_list = BaseCommand.option_list + (42 option_list = BaseCommand.option_list + (
45 make_option('--local-app-install-deb',43 make_option('--local-app-install-deb',
46 action='store_true',44 action='store',
47 dest='local_app_install_deb',45 dest='local_app_install_deb',
48 default='',46 default='',
49 help=('Use a local app-install-data deb package rather than '47 help=('Use a local app-install-data deb package rather than '
@@ -52,7 +50,6 @@
5250
53 def handle(self, *args, **options):51 def handle(self, *args, **options):
54 self.verbosity = int(options['verbosity'])52 self.verbosity = int(options['verbosity'])
55
56 # Download app install data when requested.53 # Download app install data when requested.
57 if options['local_app_install_deb'] == '':54 if options['local_app_install_deb'] == '':
58 install_data_url = ("http://archive.ubuntu.com/"55 install_data_url = ("http://archive.ubuntu.com/"
@@ -60,7 +57,7 @@
60 "app-install-data_0.11.04.7.1_all.deb")57 "app-install-data_0.11.04.7.1_all.deb")
61 if self.verbosity > 0:58 if self.verbosity > 0:
62 self.stdout.write(59 self.stdout.write(
63 "Downloading app-install-data-ubuntu from {0}...".format(60 u"Downloading app-install-data-ubuntu from {0}...".format(
64 install_data_url))61 install_data_url))
65 tmp_file = tempfile.NamedTemporaryFile()62 tmp_file = tempfile.NamedTemporaryFile()
66 urllib.urlretrieve(install_data_url, tmp_file.name)63 urllib.urlretrieve(install_data_url, tmp_file.name)
@@ -85,14 +82,15 @@
8582
86 def process_archive_member(self, member, data):83 def process_archive_member(self, member, data):
87 if member.name.endswith('desktop'):84 if member.name.endswith('desktop'):
88 parser = ConfigParser()85 form = ApplicationForm.get_form_from_desktop_data(data)
89 parser.readfp(StringIO(data))86
90 data = dict(parser.items('Desktop Entry'))87 if form.is_valid():
91 # TODO: Add a form and validate.88 app = form.save()
92 Application.objects.create(89 if self.verbosity > 0:
93 package_name=data['x-appinstall-package'],90 self.stdout.write(
94 name=data['name'],91 u"{0} created.\n".format(app.name))
95 comment=data.get('comment', ''))92 else:
96 if self.verbosity > 0:93 if self.verbosity > 0:
97 self.stdout.write(94 self.stdout.write(
98 "{0} created.\n".format(data['x-appinstall-package']))95 u"Skipping {0} as input failed validation: {1}.\n".format(
96 member.name, form.errors))
9997
=== modified file 'src/webcatalog/models.py'
--- src/webcatalog/models.py 2011-04-08 12:25:25 +0000
+++ src/webcatalog/models.py 2011-04-12 15:56:34 +0000
@@ -39,27 +39,28 @@
39 # The following fields are extracted from app-install-data.39 # The following fields are extracted from app-install-data.
40 package_name = models.SlugField(max_length=100)40 package_name = models.SlugField(max_length=100)
41 name = models.CharField(max_length=255)41 name = models.CharField(max_length=255)
42 comment = models.CharField(max_length=255)42 comment = models.CharField(max_length=255, blank=True)
43 popcon = models.IntegerField()
44 channel = models.CharField(max_length=255, blank=True)
45 screenshot_url = models.URLField(blank=True,
46 help_text="Only use this if it is other than the normal screenshot url.")
47 mimetype = models.CharField(max_length=255, blank=True)
48 architectures = models.CharField(max_length=255, blank=True)
49 keywords = models.CharField(max_length=255, blank=True)
50 app_type = models.CharField(max_length=32)
51 section = models.CharField(max_length=32)
52 categories = models.CharField(max_length=255)
53
43 # Other desktop fields used by s-c54 # Other desktop fields used by s-c
55 gnome_full_name = models.CharField(max_length=255, blank=True)
44 # x-gnome-fullname56 # x-gnome-fullname
45 # X-AppInstall-Ignore57 # X-AppInstall-Ignore
46 # X-AppInstall-Package
47 # X-AppInstall-Section
48 # X-AppInstall-Channel
49 # X-AppInstall-Screenshot-Url
50 # Icon58 # Icon
51 # Type
52 # X-AppInstall-Architectures
53 # X-AppInstall-Description
54 # X-AppInstall-Popcon
55 # Comment
56 # X-AppInstall-Keywords
57 # mime and categories too
5859
59 # The following fields will need to be imported from the apt-cache60 # The following fields will need to be imported from the apt-cache
60 # (using python-apt - as above we'll need access to info from different61 # (using python-apt - as above we'll need access to info from different
61 # series etc.)62 # series etc.)
62 description = models.TextField()63 description = models.TextField(blank=True)
6364
64 def __unicode__(self):65 def __unicode__(self):
65 return u"{0} ({1})".format(self.name, self.package_name)66 return u"{0} ({1})".format(self.name, self.package_name)
6667
=== modified file 'src/webcatalog/tests/__init__.py'
--- src/webcatalog/tests/__init__.py 2011-04-08 15:48:54 +0000
+++ src/webcatalog/tests/__init__.py 2011-04-12 15:56:34 +0000
@@ -16,5 +16,6 @@
16# along with this program. If not, see <http://www.gnu.org/licenses/>.16# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
18"""Import various view, model and other tests for django's default runner."""18"""Import various view, model and other tests for django's default runner."""
19from .test_forms import *
20from .test_commands import *
19from .test_views import *21from .test_views import *
20from .test_commands import *
2122
=== modified file 'src/webcatalog/tests/factory.py'
--- src/webcatalog/tests/factory.py 2011-04-11 13:51:17 +0000
+++ src/webcatalog/tests/factory.py 2011-04-12 15:56:34 +0000
@@ -82,7 +82,7 @@
8282
83 return Application.objects.create(83 return Application.objects.create(
84 package_name=package_name, name=name, comment=comment,84 package_name=package_name, name=name, comment=comment,
85 description=description)85 description=description, popcon=999)
8686
87 def get_test_path(self, file_name):87 def get_test_path(self, file_name):
88 return os.path.join(88 return os.path.join(
8989
=== added file 'src/webcatalog/tests/test_forms.py'
--- src/webcatalog/tests/test_forms.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/tests/test_forms.py 2011-04-12 15:56:34 +0000
@@ -0,0 +1,114 @@
1# -*- coding: utf-8 -*-
2# This file is part of the Ubuntu Web Catalog
3# Copyright (C) 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as
7# published by the Free Software Foundation, either version 3 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Test cases for the web catalog forms."""
19
20from __future__ import (
21 absolute_import,
22 with_statement,
23 )
24
25from textwrap import dedent
26
27from webcatalog.forms import (
28 ApplicationForm,
29 desktop_field_mappings,
30 )
31from webcatalog.models import Application
32from webcatalog.tests.factory import TestCaseWithFactory
33
34__metaclass__ = type
35__all__ = [
36 'ApplicationFormTestCase',
37 ]
38
39
40class ApplicationFormTestCase(TestCaseWithFactory):
41
42 def get_desktop_data(self, overrides):
43 data = {
44 'Name': self.factory.get_unique_string(
45 prefix='App Name'),
46 'X-AppInstall-Package': self.factory.get_unique_string(
47 prefix='pkg_name'),
48 'X-AppInstall-Popcon': self.factory.get_unique_integer(),
49 'X-AppInstall-Section': self.factory.get_unique_string(
50 prefix='section'),
51 'Type': 'Application',
52 'Categories': 'cat1;cat2',
53 }
54 data.update(overrides)
55 return data
56
57 def get_desktop_entry(self, data):
58 desktop_entry_template = dedent("""
59 [Desktop Entry]
60 GenericName=Page Layout (Stable)
61 Terminal=false
62 Icon=scribus
63 X-StandardInstall=false
64 StartupWMClass=scribus
65 X-KDE-SubstituteUID=false
66 X-KDE-Username=
67
68 X-Ubuntu-Gettext-Domain=app-install-data""")
69 for key, value in data.items():
70 desktop_entry_template += "\n{key}={value}".format(
71 key=key, value=value)
72
73 desktop_data = desktop_entry_template.format(**data)
74
75 return desktop_data
76
77 def test_get_form_from_desktop_data(self):
78 data = {
79 'Name': 'My Package',
80 'X-AppInstall-Package': 'mypkg',
81 }
82 desktop_entry = self.get_desktop_entry(self.get_desktop_data(data))
83
84 form = ApplicationForm.get_form_from_desktop_data(desktop_entry)
85
86 self.assertTrue(form.is_valid())
87 self.assertEqual('My Package', form.cleaned_data['name'])
88 self.assertEqual('mypkg', form.cleaned_data['package_name'])
89
90 def test_extra_fields(self):
91 # There are a bunch of extra fields which are also supported by
92 # the model form.
93 extra_desktop_info = {
94 'X-AppInstall-Channel': 'channel-name',
95 'X-AppInstall-Section': 'section-name',
96 'X-AppInstall-Screenshot-Url': 'http://example.com/screenshot',
97 'Type': 'Application',
98 'X-AppInstall-Description': 'A description',
99 'X-AppInstall-Architectures': 'i386r;amd64',
100 'X-AppInstall-Popcon': 9876,
101 'X-AppInstall-Keywords': 'jazz,rock,country',
102 'Categories': 'Graphics;Jazz;Publishing',
103 'MimeType': 'application/vnd.scribus;text/javascript;'
104 }
105 desktop_entry = self.get_desktop_entry(self.get_desktop_data(
106 extra_desktop_info))
107
108 form = ApplicationForm.get_form_from_desktop_data(desktop_entry)
109
110 self.assertTrue(form.is_valid())
111 for key, value in extra_desktop_info.items():
112 form_key = desktop_field_mappings.get(key.lower(), key.lower())
113 self.assertEqual(
114 extra_desktop_info[key], form.cleaned_data[form_key])

Subscribers

People subscribed via source and target branches