11import contextlib
22import functools
3+ import itertools
34import logging
45import sys
56import warnings
2728from corva .models .merge .merge import PartialRerunMergeEvent
2829from corva .models .merge .raw import RawPartialRerunMergeEvent
2930from corva .models .scheduled .raw import RawScheduledEvent
30- from corva .models .scheduled .scheduled import ScheduledEvent , ScheduledNaturalTimeEvent
31+ from corva .models .scheduled .scheduled import ScheduledEvent , ScheduledNaturalTimeEvent , ScheduledDataTimeEvent , \
32+ ScheduledDepthEvent
33+ from corva .models .scheduled .scheduler_type import SchedulerType
3134from corva .models .stream .raw import RawStreamEvent
3235from corva .models .stream .stream import StreamEvent
3336from corva .models .task import RawTaskEvent , TaskEvent , TaskStatus
@@ -58,6 +61,7 @@ def base_handler(
5861 func : Callable ,
5962 raw_event_type : Type [RawBaseEvent ],
6063 handler : Optional [logging .Handler ],
64+ merge_events : Optional [bool ] = False
6165) -> Callable [[Any , Any ], List [Any ]]:
6266 @functools .wraps (func )
6367 def wrapper (aws_event : Any , aws_context : Any ) -> List [Any ]:
@@ -84,6 +88,8 @@ def wrapper(aws_event: Any, aws_context: Any) -> List[Any]:
8488 url = SETTINGS .CACHE_URL , decode_responses = True , max_connections = 1
8589 )
8690 data_transformation_type = raw_custom_event_type or raw_event_type
91+ if merge_events :
92+ aws_event = _merge_events (aws_event , data_transformation_type )
8793 raw_events = data_transformation_type .from_raw_event (event = aws_event )
8894
8995 if (
@@ -120,18 +126,20 @@ def stream(
120126 func : Optional [Callable [[StreamEventT , Api , UserRedisSdk ], Any ]] = None ,
121127 * ,
122128 handler : Optional [logging .Handler ] = None ,
129+ merge_events : Optional [bool ] = False
123130) -> Callable :
124131 """Runs stream app.
125132
126133 Arguments:
127134 handler: logging handler to include in Corva logger.
135+ merge_events: if True - merge all incoming events into one before passing them to func
128136 """
129137
130138 if func is None :
131- return functools .partial (stream , handler = handler )
139+ return functools .partial (stream , handler = handler , merge_events = merge_events )
132140
133141 @functools .wraps (func )
134- @functools .partial (base_handler , raw_event_type = RawStreamEvent , handler = handler )
142+ @functools .partial (base_handler , raw_event_type = RawStreamEvent , handler = handler , merge_events = merge_events )
135143 def wrapper (
136144 event : RawStreamEvent ,
137145 api_key : str ,
@@ -221,6 +229,7 @@ def scheduled(
221229 func : Optional [Callable [[ScheduledEventT , Api , UserRedisSdk ], Any ]] = None ,
222230 * ,
223231 handler : Optional [logging .Handler ] = None ,
232+ merge_events : Optional [bool ] = False
224233) -> Callable :
225234 """Runs scheduled app.
226235
@@ -229,10 +238,10 @@ def scheduled(
229238 """
230239
231240 if func is None :
232- return functools .partial (scheduled , handler = handler )
241+ return functools .partial (scheduled , handler = handler , merge_events = merge_events )
233242
234243 @functools .wraps (func )
235- @functools .partial (base_handler , raw_event_type = RawScheduledEvent , handler = handler )
244+ @functools .partial (base_handler , raw_event_type = RawScheduledEvent , handler = handler , merge_events = merge_events )
236245 def wrapper (
237246 event : RawScheduledEvent ,
238247 api_key : str ,
@@ -544,3 +553,33 @@ def _get_custom_event_type_by_raw_aws_event(
544553 if events :
545554 return event_type , handler
546555 return None , None
556+
557+
558+ def _merge_events (aws_event : Any , data_transformation_type : RawBaseEvent ) -> Any :
559+ """
560+ Merges incoming aws_events into one.
561+ Merge happens differently, depending on app type.
562+ """
563+ if data_transformation_type == RawScheduledEvent :
564+ if not isinstance (aws_event [0 ], dict ):
565+ aws_event : List [dict ] = list (itertools .chain (* aws_event ))
566+ is_depth = aws_event [0 ]["scheduler_type" ] == SchedulerType .data_depth_milestone
567+ event_start , event_end = ("top_depth" , "bottom_depth" ) if is_depth else ("schedule_start" , "schedule_end" )
568+ min_event_start , max_event_end = aws_event [0 ][event_start ], aws_event [0 ].get (event_end )
569+ for event in aws_event [1 :]:
570+ if event [event_start ] < min_event_start :
571+ min_event_start = event [event_start ]
572+ if max_event_end and event [event_end ] > max_event_end :
573+ max_event_end = event [event_end ]
574+
575+ aws_event [0 ][event_start ] = min_event_start
576+ if max_event_end :
577+ aws_event [0 ][event_end ] = max_event_end
578+ aws_event = aws_event [0 ]
579+ return aws_event
580+
581+ # stream event
582+ for event in aws_event [1 :]:
583+ aws_event [0 ]["records" ].extend (event ["records" ])
584+ aws_event = [aws_event [0 ]]
585+ return aws_event
0 commit comments