Merge lp:~michael.nelson/software-center/833982-previous-purchase-no-feedback-really-this-time into lp:software-center
- 833982-previous-purchase-no-feedback-really-this-time
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 2686 |
Proposed branch: | lp:~michael.nelson/software-center/833982-previous-purchase-no-feedback-really-this-time |
Merge into: | lp:software-center |
Diff against target: |
679 lines (+372/-196) 5 files modified
softwarecenter/db/application.py (+27/-1) softwarecenter/db/update.py (+209/-190) softwarecenter/enums.py (+3/-1) test/test_database.py (+113/-4) test/test_reinstall_purchased.py (+20/-0) |
To merge this branch: | bzr merge lp:~michael.nelson/software-center/833982-previous-purchase-no-feedback-really-this-time |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
software-store-developers | Pending | ||
Review via email: mp+89243@code.launchpad.net |
Commit message
Description of the change
Overview
========
This branch implements the backend for bug 833982 so that a purchased application that is unsupported on the current system will have a package state of:
PURCHASED_
rather than
PURCHASED_
Details
=======
There were a few things I was unsure about, so please check... specifically, the way I've defined a new sca desktop entry (why is it a desktop entry even though it's not related to a desktop file?)
Most of the change is tests and refactoring to make testing easier - for testing package states (specifically, being able to create a xapian doc for an app, without having to index it in the db... I don't yet understand why the db is even necessary for this point, still learning lots).
Preview Diff
1 | === modified file 'softwarecenter/db/application.py' |
2 | --- softwarecenter/db/application.py 2011-12-16 01:52:15 +0000 |
3 | +++ softwarecenter/db/application.py 2012-01-19 14:07:24 +0000 |
4 | @@ -18,6 +18,7 @@ |
5 | |
6 | from gi.repository import GObject, Gio |
7 | |
8 | +import json |
9 | import locale |
10 | import logging |
11 | import os |
12 | @@ -457,7 +458,23 @@ |
13 | return PkgStates.NEEDS_PURCHASE |
14 | if (self.purchase_date and |
15 | self._doc.get_value(XapianValues.ARCHIVE_DEB_LINE)): |
16 | - return PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED |
17 | + supported_distros = self.supported_distros |
18 | + |
19 | + # Until bug 917109 is fixed on the server we won't have |
20 | + # any supported_distros for a for-purchase app, so we |
21 | + # follow the current behaviour in this case. |
22 | + if not supported_distros: |
23 | + return PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED |
24 | + |
25 | + current_distro = self._distro.get_codename() |
26 | + current_arch = self._distro.get_architecture() |
27 | + if current_distro in supported_distros and ( |
28 | + current_arch in supported_distros[current_distro] or |
29 | + 'any' in supported_distros[current_distro]): |
30 | + return PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED |
31 | + else: |
32 | + return PkgStates.PURCHASED_BUT_NOT_AVAILABLE_FOR_SERIES |
33 | + |
34 | if self.component: |
35 | components = self.component.split('&') |
36 | for component in components: |
37 | @@ -474,6 +491,15 @@ |
38 | return self._doc.get_value(XapianValues.PRICE) |
39 | |
40 | @property |
41 | + def supported_distros(self): |
42 | + if self._doc: |
43 | + supported_series = self._doc.get_value(XapianValues.SC_SUPPORTED_DISTROS) |
44 | + if not supported_series: |
45 | + return {} |
46 | + |
47 | + return json.loads(supported_series) |
48 | + |
49 | + @property |
50 | def ppaname(self): |
51 | if self._doc: |
52 | return self._doc.get_value(XapianValues.ARCHIVE_PPA) |
53 | |
54 | === modified file 'softwarecenter/db/update.py' |
55 | --- softwarecenter/db/update.py 2012-01-18 09:38:45 +0000 |
56 | +++ softwarecenter/db/update.py 2012-01-19 14:07:24 +0000 |
57 | @@ -152,6 +152,7 @@ |
58 | 'Support-Url' : 'support_url', |
59 | 'Description' : 'Description', |
60 | 'Comment' : 'Comment', |
61 | + 'Supported-Distros': 'series', |
62 | } |
63 | |
64 | # map from requested key to a static data element |
65 | @@ -617,7 +618,208 @@ |
66 | # return true if we have updated entries (this can also be an empty list) |
67 | # but only if we did not got a error from the agent |
68 | return sca.good_data |
69 | - |
70 | + |
71 | + |
72 | +def make_doc_from_parser(parser, cache): |
73 | + # XXX 2012-01-19 michaeln I'm just pulling this code out from |
74 | + # index_app_info_from_parser, but it'd be great to further |
75 | + # refactor it - it looks quite scary :-) |
76 | + doc = xapian.Document() |
77 | + # app name is the data |
78 | + if parser.has_option_desktop("X-Ubuntu-Software-Center-Name"): |
79 | + name = parser.get_desktop("X-Ubuntu-Software-Center-Name") |
80 | + untranslated_name = parser.get_desktop("X-Ubuntu-Software-Center-Name", translated=False) |
81 | + elif parser.has_option_desktop("X-GNOME-FullName"): |
82 | + name = parser.get_desktop("X-GNOME-FullName") |
83 | + untranslated_name = parser.get_desktop("X-GNOME-FullName", translated=False) |
84 | + else: |
85 | + name = parser.get_desktop("Name") |
86 | + untranslated_name = parser.get_desktop("Name", translated=False) |
87 | + doc.set_data(name) |
88 | + doc.add_value(XapianValues.APPNAME_UNTRANSLATED, untranslated_name) |
89 | + |
90 | + # check if we should ignore this file |
91 | + if parser.has_option_desktop("X-AppInstall-Ignore"): |
92 | + ignore = parser.get_desktop("X-AppInstall-Ignore") |
93 | + if ignore.strip().lower() == "true": |
94 | + LOG.debug("X-AppInstall-Ignore found for '%s'" % parser.desktopf) |
95 | + return |
96 | + # architecture |
97 | + pkgname_extension = '' |
98 | + if parser.has_option_desktop("X-AppInstall-Architectures"): |
99 | + arches = parser.get_desktop("X-AppInstall-Architectures") |
100 | + doc.add_value(XapianValues.ARCHIVE_ARCH, arches) |
101 | + native_archs = get_current_arch() in arches.split(',') |
102 | + foreign_archs = list(set(arches.split(',')) & set(get_foreign_architectures())) |
103 | + if not (native_archs or foreign_archs): return |
104 | + if not native_archs and foreign_archs: |
105 | + pkgname_extension = ':' + foreign_archs[0] |
106 | + # package name |
107 | + pkgname = parser.get_desktop("X-AppInstall-Package") + pkgname_extension |
108 | + doc.add_term("AP"+pkgname) |
109 | + if '-' in pkgname: |
110 | + # we need this to work around xapian oddness |
111 | + doc.add_term(pkgname.replace('-','_')) |
112 | + doc.add_value(XapianValues.PKGNAME, pkgname) |
113 | + doc.add_value(XapianValues.DESKTOP_FILE, parser.desktopf) |
114 | + # display name |
115 | + if "display_name" in axi_values: |
116 | + doc.add_value(axi_values["display_name"], name) |
117 | + # cataloged_times |
118 | + if "catalogedtime" in axi_values: |
119 | + if pkgname in cataloged_times: |
120 | + doc.add_value(axi_values["catalogedtime"], |
121 | + xapian.sortable_serialise(cataloged_times[pkgname])) |
122 | + # pocket (main, restricted, ...) |
123 | + if parser.has_option_desktop("X-AppInstall-Section"): |
124 | + archive_section = parser.get_desktop("X-AppInstall-Section") |
125 | + doc.add_term("AS"+archive_section) |
126 | + doc.add_value(XapianValues.ARCHIVE_SECTION, archive_section) |
127 | + # section (mail, base, ..) |
128 | + if pkgname in cache and cache[pkgname].candidate: |
129 | + section = cache[pkgname].section |
130 | + doc.add_term("AE"+section) |
131 | + # channel (third party stuff) |
132 | + if parser.has_option_desktop("X-AppInstall-Channel"): |
133 | + archive_channel = parser.get_desktop("X-AppInstall-Channel") |
134 | + doc.add_term("AH"+archive_channel) |
135 | + doc.add_value(XapianValues.ARCHIVE_CHANNEL, archive_channel) |
136 | + # signing key (third party) |
137 | + if parser.has_option_desktop("X-AppInstall-Signing-Key-Id"): |
138 | + keyid = parser.get_desktop("X-AppInstall-Signing-Key-Id") |
139 | + doc.add_value(XapianValues.ARCHIVE_SIGNING_KEY_ID, keyid) |
140 | + # license (third party) |
141 | + if parser.has_option_desktop("X-AppInstall-License"): |
142 | + license = parser.get_desktop("X-AppInstall-License") |
143 | + doc.add_value(XapianValues.LICENSE, license) |
144 | + # date published |
145 | + if parser.has_option_desktop("X-AppInstall-Date-Published"): |
146 | + date_published = parser.get_desktop("X-AppInstall-Date-Published") |
147 | + if date_published: |
148 | + # strip the subseconds from the end of the published date string |
149 | + date_published = str(date_published).split(".")[0] |
150 | + doc.add_value(XapianValues.DATE_PUBLISHED, |
151 | + date_published) |
152 | + # we use the date published value for the cataloged time as well |
153 | + if "catalogedtime" in axi_values: |
154 | + LOG.debug( |
155 | + ("pkgname: %s, date_published cataloged time is: %s" % |
156 | + (pkgname, parser.get_desktop("date_published")))) |
157 | + date_published_sec = time.mktime( |
158 | + time.strptime(date_published, |
159 | + "%Y-%m-%d %H:%M:%S")) |
160 | + doc.add_value(axi_values["catalogedtime"], |
161 | + xapian.sortable_serialise(date_published_sec)) |
162 | + # purchased date |
163 | + if parser.has_option_desktop("X-AppInstall-Purchased-Date"): |
164 | + date = parser.get_desktop("X-AppInstall-Purchased-Date") |
165 | + # strip the subseconds from the end of the date string |
166 | + doc.add_value(XapianValues.PURCHASED_DATE, str(date).split(".")[0]) |
167 | + # deb-line (third party) |
168 | + if parser.has_option_desktop("X-AppInstall-Deb-Line"): |
169 | + debline = parser.get_desktop("X-AppInstall-Deb-Line") |
170 | + doc.add_value(XapianValues.ARCHIVE_DEB_LINE, debline) |
171 | + # license key (third party) |
172 | + if parser.has_option_desktop("X-AppInstall-License-Key"): |
173 | + key = parser.get_desktop("X-AppInstall-License-Key") |
174 | + doc.add_value(XapianValues.LICENSE_KEY, key) |
175 | + # license keypath (third party) |
176 | + if parser.has_option_desktop("X-AppInstall-License-Key-Path"): |
177 | + path = parser.get_desktop("X-AppInstall-License-Key-Path") |
178 | + doc.add_value(XapianValues.LICENSE_KEY_PATH, path) |
179 | + # PPA (third party stuff) |
180 | + if parser.has_option_desktop("X-AppInstall-PPA"): |
181 | + archive_ppa = parser.get_desktop("X-AppInstall-PPA") |
182 | + doc.add_value(XapianValues.ARCHIVE_PPA, archive_ppa) |
183 | + # add archive origin data here so that its available even if |
184 | + # the PPA is not (yet) enabled |
185 | + doc.add_term("XOO"+"lp-ppa-%s" % archive_ppa.replace("/", "-")) |
186 | + # screenshot (for third party) |
187 | + if parser.has_option_desktop("X-AppInstall-Screenshot-Url"): |
188 | + url = parser.get_desktop("X-AppInstall-Screenshot-Url") |
189 | + doc.add_value(XapianValues.SCREENSHOT_URL, url) |
190 | + # thumbnail (for third party) |
191 | + if parser.has_option_desktop("X-AppInstall-Thumbnail-Url"): |
192 | + url = parser.get_desktop("X-AppInstall-Thumbnail-Url") |
193 | + doc.add_value(XapianValues.THUMBNAIL_URL, url) |
194 | + # video support (for third party mostly) |
195 | + if parser.has_option_desktop("X-AppInstall-Video-Url"): |
196 | + url = parser.get_desktop("X-AppInstall-Video-Url") |
197 | + doc.add_value(XapianValues.VIDEO_URL, url) |
198 | + # icon (for third party) |
199 | + if parser.has_option_desktop("X-AppInstall-Icon-Url"): |
200 | + url = parser.get_desktop("X-AppInstall-Icon-Url") |
201 | + doc.add_value(XapianValues.ICON_URL, url) |
202 | + if not parser.has_option_desktop("X-AppInstall-Icon"): |
203 | + # prefix pkgname to avoid name clashes |
204 | + doc.add_value(XapianValues.ICON, "%s-icon-%s" % ( |
205 | + pkgname, os.path.basename(url))) |
206 | + |
207 | + # price (pay stuff) |
208 | + if parser.has_option_desktop("X-AppInstall-Price"): |
209 | + price = parser.get_desktop("X-AppInstall-Price") |
210 | + doc.add_value(XapianValues.PRICE, price) |
211 | + # since this is a commercial app, indicate it in the component value |
212 | + doc.add_value(XapianValues.ARCHIVE_SECTION, "commercial") |
213 | + # support url (mainly pay stuff) |
214 | + if parser.has_option_desktop("X-AppInstall-Support-Url"): |
215 | + url = parser.get_desktop("X-AppInstall-Support-Url") |
216 | + doc.add_value(XapianValues.SUPPORT_SITE_URL, url) |
217 | + # icon |
218 | + if parser.has_option_desktop("Icon"): |
219 | + icon = parser.get_desktop("Icon") |
220 | + doc.add_value(XapianValues.ICON, icon) |
221 | + # write out categories |
222 | + for cat in parser.get_desktop_categories(): |
223 | + doc.add_term("AC"+cat.lower()) |
224 | + categories_string = ";".join(parser.get_desktop_categories()) |
225 | + doc.add_value(XapianValues.CATEGORIES, categories_string) |
226 | + for mime in parser.get_desktop_mimetypes(): |
227 | + doc.add_term("AM"+mime.lower()) |
228 | + # get type (to distinguish between apps and packages |
229 | + if parser.has_option_desktop("Type"): |
230 | + type = parser.get_desktop("Type") |
231 | + doc.add_term("AT"+type.lower()) |
232 | + # check gettext domain |
233 | + if parser.has_option_desktop("X-Ubuntu-Gettext-Domain"): |
234 | + domain = parser.get_desktop("X-Ubuntu-Gettext-Domain") |
235 | + doc.add_value(XapianValues.GETTEXT_DOMAIN, domain) |
236 | + # Description (software-center extension) |
237 | + if parser.has_option_desktop("X-AppInstall-Description"): |
238 | + descr = parser.get_desktop("X-AppInstall-Description") |
239 | + doc.add_value(XapianValues.SC_DESCRIPTION, descr) |
240 | + if parser.has_option_desktop("Supported-Distros"): |
241 | + doc.add_value(XapianValues.SC_SUPPORTED_DISTROS, |
242 | + json.dumps(parser.get_desktop("Supported-Distros"))) |
243 | + |
244 | + # popcon |
245 | + # FIXME: popularity not only based on popcon but also |
246 | + # on archive section, third party app etc |
247 | + if parser.has_option_desktop("X-AppInstall-Popcon"): |
248 | + popcon = float(parser.get_desktop("X-AppInstall-Popcon")) |
249 | + # sort_by_value uses string compare, so we need to pad here |
250 | + doc.add_value(XapianValues.POPCON, |
251 | + xapian.sortable_serialise(popcon)) |
252 | + global popcon_max |
253 | + popcon_max = max(popcon_max, popcon) |
254 | + |
255 | + # comment goes into the summary data if there is one, |
256 | + # other wise we try GenericName and if nothing else, |
257 | + # the summary of the package |
258 | + if parser.has_option_desktop("Comment"): |
259 | + s = parser.get_desktop("Comment") |
260 | + doc.add_value(XapianValues.SUMMARY, s) |
261 | + elif parser.has_option_desktop("GenericName"): |
262 | + s = parser.get_desktop("GenericName") |
263 | + if s != name: |
264 | + doc.add_value(XapianValues.SUMMARY, s) |
265 | + elif pkgname in cache and cache[pkgname].candidate: |
266 | + s = cache[pkgname].candidate.summary |
267 | + doc.add_value(XapianValues.SUMMARY, s) |
268 | + |
269 | + return doc |
270 | + |
271 | + |
272 | def index_app_info_from_parser(parser, db, cache): |
273 | term_generator = xapian.TermGenerator() |
274 | term_generator.set_database(db) |
275 | @@ -633,201 +835,18 @@ |
276 | term_generator.set_flags(xapian.TermGenerator.FLAG_SPELLING) |
277 | except xapian.UnimplementedError: |
278 | pass |
279 | - doc = xapian.Document() |
280 | + doc = make_doc_from_parser(parser, cache) |
281 | term_generator.set_document(doc) |
282 | - # app name is the data |
283 | - if parser.has_option_desktop("X-Ubuntu-Software-Center-Name"): |
284 | - name = parser.get_desktop("X-Ubuntu-Software-Center-Name") |
285 | - untranslated_name = parser.get_desktop("X-Ubuntu-Software-Center-Name", translated=False) |
286 | - elif parser.has_option_desktop("X-GNOME-FullName"): |
287 | - name = parser.get_desktop("X-GNOME-FullName") |
288 | - untranslated_name = parser.get_desktop("X-GNOME-FullName", translated=False) |
289 | - else: |
290 | - name = parser.get_desktop("Name") |
291 | - untranslated_name = parser.get_desktop("Name", translated=False) |
292 | + name = doc.get_data() |
293 | + |
294 | if name in seen: |
295 | LOG.debug("duplicated name '%s' (%s)" % (name, parser.desktopf)) |
296 | LOG.debug("indexing app '%s'" % name) |
297 | seen.add(name) |
298 | - doc.set_data(name) |
299 | + |
300 | index_name(doc, name, term_generator) |
301 | - doc.add_value(XapianValues.APPNAME_UNTRANSLATED, untranslated_name) |
302 | - |
303 | - # check if we should ignore this file |
304 | - if parser.has_option_desktop("X-AppInstall-Ignore"): |
305 | - ignore = parser.get_desktop("X-AppInstall-Ignore") |
306 | - if ignore.strip().lower() == "true": |
307 | - LOG.debug("X-AppInstall-Ignore found for '%s'" % parser.desktopf) |
308 | - return |
309 | - # architecture |
310 | - pkgname_extension = '' |
311 | - if parser.has_option_desktop("X-AppInstall-Architectures"): |
312 | - arches = parser.get_desktop("X-AppInstall-Architectures") |
313 | - doc.add_value(XapianValues.ARCHIVE_ARCH, arches) |
314 | - native_archs = get_current_arch() in arches.split(',') |
315 | - foreign_archs = list(set(arches.split(',')) & set(get_foreign_architectures())) |
316 | - if not (native_archs or foreign_archs): return |
317 | - if not native_archs and foreign_archs: |
318 | - pkgname_extension = ':' + foreign_archs[0] |
319 | - # package name |
320 | - pkgname = parser.get_desktop("X-AppInstall-Package") + pkgname_extension |
321 | - doc.add_term("AP"+pkgname) |
322 | - if '-' in pkgname: |
323 | - # we need this to work around xapian oddness |
324 | - doc.add_term(pkgname.replace('-','_')) |
325 | - doc.add_value(XapianValues.PKGNAME, pkgname) |
326 | - doc.add_value(XapianValues.DESKTOP_FILE, parser.desktopf) |
327 | - # display name |
328 | - if "display_name" in axi_values: |
329 | - doc.add_value(axi_values["display_name"], name) |
330 | - # cataloged_times |
331 | - if "catalogedtime" in axi_values: |
332 | - if pkgname in cataloged_times: |
333 | - doc.add_value(axi_values["catalogedtime"], |
334 | - xapian.sortable_serialise(cataloged_times[pkgname])) |
335 | - # pocket (main, restricted, ...) |
336 | - if parser.has_option_desktop("X-AppInstall-Section"): |
337 | - archive_section = parser.get_desktop("X-AppInstall-Section") |
338 | - doc.add_term("AS"+archive_section) |
339 | - doc.add_value(XapianValues.ARCHIVE_SECTION, archive_section) |
340 | - # section (mail, base, ..) |
341 | - if pkgname in cache and cache[pkgname].candidate: |
342 | - section = cache[pkgname].section |
343 | - doc.add_term("AE"+section) |
344 | - # channel (third party stuff) |
345 | - if parser.has_option_desktop("X-AppInstall-Channel"): |
346 | - archive_channel = parser.get_desktop("X-AppInstall-Channel") |
347 | - doc.add_term("AH"+archive_channel) |
348 | - doc.add_value(XapianValues.ARCHIVE_CHANNEL, archive_channel) |
349 | - # signing key (third party) |
350 | - if parser.has_option_desktop("X-AppInstall-Signing-Key-Id"): |
351 | - keyid = parser.get_desktop("X-AppInstall-Signing-Key-Id") |
352 | - doc.add_value(XapianValues.ARCHIVE_SIGNING_KEY_ID, keyid) |
353 | - # license (third party) |
354 | - if parser.has_option_desktop("X-AppInstall-License"): |
355 | - license = parser.get_desktop("X-AppInstall-License") |
356 | - doc.add_value(XapianValues.LICENSE, license) |
357 | - # date published |
358 | - if parser.has_option_desktop("X-AppInstall-Date-Published"): |
359 | - date_published = parser.get_desktop("X-AppInstall-Date-Published") |
360 | - if date_published: |
361 | - # strip the subseconds from the end of the published date string |
362 | - date_published = str(date_published).split(".")[0] |
363 | - doc.add_value(XapianValues.DATE_PUBLISHED, |
364 | - date_published) |
365 | - # we use the date published value for the cataloged time as well |
366 | - if "catalogedtime" in axi_values: |
367 | - LOG.debug( |
368 | - ("pkgname: %s, date_published cataloged time is: %s" % |
369 | - (pkgname, parser.get_desktop("date_published")))) |
370 | - date_published_sec = time.mktime( |
371 | - time.strptime(date_published, |
372 | - "%Y-%m-%d %H:%M:%S")) |
373 | - doc.add_value(axi_values["catalogedtime"], |
374 | - xapian.sortable_serialise(date_published_sec)) |
375 | - # purchased date |
376 | - if parser.has_option_desktop("X-AppInstall-Purchased-Date"): |
377 | - date = parser.get_desktop("X-AppInstall-Purchased-Date") |
378 | - # strip the subseconds from the end of the date string |
379 | - doc.add_value(XapianValues.PURCHASED_DATE, str(date).split(".")[0]) |
380 | - # deb-line (third party) |
381 | - if parser.has_option_desktop("X-AppInstall-Deb-Line"): |
382 | - debline = parser.get_desktop("X-AppInstall-Deb-Line") |
383 | - doc.add_value(XapianValues.ARCHIVE_DEB_LINE, debline) |
384 | - # license key (third party) |
385 | - if parser.has_option_desktop("X-AppInstall-License-Key"): |
386 | - key = parser.get_desktop("X-AppInstall-License-Key") |
387 | - doc.add_value(XapianValues.LICENSE_KEY, key) |
388 | - # license keypath (third party) |
389 | - if parser.has_option_desktop("X-AppInstall-License-Key-Path"): |
390 | - path = parser.get_desktop("X-AppInstall-License-Key-Path") |
391 | - doc.add_value(XapianValues.LICENSE_KEY_PATH, path) |
392 | - # PPA (third party stuff) |
393 | - if parser.has_option_desktop("X-AppInstall-PPA"): |
394 | - archive_ppa = parser.get_desktop("X-AppInstall-PPA") |
395 | - doc.add_value(XapianValues.ARCHIVE_PPA, archive_ppa) |
396 | - # add archive origin data here so that its available even if |
397 | - # the PPA is not (yet) enabled |
398 | - doc.add_term("XOO"+"lp-ppa-%s" % archive_ppa.replace("/", "-")) |
399 | - # screenshot (for third party) |
400 | - if parser.has_option_desktop("X-AppInstall-Screenshot-Url"): |
401 | - url = parser.get_desktop("X-AppInstall-Screenshot-Url") |
402 | - doc.add_value(XapianValues.SCREENSHOT_URL, url) |
403 | - # thumbnail (for third party) |
404 | - if parser.has_option_desktop("X-AppInstall-Thumbnail-Url"): |
405 | - url = parser.get_desktop("X-AppInstall-Thumbnail-Url") |
406 | - doc.add_value(XapianValues.THUMBNAIL_URL, url) |
407 | - # video support (for third party mostly) |
408 | - if parser.has_option_desktop("X-AppInstall-Video-Url"): |
409 | - url = parser.get_desktop("X-AppInstall-Video-Url") |
410 | - doc.add_value(XapianValues.VIDEO_URL, url) |
411 | - # icon (for third party) |
412 | - if parser.has_option_desktop("X-AppInstall-Icon-Url"): |
413 | - url = parser.get_desktop("X-AppInstall-Icon-Url") |
414 | - doc.add_value(XapianValues.ICON_URL, url) |
415 | - if not parser.has_option_desktop("X-AppInstall-Icon"): |
416 | - # prefix pkgname to avoid name clashes |
417 | - doc.add_value(XapianValues.ICON, "%s-icon-%s" % ( |
418 | - pkgname, os.path.basename(url))) |
419 | - |
420 | - # price (pay stuff) |
421 | - if parser.has_option_desktop("X-AppInstall-Price"): |
422 | - price = parser.get_desktop("X-AppInstall-Price") |
423 | - doc.add_value(XapianValues.PRICE, price) |
424 | - # since this is a commercial app, indicate it in the component value |
425 | - doc.add_value(XapianValues.ARCHIVE_SECTION, "commercial") |
426 | - # support url (mainly pay stuff) |
427 | - if parser.has_option_desktop("X-AppInstall-Support-Url"): |
428 | - url = parser.get_desktop("X-AppInstall-Support-Url") |
429 | - doc.add_value(XapianValues.SUPPORT_SITE_URL, url) |
430 | - # icon |
431 | - if parser.has_option_desktop("Icon"): |
432 | - icon = parser.get_desktop("Icon") |
433 | - doc.add_value(XapianValues.ICON, icon) |
434 | - # write out categories |
435 | - for cat in parser.get_desktop_categories(): |
436 | - doc.add_term("AC"+cat.lower()) |
437 | - categories_string = ";".join(parser.get_desktop_categories()) |
438 | - doc.add_value(XapianValues.CATEGORIES, categories_string) |
439 | - for mime in parser.get_desktop_mimetypes(): |
440 | - doc.add_term("AM"+mime.lower()) |
441 | - # get type (to distinguish between apps and packages |
442 | - if parser.has_option_desktop("Type"): |
443 | - type = parser.get_desktop("Type") |
444 | - doc.add_term("AT"+type.lower()) |
445 | - # check gettext domain |
446 | - if parser.has_option_desktop("X-Ubuntu-Gettext-Domain"): |
447 | - domain = parser.get_desktop("X-Ubuntu-Gettext-Domain") |
448 | - doc.add_value(XapianValues.GETTEXT_DOMAIN, domain) |
449 | - # Description (software-center extension) |
450 | - if parser.has_option_desktop("X-AppInstall-Description"): |
451 | - descr = parser.get_desktop("X-AppInstall-Description") |
452 | - doc.add_value(XapianValues.SC_DESCRIPTION, descr) |
453 | - # popcon |
454 | - # FIXME: popularity not only based on popcon but also |
455 | - # on archive section, third party app etc |
456 | - if parser.has_option_desktop("X-AppInstall-Popcon"): |
457 | - popcon = float(parser.get_desktop("X-AppInstall-Popcon")) |
458 | - # sort_by_value uses string compare, so we need to pad here |
459 | - doc.add_value(XapianValues.POPCON, |
460 | - xapian.sortable_serialise(popcon)) |
461 | - global popcon_max |
462 | - popcon_max = max(popcon_max, popcon) |
463 | - |
464 | - # comment goes into the summary data if there is one, |
465 | - # other wise we try GenericName and if nothing else, |
466 | - # the summary of the package |
467 | - if parser.has_option_desktop("Comment"): |
468 | - s = parser.get_desktop("Comment") |
469 | - doc.add_value(XapianValues.SUMMARY, s) |
470 | - elif parser.has_option_desktop("GenericName"): |
471 | - s = parser.get_desktop("GenericName") |
472 | - if s != name: |
473 | - doc.add_value(XapianValues.SUMMARY, s) |
474 | - elif pkgname in cache and cache[pkgname].candidate: |
475 | - s = cache[pkgname].candidate.summary |
476 | - doc.add_value(XapianValues.SUMMARY, s) |
477 | - |
478 | + |
479 | + pkgname = doc.get_value(XapianValues.PKGNAME) |
480 | # add packagename as meta-data too |
481 | term_generator.index_text_without_positions(pkgname, WEIGHT_APT_PKGNAME) |
482 | |
483 | |
484 | === modified file 'softwarecenter/enums.py' |
485 | --- softwarecenter/enums.py 2012-01-18 21:27:30 +0000 |
486 | +++ softwarecenter/enums.py 2012-01-19 14:07:24 +0000 |
487 | @@ -140,6 +140,7 @@ |
488 | VIDEO_URL = 195 |
489 | DATE_PUBLISHED = 196 |
490 | SUPPORT_SITE_URL = 197 |
491 | + SC_SUPPORTED_DISTROS = 198 |
492 | |
493 | # fake channels |
494 | PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME = "for-pay-needs-reinstall" |
495 | @@ -179,7 +180,8 @@ |
496 | # this *needs* to be last (for test_appdetails.py) and means |
497 | # something went wrong and we don't have a state for this PKG |
498 | UNKNOWN, |
499 | - ) = range(15) |
500 | + PURCHASED_BUT_NOT_AVAILABLE_FOR_SERIES, |
501 | + ) = range(16) |
502 | |
503 | # visibility of non applications in the search results |
504 | class NonAppVisibility: |
505 | |
506 | === modified file 'test/test_database.py' |
507 | --- test/test_database.py 2012-01-18 21:27:30 +0000 |
508 | +++ test/test_database.py 2012-01-19 14:07:24 +0000 |
509 | @@ -10,19 +10,28 @@ |
510 | import unittest |
511 | import xapian |
512 | |
513 | +from piston_mini_client import PistonResponseObject |
514 | + |
515 | from softwarecenter.db.application import Application, AppDetails |
516 | from softwarecenter.db.database import StoreDatabase |
517 | from softwarecenter.db.enquire import AppEnquire |
518 | from softwarecenter.db.database import parse_axi_values_file |
519 | from softwarecenter.db.pkginfo import get_pkg_info |
520 | -from softwarecenter.db.update import (update_from_app_install_data, |
521 | - update_from_var_lib_apt_lists, |
522 | - update_from_appstream_xml, |
523 | - update_from_software_center_agent) |
524 | +from softwarecenter.db.update import ( |
525 | + make_doc_from_parser, |
526 | + update_from_app_install_data, |
527 | + update_from_var_lib_apt_lists, |
528 | + update_from_appstream_xml, |
529 | + update_from_software_center_agent, |
530 | + SCAPurchasedApplicationParser, |
531 | + ) |
532 | +from softwarecenter.distro import get_distro |
533 | from softwarecenter.enums import ( |
534 | XapianValues, |
535 | PkgStates, |
536 | ) |
537 | +from softwarecenter.testutils import get_test_db |
538 | + |
539 | |
540 | class TestDatabase(unittest.TestCase): |
541 | """ tests the store database """ |
542 | @@ -371,6 +380,106 @@ |
543 | self.assertTrue(len(enquirer.get_docids()) > 0) |
544 | # FIXME: test more of the interface |
545 | |
546 | + |
547 | +class AppDetailsPkgStateTestCase(unittest.TestCase): |
548 | + |
549 | + def _make_app_details(self, supported_series=None): |
550 | + subscription = { |
551 | + u'application': { |
552 | + u'archive_id': u'commercial-ppa-uploaders/photobomb', |
553 | + u'description': u"Easy and Social Image Editor\nPhotobomb " |
554 | + u"give you easy access to images in your " |
555 | + u"social networking feeds, pictures on ...", |
556 | + u'name': u'Photobomb', |
557 | + u'package_name': u'photobomb', |
558 | + u'signing_key_id': u'1024R/75254D99' |
559 | + }, |
560 | + u'deb_line': u'deb https://some.user:ABCDEFGHIJKLMNOP@' |
561 | + u'private-ppa.launchpad.net/commercial-ppa-uploaders/' |
562 | + u'photobomb/ubuntu natty main', |
563 | + u'distro_series': {u'code_name': u'natty', u'version': u'11.04'}, |
564 | + u'failures': [], |
565 | + u'open_id': u'https://login.ubuntu.com/+id/ABCDEF', |
566 | + u'purchase_date': u'2011-09-16 06:37:52', |
567 | + u'purchase_price': u'2.99', |
568 | + u'state': u'Complete', |
569 | + } |
570 | + |
571 | + if supported_series != None: |
572 | + subscription['application']['series'] = supported_series |
573 | + |
574 | + item = PistonResponseObject.from_dict(subscription) |
575 | + parser = SCAPurchasedApplicationParser(item) |
576 | + doc = make_doc_from_parser(parser, self.db._aptcache) |
577 | + app_details = AppDetails(self.db, doc) |
578 | + return app_details |
579 | + |
580 | + @classmethod |
581 | + def setUpClass(cls): |
582 | + # Set these as class attributes as we don't modify either |
583 | + # during the tests. |
584 | + cls.distro = get_distro() |
585 | + cls.db = get_test_db() |
586 | + |
587 | + def test_package_state_purchased_enable_repo(self): |
588 | + # If the current series is supported by the app, the state should |
589 | + # be PURCHASED_BUT_REPO_MUST_BE_ENABLED. |
590 | + app_details = self._make_app_details( |
591 | + supported_series={ |
592 | + 'current-1': ['i386', 'amd64'], |
593 | + self.distro.get_codename(): [self.distro.get_architecture()] |
594 | + }) |
595 | + |
596 | + state = app_details.pkg_state |
597 | + |
598 | + self.assertEqual( |
599 | + PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED, |
600 | + state) |
601 | + |
602 | + def test_package_state_purchased_not_available(self): |
603 | + # If the current series is NOT supported by the app, the state should |
604 | + # be PURCHASED_BUT_NOT_AVAILABLE_FOR_SERIES. |
605 | + app_details = self._make_app_details( |
606 | + supported_series={ |
607 | + 'current-1': ['i386', 'amd64'], |
608 | + self.distro.get_codename(): ['newarch', 'amdm128'], |
609 | + }) |
610 | + |
611 | + state = app_details.pkg_state |
612 | + |
613 | + self.assertEqual( |
614 | + PkgStates.PURCHASED_BUT_NOT_AVAILABLE_FOR_SERIES, |
615 | + state) |
616 | + |
617 | + def test_package_state_no_series(self): |
618 | + # Until the fix for bug 917109 is deployed on production, we |
619 | + # should default to the current (broken) behaviour of |
620 | + # indicating that the repo just needs enabling. |
621 | + app_details = self._make_app_details(supported_series=None) |
622 | + |
623 | + state = app_details.pkg_state |
624 | + |
625 | + self.assertEqual( |
626 | + PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED, |
627 | + state) |
628 | + |
629 | + def test_package_state_arch_any(self): |
630 | + # In the future the supported arches returned by sca will include |
631 | + # any - let's not break when that happens. |
632 | + app_details = self._make_app_details( |
633 | + supported_series={ |
634 | + 'current-1': ['i386', 'amd64'], |
635 | + self.distro.get_codename(): ['newarch', 'any'], |
636 | + }) |
637 | + |
638 | + state = app_details.pkg_state |
639 | + |
640 | + self.assertEqual( |
641 | + PkgStates.PURCHASED_BUT_REPO_MUST_BE_ENABLED, |
642 | + state) |
643 | + |
644 | + |
645 | + |
646 | if __name__ == "__main__": |
647 | import logging |
648 | logging.basicConfig(level=logging.DEBUG) |
649 | |
650 | === modified file 'test/test_reinstall_purchased.py' |
651 | --- test/test_reinstall_purchased.py 2012-01-18 09:38:45 +0000 |
652 | +++ test/test_reinstall_purchased.py 2012-01-19 14:07:24 +0000 |
653 | @@ -295,6 +295,26 @@ |
654 | PURCHASED_NEEDS_REINSTALL_MAGIC_CHANNEL_NAME, |
655 | parser.get_desktop('Channel')) |
656 | |
657 | + def test_will_handle_supported_distros_when_available(self): |
658 | + # When the fix for bug 917109 reaches production, we will be |
659 | + # able to use the supported series. |
660 | + parser = self._make_application_parser() |
661 | + supported_distros = { |
662 | + "maverick": [ |
663 | + "i386", |
664 | + "amd64" |
665 | + ], |
666 | + "natty": [ |
667 | + "i386", |
668 | + "amd64" |
669 | + ], |
670 | + } |
671 | + parser.sca_application.series = supported_distros |
672 | + |
673 | + self.assertEqual( |
674 | + supported_distros, |
675 | + parser.get_desktop('Supported-Distros')) |
676 | + |
677 | |
678 | if __name__ == "__main__": |
679 | logging.basicConfig(level=logging.DEBUG) |