suratan.boon commited on
Commit
1b5eb19
·
1 Parent(s): 793a998

add dashboard page

Browse files
src/chatbot/dashboard/dashboard_page.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sqlite3
3
+ import pandas as pd
4
+ import gradio as gr
5
+ import plotly.graph_objects as go
6
+ import plotly.express as px
7
+
8
+ from dashboard.visualize_component import (
9
+ generate_budget_utilization_gauge_chart,
10
+ generate_deliverable_budget_vs_expense_bar_chart,
11
+ generate_spending_distribution_pie_chart,
12
+ generate_daily_spending_bar_chart,
13
+ generate_cumulative_spending_line_chart,
14
+ generate_risk_level_distribution_pie_chart,
15
+ generate_deliverable_timeline_gantt_chart,
16
+ render_deliverable_summary_cards
17
+ )
18
+
19
+ DB_PATH = "database/hello_earth_data.db"
20
+
21
+ # ------------------------ Database Utilities ------------------------
22
+
23
+ def connect_db():
24
+ return sqlite3.connect(DB_PATH)
25
+
26
+ def fetch_df(query, params=None):
27
+ conn = connect_db()
28
+ df = pd.read_sql_query(query, conn, params=params or ())
29
+ conn.close()
30
+ return df
31
+
32
+ def get_project_data():
33
+ return fetch_df("SELECT * FROM Project")
34
+
35
+ def get_deliverable_data(project_id):
36
+ return fetch_df("SELECT * FROM Deliverable WHERE project_id = ?", (project_id,))
37
+
38
+ def get_all_deliverable_budgets(project_id):
39
+ query = """
40
+ SELECT d.title, db.*
41
+ FROM Deliverable_Budget db
42
+ JOIN Deliverable d ON db.deliverable_id = d.deliverable_id
43
+ WHERE d.project_id = ?
44
+ """
45
+ return fetch_df(query, (project_id,))
46
+
47
+ def get_all_expenses(project_id):
48
+ query = """
49
+ SELECT e.*
50
+ FROM Expense e
51
+ JOIN Deliverable d ON e.associated_deliverable_id = d.deliverable_id
52
+ WHERE d.project_id = ?
53
+ """
54
+ return fetch_df(query, (project_id,))
55
+
56
+ # ------------------------ Business Logic ------------------------
57
+
58
+ def get_total_expense(df, status=None):
59
+ if status:
60
+ df = df[df['status'] == status]
61
+ return df['total_payment_amount'].sum()
62
+
63
+ def get_total_budget(df):
64
+ return df[['wage', 'materials', 'tools_equipment', 'services', 'misc']].sum().sum()
65
+
66
+ def get_title_by_id(project_options, pid):
67
+ return next((p['title'] for p in project_options if p['project_id'] == pid), "Unknown")
68
+
69
+ def filter_expenses(search_term, df):
70
+ if not search_term:
71
+ return df
72
+ search_term = search_term.lower()
73
+ mask = pd.Series(False, index=df.index)
74
+ for col in [
75
+ 'expense_id', 'associated_deliverable_id', 'seller_name',
76
+ 'seller_address', 'seller_phone_number', 'buyer_name',
77
+ 'buyer_address', 'transaction_date', 'total_payment_amount',
78
+ 'expense_description', 'status'
79
+ ]:
80
+ mask |= df[col].astype(str).str.lower().str.contains(search_term)
81
+ return df[mask]
82
+
83
+ # ------------------------ UI Setup ------------------------
84
+
85
+ def create_dashboard_page():
86
+ project_list = get_project_data()
87
+ project_options = project_list[['project_id', 'title']].to_dict("records")
88
+
89
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as page:
90
+ selected_project_id = gr.State()
91
+ expense_df_state = gr.State()
92
+
93
+ # --- Page 1: Project Selection ---
94
+ with gr.Column(visible=True) as project_selector_page:
95
+ gr.Markdown("# 🌍 Select a Project")
96
+ project_dropdown = gr.Dropdown(
97
+ choices=[(p['title'], p['project_id']) for p in project_options],
98
+ label="Choose a Project"
99
+ )
100
+ project_go_button = gr.Button("View Dashboard")
101
+
102
+ # --- Page 2: Dashboard View ---
103
+ with gr.Column(visible=False) as dashboard_page:
104
+ dashboard_header = gr.Markdown("")
105
+ back_button = gr.Button("⬅ Back to Projects")
106
+
107
+ with gr.Column():
108
+ gr.Markdown("## Overall Project Financial Snapshot")
109
+ with gr.Row():
110
+ total_budget_md = gr.Markdown()
111
+ total_expense_md = gr.Markdown()
112
+ leftover_md = gr.Markdown()
113
+ gauge_plot = gr.Plot()
114
+
115
+ gr.Markdown("## Deliverable-Level Financial Insights")
116
+ with gr.Row():
117
+ bar_chart_plot = gr.Plot()
118
+ pie_chart_plot = gr.Plot()
119
+ with gr.Row():
120
+ daily_plot = gr.Plot()
121
+ cum_plot = gr.Plot()
122
+ with gr.Row():
123
+ with gr.Column(scale=1):
124
+ risk_plot = gr.Plot()
125
+ with gr.Column(scale=2):
126
+ cards_html = gr.HTML()
127
+ gantt_plot = gr.Plot()
128
+
129
+ gr.Markdown("## 💵 Expense Table")
130
+ search_box = gr.Textbox(placeholder="Search expenses", label="Search")
131
+ expense_table = gr.DataFrame()
132
+
133
+ # --- Event Handlers ---
134
+
135
+ search_box.change(
136
+ fn=filter_expenses,
137
+ inputs=[search_box, expense_df_state],
138
+ outputs=expense_table
139
+ )
140
+
141
+ def go_to_dashboard(project_id):
142
+ deliverables = get_deliverable_data(project_id)
143
+ budgets = get_all_deliverable_budgets(project_id)
144
+ expenses = get_all_expenses(project_id)
145
+
146
+ total_budget = get_total_budget(budgets)
147
+ total_expense = get_total_expense(expenses, status='approved')
148
+ leftover = total_budget - total_expense
149
+
150
+ if leftover < 0:
151
+ status, color = "⚠️ Over budget", "red"
152
+ elif leftover < total_budget * 0.1:
153
+ status, color = "⚠️ Low budget remaining", "orange"
154
+ else:
155
+ status, color = "✅ Budget on track", "green"
156
+
157
+ return [
158
+ project_id,
159
+ gr.update(visible=False),
160
+ gr.update(visible=True),
161
+ gr.update(value=f"# 📊 Financial Dashboard — {get_title_by_id(project_options, project_id)}"),
162
+ gr.update(value=f"### Total Money Granted\n## {total_budget:,.0f}"),
163
+ gr.update(value=f"### Total Expense Spendings\n## {total_expense:,.0f}\n({(total_expense / total_budget * 100):.1f}% of budget)"),
164
+ gr.update(value=f"### Leftover Budget\n## {leftover:,.0f}\n<span style='color: {color};'>{status}</span>"),
165
+ gr.update(value=generate_budget_utilization_gauge_chart(total_budget, total_expense)),
166
+ gr.update(value=generate_deliverable_budget_vs_expense_bar_chart(budgets, expenses)),
167
+ gr.update(value=generate_spending_distribution_pie_chart(expenses, budgets)),
168
+ gr.update(value=generate_daily_spending_bar_chart(expenses)),
169
+ gr.update(value=generate_cumulative_spending_line_chart(expenses)),
170
+ gr.update(value=generate_risk_level_distribution_pie_chart(deliverables)),
171
+ gr.update(value=generate_deliverable_timeline_gantt_chart(deliverables)),
172
+ gr.update(value=render_deliverable_summary_cards(deliverables)),
173
+ gr.update(value=expenses),
174
+ expenses
175
+ ]
176
+
177
+ def go_back_to_project_list():
178
+ return [gr.update(visible=True), gr.update(visible=False)]
179
+
180
+ # --- Button Triggers ---
181
+
182
+ project_go_button.click(
183
+ fn=go_to_dashboard,
184
+ inputs=project_dropdown,
185
+ outputs=[
186
+ selected_project_id,
187
+ project_selector_page,
188
+ dashboard_page,
189
+ dashboard_header,
190
+ total_budget_md,
191
+ total_expense_md,
192
+ leftover_md,
193
+ gauge_plot,
194
+ bar_chart_plot,
195
+ pie_chart_plot,
196
+ daily_plot,
197
+ cum_plot,
198
+ risk_plot,
199
+ gantt_plot,
200
+ cards_html,
201
+ expense_table,
202
+ expense_df_state
203
+ ]
204
+ )
205
+
206
+ back_button.click(
207
+ fn=go_back_to_project_list,
208
+ outputs=[project_selector_page, dashboard_page]
209
+ )
210
+
211
+ return page
212
+
213
+ if __name__ == "__main__":
214
+ page = create_dashboard_page()
215
+ page.launch()
src/chatbot/dashboard/visualize_component.py ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import plotly.graph_objects as go
2
+ import plotly.express as px
3
+ import pandas as pd
4
+ from datetime import datetime
5
+
6
+ def generate_budget_utilization_gauge_chart(total_budget, total_expense):
7
+ """Generate a gauge chart comparing budget vs. total expense."""
8
+
9
+ gauge_steps = [
10
+ {'range': [0, total_budget * 0.7], 'color': '#d4f1dd'},
11
+ {'range': [total_budget * 0.7, total_budget * 0.9], 'color': '#fff2cc'},
12
+ {'range': [total_budget * 0.9, total_budget * 1.2], 'color': '#f8d7da'}
13
+ ]
14
+
15
+ gauge_threshold = {
16
+ 'line': {'color': 'red', 'width': 4},
17
+ 'thickness': 0.75,
18
+ 'value': total_budget
19
+ }
20
+
21
+ gauge = go.Indicator(
22
+ mode="gauge+number+delta",
23
+ value=total_expense,
24
+ title={'text': "Total Expense", 'font': {'size': 24}},
25
+ delta={
26
+ 'reference': total_budget,
27
+ 'increasing': {'color': 'red'},
28
+ 'decreasing': {'color': 'green'}
29
+ },
30
+ gauge={
31
+ 'axis': {'range': [None, total_budget * 1.2], 'tickwidth': 1},
32
+ 'bar': {'color': '#1f77b4'},
33
+ 'bgcolor': 'white',
34
+ 'borderwidth': 2,
35
+ 'bordercolor': 'gray',
36
+ 'steps': gauge_steps,
37
+ 'threshold': gauge_threshold
38
+ }
39
+ )
40
+
41
+ fig = go.Figure(gauge)
42
+
43
+ fig.update_layout(
44
+ paper_bgcolor='rgba(0,0,0,0)',
45
+ plot_bgcolor='rgba(0,0,0,0)',
46
+ margin=dict(t=50, r=25, l=25, b=25),
47
+ height=300
48
+ )
49
+
50
+ return fig
51
+
52
+ def generate_deliverable_budget_vs_expense_bar_chart(budget_df, expense_df):
53
+ """Generate a grouped bar chart comparing budget vs. actual expense per deliverable."""
54
+
55
+ # Calculate total budget per deliverable
56
+ budget_df['total_budget'] = budget_df[['wage', 'materials', 'tools_equipment', 'services', 'misc']].sum(axis=1)
57
+ budget_df['title'] = budget_df.get('title', pd.Series(["Unnamed Deliverable"] * len(budget_df)))
58
+
59
+ # Prepare budget summary
60
+ budget_summary = budget_df[['title', 'total_budget']].rename(columns={'title': 'deliverable_title'})
61
+
62
+ # Summarize expenses per deliverable
63
+ expense_summary = (
64
+ expense_df.groupby('associated_deliverable_id')['total_payment_amount']
65
+ .sum()
66
+ .reset_index()
67
+ )
68
+
69
+ # Map deliverable titles to expenses
70
+ deliverable_titles = budget_df[['deliverable_id', 'title']].drop_duplicates()
71
+ expense_summary = (
72
+ pd.merge(
73
+ expense_summary,
74
+ deliverable_titles,
75
+ left_on='associated_deliverable_id',
76
+ right_on='deliverable_id',
77
+ how='left'
78
+ )
79
+ .rename(columns={
80
+ 'title': 'deliverable_title',
81
+ 'total_payment_amount': 'spending'
82
+ })
83
+ )
84
+
85
+ # Merge budget and expense summaries
86
+ merged_df = pd.merge(
87
+ budget_summary,
88
+ expense_summary[['deliverable_title', 'spending']],
89
+ on='deliverable_title',
90
+ how='left'
91
+ ).fillna({'spending': 0})
92
+
93
+ # Create bar chart
94
+ fig = go.Figure()
95
+
96
+ fig.add_trace(go.Bar(
97
+ x=merged_df['deliverable_title'],
98
+ y=merged_df['total_budget'],
99
+ name='Budget',
100
+ marker_color='green'
101
+ ))
102
+
103
+ fig.add_trace(go.Bar(
104
+ x=merged_df['deliverable_title'],
105
+ y=merged_df['spending'],
106
+ name='Expense',
107
+ marker_color='red'
108
+ ))
109
+
110
+ fig.update_layout(
111
+ title='Budget vs Expense by Deliverable',
112
+ barmode='group',
113
+ xaxis_title='Deliverables',
114
+ yaxis_title='Amount (THB)',
115
+ legend=dict(orientation='h', y=-0.2),
116
+ height=400,
117
+ paper_bgcolor='rgba(0,0,0,0)',
118
+ plot_bgcolor='rgba(0,0,0,0)',
119
+ margin=dict(t=50, r=25, l=25, b=25)
120
+ )
121
+
122
+ return fig
123
+
124
+ def generate_spending_distribution_pie_chart(expense_df, budget_df):
125
+ """Generate a pie chart showing the distribution of spending by deliverables."""
126
+
127
+ # Summarize total spending per deliverable
128
+ spending_summary = (
129
+ expense_df.groupby('associated_deliverable_id')['total_payment_amount']
130
+ .sum()
131
+ .reset_index()
132
+ )
133
+
134
+ # Map deliverable titles
135
+ deliverable_titles = budget_df[['deliverable_id', 'title']].drop_duplicates()
136
+ spending_summary = (
137
+ pd.merge(
138
+ spending_summary,
139
+ deliverable_titles,
140
+ left_on='associated_deliverable_id',
141
+ right_on='deliverable_id',
142
+ how='left'
143
+ )
144
+ .rename(columns={
145
+ 'title': 'deliverable_title',
146
+ 'total_payment_amount': 'amount'
147
+ })
148
+ )
149
+
150
+ # Handle missing titles
151
+ spending_summary['deliverable_title'] = spending_summary['deliverable_title'].fillna("Unnamed Deliverable")
152
+
153
+ # Create pie chart
154
+ fig = px.pie(
155
+ spending_summary,
156
+ values='amount',
157
+ names='deliverable_title',
158
+ title='Spending by Deliverables',
159
+ color_discrete_sequence=px.colors.qualitative.Set2
160
+ )
161
+
162
+ fig.update_traces(
163
+ textinfo='label+value',
164
+ hoverinfo='label+percent',
165
+ textposition='inside',
166
+ insidetextorientation='radial'
167
+ )
168
+
169
+ fig.update_layout(
170
+ paper_bgcolor='rgba(0,0,0,0)',
171
+ plot_bgcolor='rgba(0,0,0,0)',
172
+ margin=dict(t=50, r=25, l=25, b=25),
173
+ height=400
174
+ )
175
+
176
+ return fig
177
+
178
+ def generate_daily_spending_bar_chart(expense_df):
179
+ """Generate a bar chart showing daily spending amounts."""
180
+
181
+ # Prepare date and group by date
182
+ expense_df['transaction_date'] = pd.to_datetime(expense_df['transaction_date']).dt.date
183
+ daily_summary = (
184
+ expense_df.groupby('transaction_date')['total_payment_amount']
185
+ .sum()
186
+ .reset_index()
187
+ .rename(columns={
188
+ 'transaction_date': 'date',
189
+ 'total_payment_amount': 'amount'
190
+ })
191
+ .sort_values('date')
192
+ )
193
+
194
+ # Create bar chart
195
+ fig = go.Figure()
196
+
197
+ fig.add_trace(go.Bar(
198
+ x=daily_summary['date'],
199
+ y=daily_summary['amount'],
200
+ name='Daily Spending',
201
+ marker_color='#1f77b4'
202
+ ))
203
+
204
+ fig.update_layout(
205
+ title='Daily Spending',
206
+ xaxis_title='Date',
207
+ yaxis_title='Amount (THB)',
208
+ paper_bgcolor='rgba(0,0,0,0)',
209
+ plot_bgcolor='rgba(0,0,0,0)',
210
+ margin=dict(t=50, r=25, l=25, b=25),
211
+ height=400
212
+ )
213
+
214
+ return fig
215
+
216
+ def generate_cumulative_spending_line_chart(expense_df):
217
+ """Generate a line chart showing cumulative spending over time."""
218
+
219
+ # Convert to date and summarize spending per day
220
+ expense_df['transaction_date'] = pd.to_datetime(expense_df['transaction_date']).dt.date
221
+ daily_summary = (
222
+ expense_df.groupby('transaction_date')['total_payment_amount']
223
+ .sum()
224
+ .reset_index()
225
+ .rename(columns={'transaction_date': 'date', 'total_payment_amount': 'amount'})
226
+ .sort_values('date')
227
+ )
228
+
229
+ # Calculate cumulative spending
230
+ daily_summary['cumulative_amount'] = daily_summary['amount'].cumsum()
231
+
232
+ # Create line chart
233
+ fig = go.Figure()
234
+
235
+ fig.add_trace(go.Scatter(
236
+ x=daily_summary['date'],
237
+ y=daily_summary['cumulative_amount'],
238
+ mode='lines+markers',
239
+ name='Cumulative Spending',
240
+ marker_color='#ff7f0e'
241
+ ))
242
+
243
+ fig.update_layout(
244
+ title='Cumulative Spending Over Time',
245
+ xaxis_title='Date',
246
+ yaxis_title='Cumulative Amount (THB)',
247
+ paper_bgcolor='rgba(0,0,0,0)',
248
+ plot_bgcolor='rgba(0,0,0,0)',
249
+ margin=dict(t=50, r=25, l=25, b=25),
250
+ height=400
251
+ )
252
+
253
+ return fig
254
+
255
+ def generate_risk_level_distribution_pie_chart(deliverable_df):
256
+ """Generate a pie chart showing the distribution of deliverables by risk level."""
257
+
258
+ risk_summary = (
259
+ deliverable_df['risk_level']
260
+ .value_counts()
261
+ .reset_index(name='count')
262
+ .rename(columns={'index': 'risk_level'})
263
+ )
264
+
265
+ fig = px.pie(
266
+ risk_summary,
267
+ values='count',
268
+ names='risk_level',
269
+ title='Distribution of Deliverables by Risk Level',
270
+ color_discrete_sequence=px.colors.qualitative.Pastel
271
+ )
272
+
273
+ fig.update_traces(
274
+ textinfo='label+percent',
275
+ hoverinfo='label+value+percent',
276
+ textposition='inside'
277
+ )
278
+
279
+ fig.update_layout(
280
+ paper_bgcolor='rgba(0,0,0,0)',
281
+ plot_bgcolor='rgba(0,0,0,0)',
282
+ margin=dict(t=50, r=25, l=25, b=25),
283
+ height=400
284
+ )
285
+
286
+ return fig
287
+
288
+ def generate_deliverable_timeline_gantt_chart(deliverable_df):
289
+ """Generate a Gantt chart showing deliverables over time, colored by risk level."""
290
+
291
+ df = deliverable_df.copy()
292
+
293
+ # Ensure proper date format
294
+ df['start_date'] = pd.to_datetime(df['start_date'])
295
+ df['end_date'] = pd.to_datetime(df['end_date'])
296
+
297
+ # Fill missing values
298
+ df['title'] = df['title'].fillna("Unnamed Deliverable")
299
+ df['risk_level'] = df['risk_level'].fillna("Unknown")
300
+
301
+ # Create Gantt chart
302
+ fig = px.timeline(
303
+ df,
304
+ x_start='start_date',
305
+ x_end='end_date',
306
+ y='title',
307
+ color='risk_level',
308
+ title='Deliverable Timeline (Gantt Chart)',
309
+ hover_data=['status', 'risk_level_rationale']
310
+ )
311
+
312
+ # Reverse Y-axis to have earliest on top
313
+ fig.update_yaxes(autorange='reversed')
314
+
315
+ # Add vertical "Today" line
316
+ today = datetime.today()
317
+
318
+ fig.add_shape(
319
+ type='line',
320
+ x0=today,
321
+ x1=today,
322
+ y0=0,
323
+ y1=1,
324
+ xref='x',
325
+ yref='paper',
326
+ line=dict(color='red', width=2, dash='dash')
327
+ )
328
+
329
+ fig.add_annotation(
330
+ x=today,
331
+ y=1.02,
332
+ xref='x',
333
+ yref='paper',
334
+ text='Today',
335
+ showarrow=False,
336
+ font=dict(color='red', size=12)
337
+ )
338
+
339
+ # Layout customization
340
+ fig.update_layout(
341
+ paper_bgcolor='rgba(0,0,0,0)',
342
+ plot_bgcolor='rgba(0,0,0,0)',
343
+ margin=dict(t=50, r=25, l=25, b=25),
344
+ height=500
345
+ )
346
+
347
+ return fig
348
+
349
+ def render_deliverable_summary_cards(deliverable_df):
350
+ """Render a grid of HTML cards summarizing deliverables with risk level and status indicators."""
351
+
352
+ def format_date(date_str):
353
+ if pd.isna(date_str):
354
+ return "-"
355
+ return pd.to_datetime(date_str).strftime("%d %b %Y")
356
+
357
+ # Define color mappings
358
+ risk_level_colors = {
359
+ "green": "#28a745",
360
+ "yellow": "#ffc107",
361
+ "red": "#dc3545",
362
+ "unknown": "#6c757d"
363
+ }
364
+
365
+ status_colors = {
366
+ "ongoing": "#17a2b8",
367
+ "done": "#007bff",
368
+ "reviewing": "#ffc107",
369
+ "approved": "#28a745"
370
+ }
371
+
372
+ # Start HTML grid
373
+ cards_html = """
374
+ <div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'>
375
+ """
376
+
377
+ for _, row in deliverable_df.iterrows():
378
+ title = row.get('title', 'Untitled Deliverable')
379
+ deliverable_id = row.get('deliverable_id', '-')
380
+ status = str(row.get('status', 'N/A')).lower()
381
+ risk_level = str(row.get('risk_level', 'unknown')).lower()
382
+
383
+ status_color = status_colors.get(status, "#6c757d")
384
+ risk_color = risk_level_colors.get(risk_level, "#6c757d")
385
+
386
+ start_date = format_date(row.get('start_date'))
387
+ end_date = format_date(row.get('end_date'))
388
+
389
+ cards_html += f"""
390
+ <div style='border: 1px solid #dee2e6; border-radius: 10px; padding: 15px; background-color: #ffffff;'>
391
+ <div style='font-weight: bold; font-size: 1.1em; margin-bottom: 5px;'>
392
+ {title}
393
+ <div style='font-size: 0.85em; color: #6c757d;'>ID: {deliverable_id}</div>
394
+ </div>
395
+ <div style='margin: 8px 0;'>
396
+ <span style='padding: 3px 8px; border-radius: 4px; font-size: 0.75em; background-color: {status_color}; color: white; margin-right: 5px;'>
397
+ {status.capitalize()}
398
+ </span>
399
+ <span style='padding: 3px 8px; border-radius: 4px; font-size: 0.75em; background-color: {risk_color}; color: white;'>
400
+ Risk: {risk_level.capitalize()}
401
+ </span>
402
+ </div>
403
+ <div style='font-size: 0.9em; color: #6c757d;'>
404
+ <strong>Start:</strong> {start_date}<br>
405
+ <strong>End:</strong> {end_date}
406
+ </div>
407
+ </div>
408
+ """
409
+
410
+ cards_html += "</div>"
411
+ return cards_html
src/chatbot/main.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from dashboard.dashboard_page import create_dashboard_page
3
+
4
+ dashboard_page = create_dashboard_page()
5
+
6
+ dashboard = gr.TabbedInterface(
7
+ [dashboard_page],
8
+ ["Project"],
9
+ )
10
+
11
+ if __name__ == "__main__":
12
+ dashboard.launch()