25 Commits

Author SHA1 Message Date
893b229cc6 j 2025-02-07 22:14:08 -05:00
06f539aea2 fk 2025-02-07 21:55:30 -05:00
d0c2960ec9 frick 2025-02-07 21:50:53 -05:00
6b1362c166 database 2025-02-07 21:49:40 -05:00
8cadc6df4c asdf 2025-02-07 21:38:09 -05:00
1ca6f98684 fffff 2025-02-07 21:32:50 -05:00
8bb337a9c3 ffff 2025-02-07 21:31:13 -05:00
65aba280c5 aa 2025-02-07 21:26:16 -05:00
59ef03a59e asdf 2025-02-07 21:24:21 -05:00
f44d5740fc aaa 2025-02-07 21:23:32 -05:00
13c96b1643 sdf 2025-02-07 21:18:54 -05:00
949c795fd1 asdf 2025-02-07 21:17:53 -05:00
8c3cd423fe app2 2025-02-07 20:56:01 -05:00
78eafc739e app 2025-02-07 20:54:55 -05:00
dc47eced14 asdfasdfasdf 2025-02-07 20:43:15 -05:00
e24bcae88c a 2025-02-07 20:39:44 -05:00
c894451bfe req 2025-02-07 20:38:20 -05:00
3d09869562 wrong number = code dont work lol i love computers 2025-02-07 20:33:27 -05:00
4c93a1271b q 2025-02-07 20:29:39 -05:00
1f5361da88 same as original code now -5 days of my life 2025-02-07 18:27:20 -05:00
511b070cbb pricey worky 2025-02-07 13:52:28 -05:00
964fdd641b prep for pricing service work 2025-02-07 11:37:29 -05:00
a78c3bcba3 more stuff yay 2025-02-05 21:51:22 -05:00
bd9cfca7a9 GIGA FIXED EVERYTHING OMG 2025-02-04 22:30:33 -05:00
85510a4671 data model change and some new services 2025-02-04 00:01:34 -05:00
10 changed files with 42 additions and 260 deletions

View File

@@ -243,7 +243,7 @@ class File(Base):
filepath = Column(String) # backup location
filesize_kb = Column(Float)
status = Column(String)
box_id = Column(String, nullable=True)
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)

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, BackgroundTasks, Request
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, BackgroundTasks
from fastapi.responses import StreamingResponse
from typing import Optional, List
from io import BytesIO
@@ -232,20 +232,18 @@ async def delete_open_box(
logger.error(f"Delete open box failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e)
)
class InventoryAddRequest(BaseModel):
open_box_ids: List[str]
@router.post("/tcgplayer/inventory/add", response_class=StreamingResponse)
async def create_inventory_add_file(
body: InventoryAddRequest,
request: dict, # Just use a dict instead
pricing_service: PricingService = Depends(get_pricing_service),
):
"""Create a new inventory add file for download."""
try:
content = pricing_service.generate_tcgplayer_inventory_update_file_with_pricing(body.open_box_ids)
# Get IDs directly from the dict
open_box_ids = request.get('open_box_ids', [])
content = pricing_service.generate_tcgplayer_inventory_update_file_with_pricing(open_box_ids)
stream = BytesIO(content)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -292,20 +290,21 @@ async def update_cookies(
cookie_data: CookieUpdate
):
try:
# see if cookie file exists
if not os.path.exists('cookies') or os.path.exists('cookies/tcg_cookies.json'):
logger.info("Cannot find cookies")
# Create cookies directory if it doesn't exist
os.makedirs('cookies', exist_ok=True)
# Save cookies with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
cookie_path = 'cookies/tcg_cookies.json'
cookie_path = f'cookies/tcg_cookies.json'
# Save new cookies
with open(cookie_path, 'w') as f:
json.dump(cookie_data.cookies, f, indent=2)
# Update the "latest" cookies file
with open('cookies/tcg_cookies_latest.json', 'w') as f:
json.dump(cookie_data.cookies, f, indent=2)
return {"message": "Cookies updated successfully"}
except Exception as e:

View File

@@ -57,7 +57,7 @@ class CreateOpenBoxRequest(BaseModel):
product_id: str = Field(..., title="Product ID")
file_ids: list[str] = Field(None, title="File IDs")
num_cards_actual: Optional[int] = Field(None, title="Number of cards actual")
date_opened: Optional[str] = Field(None, title="Date Opened")
date_opened: Optional [str] = Field(None, title="Date Opened")
# RESPONSE
class CreateOpenBoxResponse(BaseModel):

View File

@@ -45,7 +45,7 @@ class BoxService:
def add_products_to_open_box(self, open_box: OpenBox, product_data: Dict[Product, int]) -> None:
"""Add products to an open box."""
for product, quantity in product_data.items(): # TODO BATCH THIS
for product, quantity in product_data.items():
open_box_card = OpenBoxCard(
id=str(uuid4()),
open_box_id=open_box.id,
@@ -86,8 +86,6 @@ class BoxService:
type='box',
product_line='mtg'
)
self.db.add(product)
self.db.flush()
box = Box(
product_id=product.id,
type=create_box_data.type,
@@ -95,6 +93,7 @@ class BoxService:
sku=create_box_data.sku,
num_cards_expected=create_box_data.num_cards_expected
)
self.db.add(product)
self.db.add(box)
return box, True

View File

@@ -38,8 +38,7 @@ class InventoryService:
if inventory is None:
inventory = Inventory(
product_id=product.id,
quantity=quantity,
warehouse_id="0f0d01b1-97ba-4ab2-9082-22062bca9b06" # TODO FIX
quantity=quantity
)
self.db.add(inventory)
else:
@@ -62,7 +61,7 @@ class InventoryService:
"""
try:
with db_transaction(self.db):
for product, quantity in product_data.items(): # TODO BATCH THIS
for product, quantity in product_data.items():
self.add_inventory(product, quantity)
return UpdateInventoryResponse(success=True)
except SQLAlchemyError:

View File

@@ -117,7 +117,6 @@ class PricingService:
"""Default pricing algorithm with complex pricing rules"""
tcg_low = row.get('tcg_low_price')
tcg_low_shipping = row.get('tcg_low_price_with_shipping')
tcg_market_price = row.get('tcg_market_price')
if pd.isna(tcg_low) or pd.isna(tcg_low_shipping):
logger.warning(f"Missing pricing data for row: {row}")
@@ -125,22 +124,14 @@ class PricingService:
return row
# Apply pricing rules
if tcg_market_price < 1 and tcg_market_price > 0.25:
new_price = tcg_market_price * 1.05
elif tcg_market_price < 0.25:
new_price = 0.25
if tcg_low < 0.35:
new_price = 0.35
elif tcg_low < 5 or tcg_low_shipping < 5:
new_price = round(tcg_low+((abs(tcg_market_price-tcg_low))*.75), 2)
elif tcg_low_shipping > 20:
new_price = round(tcg_low_shipping * 1.0125, 2)
new_price = round(tcg_low * 1.25, 2)
elif tcg_low_shipping > 25:
new_price = round(tcg_low_shipping * 1.025, 2)
else:
# new_price = round(tcg_low_shipping * 1.08, 2)
new_price = round(tcg_market_price * 1.03)
# if new price is less than half of market price, set to 90% market
if new_price < (tcg_market_price / 2):
new_price = round(tcg_market_price * 0.85, 2)
if new_price < 0.25:
new_price = 0.25
new_price = round(tcg_low_shipping * 1.10, 2)
row['new_price'] = new_price
return row

View File

@@ -1,168 +0,0 @@
from brother_ql.conversion import convert
from brother_ql.backends.helpers import send
from brother_ql.raster import BrotherQLRaster
from PIL import Image, ImageDraw, ImageFont
import platform
import pandas as pd
from time import sleep
import pdf2image
import io
# Printer settings
printer_model = "QL-1100"
backend = 'pyusb' # Changed from network to USB
printer = 'usb://0x04f9:0x20a7'
def convert_pdf_to_image(pdf_path):
"""Converts a PDF to PIL Image"""
try:
# Convert PDF to image
images = pdf2image.convert_from_path(pdf_path)
if images:
return images[0] # Return first page
return None
except Exception as e:
print(f"Error converting PDF: {str(e)}")
return None
def create_address_label(input_data, font_size=30, is_pdf=False):
"""Creates and returns the label image without printing"""
if is_pdf:
if isinstance(input_data, str): # If path is provided
return convert_pdf_to_image(input_data)
else: # If PIL Image is provided
return input_data
# Regular text-based label creation
label_width = 991
label_height = 306
image = Image.new('L', (label_width, label_height), 'white')
draw = ImageDraw.Draw(image)
# Font selection based on OS
if platform.system() == 'Windows':
font = ImageFont.truetype("C:\\Windows\\Fonts\\arial.ttf", size=font_size)
elif platform.system() == 'Darwin':
font = ImageFont.truetype("/Library/Fonts/Arial.ttf", size=font_size)
margin = 20
lines = input_data.split('\n')
line_height = font_size + 5
total_height = line_height * len(lines)
start_y = (label_height - total_height) // 2
for i, line in enumerate(lines):
y = start_y + (i * line_height)
draw.text((margin, y), line, font=font, fill='black')
return image
def preview_label(input_data, font_size=30, is_pdf=False):
"""Creates and displays the label preview"""
image = create_address_label(input_data, font_size, is_pdf)
if image:
image.show()
def print_address_label(input_data, font_size=30, is_pdf=False, label_size='29x90'):
"""Prints the label with support for both text and PDF inputs"""
try:
image = create_address_label(input_data, font_size, is_pdf)
if not image:
raise Exception("Failed to create label image")
# For 4x6 shipping labels from Pirate Ship
if label_size == '4x6':
# Resize image to fit 4x6 format if needed
target_width = 1164 # Adjusted for 4x6 format
target_height = 1660
image = image.resize((target_width, target_height), Image.LANCZOS)
qlr = BrotherQLRaster(printer_model)
qlr.exception_on_warning = True
print("Converting image to printer instructions...")
instructions = convert(
qlr=qlr,
images=[image],
label='29x90' if label_size == '29x90' else '102x152',
threshold=70.0,
dither=False,
compress=False,
red=False,
dpi_600=False,
hq=True,
#cut=True
cut=False
)
print("Sending to printer...")
send(
instructions=instructions,
printer_identifier=printer,
backend_identifier=backend,
blocking=True
)
print("Print job sent successfully")
except Exception as e:
print(f"Error during printing: {str(e)}")
def process_pirate_ship_pdf(pdf_path, preview=False):
"""Process and print a Pirate Ship PDF shipping label"""
if preview:
preview_label(pdf_path, is_pdf=True)
else:
print_address_label(pdf_path, is_pdf=True, label_size='4x6')
def process_tcg_shipping_export(file_path, require_input=False, font_size=60, preview=False):
# Load the CSV file, all columns are strings
df = pd.read_csv(file_path, dtype=str)
print(df.dtypes)
for i, row in df.iterrows():
line1 = str(row['FirstName']) + ' ' + str(row['LastName'])
line2 = str(row['Address1'])
if not pd.isna(row['Address2']):
line2 += ' ' + str(row['Address2'])
line3 = str(row['City']) + ', ' + str(row['State']) + ' ' + str(row['PostalCode'])
address = f"{line1}\n{line2}\n{line3}"
if preview:
preview_label(address, font_size=font_size)
else:
print_address_label(address, font_size=font_size)
if require_input:
input("Press Enter to continue...")
else:
sleep(1)
# Example usage
if __name__ == "__main__":
# Example for regular address label
address = """John Doe
123 Main Street
Apt 4B
City, State 12345"""
# Example for TCG Player export
shipping_export_file = "_TCGplayer_ShippingExport_20250201_115949.csv"
# Example for Pirate Ship PDF
pirate_ship_pdf = "C:\\Users\\joshu\\Downloads\\2025-02-10---greg-creek---9400136208070411592215.pdf"
# Choose which type to process
label_type = input("Enter label type (1 for regular, 2 for TCG, 3 for Pirate Ship): ")
if label_type == "1":
preview_label(address, font_size=60)
user_input = input("Press 'p' to print the label or any other key to cancel: ")
if user_input.lower() == 'p':
print_address_label(address, font_size=60)
elif label_type == "2":
process_tcg_shipping_export(shipping_export_file, font_size=60, preview=False)
elif label_type == "3":
process_pirate_ship_pdf(pirate_ship_pdf, preview=True)
user_input = input("Press 'p' to print the label or any other key to cancel: ")
if user_input.lower() == 'p':
process_pirate_ship_pdf(pirate_ship_pdf, preview=False)

View File

@@ -1,6 +0,0 @@
@ IN MX 1 aspmx.l.google.com.
@ IN MX 5 alt1.aspmx.l.google.com.
@ IN MX 5 alt2.aspmx.l.google.com.
@ IN MX 10 alt3.aspmx.l.google.com.
@ IN MX 10 alt4.aspmx.l.google.com.
@ IN TXT "v=spf1 include:_spf.google.com ~all"

View File

@@ -1,17 +1,23 @@
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": ["fb629d9d-13d2-405e-9a69-6c44294d55de"]}' \
--remote-name
curl -J -X POST \ -H "Content-Type: application/json" \
-d '{"open_box_ids": ["e20cc342-23cb-4593-89cb-56a0cb3ed3f3"]}' \
http://192.168.1.41:8000/api/tcgplayer/inventory/add --remote-name
curl -X POST http://192.168.1.41:8000/api/boxes \
-F "type=draft" \
-F "set_code=CLB" \
-F "sku=195166181127" \
-F "num_cards_expected=480"
-H "Content-Type: application/json" \
-d '{
"type": "collector",
"set_code": "MOM",
"sku": "ABC123",
"num_cards_expected": 15
}'
curl -X POST "http://192.168.1.41:8000/api/boxes/0d31b9c3-3093-438a-9e8c-b6b70a2d437e/open" \
-F "product_id=0d31b9c3-3093-438a-9e8c-b6b70a2d437e" \
-F "file_ids=bb4a022c-5427-49b5-b57c-0147b5e9c4a9" \
-F "date_opened=2025-02-15"
curl -X POST http://192.168.1.41:8000/api/boxes/box123/open \
-H "Content-Type: application/json" \
-d '{
"product_id": "box123",
"file_ids": ["file1", "file2"],
"num_cards_actual": 15,
"date_opened": "2025-02-07T12:00:00Z"
}'

View File

@@ -1,38 +0,0 @@
import browser_cookie3
import requests
import json
def send_tcg_cookies(api_url: str, browser_type='brave'):
"""Get TCGPlayer cookies and send them to the API"""
try:
# Get cookies from browser
cookie_getter = getattr(browser_cookie3, browser_type)
cookie_jar = cookie_getter(domain_name='tcgplayer.com')
# Filter essential cookies
cookies = {}
for cookie in cookie_jar:
if any(key in cookie.name.lower() for key in ['.aspnet', 'tcg', 'session']):
cookies[cookie.name] = cookie.value
# Send to API
headers = {
'Content-Type': 'application/json'
}
response = requests.post(
f"{api_url}",
headers=headers,
json={'cookies': cookies}
)
response.raise_for_status()
print("Cookies updated successfully!")
except Exception as e:
print(f"Error updating cookies: {e}")
if __name__ == "__main__":
API_URL = "http://192.168.1.41:8000/api/cookies" # Update with your API URL
send_tcg_cookies(API_URL)