ai_giga_tcg/app/services/address_label_service.py

143 lines
5.8 KiB
Python

from typing import List, Dict, Optional, Literal
import csv
import os
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML
import logging
import asyncio
from concurrent.futures import ThreadPoolExecutor
from sqlalchemy.orm import Session
from app.schemas.file import FileInDB
from app.services.base_service import BaseService
logger = logging.getLogger(__name__)
class AddressLabelService(BaseService):
def __init__(self):
super().__init__(None) # BaseService doesn't need a model for this service
self.template_dir = Path("app/data/assets/templates")
self.env = Environment(loader=FileSystemLoader(str(self.template_dir)))
self.templates = {
"dk1241": self.env.get_template("address_label_dk1241.html"),
"dk1201": self.env.get_template("address_label_dk1201.html")
}
self.return_address_path = "file://" + os.path.abspath("app/data/assets/images/ccrcardsaddress.png")
self.executor = ThreadPoolExecutor()
async def get_or_create_address_labels(self, db: Session, order_ids: list[str], label_type: Literal["dk1201", "dk1241"]) -> List[FileInDB]:
"""Get or create address labels for the specified orders.
Args:
db: Database session
order_ids: List of TCGPlayer order numbers
label_type: Type of label to generate ("dk1201" or "dk1241")
Returns:
List of FileInDB objects for generated PDF files
"""
# check if address labels exist for the order ids
file_service = self.get_service('file')
# honestly i just dont feel like caching the address labels bc its hard
shipping_csv = await file_service.get_file_by_metadata(db, "order_ids", order_ids, "shipping_csv", "text/csv")
if shipping_csv:
return await self.generate_labels_from_csv(db, shipping_csv.path, label_type)
else:
order_management = self.get_service('order_management')
shipping_csv = await order_management.get_shipping_csv(db, order_ids)
return await self.generate_labels_from_csv(db, shipping_csv.path, label_type)
async def generate_labels_from_csv(self, db: Session, csv_path: str, label_type: Literal["dk1201", "dk1241"]) -> List[FileInDB]:
"""Generate address labels from a CSV file and save them as PDFs.
Args:
db: Database session
csv_path: Path to the CSV file containing address data
label_type: Type of label to generate ("6x4" or "dk1201")
Returns:
List of FileInDB objects for generated PDF files
"""
generated_files = []
# Read CSV file in a thread pool
loop = asyncio.get_event_loop()
rows = await loop.run_in_executor(self.executor, self._read_csv, csv_path)
for row in rows:
# if value of Value Of Products is greater than 50, skip
if row.get('Value Of Products') and float(row['Value Of Products']) > 50:
logger.info(f"Skipping order {row.get('Order #')} because value of products is greater than 50")
continue
# Generate label for each row
file_record = await self._generate_single_label(db, row, label_type)
if file_record:
generated_files.append(file_record)
return generated_files
def _read_csv(self, csv_path: str) -> List[Dict[str, str]]:
"""Read CSV file and return list of rows."""
with open(csv_path, 'r') as csvfile:
reader = csv.DictReader(csvfile)
return list(reader)
async def _generate_single_label(self, db: Session, row: Dict[str, str], label_type: Literal["dk1201", "dk1241"]) -> Optional[FileInDB]:
"""Generate a single address label PDF.
Args:
db: Database session
row: Dictionary containing address data
label_type: Type of label to generate ("6x4" or "dk1201")
Returns:
FileInDB object for the generated PDF file or None if generation failed
"""
try:
# Prepare template data
template_data = {
"recipient_name": f"{row['FirstName']} {row['LastName']}",
"address_line1": row['Address1'],
"address_line2": row['Address2'],
"city": row['City'],
"state": row['State'],
"zip_code": row['PostalCode']
}
# Add return address path only for 6x4 labels
if label_type == "dk1241":
template_data["return_address_path"] = self.return_address_path
# Render HTML
html_content = self.templates[label_type].render(**template_data)
# Generate PDF in a thread pool
loop = asyncio.get_event_loop()
pdf_content = await loop.run_in_executor(
self.executor,
lambda: HTML(string=html_content).write_pdf()
)
# Prepare metadata
metadata = {
"order_number": row.get('Order #'),
"label_type": label_type
}
# Save using FileService
filename = f"{row['Order #']}_{label_type}.pdf"
file_record = await self.file_service.save_file(
db=db,
file_data=pdf_content,
filename=filename,
subdir="address_labels",
file_type="address_label",
content_type="application/pdf",
metadata=metadata
)
return file_record
except Exception as e:
logger.error(f"Error generating label for order {row.get('Order #', 'unknown')}: {str(e)}")
return None