Update app.R
Browse files
app.R
CHANGED
@@ -1,58 +1,179 @@
|
|
1 |
library(shiny)
|
2 |
library(bslib)
|
3 |
-
library(
|
4 |
-
library(
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
),
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
)
|
26 |
|
27 |
-
server
|
28 |
-
|
29 |
-
req(input$species)
|
30 |
-
df |> filter(Species %in% input$species)
|
31 |
-
})
|
32 |
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
}
|
57 |
|
58 |
-
|
|
|
|
1 |
library(shiny)
|
2 |
library(bslib)
|
3 |
+
library(bs4Dash)
|
4 |
+
library(ellmer)
|
5 |
+
library(querychat)
|
6 |
+
library(shinythemes)
|
7 |
+
library(gapminder)
|
8 |
+
library(highcharter)
|
9 |
+
library(reactable)
|
10 |
+
library(shinychat)
|
11 |
+
|
12 |
+
Sys.setenv(GOOGLE_API_KEY = "AIzaSyAau0taEMZoOPtqDS-K_ZxiN6lycfcJ_pE")
|
13 |
+
|
14 |
+
# get Gemini api key
|
15 |
+
api_key = Sys.getenv("GOOGLE_API_KEY")
|
16 |
+
|
17 |
+
# create data of the first 500 rows in gapminder
|
18 |
+
gdp_data <- gapminder
|
19 |
+
|
20 |
+
|
21 |
+
# initialize querychat with mtcars dataset and Gemini chat function
|
22 |
+
querychat_config <- querychat_init(gdp_data,
|
23 |
+
create_chat_func = purrr::partial(ellmer::chat_google_gemini, model = "gemini-2.0-flash"),
|
24 |
+
greeting = "")
|
25 |
+
|
26 |
+
# define the UI of the dashboard
|
27 |
+
ui <- tabsetPanel(
|
28 |
+
id = "tabs",
|
29 |
+
type = "pills",
|
30 |
+
tabPanel(
|
31 |
+
id = "query-tab",
|
32 |
+
title = strong("Data Query"),
|
33 |
+
icon = icon("chart-line",lib = "font-awesome",),
|
34 |
+
page_sidebar(
|
35 |
+
# hover bounce effect of value boxes
|
36 |
+
tags$head(
|
37 |
+
tags$style(HTML("
|
38 |
+
.bounce-hover:hover {
|
39 |
+
animation: bounce 0.6s infinite alternate;
|
40 |
+
}
|
41 |
+
|
42 |
+
@keyframes bounce {
|
43 |
+
from {
|
44 |
+
transform: translateY(0);
|
45 |
+
}
|
46 |
+
to {
|
47 |
+
transform: translateY(-10px);
|
48 |
+
}
|
49 |
+
}
|
50 |
+
"))
|
51 |
+
),
|
52 |
+
sidebar = querychat_sidebar("chat",width = "25%"),
|
53 |
+
# to change div background color on hover: onmouseover = "this.style.backgroundColor='mediumseagreen';", onmouseout="this.style.backgroundColor='DodgerBlue';". In style = "cursor: pointer;
|
54 |
+
fluidRow(column(4,tags$div(class = "bounce-hover",valueBoxOutput("GDP"), style = "text-align:center; font-size: 20px; font-weight: bold;color:black;")),
|
55 |
+
column(4,tags$div(class = "bounce-hover",valueBoxOutput("Population"), style = "text-align:center; font-size: 20px; font-weight: bold;")),
|
56 |
+
column(4,tags$div(class = "bounce-hover",valueBoxOutput("LifeExp"),style = "text-align:center; font-size: 20px; font-weight: bold;"))),
|
57 |
+
fluidRow(column(6,highchartOutput("line",width = "98%")),
|
58 |
+
column(6,highchartOutput("column",width = "98%"))),
|
59 |
+
fluidRow(reactable::reactableOutput("table",width = "100%"))
|
60 |
+
)
|
61 |
),
|
62 |
+
tabPanel(
|
63 |
+
id = "chat-tab",
|
64 |
+
title = strong("EconoBot"),
|
65 |
+
icon = icon("robot",lib = "font-awesome"),
|
66 |
+
h3(strong("🤖 EconoBot"),style="text-align:center;color:DodgerBlue;"),
|
67 |
+
h5(strong("Answers your economic and financial questions"),style="text-align:center;color:#37474f;"),
|
68 |
+
chat_ui("gemini_chat",placeholder = "Type your message here...")
|
69 |
+
)
|
70 |
)
|
71 |
|
72 |
+
# define the server logic required to interact with the data
|
73 |
+
server <- function(input, output) {
|
|
|
|
|
|
|
74 |
|
75 |
+
# create a querychat object using the config from step 1.
|
76 |
+
query_chat <- querychat_server("chat", querychat_config)
|
77 |
+
|
78 |
+
output$table <- reactable::renderReactable({
|
79 |
+
reactable::reactable(query_chat$df(),
|
80 |
+
bordered = T,
|
81 |
+
compact = T,
|
82 |
+
highlight = T,
|
83 |
+
striped = T,
|
84 |
+
searchable = T,
|
85 |
+
filterable = T,
|
86 |
+
defaultColDef = colDef(align = "center"),
|
87 |
+
theme = reactable::reactableTheme(
|
88 |
+
stripedColor = "#0091ea",
|
89 |
+
borderColor = "#0091ea",
|
90 |
+
borderWidth = 3
|
91 |
+
))
|
92 |
+
})
|
93 |
+
|
94 |
+
output$line <- renderHighchart({
|
95 |
+
req(query_chat$df())
|
96 |
+
my_colors <- c("Africa" = "#1f77b4", "Asia" = "#ff7f0e", "Europe" = "#2ca02c",
|
97 |
+
"Americas" = "#d62728", "Oceania" = "#9467bd")
|
98 |
+
|
99 |
+
hchart(query_chat$df(),
|
100 |
+
type = "line",
|
101 |
+
hcaes(x = year, y = pop, group = continent)) |>
|
102 |
+
hc_colors(unname(my_colors)) |>
|
103 |
+
hc_title(text = "Population Per Continent or Country")
|
104 |
+
})
|
105 |
+
|
106 |
+
output$column <- renderHighchart({
|
107 |
+
req(query_chat$df())
|
108 |
+
my_colors <- c("Africa" = "#1f77b4", "Asia" = "#ff7f0e", "Europe" = "#2ca02c",
|
109 |
+
"Americas" = "#d62728", "Oceania" = "#9467bd")
|
110 |
+
|
111 |
+
hchart(query_chat$df(),
|
112 |
+
type = "column",
|
113 |
+
hcaes(x = year, y = gdpPercap, group = continent)) |>
|
114 |
+
hc_colors(unname(my_colors)) |>
|
115 |
+
hc_title(text = "GDP Per Capita Per Continent or Country")
|
116 |
+
})
|
117 |
+
|
118 |
+
output$GDP <- renderValueBox({
|
119 |
+
req(query_chat$df())
|
120 |
+
valueBox(
|
121 |
+
value = round(mean(query_chat$df()$gdpPercap, na.rm = TRUE), 2),
|
122 |
+
color = "success",
|
123 |
+
icon = icon("dollar"),
|
124 |
+
elevation = 4,
|
125 |
+
width = 4,
|
126 |
+
subtitle = " ",
|
127 |
+
footer = "Average GDP Per Capita"
|
128 |
+
)
|
129 |
+
})
|
130 |
+
|
131 |
+
output$Population <- renderValueBox({
|
132 |
+
req(query_chat$df())
|
133 |
+
valueBox(
|
134 |
+
value = round(mean(query_chat$df()$pop, na.rm = TRUE), 0),
|
135 |
+
color = "primary",
|
136 |
+
icon = icon("people-group"),
|
137 |
+
elevation = 4,
|
138 |
+
width = 4,
|
139 |
+
subtitle = " ",
|
140 |
+
footer = "Average Population"
|
141 |
+
)
|
142 |
+
})
|
143 |
+
|
144 |
+
output$LifeExp <- renderValueBox({
|
145 |
+
req(query_chat$df())
|
146 |
+
valueBox(
|
147 |
+
value = round(mean(query_chat$df()$lifeExp, na.rm = TRUE), 2),
|
148 |
+
color = "warning",
|
149 |
+
icon = icon("person-walking"),
|
150 |
+
elevation = 4,
|
151 |
+
width = 4,
|
152 |
+
subtitle = " ",
|
153 |
+
footer = "Average Life Expectancy"
|
154 |
+
)
|
155 |
+
})
|
156 |
+
|
157 |
+
# write logic for Gemini chat
|
158 |
+
gemini <- chat_google_gemini(
|
159 |
+
system_prompt = "You are a friendly and helpful economic and financial expert who only answers questions around country economy and finance. If a user asks a question that is not related to economics or finance, give the following polite response: 'I'm sorry, I can only provide economic and financial information'.
|
160 |
+
Also, if a user asks for specific or personal financial advice, give the following polite response: 'I'm sorry, I cannot provide specific financial advice. Please consult a financial advisor for personalized guidance.'",
|
161 |
+
model = "gemini-2.0-flash",
|
162 |
+
api_key = api_key,
|
163 |
+
echo = T
|
164 |
)
|
165 |
+
|
166 |
+
observeEvent(input$gemini_chat_user_input, {
|
167 |
+
|
168 |
+
# input$gemini_chat_user_input is from the chat id in the UI
|
169 |
+
gemini$chat_async(input$gemini_chat_user_input)$then(function(response) {
|
170 |
+
chat_append("gemini_chat", response)
|
171 |
+
})$catch(function(error) {
|
172 |
+
warning("An error occurred during chat_async: ", error$message)
|
173 |
+
})
|
174 |
+
})
|
175 |
+
|
176 |
}
|
177 |
|
178 |
+
# Run the application
|
179 |
+
shinyApp(ui = ui, server = server)
|