joonis Logo

ZpCron - Cronjobs for Zope

With ZpCron you can schedule cron-like tasks on Zope. Unfortunately it has a buggy and erroneous crontab parser we have to fix.

  1. At first make sure to get the revised version of ZpCron.
  2. Create a file named cron.py within the ZpCron products folder and insert the code from the Crontab Scheduler.
  3. Now we have to patch some files of ZpCron:

__init__.py (Make it work with current Zope versions)

--- __init__.py
+++ __init__patched.py
@@ -27,7 +27,15 @@

 def initialize(context):
     try:
-        cp = context._ProductContext__app.Control_Panel
+        try:
+            from Zope2 import bobo_application
+        except ImportError:
+            bobo_application = None
+        if bobo_application is not None:
+            app = bobo_application()
+        else:
+            app = context._ProductContext__app
+        cp = app.Control_Panel

         if not getattr(aq_base(cp), 'ZpCron', None):
             zLOG.LOG('ZpCron', zLOG.INFO, 'Adding ZpCron to Control Panel')

crontab.py (This patch fixes the crontab parser and time calculator)

--- crontab.py
+++ crontab_patched.py
@@ -16,8 +16,7 @@

 import time
 import datetime
-import calendar
-import sys
+from cron import SimpleCrontab
 import bisect

 class CronTab(object):
@@ -59,42 +58,8 @@
         return command
     ### </override this> ###

-    def getMinMax(self, times, type):
-#        if type == 2:
-#            monthmax = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
-#            month = times[3]
-#            return 1, monthmax[month]
-#        else:
-        minmax = ((0, 59), (0, 23), (1, 29), (1, 12), (0, 7))
-        return minmax[type]
-
     def normalizeTimes(self, times):
-        ret = []
-        for type, t1 in enumerate(times):
-            min, max = self.getMinMax(ret, type)
-            x = []
-            for t in t1.split(','):
-                t = t.split('/')
-                if len(t) > 1:
-                    interval = int(t[1])
-                else:
-                    interval = 1
-                if t[0] == '*':
-                    x.extend(range(min, max, interval))
-                else:
-                    t = t[0].split('-')
-                    if len(t) > 1:
-                        x.extend(range(int(t[0]), int(t[1])+1, interval))
-                    else:
-                        x.append(int(t[0]))
-            x = list(set(x))
-            x.sort()
-            ret.append(x)
-        tmp = ret[-1]
-        ret[-1] = set()
-        for i in tmp:
-            ret[-1].add((i+6)%7)
-        return ret
+        return SimpleCrontab(times, None)

     def addLine(self, extime, cmd, now = None):
         if now is None:
@@ -137,36 +102,7 @@
     def calculateNextTime(self, extime, now = None):
         if now is None:
             now = time.time()
-        year, mon, day, hour, min, sec, wday, yday, isdst = time.localtime(now)
-        MIN, HOUR, DAY, MON, WDAY = extime
-        for i in MIN:
-            if i > min:
-                min = i
-                break
-        else:
-            min = MIN[0]
-            for i in HOUR:
-                if i > hour:
-                    hour = i
-                    break
-            else:
-                hour = HOUR[0]
-                while True:
-                    for i in DAY:
-                        if i > day:
-                            day = i
-                            break
-                    else:
-                        day = DAY[0]
-                        for i in MON:
-                            if i > mon:
-                                mon = i
-                                break
-                        else:
-                            mon = MON[0]
-                    if calendar.weekday(year, mon, day) in WDAY:
-                        break
-        return time.mktime((year, mon, day, hour, min, 0, wday, -1, -1))
+        return extime.next_run(now)

     def getFirstEvents(self, now = None):
         if now is None:

Product.py (This patch fixes an infinite timer recursion when updating the crontab and adds support for transactions)

--- Product.py
+++ Product_patched.py
@@ -85,12 +85,12 @@

         self.reset()
         self.parseFile(cStringIO.StringIO(self.crontab))
-        if timerObject is not None:
-            timerObject.change()
+        self.restart()

     # execution
     @staticmethod
     def tick():
+        import transaction
         from Zope2 import DB
         conn = DB.open()

@@ -125,13 +125,16 @@
             obj = app.restrictedTraverse(url)
             output = None
             try:
+                transaction.begin()
                 output = obj(**dict(params))
+                transaction.commit()
             except:
                 exc, val, trac = sys.exc_info()
                 try:
                     output = zpublisher_exception_hook(obj, app.REQUEST, exc, val, trac)
                 except:
                     pass
+                transaction.abort()
             if output:
                 ret.append(output)

Done.

Can I
  help you?


Just drop me a line at
giraffe@joonis.de