diff --git a/BackendTest_venv/bin/Activate.ps1 b/BackendTest_venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/BackendTest_venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/BackendTest_venv/bin/activate b/BackendTest_venv/bin/activate new file mode 100644 index 0000000..12017ce --- /dev/null +++ b/BackendTest_venv/bin/activate @@ -0,0 +1,69 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(BackendTest_venv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(BackendTest_venv) " + export VIRTUAL_ENV_PROMPT +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/BackendTest_venv/bin/activate.csh b/BackendTest_venv/bin/activate.csh new file mode 100644 index 0000000..17f605b --- /dev/null +++ b/BackendTest_venv/bin/activate.csh @@ -0,0 +1,26 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(BackendTest_venv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(BackendTest_venv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/BackendTest_venv/bin/activate.fish b/BackendTest_venv/bin/activate.fish new file mode 100644 index 0000000..6e158d3 --- /dev/null +++ b/BackendTest_venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(BackendTest_venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(BackendTest_venv) " +end diff --git a/BackendTest_venv/bin/django-admin b/BackendTest_venv/bin/django-admin new file mode 100755 index 0000000..e05aeff --- /dev/null +++ b/BackendTest_venv/bin/django-admin @@ -0,0 +1,8 @@ +#!/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from django.core.management import execute_from_command_line +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(execute_from_command_line()) diff --git a/BackendTest_venv/bin/pip b/BackendTest_venv/bin/pip new file mode 100755 index 0000000..d6f14f2 --- /dev/null +++ b/BackendTest_venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/BackendTest_venv/bin/pip3 b/BackendTest_venv/bin/pip3 new file mode 100755 index 0000000..d6f14f2 --- /dev/null +++ b/BackendTest_venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/BackendTest_venv/bin/pip3.10 b/BackendTest_venv/bin/pip3.10 new file mode 100755 index 0000000..d6f14f2 --- /dev/null +++ b/BackendTest_venv/bin/pip3.10 @@ -0,0 +1,8 @@ +#!/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/BackendTest_venv/bin/python b/BackendTest_venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/BackendTest_venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/BackendTest_venv/bin/python3 b/BackendTest_venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/BackendTest_venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/BackendTest_venv/bin/python3.10 b/BackendTest_venv/bin/python3.10 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/BackendTest_venv/bin/python3.10 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/BackendTest_venv/bin/sqlformat b/BackendTest_venv/bin/sqlformat new file mode 100755 index 0000000..abbbcd2 --- /dev/null +++ b/BackendTest_venv/bin/sqlformat @@ -0,0 +1,8 @@ +#!/home/hp/Desktop/project/Backend_test/Backend-Test-/BackendTest_venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from sqlparse.__main__ import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/BackendTest_venv/lib64 b/BackendTest_venv/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/BackendTest_venv/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/BackendTest_venv/pyvenv.cfg b/BackendTest_venv/pyvenv.cfg new file mode 100644 index 0000000..0537ffc --- /dev/null +++ b/BackendTest_venv/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.10.12 diff --git a/src_codes/core/.gitignore b/src_codes/core/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/src_codes/core/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/src_codes/core/Dockerfile b/src_codes/core/Dockerfile new file mode 100644 index 0000000..feced58 --- /dev/null +++ b/src_codes/core/Dockerfile @@ -0,0 +1,19 @@ +# Using the official Python image on dockerhub. +FROM python:3.9-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# Set work directory +WORKDIR /usr/src/app + +# Install dependencies +COPY requirements.txt /usr/src/app/ +RUN pip install --no-cache-dir -r requirements.txt + +# Copy project +COPY . /usr/src/app/ + +# Run the Django development server +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"] diff --git a/src_codes/core/Order_App/__init__.py b/src_codes/core/Order_App/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/Order_App/admin.py b/src_codes/core/Order_App/admin.py new file mode 100644 index 0000000..4657924 --- /dev/null +++ b/src_codes/core/Order_App/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from Order_App.models import Order +# Register your models here. +admin.site.register(Order) + diff --git a/src_codes/core/Order_App/apps.py b/src_codes/core/Order_App/apps.py new file mode 100644 index 0000000..beb66c4 --- /dev/null +++ b/src_codes/core/Order_App/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class OrderAppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'Order_App' diff --git a/src_codes/core/Order_App/migrations/0001_initial.py b/src_codes/core/Order_App/migrations/0001_initial.py new file mode 100644 index 0000000..d1999c6 --- /dev/null +++ b/src_codes/core/Order_App/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 5.0.7 on 2024-07-23 10:44 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('User_App', '0001_initial'), + ('products_App', '0003_rename_product_name_products_name_delete_order'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Order', + fields=[ + ('Order_id', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('products', models.ManyToManyField(to='products_App.products')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['Order_id', '-created_at'], + }, + ), + ] diff --git a/src_codes/core/Order_App/migrations/__init__.py b/src_codes/core/Order_App/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/Order_App/models.py b/src_codes/core/Order_App/models.py new file mode 100644 index 0000000..479a511 --- /dev/null +++ b/src_codes/core/Order_App/models.py @@ -0,0 +1,25 @@ +from django.db import models +from User_App.models import CustomUser +from products_App.models import Products +# Create your models here. +class Order(models.Model): + """_summary_ + + Args: + models (_type_): _description_ + + Returns: + _type_: _description_ + """ + Order_id = models.OneToOneField(CustomUser, on_delete=models.CASCADE, primary_key=True) + user = models.ForeignKey(CustomUser, related_name='orders', on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True) + products = models.ManyToManyField(Products) + + + class Meta: + ordering = ['Order_id', '-created_at'] + + + def __str__(self): + return f'{self.Order_id}' diff --git a/src_codes/core/Order_App/serializers.py b/src_codes/core/Order_App/serializers.py new file mode 100644 index 0000000..82760a3 --- /dev/null +++ b/src_codes/core/Order_App/serializers.py @@ -0,0 +1,17 @@ +from rest_framework import serializers +from Order_App.models import Order + +class OrderSerializers(serializers.ModelSerializer): + """_summary_ + + Args: + serializers (_type_): _description_ + """ + class Meta: + model =Order + fields =[ + "Order_id", + "user", + "created_at", + "products" + ] \ No newline at end of file diff --git a/src_codes/core/Order_App/test/tests_views.py b/src_codes/core/Order_App/test/tests_views.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/src_codes/core/Order_App/test/tests_views.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src_codes/core/Order_App/tests.py b/src_codes/core/Order_App/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/src_codes/core/Order_App/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src_codes/core/Order_App/urls.py b/src_codes/core/Order_App/urls.py new file mode 100644 index 0000000..417a455 --- /dev/null +++ b/src_codes/core/Order_App/urls.py @@ -0,0 +1,14 @@ +from Order_App.views import OrderViewSet +from django.urls import path, include +from rest_framework.routers import DefaultRouter + +router = DefaultRouter() +#router.register(r'categories', CategoryViewSet) +#router.register(r'products', ProductViewSet) +router.register(r'orders', OrderViewSet) + +urlpatterns = [ + path('', include(router.urls)), + path('orders/place_order/', OrderViewSet.as_view({'post': 'place_order'}), name='place_order'), + path('orders/order_history/', OrderViewSet.as_view({'get': 'order_history'}), name='order_history'), +] \ No newline at end of file diff --git a/src_codes/core/Order_App/views.py b/src_codes/core/Order_App/views.py new file mode 100644 index 0000000..f47593b --- /dev/null +++ b/src_codes/core/Order_App/views.py @@ -0,0 +1,45 @@ +from django.shortcuts import render +from Order_App.models import Order +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework import viewsets +from Order_App.serializers import OrderSerializers + +# Create your views here. +""" +class CategoryViewSet(viewsets.ModelViewSet): + queryset = Category.objects.all() + serializer_class = CategorySerializer + +class ProductViewSet(viewsets.ModelViewSet): + queryset = Product.objects.all() + serializer_class = ProductSerializer +""" + +class OrderViewSet(viewsets.ModelViewSet): + queryset = Order.objects.all() + serializer_class = OrderSerializers + permission_classes = [IsAuthenticated] + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + + @action(detail=False, methods=['post'], permission_classes=[IsAuthenticated]) + def place_order(self, request): + serializer = self.get_serializer(data=request.data) + if serializer.is_valid(): + serializer.save(user=request.user) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated]) + def order_history(self, request): + orders = Order.objects.filter(user=request.user) + serializer = self.get_serializer(orders, many=True) + return Response(serializer.data) + + + + diff --git a/src_codes/core/User_App/__init__.py b/src_codes/core/User_App/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/User_App/admin.py b/src_codes/core/User_App/admin.py new file mode 100644 index 0000000..e5b358e --- /dev/null +++ b/src_codes/core/User_App/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from User_App.models import CustomUser, Group +# Register your models here. + +admin.site.register(CustomUser) +admin.site.register(Group) diff --git a/src_codes/core/User_App/apps.py b/src_codes/core/User_App/apps.py new file mode 100644 index 0000000..91e3b30 --- /dev/null +++ b/src_codes/core/User_App/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UserAppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'User_App' diff --git a/src_codes/core/User_App/migrations/0001_initial.py b/src_codes/core/User_App/migrations/0001_initial.py new file mode 100644 index 0000000..bcc59ff --- /dev/null +++ b/src_codes/core/User_App/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# Generated by Django 5.0.7 on 2024-07-20 20:53 + +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('email', models.EmailField(max_length=254, unique=True)), + ('first_name', models.CharField(blank=True, max_length=30)), + ('last_name', models.CharField(blank=True, max_length=30)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('is_seller', models.BooleanField(default=False)), + ('is_buyer', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Group', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('group_name', models.CharField(max_length=300)), + ('group_description', models.TextField()), + ('date_created', models.DateTimeField(default=django.utils.timezone.now)), + ('group_members', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/src_codes/core/User_App/migrations/__init__.py b/src_codes/core/User_App/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/User_App/models.py b/src_codes/core/User_App/models.py new file mode 100644 index 0000000..79eb184 --- /dev/null +++ b/src_codes/core/User_App/models.py @@ -0,0 +1,65 @@ +from django.db import models +from django.contrib.auth.models import User +from django.utils import timezone + +# Create your models here. +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin + + +class CustomUserManager(BaseUserManager): + def create_user(self, email, password=None, **extra_fields): + if not email: + raise ValueError('The Email field must be set') + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save(using=self._db) + return user + + def create_superuser(self, email, password=None, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + + if extra_fields.get('is_staff') is not True: + raise ValueError('Superuser must have is_staff=True.') + if extra_fields.get('is_superuser') is not True: + raise ValueError('Superuser must have is_superuser=True.') + + return self.create_user(email, password, **extra_fields) + +class CustomUser(AbstractBaseUser, PermissionsMixin): + email = models.EmailField(unique=True) + first_name = models.CharField(max_length=30, blank=True) + last_name = models.CharField(max_length=30, blank=True) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + is_seller = models.BooleanField(default=False) + is_buyer = models.BooleanField(default=False) + + + + objects = CustomUserManager() + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = [] + + def __str__(self): + return self.email + +class Group(models.Model): + """Defines parameters for creating user group + + Args: + models (_type_): _description_ + + Returns: + _type_: group_namee + """ + group_name = models.CharField(max_length=300,) + group_description = models.TextField() + group_members = models.ManyToManyField(CustomUser, blank=True ) + date_created = models.DateTimeField(default=timezone.now) + + def __str__(self): + return self.group_name + diff --git a/src_codes/core/User_App/serializers.py b/src_codes/core/User_App/serializers.py new file mode 100644 index 0000000..20eec0b --- /dev/null +++ b/src_codes/core/User_App/serializers.py @@ -0,0 +1,30 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers +from User_App.models import CustomUser + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = CustomUser + fields = ('id', 'email', 'first_name', 'last_name') + +class RegisterSerializer(serializers.ModelSerializer): + password = serializers.CharField(write_only=True) + + class Meta: + model = CustomUser + fields = ('email', 'password', 'first_name', 'last_name', 'is_seller', 'is_buyer') + + def create(self, validated_data): + user = CustomUser.objects.create_user( + email=validated_data['email'], + password=validated_data['password'], + first_name=validated_data.get('first_name'), + last_name=validated_data.get('last_name'), + is_seller=validated_data.get('is_seller'), + is_buyer=validated_data.get('is_buyer') + ) + return user + +class LoginSerializer(serializers.Serializer): + email = serializers.EmailField() + password = serializers.CharField(write_only=True) diff --git a/src_codes/core/User_App/tests/__init__.py b/src_codes/core/User_App/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/User_App/tests/test_views.py b/src_codes/core/User_App/tests/test_views.py new file mode 100644 index 0000000..5105ff0 --- /dev/null +++ b/src_codes/core/User_App/tests/test_views.py @@ -0,0 +1,59 @@ +from django.test import TestCase +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from django.contrib.auth import get_user_model +from rest_framework_simplejwt.tokens import RefreshToken +from User_App.models import CustomUser + +User = get_user_model() + +class User_AppTests(APITestCase): + + def setUp(self): + self.register_url = reverse('register') + self.login_url = reverse('login') + self.logout_url = reverse('logout') + self.user_data = { + "email": "user@example.com", + #"password": "string", + "first_name": "string", + "last_name": "string", + "is_active": True, + "is_staff": False, + "is_seller": True, + "is_buyer": False +} + self.user = CustomUser.objects.create_user(**self.user_data) + #self.user.set_password(self.user_data['password']) + self.user.save() + + + def test_register_user(self): + response = self.client.post(self.register_url, self.user_data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(CustomUser.objects.count(), 2) # One user is created in setUp and one by this test + self.assertEqual(CustomUser.objects.get(id=2).email, self.user_data['email']) + +""" + def test_login_user(self): + login_data = { + 'email': self.user_data['email'], + 'password': self.user_data['password'] + } + response = self.client.post(self.login_url, login_data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('access', response.data) + self.assertIn('refresh', response.data) + + def test_logout_user(self): + login_data = { + 'email': self.user_data['email'], + 'password': self.user_data['password'] + } + login_response = self.client.post(self.login_url, login_data, format='json') + refresh_token = login_response.data['refresh'] + response = self.client.post(self.logout_url, {'refresh': refresh_token}, format='json') + self.assertEqual(response.status_code, status.HTTP_205_RESET_CONTENT) + +""" \ No newline at end of file diff --git a/src_codes/core/User_App/urls.py b/src_codes/core/User_App/urls.py new file mode 100644 index 0000000..f29f133 --- /dev/null +++ b/src_codes/core/User_App/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from User_App.views import RegisterView, LoginView, LogoutView + +urlpatterns = [ + path('register/', RegisterView.as_view(), name='register'), + path('login/', LoginView.as_view(), name='login'), + path('logout/', LogoutView.as_view(), name='logout'), +] diff --git a/src_codes/core/User_App/views.py b/src_codes/core/User_App/views.py new file mode 100644 index 0000000..2e3a07f --- /dev/null +++ b/src_codes/core/User_App/views.py @@ -0,0 +1,59 @@ +from django.shortcuts import render +from django.contrib.auth import authenticate +from rest_framework import generics, status +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework_simplejwt.tokens import RefreshToken +from User_App.serializers import RegisterSerializer, LoginSerializer, UserSerializer +from django.contrib.auth import get_user_model +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema + +User = get_user_model() + +class RegisterView(generics.CreateAPIView): + queryset = User.objects.all() + serializer_class = RegisterSerializer + +class LoginView(APIView): + serializer_class = LoginSerializer + + @swagger_auto_schema( + operation_description="Login User", + request_body=LoginSerializer, + responses={200: LoginSerializer}, + examples={ + "application/json": { + "email": "example@gmail", + "password": "mypassword" + } + } + ) + + def post(self, request, *args, **kwargs): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + user = authenticate( + request, + email=serializer.validated_data['email'], + password=serializer.validated_data['password'] + ) + if user is not None: + refresh = RefreshToken.for_user(user) + return Response({ + 'refresh': str(refresh), + 'access': str(refresh.access_token), + }) + return Response({'detail': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED) + + +class LogoutView(APIView): + + def post(self, request, *args, **kwargs): + try: + refresh_token = request.data["refresh"] + token = RefreshToken(refresh_token) + token.blacklist() + return Response(status=status.HTTP_205_RESET_CONTENT) + except Exception as e: + return Response(status=status.HTTP_400_BAD_REQUEST) diff --git a/src_codes/core/core/__init__.py b/src_codes/core/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/core/asgi.py b/src_codes/core/core/asgi.py new file mode 100644 index 0000000..b0f9e57 --- /dev/null +++ b/src_codes/core/core/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for core project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_asgi_application() diff --git a/src_codes/core/core/settings.py b/src_codes/core/core/settings.py new file mode 100644 index 0000000..1351e7b --- /dev/null +++ b/src_codes/core/core/settings.py @@ -0,0 +1,176 @@ +""" +Django settings for core project. + +Generated by 'django-admin startproject' using Django 5.0.7. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" +import os +from pathlib import Path +from datetime import timedelta + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-4#t9zq998^jxlkbgzrb2^w67jq9un&l-zgm2vk0q36j$@44is2' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + #third party apps + 'rest_framework', + 'rest_framework_simplejwt', + "django_filters", + 'drf_yasg', + + #user defined apps + 'User_App', + 'products_App', + 'Order_App' + +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'core.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'core.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +if DEBUG: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('POSTGRES_DB'), + 'USER': os.getenv('POSTGRES_USER'), + 'PASSWORD': os.getenv('POSTGRES_PASSWORD'), + 'HOST': os.getenv('POSTGRES_HOST'), + 'PORT': os.getenv('POSTGRES_PORT'), + } +} + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), +} + +SIMPLE_JWT = { + 'AUTH_HEADER_TYPES': ('Bearer',), +} + +AUTH_USER_MODEL = 'User_App.CustomUser' + +""" +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), +} + +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ROTATE_REFRESH_TOKENS': False, + 'BLACKLIST_AFTER_ROTATION': True, +} +AUTH_USER_MODEL = 'User_App.CustomUser' +""" \ No newline at end of file diff --git a/src_codes/core/core/urls.py b/src_codes/core/core/urls.py new file mode 100644 index 0000000..9a7e9ec --- /dev/null +++ b/src_codes/core/core/urls.py @@ -0,0 +1,48 @@ +""" +URL configuration for core project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" + +# myproject/urls.py +ROOT_API_URL ="api/v1/" +from django.contrib import admin +from django.urls import path, include +from drf_yasg.views import get_schema_view +from drf_yasg import openapi +from rest_framework.permissions import AllowAny +from rest_framework import permissions + +schema_view = get_schema_view( + openapi.Info( + title="paybox 360 API", + default_version="v1", + description="Welcome to the API for aybox 360 API. Please do not use without permissions", + terms_of_service="https://www.paybox_360.co", + contact=openapi.Contact(email="support@paybox360.co"), + license=openapi.License(name="Awesome IP"), + ), + public=True, + permission_classes=(permissions.AllowAny,), +) + + +urlpatterns = [ + path("", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"), + path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + path('admin/', admin.site.urls), + path(ROOT_API_URL+'api/', include('Order_App.urls')), + path(ROOT_API_URL+'api/auth/', include('User_App.urls')), + path(ROOT_API_URL+'api/products/', include('products_App.urls')), +] diff --git a/src_codes/core/core/wsgi.py b/src_codes/core/core/wsgi.py new file mode 100644 index 0000000..53399e0 --- /dev/null +++ b/src_codes/core/core/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for core project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_wsgi_application() diff --git a/src_codes/core/docker-compose.yml b/src_codes/core/docker-compose.yml new file mode 100644 index 0000000..4d2d01d --- /dev/null +++ b/src_codes/core/docker-compose.yml @@ -0,0 +1,32 @@ +version: '3.8' + +services: + db: + image: postgres:13 + env_file: + - .env + volumes: + - postgres_data:/var/lib/postgresql/data + + web: + build: . + command: gunicorn core.wsgi:application --bind 0.0.0.0:8000 + volumes: + - .:/usr/src/app + ports: + - "8000:8000" + env_file: + - .env + depends_on: + - db + nginx: + image: nginx:latest + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + ports: + - "80:80" + depends_on: + - web + +volumes: + postgres_data: diff --git a/src_codes/core/manage.py b/src_codes/core/manage.py new file mode 100755 index 0000000..f2a662c --- /dev/null +++ b/src_codes/core/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/src_codes/core/nginx.conf b/src_codes/core/nginx.conf new file mode 100644 index 0000000..d48b630 --- /dev/null +++ b/src_codes/core/nginx.conf @@ -0,0 +1,19 @@ +server { + listen 80; + + location / { + proxy_pass http://web:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /static/ { + alias /usr/src/app/static/; + } + + location /media/ { + alias /usr/src/app/media/; + } +} diff --git a/src_codes/core/products_App/__init__.py b/src_codes/core/products_App/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/products_App/admin.py b/src_codes/core/products_App/admin.py new file mode 100644 index 0000000..b2f2c0c --- /dev/null +++ b/src_codes/core/products_App/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from products_App.models import Products, Category +# Register your models here. +admin.site.register(Products) +admin.site.register(Category) + + diff --git a/src_codes/core/products_App/apps.py b/src_codes/core/products_App/apps.py new file mode 100644 index 0000000..cc1e652 --- /dev/null +++ b/src_codes/core/products_App/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ProductsAppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'products_App' diff --git a/src_codes/core/products_App/migrations/0001_initial.py b/src_codes/core/products_App/migrations/0001_initial.py new file mode 100644 index 0000000..297e391 --- /dev/null +++ b/src_codes/core/products_App/migrations/0001_initial.py @@ -0,0 +1,57 @@ +# Generated by Django 5.0.7 on 2024-07-20 20:53 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('User_App', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + 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=255)), + ], + options={ + 'verbose_name_plural': 'Categories', + }, + ), + migrations.CreateModel( + name='Products', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Product_id', models.CharField(max_length=10)), + ('Product_Name', models.CharField(max_length=100)), + ('Price', models.IntegerField()), + ('Stock', models.IntegerField()), + ('imageUrl', models.URLField()), + ('Status', models.BooleanField(default=True)), + ('Date_created', models.DateField(auto_now_add=True)), + ('Category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='products_App.category')), + ('Created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['Date_created'], + }, + ), + migrations.CreateModel( + name='Cart', + fields=[ + ('cart_id', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('products', models.ManyToManyField(to='products_App.products')), + ], + options={ + 'ordering': ['cart_id', '-created_at'], + }, + ), + ] diff --git a/src_codes/core/products_App/migrations/0002_order_delete_cart.py b/src_codes/core/products_App/migrations/0002_order_delete_cart.py new file mode 100644 index 0000000..7c88aa4 --- /dev/null +++ b/src_codes/core/products_App/migrations/0002_order_delete_cart.py @@ -0,0 +1,32 @@ +# Generated by Django 5.0.7 on 2024-07-21 16:15 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('User_App', '0001_initial'), + ('products_App', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Order', + fields=[ + ('Order_id', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('products', models.ManyToManyField(to='products_App.products')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['Order_id', '-created_at'], + }, + ), + migrations.DeleteModel( + name='Cart', + ), + ] diff --git a/src_codes/core/products_App/migrations/0003_rename_product_name_products_name_delete_order.py b/src_codes/core/products_App/migrations/0003_rename_product_name_products_name_delete_order.py new file mode 100644 index 0000000..98e8546 --- /dev/null +++ b/src_codes/core/products_App/migrations/0003_rename_product_name_products_name_delete_order.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.7 on 2024-07-23 10:44 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('products_App', '0002_order_delete_cart'), + ] + + operations = [ + migrations.RenameField( + model_name='products', + old_name='Product_Name', + new_name='Name', + ), + migrations.DeleteModel( + name='Order', + ), + ] diff --git a/src_codes/core/products_App/migrations/0004_remove_products_created_by_alter_products_product_id.py b/src_codes/core/products_App/migrations/0004_remove_products_created_by_alter_products_product_id.py new file mode 100644 index 0000000..1fde33b --- /dev/null +++ b/src_codes/core/products_App/migrations/0004_remove_products_created_by_alter_products_product_id.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.7 on 2024-07-24 07:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('products_App', '0003_rename_product_name_products_name_delete_order'), + ] + + operations = [ + migrations.RemoveField( + model_name='products', + name='Created_by', + ), + migrations.AlterField( + model_name='products', + name='Product_id', + field=models.IntegerField(), + ), + ] diff --git a/src_codes/core/products_App/migrations/0005_remove_products_status.py b/src_codes/core/products_App/migrations/0005_remove_products_status.py new file mode 100644 index 0000000..d50127b --- /dev/null +++ b/src_codes/core/products_App/migrations/0005_remove_products_status.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.7 on 2024-07-24 07:44 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('products_App', '0004_remove_products_created_by_alter_products_product_id'), + ] + + operations = [ + migrations.RemoveField( + model_name='products', + name='Status', + ), + ] diff --git a/src_codes/core/products_App/migrations/0006_remove_products_product_id_category_descriptions_and_more.py b/src_codes/core/products_App/migrations/0006_remove_products_product_id_category_descriptions_and_more.py new file mode 100644 index 0000000..27ba539 --- /dev/null +++ b/src_codes/core/products_App/migrations/0006_remove_products_product_id_category_descriptions_and_more.py @@ -0,0 +1,42 @@ +# Generated by Django 5.0.7 on 2024-07-24 09:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('products_App', '0005_remove_products_status'), + ] + + operations = [ + migrations.RemoveField( + model_name='products', + name='Product_id', + ), + migrations.AddField( + model_name='category', + name='Descriptions', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='products', + name='Descriptions', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='products', + name='Updated_at', + field=models.DateField(auto_now=True), + ), + migrations.AlterField( + model_name='products', + name='Price', + field=models.FloatField(), + ), + migrations.AlterField( + model_name='products', + name='Stock', + field=models.FloatField(), + ), + ] diff --git a/src_codes/core/products_App/migrations/0007_rename_descriptions_category_description_and_more.py b/src_codes/core/products_App/migrations/0007_rename_descriptions_category_description_and_more.py new file mode 100644 index 0000000..eae2f0d --- /dev/null +++ b/src_codes/core/products_App/migrations/0007_rename_descriptions_category_description_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.7 on 2024-07-24 11:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('products_App', '0006_remove_products_product_id_category_descriptions_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='category', + old_name='Descriptions', + new_name='Description', + ), + migrations.RenameField( + model_name='products', + old_name='Descriptions', + new_name='Description', + ), + ] diff --git a/src_codes/core/products_App/migrations/__init__.py b/src_codes/core/products_App/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src_codes/core/products_App/models.py b/src_codes/core/products_App/models.py new file mode 100644 index 0000000..da7c538 --- /dev/null +++ b/src_codes/core/products_App/models.py @@ -0,0 +1,49 @@ +from django.db import models +from django.contrib.auth.models import User +from User_App.models import CustomUser +# Create your models here. + +class Category(models.Model): + """Define parameters for creating product catergory + + Args: + models (_type_): _description_ + + Returns: + _type_: _description_ + """ + title = models.CharField(max_length=255, null=False) + Description = models.TextField(blank=True, null=True) + + class Meta: + verbose_name_plural = 'Categories' + def __str__(self): + return self.title + + +class Products(models.Model): + """_summary_ + + Args: + models (_type_): _description_ + + Returns: + _type_: _description_ + """ + Name = models.CharField(max_length=100) + Description = models.TextField(blank=True, null=True) + Category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE) + Price = models.FloatField(null=False) + Stock = models.FloatField(null=False) + imageUrl = models.URLField() + #Created_by = models.ForeignKey(CustomUser, related_name='products', on_delete=models.CASCADE) + Updated_at = models.DateField(auto_now=True) + Date_created = models.DateField(auto_now_add=True) + + class Meta: + ordering = ['Date_created'] + + def __str__(self): + return '{} {}'.format(self.Price, self.name) + + diff --git a/src_codes/core/products_App/pagination.py b/src_codes/core/products_App/pagination.py new file mode 100644 index 0000000..112f39a --- /dev/null +++ b/src_codes/core/products_App/pagination.py @@ -0,0 +1,6 @@ +from rest_framework.pagination import PageNumberPagination + +class CustomPagination(PageNumberPagination): + page_size = 5 + page_size_query_param = 'page_size' + max_page_size = 100 \ No newline at end of file diff --git a/src_codes/core/products_App/serializers.py b/src_codes/core/products_App/serializers.py new file mode 100644 index 0000000..bea4769 --- /dev/null +++ b/src_codes/core/products_App/serializers.py @@ -0,0 +1,30 @@ +from rest_framework import serializers +from products_App.models import Products, Category + + +class ProductSerializers(serializers.ModelSerializer): + """_summary_ + + Args: + serializers (_type_): _description_ + """ + class Meta: + model =Products + fields ='__all__' + + +class CartegorySerializers(serializers.ModelSerializer): + """_summary_ + + Args: + serializers (_type_): _description_ + """ + class Meta: + model =Category + fields =[ + "title" + ] + + + + diff --git a/src_codes/core/products_App/test/tests_view.py b/src_codes/core/products_App/test/tests_view.py new file mode 100644 index 0000000..724e4ce --- /dev/null +++ b/src_codes/core/products_App/test/tests_view.py @@ -0,0 +1,17 @@ +from django.test import TestCase +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase +from django.contrib.auth import get_user_model +from rest_framework_simplejwt.tokens import RefreshToken + +User = get_user_model() + +class AccountTests(APITestCase): + + def setUp(self): + self.product_url = reverse('login') + self.user_data = { + + } + \ No newline at end of file diff --git a/src_codes/core/products_App/urls.py b/src_codes/core/products_App/urls.py new file mode 100644 index 0000000..51c9839 --- /dev/null +++ b/src_codes/core/products_App/urls.py @@ -0,0 +1,14 @@ +from django.urls import path, include +from products_App.views import ProductViewSet, CategoryViewSet +from rest_framework.routers import DefaultRouter + +router = DefaultRouter() +router.register(r'category', CategoryViewSet) +router.register(r'products', ProductViewSet) +urlpatterns = [ + path('', include(router.urls)), + #path('CreateProduct/', productCreateRetreiveAll.as_view(), name='CreateProduct'), + #path('products/', productCreateRetreiveAll.as_view(), name='products'), + #path('DeleteProduct/', ProductDetail.as_view(), name='DeleteProduct'), + #path('UpdateProduct/', ProductDetail.as_view(), name='UpdateProduct'), +] \ No newline at end of file diff --git a/src_codes/core/products_App/views.py b/src_codes/core/products_App/views.py new file mode 100644 index 0000000..63dfa7a --- /dev/null +++ b/src_codes/core/products_App/views.py @@ -0,0 +1,116 @@ +from django.shortcuts import render +from products_App.models import Category, Products +from rest_framework import status +from rest_framework.views import APIView +from products_App.serializers import CartegorySerializers,ProductSerializers +from rest_framework.response import Response +from rest_framework import viewsets +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import SearchFilter +from products_App.pagination import CustomPagination +# Create your views here. + + +class ProductViewSet(viewsets.ModelViewSet): + queryset = Products.objects.all() + serializer_class = ProductSerializers + +class CategoryViewSet(viewsets.ModelViewSet): + queryset = Category.objects.all() + serializer_class = CartegorySerializers +""" +class productCreateRetreiveAll(APIView): + + def post (self, request): + serializer_class = ProductSerializers(data=request.data) + if serializer_class.is_valid(): + serializer_class.save() + return Response(data=serializer_class.data, status=status.HTTP_201_CREATED) + + def get(self, request): + user = Products.objects.all() + serializer_class = ProductSerializers(user, many = True) + pagination_class = CustomPagination + return Response(data=serializer_class.data, status= status.HTTP_200_OK) + +class ProductDetail(APIView): + + def get_object(self, pk): + try: + return Products.objects.get(pk=pk) + except: + raise Response(status=status.HTTP_404_NOT_FOUND) + + def get(self, request, pk, format = None): + product = self.get_object(pk) + serializer = ProductSerializers(product) + return Response(serializer.data) + + def put(self, request, pk, format=None): + products = self.get_object(pk) + serializer = ProductSerializers(products, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk, format=None): + product = self.get_object(pk) + product.delete() + return Response(status=status.HTTP_204_NO_CONTENT) +""" + +class ProductSearch(viewsets.ModelViewSet): + queryset = Products.objects.all() + serializer_class = ProductSerializers + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['category'] + search_fields = ['Product_Name', 'Price'] + +class CatergoryCreateRetreiveAll(APIView): + """Create and get all products + + Args: + APIView (_type_): _description_ + """ + def post (self, request): + serializer = CartegorySerializers(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(data=serializer.data, status=status.HTTP_201_CREATED) + + def get(self, request): + categories = Category.objects.all() + serializer = CartegorySerializers(categories, many = True) + return Response(data=serializer.data, status= status.HTTP_200_OK) + +class CatergoryDetail(APIView): + """Retrieve, update or delete a Catergory from the database + + Args: + APIView (_type_): _description_ + """ + def get_object(self, pk): + try: + return Category.objects.get(pk=pk) + except: + raise Response(status=status.HTTP_404_NOT_FOUND) + + def get(self, request, pk, format = None): + product_cart = self.get_object(pk) + serializer = ProductSerializers(product_cart) + return Response(serializer.data) + + def put(self, request, pk, format=None): + product_cart = self.get_object(pk) + serializer = CartegorySerializers(product_cart, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk, format=None): + product_cart = self.get_object(pk) + product_cart.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + diff --git a/src_codes/core/requirements.txt b/src_codes/core/requirements.txt new file mode 100644 index 0000000..9d5f2d5 --- /dev/null +++ b/src_codes/core/requirements.txt @@ -0,0 +1,16 @@ +asgiref==3.8.1 +Django==5.0.7 +django-filter==24.2 +djangorestframework==3.15.2 +djangorestframework-simplejwt==5.3.1 +drf-yasg==1.21.7 +gunicorn==22.0.0 +inflection==0.5.1 +packaging==24.1 +psycopg2-binary==2.9.9 +PyJWT==2.8.0 +pytz==2024.1 +PyYAML==6.0.1 +sqlparse==0.5.1 +typing_extensions==4.12.2 +uritemplate==4.1.1