samyhusy commited on
Commit
a47aeaa
·
verified ·
1 Parent(s): 1110a74

Upload utils.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. utils.py +353 -0
utils.py ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import plotly.express as px
4
+ import plotly.graph_objects as go
5
+ import io
6
+
7
+ # Month order for Persian calendar
8
+ MONTH_ORDER = [
9
+ "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور",
10
+ "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"
11
+ ]
12
+
13
+ # Available charts list
14
+ AVAILABLE_CHARTS = [
15
+ "مقایسه روند فروش ماهانه (ناخالص، خالص با/بدون ارزش افزوده)",
16
+ "روند فروش ماهانه بر اساس فروش ناخالص",
17
+ "روند فروش ماهانه بر اساس فروش خالص (بدون ارزش افزوده)",
18
+ "روند فروش ماهانه بر اساس فروش خالص (با ارزش افزوده)",
19
+ "تحلیل کالاها (80/20)",
20
+ "محصولات با بیشترین مرجوعی",
21
+ "گروه محصولات پرفروش",
22
+ "ده تولید کننده پرفروش",
23
+ "نرخ مرجوعی بر اساس برند (درصد)",
24
+ "سهم فروش گروه محصولات در ماه",
25
+ "تحلیل همبستگی تخفیف و فروش",
26
+ "میانگین ارزش هر فاکتور (ریالی)",
27
+ "ماتریس همبستگی متغیرها (Heatmap)"
28
+ ]
29
+
30
+
31
+ def read_data_file(file_obj):
32
+ """Read data file based on its format."""
33
+ file_name = file_obj.name
34
+ file_extension = file_name.split('.')[-1].lower()
35
+
36
+ if file_extension == 'csv':
37
+ try:
38
+ return pd.read_csv(file_obj, encoding='utf-8')
39
+ except UnicodeDecodeError:
40
+ file_obj.seek(0)
41
+ try:
42
+ return pd.read_csv(file_obj, encoding='windows-1256')
43
+ except:
44
+ file_obj.seek(0)
45
+ return pd.read_csv(file_obj, encoding='latin1')
46
+ elif file_extension in ['xls', 'xlsx']:
47
+ return pd.read_excel(file_obj)
48
+ else:
49
+ raise ValueError("فرمت فایل پشتیبانی نمی‌شود")
50
+
51
+
52
+ def load_and_process_data(file_obj):
53
+ """Upload file and prepare data with Persian headers."""
54
+ try:
55
+ df = read_data_file(file_obj)
56
+
57
+ # Standardize column names
58
+ rename_map = {
59
+ "نام تجاری": "نام برند",
60
+ "گروه کامل کالا": "گروه کامل کالا",
61
+ "Net_Sales_Amount": "مبلغ فروش خالص (با مالیات بر ارزش افزوده)",
62
+ "ناخالص فروش با کسر تخفیف": "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)",
63
+ "ناخالص فروش با کسر تخفيف": "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)",
64
+ "مبلغ فروش خالص": "مبلغ فروش خالص (با مالیات بر ارزش افزوده)"
65
+ }
66
+ df.rename(columns=rename_map, inplace=True)
67
+
68
+ # Validate required columns
69
+ required_columns = [
70
+ "ماه", "کد کالا", "نام برند", "تولید کننده",
71
+ "نام کالا", "مبلغ فروش ناخالص", "مبلغ تخفیف",
72
+ "مبلغ فروش خالص (با مالیات بر ارزش افزوده)",
73
+ "مبلغ برگشتی خالص",
74
+ "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"
75
+ ]
76
+
77
+ missing_cols = [col for col in required_columns if col not in df.columns]
78
+ if missing_cols:
79
+ return {
80
+ 'success': False,
81
+ 'error': f"ستون‌های زیر در فایل یافت نشدند: {', '.join(missing_cols)}",
82
+ 'data': None,
83
+ 'kpis': None
84
+ }
85
+
86
+ # Clean data
87
+ df["ماه"] = df["ماه"].astype(str)
88
+ present_months = [m for m in MONTH_ORDER if m in df["ماه"].unique()]
89
+ if present_months:
90
+ df["ماه"] = pd.Categorical(df["ماه"], categories=MONTH_ORDER, ordered=True)
91
+
92
+ numeric_cols = [
93
+ "مبلغ فروش ناخالص",
94
+ "مبلغ فروش خالص (با مالیات بر ارزش افزوده)",
95
+ "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)",
96
+ "مبلغ برگشتی خالص",
97
+ "تعداد فاکتور",
98
+ "مبلغ تخفیف"
99
+ ]
100
+
101
+ for col in numeric_cols:
102
+ if col in df.columns:
103
+ df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
104
+
105
+ if "گروه محصول" in df.columns:
106
+ df["Analyzed_Group"] = df["گروه محصول"]
107
+ else:
108
+ df["Analyzed_Group"] = df["نام برند"]
109
+
110
+ # Calculate KPIs
111
+ kpis = {
112
+ 'net_sale_without_VAT': df["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum(),
113
+ 'net_sale_with_VAT': df["مبلغ فروش خالص (با مالیات بر ��رزش افزوده)"].sum(),
114
+ 'gross_sales': df["مبلغ فروش ناخالص"].sum(),
115
+ 'total_product': df["کد کالا"].nunique(),
116
+ 'total_brand': df["نام برند"].nunique(),
117
+ 'total_return': df["مبلغ برگشتی خالص"].sum()
118
+ }
119
+
120
+ return {
121
+ 'success': True,
122
+ 'error': None,
123
+ 'data': df,
124
+ 'kpis': kpis
125
+ }
126
+
127
+ except Exception as e:
128
+ return {
129
+ 'success': False,
130
+ 'error': str(e),
131
+ 'data': None,
132
+ 'kpis': None
133
+ }
134
+
135
+
136
+ def create_specific_chart(chart_name, df_state):
137
+ """Create specific chart based on selection."""
138
+ if df_state is None or df_state.empty:
139
+ fig = go.Figure()
140
+ fig.update_layout(
141
+ title="لطفا ابتدا فایل داده را بارگذاری کنید 📂",
142
+ xaxis={"visible": False},
143
+ yaxis={"visible": False},
144
+ annotations=[{
145
+ "text": "No Data",
146
+ "xref": "paper",
147
+ "yref": "paper",
148
+ "showarrow": False,
149
+ "font": {"size": 20}
150
+ }]
151
+ )
152
+ return fig
153
+
154
+ df = df_state.copy()
155
+ fig = None
156
+
157
+ Y_LABEL_GROSS = 'مبلغ فروش ناخالص (ریال)'
158
+ Y_LABEL_NET_NO_TAX = 'مبلغ فروش خالص بدون ارزش افزوده (ریال)'
159
+ Y_LABEL_NET = 'مبلغ فروش خالص (ریال)'
160
+ X_LABEL_MONTH = 'ماه'
161
+
162
+ try:
163
+ if chart_name == "مقایسه روند فروش ماهانه (ناخالص، خالص با/بدون ارزش افزوده)":
164
+ cols_to_agg = {
165
+ "مبلغ فروش ناخالص": "sum",
166
+ "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)": "sum",
167
+ "مبلغ فروش خالص (با مالیات بر ارزش افزوده)": "sum"
168
+ }
169
+ monthly = df.groupby("ماه").agg(cols_to_agg).reset_index().sort_values("ماه")
170
+ monthly_long = monthly.melt(
171
+ id_vars='ماه',
172
+ var_name='نوع فروش',
173
+ value_name='مبلغ'
174
+ )
175
+ fig = px.line(
176
+ monthly_long, x="ماه", y="مبلغ", color='نوع فروش', markers=True,
177
+ labels={'ماه': X_LABEL_MONTH, 'مبلغ': 'مبلغ فروش (ریال)', 'نوع فروش': 'شاخص فروش'}
178
+ )
179
+ fig.update_layout(
180
+ title={"text": "مقایسه جامع روند ماهانه شاخص‌های فروش", "x": 0.5, "y": 0.95},
181
+ legend_title_text='شاخص فروش',
182
+ legend=dict(orientation="h", y=-0.2)
183
+ )
184
+
185
+ elif chart_name == "روند فروش ماهانه بر اساس فروش ناخالص":
186
+ monthly = df.groupby("ماه")["مبلغ فروش ناخالص"].sum().reset_index().sort_values("ماه")
187
+ fig = px.line(
188
+ monthly, x="ماه", y="مبلغ فروش ناخالص", markers=True,
189
+ labels={'ماه': X_LABEL_MONTH, 'مبلغ فروش ناخالص': Y_LABEL_GROSS}
190
+ )
191
+ fig.update_layout(title={"text": "روند فروش بر اساس فروش ناخالص", "x": 0.5, "y": 0.95})
192
+
193
+ elif chart_name == "روند فروش ماهانه بر اساس فروش خالص (بدون ارزش افزوده)":
194
+ monthly = df.groupby("ماه")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index().sort_values("ماه")
195
+ fig = px.line(
196
+ monthly, x="ماه", y="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", markers=True,
197
+ labels={'ماه': X_LABEL_MONTH, 'مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)': Y_LABEL_NET_NO_TAX}
198
+ )
199
+ fig.update_layout(title={"text": "روند فروش خالص بدون ارزش افزوده ماهانه", "x": 0.5, "y": 0.95})
200
+
201
+ elif chart_name == "روند فروش ماهانه بر اساس فروش خالص (با ارزش افزوده)":
202
+ col_name = "مبلغ فروش خالص (با مالیات بر ارزش افزوده)"
203
+ monthly = df.groupby("ماه")[col_name].sum().reset_index().sort_values("ماه")
204
+ fig = px.line(
205
+ monthly, x="ماه", y=col_name, markers=True,
206
+ labels={'ماه': X_LABEL_MONTH, col_name: Y_LABEL_NET}
207
+ )
208
+ fig.update_layout(title={"text": "روند خالص فروش ماهانه (با ارزش افزوده)", "x": 0.5, "y": 0.95})
209
+
210
+ elif chart_name == "تحلیل کالاها (80/20)":
211
+ sales_data = df.groupby("نام کالا")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index()
212
+ sales_data = sales_data[sales_data['مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)'] > 0]
213
+ sales_data = sales_data.sort_values("مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", ascending=False).reset_index(drop=True)
214
+
215
+ if sales_data.empty:
216
+ return go.Figure().update_layout(title="هیچ فروش مثبتی برای تحلیل پارتو وجود ندارد")
217
+
218
+ sales_data['Cumulative %'] = 100 * (
219
+ sales_data["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].cumsum() /
220
+ sales_data["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum()
221
+ )
222
+
223
+ over_80_subset = sales_data[sales_data['Cumulative %'] >= 80]
224
+ pareto_rank = over_80_subset.index[0] + 1 if not over_80_subset.empty else len(sales_data)
225
+ total_product = df["کد کالا"].nunique()
226
+
227
+ display_count = max(20, pareto_rank + 3)
228
+ top_sales = sales_data.head(display_count)
229
+
230
+ fig = go.Figure()
231
+ fig.add_trace(go.Bar(
232
+ x=top_sales["نام کالا"],
233
+ y=top_sales["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"],
234
+ name="فروش",
235
+ marker_color='#667eea'
236
+ ))
237
+ fig.add_trace(go.Scatter(
238
+ x=top_sales["نام کالا"],
239
+ y=top_sales["Cumulative %"],
240
+ name="تجمعی %",
241
+ yaxis="y2",
242
+ mode="lines+markers",
243
+ marker_color='#f5576c'
244
+ ))
245
+
246
+ fig.add_shape(
247
+ type="line", xref="paper", yref="y2",
248
+ x0=0, y0=80, x1=1, y1=80,
249
+ line=dict(color="red", width=2, dash="dash")
250
+ )
251
+
252
+ fig.update_layout(
253
+ title={"text": f"تحلیل پارتو: {pareto_rank} کالا از سبد {total_product} کالا 80% فروش را تامین میکند", "x": 0.5, "y": 0.95},
254
+ xaxis_title="نام کالا",
255
+ yaxis_title="مبلغ فروش خالص (بدون ارزش افزوده) (ریال)",
256
+ yaxis2=dict(title="درصد تجمعی", overlaying="y", side="right", range=[0, 105]),
257
+ legend=dict(x=0.4, y=0.5, orientation='h'),
258
+ barmode='overlay'
259
+ )
260
+
261
+ elif chart_name == "محصولات با بیشترین مرجوعی":
262
+ top_returns = df.groupby("نام کالا")["مبلغ برگشتی خالص"].sum().abs().reset_index()
263
+ top_returns = top_returns[top_returns["مبلغ برگشتی خالص"] > 0]
264
+
265
+ if not top_returns.empty:
266
+ top_returns = top_returns.sort_values("مبلغ برگشتی خالص", ascending=False).head(10)
267
+ fig = px.bar(
268
+ top_returns, x="مبلغ برگشتی خالص", y="نام کالا",
269
+ orientation='h', color_discrete_sequence=['#f5576c']
270
+ )
271
+ fig.update_layout(
272
+ title={"text": "محصولات با بیشترین مرجوعی", "x": 0.5, "y": 0.95},
273
+ yaxis={'categoryorder': 'total ascending'}
274
+ )
275
+ fig.update_xaxes(title="مبلغ مرجوعی (ریال)")
276
+ else:
277
+ fig = go.Figure().update_layout(title="هیچ مرجوعی ثبت نشده است")
278
+
279
+ elif chart_name == "گروه محصولات پرفروش":
280
+ top_group = df.groupby("Analyzed_Group")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index()
281
+ top_group = top_group.sort_values("مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", ascending=False).head(10)
282
+ fig = px.bar(
283
+ top_group, x="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", y="Analyzed_Group",
284
+ orientation="h", color_discrete_sequence=['#667eea']
285
+ )
286
+ fig.update_layout(
287
+ title={"text": "گروه محصولات پرفروش", "x": 0.5, "y": 0.95},
288
+ yaxis={'categoryorder': 'total ascending'}
289
+ )
290
+
291
+ elif chart_name == "ده تولید کننده پرفروش":
292
+ top_prod = df.groupby("تولید کننده")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index()
293
+ top_prod = top_prod.sort_values("مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", ascending=False).head(10)
294
+ fig = px.bar(
295
+ top_prod, x="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", y="تولید کننده",
296
+ orientation="h", color_discrete_sequence=['#38ef7d']
297
+ )
298
+ fig.update_layout(
299
+ title={"text": "ده تولید کننده پرفروش", "x": 0.5, "y": 0.95},
300
+ yaxis={'categoryorder': 'total ascending'}
301
+ )
302
+
303
+ elif chart_name == "نرخ مرجوعی بر اساس برند (درصد)":
304
+ brand_stats = df.groupby("نام برند")[["مبلغ برگشتی خالص", "مبلغ فروش ناخالص"]].sum().reset_index()
305
+ brand_stats = brand_stats[brand_stats["مبلغ فروش ناخالص"] > 0]
306
+ brand_stats["Return_Rate"] = (brand_stats["مبلغ برگشتی خالص"] / brand_stats["مبلغ فروش ناخالص"]) * 100
307
+ brand_stats = brand_stats[brand_stats["مبلغ فروش ناخالص"] > 10000000]
308
+ brand_stats = brand_stats.sort_values("Return_Rate", ascending=False).head(15)
309
+
310
+ fig = px.bar(
311
+ brand_stats, x="Return_Rate", y="نام برند", orientation='h',
312
+ color="Return_Rate", color_continuous_scale="Reds",
313
+ hover_data={"Return_Rate": ":,.2f"}
314
+ )
315
+ fig.update_layout(
316
+ title={"text": "درصد نرخ مرجوعی بر اساس برند", "x": 0.5, "y": 0.95},
317
+ yaxis={'categoryorder': 'total ascending'}
318
+ )
319
+ fig.update_xaxes(title="درصد مرجوعی از فروش (%)")
320
+ fig.update_yaxes(title="نام برند")
321
+
322
+ elif chart_name == "سهم فروش گروه محصولات در ماه":
323
+ cat_trend = df.groupby(["ماه", "Analyzed_Group"])["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index()
324
+ fig = px.bar(
325
+ cat_trend, x="ماه", y="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)",
326
+ color="Analyzed_Group", title="ترکیب فروش ماهانه بر اساس گروه محصول"
327
+ )
328
+ fig.update_layout(title={"x": 0.5, "y": 0.95}, barmode='stack')
329
+ fig.update_yaxes(title="فروش (ریال)")
330
+
331
+ elif chart_name == "تحلیل همبستگی تخفیف و فروش":
332
+ prod_perf = df.groupby("نام کالا")[["مبلغ فروش ناخالص", "مبلغ تخفیف", "تعداد فاکتور"]].sum().reset_index()
333
+ prod_perf = prod_perf[prod_perf["مبلغ فروش ناخالص"] > 0]
334
+ prod_perf["Discount_Pct"] = (prod_perf["مبلغ تخفیف"] / prod_perf["مبلغ فروش ناخالص"]) * 100
335
+ prod_perf = prod_perf[prod_perf["Discount_Pct"] <= 100]
336
+
337
+ fig = px.scatter(
338
+ prod_perf, x="مبلغ فروش ناخالص", y="Discount_Pct",
339
+ size="تعداد فاکتور", hover_name="نام کالا",
340
+ color="Discount_Pct", color_continuous_scale="Viridis",
341
+ title="آیا تخفیف بیشتر منجر به فروش بیشتر می‌شود؟",
342
+ hover_data={
343
+ "مبلغ فروش ناخالص": ":,.2f",
344
+ "Discount_Pct": ":.2f",
345
+ "تعداد فاکتور": ":,.0f"
346
+ }
347
+ )
348
+ fig.update_layout(title={"x": 0.5, "y": 0.95})
349
+ fig.update_xaxes(title="مبلغ فروش ناخالص (ریال)")
350
+ fig.update_yaxes(title="درصد تخفیف اعطا شده (%)")
351
+
352
+ elif chart_name == "میانگین ارزش هر فاکتور (ریالی)":
353
+ col_name = "مبلغ فروش خالص (با مالیات بر ارز