refactor: modernize code quality tooling with Ruff

- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter)
- Add comprehensive pyproject.toml configuration
- Simplify Makefile code quality targets
- Configure exclusions for venv/.venv in pyproject.toml
- Auto-fix 1,359 linting issues across codebase

Benefits:
- Much faster builds (Ruff is written in Rust)
- Single tool replaces multiple tools
- More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q)
- All configuration centralized in pyproject.toml
- Better import sorting and formatting consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-28 19:37:38 +01:00
parent 21c13ca39b
commit 238c1ec9b8
169 changed files with 2183 additions and 1784 deletions

View File

@@ -9,10 +9,9 @@ This module provides functions for:
"""
import logging
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
from typing import Any
from sqlalchemy import and_, or_
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.exceptions import AdminOperationException
@@ -33,10 +32,10 @@ class AdminAuditService:
action: str,
target_type: str,
target_id: str,
details: Optional[Dict[str, Any]] = None,
ip_address: Optional[str] = None,
user_agent: Optional[str] = None,
request_id: Optional[str] = None,
details: dict[str, Any] | None = None,
ip_address: str | None = None,
user_agent: str | None = None,
request_id: str | None = None,
) -> AdminAuditLog:
"""
Log an admin action to the audit trail.
@@ -85,7 +84,7 @@ class AdminAuditService:
def get_audit_logs(
self, db: Session, filters: AdminAuditLogFilters
) -> List[AdminAuditLogResponse]:
) -> list[AdminAuditLogResponse]:
"""
Get filtered admin audit logs with pagination.
@@ -187,14 +186,14 @@ class AdminAuditService:
def get_recent_actions_by_admin(
self, db: Session, admin_user_id: int, limit: int = 10
) -> List[AdminAuditLogResponse]:
) -> list[AdminAuditLogResponse]:
"""Get recent actions by a specific admin."""
filters = AdminAuditLogFilters(admin_user_id=admin_user_id, limit=limit)
return self.get_audit_logs(db, filters)
def get_actions_by_target(
self, db: Session, target_type: str, target_id: str, limit: int = 50
) -> List[AdminAuditLogResponse]:
) -> list[AdminAuditLogResponse]:
"""Get all actions performed on a specific target."""
try:
logs = (

View File

@@ -13,17 +13,21 @@ This module provides classes and functions for:
import logging
import secrets
import string
from datetime import datetime, timezone
from typing import List, Optional, Tuple
from datetime import UTC, datetime
from sqlalchemy import func, or_
from sqlalchemy.orm import Session
from app.exceptions import (AdminOperationException, CannotModifySelfException,
UserNotFoundException, UserStatusChangeException,
ValidationException, VendorAlreadyExistsException,
VendorNotFoundException,
VendorVerificationException)
from app.exceptions import (
AdminOperationException,
CannotModifySelfException,
UserNotFoundException,
UserStatusChangeException,
ValidationException,
VendorAlreadyExistsException,
VendorNotFoundException,
VendorVerificationException,
)
from models.database.marketplace_import_job import MarketplaceImportJob
from models.database.user import User
from models.database.vendor import Role, Vendor, VendorUser
@@ -40,7 +44,7 @@ class AdminService:
# USER MANAGEMENT
# ============================================================================
def get_all_users(self, db: Session, skip: int = 0, limit: int = 100) -> List[User]:
def get_all_users(self, db: Session, skip: int = 0, limit: int = 100) -> list[User]:
"""Get paginated list of all users."""
try:
return db.query(User).offset(skip).limit(limit).all()
@@ -52,7 +56,7 @@ class AdminService:
def toggle_user_status(
self, db: Session, user_id: int, current_admin_id: int
) -> Tuple[User, str]:
) -> tuple[User, str]:
"""Toggle user active status."""
user = self._get_user_by_id_or_raise(db, user_id)
@@ -72,7 +76,7 @@ class AdminService:
try:
original_status = user.is_active
user.is_active = not user.is_active
user.updated_at = datetime.now(timezone.utc)
user.updated_at = datetime.now(UTC)
db.commit()
db.refresh(user)
@@ -98,7 +102,7 @@ class AdminService:
def create_vendor_with_owner(
self, db: Session, vendor_data: VendorCreate
) -> Tuple[Vendor, User, str]:
) -> tuple[Vendor, User, str]:
"""
Create vendor with owner user account.
@@ -222,10 +226,10 @@ class AdminService:
db: Session,
skip: int = 0,
limit: int = 100,
search: Optional[str] = None,
is_active: Optional[bool] = None,
is_verified: Optional[bool] = None,
) -> Tuple[List[Vendor], int]:
search: str | None = None,
is_active: bool | None = None,
is_verified: bool | None = None,
) -> tuple[list[Vendor], int]:
"""Get paginated list of all vendors with filtering."""
try:
query = db.query(Vendor)
@@ -261,17 +265,17 @@ class AdminService:
"""Get vendor by ID."""
return self._get_vendor_by_id_or_raise(db, vendor_id)
def verify_vendor(self, db: Session, vendor_id: int) -> Tuple[Vendor, str]:
def verify_vendor(self, db: Session, vendor_id: int) -> tuple[Vendor, str]:
"""Toggle vendor verification status."""
vendor = self._get_vendor_by_id_or_raise(db, vendor_id)
try:
original_status = vendor.is_verified
vendor.is_verified = not vendor.is_verified
vendor.updated_at = datetime.now(timezone.utc)
vendor.updated_at = datetime.now(UTC)
if vendor.is_verified:
vendor.verified_at = datetime.now(timezone.utc)
vendor.verified_at = datetime.now(UTC)
db.commit()
db.refresh(vendor)
@@ -291,14 +295,14 @@ class AdminService:
current_verification_status=original_status,
)
def toggle_vendor_status(self, db: Session, vendor_id: int) -> Tuple[Vendor, str]:
def toggle_vendor_status(self, db: Session, vendor_id: int) -> tuple[Vendor, str]:
"""Toggle vendor active status."""
vendor = self._get_vendor_by_id_or_raise(db, vendor_id)
try:
original_status = vendor.is_active
vendor.is_active = not vendor.is_active
vendor.updated_at = datetime.now(timezone.utc)
vendor.updated_at = datetime.now(UTC)
db.commit()
db.refresh(vendor)
@@ -347,7 +351,10 @@ class AdminService:
)
def update_vendor(
self, db: Session, vendor_id: int, vendor_update # VendorUpdate schema
self,
db: Session,
vendor_id: int,
vendor_update, # VendorUpdate schema
) -> Vendor:
"""
Update vendor information (Admin only).
@@ -402,7 +409,7 @@ class AdminService:
for field, value in update_data.items():
setattr(vendor, field, value)
vendor.updated_at = datetime.now(timezone.utc)
vendor.updated_at = datetime.now(UTC)
db.commit()
db.refresh(vendor)
@@ -430,7 +437,7 @@ class AdminService:
db: Session,
vendor_id: int,
transfer_data, # VendorTransferOwnership schema
) -> Tuple[Vendor, User, User]:
) -> tuple[Vendor, User, User]:
"""
Transfer vendor ownership to another user.
@@ -556,7 +563,7 @@ class AdminService:
# Update vendor owner_user_id
vendor.owner_user_id = new_owner.id
vendor.updated_at = datetime.now(timezone.utc)
vendor.updated_at = datetime.now(UTC)
db.commit()
db.refresh(vendor)
@@ -593,12 +600,12 @@ class AdminService:
def get_marketplace_import_jobs(
self,
db: Session,
marketplace: Optional[str] = None,
vendor_name: Optional[str] = None,
status: Optional[str] = None,
marketplace: str | None = None,
vendor_name: str | None = None,
status: str | None = None,
skip: int = 0,
limit: int = 100,
) -> List[MarketplaceImportJobResponse]:
) -> list[MarketplaceImportJobResponse]:
"""Get filtered and paginated marketplace import jobs."""
try:
query = db.query(MarketplaceImportJob)
@@ -633,7 +640,7 @@ class AdminService:
# STATISTICS
# ============================================================================
def get_recent_vendors(self, db: Session, limit: int = 5) -> List[dict]:
def get_recent_vendors(self, db: Session, limit: int = 5) -> list[dict]:
"""Get recently created vendors."""
try:
vendors = (
@@ -656,7 +663,7 @@ class AdminService:
logger.error(f"Failed to get recent vendors: {str(e)}")
return []
def get_recent_import_jobs(self, db: Session, limit: int = 10) -> List[dict]:
def get_recent_import_jobs(self, db: Session, limit: int = 10) -> list[dict]:
"""Get recent marketplace import jobs."""
try:
jobs = (

View File

@@ -10,17 +10,23 @@ This module provides functions for:
import json
import logging
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
from datetime import UTC, datetime
from typing import Any
from sqlalchemy import func
from sqlalchemy.orm import Session
from app.exceptions import (AdminOperationException, ResourceNotFoundException,
ValidationException)
from app.exceptions import (
AdminOperationException,
ResourceNotFoundException,
ValidationException,
)
from models.database.admin import AdminSetting
from models.schema.admin import (AdminSettingCreate, AdminSettingResponse,
AdminSettingUpdate)
from models.schema.admin import (
AdminSettingCreate,
AdminSettingResponse,
AdminSettingUpdate,
)
logger = logging.getLogger(__name__)
@@ -28,7 +34,7 @@ logger = logging.getLogger(__name__)
class AdminSettingsService:
"""Service for managing platform-wide settings."""
def get_setting_by_key(self, db: Session, key: str) -> Optional[AdminSetting]:
def get_setting_by_key(self, db: Session, key: str) -> AdminSetting | None:
"""Get setting by key."""
try:
return (
@@ -60,14 +66,13 @@ class AdminSettingsService:
try:
if setting.value_type == "integer":
return int(setting.value)
elif setting.value_type == "float":
if setting.value_type == "float":
return float(setting.value)
elif setting.value_type == "boolean":
if setting.value_type == "boolean":
return setting.value.lower() in ("true", "1", "yes")
elif setting.value_type == "json":
if setting.value_type == "json":
return json.loads(setting.value)
else:
return setting.value
return setting.value
except Exception as e:
logger.error(f"Failed to convert setting {key} value: {str(e)}")
return default
@@ -75,9 +80,9 @@ class AdminSettingsService:
def get_all_settings(
self,
db: Session,
category: Optional[str] = None,
is_public: Optional[bool] = None,
) -> List[AdminSettingResponse]:
category: str | None = None,
is_public: bool | None = None,
) -> list[AdminSettingResponse]:
"""Get all settings with optional filtering."""
try:
query = db.query(AdminSetting)
@@ -100,7 +105,7 @@ class AdminSettingsService:
operation="get_all_settings", reason="Database query failed"
)
def get_settings_by_category(self, db: Session, category: str) -> Dict[str, Any]:
def get_settings_by_category(self, db: Session, category: str) -> dict[str, Any]:
"""
Get all settings in a category as a dictionary.
@@ -198,7 +203,7 @@ class AdminSettingsService:
if update_data.description is not None:
setting.description = update_data.description
setting.last_modified_by_user_id = admin_user_id
setting.updated_at = datetime.now(timezone.utc)
setting.updated_at = datetime.now(UTC)
db.commit()
db.refresh(setting)
@@ -228,8 +233,7 @@ class AdminSettingsService:
value=setting_data.value, description=setting_data.description
)
return self.update_setting(db, setting_data.key, update_data, admin_user_id)
else:
return self.create_setting(db, setting_data, admin_user_id)
return self.create_setting(db, setting_data, admin_user_id)
def delete_setting(self, db: Session, key: str, admin_user_id: int) -> str:
"""Delete setting."""

View File

@@ -9,13 +9,17 @@ This module provides classes and functions for:
"""
import logging
from typing import Any, Dict, Optional
from datetime import UTC
from typing import Any
from sqlalchemy.orm import Session
from app.exceptions import (InvalidCredentialsException,
UserAlreadyExistsException, UserNotActiveException,
ValidationException)
from app.exceptions import (
InvalidCredentialsException,
UserAlreadyExistsException,
UserNotActiveException,
ValidationException,
)
from middleware.auth import AuthManager
from models.database.user import User
from models.schema.auth import UserLogin, UserRegister
@@ -82,7 +86,7 @@ class AuthService:
logger.error(f"Error registering user: {str(e)}")
raise ValidationException("Registration failed")
def login_user(self, db: Session, user_credentials: UserLogin) -> Dict[str, Any]:
def login_user(self, db: Session, user_credentials: UserLogin) -> dict[str, Any]:
"""
Login user and return JWT token with user data.
@@ -120,7 +124,7 @@ class AuthService:
logger.error(f"Error during login: {str(e)}")
raise InvalidCredentialsException()
def get_user_by_email(self, db: Session, email: str) -> Optional[User]:
def get_user_by_email(self, db: Session, email: str) -> User | None:
"""Get user by email."""
try:
return db.query(User).filter(User.email == email).first()
@@ -128,7 +132,7 @@ class AuthService:
logger.error(f"Error getting user by email: {str(e)}")
return None
def get_user_by_username(self, db: Session, username: str) -> Optional[User]:
def get_user_by_username(self, db: Session, username: str) -> User | None:
"""Get user by username."""
try:
return db.query(User).filter(User.username == username).first()
@@ -138,7 +142,7 @@ class AuthService:
def authenticate_user(
self, db: Session, username: str, password: str
) -> Optional[User]:
) -> User | None:
"""Authenticate user with username/password."""
try:
return self.auth_manager.authenticate_user(db, username, password)
@@ -146,7 +150,7 @@ class AuthService:
logger.error(f"Error authenticating user: {str(e)}")
return None
def create_access_token(self, user: User) -> Dict[str, Any]:
def create_access_token(self, user: User) -> dict[str, Any]:
"""Create access token for user."""
try:
return self.auth_manager.create_access_token(user)
@@ -182,7 +186,7 @@ class AuthService:
Returns:
Dictionary with access_token, token_type, and expires_in
"""
from datetime import datetime, timedelta, timezone
from datetime import datetime, timedelta
from jose import jwt
@@ -190,13 +194,13 @@ class AuthService:
try:
expires_delta = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
expire = datetime.now(timezone.utc) + expires_delta
expire = datetime.now(UTC) + expires_delta
# Build payload with provided data
payload = {
**data,
"exp": expire,
"iat": datetime.now(timezone.utc),
"iat": datetime.now(UTC),
}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")

View File

@@ -9,20 +9,18 @@ This module provides:
"""
import logging
from datetime import datetime, timezone
from typing import Dict, List, Optional
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.exceptions import (CartItemNotFoundException, CartValidationException,
InsufficientInventoryForCartException,
InvalidCartQuantityException,
ProductNotAvailableForCartException,
ProductNotFoundException)
from app.exceptions import (
CartItemNotFoundException,
InsufficientInventoryForCartException,
InvalidCartQuantityException,
ProductNotFoundException,
)
from models.database.cart import CartItem
from models.database.product import Product
from models.database.vendor import Vendor
logger = logging.getLogger(__name__)
@@ -30,7 +28,7 @@ logger = logging.getLogger(__name__)
class CartService:
"""Service for managing shopping carts."""
def get_cart(self, db: Session, vendor_id: int, session_id: str) -> Dict:
def get_cart(self, db: Session, vendor_id: int, session_id: str) -> dict:
"""
Get cart contents for a session.
@@ -43,7 +41,7 @@ class CartService:
Cart data with items and totals
"""
logger.info(
f"[CART_SERVICE] get_cart called",
"[CART_SERVICE] get_cart called",
extra={
"vendor_id": vendor_id,
"session_id": session_id,
@@ -111,7 +109,7 @@ class CartService:
session_id: str,
product_id: int,
quantity: int = 1,
) -> Dict:
) -> dict:
"""
Add product to cart.
@@ -130,7 +128,7 @@ class CartService:
InsufficientInventoryException: If not enough inventory
"""
logger.info(
f"[CART_SERVICE] add_to_cart called",
"[CART_SERVICE] add_to_cart called",
extra={
"vendor_id": vendor_id,
"session_id": session_id,
@@ -154,7 +152,7 @@ class CartService:
if not product:
logger.error(
f"[CART_SERVICE] Product not found",
"[CART_SERVICE] Product not found",
extra={"product_id": product_id, "vendor_id": vendor_id},
)
raise ProductNotFoundException(product_id=product_id, vendor_id=vendor_id)
@@ -191,7 +189,7 @@ class CartService:
# Check inventory for new total quantity
if product.available_inventory < new_quantity:
logger.warning(
f"[CART_SERVICE] Insufficient inventory for update",
"[CART_SERVICE] Insufficient inventory for update",
extra={
"product_id": product_id,
"current_in_cart": existing_item.quantity,
@@ -212,7 +210,7 @@ class CartService:
db.refresh(existing_item)
logger.info(
f"[CART_SERVICE] Updated existing cart item",
"[CART_SERVICE] Updated existing cart item",
extra={"cart_item_id": existing_item.id, "new_quantity": new_quantity},
)
@@ -221,50 +219,49 @@ class CartService:
"product_id": product_id,
"quantity": new_quantity,
}
else:
# Check inventory for new item
if product.available_inventory < quantity:
logger.warning(
f"[CART_SERVICE] Insufficient inventory",
extra={
"product_id": product_id,
"requested": quantity,
"available": product.available_inventory,
},
)
raise InsufficientInventoryForCartException(
product_id=product_id,
product_name=product.marketplace_product.title,
requested=quantity,
available=product.available_inventory,
)
# Create new cart item
cart_item = CartItem(
vendor_id=vendor_id,
session_id=session_id,
product_id=product_id,
quantity=quantity,
price_at_add=current_price,
)
db.add(cart_item)
db.commit()
db.refresh(cart_item)
logger.info(
f"[CART_SERVICE] Created new cart item",
# Check inventory for new item
if product.available_inventory < quantity:
logger.warning(
"[CART_SERVICE] Insufficient inventory",
extra={
"cart_item_id": cart_item.id,
"quantity": quantity,
"price": current_price,
"product_id": product_id,
"requested": quantity,
"available": product.available_inventory,
},
)
raise InsufficientInventoryForCartException(
product_id=product_id,
product_name=product.marketplace_product.title,
requested=quantity,
available=product.available_inventory,
)
return {
"message": "Product added to cart",
"product_id": product_id,
# Create new cart item
cart_item = CartItem(
vendor_id=vendor_id,
session_id=session_id,
product_id=product_id,
quantity=quantity,
price_at_add=current_price,
)
db.add(cart_item)
db.commit()
db.refresh(cart_item)
logger.info(
"[CART_SERVICE] Created new cart item",
extra={
"cart_item_id": cart_item.id,
"quantity": quantity,
}
"price": current_price,
},
)
return {
"message": "Product added to cart",
"product_id": product_id,
"quantity": quantity,
}
def update_cart_item(
self,
@@ -273,7 +270,7 @@ class CartService:
session_id: str,
product_id: int,
quantity: int,
) -> Dict:
) -> dict:
"""
Update quantity of item in cart.
@@ -344,7 +341,7 @@ class CartService:
db.refresh(cart_item)
logger.info(
f"[CART_SERVICE] Updated cart item quantity",
"[CART_SERVICE] Updated cart item quantity",
extra={
"cart_item_id": cart_item.id,
"product_id": product_id,
@@ -360,7 +357,7 @@ class CartService:
def remove_from_cart(
self, db: Session, vendor_id: int, session_id: str, product_id: int
) -> Dict:
) -> dict:
"""
Remove item from cart.
@@ -398,7 +395,7 @@ class CartService:
db.commit()
logger.info(
f"[CART_SERVICE] Removed item from cart",
"[CART_SERVICE] Removed item from cart",
extra={
"cart_item_id": cart_item.id,
"product_id": product_id,
@@ -408,7 +405,7 @@ class CartService:
return {"message": "Item removed from cart", "product_id": product_id}
def clear_cart(self, db: Session, vendor_id: int, session_id: str) -> Dict:
def clear_cart(self, db: Session, vendor_id: int, session_id: str) -> dict:
"""
Clear all items from cart.
@@ -432,7 +429,7 @@ class CartService:
db.commit()
logger.info(
f"[CART_SERVICE] Cleared cart",
"[CART_SERVICE] Cleared cart",
extra={
"session_id": session_id,
"vendor_id": vendor_id,

View File

@@ -7,16 +7,16 @@ import json
import logging
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from sqlalchemy import desc, func
from sqlalchemy.orm import Session
from app.models.architecture_scan import (ArchitectureRule, ArchitectureScan,
ArchitectureViolation,
ViolationAssignment,
ViolationComment)
from app.models.architecture_scan import (
ArchitectureScan,
ArchitectureViolation,
ViolationAssignment,
ViolationComment,
)
logger = logging.getLogger(__name__)
@@ -118,7 +118,7 @@ class CodeQualityService:
logger.info(f"Scan completed: {scan.total_violations} violations found")
return scan
def get_latest_scan(self, db: Session) -> Optional[ArchitectureScan]:
def get_latest_scan(self, db: Session) -> ArchitectureScan | None:
"""Get the most recent scan"""
return (
db.query(ArchitectureScan)
@@ -126,11 +126,11 @@ class CodeQualityService:
.first()
)
def get_scan_by_id(self, db: Session, scan_id: int) -> Optional[ArchitectureScan]:
def get_scan_by_id(self, db: Session, scan_id: int) -> ArchitectureScan | None:
"""Get scan by ID"""
return db.query(ArchitectureScan).filter(ArchitectureScan.id == scan_id).first()
def get_scan_history(self, db: Session, limit: int = 30) -> List[ArchitectureScan]:
def get_scan_history(self, db: Session, limit: int = 30) -> list[ArchitectureScan]:
"""
Get scan history for trend graphs
@@ -158,7 +158,7 @@ class CodeQualityService:
file_path: str = None,
limit: int = 100,
offset: int = 0,
) -> Tuple[List[ArchitectureViolation], int]:
) -> tuple[list[ArchitectureViolation], int]:
"""
Get violations with filtering and pagination
@@ -217,7 +217,7 @@ class CodeQualityService:
def get_violation_by_id(
self, db: Session, violation_id: int
) -> Optional[ArchitectureViolation]:
) -> ArchitectureViolation | None:
"""Get single violation with details"""
return (
db.query(ArchitectureViolation)
@@ -348,7 +348,7 @@ class CodeQualityService:
logger.info(f"Comment added to violation {violation_id} by user {user_id}")
return comment_obj
def get_dashboard_stats(self, db: Session) -> Dict:
def get_dashboard_stats(self, db: Session) -> dict:
"""
Get statistics for dashboard
@@ -507,7 +507,7 @@ class CodeQualityService:
score = 100 - (scan.errors * 0.5 + scan.warnings * 0.05)
return max(0, min(100, int(score))) # Clamp to 0-100
def _get_git_commit_hash(self) -> Optional[str]:
def _get_git_commit_hash(self) -> str | None:
"""Get current git commit hash"""
try:
result = subprocess.run(

View File

@@ -17,10 +17,9 @@ This allows:
"""
import logging
from datetime import datetime, timezone
from typing import List, Optional
from datetime import UTC, datetime
from sqlalchemy import and_, or_
from sqlalchemy import and_
from sqlalchemy.orm import Session
from models.database.content_page import ContentPage
@@ -35,9 +34,9 @@ class ContentPageService:
def get_page_for_vendor(
db: Session,
slug: str,
vendor_id: Optional[int] = None,
vendor_id: int | None = None,
include_unpublished: bool = False,
) -> Optional[ContentPage]:
) -> ContentPage | None:
"""
Get content page for a vendor with fallback to platform default.
@@ -90,11 +89,11 @@ class ContentPageService:
@staticmethod
def list_pages_for_vendor(
db: Session,
vendor_id: Optional[int] = None,
vendor_id: int | None = None,
include_unpublished: bool = False,
footer_only: bool = False,
header_only: bool = False,
) -> List[ContentPage]:
) -> list[ContentPage]:
"""
List all available pages for a vendor (includes vendor overrides + platform defaults).
@@ -156,16 +155,16 @@ class ContentPageService:
slug: str,
title: str,
content: str,
vendor_id: Optional[int] = None,
vendor_id: int | None = None,
content_format: str = "html",
template: str = "default",
meta_description: Optional[str] = None,
meta_keywords: Optional[str] = None,
meta_description: str | None = None,
meta_keywords: str | None = None,
is_published: bool = False,
show_in_footer: bool = True,
show_in_header: bool = False,
display_order: int = 0,
created_by: Optional[int] = None,
created_by: int | None = None,
) -> ContentPage:
"""
Create a new content page.
@@ -199,7 +198,7 @@ class ContentPageService:
meta_description=meta_description,
meta_keywords=meta_keywords,
is_published=is_published,
published_at=datetime.now(timezone.utc) if is_published else None,
published_at=datetime.now(UTC) if is_published else None,
show_in_footer=show_in_footer,
show_in_header=show_in_header,
display_order=display_order,
@@ -220,18 +219,18 @@ class ContentPageService:
def update_page(
db: Session,
page_id: int,
title: Optional[str] = None,
content: Optional[str] = None,
content_format: Optional[str] = None,
template: Optional[str] = None,
meta_description: Optional[str] = None,
meta_keywords: Optional[str] = None,
is_published: Optional[bool] = None,
show_in_footer: Optional[bool] = None,
show_in_header: Optional[bool] = None,
display_order: Optional[int] = None,
updated_by: Optional[int] = None,
) -> Optional[ContentPage]:
title: str | None = None,
content: str | None = None,
content_format: str | None = None,
template: str | None = None,
meta_description: str | None = None,
meta_keywords: str | None = None,
is_published: bool | None = None,
show_in_footer: bool | None = None,
show_in_header: bool | None = None,
display_order: int | None = None,
updated_by: int | None = None,
) -> ContentPage | None:
"""
Update an existing content page.
@@ -275,7 +274,7 @@ class ContentPageService:
if is_published is not None:
page.is_published = is_published
if is_published and not page.published_at:
page.published_at = datetime.now(timezone.utc)
page.published_at = datetime.now(UTC)
if show_in_footer is not None:
page.show_in_footer = show_in_footer
if show_in_header is not None:
@@ -316,14 +315,14 @@ class ContentPageService:
return True
@staticmethod
def get_page_by_id(db: Session, page_id: int) -> Optional[ContentPage]:
def get_page_by_id(db: Session, page_id: int) -> ContentPage | None:
"""Get content page by ID."""
return db.query(ContentPage).filter(ContentPage.id == page_id).first()
@staticmethod
def list_all_vendor_pages(
db: Session, vendor_id: int, include_unpublished: bool = False
) -> List[ContentPage]:
) -> list[ContentPage]:
"""
List only vendor-specific pages (no platform defaults).
@@ -350,7 +349,7 @@ class ContentPageService:
@staticmethod
def list_all_platform_pages(
db: Session, include_unpublished: bool = False
) -> List[ContentPage]:
) -> list[ContentPage]:
"""
List only platform default pages.

View File

@@ -7,22 +7,22 @@ with complete vendor isolation.
"""
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, Optional
from datetime import UTC, datetime, timedelta
from typing import Any
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.exceptions.customer import (CustomerAlreadyExistsException,
CustomerNotActiveException,
CustomerNotFoundException,
CustomerValidationException,
DuplicateCustomerEmailException,
InvalidCustomerCredentialsException)
from app.exceptions.vendor import (VendorNotActiveException,
VendorNotFoundException)
from app.exceptions.customer import (
CustomerNotActiveException,
CustomerNotFoundException,
CustomerValidationException,
DuplicateCustomerEmailException,
InvalidCustomerCredentialsException,
)
from app.exceptions.vendor import VendorNotActiveException, VendorNotFoundException
from app.services.auth_service import AuthService
from models.database.customer import Customer, CustomerAddress
from models.database.customer import Customer
from models.database.vendor import Vendor
from models.schema.auth import UserLogin
from models.schema.customer import CustomerRegister, CustomerUpdate
@@ -128,7 +128,7 @@ class CustomerService:
def login_customer(
self, db: Session, vendor_id: int, credentials: UserLogin
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""
Authenticate customer and generate JWT token.
@@ -177,13 +177,13 @@ class CustomerService:
# Generate JWT token with customer context
# Use auth_manager directly since Customer is not a User model
from datetime import datetime, timedelta, timezone
from datetime import datetime
from jose import jwt
auth_manager = self.auth_service.auth_manager
expires_delta = timedelta(minutes=auth_manager.token_expire_minutes)
expire = datetime.now(timezone.utc) + expires_delta
expire = datetime.now(UTC) + expires_delta
payload = {
"sub": str(customer.id),
@@ -191,7 +191,7 @@ class CustomerService:
"vendor_id": vendor_id,
"type": "customer",
"exp": expire,
"iat": datetime.now(timezone.utc),
"iat": datetime.now(UTC),
}
token = jwt.encode(
@@ -239,7 +239,7 @@ class CustomerService:
def get_customer_by_email(
self, db: Session, vendor_id: int, email: str
) -> Optional[Customer]:
) -> Customer | None:
"""
Get customer by email (vendor-scoped).

View File

@@ -1,24 +1,27 @@
# app/services/inventory_service.py
import logging
from datetime import datetime, timezone
from typing import List, Optional
from datetime import UTC, datetime
from sqlalchemy.orm import Session
from app.exceptions import (InsufficientInventoryException,
InvalidInventoryOperationException,
InvalidQuantityException,
InventoryNotFoundException,
InventoryValidationException,
NegativeInventoryException,
ProductNotFoundException, ValidationException)
from app.exceptions import (
InsufficientInventoryException,
InvalidQuantityException,
InventoryNotFoundException,
InventoryValidationException,
ProductNotFoundException,
ValidationException,
)
from models.database.inventory import Inventory
from models.database.product import Product
from models.database.vendor import Vendor
from models.schema.inventory import (InventoryAdjust, InventoryCreate,
InventoryLocationResponse,
InventoryReserve, InventoryUpdate,
ProductInventorySummary)
from models.schema.inventory import (
InventoryAdjust,
InventoryCreate,
InventoryLocationResponse,
InventoryReserve,
InventoryUpdate,
ProductInventorySummary,
)
logger = logging.getLogger(__name__)
@@ -58,7 +61,7 @@ class InventoryService:
if existing:
old_qty = existing.quantity
existing.quantity = inventory_data.quantity
existing.updated_at = datetime.now(timezone.utc)
existing.updated_at = datetime.now(UTC)
db.commit()
db.refresh(existing)
@@ -67,24 +70,23 @@ class InventoryService:
f"{old_qty}{inventory_data.quantity}"
)
return existing
else:
# Create new inventory entry
new_inventory = Inventory(
product_id=inventory_data.product_id,
vendor_id=vendor_id,
location=location,
quantity=inventory_data.quantity,
gtin=product.marketplace_product.gtin, # Optional reference
)
db.add(new_inventory)
db.commit()
db.refresh(new_inventory)
# Create new inventory entry
new_inventory = Inventory(
product_id=inventory_data.product_id,
vendor_id=vendor_id,
location=location,
quantity=inventory_data.quantity,
gtin=product.marketplace_product.gtin, # Optional reference
)
db.add(new_inventory)
db.commit()
db.refresh(new_inventory)
logger.info(
f"Created inventory for product {inventory_data.product_id} at {location}: "
f"{inventory_data.quantity}"
)
return new_inventory
logger.info(
f"Created inventory for product {inventory_data.product_id} at {location}: "
f"{inventory_data.quantity}"
)
return new_inventory
except (
ProductNotFoundException,
@@ -162,7 +164,7 @@ class InventoryService:
)
existing.quantity = new_qty
existing.updated_at = datetime.now(timezone.utc)
existing.updated_at = datetime.now(UTC)
db.commit()
db.refresh(existing)
@@ -224,7 +226,7 @@ class InventoryService:
# Reserve inventory
inventory.reserved_quantity += reserve_data.quantity
inventory.updated_at = datetime.now(timezone.utc)
inventory.updated_at = datetime.now(UTC)
db.commit()
db.refresh(inventory)
@@ -284,7 +286,7 @@ class InventoryService:
else:
inventory.reserved_quantity -= reserve_data.quantity
inventory.updated_at = datetime.now(timezone.utc)
inventory.updated_at = datetime.now(UTC)
db.commit()
db.refresh(inventory)
@@ -350,7 +352,7 @@ class InventoryService:
inventory.reserved_quantity = max(
0, inventory.reserved_quantity - reserve_data.quantity
)
inventory.updated_at = datetime.now(timezone.utc)
inventory.updated_at = datetime.now(UTC)
db.commit()
db.refresh(inventory)
@@ -443,9 +445,9 @@ class InventoryService:
vendor_id: int,
skip: int = 0,
limit: int = 100,
location: Optional[str] = None,
low_stock_threshold: Optional[int] = None,
) -> List[Inventory]:
location: str | None = None,
low_stock_threshold: int | None = None,
) -> list[Inventory]:
"""
Get all inventory for a vendor with filtering.
@@ -504,7 +506,7 @@ class InventoryService:
if inventory_update.location:
inventory.location = self._validate_location(inventory_update.location)
inventory.updated_at = datetime.now(timezone.utc)
inventory.updated_at = datetime.now(UTC)
db.commit()
db.refresh(inventory)
@@ -565,7 +567,7 @@ class InventoryService:
def _get_inventory_entry(
self, db: Session, product_id: int, location: str
) -> Optional[Inventory]:
) -> Inventory | None:
"""Get inventory entry by product and location."""
return (
db.query(Inventory)

View File

@@ -1,19 +1,20 @@
# app/services/marketplace_import_job_service.py
import logging
from datetime import datetime, timezone
from typing import List, Optional
from sqlalchemy.orm import Session
from app.exceptions import (ImportJobCannotBeCancelledException,
ImportJobCannotBeDeletedException,
ImportJobNotFoundException,
ImportJobNotOwnedException, ValidationException)
from app.exceptions import (
ImportJobNotFoundException,
ImportJobNotOwnedException,
ValidationException,
)
from models.database.marketplace_import_job import MarketplaceImportJob
from models.database.user import User
from models.database.vendor import Vendor
from models.schema.marketplace_import_job import (MarketplaceImportJobRequest,
MarketplaceImportJobResponse)
from models.schema.marketplace_import_job import (
MarketplaceImportJobRequest,
MarketplaceImportJobResponse,
)
logger = logging.getLogger(__name__)
@@ -98,10 +99,10 @@ class MarketplaceImportJobService:
db: Session,
vendor: Vendor, # ADDED: Vendor filter
user: User,
marketplace: Optional[str] = None,
marketplace: str | None = None,
skip: int = 0,
limit: int = 50,
) -> List[MarketplaceImportJob]:
) -> list[MarketplaceImportJob]:
"""Get marketplace import jobs for a specific vendor."""
try:
query = db.query(MarketplaceImportJob).filter(

View File

@@ -8,29 +8,31 @@ This module provides classes and functions for:
- Inventory information integration
- CSV export functionality
"""
import csv
import logging
from datetime import datetime, timezone
from collections.abc import Generator
from datetime import UTC, datetime
from io import StringIO
from typing import Generator, List, Optional, Tuple
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from app.exceptions import (InvalidMarketplaceProductDataException,
MarketplaceProductAlreadyExistsException,
MarketplaceProductNotFoundException,
MarketplaceProductValidationException,
ValidationException)
from app.services.marketplace_import_job_service import \
marketplace_import_job_service
from app.exceptions import (
InvalidMarketplaceProductDataException,
MarketplaceProductAlreadyExistsException,
MarketplaceProductNotFoundException,
MarketplaceProductValidationException,
ValidationException,
)
from app.utils.data_processing import GTINProcessor, PriceProcessor
from models.database.inventory import Inventory
from models.database.marketplace_product import MarketplaceProduct
from models.schema.inventory import (InventoryLocationResponse,
InventorySummaryResponse)
from models.schema.marketplace_product import (MarketplaceProductCreate,
MarketplaceProductUpdate)
from models.schema.inventory import InventoryLocationResponse, InventorySummaryResponse
from models.schema.marketplace_product import (
MarketplaceProductCreate,
MarketplaceProductUpdate,
)
logger = logging.getLogger(__name__)
@@ -109,10 +111,9 @@ class MarketplaceProductService:
raise MarketplaceProductAlreadyExistsException(
product_data.marketplace_product_id
)
else:
raise MarketplaceProductValidationException(
"Data integrity constraint violation"
)
raise MarketplaceProductValidationException(
"Data integrity constraint violation"
)
except Exception as e:
db.rollback()
logger.error(f"Error creating product: {str(e)}")
@@ -120,7 +121,7 @@ class MarketplaceProductService:
def get_product_by_id(
self, db: Session, marketplace_product_id: str
) -> Optional[MarketplaceProduct]:
) -> MarketplaceProduct | None:
"""Get a product by its ID."""
try:
return (
@@ -160,13 +161,13 @@ class MarketplaceProductService:
db: Session,
skip: int = 0,
limit: int = 100,
brand: Optional[str] = None,
category: Optional[str] = None,
availability: Optional[str] = None,
marketplace: Optional[str] = None,
vendor_name: Optional[str] = None,
search: Optional[str] = None,
) -> Tuple[List[MarketplaceProduct], int]:
brand: str | None = None,
category: str | None = None,
availability: str | None = None,
marketplace: str | None = None,
vendor_name: str | None = None,
search: str | None = None,
) -> tuple[list[MarketplaceProduct], int]:
"""
Get products with filtering and pagination.
@@ -269,7 +270,7 @@ class MarketplaceProductService:
for key, value in update_data.items():
setattr(product, key, value)
product.updated_at = datetime.now(timezone.utc)
product.updated_at = datetime.now(UTC)
db.commit()
db.refresh(product)
@@ -324,7 +325,7 @@ class MarketplaceProductService:
def get_inventory_info(
self, db: Session, gtin: str
) -> Optional[InventorySummaryResponse]:
) -> InventorySummaryResponse | None:
"""
Get inventory information for a product by GTIN.
@@ -358,15 +359,14 @@ class MarketplaceProductService:
import csv
from io import StringIO
from typing import Generator, Optional
from sqlalchemy.orm import Session
def generate_csv_export(
self,
db: Session,
marketplace: Optional[str] = None,
vendor_name: Optional[str] = None,
marketplace: str | None = None,
vendor_name: str | None = None,
) -> Generator[str, None, None]:
"""
Generate CSV export with streaming for memory efficiency and proper CSV escaping.

View File

@@ -11,15 +11,17 @@ This module provides:
import logging
import random
import string
from datetime import datetime, timezone
from typing import List, Optional, Tuple
from datetime import UTC, datetime
from sqlalchemy import and_, or_
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.exceptions import (CustomerNotFoundException,
InsufficientInventoryException,
OrderNotFoundException, ValidationException)
from app.exceptions import (
CustomerNotFoundException,
InsufficientInventoryException,
OrderNotFoundException,
ValidationException,
)
from models.database.customer import Customer, CustomerAddress
from models.database.order import Order, OrderItem
from models.database.product import Product
@@ -38,7 +40,7 @@ class OrderService:
Format: ORD-{VENDOR_ID}-{TIMESTAMP}-{RANDOM}
Example: ORD-1-20250110-A1B2C3
"""
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d")
timestamp = datetime.now(UTC).strftime("%Y%m%d")
random_suffix = "".join(
random.choices(string.ascii_uppercase + string.digits, k=6)
)
@@ -266,9 +268,9 @@ class OrderService:
vendor_id: int,
skip: int = 0,
limit: int = 100,
status: Optional[str] = None,
customer_id: Optional[int] = None,
) -> Tuple[List[Order], int]:
status: str | None = None,
customer_id: int | None = None,
) -> tuple[list[Order], int]:
"""
Get orders for vendor with filtering.
@@ -306,7 +308,7 @@ class OrderService:
customer_id: int,
skip: int = 0,
limit: int = 100,
) -> Tuple[List[Order], int]:
) -> tuple[list[Order], int]:
"""Get orders for a specific customer."""
return self.get_vendor_orders(
db=db, vendor_id=vendor_id, skip=skip, limit=limit, customer_id=customer_id
@@ -335,7 +337,7 @@ class OrderService:
order.status = order_update.status
# Update timestamp based on status
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
if order_update.status == "shipped" and not order.shipped_at:
order.shipped_at = now
elif order_update.status == "delivered" and not order.delivered_at:
@@ -351,7 +353,7 @@ class OrderService:
if order_update.internal_notes:
order.internal_notes = order_update.internal_notes
order.updated_at = datetime.now(timezone.utc)
order.updated_at = datetime.now(UTC)
db.commit()
db.refresh(order)

View File

@@ -9,13 +9,15 @@ This module provides:
"""
import logging
from datetime import datetime, timezone
from typing import List, Optional, Tuple
from datetime import UTC, datetime
from sqlalchemy.orm import Session
from app.exceptions import (ProductAlreadyExistsException,
ProductNotFoundException, ValidationException)
from app.exceptions import (
ProductAlreadyExistsException,
ProductNotFoundException,
ValidationException,
)
from models.database.marketplace_product import MarketplaceProduct
from models.database.product import Product
from models.schema.product import ProductCreate, ProductUpdate
@@ -106,7 +108,7 @@ class ProductService:
if existing:
raise ProductAlreadyExistsException(
f"Product already exists in catalog"
"Product already exists in catalog"
)
# Create product
@@ -167,7 +169,7 @@ class ProductService:
for key, value in update_data.items():
setattr(product, key, value)
product.updated_at = datetime.now(timezone.utc)
product.updated_at = datetime.now(UTC)
db.commit()
db.refresh(product)
@@ -216,9 +218,9 @@ class ProductService:
vendor_id: int,
skip: int = 0,
limit: int = 100,
is_active: Optional[bool] = None,
is_featured: Optional[bool] = None,
) -> Tuple[List[Product], int]:
is_active: bool | None = None,
is_featured: bool | None = None,
) -> tuple[list[Product], int]:
"""
Get products in vendor catalog with filtering.

View File

@@ -11,7 +11,7 @@ This module provides:
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, List
from typing import Any
from sqlalchemy import func
from sqlalchemy.orm import Session
@@ -36,7 +36,7 @@ class StatsService:
# VENDOR-SPECIFIC STATISTICS
# ========================================================================
def get_vendor_stats(self, db: Session, vendor_id: int) -> Dict[str, Any]:
def get_vendor_stats(self, db: Session, vendor_id: int) -> dict[str, Any]:
"""
Get statistics for a specific vendor.
@@ -177,7 +177,7 @@ class StatsService:
def get_vendor_analytics(
self, db: Session, vendor_id: int, period: str = "30d"
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""
Get a specific vendor analytics for a time period.
@@ -283,7 +283,7 @@ class StatsService:
# SYSTEM-WIDE STATISTICS (ADMIN)
# ========================================================================
def get_comprehensive_stats(self, db: Session) -> Dict[str, Any]:
def get_comprehensive_stats(self, db: Session) -> dict[str, Any]:
"""
Get comprehensive system statistics for admin dashboard.
@@ -333,7 +333,7 @@ class StatsService:
reason=f"Database query failed: {str(e)}",
)
def get_marketplace_breakdown_stats(self, db: Session) -> List[Dict[str, Any]]:
def get_marketplace_breakdown_stats(self, db: Session) -> list[dict[str, Any]]:
"""
Get statistics broken down by marketplace.
@@ -382,7 +382,7 @@ class StatsService:
reason=f"Database query failed: {str(e)}",
)
def get_user_statistics(self, db: Session) -> Dict[str, Any]:
def get_user_statistics(self, db: Session) -> dict[str, Any]:
"""
Get user statistics for admin dashboard.
@@ -416,7 +416,7 @@ class StatsService:
operation="get_user_statistics", reason="Database query failed"
)
def get_import_statistics(self, db: Session) -> Dict[str, Any]:
def get_import_statistics(self, db: Session) -> dict[str, Any]:
"""
Get import job statistics.
@@ -457,7 +457,7 @@ class StatsService:
"success_rate": 0,
}
def get_order_statistics(self, db: Session) -> Dict[str, Any]:
def get_order_statistics(self, db: Session) -> dict[str, Any]:
"""
Get order statistics.
@@ -472,7 +472,7 @@ class StatsService:
"""
return {"total_orders": 0, "pending_orders": 0, "completed_orders": 0}
def get_product_statistics(self, db: Session) -> Dict[str, Any]:
def get_product_statistics(self, db: Session) -> dict[str, Any]:
"""
Get product statistics.
@@ -548,7 +548,7 @@ class StatsService:
.count()
)
def _get_inventory_statistics(self, db: Session) -> Dict[str, int]:
def _get_inventory_statistics(self, db: Session) -> dict[str, int]:
"""
Get inventory-related statistics.

View File

@@ -9,13 +9,12 @@ This module provides:
"""
import logging
from datetime import datetime, timezone
from typing import Any, Dict, List
from datetime import UTC, datetime
from typing import Any
from sqlalchemy.orm import Session
from app.exceptions import (UnauthorizedVendorAccessException,
ValidationException)
from app.exceptions import ValidationException
from models.database.user import User
from models.database.vendor import Role, VendorUser
@@ -27,7 +26,7 @@ class TeamService:
def get_team_members(
self, db: Session, vendor_id: int, current_user: User
) -> List[Dict[str, Any]]:
) -> list[dict[str, Any]]:
"""
Get all team members for vendor.
@@ -69,7 +68,7 @@ class TeamService:
def invite_team_member(
self, db: Session, vendor_id: int, invitation_data: dict, current_user: User
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""
Invite a new team member.
@@ -102,7 +101,7 @@ class TeamService:
user_id: int,
update_data: dict,
current_user: User,
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""
Update team member role or status.
@@ -135,7 +134,7 @@ class TeamService:
if "is_active" in update_data:
vendor_user.is_active = update_data["is_active"]
vendor_user.updated_at = datetime.now(timezone.utc)
vendor_user.updated_at = datetime.now(UTC)
db.commit()
db.refresh(vendor_user)
@@ -178,7 +177,7 @@ class TeamService:
# Soft delete
vendor_user.is_active = False
vendor_user.updated_at = datetime.now(timezone.utc)
vendor_user.updated_at = datetime.now(UTC)
db.commit()
logger.info(f"Removed user {user_id} from vendor {vendor_id}")
@@ -189,7 +188,7 @@ class TeamService:
logger.error(f"Error removing team member: {str(e)}")
raise ValidationException("Failed to remove team member")
def get_vendor_roles(self, db: Session, vendor_id: int) -> List[Dict[str, Any]]:
def get_vendor_roles(self, db: Session, vendor_id: int) -> list[dict[str, Any]]:
"""
Get available roles for vendor.

View File

@@ -12,25 +12,23 @@ This module provides classes and functions for:
import logging
import secrets
from datetime import datetime, timezone
from typing import List, Optional, Tuple
from datetime import UTC, datetime
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.exceptions import (DNSVerificationException,
DomainAlreadyVerifiedException,
DomainNotVerifiedException,
DomainVerificationFailedException,
InvalidDomainFormatException,
MaxDomainsReachedException,
MultiplePrimaryDomainsException,
ReservedDomainException,
UnauthorizedDomainAccessException,
ValidationException,
VendorDomainAlreadyExistsException,
VendorDomainNotFoundException,
VendorNotFoundException)
from app.exceptions import (
DNSVerificationException,
DomainAlreadyVerifiedException,
DomainNotVerifiedException,
DomainVerificationFailedException,
InvalidDomainFormatException,
MaxDomainsReachedException,
ReservedDomainException,
ValidationException,
VendorDomainAlreadyExistsException,
VendorDomainNotFoundException,
VendorNotFoundException,
)
from models.database.vendor import Vendor
from models.database.vendor_domain import VendorDomain
from models.schema.vendor_domain import VendorDomainCreate, VendorDomainUpdate
@@ -135,7 +133,7 @@ class VendorDomainService:
logger.error(f"Error adding domain: {str(e)}")
raise ValidationException("Failed to add domain")
def get_vendor_domains(self, db: Session, vendor_id: int) -> List[VendorDomain]:
def get_vendor_domains(self, db: Session, vendor_id: int) -> list[VendorDomain]:
"""
Get all domains for a vendor.
@@ -272,7 +270,7 @@ class VendorDomainService:
logger.error(f"Error deleting domain: {str(e)}")
raise ValidationException("Failed to delete domain")
def verify_domain(self, db: Session, domain_id: int) -> Tuple[VendorDomain, str]:
def verify_domain(self, db: Session, domain_id: int) -> tuple[VendorDomain, str]:
"""
Verify domain ownership via DNS TXT record.
@@ -313,7 +311,7 @@ class VendorDomainService:
if txt_value == domain.verification_token:
# Verification successful
domain.is_verified = True
domain.verified_at = datetime.now(timezone.utc)
domain.verified_at = datetime.now(UTC)
db.commit()
db.refresh(domain)
@@ -419,7 +417,7 @@ class VendorDomainService:
raise ReservedDomainException(domain, first_part)
def _unset_primary_domains(
self, db: Session, vendor_id: int, exclude_domain_id: Optional[int] = None
self, db: Session, vendor_id: int, exclude_domain_id: int | None = None
) -> None:
"""Unset all primary domains for vendor."""
query = db.query(VendorDomain).filter(

View File

@@ -10,18 +10,20 @@ This module provides classes and functions for:
"""
import logging
from typing import List, Optional, Tuple
from sqlalchemy import func
from sqlalchemy.orm import Session
from app.exceptions import (InvalidVendorDataException,
MarketplaceProductNotFoundException,
MaxVendorsReachedException,
ProductAlreadyExistsException,
UnauthorizedVendorAccessException,
ValidationException, VendorAlreadyExistsException,
VendorNotFoundException)
from app.exceptions import (
InvalidVendorDataException,
MarketplaceProductNotFoundException,
MaxVendorsReachedException,
ProductAlreadyExistsException,
UnauthorizedVendorAccessException,
ValidationException,
VendorAlreadyExistsException,
VendorNotFoundException,
)
from models.database.marketplace_product import MarketplaceProduct
from models.database.product import Product
from models.database.user import User
@@ -108,7 +110,7 @@ class VendorService:
limit: int = 100,
active_only: bool = True,
verified_only: bool = False,
) -> Tuple[List[Vendor], int]:
) -> tuple[list[Vendor], int]:
"""
Get vendors with filtering.
@@ -257,7 +259,7 @@ class VendorService:
limit: int = 100,
active_only: bool = True,
featured_only: bool = False,
) -> Tuple[List[Product], int]:
) -> tuple[list[Product], int]:
"""
Get products in vendor catalog with filtering.

View File

@@ -8,20 +8,23 @@ Handles:
- Role assignment
- Permission management
"""
import logging
import secrets
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
from typing import Any
from sqlalchemy.orm import Session
from app.core.permissions import get_preset_permissions
from app.exceptions import (CannotRemoveOwnerException,
InvalidInvitationTokenException,
MaxTeamMembersReachedException,
TeamInvitationAlreadyAcceptedException,
TeamMemberAlreadyExistsException,
UserNotFoundException, VendorNotFoundException)
from app.exceptions import (
CannotRemoveOwnerException,
InvalidInvitationTokenException,
MaxTeamMembersReachedException,
TeamInvitationAlreadyAcceptedException,
TeamMemberAlreadyExistsException,
UserNotFoundException,
)
from middleware.auth import AuthManager
from models.database.user import User
from models.database.vendor import Role, Vendor, VendorUser, VendorUserType
@@ -43,8 +46,8 @@ class VendorTeamService:
inviter: User,
email: str,
role_name: str,
custom_permissions: Optional[List[str]] = None,
) -> Dict[str, Any]:
custom_permissions: list[str] | None = None,
) -> dict[str, Any]:
"""
Invite a new team member to a vendor.
@@ -196,9 +199,9 @@ class VendorTeamService:
db: Session,
invitation_token: str,
password: str,
first_name: Optional[str] = None,
last_name: Optional[str] = None,
) -> Dict[str, Any]:
first_name: str | None = None,
last_name: str | None = None,
) -> dict[str, Any]:
"""
Accept a team invitation and activate account.
@@ -330,7 +333,7 @@ class VendorTeamService:
vendor: Vendor,
user_id: int,
new_role_name: str,
custom_permissions: Optional[List[str]] = None,
custom_permissions: list[str] | None = None,
) -> VendorUser:
"""
Update a team member's role.
@@ -392,7 +395,7 @@ class VendorTeamService:
db: Session,
vendor: Vendor,
include_inactive: bool = False,
) -> List[Dict[str, Any]]:
) -> list[dict[str, Any]]:
"""
Get all team members for a vendor.
@@ -445,7 +448,7 @@ class VendorTeamService:
db: Session,
vendor: Vendor,
role_name: str,
custom_permissions: Optional[List[str]] = None,
custom_permissions: list[str] | None = None,
) -> Role:
"""Get existing role or create new one with preset/custom permissions."""
# Try to find existing role with same name
@@ -492,7 +495,6 @@ class VendorTeamService:
# - Vendor name
# - Inviter name
# - Expiry date
pass
# Create service instance

View File

@@ -8,21 +8,24 @@ Handles theme CRUD operations, preset application, and validation.
import logging
import re
from typing import Dict, List, Optional
from sqlalchemy.orm import Session
from app.core.theme_presets import (THEME_PRESETS, apply_preset,
get_available_presets, get_preset_preview)
from app.core.theme_presets import (
THEME_PRESETS,
apply_preset,
get_available_presets,
get_preset_preview,
)
from app.exceptions.vendor import VendorNotFoundException
from app.exceptions.vendor_theme import (InvalidColorFormatException,
InvalidFontFamilyException,
InvalidThemeDataException,
ThemeOperationException,
ThemePresetAlreadyAppliedException,
ThemePresetNotFoundException,
ThemeValidationException,
VendorThemeNotFoundException)
from app.exceptions.vendor_theme import (
InvalidColorFormatException,
InvalidFontFamilyException,
ThemeOperationException,
ThemePresetNotFoundException,
ThemeValidationException,
VendorThemeNotFoundException,
)
from models.database.vendor import Vendor
from models.database.vendor_theme import VendorTheme
from models.schema.vendor_theme import ThemePresetPreview, VendorThemeUpdate
@@ -77,7 +80,7 @@ class VendorThemeService:
# THEME RETRIEVAL
# ============================================================================
def get_theme(self, db: Session, vendor_code: str) -> Dict:
def get_theme(self, db: Session, vendor_code: str) -> dict:
"""
Get theme for vendor. Returns default if no custom theme exists.
@@ -107,7 +110,7 @@ class VendorThemeService:
return theme.to_dict()
def _get_default_theme(self) -> Dict:
def _get_default_theme(self) -> dict:
"""
Get default theme configuration.
@@ -329,7 +332,7 @@ class VendorThemeService:
operation="apply_preset", vendor_code=vendor_code, reason=str(e)
)
def get_available_presets(self) -> List[ThemePresetPreview]:
def get_available_presets(self) -> list[ThemePresetPreview]:
"""
Get list of available theme presets.
@@ -351,7 +354,7 @@ class VendorThemeService:
# THEME DELETION
# ============================================================================
def delete_theme(self, db: Session, vendor_code: str) -> Dict:
def delete_theme(self, db: Session, vendor_code: str) -> dict:
"""
Delete custom theme for vendor (reverts to default).