Merge lp:~michael.nelson/software-center/833982-previous-purchase-no-feedback-really-this-time into lp:software-center

Proposed by Michael Nelson
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
Reviewer Review Type Date Requested Status
software-store-developers Pending
Review via email: mp+89243@code.launchpad.net

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_BUT_NOT_AVAILABLE_FOR_SERIES

rather than

PURCHASED_BUT_REPO_MUST_BE_ENABLED

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).

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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)