-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfilter.py
More file actions
304 lines (245 loc) · 9.07 KB
/
filter.py
File metadata and controls
304 lines (245 loc) · 9.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
import abc
import enum
import sys
import typing
import pydantic
if sys.version_info < (3, 11):
from typing_extensions import Self
else:
from typing import Self
class Status(str, enum.Enum):
Created = "created"
Running = "running"
Completed = "completed"
Lost = "lost"
Terminated = "terminated"
Failed = "failed"
class Time(str, enum.Enum):
Created = "created"
Started = "started"
Modified = "modified"
Ended = "ended"
class System(str, enum.Enum):
Working_Directory = "cwd"
Hostname = "hostname"
Python_Version = "pythonversion"
Platform_System = "platform.system"
Platform_Release = "platform.release"
Platform_Version = "platform.version"
CPU_Architecture = "cpu.arch"
CPU_Processor = "cpu.processor"
GPU_Name = "gpu.name"
GPU_Driver = "gpu.driver"
class RestAPIFilter(abc.ABC):
def __init__(self) -> None:
self._filters: list[str] = []
self._generate_members()
def _time_within(
self, time_type: Time, *, hours: int = 0, days: int = 0, years: int = 0
) -> Self:
if len(_non_zero := list(i for i in (hours, days, years) if i != 0)) > 1:
raise AssertionError(
"Only one duration type may be provided: hours, days or years"
)
if len(_non_zero) < 1:
raise AssertionError(
f"No duration provided for filter '{time_type.value}_within'"
)
if hours:
self._filters.append(f"{time_type.value} < {hours}h")
elif days:
self._filters.append(f"{time_type.value} < {days}d")
else:
self._filters.append(f"{time_type.value} < {years}y")
return self
@abc.abstractmethod
def _generate_members(self) -> None:
pass
@pydantic.validate_call
def has_name(self, name: str) -> Self:
self._filters.append(f"name == {name}")
return self
@pydantic.validate_call
def has_name_containing(self, name: str) -> Self:
self._filters.append(f"name contains {name}")
return self
@pydantic.validate_call
def created_within(
self,
*,
hours: pydantic.NonNegativeInt = 0,
days: pydantic.NonNegativeInt = 0,
years: pydantic.NonNegativeInt = 0,
) -> Self:
return self._time_within(Time.Created, hours=hours, days=days, years=years)
@pydantic.validate_call
def has_description_containing(self, search_str: str) -> Self:
self._filters.append(f"description contains {search_str}")
return self
@pydantic.validate_call
def exclude_description_containing(self, search_str: str) -> Self:
self._filters.append(f"description not contains {search_str}")
return self
@pydantic.validate_call
def has_tag(self, tag: str) -> Self:
self._filters.append(f"has tag.{tag}")
return self
def as_list(self) -> list[str]:
return self._filters
def clear(self) -> None:
self._filters = []
class FoldersFilter(RestAPIFilter):
def has_path(self, name: str) -> "FoldersFilter":
self._filters.append(f"path == {name}")
return self
def has_path_containing(self, name: str) -> "FoldersFilter":
self._filters.append(f"path contains {name}")
return self
def _generate_members(self) -> None:
return super()._generate_members()
class RunsFilter(RestAPIFilter):
def _generate_members(self) -> None:
_global_comparators: list[typing.Callable] = [
self._value_contains,
self._value_eq,
self._value_neq,
]
_numeric_comparators: list[typing.Callable] = [
self._value_geq,
self._value_leq,
self._value_lt,
self._value_gt,
]
for label, system_spec in System.__members__.items():
for function in _global_comparators:
_label: str = label.lower()
_func_name: str = function.__name__.replace("_value", _label)
def _out_func(value, func=function):
return func("system", system_spec.value, value)
_out_func.__name__ = _func_name
setattr(self, _func_name, _out_func)
for function in _global_comparators + _numeric_comparators:
_func_name = function.__name__.replace("_value", "metadata")
def _out_func(attribute, value, func=function):
return func("metadata", attribute, value)
_out_func.__name__ = _func_name
setattr(self, _func_name, _out_func)
@pydantic.validate_call
def author(self, username: str = "self") -> Self:
self._filters.append(f"user == {username}")
return self
@pydantic.validate_call
def exclude_author(self, username: str = "self") -> Self:
self._filters.append(f"user != {username}")
return self
def starred(self) -> Self:
self._filters.append("starred")
return self
@pydantic.validate_call
def has_name(self, name: str) -> Self:
self._filters.append(f"name == {name}")
return self
@pydantic.validate_call
def has_name_containing(self, name: str) -> Self:
self._filters.append(f"name contains {name}")
return self
@pydantic.validate_call
def has_status(self, status: Status) -> Self:
self._filters.append(f"status == {status.value}")
return self
def is_running(self) -> Self:
return self.has_status(Status.Running)
def is_lost(self) -> Self:
return self.has_status(Status.Lost)
def has_completed(self) -> Self:
return self.has_status(Status.Completed)
def has_failed(self) -> Self:
return self.has_status(Status.Failed)
@pydantic.validate_call
def has_alert(
self, alert_name: str, is_critical: typing.Optional[bool] = None
) -> Self:
self._filters.append(f"alert.name == {alert_name}")
if is_critical is True:
self._filters.append("alert.status == critical")
elif is_critical is False:
self._filters.append("alert.status == ok")
return self
@pydantic.validate_call
def started_within(
self,
*,
hours: pydantic.PositiveInt = 0,
days: pydantic.PositiveInt = 0,
years: pydantic.PositiveInt = 0,
) -> Self:
return self._time_within(Time.Started, hours=hours, days=days, years=years)
@pydantic.validate_call
def modified_within(
self,
*,
hours: pydantic.PositiveInt = 0,
days: pydantic.PositiveInt = 0,
years: pydantic.PositiveInt = 0,
) -> Self:
return self._time_within(Time.Modified, hours=hours, days=days, years=years)
@pydantic.validate_call
def ended_within(
self,
*,
hours: pydantic.PositiveInt = 0,
days: pydantic.PositiveInt = 0,
years: pydantic.PositiveInt = 0,
) -> Self:
return self._time_within(Time.Ended, hours=hours, days=days, years=years)
@pydantic.validate_call
def in_folder(self, folder_name: str) -> Self:
self._filters.append(f"folder.path == {folder_name}")
return self
@pydantic.validate_call
def has_metadata_attribute(self, attribute: str) -> Self:
self._filters.append(f"metadata.{attribute} exists")
return self
@pydantic.validate_call
def exclude_metadata_attribute(self, attribute: str) -> Self:
self._filters.append(f"metadata.{attribute} not exists")
return self
def _value_eq(
self, category: str, attribute: str, value: typing.Union[str, int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} == {value}")
return self
def _value_neq(
self, category: str, attribute: str, value: typing.Union[str, int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} != {value}")
return self
def _value_contains(
self, category: str, attribute: str, value: typing.Union[str, int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} contains {value}")
return self
def _value_leq(
self, category: str, attribute: str, value: typing.Union[int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} <= {value}")
return self
def _value_geq(
self, category: str, attribute: str, value: typing.Union[int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} >= {value}")
return self
def _value_lt(
self, category: str, attribute: str, value: typing.Union[int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} < {value}")
return self
def _value_gt(
self, category: str, attribute: str, value: typing.Union[int, float]
) -> Self:
self._filters.append(f"{category}.{attribute} > {value}")
return self
def __str__(self) -> str:
return " && ".join(self._filters) if self._filters else "None"
def __repr__(self) -> str:
return f"{super().__repr__()[:-1]}, filters={self._filters}>"