giga_tcg/services/data.py
2025-01-31 13:05:48 -05:00

149 lines
7.1 KiB
Python

from sqlalchemy.orm import Session
import logging
from fastapi import BackgroundTasks
from db.models import TCGPlayerGroups, SetCodeGroupIdMapping, ManaboxExportData, TCGPlayerProduct, ManaboxTCGPlayerMapping, UnmatchedManaboxData, TCGPlayerInventory
from db.utils import db_transaction
import uuid
from services.tcgplayer import TCGPlayerService
from sqlalchemy.sql import exists
logger = logging.getLogger(__name__)
class DataService:
def __init__(self, db: Session, tcgplayer_service: TCGPlayerService):
self.db = db
self.tcgplayer_service = tcgplayer_service
def _normalize_rarity(self, rarity: str) -> str:
if rarity.lower() == "rare":
return "R"
elif rarity.lower() == "mythic":
return "M"
elif rarity.lower() == "uncommon":
return "U"
elif rarity.lower() == "common":
return "C"
elif rarity.lower() in ["R", "M", "U", "C"]:
return rarity.upper()
else:
raise ValueError(f"Invalid rarity: {rarity}")
def _normalize_condition(self, condition: str, foil: str) -> str:
if condition.lower() == "near_mint":
condition1 = "Near Mint"
else:
raise ValueError(f"Invalid condition: {condition}")
if foil.lower() == "foil":
condition2 = " Foil"
elif foil.lower() == "normal":
condition2 = ""
else:
raise ValueError(f"Invalid foil: {foil}")
return condition1 + condition2
def _normalize_number(self, number: str) -> str:
return str(number.split(".")[0])
def _convert_set_code_to_group_id(self, set_code: str) -> str:
group = self.db.query(TCGPlayerGroups).filter(TCGPlayerGroups.abbreviation == set_code).first()
return group.group_id
def _add_set_group_mapping(self, set_code: str, group_id: str) -> None:
with db_transaction(self.db):
self.db.add(SetCodeGroupIdMapping(id=str(uuid.uuid4()), set_code=set_code, group_id=group_id))
def _get_set_codes(self, **filters) -> list:
query = self.db.query(ManaboxExportData.set_code).distinct()
for field, value in filters.items():
if value is not None:
query = query.filter(getattr(ManaboxExportData, field) == value)
return [code[0] for code in query.all()]
async def bg_set_manabox_tcg_relationship(self, box_id: str = None, upload_id: str = None) -> None:
if not bool(box_id) ^ bool(upload_id):
raise ValueError("Must provide exactly one of box_id or upload_id")
filters = {"box_id": box_id} if box_id else {"upload_id": upload_id}
set_codes = self._get_set_codes(**filters)
for set_code in set_codes:
try:
group_id = self._convert_set_code_to_group_id(set_code)
except AttributeError:
logger.warning(f"No group found for set code {set_code}")
continue
self._add_set_group_mapping(set_code, group_id)
# update pricing for groups
if self.db.query(TCGPlayerProduct).filter(TCGPlayerProduct.group_id == group_id).count() == 0:
self.tcgplayer_service.update_pricing(set_name_ids={"set_name_ids":[group_id]})
# match manabox data to tcgplayer pricing data
# match on manabox - set_code (through group_id), collector_number, foil, rarity, condition
# match on tcgplayer - group_id, number, rarity, condition (condition + foil)
# use normalizing functions
matched_records = self.db.query(ManaboxExportData).filter(ManaboxExportData.set_code.in_(set_codes)).all()
for record in matched_records:
rarity = self._normalize_rarity(record.rarity)
condition = self._normalize_condition(record.condition, record.foil)
number = self._normalize_number(record.collector_number)
group_id = self._convert_set_code_to_group_id(record.set_code)
tcg_record = self.db.query(TCGPlayerProduct).filter(
TCGPlayerProduct.group_id == group_id,
TCGPlayerProduct.number == number,
TCGPlayerProduct.rarity == rarity,
TCGPlayerProduct.condition == condition
).all()
if len(tcg_record) == 0:
logger.warning(f"No match found for {record.name}")
if self.db.query(UnmatchedManaboxData).filter(UnmatchedManaboxData.manabox_id == record.id).count() == 0:
with db_transaction(self.db):
self.db.add(UnmatchedManaboxData(id=str(uuid.uuid4()), manabox_id=record.id, reason="No match found"))
elif len(tcg_record) > 1:
logger.warning(f"Multiple matches found for {record.name}")
if self.db.query(UnmatchedManaboxData).filter(UnmatchedManaboxData.manabox_id == record.id).count() == 0:
with db_transaction(self.db):
self.db.add(UnmatchedManaboxData(id=str(uuid.uuid4()), manabox_id=record.id, reason="Multiple matches found"))
else:
with db_transaction(self.db):
self.db.add(ManaboxTCGPlayerMapping(id=str(uuid.uuid4()), manabox_id=record.id, tcgplayer_id=tcg_record[0].id))
async def bg_set_tcg_inventory_product_relationship(self, export_id: str) -> None:
inventory_without_product = (
self.db.query(TCGPlayerInventory.tcgplayer_id, TCGPlayerInventory.set_name)
.filter(TCGPlayerInventory.total_quantity > 0)
.filter(TCGPlayerInventory.product_line == "Magic")
.filter(TCGPlayerInventory.export_id == export_id)
.filter(TCGPlayerInventory.tcgplayer_product_id.is_(None))
.filter(~exists().where(
TCGPlayerProduct.id == TCGPlayerInventory.tcgplayer_product_id
))
.all()
)
set_names = list(set(inv.set_name for inv in inventory_without_product
if inv.set_name is not None and isinstance(inv.set_name, str)))
group_ids = self.db.query(TCGPlayerGroups.group_id).filter(
TCGPlayerGroups.name.in_(set_names)
).all()
group_ids = [str(group_id[0]) for group_id in group_ids]
self.tcgplayer_service.update_pricing(set_name_ids={"set_name_ids": group_ids})
for inventory in inventory_without_product:
product = self.db.query(TCGPlayerProduct).filter(
TCGPlayerProduct.tcgplayer_id == inventory.tcgplayer_id
).first()
if product:
with db_transaction(self.db):
inventory_record = self.db.query(TCGPlayerInventory).filter(
TCGPlayerInventory.tcgplayer_id == inventory.tcgplayer_id,
TCGPlayerInventory.export_id == export_id
).first()
if inventory_record:
inventory_record.tcgplayer_product_id = product.id
self.db.add(inventory_record)