portal: added wip for PI validation page
[myslice.git] / manifold / util / predicate.py
1 from types import StringTypes
2
3 from operator import (
4     and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg
5     )
6
7 # Define the inclusion operators
8 class contains(type): pass
9 class included(type): pass
10
11
12 # New modifier: { contains 
13 class Predicate:
14
15     operators = {
16         '=='       : eq,
17         '!='       : ne,
18         '<'        : lt,
19         '<='       : le,
20         '>'        : gt,
21         '>='       : ge,
22         '&&'       : and_,
23         '||'       : or_,
24         'CONTAINS' : contains,
25         'INCLUDED' : included
26     }
27
28     operators_short = {
29         '=' : eq,
30         '~' : ne,
31         '<' : lt,
32         '[' : le,
33         '>' : gt,
34         ']' : ge,
35         '&' : and_,
36         '|' : or_,
37         '}' : contains,
38         '{' : included
39     }
40
41     def __init__(self, *args, **kwargs):
42         """
43         \brief Build a predicate (left, operator, right)
44         \param You can pass:
45             - three args (left, operator, right)
46             - one argument (list or tuple) containing three elements (variable, operator, value)
47             "operator" is a String defined in operators or in operators_short and refers
48                 tMao a binary operation.
49             "left" and "right" refers to a variable/constant involved in the Predicate.
50         """
51         if len(args) == 3:
52             key, op, value = args
53         elif len(args) == 1 and isinstance(args[0], (tuple,list)) and len(args[0]) == 3:
54             key, op, value = args[0]
55         elif len(args) == 1 and isinstance(args[0], Predicate):
56             key, op, value = args[0].get_tuple()
57         else:
58             raise Exception, "Bad initializer for Predicate (args=%r)" % args
59
60         assert not isinstance(value, (frozenset, dict, set)), "Invalid value type (the only valid containers are tuples and lists) (type = %r)" % type(value)
61         if isinstance(value, list):
62             value = tuple(value)
63
64         self.key = key
65         if isinstance(op, StringTypes):
66             op = op.upper()
67         if op in self.operators.keys():
68             self.op = self.operators[op]
69         elif op in self.operators_short.keys():
70             self.op = self.operators_short[op]
71         else:
72             self.op = op
73
74         if isinstance(value, list):
75             self.value = tuple(value)
76         else:
77             self.value = value
78
79     def __str__(self):
80         key, op, value = self.get_str_tuple()
81         if isinstance(value, (tuple, list, set, frozenset)):
82             value = [repr(v) for v in value]
83             value = "[%s]" % ", ".join(value)
84         return "%s %s %r" % (key, op, value) 
85
86     def __repr__(self):
87         return "Predicate<%s %s %r>" % self.get_str_tuple()
88
89     def __hash__(self):
90         return hash(self.get_tuple())
91
92     def __eq__(self, predicate):
93         if not predicate:
94             return False
95         return self.get_tuple() == predicate.get_tuple()
96
97     def get_key(self):
98         return self.key
99     
100     def set_key(self, key):
101         self.key = key
102
103     def get_op(self):
104         return self.op
105
106     def get_value(self):
107         return self.value
108
109     def set_value(self, value):
110         self.value = value
111
112     def get_tuple(self):
113         return (self.key, self.op, self.value)
114
115
116     def get_str_op(self):
117         op_str = [s for s, op in self.operators.iteritems() if op == self.op]
118         return op_str[0]
119
120     def get_str_tuple(self):
121         return (self.key, self.get_str_op(), self.value,)
122
123     def to_list(self):
124         return list(self.get_str_tuple())
125
126     def match(self, dic, ignore_missing=False):
127         if isinstance(self.key, tuple):
128             print "PREDICATE MATCH", self.key
129             print dic
130             print "-----------------------------"
131         
132         # Can we match ?
133         if self.key not in dic:
134             return ignore_missing
135
136         if self.op == eq:
137             if isinstance(self.value, list):
138                 return (dic[self.key] in self.value) # array ?
139             else:
140                 return (dic[self.key] == self.value)
141         elif self.op == ne:
142             if isinstance(self.value, list):
143                 return (dic[self.key] not in self.value) # array ?
144             else:
145                 return (dic[self.key] != self.value) # array ?
146         elif self.op == lt:
147             if isinstance(self.value, StringTypes):
148                 # prefix match
149                 return dic[self.key].startswith('%s.' % self.value)
150             else:
151                 return (dic[self.key] < self.value)
152         elif self.op == le:
153             if isinstance(self.value, StringTypes):
154                 return dic[self.key] == self.value or dic[self.key].startswith('%s.' % self.value)
155             else:
156                 return (dic[self.key] <= self.value)
157         elif self.op == gt:
158             if isinstance(self.value, StringTypes):
159                 # prefix match
160                 return self.value.startswith('%s.' % dic[self.key])
161             else:
162                 return (dic[self.key] > self.value)
163         elif self.op == ge:
164             if isinstance(self.value, StringTypes):
165                 # prefix match
166                 return dic[self.key] == self.value or self.value.startswith('%s.' % dic[self.key])
167             else:
168                 return (dic[self.key] >= self.value)
169         elif self.op == and_:
170             return (dic[self.key] & self.value) # array ?
171         elif self.op == or_:
172             return (dic[self.key] | self.value) # array ?
173         elif self.op == contains:
174             method, subfield = self.key.split('.', 1)
175             return not not [ x for x in dic[method] if x[subfield] == self.value] 
176         elif self.op == included:
177             return dic[self.key] in self.value
178         else:
179             raise Exception, "Unexpected table format: %r" % dic
180
181     def filter(self, dic):
182         """
183         Filter dic according to the current predicate.
184         """
185
186         if '.' in self.key:
187             # users.hrn
188             method, subfield = self.key.split('.', 1)
189             if not method in dic:
190                 return None # XXX
191
192             if isinstance(dic[method], dict):
193                 # We have a 1..1 relationship: apply the same filter to the dict
194                 subpred = Predicate(subfield, self.op, self.value)
195                 match = subpred.match(dic[method])
196                 return dic if match else None
197
198             elif isinstance(dic[method], (list, tuple)):
199                 # 1..N relationships
200                 match = False
201                 if self.op == contains:
202                     return dic if self.match(dic) else None
203                 else:
204                     subpred = Predicate(subfield, self.op, self.value)
205                     dic[method] = subpred.filter(dic[method])
206                     return dic
207             else:
208                 raise Exception, "Unexpected table format: %r", dic
209
210
211         else:
212             # Individual field operations: this could be simplified, since we are now using operators_short !!
213             # XXX match
214             print "current predicate", self
215             print "matching", dic
216             print "----"
217             return dic if self.match(dic) else None
218
219     def get_field_names(self):
220         if isinstance(self.key, (list, tuple, set, frozenset)):
221             return set(self.key)
222         else:
223             return set([self.key])
224
225     def get_value_names(self):
226         if isinstance(self.value, (list, tuple, set, frozenset)):
227             return set(self.value)
228         else:
229             return set([self.value])
230
231     def has_empty_value(self):
232         if isinstance(self.value, (list, tuple, set, frozenset)):
233             return not any(self.value)
234         else:
235             return not self.value