From e6c7c2127bf2dbad94a9044ec30a7ae50f77d2cc Mon Sep 17 00:00:00 2001 From: Ketan Date: Fri, 31 Aug 2018 12:19:28 +0530 Subject: [PATCH 01/24] updated minor version --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index fcc76f1..0dd871b 100644 --- a/setup.py +++ b/setup.py @@ -19,10 +19,10 @@ setup( name='django-cron', - version='0.5.1', + version='0.5.1.1', author='Sumit Chachra', author_email='chachra@tivix.com', - url='http://github.com/tivix/django-cron', + url='https://github.com/Trendlyne-technologies/django-cron', description='Running python crons in a Django project', packages=find_packages(), long_description=long_description, From 1bfa94a019de5f9f2fee1e875dc229730c2e8bef Mon Sep 17 00:00:00 2001 From: Ketan Date: Fri, 31 Aug 2018 14:14:58 +0530 Subject: [PATCH 02/24] Fixed query to find latest cron job log --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0dd871b..1175274 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name='django-cron', - version='0.5.1.1', + version='0.5.1.2', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From baf5a351d6c5f7acdf0927a588d7141b1b30260f Mon Sep 17 00:00:00 2001 From: Amber Pabreja Date: Wed, 19 Jun 2019 18:59:23 +0530 Subject: [PATCH 03/24] 1. Support for different cron classes via --run_class_list_name . This means that different servers can now run say ANYSERVER_CRON_CLASSES or ALLSERVER_CRON_CLASSES etc. 2. Added support to change code in CronJobLog based in internal IP address via hostname -I. Sanity punching crons can now be implemented APPEND_IP_TO_CODE = True directive. This means that the same cron detecting local failures will run on ALL servers since they all have unique codes. --- django_cron/__init__.py | 19 ++++++++++++++----- django_cron/management/commands/runcrons.py | 11 +++++++++++ setup.py | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index ed65511..cf5f9e2 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -2,6 +2,7 @@ from datetime import datetime, timedelta import traceback import time +from subprocess import check_output import sys from django.conf import settings @@ -63,6 +64,14 @@ class CronJobBase(object): def __init__(self): self.prev_success_cron = None + def get_code(self): + try: + if self.APPEND_IP_TO_CODE: + myserverip = check_output(['hostname', '-I']) + return self.code + '-' + myserverip + except: + return self.code + def set_prev_success_cron(self, prev_success_cron): self.prev_success_cron = prev_success_cron @@ -116,14 +125,14 @@ def should_run_now(self, force=False): if cron_job.schedule.retry_after_failure_mins: # We check last job - success or not - last_job = CronJobLog.objects.filter(code=cron_job.code).order_by('-start_time').first() + last_job = CronJobLog.objects.filter(code=cron_job.get_code()).order_by('-start_time').first() if last_job and not last_job.is_success and get_current_time() <= last_job.start_time + timedelta(minutes=cron_job.schedule.retry_after_failure_mins): return False if cron_job.schedule.run_every_mins is not None: try: self.previously_ran_successful_cron = CronJobLog.objects.filter( - code=cron_job.code, + code=cron_job.get_code(), is_success=True, ran_at_time__isnull=True ).latest('start_time') @@ -143,7 +152,7 @@ def should_run_now(self, force=False): actual_time = time.strptime("%s:%s" % (now.hour, now.minute), "%H:%M") if actual_time >= user_time: qset = CronJobLog.objects.filter( - code=cron_job.code, + code=cron_job.get_code(), ran_at_time=time_data, is_success=True ).filter( @@ -159,7 +168,7 @@ def make_log(self, *messages, **kwargs): cron_log = self.cron_log cron_job = getattr(self, 'cron_job', self.cron_job_class) - cron_log.code = cron_job.code + cron_log.code = cron_job.get_code() cron_log.is_success = kwargs.get('success', True) cron_log.message = self.make_log_msg(messages) @@ -230,7 +239,7 @@ def run(self, force=False): if self.should_run_now(force): if not self.dry_run: - logger.debug("Running cron: %s code %s", cron_job_class.__name__, self.cron_job.code) + logger.debug("Running cron: %s code %s", cron_job_class.__name__, self.cron_job.get_code()) self.msg = self.cron_job.do() self.make_log(self.msg, success=True) self.cron_job.set_prev_success_cron(self.previously_ran_successful_cron) diff --git a/django_cron/management/commands/runcrons.py b/django_cron/management/commands/runcrons.py index d5741e6..faf55ad 100644 --- a/django_cron/management/commands/runcrons.py +++ b/django_cron/management/commands/runcrons.py @@ -34,17 +34,28 @@ def add_arguments(self, parser): action='store_true', help="Just show what crons would be run; don't actually run them" ) + parser.add_argument( + '--run_class_list_name', + nargs='?', + help='Runs all the crons in the specified class list from settings. This is to override CRON_CLASSES hardcoding' + ) def handle(self, *args, **options): """ Iterates over all the CRON_CLASSES (or if passed in as a commandline argument) and runs them. """ + if not options['silent']: self.stdout.write("Running Crons\n") self.stdout.write("{0}\n".format("=" * 40)) cron_classes = options['cron_classes'] + + if options['run_class_list_name']: + list_name = options['run_class_list_name'] + cron_classes = getattr(settings, list_name, []) + if cron_classes: cron_class_names = cron_classes else: diff --git a/setup.py b/setup.py index 1175274..591286d 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name='django-cron', - version='0.5.1.2', + version='0.5.1.6', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From 3ec366c6bec8646874ecd7bfe73510896c3f6bba Mon Sep 17 00:00:00 2001 From: Amber Pabreja Date: Tue, 20 Aug 2019 13:04:13 +0530 Subject: [PATCH 04/24] Make sure that the codes are stripped to remove return carriages --- django_cron/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index cf5f9e2..2138941 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -68,6 +68,7 @@ def get_code(self): try: if self.APPEND_IP_TO_CODE: myserverip = check_output(['hostname', '-I']) + myserverip = myserverip.strip() return self.code + '-' + myserverip except: return self.code From 27ff3f26884b75b9ec91df4bc8cf739978d8b2aa Mon Sep 17 00:00:00 2001 From: Ketan Date: Thu, 5 Dec 2019 14:59:15 +0530 Subject: [PATCH 05/24] lock changed get_code from code --- django_cron/backends/lock/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_cron/backends/lock/base.py b/django_cron/backends/lock/base.py index 76178de..2b25603 100644 --- a/django_cron/backends/lock/base.py +++ b/django_cron/backends/lock/base.py @@ -27,7 +27,7 @@ def __init__(self, cron_class, silent, *args, **kwargs): for you. The rest is backend-specific. """ self.job_name = cron_class.__name__ - self.job_code = cron_class.code + self.job_code = cron_class.get_code() self.parallel = getattr(cron_class, 'ALLOW_PARALLEL_RUNS', False) self.silent = silent From 86575cf60e8320313ae5f9f88a6336bfcbe31bc3 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Thu, 5 Aug 2021 20:57:12 +0530 Subject: [PATCH 06/24] get_code made classmethond --- django_cron/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 2138941..2b27dc5 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -63,7 +63,8 @@ class CronJobBase(object): """ def __init__(self): self.prev_success_cron = None - + + @classmethod def get_code(self): try: if self.APPEND_IP_TO_CODE: From a99a6d49ec88504c70023c16ca798216a093f026 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Sun, 31 Oct 2021 20:12:15 +0530 Subject: [PATCH 07/24] byte + string error and vsrsion bump up --- django_cron/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 2b27dc5..a5146c9 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -70,7 +70,7 @@ def get_code(self): if self.APPEND_IP_TO_CODE: myserverip = check_output(['hostname', '-I']) myserverip = myserverip.strip() - return self.code + '-' + myserverip + return self.code + '-' + myserverip.decode('utf-8') except: return self.code diff --git a/setup.py b/setup.py index 591286d..d311577 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name='django-cron', - version='0.5.1.6', + version='0.5.1.7', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From 5712562bbff280337dec77de9539c5faa1d72dc9 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Sat, 29 Jan 2022 21:41:48 +0530 Subject: [PATCH 08/24] ec2 get public ip --- django_cron/__init__.py | 10 ++++++++-- setup.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index a5146c9..8d85f73 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -68,8 +68,14 @@ def __init__(self): def get_code(self): try: if self.APPEND_IP_TO_CODE: - myserverip = check_output(['hostname', '-I']) - myserverip = myserverip.strip() + myserverip = None + try: + myserverip = check_output(['/usr/bin/ec2metadata', '--public-ipv4']) + except: + pass + if not myserverip: + myserverip = check_output(['hostname', '-I']) + myserverip = myserverip.strip() return self.code + '-' + myserverip.decode('utf-8') except: return self.code diff --git a/setup.py b/setup.py index d311577..16e36ca 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name='django-cron', - version='0.5.1.7', + version='0.5.1.8', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From 012667835e6a6fdd0e7ce7c63149642a555c7fec Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Wed, 9 Mar 2022 11:37:06 +0530 Subject: [PATCH 09/24] stripping \n from public ip --- django_cron/__init__.py | 1 + setup.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 8d85f73..4da0601 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -71,6 +71,7 @@ def get_code(self): myserverip = None try: myserverip = check_output(['/usr/bin/ec2metadata', '--public-ipv4']) + myserverip = myserverip.strip() except: pass if not myserverip: diff --git a/setup.py b/setup.py index 16e36ca..54c027a 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name='django-cron', - version='0.5.1.8', + version='0.5.1.9', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From b29b1ff54869ead2655599ec449abc36db65c159 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Tue, 5 Apr 2022 14:43:12 +0530 Subject: [PATCH 10/24] send failed cron email when cron fails --- django_cron/__init__.py | 25 +++++++++++++++++++++++++ django_cron/backends/lock/cache.py | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 4da0601..0537b43 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -8,6 +8,7 @@ from django.conf import settings from django.utils.timezone import now as utc_now, localtime, is_naive from django.db.models import Q +from django_common.helper import send_mail DEFAULT_LOCK_BACKEND = 'django_cron.backends.lock.cache.CacheLock' @@ -61,6 +62,9 @@ class CronJobBase(object): Following functions: + do - This is the actual business logic to be run at the given schedule """ + + SEND_FAILED_EMAIL = [] + def __init__(self): self.prev_success_cron = None @@ -114,6 +118,7 @@ def __init__(self, cron_job_class, silent=False, dry_run=False, stdout=None): self.lock_class = self.get_lock_class() self.previously_ran_successful_cron = None self.write_log = getattr(settings, 'DJANGO_CRON_OUTPUT_ERRORS', DJANGO_CRON_OUTPUT_ERRORS) + self.send_error_email = getattr(settings, 'DJANGO_CRON_SEND_ERROR_EMAIL', False) def should_run_now(self, force=False): from django_cron.models import CronJobLog @@ -187,6 +192,26 @@ def make_log(self, *messages, **kwargs): if not cron_log.is_success and self.write_log: logger.error("%s cronjob error:\n%s" % (cron_log.code, cron_log.message)) + + if self.send_error_email and not cron_log.is_success: + try: + emails = [admin[1] for admin in settings.ADMINS] + if getattr(cron_job, "SEND_FAILED_EMAIL", []): + emails.extend(cron_job.SEND_FAILED_EMAIL) + + failed_runs_cronjob_email_prefix = getattr(settings, 'FAILED_RUNS_CRONJOB_EMAIL_PREFIX', '') + min_failures = getattr(cron_job, 'MIN_NUM_FAILURES', 10) + send_mail( + '%s%s failed %s times in a row!' % ( + failed_runs_cronjob_email_prefix, + cron_log.code, + min_failures, + ), + cron_log.message, + settings.DEFAULT_FROM_EMAIL, emails + ) + except Exception as e: + logger.exception(e) def make_log_msg(self, messages): full_message = '' diff --git a/django_cron/backends/lock/cache.py b/django_cron/backends/lock/cache.py index 4e92859..6416bfb 100644 --- a/django_cron/backends/lock/cache.py +++ b/django_cron/backends/lock/cache.py @@ -55,7 +55,7 @@ def get_cache_by_name(self): return caches[cache_name] def get_lock_name(self): - return self.job_name + return self.job_code def get_cache_timeout(self, cron_class): timeout = self.DEFAULT_LOCK_TIME From 7b000e1870503e77860f3edd03ad5e6c630e6b43 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Wed, 6 Apr 2022 15:42:17 +0530 Subject: [PATCH 11/24] email on error while importing crons --- django_cron/__init__.py | 2 +- django_cron/management/commands/runcrons.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 0537b43..4f0660e 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -118,7 +118,7 @@ def __init__(self, cron_job_class, silent=False, dry_run=False, stdout=None): self.lock_class = self.get_lock_class() self.previously_ran_successful_cron = None self.write_log = getattr(settings, 'DJANGO_CRON_OUTPUT_ERRORS', DJANGO_CRON_OUTPUT_ERRORS) - self.send_error_email = getattr(settings, 'DJANGO_CRON_SEND_ERROR_EMAIL', False) + self.send_error_email = getattr(settings, 'DJANGO_CRON_SEND_IMMEDIATE_ERROR_EMAIL', False) def should_run_now(self, force=False): from django_cron.models import CronJobLog diff --git a/django_cron/management/commands/runcrons.py b/django_cron/management/commands/runcrons.py index faf55ad..3110d2d 100644 --- a/django_cron/management/commands/runcrons.py +++ b/django_cron/management/commands/runcrons.py @@ -5,6 +5,7 @@ from django.core.management.base import BaseCommand from django.conf import settings from django.db import close_old_connections +from django_common.helper import send_mail from django_cron import CronJobManager, get_class, get_current_time from django_cron.models import CronJobLog @@ -66,6 +67,17 @@ def handle(self, *args, **options): except ImportError: error = traceback.format_exc() self.stdout.write('ERROR: Make sure these are valid cron class names: %s\n\n%s' % (cron_class_names, error)) + try: + emails = [admin[1] for admin in settings.ADMINS] + failed_runs_cronjob_email_prefix = getattr(settings, 'FAILED_RUNS_CRONJOB_EMAIL_PREFIX', '') + send_mail( + "URGENT!!! {} Error while importing crons".format(failed_runs_cronjob_email_prefix), + error, + settings.DEFAULT_FROM_EMAIL, emails + ) + except Exception as e: + self.stdout.write( + 'ERROR: While sending email: %s' % (e)) return for cron_class in crons_to_run: From fa701d33033650d4df6d165a5ae7a3d7a4a936d6 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Wed, 6 Apr 2022 15:43:52 +0530 Subject: [PATCH 12/24] version upgrade --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 54c027a..04b5adc 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( name='django-cron', - version='0.5.1.9', + version='0.5.1.10', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From caf572ab35beed72018e7e035f2b57d93b372964 Mon Sep 17 00:00:00 2001 From: Pritesh Patel Date: Wed, 6 Apr 2022 16:35:31 +0530 Subject: [PATCH 13/24] checking minimum failires before sending email --- django_cron/__init__.py | 26 +++++++++++++++++--------- django_cron/models.py | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 4f0660e..2f0ff84 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -194,6 +194,7 @@ def make_log(self, *messages, **kwargs): logger.error("%s cronjob error:\n%s" % (cron_log.code, cron_log.message)) if self.send_error_email and not cron_log.is_success: + from django_cron.models import CronJobLog try: emails = [admin[1] for admin in settings.ADMINS] if getattr(cron_job, "SEND_FAILED_EMAIL", []): @@ -201,15 +202,22 @@ def make_log(self, *messages, **kwargs): failed_runs_cronjob_email_prefix = getattr(settings, 'FAILED_RUNS_CRONJOB_EMAIL_PREFIX', '') min_failures = getattr(cron_job, 'MIN_NUM_FAILURES', 10) - send_mail( - '%s%s failed %s times in a row!' % ( - failed_runs_cronjob_email_prefix, - cron_log.code, - min_failures, - ), - cron_log.message, - settings.DEFAULT_FROM_EMAIL, emails - ) + if not min_failures: + min_failures = 10 + + last_min_cron_status = list(CronJobLog.objects.using("default").filter( + code=cron_log.code).order_by("-end_time").values_list("is_success", flat=True)[:min_failures]) + + if not any(last_min_cron_status): + send_mail( + '%s%s failed %s times in a row!' % ( + failed_runs_cronjob_email_prefix, + cron_log.code, + min_failures, + ), + cron_log.message, + settings.DEFAULT_FROM_EMAIL, emails + ) except Exception as e: logger.exception(e) diff --git a/django_cron/models.py b/django_cron/models.py index 73e093a..14412f1 100644 --- a/django_cron/models.py +++ b/django_cron/models.py @@ -16,7 +16,7 @@ class CronJobLog(models.Model): # Jobs that run every X minutes, have this field empty. ran_at_time = models.TimeField(null=True, blank=True, db_index=True, editable=False) - def __unicode__(self): + def __str__(self): return '%s (%s)' % (self.code, 'Success' if self.is_success else 'Fail') class Meta: From 4953ebc923658c9185727048e8d0b4dfd18c3542 Mon Sep 17 00:00:00 2001 From: Amber Pabreja Date: Fri, 8 Apr 2022 11:56:01 +0530 Subject: [PATCH 14/24] Added comments changed gitignore --- .gitignore | 5 +++++ django_cron/__init__.py | 2 ++ django_cron/backends/lock/cache.py | 2 ++ django_cron/management/commands/runcrons.py | 1 + 4 files changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index fad4983..be1f18c 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,8 @@ db.sqlite3 # Vim *.swp +#Amber cscope files +cscope.files +cscope.out +mk_cscope +tags diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 2f0ff84..46014f1 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -208,6 +208,8 @@ def make_log(self, *messages, **kwargs): last_min_cron_status = list(CronJobLog.objects.using("default").filter( code=cron_log.code).order_by("-end_time").values_list("is_success", flat=True)[:min_failures]) + #All of them should be failed ie false. Then only we have to send email + # Send on 3 failures. [True, False, False] ie [success, failed, failed] does not trigger email if not any(last_min_cron_status): send_mail( '%s%s failed %s times in a row!' % ( diff --git a/django_cron/backends/lock/cache.py b/django_cron/backends/lock/cache.py index 6416bfb..bb9edb2 100644 --- a/django_cron/backends/lock/cache.py +++ b/django_cron/backends/lock/cache.py @@ -55,6 +55,8 @@ def get_cache_by_name(self): return caches[cache_name] def get_lock_name(self): + #We are using IP based codes. Name is Check_Documents_Cron ie the Class name. This is terrible since 2 apps can have the same class name + #Code is fmeca.check_documents_cron.203.23.34.56 return self.job_code def get_cache_timeout(self, cron_class): diff --git a/django_cron/management/commands/runcrons.py b/django_cron/management/commands/runcrons.py index 3110d2d..6b590b2 100644 --- a/django_cron/management/commands/runcrons.py +++ b/django_cron/management/commands/runcrons.py @@ -65,6 +65,7 @@ def handle(self, *args, **options): try: crons_to_run = [get_class(x) for x in cron_class_names] except ImportError: + # Send an email to admin when the module load fails error = traceback.format_exc() self.stdout.write('ERROR: Make sure these are valid cron class names: %s\n\n%s' % (cron_class_names, error)) try: From d475093f84df3a101e5fa86ecc9f0dd449d597fd Mon Sep 17 00:00:00 2001 From: Alok Date: Tue, 9 May 2023 11:41:02 +0530 Subject: [PATCH 15/24] removing duplicate parser.add_argument --- django_cron/management/commands/runcrons.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/django_cron/management/commands/runcrons.py b/django_cron/management/commands/runcrons.py index 5700667..9d0f8c2 100644 --- a/django_cron/management/commands/runcrons.py +++ b/django_cron/management/commands/runcrons.py @@ -31,11 +31,6 @@ def add_arguments(self, parser): nargs='?', help='Runs all the crons in the specified class list from settings. This is to override CRON_CLASSES hardcoding' ) - parser.add_argument( - '--run_class_list_name', - nargs='?', - help='Runs all the crons in the specified class list from settings. This is to override CRON_CLASSES hardcoding' - ) def handle(self, *args, **options): """ From 15e03d8332623b7b4fb6fcc7b3ee38d586b3d5f4 Mon Sep 17 00:00:00 2001 From: Alok Date: Tue, 9 May 2023 15:52:53 +0530 Subject: [PATCH 16/24] update changes --- django_cron/__init__.py | 310 ------------------------------ django_cron/backends/lock/base.py | 2 +- django_cron/core.py | 61 +++++- django_cron/models.py | 5 +- setup.py | 2 +- 5 files changed, 62 insertions(+), 318 deletions(-) diff --git a/django_cron/__init__.py b/django_cron/__init__.py index 8b7df82..b5f77b8 100644 --- a/django_cron/__init__.py +++ b/django_cron/__init__.py @@ -1,312 +1,2 @@ from django_cron.core import * from django_cron.helpers import get_class, get_current_time -import logging -from datetime import datetime, timedelta -import traceback -import time -from subprocess import check_output -import sys - -from django.conf import settings -from django.utils.timezone import now as utc_now, localtime, is_naive -from django.db.models import Q -from django_common.helper import send_mail - - -DEFAULT_LOCK_BACKEND = 'django_cron.backends.lock.cache.CacheLock' -DJANGO_CRON_OUTPUT_ERRORS = False -logger = logging.getLogger('django_cron') - - -class BadCronJobError(AssertionError): - pass - - -def get_class(kls): - """ - TODO: move to django-common app. - Converts a string to a class. - Courtesy: http://stackoverflow.com/questions/452969/does-python-have-an-equivalent-to-java-class-forname/452981#452981 - """ - parts = kls.split('.') - - if len(parts) == 1: - raise ImportError("'{0}'' is not a valid import path".format(kls)) - - module = ".".join(parts[:-1]) - m = __import__(module) - for comp in parts[1:]: - m = getattr(m, comp) - return m - - -def get_current_time(): - now = utc_now() - return now if is_naive(now) else localtime(now) - - -class Schedule(object): - def __init__(self, run_every_mins=None, run_at_times=None, retry_after_failure_mins=None, run_on_days=None): - if run_at_times is None: - run_at_times = [] - self.run_every_mins = run_every_mins - self.run_at_times = run_at_times - self.retry_after_failure_mins = retry_after_failure_mins - self.run_on_days = run_on_days - - -class CronJobBase(object): - """ - Sub-classes should have the following properties: - + code - This should be a code specific to the cron being run. Eg. 'general.stats' etc. - + schedule - - Following functions: - + do - This is the actual business logic to be run at the given schedule - """ - - SEND_FAILED_EMAIL = [] - - def __init__(self): - self.prev_success_cron = None - - @classmethod - def get_code(self): - try: - if self.APPEND_IP_TO_CODE: - myserverip = None - try: - myserverip = check_output(['/usr/bin/ec2metadata', '--public-ipv4']) - myserverip = myserverip.strip() - except: - pass - if not myserverip: - myserverip = check_output(['hostname', '-I']) - myserverip = myserverip.strip() - return self.code + '-' + myserverip.decode('utf-8') - except: - return self.code - - def set_prev_success_cron(self, prev_success_cron): - self.prev_success_cron = prev_success_cron - - def get_prev_success_cron(self): - return self.prev_success_cron - - @classmethod - def get_time_until_run(cls): - from django_cron.models import CronJobLog - try: - last_job = CronJobLog.objects.filter( - code=cls.code).latest('start_time') - except CronJobLog.DoesNotExist: - return timedelta() - return (last_job.start_time + - timedelta(minutes=cls.schedule.run_every_mins) - utc_now()) - - -class CronJobManager(object): - """ - A manager instance should be created per cron job to be run. - Does all the logger tracking etc. for it. - Used as a context manager via 'with' statement to ensure - proper logger in cases of job failure. - """ - def __init__(self, cron_job_class, silent=False, dry_run=False, stdout=None): - self.cron_job_class = cron_job_class - self.silent = silent - self.dry_run = dry_run - self.stdout = stdout or sys.stdout - self.lock_class = self.get_lock_class() - self.previously_ran_successful_cron = None - self.write_log = getattr(settings, 'DJANGO_CRON_OUTPUT_ERRORS', DJANGO_CRON_OUTPUT_ERRORS) - self.send_error_email = getattr(settings, 'DJANGO_CRON_SEND_IMMEDIATE_ERROR_EMAIL', False) - - def should_run_now(self, force=False): - from django_cron.models import CronJobLog - cron_job = self.cron_job - """ - Returns a boolean determining whether this cron should run now or not! - """ - self.user_time = None - self.previously_ran_successful_cron = None - - # If we pass --force options, we force cron run - if force: - return True - - if cron_job.schedule.run_on_days is not None: - if not datetime.today().weekday() in cron_job.schedule.run_on_days: - return False - - if cron_job.schedule.retry_after_failure_mins: - # We check last job - success or not - last_job = CronJobLog.objects.filter(code=cron_job.get_code()).order_by('-start_time').first() - if last_job and not last_job.is_success and get_current_time() <= last_job.start_time + timedelta(minutes=cron_job.schedule.retry_after_failure_mins): - return False - - if cron_job.schedule.run_every_mins is not None: - try: - self.previously_ran_successful_cron = CronJobLog.objects.filter( - code=cron_job.get_code(), - is_success=True, - ran_at_time__isnull=True - ).latest('start_time') - except CronJobLog.DoesNotExist: - pass - - if self.previously_ran_successful_cron: - if get_current_time() > self.previously_ran_successful_cron.start_time + timedelta(minutes=cron_job.schedule.run_every_mins): - return True - else: - return True - - if cron_job.schedule.run_at_times: - for time_data in cron_job.schedule.run_at_times: - user_time = time.strptime(time_data, "%H:%M") - now = get_current_time() - actual_time = time.strptime("%s:%s" % (now.hour, now.minute), "%H:%M") - if actual_time >= user_time: - qset = CronJobLog.objects.filter( - code=cron_job.get_code(), - ran_at_time=time_data, - is_success=True - ).filter( - Q(start_time__gt=now) | Q(end_time__gte=now.replace(hour=0, minute=0, second=0, microsecond=0)) - ) - if not qset: - self.user_time = time_data - return True - - return False - - def make_log(self, *messages, **kwargs): - cron_log = self.cron_log - - cron_job = getattr(self, 'cron_job', self.cron_job_class) - cron_log.code = cron_job.get_code() - - cron_log.is_success = kwargs.get('success', True) - cron_log.message = self.make_log_msg(messages) - cron_log.ran_at_time = getattr(self, 'user_time', None) - cron_log.end_time = get_current_time() - cron_log.save() - - if not cron_log.is_success and self.write_log: - logger.error("%s cronjob error:\n%s" % (cron_log.code, cron_log.message)) - - if self.send_error_email and not cron_log.is_success: - from django_cron.models import CronJobLog - try: - emails = [admin[1] for admin in settings.ADMINS] - if getattr(cron_job, "SEND_FAILED_EMAIL", []): - emails.extend(cron_job.SEND_FAILED_EMAIL) - - failed_runs_cronjob_email_prefix = getattr(settings, 'FAILED_RUNS_CRONJOB_EMAIL_PREFIX', '') - min_failures = getattr(cron_job, 'MIN_NUM_FAILURES', 10) - if not min_failures: - min_failures = 10 - - last_min_cron_status = list(CronJobLog.objects.using("default").filter( - code=cron_log.code).order_by("-end_time").values_list("is_success", flat=True)[:min_failures]) - - #All of them should be failed ie false. Then only we have to send email - # Send on 3 failures. [True, False, False] ie [success, failed, failed] does not trigger email - if not any(last_min_cron_status): - send_mail( - '%s%s failed %s times in a row!' % ( - failed_runs_cronjob_email_prefix, - cron_log.code, - min_failures, - ), - cron_log.message, - settings.DEFAULT_FROM_EMAIL, emails - ) - except Exception as e: - logger.exception(e) - - def make_log_msg(self, messages): - full_message = '' - if messages: - for message in messages: - if len(message): - full_message += message - full_message += '\n' - - return full_message - - def __enter__(self): - from django_cron.models import CronJobLog - self.cron_log = CronJobLog(start_time=get_current_time()) - - return self - - def __exit__(self, ex_type, ex_value, ex_traceback): - if ex_type is None: - return True - - non_logging_exceptions = [ - BadCronJobError, self.lock_class.LockFailedException - ] - - if ex_type in non_logging_exceptions: - if not self.silent: - self.stdout.write("{0}\n".format(ex_value)) - logger.info(ex_value) - else: - if not self.silent: - self.stdout.write(u"[\N{HEAVY BALLOT X}] {0}\n".format(self.cron_job_class.code)) - try: - trace = "".join(traceback.format_exception(ex_type, ex_value, ex_traceback)) - self.make_log(self.msg, trace, success=False) - except Exception as e: - err_msg = "Error saving cronjob (%s) log message: %s" % (self.cron_job_class, e) - logger.error(err_msg) - - return True # prevent exception propagation - - def run(self, force=False): - """ - apply the logic of the schedule and call do() on the CronJobBase class - """ - cron_job_class = self.cron_job_class - - if not issubclass(cron_job_class, CronJobBase): - raise BadCronJobError('The cron_job to be run must be a subclass of %s' % CronJobBase.__name__) - - if not hasattr(cron_job_class, 'code'): - raise BadCronJobError( - "Cron class '{0}' does not have a code attribute" - .format(cron_job_class.__name__) - ) - - with self.lock_class(cron_job_class, self.silent): - self.cron_job = cron_job_class() - - if self.should_run_now(force): - if not self.dry_run: - logger.debug("Running cron: %s code %s", cron_job_class.__name__, self.cron_job.get_code()) - self.msg = self.cron_job.do() - self.make_log(self.msg, success=True) - self.cron_job.set_prev_success_cron(self.previously_ran_successful_cron) - if not self.silent: - self.stdout.write(u"[\N{HEAVY CHECK MARK}] {0}\n".format(self.cron_job.code)) - elif not self.silent: - self.stdout.write(u"[ ] {0}\n".format(self.cron_job.code)) - - def get_lock_class(self): - name = getattr(settings, 'DJANGO_CRON_LOCK_BACKEND', DEFAULT_LOCK_BACKEND) - try: - return get_class(name) - except Exception as err: - raise Exception("invalid lock module %s. Can't use it: %s." % (name, err)) - - @property - def msg(self): - return getattr(self, '_msg', '') - - @msg.setter - def msg(self, msg): - if msg is None: - msg = '' - self._msg = msg diff --git a/django_cron/backends/lock/base.py b/django_cron/backends/lock/base.py index a003b4a..75db9f2 100644 --- a/django_cron/backends/lock/base.py +++ b/django_cron/backends/lock/base.py @@ -28,7 +28,7 @@ def __init__(self, cron_class, silent, *args, **kwargs): for you. The rest is backend-specific. """ self.job_name = '.'.join([cron_class.__module__, cron_class.__name__]) - self.job_code = cron_class.code + self.job_code = cron_class.get_code() self.parallel = getattr(cron_class, 'ALLOW_PARALLEL_RUNS', False) self.silent = silent diff --git a/django_cron/core.py b/django_cron/core.py index 9d5a7e6..3a22feb 100644 --- a/django_cron/core.py +++ b/django_cron/core.py @@ -7,9 +7,12 @@ from django.conf import settings from django.utils.timezone import now as utc_now from django.db.models import Q - +from subprocess import check_output from django_cron.helpers import get_class, get_current_time +from django_common.helper import send_mail + + DEFAULT_LOCK_BACKEND = 'django_cron.backends.lock.cache.CacheLock' DJANGO_CRON_OUTPUT_ERRORS = False logger = logging.getLogger('django_cron') @@ -48,12 +51,29 @@ class CronJobBase(object): Following functions: + do - This is the actual business logic to be run at the given schedule """ - + SEND_FAILED_EMAIL = [] remove_successful_cron_logs = False def __init__(self): self.prev_success_cron = None + @classmethod + def get_code(self): + try: + if self.APPEND_IP_TO_CODE: + myserverip = None + try: + myserverip = check_output(['/usr/bin/ec2metadata', '--public-ipv4']) + myserverip = myserverip.strip() + except: + pass + if not myserverip: + myserverip = check_output(['hostname', '-I']) + myserverip = myserverip.strip() + return self.code + '-' + myserverip.decode('utf-8') + except: + return self.code + def set_prev_success_cron(self, prev_success_cron): self.prev_success_cron = prev_success_cron @@ -93,6 +113,9 @@ def __init__(self, cron_job_class, silent=False, dry_run=False, stdout=None): self.write_log = getattr( settings, 'DJANGO_CRON_OUTPUT_ERRORS', DJANGO_CRON_OUTPUT_ERRORS ) + self.send_error_email = getattr(settings, + 'DJANGO_CRON_SEND_IMMEDIATE_ERROR_EMAIL', + False) def should_run_now(self, force=False): from django_cron.models import CronJobLog @@ -177,7 +200,7 @@ def make_log(self, *messages, **kwargs): cron_log = self.cron_log cron_job = getattr(self, 'cron_job', self.cron_job_class) - cron_log.code = cron_job.code + cron_log.code = cron_job.get_code() cron_log.is_success = kwargs.get('success', True) cron_log.message = self.make_log_msg(messages) @@ -187,6 +210,36 @@ def make_log(self, *messages, **kwargs): if not cron_log.is_success and self.write_log: logger.error("%s cronjob error:\n%s" % (cron_log.code, cron_log.message)) + + if self.send_error_email and not cron_log.is_success: + from django_cron.models import CronJobLog + try: + emails = [admin[1] for admin in settings.ADMINS] + if getattr(cron_job, "SEND_FAILED_EMAIL", []): + emails.extend(cron_job.SEND_FAILED_EMAIL) + + failed_runs_cronjob_email_prefix = getattr(settings, 'FAILED_RUNS_CRONJOB_EMAIL_PREFIX', '') + min_failures = getattr(cron_job, 'MIN_NUM_FAILURES', 10) + if not min_failures: + min_failures = 10 + + last_min_cron_status = list(CronJobLog.objects.using("default").filter( + code=cron_log.code).order_by("-end_time").values_list("is_success", flat=True)[:min_failures]) + + #All of them should be failed ie false. Then only we have to send email + # Send on 3 failures. [True, False, False] ie [success, failed, failed] does not trigger email + if not any(last_min_cron_status): + send_mail( + '%s%s failed %s times in a row!' % ( + failed_runs_cronjob_email_prefix, + cron_log.code, + min_failures, + ), + cron_log.message, + settings.DEFAULT_FROM_EMAIL, emails + ) + except Exception as e: + logger.exception(e) def make_log_msg(self, messages): full_message = '' @@ -260,7 +313,7 @@ def run(self, force=False): logger.debug( "Running cron: %s code %s", cron_job_class.__name__, - self.cron_job.code, + self.cron_job.get_code(), ) self.make_log('Job in progress', success=True) self.msg = self.cron_job.do() diff --git a/django_cron/models.py b/django_cron/models.py index 4043956..12719e8 100644 --- a/django_cron/models.py +++ b/django_cron/models.py @@ -17,11 +17,12 @@ class CronJobLog(models.Model): # Jobs that run every X minutes, have this field empty. ran_at_time = models.TimeField(null=True, blank=True, db_index=True, editable=False) + def __unicode__(self): + return '%s (%s)' % (self.code, 'Success' if self.is_success else 'Fail') + def __str__(self): return '%s (%s)' % (self.code, 'Success' if self.is_success else 'Fail') - def __str__(self): - return "%s (%s)" % (self.code, "Success" if self.is_success else "Fail") class Meta: index_together = [ diff --git a/setup.py b/setup.py index 41afa8e..3082b88 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ setup( name='django-cron', - version='0.5.11', + version='0.6.1', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From dae11faa9c3f3f8e86975d45906d4fbdd3758971 Mon Sep 17 00:00:00 2001 From: Alok Date: Thu, 18 May 2023 11:14:37 +0530 Subject: [PATCH 17/24] admin changes --- django_cron/admin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/django_cron/admin.py b/django_cron/admin.py index c43365d..5b11c6c 100644 --- a/django_cron/admin.py +++ b/django_cron/admin.py @@ -58,5 +58,9 @@ def humanize_duration(self, obj): humanize_duration.admin_order_field = 'duration' +class CronJobLockAdmin(admin.ModelAdmin): + list_display = ('pk', 'job_name', 'locked') + list_filter = ('locked') + admin.site.register(CronJobLog, CronJobLogAdmin) -admin.site.register(CronJobLock) +admin.site.register(CronJobLock, CronJobLockAdmin) From 27b93397628f60e8323bac2caf5433354043d4b8 Mon Sep 17 00:00:00 2001 From: Alok Date: Thu, 18 May 2023 12:02:50 +0530 Subject: [PATCH 18/24] replacing datetime.today() with get_current_time() --- django_cron/core.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/django_cron/core.py b/django_cron/core.py index 3a22feb..d4a1e48 100644 --- a/django_cron/core.py +++ b/django_cron/core.py @@ -1,5 +1,5 @@ import logging -from datetime import datetime, timedelta +from datetime import timedelta import traceback import time import sys @@ -132,11 +132,11 @@ def should_run_now(self, force=False): return True if cron_job.schedule.run_monthly_on_days is not None: - if not datetime.today().day in cron_job.schedule.run_monthly_on_days: + if not get_current_time().day in cron_job.schedule.run_monthly_on_days: return False if cron_job.schedule.run_weekly_on_days is not None: - if not datetime.today().weekday() in cron_job.schedule.run_weekly_on_days: + if not get_current_time().weekday() in cron_job.schedule.run_weekly_on_days: return False if cron_job.schedule.retry_after_failure_mins: @@ -144,7 +144,7 @@ def should_run_now(self, force=False): last_job = ( CronJobLog.objects.filter(code=cron_job.code) .order_by('-start_time') - .exclude(start_time__gt=datetime.today()) + .exclude(start_time__gt=get_current_time()) .first() ) if ( @@ -160,7 +160,7 @@ def should_run_now(self, force=False): try: self.previously_ran_successful_cron = CronJobLog.objects.filter( code=cron_job.code, is_success=True - ).exclude(start_time__gt=datetime.today()).latest('start_time') + ).exclude(start_time__gt=get_current_time()).latest('start_time') except CronJobLog.DoesNotExist: pass From 3472b048eb74d55882815c147eca8c0653c7967a Mon Sep 17 00:00:00 2001 From: Alok Date: Thu, 18 May 2023 12:55:34 +0530 Subject: [PATCH 19/24] , error --- django_cron/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_cron/admin.py b/django_cron/admin.py index 5b11c6c..5a36c95 100644 --- a/django_cron/admin.py +++ b/django_cron/admin.py @@ -60,7 +60,7 @@ def humanize_duration(self, obj): class CronJobLockAdmin(admin.ModelAdmin): list_display = ('pk', 'job_name', 'locked') - list_filter = ('locked') + list_filter = ('locked',) admin.site.register(CronJobLog, CronJobLogAdmin) admin.site.register(CronJobLock, CronJobLockAdmin) From 604236a56396ce1bd3e177c55e4f44cf4bef6de9 Mon Sep 17 00:00:00 2001 From: Alok Date: Thu, 18 May 2023 18:28:12 +0530 Subject: [PATCH 20/24] adding get_code() --- django_cron/core.py | 6 +++--- django_cron/cron.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/django_cron/core.py b/django_cron/core.py index d4a1e48..8b8f3a1 100644 --- a/django_cron/core.py +++ b/django_cron/core.py @@ -142,7 +142,7 @@ def should_run_now(self, force=False): if cron_job.schedule.retry_after_failure_mins: # We check last job - success or not last_job = ( - CronJobLog.objects.filter(code=cron_job.code) + CronJobLog.objects.filter(code=cron_job.get_code()) .order_by('-start_time') .exclude(start_time__gt=get_current_time()) .first() @@ -159,7 +159,7 @@ def should_run_now(self, force=False): if cron_job.schedule.run_every_mins is not None: try: self.previously_ran_successful_cron = CronJobLog.objects.filter( - code=cron_job.code, is_success=True + code=cron_job.get_code(), is_success=True ).exclude(start_time__gt=get_current_time()).latest('start_time') except CronJobLog.DoesNotExist: pass @@ -181,7 +181,7 @@ def should_run_now(self, force=False): actual_time = time.strptime("%s:%s" % (now.hour, now.minute), "%H:%M") if actual_time >= user_time: qset = CronJobLog.objects.filter( - code=cron_job.code, ran_at_time=time_data, is_success=True + code=cron_job.get_code(), ran_at_time=time_data, is_success=True ).filter( Q(start_time__gt=now) | Q( diff --git a/django_cron/cron.py b/django_cron/cron.py index 8d65f49..44b3104 100644 --- a/django_cron/cron.py +++ b/django_cron/cron.py @@ -28,7 +28,7 @@ def do(self): for cron in crons_to_check: min_failures = getattr(cron, 'MIN_NUM_FAILURES', 10) - jobs = CronJobLog.objects.filter(code=cron.code).order_by('-end_time')[ + jobs = CronJobLog.objects.filter(code=cron.get_code()).order_by('-end_time')[ :min_failures ] failures = 0 From 4868b9689f2b3a9e00df50288854c8df34246718 Mon Sep 17 00:00:00 2001 From: Alok Date: Thu, 18 May 2023 19:05:18 +0530 Subject: [PATCH 21/24] removing editable false --- django_cron/admin.py | 2 +- django_cron/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/django_cron/admin.py b/django_cron/admin.py index 5a36c95..c68adc7 100644 --- a/django_cron/admin.py +++ b/django_cron/admin.py @@ -37,7 +37,7 @@ class Meta: search_fields = ('code', 'message') ordering = ('-start_time',) - list_display = ('code', 'start_time', 'end_time', 'humanize_duration', 'is_success') + list_display = ('code', 'start_time', 'end_time', 'humanize_duration', 'is_success', 'ran_at_time') list_filter = ('code', 'start_time', 'is_success', DurationFilter) def get_queryset(self, request): diff --git a/django_cron/models.py b/django_cron/models.py index 12719e8..294cfd4 100644 --- a/django_cron/models.py +++ b/django_cron/models.py @@ -15,7 +15,7 @@ class CronJobLog(models.Model): # This field is used to mark jobs executed in exact time. # Jobs that run every X minutes, have this field empty. - ran_at_time = models.TimeField(null=True, blank=True, db_index=True, editable=False) + ran_at_time = models.TimeField(null=True, blank=True, db_index=True, editable=True) def __unicode__(self): return '%s (%s)' % (self.code, 'Success' if self.is_success else 'Fail') From b74a49f9b92889dae6e59465f0257aa88fc921ef Mon Sep 17 00:00:00 2001 From: Alok Date: Fri, 19 May 2023 11:29:08 +0530 Subject: [PATCH 22/24] adding job_code --- django_cron/backends/lock/database.py | 4 ++-- django_cron/backends/lock/file.py | 2 +- django_cron/models.py | 3 +++ setup.py | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/django_cron/backends/lock/database.py b/django_cron/backends/lock/database.py index 3bf78a1..357d0be 100644 --- a/django_cron/backends/lock/database.py +++ b/django_cron/backends/lock/database.py @@ -11,7 +11,7 @@ class DatabaseLock(DjangoCronJobLock): @transaction.atomic def lock(self): - lock, created = CronJobLock.objects.get_or_create(job_name=self.job_name) + lock, created = CronJobLock.objects.get_or_create(job_name=self.job_code) if lock.locked: return False else: @@ -21,6 +21,6 @@ def lock(self): @transaction.atomic def release(self): - lock = CronJobLock.objects.filter(job_name=self.job_name, locked=True).first() + lock = CronJobLock.objects.filter(job_name=self.job_code, locked=True).first() lock.locked = False lock.save() diff --git a/django_cron/backends/lock/file.py b/django_cron/backends/lock/file.py index 1d14d31..dd3621b 100644 --- a/django_cron/backends/lock/file.py +++ b/django_cron/backends/lock/file.py @@ -33,5 +33,5 @@ def get_lock_name(self): # let it die if failed, can't run further anyway os.makedirs(path, exist_ok=True) - filename = self.job_name + '.lock' + filename = self.job_code + '.lock' return os.path.join(path, filename) diff --git a/django_cron/models.py b/django_cron/models.py index 294cfd4..b6fbdcd 100644 --- a/django_cron/models.py +++ b/django_cron/models.py @@ -39,3 +39,6 @@ class Meta: class CronJobLock(models.Model): job_name = models.CharField(max_length=200, unique=True) locked = models.BooleanField(default=False) + + def __str__(self): + return '%s' % (self.job_name) \ No newline at end of file diff --git a/setup.py b/setup.py index 3082b88..6868620 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ setup( name='django-cron', - version='0.6.1', + version='0.6.2', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron', From 354418c42442d81a9a06181ddb73d6db79ea40a1 Mon Sep 17 00:00:00 2001 From: Alok Date: Fri, 19 May 2023 11:44:12 +0530 Subject: [PATCH 23/24] adding locked in str and also logger will show job_code --- django_cron/backends/lock/base.py | 2 +- django_cron/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/django_cron/backends/lock/base.py b/django_cron/backends/lock/base.py index 75db9f2..6a09cdc 100644 --- a/django_cron/backends/lock/base.py +++ b/django_cron/backends/lock/base.py @@ -55,7 +55,7 @@ def release(self): ) def lock_failed_message(self): - return "%s: lock found. Will try later." % self.job_name + return "%s: lock found. Will try later." % self.job_code def __enter__(self): if not self.parallel and not self.lock(): diff --git a/django_cron/models.py b/django_cron/models.py index b6fbdcd..8e49cc2 100644 --- a/django_cron/models.py +++ b/django_cron/models.py @@ -41,4 +41,4 @@ class CronJobLock(models.Model): locked = models.BooleanField(default=False) def __str__(self): - return '%s' % (self.job_name) \ No newline at end of file + return '%s (%s)' % (self.job_name, self.locked) \ No newline at end of file From 5899e02f687ce49975579d10cc785fd09d496912 Mon Sep 17 00:00:00 2001 From: Pritesh Date: Fri, 19 May 2023 15:50:28 +0530 Subject: [PATCH 24/24] .using(default) added --- django_cron/backends/lock/database.py | 9 +++++---- setup.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/django_cron/backends/lock/database.py b/django_cron/backends/lock/database.py index 357d0be..b89d4f7 100644 --- a/django_cron/backends/lock/database.py +++ b/django_cron/backends/lock/database.py @@ -11,7 +11,7 @@ class DatabaseLock(DjangoCronJobLock): @transaction.atomic def lock(self): - lock, created = CronJobLock.objects.get_or_create(job_name=self.job_code) + lock, created = CronJobLock.objects.using("default").get_or_create(job_name=self.job_code) if lock.locked: return False else: @@ -21,6 +21,7 @@ def lock(self): @transaction.atomic def release(self): - lock = CronJobLock.objects.filter(job_name=self.job_code, locked=True).first() - lock.locked = False - lock.save() + lock = CronJobLock.objects.using("default").filter(job_name=self.job_code, locked=True).first() + if lock: + lock.locked = False + lock.save() diff --git a/setup.py b/setup.py index 6868620..4ee5bea 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ setup( name='django-cron', - version='0.6.2', + version='0.6.3', author='Sumit Chachra', author_email='chachra@tivix.com', url='https://github.com/Trendlyne-technologies/django-cron',