From f25f797a57c01eea138e0c712dd39dff78b7b044 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sat, 5 Jul 2025 13:55:06 +0300 Subject: [PATCH 01/12] Initialize `app_run` Django app and register it in base settings. --- app_run/__init__.py | 0 app_run/admin.py | 3 +++ app_run/apps.py | 6 ++++++ app_run/migrations/__init__.py | 0 app_run/models.py | 3 +++ app_run/tests.py | 3 +++ app_run/views.py | 3 +++ project_run/settings/base.py | 1 + 8 files changed, 19 insertions(+) create mode 100644 app_run/__init__.py create mode 100644 app_run/admin.py create mode 100644 app_run/apps.py create mode 100644 app_run/migrations/__init__.py create mode 100644 app_run/models.py create mode 100644 app_run/tests.py create mode 100644 app_run/views.py diff --git a/app_run/__init__.py b/app_run/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app_run/admin.py b/app_run/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/app_run/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/app_run/apps.py b/app_run/apps.py new file mode 100644 index 00000000..660c9321 --- /dev/null +++ b/app_run/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AppRunConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'app_run' diff --git a/app_run/migrations/__init__.py b/app_run/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app_run/models.py b/app_run/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/app_run/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/app_run/tests.py b/app_run/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/app_run/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app_run/views.py b/app_run/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/app_run/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/project_run/settings/base.py b/project_run/settings/base.py index fac33f1f..f2011f64 100644 --- a/project_run/settings/base.py +++ b/project_run/settings/base.py @@ -37,6 +37,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'app_run', ] MIDDLEWARE = [ From 79fffe5236ad22ccbcc9de0a8dc019e09158f12b Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sat, 5 Jul 2025 14:15:51 +0300 Subject: [PATCH 02/12] Add Django Rest Framework to project dependencies and settings --- project_run/settings/base.py | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/project_run/settings/base.py b/project_run/settings/base.py index f2011f64..041808c4 100644 --- a/project_run/settings/base.py +++ b/project_run/settings/base.py @@ -37,6 +37,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', 'app_run', ] diff --git a/requirements.txt b/requirements.txt index f1b0b31f..dc468b54 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ Django==5.2 +djangorestframework==3.16.0 psycopg2-binary==2.9.10 django-storages==1.14.6 boto3==1.37.37 From 73908008c420c3dd4d48a90ad44b24b1ddbe17b7 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:07:48 +0300 Subject: [PATCH 03/12] Add `company_details` API endpoint to provide company metadata --- app_run/views.py | 13 +++++++++++-- project_run/settings/base.py | 4 ++++ project_run/urls.py | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app_run/views.py b/app_run/views.py index 91ea44a2..eb80e70c 100644 --- a/app_run/views.py +++ b/app_run/views.py @@ -1,3 +1,12 @@ -from django.shortcuts import render +from rest_framework.decorators import api_view +from rest_framework.response import Response +from django.conf import settings -# Create your views here. +@api_view(['GET']) +def company_details(request): + details = { + 'company_name': settings.COMPANY_NAME, + 'slogan': settings.SLOGAN, + 'contacts': settings.CONTACTS + } + return Response(details) diff --git a/project_run/settings/base.py b/project_run/settings/base.py index 041808c4..0ac13f35 100644 --- a/project_run/settings/base.py +++ b/project_run/settings/base.py @@ -112,3 +112,7 @@ # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +COMPANY_NAME = 'Running Skuf' +SLOGAN = 'Run, Skuf, Run' +CONTACTS = '@pan_ded' diff --git a/project_run/urls.py b/project_run/urls.py index acda9f53..0701a74f 100644 --- a/project_run/urls.py +++ b/project_run/urls.py @@ -16,9 +16,9 @@ """ from django.contrib import admin from django.urls import path -from django.conf.urls.static import static -from django.conf import settings +from app_run.views import company_details urlpatterns = [ path('admin/', admin.site.urls), + path('api/company_details/', company_details) ] \ No newline at end of file From 32923693ac30736bad70457f69438b5ed3210356 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:44:59 +0300 Subject: [PATCH 04/12] Add `Run` model, serializer, and API endpoint for managing runs --- app_run/migrations/0001_initial.py | 26 ++++++++++++++++++++++++++ app_run/models.py | 9 ++++++++- app_run/serializers.py | 9 +++++++++ app_run/views.py | 9 +++++++++ project_run/urls.py | 6 ++++-- 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 app_run/migrations/0001_initial.py create mode 100644 app_run/serializers.py diff --git a/app_run/migrations/0001_initial.py b/app_run/migrations/0001_initial.py new file mode 100644 index 00000000..cb564490 --- /dev/null +++ b/app_run/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2 on 2025-07-05 16:29 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Run', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('comment', models.TextField(blank=True)), + ('athlete', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/app_run/models.py b/app_run/models.py index 71a83623..43d7780f 100644 --- a/app_run/models.py +++ b/app_run/models.py @@ -1,3 +1,10 @@ from django.db import models -# Create your models here. + +class Run(models.Model): + athlete = models.ForeignKey('auth.User', on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True) + comment = models.TextField(blank=True) + + def __str__(self) -> str: + return f"Run by {self.athlete.username} on {self.created_at.date()}" diff --git a/app_run/serializers.py b/app_run/serializers.py new file mode 100644 index 00000000..cc7bcc19 --- /dev/null +++ b/app_run/serializers.py @@ -0,0 +1,9 @@ +from rest_framework.serializers import ModelSerializer + +from app_run.models import Run + + +class RunSerializer(ModelSerializer): + class Meta: + model = Run + fields = '__all__' diff --git a/app_run/views.py b/app_run/views.py index eb80e70c..ac6bb189 100644 --- a/app_run/views.py +++ b/app_run/views.py @@ -1,7 +1,11 @@ from rest_framework.decorators import api_view +from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from django.conf import settings +from app_run.models import Run +from app_run.serializers import RunSerializer + @api_view(['GET']) def company_details(request): details = { @@ -10,3 +14,8 @@ def company_details(request): 'contacts': settings.CONTACTS } return Response(details) + + +class RunViewSet(ModelViewSet): + queryset = Run.objects.all() + serializer_class = RunSerializer diff --git a/project_run/urls.py b/project_run/urls.py index 0701a74f..4f399790 100644 --- a/project_run/urls.py +++ b/project_run/urls.py @@ -16,9 +16,11 @@ """ from django.contrib import admin from django.urls import path -from app_run.views import company_details + +from app_run.views import company_details, RunViewSet urlpatterns = [ path('admin/', admin.site.urls), - path('api/company_details/', company_details) + path('api/company_details/', company_details), + path('api/runs/', RunViewSet.as_view({'get': 'list'})), ] \ No newline at end of file From 17130a9d88b7bf36df1f97dc0bb7ed6a087dd2c0 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:58:34 +0300 Subject: [PATCH 05/12] Enable `POST` method for `RunViewSet` and register `Run` model in admin panel --- app_run/admin.py | 7 ++++++- project_run/urls.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app_run/admin.py b/app_run/admin.py index 8c38f3f3..686353a0 100644 --- a/app_run/admin.py +++ b/app_run/admin.py @@ -1,3 +1,8 @@ from django.contrib import admin -# Register your models here. +from app_run.models import Run + + +@admin.register(Run) +class RunAdmin(admin.ModelAdmin): + list_display = ('athlete', 'created_at', 'comment') diff --git a/project_run/urls.py b/project_run/urls.py index 4f399790..39a298cc 100644 --- a/project_run/urls.py +++ b/project_run/urls.py @@ -22,5 +22,5 @@ urlpatterns = [ path('admin/', admin.site.urls), path('api/company_details/', company_details), - path('api/runs/', RunViewSet.as_view({'get': 'list'})), + path('api/runs/', RunViewSet.as_view({'get': 'list', 'post': 'create'})), ] \ No newline at end of file From 4d7948b01b8d3845c539cd8fc72cc822ea98d98a Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sat, 5 Jul 2025 20:02:14 +0300 Subject: [PATCH 06/12] Add detailed CRUD endpoints for `RunViewSet` in `urls.py` --- project_run/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/project_run/urls.py b/project_run/urls.py index 39a298cc..fc35d809 100644 --- a/project_run/urls.py +++ b/project_run/urls.py @@ -23,4 +23,5 @@ path('admin/', admin.site.urls), path('api/company_details/', company_details), path('api/runs/', RunViewSet.as_view({'get': 'list', 'post': 'create'})), + path('api/runs//', RunViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})), ] \ No newline at end of file From 38f83bd9084b6573a597f5a7c6e34abda0c9ea06 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:15:55 +0300 Subject: [PATCH 07/12] Add `UserViewSet` with filtered user retrieval and update `urls.py` with user API endpoint --- app_run/serializers.py | 8 ++++++++ app_run/views.py | 42 ++++++++++++++++++++++++++++++++++++++++-- project_run/urls.py | 3 ++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/app_run/serializers.py b/app_run/serializers.py index cc7bcc19..6dac8d1c 100644 --- a/app_run/serializers.py +++ b/app_run/serializers.py @@ -1,3 +1,5 @@ +from django.contrib.auth.models import User + from rest_framework.serializers import ModelSerializer from app_run.models import Run @@ -7,3 +9,9 @@ class RunSerializer(ModelSerializer): class Meta: model = Run fields = '__all__' + + +class UserSerializer(ModelSerializer): + class Meta: + model = User + fields = ['id', 'username', 'date_joined', 'first_name', 'last_name'] diff --git a/app_run/views.py b/app_run/views.py index ac6bb189..6b993912 100644 --- a/app_run/views.py +++ b/app_run/views.py @@ -1,10 +1,12 @@ from rest_framework.decorators import api_view -from rest_framework.viewsets import ModelViewSet +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.response import Response from django.conf import settings +from django.contrib.auth.models import User +from django.db.models import QuerySet from app_run.models import Run -from app_run.serializers import RunSerializer +from app_run.serializers import RunSerializer, UserSerializer @api_view(['GET']) def company_details(request): @@ -19,3 +21,39 @@ def company_details(request): class RunViewSet(ModelViewSet): queryset = Run.objects.all() serializer_class = RunSerializer + + +class UserViewSet(ReadOnlyModelViewSet): + """ + ViewSet for retrieving user information. + + This viewset provides 'list' and 'retrieve' actions for User objects, + excluding superusers. It supports filtering by user type ('coach' or 'athlete') + via query parameters. + """ + serializer_class = UserSerializer + queryset = User.objects.filter(is_superuser=False) + lookup_field = 'is_staff' + + def get_queryset(self) -> 'QuerySet[User]': + """ + Filter the queryset based on the 'type' query parameter. + + Returns: + QuerySet[User]: Filtered queryset of User objects. + - If type='coach': returns staff users + - If type='athlete': returns non-staff users + - Otherwise: returns all non-superuser users + """ + user_type = self.request.query_params.get('type') + + # Use a dictionary mapping for cleaner, more maintainable code + type_filters = { + 'coach': {'is_staff': True}, + 'athlete': {'is_staff': False} + } + + if user_type in type_filters: + return self.queryset.filter(**type_filters[user_type]) + + return self.queryset \ No newline at end of file diff --git a/project_run/urls.py b/project_run/urls.py index fc35d809..0f567dc2 100644 --- a/project_run/urls.py +++ b/project_run/urls.py @@ -17,11 +17,12 @@ from django.contrib import admin from django.urls import path -from app_run.views import company_details, RunViewSet +from app_run.views import company_details, RunViewSet, UserViewSet urlpatterns = [ path('admin/', admin.site.urls), path('api/company_details/', company_details), path('api/runs/', RunViewSet.as_view({'get': 'list', 'post': 'create'})), path('api/runs//', RunViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})), + path('api/users/', UserViewSet.as_view({'get': 'list'})), ] \ No newline at end of file From 29f79565152740fb7f679a1de897ee9cc2211d74 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:24:43 +0300 Subject: [PATCH 08/12] Add `type` field to `UserSerializer` to differentiate user roles --- app_run/serializers.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app_run/serializers.py b/app_run/serializers.py index 6dac8d1c..8833a0df 100644 --- a/app_run/serializers.py +++ b/app_run/serializers.py @@ -1,6 +1,6 @@ from django.contrib.auth.models import User -from rest_framework.serializers import ModelSerializer +from rest_framework.serializers import ModelSerializer, SerializerMethodField from app_run.models import Run @@ -12,6 +12,13 @@ class Meta: class UserSerializer(ModelSerializer): + type = SerializerMethodField() + class Meta: model = User - fields = ['id', 'username', 'date_joined', 'first_name', 'last_name'] + fields = ['id', 'username', 'date_joined', 'first_name', 'last_name', 'type'] + + def get_type(self, obj): + if obj.is_staff: + return 'coach' + return 'athlete' From 1dbd2ac7f8b20caf6f05acbdd4adf3e0c586d250 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:54:03 +0300 Subject: [PATCH 09/12] Refactor serializers and optimize `RunViewSet` with `select_related`; add database query logging in local settings --- app_run/serializers.py | 20 +++++++++++--------- app_run/views.py | 2 +- project_run/settings/local.py | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/app_run/serializers.py b/app_run/serializers.py index 8833a0df..86c34533 100644 --- a/app_run/serializers.py +++ b/app_run/serializers.py @@ -1,18 +1,12 @@ from django.contrib.auth.models import User -from rest_framework.serializers import ModelSerializer, SerializerMethodField +from rest_framework import serializers from app_run.models import Run -class RunSerializer(ModelSerializer): - class Meta: - model = Run - fields = '__all__' - - -class UserSerializer(ModelSerializer): - type = SerializerMethodField() +class UserSerializer(serializers.ModelSerializer): + type = serializers.SerializerMethodField() class Meta: model = User @@ -22,3 +16,11 @@ def get_type(self, obj): if obj.is_staff: return 'coach' return 'athlete' + + +class RunSerializer(serializers.ModelSerializer): + athlete_data = UserSerializer(source='athlete', read_only=True) + + class Meta: + model = Run + fields = '__all__' diff --git a/app_run/views.py b/app_run/views.py index 6b993912..70897c78 100644 --- a/app_run/views.py +++ b/app_run/views.py @@ -19,7 +19,7 @@ def company_details(request): class RunViewSet(ModelViewSet): - queryset = Run.objects.all() + queryset = Run.objects.select_related('athlete').all() serializer_class = RunSerializer diff --git a/project_run/settings/local.py b/project_run/settings/local.py index 128ab27b..ec340413 100644 --- a/project_run/settings/local.py +++ b/project_run/settings/local.py @@ -8,4 +8,21 @@ 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } +} + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + }, + }, + "loggers": { + "django.db.backends": { + "handlers": ["console"], + "level": "DEBUG", + }, + }, } \ No newline at end of file From 85537e8558d998fb405a0eb2b633014d58099b20 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Mon, 7 Jul 2025 00:01:28 +0300 Subject: [PATCH 10/12] Refactor `RunSerializer` to use `RunUserSerializer` for improved athlete data representation. --- app_run/serializers.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app_run/serializers.py b/app_run/serializers.py index 86c34533..a82fe8f1 100644 --- a/app_run/serializers.py +++ b/app_run/serializers.py @@ -4,6 +4,19 @@ from app_run.models import Run +class RunUserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['id', 'username', 'first_name', 'last_name'] + + +class RunSerializer(serializers.ModelSerializer): + athlete_data = RunUserSerializer(source='athlete', read_only=True) + + class Meta: + model = Run + fields = '__all__' + class UserSerializer(serializers.ModelSerializer): type = serializers.SerializerMethodField() @@ -16,11 +29,3 @@ def get_type(self, obj): if obj.is_staff: return 'coach' return 'athlete' - - -class RunSerializer(serializers.ModelSerializer): - athlete_data = UserSerializer(source='athlete', read_only=True) - - class Meta: - model = Run - fields = '__all__' From e1226ab9abcd1daf12b81687a5a195fd4b9abdd0 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Wed, 9 Jul 2025 22:54:29 +0300 Subject: [PATCH 11/12] Add search functionality to `RunViewSet` and `UserViewSet` through `SearchFilter` --- app_run/views.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app_run/views.py b/app_run/views.py index 70897c78..bb0bf5b7 100644 --- a/app_run/views.py +++ b/app_run/views.py @@ -1,6 +1,7 @@ from rest_framework.decorators import api_view from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.response import Response +from rest_framework.filters import SearchFilter from django.conf import settings from django.contrib.auth.models import User from django.db.models import QuerySet @@ -21,6 +22,8 @@ def company_details(request): class RunViewSet(ModelViewSet): queryset = Run.objects.select_related('athlete').all() serializer_class = RunSerializer + filter_backends = [SearchFilter] + search_fields = ['first_name', 'last_name'] class UserViewSet(ReadOnlyModelViewSet): @@ -34,6 +37,8 @@ class UserViewSet(ReadOnlyModelViewSet): serializer_class = UserSerializer queryset = User.objects.filter(is_superuser=False) lookup_field = 'is_staff' + filter_backends = [SearchFilter] + search_fields = ['first_name', 'last_name'] def get_queryset(self) -> 'QuerySet[User]': """ From cfe14b14daa342bcdca0bd16634d3f601f79f155 Mon Sep 17 00:00:00 2001 From: Alexander Tyapkin <4105222+Tyapkin@users.noreply.github.com> Date: Fri, 11 Jul 2025 23:39:58 +0300 Subject: [PATCH 12/12] Add `status` field to `Run` model with choices; implement `start` and `stop` API endpoints for run management --- app_run/admin.py | 1 + app_run/migrations/0002_run_status.py | 18 +++++++++++++++++ app_run/models.py | 10 +++++++++ app_run/views.py | 29 ++++++++++++++++++++++++++- project_run/urls.py | 6 ++++-- 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 app_run/migrations/0002_run_status.py diff --git a/app_run/admin.py b/app_run/admin.py index 686353a0..bd39f3fb 100644 --- a/app_run/admin.py +++ b/app_run/admin.py @@ -6,3 +6,4 @@ @admin.register(Run) class RunAdmin(admin.ModelAdmin): list_display = ('athlete', 'created_at', 'comment') + list_filter = ('status',) diff --git a/app_run/migrations/0002_run_status.py b/app_run/migrations/0002_run_status.py new file mode 100644 index 00000000..7d8c5e22 --- /dev/null +++ b/app_run/migrations/0002_run_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2 on 2025-07-10 18:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app_run', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='run', + name='status', + field=models.CharField(choices=[('init', 'Init'), ('in_progress', 'In Progress'), ('finished', 'Finished')], default='init', max_length=11), + ), + ] diff --git a/app_run/models.py b/app_run/models.py index 43d7780f..942d3b82 100644 --- a/app_run/models.py +++ b/app_run/models.py @@ -2,9 +2,19 @@ class Run(models.Model): + class RunStatus(models.TextChoices): + INIT = 'init' + IN_PROGRESS = 'in_progress' + FINISHED = 'finished' + athlete = models.ForeignKey('auth.User', on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) comment = models.TextField(blank=True) + status = models.CharField( + max_length=11, + choices=RunStatus, + default=RunStatus.INIT + ) def __str__(self) -> str: return f"Run by {self.athlete.username} on {self.created_at.date()}" diff --git a/app_run/views.py b/app_run/views.py index bb0bf5b7..75c1a36d 100644 --- a/app_run/views.py +++ b/app_run/views.py @@ -1,10 +1,13 @@ from rest_framework.decorators import api_view from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.response import Response +from rest_framework.views import APIView from rest_framework.filters import SearchFilter +from rest_framework import status from django.conf import settings from django.contrib.auth.models import User from django.db.models import QuerySet +from django.shortcuts import get_object_or_404 from app_run.models import Run from app_run.serializers import RunSerializer, UserSerializer @@ -61,4 +64,28 @@ def get_queryset(self) -> 'QuerySet[User]': if user_type in type_filters: return self.queryset.filter(**type_filters[user_type]) - return self.queryset \ No newline at end of file + return self.queryset + + +class RunStartAPIView(APIView): + def put(self, request, pk, format=None): + run = get_object_or_404(Run, pk=pk) + if run.status not in [Run.RunStatus.IN_PROGRESS, Run.RunStatus.FINISHED]: + serializer = RunSerializer(run, data={'status': Run.RunStatus.IN_PROGRESS}, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + + return Response({'message': 'Run already started'}, status=status.HTTP_400_BAD_REQUEST) + + +class RunStopAPIView(APIView): + def put(self, request, pk, format=None): + run = get_object_or_404(Run, pk=pk) + if run.status == Run.RunStatus.IN_PROGRESS: + serializer = RunSerializer(run, data={'status': Run.RunStatus.FINISHED}, partial=True) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + + return Response({'message': 'Run not started'}, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/project_run/urls.py b/project_run/urls.py index 0f567dc2..cdb1634b 100644 --- a/project_run/urls.py +++ b/project_run/urls.py @@ -17,12 +17,14 @@ from django.contrib import admin from django.urls import path -from app_run.views import company_details, RunViewSet, UserViewSet +from app_run.views import RunStartAPIView, RunStopAPIView, company_details, UserViewSet, RunViewSet urlpatterns = [ path('admin/', admin.site.urls), path('api/company_details/', company_details), path('api/runs/', RunViewSet.as_view({'get': 'list', 'post': 'create'})), - path('api/runs//', RunViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})), + path('api/runs//', RunViewSet.as_view({'get': 'retrieve'})), path('api/users/', UserViewSet.as_view({'get': 'list'})), + path('api/runs//start/', RunStartAPIView.as_view()), + path('api/runs//stop/', RunStopAPIView.as_view()), ] \ No newline at end of file