so much stuff lol
This commit is contained in:
146
app/services/file_processing_service.py
Normal file
146
app/services/file_processing_service.py
Normal file
@ -0,0 +1,146 @@
|
||||
from typing import Optional, List, Dict
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import Session
|
||||
from app.db.database import transaction
|
||||
from app.models.inventory import Inventory
|
||||
from app.models.tcgplayer_product import TCGPlayerProduct
|
||||
from app.services.inventory_service import InventoryService
|
||||
|
||||
class FileProcessingService:
|
||||
def __init__(self, cache_dir: str = "app/data/cache/tcgplayer"):
|
||||
self.cache_dir = cache_dir
|
||||
self.inventory_service = InventoryService()
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
|
||||
def _get_cache_path(self, filename: str) -> str:
|
||||
return os.path.join(self.cache_dir, filename)
|
||||
|
||||
async def _cache_export(self, file_bytes: bytes, export_type: str):
|
||||
cache_path = self._get_cache_path(f"{export_type}_export.csv")
|
||||
with open(cache_path, 'wb') as f:
|
||||
f.write(file_bytes)
|
||||
|
||||
async def _load_cached_export(self, export_type: str) -> Optional[bytes]:
|
||||
cache_path = self._get_cache_path(f"{export_type}_export.csv")
|
||||
if os.path.exists(cache_path):
|
||||
with open(cache_path, 'rb') as f:
|
||||
return f.read()
|
||||
return None
|
||||
|
||||
async def process_tcgplayer_export(self, db: Session, file_bytes: bytes, export_type: str = "live", use_cache: bool = False) -> dict:
|
||||
"""
|
||||
Process a TCGPlayer export file and load it into the inventory table.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
file_bytes: The downloaded file content as bytes
|
||||
export_type: Type of export (staged, live, pricing)
|
||||
use_cache: Whether to use cached export file for development
|
||||
|
||||
Returns:
|
||||
dict: Processing statistics
|
||||
"""
|
||||
stats = {
|
||||
"total_rows": 0,
|
||||
"processed_rows": 0,
|
||||
"errors": 0,
|
||||
"error_messages": []
|
||||
}
|
||||
|
||||
try:
|
||||
# For development, use cached file if available
|
||||
if use_cache:
|
||||
cached_bytes = await self._load_cached_export(export_type)
|
||||
if cached_bytes:
|
||||
file_bytes = cached_bytes
|
||||
else:
|
||||
await self._cache_export(file_bytes, export_type)
|
||||
|
||||
# Convert bytes to string and create a file-like object
|
||||
file_content = file_bytes.decode('utf-8')
|
||||
file_like = io.StringIO(file_content)
|
||||
|
||||
# Read CSV file
|
||||
csv_reader = csv.DictReader(file_like)
|
||||
|
||||
with transaction(db):
|
||||
for row in csv_reader:
|
||||
stats["total_rows"] += 1
|
||||
try:
|
||||
# Process each row and create/update inventory item in database
|
||||
inventory_data = self._map_tcgplayer_row_to_inventory(row)
|
||||
tcgplayer_id = inventory_data["tcgplayer_id"]
|
||||
|
||||
# Check if inventory item already exists
|
||||
existing_item = self.inventory_service.get_by_tcgplayer_id(db, tcgplayer_id)
|
||||
|
||||
# Find matching TCGPlayer product
|
||||
product_id = int(tcgplayer_id) if tcgplayer_id.isdigit() else None
|
||||
if product_id:
|
||||
tcg_product = db.query(TCGPlayerProduct).filter(TCGPlayerProduct.product_id == product_id).first()
|
||||
if tcg_product:
|
||||
# Update inventory data with product information if available
|
||||
inventory_data.update({
|
||||
"product_name": tcg_product.name,
|
||||
"photo_url": tcg_product.image_url,
|
||||
"rarity": tcg_product.ext_rarity,
|
||||
"number": tcg_product.ext_number
|
||||
})
|
||||
|
||||
if existing_item:
|
||||
# Update existing item
|
||||
self.inventory_service.update(db, existing_item, inventory_data)
|
||||
else:
|
||||
# Create new item
|
||||
self.inventory_service.create(db, inventory_data)
|
||||
|
||||
stats["processed_rows"] += 1
|
||||
except Exception as e:
|
||||
stats["errors"] += 1
|
||||
stats["error_messages"].append(f"Error processing row {stats['total_rows']}: {str(e)}")
|
||||
|
||||
return stats
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to process TCGPlayer export: {str(e)}")
|
||||
|
||||
def _map_tcgplayer_row_to_inventory(self, row: dict) -> dict:
|
||||
"""
|
||||
Map TCGPlayer export row to inventory model fields.
|
||||
"""
|
||||
def safe_float(value: str) -> float:
|
||||
"""Convert string to float, returning 0.0 for empty strings or invalid values"""
|
||||
try:
|
||||
return float(value) if value else 0.0
|
||||
except ValueError:
|
||||
return 0.0
|
||||
|
||||
def safe_int(value: str) -> int:
|
||||
"""Convert string to int, returning 0 for empty strings or invalid values"""
|
||||
try:
|
||||
return int(value) if value else 0
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
return {
|
||||
"tcgplayer_id": row.get("TCGplayer Id", ""),
|
||||
"product_line": row.get("Product Line", ""),
|
||||
"set_name": row.get("Set Name", ""),
|
||||
"product_name": row.get("Product Name", ""),
|
||||
"title": row.get("Title", ""),
|
||||
"number": row.get("Number", ""),
|
||||
"rarity": row.get("Rarity", ""),
|
||||
"condition": row.get("Condition", ""),
|
||||
"tcg_market_price": safe_float(row.get("TCG Market Price", "")),
|
||||
"tcg_direct_low": safe_float(row.get("TCG Direct Low", "")),
|
||||
"tcg_low_price_with_shipping": safe_float(row.get("TCG Low Price With Shipping", "")),
|
||||
"tcg_low_price": safe_float(row.get("TCG Low Price", "")),
|
||||
"total_quantity": safe_int(row.get("Total Quantity", "")),
|
||||
"add_to_quantity": safe_int(row.get("Add to Quantity", "")),
|
||||
"tcg_marketplace_price": safe_float(row.get("TCG Marketplace Price", "")),
|
||||
"photo_url": row.get("Photo URL", "")
|
||||
}
|
Reference in New Issue
Block a user