From 9dd177bddc4b8b1d43a68b2ca3337989ae1f3c0d Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Fri, 5 Sep 2025 17:27:39 +0200 Subject: [PATCH] Initial commit --- .env | 24 + .env.example | 23 + .idea/.gitignore | 4 + .idea/Letzshop-Import-v2.iml | 14 + .idea/inspectionProfiles/Project_Default.xml | 14 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + Dockerfile | 35 + Makefile | 50 ++ README.md | 500 ++++++++++++ TODO | 15 + alembic-env.py | 56 ++ alembic.ini | 43 ++ alembic/README | 1 + alembic/env.py | 59 ++ alembic/script.py.mako | 26 + auth_example.py | 130 ++++ config/settings.py | 31 + docker-compose.yml | 57 ++ ecommerce.db | Bin 0 -> 1101824 bytes init.sql | 10 + main.py | 712 ++++++++++++++++++ middleware/auth.py | 172 +++++ middleware/error_handler.py | 55 ++ middleware/logging_middleware.py | 46 ++ middleware/rate_limiter.py | 82 ++ models/api_models.py | 209 +++++ models/database_models.py | 120 +++ requirements.txt | 30 + scripts/setup_dev.py | 168 +++++ tests/test_utils.py | 61 ++ updated_readme.md | 569 ++++++++++++++ utils/csv_processor.py | 253 +++++++ utils/data_processing.py | 129 ++++ utils/database.py | 36 + 36 files changed, 3755 insertions(+) create mode 100644 .env create mode 100644 .env.example create mode 100644 .idea/.gitignore create mode 100644 .idea/Letzshop-Import-v2.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 TODO create mode 100644 alembic-env.py create mode 100644 alembic.ini create mode 100644 alembic/README create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako create mode 100644 auth_example.py create mode 100644 config/settings.py create mode 100644 docker-compose.yml create mode 100644 ecommerce.db create mode 100644 init.sql create mode 100644 main.py create mode 100644 middleware/auth.py create mode 100644 middleware/error_handler.py create mode 100644 middleware/logging_middleware.py create mode 100644 middleware/rate_limiter.py create mode 100644 models/api_models.py create mode 100644 models/database_models.py create mode 100644 requirements.txt create mode 100644 scripts/setup_dev.py create mode 100644 tests/test_utils.py create mode 100644 updated_readme.md create mode 100644 utils/csv_processor.py create mode 100644 utils/data_processing.py create mode 100644 utils/database.py diff --git a/.env b/.env new file mode 100644 index 00000000..eaef3702 --- /dev/null +++ b/.env @@ -0,0 +1,24 @@ +# .env.example +# Database Configuration +# DATABASE_URL=postgresql://username:password@localhost:5432/ecommerce_db +# For development, you can use SQLite: +DATABASE_URL=sqlite:///./ecommerce.db + +# JWT Configuration +JWT_SECRET_KEY=your-super-secret-jwt-key-change-in-production +JWT_EXPIRE_HOURS=24 +JWT_EXPIRE_MINUTES=30 + +# API Configuration +API_HOST=0.0.0.0 +API_PORT=8000 +DEBUG=False + +# Rate Limiting +RATE_LIMIT_ENABLED=True +DEFAULT_RATE_LIMIT=100 +DEFAULT_WINDOW_SECONDS=3600 + +# Logging +LOG_LEVEL=INFO +LOG_FILE=app.log \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..db98bb37 --- /dev/null +++ b/.env.example @@ -0,0 +1,23 @@ +# .env.example +# Database Configuration +DATABASE_URL=postgresql://username:password@localhost:5432/ecommerce_db +# For development, you can use SQLite: +# DATABASE_URL=sqlite:///./ecommerce.db + +# JWT Configuration +JWT_SECRET_KEY=your-super-secret-jwt-key-change-in-production +JWT_EXPIRE_HOURS=24 + +# API Configuration +API_HOST=0.0.0.0 +API_PORT=8000 +DEBUG=False + +# Rate Limiting +RATE_LIMIT_ENABLED=True +DEFAULT_RATE_LIMIT=100 +DEFAULT_WINDOW_SECONDS=3600 + +# Logging +LOG_LEVEL=INFO +LOG_FILE=app.log \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..351c96db --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,4 @@ +# Default ignored files +/shelf/ +/workspace.xml + diff --git a/.idea/Letzshop-Import-v2.iml b/.idea/Letzshop-Import-v2.iml new file mode 100644 index 00000000..72ead01c --- /dev/null +++ b/.idea/Letzshop-Import-v2.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..930c0173 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..5082f5ca --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..37b2c337 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..a93b0f67 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Dockerfile +FROM python:3.11-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + libpq-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Create logs directory +RUN mkdir -p logs + +# Create non-root user +RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app +USER appuser + +# Expose port +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:8000/health || exit 1 + +# Run the application +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ff729f34 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +# Makefile +.PHONY: install dev test lint format docker-build docker-up docker-down migrate + +# Development setup +install: + pip install -r requirements.txt + +dev: + uvicorn main:app --reload --host 0.0.0.0 --port 8000 + +test: + pytest -v + +lint: + flake8 . --max-line-length=88 --extend-ignore=E203 + mypy . + +format: + black . + isort . + +# Database migrations +migrate-create: + alembic revision --autogenerate -m "$(message)" + +migrate-up: + alembic upgrade head + +migrate-down: + alembic downgrade -1 + +# Docker commands +docker-build: + docker-compose build + +docker-up: + docker-compose up -d + +docker-down: + docker-compose down + +docker-logs: + docker-compose logs -f api + +# Production deployment +deploy-staging: + docker-compose -f docker-compose.staging.yml up -d + +deploy-prod: + docker-compose -f docker-compose.prod.yml up -d diff --git a/README.md b/README.md new file mode 100644 index 00000000..d61b4111 --- /dev/null +++ b/README.md @@ -0,0 +1,500 @@ +# Ecommerce Backend API v2.0 + +A robust, production-ready FastAPI backend for ecommerce product catalog and inventory management with advanced CSV import capabilities. + +## Key Improvements from v1 + +### Architecture Improvements +- **Modular Design**: Separated concerns into utility modules, middleware, and models +- **Database Optimization**: Added proper indexing strategy and foreign key relationships +- **Connection Pooling**: PostgreSQL support with connection pooling for production scalability +- **Background Processing**: Asynchronous CSV import with job tracking + +### Security Enhancements +- **JWT Authentication**: Token-based authentication with role-based access control +- **Rate Limiting**: Sliding window rate limiter to prevent API abuse +- **Input Validation**: Enhanced Pydantic models with comprehensive validation + +### Performance Optimizations +- **Batch Processing**: CSV imports processed in configurable batches +- **Database Indexes**: Strategic indexing for common query patterns +- **Streaming Export**: Memory-efficient CSV export for large datasets +- **Caching Ready**: Architecture supports Redis integration + +### Data Processing +- **Robust GTIN Handling**: Centralized GTIN normalization and validation +- **Multi-currency Support**: Advanced price parsing with currency extraction +- **International Content**: Multi-encoding CSV support for global data + +## Project Structure + +``` +ecommerce_api/ +├── main.py # FastAPI application entry point +├── models/ +│ ├── database_models.py # SQLAlchemy ORM models +│ └── api_models.py # Pydantic API models +├── utils/ +│ ├── data_processing.py # GTIN and price processing utilities +│ ├── csv_processor.py # CSV import/export handling +│ └── database.py # Database configuration +├── middleware/ +│ ├── auth.py # JWT authentication +│ ├── rate_limiter.py # Rate limiting implementation +│ └── logging_middleware.py # Request/response logging +├── config/ +│ └── settings.py # Application configuration +├── tests/ +│ └── test_utils.py # Unit tests +├── alembic/ # Database migrations +├── docker-compose.yml # Docker deployment +├── Dockerfile # Container definition +├── requirements.txt # Python dependencies +└── README.md # This file +``` + +## Quick Start + +### 1. Development Setup + +```bash +# Clone the repository +git clone +cd ecommerce-api + +# Set up virtual environment +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt + +# Set up environment variables +cp .env.example .env +# Edit .env with your database configuration +``` + +### 2. Database Setup + +**For SQLite (Development):** +```bash +# Your .env file should have: +# DATABASE_URL=sqlite:///./ecommerce.db + +# Initialize Alembic (only needed once) +alembic init alembic + +# Update alembic/env.py with the provided configuration (see below) + +# Create initial migration +alembic revision --autogenerate -m "Initial migration" + +# Apply migrations +alembic upgrade head +``` + +**For PostgreSQL (Production):** +```bash +# 1. Create PostgreSQL database +createdb ecommerce_db + +# 2. Update .env file: +# DATABASE_URL=postgresql://username:password@localhost:5432/ecommerce_db + +# 3. Initialize and run migrations +alembic init alembic +# Update alembic/env.py and alembic.ini (see configuration section) +alembic revision --autogenerate -m "Initial migration" +alembic upgrade head +``` + +**Important Alembic Configuration:** + +After running `alembic init alembic`, you must update two files: + +**1. Update `alembic/env.py`:** +```python +from logging.config import fileConfig +from sqlalchemy import engine_from_config, pool +from alembic import context +import os +import sys + +# Add your project directory to the Python path +sys.path.append(os.path.dirname(os.path.dirname(__file__))) + +from models.database_models import Base +from config.settings import settings + +# Alembic Config object +config = context.config + +# Override sqlalchemy.url with our settings +config.set_main_option("sqlalchemy.url", settings.database_url) + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +target_metadata = Base.metadata + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode.""" + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() +``` + +**2. Update `alembic.ini` sqlalchemy.url line:** +```ini +# For SQLite: +sqlalchemy.url = sqlite:///./ecommerce.db + +# For PostgreSQL: +sqlalchemy.url = postgresql://username:password@localhost:5432/ecommerce_db +``` + +### 3. Configuration + +Edit `.env` file with your settings: + +```env +DATABASE_URL=postgresql://user:password@localhost:5432/ecommerce_db +JWT_SECRET_KEY=your-super-secret-key-change-in-production +API_HOST=0.0.0.0 +API_PORT=8000 +DEBUG=False +``` + +### 3. Run Development Server + +```bash +# Using make +make dev + +# Or directly +uvicorn main:app --reload --host 0.0.0.0 --port 8000 +``` + +### 4. Docker Deployment + +```bash +# Start all services +docker-compose up -d + +# View logs +docker-compose logs -f api + +# Stop services +docker-compose down +``` + +## API Endpoints + +### Authentication +- `POST /auth/login` - Get JWT token +- `POST /auth/refresh` - Refresh token + +### Products +- `GET /products` - List products with filtering and search +- `POST /products` - Create new product +- `GET /products/{product_id}` - Get product with stock info +- `PUT /products/{product_id}` - Update product +- `DELETE /products/{product_id}` - Delete product and associated stock + +### CSV Operations +- `POST /import-csv` - Start background CSV import +- `GET /import-status/{job_id}` - Check import job status +- `GET /export-csv` - Export products as CSV (streaming) + +### Stock Management +- `POST /stock` - Set exact stock quantity +- `POST /stock/add` - Add to existing stock +- `POST /stock/remove` - Remove from stock +- `GET /stock/{gtin}` - Get stock summary by GTIN +- `GET /stock/{gtin}/total` - Get total stock for GTIN +- `GET /stock` - List all stock entries with filtering +- `PUT /stock/{stock_id}` - Update stock entry +- `DELETE /stock/{stock_id}` - Delete stock entry + +### System +- `GET /` - API information +- `GET /health` - Health check +- `GET /stats` - System statistics + +## Advanced Features + +### Background CSV Import + +The API supports background processing of large CSV files: + +```python +# Start import +response = requests.post('/import-csv', json={ + 'url': 'https://example.com/products.csv', + 'batch_size': 1000 +}) + +job_id = response.json()['job_id'] + +# Check status +status = requests.get(f'/import-status/{job_id}') +``` + +### Rate Limiting + +Built-in rate limiting protects against API abuse: + +- Default: 100 requests per hour per client +- CSV imports: 10 per hour +- Configurable per endpoint + +### Search and Filtering + +Advanced product search capabilities: + +```bash +# Search in title and description +GET /products?search=laptop + +# Filter by brand and category +GET /products?brand=Apple&category=Electronics + +# Combine filters +GET /products?brand=Samsung&availability=in stock&search=phone +``` + +### Data Validation + +Comprehensive validation for all inputs: + +- GTIN format validation and normalization +- Price parsing with currency extraction +- Required field validation +- Type conversion and sanitization + +## Database Schema + +### Products Table +- Full product catalog with Google Shopping compatibility +- Indexed fields: `gtin`, `brand`, `google_product_category`, `availability` +- Timestamps for creation and updates + +### Stock Table +- Location-based inventory tracking +- GTIN-based product linking +- Unique constraint on GTIN+location combinations +- Composite indexes for efficient queries + +### Import Jobs Table +- Track background import operations +- Status monitoring and error handling +- Performance metrics + +## Development + +### Running Tests + +```bash +# All tests +make test + +# Specific test file +pytest tests/test_utils.py -v + +# With coverage +pytest --cov=. tests/ +``` + +### Code Quality + +```bash +# Format code +make format + +# Lint code +make lint + +# Type checking +mypy . +``` + +### Database Migrations + +**Creating Migrations:** +```bash +# After making changes to models/database_models.py, create a new migration +alembic revision --autogenerate -m "Description of changes" + +# Review the generated migration file in alembic/versions/ +# Edit if needed, then apply: +alembic upgrade head +``` + +**Common Migration Commands:** +```bash +# Check current migration status +alembic current + +# View migration history +alembic history + +# Upgrade to specific revision +alembic upgrade + +# Downgrade one step +alembic downgrade -1 + +# Downgrade to specific revision +alembic downgrade + +# Reset database (WARNING: destroys all data) +alembic downgrade base +alembic upgrade head +``` + +**Troubleshooting Alembic:** +```bash +# If you get template errors, reinitialize Alembic: +rm -rf alembic/ +alembic init alembic +# Then update alembic/env.py and alembic.ini as shown above + +# If migrations conflict, you may need to merge: +alembic merge -m "Merge migrations" +``` + +## Production Deployment + +### Environment Setup + +1. **Database**: PostgreSQL 13+ recommended +2. **Cache**: Redis for session storage and rate limiting +3. **Reverse Proxy**: Nginx for SSL termination and load balancing +4. **Monitoring**: Consider adding Prometheus metrics + +### Security Checklist + +- [ ] Change default JWT secret key +- [ ] Set up HTTPS/TLS +- [ ] Configure CORS appropriately +- [ ] Set up database connection limits +- [ ] Enable request logging +- [ ] Configure rate limiting per your needs +- [ ] Set up monitoring and alerting + +### Docker Production + +```yaml +# docker-compose.prod.yml +version: '3.8' +services: + api: + build: . + environment: + - DEBUG=False + - DATABASE_URL=${DATABASE_URL} + - JWT_SECRET_KEY=${JWT_SECRET_KEY} + restart: unless-stopped + # Add your production configuration +``` + +## Performance Considerations + +### Database Optimization +- Use PostgreSQL for production workloads +- Monitor query performance with `EXPLAIN ANALYZE` +- Consider read replicas for read-heavy workloads +- Regular `VACUUM` and `ANALYZE` operations + +### CSV Import Performance +- Batch size affects memory usage vs. speed +- Larger batches = faster import but more memory +- Monitor import job status for optimization + +### API Response Times +- Database indexes are crucial for filtering +- Use pagination for large result sets +- Consider caching frequently accessed data + +## Troubleshooting + +### Common Issues + +1. **CSV Import Failures** + - Check encoding: try different separators + - Validate required columns: `product_id`, `title` + - Monitor import job status for specific errors + +2. **Database Connection Issues** + - Verify DATABASE_URL format + - Check connection limits + - Ensure database server is accessible + +3. **Authentication Problems** + - Verify JWT_SECRET_KEY is set + - Check token expiration settings + - Validate token format + +### Logging + +Logs are structured and include: +- Request/response times +- Error details with stack traces +- Import job progress +- Rate limiting events + +```bash +# View live logs +tail -f logs/app.log + +# Docker logs +docker-compose logs -f api +``` + +## Contributing + +1. Fork the repository +2. Create a feature branch +3. Make changes with tests +4. Run quality checks: `make lint test` +5. Submit a pull request + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Support + +For issues and questions: +1. Check the troubleshooting section +2. Review existing GitHub issues +3. Create a new issue with detailed information +4. For security issues, contact maintainers directly \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 00000000..2b7c04f8 --- /dev/null +++ b/TODO @@ -0,0 +1,15 @@ +Best Practice Recommendation +For production applications, consider using database migrations (like Alembic) to manage schema changes including index +creation, rather than creating them programmatically at startup. This provides better control and tracking of database changes. + + +https://letzshop-google-shopping-product-exports-production.s3.eu-west-1.amazonaws.com/city_zones/strassen/vendors/wizard-cloud-marketing-s-a-r-l/google_shopping/products_fr.csv + +what are those? +INFO: ('192.168.1.125', 53912) - "WebSocket /" 403 +INFO: connection rejected (403 Forbidden) +INFO: connection closed +INFO: 192.168.1.125:53913 - "GET /loginMsg.js HTTP/1.1" 404 Not Found +INFO: 192.168.1.125:53914 - "GET /cgi/get.cgi?cmd=home_login HTTP/1.1" 404 Not Found +INFO: 192.168.1.125:53915 - "POST /boaform/admin/formTracert HTTP/1.1" 404 Not Found + diff --git a/alembic-env.py b/alembic-env.py new file mode 100644 index 00000000..13180d78 --- /dev/null +++ b/alembic-env.py @@ -0,0 +1,56 @@ +from logging.config import fileConfig +from sqlalchemy import engine_from_config, pool +from alembic import context +import os +import sys + +# Add your project directory to the Python path +sys.path.append(os.path.dirname(os.path.dirname(__file__))) + +from models.database_models import Base +from config.settings import settings + +# Alembic Config object +config = context.config + +# Override sqlalchemy.url with our settings +config.set_main_option("sqlalchemy.url", settings.database_url) + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +target_metadata = Base.metadata + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode.""" + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 00000000..d7549228 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,43 @@ +# alembic.ini +[alembic] +script_location = alembic +prepend_sys_path = . +version_path_separator = os +sqlalchemy.url = sqlite:///./ecommerce.db +# for PROD: sqlalchemy.url = postgresql://username:password@localhost:5432/ecommerce_db + +[post_write_hooks] + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/alembic/README b/alembic/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 00000000..cec52eba --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,59 @@ +from logging.config import fileConfig +from sqlalchemy import engine_from_config, pool +from alembic import context +import os +import sys + +# Add your project directory to the Python path +sys.path.append(os.path.dirname(os.path.dirname(__file__))) + +from models.database_models import Base +from config.settings import settings + +# Alembic Config object +config = context.config + +# Override sqlalchemy.url with our settings +config.set_main_option("sqlalchemy.url", settings.database_url) + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +target_metadata = Base.metadata + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode.""" + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 00000000..fbc4b07d --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,26 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/auth_example.py b/auth_example.py new file mode 100644 index 00000000..9358dafe --- /dev/null +++ b/auth_example.py @@ -0,0 +1,130 @@ +# Authentication Usage Example +# This file demonstrates how to use the authentication endpoints + +import requests +import json + +# API Base URL +BASE_URL = "http://localhost:8000" + +def register_user(email, username, password): + """Register a new user""" + response = requests.post(f"{BASE_URL}/register", json={ + "email": email, + "username": username, + "password": password + }) + return response.json() + +def login_user(username, password): + """Login and get JWT token""" + response = requests.post(f"{BASE_URL}/login", json={ + "username": username, + "password": password + }) + if response.status_code == 200: + data = response.json() + return data["access_token"] + else: + print(f"Login failed: {response.json()}") + return None + +def get_user_info(token): + """Get current user info""" + headers = {"Authorization": f"Bearer {token}"} + response = requests.get(f"{BASE_URL}/me", headers=headers) + return response.json() + +def get_products(token, skip=0, limit=10): + """Get products (requires authentication)""" + headers = {"Authorization": f"Bearer {token}"} + response = requests.get(f"{BASE_URL}/products?skip={skip}&limit={limit}", headers=headers) + return response.json() + +def create_product(token, product_data): + """Create a new product (requires authentication)""" + headers = {"Authorization": f"Bearer {token}"} + response = requests.post(f"{BASE_URL}/products", json=product_data, headers=headers) + return response.json() + +# Example usage +if __name__ == "__main__": + # 1. Register a new user + print("1. Registering new user...") + try: + user_result = register_user("test@example.com", "testuser", "password123") + print(f"User registered: {user_result}") + except Exception as e: + print(f"Registration failed: {e}") + + # 2. Login with default admin user + print("\n2. Logging in as admin...") + admin_token = login_user("admin", "admin123") + if admin_token: + print(f"Admin login successful! Token: {admin_token[:50]}...") + + # 3. Get user info + print("\n3. Getting admin user info...") + user_info = get_user_info(admin_token) + print(f"User info: {user_info}") + + # 4. Create a sample product + print("\n4. Creating a sample product...") + sample_product = { + "product_id": "TEST001", + "title": "Test Product", + "description": "A test product for demonstration", + "price": "19.99", + "brand": "Test Brand", + "availability": "in stock" + } + + product_result = create_product(admin_token, sample_product) + print(f"Product created: {product_result}") + + # 5. Get products list + print("\n5. Getting products list...") + products = get_products(admin_token) + print(f"Products: {products}") + + # 6. Login with regular user + print("\n6. Logging in as regular user...") + user_token = login_user("testuser", "password123") + if user_token: + print(f"User login successful! Token: {user_token[:50]}...") + + # Regular users can also access protected endpoints + user_info = get_user_info(user_token) + print(f"Regular user info: {user_info}") + + products = get_products(user_token, limit=5) + print(f"Products accessible to regular user: {len(products.get('products', []))} products") + + print("\nAuthentication example completed!") + +# Example cURL commands: +""" +# Register a new user +curl -X POST "http://localhost:8000/register" \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com", "username": "newuser", "password": "password123"}' + +# Login (get JWT token) +curl -X POST "http://localhost:8000/login" \ + -H "Content-Type: application/json" \ + -d '{"username": "admin", "password": "admin123"}' + +# Use token to access protected endpoint +curl -X GET "http://localhost:8000/me" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN_HERE" + +# Get products (protected) +curl -X GET "http://localhost:8000/products" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN_HERE" + +# Create product (protected) +curl -X POST "http://localhost:8000/products" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN_HERE" \ + -H "Content-Type: application/json" \ + -d '{"product_id": "TEST001", "title": "Test Product", "price": "19.99"}' +""" \ No newline at end of file diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 00000000..998df2db --- /dev/null +++ b/config/settings.py @@ -0,0 +1,31 @@ +# config/settings.py +from pydantic_settings import BaseSettings # This is the correct import for Pydantic v2 +from typing import Optional + + +class Settings(BaseSettings): + # Database + database_url: str = "sqlite:///./ecommerce.db" + + # JWT + jwt_secret_key: str = "change-this-in-production" + jwt_expire_hours: int = 24 + + # API + api_host: str = "0.0.0.0" + api_port: int = 8000 + debug: bool = False + + # Rate Limiting + rate_limit_enabled: bool = True + default_rate_limit: int = 100 + default_window_seconds: int = 3600 + + # Logging + log_level: str = "INFO" + log_file: Optional[str] = None + + model_config = {"env_file": ".env"} # Updated syntax for Pydantic v2 + + +settings = Settings() diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..f6ab4b43 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,57 @@ +# docker-compose.yml +version: '3.8' + +services: + db: + image: postgres:15 + restart: always + environment: + POSTGRES_DB: ecommerce_db + POSTGRES_USER: ecommerce_user + POSTGRES_PASSWORD: secure_password + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ecommerce_user -d ecommerce_db"] + interval: 30s + timeout: 10s + retries: 3 + + redis: + image: redis:7-alpine + restart: always + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 30s + timeout: 10s + retries: 3 + + api: + build: . + restart: always + ports: + - "8000:8000" + environment: + DATABASE_URL: postgresql://ecommerce_user:secure_password@db:5432/ecommerce_db + JWT_SECRET_KEY: ${JWT_SECRET_KEY:-your-super-secret-key} + REDIS_URL: redis://redis:6379/0 + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + volumes: + - ./logs:/app/logs + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + postgres_data: diff --git a/ecommerce.db b/ecommerce.db new file mode 100644 index 0000000000000000000000000000000000000000..28d7063c6cb2f1c23de6ea69edd0d6519e50c892 GIT binary patch literal 1101824 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

s3J92roQ=DCGZ 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 literal 0 HcmV?d00001 diff --git a/init.sql b/init.sql new file mode 100644 index 00000000..880a0d4e --- /dev/null +++ b/init.sql @@ -0,0 +1,10 @@ + + +# init.sql +-- Initial database setup +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- Create indexes for better performance +-- These will be managed by Alembic in production + + diff --git a/main.py b/main.py new file mode 100644 index 00000000..cbd0cc71 --- /dev/null +++ b/main.py @@ -0,0 +1,712 @@ +from fastapi import FastAPI, HTTPException, Query, Depends, BackgroundTasks +from fastapi.responses import StreamingResponse +from fastapi.middleware.cors import CORSMiddleware +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from pydantic import BaseModel, Field, validator +from typing import Optional, List, Dict, Any +from datetime import datetime, timedelta +from sqlalchemy import create_engine, Column, Integer, String, DateTime, text, ForeignKey, Index +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, Session, relationship +from contextlib import asynccontextmanager +import pandas as pd +import requests +from io import StringIO, BytesIO +import logging +import asyncio +import time +from functools import wraps +import os +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +# Import utility modules +from utils.data_processing import GTINProcessor, PriceProcessor +from utils.csv_processor import CSVProcessor +from utils.database import get_db_engine, get_session_local +from models.database_models import Base, Product, Stock, ImportJob, User +from models.api_models import * +from middleware.rate_limiter import RateLimiter +from middleware.auth import AuthManager + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +# Initialize processors +gtin_processor = GTINProcessor() +price_processor = PriceProcessor() +csv_processor = CSVProcessor() + +# Database setup +DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@localhost/ecommerce") +engine = get_db_engine(DATABASE_URL) +SessionLocal = get_session_local(engine) + +# Rate limiter and auth manager +rate_limiter = RateLimiter() +auth_manager = AuthManager() + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """Application lifespan events""" + # Startup + logger.info("Starting up ecommerce API with authentication") + + # Create tables + Base.metadata.create_all(bind=engine) + + # Create default admin user + db = SessionLocal() + try: + auth_manager.create_default_admin_user(db) + except Exception as e: + logger.error(f"Failed to create default admin user: {e}") + finally: + db.close() + + # Add indexes + with engine.connect() as conn: + try: + # User indexes + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_user_email ON users(email)")) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_user_username ON users(username)")) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_user_role ON users(role)")) + + # Product indexes + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_product_gtin ON products(gtin)")) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_product_brand ON products(brand)")) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_product_category ON products(google_product_category)")) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_product_availability ON products(availability)")) + + # Stock indexes + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_stock_gtin_location ON stock(gtin, location)")) + conn.execute(text("CREATE INDEX IF NOT EXISTS idx_stock_location ON stock(location)")) + + conn.commit() + logger.info("Database indexes created successfully") + except Exception as e: + logger.warning(f"Index creation warning: {e}") + + yield + + # Shutdown + logger.info("Shutting down ecommerce API") + + +# FastAPI app with lifespan +app = FastAPI( + title="Ecommerce Backend API", + description="Advanced product management system with JWT authentication, CSV import/export and stock management", + version="2.1.0", + lifespan=lifespan +) + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Configure appropriately for production + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Security +security = HTTPBearer() + + +# Database dependency with connection pooling +def get_db(): + db = SessionLocal() + try: + yield db + except Exception as e: + db.rollback() + logger.error(f"Database error: {e}") + raise + finally: + db.close() + + +# Authentication dependencies +def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)): + """Get current authenticated user""" + return auth_manager.get_current_user(db, credentials) + + +def get_current_admin_user(current_user: User = Depends(get_current_user)): + """Require admin user""" + return auth_manager.require_admin(current_user) + + +# Rate limiting decorator +def rate_limit(max_requests: int = 100, window_seconds: int = 3600): + def decorator(func): + @wraps(func) + async def wrapper(*args, **kwargs): + # Extract client IP or user ID for rate limiting + client_id = "anonymous" # In production, extract from request + + if not rate_limiter.allow_request(client_id, max_requests, window_seconds): + raise HTTPException( + status_code=429, + detail="Rate limit exceeded" + ) + + return await func(*args, **kwargs) + + return wrapper + + return decorator + + +# Authentication Routes +@app.post("/register", response_model=UserResponse) +def register_user(user_data: UserRegister, db: Session = Depends(get_db)): + """Register a new user""" + + # Check if email already exists + existing_email = db.query(User).filter(User.email == user_data.email).first() + if existing_email: + raise HTTPException(status_code=400, detail="Email already registered") + + # Check if username already exists + existing_username = db.query(User).filter(User.username == user_data.username).first() + if existing_username: + raise HTTPException(status_code=400, detail="Username already taken") + + # Hash password and create user + hashed_password = auth_manager.hash_password(user_data.password) + new_user = User( + email=user_data.email, + username=user_data.username, + hashed_password=hashed_password, + role="user", # Default role + is_active=True + ) + + db.add(new_user) + db.commit() + db.refresh(new_user) + + logger.info(f"New user registered: {new_user.username}") + return new_user + + +@app.post("/login", response_model=LoginResponse) +def login_user(user_credentials: UserLogin, db: Session = Depends(get_db)): + """Login user and return JWT token""" + + user = auth_manager.authenticate_user(db, user_credentials.username, user_credentials.password) + if not user: + raise HTTPException( + status_code=401, + detail="Incorrect username or password" + ) + + # Create access token + token_data = auth_manager.create_access_token(user) + + logger.info(f"User logged in: {user.username}") + + return LoginResponse( + access_token=token_data["access_token"], + token_type=token_data["token_type"], + expires_in=token_data["expires_in"], + user=UserResponse.model_validate(user) + ) + + +@app.get("/me", response_model=UserResponse) +def get_current_user_info(current_user: User = Depends(get_current_user)): + """Get current user information""" + return UserResponse.model_validate(current_user) + + +# Public Routes (no authentication required) +@app.get("/") +def root(): + return { + "message": "Ecommerce Backend API v2.1 with JWT Authentication", + "status": "operational", + "auth_required": "Most endpoints require Bearer token authentication" + } + + +@app.get("/health") +def health_check(db: Session = Depends(get_db)): + """Health check endpoint""" + try: + # Test database connection + db.execute(text("SELECT 1")) + return {"status": "healthy", "timestamp": datetime.utcnow()} + except Exception as e: + logger.error(f"Health check failed: {e}") + raise HTTPException(status_code=503, detail="Service unhealthy") + + +# Protected Routes (authentication required) +@app.post("/import-csv", response_model=ImportJobResponse) +@rate_limit(max_requests=10, window_seconds=3600) # Limit CSV imports +async def import_csv_from_url( + request: CSVImportRequest, + background_tasks: BackgroundTasks, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Import products from CSV URL with background processing (Protected)""" + + # Create import job record + import_job = ImportJob( + status="pending", + source_url=request.url, + user_id=current_user.id, + created_at=datetime.utcnow() + ) + db.add(import_job) + db.commit() + db.refresh(import_job) + + # Process in background + background_tasks.add_task( + process_csv_import, + import_job.id, + request.url, + request.batch_size or 1000 + ) + + return ImportJobResponse( + job_id=import_job.id, + status="pending", + message="CSV import started. Check status with /import-status/{job_id}" + ) + + +async def process_csv_import(job_id: int, url: str, batch_size: int = 1000): + """Background task to process CSV import with batching""" + db = SessionLocal() + + try: + # Update job status + job = db.query(ImportJob).filter(ImportJob.id == job_id).first() + if not job: + logger.error(f"Import job {job_id} not found") + return + + job.status = "processing" + job.started_at = datetime.utcnow() + db.commit() + + # Process CSV + result = await csv_processor.process_csv_from_url(url, batch_size, db) + + # Update job with results + job.status = "completed" + job.completed_at = datetime.utcnow() + job.imported_count = result["imported"] + job.updated_count = result["updated"] + job.error_count = result.get("errors", 0) + job.total_processed = result["total_processed"] + + if result.get("errors", 0) > 0: + job.status = "completed_with_errors" + job.error_message = f"{result['errors']} rows had errors" + + db.commit() + logger.info(f"Import job {job_id} completed successfully") + + except Exception as e: + logger.error(f"Import job {job_id} failed: {e}") + job.status = "failed" + job.completed_at = datetime.utcnow() + job.error_message = str(e) + db.commit() + + finally: + db.close() + + +@app.get("/import-status/{job_id}", response_model=ImportJobResponse) +def get_import_status(job_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): + """Get status of CSV import job (Protected)""" + job = db.query(ImportJob).filter(ImportJob.id == job_id).first() + if not job: + raise HTTPException(status_code=404, detail="Import job not found") + + # Users can only see their own jobs, admins can see all + if current_user.role != "admin" and job.user_id != current_user.id: + raise HTTPException(status_code=403, detail="Access denied to this import job") + + return ImportJobResponse( + job_id=job.id, + status=job.status, + imported=job.imported_count or 0, + updated=job.updated_count or 0, + total_processed=job.total_processed or 0, + error_count=job.error_count or 0, + error_message=job.error_message, + created_at=job.created_at, + started_at=job.started_at, + completed_at=job.completed_at + ) + + +@app.get("/products", response_model=ProductListResponse) +def get_products( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + brand: Optional[str] = Query(None), + category: Optional[str] = Query(None), + availability: Optional[str] = Query(None), + search: Optional[str] = Query(None), + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Get products with advanced filtering and search (Protected)""" + + query = db.query(Product) + + # Apply filters + if brand: + query = query.filter(Product.brand.ilike(f"%{brand}%")) + if category: + query = query.filter(Product.google_product_category.ilike(f"%{category}%")) + if availability: + query = query.filter(Product.availability == availability) + if search: + # Search in title and description + search_term = f"%{search}%" + query = query.filter( + (Product.title.ilike(search_term)) | + (Product.description.ilike(search_term)) + ) + + # Get total count for pagination + total = query.count() + + # Apply pagination + products = query.offset(skip).limit(limit).all() + + return ProductListResponse( + products=products, + total=total, + skip=skip, + limit=limit + ) + + +@app.post("/products", response_model=ProductResponse) +def create_product( + product: ProductCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Create a new product with validation (Protected)""" + + # Check if product_id already exists + existing = db.query(Product).filter(Product.product_id == product.product_id).first() + if existing: + raise HTTPException(status_code=400, detail="Product with this ID already exists") + + # Process and validate GTIN if provided + if product.gtin: + normalized_gtin = gtin_processor.normalize(product.gtin) + if not normalized_gtin: + raise HTTPException(status_code=400, detail="Invalid GTIN format") + product.gtin = normalized_gtin + + # Process price if provided + if product.price: + parsed_price, currency = price_processor.parse_price_currency(product.price) + if parsed_price: + product.price = parsed_price + product.currency = currency + + db_product = Product(**product.dict()) + db.add(db_product) + db.commit() + db.refresh(db_product) + + return db_product + + +@app.get("/products/{product_id}", response_model=ProductDetailResponse) +def get_product(product_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): + """Get product with stock information (Protected)""" + + product = db.query(Product).filter(Product.product_id == product_id).first() + if not product: + raise HTTPException(status_code=404, detail="Product not found") + + # Get stock information if GTIN exists + stock_info = None + if product.gtin: + stock_entries = db.query(Stock).filter(Stock.gtin == product.gtin).all() + if stock_entries: + total_quantity = sum(entry.quantity for entry in stock_entries) + locations = [ + StockLocationResponse(location=entry.location, quantity=entry.quantity) + for entry in stock_entries + ] + stock_info = StockSummaryResponse( + gtin=product.gtin, + total_quantity=total_quantity, + locations=locations + ) + + return ProductDetailResponse( + product=product, + stock_info=stock_info + ) + + +@app.put("/products/{product_id}", response_model=ProductResponse) +def update_product( + product_id: str, + product_update: ProductUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Update product with validation (Protected)""" + + product = db.query(Product).filter(Product.product_id == product_id).first() + if not product: + raise HTTPException(status_code=404, detail="Product not found") + + # Update fields + update_data = product_update.dict(exclude_unset=True) + + # Validate GTIN if being updated + if "gtin" in update_data and update_data["gtin"]: + normalized_gtin = gtin_processor.normalize(update_data["gtin"]) + if not normalized_gtin: + raise HTTPException(status_code=400, detail="Invalid GTIN format") + update_data["gtin"] = normalized_gtin + + # Process price if being updated + if "price" in update_data and update_data["price"]: + parsed_price, currency = price_processor.parse_price_currency(update_data["price"]) + if parsed_price: + update_data["price"] = parsed_price + update_data["currency"] = currency + + for key, value in update_data.items(): + setattr(product, key, value) + + product.updated_at = datetime.utcnow() + db.commit() + db.refresh(product) + + return product + + +@app.delete("/products/{product_id}") +def delete_product( + product_id: str, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Delete product and associated stock (Protected)""" + + product = db.query(Product).filter(Product.product_id == product_id).first() + if not product: + raise HTTPException(status_code=404, detail="Product not found") + + # Delete associated stock entries if GTIN exists + if product.gtin: + db.query(Stock).filter(Stock.gtin == product.gtin).delete() + + db.delete(product) + db.commit() + + return {"message": "Product and associated stock deleted successfully"} + + +# Stock Management Routes (Protected) +@app.post("/stock", response_model=StockResponse) +def set_stock( + stock: StockCreate, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Set stock with GTIN validation (Protected)""" + + # Normalize and validate GTIN + normalized_gtin = gtin_processor.normalize(stock.gtin) + if not normalized_gtin: + raise HTTPException(status_code=400, detail="Invalid GTIN format") + + # Verify GTIN exists in products + product = db.query(Product).filter(Product.gtin == normalized_gtin).first() + if not product: + logger.warning(f"Setting stock for GTIN {normalized_gtin} without corresponding product") + + # Check existing stock + existing_stock = db.query(Stock).filter( + Stock.gtin == normalized_gtin, + Stock.location == stock.location.strip().upper() + ).first() + + if existing_stock: + existing_stock.quantity = stock.quantity + existing_stock.updated_at = datetime.utcnow() + db.commit() + db.refresh(existing_stock) + return existing_stock + else: + new_stock = Stock( + gtin=normalized_gtin, + location=stock.location.strip().upper(), + quantity=stock.quantity + ) + db.add(new_stock) + db.commit() + db.refresh(new_stock) + return new_stock + + +@app.get("/stock/{gtin}", response_model=StockSummaryResponse) +def get_stock_by_gtin(gtin: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): + """Get stock summary with product validation (Protected)""" + + normalized_gtin = gtin_processor.normalize(gtin) + if not normalized_gtin: + raise HTTPException(status_code=400, detail="Invalid GTIN format") + + stock_entries = db.query(Stock).filter(Stock.gtin == normalized_gtin).all() + if not stock_entries: + raise HTTPException(status_code=404, detail=f"No stock found for GTIN: {gtin}") + + total_quantity = sum(entry.quantity for entry in stock_entries) + locations = [ + StockLocationResponse(location=entry.location, quantity=entry.quantity) + for entry in stock_entries + ] + + # Get product info + product = db.query(Product).filter(Product.gtin == normalized_gtin).first() + + return StockSummaryResponse( + gtin=normalized_gtin, + total_quantity=total_quantity, + locations=locations, + product_title=product.title if product else None + ) + + +@app.get("/stats", response_model=StatsResponse) +def get_stats(db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): + """Get comprehensive statistics (Protected)""" + + # Use more efficient queries with proper indexes + total_products = db.query(Product).count() + + unique_brands = db.query(Product.brand).filter( + Product.brand.isnot(None), + Product.brand != "" + ).distinct().count() + + unique_categories = db.query(Product.google_product_category).filter( + Product.google_product_category.isnot(None), + Product.google_product_category != "" + ).distinct().count() + + # Additional stock statistics + total_stock_entries = db.query(Stock).count() + total_inventory = db.query(Stock.quantity).scalar() or 0 + + return StatsResponse( + total_products=total_products, + unique_brands=unique_brands, + unique_categories=unique_categories, + total_stock_entries=total_stock_entries, + total_inventory_quantity=total_inventory + ) + + +# Export with streaming for large datasets (Protected) +@app.get("/export-csv") +async def export_csv( + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """Export products as CSV with streaming (Protected)""" + + def generate_csv(): + # Stream CSV generation for memory efficiency + yield "product_id,title,description,link,image_link,availability,price,currency,brand,gtin\n" + + batch_size = 1000 + offset = 0 + + while True: + products = db.query(Product).offset(offset).limit(batch_size).all() + if not products: + break + + for product in products: + # Create CSV row + row = f'"{product.product_id}","{product.title or ""}","{product.description or ""}","{product.link or ""}","{product.image_link or ""}","{product.availability or ""}","{product.price or ""}","{product.currency or ""}","{product.brand or ""}","{product.gtin or ""}"\n' + yield row + + offset += batch_size + + return StreamingResponse( + generate_csv(), + media_type="text/csv", + headers={"Content-Disposition": "attachment; filename=products_export.csv"} + ) + + +# Admin-only routes +@app.get("/admin/users", response_model=List[UserResponse]) +def get_all_users( + skip: int = Query(0, ge=0), + limit: int = Query(100, ge=1, le=1000), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user) +): + """Get all users (Admin only)""" + users = db.query(User).offset(skip).limit(limit).all() + return [UserResponse.model_validate(user) for user in users] + + +@app.put("/admin/users/{user_id}/status") +def toggle_user_status( + user_id: int, + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user) +): + """Toggle user active status (Admin only)""" + user = db.query(User).filter(User.id == user_id).first() + if not user: + raise HTTPException(status_code=404, detail="User not found") + + if user.id == current_admin.id: + raise HTTPException(status_code=400, detail="Cannot deactivate your own account") + + user.is_active = not user.is_active + user.updated_at = datetime.utcnow() + db.commit() + db.refresh(user) + + status = "activated" if user.is_active else "deactivated" + return {"message": f"User {user.username} has been {status}"} + + +if __name__ == "__main__": + import uvicorn + + uvicorn.run( + "main:app", + host="0.0.0.0", + port=8000, + reload=True, + log_level="info" + ) diff --git a/middleware/auth.py b/middleware/auth.py new file mode 100644 index 00000000..10a8a6fd --- /dev/null +++ b/middleware/auth.py @@ -0,0 +1,172 @@ +# middleware/auth.py +from fastapi import HTTPException, Depends +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from passlib.context import CryptContext +from jose import jwt, JWTError +from datetime import datetime, timedelta +from typing import Dict, Any, Optional +from sqlalchemy.orm import Session +from models.database_models import User +import os +import logging + +logger = logging.getLogger(__name__) + +# Password context for bcrypt hashing +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +# Security scheme +security = HTTPBearer() + + +class AuthManager: + """JWT-based authentication manager with bcrypt password hashing""" + + def __init__(self): + self.secret_key = os.getenv("JWT_SECRET_KEY", "your-secret-key-change-in-production-please") + self.algorithm = "HS256" + self.token_expire_minutes = int(os.getenv("JWT_EXPIRE_MINUTES", "30")) + + def hash_password(self, password: str) -> str: + """Hash password using bcrypt""" + return pwd_context.hash(password) + + def verify_password(self, plain_password: str, hashed_password: str) -> bool: + """Verify password against hash""" + return pwd_context.verify(plain_password, hashed_password) + + def authenticate_user(self, db: Session, username: str, password: str) -> Optional[User]: + """Authenticate user and return user object if valid""" + user = db.query(User).filter( + (User.username == username) | (User.email == username) + ).first() + + if not user: + return None + + if not user.is_active: + return None + + if not self.verify_password(password, user.hashed_password): + return None + + # Update last login + user.last_login = datetime.utcnow() + db.commit() + db.refresh(user) + + return user + + def create_access_token(self, user: User) -> Dict[str, Any]: + """Create JWT access token for user""" + expires_delta = timedelta(minutes=self.token_expire_minutes) + expire = datetime.utcnow() + expires_delta + + payload = { + "sub": str(user.id), + "username": user.username, + "email": user.email, + "role": user.role, + "exp": expire, + "iat": datetime.utcnow() + } + + token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm) + + return { + "access_token": token, + "token_type": "bearer", + "expires_in": self.token_expire_minutes * 60 # Return in seconds + } + + def verify_token(self, token: str) -> Dict[str, Any]: + """Verify JWT token and return user data""" + try: + payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm]) + + # Check if token has expired + exp = payload.get("exp") + if exp is None: + raise HTTPException(status_code=401, detail="Token missing expiration") + + if datetime.utcnow() > datetime.fromtimestamp(exp): + raise HTTPException(status_code=401, detail="Token has expired") + + # Extract user data + user_id = payload.get("sub") + if user_id is None: + raise HTTPException(status_code=401, detail="Token missing user identifier") + + return { + "user_id": int(user_id), + "username": payload.get("username"), + "email": payload.get("email"), + "role": payload.get("role", "user") + } + + except jwt.ExpiredSignatureError: + raise HTTPException(status_code=401, detail="Token has expired") + except jwt.JWTError as e: + logger.error(f"JWT decode error: {e}") + raise HTTPException(status_code=401, detail="Could not validate credentials") + except Exception as e: + logger.error(f"Token verification error: {e}") + raise HTTPException(status_code=401, detail="Authentication failed") + + def get_current_user(self, db: Session, credentials: HTTPAuthorizationCredentials = Depends(security)) -> User: + """Get current authenticated user from database""" + user_data = self.verify_token(credentials.credentials) + + user = db.query(User).filter(User.id == user_data["user_id"]).first() + if not user: + raise HTTPException(status_code=401, detail="User not found") + + if not user.is_active: + raise HTTPException(status_code=401, detail="User account is inactive") + + return user + + def require_role(self, required_role: str): + """Decorator to require specific role""" + + def decorator(func): + def wrapper(current_user: User, *args, **kwargs): + if current_user.role != required_role: + raise HTTPException( + status_code=403, + detail=f"Required role '{required_role}' not found. Current role: '{current_user.role}'" + ) + return func(current_user, *args, **kwargs) + + return wrapper + + return decorator + + def require_admin(self, current_user: User): + """Require admin role""" + if current_user.role != "admin": + raise HTTPException( + status_code=403, + detail="Admin privileges required" + ) + return current_user + + def create_default_admin_user(self, db: Session): + """Create default admin user if it doesn't exist""" + admin_user = db.query(User).filter(User.username == "admin").first() + + if not admin_user: + hashed_password = self.hash_password("admin123") + admin_user = User( + email="admin@example.com", + username="admin", + hashed_password=hashed_password, + role="admin", + is_active=True + ) + db.add(admin_user) + db.commit() + db.refresh(admin_user) + logger.info("Default admin user created: username='admin', password='admin123'") + + return admin_user diff --git a/middleware/error_handler.py b/middleware/error_handler.py new file mode 100644 index 00000000..f4d3f0e4 --- /dev/null +++ b/middleware/error_handler.py @@ -0,0 +1,55 @@ +# middleware/error_handler.py +from fastapi import Request, HTTPException +from fastapi.responses import JSONResponse +from fastapi.exceptions import RequestValidationError +from starlette.exceptions import HTTPException as StarletteHTTPException +import logging + +logger = logging.getLogger(__name__) + +async def custom_http_exception_handler(request: Request, exc: HTTPException): + """Custom HTTP exception handler""" + logger.error(f"HTTP {exc.status_code}: {exc.detail} - {request.method} {request.url}") + + return JSONResponse( + status_code=exc.status_code, + content={ + "error": { + "code": exc.status_code, + "message": exc.detail, + "type": "http_exception" + } + } + ) + +async def validation_exception_handler(request: Request, exc: RequestValidationError): + """Handle Pydantic validation errors""" + logger.error(f"Validation error: {exc.errors()} - {request.method} {request.url}") + + return JSONResponse( + status_code=422, + content={ + "error": { + "code": 422, + "message": "Validation error", + "type": "validation_error", + "details": exc.errors() + } + } + ) + +async def general_exception_handler(request: Request, exc: Exception): + """Handle unexpected exceptions""" + logger.error(f"Unexpected error: {str(exc)} - {request.method} {request.url}", exc_info=True) + + return JSONResponse( + status_code=500, + content={ + "error": { + "code": 500, + "message": "Internal server error", + "type": "server_error" + } + } + ) + diff --git a/middleware/logging_middleware.py b/middleware/logging_middleware.py new file mode 100644 index 00000000..f0f71829 --- /dev/null +++ b/middleware/logging_middleware.py @@ -0,0 +1,46 @@ +# middleware/logging_middleware.py +import logging +import time +from fastapi import Request, Response +from starlette.middleware.base import BaseHTTPMiddleware +from typing import Callable + +logger = logging.getLogger(__name__) + + +class LoggingMiddleware(BaseHTTPMiddleware): + """Middleware for request/response logging and performance monitoring""" + + async def dispatch(self, request: Request, call_next: Callable) -> Response: + # Start timing + start_time = time.time() + + # Log request + client_ip = request.client.host if request.client else "unknown" + logger.info(f"Request: {request.method} {request.url.path} from {client_ip}") + + # Process request + try: + response = await call_next(request) + + # Calculate duration + duration = time.time() - start_time + + # Log response + logger.info( + f"Response: {response.status_code} for {request.method} {request.url.path} " + f"({duration:.3f}s)" + ) + + # Add performance headers + response.headers["X-Process-Time"] = str(duration) + + return response + + except Exception as e: + duration = time.time() - start_time + logger.error( + f"Error: {str(e)} for {request.method} {request.url.path} " + f"({duration:.3f}s)" + ) + raise \ No newline at end of file diff --git a/middleware/rate_limiter.py b/middleware/rate_limiter.py new file mode 100644 index 00000000..62c64c9a --- /dev/null +++ b/middleware/rate_limiter.py @@ -0,0 +1,82 @@ +# middleware/rate_limiter.py +from typing import Dict, Tuple +from datetime import datetime, timedelta +import logging +from collections import defaultdict, deque + +logger = logging.getLogger(__name__) + + +class RateLimiter: + """In-memory rate limiter using sliding window""" + + def __init__(self): + # Dictionary to store request timestamps for each client + self.clients: Dict[str, deque] = defaultdict(lambda: deque()) + self.cleanup_interval = 3600 # Clean up old entries every hour + self.last_cleanup = datetime.utcnow() + + def allow_request(self, client_id: str, max_requests: int, window_seconds: int) -> bool: + """ + Check if client is allowed to make a request + Uses sliding window algorithm + """ + now = datetime.utcnow() + window_start = now - timedelta(seconds=window_seconds) + + # Clean up old entries periodically + if (now - self.last_cleanup).seconds > self.cleanup_interval: + self._cleanup_old_entries() + self.last_cleanup = now + + # Get client's request history + client_requests = self.clients[client_id] + + # Remove requests outside the window + while client_requests and client_requests[0] < window_start: + client_requests.popleft() + + # Check if under rate limit + if len(client_requests) < max_requests: + client_requests.append(now) + return True + + logger.warning(f"Rate limit exceeded for client {client_id}: {len(client_requests)}/{max_requests}") + return False + + def _cleanup_old_entries(self): + """Clean up old entries to prevent memory leaks""" + cutoff_time = datetime.utcnow() - timedelta(hours=24) + + clients_to_remove = [] + for client_id, requests in self.clients.items(): + # Remove old requests + while requests and requests[0] < cutoff_time: + requests.popleft() + + # Mark empty clients for removal + if not requests: + clients_to_remove.append(client_id) + + # Remove empty clients + for client_id in clients_to_remove: + del self.clients[client_id] + + logger.info(f"Rate limiter cleanup completed. Removed {len(clients_to_remove)} inactive clients") + + def get_client_stats(self, client_id: str) -> Dict[str, int]: + """Get statistics for a specific client""" + client_requests = self.clients.get(client_id, deque()) + + now = datetime.utcnow() + hour_ago = now - timedelta(hours=1) + day_ago = now - timedelta(days=1) + + requests_last_hour = sum(1 for req_time in client_requests if req_time > hour_ago) + requests_last_day = sum(1 for req_time in client_requests if req_time > day_ago) + + return { + "requests_last_hour": requests_last_hour, + "requests_last_day": requests_last_day, + "total_tracked_requests": len(client_requests) + } \ No newline at end of file diff --git a/models/api_models.py b/models/api_models.py new file mode 100644 index 00000000..18da67e4 --- /dev/null +++ b/models/api_models.py @@ -0,0 +1,209 @@ +# models/api_models.py +from pydantic import BaseModel, Field, field_validator, EmailStr +from typing import Optional, List +from datetime import datetime + + +# User Authentication Models +class UserRegister(BaseModel): + email: EmailStr = Field(..., description="Valid email address") + username: str = Field(..., min_length=3, max_length=50, description="Username (3-50 characters)") + password: str = Field(..., min_length=6, description="Password (minimum 6 characters)") + + @field_validator('username') + @classmethod + def validate_username(cls, v): + if not v.isalnum(): + raise ValueError('Username must contain only alphanumeric characters') + return v.lower().strip() + + @field_validator('password') + @classmethod + def validate_password(cls, v): + if len(v) < 6: + raise ValueError('Password must be at least 6 characters long') + return v + + +class UserLogin(BaseModel): + username: str = Field(..., description="Username") + password: str = Field(..., description="Password") + + @field_validator('username') + @classmethod + def validate_username(cls, v): + return v.strip() + + +class UserResponse(BaseModel): + id: int + email: str + username: str + role: str + is_active: bool + last_login: Optional[datetime] = None + created_at: datetime + updated_at: datetime + + model_config = {"from_attributes": True} + + +class LoginResponse(BaseModel): + access_token: str + token_type: str = "bearer" + expires_in: int + user: UserResponse + + +# Base Product Models +class ProductBase(BaseModel): + product_id: Optional[str] = None + title: Optional[str] = None + description: Optional[str] = None + link: Optional[str] = None + image_link: Optional[str] = None + availability: Optional[str] = None + price: Optional[str] = None + brand: Optional[str] = None + gtin: Optional[str] = None + mpn: Optional[str] = None + condition: Optional[str] = None + adult: Optional[str] = None + multipack: Optional[int] = None + is_bundle: Optional[str] = None + age_group: Optional[str] = None + color: Optional[str] = None + gender: Optional[str] = None + material: Optional[str] = None + pattern: Optional[str] = None + size: Optional[str] = None + size_type: Optional[str] = None + size_system: Optional[str] = None + item_group_id: Optional[str] = None + google_product_category: Optional[str] = None + product_type: Optional[str] = None + custom_label_0: Optional[str] = None + custom_label_1: Optional[str] = None + custom_label_2: Optional[str] = None + custom_label_3: Optional[str] = None + custom_label_4: Optional[str] = None + additional_image_link: Optional[str] = None + sale_price: Optional[str] = None + unit_pricing_measure: Optional[str] = None + unit_pricing_base_measure: Optional[str] = None + identifier_exists: Optional[str] = None + shipping: Optional[str] = None + currency: Optional[str] = None + + +class ProductCreate(ProductBase): + product_id: str = Field(..., min_length=1, description="Product ID is required") + title: str = Field(..., min_length=1, description="Title is required") + + @field_validator('product_id', 'title') + @classmethod + def validate_required_fields(cls, v): + if not v or not v.strip(): + raise ValueError('Field cannot be empty') + return v.strip() + + +class ProductUpdate(ProductBase): + pass + + +class ProductResponse(ProductBase): + id: int + created_at: datetime + updated_at: datetime + + model_config = {"from_attributes": True} + + +# Stock Models +class StockBase(BaseModel): + gtin: str = Field(..., min_length=1, description="GTIN is required") + location: str = Field(..., min_length=1, description="Location is required") + + +class StockCreate(StockBase): + quantity: int = Field(ge=0, description="Quantity must be non-negative") + + +class StockAdd(StockBase): + quantity: int = Field(gt=0, description="Quantity to add must be positive") + + +class StockUpdate(BaseModel): + quantity: int = Field(ge=0, description="Quantity must be non-negative") + + +class StockResponse(BaseModel): + id: int + gtin: str + location: str + quantity: int + created_at: datetime + updated_at: datetime + + model_config = {"from_attributes": True} + + +class StockLocationResponse(BaseModel): + location: str + quantity: int + + +class StockSummaryResponse(BaseModel): + gtin: str + total_quantity: int + locations: List[StockLocationResponse] + product_title: Optional[str] = None + + +# Import Models +class CSVImportRequest(BaseModel): + url: str = Field(..., description="URL to CSV file") + batch_size: Optional[int] = Field(1000, gt=0, le=10000, description="Batch size for processing") + + @field_validator('url') + @classmethod + def validate_url(cls, v): + if not v.startswith(('http://', 'https://')): + raise ValueError('URL must start with http:// or https://') + return v + + +class ImportJobResponse(BaseModel): + job_id: int + status: str + message: Optional[str] = None + imported: Optional[int] = 0 + updated: Optional[int] = 0 + total_processed: Optional[int] = 0 + error_count: Optional[int] = 0 + error_message: Optional[str] = None + created_at: Optional[datetime] = None + started_at: Optional[datetime] = None + completed_at: Optional[datetime] = None + + +# Response Models +class ProductListResponse(BaseModel): + products: List[ProductResponse] + total: int + skip: int + limit: int + + +class ProductDetailResponse(BaseModel): + product: ProductResponse + stock_info: Optional[StockSummaryResponse] = None + + +class StatsResponse(BaseModel): + total_products: int + unique_brands: int + unique_categories: int + total_stock_entries: int = 0 + total_inventory_quantity: int = 0 diff --git a/models/database_models.py b/models/database_models.py new file mode 100644 index 00000000..1801f52e --- /dev/null +++ b/models/database_models.py @@ -0,0 +1,120 @@ +# models/database_models.py +from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Index, UniqueConstraint, Boolean +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship +from datetime import datetime + +Base = declarative_base() + + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + email = Column(String, unique=True, index=True, nullable=False) + username = Column(String, unique=True, index=True, nullable=False) + hashed_password = Column(String, nullable=False) + role = Column(String, nullable=False, default="user") # 'admin' or 'user' + is_active = Column(Boolean, default=True, nullable=False) + last_login = Column(DateTime, nullable=True) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) + + def __repr__(self): + return f"" + + +class Product(Base): + __tablename__ = "products" + + id = Column(Integer, primary_key=True, index=True) + product_id = Column(String, unique=True, index=True, nullable=False) + title = Column(String, nullable=False) + description = Column(String) + link = Column(String) + image_link = Column(String) + availability = Column(String, index=True) # Index for filtering + price = Column(String) + brand = Column(String, index=True) # Index for filtering + gtin = Column(String, index=True) # Index for stock lookups + mpn = Column(String) + condition = Column(String) + adult = Column(String) + multipack = Column(Integer) + is_bundle = Column(String) + age_group = Column(String) + color = Column(String) + gender = Column(String) + material = Column(String) + pattern = Column(String) + size = Column(String) + size_type = Column(String) + size_system = Column(String) + item_group_id = Column(String) + google_product_category = Column(String, index=True) # Index for filtering + product_type = Column(String) + custom_label_0 = Column(String) + custom_label_1 = Column(String) + custom_label_2 = Column(String) + custom_label_3 = Column(String) + custom_label_4 = Column(String) + additional_image_link = Column(String) + sale_price = Column(String) + unit_pricing_measure = Column(String) + unit_pricing_base_measure = Column(String) + identifier_exists = Column(String) + shipping = Column(String) + currency = Column(String) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) + + # Relationship to stock (one-to-many via GTIN) + stock_entries = relationship("Stock", foreign_keys="Stock.gtin", primaryjoin="Product.gtin == Stock.gtin", + viewonly=True) + + def __repr__(self): + return f"" + + +class Stock(Base): + __tablename__ = "stock" + + id = Column(Integer, primary_key=True, index=True) + gtin = Column(String, index=True, nullable=False) # Foreign key relationship would be ideal + location = Column(String, nullable=False, index=True) + quantity = Column(Integer, nullable=False, default=0) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) + + # Composite unique constraint to prevent duplicate GTIN-location combinations + __table_args__ = ( + UniqueConstraint('gtin', 'location', name='uq_stock_gtin_location'), + Index('idx_stock_gtin_location', 'gtin', 'location'), # Composite index for efficient queries + ) + + def __repr__(self): + return f"" + + +class ImportJob(Base): + __tablename__ = "import_jobs" + + id = Column(Integer, primary_key=True, index=True) + status = Column(String, nullable=False, + default="pending") # pending, processing, completed, failed, completed_with_errors + source_url = Column(String, nullable=False) + user_id = Column(Integer, ForeignKey('users.id')) # Foreign key to users table + imported_count = Column(Integer, default=0) + updated_count = Column(Integer, default=0) + error_count = Column(Integer, default=0) + total_processed = Column(Integer, default=0) + error_message = Column(String) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + started_at = Column(DateTime) + completed_at = Column(DateTime) + + # Relationship to user + user = relationship("User", foreign_keys=[user_id]) + + def __repr__(self): + return f"" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..93513fee --- /dev/null +++ b/requirements.txt @@ -0,0 +1,30 @@ +# requirements.txt +# Core FastAPI and web framework +fastapi==0.104.1 +uvicorn[standard]==0.24.0 +pydantic==2.5.0 +pydantic-settings==2.1.0 # Required for BaseSettings +pydantic[email]==2.5.0 + +# Database +sqlalchemy==2.0.23 +psycopg2-binary==2.9.7 # PostgreSQL adapter +alembic==1.12.1 # For database migrations + +# Authentication and Security +python-jose[cryptography]==3.3.0 # JWT handling +passlib[bcrypt]==1.7.4 # Password hashing with bcrypt +bcrypt==4.0.1 # Explicit bcrypt version for compatibility +python-multipart==0.0.6 # Form data parsing + +# Data processing +pandas==2.1.3 +requests==2.31.0 + +# Environment and configuration +python-dotenv==1.0.0 + +# Development and testing (optional) +pytest==7.4.3 +pytest-asyncio==0.21.1 +httpx==0.25.2 # For testing FastAPI endpoints \ No newline at end of file diff --git a/scripts/setup_dev.py b/scripts/setup_dev.py new file mode 100644 index 00000000..6b416df0 --- /dev/null +++ b/scripts/setup_dev.py @@ -0,0 +1,168 @@ +# scripts/setup_dev.py +# !/usr/bin/env python3 +"""Development environment setup script""" + +import os +import sys +import subprocess +from pathlib import Path + + +def run_command(command, description): + """Run a shell command and handle errors""" + print(f"Running: {description}") + try: + subprocess.run(command, shell=True, check=True) + print(f"✅ {description} completed successfully") + except subprocess.CalledProcessError as e: + print(f"❌ {description} failed: {e}") + return False + return True + + +def setup_alembic(): + """Set up Alembic for database migrations""" + alembic_dir = Path("alembic") + + # Check if alembic directory exists and has necessary files + if not alembic_dir.exists() or not (alembic_dir / "script.py.mako").exists(): + print("📝 Initializing Alembic...") + if alembic_dir.exists(): + # Remove incomplete alembic directory + import shutil + shutil.rmtree(alembic_dir) + + if not run_command("alembic init alembic", "Initializing Alembic"): + return False + + # Update alembic/env.py with proper configuration + env_py_content = '''from logging.config import fileConfig +from sqlalchemy import engine_from_config, pool +from alembic import context +import os +import sys + +# Add your project directory to the Python path +sys.path.append(os.path.dirname(os.path.dirname(__file__))) + +from models.database_models import Base +from config.settings import settings + +# Alembic Config object +config = context.config + +# Override sqlalchemy.url with our settings +config.set_main_option("sqlalchemy.url", settings.database_url) + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +target_metadata = Base.metadata + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode.""" + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() +''' + + env_py_path = alembic_dir / "env.py" + env_py_path.write_text(env_py_content) + print("✅ Updated alembic/env.py with project configuration") + + return True + + +def setup_environment(): + """Set up the development environment""" + print("🚀 Setting up ecommerce API development environment...") + + # Check Python version + if sys.version_info < (3, 8): + print("❌ Python 3.8+ is required") + return False + + # Create .env file if it doesn't exist + env_file = Path(".env") + env_example = Path(".env.example") + + if not env_file.exists() and env_example.exists(): + print("📝 Creating .env file from .env.example...") + env_file.write_text(env_example.read_text()) + elif not env_file.exists(): + print("📝 Creating default .env file...") + default_env = """DATABASE_URL=sqlite:///./ecommerce.db +JWT_SECRET_KEY=development-secret-key-change-in-production +JWT_EXPIRE_HOURS=24 +API_HOST=0.0.0.0 +API_PORT=8000 +DEBUG=True +RATE_LIMIT_ENABLED=True +DEFAULT_RATE_LIMIT=100 +DEFAULT_WINDOW_SECONDS=3600 +LOG_LEVEL=INFO +""" + env_file.write_text(default_env) + + # Install dependencies + if not run_command("pip install -r requirements.txt", "Installing dependencies"): + return False + + # Set up Alembic + if not setup_alembic(): + print("⚠️ Alembic setup failed. You'll need to set up database migrations manually.") + return False + + # Create initial migration + if not run_command("alembic revision --autogenerate -m \"Initial migration\"", "Creating initial migration"): + print("⚠️ Initial migration creation failed. Check your database models.") + + # Apply migrations + if not run_command("alembic upgrade head", "Setting up database"): + print("⚠️ Database setup failed. Make sure your DATABASE_URL is correct in .env") + + # Run tests + if not run_command("pytest", "Running tests"): + print("⚠️ Some tests failed. Check the output above.") + + print("\n🎉 Development environment setup complete!") + print("To start the development server, run:") + print(" uvicorn main:app --reload") + print("\nDatabase commands:") + print(" alembic revision --autogenerate -m \"Description\" # Create migration") + print(" alembic upgrade head # Apply migrations") + print(" alembic current # Check status") + + return True + + +if __name__ == "__main__": + setup_environment() diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..6cd87525 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,61 @@ +# tests/test_utils.py +import pytest +from utils.data_processing import GTINProcessor, PriceProcessor + + +class TestGTINProcessor: + def setup_method(self): + self.processor = GTINProcessor() + + def test_normalize_valid_gtin(self): + # Test EAN-13 + assert self.processor.normalize("1234567890123") == "1234567890123" + + # Test UPC-A + assert self.processor.normalize("123456789012") == "123456789012" + + # Test with decimal + assert self.processor.normalize("123456789012.0") == "123456789012" + + def test_normalize_invalid_gtin(self): + assert self.processor.normalize("") is None + assert self.processor.normalize(None) is None + assert self.processor.normalize("abc") is None + assert self.processor.normalize("123") == "000000000123" # Padded to 12 digits + + def test_validate_gtin(self): + assert self.processor.validate("1234567890123") is True + assert self.processor.validate("123456789012") is True + assert self.processor.validate("12345678") is True + assert self.processor.validate("123") is False + assert self.processor.validate("") is False + + +class TestPriceProcessor: + def setup_method(self): + self.processor = PriceProcessor() + + def test_parse_price_currency(self): + # Test EUR with symbol + price, currency = self.processor.parse_price_currency("8.26 EUR") + assert price == "8.26" + assert currency == "EUR" + + # Test USD with symbol + price, currency = self.processor.parse_price_currency("$12.50") + assert price == "12.50" + assert currency == "USD" + + # Test with comma decimal separator + price, currency = self.processor.parse_price_currency("8,26 €") + assert price == "8.26" + assert currency == "EUR" + + def test_parse_invalid_price(self): + price, currency = self.processor.parse_price_currency("") + assert price is None + assert currency is None + + price, currency = self.processor.parse_price_currency(None) + assert price is None + assert currency is None \ No newline at end of file diff --git a/updated_readme.md b/updated_readme.md new file mode 100644 index 00000000..f945aada --- /dev/null +++ b/updated_readme.md @@ -0,0 +1,569 @@ +# Ecommerce Backend API v2.1 + +A robust, production-ready FastAPI backend for ecommerce product catalog and inventory management with JWT authentication and advanced CSV import capabilities. + +## Key Features + +### Security & Authentication +- **JWT Authentication**: Secure token-based authentication with configurable expiration (30 minutes default) +- **User Management**: Registration, login, role-based access control (Admin/User roles) +- **Password Security**: Bcrypt hashing for secure password storage +- **Protected Endpoints**: All product management operations require authentication +- **Default Admin Account**: Auto-created admin user for immediate system access + +### Architecture Improvements +- **Modular Design**: Separated concerns into utility modules, middleware, and models +- **Database Optimization**: Added proper indexing strategy and foreign key relationships +- **Connection Pooling**: PostgreSQL support with connection pooling for production scalability +- **Background Processing**: Asynchronous CSV import with job tracking + +### Performance Optimizations +- **Batch Processing**: CSV imports processed in configurable batches +- **Database Indexes**: Strategic indexing for common query patterns +- **Streaming Export**: Memory-efficient CSV export for large datasets +- **Rate Limiting**: Sliding window rate limiter to prevent API abuse + +### Data Processing +- **Robust GTIN Handling**: Centralized GTIN normalization and validation +- **Multi-currency Support**: Advanced price parsing with currency extraction +- **International Content**: Multi-encoding CSV support for global data + +## Project Structure + +``` +ecommerce_api/ +├── main.py # FastAPI application entry point with auth +├── models/ +│ ├── database_models.py # SQLAlchemy ORM models (User, Product, Stock, ImportJob) +│ └── api_models.py # Pydantic API models with auth models +├── utils/ +│ ├── data_processing.py # GTIN and price processing utilities +│ ├── csv_processor.py # CSV import/export handling +│ └── database.py # Database configuration +├── middleware/ +│ ├── auth.py # JWT authentication with bcrypt +│ ├── rate_limiter.py # Rate limiting implementation +│ ├── error_handler.py # Centralized error handling +│ └── logging_middleware.py # Request/response logging +├── tests/ +│ └── test_auth.py # Authentication tests +├── requirements.txt # Python dependencies with auth packages +└── README.md # This file +``` + +## Quick Start + +### 1. Installation + +```bash +# Clone the repository +git clone +cd ecommerce-api + +# Set up virtual environment +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt +``` + +### 2. Environment Configuration + +Create a `.env` file in the project root: + +```env +# Database +DATABASE_URL=postgresql://user:password@localhost:5432/ecommerce_db +# For SQLite (development): DATABASE_URL=sqlite:///./ecommerce.db + +# JWT Configuration +JWT_SECRET_KEY=your-super-secret-key-change-in-production-immediately +JWT_EXPIRE_MINUTES=30 + +# Server Configuration +API_HOST=0.0.0.0 +API_PORT=8000 +DEBUG=False +``` + +**Important Security Note**: Always change the `JWT_SECRET_KEY` in production! + +### 3. Database Setup + +**For SQLite (Development):** +```bash +# Run the application - it will create tables automatically +python main.py +``` + +**For PostgreSQL (Production):** +```bash +# Create PostgreSQL database +createdb ecommerce_db + +# Run the application - it will create tables and indexes automatically +python main.py +``` + +### 4. Start the Server + +```bash +# Development server +uvicorn main:app --reload --host 0.0.0.0 --port 8000 + +# Production server +uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 +``` + +The API will be available at `http://localhost:8000` + +### 5. Default Admin Access + +The system automatically creates a default admin user: +- **Username**: `admin` +- **Password**: `admin123` +- **Email**: `admin@example.com` + +**Security Warning**: Change the admin password immediately in production! + +## Authentication Flow + +### 1. Register a New User + +```bash +curl -X POST "http://localhost:8000/register" \ + -H "Content-Type: application/json" \ + -d '{ + "email": "user@example.com", + "username": "newuser", + "password": "securepassword123" + }' +``` + +### 2. Login and Get JWT Token + +```bash +curl -X POST "http://localhost:8000/login" \ + -H "Content-Type: application/json" \ + -d '{ + "username": "admin", + "password": "admin123" + }' +``` + +Response: +```json +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "token_type": "bearer", + "expires_in": 1800, + "user": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "role": "admin", + "is_active": true + } +} +``` + +### 3. Use Token for Protected Endpoints + +```bash +curl -X GET "http://localhost:8000/products" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN_HERE" +``` + +## API Endpoints + +### Public Endpoints +- `GET /` - API information +- `GET /health` - Health check +- `POST /register` - Register new user +- `POST /login` - Login and get JWT token + +### Protected Endpoints (Require Authentication) + +#### User Management +- `GET /me` - Get current user information + +#### Products (All users) +- `GET /products` - List products with filtering and search +- `POST /products` - Create new product +- `GET /products/{product_id}` - Get product with stock info +- `PUT /products/{product_id}` - Update product +- `DELETE /products/{product_id}` - Delete product and associated stock + +#### Stock Management (All users) +- `POST /stock` - Set exact stock quantity +- `GET /stock/{gtin}` - Get stock summary by GTIN + +#### CSV Operations (All users) +- `POST /import-csv` - Start background CSV import +- `GET /import-status/{job_id}` - Check import job status (own jobs only) +- `GET /export-csv` - Export products as CSV (streaming) + +#### Statistics (All users) +- `GET /stats` - System statistics + +#### Admin-Only Endpoints +- `GET /admin/users` - List all users +- `PUT /admin/users/{user_id}/status` - Activate/deactivate users + +## User Roles and Permissions + +### Regular Users +- Can register and login +- Access all product and stock management features +- Can import/export CSV files +- Can only view their own import jobs +- Cannot manage other users + +### Admin Users +- All regular user permissions +- Can view and manage all users +- Can view all import jobs from any user +- Can activate/deactivate user accounts + +## Security Features + +### Password Security +- Passwords hashed using bcrypt with salt +- Minimum password length: 6 characters +- No password storage in plaintext anywhere + +### JWT Token Security +- Tokens include expiration timestamp +- Tokens include user role and permissions +- Configurable expiration time (default: 30 minutes) +- Secure token validation on each request + +### Rate Limiting +- CSV imports: 10 requests per hour +- General API: 100 requests per hour per client +- Customizable per endpoint + +### Input Validation +- All inputs validated using Pydantic models +- Email format validation for registration +- Username alphanumeric validation +- GTIN format validation and normalization + +## Advanced Features + +### Background CSV Import + +Import large CSV files asynchronously: + +```python +import requests + +# Start import +response = requests.post( + 'http://localhost:8000/import-csv', + headers={'Authorization': 'Bearer YOUR_TOKEN'}, + json={ + 'url': 'https://example.com/products.csv', + 'batch_size': 1000 + } +) + +job_id = response.json()['job_id'] + +# Check status +status_response = requests.get( + f'http://localhost:8000/import-status/{job_id}', + headers={'Authorization': 'Bearer YOUR_TOKEN'} +) +print(status_response.json()) +``` + +### Product Search and Filtering + +```bash +# Search in title and description +GET /products?search=laptop + +# Filter by brand and category +GET /products?brand=Apple&category=Electronics + +# Combine filters with pagination +GET /products?brand=Samsung&availability=in%20stock&search=phone&skip=0&limit=50 +``` + +### Stock Management + +```bash +# Set stock for a specific location +curl -X POST "http://localhost:8000/stock" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "gtin": "1234567890123", + "location": "WAREHOUSE_A", + "quantity": 100 + }' + +# Get stock summary +curl -X GET "http://localhost:8000/stock/1234567890123" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +## Database Schema + +### Users Table +```sql +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR UNIQUE NOT NULL, + username VARCHAR UNIQUE NOT NULL, + hashed_password VARCHAR NOT NULL, + role VARCHAR DEFAULT 'user', + is_active BOOLEAN DEFAULT true, + last_login TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +``` + +### Products Table +- Full product catalog with Google Shopping compatibility +- Indexed fields: `gtin`, `brand`, `google_product_category`, `availability` +- Supports all Google Shopping feed attributes + +### Stock Table +- Location-based inventory tracking +- GTIN-based product linking +- Unique constraint on GTIN+location combinations + +### Import Jobs Table +- Track background import operations +- User ownership and access control +- Status monitoring and error handling + +## Development + +### Running Tests + +```bash +# Install test dependencies +pip install pytest pytest-asyncio httpx + +# Run all tests +pytest + +# Run with coverage +pytest --cov=. tests/ +``` + +### Development Server with Auto-reload + +```bash +uvicorn main:app --reload --host 0.0.0.0 --port 8000 +``` + +## Production Deployment + +### Security Checklist + +- [ ] Change default admin password immediately +- [ ] Set strong JWT_SECRET_KEY (32+ random characters) +- [ ] Configure JWT_EXPIRE_MINUTES appropriately +- [ ] Set up HTTPS/TLS termination +- [ ] Configure CORS for your frontend domains only +- [ ] Set up database connection limits and pooling +- [ ] Enable request logging and monitoring +- [ ] Configure rate limiting per your needs +- [ ] Set up user account monitoring and alerting +- [ ] Regular security audits of user accounts + +### Environment Variables for Production + +```env +# Security +JWT_SECRET_KEY=your-very-long-random-secret-key-at-least-32-characters +JWT_EXPIRE_MINUTES=30 + +# Database (use PostgreSQL in production) +DATABASE_URL=postgresql://user:password@db-host:5432/ecommerce_prod + +# Server +DEBUG=False +API_HOST=0.0.0.0 +API_PORT=8000 + +# Optional: External services +REDIS_URL=redis://redis-host:6379/0 +``` + +### Docker Deployment + +```yaml +# docker-compose.yml +version: '3.8' +services: + db: + image: postgres:15 + environment: + POSTGRES_DB: ecommerce + POSTGRES_USER: ecommerce_user + POSTGRES_PASSWORD: secure_password + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + + api: + build: . + environment: + DATABASE_URL: postgresql://ecommerce_user:secure_password@db:5432/ecommerce + JWT_SECRET_KEY: your-production-secret-key + JWT_EXPIRE_MINUTES: 30 + ports: + - "8000:8000" + depends_on: + - db + restart: unless-stopped + +volumes: + postgres_data: +``` + +### Nginx Configuration + +```nginx +server { + listen 80; + server_name your-api-domain.com; + + location / { + proxy_pass http://localhost:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +## Troubleshooting + +### Authentication Issues + +**Invalid Token Errors:** +- Check token expiration (default 30 minutes) +- Verify JWT_SECRET_KEY is consistent +- Ensure Bearer token format: `Authorization: Bearer ` + +**Login Failures:** +- Verify username/email and password +- Check if user account is active (`is_active: true`) +- Review user registration process + +**Permission Denied:** +- Confirm user role permissions +- Check endpoint access requirements +- Verify token contains correct role information + +### Database Issues + +**Connection Errors:** +- Verify DATABASE_URL format and credentials +- Check database server accessibility +- Monitor connection pool limits + +**Migration Issues:** +- Tables are created automatically on startup +- For schema changes, implement proper migrations +- Backup data before major updates + +### Common API Issues + +**CSV Import Failures:** +- Check file URL accessibility +- Verify CSV format and encoding +- Monitor import job status for detailed errors +- Ensure proper authentication token + +**Rate Limiting:** +- Default limits: 100 requests/hour, 10 CSV imports/hour +- Check rate limit headers in responses +- Implement proper retry logic with backoff + +### Logging and Monitoring + +Application logs include: +- Authentication events (login, failed attempts) +- API request/response times +- Import job progress and errors +- Rate limiting events +- Database query performance + +```bash +# View logs in development +python main.py # Logs to console + +# Docker logs +docker-compose logs -f api +``` + +## Migration from v2.0 + +If upgrading from v2.0 to v2.1: + +1. **Install new dependencies:** + ```bash + pip install -r requirements.txt + ``` + +2. **Update environment variables:** + ```bash + echo "JWT_SECRET_KEY=your-secret-key" >> .env + echo "JWT_EXPIRE_MINUTES=30" >> .env + ``` + +3. **Database migration:** + The application will automatically create the new `users` table and update the `import_jobs` table on startup. + +4. **Update client code:** + - Add authentication to all product management API calls + - Implement login flow in your frontend + - Handle JWT token refresh + +## Contributing + +1. Fork the repository +2. Create a feature branch: `git checkout -b feature-name` +3. Make changes with proper tests +4. Run security and quality checks +5. Update documentation if needed +6. Submit a pull request + +### Code Quality Standards + +- All endpoints must have proper authentication +- Password handling must use bcrypt +- JWT tokens must have expiration +- Input validation using Pydantic models +- Comprehensive error handling +- Unit tests for authentication logic + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Security + +For security issues, please email the maintainers directly instead of creating a public issue. + +## Support + +For issues and questions: +1. Check the troubleshooting section above +2. Review existing GitHub issues +3. Create a new issue with detailed information including: + - Authentication steps you've tried + - Error messages and logs + - API endpoint and request details + - Environment configuration (without secrets) \ No newline at end of file diff --git a/utils/csv_processor.py b/utils/csv_processor.py new file mode 100644 index 00000000..ca4cbf41 --- /dev/null +++ b/utils/csv_processor.py @@ -0,0 +1,253 @@ +# utils/csv_processor.py +import pandas as pd +import requests +from io import StringIO +from typing import Dict, Any, Optional +from sqlalchemy.orm import Session +from models.database_models import Product +from datetime import datetime +import logging + +logger = logging.getLogger(__name__) + + +class CSVProcessor: + """Handles CSV import with robust parsing and batching""" + + ENCODINGS = ['utf-8', 'latin-1', 'iso-8859-1', 'cp1252', 'utf-8-sig'] + + COLUMN_MAPPING = { + # Standard variations + 'id': 'product_id', + 'ID': 'product_id', + 'Product ID': 'product_id', + 'name': 'title', + 'Name': 'title', + 'product_name': 'title', + 'Product Name': 'title', + + # Google Shopping feed standard + 'g:id': 'product_id', + 'g:title': 'title', + 'g:description': 'description', + 'g:link': 'link', + 'g:image_link': 'image_link', + 'g:availability': 'availability', + 'g:price': 'price', + 'g:brand': 'brand', + 'g:gtin': 'gtin', + 'g:mpn': 'mpn', + 'g:condition': 'condition', + 'g:adult': 'adult', + 'g:multipack': 'multipack', + 'g:is_bundle': 'is_bundle', + 'g:age_group': 'age_group', + 'g:color': 'color', + 'g:gender': 'gender', + 'g:material': 'material', + 'g:pattern': 'pattern', + 'g:size': 'size', + 'g:size_type': 'size_type', + 'g:size_system': 'size_system', + 'g:item_group_id': 'item_group_id', + 'g:google_product_category': 'google_product_category', + 'g:product_type': 'product_type', + 'g:custom_label_0': 'custom_label_0', + 'g:custom_label_1': 'custom_label_1', + 'g:custom_label_2': 'custom_label_2', + 'g:custom_label_3': 'custom_label_3', + 'g:custom_label_4': 'custom_label_4', + + # Handle complex shipping column + 'shipping(country:price:max_handling_time:min_transit_time:max_transit_time)': 'shipping' + } + + def __init__(self): + from utils.data_processing import GTINProcessor, PriceProcessor + self.gtin_processor = GTINProcessor() + self.price_processor = PriceProcessor() + + def download_csv(self, url: str) -> str: + """Download and decode CSV with multiple encoding attempts""" + try: + response = requests.get(url, timeout=30) + response.raise_for_status() + + content = response.content + + # Try different encodings + for encoding in self.ENCODINGS: + try: + decoded_content = content.decode(encoding) + logger.info(f"Successfully decoded CSV with encoding: {encoding}") + return decoded_content + except UnicodeDecodeError: + continue + + # Fallback with error ignoring + decoded_content = content.decode('utf-8', errors='ignore') + logger.warning("Used UTF-8 with error ignoring for CSV decoding") + return decoded_content + + except requests.RequestException as e: + logger.error(f"Error downloading CSV: {e}") + raise + + def parse_csv(self, csv_content: str) -> pd.DataFrame: + """Parse CSV with multiple separator attempts""" + parsing_configs = [ + # Try auto-detection first + {'sep': None, 'engine': 'python'}, + # Try semicolon (common in European CSVs) + {'sep': ';', 'engine': 'python'}, + # Try comma + {'sep': ',', 'engine': 'python'}, + # Try tab + {'sep': '\t', 'engine': 'python'}, + ] + + for config in parsing_configs: + try: + df = pd.read_csv( + StringIO(csv_content), + on_bad_lines='skip', + quotechar='"', + skip_blank_lines=True, + skipinitialspace=True, + **config + ) + logger.info(f"Successfully parsed CSV with config: {config}") + return df + except pd.errors.ParserError: + continue + + raise pd.errors.ParserError("Could not parse CSV with any configuration") + + def normalize_columns(self, df: pd.DataFrame) -> pd.DataFrame: + """Normalize column names using mapping""" + # Clean column names + df.columns = df.columns.str.strip() + + # Apply mapping + df = df.rename(columns=self.COLUMN_MAPPING) + + logger.info(f"Normalized columns: {list(df.columns)}") + return df + + def process_row(self, row_data: Dict[str, Any]) -> Dict[str, Any]: + """Process a single row with data normalization""" + # Handle NaN values + processed_data = {k: (v if pd.notna(v) else None) for k, v in row_data.items()} + + # Process GTIN + if processed_data.get('gtin'): + processed_data['gtin'] = self.gtin_processor.normalize(processed_data['gtin']) + + # Process price and currency + if processed_data.get('price'): + parsed_price, currency = self.price_processor.parse_price_currency(processed_data['price']) + processed_data['price'] = parsed_price + processed_data['currency'] = currency + + # Process sale_price + if processed_data.get('sale_price'): + parsed_sale_price, _ = self.price_processor.parse_price_currency(processed_data['sale_price']) + processed_data['sale_price'] = parsed_sale_price + + # Clean MPN (remove .0 endings) + if processed_data.get('mpn'): + mpn_str = str(processed_data['mpn']).strip() + if mpn_str.endswith('.0'): + processed_data['mpn'] = mpn_str[:-2] + + # Handle multipack type conversion + if processed_data.get('multipack') is not None: + try: + processed_data['multipack'] = int(float(processed_data['multipack'])) + except (ValueError, TypeError): + processed_data['multipack'] = None + + return processed_data + + async def process_csv_from_url(self, url: str, batch_size: int, db: Session) -> Dict[str, int]: + """Process CSV import with batching""" + # Download and parse CSV + csv_content = self.download_csv(url) + df = self.parse_csv(csv_content) + df = self.normalize_columns(df) + + logger.info(f"Processing CSV with {len(df)} rows") + + imported = 0 + updated = 0 + errors = 0 + + # Process in batches + for i in range(0, len(df), batch_size): + batch_df = df.iloc[i:i + batch_size] + batch_imported, batch_updated, batch_errors = self._process_batch(batch_df, db) + + imported += batch_imported + updated += batch_updated + errors += batch_errors + + # Commit batch + try: + db.commit() + logger.info( + f"Processed batch {i // batch_size + 1}: +{batch_imported} imported, +{batch_updated} updated, +{batch_errors} errors") + except Exception as e: + db.rollback() + logger.error(f"Batch commit failed: {e}") + errors += len(batch_df) + + return { + "imported": imported, + "updated": updated, + "errors": errors, + "total_processed": imported + updated + errors + } + + def _process_batch(self, df_batch: pd.DataFrame, db: Session) -> tuple: + """Process a single batch of rows""" + imported = 0 + updated = 0 + errors = 0 + + for _, row in df_batch.iterrows(): + try: + product_data = self.process_row(row.to_dict()) + + # Validate required fields + product_id = product_data.get('product_id') + title = product_data.get('title') + + if not product_id or not title: + errors += 1 + continue + + # Check for existing product + existing_product = db.query(Product).filter( + Product.product_id == product_id + ).first() + + if existing_product: + # Update existing + for key, value in product_data.items(): + if key not in ['id', 'created_at'] and hasattr(existing_product, key): + setattr(existing_product, key, value) + existing_product.updated_at = datetime.utcnow() + updated += 1 + else: + # Create new + filtered_data = {k: v for k, v in product_data.items() + if k not in ['id', 'created_at', 'updated_at'] and hasattr(Product, k)} + new_product = Product(**filtered_data) + db.add(new_product) + imported += 1 + + except Exception as e: + logger.error(f"Error processing row: {e}") + errors += 1 + + return imported, updated, errors diff --git a/utils/data_processing.py b/utils/data_processing.py new file mode 100644 index 00000000..a6c35b5c --- /dev/null +++ b/utils/data_processing.py @@ -0,0 +1,129 @@ +# utils/data_processing.py +import re +import pandas as pd +from typing import Tuple, Optional +import logging + +logger = logging.getLogger(__name__) + + +class GTINProcessor: + """Handles GTIN normalization and validation""" + + VALID_LENGTHS = [8, 12, 13, 14] + + def normalize(self, gtin_value: any) -> Optional[str]: + """ + Normalize GTIN to proper format + Returns None for invalid GTINs + """ + if not gtin_value or pd.isna(gtin_value): + return None + + gtin_str = str(gtin_value).strip() + if not gtin_str: + return None + + # Remove decimal point (e.g., "889698116923.0" -> "889698116923") + if '.' in gtin_str: + gtin_str = gtin_str.split('.')[0] + + # Keep only digits + gtin_clean = ''.join(filter(str.isdigit, gtin_str)) + + if not gtin_clean: + return None + + # Validate and normalize length + length = len(gtin_clean) + + if length in self.VALID_LENGTHS: + # Standard lengths - pad appropriately + if length == 8: + return gtin_clean.zfill(8) # EAN-8 + elif length == 12: + return gtin_clean.zfill(12) # UPC-A + elif length == 13: + return gtin_clean.zfill(13) # EAN-13 + elif length == 14: + return gtin_clean.zfill(14) # GTIN-14 + + elif length > 14: + # Too long - truncate to EAN-13 + logger.warning(f"GTIN too long, truncating: {gtin_clean}") + return gtin_clean[-13:] + + elif 0 < length < 8: + # Too short - pad to UPC-A + logger.warning(f"GTIN too short, padding: {gtin_clean}") + return gtin_clean.zfill(12) + + logger.warning(f"Invalid GTIN format: '{gtin_value}'") + return None + + def validate(self, gtin: str) -> bool: + """Validate GTIN format""" + if not gtin: + return False + return len(gtin) in self.VALID_LENGTHS and gtin.isdigit() + + +class PriceProcessor: + """Handles price parsing and currency extraction""" + + CURRENCY_PATTERNS = { + # Amount followed by currency + r'([0-9.,]+)\s*(EUR|€)': lambda m: (m.group(1), 'EUR'), + r'([0-9.,]+)\s*(USD|\$)': lambda m: (m.group(1), 'USD'), + r'([0-9.,]+)\s*(GBP|£)': lambda m: (m.group(1), 'GBP'), + r'([0-9.,]+)\s*(CHF)': lambda m: (m.group(1), 'CHF'), + r'([0-9.,]+)\s*(CAD|AUD|JPY|¥)': lambda m: (m.group(1), m.group(2).upper()), + + # Currency followed by amount + r'(EUR|€)\s*([0-9.,]+)': lambda m: (m.group(2), 'EUR'), + r'(USD|\$)\s*([0-9.,]+)': lambda m: (m.group(2), 'USD'), + r'(GBP|£)\s*([0-9.,]+)': lambda m: (m.group(2), 'GBP'), + + # Generic 3-letter currency codes + r'([0-9.,]+)\s*([A-Z]{3})': lambda m: (m.group(1), m.group(2)), + r'([A-Z]{3})\s*([0-9.,]+)': lambda m: (m.group(2), m.group(1)), + } + + def parse_price_currency(self, price_str: any) -> Tuple[Optional[str], Optional[str]]: + """ + Parse price string into (price, currency) tuple + Returns (None, None) if parsing fails + """ + if not price_str or pd.isna(price_str): + return None, None + + price_str = str(price_str).strip() + if not price_str: + return None, None + + # Try each pattern + for pattern, extract_func in self.CURRENCY_PATTERNS.items(): + match = re.search(pattern, price_str, re.IGNORECASE) + if match: + try: + price_val, currency_val = extract_func(match) + # Normalize price (remove spaces, handle comma as decimal) + price_val = price_val.replace(' ', '').replace(',', '.') + # Validate numeric + float(price_val) + return price_val, currency_val.upper() + except (ValueError, AttributeError): + continue + + # Fallback: extract just numbers + number_match = re.search(r'([0-9.,]+)', price_str) + if number_match: + try: + price_val = number_match.group(1).replace(',', '.') + float(price_val) # Validate + return price_val, None + except ValueError: + pass + + logger.warning(f"Could not parse price: '{price_str}'") + return price_str, None diff --git a/utils/database.py b/utils/database.py new file mode 100644 index 00000000..b1523090 --- /dev/null +++ b/utils/database.py @@ -0,0 +1,36 @@ +# utils/database.py +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import QueuePool +import logging + +logger = logging.getLogger(__name__) + + +def get_db_engine(database_url: str): + """Create database engine with connection pooling""" + if database_url.startswith('sqlite'): + # SQLite configuration + engine = create_engine( + database_url, + connect_args={"check_same_thread": False}, + echo=False + ) + else: + # PostgreSQL configuration with connection pooling + engine = create_engine( + database_url, + poolclass=QueuePool, + pool_size=10, + max_overflow=20, + pool_pre_ping=True, + echo=False + ) + + logger.info(f"Database engine created for: {database_url.split('@')[0]}@...") + return engine + + +def get_session_local(engine): + """Create session factory""" + return sessionmaker(autocommit=False, autoflush=False, bind=engine)