Skip to content

Commit bd7c864

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

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-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

+117
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,76 @@ 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, 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+
console.log("view", view);
260+
261+
try {
262+
await client.views.update({
263+
view_id: view.id,
264+
view: {
265+
type: "modal",
266+
callback_id: "inc_stats_modal",
267+
private_metadata: JSON.stringify({ numberOfDays }), // Store the number of days in the metadata
268+
title: {
269+
type: "plain_text",
270+
text: "Inc stats",
271+
},
272+
blocks: [
273+
...view.blocks.filter(block => block.block_id !== "chart_block_category_by_week"),
274+
{
275+
type: "image",
276+
block_id: "chart_block_category_by_week",
277+
image_url: chartUrl,
278+
alt_text: "Incidents per Week",
279+
},
280+
],
281+
submit: {
282+
type: "plain_text",
283+
text: "Submit",
284+
},
285+
},
286+
});
287+
} catch (error) {
288+
console.error("Error updating modal:", error);
289+
}
290+
});
291+
216292
(async () => {
217293
await app.start(process.env.PORT || 3000);
218294
console.log("⚡️ Bolt app started");
@@ -301,3 +377,44 @@ async function generateIncByCategoryChart(numberOfDays) {
301377
console.log("url", chart.getUrl());
302378
return chart.getUrl();
303379
}
380+
381+
async function generateIncByCategoryOverWeeksChart(category, numberOfDays) {
382+
const dbResponse = await getIncCountForCategoryByWeek(category, numberOfDays);
383+
384+
const labels = dbResponse.rows.map((row) => row.category);
385+
const data = dbResponse.rows.map((row) => parseInt(row.count, 10));
386+
387+
// Generate the bar chart URL using QuickChart
388+
const chart = new QuickChart();
389+
chart.setConfig({
390+
type: "line",
391+
data: {
392+
labels: labels,
393+
datasets: [
394+
{
395+
label: `Incidents for ${category}`,
396+
data: data,
397+
backgroundColor: "rgba(75, 192, 192, 0.2)",
398+
borderColor: "rgba(75, 192, 192, 1)",
399+
borderWidth: 1,
400+
},
401+
],
402+
},
403+
options: {
404+
scales: {
405+
yAxes: [{
406+
ticks: {
407+
beginAtZero: true,
408+
min: 0,
409+
stepSize: 1,
410+
precision: 0
411+
}
412+
}]
413+
}
414+
}
415+
});
416+
chart.setWidth(800);
417+
chart.setHeight(400);
418+
console.log("url", chart.getUrl());
419+
return chart.getUrl();
420+
}

0 commit comments

Comments
 (0)