Skip to content

Backend Test submission - Python/Django role #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
204ef91
feat: init django server
jppradoleal Aug 15, 2022
cc68975
feat: configures db, app and debugpy through Docker
jppradoleal Aug 15, 2022
af9f609
feat: creates investment model
jppradoleal Aug 15, 2022
0b789a8
feat: configures pytest
jppradoleal Aug 15, 2022
37e02a8
feat: configures drf; fix: returns with debugpy
jppradoleal Aug 15, 2022
6536784
feat: adds user CRUD
jppradoleal Aug 15, 2022
5a8f3ff
feat: configures swagger through drf-yasg
jppradoleal Aug 15, 2022
bc2bda0
test: fixes pytest config
jppradoleal Aug 16, 2022
8a5261f
test: tests for user
jppradoleal Aug 16, 2022
dcdbb0d
feat: moves investment to its on app
jppradoleal Aug 16, 2022
dec595e
feat: basic http verbs for investments
jppradoleal Aug 16, 2022
882b920
feat: improves user viewset
jppradoleal Aug 16, 2022
a21a4f9
feat: list gains on investments list
jppradoleal Aug 16, 2022
aab907a
feat: basic withdrawal action
jppradoleal Aug 16, 2022
e378223
refact: moves common fixtures to conftest
jppradoleal Aug 16, 2022
b1fa117
style: applies black formatter
jppradoleal Aug 16, 2022
328bcf4
feat: fixes date validation for withdrawn action
jppradoleal Aug 17, 2022
7532769
fix: fix compound interest formula
jppradoleal Aug 17, 2022
f7f82ff
ci: initial config
jppradoleal Aug 17, 2022
f2eb748
docs: fixes typo and adds pytest-xdist entry
jppradoleal Aug 17, 2022
d03ce55
feat: adds db_driver env and removes month division on interest_svc
jppradoleal Aug 17, 2022
226c9ce
feat: configures celery
jppradoleal Aug 17, 2022
2fc72a0
fix: fixes querysets that filters by user
jppradoleal Aug 17, 2022
8fd8c04
test: mocks celery task
jppradoleal Aug 17, 2022
4389cf9
feat: changes amount float field to decimal field as it's more suitab…
jppradoleal Aug 17, 2022
786d9a0
docs: updates README.MD
jppradoleal Aug 17, 2022
ae2684c
docs: updates compound gain formula
jppradoleal Aug 17, 2022
6cc988d
test: updates investment tests
jppradoleal Aug 17, 2022
6836653
Merge branch 'development' of github.com:jppradoleal/backend-test-cod…
jppradoleal Aug 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SECRET_KEY=cj&61by%3&!7zlb04f4w9c8&@l@=)6h3@*@4n)5xlz3ikdz3k0
DB_DRIVER=django.db.backends.postgresql
DB_NAME=coderockr
DB_USER=coderockr
DB_PASSWORD=coderockr
DB_HOST=db
DB_PORT=5432

# Celery

CELERY_BROKER_URL=amqp://coderockr:coderockr@rabbitmq:5672/coderockr
CELERY_RESULT_BACKEND=redis://redis:6379

# MAIL

MAIL_HOST=mail
MAIL_PORT=8025
29 changes: 29 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Build

on: [push]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up python 🐍
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Poetry ✒
uses: snok/install-poetry@v1
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v2
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies 🚛
if: steps.cache-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install -n
- name: Test 👨‍🔬
run: poetry run pytest
- name: Linting 👿
run: poetry run black --check .
177 changes: 177 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Created by https://www.toptal.com/developers/gitignore/api/django
# Edit at https://www.toptal.com/developers/gitignore?templates=django

### Django ###
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
db.sqlite3-journal
media

# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
# in your Git repository. Update and uncomment the following line accordingly.
# <django-project-name>/staticfiles/

### Django.Python Stack ###
# Byte-compiled / optimized / DLL files
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo

# Django stuff:

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# End of https://www.toptal.com/developers/gitignore/api/django

# Docker
/data
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.10

RUN apt-get update \
&& apt-get install --no-install-recommends -y \
curl wait-for-it

WORKDIR /app

COPY poetry.lock pyproject.toml /app/
RUN pip3 install poetry
RUN poetry config virtualenvs.create false
RUN poetry config --list
RUN poetry install

COPY . .
115 changes: 46 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,65 @@
# Back End Test Project <img src="https://coderockr.com/assets/images/coderockr.svg" align="right" height="50px" />

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.
## Build instructions.

## Scope
1. Clone this repository locally.
2. CD into the folder.
3. Run `docker-compose build`.
4. Run `docker-compose up -d`.
5. Access http://localhost:8000/swagger to view the OpenAPI Specification.
6. Create a user in the users_create route.
7. Create and copy the token in the login_create route.
8. Click in the "Authorize 🔒" button.
9. Enter "Token {token}" then press the "Authorize" button.
10. Now you're ready to interact with the API.

In this challenge you should build an API for an application that stores and manages investments, it should have the following features:
### Extra instructions.

* Run `docker-compose exec app pytest` to run the integration tests.
* Run `docker-compose exec app black .` to run the black linter.

## Dependencies and their "why's".

1. **Django** - Python "batteries-included" web-framework, was one of the requirements for the role.
2. **psycopg2** - Python adapter for PostgreSQL (The best OS database option).
3. **django-rest-framework** - Django Framework that makes the development of Rest API easier, well structured and standardized.
4. **Markdown** - It gives DRF a nice interface, a great alternative to the OpenAPI specs.
5. **drf-yasg** - Is the responsible for the Swagger route, it automatically generates the specs based on the project routes.
6. **celery** - Is a distributed task queue made to deal with lots of messages, here it is used to send emails without affecting the route response time.
7. **redis** - Python adapter for Redis, needed by celery, who's using redis as a result backend.

### Development dependencies.

1. **pytest** - Python testing lib.
2. **pytest-django** - Pytest adapter is a plugin for Django.
3. **model-bakery** - Its a utility function to mock database models.
4. **debugpy** - Service that allows debugging python remotely.
5. **black** - Python linter, to enforce coding style, it has a "zero configuration" police, I have chosen it just for being easier and faster to setup, flake8 is better.
6. **pytest-xdist** - Pytest plugin for multithreading. The `-n auto` option makes it use the available amount.

## Requirements.

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.
1. [x] The creation date of an investment can be today or a date in the past.
2. [x] 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
1. [x] Expected balance should be the sum of the invested amount and the [gains][].
2. [x] 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,
1. [x] 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.
2. [x] Withdrawals can happen in the past or today, but can't happen before the investment creation or the future.
3. [x] [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.
1. [x] This list should have pagination.

### 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.
Formula used: Final amount = Initial amount * (1 + interest) ^ months

### 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:
The tax percentage changes according to the age of the investment and its applied only to the gains:
* 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
Empty file added coderockr/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions coderockr/asgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
ASGI config for coderockr 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', 'coderockr.settings')

application = get_asgi_application()
Loading