Django dynamic choice field, appointments slots

Viewed 2467

I need some direction on how to create a dynamic choice field for a form. The context is that of appointments. I want to give people the ability to book their own appointments however it needs to be based on some criteria. The criteria being the date, time, and the number of people.

I want the time field to be a dynamic choice field. Based on the date and party size "psize" I originally thought that this could be achieved via filter, aggregate, and sum. Using the input from whomever was filing out the from.

I was thinking that I could take the date and the party size from the form and use that to filter and sum the number of people that chose the same selection (this is important because I want 15 or less people signed up for any given time period) my time periods are in 15 minute increments and are listed in a tuple (like a regular choice field). Any ideas?

models.py

class Reservation(models.Model):
"""This tuple is for the Inside, Outside, First Available 
drop down menu for the reservations tab """
    INSIDEOUTSIDE = (
        ('Inside','Inside'),
        ('Outside', 'Outside'),
        ('First Available', 'First Available'),
    )

    STATUS = (
        ('Reservation','Reservation'),
        ('Checked In','Checked In'),
        ('Seated','Seated'),
    )

    PARTYSIZE = (
        (1,'1'),
        (2,'2'),
        (3,'3'),
        (4,'4'),
        (5,'5'),
        (6,'6'),
        (7,'7'),
        (8,'8'),
        (9,'9'),
        (10,'10'),
        (11,'11'),
        (12,'12'),
    )    

    TIMEPERIOD= (
        ('11:30','11:30'),
        ('11:45','11:45'), 
        ('12:00','12:00'), 
        ('12:15','12:15'), 
    )


    name = models.CharField(max_length=22)
    psize = models.IntegerField('Party Size', choices= PARTYSIZE)
    Date = models.DateField()
    Time = models.CharField(max_length=15, choices=TIMEPERIOD)
    location = models.CharField(max_length=15, choices= INSIDEOUTSIDE, default= 'First Available')
    status = models.CharField(max_length=15, choices=STATUS, default='Reservation')

below is the filter that works, well not exactly. It returns the items that I type in it, I want to be able to pass it a list and have it do the calculations with the part size "psize" and pass back a list to the form that has the available time slots (time slots that have less that 15 people including the number that whomever imputed into the form).

Reservation.objects.filter(Date='2018-01-01',Time='2:30').aggregate(Sum('psize'))

Can anyone point me in the right direction? Any thoughts?

Update Below is the code that I use in the shell to determine the choices.

def getchoices(request):
    DATE = request.POST.get('Date')
    psize_input = request.post.get('psize')
    slots = Reservation.objects.filter(Date=DATE).values_list('Time').annotate(Sum('psize'))
    TIMEPERIOD= (
    ('11:30','11:30'),
    ('11:45','11:45'),
    ('12:00','12:00'),
    ('12:15','12:15'),
    ('12:30','12:30'),
    ('12:45','12:45'),
    ('01:00','01:00'),
    ('01:15','01:15'),
    ('01:30','01:30'),
    ('01:45','01:45'),
    ('02:00','02:00'),
    ('02:15','02:15'),
    ('02:30','02:30'),
    )
    def checkpsize():
            fullslots1=[]
            fullslots2=[]
            for object in slots:
                    if object[1] > 15-psize_input:
                            fullslots1.append(object[0])
                            fullslots2.append(object[0])
                            fullslotszip = zip(fullslots1, fullslots2)
                            fullslotstup = list(fullslotszip)
                            newchoices = list(filter(lambda x: x not in fullslotstup, TIMEPERIOD))
            return newchoices

However, I am finding it difficult to connect this to my form. I can't seem to figure out how to implement the ajax in my form to populate my time field based on the psize and date inputs. Any ideas on a good place to get started? A tutorial possibly?

1 Answers

Assuming your user inserted a value psize_input, you can use:

 Reservation.objects
    .order_by('Date', 'Time')
    .values('Date', 'Time')
    .annotate(Sum('psize'))  # adds psize sum for each distinct data/time combo
    .filter(psize__sum__lte=15-psize_input)  # filters combos with enough capacity

to obtain a list (actually a ValuesIterable) of dictionaries in the shape of:

[
    {'Date': datetime.date(2018, 10, 31), 'Time': '11:30', 'psize__sum': 5},
    {'Date': datetime.date(2018, 10, 31), 'Time': '12:15', 'psize__sum': 2},
    {'Date': datetime.date(2018, 11, 1), 'Time': '11:45', 'psize__sum': 7},
    # ...
]

containing exactly the time slots that have enough capacity left with the current psize total at that slot.

Related