From 9be0fd797f5959c7c11ad4dc8c1afd43447da48a Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 15 Dec 2021 15:09:05 +0200 Subject: [PATCH 1/8] Adding name col to CrontabSchedule model. --- django_celery_beat/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/django_celery_beat/models.py b/django_celery_beat/models.py index 583d8b63..e3f9b634 100644 --- a/django_celery_beat/models.py +++ b/django_celery_beat/models.py @@ -299,6 +299,13 @@ class CrontabSchedule(models.Model): 'Timezone to Run the Cron Schedule on. Default is UTC.'), ) + name = models.CharField( + max_length=200, unique=False, + null=True, blank=True, + verbose_name=_('Name'), + help_text=_('Short Description For This Cron Schedule'), + ) + class Meta: """Table information.""" @@ -308,7 +315,8 @@ class Meta: 'day_of_week', 'hour', 'minute', 'timezone'] def __str__(self): - return '{0} {1} {2} {3} {4} (m/h/dM/MY/d) {5}'.format( + return '{0} {1} {2} {3} {4} {5} (m/h/dM/MY/d) {6}'.format( + self.name if self.name else '', cronexp(self.minute), cronexp(self.hour), cronexp(self.day_of_month), cronexp(self.month_of_year), cronexp(self.day_of_week), str(self.timezone) From 5eeb1234c9630eb8549a4895a492f291aac6cfef Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 15 Dec 2021 15:26:53 +0200 Subject: [PATCH 2/8] Adding name col to CrontabSchedule model. --- django_celery_beat/models.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/django_celery_beat/models.py b/django_celery_beat/models.py index e3f9b634..9e9c8567 100644 --- a/django_celery_beat/models.py +++ b/django_celery_beat/models.py @@ -315,11 +315,16 @@ class Meta: 'day_of_week', 'hour', 'minute', 'timezone'] def __str__(self): - return '{0} {1} {2} {3} {4} {5} (m/h/dM/MY/d) {6}'.format( - self.name if self.name else '', + + name = None + if self.name: + name = '[{}]'.format(self.name) + + return '{0} {1} {2} {3} {4} (m/h/dM/MY/d) {5} {6}'.format( cronexp(self.minute), cronexp(self.hour), cronexp(self.day_of_month), cronexp(self.month_of_year), - cronexp(self.day_of_week), str(self.timezone) + cronexp(self.day_of_week), str(self.timezone), + name if name else '', ) @property From 27927add36e3a7cc3a1c7e17725abfdc383f06cc Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 15 Dec 2021 20:41:41 +0200 Subject: [PATCH 3/8] Adding name col to CrontabSchedule model. Add migrations. --- .../migrations/0016_crontabschedule_name.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 django_celery_beat/migrations/0016_crontabschedule_name.py diff --git a/django_celery_beat/migrations/0016_crontabschedule_name.py b/django_celery_beat/migrations/0016_crontabschedule_name.py new file mode 100644 index 00000000..e8395385 --- /dev/null +++ b/django_celery_beat/migrations/0016_crontabschedule_name.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.7 on 2021-12-15 13:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_celery_beat', '0015_edit_solarschedule_events_choices'), + ] + + operations = [ + migrations.AddField( + model_name='crontabschedule', + name='name', + field=models.CharField(blank=True, help_text='Short Description For This Cron Schedule', max_length=200, null=True, verbose_name='Name'), + ), + ] From 58f174391efe23134007d0f8de38129ce8c737f1 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Fri, 17 Dec 2021 10:00:25 +0200 Subject: [PATCH 4/8] Adding name col to CrontabSchedule model. Add migrations. Generated by Django 3.2 (venv) PS D:\Projects\..\django-celery-beat> python -m django --version 3.2 --- django_celery_beat/migrations/0016_crontabschedule_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_celery_beat/migrations/0016_crontabschedule_name.py b/django_celery_beat/migrations/0016_crontabschedule_name.py index e8395385..5d2246c6 100644 --- a/django_celery_beat/migrations/0016_crontabschedule_name.py +++ b/django_celery_beat/migrations/0016_crontabschedule_name.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.7 on 2021-12-15 13:06 +# Generated by Django 3.2 on 2021-12-17 07:58 from django.db import migrations, models From 35ac4dcc3e1a70a87253208be31a67c7b6f831a7 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 24 Jan 2022 17:21:51 +0200 Subject: [PATCH 5/8] django_celery_beat admin.py PeriodicTaskAdmin ~ list_display: added name of the "Periodic Task" rather than str: it made the name field easy to read and sort by. ~ list_filter: added "crontab" sequence to the very end, it helps to sort all "Periodic Task"s related to selected "crontab". ~ Arguments: not collapsed, usually when you're editing a task - you edit crontab or arguments. No need to click each time to open collapsed block. models.py ~ CrontabSchedule: simpler name for crontab ~ PeriodicTask: do not show interval or crontab in the name of the task, since we have a table col for that already. It also enabled sort by this name. --- django_celery_beat/admin.py | 6 +++--- django_celery_beat/models.py | 10 +++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/django_celery_beat/admin.py b/django_celery_beat/admin.py index 794a9fd7..3c25ee03 100644 --- a/django_celery_beat/admin.py +++ b/django_celery_beat/admin.py @@ -116,9 +116,9 @@ class PeriodicTaskAdmin(admin.ModelAdmin): model = PeriodicTask celery_app = current_app date_hierarchy = 'start_time' - list_display = ('__str__', 'enabled', 'interval', 'start_time', + list_display = ('name', 'enabled', 'crontab', 'interval', 'start_time', 'last_run_at', 'one_off') - list_filter = ['enabled', 'one_off', 'task', 'start_time', 'last_run_at'] + list_filter = ['enabled', 'one_off', 'task', 'start_time', 'last_run_at', 'crontab'] actions = ('enable_tasks', 'disable_tasks', 'toggle_tasks', 'run_tasks') search_fields = ('name',) fieldsets = ( @@ -133,7 +133,7 @@ class PeriodicTaskAdmin(admin.ModelAdmin): }), ('Arguments', { 'fields': ('args', 'kwargs'), - 'classes': ('extrapretty', 'wide', 'collapse', 'in'), + 'classes': ('extrapretty', 'wide', 'in'), }), ('Execution Options', { 'fields': ('expires', 'expire_seconds', 'queue', 'exchange', diff --git a/django_celery_beat/models.py b/django_celery_beat/models.py index 2087f737..5de680ad 100644 --- a/django_celery_beat/models.py +++ b/django_celery_beat/models.py @@ -316,15 +316,11 @@ class Meta: def __str__(self): - name = None - if self.name: - name = '[{}]'.format(self.name) - return '{0} {1} {2} {3} {4} (m/h/dM/MY/d) {5} {6}'.format( cronexp(self.minute), cronexp(self.hour), cronexp(self.day_of_month), cronexp(self.month_of_year), cronexp(self.day_of_week), str(self.timezone), - name if name else '', + '[{}]'.format(self.name) if self.name else '', ) @property @@ -606,9 +602,9 @@ def expires_(self): def __str__(self): fmt = '{0.name}: {{no schedule}}' if self.interval: - fmt = '{0.name}: {0.interval}' + fmt = '{0.name}' if self.crontab: - fmt = '{0.name}: {0.crontab}' + fmt = '{0.name}' if self.solar: fmt = '{0.name}: {0.solar}' if self.clocked: From b3eccccb6dba1fd6ede59aa3b0b6d413b4c26b8c Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 24 Jan 2022 18:36:42 +0200 Subject: [PATCH 6/8] django_celery_beat admin.py PeriodicTaskAdmin ~ list_display: added name of the "Periodic Task" rather than str: it made the name field easy to read and sort by. ~ list_filter: added "crontab" sequence to the very end, it helps to sort all "Periodic Task"s related to selected "crontab". ~ Arguments: not collapsed, usually when you're editing a task - you edit crontab or arguments. No need to click each time to open collapsed block. models.py ~ CrontabSchedule: simpler name for crontab ~ PeriodicTask: do not show interval or crontab in the name of the task, since we have a table col for that already. It also enabled sort by this name. admin.py ~ Added view form for CrontabSchedule, sorting by name, display name, __str__, timezone, made easy to sort\group items, rename, fix or delete obsolete ones. --- django_celery_beat/admin.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/django_celery_beat/admin.py b/django_celery_beat/admin.py index 3c25ee03..10d0c2b4 100644 --- a/django_celery_beat/admin.py +++ b/django_celery_beat/admin.py @@ -247,8 +247,19 @@ class ClockedScheduleAdmin(admin.ModelAdmin): ) +class CrontabScheduleAdmin(admin.ModelAdmin): + """Admin-interface for crontab items.""" + model = CrontabSchedule + celery_app = current_app + list_display = ( + 'name', + '__str__', + 'timezone', + ) + + admin.site.register(IntervalSchedule) -admin.site.register(CrontabSchedule) admin.site.register(SolarSchedule) +admin.site.register(CrontabSchedule, CrontabScheduleAdmin) admin.site.register(ClockedSchedule, ClockedScheduleAdmin) admin.site.register(PeriodicTask, PeriodicTaskAdmin) From 21de9a1373ffce0b33b715e5b95a9ffad6068ffc Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Tue, 25 Jan 2022 10:47:11 +0200 Subject: [PATCH 7/8] django_celery_beat admin.py PeriodicTaskAdmin ~ list_display: added name of the "Periodic Task" rather than str: it made the name field easy to read and sort by. ~ list_filter: added "crontab" sequence to the very end, it helps to sort all "Periodic Task"s related to selected "crontab". ~ Arguments: not collapsed, usually when you're editing a task - you edit crontab or arguments. No need to click each time to open collapsed block. models.py ~ CrontabSchedule: simpler name for crontab ~ PeriodicTask: do not show interval or crontab in the name of the task, since we have a table col for that already. It also enabled sort by this name. admin.py ~ Added view form for CrontabSchedule, sorting by name, display name, __str__, timezone, made easy to sort\group items, rename, fix or delete obsolete ones. unit test_models.py + Add simple test on creation named crontab entry with possible valid names. --- t/unit/test_models.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/t/unit/test_models.py b/t/unit/test_models.py index 627e47b5..44405ade 100644 --- a/t/unit/test_models.py +++ b/t/unit/test_models.py @@ -83,7 +83,7 @@ def _test_duplicate_schedules(self, cls, kwargs, schedule=None): class CrontabScheduleTestCase(TestCase, TestDuplicatesMixin): - FIRST_VALID_TIMEZONE = timezone_field.\ + FIRST_VALID_TIMEZONE = timezone_field. \ TimeZoneField.default_choices[0][0].zone def test_default_timezone_without_settings_config(self): @@ -106,6 +106,30 @@ def test_duplicate_schedules(self): schedule = schedules.crontab(hour="4") self._test_duplicate_schedules(CrontabSchedule, kwargs, schedule) + def test_name_crontab(self): + """ + Test usual crontab creation with possible symbols in name. + This shouldn't be an issue, since Django form is sorting it out. + """ + valid_names = [ + "Crontab name", + "Crontab name with | and \\ / and '' ", + "Name with 123, 56, !@#$%^&*()-+=", # Any delimiters people want to use + "", # Empty string should be fine too. + ] + for name in valid_names: + kwargs = { + "minute": "*", + "hour": "4", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + "name": name + } + CrontabSchedule.objects.create(**kwargs) + named_crontab_get = CrontabSchedule.objects.get(**kwargs) + self.assertEqual(named_crontab_get.name, name) + class SolarScheduleTestCase(TestCase): EVENT_CHOICES = SolarSchedule._meta.get_field("event").choices From 9bb7fa43ae4eb4a8160a07ce4eaf973e4e262d43 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Tue, 25 Jan 2022 11:13:30 +0200 Subject: [PATCH 8/8] django_celery_beat admin.py PeriodicTaskAdmin ~ list_display: added name of the "Periodic Task" rather than str: it made the name field easy to read and sort by. ~ list_filter: added "crontab" sequence to the very end, it helps to sort all "Periodic Task"s related to selected "crontab". ~ Arguments: not collapsed, usually when you're editing a task - you edit crontab or arguments. No need to click each time to open collapsed block. models.py ~ CrontabSchedule: simpler name for crontab ~ PeriodicTask: do not show interval or crontab in the name of the task, since we have a table col for that already. It also enabled sort by this name. admin.py ~ Added view form for CrontabSchedule, sorting by name, display name, __str__, timezone, made easy to sort\group items, rename, fix or delete obsolete ones. django_celery_beat models.py ~ There was an extra space on __str__ when name is not set. Now it shouldn't appear. unit test_models.py + Add simple test on creation named crontab entry with possible valid names. unit test_schedulers.py + Assert PeriodicTask name is in correct format and have a space and [] braces. ~ Assert PeriodicTask name without cron\interval appendix. + Assert PeriodicTask cron\interval __str__ is expected in human-readable format. --- django_celery_beat/models.py | 4 ++-- t/unit/test_schedulers.py | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/django_celery_beat/models.py b/django_celery_beat/models.py index 5de680ad..62e2520e 100644 --- a/django_celery_beat/models.py +++ b/django_celery_beat/models.py @@ -316,11 +316,11 @@ class Meta: def __str__(self): - return '{0} {1} {2} {3} {4} (m/h/dM/MY/d) {5} {6}'.format( + return '{0} {1} {2} {3} {4} (m/h/dM/MY/d) {5}{6}'.format( cronexp(self.minute), cronexp(self.hour), cronexp(self.day_of_month), cronexp(self.month_of_year), cronexp(self.day_of_week), str(self.timezone), - '[{}]'.format(self.name) if self.name else '', + ' [{}]'.format(self.name) if self.name else '', ) @property diff --git a/t/unit/test_schedulers.py b/t/unit/test_schedulers.py index fdc575e8..ed48f55b 100644 --- a/t/unit/test_schedulers.py +++ b/t/unit/test_schedulers.py @@ -612,19 +612,33 @@ def test_CrontabSchedule_unicode(self): day_of_month='*/2', month_of_year='4,6', )) == '3 3 */2 4,6 tue (m/h/dM/MY/d) UTC' + # No spaces should be present at the end of the string in asserts above! + # Check if name is correct and have a space before. + assert str(CrontabSchedule( + minute=3, + hour=3, + day_of_week='tue', + day_of_month='*/2', + month_of_year='4,6', + name='Name' + )) == '3 3 */2 4,6 tue (m/h/dM/MY/d) UTC [Name]' def test_PeriodicTask_unicode_interval(self): p = self.create_model_interval(schedule(timedelta(seconds=10))) - assert str(p) == '{0}: every 10.0 seconds'.format(p.name) + assert str(p) == '{0}'.format(p.name) # Task name is name. + assert str(p.interval) == 'every 10.0 seconds' # But interval is human-readable too. def test_PeriodicTask_unicode_crontab(self): + """ + No longer show PeriodicTask name + Crontab name. + We have a separate column in admin section, so there is no need to duplicate this info in the name. + """ p = self.create_model_crontab(crontab( hour='4, 5', day_of_week='4, 5', )) - assert str(p) == """{0}: * 4,5 * * 4,5 (m/h/dM/MY/d) UTC""".format( - p.name - ) + assert str(p) == "{0}".format(p.name) # Task name is name. + assert str(p.crontab) == """* 4,5 * * 4,5 (m/h/dM/MY/d) UTC""" # But cron is human-readable too. def test_PeriodicTask_unicode_solar(self): p = self.create_model_solar(