Skip to content

Commit

Permalink
added timezone support
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergey Podushkin committed Dec 24, 2012
1 parent 4ca412d commit 7e09a90
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 13 deletions.
17 changes: 17 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>django-background-task</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>
5 changes: 5 additions & 0 deletions .pydevproject
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
</pydev_project>
17 changes: 9 additions & 8 deletions background_task/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.conf import settings

from django.utils import simplejson
from django.utils.timezone import utc
from datetime import datetime, timedelta
from hashlib import sha1
import traceback
Expand All @@ -15,7 +16,7 @@
class TaskManager(models.Manager):

def find_available(self):
now = datetime.now()
now = datetime.utcnow().replace(tzinfo=utc)
qs = self.unlocked(now)
ready = qs.filter(run_at__lte=now, failed_at=None)
return ready.order_by('-priority', 'run_at')
Expand All @@ -32,7 +33,7 @@ def new_task(self, task_name, args=None, kwargs=None,
args = args or ()
kwargs = kwargs or {}
if run_at is None:
run_at = datetime.now()
run_at = datetime.utcnow().replace(tzinfo=utc)

task_params = simplejson.dumps((args, kwargs))
task_hash = sha1(task_name + task_params).hexdigest()
Expand Down Expand Up @@ -78,29 +79,29 @@ def params(self):
return args, kwargs

def lock(self, locked_by):
now = datetime.now()
now = datetime.utcnow().replace(tzinfo=utc)
unlocked = Task.objects.unlocked(now).filter(pk=self.pk)
updated = unlocked.update(locked_by=locked_by, locked_at=now)
if updated:
return Task.objects.get(pk=self.pk)
return None

def _extract_error(self, type, err, tb):
file = StringIO()
traceback.print_exception(type, err, tb, None, file)
return file.getvalue()

def reschedule(self, type, err, traceback):
self.last_error = self._extract_error(type, err, traceback)
max_attempts = getattr(settings, 'MAX_ATTEMPTS', 25)

if self.attempts >= max_attempts:
self.failed_at = datetime.now()
self.failed_at = datetime.utcnow().replace(tzinfo=utc)
logging.warn('Marking task %s as failed', self)
else:
self.attempts += 1
backoff = timedelta(seconds=(self.attempts ** 4) + 5)
self.run_at = datetime.now() + backoff
self.run_at = datetime.utcnow().replace(tzinfo=utc) + backoff
logging.warn('Rescheduling task %s for %s later at %s', self,
backoff, self.run_at)

Expand All @@ -109,7 +110,7 @@ def reschedule(self, type, err, traceback):
self.locked_at = None

self.save()

def save(self, *arg, **kw):
# force NULL rather than empty string
self.locked_by = self.locked_by or None
Expand Down
12 changes: 7 additions & 5 deletions background_task/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime, timedelta
from django.db import transaction
from django.utils.importlib import import_module
from django.utils.timezone import utc


class Tasks(object):
Expand All @@ -19,22 +20,22 @@ def background(self, name=None, schedule=None):
something that gets run asynchronously in
the background, at a later time
'''

# see if used as simple decorator
# where first arg is the function to be decorated
fn = None
if name and callable(name):
fn = name
name = None

def _decorator(fn):
_name = name
if not _name:
_name = '%s.%s' % (fn.__module__, fn.__name__)
proxy = TaskProxy(_name, fn, schedule, self._runner)
self._tasks[_name] = proxy
return proxy

if fn:
return _decorator(fn)

Expand Down Expand Up @@ -87,7 +88,7 @@ def merge(self, schedule):

@property
def run_at(self):
run_at = self._run_at or datetime.now()
run_at = self._run_at or datetime.utcnow().replace(tzinfo=utc)
if isinstance(run_at, int):
run_at = datetime.now() + timedelta(seconds=run_at)
if isinstance(run_at, timedelta):
Expand Down Expand Up @@ -130,7 +131,8 @@ def schedule(self, task_name, args, kwargs, run_at=None,

if action != TaskSchedule.SCHEDULE:
task_hash = task.task_hash
unlocked = Task.objects.unlocked(datetime.now())
now = datetime.utcnow().replace(tzinfo=utc)
unlocked = Task.objects.unlocked(now)
existing = unlocked.filter(task_hash=task_hash)
if action == TaskSchedule.RESCHEDULE_EXISTING:
updated = existing.update(run_at=run_at, priority=priority)
Expand Down

0 comments on commit 7e09a90

Please sign in to comment.