Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Imports 

2 

3from datetime import timedelta 

4from django.db import models 

5from django.utils.translation import gettext_lazy as _ 

6from django.utils.timezone import now 

7from ..audit.utils import is_audit_model 

8 

9# Exports 

10 

11__all__ = ( 

12 "TimedModel", 

13) 

14 

15# Constants 

16 

17SECONDS_PER_DAY = 24 * 60 * 60 

18 

19# Models 

20 

21 

22class TimedModel(models.Model): 

23 """Provides date/time fields for noting a starting time and ending time, as well as convenience methods for 

24 calculating elapsed time. 

25 

26 .. code-block:: python 

27 

28 from superdjango.db.timed.models import TimedModel 

29 

30 class Meeting(TimedModel): 

31 

32 end_date = models.DateField() 

33 end_time = models.TimeField() 

34 is_all_day = models.BooleanField() 

35 start_date = models.DateField() 

36 start_time = models.TimeField() 

37 

38 """ 

39 

40 timer_elapsed_seconds = models.PositiveIntegerField( 

41 _("elapsed seconds"), 

42 blank=True, 

43 help_text=_("Total seconds elapsed from start to end."), 

44 null=True 

45 ) 

46 

47 timer_is_running = models.BooleanField( 

48 _("running"), 

49 default=False, 

50 help_text=_("Indicates whether the timer is running.") 

51 ) 

52 

53 timer_start_dt = models.DateTimeField( 

54 _("start date/time"), 

55 blank=True, 

56 help_text=_("Date and time the timer was started."), 

57 null=True, 

58 ) 

59 

60 timer_stop_dt = models.DateTimeField( 

61 _("stop date/time"), 

62 blank=True, 

63 help_text=_("Date and time the timer was stopped."), 

64 null=True 

65 ) 

66 

67 class Meta: 

68 abstract = True 

69 

70 @property 

71 def elapsed_duration(self): 

72 """Get the elapsed duration either as the current ``timer_elapsed_seconds`` or by calling 

73 ``get_elapsed_duration()``. 

74 

75 :rtype: timedelta 

76 

77 """ 

78 if self.timer_elapsed_seconds: 

79 return timedelta(seconds=self.timer_elapsed_seconds) 

80 

81 return self.get_elapsed_duration() 

82 

83 def get_elapsed_duration(self): 

84 """Get the time that has elapsed as a duration. 

85 

86 :rtype: timedelta 

87 

88 """ 

89 return timedelta(seconds=self.get_timer_elapsed_seconds()) 

90 

91 def get_elapsed_duration_display(self): 

92 """Get a human-friendly display for the duration. 

93 

94 :rtype: str 

95 

96 """ 

97 d = self.get_elapsed_duration() 

98 total_seconds = d.seconds 

99 

100 if total_seconds == 0: 

101 return "" 

102 

103 hours, remainder = divmod(total_seconds, 3600) 

104 minutes, seconds = divmod(remainder, 60) 

105 

106 s = list() 

107 

108 if hours > 0: 

109 s.append(str(hours)) 

110 if hours == 1: 

111 s.append(str(_("hour"))) 

112 else: 

113 s.append(str(_("hours"))) 

114 

115 if minutes > 0: 

116 s.append(str(minutes)) 

117 if minutes == 1: 

118 s.append(str(_("minute"))) 

119 else: 

120 s.append(str(_("minutes"))) 

121 

122 return " ".join(s) 

123 

124 def get_timer_elapsed_seconds(self): 

125 """Get the number of seconds that have passed since the timer started until now *or* until the item ended, if a 

126 stop datetime is available. 

127 

128 :rtype: int 

129 

130 This method will: 

131 

132 - Calculate the elapsed seconds from the start datetime until the current datetime. 

133 - Also include the elapsed seconds already calculated. 

134 

135 """ 

136 if not self.timer_start_dt: 

137 return 0 

138 

139 current_dt = now() 

140 

141 delta = current_dt - self.timer_start_dt 

142 

143 return delta.seconds + (delta.days * SECONDS_PER_DAY) + (self.timer_elapsed_seconds or 0) 

144 

145 def reset_timer(self, user, commit=True): 

146 """Reset the current timer. 

147 

148 :param user: The user updating the record. If the record is an audit model, the ``audit()`` method will be 

149 called. Pass ``None`` to suppress this behavior. 

150 :type user: AUTH_USER_MODEL | None 

151 

152 :param commit: Indicates whether the record should be saved after resetting the timer. 

153 :type commit: bool 

154 

155 :rtype: int 

156 :returns: The value of elapsed seconds prior to the reset. 

157 

158 This method will: 

159 

160 - Remove the stop datetime and set elapsed seconds to ``0``. 

161 - Change the start datetime to the current datetime. 

162 - Ensure that the timer is still running. 

163 

164 """ 

165 current_elapsed_seconds = self.timer_elapsed_seconds or 0 

166 

167 self.timer_elapsed_seconds = 0 

168 self.timer_is_running = True 

169 self.timer_stop_dt = None 

170 self.timer_start_dt = now() 

171 

172 if is_audit_model(self) and user is not None: 

173 # noinspection PyUnresolvedReferences 

174 self.audit(user, commit=commit) 

175 return current_elapsed_seconds 

176 

177 if commit: 

178 self.save(update_fields=["timer_elapsed_seconds", "timer_is_running", "timer_stop_dt", "timer_start_dt"]) 

179 

180 return current_elapsed_seconds 

181 

182 def start_timer(self, user, commit=True): 

183 """Start the timer. 

184 

185 :param user: The user updating the record. If the record is an audit model, the ``audit()`` method will be 

186 called. Pass ``None`` to suppress this behavior. 

187 :type user: AUTH_USER_MODEL | None 

188 

189 :param commit: Indicates whether the record should be saved after 

190 starting the timer. 

191 :type commit: bool 

192 

193 :rtype: int 

194 :returns: The current elapsed seconds if the timer is already running. Otherwise, ``0``. 

195 

196 This method will: 

197 

198 - Set the start datetime to the current datetime. 

199 - Ensure that the time is running. 

200 - Leave the elapsed seconds unchanged. 

201 

202 """ 

203 if self.timer_is_running: 

204 return self.get_timer_elapsed_seconds() 

205 

206 self.timer_is_running = True 

207 self.timer_start_dt = now() 

208 

209 if is_audit_model(self) and user is not None: 

210 # noinspection PyUnresolvedReferences 

211 self.audit(user, commit=commit) 

212 return 0 

213 

214 if commit: 

215 self.save(update_fields=["timer_is_running", "timer_start_dt"]) 

216 

217 return 0 

218 

219 def stop_timer(self, user, commit=True): 

220 """Stop the timer. 

221 

222 :param user: The user updating the record. If the record is an audit model, the ``audit()`` method will be 

223 called. Pass ``None`` to suppress this behavior. 

224 :type user: AUTH_USER_MODEL | None 

225 

226 

227 :param commit: Indicates whether the record should be saved after 

228 stopping the timer. 

229 :type commit: bool 

230 

231 :rtype: int 

232 :returns: The elpased seconds. 

233 

234 This method will: 

235 

236 - Set the stop datetime to the current datetime. 

237 - Update the elapsed seconds. 

238 - Ensure that the time is not running. 

239 

240 """ 

241 if not self.timer_is_running: 

242 return self.get_timer_elapsed_seconds() 

243 

244 self.timer_elapsed_seconds = self.get_timer_elapsed_seconds() 

245 self.timer_is_running = False 

246 self.timer_stop_dt = now() 

247 

248 if is_audit_model(self) and user is not None: 

249 # noinspection PyUnresolvedReferences 

250 self.audit(user, commit=commit) 

251 return self.timer_elapsed_seconds 

252 

253 if commit: 

254 self.save(update_fields=["timer_elapsed_seconds", "timer_is_running", "timer_stop_dt"]) 

255 

256 return self.timer_elapsed_seconds