Skip to content

Commit

Permalink
Merge pull request #10 from githubuniverseworkshops/arilivigni/self-s…
Browse files Browse the repository at this point in the history
…ervice-prompting

Refactor README and backend setup instructions
  • Loading branch information
bryantson authored Oct 24, 2024
2 parents f3cce6e + fa5ff48 commit 44f27dc
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 53 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ Without further ado, let's get started.
1. [Story](docs/1_Story)
2. [Prerequisites and development environment setup](docs/2_Prerequisites)
3. [Getting started - app frontend and backend creation](docs/3_GettingStarted)
4. [MongoDB install and setup](docs/4_MongoDBInstallSetup/)
4. [OctoFit Tracker database and app backend setup](docs/4_BackendSettings)
5. [Populate the database with sample data](docs/5_PopulateDBwData)
10 changes: 9 additions & 1 deletion docs/1_Story/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@ OctoFit Tracker will include:
GitHub Copilot and Copilot Chat uses OpenAI GPT models for its coding suggestions and chat interaction.
OpenAI gpt-4o: *"Our high-intelligence flagship model for complex, multi-step tasks. GPT-4o is cheaper and faster than GPT-4 Turbo. Currently points to gpt-4o-2024-08-06"*

[OpenAI GPT models explained](https://platform.openai.com/docs/models)
#### [OpenAI GPT models explained](https://platform.openai.com/docs/models)

![openai gpt models](./gpt-models.png)

#### Prompt engineering

- [GitHub documentation prompt engineering](https://docs.github.com/en/copilot/using-github-copilot/prompt-engineering-for-github-copilot)
- [How to use GitHub Copilot: Prompts, tips, and use cases](https://github.blog/2023-06-20-how-to-write-better-prompts-for-github-copilot/)
- [Using GitHub Copilot in your IDE: Tips, tricks, and best practices](https://github.blog/2024-03-25-how-to-use-github-copilot-in-your-ide-tips-tricks-and-best-practices/)
- [A developer’s guide to prompt engineering and LLMs](https://docs.github.com/en/copilot/using-github-copilot/prompt-engineering-for-github-copilot#:~:text=A%20developer%E2%80%99s%20guide%20to%20prompt%20engineering%20and%20LLMs)
- [Prompting GitHub Copilot Chat to become your personal AI assistant for accessibility]https://github.blog/2023-10-09-prompting-github-copilot-chat-to-become-your-personal-ai-assistant-for-accessibility/()

OpenAI gpt-4o: *"Our high-intelligence flagship model for complex, multi-step tasks. GPT-4o is cheaper and faster than GPT-4 Turbo. Currently points to gpt-4o-2024-08-06"*

> NOTE: we will be using gpt-4o in GitHub Copilot Chat for this workshop at GitHub Universe.
Expand Down
4 changes: 2 additions & 2 deletions docs/3_GettingStarted/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ generate instructions in this order
1. Create the frontend and backend in the octofit-tracker directory of this repository in one command
2. Setup backend python venv and install octofit-tracker/requirements.txt first
3. The octofit-tracker/backend directory will store the django project with the name octofit-tracker
3. The octofit-tracker/backend directory will store the django project and app with the name octofit-tracker
4. The Django project octofit-tracker directory will have all the backend components for the app
5. Create the django app directly in the directory octofit_tracker/backend
6. Setup the octofit-tracker/frontend directory will store the react app with no subdirectories
Expand Down Expand Up @@ -75,4 +75,4 @@ Important to avoid using public code and we do NOT need to initialize the git re

![octofit-tracker app directory tree](./3_3_OctoFitTrackerDirTree.png)</br>

[Back :: Previous: Prerequisites and development environment setup](../2_Prerequisites) | [Next :: OctoFit app backend setup](../4_BackendSettings/)
[Back :: Previous: Prerequisites and development environment setup](../2_Prerequisites) | [Next :: OctoFit Tracker database and app backend setup](../4_BackendSettings/)
84 changes: 46 additions & 38 deletions docs/4_BackendSettings/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Setup database in settings.py, models, views, serializers, and populate data
# OctoFit Tracker database and app backend setup

## Use Copilot Chat
## Initialize the database, setup database and install apps in settings.py, models, serializers, urls, and views

Type the following prompt in GitHub Copilot Chat:

```text
In our next steps lets think step by step and setup the following in this order
1. Initialize the mongo octofit_db database
2. settings.py in our django project for mongodb octofit_db database including localhost and the port
3. settings.py in our django project setup for all installed apps. ex djongo, octofit_tracker, rest_framework
4. In octofit_tracker project setup and touch models, serializers, urls, and views for users, teams, activity, leaderboard, and workouts
1. Initialize the mongo octofit_db database and create a correct table structure for users, teams, activities, leaderboards, and workouts collections
2. Make sure there is a unique id for primary key for the user collection
ex. db.users.createIndex({ "email": 1 }, { unique: true })
3. settings.py in our django project for mongodb octofit_db database including localhost and the port
4. settings.py in our django project setup for all installed apps. ex djongo, octofit_tracker, rest_framework
5. In octofit_tracker project setup and use command touch models.py, serializers.py, urls.py, and views.py for users, teams, activities, leaderboards, and workouts
6. make sure urls.py has a root, admin, and api endpoints
```

![OctoFit Tracker backend files](./4_1_OctoFitTrackerBackendFiles.png)</br>
Expand Down Expand Up @@ -56,41 +59,34 @@ INSTALLED_APPS = [
```python
# FILE: octofit-tracker/backend/octofit_tracker/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
from djongo import models

class User(AbstractUser):
class User(models.Model):
_id = models.ObjectIdField()
username = models.CharField(max_length=100)
email = models.EmailField(unique=True)
groups = models.ManyToManyField(
'auth.Group',
related_name='octofit_tracker_user_set',
blank=True,
help_text='The groups this user belongs to.',
verbose_name='groups',
)
user_permissions = models.ManyToManyField(
'auth.Permission',
related_name='octofit_tracker_user_set',
blank=True,
help_text='Specific permissions for this user.',
verbose_name='user permissions',
)
password = models.CharField(max_length=100)

class Team(models.Model):
_id = models.ObjectIdField()
name = models.CharField(max_length=100)
members = models.ArrayReferenceField(to=User, on_delete=models.CASCADE)

class Activity(models.Model):
_id = models.ObjectIdField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
activity_type = models.CharField(max_length=100)

class Workout(models.Model):
name = models.CharField(max_length=100)
duration = models.DurationField()
activity = models.ForeignKey(Activity, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)

class Leaderboard(models.Model):
_id = models.ObjectIdField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
score = models.IntegerField()

class Workout(models.Model):
_id = models.ObjectIdField()
name = models.CharField(max_length=100)
description = models.TextField()
```

#### serializers.py
Expand All @@ -99,7 +95,7 @@ class Leaderboard(models.Model):
# FILE: octofit-tracker/backend/octofit_tracker/serializers.py

from rest_framework import serializers
from .models import User, Team, Activity, Workout, Leaderboard
from .models import User, Team, Activity, Leaderboard, Workout

class UserSerializer(serializers.ModelSerializer):
class Meta:
Expand All @@ -116,14 +112,14 @@ class ActivitySerializer(serializers.ModelSerializer):
model = Activity
fields = '__all__'

class WorkoutSerializer(serializers.ModelSerializer):
class LeaderboardSerializer(serializers.ModelSerializer):
class Meta:
model = Workout
model = Leaderboard
fields = '__all__'

class LeaderboardSerializer(serializers.ModelSerializer):
class WorkoutSerializer(serializers.ModelSerializer):
class Meta:
model = Leaderboard
model = Workout
fields = '__all__'
```

Expand All @@ -132,9 +128,9 @@ class LeaderboardSerializer(serializers.ModelSerializer):
```python
# FILE: octofit_tracker/views.py

from rest_framework import viewsets
from .models import User, Team, Activity, Leaderboard, Workout
from .serializers import UserSerializer, TeamSerializer, ActivitySerializer, LeaderboardSerializer, WorkoutSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view

class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
Expand All @@ -155,6 +151,17 @@ class LeaderboardViewSet(viewsets.ModelViewSet):
class WorkoutViewSet(viewsets.ModelViewSet):
queryset = Workout.objects.all()
serializer_class = WorkoutSerializer

@api_view(['GET'])
def api_root(request, format=None):
base_url = 'http://upgraded-space-happiness-959pr7vpgw3p7vv-8000.app.github.dev/'
return Response({
'users': base_url + 'api/users/?format=api',
'teams': base_url + 'api/teams/?format=api',
'activities': base_url + 'api/activities/?format=api',
'leaderboards': base_url + 'api/leaderboards/?format=api',
'workouts': base_url + 'api/workouts/?format=api'
})
```

#### urls.py
Expand All @@ -165,7 +172,7 @@ class WorkoutViewSet(viewsets.ModelViewSet):
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import UserViewSet, TeamViewSet, ActivityViewSet, LeaderboardViewSet, WorkoutViewSet
from .views import UserViewSet, TeamViewSet, ActivityViewSet, LeaderboardViewSet, WorkoutViewSet, api_root

router = DefaultRouter()
router.register(r'users', UserViewSet)
Expand All @@ -175,8 +182,9 @@ router.register(r'leaderboards', LeaderboardViewSet)
router.register(r'workouts', WorkoutViewSet)

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('', api_root, name='api-root'), # Root endpoint
path('admin/', admin.site.urls), # Admin endpoint
path('api/', include(router.urls)), # API endpoint
]
```

Expand Down
106 changes: 95 additions & 11 deletions docs/5_PopulateDBwData/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
> NOTE: This is an example of not being specific skip this prompt and go to the next one
```text
Let's use manage.py to get everything setup we need to create init.py for populate_db command include steps to migrate as well
Let's use manage.py to get everything setup we need to populate_db command include steps to migrate as well
```

### Example of not being specfic in prompting Copilot Chat
Expand All @@ -19,10 +19,11 @@ Let's use manage.py to get everything setup we need to create init.py for popula
Type the following prompt in GitHub Copilot Chat:

```text
Let's use manage.py to get the database setup and populated
Let's use manage.py to get the database setup and populated based on fields in models.py
- Populate the database with super hero users
- Create full setup for a command populate_db.py
- Create populate_db.py so it initializes and deletes previous data and recreates it
- populate_db.py creates users, teams, activities, leaderboards, and workouts
- users will be super hero users
- Include steps to migrate in the octofit_tracker project
```

Expand All @@ -33,29 +34,112 @@ Let's use manage.py to get the database setup and populated
```python
# FILE: octofit-tracker/backend/octofit_tracker/management/commands/populate_db.py

# octofit_tracker/management/commands/populate_db.py

from django.core.management.base import BaseCommand
from octofit_tracker.models import User, Team, Activity, Workout, Leaderboard
from octofit_tracker.models import User, Team, Activity, Leaderboard, Workout
from django.conf import settings
from pymongo import MongoClient
from datetime import timedelta

class Command(BaseCommand):
help = 'Populate the database with initial data'
help = 'Populate the database with superhero users, teams, activities, leaderboards, and workouts'

def handle(self, *args, **kwargs):
# Create Superhero Users
# Connect to MongoDB
client = MongoClient(settings.DATABASES['default']['HOST'], settings.DATABASES['default']['PORT'])
db = client[settings.DATABASES['default']['NAME']]

# Drop existing collections
db.users.drop()
db.teams.drop()
db.activities.drop()
db.leaderboards.drop()
db.workouts.drop()

# Populate users
users = [
{'username': 'superman', 'email': '[email protected]', 'password': 'superpassword'},
{'username': 'batman', 'email': '[email protected]', 'password': 'batpassword'},
{'username': 'wonderwoman', 'email': '[email protected]', 'password': 'wonderpassword'},
{'username': 'flash', 'email': '[email protected]', 'password': 'flashpassword'},
{'username': 'aquaman', 'email': '[email protected]', 'password': 'aquapassword'},
]

user_objects = []
for user_data in users:
user, created = User.objects.get_or_create(**user_data)
user, created = User.objects.get_or_create(email=user_data['email'], defaults=user_data)
user_objects.append(user)
if created:
self.stdout.write(self.style.SUCCESS(f"User {user.username} created"))
self.stdout.write(self.style.SUCCESS(f'Successfully created user {user.username}'))
else:
self.stdout.write(self.style.WARNING(f'User {user.username} already exists'))

# Ensure all user objects are saved
for user in user_objects:
user.save()

# Add more data population logic here if needed
# Populate teams
teams = [
{'name': 'Justice League', 'members': [user_objects[0], user_objects[1], user_objects[2], user_objects[3], user_objects[4]]},
]

self.stdout.write(self.style.SUCCESS('Database populated successfully'))
for team_data in teams:
team, created = Team.objects.get_or_create(name=team_data['name'])
if created:
for member in team_data['members']:
team.members.add(member)
self.stdout.write(self.style.SUCCESS(f'Successfully created team {team.name}'))
else:
self.stdout.write(self.style.WARNING(f'Team {team.name} already exists'))

# Populate activities
activities = [
{'user': user_objects[0], 'activity_type': 'Flying', 'duration': timedelta(hours=1)},
{'user': user_objects[1], 'activity_type': 'Martial Arts', 'duration': timedelta(hours=2)},
{'user': user_objects[2], 'activity_type': 'Training', 'duration': timedelta(hours=1, minutes=30)},
{'user': user_objects[3], 'activity_type': 'Running', 'duration': timedelta(minutes=30)},
{'user': user_objects[4], 'activity_type': 'Swimming', 'duration': timedelta(hours=1, minutes=15)},
]

for activity_data in activities:
activity, created = Activity.objects.get_or_create(**activity_data)
if created:
self.stdout.write(self.style.SUCCESS(f'Successfully created activity for {activity.user.username}'))
else:
self.stdout.write(self.style.WARNING(f'Activity for {activity.user.username} already exists'))

# Populate leaderboards
leaderboards = [
{'user': user_objects[0], 'score': 100},
{'user': user_objects[1], 'score': 90},
{'user': user_objects[2], 'score': 95},
{'user': user_objects[3], 'score': 85},
{'user': user_objects[4], 'score': 80},
]

for leaderboard_data in leaderboards:
leaderboard, created = Leaderboard.objects.get_or_create(**leaderboard_data)
if created:
self.stdout.write(self.style.SUCCESS(f'Successfully created leaderboard entry for {leaderboard.user.username}'))
else:
self.stdout.write(self.style.WARNING(f'Leaderboard entry for {leaderboard.user.username} already exists'))

# Populate workouts
workouts = [
{'name': 'Super Strength Training', 'description': 'Training for super strength'},
{'name': 'Martial Arts Training', 'description': 'Training for martial arts'},
{'name': 'Amazonian Training', 'description': 'Training for Amazonian warriors'},
{'name': 'Speed Training', 'description': 'Training for super speed'},
{'name': 'Aquatic Training', 'description': 'Training for underwater activities'},
]

for workout_data in workouts:
workout, created = Workout.objects.get_or_create(**workout_data)
if created:
self.stdout.write(self.style.SUCCESS(f'Successfully created workout {workout.name}'))
else:
self.stdout.write(self.style.WARNING(f'Workout {workout.name} already exists'))
```

![Migrate and populate db](./5_3_MigratePopulateDb.png)

0 comments on commit 44f27dc

Please sign in to comment.