Compare commits
25 Commits
721b26ce97
...
docker
Author | SHA1 | Date | |
---|---|---|---|
893b229cc6 | |||
06f539aea2 | |||
d0c2960ec9 | |||
6b1362c166 | |||
8cadc6df4c | |||
1ca6f98684 | |||
8bb337a9c3 | |||
65aba280c5 | |||
59ef03a59e | |||
f44d5740fc | |||
13c96b1643 | |||
949c795fd1 | |||
8c3cd423fe | |||
78eafc739e | |||
dc47eced14 | |||
e24bcae88c | |||
c894451bfe | |||
3d09869562 | |||
4c93a1271b | |||
1f5361da88 | |||
511b070cbb | |||
964fdd641b | |||
a78c3bcba3 | |||
bd9cfca7a9 | |||
85510a4671 |
@@ -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)
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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):
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
6
dns.txt
6
dns.txt
@@ -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"
|
30
requests.md
30
requests.md
@@ -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"
|
||||
}'
|
@@ -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)
|
Reference in New Issue
Block a user