Category: fitcypher

Random Bodyweight Workout with Video!

fitcypher

I have added a new workout page, this one will read a list of youtube videos from a json file, for example :

/src/fitcypher$ cat ui/data/darebee.json 
[
  {
    "id": "4XcGTvcSRxY",
    "title": "hop heel clicks",
    "duration": 8
  },
  {
    "id": "wqfRvXc7BHY",
    "title": "half squat walk",
    "duration": 19
  },
  {
    "id": "HAQvQXovwuo",
    "title": "side splits",
    "duration": 83
  }
]

and then will play a random youtube video for the user with a counter of the elapsed time :

alt text

Once the user has done the exercise for as long as they want, they click the Complete button, this will save the exercise and duration to the database.

If an exercise video comes up that you don’t like click the skip button and it will go to the next video and will not save the exercise to the database.

Checkout the FitCypher Workouts here :

https://alexlaverty.pythonanywhere.com/workouts

alt text

Or git clone the FitCypher github repo and run it locally :

https://github.com/alexlaverty/fitcypher


Quick Body Weight Workout

fitcypher

Now we’ve added a page to show a list of available Workouts to choose from :

alt text

Each time the user performs an exercise they can click the exercise and it will add an entry for today for that exercise and display a total count of exercises performed per day for that exercise :

alt text

I found that with this approach of creating an entry per exercise rep performed the entries page quickly became cluttered with individual reps, so I’ve updated the Entries page to group the exercises by name and provide a count of total exercises :

alt text

Try out FitCypher here :

https://alexlaverty.pythonanywhere.com/

Or git clone the FitCypher github repo and run it locally :

https://github.com/alexlaverty/fitcypher


REST API

fitcypher

We will use Django Rest Framework as the API framework to interact with this model.

Using API’s allows to either add entries in bulk, allow users to export and import from other systems, allow for automation and scheduled data syncing.

To make the API friendly to use we’ll provide an API UI :

alt text

Rest API Authentication

For now we will just use basic authentication, username and password, however in the future we’ll implement some better authentication methods like token authentication.

FitCypher API Curl Commands

GET

Curl Command :

curl - X GET http: //127.0.0.1:8000/api/entries/ -u your_username:your_password

JSON Response :

    [{
        "id": 1,
        "user": {
            "id": 1,
            "username": "alex"
        },
        "date": "2025-02-02T07:51:00Z",
        "tracking": "Food",
        "string_value": "Apple",
        "numerical_value": null,
        "notes": "",
        "tags": "",
        "source": "FitCypher"
    }, {
        "id": 2,
        "user": {
            "id": 1,
            "username": "alex"
        },
        "date": "2025-02-02T07:52:00Z",
        "tracking": "Exercise",
        "string_value": "Push Ups",
        "numerical_value": "10.00",
        "notes": "",
        "tags": "",
        "source": "FitCypher"
    }]

If you install JQ you can also get pretty printed output via :

curl -s -X GET http://127.0.0.1:8000/api/entries/ -u your_username:your_password | jq
[
  {
    "id": 1,
    "user": {
      "id": 1,
      "username": "alex"
    },
    "date": "2025-02-02T07:51:00Z",
    "tracking": "Food",
    "string_value": "Apple",
    "numerical_value": null,
    "notes": "",
    "tags": "",
    "source": "FitCypher"
  },
  {
    "id": 2,
    "user": {
      "id": 1,
      "username": "alex"
    },
    "date": "2025-02-02T07:52:00Z",
    "tracking": "Exercise",
    "string_value": "Push Ups",
    "numerical_value": "10.00",
    "notes": "",
    "tags": "",
    "source": "FitCypher"
  },
  {
    "id": 3,
    "user": {
      "id": 1,
      "username": "alex"
    },
    "date": "2023-10-15T12:00:00Z",
    "tracking": "weight",
    "string_value": null,
    "numerical_value": "70.50",
    "notes": "After breakfast",
    "tags": "morning",
    "source": "fitcypher"
  }
]

POST

Add a new entry via the API

curl -X POST http://127.0.0.1:8000/api/entries/ \
-H "Content-Type: application/json" \
-u your_username:your_password \
-d '{
    "date": "2023-10-15T12:00:00Z",
    "tracking": "weight",
    "numerical_value": 70.5,
    "notes": "After breakfast",
    "tags": "morning",
    "source": "fitcypher"
}'

Add a new entry via the API to the PythonAnywhere hosted instance :

curl -X POST https://alexlaverty.pythonanywhere.com/api/entries/ \
-H "Content-Type: application/json" \
-u your_username:your_password \
-d '{
    "date": "2023-10-15T12:00:00Z",
    "tracking": "food",
    "string_value": "Banana",
    "numerical_value": "",
    "notes": "",
    "tags": "",
    "source": "fitcypher"
}'

Try out FitCypher here :

https://alexlaverty.pythonanywhere.com/

Or git clone the FitCypher github repo and run it locally :

https://github.com/alexlaverty/fitcypher


Django Model

fitcypher

Today I am working on the FitCypher database backend, to keep it simple the nutrition and health metrics will be posted into a common Entry model :

class Entry(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="entries")
    date = models.DateTimeField()
    tracking = models.CharField(max_length=255)
    string_value = models.CharField(max_length=255, null=True, blank=True)
    numerical_value = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    notes = models.TextField(null=True, blank=True)
    tags = models.CharField(max_length=255, null=True, blank=True)
    source = models.TextField(null=True, blank=True, default="fitcypher")

    def __str__(self):
        return f"{self.date.strftime('%Y-%m-%d')}, {self.user}, {self.string_value}"

This Entry model is a flexible and generic way to store various health and fitness metrics for users.

Below is an explanation of each field and its purpose:


1. user

   user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="entries")
  • Purpose: This field establishes a relationship between the Entry and the User model. It indicates which user the entry belongs to.
  • Details:
    • on_delete=models.CASCADE: If the user is deleted, all their associated entries will also be deleted.
    • related_name="entries": This allows you to access all entries for a specific user using user.entries.

2. date

   date = models.DateTimeField()
  • Purpose: This field stores the date and time when the entry was recorded.
  • Details:
    • It is essential for tracking when a specific health metric was logged (e.g., weight on a particular day, steps taken on a specific date).

3. tracking

   tracking = models.CharField(max_length=255)
  • Purpose: This field specifies what type of health or fitness metric is being tracked (e.g., “weight”, “steps”, “calories”, “heart_rate”).
  • Details:
    • It acts as a label or category for the entry, allowing you to differentiate between different types of metrics.

4. string_value

   string_value = models.CharField(max_length=255, null=True, blank=True)
  • Purpose: This field stores a textual value for the tracked metric, if applicable.
  • Details:
    • It is optional (null=True, blank=True) because not all metrics will have a textual value.
    • Example: If tracking="mood", string_value could be “happy” or “stressed”.

5. numerical_value

   numerical_value = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
  • Purpose: This field stores a numerical value for the tracked metric, if applicable.
  • Details:
    • It is optional (null=True, blank=True) because not all metrics will have a numerical value.
    • Example: If tracking="weight", numerical_value could be 70.5 (in kg).

6. notes

   notes = models.TextField(null=True, blank=True)
  • Purpose: This field allows users to add additional comments or details about the entry.
  • Details:
    • It is optional (null=True, blank=True).
    • Example: If tracking="workout", notes could describe the type of workout performed (e.g., “30 minutes of cardio and 20 minutes of strength training”).

7. tags

   tags = models.CharField(max_length=255, null=True, blank=True)
  • Purpose: This field allows users to add tags or keywords to categorize or filter entries.
  • Details:
    • It is optional (null=True, blank=True).
    • Example: Tags like “morning”, “evening”, “high-intensity”, or “low-carb” can help organize entries.

8. source

   source = models.TextField(null=True, blank=True, default="fitcypher")
  • Purpose: This field indicates the source of the entry (e.g., a specific app, device, or manual entry).
  • Details:
    • It has a default value of "fitcypher", which could be your app’s name.
    • Example: If the data is synced from a fitness tracker like Fitbit, source could be "Fitbit".

9. __str__ Method

   def __str__(self):
       return f"{self.date.strftime('%Y-%m-%d')}, {self.user}, {self.string_value}"
  • Purpose: This method provides a human-readable string representation of the Entry object.
  • Details:
    • It is useful for debugging, logging, or displaying the entry in the Django admin interface.
    • Example output: "2023-10-15, john_doe, 70.5".

Summary of Use Cases

  • Tracking Weight: tracking="weight", numerical_value=70.5, notes="After breakfast", tags="morning".
  • Tracking Mood: tracking="mood", string_value="happy", notes="Had a great workout".
  • Tracking Steps: tracking="steps", numerical_value=10000, source="Fitbit".

This model is highly flexible and can accommodate a wide range of health and fitness metrics, making it a great foundation for a simple health and fitness tracker.

We will use Django Rest Framework as the API framework to interact with this model.

To make the API friendly to use we’ll provide an API UI :

alt text

Try out FitCypher here :

https://alexlaverty.pythonanywhere.com/

Or git clone the FitCypher github repo and run it locally :

https://github.com/alexlaverty/fitcypher


Generating FitCypher web logos with AI

fitcypher

I need a logo for the FitCypher app and I am a hopeless artist so I’m using Meta AI image generation to come up with a logo, here’s a few I’ve generated

So far this is the one I’ve chosen to go with but I am open to suggestions :

Try out FitCypher here :

https://alexlaverty.pythonanywhere.com/

Or git clone the FitCypher github repo and run it locally :

https://github.com/alexlaverty/fitcypher


New Django Project

fitcypher

Creating a new Django Project

Creating the FitCypher Django Project

I switch between linux and windows desktops so the commands will be whatever OS I’m on at the time.

Creating a new python virtual environment and activating it

D:\src\fitcypher>virtualenv env
D:\src\fitcypher>env\Scripts\activate.bat
# Install Django
pip install django

# Create a new project
django-admin startproject fitcypher

# Navigate to the project directory
cd fitcypher

# Create a new app
python manage.py startapp api

# Add the 'api' to INSTALLED_APPS in settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'api'
]

# Run initial migrations
python manage.py migrate

# Start the development server
python manage.py runserver

After that browse the local website :

http://127.0.0.1:8000/

and then hey presto we have lift off :

alt text

Deploying FitCypher to Render.com

Once you’ve built the site and pushed the code to github you need somewhere to deploy and host the website.

I’ve used Render.com before and it will monitor the github repo, and if there is a new commit it will redeploy the website with the latest code.

I’ve deployed before via docker container but I’ll give it a go trying to run the django website directly on render, we’ll see how it goes.

Going to follow the documentation here https://render.com/docs/deploy-django

pip install psycopg2-binary
pip install dj-database-url

On second thought I’m going to try out PythonAnywhere

When I tried Render free tier, first things I noticed were the website goes to sleep if it’s not receiving traffic and when it’s accessed the next time the website has to wake up which takes time and can even time out on the first attempt. Also the database is scheduled deletion every month which means having to setup the database again. It was pretty annoying.

Seems PythonAnywhere will not go to sleep and as long as you log in and click a button saying you’re still using the website it’ll extend for another 3 months, not sure what the go is with the database for free tier.

Hmmm so Render.com has a much nicer setup, you can pretty much auth to Github and click a repo and it’ll do a webhook on commit to trigger and deployment, where as PythonAnywhere is much more basic, and you basically get a virtual server and ssh console access to login, and you need to git clone your repo, manually git pull and then go into the UI and click reload to make the changes take effect, might have to look into more if there’s better ways of triggering deployment on git commit.

So the basic FitCypher django project is available here :

https://alexlaverty.pythonanywhere.com/

I also provisioned a MySQL database on PythonAnywhere, it also had PostGres DB but they required a premium account so I’ve gone with MySQL.

Try out FitCypher here :

https://alexlaverty.pythonanywhere.com/

Or git clone the FitCypher github repo and run it locally :

https://github.com/alexlaverty/fitcypher