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