Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/cal/alarm.py: 50%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

68 statements  

1""":rfc:`5545` VALARM component.""" 

2 

3from __future__ import annotations 

4 

5from datetime import date, datetime, timedelta 

6from typing import TYPE_CHECKING, NamedTuple, Optional, Union 

7 

8from icalendar.attr import ( 

9 attendees_property, 

10 create_single_property, 

11 description_property, 

12 property_del_duration, 

13 property_get_duration, 

14 property_set_duration, 

15 single_int_property, 

16 single_string_property, 

17 single_utc_property, 

18 summary_property, 

19 uid_property, 

20) 

21from icalendar.cal.component import Component 

22 

23if TYPE_CHECKING: 

24 import uuid 

25 

26 from icalendar.prop import vCalAddress 

27 

28 

29class Alarm(Component): 

30 """ 

31 A "VALARM" calendar component is a grouping of component 

32 properties that defines an alarm or reminder for an event or a 

33 to-do. For example, it may be used to define a reminder for a 

34 pending event or an overdue to-do. 

35 """ 

36 

37 name = "VALARM" 

38 # some properties MAY/MUST/MUST NOT appear depending on ACTION value 

39 required = ( 

40 "ACTION", 

41 "TRIGGER", 

42 ) 

43 singletons = ( 

44 "ATTACH", 

45 "ACTION", 

46 "DESCRIPTION", 

47 "SUMMARY", 

48 "TRIGGER", 

49 "DURATION", 

50 "REPEAT", 

51 "UID", 

52 "PROXIMITY", 

53 "ACKNOWLEDGED", 

54 ) 

55 inclusive = ( 

56 ( 

57 "DURATION", 

58 "REPEAT", 

59 ), 

60 ( 

61 "SUMMARY", 

62 "ATTENDEE", 

63 ), 

64 ) 

65 multiple = ("ATTENDEE", "ATTACH", "RELATED-TO") 

66 

67 REPEAT = single_int_property( 

68 "REPEAT", 

69 0, 

70 """The REPEAT property of an alarm component. 

71 

72 The alarm can be defined such that it triggers repeatedly. A 

73 definition of an alarm with a repeating trigger MUST include both 

74 the "DURATION" and "REPEAT" properties. The "DURATION" property 

75 specifies the delay period, after which the alarm will repeat. 

76 The "REPEAT" property specifies the number of additional 

77 repetitions that the alarm will be triggered. This repetition 

78 count is in addition to the initial triggering of the alarm. 

79 """, 

80 ) 

81 

82 DURATION = property( 

83 property_get_duration, 

84 property_set_duration, 

85 property_del_duration, 

86 """The DURATION property of an alarm component. 

87 

88 The alarm can be defined such that it triggers repeatedly. A 

89 definition of an alarm with a repeating trigger MUST include both 

90 the "DURATION" and "REPEAT" properties. The "DURATION" property 

91 specifies the delay period, after which the alarm will repeat. 

92 """, 

93 ) 

94 

95 ACKNOWLEDGED = single_utc_property( 

96 "ACKNOWLEDGED", 

97 """This is defined in RFC 9074: 

98 

99 Purpose: This property specifies the UTC date and time at which the 

100 corresponding alarm was last sent or acknowledged. 

101 

102 This property is used to specify when an alarm was last sent or acknowledged. 

103 This allows clients to determine when a pending alarm has been acknowledged 

104 by a calendar user so that any alerts can be dismissed across multiple devices. 

105 It also allows clients to track repeating alarms or alarms on recurring events or 

106 to-dos to ensure that the right number of missed alarms can be tracked. 

107 

108 Clients SHOULD set this property to the current date-time value in UTC 

109 when a calendar user acknowledges a pending alarm. Certain kinds of alarms, 

110 such as email-based alerts, might not provide feedback as to when the calendar user 

111 sees them. For those kinds of alarms, the client SHOULD set this property 

112 when the alarm is triggered and the action is successfully carried out. 

113 

114 When an alarm is triggered on a client, clients can check to see if an "ACKNOWLEDGED" 

115 property is present. If it is, and the value of that property is greater than or 

116 equal to the computed trigger time for the alarm, then the client SHOULD NOT trigger 

117 the alarm. Similarly, if an alarm has been triggered and 

118 an "alert" has been presented to a calendar user, clients can monitor 

119 the iCalendar data to determine whether an "ACKNOWLEDGED" property is added or 

120 changed in the alarm component. If the value of any "ACKNOWLEDGED" property 

121 in the alarm changes and is greater than or equal to the trigger time of the alarm, 

122 then clients SHOULD dismiss or cancel any "alert" presented to the calendar user. 

123 """, # noqa: E501 

124 ) 

125 

126 TRIGGER = create_single_property( 

127 "TRIGGER", 

128 "dt", 

129 (datetime, timedelta), 

130 Optional[Union[timedelta, datetime]], 

131 """Purpose: This property specifies when an alarm will trigger. 

132 

133 Value Type: The default value type is DURATION. The value type can 

134 be set to a DATE-TIME value type, in which case the value MUST 

135 specify a UTC-formatted DATE-TIME value. 

136 

137 Either a positive or negative duration may be specified for the 

138 "TRIGGER" property. An alarm with a positive duration is 

139 triggered after the associated start or end of the event or to-do. 

140 An alarm with a negative duration is triggered before the 

141 associated start or end of the event or to-do.""", 

142 ) 

143 

144 @property 

145 def TRIGGER_RELATED(self) -> str: # noqa: N802 

146 """The RELATED parameter of the TRIGGER property. 

147 

148 Values are either "START" (default) or "END". 

149 

150 A value of START will set the alarm to trigger off the 

151 start of the associated event or to-do. A value of END will set 

152 the alarm to trigger off the end of the associated event or to-do. 

153 

154 In this example, we create an alarm that triggers two hours after the 

155 end of its parent component: 

156 

157 >>> from icalendar import Alarm 

158 >>> from datetime import timedelta 

159 >>> alarm = Alarm() 

160 >>> alarm.TRIGGER = timedelta(hours=2) 

161 >>> alarm.TRIGGER_RELATED = "END" 

162 """ 

163 trigger = self.get("TRIGGER") 

164 if trigger is None: 

165 return "START" 

166 return trigger.params.get("RELATED", "START") 

167 

168 @TRIGGER_RELATED.setter 

169 def TRIGGER_RELATED(self, value: str): # noqa: N802 

170 """Set "START" or "END".""" 

171 trigger = self.get("TRIGGER") 

172 if trigger is None: 

173 raise ValueError( 

174 "You must set a TRIGGER before setting the RELATED parameter." 

175 ) 

176 trigger.params["RELATED"] = value 

177 

178 class Triggers(NamedTuple): 

179 """The computed times of alarm triggers. 

180 

181 start - triggers relative to the start of the Event or Todo (timedelta) 

182 

183 end - triggers relative to the end of the Event or Todo (timedelta) 

184 

185 absolute - triggers at a datetime in UTC 

186 """ 

187 

188 start: tuple[timedelta] 

189 end: tuple[timedelta] 

190 absolute: tuple[datetime] 

191 

192 @property 

193 def triggers(self): 

194 """The computed triggers of an Alarm. 

195 

196 This takes the TRIGGER, DURATION and REPEAT properties into account. 

197 

198 Here, we create an alarm that triggers 3 times before the start of the 

199 parent component: 

200 

201 >>> from icalendar import Alarm 

202 >>> from datetime import timedelta 

203 >>> alarm = Alarm() 

204 >>> alarm.TRIGGER = timedelta(hours=-4) # trigger 4 hours before START 

205 >>> alarm.DURATION = timedelta(hours=1) # after 1 hour trigger again 

206 >>> alarm.REPEAT = 2 # trigger 2 more times 

207 >>> alarm.triggers.start == (timedelta(hours=-4), timedelta(hours=-3), timedelta(hours=-2)) 

208 True 

209 >>> alarm.triggers.end 

210 () 

211 >>> alarm.triggers.absolute 

212 () 

213 """ # noqa: E501 

214 start = [] 

215 end = [] 

216 absolute = [] 

217 trigger = self.TRIGGER 

218 if trigger is not None: 

219 if isinstance(trigger, date): 

220 absolute.append(trigger) 

221 add = absolute 

222 elif self.TRIGGER_RELATED == "START": 

223 start.append(trigger) 

224 add = start 

225 else: 

226 end.append(trigger) 

227 add = end 

228 duration = self.DURATION 

229 if duration is not None: 

230 for _ in range(self.REPEAT): 

231 add.append(add[-1] + duration) 

232 return self.Triggers( 

233 start=tuple(start), end=tuple(end), absolute=tuple(absolute) 

234 ) 

235 

236 uid = single_string_property( 

237 "UID", 

238 uid_property.__doc__, 

239 "X-ALARMUID", 

240 ) 

241 summary = summary_property 

242 description = description_property 

243 attendees = attendees_property 

244 

245 @classmethod 

246 def new( 

247 cls, 

248 /, 

249 attendees: Optional[list[vCalAddress]] = None, 

250 description: Optional[str] = None, 

251 summary: Optional[str] = None, 

252 uid: Optional[str | uuid.UUID] = None, 

253 ): 

254 """Create a new alarm with all required properties. 

255 

256 This creates a new Alarm in accordance with :rfc:`5545`. 

257 

258 Arguments: 

259 attendees: The :attr:`attendees` of the alarm. 

260 description: The :attr:`description` of the alarm. 

261 summary: The :attr:`summary` of the alarm. 

262 uid: The :attr:`uid` of the alarm. 

263 

264 Returns: 

265 :class:`Alarm` 

266 

267 Raises: 

268 InvalidCalendar: If the content is not valid according to :rfc:`5545`. 

269 

270 .. warning:: As time progresses, we will be stricter with the validation. 

271 """ 

272 alarm = super().new() 

273 alarm.summary = summary 

274 alarm.description = description 

275 alarm.uid = uid 

276 alarm.attendees = attendees 

277 return alarm 

278 

279 

280__all__ = ["Alarm"]