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 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 

25 

26if TYPE_CHECKING: 

27 import uuid 

28 

29 from icalendar.prop import vCalAddress 

30 

31 

32class Alarm(Component): 

33 """ 

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

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

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

37 pending event or an overdue to-do. 

38 """ 

39 

40 name = "VALARM" 

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

42 required = ( 

43 "ACTION", 

44 "TRIGGER", 

45 ) 

46 singletons = ( 

47 "ATTACH", 

48 "ACTION", 

49 "DESCRIPTION", 

50 "SUMMARY", 

51 "TRIGGER", 

52 "DURATION", 

53 "REPEAT", 

54 "UID", 

55 "PROXIMITY", 

56 "ACKNOWLEDGED", 

57 ) 

58 inclusive = ( 

59 ( 

60 "DURATION", 

61 "REPEAT", 

62 ), 

63 ( 

64 "SUMMARY", 

65 "ATTENDEE", 

66 ), 

67 ) 

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

69 

70 REPEAT = single_int_property( 

71 "REPEAT", 

72 0, 

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

74 

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

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

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

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

79 The "REPEAT" property specifies the number of additional 

80 repetitions that the alarm will be triggered. This repetition 

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

82 """, 

83 ) 

84 

85 DURATION = property( 

86 property_get_duration, 

87 property_set_duration, 

88 property_del_duration, 

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

90 

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

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

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

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

95 """, 

96 ) 

97 

98 ACKNOWLEDGED = single_utc_property( 

99 "ACKNOWLEDGED", 

100 """This is defined in RFC 9074: 

101 

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

103 corresponding alarm was last sent or acknowledged. 

104 

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

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

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

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

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

110 

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

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

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

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

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

116 

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

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

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

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

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

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

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

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

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

126 """, # noqa: E501 

127 ) 

128 

129 TRIGGER = create_single_property( 

130 "TRIGGER", 

131 "dt", 

132 (datetime, timedelta), 

133 Optional[Union[timedelta, datetime]], 

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

135 

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

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

138 specify a UTC-formatted DATE-TIME value. 

139 

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

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

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

143 An alarm with a negative duration is triggered before the 

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

145 ) 

146 

147 @property 

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

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

150 

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

152 

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

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

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

156 

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

158 end of its parent component: 

159 

160 >>> from icalendar import Alarm 

161 >>> from datetime import timedelta 

162 >>> alarm = Alarm() 

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

164 >>> alarm.TRIGGER_RELATED = "END" 

165 """ 

166 trigger = self.get("TRIGGER") 

167 if trigger is None: 

168 return "START" 

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

170 

171 @TRIGGER_RELATED.setter 

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

173 """Set "START" or "END".""" 

174 trigger = self.get("TRIGGER") 

175 if trigger is None: 

176 raise ValueError( 

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

178 ) 

179 trigger.params["RELATED"] = value 

180 

181 class Triggers(NamedTuple): 

182 """The computed times of alarm triggers. 

183 

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

185 

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

187 

188 absolute - triggers at a datetime in UTC 

189 """ 

190 

191 start: tuple[timedelta] 

192 end: tuple[timedelta] 

193 absolute: tuple[datetime] 

194 

195 @property 

196 def triggers(self): 

197 """The computed triggers of an Alarm. 

198 

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

200 

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

202 parent component: 

203 

204 >>> from icalendar import Alarm 

205 >>> from datetime import timedelta 

206 >>> alarm = Alarm() 

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

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

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

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

211 True 

212 >>> alarm.triggers.end 

213 () 

214 >>> alarm.triggers.absolute 

215 () 

216 """ # noqa: E501 

217 start = [] 

218 end = [] 

219 absolute = [] 

220 trigger = self.TRIGGER 

221 if trigger is not None: 

222 if isinstance(trigger, date): 

223 absolute.append(trigger) 

224 add = absolute 

225 elif self.TRIGGER_RELATED == "START": 

226 start.append(trigger) 

227 add = start 

228 else: 

229 end.append(trigger) 

230 add = end 

231 duration = self.DURATION 

232 if duration is not None: 

233 for _ in range(self.REPEAT): 

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

235 return self.Triggers( 

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

237 ) 

238 

239 uid = single_string_property( 

240 "UID", 

241 uid_property.__doc__, 

242 "X-ALARMUID", 

243 ) 

244 summary = summary_property 

245 description = description_property 

246 attendees = attendees_property 

247 

248 @classmethod 

249 def new( 

250 cls, 

251 /, 

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

253 concepts: CONCEPTS_TYPE_SETTER = None, 

254 description: Optional[str] = None, 

255 links: LINKS_TYPE_SETTER = None, 

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

257 related_to: RELATED_TO_TYPE_SETTER = None, 

258 summary: Optional[str] = None, 

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

260 ): 

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

262 

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

264 

265 Arguments: 

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

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

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

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

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

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

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

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

274 

275 Returns: 

276 :class:`Alarm` 

277 

278 Raises: 

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

280 

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

282 """ 

283 alarm: Alarm = super().new( 

284 links=links, 

285 related_to=related_to, 

286 refids=refids, 

287 concepts=concepts, 

288 ) 

289 alarm.summary = summary 

290 alarm.description = description 

291 alarm.uid = uid 

292 alarm.attendees = attendees 

293 return alarm 

294 

295 

296__all__ = ["Alarm"]