Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment anon field #156

Merged
merged 3 commits into from
Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .bcipy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Experiments and Fields

BciPy experiments and fields allow users to collect metadata that may be integrated alongside core BciPy data. This is particularly useful when the experiment has completed and a researcher wants to curate their files for sharing with the community.

## Experiments

An experiment defines the name, summary and fields to collect. At this level, the requirement for collection during every task and whether or not to anonymize later will be set. The anonymization does not encrypt the data at rest, but instead defines how to handle the field later when uploading/sharing. The registered experiments are defined in `.bcipy/experiment/experiments.json` in the following format:

```js
{
name: {
fields : {
name: "",
required: bool,
anonymize: bool
},
summary: ""
}
}
```

## Fields

A field is a unit of data collection for an experiment. It has a name, help text and type. The type will determine how it is collected and validated. The registered fields are defined in `.bcipy/field/fields.json` in the following format:


```js
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't js but it displays json better this way 🤷‍♂️

{
name: {
help_text: "",
type: "FieldType"
}
}
```

where FieldType may be str, bool, int, or float.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,5 @@ htmlcov/
bcipy.egg-info/
build/
dist/
.bcipy/*

bcipy/parameters/parameters_*
bcipy/parameters/parameters_*
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ Memmott, T., Koçanaoğulları, A., Lawhead, M., Klee, D., Dudy, S., Fried-Oken,

## Dependencies
---------------
This project requires Psychopy, Python v 3.6.5, and other packages. See requirements.txt. When possible integration with other open source libraries will be done.
This project requires Python > 3.6.5 and other packages defined in the requirements.txt.


## Installation
---------------

#### BCI Setup

In order to run BCI suite on your computer, first install **Python 3.6.5** [from here.](https://www.python.org/downloads/)
In order to run BCI suite on your computer, first install **Python 3** [from here.](https://www.python.org/downloads/)

You must install Docker and Docker-Machine to use the Language Model developed by CSLU. There are instructions in the language model directory for getting the image you need (think of it as a callable server). You'll also need to download and load the language model [images](https://drive.google.com/drive/folders/1OYpUYASAceb60b2c5obyYytEZ0AZrajY?usp=sharing). If not using or rolling your own, set fake_lm to true in the parameters.json file.

Expand Down Expand Up @@ -133,7 +133,7 @@ Use this resource for examples: http://docs.python-guide.org/en/latest/writing/s
## Testing
----------

When writing tests, put them in the correct module, in a tests folder, and prefix the file and test itself with `test` in order for pytest to discover it. See other module tests for examples!
When writing tests, put them in the correct module, in a tests folder, and prefix the file and test itself with `test_` in order for pytest to discover it. See other module tests for examples!

Development requirements must be installed before running: `pip install dev_requirements.txt`

Expand All @@ -153,7 +153,7 @@ py.test acquisition
To generate test coverage metrics, in the command line:

```bash
coverage run --branch --source=bcipy -m pytest
coverage run --branch --source=bcipy -m pytest --mpl -k "not slow"

#Generate a command line report
coverage report
Expand Down
34 changes: 20 additions & 14 deletions bcipy/gui/experiments/ExperimentField.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QHBoxLayout,
QMessageBox,
QPushButton,
QScrollArea,
QVBoxLayout,
Expand All @@ -17,7 +16,9 @@

from bcipy.gui.gui_main import (
AlertMessageType,
AlertMessageResponse,
AlertResponse,
MessageBox,
app,
BoolInput,
DirectoryInput,
Expand Down Expand Up @@ -47,6 +48,7 @@ class ExperimentFieldCollection(QWidget):
'directorypath': DirectoryInput
}
require_mark = '*'
alert_timeout = 10
save_data = {}

def __init__(self, title: str, width: int, height: int, experiment_name: str, save_path: str, file_name: str):
Expand Down Expand Up @@ -95,6 +97,7 @@ def field_input(self, field_name: str, field_type: str, help_tip: str, required:
"""

form_input = self.type_inputs.get(field_type, TextInput)

if required:
field_name += self.require_mark
return form_input(
Expand All @@ -107,7 +110,7 @@ def field_input(self, field_name: str, field_type: str, help_tip: str, required:
def build_assets(self) -> None:
"""Build Assets.

Build any needed assests for the Experiment Field Widget. Currently, only a form is needed.
Build any needed assets for the Experiment Field Widget. Currently, only a form is needed.
"""
self.build_form()

Expand All @@ -124,7 +127,8 @@ def check_input(self) -> bool:
title='BciPy Alert',
message=f'Required field {name.strip(self.require_mark)} must be filled out!',
message_type=AlertMessageType.CRIT,
okay_or_cancel=True)
message_response=AlertMessageResponse.OCE,
message_timeout=self.alert_timeout)
return False
return True

Expand Down Expand Up @@ -171,36 +175,39 @@ def build_save_data(self) -> None:
title='Error',
message=f'Error saving data. Invalid value provided. \n {e}',
message_type=AlertMessageType.WARN,
okay_or_cancel=True
message_response=AlertMessageResponse.OCE,
message_timeout=self.alert_timeout
)

def write_save_data(self) -> None:
save_experiment_field_data(self.save_data, self.save_path, self.file_name)
self.throw_alert_message(
title='Success',
message=(
f'Data sucessfully written to: \n\n{self.save_path}/{self.file_name} \n\n\n'
'Please close this window to start the task!'),
f'Data successfully written to: \n\n{self.save_path}/{self.file_name} \n\n\n'
'Please wait or close this window to start the task!'),
message_type=AlertMessageType.INFO,
okay_or_cancel=True
message_response=AlertMessageResponse.OCE,
message_timeout=self.alert_timeout,
)

def throw_alert_message(self,
title: str,
message: str,
message_type: AlertMessageType = AlertMessageType.INFO,
okay_to_exit: bool = False,
okay_or_cancel: bool = False) -> QMessageBox:
message_response: AlertMessageResponse = AlertMessageResponse.OTE,
message_timeout: float = 0) -> MessageBox:
"""Throw Alert Message."""

msg = QMessageBox()
msg = MessageBox()
msg.setWindowTitle(title)
msg.setText(message)
msg.setIcon(message_type.value)
msg.setTimeout(message_timeout)

if okay_to_exit:
if message_response is AlertMessageResponse.OTE:
msg.setStandardButtons(AlertResponse.OK.value)
elif okay_or_cancel:
elif message_response is AlertMessageResponse.OCE:
msg.setStandardButtons(AlertResponse.OK.value | AlertResponse.CANCEL.value)

return msg.exec_()
Expand Down Expand Up @@ -267,7 +274,6 @@ def start_app() -> None:
from bcipy.helpers.system_utils import DEFAULT_EXPERIMENT_ID

parser = argparse.ArgumentParser()
# save path

# experiment_name
parser.add_argument('-p', '--path', default='.',
Expand All @@ -292,7 +298,7 @@ def start_app() -> None:
file_name = args.filename

bcipy_gui = app(sys.argv)
# todo change height based on field #?

ex = MainPanel(
title='Experiment Field Collection',
height=250,
Expand Down
Loading