Skip to content

Commit aca5cdd

Browse files
authored
Create tag_resolution_outliers.js
1 parent 8e468ea commit aca5cdd

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Background Script: Tag incident resolution outliers by z score
2+
(function() {
3+
var TABLE = 'incident';
4+
var DAYS = 30;
5+
var Z_THRESHOLD = 2.5;
6+
var FLAG_FIELD = 'u_outlier'; // create this boolean field or change action to add work_notes - potential to change to tag as well if one exists
7+
8+
// Build look-back cutoff
9+
var cutoff = new GlideDateTime();
10+
cutoff.addDaysUTC(-DAYS);
11+
12+
// First pass: mean and std dev of resolution minutes
13+
// Compute duration per record as closed_at - opened_at in minutes
14+
var minutes = [];
15+
var gr = new GlideRecord(TABLE);
16+
gr.addQuery('closed_at', '>=', cutoff);
17+
gr.addQuery('state', '>=', 6); // resolved or closed
18+
gr.addNotNullQuery('opened_at');
19+
gr.addNotNullQuery('closed_at');
20+
gr.query();
21+
while (gr.next()) {
22+
var opened = String(gr.getValue('opened_at'));
23+
var closed = String(gr.getValue('closed_at'));
24+
var mins = gs.dateDiff(opened, closed, true) / 60;
25+
minutes.push({ id: gr.getUniqueValue(), mins: mins });
26+
}
27+
if (!minutes.length) {
28+
gs.info('No records in window. Exiting.');
29+
return;
30+
}
31+
32+
var sum = minutes.reduce(function(a, x) { return a + x.mins; }, 0);
33+
var mean = sum / minutes.length;
34+
35+
var variance = minutes.reduce(function(a, x) {
36+
var d = x.mins - mean; return a + d * d;
37+
}, 0) / minutes.length;
38+
var std = Math.sqrt(variance);
39+
40+
// Second pass: tag outliers
41+
var tagged = 0;
42+
minutes.forEach(function(row) {
43+
var z = std > 0 ? (row.mins - mean) / std : 0;
44+
if (z >= Z_THRESHOLD) {
45+
var r = new GlideRecord(TABLE);
46+
if (r.get(row.id)) {
47+
if (r.isValidField(FLAG_FIELD)) {
48+
r[FLAG_FIELD] = true;
49+
r.update();
50+
} else {
51+
r.work_notes = 'Marked outlier by automation. z=' + z.toFixed(2) + ', mean=' + Math.round(mean) + 'm, std=' + Math.round(std) + 'm';
52+
r.update();
53+
}
54+
tagged++;
55+
}
56+
}
57+
});
58+
59+
gs.info('Outlier tagging complete. Window=' + DAYS + 'd, N=' + minutes.length + ', mean=' + Math.round(mean) + 'm, std=' + Math.round(std) + 'm, tagged=' + tagged);
60+
})();

0 commit comments

Comments
 (0)