16 Commits

Author SHA1 Message Date
8f1789101c j
All checks were successful
Deploy App to Docker / deploy (push) Successful in 19s
2025-04-20 19:33:56 -04:00
976f2683a4 asdfsad
All checks were successful
Deploy App to Docker / deploy (push) Successful in 19s
2025-04-20 19:29:33 -04:00
b151ef9920 omg
All checks were successful
Deploy App to Docker / deploy (push) Successful in 33s
2025-04-20 19:26:48 -04:00
fb3134723a sadf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 40s
2025-04-14 22:27:37 -04:00
8155ba2f8d a
All checks were successful
Deploy App to Docker / deploy (push) Successful in 27s
2025-04-14 21:37:11 -04:00
a97ba6e858 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 28s
2025-04-14 21:01:04 -04:00
3939c21a72 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 29s
2025-04-14 20:31:03 -04:00
9ff87dc107 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 44s
2025-04-14 19:18:49 -04:00
de3821aa80 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 30s
2025-04-12 14:33:03 -04:00
e67c1aa9f3 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 28s
2025-04-12 14:17:33 -04:00
94a3a517c7 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 29s
2025-04-12 14:01:57 -04:00
64897392f1 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 57s
2025-04-12 13:50:01 -04:00
3199a3259f asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 33s
2025-04-12 13:27:43 -04:00
0e5ba991db FUCK
All checks were successful
Deploy App to Docker / deploy (push) Successful in 1m2s
2025-04-11 14:57:20 -04:00
3601bcc81b asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 42s
2025-04-11 13:32:35 -04:00
c234285788 asdf
All checks were successful
Deploy App to Docker / deploy (push) Successful in 49s
2025-04-11 12:06:56 -04:00
2 changed files with 90 additions and 54 deletions

View File

@ -27,6 +27,8 @@ class PricingService:
# function for taking a tcgplayer pricing export with all set ids and loading it into the price table
# can be run as needed or scheduled
def get_pricing_export_content(self, file: File = None) -> bytes:
if ACTIVE_PRICING_ALGORITHIM == 'default_pricing_algo' and FREE_SHIPPING == True:
print("asdf")
if file:
file_content = self.file_service.get_file_content(file.id)
else:
@ -101,16 +103,19 @@ class PricingService:
self.tcgplayer_service.load_tcgplayer_cards(file_content)
self.load_pricing_csv_content_to_db(file_content)
def get_all_prices_for_products(self, product_ids: List[str]) -> Dict[str, Dict[str, float]]:
all_prices = self.db.query(Price).filter(
def get_latest_prices_for_products(self, product_ids: List[str]) -> Dict[str, Dict[str, float]]:
latest_prices = self.db.query(Price).filter(
Price.product_id.in_(product_ids)
).distinct(Price.product_id, Price.type).order_by(
Price.product_id,
Price.type,
Price.date_created.desc()
).all()
price_lookup = {}
for price in all_prices:
if price.product_id not in price_lookup:
price_lookup[price.product_id] = {}
price_lookup[price.product_id][price.type] = price.price
for price in latest_prices:
price_lookup.setdefault(price.product_id, {})[price.type] = price.price
return price_lookup
def apply_price_to_df_columns(self, row: pd.Series, price_lookup: Dict[str, Dict[str, float]]) -> pd.Series:
@ -119,7 +124,7 @@ class PricingService:
row[price_type] = price
return row
def smooth_markup(price, markup_bands):
def smooth_markup(self, price, markup_bands):
"""
Applies a smoothed markup based on the given price and markup bands.
Uses numpy for smooth transitions.
@ -154,7 +159,7 @@ class PricingService:
tcg_low = Decimal(str(row.get('tcg_low_price'))) if not pd.isna(row.get('tcg_low_price')) else None
tcg_low_shipping = Decimal(str(row.get('tcg_low_price_with_shipping'))) if not pd.isna(row.get('tcg_low_price_with_shipping')) else None
tcg_market_price = Decimal(str(row.get('tcg_market_price'))) if not pd.isna(row.get('tcg_market_price')) else None
current_price = Decimal(str(row.get('tcg_marketplace_price'))) if not pd.isna(row.get('tcgplayer_marketplace_price')) else None
#current_price = Decimal(str(row.get('tcg_marketplace_price'))) if not pd.isna(row.get('tcg_marketplace_price')) else None
total_quantity = str(row.get('total_quantity')) if not pd.isna(row.get('total_quantity')) else "0"
added_quantity = str(row.get('add_to_quantity')) if not pd.isna(row.get('add_to_quantity')) else "0"
quantity = int(total_quantity) + int(added_quantity)
@ -163,54 +168,74 @@ class PricingService:
logger.warning(f"Missing pricing data for row: {row}")
row['new_price'] = None
return row
# Define precision for rounding
TWO_PLACES = Decimal('0.01')
# Apply pricing rules
# Original markup bands
markup_bands = {
2.53: (Decimal('0.01'), Decimal('0.50')),
1.42: (Decimal('0.51'), Decimal('1.00')),
1.29: (Decimal('1.01'), Decimal('3.00')),
1.17: (Decimal('3.01'), Decimal('20.00')),
1.07: (Decimal('20.01'), Decimal('35.00')),
1.05: (Decimal('35.01'), Decimal('50.00')),
1.03: (Decimal('50.01'), Decimal('100.00')),
1.02: (Decimal('100.01'), Decimal('200.00')),
1.01: (Decimal('200.01'), Decimal('1000.00'))
Decimal('2.34'): (Decimal('0.01'), Decimal('0.50')),
Decimal('1.36'): (Decimal('0.51'), Decimal('1.00')),
Decimal('1.24'): (Decimal('1.01'), Decimal('3.00')),
Decimal('1.15'): (Decimal('3.01'), Decimal('20.00')),
Decimal('1.06'): (Decimal('20.01'), Decimal('35.00')),
Decimal('1.05'): (Decimal('35.01'), Decimal('50.00')),
Decimal('1.03'): (Decimal('50.01'), Decimal('100.00')),
Decimal('1.02'): (Decimal('100.01'), Decimal('200.00')),
Decimal('1.01'): (Decimal('200.01'), Decimal('1000.00'))
}
# Adjust markups if quantity is high
if quantity > 3:
quantity_markup = Decimal('0.1')
for markup in markup_bands:
markup = markup + quantity_markup
quantity_markup = quantity_markup - Decimal('0.01')
adjusted_bands = {}
increment = Decimal('0.20')
for markup, price_range in zip(markup_bands.keys(), markup_bands.values()):
new_markup = Decimal(str(markup)) + increment
adjusted_bands[new_markup] = price_range
increment -= Decimal('0.02')
markup_bands = adjusted_bands
if FREE_SHIPPING:
free_shipping_markup = Decimal('0.05')
for markup in markup_bands:
markup = markup + free_shipping_markup
free_shipping_markup = free_shipping_markup - Decimal('0.005')
#if FREE_SHIPPING:
#if tcg_low_shipping and (tcg_low >= Decimal('5.00')):
#tcg_compare_price = tcg_low_shipping
#elif tcg_low_shipping and (tcg_low < Decimal('5.00')):
#tcg_compare_price = max(tcg_low_shipping - Decimal('1.31'), tcg_low)
#elif tcg_low:
#tcg_compare_price = tcg_low
#else:
#logger.warning(f"No TCG low or shipping price available for row: {row}")
#row['new_price'] = None
#return row
#else:
#tcg_compare_price = tcg_low
#if tcg_compare_price is None:
#logger.warning(f"No TCG low price available for row: {row}")
#row['new_price'] = None
#return row
tcg_compare_price = tcg_low
# Apply the smoothed markup
new_price = self.smooth_markup(tcg_market_price, markup_bands)
new_price = self.smooth_markup(tcg_compare_price, markup_bands)
if tcg_low_shipping is not None and tcg_low_shipping < new_price:
new_price = tcg_low_shipping
if new_price < Decimal('0.25'):
# Enforce minimum price
if new_price < Decimal('0.35'):
new_price = Decimal('0.25')
if current_price / new_price > Decimal('0.25'):
logger.warning(f"Price drop too large for row: {row}")
new_price = current_price
# Avoid huge price drops
#if current_price is not None and Decimal(str(((current_price - new_price) / current_price))) > Decimal('0.5'):
#logger.warning(f"Price drop too large for row: {row}")
#new_price = current_price
# Ensure exactly 2 decimal places
# Round to 2 decimal places
new_price = new_price.quantize(TWO_PLACES, rounding=ROUND_HALF_UP)
# Convert back to float or string as needed for your dataframe
# Convert back to float for dataframe
row['new_price'] = float(new_price)
return row
def default_pricing_algo(self, row: pd.Series) -> pd.Series:
"""Default pricing algorithm with complex pricing rules"""
@ -263,11 +288,17 @@ class PricingService:
def apply_pricing_algo(self, row: pd.Series, pricing_algo: callable = None) -> pd.Series:
"""Modified to handle the pricing algorithm as an instance method"""
if pricing_algo is None or ACTIVE_PRICING_ALGORITHIM == 'default_pricing_algo':
if pricing_algo:
logger.debug(f"Using custom pricing algorithm: {pricing_algo.__name__}")
return pricing_algo(row)
elif ACTIVE_PRICING_ALGORITHIM == 'default_pricing_algo':
logger.debug(f"Using default pricing algorithm: {self.default_pricing_algo.__name__}")
pricing_algo = self.default_pricing_algo
elif ACTIVE_PRICING_ALGORITHIM == 'tcgplayer_recommended_algo':
logger.debug(f"Using TCGPlayer recommended algorithm: {self.tcgplayer_recommended_algo.__name__}")
pricing_algo = self.tcgplayer_recommended_algo
else:
logger.debug(f"Using default pricing algorithm: {self.default_pricing_algo.__name__}")
pricing_algo = self.default_pricing_algo
return pricing_algo(row)
@ -298,6 +329,8 @@ class PricingService:
.filter(CardTCGPlayer.tcgplayer_id.in_(tcgplayer_ids))
.all()
}
# set listed price
df['listed_price'] = df['tcg_marketplace_price'].copy()
# Map tcgplayer_id to product_id, ensure strings, keep None if not found
df['product_id'] = df['tcgplayer_id'].map(product_id_mapping).apply(lambda x: str(x) if pd.notnull(x) else None)
@ -307,11 +340,13 @@ class PricingService:
if null_product_ids:
logger.warning(f"The following tcgplayer_ids could not be mapped to a product_id: {null_product_ids}")
price_lookup = self.get_all_prices_for_products(df['product_id'].unique())
price_lookup = self.get_latest_prices_for_products(df['product_id'].unique())
# Apply price columns
df = df.apply(lambda row: self.apply_price_to_df_columns(row, price_lookup), axis=1)
logger.debug(f"Applying pricing algorithm: {ACTIVE_PRICING_ALGORITHIM}")
# Apply pricing algorithm
df = df.apply(self.apply_pricing_algo, axis=1)
@ -319,6 +354,7 @@ class PricingService:
if update_type == 'update':
df = df[df['new_price'] != df['listed_price']]
# Set marketplace price
df['TCG Marketplace Price'] = df['new_price']

View File

@ -2,19 +2,19 @@ curl -J http://192.168.1.41:8000/api/tcgplayer/inventory/update --remote-name
curl -J -X POST http://192.168.1.41:8000/api/tcgplayer/inventory/add \
-H "Content-Type: application/json" \
-d '{"open_box_ids": ["fcc28f9e-bc8b-4e89-a46e-7da746a96a5b","4a09930f-1830-4b8b-8006-a391e1adca66"]}' \
-d '{"open_box_ids": ["f4a8e94c-5592-4b27-97b7-5cdb3eb45a71","81918f03-cbd2-4129-9f5c-eada6e1a811f","c2083c29-6fac-4621-b4b6-30c2dac75fab", "4ac89a6b-b8b7-44cf-8a4e-4a95c8e9006d", "0e809522-cef6-4c3c-b8a3-742c2e3c83fd","9e68466f-5abb-4725-9da8-91e5aaa4e805"]}' \
--remote-name
curl -X POST http://192.168.1.41:8000/api/boxes \
-F "type=collector" \
-F "type=play" \
-F "set_code=TDM" \
-F "sku=1234" \
-F "num_cards_expected=123"
-F "num_cards_expected=420"
curl -X POST "http://192.168.1.41:8000/api/boxes/d95d26a8-1f82-47f2-89fa-3f88a4636823/open" \
-F "product_id=d95d26a8-1f82-47f2-89fa-3f88a4636823" \
-F "file_ids=0f29efd2-448c-4a05-ba49-1420fd3d524b" \
-F "date_opened=2025-04-04"
curl -X POST "http://192.168.1.41:8000/api/boxes/a77194be-8bd6-41cc-89a0-820e92ef9c04/open" \
-F "product_id=a77194be-8bd6-41cc-89a0-820e92ef9c04" \
-F "file_ids=b11a0292-bfdc-43de-90a8-6eb383332201" \
-F "date_opened=2025-04-14"
curl -X POST "http://192.168.1.41:8000/api/processOrders" \
-H "Content-Type: application/json" \