groceries/README.md
2025-05-28 11:01:42 +02:00

465 lines
19 KiB
Markdown

# 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](#features)
- [Quick Start with Docker](#quick-start-with-docker)
- [Architecture](#architecture)
- [Data Model](#data-model)
- [Development Setup](#development-setup)
- [API Endpoints](#api-endpoints)
- [Usage](#usage)
- [Deployment](#deployment)
- [Development](#development)
- [Contributing](#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
1. **Clone and setup:**
```bash
git clone <your-repo-url>
cd groceries
cp docker.env.example .env
# Edit .env with your secure passwords
```
2. **Start all services:**
```bash
docker-compose up -d
```
3. **Initialize database:**
```bash
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](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)
#### Related Products (`related_products` table)
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:**
```bash
cd backend
```
2. **Create virtual environment:**
```bash
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. **Install dependencies:**
```bash
pip install -r requirements.txt
```
4. **Setup database:**
```bash
# 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:**
```bash
alembic upgrade head # After setting up alembic
```
6. **Start the backend server:**
```bash
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:**
```bash
cd frontend
```
2. **Install dependencies:**
```bash
npm install
```
3. **Start the development server:**
```bash
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
### Related Products
- `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
### 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:**
```bash
# 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](DOCKER_DEPLOYMENT.md)**.
### Manual Deployment
For development or custom deployments, you can also run the services manually using the [Development Setup](#development-setup) instructions above.
## Development
### Running Tests
**Backend:**
```bash
cd backend
pytest
```
**Frontend:**
```bash
cd frontend
npm test
```
### Database Migrations
```bash
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