Projet

Général

Profil

0001-booking-calendar-cell-display-first-available-slot-i.patch

Josué Kouka, 03 novembre 2017 14:57

Télécharger (7,77 ko)

Voir les différences:

Subject: [PATCH] booking calendar cell: display first available slot if any
 (#19460)

 combo/apps/calendar/static/css/calendar.css        |  5 +++
 .../templates/calendar/booking_calendar_cell.html  |  7 +++-
 combo/apps/calendar/utils.py                       | 47 +++++++++++++++-------
 tests/test_calendar.py                             | 23 +++++++++--
 4 files changed, 62 insertions(+), 20 deletions(-)
combo/apps/calendar/static/css/calendar.css
1 1
div.cell.bookingcalendar a {
2 2
    cursor: pointer;
3 3
}
4

  
5
div.cell.bookingcalendar h2 > span.calinfo {
6
    font-style: italic;
7
    font-size: 13px;
8
}
combo/apps/calendar/templates/calendar/booking_calendar_cell.html
1 1
{% load i18n calendar %}
2 2

  
3 3
{% if cell.title %}
4
<h2>{{cell.title}}</h2>
4
<h2>
5
    <span>{{cell.title}}</span>
6
    {% if calendar %}
7
    <span class="calinfo">{{ calendar.get_info }}</span>
8
    {% endif %}
9
</h2>
5 10
{% endif %}
6 11
<div>
7 12
{% include 'calendar/booking_calendar_content.html' %}
combo/apps/calendar/utils.py
18 18
import math
19 19
import urllib
20 20

  
21

  
22 21
from django.conf import settings
23 22
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
23
from django.utils.dateformat import DateFormat
24 24
from django.utils.dateparse import parse_datetime
25
from django.utils.timezone import now
25
from django.utils.formats import get_format
26
from django.utils.timezone import localtime, make_aware, now
27
from django.utils.translation import ugettext_lazy as _
26 28

  
27 29

  
28 30
from combo.utils import requests
......
85 87
    request = context['request']
86 88
    cell = context['cell']
87 89
    page = request.GET.get('chunk_%s' % cell.pk, 1)
88
    calendar = get_calendar(cell.agenda_reference, cell.slot_duration, cell.chunk_size)
90
    calendar = get_calendar(cell.agenda_reference, cell.slot_duration, cell.chunk_size, cell.minimal_booking_duration)
89 91
    paginator = Paginator(list(calendar.get_days()), cell.chunk_size)
90 92
    try:
91 93
        cal_page = paginator.page(page)
......
99 101
    return context
100 102

  
101 103

  
102
<<<<<<< HEAD
103
def get_calendar(agenda_reference, offset, chunk_size):
104
def get_calendar(agenda_reference, offset, chunk_size, min_duration):
104 105
    if not agenda_reference:
105 106
        return []
106 107
    events = get_chrono_events(agenda_reference)
107
    calendar = Calendar(offset, chunk_size)
108
=======
109
def get_calendar(agenda_reference, offset):
110
    if not agenda_reference:
111
        return []
112
    events = get_chrono_events(agenda_reference)
113
    calendar = Calendar(offset)
114
>>>>>>> booking calendar: display availablilities in rolling days (#19368)
108
    calendar = Calendar(offset, chunk_size, min_duration)
115 109
    for event in events:
116 110
        event_datetime = parse_datetime(event['datetime'])
117 111
        if not calendar.has_day(event_datetime.date()):
......
141 135
class DaySlot(object):
142 136

  
143 137
    def __init__(self, date_time, available, exist=True):
144
        self.date_time = date_time
138
        self.date_time = localtime(make_aware(date_time))
145 139
        self.available = available
146 140
        self.exist = exist
147 141

  
148 142
    def __repr__(self):
149 143
        return '<DaySlot date_time=%s - available=%s>' % (self.date_time.isoformat(), self.available)
150 144

  
145
    def __str__(self):
146
        date = DateFormat(self.date_time).format(get_format('DATE_FORMAT'))
147
        return _('%s at %s') % (date, self.date_time.strftime('%H:%M'))
148

  
151 149
    @property
152 150
    def label(self):
153 151
        return '%s' % self.date_time.isoformat()
......
179 177
    def get_maximum_slot(self):
180 178
        return max(self.slots, key=lambda x: x.date_time.time())
181 179

  
180
    def get_first_available_slot(self, offset, min_duration):
181
        step = min_duration.seconds / offset.seconds
182
        for idx in range(len(self.slots)):
183
            tmp = self.slots[idx:idx + step]
184
            if (tmp[-1].date_time - tmp[0].date_time) != ((step - 1) * offset):
185
                continue
186
            if not all(map(lambda x: x.available, tmp)):
187
                continue
188
            return tmp[0]
189
        return None
190

  
182 191

  
183 192
class Calendar(object):
184 193

  
185
    def __init__(self, offset, chunk_size):
194
    def __init__(self, offset, chunk_size, min_duration):
186 195
        self.offset = offset
187 196
        self.chunk_size = chunk_size
188 197
        self.days = []
198
        self.min_duration = min_duration
189 199

  
190 200
    def __repr__(self):
191 201
        return '<Calendar>'
192 202

  
203
    def get_info(self):
204
        for day in self.days:
205
            first_available_slot = day.get_first_available_slot(self.offset, self.min_duration)
206
            if first_available_slot:
207
                return _('(Next available slot: %s)') % first_available_slot
208
        return _('(No slot available)')
209

  
193 210
    def get_slots(self):
194 211
        start = self.get_minimum_slot()
195 212
        end = self.get_maximum_slot()
tests/test_calendar.py
5 5
import pytest
6 6
import mock
7 7

  
8
from django.utils.dateparse import parse_time
9 8
from django.utils.timezone import now
10 9
from django.contrib.auth.models import User
11 10

  
......
222 221
    assert parsed.path == '/test/'
223 222
    qs = urlparse.parse_qs(parsed.query)
224 223
    assert qs['session_var_booking_agenda_slug'] == ['test']
225
    assert qs['session_var_booking_start'] == ['2017-06-13T08:00:00']
226
    assert qs['session_var_booking_end'] == ['2017-06-13T09:30:00']
224
    assert qs['session_var_booking_start'] == ['2017-06-13T08:00:00+00:00']
225
    assert qs['session_var_booking_end'] == ['2017-06-13T09:30:00+00:00']
227 226

  
228 227

  
229 228
@mock.patch('combo.apps.calendar.utils.now', mocked_now)
230 229
@mock.patch('combo.apps.calendar.utils.requests.get', side_effect=mocked_requests_get)
231 230
def test_calendar(mocked_get, cell):
232
    cal = get_calendar('default:whatever', cell.slot_duration, 7)
231
    cal = get_calendar('default:whatever', cell.slot_duration, 7, cell.minimal_booking_duration)
233 232
    assert len(cal.days) == 3
234 233
    for day in cal.get_days():
235 234
        assert day in [
......
244 243
    assert cal.get_minimum_slot() == min_slot.time()
245 244
    assert cal.get_maximum_slot() == max_slot.time()
246 245
    assert cal.get_day(max_slot.date()).slots[-1].available is False
246

  
247

  
248
@mock.patch('combo.apps.calendar.utils.requests.get', side_effect=mocked_requests_get)
249
def test_cell_rendering_cal_info(mocked_get, client, cell):
250
    page = client.get('/booking/')
251
    assert '(Next available slot: June 13' in page.content
252
    # test when no slot is available
253
    with mock.patch('combo.utils.requests.get') as request_get:
254
        def side_effect(*args, **kwargs):
255
            if 'chrono' in kwargs['remote_service']['url']:
256
                return MockedRequestResponse(content=json.dumps({"data": []}))
257
            return MockedRequestResponse(content=json.dumps(WCS_FORMDEFS))
258

  
259
        request_get.side_effect = side_effect
260
        page = client.get('/booking/')
261
        assert '(No slot available)' in page.content
247
-