Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/prompts/update-octofit-tracker-app.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
description: Update Django project files for octofit-tracker app
---

# Django App Updates

- All Django project files are in the `octofit-tracker/backend/octofit_tracker` directory.

1. Update `settings.py` for MongoDB connection and CORS. And make sure `octofit_tracker`, `rest_framework`, and `djongo` are in `INSTALLED_APPS`.
2. Update `models.py`, `serializers.py`, `urls.py`, `views.py`, `tests.py`, and `admin.py` to support users, teams, activities, leaderboard, and workouts collections.
3. Ensure `/` points to the api and `api_root` is present in `urls.py`.
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"env": {
"PYTHONPATH": "${workspaceFolder}/octofit-tracker/backend/venv/bin/python",
"VIRTUAL_ENV": "${workspaceFolder}/octofit-tracker/backend/venv",
"PATH": "${workspaceFolder}/octofit-tracker/backend/venv/bin:${env:PATH}"
"PATH": "${workspaceFolder}/octofit-tracker/backend/venv/bin:${env:PATH}",
"CODESPACE_NAME": "${env:CODESPACE_NAME}"
}
},
{
Expand Down
Binary file added octofit-tracker/backend/db.sqlite3
Binary file not shown.
22 changes: 22 additions & 0 deletions octofit-tracker/backend/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'octofit_tracker.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)


if __name__ == '__main__':
main()
Empty file.
35 changes: 35 additions & 0 deletions octofit-tracker/backend/octofit_tracker/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.contrib import admin
from .models import User, Team, Activity, Leaderboard, Workout


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'team', 'fitness_level', 'created_at')
list_filter = ('fitness_level', 'team')
search_fields = ('name', 'email')


@admin.register(Team)
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'description', 'created_at')
search_fields = ('name',)


@admin.register(Activity)
class ActivityAdmin(admin.ModelAdmin):
list_display = ('user', 'activity_type', 'duration', 'date')
list_filter = ('activity_type',)
search_fields = ('user__name',)


@admin.register(Leaderboard)
class LeaderboardAdmin(admin.ModelAdmin):
list_display = ('user', 'score', 'rank')
ordering = ('rank',)


@admin.register(Workout)
class WorkoutAdmin(admin.ModelAdmin):
list_display = ('name', 'difficulty')
list_filter = ('difficulty',)
search_fields = ('name',)
16 changes: 16 additions & 0 deletions octofit-tracker/backend/octofit_tracker/asgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
ASGI config for octofit_tracker project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'octofit_tracker.settings')

application = get_asgi_application()
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import timedelta
import random
from pymongo import MongoClient
from octofit_tracker.models import User, Team, Activity, Leaderboard, Workout


class Command(BaseCommand):
help = 'Populate the octofit_db database with test data'

def handle(self, *args, **options):
# Clear existing data directly via pymongo to avoid ORM issues with
# malformed documents that may have non-integer primary keys.
self.stdout.write('Clearing existing data...')
_client = MongoClient('localhost', 27017)
_db = _client['octofit_db']
for col in ['users', 'teams', 'activities', 'leaderboard', 'workouts']:
_db[col].delete_many({})
_client.close()

# Create Teams using Django ORM
self.stdout.write('Inserting teams...')
Team.objects.create(
id=1,
name='Team Marvel',
description="Earth's Mightiest Heroes",
)
Team.objects.create(
id=2,
name='Team DC',
description='Justice League United',
)
# Re-fetch to get clean integer PKs (djongo can return ObjectId on save)
team_marvel = Team.objects.get(pk=1)
team_dc = Team.objects.get(pk=2)
self.stdout.write(self.style.SUCCESS('Inserted 2 teams'))

# Create Users using Django ORM
self.stdout.write('Inserting users...')
users_info = [
# Team Marvel
(1, 'Tony Stark', 'ironman@avengers.com', team_marvel, '🦾', 'advanced'),
(2, 'Steve Rogers', 'captain@avengers.com', team_marvel, '🛡️', 'advanced'),
(3, 'Natasha Romanoff', 'blackwidow@avengers.com', team_marvel, '🕷️', 'advanced'),
(4, 'Bruce Banner', 'hulk@avengers.com', team_marvel, '💚', 'advanced'),
(5, 'Thor Odinson', 'thor@asgard.com', team_marvel, '⚡', 'god-tier'),
# Team DC
(6, 'Clark Kent', 'superman@justiceleague.com', team_dc, '🦸', 'god-tier'),
(7, 'Bruce Wayne', 'batman@gotham.com', team_dc, '🦇', 'advanced'),
(8, 'Diana Prince', 'wonderwoman@themyscira.com', team_dc, '⭐', 'god-tier'),
(9, 'Barry Allen', 'flash@speedforce.com', team_dc, '⚡', 'advanced'),
(10, 'Arthur Curry', 'aquaman@atlantis.com', team_dc, '🔱', 'advanced'),
]

users = []
for uid, name, email, team, avatar, fitness_level in users_info:
User.objects.create(
id=uid,
name=name,
email=email,
team=team,
avatar=avatar,
fitness_level=fitness_level,
)
# Re-fetch users with clean integer PKs
users = list(User.objects.all().order_by('id'))
self.stdout.write(self.style.SUCCESS(f'Inserted {len(users)} users'))

# Create Activities using Django ORM
self.stdout.write('Inserting activities...')
activity_types = ['running', 'cycling', 'swimming', 'strength_training', 'yoga']
activities_created = 0
activity_id = 1
for user in users:
num_activities = random.randint(5, 10)
for j in range(num_activities):
days_ago = random.randint(0, 30)
activity_date = timezone.now() - timedelta(days=days_ago)
Activity.objects.create(
id=activity_id,
user=user,
activity_type=random.choice(activity_types),
duration=random.randint(15, 120),
date=activity_date,
notes=f'Great workout session #{j + 1}',
)
activity_id += 1
activities_created += 1
self.stdout.write(self.style.SUCCESS(f'Inserted {activities_created} activities'))

# Create Leaderboard entries using Django ORM
self.stdout.write('Calculating leaderboard...')
leaderboard_entries = []
for user in users:
total_duration = sum(
a.duration for a in Activity.objects.filter(user=user)
)
leaderboard_entries.append((user, total_duration))

# Sort by total duration descending and assign ranks
leaderboard_entries.sort(key=lambda x: x[1], reverse=True)
for rank, (user, score) in enumerate(leaderboard_entries, start=1):
Leaderboard.objects.create(id=rank, user=user, score=score, rank=rank)
self.stdout.write(self.style.SUCCESS(f'Inserted {len(leaderboard_entries)} leaderboard entries'))

# Create Workout suggestions using Django ORM
self.stdout.write('Inserting workout suggestions...')
workouts_data = [
{
'name': 'Super Soldier Circuit',
'description': "Captain America's legendary training routine",
'difficulty': 'advanced',
'exercises': [
{'name': 'Push-ups', 'reps': 50, 'sets': 4},
{'name': 'Pull-ups', 'reps': 20, 'sets': 4},
{'name': 'Squats', 'reps': 50, 'sets': 4},
{'name': 'Burpees', 'reps': 30, 'sets': 3},
],
},
{
'name': 'Speedster Sprint Training',
'description': "Barry Allen's speed-building workout",
'difficulty': 'intermediate',
'exercises': [
{'name': 'Sprint Intervals', 'duration': '30 sec', 'sets': 10},
{'name': 'High Knees', 'duration': '1 min', 'sets': 5},
{'name': 'Mountain Climbers', 'reps': 40, 'sets': 4},
],
},
{
'name': 'Amazonian Warrior Training',
'description': "Wonder Woman's combat conditioning",
'difficulty': 'advanced',
'exercises': [
{'name': 'Sword Swings (weighted)', 'reps': 30, 'sets': 5},
{'name': 'Shield Holds', 'duration': '2 min', 'sets': 3},
{'name': 'Battle Rope', 'duration': '1 min', 'sets': 5},
{'name': 'Box Jumps', 'reps': 25, 'sets': 4},
],
},
{
'name': 'Atlantean Swim Power',
'description': "Aquaman's underwater endurance training",
'difficulty': 'intermediate',
'exercises': [
{'name': 'Freestyle Swimming', 'distance': '1000m', 'sets': 3},
{'name': 'Underwater Breath Hold', 'duration': '2 min', 'sets': 5},
{'name': 'Treading Water', 'duration': '5 min', 'sets': 3},
],
},
{
'name': 'Zen Master Flow',
'description': "Black Widow's flexibility and balance routine",
'difficulty': 'beginner',
'exercises': [
{'name': 'Sun Salutations', 'reps': 10, 'sets': 3},
{'name': 'Warrior Poses', 'duration': '1 min each', 'sets': 3},
{'name': 'Tree Pose', 'duration': '1 min', 'sets': 3},
{'name': 'Cobra Stretch', 'duration': '30 sec', 'sets': 4},
],
},
{
'name': 'Dark Knight Conditioning',
'description': "Batman's stealth and agility training",
'difficulty': 'advanced',
'exercises': [
{'name': 'Parkour Drills', 'duration': '10 min', 'sets': 1},
{'name': 'Rope Climbing', 'reps': 10, 'sets': 3},
{'name': 'Handstand Push-ups', 'reps': 15, 'sets': 3},
{'name': 'Ninja Rolls', 'reps': 20, 'sets': 4},
],
},
{
'name': 'Arc Reactor Cardio',
'description': "Iron Man's heart-healthy workout",
'difficulty': 'intermediate',
'exercises': [
{'name': 'Cycling', 'duration': '20 min', 'sets': 1},
{'name': 'Jumping Jacks', 'reps': 50, 'sets': 3},
{'name': 'Step-ups', 'reps': 30, 'sets': 4},
],
},
]

for w_id, w in enumerate(workouts_data, start=1):
Workout.objects.create(
id=w_id,
name=w['name'],
description=w['description'],
difficulty=w['difficulty'],
exercises=w['exercises'],
)
self.stdout.write(self.style.SUCCESS(f'Inserted {len(workouts_data)} workout suggestions'))

# Summary
self.stdout.write('\n' + '=' * 50)
self.stdout.write(self.style.SUCCESS('DATABASE POPULATION COMPLETE!'))
self.stdout.write('=' * 50 + '\n')
self.stdout.write('Collection counts:')
self.stdout.write(f' Users: {User.objects.count()}')
self.stdout.write(f' Teams: {Team.objects.count()}')
self.stdout.write(f' Activities: {Activity.objects.count()}')
self.stdout.write(f' Leaderboard: {Leaderboard.objects.count()}')
self.stdout.write(f' Workouts: {Workout.objects.count()}')

82 changes: 82 additions & 0 deletions octofit-tracker/backend/octofit_tracker/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Generated by Django 4.1.7 on 2026-02-19 11:35

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Team',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.CharField(blank=True, default='', max_length=500)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'db_table': 'teams',
},
),
migrations.CreateModel(
name='Workout',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.CharField(blank=True, default='', max_length=1000)),
('exercises', models.JSONField(default=list)),
('difficulty', models.CharField(choices=[('beginner', 'Beginner'), ('intermediate', 'Intermediate'), ('advanced', 'Advanced'), ('god-tier', 'God-Tier')], default='beginner', max_length=50)),
],
options={
'db_table': 'workouts',
},
),
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254, unique=True)),
('avatar', models.CharField(blank=True, default='', max_length=10)),
('fitness_level', models.CharField(blank=True, default='beginner', max_length=50)),
('created_at', models.DateTimeField(auto_now_add=True)),
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='octofit_tracker.team')),
],
options={
'db_table': 'users',
},
),
migrations.CreateModel(
name='Leaderboard',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('score', models.FloatField(default=0.0)),
('rank', models.IntegerField(default=0)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leaderboard_entries', to='octofit_tracker.user')),
],
options={
'db_table': 'leaderboard',
'ordering': ['rank'],
},
),
migrations.CreateModel(
name='Activity',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('activity_type', models.CharField(choices=[('running', 'Running'), ('cycling', 'Cycling'), ('swimming', 'Swimming'), ('strength_training', 'Strength Training'), ('yoga', 'Yoga'), ('walking', 'Walking'), ('other', 'Other')], max_length=50)),
('duration', models.FloatField(help_text='Duration in minutes')),
('date', models.DateTimeField()),
('notes', models.CharField(blank=True, default='', max_length=500)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='octofit_tracker.user')),
],
options={
'db_table': 'activities',
},
),
]
Empty file.
Loading