Skip to content

Commit

Permalink
Merge pull request #358 from FJNR-inc/develop
Browse files Browse the repository at this point in the history
New release
  • Loading branch information
RignonNoel authored Jan 23, 2020
2 parents 1912f9a + 40d5d07 commit 2e74e07
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 124 deletions.
Binary file added docs/assets/wait_queue_management_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/wait_queue_management_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
104 changes: 104 additions & 0 deletions docs/use_cases/wait_queue_management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Wait Queue Management

This document is here to illustrate the wait queue functionnality of this system.

## Terminology

| Term | Definition
|------------------------------ |--------------------------
| **WaitQueue** | A wait queue is waiting place of a user for the specific retreat. WaitQueues are ordered by creation date (first in, first out)
| **WaitQueuePlace** | A WaitQueuePlace is a retirement place available for waiting people. Each time a new WaitQueuePlace is created, the system will begin notify people in order to fill it and find a new customer.
| **WaitQueuePlaceReserved** | A WaitQueuePlaceReserved is kind of a ticket authorization that we give to waiting people to say that they have the right to fill a WaitQueuePlace. The system create WaitQueuePlaceReserverd each 24h for the next customer in line.

## Process

### Initialisation

At the beginning, there is nothing.

We can see on the left the WaitQueue (people in queue) and on the bottom the new place that we need to fill.

![Capture](../assets/wait_queue_management_1.png)

### A reservation is cancelled

When a reservation is cancelled, the system will automatically create a first WaitQueuePlace in order to notify waiting people and find a new customer.

Here we can see that the first user has automatically been notified, he now have a WaitQueuePlaceReserved

![Capture](../assets/wait_queue_management_2.png)

### Nothing new, time fly

If the first user in queue (WaitQueue 1) don't buy the retirement's place available for him, the system automatically notify the second user in queue in order to find a customer motivated with the product.

Here we can see that the second user has automatically been notified, he now have a WaitQueuePlaceReserved

NB: At this step, both users can reserve the WaitQueuePlace and fill the empty seat of the retirement. The second user need to be fast if he don't want to be notified for nothing.

![Capture](../assets/wait_queue_management_3.png)

### A second reservation is cancelled

Let's imagine that a second reservation is now cancelled:

- The first WaitQueuePlace is not filled for the moment
- A second WaitQueuePlace is created and we need to find a customer
- User 1 & 2 are already notified that they are allowed to buy a place

Since there is no reason to re-notify user 1 & 2, the system will automatically notify the third user.

NB: We can see that we create WaitQueuePlaceReserved for user 1 & 2 but without notification (ie: emails) to allow us to reserve the second place but without being spammed with out notification.

![Capture](../assets/wait_queue_management_4.png)

### User 2 reserve a place

Now, let's see what's going on if user 2 want to reserve a place to the retirement:

- The first WaitQueuePlace is now filled
- WaitingQueuePlaceReserved for user 1 on place 1 is now unavailable

Since user 1 has already been notify at the beginning and that he is always authorize to reserve a place, there is no need to notify him.

NB: At this step we now have only one place available for waiting user.

![Capture](../assets/wait_queue_management_5.png)


### Notification interval is done

After the notification interval (24H), what will be the next step for the automatic system ? :

- Only one WaitingQueuePlace is available (#2)
- User 1 & 3 are already notified
- User 2 is already participant on the retirement
- We will notify the next user on queue (user 4)

We are exactly in the same case as when we notified user 2 for the WaitingQueuePlace 1.

![Capture](../assets/wait_queue_management_6.png)

### User 3 reserve a place

Now, let's imagine we are on a good day and user 3 reserve a place (24H):

- The second WaitQueuePlace is now filled
- WaitingQueuePlaceReserved for all users of WaitingQueuePlace2 are now unavailable

Since there is no more place available, the waiting queue is just stopped until a new reservation will be cancelled

![Capture](../assets/wait_queue_management_7.png)

### An other reservation is cancelled

If a new retirement cancellation occur:

- A new WaitingQueuePlace is created (as always)
- A WaitingQueuePlaceReserved is created for user 1
- User 1 is notify since he have now a new chance to reserve a place

This step is kind of a new beginning since all the users lost their right to reserve a place before. We need to re-notify all of them, one by one.

![Capture](../assets/wait_queue_management_8.png)

7 changes: 6 additions & 1 deletion retirement/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class WaitQueuePlaceReservedInline(admin.StackedInline):
model = WaitQueuePlaceReserved
can_delete = True
show_change_link = True
autocomplete_fields = ('user', 'wait_queue_place')
verbose_name_plural = _('Wait Queue places reserved')


Expand All @@ -160,10 +161,14 @@ class WaitQueuePlaceReservedAdmin(admin.ModelAdmin):
'wait_queue_place',
'user',
'create',
'notified'
'notified',
'used',
)
list_filter = (
'wait_queue_place__retreat',
'wait_queue_place',
'notified',
'used',
'user'
)
autocomplete_fields = ('user', 'wait_queue_place')
Expand Down
28 changes: 28 additions & 0 deletions retirement/migrations/0024_auto_20200123_0515.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 2.2.8 on 2020-01-23 10:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('retirement', '0023_auto_20191222_0347'),
]

operations = [
migrations.AddField(
model_name='historicalwaitqueue',
name='used',
field=models.BooleanField(default=False, verbose_name='Used'),
),
migrations.AddField(
model_name='waitqueue',
name='used',
field=models.BooleanField(default=False, verbose_name='Used'),
),
migrations.AddField(
model_name='waitqueueplacereserved',
name='used',
field=models.BooleanField(default=False, verbose_name='Used'),
),
]
68 changes: 50 additions & 18 deletions retirement/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,24 +346,35 @@ def get_wait_queue_place_reserved(self, user):
def check_and_use_reserved_place(self, user):
wait_queue_place = self.get_wait_queue_place_reserved(user)
if wait_queue_place:
self.wait_queue.filter(user=user).delete()
wait_queues = self.wait_queue.filter(user=user)
for wait_queue in wait_queues:
wait_queue.used = True
wait_queue.save()

wait_queue_place.available = False
wait_queue_place.save()

WaitQueuePlaceReserved.objects.filter(
user_places_reserved = WaitQueuePlaceReserved.objects.filter(
wait_queue_place__retreat=self,
user=user
).delete()
)
for user_place_reserved in user_places_reserved:
user_place_reserved.used = True
user_place_reserved.save()

def can_order_the_retreat(self, user, invitation=None):
has_raimimng_place = self.has_places_remaining(invitation)
has_remaining_place = self.has_places_remaining(invitation)

wait_queue_place = self.get_wait_queue_place_reserved(user)
has_reserved_place = wait_queue_place is not None
can_order_the_retreat = has_raimimng_place or has_reserved_place
can_order_the_retreat = has_remaining_place or has_reserved_place

return can_order_the_retreat

def get_datetime_refund(self):
return self.start_time - timedelta(
days=self.min_day_refund)


class Picture(models.Model):
"""Represents pictures representing a retreat place"""
Expand Down Expand Up @@ -559,6 +570,11 @@ class Meta:
related_name='wait_queue',
)

used = models.BooleanField(
verbose_name=_("Used"),
default=False
)

created_at = models.DateTimeField(auto_now_add=True)

history = HistoricalRecords()
Expand Down Expand Up @@ -665,34 +681,45 @@ class WaitQueuePlace(models.Model):
def __str__(self):
return f'{self.retreat} {self.pk}'

def notify(self):
# Get all user that have no wait_queue_places_reserved
# for this WaitQueuePlace

def get_user_without_places_reserved(self):
wait_queue_places_reserved_ids = \
self.wait_queue_places_reserved.filter(
notified=True).values('user_id')
used=False).values('user_id')

retreat_wait_queues = self.retreat.wait_queue \
retreat_wait_queues = self.retreat.wait_queue\
.filter(used=False) \
.exclude(user_id__in=wait_queue_places_reserved_ids) \
.order_by('created_at')

datetime_refund = self.retreat.start_time - timedelta(
days=self.retreat.min_day_refund)
# if we are after the refund delay, we notify every waiting user
less_than_min_day_refund = timezone.now() >= datetime_refund
return retreat_wait_queues

stop = timezone.now() >= self.retreat.start_time
def notify(self):

users_notified = []

# Stop the notification process if place not available
if not self.available:
return 'Wait queue place not available', True

stop = timezone.now() >= self.retreat.start_time
if stop:
return users_notified, stop
return 'Retreat already started', stop

# Get all user that have no wait_queue_places_reserved
# for this WaitQueuePlace
retreat_wait_queues = self.get_user_without_places_reserved()

# if we are after the refund delay, we notify every waiting user
less_than_min_day_refund = \
timezone.now() >= self.retreat.get_datetime_refund()

for wait_queue in retreat_wait_queues:
# check if the user is already notified for this retreat
user_already_notified = WaitQueuePlaceReserved.objects.filter(
wait_queue_place__available=True,
user=wait_queue.user,
notified=True,
used=False,
wait_queue_place__available=True,
wait_queue_place__retreat=self.retreat
).exists()

Expand Down Expand Up @@ -734,6 +761,11 @@ class WaitQueuePlaceReserved(models.Model):
default=False
)

used = models.BooleanField(
verbose_name=_("Used"),
default=False
)

def __str__(self):
return f'{self.wait_queue_place}-{self.user}'

Expand Down
Loading

0 comments on commit 2e74e07

Please sign in to comment.