def get_best_adjacent_in_section(self, section_id, seat_count, expiry_seconds=constants.DEFAULT_PREALLOCATION_EXPIRY): """ Tries to get the best available seats available in the section """ # Try to get the section by id try: section = (section for section in self.layout_data if section["section_id"] == section_id).__next__() except StopIteration as e: raise exp.NoSuchSectionException # Iterate through the rows in the section starting with the "highest quality" rows row_quality_list = sorted(section["rows"], key = lambda row: row["quality"]) for row in row_quality_list: # Try to find a row that has enough seats available_seats = [seat for seat in row['seats'] if seat["status"] == constants.SEAT_FREE] # TODO improvement would be to find the number of consecutive seats that fit the requirement most accurately # i.e. If there is multiple consecutive seats numbers available that fit the requirement, use the smallest max_consecutive_seats = (util.max_consecutive_series([seat["seat_id"] for seat in available_seats])) # Check the correct amount of consecutive seats are available if len(available_seats) >= seat_count and seat_count <= len(max_consecutive_seats): seat_ids_allocated = [] # Get those available seats with IDs that match the consecutive seat numbers selected_seats = [seat for seat in available_seats if seat["seat_id"] in max_consecutive_seats] # Limit the amount of seats to the amount we actually want from the selected seats list for seat in selected_seats[:seat_count]: seat["status"] = constants.SEAT_PREBOOKED seat_ids_allocated.append(seat["seat_id"]) # decrement available seats counter self.available_seats -= 1 # Make a note of the prebookings with an expiry time to allow future expiring # if the booking is not complete prebooking_token = str(uuid.uuid4()) expiry_time = datetime.datetime.now() + datetime.timedelta(0, expiry_seconds) self.preallocated[prebooking_token] = {'section_id' : section_id, 'row_id' : row["row_id"], 'seat_ids' : seat_ids_allocated, 'expiry_time' : expiry_time } # Include the row and seat ids in the return message return_value = {'status' : True, 'errors' : [], 'data' : {'prebook_token' : prebooking_token, 'expiry_time' : expiry_time, 'row_id' : row["row_id"], 'row_quality' : row['quality'], 'seat_ids' : seat_ids_allocated, } } break else: # No row in this section had enough seats available to satisfy the amount requested return_value = {'status' : False, 'errors' : [err.SEAT_REQUIREMENT_COULD_NOT_BE_FILLED], 'data' : {}} return return_value
def test_consecutive_seat_finder(self): self.assertEqual(util.max_consecutive_series([1,2,3,5,6,9]), [1,2,3]) self.assertEqual(util.max_consecutive_series([1,3,5]), [1,]) self.assertEqual(util.max_consecutive_series([1,2,3,5,6,9, 14,15,16,17]), [14,15,16,17])
def get_best_adjacent_in_event(self, seat_count, expiry_seconds=constants.DEFAULT_PREALLOCATION_EXPIRY): """ Tries to get the best available seats in the venue by checking all sections """ best_found = collections.defaultdict(list) # Defaultdict of best seat combos found by row quality # Get all sections for section in self.layout_data: # Iterate through the rows in the section starting with the "highest quality" rows row_quality_list = sorted(section["rows"], key = lambda row: row["quality"]) for row in row_quality_list: # Try to find a row that has enough seats available_seats = [seat for seat in row['seats'] if seat["status"] == constants.SEAT_FREE] # TODO improvement would be to find the number of consecutive seats that fit the requirement most accurately # i.e. If there is multiple consecutive seats numbers available that fit the requirement, use the smallest max_consecutive_seats = (util.max_consecutive_series([seat["seat_id"] for seat in available_seats])) # Check the correct amount of consecutive seats are available if len(available_seats) >= seat_count and seat_count <= len(max_consecutive_seats): # Get those available seats with IDs that match the consecutive seat numbers selected_seats = [seat for seat in available_seats if seat["seat_id"] in max_consecutive_seats][:seat_count] possible_seat_selection = {'section_id' : section['section_id'], 'row_id' : row["row_id"], 'row_quality' : row["quality"], 'seat_ids' : [seat["seat_id"] for seat in selected_seats], } # Add to defualt dict grouped by row quality best_found[row["quality"]].append(possible_seat_selection) # Use the first "best" row we find for best in sorted(best_found.items())[:1]: # Use the first one we get to best_booking = (best[1][0]) # Get section, row and seat objects section, row, seats = self.get_section_row_and_seats_from_ids(best_booking["section_id"], best_booking["row_id"], best_booking["seat_ids"]) # Preallocate the seats for seat in seats: seat["status"] = constants.SEAT_PREBOOKED self.available_seats -= 1 # decrement available seats counter # Make a note of the prebookings with an expiry time to allow future expiring # if the booking is not complete prebooking_token = str(uuid.uuid4()) expiry_time = datetime.datetime.now() + datetime.timedelta(0, expiry_seconds) self.preallocated[prebooking_token] = {'section_id' : best_booking["section_id"], 'row_id' : best_booking["row_id"], 'seat_ids' : best_booking["seat_ids"], 'expiry_time' : expiry_time } # Include the section, row and seat ids in the return message return_value = {'status' : True, 'errors' : [], 'data' : {'prebook_token' : prebooking_token, 'expiry_time' : expiry_time, 'section_id' : best_booking["section_id"], 'row_id' : best_booking["row_id"], 'seat_ids' : best_booking["seat_ids"], } } break else: # No seats could be found in the entire Event return_value = {'status' : False, 'errors' : [err.SEAT_REQUIREMENT_COULD_NOT_BE_FILLED], 'data' : {}} return return_value