commit 893b229cc6b35c09181a84050f34fb79024e41c2 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 22:14:08 2025 -0500 j commit 06f539aea2f4fff9da7038d43d0de553c4423796 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:55:30 2025 -0500 fk commit d0c2960ec9f334448d2eb3573b9d7817482abf46 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:50:53 2025 -0500 frick commit 6b1362c166fc5f51c3bcf316a99116f0d11074a5 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:49:40 2025 -0500 database commit 8cadc6df4c817d9d05503807e56287fd00e5e939 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:38:09 2025 -0500 asdf commit 1ca6f9868452e34143b8df4a412be35e6902a31e Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:32:50 2025 -0500 fffff commit 8bb337a9c35e830ef9ce3dac0a0f2df3fe9bc5a0 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:31:13 2025 -0500 ffff commit 65aba280c55fa09c6a37f688f485efab1f70792b Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:26:16 2025 -0500 aa commit 59ef03a59ee4a15c30e080a1aef7c31c0214a2e3 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:24:21 2025 -0500 asdf commit f44d5740fc9315ccb0792ecac3e8ec9f28f171be Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:23:32 2025 -0500 aaa commit 13c96b164316b4908d9d01e454cbdc9103157558 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:18:54 2025 -0500 sdf commit 949c795fd13d93c9618613740fb093f6bb7b7710 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 21:17:53 2025 -0500 asdf commit 8c3cd423fe228e8aff112a050170246a5fc9f8bd Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:56:01 2025 -0500 app2 commit 78eafc739ebb7f100f657964b3ad8f4937a4046b Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:54:55 2025 -0500 app commit dc47eced143e77ebec415bdfbe209d9466b7bcf1 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:43:15 2025 -0500 asdfasdfasdf commit e24bcae88cf8c14ea543f49b639b2976c627d201 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:39:44 2025 -0500 a commit c894451bfe790c97ac0e01085615d7c7288a39da Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:38:20 2025 -0500 req commit 3d09869562a96b5adc7c4be279bc8c003bbb37b2 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:33:27 2025 -0500 wrong number = code dont work lol i love computers commit 4c93a1271b8aea159cf53f8d7879b00513886d6f Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 20:29:39 2025 -0500 q commit 1f5361da88fe3903a1e92a345fa56bb390f69d92 Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 18:27:20 2025 -0500 same as original code now -5 days of my life commit 511b070cbbcd29b4e784e9a09d58481e50e6e82f Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 13:52:28 2025 -0500 pricey worky commit 964fdd641b63530c59e038ebc7d1e01e9570d75c Author: zman <joshua.k.rzemien@gmail.com> Date: Fri Feb 7 11:37:29 2025 -0500 prep for pricing service work commit a78c3bcba303c2605b6277c1db33b155abe4db1b Author: zman <joshua.k.rzemien@gmail.com> Date: Wed Feb 5 21:51:22 2025 -0500 more stuff yay commit bd9cfca7a95c89b2140eec57bf52bc84432b9a4e Author: zman <joshua.k.rzemien@gmail.com> Date: Tue Feb 4 22:30:33 2025 -0500 GIGA FIXED EVERYTHING OMG commit 85510a46713e0ac660e70c7befb4e94ccf11912e Author: zman <joshua.k.rzemien@gmail.com> Date: Tue Feb 4 00:01:34 2025 -0500 data model change and some new services
256 lines
8.3 KiB
Python
256 lines
8.3 KiB
Python
from uuid import uuid4
|
|
from typing import List, TypedDict
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.db.utils import db_transaction
|
|
from app.db.models import (
|
|
Warehouse,
|
|
User,
|
|
StagedFileProduct,
|
|
StorageBlock,
|
|
ProductBlock,
|
|
File,
|
|
CardTCGPlayer
|
|
)
|
|
|
|
class ProductAttributes(TypedDict):
|
|
"""Attributes for a product to be stored."""
|
|
product_id: str
|
|
card_number: str
|
|
|
|
class StorageService:
|
|
"""Service for managing product storage and warehouse operations."""
|
|
|
|
def __init__(self, db: Session) -> None:
|
|
"""Initialize the storage service.
|
|
|
|
Args:
|
|
db: SQLAlchemy database session
|
|
"""
|
|
self.db = db
|
|
|
|
def get_or_create_user(self, username: str) -> User:
|
|
"""Get an existing user or create a new one if not found.
|
|
|
|
Args:
|
|
username: Username to look up or create
|
|
|
|
Returns:
|
|
The existing or newly created User
|
|
"""
|
|
user = self.db.query(User).filter(User.username == username).first()
|
|
if user is None:
|
|
user = User(
|
|
id=str(uuid4()),
|
|
username=username
|
|
)
|
|
with db_transaction(self.db):
|
|
self.db.add(user)
|
|
return user
|
|
|
|
def get_or_create_warehouse(self) -> Warehouse:
|
|
"""Get the default warehouse or create it if it doesn't exist.
|
|
|
|
Returns:
|
|
The existing or newly created Warehouse
|
|
"""
|
|
warehouse = self.db.query(Warehouse).first()
|
|
user = self.get_or_create_user('admin')
|
|
if warehouse is None:
|
|
warehouse = Warehouse(
|
|
id=str(uuid4()),
|
|
user_id=user.id
|
|
)
|
|
with db_transaction(self.db):
|
|
self.db.add(warehouse)
|
|
return warehouse
|
|
|
|
def get_staged_product(self, file_id: str) -> List[StagedFileProduct]:
|
|
"""Get all staged products for a given file.
|
|
|
|
Args:
|
|
file_id: ID of the file to get staged products for
|
|
|
|
Returns:
|
|
List of staged products
|
|
"""
|
|
return self.db.query(StagedFileProduct).filter(
|
|
StagedFileProduct.file_id == file_id
|
|
).all()
|
|
|
|
def get_storage_block_name(self, warehouse: Warehouse, file_id: str) -> str:
|
|
"""Generate a unique name for a new storage block.
|
|
|
|
Args:
|
|
warehouse: Warehouse the block belongs to
|
|
file_id: ID of the file being processed
|
|
|
|
Returns:
|
|
Unique storage block name
|
|
|
|
Raises:
|
|
ValueError: If no file is found with the given ID
|
|
"""
|
|
current_file = self.db.query(File).filter(File.id == file_id).first()
|
|
if not current_file:
|
|
raise ValueError(f"No file found with id {file_id}")
|
|
|
|
storage_block_type = 'rare' if 'rare' in current_file.type else 'common'
|
|
prefix = storage_block_type[0]
|
|
|
|
latest_block = (
|
|
self.db.query(StorageBlock)
|
|
.filter(
|
|
StorageBlock.warehouse_id == warehouse.id,
|
|
StorageBlock.type == storage_block_type
|
|
)
|
|
.order_by(StorageBlock.date_created.desc())
|
|
.first()
|
|
)
|
|
|
|
start_number = 1 if not latest_block else int(latest_block.name[1:]) + 1
|
|
|
|
while True:
|
|
new_name = f"{prefix}{start_number}"
|
|
exists = (
|
|
self.db.query(StorageBlock)
|
|
.filter(
|
|
StorageBlock.warehouse_id == warehouse.id,
|
|
StorageBlock.name == new_name
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if not exists:
|
|
return new_name
|
|
start_number += 1
|
|
|
|
def create_storage_block(self, warehouse: Warehouse, file_id: str) -> StorageBlock:
|
|
"""Create a new storage block for the given warehouse and file.
|
|
|
|
Args:
|
|
warehouse: Warehouse to create the block in
|
|
file_id: ID of the file being processed
|
|
|
|
Returns:
|
|
Newly created StorageBlock
|
|
|
|
Raises:
|
|
ValueError: If no file is found with the given ID
|
|
"""
|
|
current_file = self.db.query(File).filter(File.id == file_id).first()
|
|
if not current_file:
|
|
raise ValueError(f"No file found with id {file_id}")
|
|
|
|
storage_block_type = 'rare' if 'rare' in current_file.type else 'common'
|
|
|
|
storage_block = StorageBlock(
|
|
id=str(uuid4()),
|
|
warehouse_id=warehouse.id,
|
|
name=self.get_storage_block_name(warehouse, file_id),
|
|
type=storage_block_type
|
|
)
|
|
with db_transaction(self.db):
|
|
self.db.add(storage_block)
|
|
return storage_block
|
|
|
|
def add_staged_product_to_product_block(
|
|
self,
|
|
staged_product: StagedFileProduct,
|
|
storage_block: StorageBlock,
|
|
product_attributes: ProductAttributes,
|
|
block_index: int
|
|
) -> ProductBlock:
|
|
"""Create a new ProductBlock for a single unit of a staged product.
|
|
|
|
Args:
|
|
staged_product: The staged product to store
|
|
storage_block: The block to store the product in
|
|
product_attributes: Additional product attributes
|
|
block_index: Index within the storage block
|
|
|
|
Returns:
|
|
Newly created ProductBlock
|
|
"""
|
|
product_block = ProductBlock(
|
|
id=str(uuid4()),
|
|
product_id=staged_product.product_id,
|
|
block_id=storage_block.id,
|
|
block_index=block_index
|
|
)
|
|
|
|
with db_transaction(self.db):
|
|
self.db.add(product_block)
|
|
|
|
return product_block
|
|
|
|
def get_staged_product_attributes_for_storage(
|
|
self,
|
|
staged_product: StagedFileProduct
|
|
) -> List[ProductAttributes]:
|
|
"""Get attributes for each unit of a staged product.
|
|
|
|
Args:
|
|
staged_product: The staged product to get attributes for
|
|
|
|
Returns:
|
|
List of attributes for each unit of the product
|
|
"""
|
|
result = (
|
|
self.db.query(
|
|
StagedFileProduct.product_id,
|
|
StagedFileProduct.quantity,
|
|
CardTCGPlayer.number
|
|
)
|
|
.join(CardTCGPlayer, CardTCGPlayer.product_id == StagedFileProduct.product_id)
|
|
.filter(StagedFileProduct.id == staged_product.id)
|
|
.first()
|
|
)
|
|
|
|
if not result:
|
|
return []
|
|
|
|
return [
|
|
ProductAttributes(
|
|
product_id=result.product_id,
|
|
card_number=result.number
|
|
)
|
|
for _ in range(result.quantity)
|
|
]
|
|
|
|
def store_staged_products_for_file(self, file_id: str) -> StorageBlock:
|
|
"""Store all staged products for a file in a new storage block.
|
|
|
|
Args:
|
|
file_id: ID of the file containing staged products
|
|
|
|
Returns:
|
|
The newly created StorageBlock containing all products
|
|
"""
|
|
warehouse = self.get_or_create_warehouse()
|
|
storage_block = self.create_storage_block(warehouse, file_id)
|
|
staged_products = self.get_staged_product(file_id)
|
|
|
|
# Collect all product attributes first
|
|
all_product_attributes = []
|
|
for staged_product in staged_products:
|
|
product_attributes_list = self.get_staged_product_attributes_for_storage(staged_product)
|
|
for attrs in product_attributes_list:
|
|
all_product_attributes.append((staged_product, attrs))
|
|
|
|
# Sort by card number as integer to determine block indices
|
|
sorted_attributes = sorted(
|
|
all_product_attributes,
|
|
key=lambda x: int(''.join(filter(str.isdigit, x[1]['card_number'])))
|
|
)
|
|
|
|
# Add products with correct block indices
|
|
for block_index, (staged_product, product_attributes) in enumerate(sorted_attributes, 1):
|
|
self.add_staged_product_to_product_block(
|
|
staged_product=staged_product,
|
|
storage_block=storage_block,
|
|
product_attributes=product_attributes,
|
|
block_index=block_index
|
|
)
|
|
|
|
return storage_block |