Skip to content

Logo Logo

Gear Building Tutorial Part 2c: The Manifest and SDK

Introduction

The Manifest

The manifest is going to provide Flywheel with all the information it needs to run the gear.  This includes metadata such as the gear's author, the gear version, any runtime environment variables, and so on. It also includes any inputs and configuration settings, which the user will have a chance to enter and modify when the gear is run in the Flywheel UI.  

The manifest is written in JSON file format and follows the standard {"key": "value"} syntax.  To learn more about the json format, you can read about their schema.  The manifest must always be named manifest.json.  When the user enters all the input/config settings the manifest requires, flywheel creates a condensed version of this file called config.json, which has all the information necessary to run the gear.

Instruction Steps

Gear Manifest Template

Here is a friendly walkthrough of some of the keys found in the manifest file.  We recommend you look at the full gear specifications. An example of common manifest values is presented here, with some descriptions of what each key represents:

{
// Computer-friendly name; unique for your organization
"name": "example-gear",

// Human-friendly name; displayed in user interface
"label": "Example Gear",

// A brief description of the gear's purpose; ideally 1-4 sentences
"description": "A gear that performs a task.",

// Human-friendly version identifier
"version": "1.0",

// The author of this gear or algorithm.
"author": "Flywheel",

// (Optional) the maintainer, which may be distinct from the algorithm author.
// Can be the same as the author field if both roles were filled by the same individual.
"maintainer": "Nathaniel Kofalt",

// (Optional) Any citations you wish to add.
"cite": "",

// Must be an OSI-approved SPDX license string or 'Other'. Ref: https://spdx.org/licenses.
"license": "Apache-2.0",

// Where to go to learn more about the gear. You can leave this blank.
"url": "http://example.example",

// Where to go for the source code, if applicable. You can leave this blank.
"source": "http://example.example/code",

// (Optional) Environment variables to set for the gear.
"environment": {
 // This passes the string "/flywheel/v0" into the gear.
 // As the environment variable "FLYWHEEL".
 "FLYWHEEL":"/flywheel/v0"
 },

// Custom tags, in which the user can add any values they would like.
// Some of these values are required, such as these gear builder tags.
"custom": {

 "gear-builder": {
 // This determines under which category the gear appears. 
 // Options are analysis, converter, utility, classifier, or qa 
 "category": "analysis",

 // This is the name of the docker image you'll use in your gear.
 "image": "flywheel/core:latest"
 },

 // The flywheel key can provide useful information to the flywheel UI.
 "flywheel": {

 // The "suite" key categorizes your gear in the dropdown menu.
 // Making gears for different functions easy to identify and distinguish.
 "suite": "My Lab's Suite"
 }
},

// (Optional) Command to execute. Ran inside a bash shell.
"command": "python script.py"

// Options that the gear can use.
"config": {

 // A name for this option to show in the user interface.
 "speed": {
 "type": "integer",
 // (Optional) json-schema syntax to provide further guidance.
 "minimum": 0,
 "maximum": 3,
 "description": "How fast do you want the gear to run? Choose 0-3."
 },

 "coordinates": {
 "type": "array",
 "items": {
 "type": "number",
 "minItems": 3,
 "maxItems": 3,
 },
 "description": "A set of 3D coordinates."
 },
},

// Inputs that the gear consumes.
"inputs": {
 // A label - describes one of the inputs. Used by the user interface and by the run script.
 "dicom": {
 // Specifies that the input is a single file. For now, it's the only type.
 "base": "file",
 // (Optional) json-schema syntax to provide further guidance.
 "type": { "enum": [ "dicom" ] },
 "description": "Any dicom file."
 },

Note that the lines above that start with "//" are comments and that these are not actually allowed in .json files.

This looks complicated, but let's break it down in the context of our Hello World gear.  To begin, we'll create a file called manifest.json. This section does require some familiarity with the JSON format.  In the future, a manifest template can be used to put in many of these fields for you, only leaving you to edit the information.  Below is the first part of the manifest file we will create, with some values left blank because we haven't covered them yet. We'll come back to these.

part2-manifest-metadata.png

Let's break this down piece by piece.

Gear Metadata (red)

The gear metadata, highlighted in red, stores information that Flywheel uses to label the gear and track any changes.  We can begin entering these same keys into our manifest file, and simply modify the values assigned to each key. Below is a description of each key, and how it's used in Flywheel.

  • name: a machine-friendly gear name ("hello-world").  This name must only contain lower-case letters, numbers, and dashes (-).
  • label: a human friendly label ("Hello World: My First Gear").  This will show up in the gear selection menu in the Flywheel UI.
  • description: a couple sentences to describe the gear.  This also shows up in the user UI
  • version: This tracks the development of the gear, and if there have been and updates/patches
  • author: Who wrote the gear
  • maintainer: Who provides support for the gear
  • cite: There's no citation for this gear, so that is left blank.  Sometimes it's good to leave a key blank rather than omit it, so that we know it wasn't simply "forgotten" by accident.
  • license:  Usually "MIT" or "Apache".  The license with which users may use the gear.
  • url: a link to the gear's website, maybe providing documentation.
  • source:  A direct link to the gear's source code, usually a GitHub repository.

Environment variables (orange)

The next section, highlighted in orange, is not metadata, it's actually an environment input that will go into the gear's runtime environment.  Any key:value pairs in the "environment" section will be passed in with the "key" being the variable name, and the "value" being the variable value.  For example, the key:value pair used above are equivalent to executing the following command in the gear's runtime:

export FLYWHEEL="/flywheel/v0"

This can be used to set up certain environment variables directly from the manifest.

Custom Data (yellow)

The custom data section has some required information you need to enter. The "gear-builder" section has two keys that your gear won’t run without:

  • Category:  Here, we tell flywheel if this gear is a utility gear, or an analysis gear. This also determines where the gear appears in the Flywheel UI. The options are "utility", "analysis", "converter", "classifier", or "qa".

Utility gears are QA and conversion gears, and their output is stored in the acquisition view.

In contrast, Analysis gears result in a new analysis for the subject, which has its own view with metadata, including the gear input and output. For analysis gears use "category": "analysis".

For our sample gear you can choose any type.

  • Image: Here, we tell the gear which docker image to use to run the gear on.  This should point to an image on Dockerhub, which will be copied into the Flywheel instance upon gear upload.  

Note: We will come back to this "image" after we cover Dockerfiles.

User defined input

Now we'll look at the next portion of the manifest file: the gear inputs/config settings.  Following the template above, we know that we need an input for the message.txt file, and two config settings: one for your name, and one for how many times to say "Hello".  

The result will look something like this:

part2-manifest-Inputs.png

Gear inputs (green)

The gear inputs key (in green) indicates to Flywheel that the user will provide a file to use in the gear.  We name our input message_file, and give it the following properties:

  • description: A short description of the file input that the user can see in the UI
  • base: Describing what kind of input it is.  Presently, this can only be "file"
  • type: Type will contain a list of possible file types that this input can be.  This simply allows the flywheel UI to look for this type of file, and suggest it when the user goes to select an input. It also enforces that the selected file is of the specified file type.  See our full list of flywheel-supported file types. If the file type is not in the supported list, you can just omit the "type" key.  Flywheel will still allow the subject to choose a file for the input, it will just be unable to suggest default files or enforce file type.

Gear Config (blue)

The gear config represents any other input settings that are not files.  For this gear, we need two more pieces of input. Our name and the number of times to say hello.  because of this, we'll create two keys inside the config.  

First, let's look at our config key my_name, and give it give the following properties:

  • description: Just like the inputs, config settings get a short description that the user will be able to view in the UI.  This should give someone who's unfamiliar with the gear an idea of how to properly set these values.
  • type: Like the input type, this determines what kind of values will be accepted for this config setting.  Unlike the input file, the types for config settings can either be string, integer, number, or boolean (outlined in the flywheel gear spec).  Since the value my_name is a string, we set the type to "string"

Now let's consider the next config value, num_rep, the number of times to say hello.  It has the following properties:

  • description: same as above
  • default: (optional) a default value to have in case the user doesn't want to bother setting it manually.  We'll set this to "1" so that it only says "Hello" once
  • minimum: (optional) a minimum value.  We'll set this to "0" since you can't say "hello" a negative number of times
  • maximum: (optional) a maximum value.  We'll set this to "10" so that you can't overload the system with 9 million "hello's".
  • type: As above, we will restrict the kind of input allowed.  Since you can only say hello an integer number of times, we'll set this to "integer"

Gear Command (purple)

This is the command that the gear will run when we execute it.  In this case, we just want it to run our run.py file as a python script, so we set it as:

"command": "python run.py"

The Full Manifest

The full manifest can be found here.Your full directory structure should now look like this:

MyFirstGear  
|- run  
|- message.txt  
|- manifest.json

The Run Script and the Manifest

The manifest will be used to generate a UI in which users can pass in values to the gear.  In this case, the user will have three values to fill:

The input:

  • message_file

And two config settings

  • my_name
  • num_reps

In the UI, these inputs look like this:

GearUI.png

The first tab (Left image) is the inputs tab, and you can see an input labeled message_file.  This label comes directly from the manifest. Hovering over the information icon next to the input field displays the description entered the manifest.  As you can see, input names and descriptions placed in the manifest are actually important, and should be well-thought-out.

The next tab (config, right side) has the two config settings we need.  Like the input, the config names come from the manifest, and the description we set for them can be viewed by hovering over the information icon

Once the user fills out these values and starts the gear, the input values will be saved in a config.json file.  Flywheel automatically creates this file and fills it with the input values and other relevant information needed to run the gear.

The Flywheel SDK

The config.json file has a wealth of gear information that most run scripts will need access to.  While it's certainly possible to parse this file yourself, the Flywheel SDK can automatically read this information in a user-friendly way.  We'll show you how to use the SDK to pull in these values.

More information on the Flywheel Python SDK can be found on its documentation page.  The SDK is a powerful and extensive tool.  We'll only cover some very basic functionality here, but it will be enough to get you going for most of your basic gear development!

Modify the Run Script for Use in a Gear

First, let's modify the first few lines of our code by importing the Flywheel SDK, which we installed in part 1 of the gear building tutorial:

  1. Open the run.py script
  2. Modify the first lines as follows:
#!/usr/bin/env python3  
import flywheel

Now from the SDK, we'll pull in what's called the gear context. The gear context includes information about the system and environment, the Flywheel instance it's running on, and the inputs and outputs of the gear itself.  In fact, the entire config.json file is read into the gear context as callable values.  

  1. To work with the gear context, and then the config values, add the following python call:
context = flywheel.GearContext()  # Get the gear context  
config = context.config           # from the gear context, get the config settings

Gear context.config

The context Python object contains representation of the entire config.json file.  A config.json file has all the inputs you specified in the manifest, plus the values you assigned to them.  An example of a config.json file can be seen in our gear spec documentation.  For example, according to the gear spec linked above, the config.json file has the following fields for an example gear with two config setting (speed and coordinates), and two inputs (dicom and matlab_license_code):

"config": {  
    "speed": 2,  
   "coordinates": [1, 2, 3]  
},  
"inputs" : {  
    "dicom" : {  
        "base" : "file",  
        "hierarchy" : {  
            "type" : "acquisition",  
            "id" : "5988d38b3b49ee001bde0853"  
        },  
        "location" : {  
            "path" : "/flywheel/v0/input/dicom/example.dcm",  
            "name" : "example.dcm"  
        },  

        "object" : {  
            "info" : {},  
            "mimetype" : "application/octet-stream",  
            "tags" : [],  
            "measurements" : [],  
            "type" : "dicom",  
            "modality" : null,  
            "size" : 2913379  
        }  
    },  
    "matlab_license_code": {  
        "base": "context",  
        "found": true,  
        "value": "ABC"  
<    }  
}

Each of these fields is accessible through the config.json as dictionaries.  Inputs are accessed using the get_input() call, while config values are accessed through the config dictionary object.  For example, to see the value of the config option speed, you could call:

context.config['speed']

In your gear, these values will take on the name that you gave them in the manifest.  For the example used here, our config settings would be "my_name" and "num_reps", and would be callable as follows:

context.config['my_name']  
context.config['num_rep']

Moving on the gear inputs, if you want to see the size of the input file in the example above, make the following call:

size = context.get_input('input_name1')['object']['size']

Similarly, you can find the file name and path as follows:

name = context.get\_input('input\_name1')['location']['name']  
path = context.get\_input('input\_name1')['location']['path']

As you can see, each level of the .json format is represented as a Python dictionary.

However, Flywheel has anticipated the frequent use of certain fields in the gear config, and so custom functions to retrieve those values have been created.  Input file path is one of these fields, and so a simplified call can be made to retrieve these values:

path = context.get_input_path('input_name1')

The context python object is described in our SDK core source code, which lists all internal functions to the context object.

One small drawback about coding with the gear context is that debugging must now be done with the Flywheel CLI using the fw gear local command.  We'll cover that in a later section.  For now, we'll just use the SDK in the most basic way to get our inputs.

Modify Your Run Script to Use Config Settings

Since our variable values are now all coming from the config file, we can replace the lines where we set our variable values manually with the following:

## Load in values from the gear configuration  
my_name = config['my_name']  
num_rep = config['num_rep']  

## Load in paths to input files for the gear  
message_file = context.get_input_path('message_file')

Final Run Script

That's all the extra modification we need to get our run script to run with flywheel as a gear!  Your final run.py script should look like this:

#!/usr/bin/env python3  
# The Shebang tell the computer what to call the file with when it runs.  
# For more info:https://bash.cyberciti.biz/guide/Shebang.  

import flywheel  

context = flywheel.GearContext()  # Get the gear context.  
config = context.config           # from the gear context, get the config settings.  

## Load in values from the gear configuration.  
my\_name = config['my\_name']  
num\_rep = config['num\_rep']  

## Load in paths to input files for the gear.  
message\_file = context.get\_input\_path('message\_file')  

while (num\_rep > 0):                      # While the num\_rep variable is greater than zero.  
    print("Hello, {}!".format(my\_name))   # Print "Hello Name!" every loop.  
    num\_rep -= 1                          # Decrease the num\_rep variable by one.  

# Now read the custom message:  
message\_file = open(custom\_message,'r')   # Open the file with the intent to read.  
print('\n')                               # Print a blank line to separate the message from the "hello's".  
print(message\_file.read())                # Read and print the file.

Part 1: Environment Setup

Part 2: Gear Intro

Part 2a: The Flywheel Environment

Part 2b: The Run Script and Logging

Part 2c: The Manifest

Part 2d: The Dockerfile

Part 2e: Testing/Debugging