Skip to content
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

feat: task #23

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 10 additions & 8 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[run]
branch = True
omit = *migrations*,
*urls*,
*test*,
*admin*,
./manage.py,
./flite/config/*,
./flite/wsgi.py,
*__init__*
omit =
*/migrations/*,
*/tests/*,
*/admin.py,
*urls*,
flite/core/apps.py,
manage.py,
./flite/config/*,
flite/wsgi.py,
*/__init__.py
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,6 @@ venv.bak/
# mypy
.mypy_cache/
.vscode/
.idea/
.idea/
.DS_Store
data/
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.6
FROM python:3.8
ENV PYTHONUNBUFFERED 1

# Allows docker to cache installed dependencies between builds
Expand All @@ -14,4 +14,4 @@ EXPOSE 8000
# Migrates the database, uploads staticfiles, and runs the production server
CMD ./manage.py migrate && \
./manage.py collectstatic --noinput && \
newrelic-admin run-program gunicorn --bind 0.0.0.0:$PORT --access-logfile - flite.wsgi:application
newrelic-admin run-program gunicorn --bind 0.0.0.0:$PORT --access-logfile - flite.wsgi:application
124 changes: 123 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,125 @@
# Flite

#TODO: update readme for local development
## Features

- RESTful API endpoints for seamless integration with frontend applications
- User authentication and authorization using Django's built-in authentication system
- Asynchronous task processing with Celery and RabbitMQ
- Dockerized development environment for easy setup and deployment
- Comprehensive test suite for ensuring code quality and reliability
- Integration with [Mailpit](https://github.com/axllent/mailpit) for capturing outgoing emails during development
- Monitoring and administration of Celery tasks using Flower

## Prerequisites

Before getting started with Flite, ensure that you have the following prerequisites installed on your system:

- Docker: [Install Docker](https://docs.docker.com/get-docker/)
- Docker Compose: [Install Docker Compose](https://docs.docker.com/compose/install/)

## Getting Started

To set up the Flite project on your local machine, follow these steps:

1. Clone the repository:
```
git clone https://github.com/smyja/flite.git
```

2. Navigate to the project directory:
```
cd flite
```

3. Create a `.env` file in the project root and provide the necessary environment variables, example variables are in the `.env.example` file.

4. Build and start the Docker containers:
```
docker-compose up --build
```
This command will build the required Docker images and start the containers defined in the `docker-compose.yml` file.

5. Run database migrations:
Open another terminal and run the following commands
```
docker-compose exec django python manage.py makemigrations
```
and

```
docker-compose exec django python manage.py migrate
```

This command will apply the database migrations and set up the required tables.

### Access the application:
- Django server: http://0.0.0.0:8000
- Mailpit: http://0.0.0.0:8025
- Flower: http://0.0.0.0:5555

## API Documentation

The API documentation for Flite is generated using drf-yasg, a Swagger generation tool for Django REST Framework. To access the API documentation, follow these steps:

1. Start the Django development server:
```
docker-compose up
```

2. Open your web browser and navigate to: `http://0.0.0.0:8000/docs/`

This will display the Redoc UI, where you can explore the available API endpoints, view request and response schemas, To interact with the API navigate to `http://0.0.0.0:8000/api/playground/`
To see all the endpoints, create a superuser account and login to the admin, then refresh the docs to see the rest of the endpoints that are protected.

### Available endpoints
Sure! Here's a list of the available endpoints based on the provided code:

1. Budget Category List:
- URL: `/budget_categories/`
- Methods:
- GET: Retrieve a list of budget categories for the authenticated user.
- POST: Create a new budget category for the authenticated user.

2. Budget Category Detail:
- URL: `/budget_categories/<int:pk>/`
- Methods:
- GET: Retrieve details of a specific budget category.
- PUT: Update a specific budget category.
- DELETE: Delete a specific budget category.

3. Transaction List:
- URL: `/transactions/`
- Methods:
- GET: Retrieve a list of transactions for the authenticated user.
- POST: Create a new transaction for the authenticated user.

4. Transaction Detail:
- URL: `/transactions/<int:pk>/`
- Methods:
- GET: Retrieve details of a specific transaction.
- PUT: Update a specific transaction.
- DELETE: Delete a specific transaction.

Note that these endpoints require authentication using token-based authentication. Users need to provide a valid token in the request headers to access these endpoints. For example
```bash
curl -X POST \
-H "Authorization: Token your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Food",
"description": "Budget for groceries and dining out",
"max_spend": 500.00
}' \
http://localhost:8000/budget_categories/
```

## Running Tests

To run the test suite for the Flite project, use the following command in another terminal/tab:

```
docker-compose exec django python manage.py test
```

This command will execute the test cases defined in the Django application and provide the test results.

47 changes: 45 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@ services:
POSTGRES_USER: postgres
ports:
- "5432:5432"

rabbitmq:
image: "rabbitmq:3-management"
container_name: rabbitmq
environment:
- RABBITMQ_DEFAULT_USER=guest
- RABBITMQ_DEFAULT_PASS=guest
ports:
- "5672:5672"
- "15672:15672"
django:
restart: always
environment:
- DJANGO_SECRET_KEY=local
- DJANGO_SECRET_KEY=Local
image: django
container_name: django
build: ./
Expand All @@ -32,3 +40,38 @@ services:
- "8000:8000"
depends_on:
- postgres
celery-worker:
build: .
command: celery -A flite worker -l info
environment:
- DJANGO_SETTINGS_MODULE=flite.config
- CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672/
volumes:
- ./:/code
depends_on:
- django
- rabbitmq
mailpit:
image: axllent/mailpit
container_name: mailpit
restart: unless-stopped
volumes:
- ./data:/data
ports:
- 8025:8025
- 1025:1025
environment:
MP_MAX_MESSAGES: 5000
MP_DATABASE: /data/mailpit.db
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
flower:
image: mher/flower
command: celery flower
environment:
- CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672//
ports:
- 5555:5555
depends_on:
- rabbitmq
restart: always
6 changes: 4 additions & 2 deletions env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
DJANGO_SECRET_KEY=thisgvbhgjahsjgbasfdafsadgh
DATABASE_URL=postgres://postgres:postgres@postgres:5432/flite
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_PASSWORD=postgres
POSTGRES_DB=flite
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
DJANGO_SECRET_KEY=local
RABBITMQ_DEFAULT_USER=guest
RABBITMQ_DEFAULT_PASS=guest
5 changes: 5 additions & 0 deletions flite/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)
17 changes: 17 additions & 0 deletions flite/celery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import absolute_import
import os

from celery import Celery

# Set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "flite.config.local")
os.environ.setdefault('DJANGO_CONFIGURATION', 'Local')
import configurations
configurations.setup()

app = Celery('flite')

app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django apps.
app.autodiscover_tasks()
9 changes: 5 additions & 4 deletions flite/config/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ class Common(Configuration):
# Third party apps
'rest_framework', # utilities for rest apis
'rest_framework.authtoken', # token authentication
'django_filters', # for filtering rest endpoints

'django_filters',
"drf_yasg",

# Your apps
'flite.users',
'flite.core',
'flite.core.apps.CoreConfig',

)

Expand Down Expand Up @@ -85,7 +86,7 @@ class Common(Configuration):
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

CELERY_BROKER_URL = 'amqp://guest:guest@rabbitmq:5672//'
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# Media files
MEDIA_ROOT = join(os.path.dirname(BASE_DIR), 'media')
Expand Down
4 changes: 2 additions & 2 deletions flite/config/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ class Local(Common):
]

# Mail
EMAIL_HOST = 'localhost'
EMAIL_HOST = 'mailpit'
EMAIL_PORT = 1025
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
4 changes: 3 additions & 1 deletion flite/core/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from django.contrib import admin
from .models import BudgetCategory,Transaction


admin.site.register(BudgetCategory)
admin.site.register(Transaction)
11 changes: 9 additions & 2 deletions flite/core/apps.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
from django.apps import AppConfig

from django.db.models.signals import post_save

class CoreConfig(AppConfig):
name = 'core'
default_auto_field = 'django.db.models.BigAutoField'
name = 'flite.core'

def ready(self):
from .models import Transaction
from .tasks import check_budget_threshold_signal

post_save.connect(check_budget_threshold_signal, sender=Transaction)
38 changes: 38 additions & 0 deletions flite/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 3.2.16 on 2024-05-01 16:35

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='BudgetCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('max_spend', models.DecimalField(decimal_places=2, max_digits=10)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='budget_categories', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, max_digits=10)),
('description', models.TextField()),
('date', models.DateTimeField(auto_now_add=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.budgetcategory')),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to=settings.AUTH_USER_MODEL)),
],
),
]
Loading