Title: Update MoCo Acquisition Label

Date: 04-03-2020

Description:
This notebook is used to rename MoCo Series based on the Scan Name, end with _moco prefix

Functions:

  1. get_scan_moco_label : Identify the original scan label
  2. modify_acq_name : Modify Acquisition name based on the session timepoint
  3. update_acq_label : Update the acquisition label on the FW instances

Install and import dependencies¶

In [ ]:
# Install specific packages required for this notebook
!pip install flywheel-sdk pandas
In [ ]:
# Import packages
from getpass import getpass
import logging
import os

import pandas as pd
import flywheel
from permission import check_user_permission
In [ ]:
# Instantiate a logger
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
log = logging.getLogger('root')

Flywheel API Key and Client¶

Get a API_KEY. More on this at in the Flywheel SDK doc here.

In [ ]:
API_KEY = getpass('Enter API_KEY here: ')

Instantiate the Flywheel API client

In [ ]:
fw = flywheel.Client(API_KEY if 'API_KEY' in locals() else os.environ.get('FW_KEY'))

Show Flywheel logging information

In [ ]:
log.info('You are now logged in as %s to %s', fw.get_current_user()['email'], fw.get_config()['site']['api_url'])

Constants¶

In [ ]:
PROJECT_LABEL = input('Enter your project label here: ')

Requirements¶

Before starting off, we want to check your permission on the Flywheel Instance in order to proceed in this notebook.

In [ ]:
min_reqs = {
    "site": "user",
    "group": "ro",
    "project": ['containers_modify_metadata','files_view_contents']
    
}
Tip: Group ID and Project Label can be found on top of the Project page on the Flywheel Instance as shown in the snippet below.
In [ ]:
GROUP_ID = input('Please enter the Group ID that you will use in this notebook: ')

check_user_permission will return True if both the group and project meet the minimum requirement, else a compatible list will be printed.

In [ ]:
check_user_permission(fw, min_reqs, group=GROUP_ID, project=PROJECT_LABEL)

Helper functions¶

In [ ]:
def update_moco_acq_label(acq_list):
    """Return `acq_list` with the updated MoCo label.

    The `acq_label` is updated if the `acq_label` starts with ('MoCo') or ends with ('_moco') as well as the previous `acq_label` item in the `acq_list`.

    Args:
       acq_list : List of dictionaries that consists of 
                  each acquisition label (`acq_label`), 
                  acquisition id (`acq_id`) ,and 
                  series number (`series_number`) 
                  on one Session

    Returns:
       (list): `acq_list` with updated label name if label name is modified, NaN otherwise.

    """

    for index, acq in enumerate(acq_list):

    acq_label = acq['acq_label']

    if acq_label.startswith('MoCo') or acq_label.endswith('_moco'):
        # Get the original scan label (item before MoCo series is generated)
        scan_acq_label = acq_list[index - 1]['acq_label']
        scan_acq_id = acq_list[index - 1]['acq_id']
        # modified the original scan label and update on the acquisition container
        new_scan_label = modify_acq_name(scan_acq_label)
        acq_list[index-1]['updated_label'] = new_scan_label
        update_acq_label(scan_acq_id, new_scan_label)
        # Rename the label for MoCo series with modified acq_label that ends with '_moco' prefix
        moco_label = new_scan_label + '_moco'
        acq_list[index]['updated_label'] = moco_label
        update_acq_label(acq['acq_id'], moco_label)


    return acq_list

def modify_acq_name(acq_name):
    """Get the modified Acquisition Label Name.

    Note: 
        This function can be modified accordingly based on how you would like to structure your acquisition label.

    Args:
        acq_name (str) : Name for the Acquisition

    Returns:
      (str): `acq_name` with updated value if they matches the prefix
    """
    
    separator = '_'

    # In this use case, if the label ends either with `_0` or `_1` will be identified as run 2 (r2) and run 3 (r3) respectively
    # if they do not match either of the prefix above, we will assume it ie run 1 (r1)
    if acq_name.endswith('_0'):
        split_name = acq_name.replace('0', 'r2').split(separator)
        acq_name = split_name[0] + '_' + split_name[2]
    elif acq_name.endswith('_1'):
        split_name = acq_name.replace('_1', '_r3').split(separator)
        acq_name = split_name[0] + '_' + split_name[2]
    else:
        acq_name = acq_name + '_r1'
    
    return acq_name


def update_acq_label(acq_id, new_label):
    """Update the acquisition label on the project container.

    Args:
        acq_id (str) : Acquisition ID
        new_label (str) : Updated Acquisition Label

    """
    new_acq_object = flywheel.models.Acquisition(label = new_label)
    # modifying the acquisition label here 
    fw.modify_acquisition(acquisition_id= acq_id, body = new_acq_object)

Main script¶

Here we will be iterating through each sessions and respective acquisitions, within the project container, to get the series_number, acq_label and the acq_id. Then we will call the update_moco_acq_label function to update the acquisition label.

In [ ]:
# Get the project container
project = fw.projects.find_first(f'label={PROJECT_LABEL}')
# Generate an empty dataframe to be append later
df = pd.DataFrame()

for session in project.sessions.iter():
    all_acq_list = []
    for acquisition in session.acquisitions.iter():
        # reload is necessary to load the entire acquisition
        acquisition = acquisition.reload()

        for file in acquisition.files:
            if file['type'] == 'dicom':
                item = {
                    'series_number': file.info.get('SeriesNumber'),
                    'acq_label': acquisition.label,
                    'acq_id': acquisition.id
                }
                all_acq_list.append(item)
    
    # sort the list by their series number that is generated from the scanner
    all_acq_list = sorted(all_acq_list, key=lambda k: k['series_number'])

    acq_list = update_moco_acq_label(all_acq_list)
    # to vizualize what has been modified in a tableview.
    df = df.append(acq_list, ignore_index=True)

Display Updated Acquisition Label¶

In [ ]:
df.iloc[:, [0,1,3]].head(20)