from sqlalchemy.orm import Session from db.utils import db_transaction from db.models import Product, File, CardManabox, Card, StagedFileProduct, CardTCGPlayer from io import StringIO import pandas as pd from services.file import FileService from services.tcgplayer import TCGPlayerService from uuid import uuid4 as uuid import logging logger = logging.getLogger(__name__) class ManaboxRow: def __init__(self, row: pd.Series): self.name = row['name'] self.set_code = row['set_code'] self.set_name = row['set_name'] self.collector_number = row['collector_number'] self.foil = row['foil'] self.rarity = row['rarity'] self.manabox_id = row['manabox_id'] self.scryfall_id = row['scryfall_id'] self.condition = row['condition'] self.language = row['language'] self.quantity = row['quantity'] class ProductService: def __init__(self, db: Session, file_service: FileService, tcgplayer_service: TCGPlayerService): self.db = db self.file_service = file_service self.tcgplayer_service = tcgplayer_service def _format_manabox_df(self, df: pd.DataFrame) -> pd.DataFrame: # format columns df.columns = df.columns.str.lower() df.columns = df.columns.str.replace(' ', '_') return df def _manabox_file_to_df(self, file: File) -> pd.DataFrame: with open(file.filepath, 'rb') as f: content = f.read() content = content.decode('utf-8') df = pd.read_csv(StringIO(content)) df = self._format_manabox_df(df) return df def create_staged_product(self, file: File, card_manabox:CardManabox, row: ManaboxRow) -> StagedFileProduct: staged_product = StagedFileProduct( id = str(uuid()), file_id = file.id, product_id = card_manabox.product_id, quantity = row.quantity ) with db_transaction(self.db): self.db.add(staged_product) return staged_product def create_card_manabox(self, manabox_row: ManaboxRow) -> CardManabox: card_manabox = CardManabox( product_id = str(uuid()), name = manabox_row.name, set_code = manabox_row.set_code, set_name = manabox_row.set_name, collector_number = manabox_row.collector_number, foil = manabox_row.foil, rarity = manabox_row.rarity, manabox_id = manabox_row.manabox_id, scryfall_id = manabox_row.scryfall_id, condition = manabox_row.condition, language = manabox_row.language ) return card_manabox def create_product(self, card_manabox: CardManabox) -> Product: product = Product( id = card_manabox.product_id, name = card_manabox.name, set_code = card_manabox.set_code, set_name = card_manabox.set_name, type = 'card', product_line = 'mtg' ) return product def get_tcgplayer_card(self, card_manabox: CardManabox) -> CardTCGPlayer: # check if tcgplayer_id exists for product_id in CardTCGPlayer tcgplayer_card = self.db.query(CardTCGPlayer).filter(CardTCGPlayer.product_id == card_manabox.product_id).first() if tcgplayer_card: return tcgplayer_card # if not, get tcgplayer_id from tcgplayer_service tcgplayer_card = self.tcgplayer_service.get_tcgplayer_card(card_manabox) return tcgplayer_card def create_card(self, card_manabox: CardManabox) -> Card: tcgplayer_card = self.get_tcgplayer_card(card_manabox) card = Card( product_id = tcgplayer_card.product_id if tcgplayer_card else card_manabox.product_id, number = card_manabox.collector_number, foil = card_manabox.foil, rarity = card_manabox.rarity, condition = card_manabox.condition, language = card_manabox.language, scryfall_id = card_manabox.scryfall_id, manabox_id = card_manabox.manabox_id, tcgplayer_id = tcgplayer_card.tcgplayer_id if tcgplayer_card else None ) return card def card_manabox_lookup_create_if_not_exist(self, manabox_row: ManaboxRow) -> CardManabox: # query based on all fields in manabox_row card_manabox = self.db.query(CardManabox).filter( CardManabox.name == manabox_row.name, CardManabox.set_code == manabox_row.set_code, CardManabox.set_name == manabox_row.set_name, CardManabox.collector_number == manabox_row.collector_number, CardManabox.foil == manabox_row.foil, CardManabox.rarity == manabox_row.rarity, CardManabox.manabox_id == manabox_row.manabox_id, CardManabox.scryfall_id == manabox_row.scryfall_id, CardManabox.condition == manabox_row.condition, CardManabox.language == manabox_row.language ).first() if not card_manabox: # create new card_manabox, card, and product with db_transaction(self.db): card_manabox = self.create_card_manabox(manabox_row) product = self.create_product(card_manabox) card = self.create_card(card_manabox) card_manabox.product_id = card.product_id product.id = card.product_id self.db.add(card_manabox) self.db.add(product) self.db.add(card) return card_manabox def bg_process_manabox_file(self, file_id: str): try: file = self.file_service.get_file(file_id) df = self._manabox_file_to_df(file) for index, row in df.iterrows(): manabox_row = ManaboxRow(row) card_manabox = self.card_manabox_lookup_create_if_not_exist(manabox_row) staged_product = self.create_staged_product(file, card_manabox, row) # update file status with db_transaction(self.db): file.status = 'completed' except Exception as e: with db_transaction(self.db): file.status = 'error' raise e