diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5ef76aa --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,37 @@ +name: Python application + +on: [push] + +jobs: + build: + strategy: + fail-fast: false + matrix: + python-version: [3.8, 3.9] + poetry-version: [1.1.4] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install poetry ${{ matrix.poetry-version }} + run: | + curl -sSL https://install.python-poetry.org | python3 - + echo "$HOME/.local/bin" >> $GITHUB_PATH + + + - name: View poetry --help + run: poetry --help + + - name: Install dependencies + run: poetry install + + - name: Test + run: | + poetry run python manage.py test \ No newline at end of file diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml new file mode 100644 index 0000000..2af4ce3 --- /dev/null +++ b/.github/workflows/github-actions-demo.yml @@ -0,0 +1,17 @@ +name: GitHub Actions Demo +on: [push] +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + - name: Check out repository code + uses: actions/checkout@v2 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + - run: echo "🖥️ The workflow is now ready to test your code on the runner." + - name: List files in the repository + run: | + ls ${{ github.workspace }} + - run: echo "🍏 This job's status is ${{ job.status }}." \ No newline at end of file diff --git a/.gitignore b/.gitignore index ae2e4d0..3b59196 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -env + .env /env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c1403ba --- /dev/null +++ b/Dockerfile @@ -0,0 +1,72 @@ +# `python-base` sets up all our shared environment variables +FROM python:3.12.0-slim as python-base +# FROM python:3.8.1-slim as python-base + + # python +ENV PYTHONUNBUFFERED=1 \ + # prevents python creating .pyc files + PYTHONDONTWRITEBYTECODE=1 \ + \ + # pip + PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 \ + \ + # poetry + # https://python-poetry.org/docs/configuration/#using-environment-variables + POETRY_VERSION=1.8.2 \ + # make poetry install to this location + POETRY_HOME="/opt/poetry" \ + # make poetry create the virtual environment in the project's root + # it gets named `.venv` + POETRY_VIRTUALENVS_IN_PROJECT=true \ + # do not ask any interactive question + POETRY_NO_INTERACTION=1 \ + \ + # paths + # this is where our requirements + virtual environment will live + PYSETUP_PATH="/opt/pysetup" \ + VENV_PATH="/opt/pysetup/.venv" + + +# prepend poetry and venv to path +ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" + +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + # deps for installing poetry + curl \ + # deps for building python deps + build-essential + +# install poetry - respects $POETRY_VERSION & $POETRY_HOME +# RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python +# RUN pip install poetry poetry init +# RUN curl -sSL https://install.python-poetry.org | python3 - +RUN curl -sSL https://raw.githubusercontent.com.sdispater/poetry/get-poetry.py | python + +RUN curl -sSL https://install.python-poetry.org | python3 - + + +# install postgres dependencies inside of Docker +RUN apt-get update \ + && apt-get -y install libpq-dev gcc \ + && pip install psycopg2 + +# copy project requirement files here to ensure they will be cached. +WORKDIR $PYSETUP_PATH +COPY poetry.lock pyproject.toml ./ + +# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally +RUN poetry install --no-dev + +# quicker install as runtime deps are already installed +RUN poetry install + +WORKDIR /app + +COPY . /app/ + +EXPOSE 8000 + +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8f40f40 --- /dev/null +++ b/Makefile @@ -0,0 +1,122 @@ +# Set this to ~use it everywhere in the project setup +PYTHON_VERSION ?= 3.8.10 +# the directories containing the library modules this repo builds +LIBRARY_DIRS = mylibrary +# build artifacts organized in this Makefile +BUILD_DIR ?= build + +# PyTest options +PYTEST_HTML_OPTIONS = --html=$(BUILD_DIR)/report.html --self-contained-html +PYTEST_TAP_OPTIONS = --tap-combined --tap-outdir $(BUILD_DIR) +PYTEST_COVERAGE_OPTIONS = --cov=$(LIBRARY_DIRS) +PYTEST_OPTIONS ?= $(PYTEST_HTML_OPTIONS) $(PYTEST_TAP_OPTIONS) $(PYTEST_COVERAGE_OPTIONS) + +# MyPy typechecking options +MYPY_OPTS ?= --python-version $(basename $(PYTHON_VERSION)) --show-column-numbers --pretty --html-report $(BUILD_DIR)/mypy +# Python installation artifacts +PYTHON_VERSION_FILE=.python-version +ifeq ($(shell which pyenv),) +# pyenv isn't installed, guess the eventual path FWIW +PYENV_VERSION_DIR ?= $(HOME)/.pyenv/versions/$(PYTHON_VERSION) +else +# pyenv is installed +PYENV_VERSION_DIR ?= $(shell pyenv root)/versions/$(PYTHON_VERSION) +endif +PIP ?= pip3 + +POETRY_OPTS ?= +POETRY ?= poetry $(POETRY_OPTS) +RUN_PYPKG_BIN = $(POETRY) run + +COLOR_ORANGE = \033[33m +COLOR_RESET = \033[0m + +##@ Utility + +.PHONY: help +help: ## Display this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +.PHONY: version-python +version-python: ## Echos the version of Python in use + @echo $(PYTHON_VERSION) + +##@ Testing + +.PHONY: test +test: ## Runs tests + $(RUN_PYPKG_BIN) pytest \ + $(PYTEST_OPTIONS) \ + tests/*.py + +##@ Building and Publishing + +.PHONY: build +build: ## Runs a build + $(POETRY) build + +.PHONY: publish +publish: ## Publish a build to the configured repo + $(POETRY) publish $(POETRY_PUBLISH_OPTIONS_SET_BY_CI_ENV) + +.PHONY: deps-py-update +deps-py-update: pyproject.toml ## Update Poetry deps, e.g. after adding a new one manually + $(POETRY) update + +##@ Setup +# dynamic-ish detection of Python installation directory with pyenv +$(PYENV_VERSION_DIR): + pyenv install --skip-existing $(PYTHON_VERSION) +$(PYTHON_VERSION_FILE): $(PYENV_VERSION_DIR) + pyenv local $(PYTHON_VERSION) + +.PHONY: deps +deps: deps-brew deps-py ## Installs all dependencies + +.PHONY: deps-brew +deps-brew: Brewfile ## Installs development dependencies from Homebrew + brew bundle --file=Brewfile + @echo "$(COLOR_ORANGE)Ensure that pyenv is setup in your shell.$(COLOR_RESET)" + @echo "$(COLOR_ORANGE)It should have something like 'eval \$$(pyenv init -)'$(COLOR_RESET)" + +.PHONY: deps-py +deps-py: $(PYTHON_VERSION_FILE) ## Installs Python development and runtime dependencies + $(PIP) install --upgrade \ + --index-url $(PYPI_PROXY) \ + pip + $(PIP) install --upgrade \ + --index-url $(PYPI_PROXY) \ + poetry + $(POETRY) install + +##@ Code Quality + +.PHONY: check +check: check-py ## Runs linters and other important tools + +.PHONY: check-py +check-py: check-py-flake8 check-py-black check-py-mypy ## Checks only Python files + +.PHONY: check-py-flake8 +check-py-flake8: ## Runs flake8 linter + $(RUN_PYPKG_BIN) flake8 . + +.PHONY: check-py-black +check-py-black: ## Runs black in check mode (no changes) + $(RUN_PYPKG_BIN) black --check --line-length 118 --fast . + +.PHONY: check-py-mypy +check-py-mypy: ## Runs mypy + $(RUN_PYPKG_BIN) mypy $(MYPY_OPTS) $(LIBRARY_DIRS) + +.PHONY: format-py +format-py: ## Runs black, makes changes where necessary + $(RUN_PYPKG_BIN) black . + +.PHONY: format-autopep8 +format-autopep8: + $(RUN_PYPKG_BIN) autopep8 --in-place --recursive . + +.PHONY: format-isort +format-isort: + $(RUN_PYPKG_BIN) isort --recursive . \ No newline at end of file diff --git a/README.me b/README.me new file mode 100644 index 0000000..e733b6b --- /dev/null +++ b/README.me @@ -0,0 +1,18 @@ +# Scripts do Projeto: + +Git clone ... + +## Criando Ambiente Virtual: +python -m venv env +env/scripts/activate + + + +## Instalação das dependências do Projeto: +pip install -r requirements.txt + +## Build Docker: +* Abrir o Docker e digitar no terminal +docker run +docker-compose up --build + diff --git a/api/admin.py b/api/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/api/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/api/apps.py b/api/apps.py deleted file mode 100644 index 878e7d5..0000000 --- a/api/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class ApiConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "api" diff --git a/api/models.py b/api/models.py deleted file mode 100644 index 71a8362..0000000 --- a/api/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/api/tests.py b/api/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/api/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/api/views.py b/api/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/api/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/bookstore/__pycache__/__init__.cpython-310.pyc b/bookstore/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..b2109ba Binary files /dev/null and b/bookstore/__pycache__/__init__.cpython-310.pyc differ diff --git a/bookstore/__pycache__/__init__.cpython-312.pyc b/bookstore/__pycache__/__init__.cpython-312.pyc index 3bf3045..b6a423e 100644 Binary files a/bookstore/__pycache__/__init__.cpython-312.pyc and b/bookstore/__pycache__/__init__.cpython-312.pyc differ diff --git a/bookstore/__pycache__/settings.cpython-310.pyc b/bookstore/__pycache__/settings.cpython-310.pyc new file mode 100644 index 0000000..236acfb Binary files /dev/null and b/bookstore/__pycache__/settings.cpython-310.pyc differ diff --git a/bookstore/__pycache__/settings.cpython-312.pyc b/bookstore/__pycache__/settings.cpython-312.pyc index 2b752d8..cbb2987 100644 Binary files a/bookstore/__pycache__/settings.cpython-312.pyc and b/bookstore/__pycache__/settings.cpython-312.pyc differ diff --git a/bookstore/__pycache__/urls.cpython-312.pyc b/bookstore/__pycache__/urls.cpython-312.pyc index 1c8deea..5338c85 100644 Binary files a/bookstore/__pycache__/urls.cpython-312.pyc and b/bookstore/__pycache__/urls.cpython-312.pyc differ diff --git a/bookstore/__pycache__/wsgi.cpython-312.pyc b/bookstore/__pycache__/wsgi.cpython-312.pyc index 315e3c2..4f1cec5 100644 Binary files a/bookstore/__pycache__/wsgi.cpython-312.pyc and b/bookstore/__pycache__/wsgi.cpython-312.pyc differ diff --git a/bookstore/settings.py b/bookstore/settings.py index 4cae424..eb446c5 100644 --- a/bookstore/settings.py +++ b/bookstore/settings.py @@ -1,15 +1,16 @@ """ Django settings for bookstore project. -Generated by 'django-admin startproject' using Django 5.0.3. +Generated by 'django-admin startproject' using Django 3.2.8. For more information on this file, see -https://docs.djangoproject.com/en/5.0/topics/settings/ +https://docs.djangoproject.com/en/3.2/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.0/ref/settings/ +https://docs.djangoproject.com/en/3.2/ref/settings/ """ +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -17,10 +18,10 @@ # Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ +# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-ytvw$u%b^9xle0&yw3*%=$s2zz@spz94k*%*88xr50s$dggo#e" +SECRET_KEY = "django-insecure-f*k@=53bc5!shef1-6w+m$-g)kspbaljz%8k4(j7iuc-u2_dyd" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -37,6 +38,12 @@ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "django_extensions", + "order", + "product", + "rest_framework", + "debug_toolbar", + "rest_framework.authtoken", ] MIDDLEWARE = [ @@ -47,6 +54,7 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "debug_toolbar.middleware.DebugToolbarMiddleware", ] ROOT_URLCONF = "bookstore.urls" @@ -71,18 +79,21 @@ # Database -# https://docs.djangoproject.com/en/5.0/ref/settings/#databases +# https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = { "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", + "ENGINE": os.environ.get("SQL_ENGINE", "django.db.backends.sqlite3"), + "NAME": os.environ.get("SQL_DATABASE", BASE_DIR / "db.sqlite3"), + "USER": os.environ.get("SQL_USER", "user"), + "PASSWORD": os.environ.get("SQL_PASSWORD", "password"), + "HOST": os.environ.get("SQL_HOST", "localhost"), + "PORT": os.environ.get("SQL_PORT", "5432"), } } - # Password validation -# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators +# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { @@ -101,7 +112,7 @@ # Internationalization -# https://docs.djangoproject.com/en/5.0/topics/i18n/ +# https://docs.djangoproject.com/en/3.2/topics/i18n/ LANGUAGE_CODE = "en-us" @@ -109,15 +120,42 @@ USE_I18N = True +USE_L10N = True + USE_TZ = True # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.0/howto/static-files/ +# https://docs.djangoproject.com/en/3.2/howto/static-files/ -STATIC_URL = "static/" +STATIC_URL = "/static/" # Default primary key field type -# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + + +REST_FRAMEWORK = { + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "PAGE_SIZE": 5, + "DEFAULT_AUTHENTICATION_CLASSES": [ + "rest_framework.authentication.BasicAuthentication", + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", + ], +} + +INTERNAL_IPS = [ + "127.0.0.1", +] + +# SECRET_KEY = os.environ.get("SECRET_KEY") + +# DEBUG = int(os.environ.get("DEBUG", default=0)) + +# 'DJANGO_ALLOWED_HOSTS' should be a single string of hosts with a space between each. +# For example: 'DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]' +# ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS").split(" ") + +ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'ebac-bookstore-api.herokuapp.com'] \ No newline at end of file diff --git a/bookstore/urls.py b/bookstore/urls.py index 420ec54..3be4bfd 100644 --- a/bookstore/urls.py +++ b/bookstore/urls.py @@ -15,9 +15,15 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +import debug_toolbar from django.contrib import admin -from django.urls import path +from django.urls import path, re_path, include +from rest_framework.authtoken.views import obtain_auth_token urlpatterns = [ + path("__debug__", include(debug_toolbar.urls)), path("admin/", admin.site.urls), + re_path("bookstore/(?P(v1|v2))/", include("order.urls")), + re_path("bookstore/(?P(v1|v2))/", include("product.urls")), + path("api-token-auth/", obtain_auth_token, name="api_token_auth"), ] diff --git a/db.sqlite3 b/db.sqlite3 index 07c4bca..92faf21 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b1dbbcc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3.9' + +services: + db: + image: postgres:13.0-alpine + ports: + - 5432:5432 + volumes: + - postgres_data:/var/lib/postgresql/data/ + environment: + - POSTGRES_USER=bookstore_dev + - POSTGRES_PASSWORD=bookstore_dev + - POSTGRES_DB=bookstore_dev_db + web: + build: . + command: python manage.py runserver 0.0.0.0:8000 + volumes: + - app_data:/usr/src/app/ + ports: + - 8000:8000 + env_file: + - ./env.dev + depends_on: + - db + +volumes: + postgres_data: + app_data: \ No newline at end of file diff --git a/env.dev b/env.dev new file mode 100644 index 0000000..491c2f0 --- /dev/null +++ b/env.dev @@ -0,0 +1,9 @@ +DEBUG=1 +SECRET_KEY=foo +DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1] +SQL_ENGINE=django.db.backends.postgresql +SQL_DATABASE=bookstore_dev_db +SQL_USER=bookstore_dev +SQL_PASSWORD=bookstore_dev +SQL_HOST=db +SQL_PORT=5432 \ No newline at end of file diff --git a/order/__pycache__/__init__.cpython-310.pyc b/order/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..cee84fd Binary files /dev/null and b/order/__pycache__/__init__.cpython-310.pyc differ diff --git a/order/__pycache__/__init__.cpython-312.pyc b/order/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e3ae2ca Binary files /dev/null and b/order/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/__pycache__/admin.cpython-310.pyc b/order/__pycache__/admin.cpython-310.pyc new file mode 100644 index 0000000..e69e6cd Binary files /dev/null and b/order/__pycache__/admin.cpython-310.pyc differ diff --git a/order/__pycache__/admin.cpython-312.pyc b/order/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..71bb81c Binary files /dev/null and b/order/__pycache__/admin.cpython-312.pyc differ diff --git a/order/__pycache__/apps.cpython-310.pyc b/order/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000..9ece4fa Binary files /dev/null and b/order/__pycache__/apps.cpython-310.pyc differ diff --git a/order/__pycache__/apps.cpython-312.pyc b/order/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..930cf69 Binary files /dev/null and b/order/__pycache__/apps.cpython-312.pyc differ diff --git a/order/__pycache__/factories.cpython-312.pyc b/order/__pycache__/factories.cpython-312.pyc new file mode 100644 index 0000000..50fecba Binary files /dev/null and b/order/__pycache__/factories.cpython-312.pyc differ diff --git a/order/__pycache__/tests.cpython-312.pyc b/order/__pycache__/tests.cpython-312.pyc new file mode 100644 index 0000000..d4e3772 Binary files /dev/null and b/order/__pycache__/tests.cpython-312.pyc differ diff --git a/order/__pycache__/urls.cpython-312.pyc b/order/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..62b0d80 Binary files /dev/null and b/order/__pycache__/urls.cpython-312.pyc differ diff --git a/order/admin.py b/order/admin.py index 8c38f3f..6a9d37e 100644 --- a/order/admin.py +++ b/order/admin.py @@ -1,3 +1,4 @@ from django.contrib import admin # Register your models here. +from .models import Order diff --git a/order/factories.py b/order/factories.py new file mode 100644 index 0000000..b0b0164 --- /dev/null +++ b/order/factories.py @@ -0,0 +1,30 @@ +import factory +from django.contrib.auth.models import User + +from order.models import Order +from product.factories import ProductFactory + + + +class UserFactory(factory.django.DjangoModelFactory): + email = factory.Faker("pystr") + username = factory.Faker("pystr") + + class Meta: + model = User + + +class OrderFactory(factory.django.DjangoModelFactory): + user = factory.SubFactory(UserFactory) + + @factory.post_generation + def product(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + for product in extracted: + self.product.add(product) + + class Meta: + model = Order diff --git a/order/migrations/0001_initial.py b/order/migrations/0001_initial.py new file mode 100644 index 0000000..e9182d0 --- /dev/null +++ b/order/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# Generated by Django 5.0.3 on 2024-03-10 22:00 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("product", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Order", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("product", models.ManyToManyField(to="product.product")), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/order/migrations/0002_alter_order_user.py b/order/migrations/0002_alter_order_user.py new file mode 100644 index 0000000..607a54c --- /dev/null +++ b/order/migrations/0002_alter_order_user.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.3 on 2024-03-29 17:41 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("order", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="order", + name="user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/order/migrations/0003_alter_order_user.py b/order/migrations/0003_alter_order_user.py new file mode 100644 index 0000000..d0b2ae1 --- /dev/null +++ b/order/migrations/0003_alter_order_user.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.3 on 2024-03-29 17:46 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("order", "0002_alter_order_user"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="order", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/order/migrations/__pycache__/0001_initial.cpython-312.pyc b/order/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 0000000..e09f603 Binary files /dev/null and b/order/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/order/migrations/__pycache__/0002_alter_order_user.cpython-312.pyc b/order/migrations/__pycache__/0002_alter_order_user.cpython-312.pyc new file mode 100644 index 0000000..a949783 Binary files /dev/null and b/order/migrations/__pycache__/0002_alter_order_user.cpython-312.pyc differ diff --git a/order/migrations/__pycache__/0003_alter_order_user.cpython-312.pyc b/order/migrations/__pycache__/0003_alter_order_user.cpython-312.pyc new file mode 100644 index 0000000..a0d0307 Binary files /dev/null and b/order/migrations/__pycache__/0003_alter_order_user.cpython-312.pyc differ diff --git a/order/migrations/__pycache__/__init__.cpython-312.pyc b/order/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0138771 Binary files /dev/null and b/order/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/models.py b/order/models.py deleted file mode 100644 index 71a8362..0000000 --- a/order/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/order/models/__init__.py b/order/models/__init__.py new file mode 100644 index 0000000..a611692 --- /dev/null +++ b/order/models/__init__.py @@ -0,0 +1 @@ +from .order import Order diff --git a/order/models/__pycache__/__init__.cpython-310.pyc b/order/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..92afda2 Binary files /dev/null and b/order/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/order/models/__pycache__/__init__.cpython-312.pyc b/order/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..e427957 Binary files /dev/null and b/order/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/models/__pycache__/order.cpython-310.pyc b/order/models/__pycache__/order.cpython-310.pyc new file mode 100644 index 0000000..9d8303d Binary files /dev/null and b/order/models/__pycache__/order.cpython-310.pyc differ diff --git a/order/models/__pycache__/order.cpython-312.pyc b/order/models/__pycache__/order.cpython-312.pyc new file mode 100644 index 0000000..ad343ad Binary files /dev/null and b/order/models/__pycache__/order.cpython-312.pyc differ diff --git a/order/models/order.py b/order/models/order.py new file mode 100644 index 0000000..f616ca0 --- /dev/null +++ b/order/models/order.py @@ -0,0 +1,8 @@ +from django.db import models +from product.models.product import Product +from django.contrib.auth.models import User + + +class Order(models.Model): + product = models.ManyToManyField(Product, blank=False) + user = models.ForeignKey(User, on_delete=models.CASCADE) diff --git a/order/serializers/__init__.py b/order/serializers/__init__.py new file mode 100644 index 0000000..e7396e1 --- /dev/null +++ b/order/serializers/__init__.py @@ -0,0 +1 @@ +from .order_serializer import OrderSerializer diff --git a/order/serializers/__pycache__/__init__.cpython-312.pyc b/order/serializers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ad967c9 Binary files /dev/null and b/order/serializers/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/serializers/__pycache__/order_serializer.cpython-312.pyc b/order/serializers/__pycache__/order_serializer.cpython-312.pyc new file mode 100644 index 0000000..af7b587 Binary files /dev/null and b/order/serializers/__pycache__/order_serializer.cpython-312.pyc differ diff --git a/order/serializers/order_serializer.py b/order/serializers/order_serializer.py new file mode 100644 index 0000000..2a3e7d5 --- /dev/null +++ b/order/serializers/order_serializer.py @@ -0,0 +1,32 @@ +from rest_framework import serializers + +from order.models import Order +from product.models import Product +from product.serializers.product_serializer import ProductSerializer + + +class OrderSerializer(serializers.ModelSerializer): + product = ProductSerializer(read_only=True, many=True) + products_id = serializers.PrimaryKeyRelatedField( + queryset=Product.objects.all(), write_only=True, many=True + ) + total = serializers.SerializerMethodField() + + def get_total(self, instance): + total = sum([product.price for product in instance.product.all()]) + return total + + class Meta: + model = Order + fields = ["product", "total", "user", "products_id"] + extra_kwargs = {"product": {"required": False}} + + def create(self, validated_data): + product_data = validated_data.pop("products_id") + user_data = validated_data.pop("user") + + order = Order.objects.create(user=user_data) + for product in product_data: + order.product.add(product) + + return order diff --git a/order/tests.py b/order/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/order/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/api/__init__.py b/order/tests/__init__.py similarity index 100% rename from api/__init__.py rename to order/tests/__init__.py diff --git a/order/tests/__pycache__/__init__.cpython-312.pyc b/order/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d81048f Binary files /dev/null and b/order/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/api/migrations/__init__.py b/order/tests/test_serializers/__init__.py similarity index 100% rename from api/migrations/__init__.py rename to order/tests/test_serializers/__init__.py diff --git a/order/tests/test_serializers/__pycache__/__init__.cpython-312.pyc b/order/tests/test_serializers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c1524b4 Binary files /dev/null and b/order/tests/test_serializers/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/tests/test_serializers/__pycache__/test_order_serializer.cpython-312.pyc b/order/tests/test_serializers/__pycache__/test_order_serializer.cpython-312.pyc new file mode 100644 index 0000000..bdaaf47 Binary files /dev/null and b/order/tests/test_serializers/__pycache__/test_order_serializer.cpython-312.pyc differ diff --git a/order/tests/test_serializers/test_order_serializer.py b/order/tests/test_serializers/test_order_serializer.py new file mode 100644 index 0000000..6ed1588 --- /dev/null +++ b/order/tests/test_serializers/test_order_serializer.py @@ -0,0 +1,20 @@ +from django.test import TestCase + +from order.factories import OrderFactory, ProductFactory +from order.serializers import OrderSerializer + + +class TestOrderSerializer(TestCase): + def setUp(self) -> None: + self.product_1 = ProductFactory() + self.product_2 = ProductFactory() + + self.order = OrderFactory(product=(self.product_1, self.product_2)) + self.order_serializer = OrderSerializer(self.order) + + def test_order_serializer(self): + serializer_data = self.order_serializer.data + self.assertEqual( + serializer_data["product"][0]["title"], self.product_1.title) + self.assertEqual( + serializer_data["product"][1]["title"], self.product_2.title) diff --git a/order/tests/test_viewsets/__init__.py b/order/tests/test_viewsets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/order/tests/test_viewsets/__pycache__/__init__.cpython-312.pyc b/order/tests/test_viewsets/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c4de5a6 Binary files /dev/null and b/order/tests/test_viewsets/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/tests/test_viewsets/__pycache__/test_order_viewset.cpython-312-pytest-8.0.2.pyc b/order/tests/test_viewsets/__pycache__/test_order_viewset.cpython-312-pytest-8.0.2.pyc new file mode 100644 index 0000000..47a02df Binary files /dev/null and b/order/tests/test_viewsets/__pycache__/test_order_viewset.cpython-312-pytest-8.0.2.pyc differ diff --git a/order/tests/test_viewsets/__pycache__/test_order_viewset.cpython-312.pyc b/order/tests/test_viewsets/__pycache__/test_order_viewset.cpython-312.pyc new file mode 100644 index 0000000..c2d3383 Binary files /dev/null and b/order/tests/test_viewsets/__pycache__/test_order_viewset.cpython-312.pyc differ diff --git a/order/tests/test_viewsets/test_order_viewset.py b/order/tests/test_viewsets/test_order_viewset.py new file mode 100644 index 0000000..4695348 --- /dev/null +++ b/order/tests/test_viewsets/test_order_viewset.py @@ -0,0 +1,66 @@ +import json + +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase, APIClient + + +from order.factories import UserFactory, OrderFactory +from order.models import Order +from product.factories import CategoryFactory, ProductFactory +from product.models import Product + + +class TestOrderViewSet(APITestCase): + + client = APIClient() + + def setUp(self): + self.category = CategoryFactory(title="technology") + self.product = ProductFactory( + title="mouse", price=100, category=[self.category] + ) + self.order = OrderFactory(product=[self.product]) + + def test_order(self): + response = self.client.get( + reverse("order-list", kwargs={"version": "v1"})) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + order_data = json.loads(response.content) + self.assertEqual( + order_data["results"][0]["product"][0]["title"], self.product.title + ) + self.assertEqual( + order_data["results"][0]["product"][0]["price"], self.product.price + ) + self.assertEqual( + order_data["results"][0]["product"][0]["active"], self.product.active + ) + self.assertEqual( + order_data["results"][0]["product"][0]["category"][0]["title"], + self.category.title, + ) + + def test_create_order(self): + user = UserFactory() + product = ProductFactory() + data = json.dumps( + { + "products_id": [product.id], + "user": user.id, + } + ) + + print(data) + + response = self.client.post( + reverse("order-list", kwargs={"version": "v1"}), + data=data, + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + created_order = Order.objects.get(user=user) diff --git a/order/urls.py b/order/urls.py new file mode 100644 index 0000000..20a55d4 --- /dev/null +++ b/order/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, include +from rest_framework import routers + +from order import viewsets + +router = routers.SimpleRouter() +router.register(r"order", viewsets.OrderViewSet, basename="order") + +urlpatterns = [ + path("", include(router.urls)), +] diff --git a/order/viewsets/__init__.py b/order/viewsets/__init__.py new file mode 100644 index 0000000..0b4e286 --- /dev/null +++ b/order/viewsets/__init__.py @@ -0,0 +1 @@ +from .order_viewset import OrderViewSet diff --git a/order/viewsets/__pycache__/__init__.cpython-312.pyc b/order/viewsets/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3eb660c Binary files /dev/null and b/order/viewsets/__pycache__/__init__.cpython-312.pyc differ diff --git a/order/viewsets/__pycache__/order_viewset.cpython-312.pyc b/order/viewsets/__pycache__/order_viewset.cpython-312.pyc new file mode 100644 index 0000000..bb3ba95 Binary files /dev/null and b/order/viewsets/__pycache__/order_viewset.cpython-312.pyc differ diff --git a/order/viewsets/order_viewset.py b/order/viewsets/order_viewset.py new file mode 100644 index 0000000..ad52962 --- /dev/null +++ b/order/viewsets/order_viewset.py @@ -0,0 +1,10 @@ +from rest_framework.viewsets import ModelViewSet + +from order.models import Order +from order.serializers import OrderSerializer + + +class OrderViewSet(ModelViewSet): + + serializer_class = OrderSerializer + queryset = Order.objects.all().order_by("id") diff --git a/poetry.lock b/poetry.lock index 9bc2d6c..9a59f72 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "asgiref" @@ -11,9 +11,87 @@ files = [ {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, ] +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +[[package]] +name = "atomicwrites" +version = "1.4.1" +description = "Atomic file writes." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "autopep8" +version = "1.7.0" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = "*" +files = [ + {file = "autopep8-1.7.0-py2.py3-none-any.whl", hash = "sha256:6f09e90a2be784317e84dc1add17ebfc7abe3924239957a37e5040e27d812087"}, + {file = "autopep8-1.7.0.tar.gz", hash = "sha256:ca9b1a83e53a7fad65d731dc7a2a2d50aa48f43850407c59f6a1a306c4201142"}, +] + +[package.dependencies] +pycodestyle = ">=2.9.1" +toml = "*" + +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +description = "Backport of the standard library zoneinfo module" +optional = false +python-versions = ">=3.6" +files = [ + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + +[package.extras] +tzdata = ["tzdata"] + [[package]] name = "colorama" version = "0.4.6" @@ -27,24 +105,81 @@ files = [ [[package]] name = "django" -version = "5.0.3" -description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +version = "3.2.25" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." optional = false -python-versions = ">=3.10" +python-versions = ">=3.6" files = [ - {file = "Django-5.0.3-py3-none-any.whl", hash = "sha256:5c7d748ad113a81b2d44750ccc41edc14e933f56581683db548c9257e078cc83"}, - {file = "Django-5.0.3.tar.gz", hash = "sha256:5fb37580dcf4a262f9258c1f4373819aacca906431f505e4688e37f3a99195df"}, + {file = "Django-3.2.25-py3-none-any.whl", hash = "sha256:a52ea7fcf280b16f7b739cec38fa6d3f8953a5456986944c3ca97e79882b4e38"}, + {file = "Django-3.2.25.tar.gz", hash = "sha256:7ca38a78654aee72378594d63e51636c04b8e28574f5505dff630895b5472777"}, ] [package.dependencies] -asgiref = ">=3.7.0,<4" -sqlparse = ">=0.3.1" -tzdata = {version = "*", markers = "sys_platform == \"win32\""} +asgiref = ">=3.3.2,<4" +pytz = "*" +sqlparse = ">=0.2.2" [package.extras] argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] +[[package]] +name = "django-debug-toolbar" +version = "3.8.1" +description = "A configurable set of panels that display various debug information about the current request/response." +optional = false +python-versions = ">=3.7" +files = [ + {file = "django_debug_toolbar-3.8.1-py3-none-any.whl", hash = "sha256:879f8a4672d41621c06a4d322dcffa630fc4df056cada6e417ed01db0e5e0478"}, + {file = "django_debug_toolbar-3.8.1.tar.gz", hash = "sha256:24ef1a7d44d25e60d7951e378454c6509bf536dce7e7d9d36e7c387db499bc27"}, +] + +[package.dependencies] +django = ">=3.2.4" +sqlparse = ">=0.2" + +[[package]] +name = "django-extensions" +version = "3.2.3" +description = "Extensions for Django" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, + {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-rest-framework" +version = "0.1.0" +description = "alias." +optional = false +python-versions = "*" +files = [ + {file = "django-rest-framework-0.1.0.tar.gz", hash = "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a"}, +] + +[package.dependencies] +djangorestframework = "*" + +[[package]] +name = "djangorestframework" +version = "3.15.1" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "djangorestframework-3.15.1-py3-none-any.whl", hash = "sha256:3ccc0475bce968608cf30d07fb17d8e52d1d7fc8bfe779c905463200750cbca6"}, + {file = "djangorestframework-3.15.1.tar.gz", hash = "sha256:f88fad74183dfc7144b2756d0d2ac716ea5b4c7c9840995ac3bfd8ec034333c1"}, +] + +[package.dependencies] +"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} +django = ">=3.0" + [[package]] name = "factory-boy" version = "3.3.0" @@ -58,6 +193,7 @@ files = [ [package.dependencies] Faker = ">=0.7.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "sqlalchemy-utils", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"] @@ -65,17 +201,58 @@ doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] [[package]] name = "faker" -version = "24.0.0" +version = "18.13.0" description = "Faker is a Python package that generates fake data for you." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "Faker-24.0.0-py3-none-any.whl", hash = "sha256:2456d674f40bd51eb3acbf85221277027822e529a90cc826453d9a25dff932b1"}, - {file = "Faker-24.0.0.tar.gz", hash = "sha256:ea6f784c40730de0f77067e49e78cdd590efb00bec3d33f577492262206c17fc"}, + {file = "Faker-18.13.0-py3-none-any.whl", hash = "sha256:801d1a2d71f1fc54d332de2ab19de7452454309937233ea2f7485402882d67b3"}, + {file = "Faker-18.13.0.tar.gz", hash = "sha256:84bcf92bb725dd7341336eea4685df9a364f16f2470c4d29c1d7e6c5fd5a457d"}, ] [package.dependencies] python-dateutil = ">=2.4" +typing-extensions = {version = ">=3.10.0.1", markers = "python_version < \"3.8\""} + +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, + {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, +] + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "importlib-metadata" +version = "6.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, + {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "iniconfig" @@ -88,51 +265,179 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "isort" +version = "5.11.5" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.2.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "psycopg2-binary" +version = "2.9.9" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"}, + {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"}, + {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"}, + {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"}, + {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"}, + {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"}, + {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, +] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pycodestyle" +version = "2.10.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, + {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, +] + [[package]] name = "pytest" -version = "8.0.2" +version = "6.2.5" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.6" files = [ - {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, - {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] [package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.3.0,<2.0" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] name = "python-dateutil" @@ -148,6 +453,33 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "setuptools" +version = "68.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -176,17 +508,57 @@ doc = ["sphinx"] test = ["pytest", "pytest-cov"] [[package]] -name = "tzdata" -version = "2024.1" -description = "Provider of IANA time zone data" +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" optional = false -python-versions = ">=2" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "whitenoise" +version = "5.3.0" +description = "Radically simplified static file serving for WSGI applications" +optional = false +python-versions = ">=3.5, <4" +files = [ + {file = "whitenoise-5.3.0-py2.py3-none-any.whl", hash = "sha256:d963ef25639d1417e8a247be36e6aedd8c7c6f0a08adcb5a89146980a96b577c"}, + {file = "whitenoise-5.3.0.tar.gz", hash = "sha256:d234b871b52271ae7ed6d9da47ffe857c76568f11dd30e28e18c5869dbd11e12"}, +] + +[package.extras] +brotli = ["Brotli"] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "2.0" -python-versions = "^3.12" -content-hash = "53e5b2a828b776d376ea78a8931feee2baf8a7d3c3e8515c368cc70d5a208f90" +python-versions = "^3.7" +content-hash = "3d24a74ddb066994975c85b456e5889359e72bfa95d271ccd733f12dee92b275" diff --git a/product/__pycache__/__init__.cpython-310.pyc b/product/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..87be3e2 Binary files /dev/null and b/product/__pycache__/__init__.cpython-310.pyc differ diff --git a/product/__pycache__/__init__.cpython-312.pyc b/product/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c10d763 Binary files /dev/null and b/product/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/__pycache__/admin.cpython-310.pyc b/product/__pycache__/admin.cpython-310.pyc new file mode 100644 index 0000000..5843aa7 Binary files /dev/null and b/product/__pycache__/admin.cpython-310.pyc differ diff --git a/product/__pycache__/admin.cpython-312.pyc b/product/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..bfb9952 Binary files /dev/null and b/product/__pycache__/admin.cpython-312.pyc differ diff --git a/product/__pycache__/apps.cpython-310.pyc b/product/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000..474891a Binary files /dev/null and b/product/__pycache__/apps.cpython-310.pyc differ diff --git a/product/__pycache__/apps.cpython-312.pyc b/product/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..20f93da Binary files /dev/null and b/product/__pycache__/apps.cpython-312.pyc differ diff --git a/product/__pycache__/factories.cpython-312.pyc b/product/__pycache__/factories.cpython-312.pyc new file mode 100644 index 0000000..6000fbc Binary files /dev/null and b/product/__pycache__/factories.cpython-312.pyc differ diff --git a/product/__pycache__/urls.cpython-312.pyc b/product/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..e6d483d Binary files /dev/null and b/product/__pycache__/urls.cpython-312.pyc differ diff --git a/product/admin.py b/product/admin.py index 8c38f3f..398d249 100644 --- a/product/admin.py +++ b/product/admin.py @@ -1,3 +1,4 @@ from django.contrib import admin # Register your models here. +from .models import Product, Category diff --git a/product/factories.py b/product/factories.py new file mode 100644 index 0000000..8a6ebef --- /dev/null +++ b/product/factories.py @@ -0,0 +1,31 @@ +import factory + +from product.models import Product, Category + + +class CategoryFactory(factory.django.DjangoModelFactory): + title = factory.Faker("pystr") + slug = factory.Faker("pystr") + description = factory.Faker("pystr") + active = factory.Iterator([True, False]) + + class Meta: + model = Category + + +class ProductFactory(factory.django.DjangoModelFactory): + price = factory.Faker("pyint") + category = factory.LazyAttribute(CategoryFactory) + title = factory.Faker("pystr") + + @factory.post_generation + def category(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + for category in extracted: + self.category.add(category) + + class Meta: + model = Product diff --git a/product/migrations/0001_initial.py b/product/migrations/0001_initial.py new file mode 100644 index 0000000..5f11f77 --- /dev/null +++ b/product/migrations/0001_initial.py @@ -0,0 +1,56 @@ +# Generated by Django 5.0.3 on 2024-03-10 22:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Category", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=100)), + ("slug", models.SlugField(unique=True)), + ( + "description", + models.CharField(blank=True, max_length=200, null=True), + ), + ("active", models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name="Product", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=100)), + ( + "description", + models.TextField(blank=True, max_length=500, null=True), + ), + ("price", models.PositiveIntegerField(null=True)), + ("active", models.BooleanField(default=True)), + ("category", models.ManyToManyField(blank=True, to="product.category")), + ], + ), + ] diff --git a/product/migrations/__pycache__/0001_initial.cpython-312.pyc b/product/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 0000000..efd00a2 Binary files /dev/null and b/product/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/product/migrations/__pycache__/__init__.cpython-312.pyc b/product/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d8cd976 Binary files /dev/null and b/product/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/models.py b/product/models.py deleted file mode 100644 index 71a8362..0000000 --- a/product/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/product/models/__init__.py b/product/models/__init__.py new file mode 100644 index 0000000..4114c9c --- /dev/null +++ b/product/models/__init__.py @@ -0,0 +1,2 @@ +from .category import Category +from .product import Product diff --git a/product/models/__pycache__/__init__.cpython-310.pyc b/product/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e87e6d2 Binary files /dev/null and b/product/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/product/models/__pycache__/__init__.cpython-312.pyc b/product/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8fd1688 Binary files /dev/null and b/product/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/models/__pycache__/category.cpython-310.pyc b/product/models/__pycache__/category.cpython-310.pyc new file mode 100644 index 0000000..42a1245 Binary files /dev/null and b/product/models/__pycache__/category.cpython-310.pyc differ diff --git a/product/models/__pycache__/category.cpython-312.pyc b/product/models/__pycache__/category.cpython-312.pyc new file mode 100644 index 0000000..1f683fe Binary files /dev/null and b/product/models/__pycache__/category.cpython-312.pyc differ diff --git a/product/models/__pycache__/product.cpython-310.pyc b/product/models/__pycache__/product.cpython-310.pyc new file mode 100644 index 0000000..09e6119 Binary files /dev/null and b/product/models/__pycache__/product.cpython-310.pyc differ diff --git a/product/models/__pycache__/product.cpython-312.pyc b/product/models/__pycache__/product.cpython-312.pyc new file mode 100644 index 0000000..6317789 Binary files /dev/null and b/product/models/__pycache__/product.cpython-312.pyc differ diff --git a/product/models/category.py b/product/models/category.py new file mode 100644 index 0000000..fe10920 --- /dev/null +++ b/product/models/category.py @@ -0,0 +1,11 @@ +from django.db import models + + +class Category(models.Model): + title = models.CharField(max_length=100) + slug = models.SlugField(unique=True) + description = models.CharField(max_length=200, blank=True, null=True) + active = models.BooleanField(default=True) + + def __unicode__(self): + return self.title diff --git a/product/models/product.py b/product/models/product.py new file mode 100644 index 0000000..21513b2 --- /dev/null +++ b/product/models/product.py @@ -0,0 +1,11 @@ +from django.db import models + +from product.models import Category + + +class Product(models.Model): + title = models.CharField(max_length=100) + description = models.TextField(max_length=500, blank=True, null=True) + price = models.PositiveIntegerField(null=True) + active = models.BooleanField(default=True) + category = models.ManyToManyField(Category, blank=True) diff --git a/product/serializers/__init__.py b/product/serializers/__init__.py new file mode 100644 index 0000000..dd0bfa2 --- /dev/null +++ b/product/serializers/__init__.py @@ -0,0 +1,2 @@ +from .product_serializer import ProductSerializer +from .category_serializer import CategorySerializer diff --git a/product/serializers/__pycache__/__init__.cpython-312.pyc b/product/serializers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0c80e24 Binary files /dev/null and b/product/serializers/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/serializers/__pycache__/category_serializer.cpython-312.pyc b/product/serializers/__pycache__/category_serializer.cpython-312.pyc new file mode 100644 index 0000000..687a909 Binary files /dev/null and b/product/serializers/__pycache__/category_serializer.cpython-312.pyc differ diff --git a/product/serializers/__pycache__/product_serializer.cpython-312.pyc b/product/serializers/__pycache__/product_serializer.cpython-312.pyc new file mode 100644 index 0000000..9739049 Binary files /dev/null and b/product/serializers/__pycache__/product_serializer.cpython-312.pyc differ diff --git a/product/serializers/category_serializer.py b/product/serializers/category_serializer.py new file mode 100644 index 0000000..7da9ace --- /dev/null +++ b/product/serializers/category_serializer.py @@ -0,0 +1,15 @@ +from rest_framework import serializers + +from product.models.category import Category + + +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category + fields = [ + "title", + "slug", + "description", + "active", + ] + extra_kwargs = {"slug": {"required": False}} diff --git a/product/serializers/product_serializer.py b/product/serializers/product_serializer.py new file mode 100644 index 0000000..4a5d5d7 --- /dev/null +++ b/product/serializers/product_serializer.py @@ -0,0 +1,32 @@ +from rest_framework import serializers + +from product.models.product import Product, Category +from product.serializers.category_serializer import CategorySerializer + + +class ProductSerializer(serializers.ModelSerializer): + category = CategorySerializer(read_only=True, many=True) + categories_id = serializers.PrimaryKeyRelatedField( + queryset=Category.objects.all(), write_only=True, many=True + ) + + class Meta: + model = Product + fields = [ + "id", + "title", + "description", + "price", + "active", + "category", + "categories_id", + ] + + def create(self, validated_data): + category_data = validated_data.pop("categories_id") + + product = Product.objects.create(**validated_data) + for category in category_data: + product.category.add(category) + + return product diff --git a/product/tests.py b/product/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/product/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/product/tests/__init__.py b/product/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/product/tests/__pycache__/__init__.cpython-312.pyc b/product/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..822a00d Binary files /dev/null and b/product/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/tests/test_serializers/__init__.py b/product/tests/test_serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/product/tests/test_serializers/__pycache__/__init__.cpython-312.pyc b/product/tests/test_serializers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3e40150 Binary files /dev/null and b/product/tests/test_serializers/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/tests/test_serializers/__pycache__/test_category_serializer.cpython-312.pyc b/product/tests/test_serializers/__pycache__/test_category_serializer.cpython-312.pyc new file mode 100644 index 0000000..6c1202d Binary files /dev/null and b/product/tests/test_serializers/__pycache__/test_category_serializer.cpython-312.pyc differ diff --git a/product/tests/test_serializers/__pycache__/test_product_serializer.cpython-312.pyc b/product/tests/test_serializers/__pycache__/test_product_serializer.cpython-312.pyc new file mode 100644 index 0000000..64b1904 Binary files /dev/null and b/product/tests/test_serializers/__pycache__/test_product_serializer.cpython-312.pyc differ diff --git a/product/tests/test_serializers/test_category_serializer.py b/product/tests/test_serializers/test_category_serializer.py new file mode 100644 index 0000000..a18307c --- /dev/null +++ b/product/tests/test_serializers/test_category_serializer.py @@ -0,0 +1,15 @@ +from django.test import TestCase + +from product.factories import CategoryFactory, ProductFactory +from product.serializers import CategorySerializer + +# Exercício EBAC - tests +class TestCategorySerializer(TestCase): + def setUp(self) -> None: + self.category = CategoryFactory(title="food") + self.category_serializer = CategorySerializer(self.category) + + def test_order_serializer(self): + serializer_data = self.category_serializer.data + + self.assertEqual(serializer_data["title"], "food") diff --git a/product/tests/test_serializers/test_product_serializer.py b/product/tests/test_serializers/test_product_serializer.py new file mode 100644 index 0000000..2be469a --- /dev/null +++ b/product/tests/test_serializers/test_product_serializer.py @@ -0,0 +1,20 @@ +from django.test import TestCase + +from product.factories import CategoryFactory, ProductFactory +from product.serializers import ProductSerializer + + +class TestProductSerializer(TestCase): + def setUp(self) -> None: + self.category = CategoryFactory(title="technology") + self.product_1 = ProductFactory( + title="mouse", price=100, category=[self.category] + ) + self.product_serializer = ProductSerializer(self.product_1) + + def test_product_serializer(self): + serializer_data = self.product_serializer.data + self.assertEqual(serializer_data["price"], 100) + self.assertEqual(serializer_data["title"], "mouse") + self.assertEqual( + serializer_data["category"][0]["title"], "technology") diff --git a/product/tests/tests_viewsets/__init__.py b/product/tests/tests_viewsets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/product/tests/tests_viewsets/__pycache__/__init__.cpython-312.pyc b/product/tests/tests_viewsets/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..c085ab4 Binary files /dev/null and b/product/tests/tests_viewsets/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/tests/tests_viewsets/__pycache__/test_category_viewset.cpython-312.pyc b/product/tests/tests_viewsets/__pycache__/test_category_viewset.cpython-312.pyc new file mode 100644 index 0000000..493a564 Binary files /dev/null and b/product/tests/tests_viewsets/__pycache__/test_category_viewset.cpython-312.pyc differ diff --git a/product/tests/tests_viewsets/__pycache__/test_product_viewset.cpython-312-pytest-8.0.2.pyc b/product/tests/tests_viewsets/__pycache__/test_product_viewset.cpython-312-pytest-8.0.2.pyc new file mode 100644 index 0000000..5961e90 Binary files /dev/null and b/product/tests/tests_viewsets/__pycache__/test_product_viewset.cpython-312-pytest-8.0.2.pyc differ diff --git a/product/tests/tests_viewsets/__pycache__/test_product_viewset.cpython-312.pyc b/product/tests/tests_viewsets/__pycache__/test_product_viewset.cpython-312.pyc new file mode 100644 index 0000000..5302a18 Binary files /dev/null and b/product/tests/tests_viewsets/__pycache__/test_product_viewset.cpython-312.pyc differ diff --git a/product/tests/tests_viewsets/test_category_viewset.py b/product/tests/tests_viewsets/test_category_viewset.py new file mode 100644 index 0000000..5a7d755 --- /dev/null +++ b/product/tests/tests_viewsets/test_category_viewset.py @@ -0,0 +1,40 @@ +import json + +from django.urls import reverse +from rest_framework.test import APIClient, APITestCase +from rest_framework.views import status + +from product.factories import CategoryFactory +from product.models import Category + + +class CategoryViewSet(APITestCase): + client = APIClient() + + def setUp(self): + self.category = CategoryFactory(title="books") + + def test_get_all_category(self): + response = self.client.get( + reverse("category-list", kwargs={"version": "v1"})) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + category_data = json.loads(response.content) + + self.assertEqual(category_data["results"] + [0]["title"], self.category.title) + + def test_create_category(self): + data = json.dumps({"title": "technology"}) + + response = self.client.post( + reverse("category-list", kwargs={"version": "v1"}), + data=data, + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + created_category = Category.objects.get(title="technology") + + self.assertEqual(created_category.title, "technology") diff --git a/product/tests/tests_viewsets/test_product_viewset.py b/product/tests/tests_viewsets/test_product_viewset.py new file mode 100644 index 0000000..ac6eddc --- /dev/null +++ b/product/tests/tests_viewsets/test_product_viewset.py @@ -0,0 +1,58 @@ +from django.urls import reverse +from rest_framework.authtoken.models import Token +from rest_framework.test import APITestCase, APIClient +from rest_framework import status +from product.factories import CategoryFactory, ProductFactory +from order.factories import UserFactory +from product.models import Product +import json + + +class TestProductViewSet(APITestCase): + client = APIClient() + + def setUp(self): + self.user = UserFactory() + token = Token.objects.create(user=self.user) + token.save() + + self.product = ProductFactory( + title="pro controller", + price=200.00, + ) + + def test_get_all_product(self): + token = Token.objects.get(user__username=self.user.username) + self.client.credentials( + HTTP_AUTHORIZATION="Token " + token.key) + response = self.client.get( + reverse("product-list", kwargs={"version": "v1"})) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + product_data = json.loads(response.content) + + self.assertEqual(product_data["results"][0]["title"], self.product.title) + self.assertEqual(product_data["results"][0]["price"], self.product.price) + self.assertEqual(product_data["results"][0]["active"], self.product.active) + + def test_create_product(self): + token = Token.objects.get(user__username=self.user.username) + self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key) + category = CategoryFactory() + data = json.dumps( + {"title": "notebook", "price": 800.00, + "categories_id": [category.id]} + ) + + response = self.client.post( + reverse("product-list", kwargs={"version": "v1"}), + data=data, + content_type="application/json", + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + created_product = Product.objects.get(title="notebook") + + self.assertEqual(created_product.title, "notebook") + self.assertEqual(created_product.price, 800.00) diff --git a/product/urls.py b/product/urls.py new file mode 100644 index 0000000..381f1c0 --- /dev/null +++ b/product/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework import routers + +from product import viewsets + +router = routers.SimpleRouter() +router.register(r"product", viewsets.ProductViewSet, basename="product") +router.register(r"category", viewsets.CategoryViewSet, basename="category") + +urlpatterns = [ + path("", include(router.urls)), +] diff --git a/product/viewsets/__init__.py b/product/viewsets/__init__.py new file mode 100644 index 0000000..a68317b --- /dev/null +++ b/product/viewsets/__init__.py @@ -0,0 +1,2 @@ +from .product_viewset import ProductViewSet +from .category_viewset import CategoryViewSet diff --git a/product/viewsets/__pycache__/__init__.cpython-312.pyc b/product/viewsets/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1d5695c Binary files /dev/null and b/product/viewsets/__pycache__/__init__.cpython-312.pyc differ diff --git a/product/viewsets/__pycache__/category_viewset.cpython-312.pyc b/product/viewsets/__pycache__/category_viewset.cpython-312.pyc new file mode 100644 index 0000000..7903905 Binary files /dev/null and b/product/viewsets/__pycache__/category_viewset.cpython-312.pyc differ diff --git a/product/viewsets/__pycache__/product_viewset.cpython-312.pyc b/product/viewsets/__pycache__/product_viewset.cpython-312.pyc new file mode 100644 index 0000000..a83e47d Binary files /dev/null and b/product/viewsets/__pycache__/product_viewset.cpython-312.pyc differ diff --git a/product/viewsets/category_viewset.py b/product/viewsets/category_viewset.py new file mode 100644 index 0000000..511d41a --- /dev/null +++ b/product/viewsets/category_viewset.py @@ -0,0 +1,11 @@ +from rest_framework.viewsets import ModelViewSet + +from product.models import Category +from product.serializers.category_serializer import CategorySerializer + + +class CategoryViewSet(ModelViewSet): + serializer_class = CategorySerializer + + def get_queryset(self): + return Category.objects.all().order_by("id") diff --git a/product/viewsets/product_viewset.py b/product/viewsets/product_viewset.py new file mode 100644 index 0000000..42e3ec2 --- /dev/null +++ b/product/viewsets/product_viewset.py @@ -0,0 +1,13 @@ +from rest_framework.viewsets import ModelViewSet +from rest_framework.permissions import IsAuthenticated + + +from product.models import Product +from product.serializers.product_serializer import ProductSerializer + + +class ProductViewSet(ModelViewSet): + serializer_class = ProductSerializer + + def get_queryset(self): + return Product.objects.all().order_by("id") diff --git a/pyproject.toml b/pyproject.toml index 8f598c7..a0486e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,16 +2,24 @@ name = "bookstore" version = "0.1.0" description = "Bookstore API" -authors = ["matheus-dev-fullstack "] -readme = "README.md" +authors = ["Daniel Santos "] [tool.poetry.dependencies] -python = "^3.12" -pytest = "^8.0.2" -factory-boy = "^3.3.0" -django = "^5.0.3" +python = "^3.7" +Django = "^3.2.8" +django-rest-framework = "^0.1.0" +django-extensions = "^3.1.3" +django-debug-toolbar = "^3.2.2" +psycopg2-binary = "^2.9.2" +autopep8 = "^1.6.0" +isort = "^5.10.1" +whitenoise = "^5.3.0" +gunicorn = "^20.1.0" +[tool.poetry.dev-dependencies] +pytest = "^6.2.5" +factory-boy = "^3.2.0" [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9783973 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,57 @@ +asgiref==3.7.2 +build==1.2.1 +CacheControl==0.14.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +cleo==2.1.0 +colorama==0.4.6 +crashtest==0.4.1 +distlib==0.3.8 +Django==5.0.3 +django-debug-toolbar==4.3.0 +django-extensions==3.2.3 +django-rest-framework==0.1.0 +djangorestframework==3.15.1 +dulwich==0.21.7 +factory-boy==3.3.0 +Faker==24.0.0 +fastjsonschema==2.19.1 +filelock==3.14.0 +flake8==7.0.0 +idna==3.7 +iniconfig==2.0.0 +installer==0.7.0 +jaraco.classes==3.4.0 +keyring==24.3.1 +mccabe==0.7.0 +more-itertools==10.2.0 +msgpack==1.0.8 +packaging==23.2 +pexpect==4.9.0 +pkginfo==1.10.0 +platformdirs==4.2.1 +pluggy==1.4.0 +poetry==1.8.3 +poetry-core==1.9.0 +poetry-plugin-export==1.7.1 +psycopg2==2.9.9 +ptyprocess==0.7.0 +pycodestyle==2.11.1 +pyflakes==3.2.0 +pyproject_hooks==1.1.0 +pytest==8.0.2 +python-dateutil==2.9.0.post0 +pywin32-ctypes==0.2.2 +rapidfuzz==3.9.0 +requests==2.31.0 +requests-toolbelt==1.0.0 +shellingham==1.5.4 +six==1.16.0 +sqlparse==0.4.4 +tomlkit==0.12.5 +trove-classifiers==2024.4.10 +tzdata==2024.1 +urllib3==2.2.1 +virtualenv==20.26.1 + +# Exercício EBAC Build