File size: 9,383 Bytes
31bfcac 273a8ca e508c3f 273a8ca 059d856 273a8ca 059d856 3a5cb12 e508c3f 059d856 3a5cb12 f6b843b 8f2c076 3a5cb12 8f2c076 273a8ca 3a5cb12 273a8ca 3a5cb12 8f2c076 3a5cb12 8f2c076 273a8ca f6b843b 273a8ca 8f2c076 273a8ca 3a5cb12 273a8ca 8f2c076 3a5cb12 273a8ca 8f2c076 3a5cb12 8f2c076 273a8ca 3a5cb12 273a8ca 3a5cb12 8f2c076 3a5cb12 8f2c076 3a5cb12 8f2c076 3a5cb12 8f2c076 3a5cb12 8f2c076 3a5cb12 8f2c076 3a5cb12 f6b843b 3a5cb12 8f2c076 3a5cb12 273a8ca 3a5cb12 273a8ca 8f2c076 3a5cb12 8f2c076 273a8ca 3a5cb12 8f2c076 273a8ca 3a5cb12 273a8ca 3a5cb12 273a8ca 3a5cb12 273a8ca 3a5cb12 273a8ca 3a5cb12 273a8ca 8f2c076 3a5cb12 8f2c076 273a8ca 3a5cb12 8f2c076 273a8ca 8f2c076 3a5cb12 8f2c076 273a8ca 8f2c076 3a5cb12 8f2c076 e508c3f 3a5cb12 273a8ca 3a5cb12 273a8ca e508c3f 3a5cb12 059d856 3a5cb12 f6b843b 273a8ca d8a38a8 059d856 8f2c076 e508c3f 273a8ca a919f44 3a5cb12 273a8ca a919f44 d8a38a8 a3a8092 a919f44 8f2c076 a919f44 8f2c076 a919f44 a3a8092 d8a38a8 273a8ca 3a5cb12 273a8ca a919f44 273a8ca | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | import os
import zipfile
import gradio as gr
import pandas as pd
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import inch
import barcode
from barcode.writer import ImageWriter
# Directories for generated files
LABELS_DIR = "labels"
BARCODES_DIR = "barcodes"
os.makedirs(LABELS_DIR, exist_ok=True)
os.makedirs(BARCODES_DIR, exist_ok=True)
# Required CSV/Excel columns
REQUIRED_COLUMNS = [
"Order ID", "Order Date", "Shipper Name", "Shipper Address", "Shipper Phone",
"Receiver Name", "Receiver Address", "Receiver Phone", "Package Weight (kg)",
"Shipping Method", "Tracking Number", "Estimated Delivery Date"
]
###############################################################################
# 1) Draw the static collage (top portion) for a 4x6 USPS label
###############################################################################
def draw_label_collage(c, width, height):
"""
Draws the top portion: big 'F', USPS text, placeholder QR code,
'USPS FIRST-CLASS PKG' title, plus a top horizontal line.
"""
# Big "F" near top-left
c.setFont("Helvetica-Bold", 80)
c.drawString(15, height - 85, "F")
# USPS postage info block (small text under "F")
c.setFont("Helvetica", 6)
c.drawString(70, height - 20, "US POSTAGE AND FEES PAID")
c.drawString(70, height - 30, "Apr 12 2018")
c.drawString(70, height - 40, "Mailed from ZIP 77024")
c.drawString(70, height - 50, "First-Class Pkg Svc")
c.drawString(70, height - 60, "CommercialBasePrice")
# Placeholder for QR code in top-right
c.setFont("Helvetica", 6)
c.drawString(width - 50, height - 20, "QR CODE")
# "USPS FIRST-CLASS PKG" in bold, centered near top
c.setFont("Helvetica-Bold", 14)
c.drawCentredString(width / 2, height - 110, "USPS FIRST-CLASS PKG")
# Horizontal line across the label, just below the header
c.setLineWidth(1)
c.line(15, height - 115, width - 15, height - 115)
###############################################################################
# 2) Overlay dynamic text + horizontal lines to mimic the sample layout
###############################################################################
def draw_label_text(c, row, width, height):
"""
Places:
- Warehouse & Order info, then a horizontal line
- Recipient address, then another line
- "USPS TRACKING #" heading, then another line
- Tracking number below that line
"""
# Convert row to local variables (with fallback)
order_id = row.get("Order ID", "")
order_date = row.get("Order Date", "")
shipper_name = row.get("Shipper Name", "")
shipper_addr = row.get("Shipper Address", "")
shipper_phone = row.get("Shipper Phone", "")
receiver_name = row.get("Receiver Name", "")
receiver_addr = row.get("Receiver Address", "")
receiver_phone = row.get("Receiver Phone", "")
weight = row.get("Package Weight (kg)", "")
ship_method = row.get("Shipping Method", "")
tracking_num = row.get("Tracking Number", "")
eta_date = row.get("Estimated Delivery Date", "")
# We'll start below the top line from the collage
top_line_y = height - 115
############################
# Warehouse + Order Info
############################
# Left: Warehouse lines
c.setFont("Helvetica-Bold", 9)
c.drawString(15, top_line_y - 15, shipper_name or "WAREHOUSE 2")
c.setFont("Helvetica", 9)
c.drawString(15, top_line_y - 30, shipper_addr or "11919 WINK RD")
c.drawString(15, top_line_y - 45, f"{shipper_phone}" or "HOUSTON TX 77024-7134")
# Right: "Order: 286"
c.setFont("Helvetica", 9)
c.drawRightString(width - 15, top_line_y - 15, f"Order: {order_id}")
c.drawRightString(width - 15, top_line_y - 30, f"Date: {order_date}")
# Horizontal line under Warehouse/Order block
warehouse_line_y = top_line_y - 60
c.line(15, warehouse_line_y, width - 15, warehouse_line_y)
############################
# Recipient Block
############################
# Bold name, address, phone
c.setFont("Helvetica-Bold", 11)
c.drawString(15, warehouse_line_y - 15, receiver_name)
c.setFont("Helvetica", 9)
c.drawString(15, warehouse_line_y - 30, receiver_addr)
c.drawString(15, warehouse_line_y - 45, f"Phone: {receiver_phone}")
# Possibly show shipping details to the right
c.setFont("Helvetica-Bold", 9)
c.drawRightString(width - 15, warehouse_line_y - 15, f"Weight: {weight} kg")
c.drawRightString(width - 15, warehouse_line_y - 30, f"Method: {ship_method}")
c.drawRightString(width - 15, warehouse_line_y - 45, f"ETA: {eta_date}")
# Another line below the recipient block
recipient_line_y = warehouse_line_y - 60
c.line(15, recipient_line_y, width - 15, recipient_line_y)
############################
# USPS TRACKING #
############################
# Place "USPS TRACKING #" in bold, centered
c.setFont("Helvetica-Bold", 12)
c.drawCentredString(width / 2, recipient_line_y - 20, "USPS TRACKING #")
# Another line under "USPS TRACKING #"
tracking_line_y = recipient_line_y - 35
c.line(15, tracking_line_y, width - 15, tracking_line_y)
# Tracking number below that line, centered
c.setFont("Helvetica-Bold", 10)
c.drawCentredString(width / 2, tracking_line_y - 20, tracking_num)
###############################################################################
# 3) Create the 4x6 USPS-style label
###############################################################################
def create_usps_label(row, index=1):
"""
1) Generate a Code128 barcode from 'Tracking Number'
2) Draw collage
3) Draw dynamic text & lines
4) Place barcode near the bottom
"""
# Barcode
tracking_number = row.get("Tracking Number", "0000000000")
barcode_filename = os.path.join(BARCODES_DIR, f"barcode_{index}.png")
code128 = barcode.get_barcode_class("code128")
code128_obj = code128(tracking_number, writer=ImageWriter())
code128_obj.save(barcode_filename)
# 4x6 inches
width, height = 4 * inch, 6 * inch
label_filename = os.path.join(LABELS_DIR, f"label_{index}.pdf")
c = canvas.Canvas(label_filename, pagesize=(width, height))
# Draw the collage
draw_label_collage(c, width, height)
# Overlay text & lines
draw_label_text(c, row, width, height)
# Place barcode near bottom, centered horizontally
if os.path.exists(barcode_filename):
barcode_w = 3 * inch
barcode_h = 0.8 * inch
x_pos = (width - barcode_w) / 2
y_pos = 0.5 * inch # ~0.5 inch from bottom
c.drawImage(barcode_filename, x_pos, y_pos, width=barcode_w, height=barcode_h)
else:
c.setFont("Helvetica", 10)
c.drawString(15, 0.5 * inch, "Barcode generation failed.")
c.showPage()
c.save()
return label_filename
###############################################################################
# 4) Gradio function to process file uploads
###############################################################################
def process_file(file_obj):
"""
Reads CSV/Excel, checks columns,
generates one label per row, zips them.
"""
try:
# Clean out old labels/barcodes
for f in os.listdir(LABELS_DIR):
os.remove(os.path.join(LABELS_DIR, f))
for f in os.listdir(BARCODES_DIR):
os.remove(os.path.join(BARCODES_DIR, f))
# Read the uploaded file
if file_obj.name.endswith(".csv"):
df = pd.read_csv(file_obj)
elif file_obj.name.endswith((".xls", ".xlsx")):
df = pd.read_excel(file_obj)
else:
return "Invalid file format. Please upload a .csv, .xls, or .xlsx file.", None
# Check required columns
missing = [col for col in REQUIRED_COLUMNS if col not in df.columns]
if missing:
return f"Missing columns: {', '.join(missing)}", None
# Generate labels
for i, row in df.iterrows():
create_usps_label(row.to_dict(), index=i+1)
# Zip them up
zip_filename = "usps_labels.zip"
with zipfile.ZipFile(zip_filename, "w") as zf:
for f in os.listdir(LABELS_DIR):
if f.endswith(".pdf"):
zf.write(os.path.join(LABELS_DIR, f), f)
return "Labels generated successfully.", zip_filename
except Exception as e:
return f"Error: {str(e)}", None
###############################################################################
# 5) Gradio Interface for Hugging Face
###############################################################################
iface = gr.Interface(
fn=process_file,
inputs=gr.File(type="filepath"),
outputs=[gr.Text(label="Status"), gr.File(label="Download Zipped Labels")],
title="USPS-Style Label Generator (4x6)",
description=(
"Upload a CSV/XLS/XLSX file with columns:\n"
"Order ID, Order Date, Shipper Name, Shipper Address, Shipper Phone,\n"
"Receiver Name, Receiver Address, Receiver Phone, Package Weight (kg),\n"
"Shipping Method, Tracking Number, Estimated Delivery Date\n\n"
"Generates a 4x6 USPS-style PDF label per row, including lines that match the sample."
),
)
if __name__ == "__main__":
iface.launch() |