diff --git a/.github/workflows/gait_analysis.yml b/.github/workflows/gait_analysis.yml new file mode 100644 index 0000000..5f19586 --- /dev/null +++ b/.github/workflows/gait_analysis.yml @@ -0,0 +1,74 @@ +# This workflow will build and push a new container image to Amazon ECR, +# and then will deploy a new task definition to Amazon ECS, on every push +# to the master branch. +# +# To use this workflow, you will need to complete the following set-up steps: +# +# 1. Create an ECR repository to store your images. +# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`. +# Replace the value of `ECR_REPOSITORY` in the workflow below with your repository's name. +# Replace the value of `aws-region` in the workflow below with your repository's region. +# +# 2. Create an ECS task definition, an ECS cluster, and an ECS service. +# For example, follow the Getting Started guide on the ECS console: +# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun +# Replace the values for `service` and `cluster` in the workflow below with your service and cluster names. +# +# 3. Store your ECS task definition as a JSON file in your repository. +# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`. +# Replace the value of `task-definition` in the workflow below with your JSON file's name. +# Replace the value of `container-name` in the workflow below with the name of the container +# in the `containerDefinitions` section of the task definition. +# +# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. +# See the documentation for each action used below for the recommended IAM policies for this IAM user, +# and best practices on handling the access key credentials. + +on: + push: + branches: + - main + paths: + - 'gait_analysis/**' + workflow_dispatch: + +name: PROD Analysis "gait analysis" build & deployment + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v1 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-west-2 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + IMAGE_TAG: latest # ${{ github.sha }} + run: | + # Build a docker container and + # push it to ECR so that it can + # be deployed to ECS. + cd gait_analysis + docker build -f Dockerfile -t 660440363484.dkr.ecr.us-west-2.amazonaws.com/opencap-analysis/gait_analysis:$IMAGE_TAG . + docker push 660440363484.dkr.ecr.us-west-2.amazonaws.com/opencap-analysis/gait_analysis:$IMAGE_TAG + echo "::set-output name=image::660440363484.dkr.ecr.us-west-2.amazonaws.com/opencap-analysis/gait_analysis:$IMAGE_TAG" + + - name: Force deployment + env: + IMAGE_TAG: latest # ${{ github.sha }} + run: | + aws lambda update-function-code --function-name gait-analysis --image-uri 660440363484.dkr.ecr.us-west-2.amazonaws.com/opencap-analysis/gait_analysis:$IMAGE_TAG | jq 'if .Environment.Variables.API_TOKEN? then .Environment.Variables.API_TOKEN = "REDACTED" else . end' diff --git a/.github/workflows/max_centerofmass_vpos.yml b/.github/workflows/max_centerofmass_vpos.yml index 4454b14..2eb7773 100644 --- a/.github/workflows/max_centerofmass_vpos.yml +++ b/.github/workflows/max_centerofmass_vpos.yml @@ -27,7 +27,7 @@ on: push: branches: - - dev + - main paths: - 'max_centerofmass_vpos/**' workflow_dispatch: diff --git a/gait_analysis/function/handler.py b/gait_analysis/function/handler.py index 8fcd9a5..f2cf37e 100644 --- a/gait_analysis/function/handler.py +++ b/gait_analysis/function/handler.py @@ -77,7 +77,7 @@ def handler(event, context): 'step_width': 'Step width (m)', 'cadence': 'Cadence (steps/min)', # 'single_support_time': 'Single support time (% gait cycle)', - 'double_support_time': 'Double support time (% gait cycle)', + 'double_support_time': 'Double support (% gait cycle)', 'step_length_symmetry': 'Step length symmetry (%, R/L)'} # %% Process data. @@ -98,25 +98,32 @@ def handler(event, context): # Compute scalars. gait_scalars = gait[last_leg].compute_scalars(scalar_names) + gait_scalars['gait_speed']['decimal'] = 2 + gait_scalars['step_width']['decimal'] = 2 + gait_scalars['stride_length']['decimal'] = 2 + gait_scalars['cadence']['decimal'] = 1 + gait_scalars['double_support_time']['decimal'] = 1 + gait_scalars['step_length_symmetry']['decimal'] = 1 + # %% Thresholds. metadataPath = os.path.join(sessionDir, 'sessionMetadata.yaml') metadata = import_metadata(metadataPath) subject_height = metadata['height_m'] gait_speed_threshold = 67/60 step_width_threshold = 0.14 - stride_length_threshold = subject_height * .57 + stride_length_threshold = subject_height * .45 cadence_threshold = 100 # single_support_time_threshold = 65 double_support_time_threshold = 35 step_length_symmetry_threshold = [90,110] thresholds = { - 'gait_speed': gait_speed_threshold, - 'step_width': step_width_threshold, - 'stride_length': stride_length_threshold, - 'cadence': cadence_threshold, + 'gait_speed': {'value': gait_speed_threshold, 'decimal': 2}, + 'step_width': {'value': step_width_threshold, 'decimal': 2}, + 'stride_length': {'value': stride_length_threshold, 'decimal': 2}, + 'cadence': {'value': cadence_threshold, 'decimal': 1}, # 'single_support_time': single_support_time_threshold, - 'double_support_time': double_support_time_threshold, - 'step_length_symmetry': step_length_symmetry_threshold} + 'double_support_time': {'value': double_support_time_threshold, 'decimal': 1}, + 'step_length_symmetry': {'value': step_length_symmetry_threshold, 'decimal': 1}} # Whether below-threshold values should be colored in red (default) or green (reverse). scalar_reverse_colors = ['step_width', 'double_support_time'] # Whether should be red-green-red plot @@ -136,24 +143,24 @@ def handler(event, context): metrics_out = {} for scalar_name in scalar_names: metrics_out[scalar_name] = {} - vertical_values = np.round(gait_scalars[scalar_name]['value'], 2) + vertical_values = np.round(gait_scalars[scalar_name]['value'], gait_scalars[scalar_name]['decimal']) metrics_out[scalar_name]['label'] = scalar_labels[scalar_name] metrics_out[scalar_name]['value'] = vertical_values if scalar_name in scalar_reverse_colors: # Margin zone (orange) is 10% above threshold. metrics_out[scalar_name]['colors'] = ["green", "yellow", "red"] - metrics_out[scalar_name]['min_limit'] = float(np.round(thresholds[scalar_name],2)) - metrics_out[scalar_name]['max_limit'] = float(np.round(1.10*thresholds[scalar_name],2)) + metrics_out[scalar_name]['min_limit'] = float(np.round(thresholds[scalar_name]['value'],thresholds[scalar_name]['decimal'])) + metrics_out[scalar_name]['max_limit'] = float(np.round(1.10*thresholds[scalar_name]['value'],thresholds[scalar_name]['decimal'])) elif scalar_name in scalar_centered: # Red, green, red metrics_out[scalar_name]['colors'] = ["red", "green", "red"] - metrics_out[scalar_name]['min_limit'] = float(np.round(thresholds[scalar_name][0],2)) - metrics_out[scalar_name]['max_limit'] = float(np.round(thresholds[scalar_name][1],2)) + metrics_out[scalar_name]['min_limit'] = float(np.round(thresholds[scalar_name]['value'][0],thresholds[scalar_name]['decimal'])) + metrics_out[scalar_name]['max_limit'] = float(np.round(thresholds[scalar_name]['value'][1],thresholds[scalar_name]['decimal'])) else: # Margin zone (orange) is 10% below threshold. metrics_out[scalar_name]['colors'] = ["red", "yellow", "green"] - metrics_out[scalar_name]['min_limit'] = float(np.round(0.90*thresholds[scalar_name],2)) - metrics_out[scalar_name]['max_limit'] = float(np.round(thresholds[scalar_name],2)) + metrics_out[scalar_name]['min_limit'] = float(np.round(0.90*thresholds[scalar_name]['value'],thresholds[scalar_name]['decimal'])) + metrics_out[scalar_name]['max_limit'] = float(np.round(thresholds[scalar_name]['value'],thresholds[scalar_name]['decimal'])) # Datasets colNames = gait[last_leg].coordinateValues.columns @@ -181,4 +188,4 @@ def handler(event, context): 'statusCode': 200, 'headers': {'Content-Type': 'application/json'}, 'body': results - } \ No newline at end of file + }