< Blogs
Terra API

Terra API

March 7, 2024

How to Build an AI Coach with Flask, Terra and ChatGPT

 
Terra is an API that delivers wearable data to your applications via Webhooks. In this article, you will learn how to use the Terra API and the ChatGPT API to create a chat application that acts as a personal AI coach.
 
You will feed health data from a wearable to this chat application and its AI coach will give you real-time health advice, and help you create training programs specific to your goals.
 
We will be using Python and Flask to consume sample health data from the Terra Webhook, store it in a JSON file, and then use the OpenAI API to analyze your health data. You will then create a custom AI coach to answer all your burning questions based on your health level.
 
Here is an image of the web application we are going to build:
 
 

Step 1 - Obtain Your Credentials from The Terra Dashboard

 
To communicate with a wearable through the Terra API, you need the following:
 
 
Your API Key
 
 
Your Dev ID
 
Go to your Terra Dashboard, under Connections, you will find your API Key and Dev ID in the bottom right corner in the API Credentials Popup. They are used in virtually every interaction with the API - keep them safe!
 

Step 2 - Create an MVP Chat App with Flask

 
Create a virtual environment and activate it:
 
Plain Text
Copy
python -m venv env
 
 

Plain Text
Copy
source env/bin/activate
 
 

Install the necessary packages:
 
Plain Text
Copy
pip install Flask Flask-SQLAlchemy terra-python openai
 
 

The terra-python package is a wrapper for the Terra endpoints and models. We'll use it to verify and authenticate incoming webhook data.
 
Next, create a small Flask app file called app.py:
 
Plain Text
Copy
# app.py
import os
import json
import logging
from flask import Flask, render_template, request, redirect, url_for, Response
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from terra.base_client import Terra

logging.basicConfig(level=logging.INFO)
_LOGGER = logging.getLogger("app")

terra = Terra(api_key='<API-KEY>',
              dev_id='<DEV-ID>',
              secret='<SIGNING-SECRET>')

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///chat.db'
db = SQLAlchemy(app)

# ... (rest of the code remains the same)
 
 

Note: Make sure to fill in <API-KEY> and <DEV-ID> with your API key and developer ID. The signing secret will be added later in this article.
 
This code represents a basic Flask web application that connects with the Terra wearable API, and provides a simple chatting feature. Let's break down the key components and functionality:
 
 
Logging Setup:
 
 
Logging is configured with a basic setup, and a logger named _LOGGER is created.
 
 
Terra API Configuration:
 
 
An instance of the Terra class is created with the Terra API key, device ID, and signing secret.
 
 
Flask Setup:
 
 
An instance of the Flask application is created (app).
 
 
The SQLite database for storing chat messages is configured using SQLAlchemy. The database is called chat.db and will be created inside a new instance folder that will be automatically added to your Flask project folder.
 
 
Database Model:
 
 
A Message class is defined, which is an SQLAlchemy model representing a chat message. It includes fields like id, text (the message content), timestamp (message timestamp), and is_user (a boolean indicating whether the message is from the user).
 
 
Routes:
 
 
The application defines two routes:
 
 
/: The main route that handles both GET and POST requests.
 
 
/send: A route specifically for handling POST requests when the user sends a message.
 
 
Index Route (/):
 
 
For GET requests, it retrieves all messages from the database and renders them on the index.html template.
 
 
For POST requests, it processes data received from the Terra webhook, logs the information, responds with a 200 status in case of a health check, and verifies the Terra signature. If the signature is verified, it returns a 200 response for now; otherwise, it returns a 403 response. The verification process will not work for now, as we haven't yet added our Terra signing secret, which will be added later in this article.
 
 
Send Route (/send):
 
 
Handles the user input from the form on the chat box.
 
 
Saves the user's message to the database as a Message object with is_user set to True.
 
 
Generates a server response (a default 'Sorry.. I could not understand your question.' message for now) and saves it to the database with is_user set to False.
 
 
Redirects back to the main page.
 
This code essentially creates a simple chat application where users can input messages, and the server responds.
 
Now, create a templates folder inside your Flask app's directory and create a new index.html file inside it:
 
Plain Text
Copy
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
    <title>Terra AI Coach</title>
</head>

<body>
    <h1>Terra AI Coach</h1>
    <div class="container mt-5">
        <div id="chat-box" class="mb-3">
            {% for message in messages %}
            {% if message.is_user %}
            <div class="alert alert-primary" role="alert">
                {{ message.text }}
            </div>
            {% else %}
            <div class="alert alert-secondary" role="alert">
                {{ message.text | safe }}
            </div>
            {% endif %}
            {% endfor %}
        </div>
        <hr>
        <form action="{{ url_for('send') }}" method="post">
            <textarea class="form-control" name="message" placeholder="Enter your message"
                style="margin-bottom: 10px;"></textarea>
            <button type="submit" class="btn btn-primary">ASK</button>
        </form>
    </div>
</body>

</html>
 
 

This displays all chat messages and a simple input form that allows the user to send messages to the server. This code distinguishes between user messages and server responses using Bootstrap alert classes.
 
Now, create the chat.db database using the Flask Shell:
 
Plain Text
Copy
flask shell
 
 

Plain Text
Copy
>>> from app import db, Message >>> db.create_all() >>> exit()
 
 

Next Run the application on port 8080:
 
Plain Text
Copy
flask --app app run -p 8080
 
 

As Terra cannot send Webhook data to your local development server, you have to expose your server to the Internet using Ngrok. To install it, check out this page.
 
Once you install Ngrok, create an account, then obtain your authentication token from the Ngrok dashboard. Once you get your authtoken, add it to your Ngrok agent using the following command:
 
Plain Text
Copy
ngrok config add-authtoken <TOKEN>
 
 

This will allow you to access some features such as rendering HTML files.
 
Once you set up Ngrok, use it to expose your Flask application that is currently running on port 8080:
 
Plain Text
Copy
ngrok http 8080
 
 

This command will give you a URL under Forwarding. Use your browser to access this URL, you should see your index page.
 
You can test the application by sending a test message. You should receive the default 'Sorry.. I could not understand your question.' message
 
Note: Copy your Ngrok URL. This is your Server URL, and you will use it to connect to a Terra Webhook in the next step.
 

Step 3 - Connect a Terra Webhook with Your Server

 
You will now connect a test wearable with your Flask app.
 
Note: We will use FitBit in this demo.
 
Go to your Terra Dashboard, then in Connections. Under Sources, click Add Sources, and select Fitbit, then Save.
 
Next, under Destinations click Add Destination, then select Webhook, then Next.
 
Put your Ngrok Server URL under host.
 
The Connections dashboard should now look like so:
 
 
Now, you need to obtain your Signing secret. Click the three dots to the right of Webhook then Edit.
 
Copy the Signing secret. This is needed to authenticate and verify Webhook requests.
 
To use your Webhook's signing secret, modify the secret parameter in your Terra initiation inside your app.py Flask application:
 
Plain Text
Copy
terra = Terra(api_key='<API-KEY>', dev_id='<DEV-ID>', secret='<PASTE-SIGNING-SECRET-HERE>')
 
 

Once modified, remember to rerun your Flask app:
 
Plain Text
Copy
flask --app app run -p 8080
 
 

Note: Make sure Ngrok is still running. If you've stopped it and restarted it, your Server URL will change, so make sure to edit the Webhook host in your Terra Dashboard and replace the old Ngrok URL with the new one.
 
We will now test the Webhook connection. In the Terra Dashboard, go to Tools > Generate > Select Data Source > Fitbit.
 
Then click on Body, then click Generate test data.
 
Once data is generated. Click Send to Destination.
 
Go back to your Flask server and wait for a few seconds. You should see the following message in the logs:
 
Plain Text
Copy
INFO:app:Received webhook for user <USER-ID> of type body INFO:werkzeug:127.0.0.1 - - [28/Jan/2024 14:15:30] "POST / HTTP/1.1" 200 -
 
 

This means you have successfully connected a Terra Webhook with your Flask application and you are receiving data! Next, you'll modify app.py to retrieve health data and generate an overview based on it.
 

Step 4 - Add an AI Health Overview

 
We will now use the OpenAI API (also known as ChatGPT API) to provide a health overview based on data from Terra.
 
Inside your Flask project folder, open a new file called gpt.py then add the following code to it:
 
Plain Text
Copy
# gpt.py
import os
import json
from openai import OpenAI
from flask import current_app

client = OpenAI(
    api_key=os.environ.get("OPENAI_API_KEY"),
)

def health_overview(question):
    completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": """
             Act as an advanced AI personal health coach.
             You provide short insights into health data from a wearable.
             """},
            {"role": "user",
             "content": f"{question}. Respond with HTML and Bootstrap classes!"
             }
        ]
    )

    return completion.choices[0].message.content
 
 

Here, you set up an OpenAI client, passing your OpenAI API key as an environment variable.
 
In the health_overview() function, you set up a question that essentially sets up ChatGPT as a personal health coach to assist in providing a health overview in HTML, which you'll later display in your index.html template.
 
Now, set your Open API key in an environment variable.
 
For Linux & MacOS:
 
Plain Text
Copy
export OPENAI_API_KEY='your Open API key'
 
 

For Windows:
 
Plain Text
Copy
set OPENAI_API_KEY='your Open API key'
 
 

Next, inside your app.py file, add a gpt import at the top:
 
Plain Text
Copy
# imports ... import gpt
 
 

Then, modify the POST request handler from this:
 
Plain Text
Copy
# The data is verified if verified: return Response(status=200)
 
 

To this:
 
Plain Text
Copy
# The data is verified
if verified:
    health_data = body['data'][0]['measurements_data']['measurements'][0]
    instance_path = app.instance_path
    json_file_path = os.path.join(instance_path, 'health.json')

    if not os.path.exists(json_file_path):
        with open(json_file_path, 'w') as json_file:
            _LOGGER.info('Saving data into a JSON file..')
            json.dump(health_data, json_file)

    return Response(status=201)
 
 

Here you get some measurements data from Terra, and save them in a new file called health.json inside Flask's instance folder. You also respond with an HTTP 201 status instead of 200 to indicate that a new resource was created on the server.
 
Restart your Flask server:
 
Plain Text
Copy
flask --app app run -p 8080
 
 

To test out this new code, go to your Terra Dashboard. Click Tools > Generate > Select Data Source > Fitbit.
 
Then click on Body, then click Generate test data.
 
Once data is generated. Click Send to Destination.
 
Go back to your Flask server and wait for a few seconds. You should see the following message in the logs:
 
Plain Text
Copy
INFO:app:Received webhook for user <USER-ID> of type body INFO:app:Saving data into a JSON file.. INFO:werkzeug:127.0.0.1 - - [28/Jan/2024 14:27:40] "POST / HTTP/1.1" 201 -
 
 

You should also see a new health.json file in the Flask instance folder along with the existing chat.db file.
 
Next, to generate and display a health overview. Open app.py again and modify the GET requests handler from this:
 
Plain Text
Copy
# Handle GET requests
if request.method == 'GET':
    messages = Message.query.order_by(Message.timestamp).all()
    return render_template('index.html', messages=messages)
 
 

To this:
 
Plain Text
Copy
# Handle GET requests
if request.method == 'GET':
    messages = Message.query.order_by(Message.timestamp).all()
    return render_template('index.html', messages=messages)
 
 

The preceding code reads health.json and asks ChatGPT to extract the most important metrics in the given health data. You then pass the generated overview to the index.html template.
 
Next, modify index.html and render the overview variable. To do this, Add the following <div> inside the container and directly above the chat box:
 
Plain Text
Copy
<body>
    <h1>Terra AI Coach</h1>
    <div class="container mt-5">
        <div>
            <h1>Health Overview</h1>
            {{ overview | safe }}
            <hr>
            <h1>Chat with Your AI Coach</h1>
        </div>
        <div id="chat-box" class="mb-3">
            {% for message in messages %}
                {% if message.is_user %}
                    <div class="alert alert-primary" role="alert">
                        {{ message.text }}
                    </div>
                {% else %}
                    <div class="alert alert-secondary" role="alert">
                        {{ message.text | safe }}
                    </div>
                {% endif %}
            {% endfor %}
        </div>
        <hr>
        <form action="{{ url_for('send') }}" method="post">
            <textarea class="form-control" name="message" placeholder="Enter your message" style="margin-bottom: 10px;"></textarea>
            <button type="submit" class="btn btn-primary">ASK</button>
        </form>
    </div>
</body>
 
 

To test this out. Navigate to your app's URL. You should see an overview similar to the following:
 
 
Congrats! The health overview is now finished. In the next step, we'll make the chat box functional.
 

Step 5 - Use AI to Make Your Chat a Personal Fitness Coach

 
At this point, our chat box is not very intelligent, as it only responds with a default hard coded message, indicating its sheer stupidity.
 
In this step, we'll fix this and make our chat box a fully functional AI fitness coach.
 
First, open gpt.py and add the following function to it:
 
Plain Text
Copy
def ask(question):
    instance_path = current_app.instance_path
    json_file_path = os.path.join(instance_path, 'health.json')

    with open(json_file_path, 'r') as json_file:
        health_data = json.load(json_file)

    completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": f"""
             Act as an advanced AI personal health coach.
             Provide short insights into health data from a wearable.
             Here is the data you should base your insights on:
             {health_data}
             """},
            {"role": "user",
             "content": f"{question}. Respond with HTML and Bootstrap classes!"
             }
        ]
    )

    return completion.choices[0].message.content
 
 

This is similar to the previous health_overview() function. The difference is that the ask() function incorporates the health data from health.json as part of its context.
 
To make the chat box UI function, open app.py and modify the following line in the inside the send() function:
 
Plain Text
Copy
coach_response = 'Sorry.. I could not understand your question.'
 
 

Change the default message into a gpt.ask() function call. Using the user's input as prompts:
 
Plain Text
Copy
coach_response = gpt.ask(user_input)
 
 

Finally, restart your Flask server:
 
Plain Text
Copy
flask --app app run -p 8080
 
 

Now, navigate to your index page and ask any questions you have about health and fitness.
 
You should receive clear and helpful AI powered answers:
 
 
With this our AI fitness coach is fully functional.
 

Conclusion and Next Steps

 
Congrats! You've created a personal fitness coach with Terra's API and OpenAI's API.
 
Note: This sample demo application will not cover everything accurately as it is only fed a small part of the overall health data of a user. To improve this application, here are some ideas:
 
 
Retrieve all data and metrics from a wearable using Terra, and feed this data to a custom GPT. This will provide your AI fitness coach with more context on the user's health and training.
 
 
Use Terra's Graph API to display graphs for different health and activity metrics. You can use this to display sleep analytics, historical heart rate data, and more.
 
 
Use web scraping to extract the latest health and sport science research from the web and feed it to your custom GPT. This will make your coach's responses more scientific and helpful. Python has many useful libraries to facilitate this.
 
To learn more about the awesome things you can build with Terra, check out the following articles:
 

More Topics

All Blogs
Team Spotlight
Startup Spotlight
How To
Blog
Podcast
Product Updates
Wearables
See All >
CEO and Founder of Prenuvo - Andrew Lacy

CEO and Founder of Prenuvo - Andrew Lacy

In this podcast with Kyriakos the CEO of Terra, Andrew Lacy shares his journey with Prenuvo which began from a personal health crisis.

Terra APITerra API
August 28, 2024
MedHacks: Using Wearables To Predict Heart Attacks

MedHacks: Using Wearables To Predict Heart Attacks

A few weeks ago we met Vishal, a recent engineering graduate who wanted to use Terra API as part of his MedHacks hackathon project, Cardio Clarity.

Gursukh SembiGursukh Sembi
August 19, 2024
July 2024 updates

July 2024 updates

Teams API adds Kinexon integration & new webhooks. Terra Health Scores now include Respiratory & Stress metrics. Eight Sleep integration returns with enhanced data.

Alex VenetidisAlex Venetidis
August 2, 2024
Vice President of Teamworks - Sean Harrington

Vice President of Teamworks - Sean Harrington

In this podcast with Kyriakos the CEO of Terra, Sean Harrington shares his journey from founding NoteMeal to becoming the VP of Teamworks.

Terra APITerra API
August 2, 2024
Chief Digital Product Officer of Les Mills - Amber Taylor

Chief Digital Product Officer of Les Mills - Amber Taylor

In this podcast with Kyriakos the CEO of Terra, Amber Taylor shares her journey from childhood running to her leadership role at Nike and navigating cultural differences.

Terra APITerra API
August 2, 2024
next ventures
pioneer fund
samsung next
y combinator
general catalyst

Cookie Preferences

Essential CookiesAlways On
Advertisement Cookies
Analytics Cookies

Crunch Time: Embrace the Cookie Monster Within!

We use cookies to enhance your browsing experience and analyse our traffic. By clicking “Accept All”, you consent to our use of cookies according to our Cookie Policy. You can change your mind any time by visiting out cookie policy.