@@ -79,30 +79,48 @@ def build_filter(self):
79
79
return self .cfg
80
80
81
81
82
- class MissingFilter (object ):
82
+ class MissingOrPopulatedFilter (object ):
83
83
def __init__ (self , column , classification , cfg ):
84
84
self .column = column
85
85
self .classification = classification
86
86
self .cfg = cfg
87
87
88
- def handle_missing (self , fltr ):
89
- if self .cfg is None or not self .cfg .get ("missing" , False ):
88
+ def handle_missing_or_populated (self , fltr ):
89
+ if self .cfg is None or (
90
+ not self .cfg .get ("missing" , False ) and not self .cfg .get ("populated" , False )
91
+ ):
90
92
return fltr
91
- return {
92
- "missing" : True ,
93
- "meta" : {
94
- "column" : self .column ,
95
- "classification" : self .classification ,
96
- "type" : self .cfg ["type" ],
97
- },
98
- "query" : "{col}.isnull()" .format (col = build_col_key (self .column )),
99
- }
100
-
101
- def update_missing_query_builder (self , query_builder , fltr = None ):
102
- if self .cfg is None or not self .cfg .get ("missing" , False ):
93
+ if self .cfg .get ("missing" , False ):
94
+ return {
95
+ "missing" : True ,
96
+ "meta" : {
97
+ "column" : self .column ,
98
+ "classification" : self .classification ,
99
+ "type" : self .cfg ["type" ],
100
+ },
101
+ "query" : "{col}.isnull()" .format (col = build_col_key (self .column )),
102
+ }
103
+ if self .cfg .get ("populated" , False ):
104
+ return {
105
+ "populated" : True ,
106
+ "meta" : {
107
+ "column" : self .column ,
108
+ "classification" : self .classification ,
109
+ "type" : self .cfg ["type" ],
110
+ },
111
+ "query" : "~{col}.isnull()" .format (col = build_col_key (self .column )),
112
+ }
113
+
114
+ def update_missing_or_populated_query_builder (self , query_builder , fltr = None ):
115
+ if self .cfg is None or (
116
+ not self .cfg .get ("missing" , False ) and not self .cfg .get ("populated" , False )
117
+ ):
103
118
return fltr
104
119
# TODO: how to handle scenarios where QueryBuilder doesn't support functionality so do it manually
105
- return query_builder [self .column ] != query_builder [self .column ]
120
+ if self .cfg .get ("missing" , False ):
121
+ return query_builder [self .column ] != query_builder [self .column ]
122
+ if self .cfg .get ("populated" , False ):
123
+ return query_builder [self .column ] == query_builder [self .column ]
106
124
107
125
108
126
def handle_ne (query , operand ):
@@ -117,19 +135,19 @@ def handle_query_builder_ne(query, operand):
117
135
return query
118
136
119
137
120
- class StringFilter (MissingFilter ):
138
+ class StringFilter (MissingOrPopulatedFilter ):
121
139
def __init__ (self , column , classification , cfg ):
122
140
super (StringFilter , self ).__init__ (column , classification , cfg )
123
141
124
142
def build_filter (self ):
125
143
if self .cfg is None :
126
- return super (StringFilter , self ).handle_missing (None )
144
+ return super (StringFilter , self ).handle_missing_or_populated (None )
127
145
128
146
action = self .cfg .get ("action" , "equals" )
129
147
if action == "equals" and not len (self .cfg .get ("value" , [])):
130
- return super (StringFilter , self ).handle_missing (None )
148
+ return super (StringFilter , self ).handle_missing_or_populated (None )
131
149
elif action != "equals" and not self .cfg .get ("raw" ):
132
- return super (StringFilter , self ).handle_missing (None )
150
+ return super (StringFilter , self ).handle_missing_or_populated (None )
133
151
134
152
state = self .cfg .get ("value" , [])
135
153
case_sensitive = self .cfg .get ("caseSensitive" , False )
@@ -191,17 +209,23 @@ def build_filter(self):
191
209
build_col_key (self .column ), raw
192
210
)
193
211
fltr ["query" ] = handle_ne (fltr ["query" ], operand )
194
- return super (StringFilter , self ).handle_missing (fltr )
212
+ return super (StringFilter , self ).handle_missing_or_populated (fltr )
195
213
196
214
def update_query_builder (self , query_builder ):
197
215
if self .cfg is None :
198
- return super (StringFilter , self ).update_missing_query_builder (query_builder )
216
+ return super (StringFilter , self ).update_missing_or_populated_query_builder (
217
+ query_builder
218
+ )
199
219
200
220
action = self .cfg .get ("action" , "equals" )
201
221
if action == "equals" and not len (self .cfg .get ("value" , [])):
202
- return super (StringFilter , self ).update_missing_query_builder (query_builder )
222
+ return super (StringFilter , self ).update_missing_or_populated_query_builder (
223
+ query_builder
224
+ )
203
225
elif action != "equals" and not self .cfg .get ("raw" ):
204
- return super (StringFilter , self ).update_missing_query_builder (query_builder )
226
+ return super (StringFilter , self ).update_missing_or_populated_query_builder (
227
+ query_builder
228
+ )
205
229
206
230
state = self .cfg .get ("value" , [])
207
231
case_sensitive = self .cfg .get ("caseSensitive" , False )
@@ -232,18 +256,18 @@ def update_query_builder(self, query_builder):
232
256
elif action == "length" :
233
257
# Not supported by QueryBuilder
234
258
pass
235
- return super (StringFilter , self ).update_missing_query_builder (
259
+ return super (StringFilter , self ).update_missing_or_populated_query_builder (
236
260
query_builder , fltr .get ("query" )
237
261
)
238
262
239
263
240
- class NumericFilter (MissingFilter ):
264
+ class NumericFilter (MissingOrPopulatedFilter ):
241
265
def __init__ (self , column , classification , cfg ):
242
266
super (NumericFilter , self ).__init__ (column , classification , cfg )
243
267
244
268
def build_filter (self ):
245
269
if self .cfg is None :
246
- return super (NumericFilter , self ).handle_missing (None )
270
+ return super (NumericFilter , self ).handle_missing_or_populated (None )
247
271
cfg_val , cfg_operand , cfg_min , cfg_max = (
248
272
self .cfg .get (p ) for p in ["value" , "operand" , "min" , "max" ]
249
273
)
@@ -259,7 +283,7 @@ def build_filter(self):
259
283
if cfg_operand in ["=" , "ne" ]:
260
284
state = make_list (cfg_val or [])
261
285
if not len (state ):
262
- return super (NumericFilter , self ).handle_missing (None )
286
+ return super (NumericFilter , self ).handle_missing_or_populated (None )
263
287
fltr = dict (value = cfg_val , ** base_fltr )
264
288
if len (state ) == 1 :
265
289
fltr ["query" ] = "{} {} {}" .format (
@@ -273,18 +297,18 @@ def build_filter(self):
273
297
"in" if cfg_operand == "=" else "not in" ,
274
298
", " .join (map (str , state )),
275
299
)
276
- return super (NumericFilter , self ).handle_missing (fltr )
300
+ return super (NumericFilter , self ).handle_missing_or_populated (fltr )
277
301
if cfg_operand in ["<" , ">" , "<=" , ">=" ]:
278
302
if cfg_val is None :
279
- return super (NumericFilter , self ).handle_missing (None )
303
+ return super (NumericFilter , self ).handle_missing_or_populated (None )
280
304
fltr = dict (
281
305
value = cfg_val ,
282
306
query = "{} {} {}" .format (
283
307
build_col_key (self .column ), cfg_operand , cfg_val
284
308
),
285
309
** base_fltr
286
310
)
287
- return super (NumericFilter , self ).handle_missing (fltr )
311
+ return super (NumericFilter , self ).handle_missing_or_populated (fltr )
288
312
if cfg_operand in ["[]" , "()" ]:
289
313
fltr = dict (** base_fltr )
290
314
queries = []
@@ -309,14 +333,14 @@ def build_filter(self):
309
333
if len (queries ) == 2 and cfg_max == cfg_min :
310
334
queries = ["{} == {}" .format (build_col_key (self .column ), cfg_max )]
311
335
if not len (queries ):
312
- return super (NumericFilter , self ).handle_missing (None )
336
+ return super (NumericFilter , self ).handle_missing_or_populated (None )
313
337
fltr ["query" ] = " and " .join (queries )
314
- return super (NumericFilter , self ).handle_missing (fltr )
315
- return super (NumericFilter , self ).handle_missing (None )
338
+ return super (NumericFilter , self ).handle_missing_or_populated (fltr )
339
+ return super (NumericFilter , self ).handle_missing_or_populated (None )
316
340
317
341
def update_query_builder (self , query_builder ):
318
342
if self .cfg is None :
319
- return super (NumericFilter , self ).update_missing_query_builder (
343
+ return super (NumericFilter , self ).update_missing_or_populated_query_builder (
320
344
query_builder
321
345
)
322
346
cfg_val , cfg_operand , cfg_min , cfg_max = (
@@ -328,9 +352,9 @@ def update_query_builder(self, query_builder):
328
352
if self .cfg .get ("meta" , {}).get ("type" ) == "float" :
329
353
state = [np .float64 (val ) for val in state ]
330
354
if not len (state ):
331
- return super (NumericFilter , self ). update_missing_query_builder (
332
- query_builder
333
- )
355
+ return super (
356
+ NumericFilter , self
357
+ ). update_missing_or_populated_query_builder ( query_builder )
334
358
fltr = dict (value = cfg_val , operand = cfg_operand )
335
359
if len (state ) == 1 :
336
360
fltr ["query" ] = handle_query_builder_ne (
@@ -340,14 +364,14 @@ def update_query_builder(self, query_builder):
340
364
fltr ["query" ] = handle_query_builder_ne (
341
365
query_builder [self .column ].isin (state ), cfg_operand
342
366
)
343
- return super (NumericFilter , self ).update_missing_query_builder (
367
+ return super (NumericFilter , self ).update_missing_or_populated_query_builder (
344
368
query_builder , fltr .get ("query" )
345
369
)
346
370
if cfg_operand in ["<" , ">" , "<=" , ">=" ]:
347
371
if cfg_val is None :
348
- return super (NumericFilter , self ). update_missing_query_builder (
349
- query_builder
350
- )
372
+ return super (
373
+ NumericFilter , self
374
+ ). update_missing_or_populated_query_builder ( query_builder )
351
375
fltr = dict (value = cfg_val , operand = cfg_operand )
352
376
if cfg_operand == "<" :
353
377
fltr ["query" ] = query_builder [self .column ] < cfg_val
@@ -357,24 +381,26 @@ def update_query_builder(self, query_builder):
357
381
fltr ["query" ] = query_builder [self .column ] <= cfg_val
358
382
elif cfg_operand == ">=" :
359
383
fltr ["query" ] = query_builder [self .column ] >= cfg_val
360
- return super (NumericFilter , self ).update_missing_query_builder (
384
+ return super (NumericFilter , self ).update_missing_or_populated_query_builder (
361
385
query_builder , fltr .get ("query" )
362
386
)
363
387
if cfg_operand in ["[]" , "()" ]:
364
388
# Not supported by QueryBuilder
365
- return super (NumericFilter , self ).update_missing_query_builder (
389
+ return super (NumericFilter , self ).update_missing_or_populated_query_builder (
366
390
query_builder
367
391
)
368
- return super (NumericFilter , self ).update_missing_query_builder (query_builder )
392
+ return super (NumericFilter , self ).update_missing_or_populated_query_builder (
393
+ query_builder
394
+ )
369
395
370
396
371
- class DateFilter (MissingFilter ):
397
+ class DateFilter (MissingOrPopulatedFilter ):
372
398
def __init__ (self , column , classification , cfg ):
373
399
super (DateFilter , self ).__init__ (column , classification , cfg )
374
400
375
401
def build_filter (self ):
376
402
if self .cfg is None :
377
- return super (DateFilter , self ).handle_missing (None )
403
+ return super (DateFilter , self ).handle_missing_or_populated (None )
378
404
379
405
start , end = (self .cfg .get (p ) for p in ["start" , "end" ])
380
406
fltr = dict (
@@ -394,15 +420,17 @@ def build_filter(self):
394
420
if len (queries ) == 2 and start == end :
395
421
queries = ["{} == '{}'" .format (build_col_key (self .column ), start )]
396
422
if not len (queries ):
397
- return super (DateFilter , self ).handle_missing (None )
423
+ return super (DateFilter , self ).handle_missing_or_populated (None )
398
424
fltr ["query" ] = " and " .join (queries )
399
- return super (DateFilter , self ).handle_missing (fltr )
425
+ return super (DateFilter , self ).handle_missing_or_populated (fltr )
400
426
401
427
def update_query_builder (self , query_builder ):
402
428
# TODO: need to use datetime.datetime and then for equivalence you need to do (col > input - 1) & (col <= input)
403
429
# pd.Timestamp('2023-01-04').to_pydatetime() -> to get datetime.datetime
404
430
if self .cfg is None :
405
- return super (DateFilter , self ).update_missing_query_builder (query_builder )
431
+ return super (DateFilter , self ).update_missing_or_populated_query_builder (
432
+ query_builder
433
+ )
406
434
407
435
start , end = (self .cfg .get (p ) for p in ["start" , "end" ])
408
436
fltr = dict (start = start , end = end )
@@ -426,11 +454,11 @@ def update_query_builder(self, query_builder):
426
454
& (query_builder [self .column ] <= start_end )
427
455
]
428
456
if not len (queries ):
429
- return super (DateFilter , self ).handle_missing (None )
457
+ return super (DateFilter , self ).handle_missing_or_populated (None )
430
458
if len (queries ) == 2 :
431
459
fltr ["query" ] = queries [0 ] & queries [1 ]
432
460
else :
433
461
fltr ["query" ] = queries [0 ]
434
- return super (DateFilter , self ).update_missing_query_builder (
462
+ return super (DateFilter , self ).update_missing_or_populated_query_builder (
435
463
query_builder , fltr .get ("query" )
436
464
)
0 commit comments