Spaces:
Runtime error
Runtime error
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
from io import BytesIO
|
4 |
+
|
5 |
+
def process_woocommerce_data_in_memory(netcom_file):
|
6 |
+
"""
|
7 |
+
Reads the uploaded NetCom CSV file in-memory, processes it to the WooCommerce format,
|
8 |
+
and returns the resulting CSV as bytes, suitable for download.
|
9 |
+
"""
|
10 |
+
# Define the brand-to-logo mapping
|
11 |
+
brand_logo_map = {
|
12 |
+
"Amazon Web Services": "https://devthe.tech/wp-content/uploads/2025/02/aws.png",
|
13 |
+
"Cisco": "https://devthe.tech/wp-content/uploads/2025/02/cisco-e1738593292198-1.webp",
|
14 |
+
"Microsoft": "https://devthe.tech/wp-content/uploads/2025/01/Microsoft-e1737494120985-1.png"
|
15 |
+
}
|
16 |
+
|
17 |
+
# 1. Read the uploaded CSV into a DataFrame
|
18 |
+
netcom_df = pd.read_csv(netcom_file.name, encoding='latin1')
|
19 |
+
netcom_df.columns = netcom_df.columns.str.strip() # standardize column names
|
20 |
+
|
21 |
+
# 2. Create aggregated dates and times for each Course ID
|
22 |
+
date_agg = (
|
23 |
+
netcom_df.groupby('Course ID')['Course Start Date']
|
24 |
+
.apply(lambda x: ','.join(x.astype(str).unique()))
|
25 |
+
.reset_index(name='Aggregated_Dates')
|
26 |
+
)
|
27 |
+
|
28 |
+
time_agg = (
|
29 |
+
netcom_df.groupby('Course ID')
|
30 |
+
.apply(
|
31 |
+
lambda df: ','.join(
|
32 |
+
f"{st}-{et} {tz}"
|
33 |
+
for st, et, tz in zip(df['Course Start Time'],
|
34 |
+
df['Course End Time'],
|
35 |
+
df['Time Zone'])
|
36 |
+
)
|
37 |
+
)
|
38 |
+
.reset_index(name='Aggregated_Times')
|
39 |
+
)
|
40 |
+
|
41 |
+
# 3. Extract unique parent products from the NetCom data
|
42 |
+
parent_products = netcom_df.drop_duplicates(subset=['Course ID'])
|
43 |
+
|
44 |
+
# 4. Merge aggregated dates and times into the parent product DataFrame
|
45 |
+
parent_products = parent_products.merge(date_agg, on='Course ID', how='left')
|
46 |
+
parent_products = parent_products.merge(time_agg, on='Course ID', how='left')
|
47 |
+
|
48 |
+
# 5. Create the parent (variable) product DataFrame
|
49 |
+
woo_parent_df = pd.DataFrame({
|
50 |
+
'Type': 'variable',
|
51 |
+
'SKU': parent_products['Course ID'],
|
52 |
+
'Name': parent_products['Course Name'],
|
53 |
+
'Published': 1,
|
54 |
+
'Visibility in catalog': 'visible',
|
55 |
+
'Short description': parent_products['Decription'],
|
56 |
+
'Description': parent_products['Decription'],
|
57 |
+
'Tax status': 'taxable',
|
58 |
+
'In stock?': 1,
|
59 |
+
'Stock': 1,
|
60 |
+
'Sold individually?': 1,
|
61 |
+
'Regular price': parent_products['SRP Pricing'].replace('[\$,]', '', regex=True),
|
62 |
+
'Categories': 'courses',
|
63 |
+
'Images': parent_products['Vendor'].map(brand_logo_map).fillna(''),
|
64 |
+
'Parent': '',
|
65 |
+
'Brands': parent_products['Vendor'],
|
66 |
+
'Attribute 1 name': 'Date',
|
67 |
+
'Attribute 1 value(s)': parent_products['Aggregated_Dates'],
|
68 |
+
'Attribute 1 visible': 'visible',
|
69 |
+
'Attribute 1 global': 1,
|
70 |
+
'Attribute 2 name': 'Location',
|
71 |
+
'Attribute 2 value(s)': 'Virtual',
|
72 |
+
'Attribute 2 visible': 'visible',
|
73 |
+
'Attribute 2 global': 1,
|
74 |
+
'Attribute 3 name': 'Time',
|
75 |
+
'Attribute 3 value(s)': parent_products['Aggregated_Times'],
|
76 |
+
'Attribute 3 visible': 'visible',
|
77 |
+
'Attribute 3 global': 1,
|
78 |
+
'Meta: outline': parent_products['Outline'],
|
79 |
+
'Meta: days': parent_products['Duration'],
|
80 |
+
'Meta: location': 'Virtual',
|
81 |
+
'Meta: overview': parent_products['Target Audience'],
|
82 |
+
'Meta: objectives': parent_products['Objectives'],
|
83 |
+
'Meta: prerequisites': parent_products['RequiredPrerequisite'].fillna(''),
|
84 |
+
'Meta: agenda': parent_products['Outline'] # Agenda now copies the outline
|
85 |
+
})
|
86 |
+
|
87 |
+
# 6. Create the child (variation) product DataFrame
|
88 |
+
woo_child_df = pd.DataFrame({
|
89 |
+
'Type': 'variation, virtual',
|
90 |
+
'SKU': netcom_df['Course SID'],
|
91 |
+
'Name': netcom_df['Course Name'],
|
92 |
+
'Published': 1,
|
93 |
+
'Visibility in catalog': 'visible',
|
94 |
+
'Short description': netcom_df['Decription'],
|
95 |
+
'Description': netcom_df['Decription'],
|
96 |
+
'Tax status': 'taxable',
|
97 |
+
'In stock?': 1,
|
98 |
+
'Stock': 1,
|
99 |
+
'Sold individually?': 1,
|
100 |
+
'Regular price': netcom_df['SRP Pricing'].replace('[\$,]', '', regex=True),
|
101 |
+
'Categories': 'courses',
|
102 |
+
'Images': netcom_df['Vendor'].map(brand_logo_map).fillna(''),
|
103 |
+
'Parent': netcom_df['Course ID'],
|
104 |
+
'Brands': netcom_df['Vendor'],
|
105 |
+
'Attribute 1 name': 'Date',
|
106 |
+
'Attribute 1 value(s)': netcom_df['Course Start Date'],
|
107 |
+
'Attribute 1 visible': 'visible',
|
108 |
+
'Attribute 1 global': 1,
|
109 |
+
'Attribute 2 name': 'Location',
|
110 |
+
'Attribute 2 value(s)': 'Virtual',
|
111 |
+
'Attribute 2 visible': 'visible',
|
112 |
+
'Attribute 2 global': 1,
|
113 |
+
'Attribute 3 name': 'Time',
|
114 |
+
'Attribute 3 value(s)': netcom_df.apply(
|
115 |
+
lambda row: f"{row['Course Start Time']}-{row['Course End Time']} {row['Time Zone']}", axis=1
|
116 |
+
),
|
117 |
+
'Attribute 3 visible': 'visible',
|
118 |
+
'Attribute 3 global': 1,
|
119 |
+
'Meta: outline': netcom_df['Outline'],
|
120 |
+
'Meta: days': netcom_df['Duration'],
|
121 |
+
'Meta: location': 'Virtual',
|
122 |
+
'Meta: overview': netcom_df['Target Audience'],
|
123 |
+
'Meta: objectives': netcom_df['Objectives'],
|
124 |
+
'Meta: prerequisites': netcom_df['RequiredPrerequisite'].fillna(''),
|
125 |
+
'Meta: agenda': netcom_df['Outline'] # Agenda now copies the outline
|
126 |
+
})
|
127 |
+
|
128 |
+
# 7. Combine parent and child data
|
129 |
+
woo_final_df = pd.concat([woo_parent_df, woo_child_df], ignore_index=True)
|
130 |
+
|
131 |
+
# 8. Define the desired column order (matching WooCommerce import format)
|
132 |
+
column_order = [
|
133 |
+
'Type', 'SKU', 'Name', 'Published', 'Visibility in catalog',
|
134 |
+
'Short description', 'Description', 'Tax status', 'In stock?',
|
135 |
+
'Stock', 'Sold individually?', 'Regular price', 'Categories', 'Images',
|
136 |
+
'Parent', 'Brands', 'Attribute 1 name', 'Attribute 1 value(s)', 'Attribute 1 visible',
|
137 |
+
'Attribute 1 global', 'Attribute 2 name', 'Attribute 2 value(s)', 'Attribute 2 visible',
|
138 |
+
'Attribute 2 global', 'Attribute 3 name', 'Attribute 3 value(s)', 'Attribute 3 visible',
|
139 |
+
'Attribute 3 global', 'Meta: outline', 'Meta: days', 'Meta: location', 'Meta: overview',
|
140 |
+
'Meta: objectives', 'Meta: prerequisites', 'Meta: agenda'
|
141 |
+
]
|
142 |
+
woo_final_df = woo_final_df[column_order]
|
143 |
+
|
144 |
+
# 9. Convert the final DataFrame to CSV in memory
|
145 |
+
output_buffer = BytesIO()
|
146 |
+
woo_final_df.to_csv(output_buffer, index=False, encoding='utf-8-sig')
|
147 |
+
output_buffer.seek(0)
|
148 |
+
|
149 |
+
return output_buffer
|
150 |
+
|
151 |
+
def process_file_and_return_csv(uploaded_file):
|
152 |
+
"""
|
153 |
+
Gradio wrapper function:
|
154 |
+
- Takes the uploaded file,
|
155 |
+
- Processes it,
|
156 |
+
- Returns a tuple that Gradio can interpret as a downloadable file.
|
157 |
+
"""
|
158 |
+
processed_csv_io = process_woocommerce_data_in_memory(uploaded_file)
|
159 |
+
|
160 |
+
# Gradio expects a tuple (filename, file_obj) when returning a downloadable file
|
161 |
+
return ("WooCommerce_Mapped_Data.csv", processed_csv_io.getvalue())
|
162 |
+
|
163 |
+
|
164 |
+
#########################
|
165 |
+
# Gradio App #
|
166 |
+
#########################
|
167 |
+
|
168 |
+
app = gr.Interface(
|
169 |
+
fn=process_file_and_return_csv,
|
170 |
+
inputs=gr.File(label="Upload NetCom CSV", file_types=["text", "csv"]),
|
171 |
+
outputs=gr.File(label="Download WooCommerce CSV"),
|
172 |
+
title="NetCom to WooCommerce CSV Processor",
|
173 |
+
description="Upload your NetCom Reseller Schedule CSV to generate the WooCommerce import-ready CSV."
|
174 |
+
)
|
175 |
+
|
176 |
+
if __name__ == "__main__":
|
177 |
+
app.launch()
|