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

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

72 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 CONCEPTS_TYPE_SETTER, 

10 LINKS_TYPE_SETTER, 

11 RELATED_TO_TYPE_SETTER, 

12 attendees_property, 

13 create_single_property, 

14 description_property, 

15 property_del_duration, 

16 property_get_duration, 

17 property_set_duration, 

18 single_int_property, 

19 single_string_property, 

20 single_utc_property, 

21 summary_property, 

22 uid_property, 

23) 

24from icalendar.cal.component import Component 

25from icalendar.cal.examples import get_example 

26 

27if TYPE_CHECKING: 

28 import uuid 

29 

30 from icalendar.prop import vCalAddress 

31 

32 

33class Alarm(Component): 

34 """ 

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

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

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

38 pending event or an overdue to-do. 

39 

40 Example: 

41 

42 The following example creates an alarm which uses an audio file from an FTP server.  

43  

44 .. code-block:: pycon 

45 

46 >>> from icalendar import Alarm 

47 >>> alarm = Alarm.example() 

48 >>> print(alarm.to_ical().decode()) 

49 BEGIN:VALARM 

50 ACTION:AUDIO 

51 ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/sounds/bell-01.aud 

52 DURATION:PT15M 

53 REPEAT:4 

54 TRIGGER;VALUE=DATE-TIME:19970317T133000Z 

55 END:VALARM 

56 """ 

57 

58 name = "VALARM" 

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

60 required = ( 

61 "ACTION", 

62 "TRIGGER", 

63 ) 

64 singletons = ( 

65 "ATTACH", 

66 "ACTION", 

67 "DESCRIPTION", 

68 "SUMMARY", 

69 "TRIGGER", 

70 "DURATION", 

71 "REPEAT", 

72 "UID", 

73 "PROXIMITY", 

74 "ACKNOWLEDGED", 

75 ) 

76 inclusive = ( 

77 ( 

78 "DURATION", 

79 "REPEAT", 

80 ), 

81 ( 

82 "SUMMARY", 

83 "ATTENDEE", 

84 ), 

85 ) 

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

87 

88 REPEAT = single_int_property( 

89 "REPEAT", 

90 0, 

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

92 

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

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

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

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

97 The "REPEAT" property specifies the number of additional 

98 repetitions that the alarm will be triggered. This repetition 

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

100 """, 

101 ) 

102 

103 DURATION = property( 

104 property_get_duration, 

105 property_set_duration, 

106 property_del_duration, 

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

108 

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

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

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

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

113 """, 

114 ) 

115 

116 ACKNOWLEDGED = single_utc_property( 

117 "ACKNOWLEDGED", 

118 """This is defined in RFC 9074: 

119 

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

121 corresponding alarm was last sent or acknowledged. 

122 

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

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

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

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

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

128 

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

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

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

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

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

134 

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

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

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

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

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

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

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

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

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

144 """, # noqa: E501 

145 ) 

146 

147 TRIGGER = create_single_property( 

148 "TRIGGER", 

149 "dt", 

150 (datetime, timedelta), 

151 Optional[Union[timedelta, datetime]], 

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

153 

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

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

156 specify a UTC-formatted DATE-TIME value. 

157 

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

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

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

161 An alarm with a negative duration is triggered before the 

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

163 ) 

164 

165 @property 

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

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

168 

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

170 

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

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

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

174 

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

176 end of its parent component: 

177 

178 >>> from icalendar import Alarm 

179 >>> from datetime import timedelta 

180 >>> alarm = Alarm() 

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

182 >>> alarm.TRIGGER_RELATED = "END" 

183 """ 

184 trigger = self.get("TRIGGER") 

185 if trigger is None: 

186 return "START" 

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

188 

189 @TRIGGER_RELATED.setter 

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

191 """Set "START" or "END".""" 

192 trigger = self.get("TRIGGER") 

193 if trigger is None: 

194 raise ValueError( 

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

196 ) 

197 trigger.params["RELATED"] = value 

198 

199 class Triggers(NamedTuple): 

200 """The computed times of alarm triggers. 

201 

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

203 

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

205 

206 absolute - triggers at a datetime in UTC 

207 """ 

208 

209 start: tuple[timedelta] 

210 end: tuple[timedelta] 

211 absolute: tuple[datetime] 

212 

213 @property 

214 def triggers(self): 

215 """The computed triggers of an Alarm. 

216 

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

218 

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

220 parent component: 

221 

222 >>> from icalendar import Alarm 

223 >>> from datetime import timedelta 

224 >>> alarm = Alarm() 

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

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

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

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

229 True 

230 >>> alarm.triggers.end 

231 () 

232 >>> alarm.triggers.absolute 

233 () 

234 """ # noqa: E501 

235 start = [] 

236 end = [] 

237 absolute = [] 

238 trigger = self.TRIGGER 

239 if trigger is not None: 

240 if isinstance(trigger, date): 

241 absolute.append(trigger) 

242 add = absolute 

243 elif self.TRIGGER_RELATED == "START": 

244 start.append(trigger) 

245 add = start 

246 else: 

247 end.append(trigger) 

248 add = end 

249 duration = self.DURATION 

250 if duration is not None: 

251 for _ in range(self.REPEAT): 

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

253 return self.Triggers( 

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

255 ) 

256 

257 uid = single_string_property( 

258 "UID", 

259 uid_property.__doc__, 

260 "X-ALARMUID", 

261 ) 

262 summary = summary_property 

263 description = description_property 

264 attendees = attendees_property 

265 

266 @classmethod 

267 def new( 

268 cls, 

269 /, 

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

271 concepts: CONCEPTS_TYPE_SETTER = None, 

272 description: Optional[str] = None, 

273 links: LINKS_TYPE_SETTER = None, 

274 refids: list[str] | str | None = None, 

275 related_to: RELATED_TO_TYPE_SETTER = None, 

276 summary: Optional[str] = None, 

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

278 ): 

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

280 

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

282 

283 Arguments: 

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

285 concepts: The :attr:`~icalendar.Component.concepts` of the alarm. 

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

287 links: The :attr:`~icalendar.Component.links` of the alarm. 

288 refids: :attr:`~icalendar.Component.refids` of the alarm. 

289 related_to: :attr:`~icalendar.Component.related_to` of the alarm. 

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

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

292 

293 Returns: 

294 :class:`Alarm` 

295 

296 Raises: 

297 ~error.InvalidCalendar: If the content is not valid according to :rfc:`5545`. 

298 

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

300 """ 

301 alarm: Alarm = super().new( 

302 links=links, 

303 related_to=related_to, 

304 refids=refids, 

305 concepts=concepts, 

306 ) 

307 alarm.summary = summary 

308 alarm.description = description 

309 alarm.uid = uid 

310 alarm.attendees = attendees 

311 return alarm 

312 @classmethod 

313 def example(cls, name: str = "example") -> "Alarm": 

314 """Return the alarm example with the given name.""" 

315 return cls.from_ical(get_example("alarms", name)) 

316 

317__all__ = ["Alarm"]