1
+ from typing import TYPE_CHECKING
2
+
1
3
from django .db import models
2
4
3
5
from acl .models import ACLBase
8
10
from entry .api_v2 .serializers import EntryUpdateSerializer
9
11
from entry .models import Attribute , Entry
10
12
13
+ if TYPE_CHECKING :
14
+ from django .db .models import Manager
15
+
11
16
12
17
## These are internal classes for AirOne trigger and action
13
18
class InputTriggerCondition (object ):
@@ -158,82 +163,13 @@ def _do_get_value(input_value, attr_type):
158
163
return _do_get_value (raw_input_value , self .attr .type )
159
164
160
165
161
- class TriggerAction (models .Model ):
162
- condition = models .ForeignKey ("TriggerParent" , on_delete = models .CASCADE , related_name = "actions" )
163
- attr = models .ForeignKey ("entity.EntityAttr" , on_delete = models .CASCADE )
164
-
165
- def save_actions (self , input : InputTriggerAction ):
166
- for input_action_value in input .values :
167
- params = {
168
- "action" : self ,
169
- "str_cond" : input_action_value .str_cond ,
170
- "ref_cond" : input_action_value .ref_cond ,
171
- "bool_cond" : input_action_value .bool_cond ,
172
- }
173
- TriggerActionValue .objects .create (** params )
174
-
175
- def get_serializer_acceptable_value (self , value = None , attr_type = None ):
176
- """
177
- This converts TriggerActionValue to the value that EntryUpdateSerializer can accept.
178
- """
179
- if not attr_type :
180
- attr_type = self .attr .type
181
-
182
- value = value or self .values .first ()
183
- if attr_type & AttrTypeValue ["array" ]:
184
- return [
185
- self .get_serializer_acceptable_value (x , attr_type ^ AttrTypeValue ["array" ])
186
- for x in self .values .all ()
187
- ]
188
- elif attr_type == AttrTypeValue ["boolean" ]:
189
- return value .bool_cond
190
- elif attr_type == AttrTypeValue ["named_object" ]:
191
- value .ref_cond .id if isinstance (value .ref_cond , Entry ) else None
192
- return {
193
- "name" : value .str_cond ,
194
- "id" : value .ref_cond .id ,
195
- }
196
- elif attr_type == AttrTypeValue ["string" ]:
197
- return value .str_cond
198
- elif attr_type == AttrTypeValue ["text" ]:
199
- return value .str_cond
200
- elif attr_type == AttrTypeValue ["object" ]:
201
- return value .ref_cond .id if isinstance (value .ref_cond , Entry ) else None
202
-
203
- def run (self , user , entry , call_stacks = []):
204
- # When self.id contains in call_stacks, it means that this action is already invoked.
205
- # This prevents infinite loop.
206
- if self .id in call_stacks :
207
- return
208
-
209
- # update specified Entry by configured attribute value
210
- setting_data = {
211
- "id" : entry .id ,
212
- "name" : entry .name ,
213
- "attrs" : [{"id" : self .attr .id , "value" : self .get_serializer_acceptable_value ()}],
214
- "delay_trigger" : False ,
215
- "call_stacks" : [* call_stacks , self .id ],
216
- }
217
- serializer = EntryUpdateSerializer (
218
- instance = entry , data = setting_data , context = {"request" : DRFRequest (user )}
219
- )
220
- if serializer :
221
- serializer .is_valid (raise_exception = True )
222
- serializer .save ()
223
-
224
-
225
- class TriggerActionValue (models .Model ):
226
- action = models .ForeignKey (TriggerAction , on_delete = models .CASCADE , related_name = "values" )
227
- str_cond = models .TextField (blank = True , null = True )
228
- ref_cond = models .ForeignKey ("entry.Entry" , on_delete = models .SET_NULL , null = True , blank = True )
229
- bool_cond = models .BooleanField (default = False )
230
-
231
- # TODO: Add method to register value to Attribute when action is invoked
232
-
233
-
234
166
class TriggerParent (models .Model ):
235
167
entity = models .ForeignKey ("entity.Entity" , on_delete = models .CASCADE )
236
168
169
+ if TYPE_CHECKING :
170
+ conditions : Manager ["TriggerCondition" ]
171
+ actions : Manager ["TriggerAction" ]
172
+
237
173
def is_match_condition (self , inputs : list [InputTriggerCondition ]):
238
174
if all ([c .is_same_condition (inputs ) for c in self .conditions .all ()]):
239
175
return True
@@ -251,7 +187,7 @@ def save_conditions(self, inputs: list[InputTriggerCondition]):
251
187
if not TriggerCondition .objects .filter (** params ).exists ():
252
188
TriggerCondition .objects .create (** params )
253
189
254
- def get_actions (self , recv_attrs : list ) -> list [TriggerAction ]:
190
+ def get_actions (self , recv_attrs : list ) -> list [" TriggerAction" ]:
255
191
"""
256
192
This method checks whether specified entity's Trigger is invoked by recv_attrs context.
257
193
The recv_attrs format should be compatible with APIv2 standard.
@@ -261,7 +197,7 @@ def get_actions(self, recv_attrs: list) -> list[TriggerAction]:
261
197
context to reduce DB query to get it from Attribute instance.
262
198
"""
263
199
264
- def _is_match (condition ):
200
+ def _is_match (condition : TriggerCondition ):
265
201
for attr_info in [x for x in recv_attrs if x ["attr_id" ] == condition .attr .id ]:
266
202
if condition .is_match_condition (attr_info ["value" ]):
267
203
return True
@@ -446,3 +382,79 @@ def get_invoked_actions(cls, entity: Entity, recv_data: list):
446
382
actions += parent_condition .get_actions (params )
447
383
448
384
return actions
385
+
386
+
387
+ class TriggerAction (models .Model ):
388
+ condition = models .ForeignKey (TriggerParent , on_delete = models .CASCADE , related_name = "actions" )
389
+ attr = models .ForeignKey ("entity.EntityAttr" , on_delete = models .CASCADE )
390
+
391
+ if TYPE_CHECKING :
392
+ values : Manager ["TriggerActionValue" ]
393
+
394
+ def save_actions (self , input : InputTriggerAction ):
395
+ for input_action_value in input .values :
396
+ params = {
397
+ "action" : self ,
398
+ "str_cond" : input_action_value .str_cond ,
399
+ "ref_cond" : input_action_value .ref_cond ,
400
+ "bool_cond" : input_action_value .bool_cond ,
401
+ }
402
+ TriggerActionValue .objects .create (** params )
403
+
404
+ def get_serializer_acceptable_value (self , value = None , attr_type = None ):
405
+ """
406
+ This converts TriggerActionValue to the value that EntryUpdateSerializer can accept.
407
+ """
408
+ if not attr_type :
409
+ attr_type = self .attr .type
410
+
411
+ value = value or self .values .first ()
412
+ if attr_type & AttrTypeValue ["array" ]:
413
+ return [
414
+ self .get_serializer_acceptable_value (x , attr_type ^ AttrTypeValue ["array" ])
415
+ for x in self .values .all ()
416
+ ]
417
+ elif attr_type == AttrTypeValue ["boolean" ]:
418
+ return value .bool_cond
419
+ elif attr_type == AttrTypeValue ["named_object" ]:
420
+ value .ref_cond .id if isinstance (value .ref_cond , Entry ) else None
421
+ return {
422
+ "name" : value .str_cond ,
423
+ "id" : value .ref_cond .id ,
424
+ }
425
+ elif attr_type == AttrTypeValue ["string" ]:
426
+ return value .str_cond
427
+ elif attr_type == AttrTypeValue ["text" ]:
428
+ return value .str_cond
429
+ elif attr_type == AttrTypeValue ["object" ]:
430
+ return value .ref_cond .id if isinstance (value .ref_cond , Entry ) else None
431
+
432
+ def run (self , user , entry , call_stacks = []):
433
+ # When self.id contains in call_stacks, it means that this action is already invoked.
434
+ # This prevents infinite loop.
435
+ if self .id in call_stacks :
436
+ return
437
+
438
+ # update specified Entry by configured attribute value
439
+ setting_data = {
440
+ "id" : entry .id ,
441
+ "name" : entry .name ,
442
+ "attrs" : [{"id" : self .attr .id , "value" : self .get_serializer_acceptable_value ()}],
443
+ "delay_trigger" : False ,
444
+ "call_stacks" : [* call_stacks , self .id ],
445
+ }
446
+ serializer = EntryUpdateSerializer (
447
+ instance = entry , data = setting_data , context = {"request" : DRFRequest (user )}
448
+ )
449
+ if serializer :
450
+ serializer .is_valid (raise_exception = True )
451
+ serializer .save ()
452
+
453
+
454
+ class TriggerActionValue (models .Model ):
455
+ action = models .ForeignKey (TriggerAction , on_delete = models .CASCADE , related_name = "values" )
456
+ str_cond = models .TextField (blank = True , null = True )
457
+ ref_cond = models .ForeignKey ("entry.Entry" , on_delete = models .SET_NULL , null = True , blank = True )
458
+ bool_cond = models .BooleanField (default = False )
459
+
460
+ # TODO: Add method to register value to Attribute when action is invoked
0 commit comments