11import abc
2+ from collections .abc import Generator
23import enum
4+ import json
35import sys
46
7+ import pydantic
8+
59if sys .version_info < (3 , 11 ):
6- from typing_extensions import Self
10+ from typing_extensions import Self , TYPE_CHECKING
711else :
8- from typing import Self
12+ from typing import Self , TYPE_CHECKING
13+
14+
15+ if TYPE_CHECKING :
16+ from .base import SimvueObject
17+
918
1019class Status (str , enum .Enum ):
1120 Created = "created"
@@ -22,6 +31,7 @@ class Time(str, enum.Enum):
2231 Modified = "modified"
2332 Ended = "ended"
2433
34+
2535class System (str , enum .Enum ):
2636 Working_Directory = "cwd"
2737 Hostname = "hostname"
@@ -36,16 +46,27 @@ class System(str, enum.Enum):
3646
3747
3848class RestAPIFilter (abc .ABC ):
39- def __init__ (self ) -> None :
49+ """RestAPI query filter object."""
50+
51+ def __init__ (self , simvue_object : "type[SimvueObject] | None" = None ) -> None :
52+ """Initialise a query object using a Simvue object class."""
53+ self ._sv_object : "type[SimvueObject] | None" = simvue_object
4054 self ._filters : list [str ] = []
4155 self ._generate_members ()
4256
43- def _time_within (self , time_type : Time , * , hours : int = 0 , days : int = 0 , years : int = 0 ) -> Self :
44- if len (_non_zero := list (i for i in (hours , days , years ) if i != 0 )) > 1 :
45- raise AssertionError ("Only one duration type may be provided: hours, days or years" )
57+ def _time_within (
58+ self , time_type : Time , * , hours : int = 0 , days : int = 0 , years : int = 0
59+ ) -> Self :
60+ """Define filter using time range."""
61+ if len (_non_zero := list (i for i in (hours , days , years ) if i != 0 )) > 1 :
62+ raise AssertionError (
63+ "Only one duration type may be provided: hours, days or years"
64+ )
4665 if len (_non_zero ) < 1 :
47- raise AssertionError (f"No duration provided for filter '{ time_type .value } _within'" )
48-
66+ raise AssertionError (
67+ f"No duration provided for filter '{ time_type .value } _within'"
68+ )
69+
4970 if hours :
5071 self ._filters .append (f"{ time_type .value } < { hours } h" )
5172 elif days :
@@ -56,133 +77,159 @@ def _time_within(self, time_type: Time, *, hours: int=0, days: int=0, years: int
5677
5778 @abc .abstractmethod
5879 def _generate_members (self ) -> None :
59- pass
80+ """Generate filters using specified definitions."""
6081
6182 def has_name (self , name : str ) -> Self :
83+ """Filter based on absolute object name."""
6284 self ._filters .append (f"name == { name } " )
6385 return self
64-
86+
6587 def has_name_containing (self , name : str ) -> Self :
88+ """Filter base on object name containing a term."""
6689 self ._filters .append (f"name contains { name } " )
6790 return self
68-
69- def created_within (self , * , hours : int = 0 , days : int = 0 , years : int = 0 ) -> Self :
91+
92+ def created_within (self , * , hours : int = 0 , days : int = 0 , years : int = 0 ) -> Self :
93+ """Find objects created within the last specified time period."""
7094 return self ._time_within (Time .Created , hours = hours , days = days , years = years )
71-
95+
7296 def has_description_containing (self , search_str : str ) -> Self :
97+ """Return objects containing the specified term within the description."""
7398 self ._filters .append (f"description contains { search_str } " )
7499 return self
75-
100+
76101 def exclude_description_containing (self , search_str : str ) -> Self :
102+ """Find objects not containing the specified term in their description."""
77103 self ._filters .append (f"description not contains { search_str } " )
78104 return self
79-
105+
80106 def has_tag (self , tag : str ) -> Self :
107+ """Find objects with the given tag."""
81108 self ._filters .append (f"has tag.{ tag } " )
82109 return self
83110
111+ def starred (self ) -> Self :
112+ self ._filters .append ("starred" )
113+ return self
114+
84115 def as_list (self ) -> list [str ]:
116+ """Returns the filters as a list."""
85117 return self ._filters
86118
87119 def clear (self ) -> None :
120+ """Clear all current filters."""
88121 self ._filters = []
89122
123+ def get (
124+ self ,
125+ count : pydantic .PositiveInt | None = None ,
126+ offset : pydantic .NonNegativeInt | None = None ,
127+ ** kwargs ,
128+ ) -> Generator [tuple [str , "SimvueObject | None" ], None , None ]:
129+ """Call the get method from the simvue object class."""
130+ if not self ._sv_object :
131+ raise RuntimeError ("No object type associated with filter." )
132+ _filters : str = json .dumps (self ._filters )
133+ return self ._sv_object .get (
134+ count = count , offset = offset , filters = _filters , ** kwargs
135+ )
136+
90137
91138class FoldersFilter (RestAPIFilter ):
92139 def has_path (self , name : str ) -> "FoldersFilter" :
93140 self ._filters .append (f"path == { name } " )
94141 return self
95-
142+
96143 def has_path_containing (self , name : str ) -> "FoldersFilter" :
97144 self ._filters .append (f"path contains { name } " )
98145 return self
99-
146+
100147 def _generate_members (self ) -> None :
101148 return super ()._generate_members ()
102149
103150
104151class RunsFilter (RestAPIFilter ):
105152 def _generate_members (self ) -> None :
106- _global_comparators = [
107- self ._value_contains ,
108- self ._value_eq ,
109- self ._value_neq
110- ]
153+ _global_comparators = [self ._value_contains , self ._value_eq , self ._value_neq ]
111154
112155 _numeric_comparators = [
113156 self ._value_geq ,
114157 self ._value_leq ,
115158 self ._value_lt ,
116- self ._value_gt
159+ self ._value_gt ,
117160 ]
118161
119162 for label , system_spec in System .__members__ .items ():
120163 for function in _global_comparators :
121164 _label : str = label .lower ()
122165 _func_name : str = function .__name__ .replace ("_value" , _label )
123- _out_func = lambda value , func = function : func ("system" , system_spec .value , value )
166+
167+ def _out_func (value : str | int | float , func = function ) -> Self :
168+ return func ("system" , system_spec .value , value )
169+
124170 _out_func .__name__ = _func_name
125171 setattr (self , _func_name , _out_func )
126172
127173 for function in _global_comparators + _numeric_comparators :
128- _func_name : str = function .__name__ .replace ("_value" , "metadata" )
129- _out_func = lambda attribute , value , func = function : func ("metadata" , attribute , value )
174+ _func_name = function .__name__ .replace ("_value" , "metadata" )
175+
176+ def _out_func (
177+ attribute : str , value : str | int | float , func = function
178+ ) -> Self :
179+ return func ("metadata" , attribute , value )
180+
130181 _out_func .__name__ = _func_name
131182 setattr (self , _func_name , _out_func )
132183
133- def author (self , username : str = "self" ) -> "RunsFilter" :
184+ def author (self , username : str = "self" ) -> "RunsFilter" :
134185 self ._filters .append (f"user == { username } " )
135186 return self
136187
137- def exclude_author (self , username : str = "self" ) -> "RunsFilter" :
188+ def exclude_author (self , username : str = "self" ) -> "RunsFilter" :
138189 self ._filters .append (f"user != { username } " )
139190 return self
140-
141- def starred (self ) -> "RunsFilter" :
142- self ._filters .append ("starred" )
143- return self
144-
145- def has_name (self , name : str ) -> "RunsFilter" :
146- self ._filters .append (f"name == { name } " )
147- return self
148-
149- def has_name_containing (self , name : str ) -> "RunsFilter" :
150- self ._filters .append (f"name contains { name } " )
151- return self
152191
153192 def has_status (self , status : Status ) -> "RunsFilter" :
154193 self ._filters .append (f"status == { status .value } " )
155194 return self
156-
195+
157196 def is_running (self ) -> "RunsFilter" :
158197 return self .has_status (Status .Running )
159-
198+
160199 def is_lost (self ) -> "RunsFilter" :
161200 return self .has_status (Status .Lost )
162-
201+
163202 def has_completed (self ) -> "RunsFilter" :
164203 return self .has_status (Status .Completed )
165-
204+
166205 def has_failed (self ) -> "RunsFilter" :
167206 return self .has_status (Status .Failed )
168-
169- def has_alert (self , alert_name : str , is_critical : bool | None = None ) -> "RunsFilter" :
207+
208+ def has_alert (
209+ self , alert_name : str , is_critical : bool | None = None
210+ ) -> "RunsFilter" :
170211 self ._filters .append (f"alert.name == { alert_name } " )
171212 if is_critical is True :
172213 self ._filters .append ("alert.status == critical" )
173214 elif is_critical is False :
174215 self ._filters .append ("alert.status == ok" )
175216 return self
176217
177- def started_within (self , * , hours : int = 0 , days : int = 0 , years : int = 0 ) -> "RunsFilter" :
218+ def started_within (
219+ self , * , hours : int = 0 , days : int = 0 , years : int = 0
220+ ) -> "RunsFilter" :
178221 return self ._time_within (Time .Started , hours = hours , days = days , years = years )
179-
180- def modified_within (self , * , hours : int = 0 , days : int = 0 , years : int = 0 ) -> "RunsFilter" :
222+
223+ def modified_within (
224+ self , * , hours : int = 0 , days : int = 0 , years : int = 0
225+ ) -> "RunsFilter" :
181226 return self ._time_within (Time .Modified , hours = hours , days = days , years = years )
182-
183- def ended_within (self , * , hours : int = 0 , days : int = 0 , years : int = 0 ) -> "RunsFilter" :
227+
228+ def ended_within (
229+ self , * , hours : int = 0 , days : int = 0 , years : int = 0
230+ ) -> "RunsFilter" :
184231 return self ._time_within (Time .Ended , hours = hours , days = days , years = years )
185-
232+
186233 def in_folder (self , folder_name : str ) -> "RunsFilter" :
187234 self ._filters .append (f"folder.path == { folder_name } " )
188235 return self
@@ -194,37 +241,51 @@ def has_metadata_attribute(self, attribute: str) -> "RunsFilter":
194241 def exclude_metadata_attribute (self , attribute : str ) -> "RunsFilter" :
195242 self ._filters .append (f"metadata.{ attribute } not exists" )
196243 return self
197-
198- def _value_eq (self , category : str , attribute : str , value : str | int | float ) -> "RunsFilter" :
244+
245+ def _value_eq (
246+ self , category : str , attribute : str , value : str | int | float
247+ ) -> "RunsFilter" :
199248 self ._filters .append (f"{ category } .{ attribute } == { value } " )
200249 return self
201-
202- def _value_neq (self , category : str , attribute : str , value : str | int | float ) -> "RunsFilter" :
250+
251+ def _value_neq (
252+ self , category : str , attribute : str , value : str | int | float
253+ ) -> "RunsFilter" :
203254 self ._filters .append (f"{ category } .{ attribute } != { value } " )
204255 return self
205-
206- def _value_contains (self , category : str , attribute : str , value : str | int | float ) -> "RunsFilter" :
256+
257+ def _value_contains (
258+ self , category : str , attribute : str , value : str | int | float
259+ ) -> "RunsFilter" :
207260 self ._filters .append (f"{ category } .{ attribute } contains { value } " )
208261 return self
209-
210- def _value_leq (self , category : str , attribute : str , value : int | float ) -> "RunsFilter" :
262+
263+ def _value_leq (
264+ self , category : str , attribute : str , value : int | float
265+ ) -> "RunsFilter" :
211266 self ._filters .append (f"{ category } .{ attribute } <= { value } " )
212267 return self
213-
214- def _value_geq (self , category : str , attribute : str , value : int | float ) -> "RunsFilter" :
268+
269+ def _value_geq (
270+ self , category : str , attribute : str , value : int | float
271+ ) -> "RunsFilter" :
215272 self ._filters .append (f"{ category } .{ attribute } >= { value } " )
216273 return self
217-
218- def _value_lt (self , category : str , attribute : str , value : int | float ) -> "RunsFilter" :
274+
275+ def _value_lt (
276+ self , category : str , attribute : str , value : int | float
277+ ) -> "RunsFilter" :
219278 self ._filters .append (f"{ category } .{ attribute } < { value } " )
220279 return self
221-
222- def _value_gt (self , category : str , attribute : str , value : int | float ) -> "RunsFilter" :
280+
281+ def _value_gt (
282+ self , category : str , attribute : str , value : int | float
283+ ) -> "RunsFilter" :
223284 self ._filters .append (f"{ category } .{ attribute } > { value } " )
224285 return self
225-
286+
226287 def __str__ (self ) -> str :
227288 return " && " .join (self ._filters ) if self ._filters else "None"
228-
289+
229290 def __repr__ (self ) -> str :
230291 return f"{ super ().__repr__ ()[:- 1 ]} , filters={ self ._filters } >"
0 commit comments