Assignment / README.md
redhairedshanks1's picture
Update README.md
730e337 verified
metadata
license: apache-2.0
title: Assignment
sdk: docker
emoji: 🐨
colorFrom: purple
colorTo: indigo

Place Review REST API

A production-ready REST API for a mobile app that allows users to review places (shops, doctors, restaurants, etc.), built with Django and Django REST Framework.

Features

  • User Registration: Phone-based user registration (one user per phone number)
  • Authentication: Token-based authentication for all endpoints
  • Reviews: Users can add reviews with 1-5 ratings for any place
  • Smart Search: Search places by name (exact match prioritized) and/or minimum rating
  • Place Details: View place information with all reviews, current user's review shown first
  • Automatic Place Creation: Places are automatically created when adding a review if they don't exist

Technology Stack

  • Framework: Django 5.0 with Django REST Framework
  • Database: SQLite (can be easily switched to PostgreSQL or MySQL)
  • Authentication: Token-based authentication
  • Data Generation: Faker for realistic sample data

Installation

Prerequisites

  • Python 3.8 or higher
  • pip (Python package manager)

Setup Steps

  1. Clone or extract the project

    cd "d:\SDE intern Assignment"
    
  2. Create a virtual environment (recommended)

    python -m venv venv
    
    # On Windows
    venv\Scripts\activate
    
    # On macOS/Linux
    source venv/bin/activate
    
  3. Install dependencies

    pip install -r requirements.txt
    
  4. Run database migrations

    python manage.py makemigrations
    python manage.py migrate
    
  5. Populate the database with sample data

    python manage.py populate_data
    

    This will create:

    • 50 users with realistic names and phone numbers
    • 100 places (restaurants, cafes, shops, doctors, gyms, salons)
    • 300+ reviews with varied ratings

    Note: The command will display sample user credentials that you can use for testing.

  6. Create a superuser (optional, for admin access)

    python manage.py createsuperuser
    
  7. Run the development server

    python manage.py runserver
    

    The API will be available at http://127.0.0.1:8000/

API Endpoints

All endpoints (except registration and login) require authentication via token header:

Authorization: Token YOUR_AUTH_TOKEN

1. User Registration

Endpoint: POST /api/register/

Description: Register a new user with name and phone number.

Request Body:

{
    "name": "John Doe",
    "phone": "1234567890"
}

Response (201 Created):

{
    "user": {
        "id": 1,
        "name": "John Doe",
        "phone": "1234567890"
    },
    "token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}

Error (400 Bad Request):

{
    "phone": ["user with this phone already exists."]
}

2. User Login

Endpoint: POST /api/login/

Description: Login with phone number to receive authentication token.

Request Body:

{
    "phone": "1234567890"
}

Response (200 OK):

{
    "user": {
        "id": 1,
        "name": "John Doe",
        "phone": "1234567890"
    },
    "token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}

Error (404 Not Found):

{
    "error": "User with this phone number does not exist"
}

3. Add Review

Endpoint: POST /api/reviews/add/

Description: Add a review for a place. If the place doesn't exist, it will be created automatically.

Headers:

Authorization: Token YOUR_TOKEN

Request Body:

{
    "place_name": "Joe's Pizza",
    "place_address": "123 Main St, New York, NY",
    "rating": 5,
    "review_text": "Best pizza in town! The crust is perfect."
}

Response (201 Created):

{
    "message": "Review added successfully",
    "review": {
        "id": 1,
        "user_name": "John Doe",
        "rating": 5,
        "text": "Best pizza in town! The crust is perfect.",
        "created_at": "2024-01-01T12:00:00Z"
    }
}

Notes:

  • If a place with the exact name and address exists, the review will be added to it
  • If the place doesn't exist, it will be created automatically
  • If the user has already reviewed this place, the review will be updated

4. Search Places

Endpoint: GET /api/places/search/

Description: Search for places by name and/or minimum rating.

Headers:

Authorization: Token YOUR_TOKEN

Query Parameters:

  • name (optional): Search by place name (case-insensitive, exact matches shown first)
  • min_rating (optional): Filter by minimum average rating

Examples:

Search by name only:

GET /api/places/search/?name=pizza

Search by minimum rating only:

GET /api/places/search/?min_rating=4

Search by both:

GET /api/places/search/?name=pizza&min_rating=4

Response (200 OK):

[
    {
        "id": 1,
        "name": "Joe's Pizza",
        "address": "123 Main St, New York, NY",
        "average_rating": 4.5
    },
    {
        "id": 2,
        "name": "Pizza Paradise",
        "address": "456 Oak Ave, Brooklyn, NY",
        "average_rating": 4.2
    }
]

Search Logic:

  • Exact matches are shown first, followed by partial matches
  • Case-insensitive search
  • Results are filtered by minimum rating if specified

5. Place Details

Endpoint: GET /api/places/{place_id}/

Description: Get detailed information about a place, including all reviews. The current user's review appears first, followed by all other reviews sorted by newest first.

Headers:

Authorization: Token YOUR_TOKEN

Example:

GET /api/places/1/

Response (200 OK):

{
    "id": 1,
    "name": "Joe's Pizza",
    "address": "123 Main St, New York, NY",
    "average_rating": 4.5,
    "reviews": [
        {
            "id": 5,
            "user_name": "John Doe",
            "rating": 5,
            "text": "Best pizza in town!",
            "created_at": "2024-01-01T12:00:00Z"
        },
        {
            "id": 3,
            "user_name": "Jane Smith",
            "rating": 4,
            "text": "Great pizza, slightly pricey",
            "created_at": "2024-01-02T14:00:00Z"
        },
        {
            "id": 1,
            "user_name": "Bob Johnson",
            "rating": 5,
            "text": "Amazing crust!",
            "created_at": "2023-12-30T10:00:00Z"
        }
    ]
}

Error (404 Not Found):

{
    "error": "Place not found"
}

Testing the API

Using curl

  1. Register a user:
curl -X POST http://127.0.0.1:8000/api/register/ \
  -H "Content-Type: application/json" \
  -d "{\"name\": \"Test User\", \"phone\": \"9999999999\"}"
  1. Login (use a phone from the populate_data output):
curl -X POST http://127.0.0.1:8000/api/login/ \
  -H "Content-Type: application/json" \
  -d "{\"phone\": \"1234567890\"}"
  1. Add a review (replace YOUR_TOKEN):
curl -X POST http://127.0.0.1:8000/api/reviews/add/ \
  -H "Content-Type: application/json" \
  -H "Authorization: Token YOUR_TOKEN" \
  -d "{\"place_name\": \"New Place\", \"place_address\": \"123 Test St\", \"rating\": 5, \"review_text\": \"Excellent!\"}"
  1. Search places:
curl -X GET "http://127.0.0.1:8000/api/places/search/?name=pizza&min_rating=4" \
  -H "Authorization: Token YOUR_TOKEN"
  1. Get place details:
curl -X GET http://127.0.0.1:8000/api/places/1/ \
  -H "Authorization: Token YOUR_TOKEN"

Using Postman or Similar Tools

  1. Import the API endpoints
  2. Set up an environment variable for the auth token
  3. Test each endpoint according to the examples above

Database Schema

User Model

  • id: Primary key (auto-generated)
  • phone: Unique phone number (used for authentication)
  • name: User's name
  • date_joined: Timestamp of registration

Place Model

  • id: Primary key (auto-generated)
  • name: Place name
  • address: Place address
  • created_at: Timestamp
  • Unique constraint: (name, address)

Review Model

  • id: Primary key (auto-generated)
  • user_id: Foreign key to User
  • place_id: Foreign key to Place
  • rating: Integer (1-5)
  • text: Review text
  • created_at: Timestamp
  • Unique constraint: (user, place) - one review per user per place

Production Deployment Notes

For production deployment, you should:

  1. Change the SECRET_KEY in settings.py to a secure random value
  2. Set DEBUG = False in settings.py
  3. Configure ALLOWED_HOSTS with your domain
  4. Use PostgreSQL or MySQL instead of SQLite:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'your_db_name',
            'USER': 'your_db_user',
            'PASSWORD': 'your_db_password',
            'HOST': 'localhost',
            'PORT': '5432',
        }
    }
    
  5. Set up static files serving
  6. Use a production WSGI server like Gunicorn or uWSGI
  7. Set up HTTPS with SSL certificates

Project Structure

SDE intern Assignment/
β”œβ”€β”€ manage.py                          # Django management script
β”œβ”€β”€ requirements.txt                   # Python dependencies
β”œβ”€β”€ db.sqlite3                         # SQLite database (auto-generated)
β”œβ”€β”€ place_review_api/                  # Main project directory
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ settings.py                    # Django settings
β”‚   β”œβ”€β”€ urls.py                        # Root URL configuration
β”‚   β”œβ”€β”€ asgi.py                        # ASGI configuration
β”‚   └── wsgi.py                        # WSGI configuration
└── reviews/                           # Reviews app
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ admin.py                       # Admin interface configuration
    β”œβ”€β”€ apps.py                        # App configuration
    β”œβ”€β”€ models.py                      # Database models
    β”œβ”€β”€ serializers.py                 # DRF serializers
    β”œβ”€β”€ views.py                       # API views
    β”œβ”€β”€ urls.py                        # App URL routing
    β”œβ”€β”€ management/                    # Management commands
    β”‚   β”œβ”€β”€ __init__.py
    β”‚   └── commands/
    β”‚       β”œβ”€β”€ __init__.py
    β”‚       └── populate_data.py       # Data population script
    └── migrations/                    # Database migrations (auto-generated)
        └── __init__.py

Design Decisions and Assumptions

  1. Phone-based Authentication: Users register and login with phone numbers. No password is required in this basic version, but it can be easily extended.

  2. SQLite Database: Used for portability and ease of submission. The code works with any relational database (PostgreSQL, MySQL, etc.) by simply changing the database configuration.

  3. Atomic Place Creation: When adding a review, if a place with the same name and address doesn't exist, it's created atomically using get_or_create().

  4. Review Updates: If a user reviews the same place twice, their review is updated rather than creating a duplicate.

  5. Search Priority: Exact name matches are shown before partial matches to improve relevance.

  6. Review Sorting: On the place details page, the current user's review always appears first (if they've reviewed it), followed by all other reviews sorted by newest first.

  7. Rating Distribution: The sample data generator creates reviews with ratings skewed toward positive (4-5 stars) to simulate realistic rating distributions.

  8. Token Authentication: Simple and effective for mobile apps. Tokens never expire in this version but can be configured to expire.

Code Quality Highlights

  • Type Safety: Proper use of Django's field validators and constraints
  • Database Integrity: Unique constraints and foreign key relationships
  • Atomic Operations: Uses transactions for data population
  • Error Handling: Comprehensive error responses with appropriate HTTP status codes
  • Code Documentation: Docstrings for all views and complex methods
  • DRY Principle: Reusable serializers and efficient database queries
  • Security: Token authentication required for all sensitive endpoints
  • Performance: Database indexes on commonly queried fields

License

This project is submitted as part of a technical assessment.