Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
from datetime import datetime
|
4 |
+
import pytz
|
5 |
+
import openpyxl
|
6 |
+
|
7 |
+
def process_file(file):
|
8 |
+
file_name = file.name.lower()
|
9 |
+
try:
|
10 |
+
if file_name.endswith('.csv'):
|
11 |
+
# CSV files do not have multiple sheets.
|
12 |
+
return "Error: Please upload an Excel file with a UPS sheet.", None
|
13 |
+
elif file_name.endswith(('.xls', '.xlsx', '.xlsm')):
|
14 |
+
# Only read data from the "UPS" sheet.
|
15 |
+
df = pd.read_excel(file.name, sheet_name="UPS")
|
16 |
+
else:
|
17 |
+
return f"Unsupported file format: {file_name}", None
|
18 |
+
except Exception as e:
|
19 |
+
return f"Error reading file: {str(e)}", None
|
20 |
+
|
21 |
+
# Define the new header list as specified.
|
22 |
+
output_headers = [
|
23 |
+
"Contact Name", "Company or Name", "Country", "Address 1", "Address 2", "Address 3",
|
24 |
+
"City", "State/Prov/Other", "Postal Code", "Telephone", "Ext", "Residential Ind",
|
25 |
+
"Consignee Email", "Packaging Type", "Customs Value", "Weight", "Length", "Width",
|
26 |
+
"Height", "Unit of Measure", "Description of Goods", "Documents of No Commercial Value",
|
27 |
+
"GNIFC", "Pkg Decl Value", "Service", "Delivery Confirm", "Shipper Release", "Ret of Documents",
|
28 |
+
"Saturday Deliver", "Carbon Neutral", "Large Package", "Addl handling", "Reference 1",
|
29 |
+
"Reference 2", "Reference 3", "QV Notif 1-Addr", "QV Notif 1-Ship", "QV Notif 1-Excp",
|
30 |
+
"QV Notif 1-Delv", "QV Notif 2-Addr", "QV Notif 2-Ship", "QV Notif 2-Excp", "QV Notif 2-Delv",
|
31 |
+
"QV Notif 3-Addr", "QV Notif 3-Ship", "QV Notif 3-Excp", "QV Notif 3-Delv", "QV Notif 4-Addr",
|
32 |
+
"QV Notif 4-Ship", "QV Notif 4-Excp", "QV Notif 4-Delv", "QV Notif 5-Addr", "QV Notif 5-Ship",
|
33 |
+
"QV Notif 5-Excp", "QV Notif 5-Delv", "QV Notif Msg", "QV Failure Addr", "UPS Premium Care",
|
34 |
+
"ADL Location ID", "ADL Media Type", "ADL Language", "ADL Notification Addr", "ADL Failure Addr",
|
35 |
+
"ADL COD Value", "ADL Deliver to Addressee", "ADL Shipper Media Type", "ADL Shipper Language",
|
36 |
+
"ADL Shipper Notification Addr", "ADL Direct Delivery Only", "Electronic Package Release Authentication",
|
37 |
+
"Lithium Ion Alone", "Lithium Ion In Equipment", "Lithium Ion With_Equipment", "Lithium Metal Alone",
|
38 |
+
"Lithium Metal In Equipment", "Lithium Metal With Equipment", "Weekend Commercial Delivery",
|
39 |
+
"Dry Ice Weight", "Merchandise Description", "UPS SurePost®Limited Quantity/Lithium Battery"
|
40 |
+
]
|
41 |
+
|
42 |
+
# Create an output DataFrame with the new header list.
|
43 |
+
output_df = pd.DataFrame("", index=range(len(df)), columns=output_headers)
|
44 |
+
|
45 |
+
# Mapping from input file to output based on provided rules:
|
46 |
+
# 1. Shipping First Name + Shipping Last Name + Shipping Company -> Contact Name and Company or Name
|
47 |
+
if {"Shipping First Name", "Shipping Last Name", "Shipping Company"}.issubset(df.columns):
|
48 |
+
first_name = df["Shipping First Name"].fillna("").astype(str).str.strip()
|
49 |
+
last_name = df["Shipping Last Name"].fillna("").astype(str).str.strip()
|
50 |
+
company = df["Shipping Company"].fillna("").astype(str).str.strip()
|
51 |
+
combined = (first_name + " " + last_name + " " + company).str.strip()
|
52 |
+
output_df["Contact Name"] = combined
|
53 |
+
output_df["Company or Name"] = combined
|
54 |
+
|
55 |
+
# 2. Shipping Country Code -> Country
|
56 |
+
if "Shipping Country Code" in df.columns:
|
57 |
+
output_df["Country"] = df["Shipping Country Code"].fillna("").astype(str).str.strip()
|
58 |
+
|
59 |
+
# 3. Shipping Address 1 -> Address 1
|
60 |
+
if "Shipping Address 1" in df.columns:
|
61 |
+
output_df["Address 1"] = df["Shipping Address 1"].fillna("").astype(str).str.strip()
|
62 |
+
|
63 |
+
# 4. Shipping Address 2 -> Address 2
|
64 |
+
if "Shipping Address 2" in df.columns:
|
65 |
+
output_df["Address 2"] = df["Shipping Address 2"].fillna("").astype(str).str.strip()
|
66 |
+
|
67 |
+
# 5. Shipping City -> City
|
68 |
+
if "Shipping City" in df.columns:
|
69 |
+
output_df["City"] = df["Shipping City"].fillna("").astype(str).str.strip()
|
70 |
+
|
71 |
+
# 6. Shipping Province -> State/Prov/Other
|
72 |
+
if "Shipping Province" in df.columns:
|
73 |
+
output_df["State/Prov/Other"] = df["Shipping Province"].fillna("").astype(str).str.strip()
|
74 |
+
|
75 |
+
# Convert full US state names to their 2-letter abbreviations if applicable.
|
76 |
+
us_states = {
|
77 |
+
'alabama': 'AL',
|
78 |
+
'alaska': 'AK',
|
79 |
+
'arizona': 'AZ',
|
80 |
+
'arkansas': 'AR',
|
81 |
+
'california': 'CA',
|
82 |
+
'colorado': 'CO',
|
83 |
+
'connecticut': 'CT',
|
84 |
+
'delaware': 'DE',
|
85 |
+
'florida': 'FL',
|
86 |
+
'georgia': 'GA',
|
87 |
+
'hawaii': 'HI',
|
88 |
+
'idaho': 'ID',
|
89 |
+
'illinois': 'IL',
|
90 |
+
'indiana': 'IN',
|
91 |
+
'iowa': 'IA',
|
92 |
+
'kansas': 'KS',
|
93 |
+
'kentucky': 'KY',
|
94 |
+
'louisiana': 'LA',
|
95 |
+
'maine': 'ME',
|
96 |
+
'maryland': 'MD',
|
97 |
+
'massachusetts': 'MA',
|
98 |
+
'michigan': 'MI',
|
99 |
+
'minnesota': 'MN',
|
100 |
+
'mississippi': 'MS',
|
101 |
+
'missouri': 'MO',
|
102 |
+
'montana': 'MT',
|
103 |
+
'nebraska': 'NE',
|
104 |
+
'nevada': 'NV',
|
105 |
+
'new hampshire': 'NH',
|
106 |
+
'new jersey': 'NJ',
|
107 |
+
'new mexico': 'NM',
|
108 |
+
'new york': 'NY',
|
109 |
+
'north carolina': 'NC',
|
110 |
+
'north dakota': 'ND',
|
111 |
+
'ohio': 'OH',
|
112 |
+
'oklahoma': 'OK',
|
113 |
+
'oregon': 'OR',
|
114 |
+
'pennsylvania': 'PA',
|
115 |
+
'rhode island': 'RI',
|
116 |
+
'south carolina': 'SC',
|
117 |
+
'south dakota': 'SD',
|
118 |
+
'tennessee': 'TN',
|
119 |
+
'texas': 'TX',
|
120 |
+
'utah': 'UT',
|
121 |
+
'vermont': 'VT',
|
122 |
+
'virginia': 'VA',
|
123 |
+
'washington': 'WA',
|
124 |
+
'west virginia': 'WV',
|
125 |
+
'wisconsin': 'WI',
|
126 |
+
'wyoming': 'WY'
|
127 |
+
}
|
128 |
+
|
129 |
+
def convert_state(state):
|
130 |
+
if state:
|
131 |
+
state_lower = state.lower()
|
132 |
+
return us_states.get(state_lower, state)
|
133 |
+
return state
|
134 |
+
|
135 |
+
output_df["State/Prov/Other"] = output_df["State/Prov/Other"].apply(convert_state)
|
136 |
+
|
137 |
+
# 7. Shipping ZIP -> Postal Code
|
138 |
+
if "Shipping ZIP" in df.columns:
|
139 |
+
output_df["Postal Code"] = df["Shipping ZIP"].fillna("").astype(str).str.strip()
|
140 |
+
|
141 |
+
# 8. Shipping Address Phone -> Telephone
|
142 |
+
if "Shipping Address Phone" in df.columns:
|
143 |
+
output_df["Telephone"] = df["Shipping Address Phone"].fillna("").astype(str).str.strip()
|
144 |
+
|
145 |
+
# 9. Email -> Consignee Email
|
146 |
+
if "Email" in df.columns:
|
147 |
+
output_df["Consignee Email"] = df["Email"].fillna("").astype(str).str.strip()
|
148 |
+
|
149 |
+
# 10. Total Weight -> Weight (divide by 1000)
|
150 |
+
if "Total Weight" in df.columns:
|
151 |
+
def convert_weight(x):
|
152 |
+
try:
|
153 |
+
if pd.isna(x) or x == "":
|
154 |
+
return ""
|
155 |
+
return float(x) / 1000
|
156 |
+
except:
|
157 |
+
return ""
|
158 |
+
output_df["Weight"] = df["Total Weight"].apply(convert_weight)
|
159 |
+
|
160 |
+
# Fixed value mapping (applied for rows with data in Contact Name)
|
161 |
+
mask = output_df["Contact Name"].notna() & (output_df["Contact Name"] != "")
|
162 |
+
output_df.loc[mask, "Packaging Type"] = "2"
|
163 |
+
output_df.loc[mask, "Unit of Measure"] = "KG"
|
164 |
+
output_df.loc[mask, "Description of Goods"] = "Eyes Beauty Color Cosmetics"
|
165 |
+
output_df.loc[mask, "Service"] = "7"
|
166 |
+
|
167 |
+
# Delete duplicate rows based on "Contact Name", keeping the first occurrence.
|
168 |
+
output_df = output_df.drop_duplicates(subset=["Contact Name"], keep="first")
|
169 |
+
|
170 |
+
# Generate the output file name using the current Hong Kong date.
|
171 |
+
hk_timezone = pytz.timezone("Asia/Hong_Kong")
|
172 |
+
current_date_hk = datetime.now(hk_timezone).strftime("%y%m%d")
|
173 |
+
output_file_name = f"UPS {current_date_hk}.csv"
|
174 |
+
|
175 |
+
# Save the output DataFrame to a CSV file without the header.
|
176 |
+
output_df.to_csv(output_file_name, index=False, header=False)
|
177 |
+
|
178 |
+
return output_df, output_file_name
|
179 |
+
|
180 |
+
with gr.Blocks(title="Shipping - UPS") as demo:
|
181 |
+
gr.Markdown("# Shipping - UPS")
|
182 |
+
with gr.Row():
|
183 |
+
file_input = gr.File(label="Upload Excel File with a UPS sheet")
|
184 |
+
process_button = gr.Button("Process Data")
|
185 |
+
with gr.Row():
|
186 |
+
output_data = gr.DataFrame()
|
187 |
+
output_file_component = gr.File(label="Download Processed File")
|
188 |
+
|
189 |
+
process_button.click(fn=process_file, inputs=[file_input], outputs=[output_data, output_file_component])
|
190 |
+
|
191 |
+
# Updated hyperlink block with separate sections for Shipping Tools and Administration Tools.
|
192 |
+
gr.HTML(
|
193 |
+
"""
|
194 |
+
<div style="text-align: center; font-size: 16px; margin-top: 20px;">
|
195 |
+
<h3>Shipping Tools</h3>
|
196 |
+
<a href="https://huggingface.co/spaces/leadingbridge/shipping-dhl-e-commerce">DHL</a> |
|
197 |
+
<a href="https://huggingface.co/spaces/leadingbridge/shipping-ec-ship">EC-Ship</a> |
|
198 |
+
<a href="https://huggingface.co/spaces/leadingbridge/shipping-fedex">Fedex</a> |
|
199 |
+
<a href="https://huggingface.co/spaces/leadingbridge/shipping-UPS">UPS</a>
|
200 |
+
</div>
|
201 |
+
<div style="text-align: center; font-size: 16px; margin-top: 20px;">
|
202 |
+
<h3>Administration Tools</h3>
|
203 |
+
<a href="https://huggingface.co/spaces/leadingbridge/email-template">Email Template</a> |
|
204 |
+
<a href="https://huggingface.co/spaces/leadingbridge/product-feed">Google Merchant</a> |
|
205 |
+
<a href="https://huggingface.co/spaces/leadingbridge/tss-order">Order Processing</a>
|
206 |
+
</div>
|
207 |
+
"""
|
208 |
+
)
|
209 |
+
|
210 |
+
demo.launch()
|