From 1cb93e23779127fd0d1dc02b569d083d20a24739 Mon Sep 17 00:00:00 2001 From: Brian Moe <brian.moe@ligo.org> Date: Wed, 19 Jun 2013 14:59:52 -0500 Subject: [PATCH] Query on event attributes. #933 Closes https://bugs.ligo.org/redmine/issues/933 --- gracedb/forms.py | 5 ++- gracedb/query.py | 62 +++++++++++++++++--------- templates/gracedb/query_help_frag.html | 10 +++++ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/gracedb/forms.py b/gracedb/forms.py index e3ac69bbe..2e5497ae3 100644 --- a/gracedb/forms.py +++ b/gracedb/forms.py @@ -6,7 +6,8 @@ from models import Event, Group, Label from django.contrib.auth.models import User from django.core.exceptions import FieldError -from query import parseQuery, ParseException +from query import parseQuery +from pyparsing import ParseException htmlEntityStar = "★" htmlEntityRightPointingHand = "☞" @@ -31,7 +32,7 @@ class GraceQueryField(forms.CharField): raise forms.ValidationError(mark_safe(err)) except Exception, e: # What could this be and how can we handle it better? XXX - raise forms.ValidationError(str(e)) + raise forms.ValidationError(str(e)+str(type(e))) class SimpleSearchForm(forms.Form): query = GraceQueryField(required=False, widget=forms.TextInput(attrs={'size':60})) diff --git a/gracedb/query.py b/gracedb/query.py index 60f8402f0..7abcf5f15 100644 --- a/gracedb/query.py +++ b/gracedb/query.py @@ -23,7 +23,7 @@ from pyparsing import \ Word, nums, Literal, CaselessLiteral, delimitedList, Suppress, QuotedString, \ Keyword, Combine, Or, Optional, OneOrMore, alphas, Regex, \ opAssoc, operatorPrecedence, oneOf, \ - stringStart, stringEnd, ParseException + stringStart, stringEnd def maybeRange(name, dbname=None): dbname = dbname or name @@ -170,41 +170,59 @@ labelQ.setParseAction(lambda toks: ("label", toks[0])) ########################### # Query on event attributes -attrNumExprOperators = { "<" : "__lt", - "<=": "__lte", - "=" : "", - ">" : "__gt", - ">=": "__gte", - } +lparen = Suppress('(') +rparen = Suppress(')') -attrNumExprLhs = Keyword("far") | Keyword("gpstime") +exprOperators = { "<" : "__lt", + "<=": "__lte", + "=" : "", + ">" : "__gt", + ">=": "__gte", + } + +tableTranslations = { + 'si': 'singleinspiral', + 'ci': 'coincinspiralevent', + 'mb': 'multiburstevent', + 'coincinspiral': 'coincinspiralevent', + 'multiburst': 'multiburstevent', + } + +def buildDjangoQueryField(toks): + toks = [name.lower() for name in toks] + return "__".join([tableTranslations.get(name, name) for name in toks]) exponent = Combine(Word("Ee") + Optional(Word("+-"))+Word(nums)) -afloat = Combine( Word(nums) + Optional(Combine(Literal(".") + Word(nums))) ) -attrNumExprRhs = Combine( Optional(Word("+-")) + afloat + Optional(exponent) ) -attrNumExprRhs.setParseAction(lambda toks: float(toks[0])) +afloat = Combine( Word(nums) + \ + Optional(Combine(Literal(".") + Word(nums))) ) + \ + Optional(exponent) +afloat.setParseAction(lambda toks: float(toks[0])) + +lhs = delimitedList(Word(alphas+'_'), '.') +lhs.setParseAction(buildDjangoQueryField) -attrNumExprOp = Or(map(Literal, attrNumExprOperators.keys())) -attrNumExprOp.setParseAction(lambda toks: attrNumExprOperators[toks[0]]) +rhs = afloat | QuotedString('"') -attrNumExpr = attrNumExprLhs + attrNumExprOp + attrNumExprRhs -attrNumExpr.setParseAction(lambda toks: Q(**{toks[0]+toks[1]: toks[2]})) +op = Or(map(Literal, exprOperators.keys())) +op.setParseAction(lambda toks: exprOperators[toks[0]]) -#attrIfoExpr = Keyword("ifos").suppress() + Literal("=").suppress() + Word("LVH12,") -#attrIfoExpr.setParseAction(lambda toks: Q(instruments=toks[0])) +simpleTerm = lhs + op + rhs +simpleTerm.setParseAction(lambda toks: Q(**{toks[0]+toks[1]: toks[2]})) -#attrExpr = attrIfoExpr | attrNumExpr -attrExpr = attrNumExpr +rangeTerm = lhs + Suppress('in') + rhs + Suppress(",") + rhs +rangeTerm.setParseAction(lambda toks: Q(**{toks[0]+"__range": toks[1:]})) -attrExprs = operatorPrecedence(attrExpr, +term = simpleTerm | rangeTerm + +attrExpressions = operatorPrecedence(term, [(minusop, 1, opAssoc.RIGHT, lambda a,b,toks: ~toks[0][0]), (orop, 2, opAssoc.LEFT, lambda a,b,toks: reduce(Q.__or__, toks[0].asList(), Q())), (andop, 2, opAssoc.LEFT, lambda a,b,toks: reduce(Q.__and__, toks[0].asList(), Q())), ]).setParseAction(lambda toks: toks[0]) - -attributeQ = Optional(Suppress(Keyword('attr:'))) + attrExprs.copy() +#attributeQ = lparen + attrExpressions + rparen +attributeQ = attrExpressions.copy() attributeQ.setParseAction(lambda toks: ("attr", toks[0])) diff --git a/templates/gracedb/query_help_frag.html b/templates/gracedb/query_help_frag.html index abe924f25..ae2b8b9a7 100644 --- a/templates/gracedb/query_help_frag.html +++ b/templates/gracedb/query_help_frag.html @@ -1,5 +1,15 @@ <div id="hints" style="display: none;"> + <h4>By Event Attributes</h4> + Relational and range queries can be made on event attributes. + <ul> + <li><code>instruments = "H1,L1,V1" & far < 1e-7</code></li> + <li><code>singleinspiral.mchirp >= 1.6 & eff_distance in 40.5,55 </code></li> + <li><code>(si.channel = "DMT-STRAIN" | si.channel = "DMT-PAIN") & si.snr < 5</code></li> + <li><code>mb.snr in 1,3 & mb.central_freq > 1000</code></li> + </ul> + Attributes in the common event object (eg gpstime, far, instruments) do not need qualifiers. Attributes specific to inspiral or burst events, for example, require qualification. Abbreviations are available: <code>si</code> for singleinspiral, <code>ci</code> for coincinspiral and <code>mb</code> for multiburst. + <h4>By GPS Time</h4> Specifiy an exact GPS time, or a range. Integers will be assumed to be GPS times, making the <code>gpstime:</code> -- GitLab