Giter VIP home page Giter VIP logo

lfg_backend's Introduction

Loans For Good - Backend

Summary

Demo

Frontend

https://loansforgoodfrontend-06732fd2e7f8.herokuapp.com/

Django admin

https://loans-for-good-backend-55c8893b4944.herokuapp.com/admin/

How to build the project

To build the project, you have to download this repo and the frontend one. After that, create an app tree like this

App tree

(root)
├── loans_for_good_backend
│   ├── (this repo)
├── loans_for_good_frontend
└── docker-compose.yml

and use this docker-compose.yml, setting the following config-vars as your preference or use the current ones from the example (just to test the app)

env

POSTGRES_DB=
POSTGRES_USER=
POSTGRES_PASSWORD=
DEBUG=
DATABASE_URL=
REACT_APP_API_HOSTNAME=
REDIS_URL=

docker-compose.yml

version: '3.8'

services:
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: mydatabase
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword

  backend:
    build:
      context: ./loans_for_good_backend
    command: ["python", "manage.py", "runserver", "0.0.0.0:8000"]
    volumes:
      - ./loans_for_good_backend:/app
    ports:
      - "8000:8000"
    depends_on:
      - db
    environment:
      DEBUG: 'True'
      DATABASE_URL: postgresql://myuser:mypassword@db:5432/mydatabase
      REDIS_URL: redis://redis:6379/0

  frontend:
    build:
      context: ./loans_for_good_frontend
    ports:
      - "3000:3000"
    depends_on:
      - backend
    volumes:
      - ./loans_for_good_frontend:/app
    environment:
      REACT_APP_API_HOSTNAME: http://localhost:8000
  
  redis:
    image: "redis:latest"
    ports:
      - "6379:6379"
      

  celery:
    build:
      context: ./loans_for_good_backend
    command: celery -A loans_for_good worker --loglevel=info
    volumes:
      - ./loans_for_good_backend:/app
    depends_on:
      - db
      - redis
    environment:
      DATABASE_URL: postgresql://myuser:mypassword@db:5432/mydatabase
      REDIS_URL: redis://redis:6379/0


volumes:
  postgres_data:

Django admin

Create an superuser to access localhost:8000/admin panel to create new Attributes. To do this, execute:

docker ps

# Check the container ID of backend container, in my case above, it's 3a09cf8078cd
CONTAINER ID   IMAGE                  COMMAND                  CREATED        STATUS         PORTS                                                 NAMES
8000889d73da   digital_sys_celery     "celery -A loans_for…"   2 hours ago    Up 6 minutes                                                         digital_sys_celery_1
341acdaf85aa   redis:latest           "docker-entrypoint.s…"   2 hours ago    Up 6 minutes   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp           digital_sys_redis_1
1f99eb2d240c   digital_sys_frontend   "docker-entrypoint.s…"   2 hours ago    Up 6 minutes   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp             digital_sys_frontend_1
3a09cf8078cd   digital_sys_backend    "python manage.py ru…"   2 hours ago    Up 6 minutes   0.0.0.0:8000->8000/tcp, :::8000->8000/tcp             digital_sys_backend_1
ad5ed0dc7973   postgres:13            "docker-entrypoint.s…"   30 hours ago   Up 6 minutes   5432/tcp                                              digital_sys_db_1

# open container bash
docker exec -it <container-id> bash

# create super user
python manage.py createsuperuser

Project explanation

The project simulates a financial company which loans money. To request a loan, you have to send a Proposal. Users can send, check and refresh the status of all proposals in a SPA. Admin users can register new attributes to proposal and change the status of each proposal.

The project uses an entity called Proposal to persist all the proposals send from frontend to backend. The Proposal is a model with following attributes:

name = models.CharField(max_length=100) -> the name of who is requesting the proposal
document = models.CharField(max_length=20) -> a document from who is requesting the proposal
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending_by_system') -> a status for the proposal (the list of available status is set on proposal/model.py
last_updated = models.DateTimeField(auto_now=True) -> the time of last updat on the proposal

Besides that, the Proposal model is plugged with an EAV entity value attribute pattern which makes possible to register new attributes to proposal, like city, address_line, state, current_job or whatever. The new attributes registration occurs on Django Admin and uses django-eav-2 lib under the hood. All new attributes registered in Django Admin will be sent dinamically to frontend. Further explanations of how django-eav2 works will be discuss latter.

Finnaly, we'are using an external API to send the proposal fields to our internal system to be 'approved' or 'denied', using a background job for that, plugged in Celery and Redis

EAV explanation

We choose to use EAV pattern since the company needs to register new attributes dinamically in the Django Admin panel. By the limitations of ORM (Data Integrity, Tracebility and Consistency) we cannot do that changing the schema. To do so, we are using the django-eav2 lib which provides a customized solution for it that makes easy to read, create and filter new attributes to a current model

Create new attributes

Console

from eav.models import Attribute

Attribute.objects.create(name='City', slug='city', datatype=Attribute.TYPE_TEXT)
Attribute.objects.create(name='Job Title', slug='job_title', datatype=Attribute.TYPE_TEXT)

Django Amin

  1. Select the Data Type (Text suggested)
  2. Give a human name to Attribute
  3. Select proposal and Entity Content Type
  4. Save

image

Read Values

# create a proposal
proposal = Proposal.objects.create(name='Jose', document='123', eav__city='RJ', eav__job_title='Engineer')

# to read a custom attributes registered in Attribute model (like city or job_title)
proposal.eav.city
proposal.eav.job_title

# or
proposal.eav_values.all()

Create Values

# as mentioned before
Proposal.objects.create(name='Jose', document='123', eav__city='RJ')

# or
proposal = Proposal.objects.create(name='Jose', document='123')
proposal.eav.city = 'RJ'
proposal.eav.job_title = 'Engineer'
proposal.save()

Filter by Values

Proposal.objects.filter(eav__city='RJ')

Test Coverage

To execute the tests, run:

coverage run manage.py test

To check test coverage:

coverage report

Full test coverage output:

Name                                                Stmts   Miss  Cover
-----------------------------------------------------------------------
loans_for_good/__init__.py                              0      0   100%
loans_for_good/celery.py                                7      0   100%
loans_for_good/settings.py                             30      0   100%
loans_for_good/urls.py                                  3      0   100%
manage.py                                              12      2    83%
proposal/__init__.py                                    0      0   100%
proposal/admin.py                                      17      0   100%
proposal/apps.py                                        4      0   100%
proposal/migrations/0001_initial.py                     5      0   100%
proposal/migrations/0002_proposal_last_updated.py       4      0   100%
proposal/migrations/0003_alter_proposal_status.py       4      0   100%
proposal/migrations/__init__.py                         0      0   100%
proposal/models.py                                      9      0   100%
proposal/serializers.py                                11      0   100%
proposal/services.py                                    5      0   100%
proposal/tasks.py                                      10      0   100%
proposal/tests/__init__.py                              0      0   100%
proposal/tests/test_services.py                        30      1    97%
proposal/tests/test_tasks.py                           32      1    97%
proposal/tests/test_views.py                           36      0   100%
proposal/urls.py                                        4      0   100%
proposal/views.py                                      31      0   100%
-----------------------------------------------------------------------
TOTAL                                                 254      4    98%

Code Style

In this code base we're using the PEP8 style. We configured a pre-commit hooker to prevent us to commit without PEP8 format. To run autofix, before commits execute:

pre-commit run --all-files

References

lfg_backend's People

Contributors

jsobralgitpush avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.