Projet

Général

Profil

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

Josué Kouka, 30 octobre 2017 15:49

Télécharger (7,06 ko)

Voir les différences:

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

 combo/apps/calendar/models.py                      |  2 +-
 .../templates/calendar/booking_calendar_cell.html  |  7 +++-
 combo/apps/calendar/utils.py                       | 38 ++++++++++++++++++----
 tests/test_calendar.py                             | 22 +++++++++++--
 4 files changed, 58 insertions(+), 11 deletions(-)
combo/apps/calendar/models.py
54 54

  
55 55
    def render(self, context):
56 56
        calendar = get_calendar(self.agenda_reference, self.slot_duration,
57
                                self.displayed_days)
57
                                self.displayed_days, self.minimal_booking_duration)
58 58
        context['calendar'] = calendar
59 59
        return super(BookingCalendar, self).render(context)
combo/apps/calendar/templates/calendar/booking_calendar_cell.html
2 2

  
3 3
<div id="cal-{{cell.id}}">
4 4
    {% if cell.title %}
5
    <h2>{{cell.title}}</h2>
5
    <h2>
6
        <span>{{cell.title}}</span>
7
        {% if calendar %}
8
        <span>{{ calendar.get_info }}</span>
9
        {% endif %}
10
    </h2>
6 11
    {% endif %}
7 12
    <div>
8 13
        {% if calendar.days %}
combo/apps/calendar/utils.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import urllib
18 17
import datetime
18
import urllib
19 19

  
20 20
from django.conf import settings
21
from django.utils.dateformat import DateFormat
21 22
from django.utils.dateparse import parse_datetime
22
from django.utils.timezone import now
23
from django.utils.formats import get_format
24
from django.utils.timezone import localtime, make_aware, now
25
from django.utils.translation import ugettext_lazy as _
23 26

  
24 27
from combo.utils import requests
25 28

  
......
76 79
    return result.get('data', [])
77 80

  
78 81

  
79
def get_calendar(agenda_reference, offset, size):
82
def get_calendar(agenda_reference, offset, size, min_duration):
80 83
    if not agenda_reference:
81 84
        return []
82 85
    events = get_chrono_events(agenda_reference)
83
    weekcal = Calendar(offset, size)
86
    weekcal = Calendar(offset, size, min_duration)
84 87
    for event in events:
85 88
        event_datetime = parse_datetime(event['datetime'])
86 89
        event_date = event_datetime.date()
......
110 113
class DaySlot(object):
111 114

  
112 115
    def __init__(self, date_time, available, exist=True):
113
        self.date_time = date_time
116
        self.date_time = localtime(make_aware(date_time))
114 117
        self.available = available
115 118
        self.exist = exist
116 119

  
117 120
    def __repr__(self):
118 121
        return '<DaySlot date_time=%s - available=%s>' % (self.date_time.isoformat(), self.available)
119 122

  
123
    def __str__(self):
124
        date = DateFormat(self.date_time).format(get_format('DATE_FORMAT'))
125
        return _('%s at %s') % (date, self.date_time.strftime('%H:%M'))
126

  
120 127
    @property
121 128
    def label(self):
122 129
        return '%s' % self.date_time.isoformat()
......
148 155
    def get_maximum_slot(self):
149 156
        return max(self.slots, key=lambda x: x.date_time.time())
150 157

  
158
    def get_first_available_slot(self, offset, min_duration):
159
        step = min_duration.seconds / offset.seconds
160
        for idx in range(len(self.slots)):
161
            tmp = self.slots[idx:idx + step]
162
            if (tmp[-1].date_time - tmp[0].date_time) != ((step - 1) * offset):
163
                continue
164
            if not all(map(lambda x: x.available, tmp)):
165
                continue
166
            return tmp[0]
167
        return None
168

  
151 169

  
152 170
class Calendar(object):
153 171

  
154
    def __init__(self, offset, size):
172
    def __init__(self, offset, size, min_duration):
155 173
        self.offset = offset
156 174
        self.days = []
157 175
        self.size = size
176
        self.min_duration = min_duration
158 177

  
159 178
    def __repr__(self):
160 179
        if self.days:
161 180
            return '<Calendar: %s -> %s >' % (self.days[0], self.days[-1])
162 181
        return '<Calendar>'
163 182

  
183
    def get_info(self):
184
        for day in self.days:
185
            first_available_slot = day.get_first_available_slot(self.offset, self.min_duration)
186
            if first_available_slot:
187
                return _('(Next available slot: %s)') % first_available_slot
188
        return _('(No slot available)')
189

  
164 190
    def get_slots(self):
165 191
        start = self.get_minimum_slot()
166 192
        end = self.get_maximum_slot()
tests/test_calendar.py
215 215
    assert parsed.path == '/test/'
216 216
    qs = urlparse.parse_qs(parsed.query)
217 217
    assert qs['session_var_booking_agenda_slug'] == ['test']
218
    assert qs['session_var_booking_start'] == ['2017-06-13T08:00:00']
219
    assert qs['session_var_booking_end'] == ['2017-06-13T09:30:00']
218
    assert qs['session_var_booking_start'] == ['2017-06-13T08:00:00+00:00']
219
    assert qs['session_var_booking_end'] == ['2017-06-13T09:30:00+00:00']
220 220

  
221 221

  
222 222
@mock.patch('combo.apps.calendar.utils.requests.get', side_effect=mocked_requests_get)
223 223
def test_calendar(mocked_get, cell):
224
    cal = get_calendar('default:whatever', cell.slot_duration)
224
    cal = get_calendar('default:whatever', cell.slot_duration, cell.minimal_booking_duration)
225 225
    assert len(cal.days) == 3
226 226
    for day in cal.get_days():
227 227
        assert day in [
......
236 236
    assert cal.get_minimum_slot() == min_slot.time()
237 237
    assert cal.get_maximum_slot() == max_slot.time()
238 238
    assert cal.get_day(max_slot.date()).slots[-1].available is False
239

  
240

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

  
252
        request_get.side_effect = side_effect
253
        page = client.get('/booking/')
254
        assert '(No slot available)' in page.content
239
-