Search code examples
odooodoo-14

Make Action Scheduler to be able to run in seconds?


I want to make Action Scheduler to run in every 30 seconds, but it only has 1 minute as the lowest possible selection for its interval.

How can I customize/change it so that it can run in seconds?


Solution

  • This is what I end up doing, which I need to test it for a few days before confirming if it gets the job done.

    1. Inherited ir.cron model to add seconds selection.

    2. Added seconds to _intervalTypes to make it compatible with seconds selection.

    3. Copied the original _process_job to replace in here so that the modified _intervalTypes with seconds property exists for the method.

    4. Installed this customized module.

    Here is the code.

    from odoo import models, fields, api
    from datetime import datetime
    from dateutil.relativedelta import relativedelta
    import pytz
    
    _intervalTypes = {
        'days': lambda interval: relativedelta(days=interval),
        'hours': lambda interval: relativedelta(hours=interval),
        'weeks': lambda interval: relativedelta(days=7*interval),
        'months': lambda interval: relativedelta(months=interval),
        'minutes': lambda interval: relativedelta(minutes=interval),
        'seconds': lambda interval: relativedelta(seconds=interval),
    }
    
    class IrCronInherit(models.Model):
        _inherit = 'ir.cron'
    
        interval_type = fields.Selection([
            ('seconds', 'Seconds'),
            ('minutes', 'Minutes'),
            ('hours', 'Hours'),
            ('days', 'Days'),
            ('weeks', 'Weeks'),
            ('months', 'Months')], string='Interval Unit', default='months')
    
        @classmethod
        def _process_job(cls, job_cr, job, cron_cr):
            """ Run a given job taking care of the repetition.
    
            :param job_cr: cursor to use to execute the job, safe to commit/rollback
            :param job: job to be run (as a dictionary).
            :param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date,
                must not be committed/rolled back!
            """
            with api.Environment.manage():
                try:
                    cron = api.Environment(job_cr, job['user_id'], {
                        'lastcall': fields.Datetime.from_string(job['lastcall'])
                    })[cls._name]
                    # Use the user's timezone to compare and compute datetimes,
                    # otherwise unexpected results may appear. For instance, adding
                    # 1 month in UTC to July 1st at midnight in GMT+2 gives July 30
                    # instead of August 1st!
                    now = fields.Datetime.context_timestamp(cron, datetime.now())
                    nextcall = fields.Datetime.context_timestamp(cron, fields.Datetime.from_string(job['nextcall']))
                    numbercall = job['numbercall']
    
                    ok = False
                    while nextcall < now and numbercall:
                        if numbercall > 0:
                            numbercall -= 1
                        if not ok or job['doall']:
                            cron._callback(job['cron_name'], job['ir_actions_server_id'], job['id'])
                        if numbercall:
                            nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
                        ok = True
                    addsql = ''
                    if not numbercall:
                        addsql = ', active=False'
                    cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s, lastcall=%s"+addsql+" WHERE id=%s",(
                        fields.Datetime.to_string(nextcall.astimezone(pytz.UTC)),
                        numbercall,
                        fields.Datetime.to_string(now.astimezone(pytz.UTC)),
                        job['id']
                    ))
                    cron.flush()
                    cron.invalidate_cache()
    
                finally:
                    job_cr.commit()
                    cron_cr.commit()
    

    Right now, it runs under a minute but not constantly at 30 seconds when I actually set it to 30 seconds, but it will do for now.