def test_compatible_version(self): addon = addon_factory() version = version_factory(addon=addon, version='99') version_factory(addon=addon, version='100', channel=amo.RELEASE_CHANNEL_UNLISTED) assert find_compatible_version(addon, amo.FIREFOX.id) == version
def test_compatible_version(self): addon = addon_factory() version = version_factory(addon=addon, version='99') version_factory( addon=addon, version='100', channel=amo.RELEASE_CHANNEL_UNLISTED) assert find_compatible_version(addon, amo.FIREFOX.id) == version
def process_request(self, query, addon_type='ALL', limit=10, platform='ALL', version=None, compat_mode='strict'): """ Query the search backend and serve up the XML. """ limit = min(MAX_LIMIT, int(limit)) app_id = self.request.APP.id # We currently filter for status=PUBLIC for all versions. If # that changes, the contract for API version 1.5 requires # that we continue filtering for it there. filters = { 'app': app_id, 'status': amo.STATUS_PUBLIC, 'is_experimental': False, 'is_disabled': False, 'current_version__exists': True, } # Opts may get overridden by query string filters. opts = { 'addon_type': addon_type, 'version': version, } # Specific case for Personas (bug 990768): if we search providing the # Persona addon type (9), don't filter on the platform as Personas # don't have compatible platforms to filter on. if addon_type != '9': opts['platform'] = platform if self.version < 1.5: # Fix doubly encoded query strings. try: query = urllib.unquote(query.encode('ascii')) except UnicodeEncodeError: # This fails if the string is already UTF-8. pass query, qs_filters, params = extract_filters(query, opts) qs = Addon.search().filter_query_string(query) filters.update(qs_filters) if 'type' not in filters: # Filter by ALL types, which is really all types except for apps. filters['type__in'] = list(amo.ADDON_SEARCH_TYPES) qs = qs.filter(**filters) qs = qs[:limit] total = qs.count() results = [] for addon in qs: compat_version = find_compatible_version( addon, app_id, params['version'], params['platform'], compat_mode) # Specific case for Personas (bug 990768): if we search providing # the Persona addon type (9), then don't look for a compatible # version. if compat_version or addon_type == '9': addon.compat_version = compat_version results.append(addon) if len(results) == limit: break else: # We're excluding this addon because there are no # compatible versions. Decrement the total. total -= 1 return self.render('legacy_api/search.xml', { 'results': results, 'total': total, # For caching 'version': version, 'compat_mode': compat_mode, })
def addon_filter(addons, addon_type, limit, app, platform, version, compat_mode='strict', shuffle=True): """ Filter addons by type, application, app version, and platform. Add-ons that support the current locale will be sorted to front of list. Shuffling will be applied to the add-ons supporting the locale and the others separately. Doing this in the database takes too long, so we in code and wrap it in generous caching. """ APP = app if addon_type.upper() != 'ALL': try: addon_type = int(addon_type) if addon_type: addons = [a for a in addons if a.type == addon_type] except ValueError: # `addon_type` is ALL or a type id. Otherwise we ignore it. pass # Take out personas since they don't have versions. groups = dict(partition(addons, lambda x: x.type == amo.ADDON_PERSONA)) personas, addons = groups.get(True, []), groups.get(False, []) platform = platform.lower() if platform != 'all' and platform in amo.PLATFORM_DICT: def f(ps): return pid in ps or amo.PLATFORM_ALL in ps pid = amo.PLATFORM_DICT[platform] addons = [a for a in addons if f(a.current_version.supported_platforms)] if version is not None: vint = version_int(version) def f_strict(app): return app.min.version_int <= vint <= app.max.version_int def f_ignore(app): return app.min.version_int <= vint xs = [(a, a.compatible_apps) for a in addons] # Iterate over addons, checking compatibility depending on compat_mode. addons = [] for addon, apps in xs: app = apps.get(APP) if compat_mode == 'strict': if app and f_strict(app): addons.append(addon) elif compat_mode == 'ignore': if app and f_ignore(app): addons.append(addon) elif compat_mode == 'normal': # This does a db hit but it's cached. This handles the cases # for strict opt-in, binary components, and compat overrides. v = find_compatible_version(addon, APP.id, version, platform, compat_mode) if v: # There's a compatible version. addons.append(addon) # Put personas back in. addons.extend(personas) # We prefer add-ons that support the current locale. lang = get_language() def partitioner(x): return x.description is not None and (x.description.locale == lang) groups = dict(partition(addons, partitioner)) good, others = groups.get(True, []), groups.get(False, []) if shuffle: random.shuffle(good) random.shuffle(others) # If limit=0, we return all addons with `good` coming before `others`. # Otherwise pad `good` if less than the limit and return the limit. if limit > 0: if len(good) < limit: good.extend(others[:limit - len(good)]) return good[:limit] else: good.extend(others) return good
def process_request(self, query, addon_type='ALL', limit=10, platform='ALL', version=None, compat_mode='strict'): """ Query the search backend and serve up the XML. """ limit = min(MAX_LIMIT, int(limit)) app_id = self.request.APP.id # We currently filter for status=PUBLIC for all versions. If # that changes, the contract for API version 1.5 requires # that we continue filtering for it there. filters = { 'app': app_id, 'status': amo.STATUS_PUBLIC, 'is_experimental': False, 'is_disabled': False, 'current_version__exists': True, } params = {'version': version, 'platform': None} # Specific case for Personas (bug 990768): if we search providing the # Persona addon type (9), don't filter on the platform as Personas # don't have compatible platforms to filter on. if addon_type != '9': params['platform'] = platform # Type filters. if addon_type: try: atype = int(addon_type) if atype in amo.ADDON_SEARCH_TYPES: filters['type'] = atype except ValueError: atype = amo.ADDON_SEARCH_SLUGS.get(addon_type.lower()) if atype: filters['type'] = atype if 'type' not in filters: # Filter by ALL types, which is really all types except for apps. filters['type__in'] = list(amo.ADDON_SEARCH_TYPES) if self.version < 1.5: # Fix doubly encoded query strings. try: query = urllib.unquote(query.encode('ascii')) except UnicodeEncodeError: # This fails if the string is already UTF-8. pass qs = (Addon.search().filter( **filters).filter_query_string(query)[:limit]) results = [] total = qs.count() for addon in qs: compat_version = find_compatible_version(addon, app_id, params['version'], params['platform'], compat_mode) # Specific case for Personas (bug 990768): if we search # providing the Persona addon type (9), then don't look for a # compatible version. if compat_version or addon_type == '9': addon.compat_version = compat_version results.append(addon) if len(results) == limit: break else: # We're excluding this addon because there are no # compatible versions. Decrement the total. total -= 1 return self.render( 'legacy_api/search.xml', { 'results': results, 'total': total, # For caching 'version': version, 'compat_mode': compat_mode, })
def addon_filter(addons, addon_type, limit, app, platform, version, compat_mode='strict', shuffle=True): """ Filter addons by type, application, app version, and platform. Add-ons that support the current locale will be sorted to front of list. Shuffling will be applied to the add-ons supporting the locale and the others separately. Doing this in the database takes too long, so we do it in code and wrap it in generous caching. """ APP = app if addon_type.upper() != 'ALL': try: addon_type = int(addon_type) if addon_type: addons = [a for a in addons if a.type == addon_type] except ValueError: # `addon_type` is ALL or a type id. Otherwise we ignore it. pass # Take out personas since they don't have versions. groups = dict(partition(addons, lambda x: x.type == amo.ADDON_PERSONA)) personas, addons = groups.get(True, []), groups.get(False, []) platform = platform.lower() if platform != 'all' and platform in amo.PLATFORM_DICT: def f(ps): return pid in ps or amo.PLATFORM_ALL in ps pid = amo.PLATFORM_DICT[platform] addons = [ a for a in addons if f(a.current_version.supported_platforms) ] if version is not None: vint = version_int(version) def f_strict(app): return app.min.version_int <= vint <= app.max.version_int def f_ignore(app): return app.min.version_int <= vint xs = [(a, a.compatible_apps) for a in addons] # Iterate over addons, checking compatibility depending on compat_mode. addons = [] for addon, apps in xs: app = apps.get(APP) if compat_mode == 'strict': if app and f_strict(app): addons.append(addon) elif compat_mode == 'ignore': if app and f_ignore(app): addons.append(addon) elif compat_mode == 'normal': # This does a db hit but it's cached. This handles the cases # for strict opt-in, binary components, and compat overrides. v = find_compatible_version(addon, APP.id, version, platform, compat_mode) if v: # There's a compatible version. addons.append(addon) # Put personas back in. addons.extend(personas) # We prefer add-ons that support the current locale. lang = get_language() def partitioner(x): return x.description is not None and (x.description.locale == lang) groups = dict(partition(addons, partitioner)) good, others = groups.get(True, []), groups.get(False, []) if shuffle: random.shuffle(good) random.shuffle(others) # If limit=0, we return all addons with `good` coming before `others`. # Otherwise pad `good` if less than the limit and return the limit. if limit > 0: if len(good) < limit: good.extend(others[:limit - len(good)]) return good[:limit] else: good.extend(others) return good