vhatable 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vhatable/filters.py ADDED
@@ -0,0 +1,209 @@
1
+ #! /usr/bin/env python
2
+ """This module contains only filters, they are design to be apply to a row
3
+ of a Vertical or Horizotal table."""
4
+
5
+
6
+ # This file is part of Vhatable cli.
7
+ #
8
+ # Vhatable cli is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # Vhatable cli is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with Vhatable cli. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ # Copyright 2023 Frédéric MARTIN
22
+ #
23
+ # Contributors list :
24
+ #
25
+ # Frédéric MARTIN frederic.martin.fma@gmail.com
26
+ #
27
+
28
+ import datetime
29
+ import logging
30
+ import re
31
+
32
+ from .cell import BaseCell
33
+
34
+
35
+ class Filter:
36
+ """TODO"""
37
+
38
+ def __init__(self, prop, values=None):
39
+ """prop name and value(s)"""
40
+ self.prop = prop
41
+ self.values = values
42
+ classname = self.__class__.__name__.lower()
43
+ self.log = logging.getLogger("vhatablecli.filters." + classname)
44
+
45
+ def is_enable(self):
46
+ """Return true is the filter is enabled and should be applied."""
47
+ if self.values is None:
48
+ return False
49
+ if isinstance(self.values, list):
50
+ # pylint: disable=len-as-condition
51
+ if len(self.values) == 0:
52
+ return False
53
+ return any(i is not None for i in self.values)
54
+ return True
55
+
56
+ def get_val(self, row):
57
+ """return values from the current row that need to be tested again the
58
+ current filter."""
59
+ if isinstance(self.prop, list):
60
+ vals = {}
61
+ for prop in self.prop:
62
+ vals[prop] = self._get_val(row, prop)
63
+ return vals
64
+ return self._get_val(row, self.prop)
65
+
66
+ def _get_val(self, row, prop):
67
+ val = row.get(prop)
68
+ if val is None:
69
+ raise ValueError("missing key : " + self.prop)
70
+ # self.log.debug("type: %s", type(val))
71
+ # self.log.debug("value: '%s'", val)
72
+ return val
73
+
74
+
75
+ class PartialOr(Filter):
76
+ """Get the current property into the current row, and match the result with
77
+ a list of values"""
78
+
79
+ def __init__(self, prop, values, ignorecase=False, match_raw=False):
80
+ super().__init__(prop, values)
81
+ self.match_raw = match_raw
82
+ if self.is_enable():
83
+ if not isinstance(values, list):
84
+ raise ValueError("input values should be a list")
85
+ self.log.debug("values: %s", self.values)
86
+ pattern = r"^.*(" + "|".join(self.values) + ").*$"
87
+ self.log.debug("raw pattern: %s", pattern)
88
+ if ignorecase:
89
+ self.regex = re.compile(pattern, re.IGNORECASE)
90
+ else:
91
+ self.regex = re.compile(pattern)
92
+
93
+ def __str__(self):
94
+ return "PartialOr: " + str(self.values)
95
+
96
+ def __call__(self, row):
97
+ # pylint: disable=too-many-return-statements
98
+ if not self.is_enable():
99
+ return True
100
+ vals = self.get_val(row)
101
+ if isinstance(vals, dict):
102
+ for val in list(vals.values()):
103
+ return self.__match(val)
104
+ else:
105
+ return self.__match(vals)
106
+
107
+ def __match(self, val):
108
+ if isinstance(val, str):
109
+ if self.regex.match(val):
110
+ return True
111
+ elif isinstance(val, BaseCell):
112
+ self.log.debug("cell type: %s", type(val))
113
+ if self.match_raw:
114
+ if self.regex.match(val.value):
115
+ return True
116
+ else:
117
+ if val.match(self.regex):
118
+ return True
119
+ else:
120
+ if self.regex.match(val):
121
+ return True
122
+ return False
123
+
124
+
125
+ class PartialMultipleAnd(Filter):
126
+ """Get the current property into the current row, and match the result with
127
+ a list of values"""
128
+
129
+ def __init__(self, propvalues, ignorecase=False):
130
+ super().__init__(list(propvalues.keys()), list(propvalues.values()))
131
+ self.regex = {}
132
+ self.propvalues = propvalues
133
+ if self.is_enable():
134
+ for key, value in list(propvalues.items()):
135
+ self.regex[key] = None
136
+ if value is not None:
137
+ pattern = r"^.*" + value + ".*$"
138
+ if ignorecase:
139
+ self.regex[key] = re.compile(pattern, re.IGNORECASE)
140
+ else:
141
+ self.regex[key] = re.compile(pattern)
142
+
143
+ def __call__(self, row):
144
+ if not self.is_enable():
145
+ return True
146
+ vals = self.get_val(row)
147
+ for key, val in list(vals.items()):
148
+ if self.regex[key] and not self.regex[key].match(val):
149
+ return False
150
+ return True
151
+
152
+
153
+ class PartialDate(Filter):
154
+ """Get the current property into the current row, and match the result with
155
+ a list of values.
156
+ This filter will be activated/enabled if 'value' is not None.
157
+ """
158
+
159
+ def __init__(self, prop, value):
160
+ super().__init__(prop, value)
161
+ if self.is_enable():
162
+ pattern = r"^.*" + value + ".*$"
163
+ self.regex = re.compile(pattern)
164
+
165
+ def __call__(self, row):
166
+ if not self.is_enable():
167
+ return True
168
+ val = self.get_val(row)
169
+ if isinstance(val, dict):
170
+ # no idea how it is possible
171
+ for val2 in list(val.values()):
172
+ if self.regex.match(val2):
173
+ return True
174
+ else:
175
+ formatt = "{da:%Y-%m-%d %H:%M:%S}"
176
+ val = formatt.format(da=datetime.datetime.fromtimestamp(val / 1000))
177
+ self.log.debug("type of value: %s", type(val))
178
+ self.log.debug("value: %s", val)
179
+ if self.regex.match(val):
180
+ return True
181
+ return False
182
+
183
+
184
+ class Equal(Filter):
185
+ """Get the current property into the current row, and test equality.
186
+ Only one value is possible"""
187
+
188
+ def __call__(self, row):
189
+ if not self.is_enable():
190
+ return True
191
+ val = self.get_val(row)
192
+ return val == self.values
193
+
194
+
195
+ class Equals(Filter):
196
+ """Deprecated. Use EqualMultipleOr.
197
+ Get the current property into the current row, and test equality.
198
+ A list of values will be tested"""
199
+
200
+ def __call__(self, row):
201
+ if not self.is_enable():
202
+ return True
203
+ val = self.get_val(row)
204
+ self.log.debug("type of value: %s", type(val))
205
+ self.log.debug("value: %s", val)
206
+ for v in self.values:
207
+ self.log.debug("comp: %s", v)
208
+ self.log.debug("comp type: %s", type(v))
209
+ return val in self.values
vhatable/filtersv2.py ADDED
@@ -0,0 +1,121 @@
1
+ #! /usr/bin/env python
2
+ """This module contains only filters, they are design to be apply to a row
3
+ of a Vertical or Horizotal table."""
4
+
5
+
6
+ # This file is part of Vhatable cli.
7
+ #
8
+ # Vhatable cli is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # Vhatable cli is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with Vhatable cli. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ # Copyright 2023 Frédéric MARTIN
22
+ #
23
+ # Contributors list :
24
+ #
25
+ # Frédéric MARTIN frederic.martin.fma@gmail.com
26
+ #
27
+
28
+ from datetime import datetime, timedelta
29
+
30
+ from .filters import Equals, Filter
31
+
32
+ # Equal | Partial | Greater | Older | Newer
33
+ # Single value | Multiple Value
34
+ # And | Or
35
+
36
+
37
+ class EqualMultipleOr(Equals):
38
+ """
39
+ Get the current property into the current row, and test equality with any
40
+ provided values."""
41
+
42
+
43
+ class PartialMultipleAnd(Filter):
44
+ """Get the current property into the current row, and match the result with
45
+ ALL values of the list"""
46
+
47
+ def __init__(self, prop, values):
48
+ super().__init__(prop, values)
49
+ if self.is_enable():
50
+ if not isinstance(values, list):
51
+ raise ValueError("input values should be a list")
52
+ self.log.debug("values: %s", self.values)
53
+
54
+ def __str__(self):
55
+ return "PartialMultipleAnd: " + str(self.values)
56
+
57
+ def __call__(self, row):
58
+ if not self.is_enable():
59
+ return True
60
+ # comp must be a cell
61
+ comp = str(self.get_val(row))
62
+ lower_comp = comp.lower()
63
+ for val in self.values:
64
+ lower_val = val.lower()
65
+ if val == lower_val:
66
+ if lower_val not in lower_comp:
67
+ return False
68
+ else:
69
+ if val not in comp:
70
+ return False
71
+ return True
72
+
73
+
74
+ class OlderThanFilter(Filter):
75
+ """Get the property into the row and return only row older than N days.
76
+ This filter can only be used with DateCells, inner value should be a Long.
77
+ """
78
+
79
+ def __init__(self, prop, nb_days):
80
+ super().__init__(prop, nb_days)
81
+ if self.is_enable():
82
+ date = self._get_today() - timedelta(days=nb_days)
83
+ self.timestamp_to_compare = date.timestamp()
84
+
85
+ def _get_today(self):
86
+ return datetime.today()
87
+
88
+ def __call__(self, row):
89
+ if not self.is_enable():
90
+ return True
91
+ cell = self.get_val(row)
92
+ value = cell.value
93
+ if cell.millisecond:
94
+ value = value / 1000
95
+ # self.log.debug("cell: %s, %s", cell, value)
96
+ return self.timestamp_to_compare > value
97
+
98
+
99
+ class NewerThanFilter(Filter):
100
+ """Get the property into the row and return only row newer than N days.
101
+ This filter can only be used with DateCells, inner value should be a Long.
102
+ """
103
+
104
+ def __init__(self, prop, nb_days):
105
+ super().__init__(prop, nb_days)
106
+ if self.is_enable():
107
+ date = self._get_today() - timedelta(days=nb_days)
108
+ self.timestamp_to_compare = date.timestamp()
109
+
110
+ def _get_today(self):
111
+ return datetime.today()
112
+
113
+ def __call__(self, row):
114
+ if not self.is_enable():
115
+ return True
116
+ cell = self.get_val(row)
117
+ value = cell.value
118
+ if cell.millisecond:
119
+ value = value / 1000
120
+ # self.log.debug("cell: %s, %s", cell, value)
121
+ return self.timestamp_to_compare < value