mmrech commited on
Commit
7efdf62
·
verified ·
1 Parent(s): 6ac19a4

Deploy Gradio app with multiple files

Browse files
Files changed (3) hide show
  1. app.py +160 -0
  2. data_utils.py +78 -0
  3. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ from data_utils import generate_initial_data, process_data, handle_data_selection
4
+
5
+ # --- Configuration ---
6
+ initial_df = generate_initial_data()
7
+ initial_regions = ["All"] + sorted(initial_df['Region'].unique().tolist())
8
+ MAX_PROFIT_SLIDER = initial_df['Profit'].max()
9
+ MIN_PROFIT_SLIDER = initial_df['Profit'].min()
10
+
11
+ def plot_data_updates(df, region, profit_limit):
12
+ """Processes filtered data and returns updated plot objects."""
13
+ if df is None or (isinstance(df, pd.DataFrame) and df.empty):
14
+ gr.Warning("No data available to display.")
15
+ return gr.Plot(None), gr.Plot(None)
16
+
17
+ try:
18
+ daily_summary, product_summary = process_data(df, region, profit_limit)
19
+
20
+ if daily_summary.empty and product_summary.empty:
21
+ gr.Warning("No data matches the current filter criteria.")
22
+ return gr.Plot(None), gr.Plot(None)
23
+
24
+ # Line Plot: Daily Sales Trend
25
+ line_plot_update = gr.LinePlot(
26
+ daily_summary,
27
+ x="Day",
28
+ y="Total Sales",
29
+ title=f"Daily Sales Trend (Region: {region})",
30
+ tooltip=["Day", "Total Sales"],
31
+ height=300
32
+ )
33
+
34
+ # Bar Plot: Product Quantity Breakdown
35
+ bar_plot_update = gr.BarPlot(
36
+ product_summary,
37
+ x="Product",
38
+ y="Quantity",
39
+ title="Total Quantity by Product",
40
+ color="Product",
41
+ tooltip=["Product", "Quantity"],
42
+ height=300
43
+ )
44
+
45
+ return line_plot_update, bar_plot_update
46
+
47
+ except Exception as e:
48
+ gr.Error(f"An error occurred during data processing: {e}")
49
+ return gr.Plot(None), gr.Plot(None)
50
+
51
+ def respond(message, history):
52
+ """Simple chat response function."""
53
+ if message.strip():
54
+ history = history + [[message, "You said: " + message]]
55
+ return "", history
56
+
57
+ # --- Gradio Blocks Definition ---
58
+ with gr.Blocks(theme=gr.themes.Soft(), title="Interactive Data Reporter", fill_height=True) as demo:
59
+ gr.HTML(
60
+ "<div style='text-align: center; padding: 10px; border-bottom: 1px solid #f0f0f0;'>"
61
+ "<h1>Interactive Sales Data Dashboard</h1>"
62
+ "<p>Analyze synthetic sales data using linked controls and dynamic charts.</p>"
63
+ "<p>Built with <a href='https://huggingface.co/spaces/akhaliq/anycoder' target='_blank' style='color: blue; text-decoration: none;'>anycoder</a></p>"
64
+ "</div>"
65
+ )
66
+
67
+ # State to hold the original, potentially edited dataset
68
+ raw_data_state = gr.State(value=initial_df)
69
+
70
+ with gr.Row(equal_height=False):
71
+
72
+ # --- Column 1: Controls and Raw Data Display ---
73
+ with gr.Column(scale=2, min_width=400):
74
+ with gr.Accordion("Filters and Controls", open=True):
75
+ region_dropdown = gr.Dropdown(
76
+ choices=initial_regions,
77
+ value="All",
78
+ label="Select Region",
79
+ )
80
+
81
+ profit_slider = gr.Slider(
82
+ minimum=MIN_PROFIT_SLIDER,
83
+ maximum=MAX_PROFIT_SLIDER,
84
+ value=MIN_PROFIT_SLIDER,
85
+ step=50,
86
+ label="Minimum Profit Filter",
87
+ interactive=True
88
+ )
89
+
90
+ refresh_btn = gr.Button("Apply Filters & Update Plots", variant="primary")
91
+
92
+ with gr.Group():
93
+ # Dataframe for raw data display and editing
94
+ raw_data_display = gr.Dataframe(
95
+ value=initial_df,
96
+ headers=initial_df.columns.tolist(),
97
+ datatype=['date', 'str', 'str', 'number', 'number', 'number', 'number'],
98
+ interactive=True, # Allow user edits
99
+ label="Raw Sales Data (Click a row to see details)",
100
+ height=500,
101
+ show_row_numbers=True
102
+ )
103
+
104
+ selection_output = gr.Textbox(
105
+ label="Selected Row Details",
106
+ lines=4,
107
+ interactive=False,
108
+ placeholder="Click on a row in the table above to see details here."
109
+ )
110
+
111
+ # --- Column 2: Visualizations ---
112
+ with gr.Column(scale=3, min_width=500):
113
+ with gr.Tabs():
114
+ with gr.TabItem("Graphical Report"):
115
+ with gr.Row():
116
+ line_plot_out = gr.LinePlot(label="Sales Trend Over Time")
117
+ with gr.Row():
118
+ bar_plot_out = gr.BarPlot(label="Product Quantity Breakdown")
119
+ with gr.TabItem("Chat"):
120
+ chatbot = gr.Chatbot()
121
+ msg = gr.Textbox(placeholder="Type your message here...", show_label=False)
122
+ msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
123
+
124
+ # --- Event Listeners and Interactions ---
125
+
126
+ # Define the inputs and outputs for the main plotting function
127
+ inputs_for_plot = [raw_data_state, region_dropdown, profit_slider]
128
+ outputs_for_plot = [line_plot_out, bar_plot_out]
129
+
130
+ # 1. Update plots when the "Apply" button is clicked
131
+ refresh_btn.click(plot_data_updates, inputs=inputs_for_plot, outputs=outputs_for_plot)
132
+
133
+ # 2. Update plots when filter controls are changed (live update)
134
+ region_dropdown.change(plot_data_updates, inputs=inputs_for_plot, outputs=outputs_for_plot)
135
+ profit_slider.release(plot_data_updates, inputs=inputs_for_plot, outputs=outputs_for_plot)
136
+
137
+ # 3. Update the shared `raw_data_state` if a user edits the DataFrame component.
138
+ # This ensures that plot updates use the latest user-modified data.
139
+ def update_state(df):
140
+ return df
141
+
142
+ raw_data_display.edit(
143
+ update_state,
144
+ inputs=[raw_data_display],
145
+ outputs=[raw_data_state],
146
+ queue=False # Use a fast, non-blocking update for the state
147
+ )
148
+
149
+ # 4. Handle DataFrame row selection to show details
150
+ raw_data_display.select(
151
+ handle_data_selection,
152
+ inputs=[raw_data_state],
153
+ outputs=[selection_output]
154
+ )
155
+
156
+ # 5. Initial plot generation on application load
157
+ demo.load(plot_data_updates, inputs=inputs_for_plot, outputs=outputs_for_plot)
158
+
159
+
160
+ demo.queue().launch()
data_utils.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from datetime import datetime, timedelta
4
+ import gradio as gr
5
+
6
+ def generate_initial_data(n_rows=50):
7
+ """Generates a synthetic sales DataFrame for simulation."""
8
+ dates = [datetime(2023, 1, 1) + timedelta(days=i) for i in range(n_rows)]
9
+ np.random.seed(42)
10
+
11
+ data = {
12
+ 'Date': dates,
13
+ 'Region': np.random.choice(['North', 'South', 'East', 'West'], n_rows),
14
+ 'Product': np.random.choice(['A', 'B', 'C', 'D'], n_rows),
15
+ 'Sales': np.random.randint(100, 500, n_rows),
16
+ 'Quantity': np.random.randint(5, 50, n_rows),
17
+ 'Cost': np.random.uniform(10, 50, n_rows).round(2)
18
+ }
19
+ df = pd.DataFrame(data)
20
+ # Calculate Profit
21
+ df['Profit'] = (df['Sales'] - df['Cost']) * df['Quantity']
22
+ return df
23
+
24
+ def process_data(df, selected_region, min_profit):
25
+ """Filters and aggregates data based on user controls."""
26
+ if df is None:
27
+ return pd.DataFrame(), pd.DataFrame()
28
+
29
+ # Ensure data from Gradio component is handled as a DataFrame
30
+ # This is important as gr.State might pass the raw object
31
+ if not isinstance(df, pd.DataFrame):
32
+ df = pd.DataFrame(df)
33
+
34
+ # Filtering
35
+ if selected_region != "All":
36
+ df = df[df['Region'] == selected_region]
37
+
38
+ df = df[df['Profit'] >= min_profit]
39
+
40
+ # Aggregation for Line Plot (Daily Sales Trend)
41
+ if not df.empty:
42
+ df['Date'] = pd.to_datetime(df['Date'])
43
+ daily_summary = df.groupby(df['Date'].dt.date)['Sales'].sum().reset_index()
44
+ daily_summary.rename(columns={'Date': 'Day', 'Sales': 'Total Sales'}, inplace=True)
45
+ else:
46
+ daily_summary = pd.DataFrame(columns=['Day', 'Total Sales'])
47
+
48
+ # Aggregation for Bar Plot (Product breakdown)
49
+ product_summary = df.groupby('Product')['Quantity'].sum().reset_index()
50
+
51
+ return daily_summary, product_summary
52
+
53
+ def handle_data_selection(df, evt: gr.SelectData):
54
+ """Handles the selection event on the DataFrame component."""
55
+ if df is None or not evt.index:
56
+ return "No data selected."
57
+
58
+ if not isinstance(df, pd.DataFrame):
59
+ df = pd.DataFrame(df)
60
+
61
+ row_index = evt.index[0]
62
+
63
+ if row_index >= len(df):
64
+ return "Invalid row selected."
65
+
66
+ row_data = df.iloc[row_index].to_dict()
67
+
68
+ output_text = f"Selected Row {row_index} details:\n"
69
+ for key, value in row_data.items():
70
+ # Handle datetime objects for display
71
+ if isinstance(value, (datetime, pd.Timestamp)):
72
+ value = value.strftime('%Y-%m-%d')
73
+ elif isinstance(value, np.generic):
74
+ value = value.item() # Convert numpy types to native Python types
75
+
76
+ output_text += f" {key}: {value}\n"
77
+
78
+ return output_text
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ pandas
2
+ numpy
3
+ openpyxl
4
+ gradio
5
+ requests
6
+ Pillow