Files
test-auth/django_cron/.svn/text-base/base.py.svn-base
2010-02-27 19:02:08 +01:00

128 lines
4.0 KiB
Plaintext

"""
Copyright (c) 2007-2008, Dj Gilcrease
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import cPickle
from threading import Timer
from datetime import datetime
from django.dispatch import dispatcher
from django.conf import settings
from signals import cron_done
import models
# how often to check if jobs are ready to be run (in seconds)
# in reality if you have a multithreaded server, it may get checked
# more often that this number suggests, so keep an eye on it...
# default value: 300 seconds == 5 min
polling_frequency = getattr(settings, "CRON_POLLING_FREQUENCY", 300)
class Job(object):
# 86400 seconds == 24 hours
run_every = 86400
def run(self, *args, **kwargs):
self.job()
cron_done.send(sender=self, *args, **kwargs)
def job(self):
"""
Should be overridden (this way is cleaner, but the old way - overriding run() - will still work)
"""
pass
class CronScheduler(object):
def register(self, job_class, *args, **kwargs):
"""
Register the given Job with the scheduler class
"""
job_instance = job_class()
if not isinstance(job_instance, Job):
raise TypeError("You can only register a Job not a %r" % job_class)
job, created = models.Job.objects.get_or_create(name=str(job_instance.__class__))
if created:
job.instance = cPickle.dumps(job_instance)
job.args = cPickle.dumps(args)
job.kwargs = cPickle.dumps(kwargs)
job.run_frequency = job_instance.run_every
job.save()
def execute(self):
"""
Queue all Jobs for execution
"""
status, created = models.Cron.objects.get_or_create(pk=1)
# This is important for 2 reasons:
# 1. It keeps us for running more than one instance of the
# same job at a time
# 2. It reduces the number of polling threads because they
# get killed off if they happen to check while another
# one is already executing a job (only occurs with
# multi-threaded servers)
if status.executing:
return
status.executing = True
try:
status.save()
except:
# this will fail if you're debugging, so we want it
# to fail silently and start the timer again so we
# can pick up where we left off once debugging is done
Timer(polling_frequency, self.execute).start()
return
jobs = models.Job.objects.all()
for job in jobs:
if job.queued:
time_delta = datetime.now() - job.last_run
if (time_delta.seconds + 86400*time_delta.days) > job.run_frequency:
inst = cPickle.loads(str(job.instance))
args = cPickle.loads(str(job.args))
kwargs = cPickle.loads(str(job.kwargs))
try:
inst.run(*args, **kwargs)
job.last_run = datetime.now()
job.save()
except Exception:
# if the job throws an error, just remove it from
# the queue. That way we can find/fix the error and
# requeue the job manually
job.queued = False
job.save()
status.executing = False
status.save()
# Set up for this function to run again
Timer(polling_frequency, self.execute).start()
cronScheduler = CronScheduler()