def test_empty(): """Test that an empty dataframe is valid (As long as the required columns exist!) """ empty = empty_events() check_event_specification(empty)
def test_unknown_version(events): """Test only the current specification version If a new version of the events specification is implemented, this unit test should be updated to the next version. """ with pytest.raises(ValueError): # This should fail because version='2' does not exist yet check_event_specification(events, version='2')
def preconditions(self, *, events, **inputs): """ Verify task preconditions Soft preconditions to this task are: * Input events follow the :ref:`event_specs`. """ super().preconditions(events=events, **inputs) try: with pd.HDFStore(events.file, 'r') as store: events = pd.read_hdf(store, key=self.input_hdf5_key) check_event_specification(events) except EventSpecificationError as ex: logger.info('VR selection will not run: the input does not ' 'adhere to standard event specification') raise SoftPreconditionFailed('Input did not adhere to standard ' 'event specification') from ex
def postconditions(self, results): """ Check standard event postconditions The postconditions of this task is that the result follows the event specification standard. """ super().postconditions(results) if not isinstance(results, FileAdapter): raise PostconditionFailed('Output was not a file') # Postcondition: file is empty if results.empty: return key = self.output_hdf5_key with pd.HDFStore(results.file, 'r') as store: dataframe = pd.read_hdf(store, key) check_event_specification(dataframe)
def postconditions(self, results): """ Verify task postconditions Postconditions to this tasks are: * The results object is a :py:class:`~iguazu.helpers.files.FileAdapter` * One of: * Results are empty * Results contents adhere to :ref:`event_specs`. """ super().postconditions(results) if not isinstance(results, FileAdapter): raise PostconditionFailed('Output was not a file') # Postcondition: file is empty if results.empty: return key = self.output_hdf5_key with pd.HDFStore(results.file, 'r') as store: dataframe = pd.read_hdf(store, key) check_event_specification(dataframe)
def _check_and_assert_raises(obj, code): with pytest.raises(EventSpecificationError) as ex_info: check_event_specification(obj) exception = ex_info.value assert exception.code == code
def test_datetime_contents(events, tz): """Test that datetime64 and datetime64 with utc are accepted""" # Note: tz=None gives a regular datetime64 events['begin'] = events['begin'].dt.tz_localize(tz=tz) events['end'] = events['end'].dt.tz_localize(tz=tz) check_event_specification(events)
def test_correct_event_dataframe(events): """Test a correct event dataframe""" # This should not raise anything check_event_specification(events)
def extract_space_stress_spawns_stimulations(events): """ Extract information about spawns in space stress events This function extracts events with label "unity_space-stress_game_breach_spawns" and "unity_space-stress_game_enemy_spawns". It returns a dataframe with interesting features associated to these actions (spawn position, type, id ). Parameters ---------- events: pd.DataFrame Dataframe with columns 'label' and 'data', containing events sent by unity during VR session. Returns ------- A dataframe with each row corresponding to a stimulation from the game and columns are the following: - id: id of the breach or enemy - label: unity_space-stress_game_breach_spawns. - type: Type of spawner (enemy or breach). - x: Coordinate x of the spawn. - y: Coordinate y of the spawn. - z: Coordinate z of the spawn. - wave: Index of the wave. - difficulty: Difficulty of the wave. - trajectory_length: Euclidean distance between successive spawns in plan (X,Y). Notes ------ Here is an example of how the outputshould look like: >>> data id label type x y z wave difficulty trajectory_length 2019-04-03 08:36:07.464509437+00:00 0 unity_space-stress_game_enemy_spawns enemy 1.989 2.766 5.480 1. 5. NaN 2019-04-03 08:36:07.691995137+00:00 1 unity_space-stress_game_enemy_spawns enemy 1.989 2.766 5.480 1. 5. NaN 2019-04-03 08:36:07.890431837+00:00 2 unity_space-stress_game_enemy_spawns enemy 0.278 1.919 5.673 1. 5. 1.955187 2019-04-03 08:36:08.142808336+00:00 3 unity_space-stress_game_enemy_spawns enemy 1.591 4.102 4.411 1. 5. 2.547441 2019-04-03 08:36:08.324275836+00:00 4 unity_space-stress_game_enemy_spawns enemy -1.140 1.840 5.230 1. 5. 3.546125 ... """ # check that the input meets the events standard specifications check_event_specification(events) if 'space-stress_sequence' not in extract_complete_sequences( events, label_column='name'): raise SequenceNotFound('Could not find "space-stress_sequence".') enemy_spawns = extract_metas( events, labels=['unity_space-stress_game_enemy_spawns'], meta_keys=['enemy_id', 'x', 'y', 'z'], label_column='name').rename(columns={'enemy_id': 'id'}) enemy_spawns['type'] = 'enemy' breach_spawns = extract_metas( events, labels=['unity_space-stress_game_breach_spawns'], meta_keys=['breach_id', 'x', 'y', 'z'], label_column='name').rename(columns={'breach_id': 'id'}) breach_spawns['type'] = 'breach' out = pd.concat([breach_spawns, enemy_spawns], sort=True).sort_index() out = estimate_trajectory_length(data=out, columns=['x', 'y']) return out.drop(['name', 'id'], axis=1)
def extract_space_stress_participant_actions(events): """ Extract information about participant controller actions from space stress events This function extracts events with label "unity_space-stress_game_participant_pushes_button" and construct a table with interesting features associated to the action (cursor position, context, results .. ). Parameters ---------- events: pd.DataFrame Dataframe with columns 'label' and 'data', containing events sent by unity during VR session. Returns ------- A dataframe with each row corresponding to one action from the participant and columns are the following: - label: "unity_space-stress_game_participant_pushes_button" - button: Button pressed by the participant (trigger or pad). - x: Coordinate x of the cursor position. - y: Coordinate y of the cursor position. - z: Coordinate z of the cursor position. - action_succeed: Boolean giving action output (True if success, False if fail). - failure_reason: string ("finger_confusion"|"bad_precision"|"bad_planification") or None if there is no failure. - wave: Index of the wave. - difficulty: Difficulty of the wave - trajectory_length: Euclidean distance between successive actions in plan (X,Y). Notes ------ Here is an example of how the output should look like. >>> data label button x y z action_succeed failure_reason wave difficulty trajectory_length 2019-04-03 08:35:21.521755333+00:00 unity_space-stress_game_participant_pushes_button trigger -1.428315 2.280506 5.309360 False bad_precision 1. 5. NaN 2019-04-03 08:35:21.775456032+00:00 unity_space-stress_game_participant_pushes_button trigger -1.428315 2.280506 5.309360 False bad_precision 1. 5. 0.000000 2019-04-03 08:35:22.500084331+00:00 unity_space-stress_game_participant_pushes_button trigger -1.428408 2.402699 5.282661 False bad_precision 1. 5. 0.122193 2019-04-03 08:35:22.753246430+00:00 unity_space-stress_game_participant_pushes_button trigger -1.428408 2.402699 5.282661 False bad_precision 1. 5. 0.000000 2019-04-03 08:35:22.986076530+00:00 unity_space-stress_game_participant_pushes_button trigger -1.448533 2.439976 5.272884 False bad_precision 1. 5. 0.042363 ... See the documentation of :py:func:dsu.space_stress.classify_failures """ # check that the input meets the events standard specifications check_event_specification(events) if 'space-stress_sequence' not in extract_complete_sequences( events, label_column='name'): raise SequenceNotFound('Could not find "space-stress_sequence".') space_stress_times = extract_complete_sequence_times( events, 'space-stress_sequence', pedantic='warn', label_column='name') begins, ends = space_stress_times[0] out = truncate(extract_metas( events, labels=['unity_space-stress_game_participant_pushes_button'], meta_keys=['button', 'x', 'y', 'z', 'result'], label_column='name'), begins=begins, ends=ends) out.loc[:, 'action_succeed'] = out.result.apply( lambda x: 'succeed' in x['action']) out = out.drop(out.loc[(out.button == 'pad') & (out.result == { 'action': 'action_failed' })].index) for button in ['pad', 'trigger']: # classify failure reason depending on button type idx_button = (out.button == button) func_button = functools.partial(categorize_failure, button=button) out.loc[idx_button, 'failure_reason'] = out.loc[idx_button, 'result'].apply(func_button) # estimate trajectory length of participant actions in X,Y plan. out = estimate_trajectory_length(data=out, columns=['x', 'y']) return out.drop(['result', 'name'], axis=1)