@@ -57,6 +57,20 @@ def with_uid(self, new_uid):
57
57
component ["UID" ] = new_uid
58
58
59
59
return Item ("\r \n " .join (parsed .dump_lines ()))
60
+
61
+ def has_confirmed_attendee (self , email : str ) -> bool :
62
+ """Returns True if the given attendee has accepted an invite to this event"""
63
+ parsed = _Component .parse (self .raw )
64
+ stack = [parsed ]
65
+ while stack :
66
+ component = stack .pop ()
67
+ for attendee_line in component .get_all ("ATTENDEE" ):
68
+ sections = attendee_line .split (";" )
69
+ if f"CN={ email } " in sections and "PARTSTAT=ACCEPTED" in sections :
70
+ return True
71
+ stack .extend (component .subcomponents )
72
+
73
+ return False
60
74
61
75
def without_details (self ):
62
76
"""Returns a minimal version of this item.
@@ -79,8 +93,7 @@ def without_details(self):
79
93
if subcomp .name != "VTIMEZONE"
80
94
]
81
95
for field in ["DESCRIPTION" , "ORGANIZER" , "ATTENDEE" , "LOCATION" ]:
82
- # Repeatedly delete because some fields can appear multiple times
83
- while field in component :
96
+ if field in component :
84
97
del component [field ]
85
98
86
99
stack .extend (component .subcomponents )
@@ -265,6 +278,17 @@ def _get_item_type(components, wrappers):
265
278
raise ValueError ("Not sure how to join components." )
266
279
267
280
281
+ def _extract_prop_value (line , key ):
282
+ if line .startswith (key ):
283
+ prefix_without_params = f"{ key } :"
284
+ prefix_with_params = f"{ key } ;"
285
+ if line .startswith (prefix_without_params ):
286
+ return line [len (prefix_without_params ) :]
287
+ elif line .startswith (prefix_with_params ):
288
+ return line [len (prefix_with_params ) :].split (":" , 1 )[- 1 ]
289
+
290
+ return None
291
+
268
292
class _Component :
269
293
"""
270
294
Raw outline of the components.
@@ -337,20 +361,15 @@ def dump_lines(self):
337
361
def __delitem__ (self , key ):
338
362
prefix = (f"{ key } :" , f"{ key } ;" )
339
363
new_lines = []
340
- lineiter = iter ( self . props )
341
- while True :
342
- for line in lineiter :
364
+ in_prop = False
365
+ for line in iter ( self . props ) :
366
+ if not in_prop :
343
367
if line .startswith (prefix ):
344
- break
368
+ in_prop = True
345
369
else :
346
370
new_lines .append (line )
347
- else :
348
- break
349
-
350
- for line in lineiter :
351
- if not line .startswith ((" " , "\t " )):
352
- new_lines .append (line )
353
- break
371
+ elif not line .startswith ((" " , "\t " )):
372
+ in_prop = False
354
373
355
374
self .props = new_lines
356
375
@@ -372,26 +391,25 @@ def __contains__(self, obj):
372
391
raise ValueError (obj )
373
392
374
393
def __getitem__ (self , key ):
375
- prefix_without_params = f"{ key } :"
376
- prefix_with_params = f"{ key } ;"
377
- iterlines = iter (self .props )
378
- for line in iterlines :
379
- if line .startswith (prefix_without_params ):
380
- rv = line [len (prefix_without_params ) :]
381
- break
382
- elif line .startswith (prefix_with_params ):
383
- rv = line [len (prefix_with_params ) :].split (":" , 1 )[- 1 ]
384
- break
385
- else :
394
+ try :
395
+ return next (self .get_all (key ))
396
+ except StopIteration :
386
397
raise KeyError
387
-
388
- for line in iterlines :
389
- if line .startswith ((" " , "\t " )):
390
- rv += line [1 :]
398
+
399
+ def get_all (self , key : str ):
400
+ rv = None
401
+ for line in iter (self .props ):
402
+ if rv is None :
403
+ rv = _extract_prop_value (line , key )
391
404
else :
392
- break
393
-
394
- return rv
405
+ if line .startswith ((" " , "\t " )):
406
+ rv += line [1 :]
407
+ else :
408
+ yield rv
409
+ rv = _extract_prop_value (line , key )
410
+
411
+ if rv is not None :
412
+ yield rv
395
413
396
414
def get (self , key , default = None ):
397
415
try :
0 commit comments