Skip to content

Commit 5cd6d5f

Browse files
committed
Chart for incs by categories over weeks
1 parent 985f30c commit 5cd6d5f

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed

app/db.js

+37
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,43 @@ export async function getIncNumberByWeek(numberOfDaysAgo) {
117117
return await client.query(queryText);
118118
}
119119

120+
export async function getIncCountForCategoryByWeek(category, numberOfDaysAgo) {
121+
let queryText;
122+
123+
if (numberOfDaysAgo === undefined) {
124+
queryText = {
125+
text: `
126+
SELECT DATE_TRUNC('week', created_at) AS week, COUNT(*) AS count
127+
FROM incs
128+
WHERE category = $1
129+
GROUP BY week
130+
ORDER BY week;
131+
`,
132+
values
133+
: [category],
134+
};
135+
} else {
136+
// Calculate the date range
137+
const dateAgo = new Date();
138+
dateAgo.setDate(dateAgo.getDate() - numberOfDaysAgo);
139+
const formattedDateAgo = dateAgo.toISOString(); // ISO8601 format
140+
141+
// Construct the SQL query with parameterized query
142+
queryText = {
143+
text: `
144+
SELECT DATE_TRUNC('week', created_at) AS week, COUNT(*) AS count
145+
FROM incs
146+
WHERE category = $1 AND created_at >= $2
147+
GROUP BY week
148+
ORDER BY week;
149+
`,
150+
values: [category, formattedDateAgo],
151+
};
152+
}
153+
154+
return await client.query(queryText);
155+
}
156+
120157
export async function getCategoriesArray() {
121158
try {
122159
const result = await client.query("SELECT name FROM categories");

app/server.js

+139
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getIncByCategory,
88
getCategoriesArray,
99
addCategory,
10+
getIncCountForCategoryByWeek
1011
} from "./db.js";
1112
import QuickChart from "quickchart-js";
1213

@@ -175,13 +176,26 @@ app.command("/inc_stats", async ({ ack, body, client }) => {
175176
const chartUrlByWeek = await generateIncByWeekChart(numberOfDays);
176177
const chartUrlByCategory = await generateIncByCategoryChart(numberOfDays);
177178

179+
const categories = await getCategoriesArray();
180+
181+
182+
// Format the options
183+
const categoryOptions = categories.map((category) => ({
184+
text: {
185+
type: "plain_text",
186+
text: category,
187+
},
188+
value: category,
189+
}));
190+
178191
// Post a response in the channel where the command was invoked
179192
try {
180193
await client.views.open({
181194
trigger_id: body.trigger_id,
182195
view: {
183196
type: "modal",
184197
callback_id: "inc_stats_modal",
198+
private_metadata: JSON.stringify({ numberOfDays }), // Store the number of days in the metadata
185199
title: {
186200
type: "plain_text",
187201
text: `Inc stats ${numberOfDays ? `siste ${numberOfDays} dager` : ""}`,
@@ -205,14 +219,98 @@ app.command("/inc_stats", async ({ ack, body, client }) => {
205219
image_url: chartUrlByWeek,
206220
alt_text: "Incidents per Week",
207221
},
222+
{
223+
type: "input",
224+
block_id: "category_select",
225+
element: {
226+
type: "static_select",
227+
action_id: "category_modal",
228+
placeholder: {
229+
type: "plain_text",
230+
text: "Select a category",
231+
},
232+
options: categoryOptions,
233+
},
234+
label: {
235+
type: "plain_text",
236+
text: "Category",
237+
},
238+
},
208239
],
240+
submit: {
241+
type: "plain_text",
242+
text: "Submit",
243+
},
209244
},
210245
});
211246
} catch (error) {
212247
console.error("Error posting message:", error);
213248
}
214249
});
215250

251+
app.view("inc_stats_modal", async ({ ack, body, view, client }) => {
252+
await ack();
253+
const selectedCategory = view.state.values.category_select.category_modal.selected_option.value;
254+
console.log("selectedCategory", selectedCategory);
255+
const numberOfDays = view?.metadata?.numberOfDays;
256+
console.log("numberOfDays", numberOfDays);
257+
const chartUrl = await generateIncByCategoryOverWeeksChart(selectedCategory, numberOfDays);
258+
259+
try {
260+
await client.views.update({
261+
view_id: view.id,
262+
view: {
263+
type: "modal",
264+
callback_id: "inc_stats_modal",
265+
private_metadata: JSON.stringify({ numberOfDays }), // Store the number of days in the metadata
266+
title: {
267+
type: "plain_text",
268+
text: "Inc stats",
269+
},
270+
blocks: [
271+
...body.view.blocks.filter(block => block.block_id !== "chart_block_category_by_week"),
272+
{
273+
type: "input",
274+
block_id: "category_select",
275+
element: {
276+
type: "static_select",
277+
action_id: "category_modal",
278+
placeholder: {
279+
type: "plain_text",
280+
text: "Select a category",
281+
},
282+
options: body.view.blocks[0].element.options,
283+
initial_option: {
284+
text: {
285+
type: "plain_text",
286+
text: selectedCategory,
287+
},
288+
value: selectedCategory,
289+
},
290+
},
291+
label: {
292+
type: "plain_text",
293+
text: "Category",
294+
},
295+
},
296+
{
297+
type: "image",
298+
block_id: "chart_block_category_by_week",
299+
image_url: chartUrl,
300+
alt_text: "Incidents per Week",
301+
},
302+
],
303+
submit: {
304+
type: "plain_text",
305+
text: "Submit",
306+
},
307+
},
308+
});
309+
} catch (error) {
310+
console.error("Error updating modal:", error);
311+
}
312+
});
313+
216314
(async () => {
217315
await app.start(process.env.PORT || 3000);
218316
console.log("⚡️ Bolt app started");
@@ -301,3 +399,44 @@ async function generateIncByCategoryChart(numberOfDays) {
301399
console.log("url", chart.getUrl());
302400
return chart.getUrl();
303401
}
402+
403+
async function generateIncByCategoryOverWeeksChart(category, numberOfDays) {
404+
const dbResponse = await getIncCountForCategoryByWeek(category, numberOfDays);
405+
406+
const labels = dbResponse.rows.map((row) => row.category);
407+
const data = dbResponse.rows.map((row) => parseInt(row.count, 10));
408+
409+
// Generate the bar chart URL using QuickChart
410+
const chart = new QuickChart();
411+
chart.setConfig({
412+
type: "line",
413+
data: {
414+
labels: labels,
415+
datasets: [
416+
{
417+
label: `Incidents for ${category}`,
418+
data: data,
419+
backgroundColor: "rgba(75, 192, 192, 0.2)",
420+
borderColor: "rgba(75, 192, 192, 1)",
421+
borderWidth: 1,
422+
},
423+
],
424+
},
425+
options: {
426+
scales: {
427+
yAxes: [{
428+
ticks: {
429+
beginAtZero: true,
430+
min: 0,
431+
stepSize: 1,
432+
precision: 0
433+
}
434+
}]
435+
}
436+
}
437+
});
438+
chart.setWidth(800);
439+
chart.setHeight(400);
440+
console.log("url", chart.getUrl());
441+
return chart.getUrl();
442+
}

0 commit comments

Comments
 (0)