Projet

Général

Profil

0001-create-getfreegaps-enpoint-fot-availibilty-queries-2.patch

Emmanuel Cazenave, 10 décembre 2018 19:14

Télécharger (29,1 ko)

Voir les différences:

Subject: [PATCH] create 'getfreegaps' enpoint fot availibilty queries (#28827)

It has the same capabilities than 'getdays' by using display=date
and 'getplaces' by using display=place.

Search intervals can be specificed with days delta from now.
display=full will return a data easy to use in a template to display
global availability.

The place referential gain adresses.
 passerelle/contrib/planitech/models.py | 188 ++++++------
 tests/test_planitech.py                | 377 +++++++++++++------------
 2 files changed, 297 insertions(+), 268 deletions(-)
passerelle/contrib/planitech/models.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
from datetime import datetime, time, timedelta
17
from datetime import date, datetime, time, timedelta
18 18
import hashlib
19 19
import json
20 20
import re
......
136 136
    return re.match(r'<(.*?)>', salt).groups()[0]
137 137

  
138 138

  
139
def get_utc_datetime(date_str, time_str):
140
    date_obj = parse_date(date_str)
139
def get_utc_datetime(date_p, time_str):
140
    if not isinstance(date_p, date):
141
        date_p = parse_date(date_p)
141 142
    time_obj = parse_time(time_str)
142
    datetime_obj = datetime.combine(date_obj, time_obj)
143
    datetime_obj = datetime.combine(date_p, time_obj)
143 144
    return TZ.localize(datetime_obj).astimezone(utc)
144 145

  
145 146

  
......
217 218
            )
218 219
            for place in data['requestedPlaces']:
219 220
                ref[place['identifier']]['capacity'] = place.get('capacity')
221
                ref[place['identifier']]['street_number'] = place.get('streetNumber')
222
                ref[place['identifier']]['address'] = place.get('address1')
223
                ref[place['identifier']]['city'] = place.get('city')
224
                ref[place['identifier']]['zipcode'] = place.get('zipCode')
225

  
220 226
            cache.set(cache_key, ref)
221 227
        return ref
222 228

  
......
288 294
    def hourly(self):
289 295
        self._get_places_referential(refresh_cache=True)
290 296

  
297
    def _date_display(self, raw_data):
298
        available_dates = set()
299
        for place in raw_data.get('availablePlaces', []):
300
            for freegap in place.get('freeGaps', []):
301
                available_dates.add(freegap[0].date())
302
        res = []
303
        available_dates = list(available_dates)
304
        available_dates.sort()
305
        for date_obj in available_dates:
306
            date_text = dateformat.format(date_obj, 'l d F Y')
307
            short_text = dateformat.format(date_obj, 'd/m/Y')
308
            res.append({
309
                "id": date_obj.isoformat(), "text": date_text,
310
                "short_text": short_text})
311
        return res
312

  
313
    def _place_display(self, raw_data):
314
        available_places = []
315
        for place in raw_data.get('availablePlaces', []):
316
            available_places.append(place['placeIdentifier'])
317
        places_ref = self._get_places_referential()
318
        res = []
319
        for place in available_places:
320
            res.append({"id": place, "text": places_ref[place]['label']})
321
        return res
322

  
323
    def _full_display(self, raw_data):
324
        places_ref = self._get_places_referential()
325
        res = {
326
            'date': self._date_display(raw_data),
327
            'place': self._place_display(raw_data)
328
        }
329
        all_dates = [d['id'] for d in res['date']]
330
        full = []
331
        for place in raw_data.get('availablePlaces', []):
332
            place_id = place['placeIdentifier']
333
            place_data = {
334
                'id': int(place_id),
335
                'text': places_ref[place_id]['label'],
336
                'dates': []
337
            }
338
            place_dates = []
339
            for freegap in place.get('freeGaps', []):
340
                place_dates.append(freegap[0].date().isoformat())
341
            for d in all_dates:
342
                available = d in place_dates
343
                place_data['dates'].append({'id': d, 'available': available})
344
            assert len(place_data['dates']) == len(all_dates)
345
            full.append(place_data)
346

  
347
        res['full'] = full
348
        return res
349

  
291 350
    @endpoint(
292 351
        description_get=_('Get days available for reservation'),
293 352
        methods=['get'], perm='can_access',
......
304 363
                'description': _('Start date'),
305 364
                'example_value': '2018-10-10',
306 365
            },
366
            'start_days': {
367
                'description': _('Start days'),
368
                'example_value': '2',
369
            },
307 370
            'end_date': {
308 371
                'description': _('End date'),
309 372
                'example_value': '2018-12-10',
......
325 388
                'description': _('Place identifier'),
326 389
                'example_value': '2',
327 390
                'type': 'int',
328
            }
391
            },
392
            'display': {
393
                'description': _('Display'),
394
                'example_value': 'date',
395
            },
329 396
        })
330
    def getdays(
331
            self, request, start_date, start_time, end_time, min_capacity=0,
332
            end_date=None, max_capacity=100000, weekdays=False, place_id=None):
333
        if place_id is not None:
334
            places_id = [float(place_id)]
397
    def getfreegaps(
398
            self, request, display, start_time, end_time, min_capacity=0, start_date=None,
399
            start_days=None, end_date=None, end_days=None, max_capacity=100000, weekdays=False,
400
            place_id=None):
401

  
402
        # Additional parameters check
403
        valid_diplays = ['date', 'place', 'full']
404
        if display not in valid_diplays:
405
            raise APIError("Valid display are: %s" % ", ".join(valid_diplays))
406
        if start_date is None and start_days is None:
407
            raise APIError("start_date or start_days is required")
408

  
409
        # Starting date computation
410
        if start_date is not None:
411
            utc_start_datetime = get_utc_datetime(start_date, start_time)
335 412
        else:
336
            places_id = self._get_places_by_capacity(int(min_capacity), int(max_capacity))
413
            date_obj = (datetime.now() + timedelta(int(start_days))).date()
414
            utc_start_datetime = get_utc_datetime(date_obj, start_time)
337 415

  
338
        utc_start_datetime = get_utc_datetime(start_date, start_time)
339
        if end_date is None:
340
            utc_end_datetime = utc_start_datetime + timedelta(days=365)
341
        else:
416
        # Ending date computation
417
        if end_date is None and end_days is None:
418
            utc_end_datetime = (utc_start_datetime + timedelta(days=1)).replace(hour=0, minute=0)
419
        elif end_date is not None:
342 420
            utc_end_datetime = get_utc_datetime(end_date, '00:00')
421
        elif end_days is not None:
422
            date_obj = (datetime.now() + timedelta(int(end_days))).date()
423
            utc_end_datetime = get_utc_datetime(date_obj, '00:00')
343 424

  
344 425
        duration = get_duration(start_time, end_time)
345 426

  
427
        # Places restriction
428
        if place_id is not None:
429
            places_id = [float(place_id)]
430
        else:
431
            places_id = self._get_places_by_capacity(int(min_capacity), int(max_capacity))
432

  
346 433
        params = {
347 434
            "placeIdentifiers": places_id,
348 435
            "startingDate": utc_start_datetime,
......
354 441
            params['reservationDays'] = [mste.Uint32(0), mste.Uint32(6)]
355 442

  
356 443
        raw_data = self._call_planitech(self.requests.post, 'getFreeGaps', params)
357
        available_dates = set()
358
        for place in raw_data.get('availablePlaces', []):
359
            for freegap in place.get('freeGaps', []):
360
                available_dates.add(freegap[0].date())
361 444

  
362
        res = []
363
        available_dates = list(available_dates)
364
        available_dates.sort()
365
        for date_obj in available_dates:
366
            date_text = dateformat.format(date_obj, 'l d F Y')
367
            res.append({"id": date_obj.isoformat(), "text": date_text})
368

  
369
        return {'data': res}
370

  
371
    @endpoint(
372
        description_get=_('Get places available for reservation'),
373
        methods=['get'], perm='can_access',
374
        parameters={
375
            'min_capacity': {
376
                'description': _('Minimum capacity'),
377
                'example_value': '1',
378
            },
379
            'max_capacity': {
380
                'description': _('Maximum capacity'),
381
                'example_value': '10',
382
            },
383
            'start_date': {
384
                'description': _('Start date'),
385
                'example_value': '2018-10-10',
386
            },
387
            'start_time': {
388
                'description': _('Start time'),
389
                'example_value': '10:00',
390
            },
391
            'end_time': {
392
                'description': _('End time'),
393
                'example_value': '18:00',
394
            },
395
        })
396
    def getplaces(
397
            self, request, start_date, start_time, end_time, min_capacity=0, max_capacity=100000):
398

  
399
        places_id = self._get_places_by_capacity(min_capacity, max_capacity)
400

  
401
        utc_start_datetime = get_utc_datetime(start_date, start_time)
402
        utc_end_datetime = utc_start_datetime + timedelta(days=1)
403
        duration = get_duration(start_time, end_time)
404

  
405
        params = {
406
            "placeIdentifiers": places_id,
407
            "startingDate": utc_start_datetime,
408
            "endingDate": utc_end_datetime,
409
            "requestedStartingTime": float(0),
410
            "requestedEndingTime": duration
411
        }
412

  
413
        raw_data = self._call_planitech(self.requests.post, 'getFreeGaps', params)
414
        available_places = []
415
        for place in raw_data.get('availablePlaces', []):
416
            available_places.append(place['placeIdentifier'])
417

  
418
        places_ref = self._get_places_referential()
445
        if display == 'date':
446
            return {'data': self._date_display(raw_data)}
419 447

  
420
        res = []
421
        for place in available_places:
422
            res.append({"id": place, "text": places_ref[place]['label']})
448
        if display == 'place':
449
            return {'data': self._place_display(raw_data)}
423 450

  
424
        return {'data': res}
451
        if display == 'full':
452
            return {'data': self._full_display(raw_data)}
425 453

  
426 454
    @endpoint(description_get=_('Get places referential'), methods=['get'], perm='can_access')
427 455
    def getplacesreferential(self, request):
tests/test_planitech.py
215 215
        },
216 216
        {
217 217
            'requestedPlaces': [
218
                {'identifier': 1.0, 'capacity': 10.0},
218
                {
219
                    'identifier': 1.0, 'capacity': 10.0,
220
                    'streetNumber': 1, 'address1': 'rue planitech',
221
                    'city': 'thecity', 'zipCode': '00000'
222
                },
219 223
                {'identifier': 2.0, 'capacity': 20.0}
220 224
            ]
221 225
        }
......
224 228
    response = app.get('/planitech/slug-planitech/getplacesreferential')
225 229
    expected_res = {
226 230
        u'2.0': {
227
            u'capacity': 20.0, u'label': u'salle 2', u'identifier': 2.0
231
            u'capacity': 20.0, u'label': u'salle 2', u'identifier': 2.0,
232
            'street_number': None, 'address': None,
233
            'city': None, 'zipcode': None
228 234
        },
229 235
        u'1.0': {
230
            u'capacity': 10.0, u'label': u'salle 1', u'identifier': 1.0
236
            u'capacity': 10.0, u'label': u'salle 1', u'identifier': 1.0,
237
            'street_number': 1, 'address': 'rue planitech',
238
            'city': 'thecity', 'zipcode': '00000'
231 239
        }
232 240
    }
233 241
    assert response.json['data'] == expected_res
......
241 249
    cache.delete(cache_key)
242 250

  
243 251

  
244
def test_get_days(app, connector, monkeypatch, settings):
245
    settings.LANGUAGE_CODE = 'fr-fr'
252
def test_login(connector):
253

  
254
    @urlmatch(netloc=r'(.*\.)?planitech\.com$')
255
    def planitech_mock(url, request):
256
        raise requests.exceptions.RequestException("Bad news")
257

  
258
    with HTTMock(planitech_mock):
259
        with pytest.raises(APIError) as excinfo:
260
            connector._login()
261
        assert str(excinfo.value) == 'Authentication to Planitech failed: Bad news'
262

  
263

  
264
def test_update_reservation(app, connector, monkeypatch):
265
    mock_call_planitech = mock_planitech(
266
        monkeypatch, return_value={'modificationStatus': 'OK'}
267
    )
268
    response = app.post_json(
269
        '/planitech/slug-planitech/updatereservation',
270
        params={'status': 'confirmed', 'reservation_id': 1}
271
    )
272
    json_resp = response.json
273
    assert json_resp['err'] == 0
274
    assert json_resp['data']['raw_data'] == {'modificationStatus': 'OK'}
275
    call_params = mock_call_planitech.call_args[0][2]
276
    assert call_params['reservationIdentifier'] == 1
277
    assert call_params['situation'] == 3
278

  
279
    # Update failed
280
    mock_planitech(
281
        monkeypatch, return_value={'modificationStatus': 'NOTOK'}
282
    )
283
    response = app.post_json(
284
        '/planitech/slug-planitech/updatereservation',
285
        params={'status': 'confirmed', 'reservation_id': 1}
286
    )
287
    json_resp = response.json
288
    assert json_resp['err'] == 1
289
    assert json_resp['err_desc'] == 'Update reservation failed: NOTOK'
290

  
291
    # Connector bad param
292
    response = app.post_json(
293
        '/planitech/slug-planitech/updatereservation', params={'status': 'confirmed'}, status=400)
294
    json_resp = response.json
295
    assert json_resp['err'] == 1
296
    assert json_resp['err_desc'] == "'reservation_id' is a required property"
297

  
298

  
299
def freegaps_data():
246 300
    referential = {
247 301
        2.0: {
248 302
            u'capacity': 20.0, u'label': u'salle 2', u'identifier': 2.0
......
251 305
            u'capacity': 10.0, u'label': u'salle 1', u'identifier': 1.0
252 306
        }
253 307
    }
254

  
255
    def get_free_gaps():
256
        return [
257
            [datetime(year=2018, month=11, day=11, hour=10, minute=0),
258
             datetime(year=2018, month=11, day=11, hour=11, minute=0)],
259
            [datetime(year=2018, month=11, day=12, hour=10, minute=0),
260
             datetime(year=2018, month=11, day=12, hour=11, minute=0)]
261
        ]
262

  
263
    getfreegaps = {
308
    place1_gaps = [
309
        [datetime(year=2018, month=11, day=11, hour=10, minute=0),
310
         datetime(year=2018, month=11, day=11, hour=11, minute=0)],
311
        [datetime(year=2018, month=11, day=12, hour=10, minute=0),
312
         datetime(year=2018, month=11, day=12, hour=11, minute=0)]
313
    ]
314
    place2_gaps = [
315
        [datetime(year=2018, month=11, day=11, hour=10, minute=0),
316
         datetime(year=2018, month=11, day=11, hour=11, minute=0)],
317
        [datetime(year=2018, month=11, day=12, hour=10, minute=0),
318
         datetime(year=2018, month=11, day=12, hour=11, minute=0)],
319
        [datetime(year=2018, month=11, day=13, hour=10, minute=0),
320
         datetime(year=2018, month=11, day=13, hour=11, minute=0)]
321
    ]
322
    planitech_r = {
264 323
        'availablePlaces': [
265 324
            {
266
                'freeGaps': get_free_gaps()
325
                'placeIdentifier': 1.0,
326
                'freeGaps': place1_gaps
267 327
            },
268 328
            {
269
                'freeGaps': get_free_gaps()
329
                'placeIdentifier': 2.0,
330
                'freeGaps': place2_gaps
270 331
            }
271 332
        ]
272 333
    }
334
    return referential, planitech_r
335

  
336

  
337
def test_get_freegaps(app, connector, monkeypatch, settings):
338
    settings.LANGUAGE_CODE = 'fr-fr'
339
    referential, planitech_r = freegaps_data()
273 340
    mock_call_planitech = mock_planitech(
274
        monkeypatch, return_value=getfreegaps, referential=referential)
341
        monkeypatch, return_value=planitech_r, referential=referential)
342

  
343
    # date display
275 344
    response = app.get(
276
        '/planitech/slug-planitech/getdays?start_time=10:00&&end_time=11:00'
277
        '&start_date=2018-11-11'
345
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
346
        '&start_date=2018-11-11&display=date'
278 347
    )
279 348
    assert response.json['data'] == [
280
        {u'id': u'2018-11-11', u'text': u'dimanche 11 novembre 2018'},
281
        {u'id': u'2018-11-12', u'text': u'lundi 12 novembre 2018'}
349
        {
350
            u'id': u'2018-11-11', u'text': u'dimanche 11 novembre 2018',
351
            u'short_text': '11/11/2018'
352
        },
353
        {
354
            u'id': u'2018-11-12', u'text': u'lundi 12 novembre 2018',
355
            u'short_text': '12/11/2018'
356
        },
357
        {
358
            u'id': u'2018-11-13', u'text': u'mardi 13 novembre 2018',
359
            u'short_text': '13/11/2018'
360
        }
282 361
    ]
283
    call_params = mock_call_planitech.call_args[0][2]
284
    assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0, tzinfo=utc)
285
    assert call_params['endingDate'] == datetime(2019, 11, 11, 10, 0, tzinfo=utc)
286
    assert call_params['placeIdentifiers'] == [1.0, 2.0]
287
    assert call_params['requestedStartingTime'] == 0.0
288
    assert call_params['requestedEndingTime'] == 60.0
289
    assert call_params['reservationDays'] == [0, 6]
290 362

  
291
    # capcacity
292
    mock_call_planitech.reset_mock()
293
    app.get(
294
        '/planitech/slug-planitech/getdays?start_time=10:00&&end_time=11:00'
295
        '&start_date=2018-11-11&min_capacity=15'
296
    )
297
    call_params = mock_call_planitech.call_args[0][2]
298
    assert call_params['placeIdentifiers'] == [2.0]
299

  
300
    freegaps = get_free_gaps()[:1]
301
    getfreegaps = {
302
        'availablePlaces': [
303
            {
304
                'freeGaps': freegaps
305
            },
306
            {
307
                'freeGaps': freegaps
308
            }
309
        ]
310
    }
311
    mock_call_planitech = mock_planitech(
312
        monkeypatch, return_value=getfreegaps, referential=referential)
363
    # place display
313 364
    response = app.get(
314
        '/planitech/slug-planitech/getdays?max_capacity=3&start_time=10:00&&end_time=11:00'
315
        '&start_date=2018-11-11'
365
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
366
        '&start_date=2018-11-11&display=place'
316 367
    )
317 368
    assert response.json['data'] == [
318
        {u'id': u'2018-11-11', u'text': u'dimanche 11 novembre 2018'},
369
        {u'id': 1.0, u'text': u'salle 1'},
370
        {u'id': 2.0, u'text': u'salle 2'}
319 371
    ]
320 372

  
321
    getfreegaps = {
322
        'availablePlaces': [
323
            {
324
                'freeGaps': []
325
            },
326
            {
327
                'freeGaps': []
328
            }
329
        ]
330
    }
331
    mock_call_planitech = mock_planitech(
332
        monkeypatch, return_value=getfreegaps, referential=referential)
373
    # full display
333 374
    response = app.get(
334
        '/planitech/slug-planitech/getdays?max_capacity=3&start_time=10:00&&end_time=11:00'
335
        '&start_date=2018-11-11'
375
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
376
        '&start_date=2018-11-11&display=full'
336 377
    )
337
    assert response.json['data'] == []
378
    res = response.json['data']
379
    assert 'place' in res
380
    assert 'date' in res
381
    full = res['full']
382
    place_1 = full[0]
383
    assert place_1['id'] == 1.0
384
    assert place_1['text'] == u'salle 1'
385
    assert place_1['dates'] == [
386
        {u'available': True, u'id': u'2018-11-11'},
387
        {u'available': True, u'id': u'2018-11-12'},
388
        {u'available': False, u'id': u'2018-11-13'}
389
    ]
390

  
391
    place_2 = full[1]
392
    assert place_2['id'] == 2.0
393
    assert place_2['text'] == u'salle 2'
394
    assert place_2['dates'] == [
395
        {u'available': True, u'id': u'2018-11-11'},
396
        {u'available': True, u'id': u'2018-11-12'},
397
        {u'available': True, u'id': u'2018-11-13'}
398
    ]
338 399

  
339
    # end date
400
    # general params interpretation
340 401
    mock_call_planitech.reset_mock()
341 402
    response = app.get(
342
        '/planitech/slug-planitech/getdays?start_time=10:00&&end_time=11:00'
343
        '&start_date=2018-11-11&end_date=2018-11-12'
403
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
404
        '&start_date=2018-11-11&end_date=2018-11-12&display=date'
344 405
    )
345 406
    call_params = mock_call_planitech.call_args[0][2]
346 407
    assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0, tzinfo=utc)
347 408
    assert call_params['endingDate'] == datetime(2018, 11, 12, 00, 0, tzinfo=utc)
409
    assert call_params['placeIdentifiers'] == [1.0, 2.0]
410
    assert call_params['requestedStartingTime'] == 0.0  # means startingDate
411
    assert call_params['requestedEndingTime'] == 60.0  # means startingDate + 60 minutes
412
    assert call_params['reservationDays'] == [0, 6]  # means every day of the week
348 413

  
349
    # start time
414
    # no end date means only the starting date is interesting
415
    # so ending date is the day after at midnight
416
    # which gives only results for starting date
350 417
    mock_call_planitech.reset_mock()
351 418
    response = app.get(
352
        '/planitech/slug-planitech/getdays?start_time=11:00&&end_time=12:00'
353
        '&start_date=2018-11-11'
419
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
420
        '&start_date=2018-11-11&display=place'
354 421
    )
355 422
    call_params = mock_call_planitech.call_args[0][2]
356
    assert call_params['startingDate'] == datetime(2018, 11, 11, 11, 0, tzinfo=utc)
423
    assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0, tzinfo=utc)
424
    assert call_params['endingDate'] == datetime(2018, 11, 12, 0, 0, tzinfo=utc)
357 425

  
358
    # duration
426
    # capacity used against referential to restrict placeIdentifiers
359 427
    mock_call_planitech.reset_mock()
360
    response = app.get(
361
        '/planitech/slug-planitech/getdays?start_time=11:00&&end_time=14:00'
362
        '&start_date=2018-11-11'
428
    app.get(
429
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
430
        '&start_date=2018-11-11&max_capacity=15&display=place'
363 431
    )
364 432
    call_params = mock_call_planitech.call_args[0][2]
365
    assert call_params['requestedEndingTime'] == 180.0
433
    assert call_params['placeIdentifiers'] == [1.0]
366 434

  
367
    # place_id filter
435
    # place_id parameter override capacity
368 436
    mock_call_planitech.reset_mock()
369 437
    response = app.get(
370
        '/planitech/slug-planitech/getdays?start_time=11:00&&end_time=14:00'
371
        '&start_date=2018-11-11&place_id=2'
438
        '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00'
439
        '&start_date=2018-11-11&max_capacity=15&place_id=2&display=place'
372 440
    )
373 441
    call_params = mock_call_planitech.call_args[0][2]
374 442
    assert call_params['placeIdentifiers'] == [2.0]
375 443

  
376
    # date bad format
444
    # weekdays means no week days exclusion
445
    mock_call_planitech.reset_mock()
446
    response = app.get(
447
        '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00'
448
        '&start_date=2018-11-11&max_capacity=15&place_id=2&display=place'
449
        '&weekdays=true'
450
    )
451
    call_params = mock_call_planitech.call_args[0][2]
452
    assert 'reservationDays' not in call_params
453

  
454
    # BAD REQUEST
455
    # bad date format
377 456
    response = app.get(
378
        '/planitech/slug-planitech/getdays?start_time=11:00&&end_time=14:00'
379
        '&start_date=notadate'
457
        '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00'
458
        '&start_date=notadate&display=place'
380 459
    )
381 460
    json_resp = response.json
382 461
    assert json_resp['err'] == 1
383 462
    assert json_resp['err_desc'] == "Invalid date format: notadate"
384 463

  
385
    # date time format
464
    # bad time format
386 465
    response = app.get(
387
        '/planitech/slug-planitech/getdays?start_time=notatime&&end_time=14:00'
388
        '&start_date=2018-11-11'
466
        '/planitech/slug-planitech/getfreegaps?start_time=notatime&&end_time=14:00'
467
        '&start_date=2018-11-11&display=place'
389 468
    )
390 469
    json_resp = response.json
391 470
    assert json_resp['err'] == 1
392 471
    assert json_resp['err_desc'] == "Invalid time format: notatime"
393 472

  
394

  
395
def test_get_places(app, connector, monkeypatch, settings):
396
    referential = {
397
        2.0: {
398
            u'capacity': 20.0, u'label': u'salle 2', u'identifier': 2.0
399
        },
400
        1.0: {
401
            u'capacity': 10.0, u'label': u'salle 1', u'identifier': 1.0
402
        }
403
    }
404

  
405
    def get_free_gaps():
406
        return [
407
            [datetime(year=2018, month=11, day=11, hour=10, minute=0),
408
             datetime(year=2018, month=11, day=11, hour=11, minute=0)],
409
            [datetime(year=2018, month=11, day=12, hour=10, minute=0),
410
             datetime(year=2018, month=11, day=12, hour=11, minute=0)]
411
        ]
412

  
413
    getfreegaps = {
414
        'availablePlaces': [
415
            {
416
                'placeIdentifier': 1.0,
417
                'freeGaps': get_free_gaps()
418
            },
419
            {
420
                'placeIdentifier': 2.0,
421
                'freeGaps': get_free_gaps()
422
            }
423
        ]
424
    }
425
    mock_call_planitech = mock_planitech(
426
        monkeypatch, return_value=getfreegaps, referential=referential)
473
    # start_date or start_days required
427 474
    response = app.get(
428
        '/planitech/slug-planitech/getplaces?start_time=10:00&&end_time=11:00'
429
        '&start_date=2018-11-11'
430
    )
431
    assert response.json['data'] == [
432
        {u'id': 1.0, u'text': u'salle 1'},
433
        {u'id': 2.0, u'text': u'salle 2'}
434
    ]
435
    call_params = mock_call_planitech.call_args[0][2]
436
    assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0, tzinfo=utc)
437
    assert call_params['endingDate'] == datetime(2018, 11, 12, 10, 0, tzinfo=utc)
438
    assert call_params['placeIdentifiers'] == [1.0, 2.0]
439
    assert call_params['requestedStartingTime'] == 0.0
440
    assert call_params['requestedEndingTime'] == 60.0
441

  
442
    # date bad format
443
    response = app.get(
444
        '/planitech/slug-planitech/getplaces?start_time=10:00&&end_time=11:00'
445
        '&start_date=notadate'
475
        '/planitech/slug-planitech/getfreegaps?start_time=notatime&&end_time=14:00'
476
        '&display=place'
446 477
    )
447 478
    json_resp = response.json
448 479
    assert json_resp['err'] == 1
449
    assert json_resp['err_desc'] == "Invalid date format: notadate"
480
    assert json_resp['err_desc'] == "start_date or start_days is required"
450 481

  
451
    # date time format
482
    # invalid display param
452 483
    response = app.get(
453
        '/planitech/slug-planitech/getplaces?start_time=notatime&&end_time=11:00'
454
        '&start_date=2018-11-11'
484
        '/planitech/slug-planitech/getfreegaps?start_time=notatime&&end_time=14:00'
485
        '&start_date=2018-11-11&display=unkown'
455 486
    )
456 487
    json_resp = response.json
457 488
    assert json_resp['err'] == 1
458
    assert json_resp['err_desc'] == "Invalid time format: notatime"
489
    assert json_resp['err_desc'] == "Valid display are: date, place, full"
459 490

  
460 491

  
461
def test_login(connector):
462

  
463
    @urlmatch(netloc=r'(.*\.)?planitech\.com$')
464
    def planitech_mock(url, request):
465
        raise requests.exceptions.RequestException("Bad news")
466

  
467
    with HTTMock(planitech_mock):
468
        with pytest.raises(APIError) as excinfo:
469
            connector._login()
470
        assert str(excinfo.value) == 'Authentication to Planitech failed: Bad news'
471

  
472

  
473
def test_update_reservation(app, connector, monkeypatch):
492
def test_get_freegaps_start_days(app, connector, monkeypatch, settings, freezer):
493
    freezer.move_to('2018-11-09 23:50:00')
494
    settings.LANGUAGE_CODE = 'fr-fr'
495
    referential, planitech_r = freegaps_data()
474 496
    mock_call_planitech = mock_planitech(
475
        monkeypatch, return_value={'modificationStatus': 'OK'}
476
    )
477
    response = app.post_json(
478
        '/planitech/slug-planitech/updatereservation',
479
        params={'status': 'confirmed', 'reservation_id': 1}
480
    )
481
    json_resp = response.json
482
    assert json_resp['err'] == 0
483
    assert json_resp['data']['raw_data'] == {'modificationStatus': 'OK'}
484
    call_params = mock_call_planitech.call_args[0][2]
485
    assert call_params['reservationIdentifier'] == 1
486
    assert call_params['situation'] == 3
497
        monkeypatch, return_value=planitech_r, referential=referential)
487 498

  
488
    # Update failed
489
    mock_planitech(
490
        monkeypatch, return_value={'modificationStatus': 'NOTOK'}
491
    )
492
    response = app.post_json(
493
        '/planitech/slug-planitech/updatereservation',
494
        params={'status': 'confirmed', 'reservation_id': 1}
499
    # starting_date and ending_date can be specified as days delta from now
500
    app.get(
501
        '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00'
502
        '&start_days=2&end_days=4&display=place'
495 503
    )
496
    json_resp = response.json
497
    assert json_resp['err'] == 1
498
    assert json_resp['err_desc'] == 'Update reservation failed: NOTOK'
499

  
500
    # Connector bad param
501
    response = app.post_json(
502
        '/planitech/slug-planitech/updatereservation', params={'status': 'confirmed'}, status=400)
503
    json_resp = response.json
504
    assert json_resp['err'] == 1
505
    assert json_resp['err_desc'] == "'reservation_id' is a required property"
504
    call_params = mock_call_planitech.call_args[0][2]
505
    assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0, tzinfo=utc)
506
    assert call_params['endingDate'] == datetime(2018, 11, 13, 0, 0, tzinfo=utc)
506
-