This commit is contained in:
2025-04-13 22:34:47 -04:00
parent 18b32c8514
commit 593e8960b7
6 changed files with 40 additions and 228 deletions

View File

@ -272,7 +272,7 @@ class MTGJSONService:
"sku_id": str(sku.get("skuId")),
"product_id": str(sku.get("productId")),
"condition": sku.get("condition"),
"finish": sku.get("finish", "NORMAL"), # Default to NORMAL if not specified
"finish": sku.get("finish"),
"language": sku.get("language"),
"printing": sku.get("printing"),
"card_id": card_uuid,

View File

@ -20,7 +20,7 @@ class OrderManagementService(BaseTCGPlayerService):
self.shipping_endpoint = f"/shipping/export{self.API_VERSION}"
async def get_orders(self):
async def get_orders(self, open_only: bool = False):
search_from = 0
orders = []
while True:
@ -36,6 +36,9 @@ class OrderManagementService(BaseTCGPlayerService):
"from": search_from,
"size": 25
}
if open_only:
payload["filters"]["orderStatus"] = ["Processing","ReadyToShip","Received","Pulling","ReadyForPickup"]
payload["filters"]["fulfillmentTypes"] = ["Normal"]
response = await self._make_request("POST", self.order_search_endpoint, data=payload, headers=self._get_headers("POST", "application/json"), auth_required=True)
if len(response.get("orders")) == 0:
break

View File

@ -1,182 +0,0 @@
from typing import Optional, Union, Literal
import os
import aiohttp
import cups
from pathlib import Path
from pdf2image import convert_from_path
from brother_ql.conversion import convert
from brother_ql.raster import BrotherQLRaster
import logging
import asyncio
import time
from datetime import datetime, timedelta
logger = logging.getLogger(__name__)
class PrintService:
def __init__(self, printer_name: Optional[str] = None, printer_api_url: str = "http://localhost:8000/print",
min_print_interval: int = 30):
"""Initialize the print service.
Args:
printer_name: Name of the printer to use. If None, will use default printer.
printer_api_url: URL of the printer API endpoint
min_print_interval: Minimum time in seconds between print requests for label printer
"""
self.printer_name = printer_name
self.printer_api_url = printer_api_url
self.status_url = printer_api_url.replace('/print', '/status')
self.conn = cups.Connection()
self.cache_dir = Path("app/data/cache/prints")
self.cache_dir.mkdir(parents=True, exist_ok=True)
# Rate limiting and coordination
self.min_print_interval = min_print_interval
self._last_print_time = None
self._print_lock = asyncio.Lock()
async def _wait_for_printer_ready(self, max_wait: int = 300) -> bool:
"""Wait for the printer to be ready.
Args:
max_wait: Maximum time to wait in seconds
Returns:
bool: True if printer is ready, False if timeout
"""
start_time = time.time()
while time.time() - start_time < max_wait:
try:
async with aiohttp.ClientSession() as session:
async with session.get(self.status_url) as response:
if response.status == 200:
data = await response.json()
if data.get('status') == 'ready':
return True
except Exception as e:
logger.warning(f"Error checking printer status: {e}")
await asyncio.sleep(1)
logger.error("Timeout waiting for printer to be ready")
return False
async def print_file(self, file_path: Union[str, Path], printer_type: Literal["regular", "label"] = "regular", label_type: Optional[Literal["dk1201", "dk1241"]] = None) -> bool:
"""Print a PDF or PNG file.
Args:
file_path: Path to the PDF or PNG file
printer_type: Type of printer ("regular" or "label")
label_type: Type of label to use ("dk1201" or "dk1241"). Only used when printer_type is "label".
Returns:
bool: True if print was successful, False otherwise
"""
try:
file_path = Path(file_path)
if not file_path.exists():
logger.error(f"File not found: {file_path}")
return False
if printer_type == "regular":
# For regular printers, use CUPS
printer = self.printer_name or self.conn.getDefault()
if not printer:
logger.error("No default printer found")
return False
job_id = self.conn.printFile(
printer,
str(file_path),
f"{file_path.suffix.upper()} Print",
{}
)
logger.info(f"Print job {job_id} submitted to printer {printer}")
return True
else:
# For label printers, we need to coordinate requests
if label_type is None:
logger.error("label_type must be specified when printing to label printer")
return False
# Wait for printer to be ready
if not await self._wait_for_printer_ready():
logger.error("Printer not ready after waiting")
return False
if file_path.suffix.lower() == '.pdf':
# Convert PDF to image first
images = convert_from_path(file_path)
if not images:
logger.error(f"No images could be extracted from {file_path}")
return False
image = images[0] # Only use first page
else:
# For PNG files, we can use them directly
from PIL import Image
image = Image.open(file_path)
# Convert to label format
qlr = BrotherQLRaster("QL-1100")
qlr.exception_on_warning = True
# Get label size based on type
label_size = "29x90" if label_type == "dk1201" else "102x152"
converted_image = convert(
qlr=qlr,
images=[image],
label=label_size,
rotate="0",
threshold=70.0,
dither=False,
compress=False,
red=False,
dpi_600=False,
hq=True,
cut=True
)
# Cache the converted binary data
cache_path = self.cache_dir / f"{file_path.stem}_{label_type}_converted.bin"
with open(cache_path, "wb") as f:
f.write(converted_image)
# Send to API
return await self._send_print_request(cache_path)
except Exception as e:
logger.error(f"Error printing file {file_path}: {str(e)}")
return False
async def _send_print_request(self, file_path: Union[str, Path]) -> bool:
"""Send print data to printer API.
Args:
file_path: Path to the binary data file to send to the printer
Returns:
bool: True if request was successful, False otherwise
"""
try:
# Read the binary data from the cache file
with open(file_path, "rb") as f:
print_data = f.read()
# Send the request to the printer API using aiohttp
async with aiohttp.ClientSession() as session:
async with session.post(
self.printer_api_url,
data=print_data,
timeout=30
) as response:
if response.status == 200:
return True
else:
response_text = await response.text()
logger.error(f"Print request failed with status {response.status}: {response_text}")
return False
except Exception as e:
logger.error(f"Error sending print request: {str(e)}")
return False

View File

@ -51,5 +51,11 @@ class BaseScheduler:
async def shutdown(self) -> None:
"""Shutdown the scheduler"""
self.scheduler.shutdown()
logger.info("Scheduler stopped")
try:
self.scheduler.shutdown()
logger.info("Scheduler stopped")
except AttributeError as e:
if "'NoneType' object has no attribute 'call_soon_threadsafe'" in str(e):
logger.warning("Event loop already closed, skipping scheduler shutdown")
else:
raise