def cancel_booking(db: directives.PeeweeSession, secret: hug.types.text, start_date_time: hug.types.text, for_real: hug.types.smart_boolean = False): with db.atomic(): sdt = datetime.fromisoformat(start_date_time).replace(tzinfo=None) timeslot = TimeSlot.get(TimeSlot.start_date_time == sdt) booking = Booking.select(Booking).join( Appointment).where((Booking.secret == secret) & (Appointment.time_slot == timeslot)).get() if not for_real: print( f"This would delete the booking with id '{booking.id}' and secret '{booking.secret}'. Run with " f"--for_real if you are sure.") sys.exit(1) else: print( f"Deleting the booking with id '{booking.id}' and secret '{booking.secret}'." ) booking.appointment.booked = False booking.appointment.save() q = Booking.delete().where(Booking.id == booking.id) q.execute() print("Done.")
def test_create_appointments(testing_db): assert len(Booking.select()) == 0 assert len(TimeSlot.select()) == 0 assert len(Appointment.select()) == 0 NUM_SLOTS = 5 NUM_APPOINTMENTS = 3 create_kwargs = { 'day': 20, 'month': 4, 'year': 2020, 'start_hour': 16, 'start_min': 20, 'num_slots': NUM_SLOTS, 'num_appointment_per_slot': NUM_APPOINTMENTS, 'slot_duration_min': 10 } hug.test.cli('create_appointments', module='main', **create_kwargs) assert len(Booking.select()) == 0 assert len(TimeSlot.select()) == NUM_SLOTS assert len(Appointment.select()) == NUM_APPOINTMENTS * NUM_SLOTS sdt = datetime(2020, 4, 20, 16, 20, tzinfo=None) for i in range(NUM_SLOTS): ts = TimeSlot.get(TimeSlot.start_date_time == sdt + timedelta(minutes=10 * i)) assert Appointment.select().where( Appointment.time_slot == ts).count() == NUM_APPOINTMENTS
def book_appointment(db: PeeweeSession, body: hug.types.json, user: hug.directives.user): with db.atomic(): try: assert user.coupons > 0 if all(key in body for key in ('claim_token', 'start_date_time', 'first_name', 'name', 'phone', 'office')): claim_token = body['claim_token'] start_date_time = body['start_date_time'] start_date_time_object = datetime.fromisoformat(start_date_time) now = datetime.now(tz=config.Settings.tz).replace(tzinfo=None) if start_date_time_object < now: raise ValueError("Can't claim an appointment in the past") time_slot = TimeSlot.get(TimeSlot.start_date_time == start_date_time_object) appointment = Appointment.get( (Appointment.time_slot == time_slot) & (Appointment.booked == False) & (Appointment.claim_token == claim_token) ) appointment.booked = True appointment.claim_token = None appointment.claimed_at = None appointment.save() success = False with db.atomic(): while not success: secret = get_secret_token(6) try: SlotCode.create(date=time_slot.start_date_time.date(), secret=secret) success = True except IntegrityError as e: # in the offchance that we had a collision with secret codes, retry pass booking = Booking.create(appointment=appointment, first_name=body['first_name'], surname=body['name'], phone=body['phone'], office=body['office'], secret=secret, booked_by=user.user_name) booking.save() user.coupons -= 1 user.save() return { "secret": booking.secret, "time_slot": time_slot.start_date_time, "slot_length_min": time_slot.length_min } else: raise ValueError("Missing parameter") except DoesNotExist as e: raise hug.HTTPGone except ValueError as e: raise hug.HTTPBadRequest except AssertionError as e: raise hug.HTTPBadRequest
def claim_appointment(db: PeeweeSession, start_date_time: hug.types.text, user: hug.directives.user): """ UPDATE appointment app SET claim_token = 'claimed' WHERE app.id IN ( SELECT a.id FROM appointment a JOIN timeslot t on a.time_slot_id = t.id WHERE t.start_date_time = '2020-03-25 08:30:00.000000' AND a.claim_token isnull AND NOT a.booked LIMIT 1 ) RETURNING * """ with db.atomic(): try: if user.role != UserRoles.ANON: assert user.coupons > 0 start_date_time_object = datetime.fromisoformat(start_date_time) now = datetime.now(tz=config.Settings.tz).replace(tzinfo=None) if start_date_time_object < now: raise ValueError("Can't claim an appointment in the past") time_slot = TimeSlot.get( TimeSlot.start_date_time == start_date_time_object) appointment = Appointment.select() \ .where( (Appointment.time_slot == time_slot) & (Appointment.booked == False) & (Appointment.claim_token.is_null() | (Appointment.claimed_at + timedelta( minutes=config.Settings.claim_timeout_min) < now)) ) \ .order_by(Appointment.claim_token.desc()) \ .get() appointment.claim_token = get_random_string(32) appointment.claimed_at = now appointment.save() return appointment.claim_token except DoesNotExist as e: raise hug.HTTPGone except ValueError as e: raise hug.HTTPBadRequest except AssertionError as e: raise hug.HTTPBadRequest
def test_delete_timeslots(testing_db): # this test assumes that create_apointments and cancel_booking both work. They are under test also. # first, lets create some timeslots NUM_SLOTS = 5 NUM_APPOINTMENTS = 3 create_kwargs = { 'day': 20, 'month': 4, 'year': 2020, 'start_hour': 16, 'start_min': 20, 'num_slots': NUM_SLOTS, 'num_appointment_per_slot': NUM_APPOINTMENTS, 'slot_duration_min': 10 } hug.test.cli('create_appointments', module='main', **create_kwargs) assert len(Booking.select()) == 0 assert len(TimeSlot.select()) == NUM_SLOTS assert len(Appointment.select()) == NUM_APPOINTMENTS * NUM_SLOTS # now, lets create two bookings, one of them in a to-be-deleted timeslot booking_data = { 'surname': "Mustermann", 'first_name': "Marianne", 'phone': "0123456789", 'office': "MusterOffice", 'booked_by': USER, 'booked_at': datetime.now() } sdt1 = datetime(2020, 4, 20, 16, 20, tzinfo=None) sdt2 = datetime(2020, 4, 20, 16, 40, tzinfo=None) a1 = Appointment.get(Appointment.time_slot == TimeSlot.get(TimeSlot.start_date_time == sdt1)) a2 = Appointment.get(Appointment.time_slot == TimeSlot.get(TimeSlot.start_date_time == sdt2)) a1.booked = True a1.save() a2.booked = True a2.save() Booking.create(**booking_data, secret="secret1", appointment=a1) Booking.create(**booking_data, secret="secret2", appointment=a2) # with a booking in a timeslot, we should not delete delete_kwargs = { 'year': 2020, 'month': 4, 'day': 20, 'start_hour': 16, 'start_min': 30, 'num_slots': 2, 'for_real': True } hug.test.cli('delete_timeslots', module='main', **delete_kwargs) assert len(Booking.select()) == 2 assert len(TimeSlot.select()) == NUM_SLOTS assert len(Appointment.select()) == NUM_APPOINTMENTS * NUM_SLOTS # so let's cancel the booking that conflicts hug.test.cli('cancel_booking', module='main', secret='secret2', start_date_time='2020-04-20T16:40', for_real=True) assert len(Booking.select()) == 1 assert len(TimeSlot.select()) == NUM_SLOTS assert len(Appointment.select()) == NUM_APPOINTMENTS * NUM_SLOTS # and now let's retry the deletion hug.test.cli('delete_timeslots', module='main', **delete_kwargs) assert len(Booking.select()) == 1 assert len(TimeSlot.select()) == 3 assert len(Appointment.select()) == 9 for i in [0, 3, 4]: ts = TimeSlot.get(TimeSlot.start_date_time == sdt1 + timedelta(minutes=10 * i)) assert Appointment.select().where(Appointment.time_slot == ts).count() == NUM_APPOINTMENTS