I have a Django form like this.
class TransactionForm(forms.Form):
start = forms.DateField()
end = forms.DateField()
I wanted to change the value before running validation:
def clean_start(self):
start = sef.cleaned_data.get('start')
return bs_to_ad(start) #This function returns date in 'YYYY-MM-DD' format
The problem is that this method runs in forms.ModelForm object but doesn't in forms.Form object.
You can modify the data before calling __init__ method using a custom __init__ method like this
class TransactionForm(forms.Form):
start = forms.DateField()
end = forms.DateField()
def __init__(self, **kwargs):
if "data" in kwargs:
start = kwargs.get("data").get("start")
# modify start
kwargs["data"]["start"] = start
super().__init__(self, **kwargs)
Simply doing this works fine.
class TransactionFrom(forms.Form):
start = forms.DateField()
end = forms.DateField()
def clean(self):
data = self.cleaned_data
if 'start' in data.keys():
start = bs_to_ad(data.get('start'))
if 'end' in data.keys():
end = bs_to_ad(data.get('end'))
self.cleaned_data.update({'start': start, 'end': end})
I'm looking for a library in Python which will provide at and cron like functionality.
I'd quite like have a pure Python solution, rather than relying on tools installed on the box; this way I run on machines with no cron.
For those unfamiliar with cron: you can schedule tasks based upon an expression like:
0 2 * * 7 /usr/bin/run-backup # run the backups at 0200 on Every Sunday
0 9-17/2 * * 1-5 /usr/bin/purge-temps # run the purge temps command, every 2 hours between 9am and 5pm on Mondays to Fridays.
The cron time expression syntax is less important, but I would like to have something with this sort of flexibility.
If there isn't something that does this for me out-the-box, any suggestions for the building blocks to make something like this would be gratefully received.
Edit
I'm not interested in launching processes, just "jobs" also written in Python - python functions. By necessity I think this would be a different thread, but not in a different process.
To this end, I'm looking for the expressivity of the cron time expression, but in Python.
Cron has been around for years, but I'm trying to be as portable as possible. I cannot rely on its presence.
If you're looking for something lightweight checkout schedule:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
while 1:
schedule.run_pending()
time.sleep(1)
Disclosure: I'm the author of that library.
You could just use normal Python argument passing syntax to specify your crontab. For example, suppose we define an Event class as below:
from datetime import datetime, timedelta
import time
# Some utility classes / functions first
class AllMatch(set):
"""Universal set - match everything"""
def __contains__(self, item): return True
allMatch = AllMatch()
def conv_to_set(obj): # Allow single integer to be provided
if isinstance(obj, (int,long)):
return set([obj]) # Single item
if not isinstance(obj, set):
obj = set(obj)
return obj
# The actual Event class
class Event(object):
def __init__(self, action, min=allMatch, hour=allMatch,
day=allMatch, month=allMatch, dow=allMatch,
args=(), kwargs={}):
self.mins = conv_to_set(min)
self.hours= conv_to_set(hour)
self.days = conv_to_set(day)
self.months = conv_to_set(month)
self.dow = conv_to_set(dow)
self.action = action
self.args = args
self.kwargs = kwargs
def matchtime(self, t):
"""Return True if this event should trigger at the specified datetime"""
return ((t.minute in self.mins) and
(t.hour in self.hours) and
(t.day in self.days) and
(t.month in self.months) and
(t.weekday() in self.dow))
def check(self, t):
if self.matchtime(t):
self.action(*self.args, **self.kwargs)
(Note: Not thoroughly tested)
Then your CronTab can be specified in normal python syntax as:
c = CronTab(
Event(perform_backup, 0, 2, dow=6 ),
Event(purge_temps, 0, range(9,18,2), dow=range(0,5))
)
This way you get the full power of Python's argument mechanics (mixing positional and keyword args, and can use symbolic names for names of weeks and months)
The CronTab class would be defined as simply sleeping in minute increments, and calling check() on each event. (There are probably some subtleties with daylight savings time / timezones to be wary of though). Here's a quick implementation:
class CronTab(object):
def __init__(self, *events):
self.events = events
def run(self):
t=datetime(*datetime.now().timetuple()[:5])
while 1:
for e in self.events:
e.check(t)
t += timedelta(minutes=1)
while datetime.now() < t:
time.sleep((t - datetime.now()).seconds)
A few things to note: Python's weekdays / months are zero indexed (unlike cron), and that range excludes the last element, hence syntax like "1-5" becomes range(0,5) - ie [0,1,2,3,4]. If you prefer cron syntax, parsing it shouldn't be too difficult however.
More or less same as above but concurrent using gevent :)
"""Gevent based crontab implementation"""
from datetime import datetime, timedelta
import gevent
# Some utility classes / functions first
def conv_to_set(obj):
"""Converts to set allowing single integer to be provided"""
if isinstance(obj, (int, long)):
return set([obj]) # Single item
if not isinstance(obj, set):
obj = set(obj)
return obj
class AllMatch(set):
"""Universal set - match everything"""
def __contains__(self, item):
return True
allMatch = AllMatch()
class Event(object):
"""The Actual Event Class"""
def __init__(self, action, minute=allMatch, hour=allMatch,
day=allMatch, month=allMatch, daysofweek=allMatch,
args=(), kwargs={}):
self.mins = conv_to_set(minute)
self.hours = conv_to_set(hour)
self.days = conv_to_set(day)
self.months = conv_to_set(month)
self.daysofweek = conv_to_set(daysofweek)
self.action = action
self.args = args
self.kwargs = kwargs
def matchtime(self, t1):
"""Return True if this event should trigger at the specified datetime"""
return ((t1.minute in self.mins) and
(t1.hour in self.hours) and
(t1.day in self.days) and
(t1.month in self.months) and
(t1.weekday() in self.daysofweek))
def check(self, t):
"""Check and run action if needed"""
if self.matchtime(t):
self.action(*self.args, **self.kwargs)
class CronTab(object):
"""The crontab implementation"""
def __init__(self, *events):
self.events = events
def _check(self):
"""Check all events in separate greenlets"""
t1 = datetime(*datetime.now().timetuple()[:5])
for event in self.events:
gevent.spawn(event.check, t1)
t1 += timedelta(minutes=1)
s1 = (t1 - datetime.now()).seconds + 1
print "Checking again in %s seconds" % s1
job = gevent.spawn_later(s1, self._check)
def run(self):
"""Run the cron forever"""
self._check()
while True:
gevent.sleep(60)
import os
def test_task():
"""Just an example that sends a bell and asd to all terminals"""
os.system('echo asd | wall')
cron = CronTab(
Event(test_task, 22, 1 ),
Event(test_task, 0, range(9,18,2), daysofweek=range(0,5)),
)
cron.run()
None of the listed solutions even attempt to parse a complex cron schedule string. So, here is my version, using croniter. Basic gist:
schedule = "*/5 * * * *" # Run every five minutes
nextRunTime = getNextCronRunTime(schedule)
while True:
roundedDownTime = roundDownTime()
if (roundedDownTime == nextRunTime):
####################################
### Do your periodic thing here. ###
####################################
nextRunTime = getNextCronRunTime(schedule)
elif (roundedDownTime > nextRunTime):
# We missed an execution. Error. Re initialize.
nextRunTime = getNextCronRunTime(schedule)
sleepTillTopOfNextMinute()
Helper routines:
from croniter import croniter
from datetime import datetime, timedelta
# Round time down to the top of the previous minute
def roundDownTime(dt=None, dateDelta=timedelta(minutes=1)):
roundTo = dateDelta.total_seconds()
if dt == None : dt = datetime.now()
seconds = (dt - dt.min).seconds
rounding = (seconds+roundTo/2) // roundTo * roundTo
return dt + timedelta(0,rounding-seconds,-dt.microsecond)
# Get next run time from now, based on schedule specified by cron string
def getNextCronRunTime(schedule):
return croniter(schedule, datetime.now()).get_next(datetime)
# Sleep till the top of the next minute
def sleepTillTopOfNextMinute():
t = datetime.utcnow()
sleeptime = 60 - (t.second + t.microsecond/1000000.0)
time.sleep(sleeptime)
I know there are a lot of answers, but another solution could be to go with decorators. This is an example to repeat a function everyday at a specific time. The cool think about using this way is that you only need to add the Syntactic Sugar to the function you want to schedule:
#repeatEveryDay(hour=6, minutes=30)
def sayHello(name):
print(f"Hello {name}")
sayHello("Bob") # Now this function will be invoked every day at 6.30 a.m
And the decorator will look like:
def repeatEveryDay(hour, minutes=0, seconds=0):
"""
Decorator that will run the decorated function everyday at that hour, minutes and seconds.
:param hour: 0-24
:param minutes: 0-60 (Optional)
:param seconds: 0-60 (Optional)
"""
def decoratorRepeat(func):
#functools.wraps(func)
def wrapperRepeat(*args, **kwargs):
def getLocalTime():
return datetime.datetime.fromtimestamp(time.mktime(time.localtime()))
# Get the datetime of the first function call
td = datetime.timedelta(seconds=15)
if wrapperRepeat.nextSent == None:
now = getLocalTime()
wrapperRepeat.nextSent = datetime.datetime(now.year, now.month, now.day, hour, minutes, seconds)
if wrapperRepeat.nextSent < now:
wrapperRepeat.nextSent += td
# Waiting till next day
while getLocalTime() < wrapperRepeat.nextSent:
time.sleep(1)
# Call the function
func(*args, **kwargs)
# Get the datetime of the next function call
wrapperRepeat.nextSent += td
wrapperRepeat(*args, **kwargs)
wrapperRepeat.nextSent = None
return wrapperRepeat
return decoratorRepeat
I like how the pycron package solves this problem.
import pycron
import time
while True:
if pycron.is_now('0 2 * * 0'): # True Every Sunday at 02:00
print('running backup')
time.sleep(60) # The process should take at least 60 sec
# to avoid running twice in one minute
else:
time.sleep(15) # Check again in 15 seconds
There isn't a "pure python" way to do this because some other process would have to launch python in order to run your solution. Every platform will have one or twenty different ways to launch processes and monitor their progress. On unix platforms, cron is the old standard. On Mac OS X there is also launchd, which combines cron-like launching with watchdog functionality that can keep your process alive if that's what you want. Once python is running, then you can use the sched module to schedule tasks.
Another trivial solution would be:
from aqcron import At
from time import sleep
from datetime import datetime
# Event scheduling
event_1 = At( second=5 )
event_2 = At( second=[0,20,40] )
while True:
now = datetime.now()
# Event check
if now in event_1: print "event_1"
if now in event_2: print "event_2"
sleep(1)
And the class aqcron.At is:
# aqcron.py
class At(object):
def __init__(self, year=None, month=None,
day=None, weekday=None,
hour=None, minute=None,
second=None):
loc = locals()
loc.pop("self")
self.at = dict((k, v) for k, v in loc.iteritems() if v != None)
def __contains__(self, now):
for k in self.at.keys():
try:
if not getattr(now, k) in self.at[k]: return False
except TypeError:
if self.at[k] != getattr(now, k): return False
return True
I don't know if something like that already exists. It would be easy to write your own with time, datetime and/or calendar modules, see http://docs.python.org/library/time.html
The only concern for a python solution is that your job needs to be always running and possibly be automatically "resurrected" after a reboot, something for which you do need to rely on system dependent solutions.
I am trying to expire tokens after its creation with a max duration of 1 minute to meet security requirements. my function looks like this, but I don't think is doing it the right way, and I Would like to know what is the best way to expire the token after 1 minute? I am using the technique of diffing two times. the following function works under models.py
def is_token_expired(self):
if self.token == None:
return False
now = datetime.datetime.utcnow().replace(tzinfo=utc)
timediff = now - self.created_at
if timediff.seconds / 60 - 1 > 0:
return True
return False
I think the elegant way to archive your goal is leveraging django cache.
Sample code:
class Foo(models.Model):
...
def save(self, *args, **kwargs):
if not self.pk:
# save the token when record created
cache.set('token_key', '<Your token>', timeout=60)
super(Foo, self).save(*args, **kwargs)
#property
def is_token_expired(self):
# check if the token expired
return cache.get('token_key') is None
#property
def token(self):
# get token
return cache.get('token_key')
It is better to use #property in your model:
from datetime import timedelta
class Foo(models.Model):
some_field = models.CharField()
creation_date = models.DateTimeField(auto_now_add=True)
#property
def is_expired(self):
if datetime.now > (self.creation_date + timedelta(minutes=1)):
return True
return False
you can change timedelta(minutes=1) to amount that your token is valid.
and use it in your code like this:
if your_instance.is_expired == True:
# do something
You can also use Django builtin cache system (that Enix mentioned) as a better approach.
I have a model like this;
starttime = models.TimeField('Show Start Time', )
duration = models.DurationField('Duration',)
endtime = models.TimeField('Show End Time (Optional)',blank=True, null=True )
with the starttime and duration I am trying to arrive the endtime while storing the object;
def save(self, *args, **kwargs):
startdelta=timedelta(hours=self.starttime.hour,minutes=self.starttime.minute,seconds=self.starttime.second)
enddelta = startdelta + self.duration
self.endtime = enddelta
super(Showsets, self).save(*args, **kwargs)
Above code throws me error, I want to know how the timefield and duration field works in django also please assist with the ways to query the fields based on starttime or endtime (like objects that starts (starttime) in 30mins from now).
Also curious to know if there are any django-app(add-ons) for time based querying.
Thanks a ton!
Not sure if this will be a good solution for your circumstance but I normally do something like:
start_time = models.TimeField()
end_time = models.TimeField()
def duration(self):
return self.end_time - self.start_time
It seems like a much more concise solution than storing start time with duration and then calculating the end_time on save().
You can combine starttime with today's date and add duration:
from datetime import datetime, date
self.endtime = (datetime.combine(date.today(), self.starttime) + self.duration).time()
I've been trying to make this class called Time with has the attributes of hour,minutes and seconds, that also has accessor functions and mutator functions such as set_hour, increment_hour and so on.
This is my code, I cannot get it to work I get the error Time is not defined or t is not defined when i switch the last lines around. Python 3.2.5 by the way.
class Time:
"""The Time class defines the time with
attributes: hour, minute, second
"""
#Attributes
hour = 12
minutes = 00
seconds = 00
#Functions
def get_hour(self):
return self.hour
def get_minute(self):
return self.minute
def get_second(self):
return self.second
def print_time(self):
print("Hello, the current time is", self.hour,":",self.minute,":",self.second)
def set_hour(self, new_hour):
self.hour = new_hour
t.set_hour("1")
print(t.get_hour())
print(t.print_time())
t = Time()
It seems you are invoking the method set_hour("1") on a variable t before that variable has been initialized by t = Time().
EDIT: and correct the indentation as said in the comments. I'm not a Python programmer so I didn't catch that.