mirror of
https://github.com/openSUSE/osc.git
synced 2024-12-26 09:56:13 +01:00
- reworked do_search() and osc's search interface
- removed build_xpath_predicate() - rewrote search() - added xpath_join() to join two xpath expressions - TODO: backward compatibility: currently do_search() requires a recent api version from git master in order to do some role filter stuff
This commit is contained in:
parent
bc1322803d
commit
c6c9506640
@ -4080,7 +4080,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
|
||||
help='generate output in CSV (separated by |)')
|
||||
@cmdln.alias('sm')
|
||||
@cmdln.alias('se')
|
||||
def do_search(self, subcmd, opts, *args):
|
||||
def do_search(self, subcmd, opts, search_term):
|
||||
"""${cmd_name}: Search for a project and/or package.
|
||||
|
||||
If no option is specified osc will search for projects and
|
||||
@ -4096,98 +4096,94 @@ Please submit there instead, or use --nodevelproject to force direct submission.
|
||||
osc search does not find binary rpm names. Use
|
||||
http://software.opensuse.org/search?q=binaryname
|
||||
"""
|
||||
def build_xpath(attr, what, substr = False):
|
||||
if substr:
|
||||
return 'contains(%s, \'%s\')' % (attr, what)
|
||||
else:
|
||||
return '%s = \'%s\'' % (attr, what)
|
||||
|
||||
if opts.mine:
|
||||
opts.bugowner = True
|
||||
opts.package = True
|
||||
|
||||
for_user = False
|
||||
if opts.involved or opts.bugowner or opts.maintainer:
|
||||
for_user = True
|
||||
if (opts.title or opts.description) and (opts.involved or opts.bugowner or opts.maintainer):
|
||||
raise oscerr.WrongOptions('Sorry, the options \'--title\' and/or \'--description\' ' \
|
||||
'are mutually exclusive with \'-i\'/\'-b\'/\'-m\'/\'-M\'')
|
||||
if opts.substring and opts.exact:
|
||||
raise oscerr.WrongOptions('Sorry, the options \'--substring\' and \'--exact\' are mutually exclusive')
|
||||
|
||||
search_term = None
|
||||
if len(args) > 1:
|
||||
raise oscerr.WrongArgs('Too many arguments.')
|
||||
elif len(args) < 1 and not for_user:
|
||||
raise oscerr.WrongArgs('Too few arguments.')
|
||||
elif len(args) == 1:
|
||||
search_term = args[0]
|
||||
|
||||
if (opts.title or opts.description) and for_user:
|
||||
raise oscerr.WrongArgs('Sorry, the options \'--title\' and/or \'--description\' ' \
|
||||
'are mutually exclusive with \'-i\'/\'-b\'/\'-m\'/\'-M\'')
|
||||
search_list = []
|
||||
search_for = []
|
||||
extra_limiter = ""
|
||||
if subcmd == 'sm' or opts.maintained:
|
||||
opts.bugowner = True
|
||||
opts.package = True
|
||||
opts.project = True
|
||||
opts.limit_to_attribute = conf.config['maintained_attribute']
|
||||
if opts.title:
|
||||
search_list.append('title')
|
||||
if opts.description:
|
||||
search_list.append('description')
|
||||
if opts.project:
|
||||
search_list.append('@name')
|
||||
search_for.append('project')
|
||||
if opts.package:
|
||||
search_list.append('@name')
|
||||
search_for.append('package')
|
||||
if opts.limit_to_attribute:
|
||||
extra_limiter='attribute/@name="%s"' % (opts.limit_to_attribute)
|
||||
if not opts.substring:
|
||||
opts.exact = True
|
||||
|
||||
xpath = ''
|
||||
if opts.title:
|
||||
xpath = xpath_join(xpath, build_xpath('title', search_term, opts.substring), inner=True)
|
||||
if opts.description:
|
||||
xpath = xpath_join(xpath, build_xpath('description', search_term, opts.substring), inner=True)
|
||||
if opts.project or opts.package:
|
||||
xpath = xpath_join(xpath, build_xpath('@name', search_term, opts.substring), inner=True)
|
||||
# role filter
|
||||
role_filter = ''
|
||||
if opts.bugowner or opts.maintainer or opts.involved:
|
||||
xpath = xpath_join(xpath, 'person/@userid = \'%s\'' % search_term, inner=True)
|
||||
role_filter = '%s (%s)' % (search_term, 'person')
|
||||
if opts.bugowner and not opts.maintainer:
|
||||
xpath = xpath_join(xpath, 'person/@role=\'bugowner\'', op='and')
|
||||
role_filter = '%s (%s)' % (search_term, 'bugowner')
|
||||
elif not opts.bugowner and opts.maintainer:
|
||||
xpath = xpath_join(xpath, 'person/@role=\'maintainer\'', op='and')
|
||||
role_filter = '%s (%s)' % (search_term, 'maintainer')
|
||||
if opts.limit_to_attribute:
|
||||
xpath = xpath_join(xpath, 'attribute/@name=\'%s\'' % opts.limit_to_attribute, op='and')
|
||||
|
||||
role_filter=None
|
||||
if for_user:
|
||||
search_list = [ 'person/@userid' ]
|
||||
search_term = search_term or conf.get_apiurl_usr(conf.config['apiurl'])
|
||||
if opts.bugowner and not opts.maintainer:
|
||||
role_filter = search_term+':bugowner'
|
||||
if not opts.bugowner and opts.maintainer:
|
||||
role_filter = search_term+':maintainer'
|
||||
if not xpath:
|
||||
xpath = xpath_join(xpath, build_xpath('@name', search_term, opts.substring), inner=True)
|
||||
xpath = xpath_join(xpath, build_xpath('title', search_term, opts.substring), inner=True)
|
||||
xpath = xpath_join(xpath, build_xpath('description', search_term, opts.substring), inner=True)
|
||||
what = {'project': xpath, 'package': xpath}
|
||||
if subcmd == 'sm' or opts.maintained:
|
||||
xpath = xpath_join(xpath, '(project/attribute/@name=\'%(attr)s\' or attribute/@name=\'%(attr)s\')' % {'attr': conf.config['maintained_attribute']}, op='and')
|
||||
what = {'package': xpath}
|
||||
elif opts.project and not opts.package:
|
||||
what = {'project': xpath}
|
||||
elif not opts.project and opts.package:
|
||||
what = {'package': xpath}
|
||||
res = search(conf.config['apiurl'], **what)
|
||||
for kind, root in res.iteritems():
|
||||
results = []
|
||||
for node in root.findall(kind):
|
||||
result = []
|
||||
project = node.get('project')
|
||||
package = None
|
||||
if project is None:
|
||||
project = node.get('name')
|
||||
else:
|
||||
package = node.get('name')
|
||||
result.append(project)
|
||||
if not package is None:
|
||||
result.append(package)
|
||||
if opts.verbose:
|
||||
title = node.findtext('title').strip()
|
||||
if len(title) > 60:
|
||||
title = title[:61] + '...'
|
||||
result.append(title)
|
||||
if opts.repos_baseurl:
|
||||
# FIXME: no hardcoded URL of instance
|
||||
result.append('http://download.opensuse.org/repositories/%s/' % project.replace(':', ':/'))
|
||||
results.append(result)
|
||||
|
||||
if not search_list:
|
||||
search_list = ['title', 'description', '@name']
|
||||
if not search_for:
|
||||
search_for = [ 'project', 'package' ]
|
||||
|
||||
for kind in search_for:
|
||||
# special mode only for maintainted search, search for projects based on package names
|
||||
if subcmd == 'sm' or opts.maintained:
|
||||
if kind == 'project':
|
||||
search_list.append('package/@name')
|
||||
else:
|
||||
search_list.remove('package/@name')
|
||||
search_list.append('@name')
|
||||
|
||||
result = search(conf.config['apiurl'], set(search_list), kind, search_term, opts.verbose, opts.exact, opts.repos_baseurl, role_filter, extra_limiter)
|
||||
|
||||
if not result:
|
||||
if not len(results):
|
||||
print 'No matches found for \'%s\' in %ss' % (role_filter or search_term, kind)
|
||||
continue
|
||||
|
||||
# unfortunately, there is no sort support in the api.
|
||||
# we can do it here. Maybe it would be better done in osc.core.search() already.
|
||||
if kind in ['project']:
|
||||
result.sort()
|
||||
if kind in ['package']:
|
||||
# hm... results is a flat list
|
||||
## FIXME: this messes up with se -v .
|
||||
l = [ (j, i) for i, j in zip(*[iter(result)]*2) ]
|
||||
l.sort()
|
||||
result = []
|
||||
##
|
||||
## search used to report the table as
|
||||
## 'package project', I see no reason for having package before project.
|
||||
## But it definitly hinders copy-paste.
|
||||
## Changed to more normal 'project package' ordering. 2009-10-05, jw
|
||||
##
|
||||
for j, i in l:
|
||||
result.extend([j, i])
|
||||
|
||||
# construct a sorted, flat list
|
||||
results.sort(lambda x, y: cmp(x[0], y[0]))
|
||||
new = []
|
||||
for i in results:
|
||||
new.extend(i)
|
||||
results = new
|
||||
headline = []
|
||||
if kind == 'package':
|
||||
headline = [ '# Project', '# Package' ]
|
||||
else:
|
||||
@ -4197,10 +4193,10 @@ Please submit there instead, or use --nodevelproject to force direct submission.
|
||||
if opts.repos_baseurl:
|
||||
headline.append('# URL')
|
||||
if not opts.csv:
|
||||
if len(search_for) > 1:
|
||||
if len(what.keys()) > 1:
|
||||
print '#' * 68
|
||||
print 'matches for \'%s\' in %ss:\n' % (role_filter or search_term, kind)
|
||||
for row in build_table(len(headline), result, headline, 2, csv = opts.csv):
|
||||
for row in build_table(len(headline), results, headline, 2, csv = opts.csv):
|
||||
print row
|
||||
|
||||
|
||||
|
106
osc/core.py
106
osc/core.py
@ -4058,26 +4058,6 @@ def checkRevision(prj, pac, revision, apiurl=None):
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
def build_xpath_predicate(search_list, search_term, exact_matches, extra_limiter):
|
||||
"""
|
||||
Builds and returns a xpath predicate
|
||||
"""
|
||||
|
||||
predicate = ['[']
|
||||
for i, elem in enumerate(search_list):
|
||||
predicate.append('(')
|
||||
if i > 0 and i < len(search_list):
|
||||
predicate.append(' or ')
|
||||
if exact_matches:
|
||||
predicate.append('%s=\'%s\'' % (elem, search_term))
|
||||
else:
|
||||
predicate.append('contains(%s, \'%s\')' % (elem, search_term))
|
||||
if extra_limiter:
|
||||
predicate.append(' and %s' % (extra_limiter))
|
||||
predicate.append(')')
|
||||
predicate.append(']')
|
||||
return predicate
|
||||
|
||||
def build_table(col_num, data = [], headline = [], width=1, csv = False):
|
||||
"""
|
||||
This method builds a simple table.
|
||||
@ -4126,53 +4106,53 @@ def build_table(col_num, data = [], headline = [], width=1, csv = False):
|
||||
separator = ''
|
||||
return [separator.join(row) for row in table]
|
||||
|
||||
def search(apiurl, search_list, kind, search_term, verbose = False, exact_matches = False, repos_baseurl = False, role_filter = None, extra_limiter = None):
|
||||
def xpath_join(expr, new_expr, op='or', inner=False):
|
||||
"""
|
||||
Perform a search for 'search_term'. A list which contains the
|
||||
results will be returned on success otherwise 'None'. If 'verbose' is true
|
||||
and the title-tag-text (<title>TEXT</title>) is longer than 60 chars it'll we
|
||||
truncated.
|
||||
Join two xpath expressions. If inner is False expr will
|
||||
be surrounded with parentheses (unless it's not already
|
||||
surrounded).
|
||||
"""
|
||||
|
||||
if role_filter:
|
||||
role_filter = role_filter.split(':')
|
||||
|
||||
predicate = build_xpath_predicate(search_list, search_term, exact_matches, extra_limiter)
|
||||
u = makeurl(apiurl, ['search', kind], ['match=%s' % quote_plus(''.join(predicate))])
|
||||
f = http_GET(u)
|
||||
root = ET.parse(f).getroot()
|
||||
result = []
|
||||
for node in root.findall(kind):
|
||||
if role_filter:
|
||||
skip = 1
|
||||
for p in node.findall('person'):
|
||||
if p.get('userid') == role_filter[0] and p.get('role') == role_filter[1]:
|
||||
skip = 0
|
||||
if skip:
|
||||
if not expr:
|
||||
return new_expr
|
||||
# NOTE: this is NO syntax check etc. (e.g. if a literal contains a '(' or ')'
|
||||
# the check might fail and expr will be surrounded with parentheses or NOT)
|
||||
parentheses = not inner
|
||||
if not inner and expr.startswith('(') and expr.endswith(')'):
|
||||
parentheses = False
|
||||
braces = [i for i in expr if i == '(' or i == ')']
|
||||
closed = 0
|
||||
while len(braces):
|
||||
if braces.pop() == ')':
|
||||
closed += 1
|
||||
continue
|
||||
else:
|
||||
closed += -1
|
||||
while len(braces):
|
||||
if braces.pop() == '(':
|
||||
closed += -1
|
||||
else:
|
||||
closed += 1
|
||||
if closed != 0:
|
||||
parentheses = True
|
||||
break
|
||||
if parentheses:
|
||||
expr = '(%s)' % expr
|
||||
return '%s %s %s' % (expr, op, new_expr)
|
||||
|
||||
# TODO: clarify if we need to check if node.get() returns 'None'.
|
||||
# If it returns 'None' something is broken anyway...
|
||||
if kind == 'package':
|
||||
project = node.get('project')
|
||||
package = node.get('name')
|
||||
result.append(package)
|
||||
else:
|
||||
project = node.get('name')
|
||||
result.append(project)
|
||||
if verbose:
|
||||
title = node.findtext('title').strip()
|
||||
if len(title) > 60:
|
||||
title = title[:61] + '...'
|
||||
result.append(title)
|
||||
if repos_baseurl:
|
||||
# FIXME: no hardcoded URL of instance
|
||||
result.append('http://download.opensuse.org/repositories/%s/' % project.replace(':', ':/'))
|
||||
if result:
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
def search(apiurl, **kwargs):
|
||||
"""
|
||||
Perform a search request. The requests are constructed as follows:
|
||||
kwargs = {'kind1' => xpath1, 'kind2' => xpath2, ..., 'kindN' => xpathN}
|
||||
GET /search/kind1?match=xpath1
|
||||
...
|
||||
GET /search/kindN?match=xpathN
|
||||
"""
|
||||
res = {}
|
||||
for urlpath, xpath in kwargs.iteritems():
|
||||
u = makeurl(apiurl, ['search', urlpath], ['match=%s' % quote_plus(xpath)])
|
||||
f = http_GET(u)
|
||||
res[urlpath] = ET.parse(f).getroot()
|
||||
return res
|
||||
|
||||
def set_link_rev(apiurl, project, package, revision = None):
|
||||
url = makeurl(apiurl, ['source', project, package, '_link'])
|
||||
|
Loading…
Reference in New Issue
Block a user