Logo Search packages:      
Sourcecode: jppy version File versions  Download package

queryLanguage.py

from pyparsing import Word,\
    Optional, Combine, Group, alphas, nums, alphanums,\
    ParseException, Forward, oneOf, quotedString, ZeroOrMore,\
    restOfLine, Keyword, removeQuotes, StringEnd, Combine
from sets import Set
import types
import logging
import mx.DateTime

def parseDate(query, offset, results):
    token = results[0]
    if token == "today":
        return mx.DateTime.today()
    elif token == "tomorrow":
        return mx.DateTime.today() + mx.DateTime.oneDay
    elif token == "yesterday":
        return mx.DateTime.today() - mx.DateTime.oneDay
    else:
        return mx.DateTime.strptime(token, "%Y-%m-%d")

def makeGrammer(columns=None):
    and_ = Keyword("and", caseless=True)
    not_ = Keyword("not", caseless=True)    
    or_  = Keyword("or", caseless=True)

    if columns:
        ident = Forward()
        for c in columns:
            ident = ident | Keyword(c, caseless = True).setResultsName("column")
    else:
        ident = Word( alphanums ).setResultsName("column")

    strop  = oneOf("= != startswith endswith contains", caseless=True).setResultsName("op")
    intop  = oneOf("= != > < >= <=", caseless=True).setResultsName("op")
    dateop = oneOf("= != > < >= <=", caseless=True).setResultsName("op")    

    truthValue  = (Keyword('true', caseless=True) | Keyword('false', caseless=True)
                   ).setResultsName("value")
    intValue    = Word(nums).setResultsName("value")    
    stringValue = quotedString.setResultsName("value").setParseAction(removeQuotes)
    dateValue   = (Combine( Word(nums,exact=4) +
                            "-" +
                            Word(nums,exact=2) +
                            "-" +
                            Word(nums,exact=2)) | oneOf('today tomorrow yesterday', caseless=True)
                   ).setResultsName("value").setParseAction(parseDate)  

    expression = Forward()
    condition = Group(ident + strop + stringValue) | Group(ident + dateop + dateValue) | Group(ident + intop + (truthValue | intValue))
    expression << condition + ZeroOrMore( ( and_ | or_ ) + ZeroOrMore( not_ ) + condition ) + StringEnd()
    return expression

generalgrammer = makeGrammer()

def parseQuery(query, grammer=None):
    if not grammer:
        grammer = generalgrammer
    return grammer.parseString(query)

def runQuery(conditions, rows):
    logging.debug("Query is %s", conditions)
    currentSet = Set()
    combiner = "or"
    invert_combiner = False
    for condition in conditions:
        logging.debug("Condition is %s", condition)
        if isinstance(condition, types.StringType):
            if condition == "not":
                invert_combiner = invert_combiner != True
            else:
                combiner = condition
            logging.debug("Combiner now set to %s (invert %s)", combiner, invert_combiner)
            continue
        found = []
        for row in rows:
            operator = condition['op']
            try:
                queryTerm = condition['value']
                rowTerm   = row[condition['column']]
            except KeyError, e:
                logging.warn("Unknown key %s in record %s", e, row)
                return str(e)

            if isinstance(rowTerm,types.UnicodeType):
                queryTerm = queryTerm.upper()
                rowTerm   = rowTerm.upper()
                
                if operator == "=" and rowTerm == queryTerm:
                    found.append(row)
                elif operator == "!=" and rowTerm != queryTerm:
                    found.append(row)
                elif operator == "startswith" and rowTerm.startswith(queryTerm):
                    found.append(row)
                elif operator == "endswith" and rowTerm.endswith(queryTerm):
                    found.append(row)
                elif operator == "contains" and queryTerm in rowTerm:
                    found.append(row)
            elif isinstance(rowTerm, mx.DateTime.DateTimeType):
                # only care about whole days
                rowTerm = mx.DateTime.DateTimeFromAbsDays(int(rowTerm.absdays))
                
                if operator == "=" and rowTerm == queryTerm:
                    found.append(row)
                elif operator == "!=" and rowTerm != queryTerm:
                    found.append(row)
                elif operator == ">" and rowTerm > queryTerm:
                    found.append(row)
                elif operator == "<" and rowTerm < queryTerm:
                    found.append(row)
                elif operator == ">=" and rowTerm >= queryTerm:
                    found.append(row)
                elif operator == "<=" and rowTerm <= queryTerm:
                    found.append(row)
            elif isinstance(rowTerm,types.IntType):
                if queryTerm.upper() == "TRUE":
                    queryTerm = 1
                elif queryTerm.upper() == "FALSE":
                    queryTerm = 0
                else:
                    queryTerm = int(queryTerm)
                if operator == "=" and rowTerm == queryTerm:
                    found.append(row)
                elif operator == "!=" and rowTerm != queryTerm:
                    found.append(row)
                elif operator == ">" and rowTerm > queryTerm:
                    found.append(row)
                elif operator == "<" and rowTerm < queryTerm:
                    found.append(row)
                elif operator == ">=" and rowTerm >= queryTerm:
                    found.append(row)
                elif operator == "<=" and rowTerm <= queryTerm:
                    found.append(row)                
                        
        for f in found:
            logging.debug("Found %s", f)
        logging.debug("Combiner to be used is %s (invert %s)", combiner, invert_combiner)
        if invert_combiner:
            if combiner == "and":
                logging.debug("doing difference with %s", currentSet)
                currentSet = currentSet.difference(Set(found))
            elif combiner == "or":
                logging.debug("doing symmetric_difference with %s", currentSet)
                currentSet = currentSet.symmetric_difference(Set(found))            
        else:
            if combiner == "and":
                logging.debug("doing intersection with %s", currentSet)
                currentSet = currentSet.intersection(Set(found))
            elif combiner == "or":
                logging.debug("doing union with %s", currentSet)
                currentSet = currentSet.union(Set(found))

        invert_combiner = False
        logging.debug("Combiner now reset to %s (invert %s)", combiner, invert_combiner)
                
    return list(currentSet)

if __name__ == "__main__":
    g = makeGrammer(['complete','priority','due'])
    q = parseQuery("priority > 2 and due = tomorrow", g)
    import jppy
    for i in  runQuery(q, jppy.taskList().records()):
        print i, i['priority']

Generated by  Doxygen 1.6.0   Back to index