diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..6563da8cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,130 @@ +# Django # +*.log +*.pot +*.pyc +__pycache__ +db.sqlite3 +media + +# Backup files # +*.bak + +# If you are using PyCharm # +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/gradle.xml +.idea/**/libraries +*.iws /out/ + +# Python # +*.py[cod] +*$py.class + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Sublime Text # +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files Package +Control.last-run +Control.ca-list +Control.ca-bundle +Control.system-ca-bundle +GitHub.sublime-settings + +# Visual Studio Code # +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +# Personal additions +__pycache__/ +*.pyc +__localcode +venv +env +.DS_Store +local_settings.py +_local_settings.py +gunicorn-error-log +db.sqlite3 +db copy.sqlite3 +static/ \ No newline at end of file diff --git a/README.md b/README.md index ea8115e67..fa33bef83 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,21 @@ -# Back End Test Project - -You should see this challenge as an opportunity to create an application following modern development best practices (given the stack of your choice), but also feel free to use your own architecture preferences (coding standards, code organization, third-party libraries, etc). It’s perfectly fine to use vanilla code or any framework or libraries. - -## Scope - -In this challenge you should build an API for an application that stores and manages investments, it should have the following features: - -1. __Creation__ of an investment with an owner, a creation date and an amount. - 1. The creation date of an investment can be today or a date in the past. - 2. An investment should not be or become negative. -2. __View__ of an investment with its initial amount and expected balance. - 1. Expected balance should be the sum of the invested amount and the [gains][]. - 2. If an investment was already withdrawn then the balance must reflect the gains of that investment -3. __Withdrawal__ of a investment. - 1. The withdraw will always be the sum of the initial amount and its gains, - partial withdrawn is not supported. - 2. Withdrawals can happen in the past or today, but can't happen before the investment creation or the future. - 3. [Taxes][taxes] need to be applied to the withdrawals before showing the final value. -4. __List__ of a person's investments - 1. This list should have pagination. - -__NOTE:__ the implementation of an interface will not be evaluated. - -### Gain Calculation - -The investment will pay 0.52% every month in the same day of the investment creation. - -Given that the gain is paid every month, it should be treated as [compound gain][], which means that every new period (month) the amount gained will become part of the investment balance for the next payment. - -### Taxation - -When money is withdrawn, tax is triggered. Taxes apply only to the profit/gain portion of the money withdrawn. For example, if the initial investment was 1000.00, the current balance is 1200.00, then the taxes will be applied to the 200.00. - -The tax percentage changes according to the age of the investment: -* If it is less than one year old, the percentage will be 22.5% (tax = 45.00). -* If it is between one and two years old, the percentage will be 18.5% (tax = 37.00). -* If older than two years, the percentage will be 15% (tax = 30.00). - -## Requirements -1. Create project using any technology of your preference. It’s perfectly OK to use vanilla code or any framework or libraries; -2. Although you can use as many dependencies as you want, you should manage them wisely; -3. It is not necessary to send the notification emails, however, the code required for that would be welcome; -4. The API must be documented in some way. - -## Deliverables -The project source code and dependencies should be made available in GitHub. Here are the steps you should follow: -1. Fork this repository to your GitHub account (create an account if you don't have one, you will need it working with us). -2. Create a "development" branch and commit the code to it. Do not push the code to the main branch. -3. Include a README file that describes: - - Special build instructions, if any - - List of third-party libraries used and short description of why/how they were used - - A link to the API documentation. -4. Once the work is complete, create a pull request from "development" into "main" and send us the link. -5. Avoid using huge commits hiding your progress. Feel free to work on a branch and use `git rebase` to adjust your commits before submitting the final version. - -## Coding Standards -When working on the project be as clean and consistent as possible. - -## Project Deadline -Ideally you'd finish the test project in 5 days. It shouldn't take you longer than a entire week. - -## Quality Assurance -Use the following checklist to ensure high quality of the project. - -### General -- First of all, the application should run without errors. -- Are all requirements set above met? -- Is coding style consistent? -- The API is well documented? -- The API has unit tests? - -## Submission -1. A link to the Github repository. -2. Briefly describe how you decided on the tools that you used. - -## Have Fun Coding 🤘 -- This challenge description is intentionally vague in some aspects, but if you need assistance feel free to ask for help. -- If any of the seems out of your current level, you may skip it, but remember to tell us about it in the pull request. - -## Credits - -This coding challenge was inspired on [kinvoapp/kinvo-back-end-test](https://github.com/kinvoapp/kinvo-back-end-test/blob/2f17d713de739e309d17a1a74a82c3fd0e66d128/README.md) - -[gains]: #gain-calculation -[taxes]: #taxation -[interest]: #interest-calculation -[compound gain]: https://www.investopedia.com/terms/g/gain.asp +

# investment-management

+

+ + +

+
+API para gerenciamento e armazenamento de investimentos. Feita para o projeto de teste de back-end da coderockr. + +## :hammer: Instalação do projeto + +- `1º passo`: Clone o repostorio na sua maquina. +- `2º passo`: Dentro do diretorio do projeto crie um ambiente virtual -> "python3 -m venv venv" +- `3º passo`: Ative o ambiente virtual -> "source venv/bin/activate" +- `4º passo`: instale as dependencias com o pip -> "pip install -r requirements.txt" +- `5º passo`: Rode as migrações -> "python3 manage.py migrate" +- `6º passo`: Crie um Superusuário -> "python3 manage.py createsuperuser" +- `7º passo`: Suba o servidor -> "python3 manage.py runserver" + +## ✔️ Acessar admin e Documentação da API como o swagger +-> Para acessar a pagina de admin do projeto apenas acesse a rota "localhost:8000/admin" e insira o login e senha que criou no passo 6
+-> Para acessar a documentação swagger do projeto acesse a rota "localhost:8000/api/" diff --git a/investment/__init__.py b/investment/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/investment/admin.py b/investment/admin.py new file mode 100644 index 000000000..e67b2ff66 --- /dev/null +++ b/investment/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from .models import Investments, Owner + +@admin.register(Investments) +class InvestmentsAdmin(admin.ModelAdmin): + list_display = ("owner", "creation_date", "amount") + +@admin.register(Owner) +class OwnerAdmin(admin.ModelAdmin): + list_display = ("name",) + diff --git a/investment/api/__init__.py b/investment/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/investment/api/serializers.py b/investment/api/serializers.py new file mode 100644 index 000000000..9c097ddcd --- /dev/null +++ b/investment/api/serializers.py @@ -0,0 +1,50 @@ +from rest_framework import serializers +from decimal import Decimal +from datetime import date, timedelta +from investment.models import Investments, Owner + +class OwnerSerializer(serializers.ModelSerializer): + class Meta: + model = Owner + fields = "__all__" + depth = 1 + +class InvestmentSerializer(serializers.ModelSerializer): + + class Meta: + model = Investments + fields = "__all__" + + # @staticmethod + # def calc_investment_date(update_date): + # today = date.today() + # days = (today - update_date).days + # months_in_days = days - (days % 30) + # new_update_date = update_date + timedelta(days=months_in_days) + # months = days // 30 + # return months, new_update_date + + # def create(self, validated_data): + # calc = self.calc_investment_date(validated_data.get('update_date')) + # gain = validated_data.get('amount') * (1 + Decimal(0.0052))**calc[0] + # # id = self.id + # validated_data.update({'amount':gain}) + # validated_data.update({'update_date':calc[1]}) + # # value = gain*(1+Decimal(0.0052))**1 + # investment = InvestmentSerializer(**validated_data) + # return investment + + +class InvestmentNestedSerializer(serializers.ModelSerializer): + owner = OwnerSerializer() + + class Meta: + model = Investments + fields = ('id', 'owner', 'creation_date', 'balance', 'expected_balance') + +class InvestmentGetNestedSerializer(serializers.ModelSerializer): + owner = OwnerSerializer() + + class Meta: + model = Investments + fields = ('id', 'owner', 'creation_date', 'amount', 'balance', 'expected_balance', 'income', 'withdrawal_date') diff --git a/investment/api/urls.py b/investment/api/urls.py new file mode 100644 index 000000000..9d9bce6c7 --- /dev/null +++ b/investment/api/urls.py @@ -0,0 +1,9 @@ +from rest_framework.routers import SimpleRouter + +from investment.api.views import InvestmentsViewSet, OwnerViewSet + +router = SimpleRouter(trailing_slash=False) + +router.register(r"investments", InvestmentsViewSet) +router.register(r"owners", OwnerViewSet) + diff --git a/investment/api/views.py b/investment/api/views.py new file mode 100644 index 000000000..2e6ef6c8e --- /dev/null +++ b/investment/api/views.py @@ -0,0 +1,68 @@ +from rest_framework import viewsets, filters +from rest_framework.decorators import action +from rest_framework.response import Response +from datetime import date +from decimal import Decimal +from django.shortcuts import get_object_or_404 + +from django_filters.rest_framework import DjangoFilterBackend + +from investment.api.serializers import OwnerSerializer, InvestmentSerializer, InvestmentNestedSerializer, \ +InvestmentGetNestedSerializer + +from investment.models import Investments, Owner + +class OwnerViewSet(viewsets.ModelViewSet): + queryset = Owner.objects.all() + serializer_class = OwnerSerializer + +class InvestmentsViewSet(viewsets.ModelViewSet): + queryset = Investments.objects.all() + serializer_class = InvestmentSerializer + filter_backends = [DjangoFilterBackend, filters.SearchFilter] + filterset_fields = ["owner__id"] + search_fields = ["owner__name"] + + def create(self, request, *args, **kwargs): + request.data.update({'update_date':request.data.get('creation_date')}) + request.data.update({'balance':request.data.get('amount')}) + return super().create(request, *args, **kwargs) + + + def retrieve(self, request, *args, **kwargs): + self.serializer_class = InvestmentGetNestedSerializer + return super().retrieve(request, *args, **kwargs,) + + def list(self, request, *args, **kwargs): + self.serializer_class = InvestmentNestedSerializer + return super().list(request, *args, **kwargs) + + @action(detail=True, methods=['get']) + def withdraw(self, request, pk=None): + def investment_age(creation_date): + today = date.today() + diff_days = today - creation_date + years = diff_days.days//365 + return years + + investment = get_object_or_404(Investments, pk=pk) + income = investment.income + balance = investment.balance + years = investment_age(investment.creation_date) + if years < 1: + taxation = (income*Decimal(0.225)) + elif years >= 1 and years <= 2: + taxation = (income*Decimal(0.185)) + elif years > 2: + taxation = (income*Decimal(0.15)) + amount_withdrawn = balance-taxation + if not investment.withdrawal_date: + investment.withdrawal_date = date.today() + investment.balance = investment.income + investment.save() + return Response({'valor do saque: ':amount_withdrawn}) + else: + return Response({'valor do saque: ':amount_withdrawn+investment.amount}) + + + \ No newline at end of file diff --git a/investment/apps.py b/investment/apps.py new file mode 100644 index 000000000..04218dbcf --- /dev/null +++ b/investment/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class InvestmentConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'investment' diff --git a/investment/migrations/0001_initial.py b/investment/migrations/0001_initial.py new file mode 100644 index 000000000..21f274af4 --- /dev/null +++ b/investment/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 4.1 on 2022-08-17 04:25 + +import datetime +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Owner', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ], + ), + migrations.CreateModel( + name='Investments', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('creation_date', models.DateField(validators=[django.core.validators.MaxValueValidator(datetime.date.today)], verbose_name='created in')), + ('amount', models.DecimalField(decimal_places=10, max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='amount')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to='investment.owner', verbose_name='owner')), + ], + ), + ] diff --git a/investment/migrations/0002_investments_balance_investments_income_and_more.py b/investment/migrations/0002_investments_balance_investments_income_and_more.py new file mode 100644 index 000000000..e7f3ecd4e --- /dev/null +++ b/investment/migrations/0002_investments_balance_investments_income_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.1 on 2022-08-19 01:19 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('investment', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='investments', + name='balance', + field=models.DecimalField(blank=True, decimal_places=10, max_digits=19, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='balance'), + ), + migrations.AddField( + model_name='investments', + name='income', + field=models.DecimalField(blank=True, decimal_places=10, max_digits=19, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='income'), + ), + migrations.AddField( + model_name='investments', + name='update_date', + field=models.DateField(blank=True, null=True, verbose_name='updated'), + ), + ] diff --git a/investment/migrations/0003_investments_withdrawal_date.py b/investment/migrations/0003_investments_withdrawal_date.py new file mode 100644 index 000000000..e43d096fb --- /dev/null +++ b/investment/migrations/0003_investments_withdrawal_date.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1 on 2022-08-19 14:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('investment', '0002_investments_balance_investments_income_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='investments', + name='withdrawal_date', + field=models.DateField(blank=True, null=True, verbose_name='withdrawal date'), + ), + ] diff --git a/investment/migrations/__init__.py b/investment/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/investment/models.py b/investment/models.py new file mode 100644 index 000000000..20bc33928 --- /dev/null +++ b/investment/models.py @@ -0,0 +1,60 @@ +from django.db import models +from decimal import Decimal +from django.utils.translation import gettext_lazy as _ +from datetime import date, timedelta +from django.core.validators import MinValueValidator, MaxValueValidator +import datetime + +class Owner(models.Model): + name = models.CharField( + max_length=255, verbose_name=_("name") + ) + +class Investments(models.Model): + owner = models.ForeignKey( + 'Owner', on_delete=models.CASCADE, verbose_name=_("owner"), related_name="owner" + ) + creation_date = models.DateField( + verbose_name=_("created in"), validators=[MaxValueValidator(datetime.date.today)] + ) + update_date = models.DateField( + verbose_name=_("updated"), null=True, blank=True + ) + amount = models.DecimalField( + max_digits=19, decimal_places=10, verbose_name=_("amount"), validators=[MinValueValidator(0)] + ) + income = models.DecimalField( + max_digits=19, decimal_places=10, null=True, blank=True, verbose_name=_("income"), validators=[MinValueValidator(0)] + ) + balance = models.DecimalField( + max_digits=19, decimal_places=10, null=True, blank=True, verbose_name=_("balance"), validators=[MinValueValidator(0)] + ) + withdrawal_date = models.DateField( + verbose_name=_("withdrawal date"), null=True, blank=True + ) + + @staticmethod + def calc_investment_date(update_date): + today = date.today() + days = (today - update_date).days + months_in_days = days - (days % 30) + new_update_date = update_date + datetime.timedelta(days=months_in_days) + months = days // 30 + return months, new_update_date + + @property + def expected_balance(self): + if not self.withdrawal_date: + calc = self.calc_investment_date(self.update_date) + gain = (self.balance*(1 + Decimal(0.0052))**calc[0]) - self.balance + income = self.income + gain if self.income else gain + balance = self.amount + income + id = self.id + Investments.objects.filter(pk=id).update(income=income) + Investments.objects.filter(pk=id).update(balance=balance) + Investments.objects.filter(pk=id).update(update_date=calc[1]) + value = balance*(1+Decimal(0.0052))**1 + else: + value = {} + + return value \ No newline at end of file diff --git a/investment/tests.py b/investment/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/investment/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/investment/views.py b/investment/views.py new file mode 100644 index 000000000..91ea44a21 --- /dev/null +++ b/investment/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/investment_management/__init__.py b/investment_management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/investment_management/asgi.py b/investment_management/asgi.py new file mode 100644 index 000000000..5578b1cf8 --- /dev/null +++ b/investment_management/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for investment_management 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/4.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'investment_management.settings') + +application = get_asgi_application() diff --git a/investment_management/settings.py b/investment_management/settings.py new file mode 100644 index 000000000..dba374ed4 --- /dev/null +++ b/investment_management/settings.py @@ -0,0 +1,135 @@ +""" +Django settings for investment_management project. + +Generated by 'django-admin startproject' using Django 4.1. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.1/ref/settings/ +""" + +from pathlib import Path + +# 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/4.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-et%d+zbyz6e)dg$zni3z=zjksbmqz4vv2p4#0tw_y%4#195+tw' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + # Django + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # Djano Rest + 'rest_framework', + 'django_filters', + 'drf_yasg', + # Locals + 'investment', +] + +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 = 'investment_management.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 = 'investment_management.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.1/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/4.1/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/4.1/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'PAGE_SIZE': 10 +} \ No newline at end of file diff --git a/investment_management/urls.py b/investment_management/urls.py new file mode 100644 index 000000000..377700f9a --- /dev/null +++ b/investment_management/urls.py @@ -0,0 +1,54 @@ +"""investment_management URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/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')) +""" +from django.contrib import admin +from django.urls import path, include, re_path +from drf_yasg.views import get_schema_view +from drf_yasg import openapi +from rest_framework import permissions + +from rest_framework import routers +from investment.api.urls import router as investment_router + +router = routers.SimpleRouter() +router.registry.extend(investment_router.registry) + +schema_view = get_schema_view( + openapi.Info( + title="Investment Management API", + default_version="v1", + description="API documentation of App", + ), + public=True, + permission_classes=[permissions.AllowAny], +) + + + + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include(router.urls)), + + re_path(r'^api(?P\.json|\.yaml)$', + schema_view.without_ui(cache_timeout=0), + name='schema-json'), + + re_path(r'^api/$', + schema_view.with_ui('swagger', cache_timeout=0), + name='schema-swagger-ui'), + + re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), +] diff --git a/investment_management/wsgi.py b/investment_management/wsgi.py new file mode 100644 index 000000000..cda73ea55 --- /dev/null +++ b/investment_management/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for investment_management 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/4.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'investment_management.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100755 index 000000000..d691d014d --- /dev/null +++ b/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', 'investment_management.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/requirements.txt b/requirements.txt new file mode 100644 index 000000000..5bce87e65 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,25 @@ +asgiref==3.5.2 +certifi==2022.6.15 +charset-normalizer==2.1.0 +coreapi==2.3.3 +coreschema==0.0.4 +Django==4.1 +django-filter==22.1 +djangorestframework==3.13.1 +drf-writable-nested==0.7.0 +drf-yasg==1.21.3 +idna==3.3 +inflection==0.5.1 +itypes==1.2.0 +Jinja2==3.1.2 +MarkupSafe==2.1.1 +packaging==21.3 +psycopg2==2.9.3 +pyparsing==3.0.9 +pytz==2022.2.1 +requests==2.28.1 +ruamel.yaml==0.17.21 +ruamel.yaml.clib==0.2.6 +sqlparse==0.4.2 +uritemplate==4.1.1 +urllib3==1.26.11