groceries/README.md
2025-05-26 11:10:42 +02:00

294 lines
9.4 KiB
Markdown

# Grocery Tracker
A web application for tracking grocery prices and shopping events. Built with FastAPI (Python) backend and React (TypeScript) frontend.
## Features
- **Grocery Management**: Add, edit, and track grocery items with prices, categories, and organic status
- **Shop Management**: Manage different shops with locations
- **Shopping Events**: Record purchases with multiple groceries and amounts
- **Price Tracking**: Monitor price changes over time
- **Modern UI**: Clean, responsive interface built with React and Tailwind CSS
## 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
- Axios - HTTP client for API calls
- React Hook Form - Form handling
### Component Communication
```
┌─────────────────┐ HTTP/REST API ┌─────────────────┐ SQL Queries ┌─────────────────┐
│ React │ ←─────────────────→ │ FastAPI │ ←───────────────→ │ PostgreSQL │
│ Frontend │ JSON requests │ Backend │ SQLAlchemy ORM │ Database │
│ (Port 3000) │ JSON responses │ (Port 8000) │ │ (Port 5432) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## Data Model
### Core Entities
#### Groceries (`groceries` table)
- `id`: Integer, Primary key, Auto-increment
- `name`: String, Grocery name (indexed, required)
- `category`: String, Food category (required)
- `organic`: Boolean, Organic flag (default: false)
- `weight`: Float, Weight/volume (optional)
- `weight_unit`: String, Unit of measurement (default: "piece")
- Supported units: "g", "kg", "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)
### Association Table
#### Shopping Event Groceries (`shopping_event_groceries` table)
Many-to-many relationship between shopping events and groceries with additional data:
- `id`: Integer, Primary key, Auto-increment
- `shopping_event_id`: Integer, Foreign key to shopping_events (required)
- `grocery_id`: Integer, Foreign key to groceries (required)
- `amount`: Float, Quantity purchased in this event (required, > 0)
- `price`: Float, Price at time of purchase (required, ≥ 0)
### Relationships
```
┌─────────────────┐ ┌─────────────────────────────┐ ┌─────────────────┐
│ Shops │ │ Shopping Event Groceries │ │ Groceries │
│ │ │ (Association Table) │ │ │
│ • id │ ←──────→│ • shopping_event_id │ ←──────→│ • id │
│ • name │ 1:N │ • grocery_id │ N:M │ • name │
│ • city │ │ • amount │ │ • category │
│ • address │ │ • price │ │ • organic │
│ • created_at │ │ │ │ • weight │
└─────────────────┘ └─────────────────────────────┘ │ • weight_unit │
│ │ │ • created_at │
│ │ │ • updated_at │
│ 1:N │ └─────────────────┘
▼ │
┌─────────────────┐ │
│ Shopping Events │ ←───────────────────────┘
│ │ 1:N
│ • id │
│ • shop_id │
│ • date │
│ • total_amount │
│ • notes │
│ • created_at │
└─────────────────┘
```
### Key Features
- **Price History**: Each grocery purchase stores the price at that time, enabling price tracking
- **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.)
- **Audit Trail**: All entities have creation timestamps for tracking
- **Data Integrity**: Foreign key constraints ensure referential integrity
## Setup Instructions
### 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 grocery_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
### Groceries
- `GET /groceries/` - List all groceries
- `POST /groceries/` - Create new grocery
- `GET /groceries/{id}` - Get specific grocery
- `PUT /groceries/{id}` - Update grocery
- `DELETE /groceries/{id}` - Delete grocery
### Shops
- `GET /shops/` - List all shops
- `POST /shops/` - Create new shop
- `GET /shops/{id}` - Get specific shop
### Shopping Events
- `GET /shopping-events/` - List all shopping events
- `POST /shopping-events/` - Create new shopping event
- `GET /shopping-events/{id}` - Get specific 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 groceries
2. **Add Groceries**: Create grocery items with prices and categories
3. **Record Purchases**: Use the "Add Purchase" form to record shopping events
4. **Track Prices**: Monitor how prices change over time
5. **View Statistics**: Analyze spending patterns by category and shop
## 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
```
## Docker Deployment
Create `docker-compose.yml` for easy deployment:
```yaml
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_DB: grocery_tracker
POSTGRES_USER: grocery_user
POSTGRES_PASSWORD: your_password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
backend:
build: ./backend
ports:
- "8000:8000"
depends_on:
- db
environment:
DATABASE_URL: postgresql://grocery_user:your_password@db:5432/grocery_tracker
frontend:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- backend
volumes:
postgres_data:
```
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make changes
4. Add tests
5. Submit a pull request
## License
MIT License