Skip to content

Commit

Permalink
Add solar schedules (#9)
Browse files Browse the repository at this point in the history
* Add solar schedules

* Flake and cleanup for tests

* Fix extra characters

* Change verbose_name for Solar model

* Force uniqueness for Solar fields

* Wrong field name for longitude
  • Loading branch information
mheppner authored and auvipy committed Dec 9, 2016
1 parent 8041f45 commit ec46367
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 8 deletions.
6 changes: 4 additions & 2 deletions django_celery_beat/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .models import (
PeriodicTask, PeriodicTasks,
IntervalSchedule, CrontabSchedule,
SolarSchedule
)
from .utils import is_database_scheduler

Expand Down Expand Up @@ -122,7 +123,7 @@ class PeriodicTaskAdmin(admin.ModelAdmin):
'classes': ('extrapretty', 'wide'),
}),
('Schedule', {
'fields': ('interval', 'crontab'),
'fields': ('interval', 'crontab', 'solar'),
'classes': ('extrapretty', 'wide', ),
}),
('Arguments', {
Expand All @@ -144,7 +145,7 @@ def changelist_view(self, request, extra_context=None):

def get_queryset(self, request):
qs = super(PeriodicTaskAdmin, self).get_queryset(request)
return qs.select_related('interval', 'crontab')
return qs.select_related('interval', 'crontab', 'solar')

def enable_tasks(self, request, queryset):
rows_updated = queryset.update(enabled=True)
Expand Down Expand Up @@ -175,4 +176,5 @@ def disable_tasks(self, request, queryset):

admin.site.register(IntervalSchedule)
admin.site.register(CrontabSchedule)
admin.site.register(SolarSchedule)
admin.site.register(PeriodicTask, PeriodicTaskAdmin)
52 changes: 52 additions & 0 deletions django_celery_beat/migrations/0002_auto_20161118_0346.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-11-18 03:46
from __future__ import absolute_import, unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('django_celery_beat', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='SolarSchedule',
fields=[
('id', models.AutoField(
auto_created=True, primary_key=True,
serialize=False, verbose_name='ID')),
('event', models.CharField(
choices=[('dusk_nautical', 'dusk_nautical'),
('dawn_astronomical', 'dawn_astronomical'),
('dawn_nautical', 'dawn_nautical'),
('dawn_civil', 'dawn_civil'),
('sunset', 'sunset'),
('solar_noon', 'solar_noon'),
('dusk_astronomical', 'dusk_astronomical'),
('sunrise', 'sunrise'),
('dusk_civil', 'dusk_civil')],
max_length=24, verbose_name='event')),
('latitude', models.DecimalField(
decimal_places=6, max_digits=9, verbose_name='latitude')),
('longitude', models.DecimalField(
decimal_places=6, max_digits=9, verbose_name='latitude')),
],
options={
'ordering': ['event', 'latitude', 'longitude'],
'verbose_name': 'solar',
'verbose_name_plural': 'solars',
},
),
migrations.AddField(
model_name='periodictask',
name='solar',
field=models.ForeignKey(
blank=True, help_text='Use a solar schedule',
null=True, on_delete=django.db.models.deletion.CASCADE,
to='django_celery_beat.SolarSchedule', verbose_name='solar'),
),
]
26 changes: 26 additions & 0 deletions django_celery_beat/migrations/0003_auto_20161209_0049.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2016-12-09 00:49
from __future__ import absolute_import, unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('django_celery_beat', '0002_auto_20161118_0346'),
]

operations = [
migrations.AlterModelOptions(
name='solarschedule',
options={
'ordering': ('event', 'latitude', 'longitude'),
'verbose_name': 'solar event',
'verbose_name_plural': 'solar events'},
),
migrations.AlterUniqueTogether(
name='solarschedule',
unique_together=set([('event', 'latitude', 'longitude')]),
),
]
73 changes: 67 additions & 6 deletions django_celery_beat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,61 @@
(MICROSECONDS, _('Microseconds')),
)

SOLAR_SCHEDULES = [(x, _(x)) for x in schedules.solar._all_events]


def cronexp(field):
"""Representation of cron expression."""
return field and str(field).replace(' ', '') or '*'


@python_2_unicode_compatible
class SolarSchedule(models.Model):
"""Schedule following astronomical patterns."""

event = models.CharField(
_('event'), max_length=24, choices=SOLAR_SCHEDULES
)
latitude = models.DecimalField(
_('latitude'), max_digits=9, decimal_places=6
)
longitude = models.DecimalField(
_('longitude'), max_digits=9, decimal_places=6
)

class Meta:
"""Table information."""

verbose_name = _('solar event')
verbose_name_plural = _('solar events')
ordering = ('event', 'latitude', 'longitude')
unique_together = ('event', 'latitude', 'longitude')

@property
def schedule(self):
return schedules.solar(self.event, self.latitude, self.longitude)

@classmethod
def from_schedule(cls, schedule):
spec = {'event': schedule.event,
'latitude': schedule.lat,
'longitude': schedule.lon}
try:
return cls.objects.get(**spec)
except cls.DoesNotExist:
return cls(**spec)
except MultipleObjectsReturned:
cls.objects.filter(**spec).delete()
return cls(**spec)

def __str__(self):
return '{0} ({1}, {2})'.format(
self.get_event_display(),
self.latitude,
self.longitude
)


@python_2_unicode_compatible
class IntervalSchedule(models.Model):
"""Schedule executing every n seconds."""
Expand Down Expand Up @@ -179,6 +228,10 @@ class PeriodicTask(models.Model):
CrontabSchedule, null=True, blank=True, verbose_name=_('crontab'),
help_text=_('Use one of interval/crontab'),
)
solar = models.ForeignKey(
SolarSchedule, null=True, blank=True, verbose_name=_('solar'),
help_text=_('Use a solar schedule')
)
args = models.TextField(
_('Arguments'), blank=True, default='[]',
help_text=_('JSON encoded positional arguments'),
Expand Down Expand Up @@ -224,12 +277,18 @@ class Meta:

def validate_unique(self, *args, **kwargs):
super(PeriodicTask, self).validate_unique(*args, **kwargs)
if not self.interval and not self.crontab:
raise ValidationError(
{'interval': ['One of interval or crontab must be set.']})
if self.interval and self.crontab:
raise ValidationError(
{'crontab': ['Only one of interval or crontab must be set']})
if not self.interval and not self.crontab and not self.solar:
raise ValidationError({
'interval': [
'One of interval, crontab, or solar must be set.'
]
})
if self.interval and self.crontab and self.solar:
raise ValidationError({
'crontab': [
'Only one of interval, crontab, or solar must be set'
]
})

def save(self, *args, **kwargs):
self.exchange = self.exchange or None
Expand All @@ -245,6 +304,8 @@ def __str__(self):
fmt = '{0.name}: {0.interval}'
if self.crontab:
fmt = '{0.name}: {0.crontab}'
if self.solar:
fmt = '{0.name}: {0.solar}'
return fmt.format(self)

@property
Expand Down
2 changes: 2 additions & 0 deletions django_celery_beat/schedulers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .models import (
PeriodicTask, PeriodicTasks,
CrontabSchedule, IntervalSchedule,
SolarSchedule,
)
from .utils import make_aware

Expand Down Expand Up @@ -47,6 +48,7 @@ class ModelEntry(ScheduleEntry):
model_schedules = (
(schedules.crontab, CrontabSchedule, 'crontab'),
(schedules.schedule, IntervalSchedule, 'interval'),
(schedules.solar, SolarSchedule, 'solar'),
)
save_fields = ['last_run_at', 'total_run_count', 'no_changes']

Expand Down

0 comments on commit ec46367

Please sign in to comment.