def select_participant(event): """ Indicates selection of a participant by the wheel. This will cause updates to the weights for all participants or removal of rigging if the wheel is rigged. :param event: Lambda event containing the API Gateway request path parameters wheel_id and participant_id { "pathParameters": { "wheel_id": string ID of the wheel to rig (DDB Hash Key) "participant_id": string ID of the participant to rig (DDB Hash Key) }, } :return: response dictionary """ wheel_id = event['pathParameters']['wheel_id'] participant_id = event['pathParameters']['participant_id'] wheel = Wheel.get_existing_item(Key={'id': wheel_id}) participant = WheelParticipant.get_existing_item(Key={ 'id': participant_id, 'wheel_id': wheel_id }) choice_algorithm.select_participant(wheel, participant) # Undo any rigging that has been set up Wheel.update_item(Key={'id': wheel['id']}, UpdateExpression='remove rigging')
def test_selection_cycle(mock_dynamodb, setup_data, mock_participant_table): def get_participant_with_id(participants, target_id): for p in participants: if p['id'] == target_id: return p return None rngstate = random.getstate() random.seed(0) # Make the (otherwise pseudorandom) test repeatable. participants = WheelParticipant.scan({})['Items'] wheel = setup_data['wheel'] total_weight_of_chosens = 0 num_iterations = 200 distro = {} for participant in participants: distro[participant['name']] = 0 for _ in range(0, num_iterations): chosen_id = choice_algorithm.suggest_participant(wheel) chosen_was = get_participant_with_id(participants, chosen_id) chosen_was_weight = chosen_was['weight'] distro[chosen_was['name']] = distro[chosen_was['name']] + 1 choice_algorithm.select_participant(wheel, chosen_was) participants = WheelParticipant.scan({})['Items'] chosen_now = get_participant_with_id(participants, chosen_id) chosen_now_weight = chosen_now['weight'] assert chosen_was_weight > 0.0 assert chosen_now_weight == 0 total_weight_of_chosens += chosen_was_weight total_weight = sum( [participant['weight'] for participant in participants]) assert abs(total_weight - len(participants)) < epsilon # Must match human-inspected reasonable values for the RNG seed defined # above for number of times each participant was chosen, and the total # weight of participants selected. These are a rough equivalent to # ensuring that the sequence of chosen participants matches the observed # test run. dv = list(distro.values()) list.sort(dv) human_observed_selection_counts = [26, 27, 27, 29, 29, 31, 31] human_observed_total_weight = 317.8786415239279 assert dv == human_observed_selection_counts assert abs(float(total_weight_of_chosens) - human_observed_total_weight) < epsilon # Put things back the way they were. random.setstate(rngstate)
def test_reset_wheel(mock_dynamodb, setup_data, mock_participant_table): choice_algorithm.select_participant(setup_data['wheel'], setup_data['participants'][0]) choice_algorithm.reset_wheel(setup_data['wheel']) updated_participants = mock_participant_table.query( KeyConditionExpression=Key('wheel_id').eq(setup_data['wheel'] ['id']))['Items'] participant_weights = [ participant['weight'] for participant in updated_participants ] for weight in participant_weights: assert weight == 1
def test_select_participant(mock_dynamodb, setup_data, mock_participant_table): participant_to_select = setup_data['participants'][0] choice_algorithm.select_participant(setup_data['wheel'], participant_to_select) participants = mock_participant_table.query(KeyConditionExpression=Key( 'wheel_id').eq(setup_data['wheel']['id']))['Items'] selected_participant = [ participant for participant in participants if participant['id'] == participant_to_select['id'] ][0] assert selected_participant['weight'] == 0 assert abs( sum([participant['weight'] for participant in participants]) - len(participants)) < epsilon
def test_rebalance_wheel(setup_data, mock_participant_table): def set_up_test(setup_data, mock_participant_table): # Select a participant to take everyone off their 1.0 scores. choice_algorithm.select_participant(setup_data['wheel'], setup_data['participants'][0]) # Adjust participants to different weights to take the wheel out of balance. participants = mock_participant_table.query(KeyConditionExpression=Key( 'wheel_id').eq(setup_data['wheel']['id']))['Items'] with WheelParticipant.batch_writer() as batch: for p in participants: p['weight'] += Decimal(.15) batch.put_item(Item=p) # Confirm that the wheel is out of balance. participants = mock_participant_table.query(KeyConditionExpression=Key( 'wheel_id').eq(setup_data['wheel']['id']))['Items'] participant_weights = [ participant['weight'] for participant in participants ] total_weight = Decimal(0) for weight in participant_weights: total_weight += weight assert abs(total_weight - Decimal(8.05)) < epsilon def complete_test(setup_data, mock_participant_table): # Confirm that rebalancing has taken place. participants = mock_participant_table.query(KeyConditionExpression=Key( 'wheel_id').eq(setup_data['wheel']['id']))['Items'] participant_weights = [ participant['weight'] for participant in participants ] total_weight = Decimal(0) for weight in participant_weights: total_weight += weight assert abs(total_weight - len(participants)) < epsilon set_up_test(setup_data, mock_participant_table) # Select a participant to cause rebalancing to take place. participants = mock_participant_table.query(KeyConditionExpression=Key( 'wheel_id').eq(setup_data['wheel']['id']))['Items'] choice_algorithm.select_participant(setup_data['wheel'], participants[3]) complete_test(setup_data, mock_participant_table)
def test_select_participant(mock_dynamodb, setup_data, mock_participant_table): participant_to_select = setup_data['participants'][0] choice_algorithm.select_participant(setup_data['wheel'], participant_to_select) updated_participants = mock_participant_table.query( KeyConditionExpression=Key('wheel_id').eq(setup_data['wheel'] ['id']))['Items'] selected_participant = [ participant for participant in updated_participants if participant['id'] == participant_to_select['id'] ][0] other_participants_weights = [ participant['weight'] for participant in updated_participants if participant['id'] != participant_to_select['id'] ] assert selected_participant['weight'] == 0 for weight in other_participants_weights: assert weight == 1.5
def test_fix_incorrect_participant_count(mock_dynamodb, setup_data, mock_wheel_table): out_of_whack = 999 wheel = setup_data['wheel'] wheel_id = wheel['id'] proper_participant_count = wheel['participant_count'] # # # # We will first test this on a select_participant operation. # Throw the participant count way out of whack. mock_wheel_table.update_item(Key={'id': wheel['id']}, **to_update_kwargs( {'participant_count': out_of_whack})) participant_count = mock_wheel_table.query(KeyConditionExpression=Key( 'id').eq(wheel['id']))['Items'][0].get('participant_count') # # Ensure it's out of whack. assert abs(out_of_whack - participant_count) < epsilon # Select a participant to cause correction of participant count. wheel = Wheel.get_existing_item(Key={'id': wheel_id}) choice_algorithm.select_participant(wheel, setup_data['participants'][0]) # ...and ensure it's back into whack. participant_count = mock_wheel_table.query(KeyConditionExpression=Key( 'id').eq(wheel['id']))['Items'][0].get('participant_count') assert abs(Decimal(proper_participant_count) - participant_count) < epsilon # # # # We will next test this on a delete_participant operation. # Throw the participant count way out of whack. mock_wheel_table.update_item(Key={'id': wheel['id']}, **to_update_kwargs( {'participant_count': out_of_whack})) participant_count = mock_wheel_table.query(KeyConditionExpression=Key( 'id').eq(wheel['id']))['Items'][0].get('participant_count') # # Ensure it's out of whack. assert abs(out_of_whack - participant_count) < epsilon # Delete a participant to cause correction of participant count. event = { 'body': {}, 'pathParameters': { 'wheel_id': wheel_id, 'participant_id': setup_data['participants'][0]['id'] } } wheel_participant.delete_participant(event) # # ...and ensure it's back into whack. participant_count = mock_wheel_table.query(KeyConditionExpression=Key( 'id').eq(wheel['id']))['Items'][0].get('participant_count') assert abs((Decimal(proper_participant_count) - 1) - participant_count) < epsilon # # # # We will next test this on a create_participant operation. # Throw the participant count way out of whack. mock_wheel_table.update_item(Key={'id': wheel['id']}, **to_update_kwargs( {'participant_count': out_of_whack})) participant_count = mock_wheel_table.query(KeyConditionExpression=Key( 'id').eq(wheel['id']))['Items'][0].get('participant_count') # # Ensure it's out of whack. assert abs(out_of_whack - participant_count) < epsilon # Add a participant to cause correction of participant count. event = { 'pathParameters': { 'wheel_id': wheel_id }, 'body': { 'name': 'Ishmael-on-the-Sea', 'url': 'https://amazon.com' } } wheel_participant.create_participant(event) # # ...and ensure it's back into whack. participant_count = mock_wheel_table.query(KeyConditionExpression=Key( 'id').eq(wheel['id']))['Items'][0].get('participant_count') assert abs((Decimal(proper_participant_count)) - participant_count) < epsilon