Skip to content

Commit f62c125

Browse files
committed
beta 03
1 parent 6fdeee4 commit f62c125

File tree

35 files changed

+699
-68233
lines changed

35 files changed

+699
-68233
lines changed

src/Server/Coderr.Server.App/Modules/MonthlyStats/ApplicationUsageStatisticsDto.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@ public class ApplicationUsageStatisticsDto
55
public int ApplicationId { get; set; }
66
public int ReportCount { get; set; }
77
public int IncidentCount { get; set; }
8+
public int ReOpenedCount { get; set; }
9+
public int ClosedCount { get; set; }
10+
public int IgnoredCount { get; set; }
811
}
912
}

src/Server/Coderr.Server.App/Modules/MonthlyStats/CollectStatsJob.cs

Lines changed: 122 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,103 @@ from incidents
7777
return results.ToArray();
7878
}
7979

80+
private async Task<AppResult[]> GetClosedCount(DateTime lastMonth)
81+
{
82+
var results = new List<AppResult>();
83+
using (var cmd = _unitOfWork.CreateDbCommand())
84+
{
85+
cmd.CommandText = @"SELECT Incidents.ApplicationId, count(*)
86+
FROM IncidentHistory
87+
JOIN Incidents on (Incidents.Id = IncidentId)
88+
WHERE IncidentHistory.state = 3
89+
AND IncidentHistory.CreatedAtUtc >= @fromDate AND IncidentHistory.CreatedAtUtc < @toDate
90+
GROUP BY Incidents.ApplicationId";
91+
cmd.AddParameter("fromDate", lastMonth);
92+
cmd.AddParameter("toDate", lastMonth.AddMonths(1));
93+
using (var reader = await cmd.ExecuteReaderAsync())
94+
{
95+
while (await reader.ReadAsync())
96+
{
97+
var item = new AppResult
98+
{
99+
ApplicationId = reader.GetInt32(0),
100+
Count = reader.GetInt32(1)
101+
};
102+
results.Add(item);
103+
}
104+
}
105+
}
106+
107+
return results.ToArray();
108+
}
109+
110+
private async Task<AppResult[]> GetReOpened(DateTime lastMonth)
111+
{
112+
var results = new List<AppResult>();
113+
using (var cmd = _unitOfWork.CreateDbCommand())
114+
{
115+
cmd.CommandText = @"SELECT Incidents.ApplicationId, count(*)
116+
FROM IncidentHistory
117+
JOIN Incidents on (Incidents.Id = IncidentId)
118+
WHERE IncidentHistory.state = 4
119+
AND IncidentHistory.CreatedAtUtc >= @fromDate AND IncidentHistory.CreatedAtUtc < @toDate
120+
GROUP BY Incidents.ApplicationId";
121+
cmd.AddParameter("fromDate", lastMonth);
122+
cmd.AddParameter("toDate", lastMonth.AddMonths(1));
123+
using (var reader = await cmd.ExecuteReaderAsync())
124+
{
125+
while (await reader.ReadAsync())
126+
{
127+
var item = new AppResult
128+
{
129+
ApplicationId = reader.GetInt32(0),
130+
Count = reader.GetInt32(1)
131+
};
132+
results.Add(item);
133+
}
134+
}
135+
}
136+
137+
return results.ToArray();
138+
}
139+
140+
private async Task<AppResult[]> GetIgnoredCount(DateTime lastMonth)
141+
{
142+
var results = new List<AppResult>();
143+
using (var cmd = _unitOfWork.CreateDbCommand())
144+
{
145+
cmd.CommandText = @"SELECT Incidents.ApplicationId, count(*)
146+
FROM IncidentHistory
147+
JOIN Incidents on (Incidents.Id = IncidentId)
148+
WHERE IncidentHistory.state = 2
149+
AND IncidentHistory.CreatedAtUtc >= @fromDate AND IncidentHistory.CreatedAtUtc < @toDate
150+
GROUP BY Incidents.ApplicationId";
151+
cmd.AddParameter("fromDate", lastMonth);
152+
cmd.AddParameter("toDate", lastMonth.AddMonths(1));
153+
using (var reader = await cmd.ExecuteReaderAsync())
154+
{
155+
while (await reader.ReadAsync())
156+
{
157+
var item = new AppResult
158+
{
159+
ApplicationId = reader.GetInt32(0),
160+
Count = reader.GetInt32(1)
161+
};
162+
results.Add(item);
163+
}
164+
}
165+
}
166+
167+
return results.ToArray();
168+
}
169+
80170
private async Task<AppResult[]> GetReportCounts(DateTime lastMonth)
81171
{
82172
var results = new List<AppResult>();
83173
using (var cmd = _unitOfWork.CreateDbCommand())
84174
{
85-
cmd.CommandText = @"select ApplicationId, count(*)
86-
from ErrorReports
175+
cmd.CommandText = @"SELECT ApplicationId, count(*)
176+
FROM ErrorReports
87177
where CreatedAtUtc >= @fromDate AND CreatedAtUtc < @toDate
88178
group by ApplicationId";
89179
cmd.AddParameter("fromDate", lastMonth);
@@ -121,39 +211,20 @@ private async Task<bool> ReportMonth(DateTime lastMonth)
121211
{
122212
var apps = new Dictionary<int, ApplicationUsageStatisticsDto>();
123213

124-
var incidentCounts = await GetIncidentCounts(lastMonth);
125-
foreach (var count in incidentCounts)
126-
{
127-
if (count.Count == 0)
128-
continue;
129-
if (!apps.TryGetValue(count.ApplicationId, out var value))
130-
{
131-
value = new ApplicationUsageStatisticsDto
132-
{
133-
ApplicationId = count.ApplicationId
134-
};
135-
apps[value.ApplicationId] = value;
136-
}
214+
var values = await GetIncidentCounts(lastMonth);
215+
MergeStats(values, apps, (stat, value) => stat.IncidentCount = value);
137216

138-
value.IncidentCount = count.Count;
139-
}
217+
values = await GetReportCounts(lastMonth);
218+
MergeStats(values, apps, (stat, value) => stat.ReportCount = value);
140219

141-
var reportCounts = await GetReportCounts(lastMonth);
142-
foreach (var count in reportCounts)
143-
{
144-
if (count.Count == 0)
145-
continue;
146-
if (!apps.TryGetValue(count.ApplicationId, out var value))
147-
{
148-
value = new ApplicationUsageStatisticsDto
149-
{
150-
ApplicationId = count.ApplicationId
151-
};
152-
apps[value.ApplicationId] = value;
153-
}
220+
values = await GetClosedCount(lastMonth);
221+
MergeStats(values, apps, (stat, value) => stat.ClosedCount = value);
154222

155-
value.ReportCount = count.Count;
156-
}
223+
values = await GetReOpened(lastMonth);
224+
MergeStats(values, apps, (stat, value) => stat.ReOpenedCount = value);
225+
226+
values = await GetIgnoredCount(lastMonth);
227+
MergeStats(values, apps, (stat, value) => stat.IgnoredCount = value);
157228

158229
if (_config.Value.LatestUploadedMonth == null || _config.Value.LatestUploadedMonth < lastMonth)
159230
{
@@ -177,5 +248,24 @@ private async Task<bool> ReportMonth(DateTime lastMonth)
177248
await client.PostAsync("https://coderr.io/stats/usage", content);
178249
return true;
179250
}
251+
252+
private static void MergeStats(IEnumerable<AppResult> appResults, IDictionary<int, ApplicationUsageStatisticsDto> apps, Action<ApplicationUsageStatisticsDto, int> assignMethod)
253+
{
254+
foreach (var count in appResults)
255+
{
256+
if (count.Count == 0)
257+
continue;
258+
if (!apps.TryGetValue(count.ApplicationId, out var value))
259+
{
260+
value = new ApplicationUsageStatisticsDto
261+
{
262+
ApplicationId = count.ApplicationId
263+
};
264+
apps[value.ApplicationId] = value;
265+
}
266+
267+
assignMethod(value, count.Count);
268+
}
269+
}
180270
}
181271
}

src/Server/Coderr.Server.Infrastructure/Configuration/CoderrConfigSection.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public CoderrConfigSection()
3434
/// </remarks>
3535
public string InstallationId { get; set; }
3636

37+
/// <summary>
38+
/// Estimate of how many unique errors the admin think it has.
39+
/// </summary>
40+
public int NumberOfUniqueErrors { get; set; }
41+
42+
3743
string IConfigurationSection.SectionName
3844
{
3945
get { return "ErrorTracking"; }

src/Server/Coderr.Server.Web/Areas/Installation/Views/Setup/Stats.cshtml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
ApplicationdId: int; // Id of a specific application
1919
IncidentCount: int; // Number of new incidents created the given month
2020
ReportCount: int; // Number of error reports received the given month
21+
ClosedCount: int; // Number of errors that was corrected/solved this month
22+
ReOpened: int; // Number of errors that was repopened this month
2123
}
2224
}</code></pre>
2325
@Html.Raw(ViewBag.PrevLink)

src/Server/Coderr.Server.Web/ClientApp/boot.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const routes = [
107107
name: "discover",
108108
path: ":applicationId?",
109109
component: require("./components/discover/home/home.vue.html")
110-
},
110+
}
111111
]
112112
},
113113
{
@@ -158,7 +158,6 @@ const routes = [
158158
]
159159
},
160160
{
161-
name: "manageApp",
162161
path: "/manage/application/",
163162
component: require("./components/manage/application/app.vue.html"),
164163
children: [
@@ -200,7 +199,7 @@ const routes = [
200199
},
201200
{
202201
name: "createApp",
203-
path: "application/create",
202+
path: "create/application/",
204203
component: require("./components/manage/system/create/create.vue.html"),
205204
},
206205
{

src/Server/Coderr.Server.Web/ClientApp/components/analyze/analyze.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export default class AnalyzeComponent extends Vue {
1010
created() {
1111
}
1212

13+
1314
}
Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,21 @@
1-
import { PubSubService } from "../../../services/PubSub";
2-
import { ApiClient } from '../../../services/ApiClient';
3-
import { AppRoot } from '../../../services/AppRoot';
4-
import { FindIncidents, FindIncidentsResult, FindIncidentsResultItem } from '../../../dto/Core/Incidents'
5-
import * as Mine from '../../../dto/Common/Mine'
1+
import { MyIncidents, IMyIncident } from "../myincidents";
62
import Vue from "vue";
73
import { Component } from "vue-property-decorator";
84

95
@Component
106
export default class AnalyzeHomeComponent extends Vue {
11-
12-
applicationId: number|null = null;
13-
incidentId: number|null = null;
14-
noIncidents: boolean = false;
7+
showWelcome: boolean = false;
158

169
created() {
17-
if (this.$route.params.applicationId) {
18-
this.applicationId = parseInt(this.$route.params.applicationId, 10);
19-
}
20-
if (this.$route.params.incidentId) {
21-
this.incidentId = parseInt(this.$route.params.incidentId, 10);
22-
}
23-
24-
25-
AppRoot.Instance.incidentService.getMine()
26-
.then(result => {
27-
if (result.length > 0) {
28-
this.$router.push({ name: 'analyzeIncident', params: { incidentId: result[0].Id.toString() } });
29-
} else {
30-
this.noIncidents = true;
31-
}
10+
MyIncidents.Instance.subscribeOnSelectedIncident(x => {
11+
this.showWelcome = x == null;
12+
});
13+
MyIncidents.Instance.subscribeOnListChanges(x => {
14+
this.showWelcome = MyIncidents.Instance.myIncidents.length === 0;
15+
});
16+
MyIncidents.Instance.ready()
17+
.then(x => {
18+
this.showWelcome = MyIncidents.Instance.myIncidents.length === 0;
3219
});
3320
}
34-
35-
mounted() {
36-
}
37-
38-
3921
}

src/Server/Coderr.Server.Web/ClientApp/components/analyze/home/home.vue.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="mt-4" v-if="noIncidents">
2+
<div class="mt-4" v-if="showWelcome">
33
<div class="row">
44
<div class="col">
55
<h1>Analyze</h1>

src/Server/Coderr.Server.Web/ClientApp/components/analyze/incidents/incident.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PubSubService } from "../../../services/PubSub";
1+
import { MyIncidents, IMyIncident } from "../myincidents";
22
import { ApplicationMember } from "../../../services/applications/ApplicationService";
33
import { ApiClient } from '../../../services/ApiClient';
44
import { AppRoot } from '../../../services/AppRoot';
@@ -12,14 +12,13 @@ import { Component, Watch } from "vue-property-decorator";
1212
@Component
1313
export default class AnalyzeIncidentComponent extends Vue {
1414
private static activeBtnTheme: string = 'btn-dark';
15-
private apiClient: ApiClient = new ApiClient('http://localhost:50473/cqs/');
15+
private apiClient: ApiClient = AppRoot.Instance.apiClient;
1616
private static readonly selectCollectionTitle: string = '(select collection)';
1717
private team: ApplicationMember[] = [];
1818

1919
name = '';
2020
incidentId = 0;
2121
incident = new GetIncidentResult();
22-
isEmpty: boolean = false;
2322
reports: GetReportListResultItem[] = [];
2423
currentReport = new GetReportResult();
2524
currentCollection = new GetReportResultContextCollection();
@@ -28,27 +27,17 @@ export default class AnalyzeIncidentComponent extends Vue {
2827
currentCollectionName: string = '';
2928

3029
created() {
31-
if (!this.$route.params.incidentId) {
32-
AppRoot.Instance.incidentService.getMine().then(incidents => {
33-
if (incidents.length === 0) {
34-
this.isEmpty = false;
35-
return;
36-
} else {
37-
let incidentId = incidents[0].Id;
38-
this.loadIncident(incidentId);
39-
}
40-
});
41-
42-
} else {
43-
let incidentId = parseInt(this.$route.params.incidentId, 10);
44-
this.loadIncident(incidentId);
45-
}
46-
30+
MyIncidents.Instance.subscribeOnSelectedIncident(x => {
31+
if (x == null) {
32+
this.$router.push({ name: 'analyzeHome' });
33+
} else {
34+
this.loadIncident(x.incidentId);
35+
}
36+
});
4737
}
4838

49-
@Watch('$route.params.incidentId')
50-
onIncidentSelected(value: string, oldValue: string) {
51-
var incidentId = parseInt(value, 10);
39+
mounted() {
40+
var incidentId = parseInt(this.$route.params.incidentId, 10);
5241
this.loadIncident(incidentId);
5342
}
5443

@@ -69,10 +58,8 @@ export default class AnalyzeIncidentComponent extends Vue {
6958
closeIncident() {
7059
AppRoot.Instance.incidentService.showClose(this.incident.Id, "CloseBody")
7160
.then(x => {
72-
console.log('close result', x);
7361
AppRoot.Instance.incidentService.getMine(null, this.incident.Id)
7462
.then(incidents => {
75-
console.log('my incidents', incidents);
7663
if (incidents.length === 0) {
7764
this.$router.push({ name: "discover" });
7865
} else {

src/Server/Coderr.Server.Web/ClientApp/components/analyze/incidents/incident.vue.html

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
<template>
22
<div class="IncidentView">
3-
<div v-if="isEmpty">
4-
<div>
5-
<h1>Nothing to see here</h1>
6-
<p>Looks like no incidents have been uploaded yet.</p>
7-
</div>
8-
<div>
9-
<h1>What to expect</h1>
10-
<p>Once everything is configured, you can expect something like this:</p>
11-
<p>SCREENSHOT</p>
12-
<p><a class="btn btn-primary" href="#">Learn more here</a></p>
13-
</div>
14-
</div>
15-
<div v-else>
3+
<div>
164
<div class="row">
175
<div class="col">
186
<div class="float-right">

0 commit comments

Comments
 (0)