Skip to content

Commit 5e53c0d

Browse files
Merge pull request #2 from AlgorithmicThoughts/build-octofit-app
Add registration validation and more activities
2 parents 9fad323 + c26cf93 commit 5e53c0d

35 files changed

+18670
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
mode: 'agent'
2+
model: GPT-4.1
3+
4+
# Django App Updates
5+
6+
- All Django project files are in the `octofit-tracker/backend/octofit_tracker` directory.
7+
8+
1. Update `settings.py` for MongoDB connection and CORS.
9+
2. Update `models.py`, `serializers.py`, `urls.py`, `views.py`, `tests.py`, and `admin.py` to support users, teams, activities, leaderboard, and workouts collections.
10+
3. Ensure `/` points to the api and `api_root` is present in `urls.py`.

octofit-tracker/backend/manage.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
import os
4+
import sys
5+
6+
7+
def main():
8+
"""Run administrative tasks."""
9+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'octofit_tracker.settings')
10+
try:
11+
from django.core.management import execute_from_command_line
12+
except ImportError as exc:
13+
raise ImportError(
14+
"Couldn't import Django. Are you sure it's installed and "
15+
"available on your PYTHONPATH environment variable? Did you "
16+
"forget to activate a virtual environment?"
17+
) from exc
18+
execute_from_command_line(sys.argv)
19+
20+
21+
if __name__ == '__main__':
22+
main()

octofit-tracker/backend/octofit_tracker/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
ASGI config for octofit_tracker project.
3+
4+
It exposes the ASGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
8+
"""
9+
10+
import os
11+
12+
from django.core.asgi import get_asgi_application
13+
14+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'octofit_tracker.settings')
15+
16+
application = get_asgi_application()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from django.core.management.base import BaseCommand
2+
from octofit_tracker.models import User, Team, Activity, Leaderboard, Workout
3+
from djongo import models
4+
5+
class Command(BaseCommand):
6+
help = 'Populate the octofit_db database with test data'
7+
8+
def handle(self, *args, **options):
9+
# Delete existing data
10+
Activity.objects.all().delete()
11+
Workout.objects.all().delete()
12+
Leaderboard.objects.all().delete()
13+
User.objects.all().delete()
14+
Team.objects.all().delete()
15+
16+
# Create teams
17+
marvel = Team.objects.create(name='Marvel')
18+
dc = Team.objects.create(name='DC')
19+
20+
# Create users
21+
users = [
22+
User.objects.create(name='Spider-Man', email='spiderman@marvel.com', team=marvel),
23+
User.objects.create(name='Iron Man', email='ironman@marvel.com', team=marvel),
24+
User.objects.create(name='Wonder Woman', email='wonderwoman@dc.com', team=dc),
25+
User.objects.create(name='Batman', email='batman@dc.com', team=dc),
26+
]
27+
28+
# Create activities
29+
Activity.objects.create(user=users[0], type='Running', duration=30)
30+
Activity.objects.create(user=users[1], type='Cycling', duration=45)
31+
Activity.objects.create(user=users[2], type='Swimming', duration=60)
32+
Activity.objects.create(user=users[3], type='Yoga', duration=20)
33+
34+
# Create workouts
35+
workout1 = Workout.objects.create(name='Hero HIIT', description='High intensity interval training for heroes')
36+
workout2 = Workout.objects.create(name='Power Yoga', description='Yoga for strength and flexibility')
37+
workout1.suggested_for.set([users[0], users[1]])
38+
workout2.suggested_for.set([users[2], users[3]])
39+
40+
# Create leaderboard
41+
Leaderboard.objects.create(team=marvel, points=100)
42+
Leaderboard.objects.create(team=dc, points=90)
43+
44+
# Ensure unique index on email
45+
from pymongo import MongoClient
46+
client = MongoClient('localhost', 27017)
47+
db = client['octofit_db']
48+
db.users.create_index('email', unique=True)
49+
50+
self.stdout.write(self.style.SUCCESS('Test data populated successfully!'))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from djongo import models
2+
3+
class Team(models.Model):
4+
name = models.CharField(max_length=100, unique=True)
5+
class Meta:
6+
db_table = 'teams'
7+
def __str__(self):
8+
return self.name
9+
10+
class User(models.Model):
11+
name = models.CharField(max_length=100)
12+
email = models.EmailField(unique=True)
13+
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='members')
14+
class Meta:
15+
db_table = 'users'
16+
def __str__(self):
17+
return self.name
18+
19+
class Activity(models.Model):
20+
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='activities')
21+
type = models.CharField(max_length=50)
22+
duration = models.IntegerField() # minutes
23+
timestamp = models.DateTimeField(auto_now_add=True)
24+
class Meta:
25+
db_table = 'activities'
26+
def __str__(self):
27+
return f"{self.type} - {self.user.name}"
28+
29+
class Workout(models.Model):
30+
name = models.CharField(max_length=100)
31+
description = models.TextField()
32+
suggested_for = models.ManyToManyField(User, related_name='suggested_workouts')
33+
class Meta:
34+
db_table = 'workouts'
35+
def __str__(self):
36+
return self.name
37+
38+
class Leaderboard(models.Model):
39+
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='leaderboard')
40+
points = models.IntegerField(default=0)
41+
class Meta:
42+
db_table = 'leaderboard'
43+
def __str__(self):
44+
return f"{self.team.name}: {self.points} pts"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from rest_framework import serializers
2+
from .models import User, Team, Activity, Workout, Leaderboard
3+
4+
class TeamSerializer(serializers.ModelSerializer):
5+
class Meta:
6+
model = Team
7+
fields = '__all__'
8+
9+
class UserSerializer(serializers.ModelSerializer):
10+
class Meta:
11+
model = User
12+
fields = '__all__'
13+
14+
class ActivitySerializer(serializers.ModelSerializer):
15+
class Meta:
16+
model = Activity
17+
fields = '__all__'
18+
19+
class WorkoutSerializer(serializers.ModelSerializer):
20+
class Meta:
21+
model = Workout
22+
fields = '__all__'
23+
24+
class LeaderboardSerializer(serializers.ModelSerializer):
25+
class Meta:
26+
model = Leaderboard
27+
fields = '__all__'
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
Django settings for octofit_tracker project.
3+
4+
Generated by 'django-admin startproject' using Django 4.1.7.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/4.1/topics/settings/
8+
9+
For the full list of settings and their values, see
10+
https://docs.djangoproject.com/en/4.1/ref/settings/
11+
"""
12+
13+
from pathlib import Path
14+
15+
# Build paths inside the project like this: BASE_DIR / 'subdir'.
16+
BASE_DIR = Path(__file__).resolve().parent.parent
17+
18+
19+
# Quick-start development settings - unsuitable for production
20+
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
21+
22+
# SECURITY WARNING: keep the secret key used in production secret!
23+
SECRET_KEY = 'django-insecure-zv+b2zp*2&o30ve+^9=utg8%d=68vnc*!w5=573j=$5o(8m$k='
24+
25+
# SECURITY WARNING: don't run with debug turned on in production!
26+
DEBUG = True
27+
28+
import os
29+
CODESPACE_NAME = os.environ.get('CODESPACE_NAME')
30+
codespace_host = f"{CODESPACE_NAME}-8000.app.github.dev" if CODESPACE_NAME else None
31+
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
32+
if codespace_host:
33+
ALLOWED_HOSTS.append(codespace_host)
34+
35+
36+
# Application definition
37+
38+
INSTALLED_APPS = [
39+
'django.contrib.admin',
40+
'django.contrib.auth',
41+
'django.contrib.contenttypes',
42+
'django.contrib.sessions',
43+
'django.contrib.messages',
44+
'django.contrib.staticfiles',
45+
'octofit_tracker',
46+
'rest_framework',
47+
'djongo',
48+
'corsheaders',
49+
]
50+
51+
MIDDLEWARE = [
52+
'corsheaders.middleware.CorsMiddleware',
53+
'django.middleware.security.SecurityMiddleware',
54+
'django.contrib.sessions.middleware.SessionMiddleware',
55+
'django.middleware.common.CommonMiddleware',
56+
'django.middleware.csrf.CsrfViewMiddleware',
57+
'django.contrib.auth.middleware.AuthenticationMiddleware',
58+
'django.contrib.messages.middleware.MessageMiddleware',
59+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
60+
]
61+
62+
ROOT_URLCONF = 'octofit_tracker.urls'
63+
64+
TEMPLATES = [
65+
{
66+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
67+
'DIRS': [],
68+
'APP_DIRS': True,
69+
'OPTIONS': {
70+
'context_processors': [
71+
'django.template.context_processors.debug',
72+
'django.template.context_processors.request',
73+
'django.contrib.auth.context_processors.auth',
74+
'django.contrib.messages.context_processors.messages',
75+
],
76+
},
77+
},
78+
]
79+
80+
WSGI_APPLICATION = 'octofit_tracker.wsgi.application'
81+
82+
83+
# Database
84+
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
85+
86+
DATABASES = {
87+
'default': {
88+
'ENGINE': 'djongo',
89+
'NAME': 'octofit_db',
90+
'ENFORCE_SCHEMA': False,
91+
'CLIENT': {
92+
'host': 'localhost',
93+
'port': 27017,
94+
},
95+
}
96+
}
97+
98+
99+
# Password validation
100+
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
101+
102+
AUTH_PASSWORD_VALIDATORS = [
103+
{
104+
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
105+
},
106+
{
107+
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
108+
},
109+
{
110+
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
111+
},
112+
{
113+
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
114+
},
115+
]
116+
117+
118+
# Internationalization
119+
# https://docs.djangoproject.com/en/4.1/topics/i18n/
120+
121+
LANGUAGE_CODE = 'en-us'
122+
123+
TIME_ZONE = 'UTC'
124+
125+
USE_I18N = True
126+
127+
USE_TZ = True
128+
129+
130+
# Static files (CSS, JavaScript, Images)
131+
# Static files (CSS, JavaScript, Images)
132+
# https://docs.djangoproject.com/en/4.1/howto/static-files/
133+
134+
STATIC_URL = 'static/'
135+
136+
# CORS settings
137+
CORS_ALLOW_ALL_ORIGINS = True
138+
CORS_ALLOW_CREDENTIALS = True
139+
CORS_ALLOW_HEADERS = ['*']
140+
CORS_ALLOW_METHODS = ['DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT']
141+
142+
143+
# Default primary key field type
144+
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
145+
146+
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""octofit_tracker URL Configuration
2+
3+
The `urlpatterns` list routes URLs to views. For more information please see:
4+
https://docs.djangoproject.com/en/4.1/topics/http/urls/
5+
Examples:
6+
Function views
7+
1. Add an import: from my_app import views
8+
2. Add a URL to urlpatterns: path('', views.home, name='home')
9+
Class-based views
10+
1. Add an import: from other_app.views import Home
11+
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12+
Including another URLconf
13+
1. Import the include() function: from django.urls import include, path
14+
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15+
"""
16+
import os
17+
from django.contrib import admin
18+
from django.urls import path, include
19+
from django.http import JsonResponse
20+
from rest_framework import routers
21+
from .views import UserViewSet, TeamViewSet, ActivityViewSet, LeaderboardViewSet, WorkoutViewSet
22+
23+
router = routers.DefaultRouter()
24+
router.register(r'users', UserViewSet)
25+
router.register(r'teams', TeamViewSet)
26+
router.register(r'activities', ActivityViewSet)
27+
router.register(r'leaderboard', LeaderboardViewSet)
28+
router.register(r'workouts', WorkoutViewSet)
29+
30+
def api_root(request):
31+
codespace_name = os.environ.get('CODESPACE_NAME', '')
32+
base_url = f"https://{codespace_name}-8000.app.github.dev" if codespace_name else "http://localhost:8000"
33+
return JsonResponse({
34+
"users": f"{base_url}/api/users/",
35+
"teams": f"{base_url}/api/teams/",
36+
"activities": f"{base_url}/api/activities/",
37+
"leaderboard": f"{base_url}/api/leaderboard/",
38+
"workouts": f"{base_url}/api/workouts/",
39+
})
40+
41+
urlpatterns = [
42+
path('admin/', admin.site.urls),
43+
path('api/', include(router.urls)),
44+
path('', api_root, name='api_root'),
45+
]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from rest_framework import viewsets
2+
from .models import User, Team, Activity, Leaderboard, Workout
3+
from .serializers import UserSerializer, TeamSerializer, ActivitySerializer, LeaderboardSerializer, WorkoutSerializer
4+
5+
class UserViewSet(viewsets.ModelViewSet):
6+
queryset = User.objects.all()
7+
serializer_class = UserSerializer
8+
9+
class TeamViewSet(viewsets.ModelViewSet):
10+
queryset = Team.objects.all()
11+
serializer_class = TeamSerializer
12+
13+
class ActivityViewSet(viewsets.ModelViewSet):
14+
queryset = Activity.objects.all()
15+
serializer_class = ActivitySerializer
16+
17+
class LeaderboardViewSet(viewsets.ModelViewSet):
18+
queryset = Leaderboard.objects.all()
19+
serializer_class = LeaderboardSerializer
20+
21+
class WorkoutViewSet(viewsets.ModelViewSet):
22+
queryset = Workout.objects.all()
23+
serializer_class = WorkoutSerializer

0 commit comments

Comments
 (0)