1
0
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:
Marcus Huewe 2010-03-10 23:36:09 +01:00
parent bc1322803d
commit c6c9506640
2 changed files with 119 additions and 143 deletions

View File

@ -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

View File

@ -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'])