From 882089a2422ef610dd152c21c55f51662ced3efe Mon Sep 17 00:00:00 2001 From: Lukasz Chmielewski Date: Thu, 6 Jun 2024 09:19:16 +0200 Subject: [PATCH 1/2] added tennis app with the add player form --- pyproject.toml | 14 ++--- {app => src}/app/__app_configs.py | 5 +- {app => src}/app/__init__.py | 0 {app => src}/app/asgi.py | 0 {app => src}/app/settings.py | 1 + {app => src}/app/urls.py | 3 +- {app => src}/app/views.py | 0 {app => src}/app/wsgi.py | 0 {app => src}/calculator/__calc_config.py | 1 + {app => src}/calculator/__exceptions.py | 0 {app => src}/calculator/__init__.py | 0 {app => src}/calculator/admin.py | 0 {app => src}/calculator/apps.py | 0 {app => src}/calculator/db/__init__.py | 0 {app => src}/calculator/db/models.py | 0 .../calculator/migrations/__init__.py | 0 {app => src}/calculator/models/__init__.py | 0 {app => src}/calculator/models/operation.py | 0 {app => src}/calculator/service/__init__.py | 0 {app => src}/calculator/service/operations.py | 0 {app => src}/calculator/templates/__init__.py | 0 .../templates/calculator/__init__.py | 0 .../templates/calculator/calculate.html | 0 .../templates/calculator/enter_numbers.html | 0 .../calculator/invalid_operation.html | 0 {app => src}/calculator/tests/__init__.py | 0 .../calculator/tests/services/__init__.py | 0 .../tests/services/tests_operations.py | 0 .../calculator/tests/views/__init__.py | 0 .../calculator/tests/views/test_calculate.py | 0 .../calculator/tests/views/test_health.py | 0 {app => src}/calculator/urls.py | 0 {app => src}/calculator/views/__init__.py | 0 {app => src}/calculator/views/calculate.py | 4 +- {app => src}/calculator/views/health.py | 0 {app => src}/manage.py | 0 src/{ => tennis}/__init__.py | 0 src/tennis/__tennis_config.py | 58 +++++++++++++++++++ src/tennis/admin.py | 3 + src/tennis/apps.py | 6 ++ .../forms}/__init__.py | 0 src/tennis/forms/player.py | 11 ++++ src/tennis/migrations/0001_initial.py | 33 +++++++++++ {tests => src/tennis/migrations}/__init__.py | 0 src/tennis/models/__init__.py | 0 src/tennis/models/player.py | 13 +++++ src/tennis/templates/__init__.py | 0 src/tennis/templates/tennis/__init__.py | 0 src/tennis/templates/tennis/add_player.html | 33 +++++++++++ src/tennis/templates/tennis/success.html | 14 +++++ src/tennis/tests/__init__.py | 0 src/tennis/urls.py | 8 +++ src/tennis/views/__init__.py | 0 src/tennis/views/add_player.py | 26 +++++++++ 54 files changed, 220 insertions(+), 13 deletions(-) rename {app => src}/app/__app_configs.py (93%) rename {app => src}/app/__init__.py (100%) rename {app => src}/app/asgi.py (100%) rename {app => src}/app/settings.py (99%) rename {app => src}/app/urls.py (88%) rename {app => src}/app/views.py (100%) rename {app => src}/app/wsgi.py (100%) rename {app => src}/calculator/__calc_config.py (97%) rename {app => src}/calculator/__exceptions.py (100%) rename {app => src}/calculator/__init__.py (100%) rename {app => src}/calculator/admin.py (100%) rename {app => src}/calculator/apps.py (100%) rename {app => src}/calculator/db/__init__.py (100%) rename {app => src}/calculator/db/models.py (100%) rename {app => src}/calculator/migrations/__init__.py (100%) rename {app => src}/calculator/models/__init__.py (100%) rename {app => src}/calculator/models/operation.py (100%) rename {app => src}/calculator/service/__init__.py (100%) rename {app => src}/calculator/service/operations.py (100%) rename {app => src}/calculator/templates/__init__.py (100%) rename {app => src}/calculator/templates/calculator/__init__.py (100%) rename {app => src}/calculator/templates/calculator/calculate.html (100%) rename {app => src}/calculator/templates/calculator/enter_numbers.html (100%) rename {app => src}/calculator/templates/calculator/invalid_operation.html (100%) rename {app => src}/calculator/tests/__init__.py (100%) rename {app => src}/calculator/tests/services/__init__.py (100%) rename {app => src}/calculator/tests/services/tests_operations.py (100%) rename {app => src}/calculator/tests/views/__init__.py (100%) rename {app => src}/calculator/tests/views/test_calculate.py (100%) rename {app => src}/calculator/tests/views/test_health.py (100%) rename {app => src}/calculator/urls.py (100%) rename {app => src}/calculator/views/__init__.py (100%) rename {app => src}/calculator/views/calculate.py (93%) rename {app => src}/calculator/views/health.py (100%) rename {app => src}/manage.py (100%) rename src/{ => tennis}/__init__.py (100%) create mode 100644 src/tennis/__tennis_config.py create mode 100644 src/tennis/admin.py create mode 100644 src/tennis/apps.py rename src/{sporttrackingapp => tennis/forms}/__init__.py (100%) create mode 100644 src/tennis/forms/player.py create mode 100644 src/tennis/migrations/0001_initial.py rename {tests => src/tennis/migrations}/__init__.py (100%) create mode 100644 src/tennis/models/__init__.py create mode 100644 src/tennis/models/player.py create mode 100644 src/tennis/templates/__init__.py create mode 100644 src/tennis/templates/tennis/__init__.py create mode 100644 src/tennis/templates/tennis/add_player.html create mode 100644 src/tennis/templates/tennis/success.html create mode 100644 src/tennis/tests/__init__.py create mode 100644 src/tennis/urls.py create mode 100644 src/tennis/views/__init__.py create mode 100644 src/tennis/views/add_player.py diff --git a/pyproject.toml b/pyproject.toml index 00b4391..7196166 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,11 +45,11 @@ dev = [ ] [tool.pdm.scripts] -prod = 'pdm run python app/manage.py runserver' -migrate = 'pdm run python app/manage.py migrate' -make_migration = 'pdm run python app/manage.py makemigrations' -test_calculator = "pdm run coverage run --source='.' app/manage.py test calculator" -tests = "pdm run coverage run --source='.' app/manage.py test" +prod = 'pdm run python src/manage.py runserver' +migrate = 'pdm run python src/manage.py migrate' +make_migration = 'pdm run python src/manage.py makemigrations' +test_calculator = "pdm run coverage run --source='.' src/manage.py test calculator" +tests = "pdm run coverage run --source='.' src/manage.py test" coverage_report = 'pdm run coverage report' -check_code_format = 'black --check app/' -format_code = 'black app/' \ No newline at end of file +check_code_format = 'black --check src/' +format_code = 'black src/' \ No newline at end of file diff --git a/app/app/__app_configs.py b/src/app/__app_configs.py similarity index 93% rename from app/app/__app_configs.py rename to src/app/__app_configs.py index bec30da..6b7d401 100644 --- a/app/app/__app_configs.py +++ b/src/app/__app_configs.py @@ -44,7 +44,8 @@ def to_dict(cls): class Paths(str, ValidationEnum): root = "/" admin = "admin" + admin_path = f"{admin}{root}" calc = "calculator" calc_path = f"{calc}{root}" - health = "health" - health_path = f"{health}{root}" + tennis = "tennis" + tennis_path = f"{tennis}{root}" diff --git a/app/app/__init__.py b/src/app/__init__.py similarity index 100% rename from app/app/__init__.py rename to src/app/__init__.py diff --git a/app/app/asgi.py b/src/app/asgi.py similarity index 100% rename from app/app/asgi.py rename to src/app/asgi.py diff --git a/app/app/settings.py b/src/app/settings.py similarity index 99% rename from app/app/settings.py rename to src/app/settings.py index 2bc9e7e..6c0aef9 100644 --- a/app/app/settings.py +++ b/src/app/settings.py @@ -45,6 +45,7 @@ # Application definition INSTALLED_APPS = [ "calculator.apps.CalculatorConfig", + "tennis.apps.TennisConfig", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", diff --git a/app/app/urls.py b/src/app/urls.py similarity index 88% rename from app/app/urls.py rename to src/app/urls.py index 9ba29ec..83e7ba0 100644 --- a/app/app/urls.py +++ b/src/app/urls.py @@ -22,5 +22,6 @@ urlpatterns = [ path(Paths.calc_path.value, include("calculator.urls")), - path(Paths.admin.value, admin.site.urls), + path(Paths.tennis_path.value, include("tennis.urls")), + path(Paths.admin_path.value, admin.site.urls), ] diff --git a/app/app/views.py b/src/app/views.py similarity index 100% rename from app/app/views.py rename to src/app/views.py diff --git a/app/app/wsgi.py b/src/app/wsgi.py similarity index 100% rename from app/app/wsgi.py rename to src/app/wsgi.py diff --git a/app/calculator/__calc_config.py b/src/calculator/__calc_config.py similarity index 97% rename from app/calculator/__calc_config.py rename to src/calculator/__calc_config.py index 0357320..834c1ba 100644 --- a/app/calculator/__calc_config.py +++ b/src/calculator/__calc_config.py @@ -75,3 +75,4 @@ class HttpMethods(str, ValidationEnum): class CalcTemplates(str, ValidationEnum): calculate = "calculator/calculate.html" error = "calculator/invalid_operation.html" + enter = "calculator/enter_numbers.html" diff --git a/app/calculator/__exceptions.py b/src/calculator/__exceptions.py similarity index 100% rename from app/calculator/__exceptions.py rename to src/calculator/__exceptions.py diff --git a/app/calculator/__init__.py b/src/calculator/__init__.py similarity index 100% rename from app/calculator/__init__.py rename to src/calculator/__init__.py diff --git a/app/calculator/admin.py b/src/calculator/admin.py similarity index 100% rename from app/calculator/admin.py rename to src/calculator/admin.py diff --git a/app/calculator/apps.py b/src/calculator/apps.py similarity index 100% rename from app/calculator/apps.py rename to src/calculator/apps.py diff --git a/app/calculator/db/__init__.py b/src/calculator/db/__init__.py similarity index 100% rename from app/calculator/db/__init__.py rename to src/calculator/db/__init__.py diff --git a/app/calculator/db/models.py b/src/calculator/db/models.py similarity index 100% rename from app/calculator/db/models.py rename to src/calculator/db/models.py diff --git a/app/calculator/migrations/__init__.py b/src/calculator/migrations/__init__.py similarity index 100% rename from app/calculator/migrations/__init__.py rename to src/calculator/migrations/__init__.py diff --git a/app/calculator/models/__init__.py b/src/calculator/models/__init__.py similarity index 100% rename from app/calculator/models/__init__.py rename to src/calculator/models/__init__.py diff --git a/app/calculator/models/operation.py b/src/calculator/models/operation.py similarity index 100% rename from app/calculator/models/operation.py rename to src/calculator/models/operation.py diff --git a/app/calculator/service/__init__.py b/src/calculator/service/__init__.py similarity index 100% rename from app/calculator/service/__init__.py rename to src/calculator/service/__init__.py diff --git a/app/calculator/service/operations.py b/src/calculator/service/operations.py similarity index 100% rename from app/calculator/service/operations.py rename to src/calculator/service/operations.py diff --git a/app/calculator/templates/__init__.py b/src/calculator/templates/__init__.py similarity index 100% rename from app/calculator/templates/__init__.py rename to src/calculator/templates/__init__.py diff --git a/app/calculator/templates/calculator/__init__.py b/src/calculator/templates/calculator/__init__.py similarity index 100% rename from app/calculator/templates/calculator/__init__.py rename to src/calculator/templates/calculator/__init__.py diff --git a/app/calculator/templates/calculator/calculate.html b/src/calculator/templates/calculator/calculate.html similarity index 100% rename from app/calculator/templates/calculator/calculate.html rename to src/calculator/templates/calculator/calculate.html diff --git a/app/calculator/templates/calculator/enter_numbers.html b/src/calculator/templates/calculator/enter_numbers.html similarity index 100% rename from app/calculator/templates/calculator/enter_numbers.html rename to src/calculator/templates/calculator/enter_numbers.html diff --git a/app/calculator/templates/calculator/invalid_operation.html b/src/calculator/templates/calculator/invalid_operation.html similarity index 100% rename from app/calculator/templates/calculator/invalid_operation.html rename to src/calculator/templates/calculator/invalid_operation.html diff --git a/app/calculator/tests/__init__.py b/src/calculator/tests/__init__.py similarity index 100% rename from app/calculator/tests/__init__.py rename to src/calculator/tests/__init__.py diff --git a/app/calculator/tests/services/__init__.py b/src/calculator/tests/services/__init__.py similarity index 100% rename from app/calculator/tests/services/__init__.py rename to src/calculator/tests/services/__init__.py diff --git a/app/calculator/tests/services/tests_operations.py b/src/calculator/tests/services/tests_operations.py similarity index 100% rename from app/calculator/tests/services/tests_operations.py rename to src/calculator/tests/services/tests_operations.py diff --git a/app/calculator/tests/views/__init__.py b/src/calculator/tests/views/__init__.py similarity index 100% rename from app/calculator/tests/views/__init__.py rename to src/calculator/tests/views/__init__.py diff --git a/app/calculator/tests/views/test_calculate.py b/src/calculator/tests/views/test_calculate.py similarity index 100% rename from app/calculator/tests/views/test_calculate.py rename to src/calculator/tests/views/test_calculate.py diff --git a/app/calculator/tests/views/test_health.py b/src/calculator/tests/views/test_health.py similarity index 100% rename from app/calculator/tests/views/test_health.py rename to src/calculator/tests/views/test_health.py diff --git a/app/calculator/urls.py b/src/calculator/urls.py similarity index 100% rename from app/calculator/urls.py rename to src/calculator/urls.py diff --git a/app/calculator/views/__init__.py b/src/calculator/views/__init__.py similarity index 100% rename from app/calculator/views/__init__.py rename to src/calculator/views/__init__.py diff --git a/app/calculator/views/calculate.py b/src/calculator/views/calculate.py similarity index 93% rename from app/calculator/views/calculate.py rename to src/calculator/views/calculate.py index a4406b0..bee2785 100644 --- a/app/calculator/views/calculate.py +++ b/src/calculator/views/calculate.py @@ -1,5 +1,3 @@ -from multiprocessing import context - from django.http import HttpRequest, HttpResponse from django.shortcuts import redirect from django.template import Template, loader @@ -10,7 +8,7 @@ def enter_numbers(req: HttpRequest) -> HttpResponse: - template: Template = loader.get_template("calculator/enter_numbers.html") + template: Template = loader.get_template(CalcTemplates.enter.value) context: dict = { "allowed_operations": [ operation diff --git a/app/calculator/views/health.py b/src/calculator/views/health.py similarity index 100% rename from app/calculator/views/health.py rename to src/calculator/views/health.py diff --git a/app/manage.py b/src/manage.py similarity index 100% rename from app/manage.py rename to src/manage.py diff --git a/src/__init__.py b/src/tennis/__init__.py similarity index 100% rename from src/__init__.py rename to src/tennis/__init__.py diff --git a/src/tennis/__tennis_config.py b/src/tennis/__tennis_config.py new file mode 100644 index 0000000..c540530 --- /dev/null +++ b/src/tennis/__tennis_config.py @@ -0,0 +1,58 @@ +from enum import Enum + + +class Deliminator(str, Enum): + comma = "," + space = " " + dash = "-" + vertical = "|" + forward_slash = "/" + none = "" + client_ref_field = " \\| " + + +class ValidationEnum(Enum): + """ValidationEnum is a custom Parent class used in specific validations / configs inside the package.""" + + @classmethod + def list(cls) -> list: + """Returns exact list of Enum values from the class""" + return list(map(lambda c: c.value, cls)) + + @classmethod + def to_list(cls): + """Converts the values and always returns a list of a single objects""" + elements = [] + for id in cls: + elements.extend(id.value) + return elements + + @classmethod + def to_string(cls, separator: str = Deliminator.comma.value): + """ + Returns Enum values as a string separated by a separator. + Separator defaults to Deliminator.comma.value + """ + return separator.join([str(id) for id in cls.list()]) + + @classmethod + def to_dict(cls): + """Returns Enums as a data dictionary {Enum.element.name: Enum.element.value}""" + return {member.name: member.value for member in cls} + + +class HttpMethods(str, ValidationEnum): + GET = "GET" + POST = "POST" + PUT = "PUT" + DELETE = "DELETE" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + PATCH = "PATCH" + TRACE = "TRACE" + CONNECT = "CONNECT" + + +class Templates(str, ValidationEnum): + add_player = "tennis/add_player.html" + success = "tennis/success.html" diff --git a/src/tennis/admin.py b/src/tennis/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/src/tennis/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/src/tennis/apps.py b/src/tennis/apps.py new file mode 100644 index 0000000..750b824 --- /dev/null +++ b/src/tennis/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TennisConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "tennis" diff --git a/src/sporttrackingapp/__init__.py b/src/tennis/forms/__init__.py similarity index 100% rename from src/sporttrackingapp/__init__.py rename to src/tennis/forms/__init__.py diff --git a/src/tennis/forms/player.py b/src/tennis/forms/player.py new file mode 100644 index 0000000..716c23a --- /dev/null +++ b/src/tennis/forms/player.py @@ -0,0 +1,11 @@ +from django import forms + +from ..models.player import TennisPlayer + + +class TennisPlayerForm(forms.ModelForm): + class Meta: + model = TennisPlayer + fields: list[str] = [ + field.name for field in TennisPlayer._meta.fields if field.name != "id" + ] diff --git a/src/tennis/migrations/0001_initial.py b/src/tennis/migrations/0001_initial.py new file mode 100644 index 0000000..2db1650 --- /dev/null +++ b/src/tennis/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0.6 on 2024-06-06 06:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="TennisPlayer", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("first_name", models.CharField(max_length=100)), + ("last_name", models.CharField(max_length=100)), + ("nationality", models.CharField(max_length=100)), + ("date_of_birth", models.DateField()), + ("racket_brand", models.CharField(max_length=100)), + ("racket_model", models.CharField(max_length=100)), + ], + ), + ] diff --git a/tests/__init__.py b/src/tennis/migrations/__init__.py similarity index 100% rename from tests/__init__.py rename to src/tennis/migrations/__init__.py diff --git a/src/tennis/models/__init__.py b/src/tennis/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tennis/models/player.py b/src/tennis/models/player.py new file mode 100644 index 0000000..a62a335 --- /dev/null +++ b/src/tennis/models/player.py @@ -0,0 +1,13 @@ +from django.db import models + + +class TennisPlayer(models.Model): + first_name = models.CharField(max_length=100) + last_name = models.CharField(max_length=100) + nationality = models.CharField(max_length=100) + date_of_birth = models.DateField() + racket_brand = models.CharField(max_length=100) + racket_model = models.CharField(max_length=100) + + def __str__(self): + return f"{self.first_name} {self.last_name}" diff --git a/src/tennis/templates/__init__.py b/src/tennis/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tennis/templates/tennis/__init__.py b/src/tennis/templates/tennis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tennis/templates/tennis/add_player.html b/src/tennis/templates/tennis/add_player.html new file mode 100644 index 0000000..2fe30a9 --- /dev/null +++ b/src/tennis/templates/tennis/add_player.html @@ -0,0 +1,33 @@ + + + + + + Enter Tennis Player Details + + +

Enter Tennis Player Details

+
+ {% csrf_token %} + +

+ + +

+ + +

+ + +

+ + +

+ + +

+ + +
+ + \ No newline at end of file diff --git a/src/tennis/templates/tennis/success.html b/src/tennis/templates/tennis/success.html new file mode 100644 index 0000000..843b176 --- /dev/null +++ b/src/tennis/templates/tennis/success.html @@ -0,0 +1,14 @@ + + + + + Success + + +

Success

+

The tennis player has been successfully created!

+
+ +
+ + diff --git a/src/tennis/tests/__init__.py b/src/tennis/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tennis/urls.py b/src/tennis/urls.py new file mode 100644 index 0000000..1979a01 --- /dev/null +++ b/src/tennis/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from .views.add_player import add_player, success + +urlpatterns = [ + path("", add_player, name="add_player"), + path("success/", success, name="success"), +] diff --git a/src/tennis/views/__init__.py b/src/tennis/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tennis/views/add_player.py b/src/tennis/views/add_player.py new file mode 100644 index 0000000..1ca2b5f --- /dev/null +++ b/src/tennis/views/add_player.py @@ -0,0 +1,26 @@ +from django.http import HttpRequest, HttpResponse +from django.shortcuts import redirect, render + +from ..__tennis_config import HttpMethods, Templates +from ..forms.player import TennisPlayerForm + + +def add_player(req: HttpRequest) -> HttpResponse: + print(f"req.method is: {req.method}") + if req.method == HttpMethods.POST.value: + print("inside POST") + form = TennisPlayerForm(req.POST) + print(form.is_valid()) + if form.is_valid(): + form.save() + return redirect("success") + else: + print(form.errors) + else: + form = TennisPlayerForm() + + return render(req, Templates.add_player.value, {"form": form}) + + +def success(req: HttpRequest) -> HttpResponse: + return render(req, Templates.success.value) From c884902c6e1ba6ddc74e10cead3e0db79d04d68e Mon Sep 17 00:00:00 2001 From: Lukasz Chmielewski Date: Thu, 6 Jun 2024 10:16:20 +0200 Subject: [PATCH 2/2] moved health check and updated tests --- pyproject.toml | 13 ++-- src/app/__app_configs.py | 14 +++++ src/app/{views.py => tests/__init__.py} | 0 .../tests/views => app/tests}/test_health.py | 0 src/app/tests/test_validation_enum.py | 41 ++++++++++++ src/app/urls.py | 2 + src/app/views/__init__.py | 0 src/{calculator => app}/views/health.py | 0 src/calculator/__calc_config.py | 58 +---------------- src/calculator/urls.py | 3 +- src/calculator/views/calculate.py | 10 +-- src/tennis/__tennis_config.py | 63 +++---------------- src/tennis/services/__init__.py | 0 src/tennis/services/add_player.py | 37 +++++++++++ src/tennis/templates/tennis/error.html | 15 +++++ src/tennis/urls.py | 5 +- src/tennis/views/add_player.py | 28 ++++----- 17 files changed, 152 insertions(+), 137 deletions(-) rename src/app/{views.py => tests/__init__.py} (100%) rename src/{calculator/tests/views => app/tests}/test_health.py (100%) create mode 100644 src/app/tests/test_validation_enum.py create mode 100644 src/app/views/__init__.py rename src/{calculator => app}/views/health.py (100%) create mode 100644 src/tennis/services/__init__.py create mode 100644 src/tennis/services/add_player.py create mode 100644 src/tennis/templates/tennis/error.html diff --git a/pyproject.toml b/pyproject.toml index 7196166..047340f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,10 +46,15 @@ dev = [ [tool.pdm.scripts] prod = 'pdm run python src/manage.py runserver' + migrate = 'pdm run python src/manage.py migrate' make_migration = 'pdm run python src/manage.py makemigrations' -test_calculator = "pdm run coverage run --source='.' src/manage.py test calculator" -tests = "pdm run coverage run --source='.' src/manage.py test" -coverage_report = 'pdm run coverage report' + check_code_format = 'black --check src/' -format_code = 'black src/' \ No newline at end of file +format_code = 'black src/' + +test_app = "pdm run coverage run --source='.' src/manage.py test app" +test_calculator = "pdm run coverage run --source='.' src/manage.py test calculator" +test_tennis = "pdm run coverage run --source='.' src/manage.py test tennis" + +coverage_report = 'pdm run coverage report' \ No newline at end of file diff --git a/src/app/__app_configs.py b/src/app/__app_configs.py index 6b7d401..94f4007 100644 --- a/src/app/__app_configs.py +++ b/src/app/__app_configs.py @@ -49,3 +49,17 @@ class Paths(str, ValidationEnum): calc_path = f"{calc}{root}" tennis = "tennis" tennis_path = f"{tennis}{root}" + health = "health" + health_path = f"{health}{root}" + + +class HttpMethods(str, ValidationEnum): + GET = "GET" + POST = "POST" + PUT = "PUT" + DELETE = "DELETE" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + PATCH = "PATCH" + TRACE = "TRACE" + CONNECT = "CONNECT" diff --git a/src/app/views.py b/src/app/tests/__init__.py similarity index 100% rename from src/app/views.py rename to src/app/tests/__init__.py diff --git a/src/calculator/tests/views/test_health.py b/src/app/tests/test_health.py similarity index 100% rename from src/calculator/tests/views/test_health.py rename to src/app/tests/test_health.py diff --git a/src/app/tests/test_validation_enum.py b/src/app/tests/test_validation_enum.py new file mode 100644 index 0000000..7875819 --- /dev/null +++ b/src/app/tests/test_validation_enum.py @@ -0,0 +1,41 @@ +import unittest +from enum import Enum + +from ..__app_configs import ValidationEnum + + +class TestValidationEnum(unittest.TestCase): + def test_list(self): + class TestEnum(ValidationEnum): + A = "a" + B = "b" + C = "c" + + self.assertEqual(TestEnum.list(), ["a", "b", "c"]) + + def test_to_list(self): + class TestEnum(ValidationEnum): + A = ["a"] + B = ["b"] + C = ["c"] + + self.assertEqual(TestEnum.to_list(), ["a", "b", "c"]) + + def test_to_string(self): + class TestEnum(ValidationEnum): + A = "a" + B = "b" + C = "c" + + self.assertEqual(TestEnum.to_string(), "a,b,c") + + # Testing with custom separator + self.assertEqual(TestEnum.to_string(separator="|"), "a|b|c") + + def test_to_dict(self): + class TestEnum(ValidationEnum): + A = "a" + B = "b" + C = "c" + + self.assertEqual(TestEnum.to_dict(), {"A": "a", "B": "b", "C": "c"}) diff --git a/src/app/urls.py b/src/app/urls.py index 83e7ba0..8566a02 100644 --- a/src/app/urls.py +++ b/src/app/urls.py @@ -19,9 +19,11 @@ from django.urls import include, path from .__app_configs import Paths +from .views.health import health urlpatterns = [ path(Paths.calc_path.value, include("calculator.urls")), path(Paths.tennis_path.value, include("tennis.urls")), path(Paths.admin_path.value, admin.site.urls), + path(Paths.health_path.value, health, name=Paths.health.value), ] diff --git a/src/app/views/__init__.py b/src/app/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/calculator/views/health.py b/src/app/views/health.py similarity index 100% rename from src/calculator/views/health.py rename to src/app/views/health.py diff --git a/src/calculator/__calc_config.py b/src/calculator/__calc_config.py index 834c1ba..8d00d89 100644 --- a/src/calculator/__calc_config.py +++ b/src/calculator/__calc_config.py @@ -1,44 +1,4 @@ -from enum import Enum - - -class Deliminator(str, Enum): - comma = "," - space = " " - dash = "-" - vertical = "|" - forward_slash = "/" - none = "" - client_ref_field = " \\| " - - -class ValidationEnum(Enum): - """ValidationEnum is a custom Parent class used in specific validations / configs inside the package.""" - - @classmethod - def list(cls) -> list: - """Returns exact list of Enum values from the class""" - return list(map(lambda c: c.value, cls)) - - @classmethod - def to_list(cls): - """Converts the values and always returns a list of a single objects""" - elements = [] - for id in cls: - elements.extend(id.value) - return elements - - @classmethod - def to_string(cls, separator: str = Deliminator.comma.value): - """ - Returns Enum values as a string separated by a separator. - Separator defaults to Deliminator.comma.value - """ - return separator.join([str(id) for id in cls.list()]) - - @classmethod - def to_dict(cls): - """Returns Enums as a data dictionary {Enum.element.name: Enum.element.value}""" - return {member.name: member.value for member in cls} +from app.__app_configs import ValidationEnum class Paths(str, ValidationEnum): @@ -48,8 +8,6 @@ class Paths(str, ValidationEnum): calc = "calculate" enter_num = "enter_numbers" enter_num_path = f"{enter_num}{root}" - health = "health" - health_path = f"{health}{root}" class MathOperation(str, ValidationEnum): @@ -60,19 +18,7 @@ class MathOperation(str, ValidationEnum): none = "" -class HttpMethods(str, ValidationEnum): - GET = "GET" - POST = "POST" - PUT = "PUT" - DELETE = "DELETE" - HEAD = "HEAD" - OPTIONS = "OPTIONS" - PATCH = "PATCH" - TRACE = "TRACE" - CONNECT = "CONNECT" - - -class CalcTemplates(str, ValidationEnum): +class Templates(str, ValidationEnum): calculate = "calculator/calculate.html" error = "calculator/invalid_operation.html" enter = "calculator/enter_numbers.html" diff --git a/src/calculator/urls.py b/src/calculator/urls.py index 9316079..7ac8966 100644 --- a/src/calculator/urls.py +++ b/src/calculator/urls.py @@ -2,10 +2,9 @@ from django.urls import path from .__calc_config import Paths -from .views import calculate, health +from .views import calculate urlpatterns = [ path(Paths.enter_num.value, calculate.enter_numbers, name=Paths.enter_num.value), path(Paths.none.value, calculate.calculate, name=Paths.calc.value), - path(Paths.health_path.value, health.health, name=Paths.health.value), ] diff --git a/src/calculator/views/calculate.py b/src/calculator/views/calculate.py index bee2785..0dcb63b 100644 --- a/src/calculator/views/calculate.py +++ b/src/calculator/views/calculate.py @@ -2,13 +2,15 @@ from django.shortcuts import redirect from django.template import Template, loader -from ..__calc_config import CalcTemplates, HttpMethods, MathOperation +from app.__app_configs import HttpMethods + +from ..__calc_config import MathOperation, Templates from ..__exceptions import DivisionByZeroError, OperationError from ..service.operations import Calculate, get_error_context def enter_numbers(req: HttpRequest) -> HttpResponse: - template: Template = loader.get_template(CalcTemplates.enter.value) + template: Template = loader.get_template(Templates.enter.value) context: dict = { "allowed_operations": [ operation @@ -35,11 +37,11 @@ def calculate(req: HttpRequest) -> HttpResponse: operation = req.POST.get("operation", MathOperation.none.value) try: - template: Template = loader.get_template(CalcTemplates.calculate.value) + template: Template = loader.get_template(Templates.calculate.value) context: dict = Calculate(num1, num2, operation).get_context() except (DivisionByZeroError, OperationError) as err: - template: Template = loader.get_template(CalcTemplates.error.value) + template: Template = loader.get_template(Templates.error.value) context: dict = get_error_context(num1, num2, operation, err) return HttpResponse(template.render(context, req)) diff --git a/src/tennis/__tennis_config.py b/src/tennis/__tennis_config.py index c540530..5e2283f 100644 --- a/src/tennis/__tennis_config.py +++ b/src/tennis/__tennis_config.py @@ -1,58 +1,15 @@ -from enum import Enum - - -class Deliminator(str, Enum): - comma = "," - space = " " - dash = "-" - vertical = "|" - forward_slash = "/" - none = "" - client_ref_field = " \\| " - - -class ValidationEnum(Enum): - """ValidationEnum is a custom Parent class used in specific validations / configs inside the package.""" - - @classmethod - def list(cls) -> list: - """Returns exact list of Enum values from the class""" - return list(map(lambda c: c.value, cls)) - - @classmethod - def to_list(cls): - """Converts the values and always returns a list of a single objects""" - elements = [] - for id in cls: - elements.extend(id.value) - return elements - - @classmethod - def to_string(cls, separator: str = Deliminator.comma.value): - """ - Returns Enum values as a string separated by a separator. - Separator defaults to Deliminator.comma.value - """ - return separator.join([str(id) for id in cls.list()]) - - @classmethod - def to_dict(cls): - """Returns Enums as a data dictionary {Enum.element.name: Enum.element.value}""" - return {member.name: member.value for member in cls} - - -class HttpMethods(str, ValidationEnum): - GET = "GET" - POST = "POST" - PUT = "PUT" - DELETE = "DELETE" - HEAD = "HEAD" - OPTIONS = "OPTIONS" - PATCH = "PATCH" - TRACE = "TRACE" - CONNECT = "CONNECT" +from app.__app_configs import ValidationEnum class Templates(str, ValidationEnum): add_player = "tennis/add_player.html" success = "tennis/success.html" + error = "tennis/error.html" + + +class Urls(str, ValidationEnum): + root = "/" + none = "" + success = "success" + success_path = f"{success}{root}" + add_player = "add_player" diff --git a/src/tennis/services/__init__.py b/src/tennis/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tennis/services/add_player.py b/src/tennis/services/add_player.py new file mode 100644 index 0000000..775c901 --- /dev/null +++ b/src/tennis/services/add_player.py @@ -0,0 +1,37 @@ +from django.forms import ModelForm +from django.http import HttpRequest +from django.shortcuts import redirect, render + +from app.__app_configs import HttpMethods + +from ..__tennis_config import Templates, Urls + + +class Add: + form: ModelForm + req: HttpRequest + + def __init__(self, form: ModelForm, req: HttpRequest) -> None: + self.form = form + self.req = req + + def _err_context(self) -> dict: + return {"err": self.form.errors} + + def _get_context(self) -> dict: + return {"form": self.form} + + def _post(self) -> None: + if self.form.is_valid(): + self.form.save() + return redirect(Urls.success.value) + else: + return render(self.req, Templates.error.value, self._err_context()) + + def _get(self) -> None: + return render(self.req, Templates.add_player.value, self._get_context()) + + def save(self) -> None: + return ( + self._post() if self.req.method == HttpMethods.POST.value else self._get() + ) diff --git a/src/tennis/templates/tennis/error.html b/src/tennis/templates/tennis/error.html new file mode 100644 index 0000000..14d886d --- /dev/null +++ b/src/tennis/templates/tennis/error.html @@ -0,0 +1,15 @@ + + + + + Error + + +

Error

+

Error occured when saving tennis player to database!

+

Error details: {{ err }}

+
+ +
+ + diff --git a/src/tennis/urls.py b/src/tennis/urls.py index 1979a01..3dee8a5 100644 --- a/src/tennis/urls.py +++ b/src/tennis/urls.py @@ -1,8 +1,9 @@ from django.urls import path +from .__tennis_config import Urls from .views.add_player import add_player, success urlpatterns = [ - path("", add_player, name="add_player"), - path("success/", success, name="success"), + path(Urls.none.value, add_player, name=Urls.add_player.value), + path(Urls.success_path.value, success, name=Urls.success.value), ] diff --git a/src/tennis/views/add_player.py b/src/tennis/views/add_player.py index 1ca2b5f..1e02afd 100644 --- a/src/tennis/views/add_player.py +++ b/src/tennis/views/add_player.py @@ -1,25 +1,21 @@ +from django.forms import ModelForm from django.http import HttpRequest, HttpResponse -from django.shortcuts import redirect, render +from django.shortcuts import render -from ..__tennis_config import HttpMethods, Templates +from app.__app_configs import HttpMethods + +from ..__tennis_config import Templates from ..forms.player import TennisPlayerForm +from ..services.add_player import Add def add_player(req: HttpRequest) -> HttpResponse: - print(f"req.method is: {req.method}") - if req.method == HttpMethods.POST.value: - print("inside POST") - form = TennisPlayerForm(req.POST) - print(form.is_valid()) - if form.is_valid(): - form.save() - return redirect("success") - else: - print(form.errors) - else: - form = TennisPlayerForm() - - return render(req, Templates.add_player.value, {"form": form}) + form: ModelForm = ( + TennisPlayerForm(req.POST) + if req.method == HttpMethods.POST.value + else TennisPlayerForm() + ) + return Add(form, req).save() def success(req: HttpRequest) -> HttpResponse: