Skip to content

Commit 1993645

Browse files
committed
feat(crontabs): add bunch of generation functions
1 parent 692a624 commit 1993645

File tree

3 files changed

+451
-4
lines changed

3 files changed

+451
-4
lines changed

iarp_utils/crontabs.py

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import datetime
2+
import random
13
import re
24

35
from .datetimes import weekday
@@ -153,3 +155,260 @@ def parse(crontab):
153155
day_of_week = CrontabParser(8).parse(day_of_week_raw)
154156

155157
return minutes, hours, days_of_month, months_of_year, day_of_week
158+
159+
160+
def isoweekday_sunday_zero(isoweekday):
161+
# Take datetime isoweekday value and convert it to crontab 0-6
162+
return 0 if isoweekday == 7 else isoweekday
163+
164+
165+
def is_active_now(crontab, now):
166+
minutes, hours, days_of_month, months_of_year, day_of_week = parse(crontab)
167+
168+
# print(f"{minutes=}")
169+
# print(f"{hours=}")
170+
# print(f"{days_of_month=}")
171+
# print(f"{months_of_year=}")
172+
# print(f"{day_of_week=}")
173+
174+
# minute calculation only works if this task runs and
175+
# completes within the minute it's expected to run.
176+
# If it falls outside the expected minute, then the
177+
# channel will not get scanned.
178+
if now.minute not in minutes:
179+
return
180+
if now.hour not in hours:
181+
return
182+
if now.day not in days_of_month:
183+
return
184+
185+
# weekday and isoweekday returns variations of 1-7, crontab needs 0-6
186+
if isoweekday_sunday_zero(now.isoweekday()) not in day_of_week:
187+
return
188+
if now.month not in months_of_year:
189+
return
190+
191+
return True
192+
193+
194+
def calculate_schedule(crontab, check_month=False, period=10, now=None):
195+
if not now:
196+
now = datetime.datetime.now()
197+
now = now.replace(hour=0, minute=0, second=0, microsecond=0)
198+
if check_month:
199+
now = now.replace(day=1)
200+
matched_timestamps = []
201+
start_day = now.day
202+
start_month = now.month
203+
while True:
204+
if is_active_now(crontab, now):
205+
matched_timestamps.append(now)
206+
207+
now = now + datetime.timedelta(minutes=period)
208+
if not check_month and now.day != start_day:
209+
break
210+
elif check_month and now.month != start_month:
211+
break
212+
213+
return matched_timestamps
214+
215+
216+
def validate_crontab_values(minute=None, hour=None, day_of_week=None, day_of_month=None):
217+
if isinstance(minute, int):
218+
if not 0 <= minute <= 59:
219+
raise ValueError(f"crontab minute must be between 0-59, was given {minute=}")
220+
if minute % 10 != 0:
221+
raise ValueError(f"crontab minute value must be divisible by 10, was given {minute=}")
222+
if isinstance(hour, int) and not 0 <= hour <= 23:
223+
raise ValueError(f"crontab hour must be between 0-23, was given {hour=}")
224+
if isinstance(day_of_week, int) and not 0 <= day_of_week <= 6:
225+
raise ValueError(f"crontab day_of_week must be between 0-6, was given {day_of_week=}")
226+
if isinstance(day_of_month, int) and not 1 <= day_of_month <= 31:
227+
raise ValueError(f"crontab day_of_month must be between 1-31, was given {day_of_month=}")
228+
return True
229+
230+
231+
def generate_weekly(minute=None, hour=None, day_of_week=None):
232+
"""
233+
day_of_week can be 0-7 with 0 and 7 meaning sunday.
234+
"""
235+
236+
minutes = list(range(0, 60, 10))
237+
hours = list(range(8, 20))
238+
month = "*"
239+
240+
if isinstance(hour, list):
241+
hour = random.choice(hour)
242+
243+
minute = minute or random.choice(minutes)
244+
hour = hour or random.choice(hours)
245+
day_of_month = "*"
246+
247+
if not day_of_week:
248+
day_of_week = random.choice(list(range(0, 7)))
249+
elif isinstance(day_of_week, list):
250+
day_of_week = random.choice(day_of_week)
251+
252+
validate_crontab_values(minute=minute, hour=hour, day_of_week=day_of_week)
253+
254+
return f"{minute} {hour} {day_of_month} {month} {day_of_week}"
255+
256+
257+
def generate_monthly(minute=None, hour=None, day=None):
258+
259+
if isinstance(hour, list):
260+
hour = random.choice(hour)
261+
262+
minutes = list(range(0, 60, 10))
263+
hours = list(range(8, 20))
264+
days = list(range(1, 29)) # 29 ensures it'll run even in February
265+
month = "*"
266+
267+
minute = minute or random.choice(minutes)
268+
hour = hour or random.choice(hours)
269+
day_of_month = day or random.choice(days)
270+
day_of_week = "*"
271+
272+
validate_crontab_values(minute=minute, hour=hour, day_of_month=day)
273+
274+
return f"{minute} {hour} {day_of_month} {month} {day_of_week}"
275+
276+
277+
def generate_biyearly(minute=None, hour=None, day=None):
278+
279+
minutes = list(range(0, 60, 10))
280+
hours = list(range(8, 20))
281+
days = list(range(1, 29)) # 29 ensures it'll run even in February
282+
month = list(range(1, 13))
283+
284+
if isinstance(hour, list):
285+
hour = random.choice(hour)
286+
287+
minute = minute or random.choice(minutes)
288+
hour = hour or random.choice(hours)
289+
day_of_month = day or random.choice(days)
290+
month1 = random.choice(month)
291+
month2 = (month1 + 6) % 12 or 1
292+
day_of_week = "*"
293+
294+
validate_crontab_values(minute=minute, hour=hour, day_of_month=day)
295+
296+
return f"{minute} {hour} {day_of_month} {month1},{month2} {day_of_week}"
297+
298+
299+
def generate_yearly(minute=None, hour=None, day=None):
300+
301+
minutes = list(range(0, 60, 10))
302+
hours = list(range(8, 20))
303+
days = list(range(1, 29)) # 29 ensures it'll run even in February
304+
month = list(range(1, 13))
305+
306+
if isinstance(hour, list):
307+
hour = random.choice(hour)
308+
309+
minute = minute or random.choice(minutes)
310+
hour = hour or random.choice(hours)
311+
day_of_month = day or random.choice(days)
312+
month = random.choice(month)
313+
day_of_week = "*"
314+
315+
validate_crontab_values(minute=minute, hour=hour)
316+
317+
return f"{minute} {hour} {day_of_month} {month} {day_of_week}"
318+
319+
320+
def generate_every_other_day(minute=None, hour=None):
321+
322+
minutes = list(range(0, 60, 10))
323+
hours = list(range(8, 20))
324+
325+
if isinstance(hour, list):
326+
hour = random.choice(hour)
327+
328+
minute = minute or random.choice(minutes)
329+
hour = hour or random.choice(hours)
330+
day_of_month = "*"
331+
month = "*"
332+
day_of_week = random.choice(["0-7/2", "1-7/2"])
333+
334+
validate_crontab_values(minute=minute, hour=hour)
335+
336+
return f"{minute} {hour} {day_of_month} {month} {day_of_week}"
337+
338+
339+
def generate_daily(minute=None, hour=None):
340+
341+
minutes = list(range(0, 60, 10))
342+
hours = list(range(8, 20))
343+
344+
if isinstance(hour, list):
345+
hour = random.choice(hour)
346+
347+
minute = minute or random.choice(minutes)
348+
hour = hour or random.choice(hours)
349+
day_of_month = "*"
350+
month = "*"
351+
day_of_week = "*"
352+
353+
validate_crontab_values(minute=minute, hour=hour)
354+
355+
return f"{minute} {hour} {day_of_month} {month} {day_of_week}"
356+
357+
358+
def generate_selection_monthly_crontabs(length=22, minute=0, hour=5, increment_minute=10, increment_hour=1):
359+
vals = []
360+
361+
for x in range(length):
362+
363+
day_of_month = random.choice(range(1, 30))
364+
365+
vals.append(f"{minute} {hour} {day_of_month} * *")
366+
367+
minute += increment_minute
368+
if minute >= 60:
369+
minute = 0
370+
hour += increment_hour
371+
372+
return vals
373+
374+
375+
def generate_selection_biweekly_crontabs(length=22, minute=0, hour=13, increment_minute=10, increment_hour=1):
376+
vals = []
377+
378+
week_of_month_options = [
379+
list(range(1, 14 + 1)),
380+
list(range(14, 30 + 1)),
381+
]
382+
383+
for x in range(length):
384+
385+
day_of_week_selection_list = []
386+
for opt in week_of_month_options:
387+
check_day = random.choice(opt)
388+
day_of_week_selection_list.append(str(check_day))
389+
390+
day_of_month = ",".join(day_of_week_selection_list)
391+
392+
vals.append(f"{minute} {hour} {day_of_month} * *")
393+
394+
minute += increment_minute
395+
if minute >= 60:
396+
minute = 0
397+
hour += increment_hour
398+
399+
return vals
400+
401+
402+
def generate_selection_daily_crontabs(length=22, minute=0, hour=16, increment_minute=10, increment_hour=1):
403+
vals = []
404+
405+
for x in range(length):
406+
407+
vals.append(f"{minute} {hour} * * *")
408+
409+
minute += increment_minute
410+
if minute >= 60:
411+
minute = 0
412+
hour += increment_hour
413+
414+
return vals

0 commit comments

Comments
 (0)