Skip to content
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/


app/app/static/
139 changes: 78 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,108 @@
# Ecommerce Project

# Welcome!
## Features

Hi! this your determinant test for the position of Django backend developer at **PayBox360**. If you have any issues, you can read this docs or also contact Lolu for further clarification.
### 1. User Management

- **User Registration and Login**: Users can register an account and log in using JWT authentication, ensuring secure access to the platform.

## Overview
### 2. Product Management

For this exercise you will be cover some basic concepts of web development and production ready deployment and you will hence be tested in the following basic concepts.
- **CRUD Operations for Products**: Allows for creating, reading, updating, and deleting products, providing complete control over the product catalog.
- **Product Categorization**: Enables organizing products into categories for better navigation and searchability.
- **Image Upload and Management**: Handles product images, including uploading, updating, and deleting images.

- Django and Django query-sets
- PostgreSQL Setup and connection to Django
- Cloud deployment
- PEP guidelines, conformity and quality of code
- General understanding of the python programming language.

## Test Rundown
### 3. Category Management

You will be required to fork this repository into your personal account and then carry out few operations of extending functionality of the application and then make a pull request with your branch name to the main branch as you progress.
- **Create Category**: Allows for creating new product categories, enabling better organization of the product catalog.
- **Read Category**: Provides details of a specific category, including its name and associated products.
- **Update Category**: Allows for updating existing category information, such as changing the category name or description.
- **Delete Category**: Allows for deleting a category, removing it from the product catalog.
- **List Categories**: Retrieves a list of all categories, enabling easy navigation and management of product groupings.

## Test Guide

After completing stage the process in in the rundown, please create branch for your self, please make sure to name the the branch with the following convention **\<yourname>/update**, and also all commits to your branch should carry a message in the following format **\<ACTIVITY>[Activity details]**.
### 4. Order Management

- A sample branch name would be **paul/update**, and.,
- A sample commit message would be **FIX[ADDED CORS CONTROL]**
- **Order Placement and Processing**: Users can place new orders and process existing ones, ensuring smooth order transactions.
- **Order History and Tracking**: Users can view and track the history of their orders..

## Task Description
### 5. Search and Filtering

You are required to extend a skeleton application and build it into an inventory management system to such that it can provide the abilities below:
- **Full-text Search for Products**: Provides comprehensive search functionality across the product catalog.
- **Pagination for Product Listings**: Manages pagination to ensure efficient loading and display of product listings.

## Setup and Installation

**Project: Simple E-commerce API**
### Prerequisites

**Requirements:**
1. **User Management:**
- Implement user registration and login with JWT authentication.

2. **Product Management:**
- Create models for Product and Category.
- Implement CRUD operations for products (create, read, update, delete).
- Python 3.8+
- PostgreSQL
- Docker (optional, for containerization)

3. **Order Management:**
- Create an Order model.
- Allow users to place orders with multiple products.
- Implement a basic order history endpoint for users.
### Installation Steps

**Detailed Instructions:**
1. Clone the repository:
```bash
git clone https://github.com/Nathan-Yinka/Ecommerce-Backend-Test.git
cd Ecommerce-Backend-Test
```

1. **Setup:**
- Create a new Django project.
- Configure the project with Django REST Framework.
- Make sure to use PostgreSQL
2. Create a virtual environment and install dependencies:
#### macOS/Linux
```bash
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```

2. **User Authentication:**
- Use Django's built-in User model.
- Implement registration and login endpoints using JWT for authentication.
#### Windows
```bash
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
```

3. **Product and Category Models:**
- Create models with appropriate fields (e.g., name, description, price for Product; name for Category).
- Establish relationships (e.g., a product belongs to a category).
- Implement endpoints for managing products (list, detail, create, update, delete).
3. ``` bash
cd app

4. **Order Model:**
- Create an Order model with fields like user (ForeignKey), product (ManyToManyField), quantity, and date.
- Implement an endpoint for placing orders.
- Create an endpoint to retrieve the order history for the authenticated user.
```

5. **Testing:**
- Write unit tests for each endpoint.
4. Run database migrations:
```bash
python manage.py migrate --settings=app.settings.local
```

**Evaluation Criteria:**
- Correctness: The implementation should meet the requirements.
- Code Quality: Clean, readable, and maintainable code.
- Use of Django Best Practices: Proper use of Django features and conventions.
- Testing: Quality and coverage of unit tests.
5. Start the development server:
```bash
python manage.py runserver --settings=app.settings.local
```

**Bonus:**
- Implement search functionality for products.
- Add pagination to product listing.
6. Access the application at `http://localhost:8000`

### Using Docker

## Resources for task
1. Clone the repository:
```bash
git clone https://github.com/Nathan-Yinka/Ecommerce-Backend-Test.git
cd Ecommerce-Backend-Test
```

**Finally**
You will be provided with a virtual machine IP address hosted on Digital Ocean please host your project appropriately using NGINX, GUNICORN and POSTGRESQL (as database). A password for the droplet will be provided.
2. Ensure Docker is installed and running on your machine.

- Please add your postman link to the above created endpoints for review.
- Also note that you can ignore the Docker and CI/CD instantiations on the application.
3. Build and start the containers using Docker Compose:
```bash
docker-compose up --build
```

### Good luck, as we look forward to working with you at Liberty Assured in building amazing projects and relationships.
4. Access the application at `http://localhost`

## API Documentation

Detailed API documentation can be found [here](https://documenter.getpostman.com/view/28578777/2sA3dvkChF). It includes endpoints for user authentication, product management, order processing, and more.

## Testing

Unit tests and integration tests are implemented to ensure the application's reliability. Run tests using:
```bash
python manage.py test --settings=app.settings.local
Binary file added app/app/media/products/about-02.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/app/media/products/banner-01.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/app/media/products/banner-04.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added app/app/settings/__init__.py
Empty file.
55 changes: 45 additions & 10 deletions app/app/settings.py → app/app/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
"""

from pathlib import Path
import os
from datetime import timedelta
from dotenv import load_dotenv

load_dotenv()

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand All @@ -23,9 +28,8 @@
SECRET_KEY = 'django-insecure-h-h69cr05lmc*w4vtkf+5qltg8#&#xf8fe(v9j9oxs-*-^#vjd'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []



# Application definition
Expand All @@ -37,7 +41,17 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'core'
# install apps
'core',
"product",
"order",

# 3rd party libraries
'rest_framework',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',


]

MIDDLEWARE = [
Expand Down Expand Up @@ -74,12 +88,6 @@
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}


# Password validation
Expand Down Expand Up @@ -117,10 +125,37 @@
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'static'

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

AUTH_USER_MODEL = 'core.User'
AUTH_USER_MODEL = 'core.User'


# Media files settings
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')




# rest frame work setting
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
}

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
}
11 changes: 11 additions & 0 deletions app/app/settings/local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .base import *

DEBUG = True


DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
25 changes: 25 additions & 0 deletions app/app/settings/prod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os
from .base import *


DEBUG = True

ADMINS = [
('Nathan-Yinka', 'oludarenathaniel@gmail.com'),
]

ALLOWED_HOSTS = ["*"]


DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DATABASE_NAME'),
'USER': os.environ.get('DATABASE_USER'),
'PASSWORD': os.environ.get('DATABASE_PASSWORD'),
'HOST': os.environ.get('DATABASE_HOST'),
'PORT': '5432',
}
}

STATIC_ROOT = os.path.join(BASE_DIR, 'static')
25 changes: 9 additions & 16 deletions app/app/urls.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
"""app URL Configuration.

The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.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'))
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path
from django.urls import path,include

urlpatterns = [
path('admin/', admin.site.urls),
path("auth/", include("core.urls")),
path("api/", include("product.urls")),
path('api/',include("order.urls")),
]

if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
6 changes: 6 additions & 0 deletions app/app/utilis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rest_framework.pagination import PageNumberPagination

class ProductPagePagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 100
31 changes: 28 additions & 3 deletions app/core/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
"""Manage admin page for main app."""
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _

# from django.contrib import admin

# Register your models here.
User = get_user_model()



class CustomUserAdmin(UserAdmin):
model = User
list_display = ('email', 'name', 'is_staff', 'is_active')
list_filter = ('is_staff', 'is_active')
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Personal info'), {'fields': ('name',)}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'),
}),
)
search_fields = ('email', 'name')
ordering = ('email',)

admin.site.register(User, CustomUserAdmin)
3 changes: 2 additions & 1 deletion app/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def create_superuser(self, email, password):
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True

user.save(using=self._db)

return user


Expand Down
Loading