7
7
from datetime import UTC , datetime , timedelta
8
8
from enum import Enum
9
9
from typing import Protocol
10
+ from abc import abstractmethod
10
11
11
12
import yaml
12
13
from implicitdict import ImplicitDict
@@ -40,7 +41,8 @@ def __enter__(self):
40
41
self ._start_time = datetime .now (UTC )
41
42
42
43
def __exit__ (self , exc_type , exc_val , exc_tb ):
43
- self .elapsed_time += datetime .now (UTC ) - self ._start_time
44
+ if self ._start_time :
45
+ self .elapsed_time += datetime .now (UTC ) - self ._start_time
44
46
45
47
46
48
class VolumeType (str , Enum ):
@@ -57,10 +59,12 @@ class HistoricalVolumesCollection:
57
59
active_at : datetime
58
60
59
61
60
- class HistoricalVolumesRenderer (Protocol ):
62
+ class HistoricalVolumesRenderer [T ](Protocol ):
63
+
64
+ @abstractmethod
61
65
def __call__ (
62
66
self ,
63
- log_entry : TracerLogEntry ,
67
+ log_entry : T ,
64
68
existing_volume_collections : list [HistoricalVolumesCollection ],
65
69
) -> list [HistoricalVolumesCollection ]:
66
70
"""Function that generates named collections of 4D volumes from a tracer log entry.
@@ -71,6 +75,7 @@ def __call__(
71
75
72
76
Returns: Collection of 4D volume collections.
73
77
"""
78
+ raise NotImplementedError
74
79
75
80
76
81
@dataclass
@@ -126,6 +131,10 @@ def _historical_volumes_op_intent_notification(
126
131
existing_volume_collections : list [HistoricalVolumesCollection ],
127
132
) -> list [HistoricalVolumesCollection ]:
128
133
try :
134
+
135
+ if log_entry .request .json is None :
136
+ raise ValueError ("No json data in log entry" )
137
+
129
138
req = ImplicitDict .parse (
130
139
log_entry .request .json , PutOperationalIntentDetailsParameters
131
140
)
@@ -136,7 +145,7 @@ def _historical_volumes_op_intent_notification(
136
145
return []
137
146
assert isinstance (req , PutOperationalIntentDetailsParameters )
138
147
139
- claims = get_token_claims (log_entry .request .headers )
148
+ claims = get_token_claims (log_entry .request .headers or {} )
140
149
manager = claims .get ("sub" , "[Unknown manager]" )
141
150
name = f"{ manager } { req .operational_intent_id } "
142
151
@@ -148,7 +157,7 @@ def _historical_volumes_op_intent_notification(
148
157
else :
149
158
version = "[deleted]"
150
159
state = "Ended"
151
- volumes = []
160
+ volumes = Volume4DCollection ()
152
161
153
162
# See if this op intent version already has a volumes collection
154
163
already_defined = False
@@ -183,6 +192,10 @@ def _historical_volumes_op_intent_poll(
183
192
# Add newly-polled operational intents
184
193
for op_intent_id , query in log_entry .poll .uss_queries .items ():
185
194
try :
195
+
196
+ if query .json_result is None :
197
+ raise ValueError ("No json result in query" )
198
+
186
199
resp = ImplicitDict .parse (
187
200
query .json_result , GetOperationalIntentDetailsResponse
188
201
)
@@ -222,6 +235,10 @@ def _historical_volumes_op_intent_poll(
222
235
# Remove any existing operational intents that no longer exist as of this poll
223
236
for cached_op_intent_id , cached_query in log_entry .poll .cached_uss_queries .items ():
224
237
try :
238
+
239
+ if cached_query .json_result is None :
240
+ raise ValueError ("No json result in query" )
241
+
225
242
resp = ImplicitDict .parse (
226
243
cached_query .json_result , GetOperationalIntentDetailsResponse
227
244
)
@@ -274,17 +291,19 @@ class VolumesFolder:
274
291
def truncate (self , latest_time : Time ) -> None :
275
292
to_remove = []
276
293
for v in self .volumes :
277
- if v .volume .time_start .datetime > latest_time .datetime :
294
+ if v .volume .time_start and v . volume . time_start .datetime > latest_time .datetime :
278
295
to_remove .append (v )
279
- elif v .volume .time_end .datetime > latest_time .datetime :
296
+ elif v .volume .time_end and v . volume . time_end .datetime > latest_time .datetime :
280
297
v .volume .time_end = latest_time
281
298
for v in to_remove :
282
299
self .volumes .remove (v )
283
300
for c in self .children :
284
301
c .truncate (latest_time )
285
302
286
- def to_kml_folder (self ) -> kml . Folder :
303
+ def to_kml_folder (self ):
287
304
def dt (t : Time ) -> int :
305
+ if self .reference_time is None :
306
+ return - 1
288
307
return round ((t .datetime - self .reference_time .datetime ).total_seconds ())
289
308
290
309
if self .reference_time :
@@ -293,7 +312,7 @@ def dt(t: Time) -> int:
293
312
else :
294
313
folder = kml .Folder (kml .name (self .name ))
295
314
for v in self .volumes :
296
- name = f"{ v .name } { dt (v .volume .time_start )} s-{ dt (v .volume .time_end )} s"
315
+ name = f"{ v .name } { dt (v .volume .time_start ) if v . volume . time_start else '?' } s-{ dt (v .volume .time_end ) if v . volume . time_end else '?' } s"
297
316
folder .append (
298
317
make_placemark_from_volume (v .volume , name = name , style_url = v .style )
299
318
)
@@ -408,13 +427,13 @@ def render_historical_kml(log_folder: str) -> str:
408
427
version_folder .children .append (future_folder )
409
428
410
429
for i , v in enumerate (hvc .volumes ):
411
- if v .time_end .datetime <= hvc .active_at :
430
+ if v .time_end and v . time_end .datetime <= hvc .active_at :
412
431
# This volume ended before the collection was declared, so it never actually existed
413
432
continue
414
- if v .time_start .datetime < hvc .active_at :
433
+ if v .time_start and v . time_start .datetime < hvc .active_at :
415
434
# Volume is declared in the past, but it's only visible starting now
416
435
v .time_start = t_hvc
417
- elif v .time_start .datetime > hvc .active_at :
436
+ elif v .time_start and v . time_start .datetime > hvc .active_at :
418
437
# Add a "future" volume between when this volume was declared and its start time
419
438
future_v = Volume4D (v )
420
439
future_v .time_end = v .time_start
0 commit comments