SenteraLLC / QASM

QASM is a single-serve web application that runs using React and Electron, with the ability to run customizable QA or labeling jobs from a local host, a packaged windows .exe, or a statically hosted S3 website.
http://qasm-demo-frontend.s3-website-us-east-1.amazonaws.com/
2 stars 1 forks source link
annotation-tool css desktop-application electron electron-builder frontend javascript labeling labeling-tool qa react static-site static-site-generator windows

Getting Started with QASM

Welcome to the Quality Assurance State Machine (QASM)! QASM is a single-serve web application that runs using React and Electron, with the ability to run customizable QA jobs from a local host, a packaged windows .exe, or a statically hosted S3 website.

Demo

A demo of the app can be found here. The configuration used to generate the demo is found in react-frontend/default-config.json.

Installation

1) Navigate to the frontend and install necessary packages

    >> cd react-frontend
    >> npm install

2) The main app entrypoint is the python script react-frontend/QASM.py. This script will launch the Electron app and serve the React app. It does require some python dependencies, which can be installed using:

    >> pip install -r requirements.txt

3) The app can be run locally using

    >> npm run qasm

This will launch an app based on the specifications found in ``react-frontend/config.json`` if present, else it will copy ``react-frontend/default-config.json``, load it, and save it to ``react-frontend/config.json``. 

Note that for any backend functionality that requires AWS (ie running in `"s3"` mode), you will need to have AWS credentials set up on your machine. See [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) for more information. 

Once your AWS credentials are set, you will need to deploy the backend to AWS using [Terraform](#terraform).

4) To create a Windows executable of the current configuration, run

    >> npm run qasm-build

Which will deposit the executable in ``react-frontend/dist``. Note that this will only work on Windows machines.

5) To run using a specific configuration file, use

    >> npm run qasm -- --config_path <path/to/config.json>

If encountering the error: POST http://localhost:3000/undefinedopen_dir 431 (Request Header Fields Too Large) when launching, ensure that the react-frontend/.env.development and react-frontend/.env.production files have been generated locally by running:

    >> terraform workspace select dev
    >> terraform apply
    >> terraform workspace select prod
    >> terraform apply

Note that this will also apply any changes made to the terraform code, so be sure to double check that you are not accidentily destroying any resources.

Usage

QASM is a single-serve web application that can be run locally, as a packaged windows .exe, or as a statically hosted S3 website. The difference between these options is as follows:

Deployment Options

Local

Running locally will launch the Electron app and serve the React app from the local machine. This is useful for development and testing, but is not recommended for production use. Not that running locally is different then running in local mode. Running locally will launch the Electron app and serve the React app from the local machine, but the React app will still be configured to run in either s3 or local mode, which refers to which backend framework will be used in the app. See Configuration for more details.

Windows .exe

After building a Windows .exe following the instructions in Installation, the app can be run by simply double clicking the .exe file. This will preform a one-time installation and run QASM as a Windows application, after which it can be run by double clicking the application shortcut or however you prefer to run Windows applications. Note that the "name" field in the configuration file will be used as the name of the application, so configurations with the exact same name with overwrite each other, which is intended behavior to enable versioning without creating spurious applications. If different installations are needed, simply change the "name" field in the configuration file.

S3 Static Website

The S3 Static Website deployment option is an easy way to setup a public website and url from which anyone can access a QASM job. In the config field "static_site_bucket", put the desired name of an s3 bucket that will host the QASM app. Run npm run qasm-push, which will create and setup an S3 bucket with the name you specified, build the app, and upload the contents of the react-frontend/build folder to the bucket. Note that the bucket created by this process will be public, so anyone with the url will be able to access the app. The website URL will be printed to the console if the process is successful.

Alternatively, see the AWS docs for a more detailed explanation of how to manually setup a static website on S3. Follow the steps in the link provided above to setup an S3 bucket and configure it to host a static website. Once the bucket is configured, you can build the app by running npm run qasm-build. Then upload the build files manually via the AWS console (just upload the contents of react-frontend/build to the bucket), or via the AWS CLI using

    >> aws s3 sync react-frontend/build s3://<bucket_name_here>

***Note that this deployment method currently only supports "app": "s3" mode.

Backend Options

Local

An app configured with "app": "local" will run using files on the local machine. QASM jobs that require images or other inputs will prompt the user to select files using the Windows File Explorer. Note that this option is currently not supported for S3 static websites.

S3

An app configured with "app": "s3" will run using files on S3. QASM jobs that require images or other inputs will prompt the user to select files using the S3 Browser component. The S3 Browser component will allow the user to select files from any S3 bucket that they have access to. The S3 Browser component will also allow the user to upload files to the selected bucket. Note that the S3 Browser component will only allow the user to select files from a single bucket at a time, which is specified in the config file under the "bucket" field.

Configuration

react-frontend/config.json expects the following fields:

Keybinds

Certain components support user-defined keybinds for set operations. Keybinds are stored in the following format: {<string keybind_name>: <string keybind> or Array[<string keybind1>, <string keybind2>], ...}

Example:

{   
    ...,
    "save_labels_keybind": ["ctrlKey", "s"],
    "toggle_image_layer_keybind": "b",
    "next_row_keybind": ["ctrlKey", "ArrowDown"],
    "prev_row_keybind": ["ctrlKey", "ArrowUp"],
}

The "control", "shift", and "alt" keys are represented by "ctrlKey", "shiftKey", and "altKey", since that is how they are represented in the keydown event. See here for more details. Providing an array of keybinds requires all keys to be pressed simultaneously, and thus is only supported for the "control", "shift", and "alt" keys and any single other key, eg ["ctrlKey", "shiftKey", "s"].

Terraform

Terraform automatically takes our lambda code and deploys it to all the necessary AWS services (Lambda, API Gateway, IAM, etc) to allow our serverless applications to run.

Install Terraform (https://learn.hashicorp.com/tutorials/terraform/install-cli)

Start a new project or connect to an existing Terraform project

    >> cd terraform-backend
    >> terraform init

Note: Since S3 buckets are globally scoped (no two buckets can share the same name), you may need to change the bucket name in the terraform code. To do this, open the file terraform-backend/main.tf and change references to the bucket name qasm-lambdas to something unique.

To prevent developer testing from interfering with active users, we can utilize terraform 'workspaces' to keep development and production environments seperate.

To work in the development workspace, use

    >> terraform workspace select dev

And for production,

    >> terraform workspace select prod

Both workspaces use the same source code, but the prefix "${terraform.workspace}-" is added to every unique resource, so that the two workspaces deploy to seperate 'dev' and 'prod' AWS resources. When creating new resouces, be sure to add this prefix so as to avoid hidden dependencies.

To deploy changes to a Terraform project, use

    >> terraform apply

You will be shown a summary of the changes that terraform will be applying, so be sure to double check that (a) you are on the desired workspace and (b) that you aren't accidentily destroying unexpected resources. To check what workspace you are in, you can use

    >> terraform workspace list

Once changes have been tested in development and are ready to be applied to the production resources, these changes are applied by simply switching workspaces and running the apply command.

    >> terraform workspace select prod
    >> terraform apply

Development

The core Electron logic is found in react-frontend/public/electron.js. This file will launch the Electron app and load the React app. In order to initialize the local backend, the functions found in react-frontend/public/electron_utils.js are automatically exposed to the Electron runtime via the logic found in react-frontend/public/preload.js.

The main React entrypoint is react-frontend/src/index.js. This file will load the config file and use it to initialize the React app in the configured mode (ie local or s3). The two different modes are setup in react-frontend/src/QASM/QASM.js. The local mode will load the React app using the local backend (the functions defined in react-frontend/public/electron_utils.js), while the s3 mode will load the React app using the AWS backend (the functions found in react-frontend/src/QASM/lambda_handlers.js). As long as any new functions are added to the list of function_handlers within lambda_handlers.js, they will be automatically be exposed to the React app.

When adding any backend functionality, in order for it to be compatible with both modes, the function must be defined in both electron_utils.js and lambda_handlers.js. The decision of which backend to use is made in QASM.js based on the config file, and so within the React components, both backends are accessed via this.QASM.call_backend(window, <function_name_here>, <function_args_here>). This function will automatically call the correct backend based on the configured mode.

Local Backend

As long as any new functions are added to the list of exports.function_handlers within electron_utils.js, they will be automatically be exposed to the Electron runtime in local mode. By addition the function logic to electron_utils.js, the function will be available within any React component via this.QASM.call_backend(window, <function_name_here>, <function_args_here>). Since the backend is running locally, the function will be called directly, and there is access to the local Windows file system via the fs module.

AWS Backend

In order to add a new function to the AWS backend, the function must be added to the list of exports.function_handlers within lambda_handlers.js. This will automatically expose the function to the React app in s3 mode. Since the backend is running on AWS, the function will be called via an API call to AWS API Gateway. The function will access the AWS S3 bucket that is configured in the config.json file instead of the local file system.

Adding s3 compatibility is comprised of two steps: (1) adding the function to lambda_handlers.js and (2) adding the function to the AWS API Gateway via Terraform.

1) The functions in lambda_handlers.js ultimately are just intermediate steps that call some AWS API Gateway endpoint that points to a AWS Lambda function defined in terraform-backend/lambdas/. Common operations involved opening the S3 Browser component to allow the user to make some selection, and/or parsing the input arguments and passing them to the AWS Lambda function. The AWS Lambda function will then perform the desired operation and return the result.

2) The AWS Lambda function is defined in terraform-backend/lambdas/. The function is defined in one of the python files (for example, s3_browser.py) as a single python function function_name_here(event, context). The arguments that are passed in from the lambda_handlers.js function are available in the event variable. See some of the functions in s3_browser.py for examples of how to read from the event, handle the AWS logic, and return the result.

The python logic is then passed into the AWS Lambda function via Terraform. When adding a new function, add a handler block to the list of lambda_defs in terraform-backend/lambdas.tf. The handler block should look like:

        { 
            base_name = "function-name-here"
            handler = "python_filename_here.function_name_here"
        },

This file will automatically take the python code and deploy it to the AWS Lambda function.

React Components

The React components are found in react-frontend/src/Components/. Each component is defined in a file <component_name>.js. The component is then imported into react-frontend/src/App.js and added to the list of components. To enable a component to be added to the config.json, the component must be added (1) to the list of COMPONENT_KEYS in react-frontend/src/App.js as well as (2) the list of QASM_COMPONENTS in QASM.py.