Skip to content

Commit 4553cb2

Browse files
Merge pull request #109 from datosgobar/105-synchronizer-day-of-week-select
Permito programar Synchronizers por día de semana
2 parents 164dcb0 + 5137067 commit 4553cb2

File tree

9 files changed

+177
-17
lines changed

9 files changed

+177
-17
lines changed

django_datajsonar/admin.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#!coding=utf8
22
from __future__ import unicode_literals
33

4+
import json
5+
46
from django import forms
57
from django.conf import settings
68
from django.contrib.contenttypes.models import ContentType
7-
from django.core.exceptions import ValidationError
89
from django.forms.models import formset_factory
910
from django.contrib import admin, messages
1011
from django.contrib.admin import helpers, SimpleListFilter
@@ -386,7 +387,8 @@ def _synchro_view(self, request, synchro=None):
386387
synchro_form = SynchroForm({
387388
'name': synchro.name,
388389
'frequency': synchro.frequency,
389-
'scheduled_time': synchro.scheduled_time
390+
'scheduled_time': synchro.scheduled_time,
391+
'week_days': synchro.get_days_of_week()
390392
})
391393
else:
392394
synchro_form = SynchroForm()
@@ -443,6 +445,7 @@ def post_synchro_edit(self, request, object_id=None):
443445
'name': synchro_name,
444446
'frequency': synchro_form.cleaned_data['frequency'],
445447
'scheduled_time': synchro_form.cleaned_data['scheduled_time'],
448+
'week_days': json.dumps(synchro_form.cleaned_data['week_days']),
446449
}
447450
create_or_update_synchro(object_id, stages, data)
448451

django_datajsonar/forms.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
from scheduler.models import RepeatableJob
1111

12-
from django_datajsonar.models import Synchronizer, Stage, AbstractTask
13-
from django_datajsonar.utils import get_qualified_name
12+
from django_datajsonar.models import Stage
13+
from . import strings
1414

1515

1616
class ScheduleJobForm(forms.ModelForm):
@@ -72,10 +72,32 @@ class SynchroForm(forms.Form):
7272
scheduled_time = forms.TimeField(
7373
widget=AdminTimeWidget(attrs={'type': 'time'}))
7474

75+
MON = strings.MON
76+
TUE = strings.TUE
77+
WED = strings.WED
78+
THU = strings.THU
79+
FRI = strings.FRI
80+
SAT = strings.SAT
81+
SUN = strings.SUN
82+
83+
WEEK_DAY_CHOICES = (
84+
(MON, 'Monday'),
85+
(TUE, 'Tuesday'),
86+
(WED, 'Wednesday'),
87+
(THU, 'Thursday'),
88+
(FRI, 'Friday'),
89+
(SAT, 'Saturday'),
90+
(SUN, 'Sunday'),
91+
)
92+
93+
week_days = forms.MultipleChoiceField(choices=WEEK_DAY_CHOICES, required=False)
94+
7595
def clean(self):
7696
cleaned_data = super(SynchroForm, self).clean()
77-
if cleaned_data['frequency'] == self.WEEK_DAYS:
78-
self.add_error('frequency', 'week days not yet implemented')
97+
days = cleaned_data['week_days']
98+
frequency = cleaned_data['frequency']
99+
if frequency == self.WEEK_DAYS and not days:
100+
self.add_error('week_days', 'Days of week not selected')
79101

80102

81103
class StageForm(forms.Form):

django_datajsonar/frequency.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
from croniter import croniter
44

5-
from django_datajsonar.strings import SYNCHRO_DAILY_FREQUENCY
65

6+
def get_next_run_date(start_time, scheduled_time, week_days=None):
7+
if not week_days:
8+
week_days_field = "*"
9+
else:
10+
week_days_field = ','.join(week_days)
711

8-
def get_next_run_date(start_time, scheduled_time, frequency):
9-
if frequency != SYNCHRO_DAILY_FREQUENCY:
10-
raise NotImplementedError
11-
12-
cron_string = "{} {} * * *".format(scheduled_time.minute, scheduled_time.hour)
12+
cron_string = "{} {} * * {}".format(
13+
scheduled_time.minute,
14+
scheduled_time.hour,
15+
week_days_field
16+
)
1317

1418
return croniter(cron_string, start_time=start_time).get_next(datetime)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.18 on 2019-02-05 18:55
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('django_datajsonar', '0001_auto_20190201_1344'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='synchronizer',
17+
name='week_days',
18+
field=models.TextField(blank=True),
19+
),
20+
migrations.AlterField(
21+
model_name='synchronizer',
22+
name='frequency',
23+
field=models.CharField(choices=[('every day', 'Every day'), ('week days', 'Week days')], default='every day', max_length=16),
24+
),
25+
]

django_datajsonar/models.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#! coding: utf-8
22
from __future__ import unicode_literals
33

4+
import json
45
from importlib import import_module
56

67
from django.contrib.auth.models import User
@@ -461,14 +462,16 @@ class Synchronizer(models.Model):
461462
WEEK_DAYS = SYNCHRO_WEEK_DAYS_FREQUENCY
462463
DAILY = SYNCHRO_DAILY_FREQUENCY
463464
FREQUENCY_CHOICES = (
464-
(DAILY, DAILY),
465-
(WEEK_DAYS, WEEK_DAYS),
465+
(DAILY, 'Every day'),
466+
(WEEK_DAYS, 'Week days'),
466467
)
467468
frequency = models.CharField(choices=FREQUENCY_CHOICES, max_length=16, default=DAILY)
468-
scheduled_time = models.TimeField(auto_now_add=True)
469+
scheduled_time = models.TimeField(default=timezone.now)
469470

470471
last_time_ran = models.DateTimeField(auto_now_add=True)
471472

473+
week_days = models.TextField(blank=True)
474+
472475
def begin_stage(self, stage=None):
473476
if self.status == self.RUNNING and stage is None:
474477
raise Exception('El synchronizer ya está corriendo, pero no se pasó la siguiente etapa.')
@@ -496,8 +499,9 @@ def next_stage(self):
496499
self.begin_stage(self.actual_stage.next_stage)
497500

498501
def next_start_date(self):
499-
localtime = self.last_time_ran.astimezone(timezone.get_current_timezone())
500-
return get_next_run_date(localtime, self.scheduled_time, self.frequency)
502+
start_time = self.last_time_ran.astimezone(timezone.get_current_timezone())
503+
week_days = self.get_days_of_week()
504+
return get_next_run_date(start_time, self.scheduled_time, week_days=week_days)
501505

502506
def __unicode__(self):
503507
return self.name
@@ -512,3 +516,8 @@ def get_stages(self):
512516
stages.append(current_stage)
513517
current_stage = current_stage.next_stage
514518
return stages
519+
520+
def get_days_of_week(self):
521+
if self.frequency == self.WEEK_DAYS:
522+
return json.loads(self.week_days)
523+
return []
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
window.onload = function() {
2+
const frequency = document.getElementById('id_frequency');
3+
frequency.addEventListener("change", hideWeekDays);
4+
hideWeekDays();
5+
};
6+
7+
function hideWeekDays() {
8+
const frequency = document.getElementById('id_frequency');
9+
const weekDays = document.getElementById('id_week_days');
10+
const weekDaysDiv = weekDays.closest('.field-week_days');
11+
const option = frequency.options[frequency.selectedIndex].text;
12+
13+
if (option === 'week days') {
14+
weekDaysDiv.style.display = "";
15+
}
16+
17+
if (option === 'every day') {
18+
const options = weekDays.options;
19+
for (let i = 0; i < options.length; i++) {
20+
options[i].selected = false;
21+
}
22+
23+
weekDaysDiv.style.display = "none";
24+
}
25+
}

django_datajsonar/strings.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,13 @@
55

66
SYNCHRO_DAILY_FREQUENCY = 'every day'
77
SYNCHRO_WEEK_DAYS_FREQUENCY = 'week days'
8+
9+
10+
# Valores de días de la semana leídos por librerías de crontab, según el estándar.
11+
MON = 'MON'
12+
TUE = 'TUE'
13+
WED = 'WED'
14+
THU = 'THU'
15+
FRI = 'FRI'
16+
SAT = 'SAT'
17+
SUN = 'SUN'

django_datajsonar/templates/synchronizer.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
</form>
5151
<script src="{% static 'django_datajsonar/js/jquery-3.3.1.min.js' %}"></script>
5252
<script src="{% static 'django_datajsonar/js/jquery.formset.js' %}"></script>
53+
<script src="{% static 'django_datajsonar/js/synchronizer.js' %}"></script>
5354
<script type="text/javascript">
5455
$(function() {
5556
$('#synchroForm tbody tr').formset();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from datetime import time, datetime
2+
3+
from django.test import TestCase
4+
from freezegun import freeze_time
5+
6+
from django_datajsonar.frequency import get_next_run_date
7+
from django_datajsonar import strings
8+
9+
10+
@freeze_time("2019-01-01 06:00:00") # Tuesday
11+
class NextRunDateTests(TestCase):
12+
13+
def setUp(self):
14+
self.now = datetime.now() # Localtime
15+
16+
def test_get_daily_next_run_date(self):
17+
18+
nine_am = time(9, 0)
19+
next_run_date = get_next_run_date(start_time=self.now, scheduled_time=nine_am)
20+
21+
self.assertEqual(next_run_date, datetime(2019, 1, 1, 9, 0, 0))
22+
23+
def test_get_daily_next_run_date_for_tomorrow(self):
24+
25+
before_now = time(5, 0)
26+
next_run_date = get_next_run_date(start_time=self.now, scheduled_time=before_now)
27+
28+
self.assertEqual(next_run_date, datetime(2019, 1, 2, 5, 0, 0))
29+
30+
def test_get_next_run_date_weekdays(self):
31+
32+
nine_am = time(9, 0)
33+
week_days = [strings.MON, strings.TUE]
34+
next_run_date = get_next_run_date(start_time=self.now, scheduled_time=nine_am, week_days=week_days)
35+
36+
self.assertEqual(next_run_date, datetime(2019, 1, 1, 9, 0, 0)) # Today is tuesday!
37+
38+
def test_get_next_run_date_weekdays_not_today(self):
39+
nine_am = time(9, 0)
40+
week_days = ['MON']
41+
next_run_date = get_next_run_date(start_time=self.now, scheduled_time=nine_am, week_days=week_days)
42+
43+
self.assertEqual(next_run_date, datetime(2019, 1, 7, 9, 0, 0)) # Next monday
44+
45+
def test_iterate_next_run_dates(self):
46+
nine_am = time(9, 0)
47+
week_days = [strings.MON, strings.TUE, strings.WED, strings.THU]
48+
49+
expected_dates = [ # Tuesday, Wednesday, Thursday, then next week's Monday
50+
datetime(2019, 1, 1, 9, 0, 0),
51+
datetime(2019, 1, 2, 9, 0, 0),
52+
datetime(2019, 1, 3, 9, 0, 0),
53+
datetime(2019, 1, 7, 9, 0, 0),
54+
]
55+
56+
start_time = self.now
57+
for expected_date in expected_dates:
58+
next_run_date = get_next_run_date(start_time=start_time, scheduled_time=nine_am, week_days=week_days)
59+
60+
self.assertEqual(next_run_date, expected_date)
61+
start_time = next_run_date

0 commit comments

Comments
 (0)