Merge lp:~michael.nelson/ubuntu-webcatalog/store-apps-per-distroseries into lp:ubuntu-webcatalog

Proposed by Michael Nelson
Status: Merged
Approved by: Danny Tamez
Approved revision: 19
Merged at revision: 13
Proposed branch: lp:~michael.nelson/ubuntu-webcatalog/store-apps-per-distroseries
Merge into: lp:ubuntu-webcatalog
Diff against target: 324 lines (+150/-13)
8 files modified
src/webcatalog/forms.py (+1/-0)
src/webcatalog/management/commands/import_app_install_data.py (+30/-5)
src/webcatalog/models.py (+15/-3)
src/webcatalog/static/css/webcatalog.css (+29/-0)
src/webcatalog/templates/webcatalog/application_detail.html (+3/-2)
src/webcatalog/tests/factory.py (+15/-2)
src/webcatalog/tests/test_commands.py (+47/-0)
src/webcatalog/tests/test_views.py (+10/-1)
To merge this branch: bzr merge lp:~michael.nelson/ubuntu-webcatalog/store-apps-per-distroseries
Reviewer Review Type Date Requested Status
Danny Tamez (community) Approve
Review via email: mp+60200@code.launchpad.net

Commit message

[r=zematynnad][bug=777869,777454] Store apps per distroseries, update when importing and include install button.

Description of the change

Overview
========

This branch ensures that apps are stored per distroseries (bug 777869), and that apps are updated if they already exist in the distroseries (rather than duplicated).

I also added a very simple apt url to the details page to look like this (bug 777454):
http://people.canonical.com/~michaeln/tmp/scribus_with_apturl.png

I'll create bugs for:
 1) more sensible apt-urls (only display if correct distroseries, otherwise display "Not on this distroseries but yours..", or "only for ubuntu" etc.
 2) Rendering of xpm icons for display on browser pages.

To test:
 $ fab bootstrap
 $ fab test

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

Merge trunk.

Revision history for this message
Danny Tamez (zematynnad) wrote :

Looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webcatalog/forms.py'
2--- src/webcatalog/forms.py 2011-04-13 09:28:55 +0000
3+++ src/webcatalog/forms.py 2011-05-06 15:06:19 +0000
4@@ -53,6 +53,7 @@
5
6 class Meta:
7 model = Application
8+ exclude = ('distroseries',)
9
10 @classmethod
11 def get_form_from_desktop_data(cls, str_data):
12
13=== modified file 'src/webcatalog/management/commands/import_app_install_data.py'
14--- src/webcatalog/management/commands/import_app_install_data.py 2011-04-15 09:18:10 +0000
15+++ src/webcatalog/management/commands/import_app_install_data.py 2011-05-06 15:06:19 +0000
16@@ -33,9 +33,14 @@
17 import apt
18 from apt_inst import DebFile
19 from django.core.files.images import ImageFile
20+from django.forms.models import construct_instance
21 from django.core.management.base import LabelCommand
22
23 from webcatalog.forms import ApplicationForm
24+from webcatalog.models import (
25+ Application,
26+ DistroSeries,
27+ )
28
29 __metaclass__ = type
30 __all__ = [
31@@ -55,13 +60,20 @@
32 )
33 verbosity = 0
34
35- def handle_label(self, distroseries, **options):
36+ def handle_label(self, distroseries_name, **options):
37 self.verbosity = int(options['verbosity'])
38 data_dir = tempfile.mkdtemp()
39+ distroseries, created = DistroSeries.objects.get_or_create(
40+ code_name=distroseries_name)
41+ if created and self.verbosity > 0:
42+ self.stdout.write(
43+ "Created a DistroSeries record with codename '{0}'.".format(
44+ distroseries_name))
45+
46 # Download app install data when requested.
47 if options['local_app_install_deb'] == '':
48 deb_location = self.get_latest_app_data_for_series(
49- distroseries, working_dir=data_dir)
50+ distroseries_name, working_dir=data_dir)
51 else:
52 deb_location = options['local_app_install_deb']
53
54@@ -74,7 +86,7 @@
55 matcher = data_dir + '/usr/share/app-install/desktop/*.desktop'
56 icon_dir = data_dir + '/usr/share/app-install/icons/'
57 for desktop_file in iglob(matcher):
58- self.process_desktop_file(desktop_file, icon_dir)
59+ self.process_desktop_file(desktop_file, icon_dir, distroseries)
60
61 shutil.rmtree(data_dir)
62
63@@ -121,13 +133,26 @@
64 shutil.rmtree(tmp_sources_dir)
65 return uri
66
67- def process_desktop_file(self, desktop_file_path, icon_dir):
68+ def process_desktop_file(self, desktop_file_path, icon_dir, distroseries):
69 with open(desktop_file_path, 'r') as desktop_file:
70 data = desktop_file.read()
71 form = ApplicationForm.get_form_from_desktop_data(data)
72
73 if form.is_valid():
74- app = form.save()
75+ # Check if the app already exists in our DB.
76+ existing_apps = Application.objects.filter(
77+ package_name=form.cleaned_data['package_name'],
78+ distroseries=distroseries)
79+ if existing_apps.count() > 0:
80+ # Force the form to update the existing application.
81+ # Simply setting the forms.instance doesn't work as the
82+ # form constructs its instance during is_valid, and
83+ # doesn't reconstruct it to save.
84+ app = construct_instance(form, existing_apps[0])
85+ else:
86+ app = form.save(commit=False)
87+ app.distroseries = distroseries
88+ app.save()
89 app.update_departments()
90 self.add_icon_to_app(app, icon_dir)
91 if self.verbosity > 0:
92
93=== modified file 'src/webcatalog/models.py'
94--- src/webcatalog/models.py 2011-04-19 18:46:21 +0000
95+++ src/webcatalog/models.py 2011-05-06 15:06:19 +0000
96@@ -32,15 +32,27 @@
97 __metaclass__ = type
98 __all__ = [
99 'Application',
100+ 'DistroSeries',
101 ]
102
103
104+class DistroSeries(models.Model):
105+ """An Ubuntu distribution series."""
106+ code_name = models.CharField(max_length=20, unique=True, db_index=True)
107+ version = models.CharField(max_length=10, blank=True)
108+
109+ def __unicode__(self):
110+ return "Ubuntu %s (%s)" % (self.code_name.capitalize(), self.version)
111+
112+
113 class Application(models.Model):
114 # We'll most likely need one of these per lang/series (as each lang
115 # will potentially have a different name) and names/descriptions can
116- # change for a package for different series. Regarding the language,
117- # if we have the gettext data available, we might be able to do this
118- # at runtime instead.
119+ # change for a package for different series.
120+ # We may need to have one application item per language, depending
121+ # whether we have geettext data available that we could use at
122+ # runtime (much preferred)
123+ distroseries = models.ForeignKey(DistroSeries)
124
125 # The following fields are extracted from app-install-data.
126 package_name = models.CharField(max_length=100)
127
128=== modified file 'src/webcatalog/static/css/webcatalog.css'
129--- src/webcatalog/static/css/webcatalog.css 2011-04-20 19:41:44 +0000
130+++ src/webcatalog/static/css/webcatalog.css 2011-05-06 15:06:19 +0000
131@@ -108,6 +108,35 @@
132 width: 300px;
133 }
134
135+.awesome, .awesome:visited {
136+ background-color: #222222;
137+ border-bottom: 1px solid rgba(0, 0, 0, 0.25);
138+ border-radius: 5px 5px 5px 5px;
139+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
140+ cursor: pointer;
141+ display: inline-block;
142+ font-size: 20px;
143+ font-weight: bold;
144+ line-height: 1;
145+ margin: 5px;
146+ padding: 5px;
147+ position: relative;
148+ text-decoration: none;
149+ text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
150+}
151+.awesome:hover {
152+ background-color: #D45500;
153+ color: #FFFFFF;
154+ text-decoration: none;
155+}
156+.awesome:active {
157+ top: 1px;
158+}
159+.awesome, .awesome:visited {
160+ background-color: #FF5C00;
161+ color: #FFFFFF;
162+}
163+
164 .dept-icon {
165 float:left;
166 width:56px;
167
168=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
169--- src/webcatalog/templates/webcatalog/application_detail.html 2011-04-08 12:25:25 +0000
170+++ src/webcatalog/templates/webcatalog/application_detail.html 2011-05-06 15:06:19 +0000
171@@ -9,7 +9,7 @@
172 {% if application.icon %}
173 <img src="{{ application.icon.url }}"/>
174 {% else %}
175- <img src="{{ STATIC_URL }}images/noicon_32.png"/>
176+ <img src="{{ STATIC_URL }}images/noicon_32.png"/>
177 {% endif %}
178
179 <h2>{{ application.name }}</h2>
180@@ -23,7 +23,8 @@
181 {% endcomment %}
182 <img src="http://screenshots.ubuntu.com/thumbnail-with-version/{{ application.package_name }}/ignored" />
183 </div>
184- {{ application.description }}
185+ <p>{{ application.description }}</p>
186+<a href="apt://{{ application.package_name }}" class="awesome">Download {{ application.name }}</a>
187 </div>
188 <div class="license">
189 <table>
190
191=== modified file 'src/webcatalog/tests/factory.py'
192--- src/webcatalog/tests/factory.py 2011-04-14 00:19:29 +0000
193+++ src/webcatalog/tests/factory.py 2011-05-06 15:06:19 +0000
194@@ -30,6 +30,7 @@
195 from webcatalog.models import (
196 Application,
197 Department,
198+ DistroSeries,
199 )
200
201 __metaclass__ = type
202@@ -74,7 +75,8 @@
203 user.save()
204
205 def make_application(self, package_name=None, name=None,
206- comment=None, description=None, icon_name='', icon=None):
207+ comment=None, description=None, icon_name='', icon=None,
208+ distroseries=None):
209 if name is None:
210 name = self.get_unique_string(prefix='Readable Name')
211 if package_name is None:
212@@ -83,15 +85,26 @@
213 comment = self.get_unique_string(prefix='comment')
214 if description is None:
215 description = self.get_unique_string(prefix='description')
216+ if distroseries is None:
217+ distroseries = self.make_distroseries()
218
219 return Application.objects.create(
220 package_name=package_name, name=name, comment=comment,
221 description=description, popcon=999, icon=icon,
222- icon_name=icon_name)
223+ icon_name=icon_name, distroseries=distroseries)
224
225 def make_department(self, name, parent=None):
226 return Department.objects.create(name=name, parent=parent)
227
228+ def make_distroseries(self, code_name=None, version=None):
229+ if code_name is None:
230+ code_name = self.get_unique_string(prefix='series-')
231+ if version is None:
232+ version = "1.{0}".format(self.get_unique_integer)
233+
234+ return DistroSeries.objects.create(
235+ code_name=code_name, version=version)
236+
237 def get_test_path(self, file_name):
238 return os.path.join(
239 os.path.dirname(__file__), 'test_data', file_name)
240
241=== modified file 'src/webcatalog/tests/test_commands.py'
242--- src/webcatalog/tests/test_commands.py 2011-04-15 09:14:36 +0000
243+++ src/webcatalog/tests/test_commands.py 2011-05-06 15:06:19 +0000
244@@ -97,6 +97,53 @@
245 'Graphic Page Layout and Publication (Stable)',
246 scribus.comment)
247
248+ def test_applications_updated(self):
249+ # If applications already exist for the same series, it is
250+ # updated.
251+ app = self.factory.make_application(package_name='scribus',
252+ comment='old comment',
253+ distroseries=self.factory.make_distroseries(code_name='natty'))
254+
255+ call_command(
256+ 'import_app_install_data', 'natty',
257+ local_app_install_deb=self.factory.get_test_path(
258+ 'app-install-data-test_all.deb'),
259+ verbosity=0)
260+
261+ app_reloaded = Application.objects.get(id=app.id)
262+ self.assertEqual(
263+ 'Graphic Page Layout and Publication (Stable)',
264+ app_reloaded.comment)
265+
266+ def test_same_app_different_distroseries(self):
267+ # Importing the same app (package_name) as an existing app, but
268+ # for a different distroseries creates a new record.
269+ app = self.factory.make_application(package_name='scribus',
270+ comment='old comment',
271+ distroseries=self.factory.make_distroseries(code_name='natty'))
272+
273+ call_command(
274+ 'import_app_install_data', 'oneric',
275+ local_app_install_deb=self.factory.get_test_path(
276+ 'app-install-data-test_all.deb'),
277+ verbosity=0)
278+
279+ app_reloaded = Application.objects.get(id=app.id)
280+ self.assertEqual('old comment', app_reloaded.comment)
281+ self.assertEqual(
282+ 2, Application.objects.filter(package_name='scribus').count())
283+
284+ def test_distroseries_added_to_app(self):
285+ call_command(
286+ 'import_app_install_data', 'natty',
287+ local_app_install_deb=self.factory.get_test_path(
288+ 'app-install-data-test_all.deb'),
289+ verbosity=0)
290+
291+ self.assertEqual(2, Application.objects.count())
292+ scribus = Application.objects.get(package_name='scribus')
293+ self.assertEqual('natty', scribus.distroseries.code_name)
294+
295 def test_icon_added_to_model(self):
296 call_command(
297 'import_app_install_data', 'natty',
298
299=== modified file 'src/webcatalog/tests/test_views.py'
300--- src/webcatalog/tests/test_views.py 2011-04-13 15:48:31 +0000
301+++ src/webcatalog/tests/test_views.py 2011-05-06 15:06:19 +0000
302@@ -68,6 +68,15 @@
303 response, '<img src="http://screenshots.ubuntu.com/'
304 'thumbnail-with-version/pkgfoo/ignored"')
305
306+ def test_link_to_apt_package(self):
307+ self.factory.make_application(package_name='pkgfoo')
308+ url = reverse('wc-package-detail', args=['pkgfoo'])
309+
310+ response = self.client.get(url)
311+
312+ self.assertContains( response, '<a href="apt://pkgfoo"')
313+
314+
315 class SearchTestCase(TestCaseWithFactory):
316 def test_no_search_retrieves_no_apps(self):
317 # Ensure there's some data in the DB
318@@ -170,4 +179,4 @@
319
320 response = self.client.get(reverse('wc-department', args=[dept.id]))
321
322- self.assertNotContains(response, 'Subsections')
323\ No newline at end of file
324+ self.assertNotContains(response, 'Subsections')

Subscribers

People subscribed via source and target branches