diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index eadaf92..484b731 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,7 +10,7 @@ "extensions": [ "github.copilot", "github.copilot-chat", - "Phu1237.vs-browser", + // "Phu1237.vs-browser", "markdown-lint.markdownlinter", "ms-python.python", // Python extension "ms-python.vscode-pylance" // Pylance extension for Python diff --git a/docs/1_Story/README.md b/docs/1_Story/README.md index 38e2ba9..4f9ae7f 100644 --- a/docs/1_Story/README.md +++ b/docs/1_Story/README.md @@ -1,12 +1,10 @@ -# Your journey to develop a fitness tracker with GitHub Copilot now begins! +# Your journey to develop a fitness tracker app with GitHub Copilot now begins! ![OctoFit Tracker](../../images/octofit-tracker.png) -Here's a reworked version of the workshop concept that's more suitable for a conference demo and attendee workshop: - ## OctoFit Tracker: Building a Fitness App with GitHub Copilot -Welcome to the OctoFit Tracker workshop! In this hands-on session, you'll step into the shoes of a lead developer tasked with creating a cutting-edge fitness tracker in record time. We'll leverage the power of GitHub Copilot to rapidly develop a functional prototype of OctoFit Tracker, a social fitness app that encourages users to stay active and compete with their peers. +Welcome to the OctoFit Tracker app workshop! In this hands-on session, you'll step into the shoes of a lead developer tasked with creating a cutting-edge fitness tracker app in record time. We'll leverage the power of GitHub Copilot to rapidly develop a functional prototype of OctoFit Tracker, a social fitness app that encourages users to stay active and compete with their peers. ### Workshop Overview @@ -15,7 +13,7 @@ In this workshop, you'll: 1. Set up a development environment using GitHub Codespaces 2. Use GitHub Copilot to accelerate development across multiple technologies 3. Build key components of the OctoFit Tracker app -4. Learn best practices for working with AI-assisted coding tools +4. Learn best practices and prompting techniques for working with GitHub Copilot ### Application Features @@ -27,6 +25,19 @@ OctoFit Tracker will include: - Competitive leader boards - Personalized workout suggestions +### GitHub Copilot and Copilot Chat + +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](./gpt-models.png) + +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. + ### Technology Stack We'll be using a modern web application stack: @@ -38,32 +49,36 @@ We'll be using a modern web application stack: ### Workshop Structure -1. **Introduction and Setup of prerequisites** - - Overview of OctoFit Tracker concept +1. **Introduction** + - Overview of OctoFit Tracker app concept + - OpenAI GPT models + +1. **Setup of Prerequisites** - Setting up GitHub Codespaces + - Ensure GitHub Copilot and Copilot Chat extensions are up to date -2. **Rapid Prototyping with GitHub Copilot** +1. **Rapid Prototyping with GitHub Copilot** - Creating project structure - Generating boilerplate code - - Implementing basic models and views + - Implementing basic models, serializers, urls, and views -3. **Building Core Features** +1. **Building Core Features** - User authentication - Activity logging API - Team management - Leader board functionality -4. **Frontend Development** +1. **Frontend Development** - Setting up React components - Implementing responsive UI - Connecting to backend APIs -5. **Advanced Features and Optimization** +1. **Advanced Features and Optimization** - Adding personalized workout suggestions - Implementing caching with Redis - Optimizing database queries -6. **Wrap-up and Q&A** +1. **Wrap-up and Q&A** - Reviewing what we've built - Discussing best practices and lessons learned - Q&A session @@ -74,6 +89,7 @@ By the end of this workshop, attendees will: - Gain hands-on experience with GitHub Copilot in a real-world scenario - Learn strategies for effective AI-assisted development +- Learn how to effectively prompt GitHub Copilot to get accurate responses from GitHub Copilot - Understand how to leverage Copilot across different languages and frameworks - Develop a functional prototype of a modern web application @@ -81,4 +97,4 @@ Join us for this exciting journey into the future of software development, where In this workshop, you are going to assume to be the lead developer. You will be responsible for building the application with GitHub Copilot’s help. Good luck! -[Next: Prerequisites, assumptions, and development environment setup](../2_Prerequisites) +[Next: Prerequisite and setup of the development environment](../2_Prerequisites) diff --git a/docs/1_Story/gpt-models.png b/docs/1_Story/gpt-models.png new file mode 100644 index 0000000..2d72b3d Binary files /dev/null and b/docs/1_Story/gpt-models.png differ diff --git a/docs/2_Prerequisites/2_1_codespace-create.png b/docs/2_Prerequisites/2_2_codespace-create.png similarity index 100% rename from docs/2_Prerequisites/2_1_codespace-create.png rename to docs/2_Prerequisites/2_2_codespace-create.png diff --git a/docs/2_Prerequisites/2_3_codesapce-show-extension.png b/docs/2_Prerequisites/2_3_codesapce-show-extension.png new file mode 100644 index 0000000..e0a74a4 Binary files /dev/null and b/docs/2_Prerequisites/2_3_codesapce-show-extension.png differ diff --git a/docs/2_Prerequisites/2_4_codespace-copilot-ext-reload.png b/docs/2_Prerequisites/2_4_codespace-copilot-ext-reload.png new file mode 100644 index 0000000..ed21353 Binary files /dev/null and b/docs/2_Prerequisites/2_4_codespace-copilot-ext-reload.png differ diff --git a/docs/2_Prerequisites/README.md b/docs/2_Prerequisites/README.md index b357595..92370fe 100644 --- a/docs/2_Prerequisites/README.md +++ b/docs/2_Prerequisites/README.md @@ -1,4 +1,4 @@ -# Prerequisites and development environment setup +# Prerequisites and setup of the development environment ## Prerequisites @@ -7,9 +7,9 @@ ## OctoFit Tracker technology stack -- NodeJS: Version xxx -- ReactJS: Version xxx -- Python Django: Version xxx +- NodeJS: Version v20.17.0 +- ReactJS: Version v18.3.1 +- Python Django: Version v4.1 ## Developer environment setup @@ -24,7 +24,14 @@ but, this is what we are using for the GitHub Universe workshop #### Create a GitHub codespace -![create a GitHub codespace](./2_1_codespace-create.png) +![create a GitHub codespace](./2_2_codespace-create.png) + +#### Once the GitHub Codespace is created + +When the codespace is created you may get the following message for the GitHub Copilot Chat extension + +![show extension](./2_3_codesapce-show-extension.png)
+![copilot extension reload](./2_4_codespace-copilot-ext-reload.png) ### Option 2: Use your IDE of choice diff --git a/docs/3_GettingStarted/3_1_AskCopilotProjectCreation.png b/docs/3_GettingStarted/3_1_AskCopilotProjectCreation.png index 097f2be..6c50a3f 100644 Binary files a/docs/3_GettingStarted/3_1_AskCopilotProjectCreation.png and b/docs/3_GettingStarted/3_1_AskCopilotProjectCreation.png differ diff --git a/docs/3_GettingStarted/3_2_StepByStep.png b/docs/3_GettingStarted/3_2_StepByStep.png index 2efd3cf..7dbdbb9 100644 Binary files a/docs/3_GettingStarted/3_2_StepByStep.png and b/docs/3_GettingStarted/3_2_StepByStep.png differ diff --git a/docs/3_GettingStarted/3_3_OctoFitTrackerDirTree.png b/docs/3_GettingStarted/3_3_OctoFitTrackerDirTree.png index 9f1d00a..82e9072 100644 Binary files a/docs/3_GettingStarted/3_3_OctoFitTrackerDirTree.png and b/docs/3_GettingStarted/3_3_OctoFitTrackerDirTree.png differ diff --git a/docs/3_GettingStarted/README.md b/docs/3_GettingStarted/README.md index d868fe9..358c017 100644 --- a/docs/3_GettingStarted/README.md +++ b/docs/3_GettingStarted/README.md @@ -6,7 +6,10 @@ In this section, we will start by setting up the face of our OctoFit application ## Explain to GitHub Copilot the goals and steps -It is important to lay out a plan and provide details. Type the following prompt: +It is important to lay out a plan, provide details, and be specific.
+Type the following prompt in GitHub Copilot Chat: + +> TIP: we are going to use gpt-4o as our OpenAI GPT model for this GitHub Universe Workshop ```text I want to build an OctoFit Tracker app that will include the following: @@ -22,13 +25,36 @@ It should be in one app generate instructions in this order 1. Create the frontend and backend in the octofit-tracker directory of this repository in one command -2. the octofit-tracker/backend directory will store the django app with no subdirectories -3. setup backend python venv and install octofit-tracker/requirements.txt first -4. Create the django app directly in the directory octofit_tracker/backend -5. setup the octofit-tracker/frontend directory will store the react app with no subdirectories -6. instlal bootstrap -7. Install and setup mongodb with the 'sudo service mongodb start' and 'sudo service mongodb status' - +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 +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 +7. Install bootstrap and import it +8. Install mongodb via 'apt-get' and setup mongodb with the 'sudo service mongodb start' and 'sudo service mongodb status' + +Tha directory tree for the OctoFit Tracker App + +octofit-tracker/ +├── backend/ +│ ├── venv/ +│ ├── octofit_tracker/ +│ │ ├── __init__.py +│ │ ├── models.py +│ │ ├── serializers.py +│ │ ├── settings.py +│ │ ├── views.py +│ │ ├── urls.py +│ │ ├── wsgi.py +│ │ └── asgi.py +└── frontend/ + ├── node_modules/ + ├── public/ + ├── src/ + ├── package.json + └── README.md + +All of the backend django app will be in octofit_tracker and do NOT create another app of any kind Use a Python virtual environment and install all python dependencies from file octofit-tracker/requirements.txt in this workspace @@ -43,10 +69,10 @@ Let's think about this step by step Important to avoid using public code and we do NOT need to initialize the git repository ``` -![create project plan](./3_1_AskCopilotProjectCreation.png) +![create project plan](./3_1_AskCopilotProjectCreation.png)
-![step by step](./3_2_StepByStep.png) +![step by step](./3_2_StepByStep.png)
-![octofit-tracker app directory tree](./3_3_OctoFitTrackerDirTree.png) +![octofit-tracker app directory tree](./3_3_OctoFitTrackerDirTree.png)
[Back :: Previous: Prerequisites and development environment setup](../2_Prerequisites) | [Next :: OctoFit app backend setup](../4_BackendSettings/) diff --git a/docs/4_BackendSettings/4_1_OctoFitTrackerBackendFiles.png b/docs/4_BackendSettings/4_1_OctoFitTrackerBackendFiles.png new file mode 100644 index 0000000..b7a9934 Binary files /dev/null and b/docs/4_BackendSettings/4_1_OctoFitTrackerBackendFiles.png differ diff --git a/docs/4_BackendSettings/4_1_OctoFitTrackerBackendSettings.png b/docs/4_BackendSettings/4_1_OctoFitTrackerBackendSettings.png deleted file mode 100644 index c43cb53..0000000 Binary files a/docs/4_BackendSettings/4_1_OctoFitTrackerBackendSettings.png and /dev/null differ diff --git a/docs/4_BackendSettings/4_2_CreateModelsViewsSerializers.png b/docs/4_BackendSettings/4_2_CreateModelsViewsSerializers.png deleted file mode 100644 index 1a81768..0000000 Binary files a/docs/4_BackendSettings/4_2_CreateModelsViewsSerializers.png and /dev/null differ diff --git a/docs/4_BackendSettings/4_2_UpdateSettings.png b/docs/4_BackendSettings/4_2_UpdateSettings.png new file mode 100644 index 0000000..1ad98b6 Binary files /dev/null and b/docs/4_BackendSettings/4_2_UpdateSettings.png differ diff --git a/docs/4_BackendSettings/4_3_CreateBackendProjectFiles1.png b/docs/4_BackendSettings/4_3_CreateBackendProjectFiles1.png new file mode 100644 index 0000000..c7a961d Binary files /dev/null and b/docs/4_BackendSettings/4_3_CreateBackendProjectFiles1.png differ diff --git a/docs/4_BackendSettings/4_3_CreateBackendProjectFiles2.png b/docs/4_BackendSettings/4_3_CreateBackendProjectFiles2.png new file mode 100644 index 0000000..e310f7e Binary files /dev/null and b/docs/4_BackendSettings/4_3_CreateBackendProjectFiles2.png differ diff --git a/docs/4_BackendSettings/4_3_OctoFitAppBackendUrls.png b/docs/4_BackendSettings/4_3_OctoFitAppBackendUrls.png deleted file mode 100644 index e777a1e..0000000 Binary files a/docs/4_BackendSettings/4_3_OctoFitAppBackendUrls.png and /dev/null differ diff --git a/docs/4_BackendSettings/README.md b/docs/4_BackendSettings/README.md index 1bbb053..e337338 100644 --- a/docs/4_BackendSettings/README.md +++ b/docs/4_BackendSettings/README.md @@ -1,20 +1,55 @@ # Setup database in settings.py, models, views, serializers, and populate data -## Use Copilot Chat and paste the following +## Use Copilot Chat + +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. settings.py in our django project for mongodb octofit_db database including localhost and the port -2. settings.py in our django project setup for all installed apps. ex djongo, octofit_tracker -3. In octofit_tracker project setup models, views, and serializers for users, teams, activity, leaderboard, and workouts +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 +``` + +![OctoFit Tracker backend files](./4_1_OctoFitTrackerBackendFiles.png)
+![update settings](./4_2_UpdateSettings.png)
+![project files - models and serializers](./4_3_CreateBackendProjectFiles1.png)
+![project files - views and urls](./4_3_CreateBackendProjectFiles2.png)
+ +### Sample settings.py + +```json +# FILE: octofit_tracker/settings.py + +DATABASES = { + 'default': { + 'ENGINE': 'djongo', + 'NAME': 'octofit_db', + 'HOST': 'localhost', + 'PORT': 27017, + } +} ``` -![octofit tracker backend settings](./4_1_OctoFitTrackerBackendSettings.png) -![create models serializers](./4_2_CreateModelsViewsSerializers.png) -![backend urls populate data](./4_3_OctoFitAppBackendUrls.png) +```json +# FILE: octofit_tracker/settings.py + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'djongo', + 'octofit_tracker', +] +``` -### Sample code for models.py, serializers.py, and views.py +### Sample code for models.py, serializers.py, views.py, and urls.py #### models.py @@ -22,48 +57,40 @@ In our next steps lets think step by step and setup the following in this order # FILE: octofit-tracker/backend/octofit_tracker/models.py from django.db import models -from django.contrib.auth.models import AbstractUser, Group, Permission -from djongo import models as djongo_models +from django.contrib.auth.models import AbstractUser class User(AbstractUser): - id = djongo_models.ObjectIdField(primary_key=True) + email = models.EmailField(unique=True) groups = models.ManyToManyField( - Group, - related_name='octofit_users', + '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( - Permission, - related_name='octofit_users_permissions', + 'auth.Permission', + related_name='octofit_tracker_user_set', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions', ) class Team(models.Model): - id = djongo_models.ObjectIdField(primary_key=True) name = models.CharField(max_length=100) - members = models.ManyToManyField(User) class Activity(models.Model): - id = djongo_models.ObjectIdField(primary_key=True) - user = models.ForeignKey(User, on_delete=models.CASCADE) - activity_type = models.CharField(max_length=50) - duration = models.DurationField() - timestamp = models.DateTimeField(auto_now_add=True) + activity_type = models.CharField(max_length=100) class Workout(models.Model): - id = djongo_models.ObjectIdField(primary_key=True) name = models.CharField(max_length=100) - description = models.TextField() - suggested_duration = models.DurationField() + duration = models.DurationField() + activity = models.ForeignKey(Activity, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) class Leaderboard(models.Model): - id = djongo_models.ObjectIdField(primary_key=True) user = models.ForeignKey(User, on_delete=models.CASCADE) - total_points = models.IntegerField() + score = models.IntegerField() ``` #### serializers.py @@ -72,7 +99,7 @@ class Leaderboard(models.Model): # FILE: octofit-tracker/backend/octofit_tracker/serializers.py from rest_framework import serializers -from .models import User, Team, Activity, Leaderboard, Workout +from .models import User, Team, Activity, Workout, Leaderboard class UserSerializer(serializers.ModelSerializer): class Meta: @@ -89,21 +116,21 @@ class ActivitySerializer(serializers.ModelSerializer): model = Activity fields = '__all__' -class LeaderboardSerializer(serializers.ModelSerializer): +class WorkoutSerializer(serializers.ModelSerializer): class Meta: - model = Leaderboard + model = Workout fields = '__all__' -class WorkoutSerializer(serializers.ModelSerializer): +class LeaderboardSerializer(serializers.ModelSerializer): class Meta: - model = Workout + model = Leaderboard fields = '__all__' ``` #### views.py ```python -# FILE: octofit-tracker/backend/octofit_tracker/views.py +# FILE: octofit_tracker/views.py from rest_framework import viewsets from .models import User, Team, Activity, Leaderboard, Workout @@ -130,4 +157,27 @@ class WorkoutViewSet(viewsets.ModelViewSet): serializer_class = WorkoutSerializer ``` +#### urls.py + +```python +# FILE: octofit_tracker/urls.py + +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 + +router = DefaultRouter() +router.register(r'users', UserViewSet) +router.register(r'teams', TeamViewSet) +router.register(r'activities', ActivityViewSet) +router.register(r'leaderboards', LeaderboardViewSet) +router.register(r'workouts', WorkoutViewSet) + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include(router.urls)), +] +``` + [Back :: Previous: Getting started](../3_GettingStarted) | [Next :: Populate database with data via manage.py](../5_PopulateDBwData) diff --git a/docs/5_PopulateDBwData/5_2_PopulateDbCodeOctoFitAppSecond.png b/docs/5_PopulateDBwData/5_2_PopulateDbCodeOctoFitAppSecond.png index 46e4457..ad5210d 100644 Binary files a/docs/5_PopulateDBwData/5_2_PopulateDbCodeOctoFitAppSecond.png and b/docs/5_PopulateDBwData/5_2_PopulateDbCodeOctoFitAppSecond.png differ diff --git a/docs/5_PopulateDBwData/5_3_MigratePopulateDb.png b/docs/5_PopulateDBwData/5_3_MigratePopulateDb.png index 9d3c121..25f0d8a 100644 Binary files a/docs/5_PopulateDBwData/5_3_MigratePopulateDb.png and b/docs/5_PopulateDBwData/5_3_MigratePopulateDb.png differ diff --git a/docs/5_PopulateDBwData/README.md b/docs/5_PopulateDBwData/README.md index 4b985fe..39e0f5f 100644 --- a/docs/5_PopulateDBwData/README.md +++ b/docs/5_PopulateDBwData/README.md @@ -2,7 +2,7 @@ ## Example of not being specific -Type the following prompt: +> 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 @@ -16,8 +16,14 @@ Let's use manage.py to get everything setup we need to create init.py for popula ### Let's be more specific and ask Copilot to update the output with the octofit_tracker app name +Type the following prompt in GitHub Copilot Chat: + ```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 in the octofit_tracker project +Let's use manage.py to get the database setup and populated + +- Populate the database with super hero users +- Create full setup for a command populate_db.py +- Include steps to migrate in the octofit_tracker project ``` ![populate db Code app name octofit_tracker](./5_2_PopulateDbCodeOctoFitAppSecond.png) @@ -35,25 +41,19 @@ class Command(BaseCommand): help = 'Populate the database with initial data' def handle(self, *args, **kwargs): - # Create some users - user1 = User.objects.create_user(username='user1', email='user1@example.com', password='password') - user2 = User.objects.create_user(username='user2', email='user2@example.com', password='password') - - # Create a team - team = Team.objects.create(name='Team A') - team.members.set([user1, user2]) - - # Create some activities - Activity.objects.create(user=user1, activity_type='Running', duration=timedelta(minutes=30)) - Activity.objects.create(user=user2, activity_type='Cycling', duration=timedelta(hours=1)) - - # Create some workouts - Workout.objects.create(name='Workout A', description='Description A', suggested_duration=timedelta(minutes=45)) - Workout.objects.create(name='Workout B', description='Description B', suggested_duration=timedelta(hours=1, minutes=15)) - - # Create leaderboard entries - Leaderboard.objects.create(user=user1, total_points=100) - Leaderboard.objects.create(user=user2, total_points=150) + # Create Superhero Users + users = [ + {'username': 'superman', 'email': 'superman@heroes.com', 'password': 'superpassword'}, + {'username': 'batman', 'email': 'batman@heroes.com', 'password': 'batpassword'}, + {'username': 'wonderwoman', 'email': 'wonderwoman@heroes.com', 'password': 'wonderpassword'}, + ] + + for user_data in users: + user, created = User.objects.get_or_create(**user_data) + if created: + self.stdout.write(self.style.SUCCESS(f"User {user.username} created")) + + # Add more data population logic here if needed self.stdout.write(self.style.SUCCESS('Database populated successfully')) ``` diff --git a/images/0_FinalResult.jpg b/images/0_FinalResult.jpg deleted file mode 100644 index 6acb853..0000000 Binary files a/images/0_FinalResult.jpg and /dev/null differ diff --git a/images/1_AskCopilot4Outline.jpg b/images/1_AskCopilot4Outline.jpg deleted file mode 100644 index 0d45383..0000000 Binary files a/images/1_AskCopilot4Outline.jpg and /dev/null differ diff --git a/images/2_1_AskGenerateHTMLCSS.jpg b/images/2_1_AskGenerateHTMLCSS.jpg deleted file mode 100644 index 50197a1..0000000 Binary files a/images/2_1_AskGenerateHTMLCSS.jpg and /dev/null differ diff --git a/images/2_2_AskGenerateHTMLCSS.jpg b/images/2_2_AskGenerateHTMLCSS.jpg deleted file mode 100644 index 9d48878..0000000 Binary files a/images/2_2_AskGenerateHTMLCSS.jpg and /dev/null differ diff --git a/octofit-tracker/requirements.txt b/octofit-tracker/requirements.txt index 9cc2bee..70e594d 100644 --- a/octofit-tracker/requirements.txt +++ b/octofit-tracker/requirements.txt @@ -1,5 +1,4 @@ Django==4.1 -#bson==0.5.10 djangorestframework==3.14.0 django-allauth==0.51.0 dj-rest-auth