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
367 lines
13 KiB
Python
367 lines
13 KiB
Python
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, ForeignKey
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
from sqlalchemy.orm import relationship, validates
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
Base = declarative_base()
|
|
|
|
## Core Models
|
|
|
|
class Product(Base):
|
|
"""
|
|
product is the concept of a physical item that can be sold
|
|
"""
|
|
__tablename__ = "products"
|
|
|
|
@validates("type")
|
|
def validate_type(self, key, type: str):
|
|
if type not in ProductTypeEnum or type.lower() not in ProductTypeEnum:
|
|
raise ValueError(f"Invalid product type: {type}")
|
|
return type
|
|
|
|
@validates("product_line")
|
|
def validate_product_line(self, key, product_line: str):
|
|
if product_line not in ProductLineEnum or product_line.lower() not in ProductLineEnum:
|
|
raise ValueError(f"Invalid product line: {product_line}")
|
|
return product_line
|
|
|
|
id = Column(String, primary_key=True)
|
|
type = Column(String) # box or card
|
|
product_line = Column(String) # pokemon, mtg, etc.
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Sale(Base):
|
|
"""
|
|
sale represents a transaction where a product was sold to a customer on a marketplace
|
|
"""
|
|
__tablename__ = "sales"
|
|
|
|
id = Column(String, primary_key=True)
|
|
ledger_id = Column(String, ForeignKey("ledgers.id"))
|
|
customer_id = Column(String, ForeignKey("customers.id"))
|
|
marketplace_id = Column(String, ForeignKey("marketplaces.id"))
|
|
amount = Column(Float)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Ledger(Base):
|
|
"""
|
|
ledger associates financial transactions with a user
|
|
"""
|
|
__tablename__ = "ledgers"
|
|
|
|
id = Column(String, primary_key=True)
|
|
user_id = Column(String, ForeignKey("users.id"))
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Expense(Base):
|
|
"""
|
|
expense is any cash outflow associated with moving a product
|
|
can be optionally associated with a sale or a product
|
|
"""
|
|
__tablename__ = "expenses"
|
|
|
|
id = Column(String, primary_key=True)
|
|
ledger_id = Column(String, ForeignKey("ledgers.id"))
|
|
product_id = Column(String, ForeignKey("products.id"), nullable=True)
|
|
sale_id = Column(String, ForeignKey("sales.id"), nullable=True)
|
|
cost = Column(Float)
|
|
type = Column(String) # price paid, cogs, shipping, refund, supplies, subscription, fee, etc.
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Marketplace(Base):
|
|
"""
|
|
Marketplace represents a marketplace where products can be sold
|
|
"""
|
|
__tablename__ = "marketplaces"
|
|
|
|
id = Column(String, primary_key=True)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Box(Base):
|
|
"""
|
|
Box Represents a physical product with a sku that contains trading cards
|
|
Boxes can be sealed or opened
|
|
Opened boxes have cards associated with them
|
|
A box contains cards regardless of the inventory status of those cards
|
|
"""
|
|
__tablename__ = "boxes"
|
|
|
|
@validates("type")
|
|
def validate_type(self, key, type: str):
|
|
if type not in BoxTypeEnum or type.lower() not in BoxTypeEnum:
|
|
raise ValueError(f"Invalid box type: {type}")
|
|
return type
|
|
|
|
product_id = Column(String, ForeignKey("products.id"), primary_key=True)
|
|
type = Column(String) # collector box, play box, etc.
|
|
set_code = Column(String)
|
|
sku = Column(String, nullable=True)
|
|
num_cards_expected = Column(Integer, nullable=True)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class OpenBox(Base):
|
|
__tablename__ = "open_boxes"
|
|
|
|
id = Column(String, primary_key=True)
|
|
product_id = Column(String, ForeignKey("products.id"))
|
|
num_cards_actual = Column(Integer)
|
|
date_opened = Column(DateTime, default=datetime.now)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Card(Base):
|
|
"""
|
|
Card represents the concept of a distinct card
|
|
Cards have metadata from different sources
|
|
"""
|
|
__tablename__ = "cards"
|
|
|
|
product_id = Column(String, ForeignKey("products.id"), primary_key=True)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class CardManabox(Base):
|
|
__tablename__ = "manabox_cards"
|
|
|
|
product_id = Column(String, ForeignKey("cards.product_id"), primary_key=True)
|
|
name = Column(String)
|
|
set_code = Column(String)
|
|
set_name = Column(String)
|
|
collector_number = Column(String)
|
|
foil = Column(String)
|
|
rarity = Column(String)
|
|
manabox_id = Column(Integer)
|
|
scryfall_id = Column(String)
|
|
condition = Column(String)
|
|
language = Column(String)
|
|
|
|
class CardTCGPlayer(Base):
|
|
__tablename__ = "tcgplayer_cards"
|
|
|
|
product_id = Column(String, ForeignKey("cards.product_id"), primary_key=True)
|
|
group_id = Column(Integer)
|
|
tcgplayer_id = Column(Integer)
|
|
product_line = Column(String)
|
|
set_name = Column(String)
|
|
product_name = Column(String)
|
|
title = Column(String)
|
|
number = Column(String)
|
|
rarity = Column(String)
|
|
condition = Column(String)
|
|
|
|
class Warehouse(Base):
|
|
"""
|
|
container that is associated with a user and contains inventory and stock
|
|
"""
|
|
__tablename__ = "warehouses"
|
|
|
|
id = Column(String, primary_key=True)
|
|
user_id = Column(String, ForeignKey("users.id"))
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Stock(Base):
|
|
"""
|
|
contains products that are listed for sale
|
|
"""
|
|
__tablename__ = "stocks"
|
|
|
|
product_id = Column(String, ForeignKey("products.id"), primary_key=True)
|
|
warehouse_id = Column(String, ForeignKey("warehouses.id"), default="default")
|
|
marketplace_id = Column(String, ForeignKey("marketplaces.id"))
|
|
quantity = Column(Integer)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Inventory(Base):
|
|
"""
|
|
contains products in inventory (not necessarily listed for sale)
|
|
sealed product in breakdown queue, held sealed product, speculatively held singles, etc.
|
|
inventory can contain products across multiple marketplaces
|
|
"""
|
|
__tablename__ = "inventories"
|
|
|
|
product_id = Column(String, ForeignKey("products.id"), primary_key=True)
|
|
warehouse_id = Column(String, ForeignKey("warehouses.id"), default="default")
|
|
quantity = Column(Integer)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class User(Base):
|
|
"""
|
|
User represents a user in the system
|
|
"""
|
|
__tablename__ = "users"
|
|
|
|
id = Column(String, primary_key=True)
|
|
username = Column(String)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Customer(Base):
|
|
"""
|
|
Customer represents a customer that has purchased at least 1 product
|
|
"""
|
|
__tablename__ = "customers"
|
|
|
|
id = Column(String, primary_key=True)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class StagedFileProduct(Base):
|
|
__tablename__ = "staged_file_products"
|
|
|
|
id = Column(String, primary_key=True)
|
|
product_id = Column(String, ForeignKey("products.id"))
|
|
file_id = Column(String, ForeignKey("files.id"))
|
|
quantity = Column(Integer)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class File(Base):
|
|
"""
|
|
File represents a file that has been uploaded to or retrieved by the system
|
|
"""
|
|
__tablename__ = "files"
|
|
|
|
id = Column(String, primary_key=True)
|
|
type = Column(String) # upload, export, etc.
|
|
source = Column(String) # manabox, tcgplayer, etc.
|
|
service = Column(String) # pricing, data, etc.
|
|
filename = Column(String)
|
|
filepath = Column(String) # backup location
|
|
filesize_kb = Column(Float)
|
|
status = Column(String)
|
|
box_id = Column(String, ForeignKey("boxes.product_id"), nullable=True)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class Price(Base):
|
|
__tablename__ = "prices"
|
|
|
|
id = Column(String, primary_key=True)
|
|
product_id = Column(String, ForeignKey("products.id"))
|
|
marketplace_id = Column(String, ForeignKey("marketplaces.id"))
|
|
type = Column(String) # market, direct, low, low_with_shipping, marketplace
|
|
price = Column(Float)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class StorageBlock(Base):
|
|
"""
|
|
StorageBlock represents a physical storage location for products (50 card indexed block in a box)
|
|
"""
|
|
__tablename__ = "storage_blocks"
|
|
|
|
@validates("type")
|
|
def validate_type(self, key, type: str):
|
|
if type not in StorageBlockTypeEnum or type.lower() not in StorageBlockTypeEnum:
|
|
raise ValueError(f"Invalid storage block type: {type}")
|
|
return type
|
|
|
|
id = Column(String, primary_key=True)
|
|
warehouse_id = Column(String, ForeignKey("warehouses.id"))
|
|
name = Column(String)
|
|
type = Column(String) # rare or common
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class ProductBlock(Base):
|
|
"""
|
|
ProductBlock represents the relationship between a product and a storage block
|
|
which products are in a block and at what index
|
|
"""
|
|
__tablename__ = "product_blocks"
|
|
|
|
id = Column(String, primary_key=True)
|
|
product_id = Column(String, ForeignKey("products.id"))
|
|
block_id = Column(String, ForeignKey("storage_blocks.id"))
|
|
block_index = Column(Integer)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class OpenBoxCard(Base):
|
|
"""
|
|
OpenedBoxCard represents the relationship between an opened box and the cards it contains
|
|
"""
|
|
__tablename__ = "open_box_cards"
|
|
|
|
id = Column(String, primary_key=True)
|
|
open_box_id = Column(String, ForeignKey("open_boxes.id"))
|
|
card_id = Column(String, ForeignKey("cards.product_id"))
|
|
quantity = Column(Integer)
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class ProductSale(Base):
|
|
"""
|
|
ProductSale represents the relationship between products and sales
|
|
"""
|
|
__tablename__ = "product_sales"
|
|
|
|
id = Column(String, primary_key=True)
|
|
product_id = Column(String, ForeignKey("products.id"))
|
|
sale_id = Column(String, ForeignKey("sales.id"))
|
|
date_created = Column(DateTime, default=datetime.now)
|
|
date_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
|
|
class TCGPlayerGroups(Base):
|
|
__tablename__ = 'tcgplayer_groups'
|
|
|
|
id = Column(String, primary_key=True)
|
|
group_id = Column(Integer)
|
|
name = Column(String)
|
|
abbreviation = Column(String)
|
|
is_supplemental = Column(String)
|
|
published_on = Column(String)
|
|
modified_on = Column(String)
|
|
category_id = Column(Integer)
|
|
|
|
# enums
|
|
|
|
class RarityEnum(str, Enum):
|
|
COMMON = "common"
|
|
UNCOMMON = "uncommon"
|
|
RARE = "rare"
|
|
MYTHIC = "mythic"
|
|
LAND = "land"
|
|
PROMO = "promo"
|
|
SPECIAL = "special"
|
|
|
|
class ConditionEnum(str, Enum):
|
|
MINT = "mint"
|
|
NEAR_MINT = "near_mint"
|
|
LIGHTLY_PLAYED = "lightly_played"
|
|
MODERATELY_PLAYED = "moderately_played"
|
|
HEAVILY_PLAYED = "heavily_played"
|
|
DAMAGED = "damaged"
|
|
|
|
class BoxTypeEnum(str, Enum):
|
|
COLLECTOR = "collector"
|
|
PLAY = "play"
|
|
DRAFT = "draft"
|
|
COMMANDER = "commander"
|
|
SET = "set"
|
|
|
|
class ProductLineEnum(str, Enum):
|
|
MTG = "mtg"
|
|
POKEMON = "pokemon"
|
|
|
|
class ProductTypeEnum(str, Enum):
|
|
BOX = "box"
|
|
CARD = "card"
|
|
|
|
class StorageBlockTypeEnum(str, Enum):
|
|
RARE = "rare"
|
|
COMMON = "common" |