Files
orion/models_restructure_script.ps1
2025-09-19 16:54:13 +02:00

476 lines
17 KiB
PowerShell

# restructure_models.ps1 - PowerShell script to restructure models from single files to domain-organized structure
Write-Host "🔄 Starting models restructure..." -ForegroundColor Cyan
# Create new directory structure for models
Write-Host "📁 Creating models directory structure..." -ForegroundColor Yellow
$modelDirectories = @(
"models\database",
"models\api"
)
foreach ($dir in $modelDirectories) {
New-Item -Path $dir -ItemType Directory -Force | Out-Null
Write-Host " Created: $dir" -ForegroundColor Gray
}
# Backup original model files
Write-Host "💾 Backing up original model files..." -ForegroundColor Yellow
New-Item -Path "models\backup" -ItemType Directory -Force | Out-Null
$originalFiles = @("models\database_models.py", "models\api_models.py")
foreach ($file in $originalFiles) {
if (Test-Path $file) {
Copy-Item $file "models\backup\" -Force
Write-Host " Backed up: $(Split-Path $file -Leaf)" -ForegroundColor Gray
}
}
# Create database models files
Write-Host "📄 Creating database model files..." -ForegroundColor Yellow
# models/database/__init__.py
$databaseInit = @"
# models/database/__init__.py
from .user import User
from .product import Product
from .shop import Shop, ShopProduct
from .stock import Stock
from .marketplace import MarketplaceImportJob
__all__ = [
"User",
"Product",
"Shop", "ShopProduct",
"Stock",
"MarketplaceImportJob",
]
"@
$databaseInit | Out-File -FilePath "models\database\__init__.py" -Encoding UTF8
# models/database/base.py
$databaseBase = @"
# models/database/base.py
from datetime import datetime
from sqlalchemy import Column, DateTime
from app.core.database import Base
class TimestampMixin:
"""Mixin to add created_at and updated_at timestamps to models"""
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
)
"@
$databaseBase | Out-File -FilePath "models\database\base.py" -Encoding UTF8
# models/database/user.py
$userModel = @"
# models/database/user.py
from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, Integer, String
from sqlalchemy.orm import relationship
from app.core.database import 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") # user, admin, shop_owner
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
)
# Relationships
marketplace_import_jobs = relationship(
"MarketplaceImportJob", back_populates="user"
)
owned_shops = relationship("Shop", back_populates="owner")
def __repr__(self):
return f"<User(username='{self.username}', email='{self.email}', role='{self.role}')>"
"@
$userModel | Out-File -FilePath "models\database\user.py" -Encoding UTF8
# models/database/product.py
$productModel = @"
# models/database/product.py
from datetime import datetime
from sqlalchemy import Column, DateTime, Index, Integer, String
from sqlalchemy.orm import relationship
from app.core.database import Base
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)
# New marketplace fields
marketplace = Column(
String, index=True, nullable=True, default="Letzshop"
) # Index for marketplace filtering
shop_name = Column(String, index=True, nullable=True) # Index for shop filtering
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,
)
shop_products = relationship("ShopProduct", back_populates="product")
# Additional indexes for marketplace queries
__table_args__ = (
Index(
"idx_marketplace_shop", "marketplace", "shop_name"
), # Composite index for marketplace+shop queries
Index(
"idx_marketplace_brand", "marketplace", "brand"
), # Composite index for marketplace+brand queries
)
def __repr__(self):
return (
f"<Product(product_id='{self.product_id}', title='{self.title}', marketplace='{self.marketplace}', "
f"shop='{self.shop_name}')>"
)
"@
$productModel | Out-File -FilePath "models\database\product.py" -Encoding UTF8
# models/database/shop.py
$shopModel = @"
# models/database/shop.py
from datetime import datetime
from sqlalchemy import Boolean, Column, DateTime, Float, ForeignKey, Index, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import relationship
from app.core.database import Base
class Shop(Base):
__tablename__ = "shops"
id = Column(Integer, primary_key=True, index=True)
shop_code = Column(
String, unique=True, index=True, nullable=False
) # e.g., "TECHSTORE", "FASHIONHUB"
shop_name = Column(String, nullable=False) # Display name
description = Column(Text)
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Contact information
contact_email = Column(String)
contact_phone = Column(String)
website = Column(String)
# Business information
business_address = Column(Text)
tax_number = Column(String)
# Status
is_active = Column(Boolean, default=True)
is_verified = Column(Boolean, default=False)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
owner = relationship("User", back_populates="owned_shops")
shop_products = relationship("ShopProduct", back_populates="shop")
marketplace_import_jobs = relationship(
"MarketplaceImportJob", back_populates="shop"
)
class ShopProduct(Base):
__tablename__ = "shop_products"
id = Column(Integer, primary_key=True, index=True)
shop_id = Column(Integer, ForeignKey("shops.id"), nullable=False)
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
# Shop-specific overrides (can override the main product data)
shop_product_id = Column(String) # Shop's internal product ID
shop_price = Column(Float) # Override main product price
shop_sale_price = Column(Float)
shop_currency = Column(String)
shop_availability = Column(String) # Override availability
shop_condition = Column(String)
# Shop-specific metadata
is_featured = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
display_order = Column(Integer, default=0)
# Inventory management
min_quantity = Column(Integer, default=1)
max_quantity = Column(Integer)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
shop = relationship("Shop", back_populates="shop_products")
product = relationship("Product", back_populates="shop_products")
# Constraints
__table_args__ = (
UniqueConstraint("shop_id", "product_id", name="uq_shop_product"),
Index("idx_shop_product_active", "shop_id", "is_active"),
Index("idx_shop_product_featured", "shop_id", "is_featured"),
)
"@
$shopModel | Out-File -FilePath "models\database\shop.py" -Encoding UTF8
# models/database/stock.py
$stockModel = @"
# models/database/stock.py
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, UniqueConstraint
from sqlalchemy.orm import relationship
from app.core.database import Base
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)
reserved_quantity = Column(Integer, default=0) # For orders being processed
shop_id = Column(Integer, ForeignKey("shops.id")) # Optional: shop-specific stock
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
)
# Relationships
shop = relationship("Shop")
# 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"<Stock(gtin='{self.gtin}', location='{self.location}', quantity={self.quantity})>"
"@
$stockModel | Out-File -FilePath "models\database\stock.py" -Encoding UTF8
# models/database/marketplace.py
$marketplaceModel = @"
# models/database/marketplace.py
from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String
from sqlalchemy.orm import relationship
from app.core.database import Base
class MarketplaceImportJob(Base):
__tablename__ = "marketplace_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)
marketplace = Column(
String, nullable=False, index=True, default="Letzshop"
) # Index for marketplace filtering
shop_name = Column(String, nullable=False, index=True) # Index for shop filtering
shop_id = Column(
Integer, ForeignKey("shops.id"), nullable=False
) # Add proper foreign key
user_id = Column(
Integer, ForeignKey("users.id"), nullable=False
) # Foreign key to users table
# Results
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 handling
error_message = Column(String)
# Timestamps
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])
shop = relationship("Shop", back_populates="marketplace_import_jobs")
# Additional indexes for marketplace import job queries
__table_args__ = (
Index(
"idx_marketplace_import_user_marketplace", "user_id", "marketplace"
), # User's marketplace imports
Index("idx_marketplace_import_shop_status", "status"), # Shop import status
Index("idx_marketplace_import_shop_id", "shop_id"),
)
def __repr__(self):
return (
f"<MarketplaceImportJob(id={self.id}, marketplace='{self.marketplace}', shop='{self.shop_name}', "
f"status='{self.status}', imported={self.imported_count})>"
)
"@
$marketplaceModel | Out-File -FilePath "models\database\marketplace.py" -Encoding UTF8
# Create API models files
Write-Host "📄 Creating API model files..." -ForegroundColor Yellow
# models/api/__init__.py
$apiInit = @"
# models/api/__init__.py
from .auth import UserRegister, UserLogin, UserResponse, LoginResponse
from .product import ProductBase, ProductCreate, ProductUpdate, ProductResponse, ProductListResponse, ProductDetailResponse
from .shop import ShopCreate, ShopUpdate, ShopResponse, ShopListResponse, ShopProductCreate, ShopProductResponse
from .stock import StockBase, StockCreate, StockAdd, StockUpdate, StockResponse, StockLocationResponse, StockSummaryResponse
from .marketplace import MarketplaceImportRequest, MarketplaceImportJobResponse
from .stats import StatsResponse, MarketplaceStatsResponse
__all__ = [
# Auth models
"UserRegister", "UserLogin", "UserResponse", "LoginResponse",
# Product models
"ProductBase", "ProductCreate", "ProductUpdate", "ProductResponse",
"ProductListResponse", "ProductDetailResponse",
# Shop models
"ShopCreate", "ShopUpdate", "ShopResponse", "ShopListResponse",
"ShopProductCreate", "ShopProductResponse",
# Stock models
"StockBase", "StockCreate", "StockAdd", "StockUpdate", "StockResponse",
"StockLocationResponse", "StockSummaryResponse",
# Marketplace models
"MarketplaceImportRequest", "MarketplaceImportJobResponse",
# Stats models
"StatsResponse", "MarketplaceStatsResponse",
]
"@
$apiInit | Out-File -FilePath "models\api\__init__.py" -Encoding UTF8
# models/api/base.py
$apiBase = @"
# models/api/base.py
from typing import List, TypeVar, Generic
from pydantic import BaseModel
T = TypeVar('T')
class ListResponse(BaseModel, Generic[T]):
"""Generic list response model"""
items: List[T]
total: int
skip: int
limit: int
class StatusResponse(BaseModel):
"""Generic status response"""
success: bool
message: str
"@
$apiBase | Out-File -FilePath "models\api\base.py" -Encoding UTF8
Write-Host "✅ Models directory structure created!" -ForegroundColor Green
Write-Host ""
Write-Host "📋 Next steps:" -ForegroundColor Cyan
Write-Host "1. You'll need to manually extract the remaining API model classes from api_models.py:" -ForegroundColor White
Write-Host " - Auth models → models\api\auth.py" -ForegroundColor Gray
Write-Host " - Product models → models\api\product.py" -ForegroundColor Gray
Write-Host " - Shop models → models\api\shop.py" -ForegroundColor Gray
Write-Host " - Stock models → models\api\stock.py" -ForegroundColor Gray
Write-Host " - Marketplace models → models\api\marketplace.py" -ForegroundColor Gray
Write-Host " - Stats models → models\api\stats.py" -ForegroundColor Gray
Write-Host ""
Write-Host "2. Update imports throughout your application:" -ForegroundColor White
Write-Host " Old: from models.database_models import User, Product" -ForegroundColor Gray
Write-Host " New: from models.database import User, Product" -ForegroundColor Gray
Write-Host ""
Write-Host " Old: from models.api_models import UserCreate, ProductResponse" -ForegroundColor Gray
Write-Host " New: from models.api import UserRegister, ProductResponse" -ForegroundColor Gray
Write-Host ""
Write-Host "3. Test imports after restructure:" -ForegroundColor White
Write-Host " python -c \"from models.database import User, Product, Shop\"" -ForegroundColor Yellow
Write-Host " python -c \"from models.api import UserRegister, ProductResponse\"" -ForegroundColor Yellow
Write-Host ""
Write-Host "4. Files to update (search and replace imports):" -ForegroundColor White
Write-Host " - All route files in api/v1/" -ForegroundColor Gray
Write-Host " - Service files in app/services/" -ForegroundColor Gray
Write-Host " - Test files" -ForegroundColor Gray
Write-Host " - Any utility files" -ForegroundColor Gray
Write-Host ""
Write-Host "✨ Database models restructure completed!" -ForegroundColor Green
Write-Host "📚 Original files backed up in models\backup\" -ForegroundColor Green