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

Add product search ednpoint and database design link #8

Merged
merged 3 commits into from
Jul 2, 2024
Merged
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
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.

Loading