Skip to content

Commit

Permalink
Merge pull request #8 from Horlawhumy-dev/add-product-search-ednpoint
Browse files Browse the repository at this point in the history
Add product search ednpoint and database design link
  • Loading branch information
Horlawhumy-dev authored Jul 2, 2024
2 parents c567317 + 9c686dd commit f2e55d6
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 38 deletions.
37 changes: 9 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,13 @@ A simple Django backend for a inventory mananagement that manages users, product
- Endpoints for managing users authentication and authorization with RBAC
- Endpoints for products where only admin can make full CRUD operations
- Basic error handling
- Normal can create orders with list of products
- Normal users can create orders with list of products
- Reporting endpoints for sales and products stock management

## Project Structure

drugstoc_inventory/
│ ├── init.py
│ ├── main.py
│ ├── crud/
│ │ ├── init.py
│ │ ├── author.py
│ │ └── post.py
│ ├── models/
│ │ ├── init.py
│ │ ├── author.py
│ │ └── post.py
│ ├── schemas/
│ │ ├── init.py
│ │ ├── author.py
│ │ └── post.py
│ ├── db/
│ │ ├── init.py
│ │ └── database.py
│ └── api/
│ ├── init.py
│ ├── author.py
│ └── post.py
└── README.md
└── run_tests.sh
└── requirements.txt

## SQL Database Design

[SQLDesign](https://drawsql.app/teams/peaknews/diagrams/drugstoc-inventory)

## Getting Started

Expand Down Expand Up @@ -79,6 +55,11 @@ drugstoc_inventory/
```bash
make runserver
```
6. **Create Super User**

```bash
make createsuperuser
```

## Internal API Endpoints Documentation

Expand Down
8 changes: 8 additions & 0 deletions api_doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ Inventory Products Endpoints:
- Request Body: nil
- Response: nil

Note: This search functionality works when postgres database is used
5. GET /api/inventory/products/search?q=
- Description: Search for products by the specified field
- Request Body: nil
- Response: list of products
- Search From: title, description


Inventory Orders Endpoints:

Expand All @@ -66,6 +73,7 @@ Inventory Orders Endpoints:
- Description: List orders
- Request Body: nil
- Response: list of orders
- Filters: status, date_from, date_to

3. PUT /api/inventory/orders/:id/status/
- Description: Update order status by an admin user
Expand Down
1 change: 1 addition & 0 deletions drugstoc_inventory/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
'rest_framework',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
'django_filters',
#internal apps
"users",
"inventory"
Expand Down
3 changes: 0 additions & 3 deletions inventory/tests.py

This file was deleted.

6 changes: 4 additions & 2 deletions inventory/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.urls import path
from .views import (InventoryProductList, InventoryProductCreate,
InventoryProductDetail,OrderListCreate, OrderDetail, OrderStatusUpdate,
LowStockReportView, SalesReportView)
LowStockReportView, SalesReportView, ProductSearchView)

app_name = 'inventory'

Expand All @@ -14,5 +14,7 @@
path('orders/<str:pk>/', OrderDetail.as_view(), name='order_detail'),
path('orders/<str:pk>/status/', OrderStatusUpdate.as_view(), name='order_status_update'),
path('report/stock/', LowStockReportView.as_view(), name='low-stock-report'),
path('report/sales/<str:period>/', SalesReportView.as_view(), name='sales-report')
path('report/sales/<str:period>/', SalesReportView.as_view(), name='sales-report'),
#Search will only functional with postgres database connection
path('products/search', ProductSearchView.as_view(), name='products-search')
]
57 changes: 55 additions & 2 deletions inventory/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# views.py
import logging
from django.contrib.postgres.search import SearchQuery, SearchRank
from django_filters import rest_framework as filters

from rest_framework import status, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from django.shortcuts import get_object_or_404
from django.http import Http404
from rest_framework.pagination import PageNumberPagination
from django.db.models import Sum, F
from datetime import datetime, timedelta
from .models import Product, Order, OrderItem
Expand All @@ -23,8 +27,13 @@ class InventoryProductList(APIView):
def get(self, request):
logger.info(f"User {request.user.id} requested product list")
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)

# Apply pagination
paginator = PageNumberPagination()
paginated_products = paginator.paginate_queryset(products, request)

serializer = ProductSerializer(paginated_products, many=True)
return paginator.get_paginated_response(serializer.data)

class InventoryProductCreate(APIView):
permission_classes = [permissions.IsAuthenticated, IsAdminOrReadOnly]
Expand Down Expand Up @@ -72,13 +81,30 @@ def delete(self, request, pk):
return Response(status=status.HTTP_204_NO_CONTENT)


class OrderFilter(filters.FilterSet):
status = filters.CharFilter(field_name='status', lookup_expr='iexact')
date_from = filters.DateFilter(field_name='created_at', lookup_expr='gte')
date_to = filters.DateFilter(field_name='created_at', lookup_expr='lte')

class Meta:
model = Order
fields = ['status', 'created_at']



class OrderListCreate(APIView):
permission_classes = [permissions.IsAuthenticated]
authentication_classes = [CustomJWTAuthentication]

def get(self, request):
logger.info(f"User {request.user.id} requested their order list")
orders = Order.objects.filter(owner=request.user)

filterset = OrderFilter(request.GET, queryset=orders)
if not filterset.is_valid():
return Response(filterset.errors, status=status.HTTP_400_BAD_REQUEST)

orders = filterset.qs
serializer = OrderSerializer(orders, many=True)
return Response(serializer.data)

Expand Down Expand Up @@ -215,3 +241,30 @@ def get(self, request, period='day'):

serializer = SalesReportSerializer(sales_data, many=True)
return Response(serializer.data)



class ProductSearchView(APIView):
permission_classes = [permissions.IsAuthenticated, permissions.AllowAny]
authentication_classes = [CustomJWTAuthentication]

"""
This will only functional with postgres database connection
"""

def get(self, request):
query = request.query_params.get('q', '')
if not query:
logger.warning("Search query not provided.")
return Response({"error": "Please provide a search query!"}, status=status.HTTP_400_BAD_REQUEST)

search_query = SearchQuery(query)
search_rank = SearchRank('search_vector', search_query)

results = Product.objects.filter(search_vector=search_query)\
.annotate(rank=search_rank)\
.order_by('-rank', '-created_at')

serializer = ProductSerializer(results, many=True)
logger.info(f"Search results returned for query: {query}.")
return Response(serializer.data)
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Pillow==10.3.0
pytest-django==4.8.0
django-redis==5.4.0

django_filter==24.2

whitenoise # https://github.com/evansd/whitenoise
redis # https://github.com/redis/redis-py
hiredis # https://github.com/redis/hiredis-py
Expand Down
3 changes: 0 additions & 3 deletions users/tests.py

This file was deleted.

0 comments on commit f2e55d6

Please sign in to comment.