Skip to content

Data Model

Hierarchy

Data in a Flywheel system is organized in a tree-like hierarchy, as indicated in the following diagram.

image

  • flywheel.models.user.User - An authorized entity, usually referenced by email address.
  • flywheel.models.group.Group - A grouping of users and projects.
  • flywheel.models.project.Project - A project represents a grouping of subjects and sessions, for example within a study.
  • flywheel.models.subject.Subject - An individual under study.
  • flywheel.models.session.Session - A grouping of acquired data, typically data acquired within a limited timeframe.
  • flywheel.models.acquisition.Acquisition - A set of one or more files, typically acquired as part of the same process, at the same time.
  • flywheel.models.analysis_output.AnalysisOutput - A set of one or more derivative files from analyzing files after they have been acquired.

Permissions

Permissions in Flywheel are managed at the Group and Project level. The management of permissions is different between the two.

For Groups, a user may be added with any one of three access levels: "admin", "rw", "ro". The current level assigned to users can be found under the "permissions" field of a group.

View group permissions
group = fw.get_group(group_id)

print(group.permissions)

Permissions under a group look like this:

{
    "_id": "dev@flywheel.io",
    "access": "admin"
}

Project Permissions are different. Instead of being assigned a preset "access" level, a user is assigned any number of "roles", which in turn are a grouping of actions. For example, a user assigned a role that includes the action to download files, but not upload files, would be able to do the former, but not the latter. The roles a user can be assigned in a project are limited to the roles that have been specifically set on the group as available to the projects in the group.

Add user to project with role
all_project_roles = fw.get_all_roles()

# Get the role id
role_id = [role.id for role in all_project_roles if role.label == "admin"][0]

# Add it to the group
new_role = fw.add_role_to_group(group.id, body={"_id": role_id})

# Add user with the role to a project in the group
project = fw.projects.find_first(
    f"parents.group={group_id},label=MyProjectLabel"
)
project.add_permission(
    {"_id": "dev@flywheel.io", "role_ids": [role_id]}
)

# Removing a user from a project
project.delete_permission(user_id)

Some things to keep in mind:

  • "role_ids" is a list so that a user can be assigned multiple roles for a project.
  • A user does not need to have access to a group to have a role in a project.
  • Any action taken on data in the project uses the roles assigned for the project to check authorization.

To add a user with the same access as another user, you can select the first user's permissions, change the user ID to the second user, and add the permissions to the project.

Copy permissions from one user to another
current_user_permissions = fw.get_project_user_permission(project_id, user_id)
permission.id = user_b_id
project.add_permission(permission)

Custom Roles

Custom roles can be defined for more refined control over project access. A role is defined as a dictionary with label and actions keys (for a full list of actions, see Available Actions).

Create and manage custom roles
group_id = "group_id"
project_label = "project_label"

# These actions (read_only actions) are required for any new role
# definition
REQUIRED_ACTIONS = [
    "containers_view_metadata",
    "files_view_metadata",
    "tags_view",
    "notes_view",
    "project_permissions_view",
    "data_views_view",
    "session_templates_view",
    "gear_rules_view",
    "jobs_view"
]

# Add files_modify_metadata to list
action_list = REQUIRED_ACTIONS + ["files_modify_metadata"]

# Create a dictionary defining label and actions
role_dict = {"label": "role_a_d20", "actions": action_list}

# Add the role to Flywheel
role_a_d20 = fw.add_role(role_dict)

# Add the role to a group
fw.add_role_to_group(group_id, {"_id": role_a_d20.id})

# Get a project
project = fw.lookup(f"{group_id}/{project_label}")

# Add role for a user not yet on the project (but has been added
# to the Flywheel instance)
user_id = "new_user@example.com"
project.add_permission({"_id": user_id, "role_ids": [role_a_d20.id]})

# Add role for user with existing permissions on the project
# Add containers_modify_metadata to list
action_list = REQUIRED_ACTIONS + ["containers_modify_metadata"]

# Create a dictionary defining label and actions
role_dict = {"label": "barrel_role", "actions": action_list}

# Add the role to Flywheel
barrel_role = fw.add_role(role_dict)

# Add the role to a group
fw.add_role_to_group(group_id, barrel_role.id)

# Get the current permission dictionary for user
permission_dict = fw.get_project_user_permission(project.id, user_id)

# Add role id to the current list of role ids
permission_dict["role_ids"].append(barrel_role.id)

# Update the user's permissions with the modified permission_dict
project.update_permission(user_id, permission_dict)

# List all roles
fw.get_all_roles()

# List all group roles
fw.get_all_group_roles(group_id)

# We need to remove the permission that uses the role before
# removing the role from group
project.delete_permission(user_id)

# Delete the role from group
fw.remove_role_from_group(group_id, barrel_role.id)

# Delete the role
fw.delete_role(barrel_role.id)

Available Actions

Action Description
containers_view_metadata View Container Metadata
containers_create_hierarchy Create Container Hierarchy
containers_modify_metadata Modify Container Metadata
containers_delete_hierarchy Delete Container Hierarchy
containers_delete_project Delete Project (Project Permission)
analyses_view_metadata View Analysis Metadata
analyses_create_sdk Create Adhoc Analysis
analyses_create_job Create Job-Based Analysis
analyses_modify_metadata Modify Analysis Metadata
analyses_delete Delete Analysis
files_view_metadata View File Metadata
files_view_contents View File Contents
files_download Download File
files_create_upload Create/Upload File
files_modify_metadata Modify File Metadata
files_delete_non_device_data Delete Non-Device File Data
files_delete_device_data Delete Device File Data
tags_view View Tags
tags_manage Manage Tags
notes_view View Notes
notes_manage Manage Notes
project_permissions_view View Project Permissions
project_permissions_manage Manage Project Permissions
gear_rules_view View Project Gear Rules
gear_rules_manage Manage Project Gear Rules
data_views_view View Data Views
data_views_manage Manage Data Views
session_templates_view View Session Templates
session_templates_manage Manage Session Templates
jobs_view View Jobs
jobs_run_cancel Run and Cancel Jobs
jobs_cancel_any Cancel Any Job

Containers

Projects, Subjects, Sessions, Acquisitions and Analyses are all different types of Containers. Containers in Flywheel all support the following features:

Tags

Tags are concise labels that provide descriptive metadata that can be searched on. Available tags are managed on the Group.

Manage tags on a container
# See tags on a session
session = fw.get(session_id)
print(", ".join(session.tags))

# Add a tag to a session
session.add_tag("Control")

# Remove a tag from a session
session.delete_tag("Analysis Required")

Notes

Notes are user-entered, human readable metadata attached to a container. They are timestamped and attributed to the user that entered them.

Manage notes on a container
# See notes on a session
session = fw.get(session_id)
print(session.notes)

# Add a note to a session
session.add_note("This is a note")

# Delete a note from a session
session.delete_note(session.notes[0].id)

Info

Info is free-form JSON metadata associated with a container or file.

Manage info on a container
# Print the info for an acquisition
acquisition = fw.get(acquisition_id)
print(acquisition.info)

# Replace the entire contents of acquisition info
acquisition.replace_info({"splines": 34})

# Add additional fields to acquisition info
acquisition.update_info({"curve": "bezier"})

# Delete fields from acquisition info
acquisition.delete_info("splines")

Files

Files are a set of file attachments associated with a container. See also Dealing with Files.

Manage files on a container
# List files on an acquisition
acquisition = fw.get(acquisition_id)

for file in acquisition.files:
  print(f"Name: {file.name}, type: {file.type}")

# Upload a file to an acquisition
acquisition.upload_file("/path/to/file.txt")

# Download a file to disk
acquisition.download_file("file.txt", "/path/to/file.txt")

# Files can also have metadata
print(acquisition.files[0].info)

acquisition.replace_file_info("file.txt", {"wordCount": 327})

File Classification

Flywheel supports an extensible, multi-dimensional classification scheme for files. Each dimension of classification is referred to as an aspect. The available aspects are determined by the file's modality.

For example, the MR modality provides the Intent, Measurement and Features aspects. In addition, the Custom aspect is always available, regardless of modality.

Manage file classification
# Display the aspects defined in the MR modality
mr = fw.get_modality("MR")
print(mr)

# Replace a file's modality and classification
acquisition.replace_file_classification("file.txt", {
    "Intent": ["Structural"],
    "Measurement": ["T2"]
}, modality="MR")

# Update a file's Custom classification, without changing
# existing values or modality
acquisition.update_file_classification("file.txt", {
    "Custom": ["value1", "value2"]
})

# Delete 'value1' from Custom classification
acquisition.delete_file_classification("file.txt", {
    "Custom": ["value1"]
})

Timestamps

Objects with timestamps and created or modified dates provide helper accessors to get those dates in the local (system) timezone, as well as the original timezone in the case of acquisition and session timestamps.

For example:

Access timestamp information
# Acquisition Timestamp (tz=UTC)
print(acquisition.timestamp.isoformat())

# Acquisition Timestamp (tz=Local Timezone)
print(acquisition.local_timestamp.isoformat())

# Acquisition Timestamp (tz=Original Timezone)
print(acquisition.original_timestamp.isoformat())

Age at Time of Session

Sessions have a field for subject age at the time of the session, in seconds. There are also helper accessors to get age in years, months, weeks and days.

For example:

Access subject age information
# Subject age in seconds
print(f"Subject was {session.age} seconds old")

# Subject age in years
print(f"Subject was {session.age_years} years old")