from sqlalchemy.orm import Session from db.utils import db_transaction from db.models import Product, File, CardManabox, Card, StagedFileProduct from io import StringIO import pandas as pd from services.file import FileService 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): self.db = db self.file_service = file_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 create_card(self, card_manabox: CardManabox) -> Card: card = Card( product_id = 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 ) 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) 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 = 'prepared' except Exception as e: with db_transaction(self.db): file.status = 'error' raise e