Projet

Général

Profil

0001-misc-use-Django-native-split-datetime-field-45108.patch

Benjamin Dauvergne, 21 juillet 2020 20:57

Télécharger (17,8 ko)

Voir les différences:

Subject: [PATCH] misc: use Django native split datetime field (#45108)

 chrono/manager/forms.py                       |  16 ++-
 .../manager_time_period_exception_form.html   |   4 +-
 .../templates/chrono/splitdatetime.html       |   9 ++
 .../templates/chrono/widget_datetime.html     |   4 -
 chrono/manager/widgets.py                     |  34 ++----
 tests/test_manager.py                         | 110 +++++++++---------
 tests/test_misc.py                            |   6 -
 7 files changed, 89 insertions(+), 94 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/splitdatetime.html
 delete mode 100644 chrono/manager/templates/chrono/widget_datetime.html
 delete mode 100644 tests/test_misc.py
chrono/manager/forms.py
42 42
)
43 43

  
44 44
from . import widgets
45
from .widgets import DateTimeWidget
45
from .widgets import SplitDateTimeField
46 46

  
47 47

  
48 48
class AgendaAddForm(forms.ModelForm):
......
87 87
        model = Event
88 88
        widgets = {
89 89
            'agenda': forms.HiddenInput(),
90
            'start_datetime': DateTimeWidget(),
91 90
            'publication_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
92 91
        }
93 92
        exclude = ['full', 'meeting_type', 'desk', 'slug', 'resources']
93
        field_classes = {
94
            'start_datetime': SplitDateTimeField,
95
        }
94 96

  
95 97

  
96 98
class EventForm(forms.ModelForm):
......
98 100
        model = Event
99 101
        widgets = {
100 102
            'agenda': forms.HiddenInput(),
101
            'start_datetime': DateTimeWidget(),
102 103
            'publication_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
103 104
        }
105
        field_classes = {
106
            'start_datetime': SplitDateTimeField,
107
        }
104 108
        exclude = ['full', 'meeting_type', 'desk', 'resources']
105 109

  
106 110

  
......
230 234
        fields = ['desk', 'start_datetime', 'end_datetime', 'label']
231 235
        widgets = {
232 236
            'desk': forms.HiddenInput(),
233
            'start_datetime': DateTimeWidget(),
234
            'end_datetime': DateTimeWidget(),
237
        }
238
        field_classes = {
239
            'start_datetime': SplitDateTimeField,
240
            'end_datetime': SplitDateTimeField,
235 241
        }
236 242

  
237 243
    def clean(self):
chrono/manager/templates/chrono/manager_time_period_exception_form.html
60 60
    if ($start_datetime.val()) {
61 61
      var new_date = new Date($start_datetime.val());
62 62
      new_date.setDate(new_date.getDate() + 1);
63
      $('[name="start_datetime$time"]').val('00:00');
63
      $('[name="start_datetime_1"]').val('00:00');
64 64
      $('[name="end_datetime$date"]').val(new_date.toISOString().substring(0, 10));
65
      $('[name="end_datetime$time"]').val('00:00');
65
      $('[name="end_datetime_1"]').val('00:00');
66 66
    }
67 67
  });
68 68
  </script>
chrono/manager/templates/chrono/splitdatetime.html
1
<span class="datetime">
2
{% with widget=widget.subwidgets.0 %}
3
  <input type="date" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} />
4
{% endwith %}
5
{% with widget=widget.subwidgets.1 %}
6
    <!-- {{ widget.value|pprint }} -->
7
    <input type="time" name="{{ widget.name }}" pattern="[0-9]{2}:[0-9]{2}" step="300" {% if widget.value != None %} value="{{ widget.value }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} />
8
{% endwith %}
9
</span>
chrono/manager/templates/chrono/widget_datetime.html
1
<span class="datetime">
2
<input type="date" name="{{ widget.name }}$date" {% if widget.value.date != None %} value="{{ widget.value.date }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} />
3
<input type="time" name="{{ widget.name }}$time" pattern="[0-9]{2}:[0-9]{2}" step="300" {% if widget.value.time != None %} value="{{ widget.value.time }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} />
4
</span>
chrono/manager/widgets.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17

  
18
import datetime
19

  
20
from django.forms.widgets import DateTimeInput, TimeInput, SelectMultiple
21
from django.utils import dateparse
18
from django.forms.fields import SplitDateTimeField
19
from django.forms.widgets import TimeInput, SelectMultiple, SplitDateTimeWidget
22 20
from django.utils.safestring import mark_safe
23 21

  
24 22

  
25
class DateTimeWidget(DateTimeInput):
26
    template_name = 'chrono/widget_datetime.html'
23
class SplitDateTimeWidget(SplitDateTimeWidget):
24
    template_name = 'chrono/splitdatetime.html'
27 25

  
28
    def format_value(self, value):
29
        if value:
30
            x = {
31
                'date': value.date().strftime('%Y-%m-%d'),
32
                'time': value.time().strftime('%H:%M'),
33
            }
34
            return x
35
        return None
26
    def __init__(self, *args, **kwargs):
27
        kwargs['time_format'] = '%H:%M'
28
        super().__init__(*args, **kwargs)
36 29

  
37
    def value_from_datadict(self, data, files, name):
38
        date_string = data.get(name + '$date')
39
        time_string = data.get(name + '$time')
40
        if not date_string or not time_string:
41
            return None
42
        date_value = dateparse.parse_date(date_string)
43
        time_value = dateparse.parse_time(time_string)
44
        if not date_value or not time_value:
45
            return None
46
        return datetime.datetime.combine(date_value, time_value)
30

  
31
class SplitDateTimeField(SplitDateTimeField):
32
    widget = SplitDateTimeWidget
47 33

  
48 34

  
49 35
class TimeWidget(TimeInput):
tests/test_manager.py
901 901
    assert "This agenda doesn't have any event yet." in resp.text
902 902
    year = now().year + 1
903 903
    resp = resp.click('New Event')
904
    resp.form['start_datetime$date'] = '%s-02-15' % year
905
    resp.form['start_datetime$time'] = '17:00'
904
    resp.form['start_datetime_0'] = '%s-02-15' % year
905
    resp.form['start_datetime_1'] = '17:00'
906 906
    resp.form['places'] = 10
907 907
    resp = resp.form.submit()
908 908
    resp = resp.follow()
......
920 920
    # add with a description
921 921
    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
922 922
    resp = resp.click('New Event')
923
    resp.form['start_datetime$date'] = '%s-02-15' % year
924
    resp.form['start_datetime$time'] = '18:00'
923
    resp.form['start_datetime_0'] = '%s-02-15' % year
924
    resp.form['start_datetime_1'] = '18:00'
925 925
    resp.form['publication_date'] = '2020-05-11'
926 926
    resp.form['places'] = 11
927 927
    resp.form['description'] = 'A description'
......
941 941
    ):
942 942
        resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
943 943
        resp = resp.click('New Event')
944
        resp.form['start_datetime$date'] = parts[0]
945
        resp.form['start_datetime$time'] = parts[1]
944
        resp.form['start_datetime_0'] = parts[0]
945
        resp.form['start_datetime_1'] = parts[1]
946 946
        resp.form['places'] = 10
947 947
        resp = resp.form.submit()
948
        assert resp.text.count('This field is required.') == 1
948
        assert (
949
            resp.text.count('Enter a valid date')
950
            or resp.text.count('Enter a valid time') == 1
951
            or resp.text.count('This field is required.') == 1
952
        )
949 953

  
950 954

  
951 955
def test_add_event_on_missing_agenda(app, admin_user):
......
968 972
    resp = resp.click('Settings')
969 973
    assert '<h2>Settings' in resp.text
970 974
    resp = resp.click('New Event')
971
    resp.form['start_datetime$date'] = '2016-02-15'
972
    resp.form['start_datetime$time'] = '17:00'
975
    resp.form['start_datetime_0'] = '2016-02-15'
976
    resp.form['start_datetime_1'] = '17:00'
973 977
    resp.form['places'] = 10
974 978
    resp = resp.form.submit()
975 979
    resp = resp.follow()
......
982 986
    assert event.end_datetime is None
983 987

  
984 988
    resp = resp.click('New Event')
985
    resp.form['start_datetime$date'] = '2016-02-15'
986
    resp.form['start_datetime$time'] = '17:00'
989
    resp.form['start_datetime_0'] = '2016-02-15'
990
    resp.form['start_datetime_1'] = '17:00'
987 991
    resp.form['duration'] = 45
988 992
    resp.form['places'] = 12
989 993
    resp = resp.form.submit()
......
1003 1007
    app = login(app)
1004 1008
    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
1005 1009
    resp = resp.click('Feb. 15, 2016, 5 p.m.')
1006
    assert resp.form['start_datetime$date'].value == '2016-02-15'
1007
    assert resp.form['start_datetime$time'].value == '17:00'
1010
    assert resp.form['start_datetime_0'].value == '2016-02-15'
1011
    assert resp.form['start_datetime_1'].value == '17:00'
1008 1012
    assert resp.form['publication_date'].value == ''
1009 1013
    assert resp.form['duration'].value == ''
1010
    resp.form['start_datetime$date'] = '2016-02-16'
1011
    resp.form['start_datetime$time'] = '17:00'
1014
    resp.form['start_datetime_0'] = '2016-02-16'
1015
    resp.form['start_datetime_1'] = '17:00'
1012 1016
    resp.form['publication_date'] = '2020-05-11'
1013 1017
    resp.form['duration'].value = 45
1014 1018
    resp.form['places'] = 20
......
1045 1049
    agenda.save()
1046 1050
    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
1047 1051
    resp = resp.click('Feb. 15, 2016, 5 p.m.')
1048
    assert resp.form['start_datetime$date'].value == '2016-02-15'
1049
    assert resp.form['start_datetime$time'].value == '17:00'
1052
    assert resp.form['start_datetime_0'].value == '2016-02-15'
1053
    assert resp.form['start_datetime_1'].value == '17:00'
1050 1054
    assert resp.form['publication_date'].value == '2020-05-11'
1051
    resp.form['start_datetime$date'] = '2016-02-16'
1052
    resp.form['start_datetime$time'] = '17:00'
1055
    resp.form['start_datetime_0'] = '2016-02-16'
1056
    resp.form['start_datetime_1'] = '17:00'
1053 1057
    resp.form['publication_date'] = ''
1054 1058
    resp.form['places'] = 20
1055 1059
    resp = resp.form.submit()
......
1679 1683
    tomorrow = make_aware(today + datetime.timedelta(days=1))
1680 1684
    dt_format = '%Y-%m-%d %H:%M'
1681 1685
    resp.form['label'] = 'Exception 1'
1682
    resp.form['start_datetime$date'] = tomorrow.strftime('%Y-%m-%d')
1683
    resp.form['start_datetime$time'] = '08:00'
1684
    resp.form['end_datetime$date'] = tomorrow.strftime('%Y-%m-%d')
1685
    resp.form['end_datetime$time'] = '16:00'
1686
    resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
1687
    resp.form['start_datetime_1'] = '08:00'
1688
    resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
1689
    resp.form['end_datetime_1'] = '16:00'
1686 1690
    resp = resp.form.submit().follow()
1687 1691
    assert TimePeriodException.objects.count() == 1
1688 1692
    time_period_exception = TimePeriodException.objects.first()
......
1696 1700
    resp = resp.click('Add a time period exception', index=1)
1697 1701
    future = tomorrow + datetime.timedelta(days=15)
1698 1702
    resp.form['label'] = 'Exception 2'
1699
    resp.form['start_datetime$date'] = future.strftime('%Y-%m-%d')
1700
    resp.form['start_datetime$time'] = '00:00'
1701
    resp.form['end_datetime$date'] = future.strftime('%Y-%m-%d')
1702
    resp.form['end_datetime$time'] = '16:00'
1703
    resp.form['start_datetime_0'] = future.strftime('%Y-%m-%d')
1704
    resp.form['start_datetime_1'] = '00:00'
1705
    resp.form['end_datetime_0'] = future.strftime('%Y-%m-%d')
1706
    resp.form['end_datetime_1'] = '16:00'
1703 1707
    resp = resp.form.submit().follow()
1704 1708
    assert TimePeriodException.objects.count() == 2
1705 1709
    assert 'Exception 1' in resp.text
......
1728 1732
    # fields should be marked with errors
1729 1733
    assert resp.text.count('This field is required.') == 2
1730 1734
    # try again with data in fields
1731
    resp.form['start_datetime$date'] = '2017-05-22'
1732
    resp.form['start_datetime$time'] = '08:00'
1733
    resp.form['end_datetime$date'] = '2017-05-26'
1734
    resp.form['end_datetime$time'] = '17:30'
1735
    resp.form['start_datetime_0'] = '2017-05-22'
1736
    resp.form['start_datetime_1'] = '08:00'
1737
    resp.form['end_datetime_0'] = '2017-05-26'
1738
    resp.form['end_datetime_1'] = '17:30'
1735 1739
    resp = resp.form.submit().follow()
1736 1740
    assert 'Exception added. Note: one or several bookings exists within this time slot.' in resp.text
1737 1741
    assert TimePeriodException.objects.count() == 1
......
1754 1758
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1755 1759
    resp = resp.click('Settings')
1756 1760
    resp = resp.click('Add a time period exception')
1757
    resp.form['start_datetime$date'] = '2017-05-22'
1758
    resp.form['start_datetime$time'] = '08:00'
1759
    resp.form['end_datetime$date'] = '2017-05-26'
1760
    resp.form['end_datetime$time'] = '17:30'
1761
    resp.form['start_datetime_0'] = '2017-05-22'
1762
    resp.form['start_datetime_1'] = '08:00'
1763
    resp.form['end_datetime_0'] = '2017-05-26'
1764
    resp.form['end_datetime_1'] = '17:30'
1761 1765
    resp = resp.form.submit().follow()
1762 1766
    assert 'Exception added. Note: one or several bookings exists within this time slot.' not in resp.text
1763 1767
    assert TimePeriodException.objects.count() == 1
......
1766 1770
def test_meetings_agenda_add_invalid_time_period_exception():
1767 1771
    form = TimePeriodExceptionForm(
1768 1772
        data={
1769
            'start_datetime$date': '2017-05-26',
1770
            'start_datetime$time': '17:30',
1771
            'end_datetime$date': '2017-05-22',
1772
            'end_datetime$time': '08:00',
1773
            'start_datetime_0': '2017-05-26',
1774
            'start_datetime_1': '17:30',
1775
            'end_datetime_0': '2017-05-22',
1776
            'end_datetime_1': '08:00',
1773 1777
        }
1774 1778
    )
1775 1779
    assert form.is_valid() is False
......
1778 1782
    # start_datetime is invalid
1779 1783
    form = TimePeriodExceptionForm(
1780 1784
        data={
1781
            'start_datetime$date': '2017-05-26',
1782
            'start_datetime$time': 'foo',
1783
            'end_datetime$date': '2017-05-22',
1784
            'end_datetime$time': '08:00',
1785
            'start_datetime_0': '2017-05-26',
1786
            'start_datetime_1': 'foo',
1787
            'end_datetime_0': '2017-05-22',
1788
            'end_datetime_1': '08:00',
1785 1789
        }
1786 1790
    )
1787 1791
    assert form.is_valid() is False
......
1789 1793
    # end_datetime is invalid
1790 1794
    form = TimePeriodExceptionForm(
1791 1795
        data={
1792
            'start_datetime$date': '2017-05-26',
1793
            'start_datetime$time': '17:30',
1794
            'end_datetime$date': 'bar',
1795
            'end_datetime$time': '08:00',
1796
            'start_datetime_0': '2017-05-26',
1797
            'start_datetime_1': '17:30',
1798
            'end_datetime_0': 'bar',
1799
            'end_datetime_1': '08:00',
1796 1800
        }
1797 1801
    )
1798 1802
    assert form.is_valid() is False
......
1813 1817
    tomorrow = make_aware(today + datetime.timedelta(days=15))
1814 1818
    dt_format = '%Y-%m-%d %H:%M'
1815 1819
    resp.form['label'] = 'Exception 1'
1816
    resp.form['start_datetime$date'] = tomorrow.strftime('%Y-%m-%d')
1817
    resp.form['start_datetime$time'] = '08:00'
1818
    resp.form['end_datetime$date'] = tomorrow.strftime('%Y-%m-%d')
1819
    resp.form['end_datetime$time'] = '16:00'
1820
    resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
1821
    resp.form['start_datetime_1'] = '08:00'
1822
    resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d')
1823
    resp.form['end_datetime_1'] = '16:00'
1820 1824
    resp = resp.form.submit().follow()
1821 1825
    assert TimePeriodException.objects.count() == 1
1822 1826
    time_period_exception = TimePeriodException.objects.first()
......
2857 2861
    resp = app.get(event_url)
2858 2862
    assert 'Options' in resp.text
2859 2863
    resp = resp.click('Options')
2860
    resp.form['start_datetime$date'] = agenda.event_set.first().start_datetime.strftime('%Y-%m-%d')
2861
    resp.form['start_datetime$time'] = agenda.event_set.first().start_datetime.strftime('%H:%M')
2864
    resp.form['start_datetime_0'] = agenda.event_set.first().start_datetime.strftime('%Y-%m-%d')
2865
    resp.form['start_datetime_1'] = agenda.event_set.first().start_datetime.strftime('%H:%M')
2862 2866
    resp = resp.form.submit(status=302).follow()
2863 2867
    assert event_url == resp.request.url
2864 2868

  
tests/test_misc.py
1
from chrono.manager.widgets import DateTimeWidget, TimeWidget
2

  
3

  
4
def test_widgets_init():
5
    DateTimeWidget()
6
    TimeWidget()
7
-