2025-05-28 11:01:42 +02:00
2025-05-28 11:01:42 +02:00
2025-05-26 21:55:49 +02:00
2025-05-26 21:55:49 +02:00
2025-05-26 21:55:49 +02:00
2025-05-27 14:43:00 +02:00
2025-05-26 20:20:21 +02:00
2025-05-26 20:20:21 +02:00
2025-05-28 11:01:42 +02:00

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

  • 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

  1. Clone and setup:

    git clone <your-repo-url>
    cd groceries
    cp docker.env.example .env
    # Edit .env with your secure passwords
    
  2. Start all services:

    docker-compose up -d
    
  3. Initialize database:

    docker-compose exec backend alembic upgrade head
    

Access Your Application

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-increment
  • name: 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-increment
  • name: 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-increment
  • name: 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-increment
  • name: 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-increment
  • shop_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 purchase
  • created_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-increment
  • shop_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-increment
  • shopping_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)

Many-to-many self-referential relationship between products for tracking related items:

  • id: Integer, Primary key, Auto-increment
  • product_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

  1. Python 3.8+ - For the backend
  2. Node.js 16+ - For the frontend
  3. PostgreSQL - Database

Backend Setup

  1. Navigate to backend directory:

    cd backend
    
  2. Create virtual environment:

    python3 -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
    
  3. Install dependencies:

    pip install -r requirements.txt
    
  4. Setup database:

    # Create PostgreSQL database
    createdb product_tracker
    
    # Copy environment variables
    cp env.example .env
    
    # Edit .env with your database credentials
    nano .env
    
  5. Run database migrations:

    alembic upgrade head  # After setting up alembic
    
  6. Start the backend server:

    uvicorn main:app --reload
    

    The API will be available at http://localhost:8000 API docs at http://localhost:8000/docs

Frontend Setup

  1. Navigate to frontend directory:

    cd frontend
    
  2. Install dependencies:

    npm install
    
  3. Start the development server:

    npm start
    

    The app will be available at http://localhost:3000

API Endpoints

Brands

  • GET /brands/ - List all brands
  • POST /brands/ - Create new brand
  • GET /brands/{id} - Get specific brand
  • PUT /brands/{id} - Update brand
  • DELETE /brands/{id} - Delete brand

Grocery Categories

  • GET /grocery-categories/ - List all grocery categories
  • POST /grocery-categories/ - Create new grocery category
  • GET /grocery-categories/{id} - Get specific grocery category
  • PUT /grocery-categories/{id} - Update grocery category
  • DELETE /grocery-categories/{id} - Delete grocery category

Products

  • GET /products/ - List all products
  • POST /products/ - Create new product
  • GET /products/{id} - Get specific product
  • PUT /products/{id} - Update product
  • DELETE /products/{id} - Delete product

Shops

  • GET /shops/ - List all shops
  • POST /shops/ - Create new shop
  • GET /shops/{id} - Get specific shop
  • PUT /shops/{id} - Update shop
  • DELETE /shops/{id} - Delete shop

Brands in Shops

  • GET /brands-in-shops/ - List all brand-shop associations
  • POST /brands-in-shops/ - Create new brand-shop association
  • GET /brands-in-shops/{id} - Get specific brand-shop association
  • GET /brands-in-shops/shop/{shop_id} - Get brands available in specific shop
  • GET /brands-in-shops/brand/{brand_id} - Get shops that carry specific brand
  • DELETE /brands-in-shops/{id} - Delete brand-shop association
  • GET /related-products/ - List all product relationships
  • POST /related-products/ - Create new product relationship
  • GET /related-products/{id} - Get specific product relationship
  • GET /related-products/product/{product_id} - Get all products related to a specific product
  • PUT /related-products/{id} - Update relationship type
  • DELETE /related-products/{id} - Delete product relationship

Shopping Events

  • GET /shopping-events/ - List all shopping events
  • POST /shopping-events/ - Create new shopping event
  • GET /shopping-events/{id} - Get specific shopping event
  • PUT /shopping-events/{id} - Update shopping event
  • DELETE /shopping-events/{id} - Delete shopping event

Statistics

  • GET /stats/categories - Category spending statistics
  • GET /stats/shops - Shop visit statistics

Usage

  1. Add Shops: Start by adding shops where you buy products
  2. Add Categories: Create grocery categories (e.g., "Dairy", "Produce", "Meat")
  3. Add Brands: Create brands for your products (optional)
  4. Configure Shop-Brand Availability: Associate brands with shops where they're available
  5. Add Products: Create product items linked directly to categories and optionally to brands
  6. Link Related Products: Connect products that are related (e.g., same item in different sizes, brand alternatives)
  7. Record Purchases: Use the "Add Shopping Event" form to record purchases with multiple products
  8. Track Prices: Monitor how prices change over time for the same products
  9. Import/Export Data: Use CSV files to bulk import or export your data
  10. View Statistics: Analyze spending patterns by category and shop

Deployment

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

  1. Fork the repository
  2. Create a feature branch
  3. Make changes
  4. Add tests
  5. Submit a pull request

License

MIT License

Description
No description provided
Readme 1.1 MiB
Languages
TypeScript 75.9%
Python 22.8%
Shell 0.4%
Dockerfile 0.4%
HTML 0.3%
Other 0.1%