Merge lp:~elachuni/ubuntu-webcatalog/xpmreader into lp:ubuntu-webcatalog
- xpmreader
- Merge into trunk
Proposed by
Anthony Lenton
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Anthony Lenton | ||||
Approved revision: | 39 | ||||
Merged at revision: | 31 | ||||
Proposed branch: | lp:~elachuni/ubuntu-webcatalog/xpmreader | ||||
Merge into: | lp:ubuntu-webcatalog | ||||
Diff against target: |
539 lines (+304/-52) 11 files modified
fabtasks/bootstrap.py (+39/-0) src/webcatalog/management/commands/import_app_install_data.py (+54/-46) src/webcatalog/tests/__init__.py (+1/-0) src/webcatalog/tests/test_commands.py (+2/-6) src/webcatalog/tests/test_data/access.svg (+127/-0) src/webcatalog/tests/test_data/junk.svg (+4/-0) src/webcatalog/tests/test_data/junk.xpm (+4/-0) src/webcatalog/tests/test_data/mini-gsmc.xpm (+23/-0) src/webcatalog/tests/test_utilities.py (+35/-0) src/webcatalog/utilities.py (+11/-0) src/webcatalog/views.py (+4/-0) |
||||
To merge this branch: | bzr merge lp:~elachuni/ubuntu-webcatalog/xpmreader | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Canonical ISD hackers | Pending | ||
Review via email: mp+66510@code.launchpad.net |
Commit message
Use gtk to render xpm and svg icons
Description of the change
Overview
========
This branch adds an xpmreader utility that will convert xpm icons to png format for using within the webcatalog.
Details
=======
PIL only supports reading xpms with less than 256 colors, so a small utility was added instead to parse the relatively simple xpm file format.
Tests included.
To test
=======
Follow the instructions in the readme, but basically just run: fab bootstrap test
To post a comment you must log in.
Revision history for this message
Anthony Lenton (elachuni) wrote : | # |
Revision history for this message
ISD Branch Mangler (isd-branches-mangler) wrote : | # |
There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'fabtasks/bootstrap.py' |
2 | --- fabtasks/bootstrap.py 2011-06-18 23:17:11 +0000 |
3 | +++ fabtasks/bootstrap.py 2011-07-01 15:38:25 +0000 |
4 | @@ -31,6 +31,7 @@ |
5 | local("virtualenv/bin/pip install -r requirements.txt", capture=False) |
6 | local("virtualenv/bin/python setup.py develop", capture=False) |
7 | symlink_python_apt() |
8 | + symlink_gtk2() |
9 | |
10 | |
11 | def symlink_python_apt(): |
12 | @@ -48,6 +49,44 @@ |
13 | virtualenv_libdir + 'site-packages/apt_inst.so') |
14 | |
15 | |
16 | +def symlink_gtk2(): |
17 | + minor = sys.version_info[1] |
18 | + |
19 | + def _symlink_libs(filenames): |
20 | + for filename in filenames: |
21 | + basename = os.path.basename(filename) |
22 | + _symlink(filename, 'virtualenv/lib/python2.%s/site-packages/%s' % |
23 | + (minor, basename)) |
24 | + |
25 | + _symlink_libs([ |
26 | + '/usr/lib/pymodules/python2.%s/gtk-2.0' % minor, |
27 | + '/usr/lib/pymodules/python2.%s/cairo' % minor, |
28 | + ]) |
29 | + pth_file_prenatty = '/usr/lib/pymodules/python2.%s/pygtk.pth' % minor |
30 | + pth_file_natty = '/usr/share/pyshared/pygtk.pth' |
31 | + if os.path.exists(pth_file_prenatty): |
32 | + _symlink_libs([ |
33 | + pth_file_prenatty, |
34 | + '/usr/lib/pymodules/python2.%s/pygtk.py' % minor, |
35 | + ]) |
36 | + elif os.path.exists(pth_file_natty): |
37 | + _symlink_libs([ |
38 | + pth_file_natty, |
39 | + '/usr/lib/pymodules/python2.%s/cairo' % minor, |
40 | + '/usr/lib/python2.%s/dist-packages/glib' % minor, |
41 | + '/usr/lib/python2.%s/dist-packages/gobject' % minor, |
42 | + '/usr/lib/python2.%s/dist-packages/gtk-2.0' % minor, |
43 | + '/usr/lib/python2.%s/dist-packages/gtk-2.0-pysupport-compat.pth' |
44 | + % minor, |
45 | + '/usr/share/pyshared/pygtk.py', |
46 | + '/usr/lib/python2.%s/dist-packages/gtk-2.0/gio' % minor, |
47 | + '/usr/lib/python2.%s/dist-packages/gtk-2.0/LaunchpadIntegration' |
48 | + % minor, |
49 | + ]) |
50 | + else: |
51 | + raise EnvironmentError("Unable to find system-wide gtk2 installation") |
52 | + |
53 | + |
54 | def _symlink(source, link_name): |
55 | if not os.path.exists(link_name): |
56 | os.symlink(os.path.abspath(source), link_name) |
57 | |
58 | === modified file 'src/webcatalog/management/commands/import_app_install_data.py' |
59 | --- src/webcatalog/management/commands/import_app_install_data.py 2011-06-24 07:54:02 +0000 |
60 | +++ src/webcatalog/management/commands/import_app_install_data.py 2011-07-01 15:38:25 +0000 |
61 | @@ -42,6 +42,7 @@ |
62 | Application, |
63 | DistroSeries, |
64 | ) |
65 | +from webcatalog.utilities import create_png_from_file |
66 | |
67 | __metaclass__ = type |
68 | __all__ = [ |
69 | @@ -110,20 +111,19 @@ |
70 | help=('Use a local app-install-data deb package rather than ' |
71 | 'downloading from the archive.')), |
72 | ) |
73 | - verbosity = 0 |
74 | + verbosity = 1 |
75 | |
76 | def handle_label(self, distroseries_name, **options): |
77 | + self.verbosity = int(options['verbosity']) |
78 | + data_dir = tempfile.mkdtemp() |
79 | + # First create the apps based on the current cache. |
80 | + distroseries, created = DistroSeries.objects.get_or_create( |
81 | + code_name=distroseries_name) |
82 | + if created: |
83 | + self.output( |
84 | + "Created a DistroSeries record called '{0}'.\n".format( |
85 | + distroseries_name), 1) |
86 | with Cache(distroseries_name) as cache: |
87 | - self.verbosity = int(options['verbosity']) |
88 | - data_dir = tempfile.mkdtemp() |
89 | - |
90 | - # First create the apps based on the current cache. |
91 | - distroseries, created = DistroSeries.objects.get_or_create( |
92 | - code_name=distroseries_name) |
93 | - if created and self.verbosity > 0: |
94 | - self.stdout.write( |
95 | - "Created a DistroSeries record with codename '{0}'.".format( |
96 | - distroseries_name)) |
97 | self.update_from_cache(cache, distroseries_name) |
98 | |
99 | # Download app install data when requested. |
100 | @@ -135,40 +135,38 @@ |
101 | |
102 | # Extract and parse the deb archive. |
103 | deb_file = DebFile(deb_location) |
104 | - if self.verbosity > 0: |
105 | - self.stdout.write( |
106 | - "Processing application data to update database...\n") |
107 | + self.output("Processing application data to update database...\n", 1) |
108 | deb_file.data.extractall(data_dir) |
109 | matcher = data_dir + '/usr/share/app-install/desktop/*.desktop' |
110 | icon_dir = data_dir + '/usr/share/app-install/icons/' |
111 | |
112 | for desktop_file in iglob(matcher): |
113 | - self.process_desktop_file(desktop_file, icon_dir, distroseries, |
114 | - cache) |
115 | + if self.verbosity > 1: |
116 | + self.output("Processing {0}\n".format(desktop_file)) |
117 | + elif self.verbosity > 0: |
118 | + self.output(".", flush=True) |
119 | + self.process_desktop_file(desktop_file, icon_dir, distroseries) |
120 | |
121 | shutil.rmtree(data_dir) |
122 | |
123 | - def get_latest_app_data_for_series(self, distroseries, working_dir, cache=None): |
124 | + def get_latest_app_data_for_series(self, distroseries, working_dir, |
125 | + cache=None): |
126 | # Write a temporary sources.list, update the cache and access: |
127 | # cache['app-install-data-ubuntu'].candidate.uri |
128 | - install_data_url = self.get_app_data_uri_for_series(distroseries, cache) |
129 | + install_data_url = self.get_app_data_uri_for_series(distroseries, |
130 | + cache) |
131 | rest, sep, deb_filename = install_data_url.rpartition('/') |
132 | |
133 | - if self.verbosity > 0: |
134 | - self.stdout.write( |
135 | - u"Downloading app-install-data-ubuntu from {0}...".format( |
136 | - install_data_url)) |
137 | + self.output(u"Downloading app-install-data from {0}...\n".format( |
138 | + install_data_url), 1) |
139 | deb_location = os.path.join(working_dir, deb_filename) |
140 | urllib.urlretrieve(install_data_url, deb_location) |
141 | - if self.verbosity > 0: |
142 | - self.stdout.write("Done.\n") |
143 | + self.output("Done.\n", 1) |
144 | return deb_location |
145 | |
146 | def get_app_data_uri_for_series(self, distroseries, cache=None): |
147 | - if self.verbosity > 0: |
148 | - self.stdout.write( |
149 | - "Determining URI for current version of " |
150 | - "app-install-data in {0}.\n".format(distroseries)) |
151 | + self.output("Determining URI for current version of " |
152 | + "app-install-data in {0}.\n".format(distroseries), 1) |
153 | uri = None |
154 | if cache == None: |
155 | with Cache(distroseries) as cache: |
156 | @@ -177,8 +175,7 @@ |
157 | uri = cache['app-install-data'].candidate.uri |
158 | return uri |
159 | |
160 | - def process_desktop_file(self, desktop_file_path, icon_dir, distroseries, |
161 | - cache): |
162 | + def process_desktop_file(self, desktop_file_path, icon_dir, distroseries): |
163 | with open(desktop_file_path, 'r') as desktop_file: |
164 | data = desktop_file.read() |
165 | form = ApplicationForm.get_form_from_desktop_data(data, distroseries) |
166 | @@ -189,15 +186,12 @@ |
167 | app.save() |
168 | app.update_departments() |
169 | self.add_icon_to_app(app, icon_dir) |
170 | - if self.verbosity > 0: |
171 | - self.stdout.write( |
172 | - u"{0} created.\n".format(app.name).encode('utf-8')) |
173 | + self.output(u"{0} created.\n".format(app.name).encode('utf-8'), 1) |
174 | else: |
175 | - if self.verbosity > 0: |
176 | - self.stdout.write( |
177 | - u"Skipping {0} as input failed validation: {1}.\n".format( |
178 | - form.cleaned_data['package_name'], |
179 | - form.errors).encode('utf-8')) |
180 | + self.output( |
181 | + u"Skipping {0} as input failed validation: {1}.\n".format( |
182 | + form.cleaned_data['package_name'], |
183 | + form.errors).encode('utf-8'), 1) |
184 | |
185 | def add_icon_to_app(self, app, icon_dir): |
186 | # If the desktop file specifies an icon, we check for it in the |
187 | @@ -223,27 +217,32 @@ |
188 | break |
189 | |
190 | if icon_path: |
191 | + if icon_path.endswith('.xpm') or icon_path.endswith('.svg'): |
192 | + target_path = icon_path[:-4] + '.png' |
193 | + if create_png_from_file(target_path, icon_path, 32, 32): |
194 | + icon_path = target_path |
195 | with open(icon_path, "r") as icon_file: |
196 | app.icon = ImageFile(icon_file) |
197 | app.save() |
198 | else: |
199 | - if self.verbosity > 0: |
200 | - self.stdout.write( |
201 | - u"No icon found for {0} at {1}.({2}).\n".format( |
202 | + self.output(u"No icon found for {0} at {1}.({2}).\n".format( |
203 | app.name, icon_path_generic, |
204 | - '|'.join(supported_extensions)).encode('utf-8')) |
205 | + '|'.join(supported_extensions)).encode('utf-8'), 1) |
206 | |
207 | def update_from_cache(self, cache, distroseries_name): |
208 | distroseries, created = DistroSeries.objects.get_or_create( |
209 | code_name=distroseries_name) |
210 | - if created and self.verbosity > 0: |
211 | - self.stdout.write( |
212 | - "Created a DistroSeries record with codename '{0}'.".format( |
213 | - distroseries_name)) |
214 | + if created: |
215 | + self.output("Created a DistroSeries record called '{0}'.\n".format( |
216 | + distroseries_name), 1) |
217 | |
218 | # For each package in the apt-cache, we update (or possibly |
219 | # create) a matching db entry. |
220 | for package in cache: |
221 | + if self.verbosity > 1: |
222 | + self.output("Updating {0}\n".format(package.name)) |
223 | + elif self.verbosity > 0: |
224 | + self.output(".", flush=True) |
225 | candidate = package.candidate |
226 | # USC uses the first line of the summary as the |
227 | # application name and the rest as the comment.. |
228 | @@ -272,4 +271,13 @@ |
229 | name=app_name, |
230 | comment=app_comment, |
231 | ) |
232 | + if self.verbosity == 1: |
233 | + self.output("\n") |
234 | return distroseries |
235 | + |
236 | + def output(self, message, level=None, flush=False): |
237 | + if hasattr(self, 'stdout'): |
238 | + if level is None or self.verbosity >= level: |
239 | + self.stdout.write(message) |
240 | + if flush: |
241 | + self.stdout.flush() |
242 | |
243 | === modified file 'src/webcatalog/tests/__init__.py' |
244 | --- src/webcatalog/tests/__init__.py 2011-06-29 11:59:28 +0000 |
245 | +++ src/webcatalog/tests/__init__.py 2011-07-01 15:38:25 +0000 |
246 | @@ -21,4 +21,5 @@ |
247 | from .test_forms import * |
248 | from .test_models import * |
249 | from .test_templatetags import * |
250 | +from .test_utilities import * |
251 | from .test_views import * |
252 | |
253 | === modified file 'src/webcatalog/tests/test_commands.py' |
254 | --- src/webcatalog/tests/test_commands.py 2011-06-24 07:54:02 +0000 |
255 | +++ src/webcatalog/tests/test_commands.py 2011-07-01 15:38:25 +0000 |
256 | @@ -247,12 +247,8 @@ |
257 | self.assertEqual(4, Application.objects.count()) |
258 | scribus = Application.objects.get(package_name='scribus') |
259 | firefox = Application.objects.get(package_name='firefox') |
260 | - self.assertEqual( |
261 | - os.path.getsize(self.factory.get_test_path('firefox.xpm')), |
262 | - firefox.icon.size) |
263 | - self.assertEqual( |
264 | - os.path.getsize(self.factory.get_test_path('scribus.xpm')), |
265 | - scribus.icon.size) |
266 | + self.assertTrue(firefox.icon.size > 1) |
267 | + self.assertTrue(scribus.icon.size > 1) |
268 | |
269 | def test_get_app_data_uri_for_series(self): |
270 | uri = Command().get_app_data_uri_for_series('natty') |
271 | |
272 | === added file 'src/webcatalog/tests/test_data/access.svg' |
273 | --- src/webcatalog/tests/test_data/access.svg 1970-01-01 00:00:00 +0000 |
274 | +++ src/webcatalog/tests/test_data/access.svg 2011-07-01 15:38:25 +0000 |
275 | @@ -0,0 +1,127 @@ |
276 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
277 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
278 | +<svg |
279 | + xmlns:svg="http://www.w3.org/2000/svg" |
280 | + xmlns="http://www.w3.org/2000/svg" |
281 | + xmlns:xlink="http://www.w3.org/1999/xlink" |
282 | + version="1.0" |
283 | + width="32" |
284 | + height="32" |
285 | + id="svg2522"> |
286 | + <defs |
287 | + id="defs2524"> |
288 | + <linearGradient |
289 | + id="linearGradient8838"> |
290 | + <stop |
291 | + id="stop8840" |
292 | + style="stop-color:#000000;stop-opacity:1" |
293 | + offset="0" /> |
294 | + <stop |
295 | + id="stop8842" |
296 | + style="stop-color:#000000;stop-opacity:0" |
297 | + offset="1" /> |
298 | + </linearGradient> |
299 | + <radialGradient |
300 | + cx="62.625" |
301 | + cy="4.625" |
302 | + r="10.625" |
303 | + fx="62.625" |
304 | + fy="4.625" |
305 | + id="radialGradient2502" |
306 | + xlink:href="#linearGradient8838" |
307 | + gradientUnits="userSpaceOnUse" |
308 | + gradientTransform="matrix(1.5058824,0,0,0.37647,-78.305888,26.258824)" /> |
309 | + <linearGradient |
310 | + id="linearGradient2490-182-124"> |
311 | + <stop |
312 | + id="stop2788" |
313 | + style="stop-color:#1f4b6a;stop-opacity:1" |
314 | + offset="0" /> |
315 | + <stop |
316 | + id="stop2790" |
317 | + style="stop-color:#4083c2;stop-opacity:1" |
318 | + offset="1" /> |
319 | + </linearGradient> |
320 | + <linearGradient |
321 | + x1="18.379412" |
322 | + y1="44.980297" |
323 | + x2="18.379412" |
324 | + y2="3.0816143" |
325 | + id="linearGradient2499" |
326 | + xlink:href="#linearGradient2490-182-124" |
327 | + gradientUnits="userSpaceOnUse" |
328 | + gradientTransform="matrix(0.7126612,0,0,0.7126613,-1.1038706,-1.1038704)" /> |
329 | + <linearGradient |
330 | + id="linearGradient3242-187-536"> |
331 | + <stop |
332 | + id="stop2778" |
333 | + style="stop-color:#8badea;stop-opacity:1" |
334 | + offset="0" /> |
335 | + <stop |
336 | + id="stop2780" |
337 | + style="stop-color:#6396cd;stop-opacity:1" |
338 | + offset="0.26238" /> |
339 | + <stop |
340 | + id="stop2782" |
341 | + style="stop-color:#3b7caf;stop-opacity:1" |
342 | + offset="0.66093999" /> |
343 | + <stop |
344 | + id="stop2784" |
345 | + style="stop-color:#194c70;stop-opacity:1" |
346 | + offset="1" /> |
347 | + </linearGradient> |
348 | + <radialGradient |
349 | + cx="23.895569" |
350 | + cy="3.9900031" |
351 | + r="20.397499" |
352 | + fx="23.895569" |
353 | + fy="3.9900031" |
354 | + id="radialGradient2497" |
355 | + xlink:href="#linearGradient3242-187-536" |
356 | + gradientUnits="userSpaceOnUse" |
357 | + gradientTransform="matrix(0,1.7008781,-2.245129,0,24.958071,-40.236051)" /> |
358 | + <linearGradient |
359 | + id="linearGradient4873"> |
360 | + <stop |
361 | + id="stop4875" |
362 | + style="stop-color:#ffffff;stop-opacity:1" |
363 | + offset="0" /> |
364 | + <stop |
365 | + id="stop4877" |
366 | + style="stop-color:#ffffff;stop-opacity:0" |
367 | + offset="1" /> |
368 | + </linearGradient> |
369 | + <linearGradient |
370 | + x1="63.397362" |
371 | + y1="-12.489107" |
372 | + x2="63.397362" |
373 | + y2="5.4675598" |
374 | + id="linearGradient2494" |
375 | + xlink:href="#linearGradient4873" |
376 | + gradientUnits="userSpaceOnUse" |
377 | + gradientTransform="matrix(1.5436509,0,0,1.5436158,-80.015712,21.419381)" /> |
378 | + </defs> |
379 | + <g |
380 | + id="layer1"> |
381 | + <path |
382 | + d="M 31.999998,28 C 31.999998,30.209139 24.836553,32 15.999997,32 C 7.1634417,32 -3.2e-06,30.209139 -3.2e-06,28 C -3.2e-06,25.790861 7.1634417,24 15.999997,24 C 24.836553,24 31.999998,25.790861 31.999998,28 L 31.999998,28 z" |
383 | + id="path8836" |
384 | + style="opacity:0.3;fill:url(#radialGradient2502);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999988;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> |
385 | + <path |
386 | + d="M 16.000001,1.5017975 C 8.0003938,1.5017975 1.5017978,8.0003912 1.5017978,15.999999 C 1.5017978,23.999608 8.0003938,30.498205 16.000001,30.498202 C 23.999606,30.498202 30.49821,23.999608 30.498202,15.999999 C 30.498202,8.0003912 23.999606,1.5017975 16.000001,1.5017975 z" |
387 | + id="path2555" |
388 | + style="fill:url(#radialGradient2497);fill-opacity:1;stroke:url(#linearGradient2499);stroke-width:1.00365412;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> |
389 | + <path |
390 | + d="M 29.5,15.999522 C 29.5,23.455607 23.455399,29.5 16.000172,29.5 C 8.5442598,29.5 2.5,23.455539 2.5,15.999522 C 2.5,8.5437822 8.5442598,2.5000011 16.000172,2.5000011 C 23.455399,2.5000011 29.5,8.5437822 29.5,15.999522 L 29.5,15.999522 z" |
391 | + id="path8655" |
392 | + style="opacity:0.4;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2494);stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> |
393 | + <path |
394 | + d="M 18.299799,8.1756486 C 18.299799,9.3766078 17.316035,10.351299 16.103898,10.351299 C 14.891759,10.351299 13.907996,9.3766078 13.907996,8.1756486 C 13.907996,6.9746909 14.891759,5.9999997 16.103898,5.9999997 C 17.316035,5.9999997 18.299799,6.9746909 18.299799,8.1756486 z M 23.780465,10.489071 C 23.678901,10.505375 23.582932,10.545064 23.50068,10.604851 L 15.999771,11.763753 L 8.5392604,10.588868 C 8.3464065,10.450283 8.0752623,10.408784 7.8597702,10.511673 C 7.6442771,10.614562 7.5030077,10.858427 7.5000365,11.090481 C 7.4973158,11.322567 7.646957,11.522717 7.8597702,11.630709 L 13.705808,13.468134 L 13.665824,17.686241 L 11.602993,24.003606 C 11.404317,24.291273 11.464871,24.660718 11.762875,24.852499 C 12.060892,25.044308 12.483506,24.985866 12.682202,24.698167 L 16.011195,17.879188 L 19.277349,24.686382 C 19.453959,24.984755 19.847645,25.088403 20.156699,24.917911 C 20.465753,24.747422 20.573126,24.367363 20.396522,24.068988 L 18.371982,17.724445 L 18.292054,13.753383 L 24.22012,11.608108 C 24.459637,11.445958 24.563691,11.138982 24.459951,10.874963 C 24.356202,10.610915 24.071092,10.457077 23.780445,10.489071 L 23.780465,10.489071 z" |
395 | + id="path2520" |
396 | + style="fill:#1f4b6b;fill-opacity:1;stroke:none" /> |
397 | + <path |
398 | + d="M 18.299799,9.1756488 C 18.299799,10.376608 17.316035,11.351299 16.103898,11.351299 C 14.891759,11.351299 13.907996,10.376608 13.907996,9.1756488 C 13.907996,7.9746911 14.891759,6.9999999 16.103898,6.9999999 C 17.316035,6.9999999 18.299799,7.9746911 18.299799,9.1756488 z M 23.780465,11.489071 C 23.678901,11.505375 23.582932,11.545064 23.50068,11.604851 L 15.999771,12.763753 L 8.5392604,11.588868 C 8.3464065,11.450283 8.0752623,11.408784 7.8597702,11.511673 C 7.6442771,11.614562 7.5030077,11.858427 7.5000365,12.090481 C 7.4973158,12.322567 7.646957,12.522717 7.8597702,12.630709 L 13.705808,14.468134 L 13.665824,18.686241 L 11.602993,25.003606 C 11.404317,25.291273 11.464871,25.660718 11.762875,25.852499 C 12.060892,26.044308 12.483506,25.985866 12.682202,25.698167 L 16.011195,18.879188 L 19.277349,25.686382 C 19.453959,25.984755 19.847645,26.088403 20.156699,25.917911 C 20.465753,25.747422 20.573126,25.367363 20.396522,25.068988 L 18.371982,18.724445 L 18.292054,14.753383 L 24.22012,12.608108 C 24.459637,12.445958 24.563691,12.138982 24.459951,11.874963 C 24.356202,11.610915 24.071092,11.457077 23.780445,11.489071 L 23.780465,11.489071 z" |
399 | + id="path6668" |
400 | + style="fill:#ffffff;fill-opacity:1;stroke:none" /> |
401 | + </g> |
402 | +</svg> |
403 | |
404 | === added file 'src/webcatalog/tests/test_data/empty.xpm' |
405 | === added file 'src/webcatalog/tests/test_data/junk.svg' |
406 | --- src/webcatalog/tests/test_data/junk.svg 1970-01-01 00:00:00 +0000 |
407 | +++ src/webcatalog/tests/test_data/junk.svg 2011-07-01 15:38:25 +0000 |
408 | @@ -0,0 +1,4 @@ |
409 | +This is not an xpm file. { |
410 | +"", |
411 | +Really, it's not? |
412 | +} |
413 | \ No newline at end of file |
414 | |
415 | === added file 'src/webcatalog/tests/test_data/junk.xpm' |
416 | --- src/webcatalog/tests/test_data/junk.xpm 1970-01-01 00:00:00 +0000 |
417 | +++ src/webcatalog/tests/test_data/junk.xpm 2011-07-01 15:38:25 +0000 |
418 | @@ -0,0 +1,4 @@ |
419 | +This is not an xpm file. { |
420 | +"", |
421 | +Really, it's not? |
422 | +} |
423 | \ No newline at end of file |
424 | |
425 | === added file 'src/webcatalog/tests/test_data/mini-gsmc.xpm' |
426 | --- src/webcatalog/tests/test_data/mini-gsmc.xpm 1970-01-01 00:00:00 +0000 |
427 | +++ src/webcatalog/tests/test_data/mini-gsmc.xpm 2011-07-01 15:38:25 +0000 |
428 | @@ -0,0 +1,23 @@ |
429 | +/* XPM */ |
430 | +static char * mini-mix_xpm[] = { |
431 | +"16 16 4 1", |
432 | +" c None s None", |
433 | +". c black", |
434 | +"r c red", |
435 | +"y c green", |
436 | +" .... ", |
437 | +" .. r .. ", |
438 | +" . r . ", |
439 | +" . r . ", |
440 | +" . r ... . ", |
441 | +" . .ryyy.. ", |
442 | +". .yryyy..", |
443 | +". .yyyrryy.", |
444 | +".............rrr", |
445 | +". .yyyrryy.", |
446 | +" . .yryyy. ", |
447 | +" . ryyy.. ", |
448 | +" . r .... ", |
449 | +" . r . ", |
450 | +" .. r .. ", |
451 | +" .... "}; |
452 | |
453 | === added file 'src/webcatalog/tests/test_utilities.py' |
454 | --- src/webcatalog/tests/test_utilities.py 1970-01-01 00:00:00 +0000 |
455 | +++ src/webcatalog/tests/test_utilities.py 2011-07-01 15:38:25 +0000 |
456 | @@ -0,0 +1,35 @@ |
457 | +import os |
458 | + |
459 | +from webcatalog.tests.factory import TestCaseWithFactory |
460 | +from webcatalog.utilities import create_png_from_file |
461 | + |
462 | +class CreatePNGFromFileTestCase(TestCaseWithFactory): |
463 | + def test_file_conversion(self): |
464 | + filenames = [ |
465 | + self.factory.get_test_path('firefox.xpm'), # Regular xpm |
466 | + self.factory.get_test_path('mini-gsmc.xpm'), # named colors |
467 | + self.factory.get_test_path('access.svg'), # SVG file |
468 | + ] |
469 | + pngpath = 'test_file_DELETEME.png' |
470 | + for f in filenames: |
471 | + try: |
472 | + self.assertTrue(create_png_from_file(pngpath, f, 32, 32)) |
473 | + self.assertTrue(os.path.exists(pngpath)) |
474 | + finally: |
475 | + if os.path.exists(pngpath): |
476 | + os.unlink(pngpath) |
477 | + |
478 | + def test_invalid_files(self): |
479 | + filenames = [ |
480 | + self.factory.get_test_path('junk.xpm'), |
481 | + self.factory.get_test_path('empty.xpm'), |
482 | + self.factory.get_test_path('junk.svg'), |
483 | + ] |
484 | + pngpath = 'test_file_DELETEME.png' |
485 | + for f in filenames: |
486 | + try: |
487 | + self.assertFalse(create_png_from_file(pngpath, f, 32, 32)) |
488 | + self.assertFalse(os.path.exists(pngpath)) |
489 | + finally: |
490 | + if os.path.exists(pngpath): |
491 | + os.unlink(pngpath) |
492 | |
493 | === modified file 'src/webcatalog/utilities.py' |
494 | --- src/webcatalog/utilities.py 2011-06-29 14:52:33 +0000 |
495 | +++ src/webcatalog/utilities.py 2011-07-01 15:38:25 +0000 |
496 | @@ -184,3 +184,14 @@ |
497 | return True |
498 | |
499 | return False |
500 | + |
501 | + |
502 | +def create_png_from_file(target_path, source_file, width, height): |
503 | + import gtk |
504 | + from glib import GError |
505 | + try: |
506 | + pix = gtk.gdk.pixbuf_new_from_file_at_size(source_file, width, height) |
507 | + pix.save(target_path, "png") |
508 | + return True |
509 | + except GError: |
510 | + return False |
511 | |
512 | === modified file 'src/webcatalog/views.py' |
513 | --- src/webcatalog/views.py 2011-06-29 14:27:13 +0000 |
514 | +++ src/webcatalog/views.py 2011-07-01 15:38:25 +0000 |
515 | @@ -68,6 +68,7 @@ |
516 | ors = [Q(package_name__icontains=term) for term in terms] |
517 | ors += [Q(name__icontains=term) for term in terms] |
518 | apps = Application.objects.filter(reduce(operator.or_, ors)) |
519 | + apps = apps.order_by('name') |
520 | |
521 | crumbs = [{'name': 'Get Software', 'url': reverse('wc-index')}, |
522 | {'name': 'Search results', 'url': reverse('wc-search') + '?' + |
523 | @@ -88,6 +89,7 @@ |
524 | |
525 | def index(request): |
526 | depts = Department.objects.filter(parent=None).order_by('name') |
527 | + depts = depts.order_by('name') |
528 | context = RequestContext(request, dict={'depts': depts}) |
529 | return render_to_response('webcatalog/index.html', |
530 | context_instance=context) |
531 | @@ -96,7 +98,9 @@ |
532 | def department_overview(request, dept_id): |
533 | dept = get_object_or_404(Department, pk=dept_id) |
534 | subdepts = Department.objects.filter(parent=dept) |
535 | + subdepts = subdepts.order_by('name') |
536 | apps = Application.objects.filter(departments=dept) |
537 | + apps = apps.order_by('name') |
538 | paginator = Paginator(apps, settings.PAGE_BATCH_SIZE) |
539 | page_num = _get_page_num_from_request(request, paginator) |
540 |
This has been tested against the whole set of xpm icons pulled in by both natty's and maverick's app-install-data.