1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
23 from mx import DateTime
28 from osv import fields,osv
29 from tools.safe_eval import safe_eval as eval
32 return eval('tuple(%s)' % (s or ''))
35 'work_days': lambda interval: DateTime.RelativeDateTime(days=interval),
36 'days': lambda interval: DateTime.RelativeDateTime(days=interval),
37 'hours': lambda interval: DateTime.RelativeDateTime(hours=interval),
38 'weeks': lambda interval: DateTime.RelativeDateTime(days=7*interval),
39 'months': lambda interval: DateTime.RelativeDateTime(months=interval),
40 'minutes': lambda interval: DateTime.RelativeDateTime(minutes=interval),
43 class ir_cron(osv.osv, netsvc.Agent):
46 'name': fields.char('Name', size=60, required=True),
47 'user_id': fields.many2one('res.users', 'User', required=True),
48 'active': fields.boolean('Active'),
49 'interval_number': fields.integer('Interval Number'),
50 'interval_type': fields.selection( [('minutes', 'Minutes'),
51 ('hours', 'Hours'), ('work_days','Work Days'), ('days', 'Days'),('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'),
52 'numbercall': fields.integer('Number of Calls', help='Number of time the function is called,\na negative number indicates that the function will always be called'),
53 'doall' : fields.boolean('Repeat Missed'),
54 'nextcall' : fields.datetime('Next Call Date', required=True),
55 'model': fields.char('Object', size=64),
56 'function': fields.char('Function', size=64),
57 'args': fields.text('Arguments'),
58 'priority': fields.integer('Priority', help='0=Very Urgent\n10=Not urgent')
62 'nextcall' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
63 'priority' : lambda *a: 5,
64 'user_id' : lambda obj,cr,uid,context: uid,
65 'interval_number' : lambda *a: 1,
66 'interval_type' : lambda *a: 'months',
67 'numbercall' : lambda *a: 1,
68 'active' : lambda *a: 1,
69 'doall' : lambda *a: 1
72 def _check_args(self, cr, uid, ids, context=None):
74 for this in self.browse(cr, uid, ids, context):
81 (_check_args, 'Invalid arguments', ['args']),
84 def _callback(self, cr, uid, model, func, args):
85 args = str2tuple(args)
86 m = self.pool.get(model)
87 if m and hasattr(m, func):
92 self._logger.notifyChannel('timers', netsvc.LOG_ERROR, "Job call of self.pool.get('%s').%s(cr, uid, *%r) failed" % (model, func, args))
93 self._logger.notifyChannel('timers', netsvc.LOG_ERROR, tools.exception_to_unicode(e))
96 def _poolJobs(self, db_name, check=False):
98 db, pool = pooler.get_db_and_pool(db_name)
105 cr.execute('select * from ir_cron where numbercall<>0 and active and nextcall<=now() order by priority')
106 for job in cr.dictfetchall():
107 nextcall = DateTime.strptime(job['nextcall'], '%Y-%m-%d %H:%M:%S')
108 numbercall = job['numbercall']
111 while nextcall < now and numbercall:
114 if not ok or job['doall']:
115 self._callback(cr, job['user_id'], job['model'], job['function'], job['args'])
117 nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
121 addsql = ', active=False'
122 cr.execute("update ir_cron set nextcall=%s, numbercall=%s"+addsql+" where id=%s", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id']))
126 cr.execute('select min(nextcall) as min_next_call from ir_cron where numbercall<>0 and active')
127 next_call = cr.dictfetchone()['min_next_call']
129 next_call = time.mktime(time.strptime(next_call, '%Y-%m-%d %H:%M:%S'))
131 next_call = int(time.time()) + 3600 # if do not find active cron job from database, it will run again after 1 day
134 self.setAlarm(self._poolJobs, next_call, db_name, db_name)
140 def restart(self, dbname):
142 self._poolJobs(dbname)
144 def create(self, cr, uid, vals, context=None):
145 res = super(ir_cron, self).create(cr, uid, vals, context=context)
147 self.restart(cr.dbname)
150 def write(self, cr, user, ids, vals, context=None):
151 res = super(ir_cron, self).write(cr, user, ids, vals, context=context)
153 self.restart(cr.dbname)
156 def unlink(self, cr, uid, ids, context=None):
157 res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
159 self.restart(cr.dbname)
163 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: