Product Tracker
A web application for tracking product prices and shopping events. Built with FastAPI (Python) backend and React (TypeScript) frontend.
Table of Contents
- Features
- Quick Start with Docker
- Architecture
- Data Model
- Development Setup
- API Endpoints
- Usage
- Deployment
- Development
- Contributing
Features
- Product Management: Add, edit, and track product items with prices, categories, and organic status
- Shop Management: Manage different shops with locations
- Brand Management: Track product brands and their availability in different shops
- Shopping Events: Record purchases with multiple products and amounts
- Price Tracking: Monitor price changes over time
- Import/Export: Bulk import and export data via CSV files
- Modern UI: Clean, responsive interface built with React and Tailwind CSS
Quick Start with Docker
The fastest way to get the application running is with Docker Compose:
Prerequisites
- Docker Engine 20.10+
- Docker Compose 2.0+
Deploy in 3 Steps
-
Clone and setup:
git clone <your-repo-url> cd groceries cp docker.env.example .env # Edit .env with your secure passwords -
Start all services:
docker-compose up -d -
Initialize database:
docker-compose exec backend alembic upgrade head
Access Your Application
- Frontend: http://localhost
- Backend API: http://localhost:8000
- API Documentation: http://localhost:8000/docs
For detailed Docker deployment instructions, see DOCKER_DEPLOYMENT.md.
Architecture
Technology Stack
Backend (Python):
- FastAPI - Modern, fast web framework
- SQLAlchemy - SQL toolkit and ORM
- PostgreSQL - Relational database
- Pydantic - Data validation and settings management
- Alembic - Database migrations
Frontend (React):
- React 18 with TypeScript
- React Router - Client-side routing
- Tailwind CSS - Utility-first CSS framework
- PapaParse - CSV parsing for import/export
Deployment:
- Docker & Docker Compose
- Nginx - Web server and reverse proxy
- PostgreSQL - Production database
Component Communication
┌─────────────────┐ HTTP/REST API ┌─────────────────┐ SQL Queries ┌─────────────────┐
│ React │ ←─────────────────→ │ FastAPI │ ←───────────────→ │ PostgreSQL │
│ Frontend │ JSON requests │ Backend │ SQLAlchemy ORM │ Database │
│ (Port 80) │ JSON responses │ (Port 8000) │ │ (Port 5432) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Data Model
Core Entities
Brands (brands table)
id: Integer, Primary key, Auto-incrementname: String, Brand name (indexed, required)created_at: DateTime, Creation timestamp (auto-generated)updated_at: DateTime, Last update timestamp (auto-updated)
Grocery Categories (grocery_categories table)
id: Integer, Primary key, Auto-incrementname: String, Category name (indexed, required)created_at: DateTime, Creation timestamp (auto-generated)updated_at: DateTime, Last update timestamp (auto-updated)
Products (products table)
id: Integer, Primary key, Auto-incrementname: String, Product name (indexed, required)category_id: Integer, Foreign key to grocery_categories (required)brand_id: Integer, Foreign key to brands (optional)organic: Boolean, Organic flag (default: false)weight: Float, Weight/volume (optional)weight_unit: String, Unit of measurement (default: "piece")- Supported units: "g", "kg", "lb", "oz", "ml", "l", "piece"
created_at: DateTime, Creation timestamp (auto-generated)updated_at: DateTime, Last update timestamp (auto-updated)
Shops (shops table)
id: Integer, Primary key, Auto-incrementname: String, Shop name (indexed, required)city: String, Location city (required)address: String, Full address (optional)created_at: DateTime, Creation timestamp (auto-generated)updated_at: DateTime, Last update timestamp (auto-updated)
Shopping Events (shopping_events table)
id: Integer, Primary key, Auto-incrementshop_id: Integer, Foreign key to shops (required)date: DateTime, Purchase date (required, default: current time)total_amount: Float, Total cost of shopping event (optional, auto-calculated)notes: String, Optional notes about the purchasecreated_at: DateTime, Creation timestamp (auto-generated)updated_at: DateTime, Last update timestamp (auto-updated)
Brands in Shops (brands_in_shops table)
Association table tracking which brands are available in which shops:
id: Integer, Primary key, Auto-incrementshop_id: Integer, Foreign key to shops (required)brand_id: Integer, Foreign key to brands (required)created_at: DateTime, Creation timestamp (auto-generated)updated_at: DateTime, Last update timestamp (auto-updated)
Association Tables
Shopping Event Products (shopping_event_products table)
Many-to-many relationship between shopping events and products with additional data:
id: Integer, Primary key, Auto-incrementshopping_event_id: Integer, Foreign key to shopping_events (required)product_id: Integer, Foreign key to products (required)amount: Float, Quantity purchased in this event (required, > 0)price: Float, Price at time of purchase (required, ≥ 0)discount: Boolean, Whether the product was purchased with a discount (default: false)
Related Products (related_products table)
Many-to-many self-referential relationship between products for tracking related items:
id: Integer, Primary key, Auto-incrementproduct_id: Integer, Foreign key to products (required)related_product_id: Integer, Foreign key to products (required)relationship_type: String, Type of relationship (optional)- Examples: "size_variant", "brand_variant", "similar", "alternative"
created_at: DateTime, Creation timestamp (auto-generated)
Relationships
┌─────────────────┐ 1:N ┌─────────────────┐ 1:N ┌─────────────────┐
│ Brands │ ────────→ │ Products │ ←──────── │ Grocery │
│ │ │ │ │ Categories │
│ • id │ │ • id │ │ • id │
│ • name │ │ • name │ │ • name │
│ • created_at │ │ • category_id │ │ • created_at │
│ • updated_at │ │ • brand_id │ │ • updated_at │
└─────────────────┘ │ • organic │ └─────────────────┘
│ │ • weight │
│ │ • weight_unit │
│ │ • created_at │
│ │ • updated_at │
│ └─────────────────┘
│ │ │
│ │ │ N:M (self-referential)
│ │ ▼
│ │ ┌─────────────────────────────┐
│ │ │ Related Products │
│ │ │ (Association Table) │
│ │ │ │
│ │ │ • id │
│ │ │ • product_id │
│ │ │ • related_product_id │
│ │ │ • relationship_type │
│ │ │ • created_at │
│ │ └─────────────────────────────┘
│ │
│ │ N:M
│ ▼
│ ┌─────────────────────────────┐
│ │ Shopping Event Products │
│ │ (Association Table) │
│ │ │
│ │ • id │
│ │ • shopping_event_id │
│ │ • product_id │
│ │ • amount │
│ │ • price │
│ │ • discount │
│ └─────────────────────────────┘
│ │
│ │ N:1
│ ▼
│ ┌─────────────────┐ 1:N ┌─────────────────┐
│ │ Shops │ ────────→ │ Shopping Events │
│ │ │ │ │
│ │ • id │ │ • id │
│ │ • name │ │ • shop_id │
│ │ • city │ │ • date │
│ │ • address │ │ • total_amount │
│ │ • created_at │ │ • notes │
│ │ • updated_at │ │ • created_at │
│ └─────────────────┘ │ • updated_at │
│ │ └─────────────────┘
│ │
│ │ N:M
│ ▼
│ ┌─────────────────────────────┐
└────────────→ │ Brands in Shops │
│ (Association Table) │
│ │
│ • id │
│ • shop_id │
│ • brand_id │
│ • created_at │
│ • updated_at │
└─────────────────────────────┘
Key Features
- Direct Product-Category Relationship: Products are directly linked to categories for simplified organization
- Brand Tracking: Optional brand association for products with shop availability tracking
- Related Products: Track relationships between products (size variants, brand alternatives, similar items)
- Price History: Each product purchase stores the price at that time, enabling price tracking
- Discount Tracking: Track which products were purchased with discounts for better spending analysis
- Flexible Quantities: Support for decimal amounts (e.g., 1.5 kg of apples)
- Auto-calculation: Total amount can be automatically calculated from individual items
- Free Items: Supports items with price 0 (samples, promotions, etc.)
- Shop-Brand Filtering: Products can be filtered by brands available in specific shops
- Audit Trail: All entities have creation timestamps for tracking
- Data Integrity: Foreign key constraints ensure referential integrity
- Import/Export: CSV-based bulk data operations for all entity types
Development Setup
Prerequisites
- Python 3.8+ - For the backend
- Node.js 16+ - For the frontend
- PostgreSQL - Database
Backend Setup
-
Navigate to backend directory:
cd backend -
Create virtual environment:
python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate -
Install dependencies:
pip install -r requirements.txt -
Setup database:
# Create PostgreSQL database createdb product_tracker # Copy environment variables cp env.example .env # Edit .env with your database credentials nano .env -
Run database migrations:
alembic upgrade head # After setting up alembic -
Start the backend server:
uvicorn main:app --reloadThe API will be available at
http://localhost:8000API docs athttp://localhost:8000/docs
Frontend Setup
-
Navigate to frontend directory:
cd frontend -
Install dependencies:
npm install -
Start the development server:
npm startThe app will be available at
http://localhost:3000
API Endpoints
Brands
GET /brands/- List all brandsPOST /brands/- Create new brandGET /brands/{id}- Get specific brandPUT /brands/{id}- Update brandDELETE /brands/{id}- Delete brand
Grocery Categories
GET /grocery-categories/- List all grocery categoriesPOST /grocery-categories/- Create new grocery categoryGET /grocery-categories/{id}- Get specific grocery categoryPUT /grocery-categories/{id}- Update grocery categoryDELETE /grocery-categories/{id}- Delete grocery category
Products
GET /products/- List all productsPOST /products/- Create new productGET /products/{id}- Get specific productPUT /products/{id}- Update productDELETE /products/{id}- Delete product
Shops
GET /shops/- List all shopsPOST /shops/- Create new shopGET /shops/{id}- Get specific shopPUT /shops/{id}- Update shopDELETE /shops/{id}- Delete shop
Brands in Shops
GET /brands-in-shops/- List all brand-shop associationsPOST /brands-in-shops/- Create new brand-shop associationGET /brands-in-shops/{id}- Get specific brand-shop associationGET /brands-in-shops/shop/{shop_id}- Get brands available in specific shopGET /brands-in-shops/brand/{brand_id}- Get shops that carry specific brandDELETE /brands-in-shops/{id}- Delete brand-shop association
Related Products
GET /related-products/- List all product relationshipsPOST /related-products/- Create new product relationshipGET /related-products/{id}- Get specific product relationshipGET /related-products/product/{product_id}- Get all products related to a specific productPUT /related-products/{id}- Update relationship typeDELETE /related-products/{id}- Delete product relationship
Shopping Events
GET /shopping-events/- List all shopping eventsPOST /shopping-events/- Create new shopping eventGET /shopping-events/{id}- Get specific shopping eventPUT /shopping-events/{id}- Update shopping eventDELETE /shopping-events/{id}- Delete shopping event
Statistics
GET /stats/categories- Category spending statisticsGET /stats/shops- Shop visit statistics
Usage
- Add Shops: Start by adding shops where you buy products
- Add Categories: Create grocery categories (e.g., "Dairy", "Produce", "Meat")
- Add Brands: Create brands for your products (optional)
- Configure Shop-Brand Availability: Associate brands with shops where they're available
- Add Products: Create product items linked directly to categories and optionally to brands
- Link Related Products: Connect products that are related (e.g., same item in different sizes, brand alternatives)
- Record Purchases: Use the "Add Shopping Event" form to record purchases with multiple products
- Track Prices: Monitor how prices change over time for the same products
- Import/Export Data: Use CSV files to bulk import or export your data
- View Statistics: Analyze spending patterns by category and shop
Deployment
Docker Deployment (Recommended)
The application includes a complete Docker Compose setup for easy deployment. This is the recommended way to deploy the application in production.
Quick deployment:
# Clone repository
git clone <your-repo-url>
cd groceries
# Setup environment
cp docker.env.example .env
# Edit .env with your production values
# Deploy
docker-compose up -d
# Initialize database
docker-compose exec backend alembic upgrade head
Services included:
- PostgreSQL database with persistent storage
- FastAPI backend with health checks
- React frontend served by Nginx
- Automatic service restart and dependency management
For comprehensive deployment instructions, troubleshooting, and production considerations, see DOCKER_DEPLOYMENT.md.
Manual Deployment
For development or custom deployments, you can also run the services manually using the Development Setup instructions above.
Development
Running Tests
Backend:
cd backend
pytest
Frontend:
cd frontend
npm test
Database Migrations
cd backend
alembic revision --autogenerate -m "Description"
alembic upgrade head
Contributing
- Fork the repository
- Create a feature branch
- Make changes
- Add tests
- Submit a pull request
License
MIT License