James E. Blair 0189352614 Fix searching for labels with self
We were not interpreting the 'self' username when searching labels.
Also, we were not permitting the shortened version where ',self'
is used rather than ',user=self'.  This should correct both.

Change-Id: Ifec06ca6ee12b806193c382d3123b95e534e092d
Story: 2000100
2015-01-12 11:46:55 +13:00

302 lines
12 KiB
Python

# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import re
import ply.yacc as yacc
from sqlalchemy.sql.expression import and_, or_, not_, exists, select
import gertty.db
import gertty.search
from tokenizer import tokens
def SearchParser():
def p_terms(p):
'''expression : list_expr
| paren_expr
| boolean_expr
| negative_expr
| term'''
p[0] = p[1]
def p_list_expr(p):
'''list_expr : expression expression'''
p[0] = and_(p[1], p[2])
def p_paren_expr(p):
'''paren_expr : LPAREN expression RPAREN'''
p[0] = p[2]
def p_boolean_expr(p):
'''boolean_expr : expression AND expression
| expression OR expression'''
if p[2] == 'and':
p[0] = and_(p[1], p[3])
elif p[2] == 'or':
p[0] = or_(p[1], p[3])
else:
raise SyntaxError()
def p_negative_expr(p):
'''negative_expr : NOT expression
| NEG expression'''
p[0] = not_(p[2])
def p_term(p):
'''term : age_term
| change_term
| owner_term
| reviewer_term
| commit_term
| project_term
| project_key_term
| branch_term
| topic_term
| ref_term
| label_term
| message_term
| comment_term
| has_term
| is_term
| status_term
| op_term'''
p[0] = p[1]
def p_string(p):
'''string : SSTRING
| DSTRING
| USTRING'''
p[0] = p[1]
def p_age_unit(p):
'''age_unit : SECONDS
| MINUTES
| HOURS
| DAYS
| WEEKS
| MONTHS
| YEARS'''
p[0] = p[1]
def p_age_term(p):
'''age_term : OP_AGE NUMBER age_unit'''
now = datetime.datetime.utcnow()
delta = p[1]
unit = p[2]
if unit == 'minutes':
delta = delta * 60
elif unit == 'hours':
delta = delta * 60 * 60
elif unit == 'days':
delta = delta * 60 * 60 * 60
elif unit == 'weeks':
delta = delta * 60 * 60 * 60 * 7
elif unit == 'months':
delta = delta * 60 * 60 * 60 * 30
elif unit == 'years':
delta = delta * 60 * 60 * 60 * 365
p[0] = gertty.db.change_table.c.updated < (now-delta)
def p_change_term(p):
'''change_term : OP_CHANGE CHANGE_ID
| OP_CHANGE NUMBER'''
if type(p[2]) == int:
p[0] = gertty.db.change_table.c.number == p[2]
else:
p[0] = gertty.db.change_table.c.change_id == p[2]
def p_owner_term(p):
'''owner_term : OP_OWNER string'''
if p[2] == 'self':
username = p.parser.username
p[0] = and_(gertty.db.change_table.c.account_key == gertty.db.account_table.c.key,
gertty.db.account_table.c.username == username)
else:
p[0] = and_(gertty.db.change_table.c.account_key == gertty.db.account_table.c.key,
or_(gertty.db.account_table.c.username == p[2],
gertty.db.account_table.c.email == p[2],
gertty.db.account_table.c.name == p[2]))
def p_reviewer_term(p):
'''reviewer_term : OP_REVIEWER string'''
filters = []
filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.approval_table.c.account_key == gertty.db.account_table.c.key)
if p[2] == 'self':
username = p.parser.username
filters.append(gertty.db.account_table.c.username == username)
else:
filters.append(or_(gertty.db.account_table.c.username == p[2],
gertty.db.account_table.c.email == p[2],
gertty.db.account_table.c.name == p[2]))
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
def p_commit_term(p):
'''commit_term : OP_COMMIT string'''
filters = []
filters.append(gertty.db.revision_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.revision_table.c.commit == p[2])
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
def p_project_term(p):
'''project_term : OP_PROJECT string'''
#TODO: support regex
p[0] = gertty.db.project_table.c.name == p[2]
def p_project_key_term(p):
'''project_key_term : OP_PROJECT_KEY NUMBER'''
p[0] = gertty.db.change_table.c.project_key == p[2]
def p_branch_term(p):
'''branch_term : OP_BRANCH string'''
#TODO: support regex
p[0] = gertty.db.change_table.c.branch == p[2]
def p_topic_term(p):
'''topic_term : OP_TOPIC string'''
#TODO: support regex
p[0] = gertty.db.change_table.c.topic == p[2]
def p_ref_term(p):
'''ref_term : OP_REF string'''
#TODO: support regex
p[0] = gertty.db.change_table.c.branch == p[2][len('refs/heads/'):]
label_re = re.compile(r'(?P<label>[a-zA-Z0-9_-]+([a-zA-Z]|((?<![-+])[0-9])))'
r'(?P<operator>[<>]?=?)(?P<value>[-+]?[0-9]+)'
r'($|,(user=)?(?P<user>\S+))')
def p_label_term(p):
'''label_term : OP_LABEL string'''
args = label_re.match(p[2])
label = args.group('label')
op = args.group('operator') or '='
value = int(args.group('value'))
user = args.group('user')
filters = []
filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.approval_table.c.category == label)
if op == '=':
filters.append(gertty.db.approval_table.c.value == value)
elif op == '>=':
filters.append(gertty.db.approval_table.c.value >= value)
elif op == '<=':
filters.append(gertty.db.approval_table.c.value <= value)
if user is not None:
filters.append(gertty.db.approval_table.c.account_key == gertty.db.account_table.c.key)
if user == 'self':
filters.append(gertty.db.account_table.c.username == p.parser.username)
else:
filters.append(
or_(gertty.db.account_table.c.username == user,
gertty.db.account_table.c.email == user,
gertty.db.account_table.c.name == user))
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
def p_message_term(p):
'''message_term : OP_MESSAGE string'''
filters = []
filters.append(gertty.db.revision_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.revision_table.c.message == p[2])
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
def p_comment_term(p):
'''comment_term : OP_COMMENT string'''
filters = []
filters.append(gertty.db.revision_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.revision_table.c.message == p[2])
revision_select = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
filters = []
filters.append(gertty.db.revision_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.comment_table.c.revision_key == gertty.db.revision_table.c.key)
filters.append(gertty.db.comment_table.c.message == p[2])
comment_select = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = or_(gertty.db.change_table.c.key.in_(comment_select),
gertty.db.change_table.c.key.in_(revision_select))
def p_has_term(p):
'''has_term : OP_HAS string'''
#TODO: implement star
if p[2] == 'draft':
filters = []
filters.append(gertty.db.revision_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.message_table.c.revision_key == gertty.db.revision_table.c.key)
filters.append(gertty.db.message_table.c.draft == True)
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
else:
raise gertty.search.SearchSyntaxError('Syntax error: has:%s is not supported' % p[2])
def p_is_term(p):
'''is_term : OP_IS string'''
#TODO: implement starred, watched, owner, reviewer, draft
username = p.parser.username
if p[2] == 'reviewed':
filters = []
filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.approval_table.c.value != 0)
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
elif p[2] == 'open':
p[0] = gertty.db.change_table.c.status.notin_(['MERGED', 'ABANDONED'])
elif p[2] == 'closed':
p[0] = gertty.db.change_table.c.status.in_(['MERGED', 'ABANDONED'])
elif p[2] == 'submitted':
p[0] = gertty.db.change_table.c.status == 'SUBMITTED'
elif p[2] == 'merged':
p[0] = gertty.db.change_table.c.status == 'MERGED'
elif p[2] == 'abandoned':
p[0] = gertty.db.change_table.c.status == 'ABANDONED'
elif p[2] == 'owner':
p[0] = and_(gertty.db.change_table.c.account_key == gertty.db.account_table.c.key,
gertty.db.account_table.c.username == username)
elif p[2] == 'reviewer':
filters = []
filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key)
filters.append(gertty.db.approval_table.c.account_key == gertty.db.account_table.c.key)
filters.append(gertty.db.account_table.c.username == username)
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
p[0] = gertty.db.change_table.c.key.in_(s)
else:
raise gertty.search.SearchSyntaxError('Syntax error: has:%s is not supported' % p[2])
def p_status_term(p):
'''status_term : OP_STATUS string'''
if p[2] == 'open':
p[0] = gertty.db.change_table.c.status.notin_(['MERGED', 'ABANDONED'])
elif p[2] == 'closed':
p[0] = gertty.db.change_table.c.status.in_(['MERGED', 'ABANDONED'])
else:
p[0] = gertty.db.change_table.c.status == p[2].upper()
def p_op_term(p):
'op_term : OP'
raise SyntaxError()
def p_error(p):
if p:
raise gertty.search.SearchSyntaxError('Syntax error at "%s" in search string "%s" (col %s)' % (
p.lexer.lexdata[p.lexpos:], p.lexer.lexdata, p.lexpos))
else:
raise gertty.search.SearchSyntaxError('Syntax error: EOF in search string')
return yacc.yacc(debug=0, write_tables=0)