@dra2020/dra-types 1.4.9 → 1.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/all.d.ts +3 -0
- package/dist/csv.d.ts +40 -0
- package/dist/dra-types.d.ts +0 -58
- package/dist/dra-types.js +289 -80
- package/dist/dra-types.js.map +1 -1
- package/dist/stats.d.ts +45 -0
- package/dist/vfeature.d.ts +20 -0
- package/lib/all.ts +3 -0
- package/lib/bucketmap.ts +3 -0
- package/lib/csv.ts +515 -0
- package/lib/dra-types.ts +0 -592
- package/lib/schemas.ts +3 -0
- package/lib/stats.ts +203 -0
- package/lib/vfeature.ts +105 -0
- package/package.json +4 -4
package/lib/stats.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// In constrast to logging, the stats manager is responsible for tracking live server status.
|
|
2
|
+
// The results are stored in a compact database record which can be easily queried for current
|
|
3
|
+
// status. Each server instance maintains its own field in the overall "values" record in the
|
|
4
|
+
// stats database. The reporting tool aggregates all instances to report overall server activity.
|
|
5
|
+
//
|
|
6
|
+
// There are basically three kinds of values:
|
|
7
|
+
// Sum - we want to sum the value across all server instances (e.g. number of connnected clients)
|
|
8
|
+
// Avg - we just take average across all server instances (e.g. memory heap size)
|
|
9
|
+
// Rate - we want to track the rate of some activity over some time period
|
|
10
|
+
//
|
|
11
|
+
|
|
12
|
+
export type ValType = number;
|
|
13
|
+
export const ValTypeSum = 0;
|
|
14
|
+
export const ValTypeAvg = 1;
|
|
15
|
+
export const ValTypeRate = 2;
|
|
16
|
+
|
|
17
|
+
const ExpiryAge = 1000 * 60 * 60 * 24; // throw away instance record after this time period
|
|
18
|
+
|
|
19
|
+
export function statExpiryTime(): string
|
|
20
|
+
{
|
|
21
|
+
let time = new Date();
|
|
22
|
+
time.setTime(time.getTime() + ExpiryAge);
|
|
23
|
+
return time.toJSON();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface StatRecord
|
|
27
|
+
{
|
|
28
|
+
id: string;
|
|
29
|
+
production: boolean;
|
|
30
|
+
expires: string;
|
|
31
|
+
time: string;
|
|
32
|
+
index: StatEntryIndex;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type StatEntry = StatValue | StatRate;
|
|
36
|
+
|
|
37
|
+
export type StatEntryIndex = { [name: string]: StatEntry };
|
|
38
|
+
|
|
39
|
+
export interface StatValue
|
|
40
|
+
{
|
|
41
|
+
valType?: ValType;
|
|
42
|
+
cur?: number;
|
|
43
|
+
min?: number;
|
|
44
|
+
max?: number;
|
|
45
|
+
tot?: number;
|
|
46
|
+
cnt?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface StatRate
|
|
50
|
+
{
|
|
51
|
+
valType?: ValType;
|
|
52
|
+
thisMin?: number;
|
|
53
|
+
lastMin?: number;
|
|
54
|
+
thisHour?: number;
|
|
55
|
+
lastHour?: number;
|
|
56
|
+
thisDay?: number;
|
|
57
|
+
lastDay?: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Aggregate running value. This applies both in memory (logging multiple times before
|
|
61
|
+
// saving to database) as well as aggregating into an instance structure in the DB.
|
|
62
|
+
|
|
63
|
+
export function statValueRecord(sv: StatValue, cur: number, valType: ValType): StatValue
|
|
64
|
+
{
|
|
65
|
+
if (sv === undefined) sv = {};
|
|
66
|
+
if (sv.min === undefined || cur < sv.min)
|
|
67
|
+
sv.min = cur;
|
|
68
|
+
if (sv.max === undefined || cur > sv.max)
|
|
69
|
+
sv.max = cur;
|
|
70
|
+
sv.cur = cur;
|
|
71
|
+
sv.valType = valType;
|
|
72
|
+
return sv;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function statRateIncr(sr: StatRate, incr: number = 1): StatRate
|
|
76
|
+
{
|
|
77
|
+
if (sr == null) sr = {};
|
|
78
|
+
sr.valType = ValTypeRate;
|
|
79
|
+
sr.thisMin = sr.thisMin === undefined ? incr : sr.thisMin+incr;
|
|
80
|
+
sr.thisHour = sr.thisHour === undefined ? incr : sr.thisHour+incr;
|
|
81
|
+
sr.thisDay = sr.thisDay === undefined ? incr : sr.thisDay+incr;
|
|
82
|
+
return sr;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const OneMinute = 1000 * 60;
|
|
86
|
+
export const OneHour = OneMinute * 60;
|
|
87
|
+
export const OneDay = OneHour * 24;
|
|
88
|
+
|
|
89
|
+
export function statRateRollover(sr: StatRate, period: number): void
|
|
90
|
+
{
|
|
91
|
+
if ((period % OneMinute) == 0)
|
|
92
|
+
{
|
|
93
|
+
sr.lastMin = sr.thisMin;
|
|
94
|
+
sr.thisMin = 0;
|
|
95
|
+
}
|
|
96
|
+
if ((period % OneHour) == 0)
|
|
97
|
+
{
|
|
98
|
+
sr.lastHour = sr.thisHour;
|
|
99
|
+
sr.thisHour = 0;
|
|
100
|
+
}
|
|
101
|
+
if ((period % OneDay) == 0)
|
|
102
|
+
{
|
|
103
|
+
sr.lastDay = sr.thisDay;
|
|
104
|
+
sr.thisDay = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function statEntryIndexMerge(accum: StatEntryIndex, si: StatEntryIndex): StatEntryIndex
|
|
109
|
+
{
|
|
110
|
+
if (accum == null) accum = {};
|
|
111
|
+
|
|
112
|
+
// We just discard and replace old rate information
|
|
113
|
+
Object.keys(accum).forEach((p: string) => { if (accum[p].valType == ValTypeRate) delete accum[p] });
|
|
114
|
+
|
|
115
|
+
// But merge in all the other values
|
|
116
|
+
Object.keys(si).forEach((p: string) => {
|
|
117
|
+
let se = si[p];
|
|
118
|
+
if (se.valType == ValTypeRate)
|
|
119
|
+
accum[p] = se;
|
|
120
|
+
else
|
|
121
|
+
{
|
|
122
|
+
let sv = se as StatValue;
|
|
123
|
+
accum[p] = statValueRecord(se as StatValue, sv.cur, sv.valType);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return accum;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function statEntryIndexRollover(si: StatEntryIndex, period: number): void
|
|
130
|
+
{
|
|
131
|
+
Object.keys(si).forEach((p: string) => {
|
|
132
|
+
let se = si[p];
|
|
133
|
+
if (se.valType == ValTypeRate)
|
|
134
|
+
statRateRollover(se as StatRate, period);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function statRecordMerge(accum: StatRecord, si: StatRecord): StatRecord
|
|
139
|
+
{
|
|
140
|
+
if (accum == null)
|
|
141
|
+
accum = { id: si.id, production: si.production, time: si.time, expires: si.expires, index: {} };
|
|
142
|
+
accum.expires = si.expires;
|
|
143
|
+
accum.time = si.time;
|
|
144
|
+
statEntryIndexMerge(accum.index, si.index);
|
|
145
|
+
return accum;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Accumulate a set of values together (e.g. from multiple running instances)
|
|
149
|
+
// for reporting current state.
|
|
150
|
+
//
|
|
151
|
+
|
|
152
|
+
export function statValueAccum(accum: StatValue, sv: StatValue): StatEntry
|
|
153
|
+
{
|
|
154
|
+
if (accum == null)
|
|
155
|
+
{
|
|
156
|
+
accum = {};
|
|
157
|
+
accum.valType = sv.valType;
|
|
158
|
+
}
|
|
159
|
+
if (accum.min === undefined || sv.min < accum.min)
|
|
160
|
+
accum.min = sv.min;
|
|
161
|
+
if (accum.max === undefined || sv.max > accum.max)
|
|
162
|
+
accum.max = sv.max;
|
|
163
|
+
if (accum.valType === undefined)
|
|
164
|
+
accum.valType = sv.valType;
|
|
165
|
+
if (accum.valType == ValTypeSum)
|
|
166
|
+
accum.cur = (accum.cur === undefined ? sv.cur : accum.cur+sv.cur);
|
|
167
|
+
else if (accum.valType == ValTypeAvg)
|
|
168
|
+
{
|
|
169
|
+
accum.tot = (accum.tot === undefined ? 0 : accum.tot) + sv.cur;
|
|
170
|
+
accum.cnt = (accum.cnt === undefined ? 0 : accum.cnt) + 1;
|
|
171
|
+
accum.cur = accum.tot / accum.cnt;
|
|
172
|
+
}
|
|
173
|
+
return accum;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function statRateAccum(accum: StatRate, sr: StatRate): StatRate
|
|
177
|
+
{
|
|
178
|
+
if (accum == null)
|
|
179
|
+
{
|
|
180
|
+
accum = {};
|
|
181
|
+
accum.valType = sr.valType;
|
|
182
|
+
}
|
|
183
|
+
accum.thisMin = accum.thisMin === undefined ? sr.thisMin : (accum.thisMin + (sr.thisMin === undefined ? 0 : sr.thisMin));
|
|
184
|
+
accum.lastMin = accum.lastMin === undefined ? sr.lastMin : (accum.lastMin + (sr.lastMin === undefined ? 0 : sr.lastMin));
|
|
185
|
+
accum.thisHour = accum.thisHour === undefined ? sr.thisHour : (accum.thisHour + (sr.thisHour === undefined ? 0 : sr.thisHour));
|
|
186
|
+
accum.lastHour = accum.lastHour === undefined ? sr.lastHour : (accum.lastHour + (sr.lastHour === undefined ? 0 : sr.lastHour));
|
|
187
|
+
accum.thisDay = accum.thisDay === undefined ? sr.thisDay : (accum.thisDay + (sr.thisDay === undefined ? 0 : sr.thisDay));
|
|
188
|
+
accum.lastDay = accum.lastDay === undefined ? sr.lastDay : (accum.lastDay + (sr.lastDay === undefined ? 0 : sr.lastDay));
|
|
189
|
+
return accum;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function statIndexAccum(accum: StatEntryIndex, si: StatEntryIndex): StatEntryIndex
|
|
193
|
+
{
|
|
194
|
+
if (accum == null) accum = {};
|
|
195
|
+
Object.keys(si).forEach((p: string) => {
|
|
196
|
+
let se = si[p];
|
|
197
|
+
if (se.valType == ValTypeRate)
|
|
198
|
+
accum[p] = statRateAccum(accum[p] as StatRate, se as StatRate);
|
|
199
|
+
else
|
|
200
|
+
accum[p] = statValueAccum(accum[p] as StatValue, se as StatValue);
|
|
201
|
+
});
|
|
202
|
+
return accum;
|
|
203
|
+
}
|
package/lib/vfeature.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Public libraries
|
|
2
|
+
import * as Hash from 'object-hash';
|
|
3
|
+
|
|
4
|
+
export interface SplitBlock
|
|
5
|
+
{
|
|
6
|
+
id?: string;
|
|
7
|
+
chunkKey?: string;
|
|
8
|
+
chunk?: string;
|
|
9
|
+
state: string;
|
|
10
|
+
datasource: string;
|
|
11
|
+
geoid: string;
|
|
12
|
+
blocks: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type DistrictToSplitBlock = { [districtID: string]: SplitBlock[] };
|
|
16
|
+
|
|
17
|
+
// Canonical hashing of splitblock data
|
|
18
|
+
function hash(o: any): string
|
|
19
|
+
{
|
|
20
|
+
return Hash(o,
|
|
21
|
+
{ respectType: false,
|
|
22
|
+
unorderedArrays: true,
|
|
23
|
+
unorderedObjects: true,
|
|
24
|
+
excludeKeys: (k: string) => (k === 'id' || k === 'chunk')
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function vgeoidToGeoid(vgeoid: string): string
|
|
29
|
+
{
|
|
30
|
+
let re = /vfeature_([^_]*)_.*/;
|
|
31
|
+
let a = re.exec(vgeoid);
|
|
32
|
+
if (a == null || a.length != 2)
|
|
33
|
+
return '';
|
|
34
|
+
else
|
|
35
|
+
return a[1];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function vgeoidToChunk(vgeoid: string): string
|
|
39
|
+
{
|
|
40
|
+
// vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
|
|
41
|
+
// the contents are chunked into a file of form "vfeature_chunk_[chunkid]"
|
|
42
|
+
// So extract the chunk ID and download that.
|
|
43
|
+
let re = /vfeature_([^_]*)_([^_*])_(.*)/;
|
|
44
|
+
let a = re.exec(vgeoid);
|
|
45
|
+
if (a && a.length == 4)
|
|
46
|
+
vgeoid = `vfeature_chunk_${a[2]}`;
|
|
47
|
+
else
|
|
48
|
+
vgeoid = null;
|
|
49
|
+
|
|
50
|
+
return vgeoid;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function vgeoidToHash(vgeoid: string): string
|
|
54
|
+
{
|
|
55
|
+
// vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
|
|
56
|
+
let re = /vfeature_([^_]*)_([^_*])_(.*)/;
|
|
57
|
+
let a = re.exec(vgeoid);
|
|
58
|
+
if (a && a.length == 4)
|
|
59
|
+
vgeoid = a[3];
|
|
60
|
+
else
|
|
61
|
+
vgeoid = null;
|
|
62
|
+
|
|
63
|
+
return vgeoid;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function isVfeature(geoid: string): boolean
|
|
67
|
+
{
|
|
68
|
+
return geoid.indexOf('vfeature') === 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function splitToCacheKey(s: SplitBlock): string
|
|
72
|
+
{
|
|
73
|
+
if (s.id === undefined)
|
|
74
|
+
s.id = hash(s);
|
|
75
|
+
if (s.chunk === undefined)
|
|
76
|
+
s.chunk = "0";
|
|
77
|
+
|
|
78
|
+
return `_${s.state}_${s.datasource}_vfeature_${s.geoid}_${s.chunk}_${s.id}.geojson`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function splitToChunkKey(s: SplitBlock): string
|
|
82
|
+
{
|
|
83
|
+
if (s.chunk === undefined)
|
|
84
|
+
s.chunk = "0";
|
|
85
|
+
|
|
86
|
+
return `_${s.state}_${s.datasource}_vfeature_chunk_${s.chunk}.geojson`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function splitToPrefix(s: SplitBlock): string
|
|
90
|
+
{
|
|
91
|
+
if (s.blocks === undefined)
|
|
92
|
+
{
|
|
93
|
+
let re = /_([^_]*)_(.*)_vfeature.*\.geojson$/;
|
|
94
|
+
let a = re.exec(s.id);
|
|
95
|
+
if (a && a.length == 3)
|
|
96
|
+
return `_${a[1]}_${a[2]}`;
|
|
97
|
+
return s.id;
|
|
98
|
+
}
|
|
99
|
+
return `_${s.state}_${s.datasource}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function cacheKeysToChunkHash(keys: string[]): string
|
|
103
|
+
{
|
|
104
|
+
return hash(keys);
|
|
105
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dra2020/dra-types",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.11",
|
|
4
4
|
"description": "Shared types used between client and server.",
|
|
5
5
|
"main": "dist/dra-types.js",
|
|
6
6
|
"types": "./dist/all.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"homepage": "https://github.com/dra2020/dra-types#readme",
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "12.7.2",
|
|
27
|
-
"@types/object-hash": "^1.3.
|
|
27
|
+
"@types/object-hash": "^1.3.1",
|
|
28
28
|
"source-map-loader": "^0.2.4",
|
|
29
29
|
"ts-loader": "^6.2.1",
|
|
30
30
|
"tsify": "^4.0.1",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"webpack-cli": "^3.3.11"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@dra2020/util": "^1.0.
|
|
37
|
-
"object-hash": "^2.0.
|
|
36
|
+
"@dra2020/util": "^1.0.50",
|
|
37
|
+
"object-hash": "^2.0.3"
|
|
38
38
|
}
|
|
39
39
|
}
|