@inb/oeb_visualizations 0.0.8-beta → 0.1.1
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/README.md +82 -1
- package/dist/oeb-visualizations.cjs.js +84 -13
- package/dist/oeb-visualizations.esm.js +83 -12
- package/dist/oeb-visualizations.min.js +1 -1
- package/dist/oeb-visualizations.umd.js +84 -13
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -14,7 +14,88 @@ To install the package run:
|
|
|
14
14
|
```bash
|
|
15
15
|
npm install @inb/oeb_visualizations
|
|
16
16
|
```
|
|
17
|
+
## How to contribute
|
|
18
|
+
|
|
19
|
+
If you want to contribute to this project, please read the [contributing guidelines](/CONTRIBUTING.md) first.
|
|
17
20
|
|
|
18
21
|
## Development
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
This project is built using [Vue.js](https://vuejs.org/). The components are written in [Vue](https://vuejs.org/) and the bundling is done using [Rollup](https://rollupjs.org/). The rollup configuration is in the `rollup.config.js` file and the main entry point is the `src/index.js` file, all the components are exported from this file.
|
|
24
|
+
The components are written in the `src/components` folder. Each component should be in a separate folder and should contain the `.vue` file and, optionally, a `.scss` file. In order to be used in other applications, the package should be built, which creates a `dist` folder with the compiled files.
|
|
25
|
+
|
|
26
|
+
The documentation is built using [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/). The content of the documentation is written in markdown and should be added to the `docs` folder.
|
|
27
|
+
The configuration of the documentation is in the `mkdocs.yml` file. This file contains the structure of the documentation, including navigation, the theme used, etc. More information about how to configure the documentation can be found in the [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
|
|
28
|
+
|
|
29
|
+
### How to add a new visualization.
|
|
30
|
+
|
|
31
|
+
If you want to add a new visualization, you should create a new component for it. Follow the steps below:
|
|
32
|
+
|
|
33
|
+
- Create the new component in the `src/components` folder. A component can be written in only one file or, if it is complex, it can be divided into smaller components in a folder.
|
|
34
|
+
- Add the new component to the `index.js`, as following:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import newComponent from './components/newComponent.vue'
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
newComponent,
|
|
41
|
+
...
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> If the new component is a complex component, it should be divided into smaller components. In this case, the `index.js` file should export the smaller components and the main component.
|
|
46
|
+
|
|
47
|
+
> The documentation should be updated to include the new component.
|
|
48
|
+
|
|
49
|
+
### How to document a new component.
|
|
50
|
+
|
|
51
|
+
To document a new component, add the new component to the documentation as a single file in the `docs/components` folder. Documentation is written in markdown and should contain at least the following sections:
|
|
52
|
+
|
|
53
|
+
- Description of how the data being represented.
|
|
54
|
+
- How to use it.
|
|
55
|
+
- Example of usage.
|
|
56
|
+
- Props and events.
|
|
57
|
+
|
|
58
|
+
### How to build the package locally.
|
|
59
|
+
|
|
60
|
+
To build the package locally, run:
|
|
61
|
+
```bash
|
|
62
|
+
npm run build
|
|
63
|
+
```
|
|
64
|
+
This will create a `dist` folder with the compiled files.
|
|
65
|
+
The component will be available for other applications running locally to use.
|
|
66
|
+
To use the component in another application, you should link the package locally. To do so, run:
|
|
67
|
+
```bash
|
|
68
|
+
npm link
|
|
69
|
+
```
|
|
70
|
+
Then, in the application where you want to use the component, run:
|
|
71
|
+
```bash
|
|
72
|
+
npm link @inb/oeb_visualizations
|
|
73
|
+
```
|
|
74
|
+
This will link the local package to the application.
|
|
75
|
+
|
|
76
|
+
### How to serve the documentation locally.
|
|
77
|
+
|
|
78
|
+
The documentation is built using [mkdocs](https://www.mkdocs.org/). To serve the documentation locally, run:
|
|
79
|
+
```bash
|
|
80
|
+
mkdocs serve
|
|
81
|
+
```
|
|
82
|
+
This will start a local server and the documentation will be available at `http://localhost:8000/`.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## Publishing
|
|
86
|
+
|
|
87
|
+
### Publishing a new version of the package to npm
|
|
88
|
+
|
|
89
|
+
To publish a new version of the package to [npm](https://www.npmjs.com), follow the steps below:
|
|
90
|
+
|
|
91
|
+
- Update the version in the `package.json` file. If all commit messages follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard, you can use the `cz bump --version-scheme semver` command to update the version and the `CHANGELOG.md` file.
|
|
92
|
+
- Run the `npm run build` command to build the package.
|
|
93
|
+
- Run the `npm publish` command to publish the package to npmjs.
|
|
94
|
+
|
|
95
|
+
This package is published under the `@inb` scope as `@inb/oeb_visualizations`.
|
|
96
|
+
The package is available at [https://www.npmjs.com/package/@inb/oeb_visualizations](https://www.npmjs.com/package/@inb/oeb_visualizations).
|
|
97
|
+
|
|
98
|
+
### Publishing the documentation
|
|
99
|
+
|
|
100
|
+
The documentation is built and deployed to the `gh-pages` branch using GitHub Actions. Each time a new commit is pushed to the `main` branch, the documentation is built and deployed to the `gh-pages` branch.
|
|
101
|
+
The documentation is available at [https://inab.github.io/oeb-visualizations/](https://inab.github.io/oeb-visualizations/).
|
|
@@ -13,6 +13,13 @@ var script$1 = {
|
|
|
13
13
|
}
|
|
14
14
|
}),
|
|
15
15
|
props: {
|
|
16
|
+
xrange: {
|
|
17
|
+
/*
|
|
18
|
+
xrange is the range of the x axis in ms.
|
|
19
|
+
*/
|
|
20
|
+
type: Array,
|
|
21
|
+
required: false
|
|
22
|
+
},
|
|
16
23
|
dtick: {
|
|
17
24
|
/*
|
|
18
25
|
dtick is the interval between ticks on the x axis in ms.
|
|
@@ -72,6 +79,14 @@ var script$1 = {
|
|
|
72
79
|
required: false,
|
|
73
80
|
default: 350
|
|
74
81
|
},
|
|
82
|
+
width: {
|
|
83
|
+
/*
|
|
84
|
+
width is the width of the plot in px.
|
|
85
|
+
*/
|
|
86
|
+
type: Number,
|
|
87
|
+
required: false,
|
|
88
|
+
default: 700
|
|
89
|
+
},
|
|
75
90
|
week: {
|
|
76
91
|
/*
|
|
77
92
|
whether the plot is used to show data of one week
|
|
@@ -123,9 +138,12 @@ var script$1 = {
|
|
|
123
138
|
if (value[i].code !== null && typeof value[i].code !== 'number') {
|
|
124
139
|
console.error(`[oeb-visualizations warn] code must be null or a number in dataItems prop item (at position ${i})`);
|
|
125
140
|
}
|
|
126
|
-
// Date must be a string containing a date
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
// Date must be a string containing a parseable date, or a number (timestamp)
|
|
142
|
+
const validDateString = typeof value[i].date === 'string' && !isNaN(Date.parse(value[i].date));
|
|
143
|
+
const validDateNumber = typeof value[i].date === 'number';
|
|
144
|
+
if (!validDateString && !validDateNumber) {
|
|
145
|
+
console.error(`[oeb-visualizations warn] date must be a string containing a date or a number in dataItems prop item (at position ${i})`);
|
|
146
|
+
console.error(`[oeb-visualizations warn] date type is ${typeof value[i].date} and the value is ${value[i].date}`);
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
// And date cannot be null
|
|
@@ -138,7 +156,13 @@ var script$1 = {
|
|
|
138
156
|
}
|
|
139
157
|
},
|
|
140
158
|
mounted() {
|
|
141
|
-
|
|
159
|
+
// Compute the tallest online bar from real measurements only.
|
|
160
|
+
// Math.max over an array containing null coerces null -> 0, so a site
|
|
161
|
+
// that was down the whole window would yield 0 and render the
|
|
162
|
+
// offline/NA bars with zero height (invisible). Ignore nulls/NaN and
|
|
163
|
+
// apply a floor so down-only sites still draw full-height bars.
|
|
164
|
+
const times = this.dataItems.map(item => item.access_time).filter(t => typeof t === 'number' && !isNaN(t));
|
|
165
|
+
this.max_access_time = times.length ? Math.max(...times) : 100;
|
|
142
166
|
var traces = this.buildOnlineTraces(this.dataItems); // generate line traces
|
|
143
167
|
|
|
144
168
|
traces = traces.concat(this.buildOfflineNATraces(this.dataItems)); // generate bar traces
|
|
@@ -149,6 +173,7 @@ var script$1 = {
|
|
|
149
173
|
showlegend: true,
|
|
150
174
|
autosize: true,
|
|
151
175
|
height: this.height,
|
|
176
|
+
width: this.width,
|
|
152
177
|
margin: {
|
|
153
178
|
l: 50,
|
|
154
179
|
r: 50,
|
|
@@ -176,7 +201,9 @@ var script$1 = {
|
|
|
176
201
|
tick0: this.xaxisTickZero(),
|
|
177
202
|
dtick: this.xaxisTickD(),
|
|
178
203
|
tickangle: this.xaxisTickAngle(),
|
|
179
|
-
tickformat: this.xaxisTickFormat()
|
|
204
|
+
tickformat: this.xaxisTickFormat(),
|
|
205
|
+
tickvals: this.sixMonths ? this.monthTickVales() : null,
|
|
206
|
+
range: this.xaxisRange()
|
|
180
207
|
},
|
|
181
208
|
yaxis: {
|
|
182
209
|
title: this.yaxisTitle,
|
|
@@ -253,7 +280,11 @@ var script$1 = {
|
|
|
253
280
|
var sum = 0;
|
|
254
281
|
var nonNullValues = 0;
|
|
255
282
|
for (let i = 0; i < data.length; i++) {
|
|
256
|
-
|
|
283
|
+
// Only genuine successful measurements belong on the online
|
|
284
|
+
// line. An error code with a recorded access_time must not
|
|
285
|
+
// sneak in (it would draw green and skew the average); treat it
|
|
286
|
+
// as a break, just like a null access_time.
|
|
287
|
+
if (data[i].access_time !== null && !this.isErrorCode(data[i].code)) {
|
|
257
288
|
// keep adding to subarray
|
|
258
289
|
subarrayTime.push(data[i].access_time);
|
|
259
290
|
subarrayDate.push(data[i].date);
|
|
@@ -380,15 +411,37 @@ var script$1 = {
|
|
|
380
411
|
traces.push(this.buildAvgAccessTimeTrace(subarrays.average_access_time));
|
|
381
412
|
return traces;
|
|
382
413
|
},
|
|
414
|
+
isErrorCode(code) {
|
|
415
|
+
/*
|
|
416
|
+
Returns true if the HTTP code denotes the server being offline.
|
|
417
|
+
Single source of truth shared by the online-line and offline-bar
|
|
418
|
+
logic so redness is decided consistently by the response code.
|
|
419
|
+
*/
|
|
420
|
+
const errorCodes = [400, 403, 404, 408, 500, 502, 503, 504];
|
|
421
|
+
return errorCodes.includes(code);
|
|
422
|
+
},
|
|
383
423
|
extractOfflineNADates(data) {
|
|
424
|
+
/*
|
|
425
|
+
This function extracts dates with access_time null.
|
|
426
|
+
Depending on the code, the server is offline or NA on those dates.
|
|
427
|
+
Arguments:
|
|
428
|
+
- data: array of objects with keys "access_time", "date" and "code". access_time and code can be null
|
|
429
|
+
|
|
430
|
+
Returns an object with keys:
|
|
431
|
+
- NA: array of dates with code null (no information available)
|
|
432
|
+
- down: array of dates with code in errorCodes (server offline)
|
|
433
|
+
Classification is driven by the HTTP code, not by access_time
|
|
434
|
+
null-ness: an error response that happened to record an access_time
|
|
435
|
+
is still "offline", and a missing code is "no information available".
|
|
436
|
+
*/
|
|
384
437
|
const resultNA = [];
|
|
385
438
|
const resultOffline = [];
|
|
386
439
|
for (let i = 0; i < data.length; i++) {
|
|
387
|
-
//
|
|
388
|
-
if (data[i].
|
|
440
|
+
// no code at all -> grey "No information available"
|
|
441
|
+
if (data[i].code === null) {
|
|
389
442
|
resultNA.push(data[i].date);
|
|
390
|
-
//
|
|
391
|
-
} else if (data[i].
|
|
443
|
+
// an error code -> red "Offline"
|
|
444
|
+
} else if (this.isErrorCode(data[i].code)) {
|
|
392
445
|
resultOffline.push(data[i].date);
|
|
393
446
|
}
|
|
394
447
|
}
|
|
@@ -489,7 +542,7 @@ var script$1 = {
|
|
|
489
542
|
return "%A<br>%d %b";
|
|
490
543
|
}
|
|
491
544
|
if (this.sixMonths) {
|
|
492
|
-
return "%b";
|
|
545
|
+
return "%d %b";
|
|
493
546
|
} else {
|
|
494
547
|
return "%d %b";
|
|
495
548
|
}
|
|
@@ -504,7 +557,7 @@ var script$1 = {
|
|
|
504
557
|
return 0;
|
|
505
558
|
}
|
|
506
559
|
if (this.sixMonths) {
|
|
507
|
-
return
|
|
560
|
+
return 45;
|
|
508
561
|
} else {
|
|
509
562
|
return 45;
|
|
510
563
|
}
|
|
@@ -534,6 +587,24 @@ var script$1 = {
|
|
|
534
587
|
} else {
|
|
535
588
|
return "instant";
|
|
536
589
|
}
|
|
590
|
+
},
|
|
591
|
+
xaxisRange() {
|
|
592
|
+
if (!this.xrange) {
|
|
593
|
+
return [this.dataItems[0], this.dataItems[this.dataItems.length - 1]];
|
|
594
|
+
} else {
|
|
595
|
+
return this.xrange;
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
monthTickVales() {
|
|
599
|
+
// First day of month of last date and previous five months
|
|
600
|
+
const lastDate = new Date(this.dataItems[this.dataItems.length - 1].date);
|
|
601
|
+
const lastMonth = lastDate.getMonth();
|
|
602
|
+
const lastYear = lastDate.getFullYear();
|
|
603
|
+
const tickValues = [];
|
|
604
|
+
for (let i = 0; i < 6; i++) {
|
|
605
|
+
tickValues.push(new Date(lastYear, lastMonth - i, 1));
|
|
606
|
+
}
|
|
607
|
+
return tickValues;
|
|
537
608
|
}
|
|
538
609
|
}
|
|
539
610
|
};function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
|
|
@@ -640,7 +711,7 @@ const __vue_inject_styles__$1 = undefined;
|
|
|
640
711
|
/* scoped */
|
|
641
712
|
const __vue_scope_id__$1 = undefined;
|
|
642
713
|
/* module identifier */
|
|
643
|
-
const __vue_module_identifier__$1 = "data-v-
|
|
714
|
+
const __vue_module_identifier__$1 = "data-v-6f184d3f";
|
|
644
715
|
/* functional template */
|
|
645
716
|
const __vue_is_functional_template__$1 = false;
|
|
646
717
|
/* style inject */
|
|
@@ -17,6 +17,13 @@ var script$1 = {
|
|
|
17
17
|
}
|
|
18
18
|
}),
|
|
19
19
|
props: {
|
|
20
|
+
xrange: {
|
|
21
|
+
/*
|
|
22
|
+
xrange is the range of the x axis in ms.
|
|
23
|
+
*/
|
|
24
|
+
type: Array,
|
|
25
|
+
required: false
|
|
26
|
+
},
|
|
20
27
|
dtick: {
|
|
21
28
|
/*
|
|
22
29
|
dtick is the interval between ticks on the x axis in ms.
|
|
@@ -76,6 +83,14 @@ var script$1 = {
|
|
|
76
83
|
required: false,
|
|
77
84
|
default: 350
|
|
78
85
|
},
|
|
86
|
+
width: {
|
|
87
|
+
/*
|
|
88
|
+
width is the width of the plot in px.
|
|
89
|
+
*/
|
|
90
|
+
type: Number,
|
|
91
|
+
required: false,
|
|
92
|
+
default: 700
|
|
93
|
+
},
|
|
79
94
|
week: {
|
|
80
95
|
/*
|
|
81
96
|
whether the plot is used to show data of one week
|
|
@@ -127,9 +142,12 @@ var script$1 = {
|
|
|
127
142
|
if (value[i].code !== null && typeof value[i].code !== 'number') {
|
|
128
143
|
console.error(`[oeb-visualizations warn] code must be null or a number in dataItems prop item (at position ${i})`);
|
|
129
144
|
}
|
|
130
|
-
// Date must be a string containing a date
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
// Date must be a string containing a parseable date, or a number (timestamp)
|
|
146
|
+
const validDateString = typeof value[i].date === 'string' && !isNaN(Date.parse(value[i].date));
|
|
147
|
+
const validDateNumber = typeof value[i].date === 'number';
|
|
148
|
+
if (!validDateString && !validDateNumber) {
|
|
149
|
+
console.error(`[oeb-visualizations warn] date must be a string containing a date or a number in dataItems prop item (at position ${i})`);
|
|
150
|
+
console.error(`[oeb-visualizations warn] date type is ${typeof value[i].date} and the value is ${value[i].date}`);
|
|
133
151
|
}
|
|
134
152
|
|
|
135
153
|
// And date cannot be null
|
|
@@ -142,7 +160,13 @@ var script$1 = {
|
|
|
142
160
|
}
|
|
143
161
|
},
|
|
144
162
|
mounted() {
|
|
145
|
-
|
|
163
|
+
// Compute the tallest online bar from real measurements only.
|
|
164
|
+
// Math.max over an array containing null coerces null -> 0, so a site
|
|
165
|
+
// that was down the whole window would yield 0 and render the
|
|
166
|
+
// offline/NA bars with zero height (invisible). Ignore nulls/NaN and
|
|
167
|
+
// apply a floor so down-only sites still draw full-height bars.
|
|
168
|
+
const times = this.dataItems.map(item => item.access_time).filter(t => typeof t === 'number' && !isNaN(t));
|
|
169
|
+
this.max_access_time = times.length ? Math.max(...times) : 100;
|
|
146
170
|
var traces = this.buildOnlineTraces(this.dataItems); // generate line traces
|
|
147
171
|
|
|
148
172
|
traces = traces.concat(this.buildOfflineNATraces(this.dataItems)); // generate bar traces
|
|
@@ -153,6 +177,7 @@ var script$1 = {
|
|
|
153
177
|
showlegend: true,
|
|
154
178
|
autosize: true,
|
|
155
179
|
height: this.height,
|
|
180
|
+
width: this.width,
|
|
156
181
|
margin: {
|
|
157
182
|
l: 50,
|
|
158
183
|
r: 50,
|
|
@@ -180,7 +205,9 @@ var script$1 = {
|
|
|
180
205
|
tick0: this.xaxisTickZero(),
|
|
181
206
|
dtick: this.xaxisTickD(),
|
|
182
207
|
tickangle: this.xaxisTickAngle(),
|
|
183
|
-
tickformat: this.xaxisTickFormat()
|
|
208
|
+
tickformat: this.xaxisTickFormat(),
|
|
209
|
+
tickvals: this.sixMonths ? this.monthTickVales() : null,
|
|
210
|
+
range: this.xaxisRange()
|
|
184
211
|
},
|
|
185
212
|
yaxis: {
|
|
186
213
|
title: this.yaxisTitle,
|
|
@@ -257,7 +284,11 @@ var script$1 = {
|
|
|
257
284
|
var sum = 0;
|
|
258
285
|
var nonNullValues = 0;
|
|
259
286
|
for (let i = 0; i < data.length; i++) {
|
|
260
|
-
|
|
287
|
+
// Only genuine successful measurements belong on the online
|
|
288
|
+
// line. An error code with a recorded access_time must not
|
|
289
|
+
// sneak in (it would draw green and skew the average); treat it
|
|
290
|
+
// as a break, just like a null access_time.
|
|
291
|
+
if (data[i].access_time !== null && !this.isErrorCode(data[i].code)) {
|
|
261
292
|
// keep adding to subarray
|
|
262
293
|
subarrayTime.push(data[i].access_time);
|
|
263
294
|
subarrayDate.push(data[i].date);
|
|
@@ -384,15 +415,37 @@ var script$1 = {
|
|
|
384
415
|
traces.push(this.buildAvgAccessTimeTrace(subarrays.average_access_time));
|
|
385
416
|
return traces;
|
|
386
417
|
},
|
|
418
|
+
isErrorCode(code) {
|
|
419
|
+
/*
|
|
420
|
+
Returns true if the HTTP code denotes the server being offline.
|
|
421
|
+
Single source of truth shared by the online-line and offline-bar
|
|
422
|
+
logic so redness is decided consistently by the response code.
|
|
423
|
+
*/
|
|
424
|
+
const errorCodes = [400, 403, 404, 408, 500, 502, 503, 504];
|
|
425
|
+
return errorCodes.includes(code);
|
|
426
|
+
},
|
|
387
427
|
extractOfflineNADates(data) {
|
|
428
|
+
/*
|
|
429
|
+
This function extracts dates with access_time null.
|
|
430
|
+
Depending on the code, the server is offline or NA on those dates.
|
|
431
|
+
Arguments:
|
|
432
|
+
- data: array of objects with keys "access_time", "date" and "code". access_time and code can be null
|
|
433
|
+
|
|
434
|
+
Returns an object with keys:
|
|
435
|
+
- NA: array of dates with code null (no information available)
|
|
436
|
+
- down: array of dates with code in errorCodes (server offline)
|
|
437
|
+
Classification is driven by the HTTP code, not by access_time
|
|
438
|
+
null-ness: an error response that happened to record an access_time
|
|
439
|
+
is still "offline", and a missing code is "no information available".
|
|
440
|
+
*/
|
|
388
441
|
const resultNA = [];
|
|
389
442
|
const resultOffline = [];
|
|
390
443
|
for (let i = 0; i < data.length; i++) {
|
|
391
|
-
//
|
|
392
|
-
if (data[i].
|
|
444
|
+
// no code at all -> grey "No information available"
|
|
445
|
+
if (data[i].code === null) {
|
|
393
446
|
resultNA.push(data[i].date);
|
|
394
|
-
//
|
|
395
|
-
} else if (data[i].
|
|
447
|
+
// an error code -> red "Offline"
|
|
448
|
+
} else if (this.isErrorCode(data[i].code)) {
|
|
396
449
|
resultOffline.push(data[i].date);
|
|
397
450
|
}
|
|
398
451
|
}
|
|
@@ -493,7 +546,7 @@ var script$1 = {
|
|
|
493
546
|
return "%A<br>%d %b";
|
|
494
547
|
}
|
|
495
548
|
if (this.sixMonths) {
|
|
496
|
-
return "%b";
|
|
549
|
+
return "%d %b";
|
|
497
550
|
} else {
|
|
498
551
|
return "%d %b";
|
|
499
552
|
}
|
|
@@ -508,7 +561,7 @@ var script$1 = {
|
|
|
508
561
|
return 0;
|
|
509
562
|
}
|
|
510
563
|
if (this.sixMonths) {
|
|
511
|
-
return
|
|
564
|
+
return 45;
|
|
512
565
|
} else {
|
|
513
566
|
return 45;
|
|
514
567
|
}
|
|
@@ -538,6 +591,24 @@ var script$1 = {
|
|
|
538
591
|
} else {
|
|
539
592
|
return "instant";
|
|
540
593
|
}
|
|
594
|
+
},
|
|
595
|
+
xaxisRange() {
|
|
596
|
+
if (!this.xrange) {
|
|
597
|
+
return [this.dataItems[0], this.dataItems[this.dataItems.length - 1]];
|
|
598
|
+
} else {
|
|
599
|
+
return this.xrange;
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
monthTickVales() {
|
|
603
|
+
// First day of month of last date and previous five months
|
|
604
|
+
const lastDate = new Date(this.dataItems[this.dataItems.length - 1].date);
|
|
605
|
+
const lastMonth = lastDate.getMonth();
|
|
606
|
+
const lastYear = lastDate.getFullYear();
|
|
607
|
+
const tickValues = [];
|
|
608
|
+
for (let i = 0; i < 6; i++) {
|
|
609
|
+
tickValues.push(new Date(lastYear, lastMonth - i, 1));
|
|
610
|
+
}
|
|
611
|
+
return tickValues;
|
|
541
612
|
}
|
|
542
613
|
}
|
|
543
614
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var oeb_visualizations=function(e,t){"use strict";function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var s=i(t);function a(e){return Math.random().toString(36).replace("0.",e||"")}var r=function(e,t,i,s,a,r,o,n,l,c){"boolean"!=typeof o&&(l=n,n=o,o=!1);var d,h="function"==typeof i?i.options:i;if(e&&e.render&&(h.render=e.render,h.staticRenderFns=e.staticRenderFns,h._compiled=!0,a&&(h.functional=!0)),s&&(h._scopeId=s),r?(d=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,l(e)),e&&e._registeredComponents&&e._registeredComponents.add(r)},h._ssrRegister=d):t&&(d=o?function(){t.call(this,c(this.$root.$options.shadowRoot))}:function(e){t.call(this,n(e))}),d)if(h.functional){var u=h.render;h.render=function(e,t){return d.call(t),u(e,t)}}else{var m=h.beforeCreate;h.beforeCreate=m?[].concat(m,d):[d]}return i};var o=r({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("div",{attrs:{id:e.divId}})},staticRenderFns:[]},undefined,{name:"accessibilityPlot",data:()=>({divId:a("acc_plot_"),max_access_time:0,config:{displaylogo:!1,responsive:!0,modeBarButtonsToRemove:["lasso"]}}),props:{dtick:{type:Number,required:!1,default:864e5},xaxisTitle:{type:String,required:!1,default:"Date"},yaxisTitle:{type:String,required:!1,default:"Access time (ms)"},colorOnline:{type:String,required:!1,default:"111, 176, 129"},colorOffline:{type:String,required:!1,default:"255, 153, 145"},colorNA:{type:String,required:!1,default:"204,204,204"},height:{type:Number,required:!1,default:350},week:{type:Boolean,required:!1,default:!1},sixMonths:{type:Boolean,required:!1,default:!1},dataItems:{type:Array,required:!0,validator:function(e){for(let t=0;t<e.length;t++)e[t].hasOwnProperty("access_time")?e[t].hasOwnProperty("date")?e[t].hasOwnProperty("code")||console.error(`[oeb-visualizations warn] code key is missing in dataItems prop item (at position ${t})`):console.error(`[oeb-visualizations warn] date key is missing in dataItems prop item (at position ${t})`):console.error(`[oeb-visualizations warn] access_time key is missing in dataItems prop item (at position ${t})`),null!==e[t].access_time&&"number"!=typeof e[t].access_time&&console.error(`[oeb-visualizations warn] access_time must be null or a number in dataItems prop item (at position ${t})`),null!==e[t].code&&"number"!=typeof e[t].code&&console.error(`[oeb-visualizations warn] code must be null or a number in dataItems prop item (at position ${t})`),("string"!=typeof e[t].date||isNaN(Date.parse(e[t].date)))&&console.error(`[oeb-visualizations warn] date must be a string containing a date in dataItems prop item (at position ${t})`),null===e[t].date&&console.error(`[oeb-visualizations warn] date cannot be null in dataItems prop item (at position ${t})`);return!0}}},mounted(){this.max_access_time=Math.max(...this.dataItems.map((e=>e.access_time)));var e=this.buildOnlineTraces(this.dataItems);e=e.concat(this.buildOfflineNATraces(this.dataItems));const t={bargap:0,barmode:"stack",showlegend:!0,autosize:!0,height:this.height,margin:{l:50,r:50,b:50,t:20,pad:4},xaxis:{type:"date",ticklabelmode:this.xaxisMode(),title:this.xaxisTitle,font:{size:10},tickfont:{size:10},showgrid:!!this.sixMonths,griddash:"dot",gridwidth:1,gridcolor:"#d9d7d7",showspikes:!0,spikedash:"4px",spikethickness:1,tick0:this.xaxisTickZero(),dtick:this.xaxisTickD(),tickangle:this.xaxisTickAngle(),tickformat:this.xaxisTickFormat()},yaxis:{title:this.yaxisTitle,titlefont:{size:10},tickfont:{size:10}},template:"plotly_white",legend:{orientation:"h",yanchor:"bottom",y:-.5,xanchor:"left",x:.05,font:{size:8}},hoverlabel:{bgcolor:"#FFF"},hovermode:"closest"};console.log(this.divId),s.default.newPlot(this.divId,e,t,this.config)},methods:{generateColor(e,t){let i=[];for(let s=0;s<30;s++)switch(e[s]){case"up":i.push(`rgba(${this.colorOnline},0)`);break;case"down":i.push(`rgba(${this.colorOffline},${t})`);break;case"NA":i.push(`rgba(${this.colorNA},${t})`)}return i},extractSubarraysBetweenNullValues(e){var t={access_time:[],date:[],average_access_time:0},i=[],s=[],a=0,r=0;for(let o=0;o<e.length;o++)null!==e[o].access_time?(i.push(e[o].access_time),s.push(e[o].date),a+=e[o].access_time,r+=1):i.length>0&&(t.access_time.push(i),t.date.push(s),i=[],s=[]);return t.access_time.push(i),t.date.push(s),t.average_access_time=a/r,t},buildAccessTimeTraces(e){var t=[];for(let i=0;i<e.access_time.length;i++){const s=e.access_time[i],a={x:e.date[i],y:s,name:"Online",legendgroup:"up",showlegend:0===i,mode:"markers+lines",type:"scatter",fill:"tozeroy",fillcolor:`rgba(${this.colorOnline},.2)`,connectgaps:!1,line:{color:`rgba(${this.colorOnline},.8)`,width:1.5},marker:{size:5},hovertemplate:"<b>Online</b><br>%{x|%d %b %Y}<br>%{y} ms <extra></extra>",hoveron:"points+fills"};t.push(a)}return t},buildAvgAccessTimeTrace(e){const t=new Date(this.dataItems[0].date);this.week?t.setDate(t.getDate()-.3):t.setDate(t.getDate()-1);const i=new Date(this.dataItems[this.dataItems.length-1].date);this.week?i.setDate(i.getDate()+.3):i.setDate(i.getDate()+1);return{x:[t,i],y:[e,e],name:"Average access time",showlegend:!0,mode:"lines",type:"scatter",line:{color:`rgba(${this.colorOnline},.6)`,width:1.5,dash:"4px"},marker:{size:5},hovertemplate:"<b>Average access time</b><br>%{y:.2f} ms <extra></extra>"}},buildOnlineTraces(e){const t=this.extractSubarraysBetweenNullValues(e);var i=this.buildAccessTimeTraces(t);return i.push(this.buildAvgAccessTimeTrace(t.average_access_time)),i},extractOfflineNADates(e){const t=[],i=[];for(let s=0;s<e.length;s++)null===e[s].access_time&&null===e[s].code?t.push(e[s].date):null===e[s].access_time&&i.push(e[s].date);return{NA:t,down:i}},barTrace(e,t,i,s,a,r){const o={x:e.dates,y:e.access_times,marker:{color:e.colors},name:t,type:"bar",legendgroup:r,showlegend:i,hoverinfo:s,hovertemplate:a,width:864e5};return console.log(o),o},buildBarTraces(e,t,i,s){const a=`rgba(${t},.8)`,r=`rgba(${t},.2)`,o={dates:e,access_times:Array(e.length).fill(2),colors:Array(e.length).fill(a)},n={dates:e,access_times:Array(e.length).fill(1.1*this.max_access_time),colors:Array(e.length).fill(r)},l=`<b>${i}</b><br>%{x|%d %b %Y}<br>%{y} ms <extra></extra>`;return[this.barTrace(o,i,!1,"skip","",s),this.barTrace(n,i,!0,"all",l,s)]},buildOfflineNATraces(e){var t=[];const i=this.extractOfflineNADates(e);return i.down.length>0&&(t=t.concat(this.buildBarTraces(i.down,this.colorOffline,"Offline","down")),console.log(i.down)),i.NA.length>0&&(t=t.concat(this.buildBarTraces(i.NA,this.colorNA,"No information available","na")),console.log(i.NA)),t},xaxisTickFormat(){return this.week?"%A<br>%d %b":this.sixMonths?"%b":"%d %b"},xaxisTickAngle(){return this.week||this.sixMonths?0:45},xaxisTickD(){return!0===this.sixMonths?"M1":this.dtick},xaxisTickZero(){if(!0===this.sixMonths){console.log("zero six months ago");const e=new Date(this.dataItems[this.dataItems.length-1].date);return console.log(e),console.log("six months ago: "+new Date(e.setMonth(e.getMonth()-6))),new Date(e.setMonth(e.getMonth()-6))}return this.dataItems[0]},xaxisMode(){return!0===this.sixMonths?"period":"instant"}}},undefined,!1,undefined,void 0,void 0);var n=r({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("div",{attrs:{id:e.divId}})},staticRenderFns:[]},undefined,{name:"citationsPlot",data:()=>({divId:a("cit_plot_")}),props:{dataTraces:{type:Array,required:!0},stack:{type:Boolean,required:!1,default:!1},colors:{type:Array,required:!1,default:()=>["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd"]},height:{type:Number,required:!1,default:400},showlegend:{type:Boolean,required:!1,default:!0},title:{type:String,required:!1,default:""},xaxisTitle:{type:String,required:!1,default:"Year"},yaxisTitle:{type:String,required:!1,default:"Number of citations"},darkMode:{type:Boolean,required:!1,default:!1}},mounted(){const e=this.buildTraces(),t={showlegend:this.showlegend,autosize:!0,height:this.height,margin:{l:50,r:50,b:70,t:50,pad:4},xaxis:{title:this.xaxisTitle,font:{size:10},tickfont:{size:10},tickmode:"linear",color:this.darkMode?"white":"black"},yaxis:{title:this.yaxisTitle,titlefont:{size:10},tickfont:{size:10},font:{size:10},color:this.darkMode?"white":"black"},legend:{orientation:"h",yanchor:"bottom",y:1.02,xanchor:"right",x:1,font:{size:8,color:this.darkMode?"white":"black"}},hoverlabel:{color:this.darkMode?"white":"black"},hovermode:this.stack?"x unified":"closest",hoverdistance:70,plot_bgcolor:this.darkMode?"rgb(38, 50, 56)":"white",paper_bgcolor:this.darkMode?"rgb(38, 50, 56)":"white"};s.default.newPlot(this.divId,e,t)},methods:{buildTraces(){const e=[];for(let t=0;t<this.dataTraces.length;t++){const i={x:this.dataTraces[t].data.map((e=>e.year)),y:this.dataTraces[t].data.map((e=>e.citations)),mode:"lines+markers",name:this.dataTraces[t].label,hovertemplate:this.hoverTemplate(),marker:{size:5},line:{color:this.colors[t],width:1.8},stackgroup:this.stack?"one":null};e.push(i)}return e},hoverTemplate(){return this.stack?"%{y} citations <extra></extra>":"%{y} citations in %{x} <extra></extra>"}}},undefined,!1,undefined,void 0,void 0),l=Object.freeze({__proto__:null,accessibilityPlot:o,citationsPlot:n});const c=function(e){c.installed||(c.installed=!0,Object.entries(l).forEach((([t,i])=>{e.component(t,i)})))},d={install:c};{let e=null;"undefined"!=typeof window?e=window.Vue:"undefined"!=typeof global&&(e=global.Vue),e&&e.use(d)}return e.accessibilityPlot=o,e.citationsPlot=n,e.default=d,Object.defineProperty(e,"__esModule",{value:!0}),e}({},Plotly);
|
|
1
|
+
var oeb_visualizations=function(e,t){"use strict";function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var s=i(t);function a(e){return Math.random().toString(36).replace("0.",e||"")}var r=function(e,t,i,s,a,r,o,n,l,d){"boolean"!=typeof o&&(l=n,n=o,o=!1);var c,h="function"==typeof i?i.options:i;if(e&&e.render&&(h.render=e.render,h.staticRenderFns=e.staticRenderFns,h._compiled=!0,a&&(h.functional=!0)),s&&(h._scopeId=s),r?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,l(e)),e&&e._registeredComponents&&e._registeredComponents.add(r)},h._ssrRegister=c):t&&(c=o?function(){t.call(this,d(this.$root.$options.shadowRoot))}:function(e){t.call(this,n(e))}),c)if(h.functional){var u=h.render;h.render=function(e,t){return c.call(t),u(e,t)}}else{var m=h.beforeCreate;h.beforeCreate=m?[].concat(m,c):[c]}return i};var o=r({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("div",{attrs:{id:e.divId}})},staticRenderFns:[]},undefined,{name:"accessibilityPlot",data:()=>({divId:a("acc_plot_"),max_access_time:0,config:{displaylogo:!1,responsive:!0,modeBarButtonsToRemove:["lasso"]}}),props:{xrange:{type:Array,required:!1},dtick:{type:Number,required:!1,default:864e5},xaxisTitle:{type:String,required:!1,default:"Date"},yaxisTitle:{type:String,required:!1,default:"Access time (ms)"},colorOnline:{type:String,required:!1,default:"111, 176, 129"},colorOffline:{type:String,required:!1,default:"255, 153, 145"},colorNA:{type:String,required:!1,default:"204,204,204"},height:{type:Number,required:!1,default:350},width:{type:Number,required:!1,default:700},week:{type:Boolean,required:!1,default:!1},sixMonths:{type:Boolean,required:!1,default:!1},dataItems:{type:Array,required:!0,validator:function(e){for(let t=0;t<e.length;t++){e[t].hasOwnProperty("access_time")?e[t].hasOwnProperty("date")?e[t].hasOwnProperty("code")||console.error(`[oeb-visualizations warn] code key is missing in dataItems prop item (at position ${t})`):console.error(`[oeb-visualizations warn] date key is missing in dataItems prop item (at position ${t})`):console.error(`[oeb-visualizations warn] access_time key is missing in dataItems prop item (at position ${t})`),null!==e[t].access_time&&"number"!=typeof e[t].access_time&&console.error(`[oeb-visualizations warn] access_time must be null or a number in dataItems prop item (at position ${t})`),null!==e[t].code&&"number"!=typeof e[t].code&&console.error(`[oeb-visualizations warn] code must be null or a number in dataItems prop item (at position ${t})`);const i="string"==typeof e[t].date&&!isNaN(Date.parse(e[t].date)),s="number"==typeof e[t].date;i||s||(console.error(`[oeb-visualizations warn] date must be a string containing a date or a number in dataItems prop item (at position ${t})`),console.error(`[oeb-visualizations warn] date type is ${typeof e[t].date} and the value is ${e[t].date}`)),null===e[t].date&&console.error(`[oeb-visualizations warn] date cannot be null in dataItems prop item (at position ${t})`)}return!0}}},mounted(){const e=this.dataItems.map((e=>e.access_time)).filter((e=>"number"==typeof e&&!isNaN(e)));this.max_access_time=e.length?Math.max(...e):100;var t=this.buildOnlineTraces(this.dataItems);t=t.concat(this.buildOfflineNATraces(this.dataItems));const i={bargap:0,barmode:"stack",showlegend:!0,autosize:!0,height:this.height,width:this.width,margin:{l:50,r:50,b:50,t:20,pad:4},xaxis:{type:"date",ticklabelmode:this.xaxisMode(),title:this.xaxisTitle,font:{size:10},tickfont:{size:10},showgrid:!!this.sixMonths,griddash:"dot",gridwidth:1,gridcolor:"#d9d7d7",showspikes:!0,spikedash:"4px",spikethickness:1,tick0:this.xaxisTickZero(),dtick:this.xaxisTickD(),tickangle:this.xaxisTickAngle(),tickformat:this.xaxisTickFormat(),tickvals:this.sixMonths?this.monthTickVales():null,range:this.xaxisRange()},yaxis:{title:this.yaxisTitle,titlefont:{size:10},tickfont:{size:10}},template:"plotly_white",legend:{orientation:"h",yanchor:"bottom",y:-.5,xanchor:"left",x:.05,font:{size:8}},hoverlabel:{bgcolor:"#FFF"},hovermode:"closest"};console.log(this.divId),s.default.newPlot(this.divId,t,i,this.config)},methods:{generateColor(e,t){let i=[];for(let s=0;s<30;s++)switch(e[s]){case"up":i.push(`rgba(${this.colorOnline},0)`);break;case"down":i.push(`rgba(${this.colorOffline},${t})`);break;case"NA":i.push(`rgba(${this.colorNA},${t})`)}return i},extractSubarraysBetweenNullValues(e){var t={access_time:[],date:[],average_access_time:0},i=[],s=[],a=0,r=0;for(let o=0;o<e.length;o++)null===e[o].access_time||this.isErrorCode(e[o].code)?i.length>0&&(t.access_time.push(i),t.date.push(s),i=[],s=[]):(i.push(e[o].access_time),s.push(e[o].date),a+=e[o].access_time,r+=1);return t.access_time.push(i),t.date.push(s),t.average_access_time=a/r,t},buildAccessTimeTraces(e){var t=[];for(let i=0;i<e.access_time.length;i++){const s=e.access_time[i],a={x:e.date[i],y:s,name:"Online",legendgroup:"up",showlegend:0===i,mode:"markers+lines",type:"scatter",fill:"tozeroy",fillcolor:`rgba(${this.colorOnline},.2)`,connectgaps:!1,line:{color:`rgba(${this.colorOnline},.8)`,width:1.5},marker:{size:5},hovertemplate:"<b>Online</b><br>%{x|%d %b %Y}<br>%{y} ms <extra></extra>",hoveron:"points+fills"};t.push(a)}return t},buildAvgAccessTimeTrace(e){const t=new Date(this.dataItems[0].date);this.week?t.setDate(t.getDate()-.3):t.setDate(t.getDate()-1);const i=new Date(this.dataItems[this.dataItems.length-1].date);this.week?i.setDate(i.getDate()+.3):i.setDate(i.getDate()+1);return{x:[t,i],y:[e,e],name:"Average access time",showlegend:!0,mode:"lines",type:"scatter",line:{color:`rgba(${this.colorOnline},.6)`,width:1.5,dash:"4px"},marker:{size:5},hovertemplate:"<b>Average access time</b><br>%{y:.2f} ms <extra></extra>"}},buildOnlineTraces(e){const t=this.extractSubarraysBetweenNullValues(e);var i=this.buildAccessTimeTraces(t);return i.push(this.buildAvgAccessTimeTrace(t.average_access_time)),i},isErrorCode:e=>[400,403,404,408,500,502,503,504].includes(e),extractOfflineNADates(e){const t=[],i=[];for(let s=0;s<e.length;s++)null===e[s].code?t.push(e[s].date):this.isErrorCode(e[s].code)&&i.push(e[s].date);return{NA:t,down:i}},barTrace(e,t,i,s,a,r){const o={x:e.dates,y:e.access_times,marker:{color:e.colors},name:t,type:"bar",legendgroup:r,showlegend:i,hoverinfo:s,hovertemplate:a,width:864e5};return console.log(o),o},buildBarTraces(e,t,i,s){const a=`rgba(${t},.8)`,r=`rgba(${t},.2)`,o={dates:e,access_times:Array(e.length).fill(2),colors:Array(e.length).fill(a)},n={dates:e,access_times:Array(e.length).fill(1.1*this.max_access_time),colors:Array(e.length).fill(r)},l=`<b>${i}</b><br>%{x|%d %b %Y}<br>%{y} ms <extra></extra>`;return[this.barTrace(o,i,!1,"skip","",s),this.barTrace(n,i,!0,"all",l,s)]},buildOfflineNATraces(e){var t=[];const i=this.extractOfflineNADates(e);return i.down.length>0&&(t=t.concat(this.buildBarTraces(i.down,this.colorOffline,"Offline","down")),console.log(i.down)),i.NA.length>0&&(t=t.concat(this.buildBarTraces(i.NA,this.colorNA,"No information available","na")),console.log(i.NA)),t},xaxisTickFormat(){return this.week?"%A<br>%d %b":(this.sixMonths,"%d %b")},xaxisTickAngle(){return this.week?0:(this.sixMonths,45)},xaxisTickD(){return!0===this.sixMonths?"M1":this.dtick},xaxisTickZero(){if(!0===this.sixMonths){console.log("zero six months ago");const e=new Date(this.dataItems[this.dataItems.length-1].date);return console.log(e),console.log("six months ago: "+new Date(e.setMonth(e.getMonth()-6))),new Date(e.setMonth(e.getMonth()-6))}return this.dataItems[0]},xaxisMode(){return!0===this.sixMonths?"period":"instant"},xaxisRange(){return this.xrange?this.xrange:[this.dataItems[0],this.dataItems[this.dataItems.length-1]]},monthTickVales(){const e=new Date(this.dataItems[this.dataItems.length-1].date),t=e.getMonth(),i=e.getFullYear(),s=[];for(let e=0;e<6;e++)s.push(new Date(i,t-e,1));return s}}},undefined,!1,undefined,void 0,void 0);var n=r({render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("div",{attrs:{id:e.divId}})},staticRenderFns:[]},undefined,{name:"citationsPlot",data:()=>({divId:a("cit_plot_")}),props:{dataTraces:{type:Array,required:!0},stack:{type:Boolean,required:!1,default:!1},colors:{type:Array,required:!1,default:()=>["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd"]},height:{type:Number,required:!1,default:400},showlegend:{type:Boolean,required:!1,default:!0},title:{type:String,required:!1,default:""},xaxisTitle:{type:String,required:!1,default:"Year"},yaxisTitle:{type:String,required:!1,default:"Number of citations"},darkMode:{type:Boolean,required:!1,default:!1}},mounted(){const e=this.buildTraces(),t={showlegend:this.showlegend,autosize:!0,height:this.height,margin:{l:50,r:50,b:70,t:50,pad:4},xaxis:{title:this.xaxisTitle,font:{size:10},tickfont:{size:10},tickmode:"linear",color:this.darkMode?"white":"black"},yaxis:{title:this.yaxisTitle,titlefont:{size:10},tickfont:{size:10},font:{size:10},color:this.darkMode?"white":"black"},legend:{orientation:"h",yanchor:"bottom",y:1.02,xanchor:"right",x:1,font:{size:8,color:this.darkMode?"white":"black"}},hoverlabel:{color:this.darkMode?"white":"black"},hovermode:this.stack?"x unified":"closest",hoverdistance:70,plot_bgcolor:this.darkMode?"rgb(38, 50, 56)":"white",paper_bgcolor:this.darkMode?"rgb(38, 50, 56)":"white"};s.default.newPlot(this.divId,e,t)},methods:{buildTraces(){const e=[];for(let t=0;t<this.dataTraces.length;t++){const i={x:this.dataTraces[t].data.map((e=>e.year)),y:this.dataTraces[t].data.map((e=>e.citations)),mode:"lines+markers",name:this.dataTraces[t].label,hovertemplate:this.hoverTemplate(),marker:{size:5},line:{color:this.colors[t],width:1.8},stackgroup:this.stack?"one":null};e.push(i)}return e},hoverTemplate(){return this.stack?"%{y} citations <extra></extra>":"%{y} citations in %{x} <extra></extra>"}}},undefined,!1,undefined,void 0,void 0),l=Object.freeze({__proto__:null,accessibilityPlot:o,citationsPlot:n});const d=function(e){d.installed||(d.installed=!0,Object.entries(l).forEach((([t,i])=>{e.component(t,i)})))},c={install:d};{let e=null;"undefined"!=typeof window?e=window.Vue:"undefined"!=typeof global&&(e=global.Vue),e&&e.use(c)}return e.accessibilityPlot=o,e.citationsPlot=n,e.default=c,Object.defineProperty(e,"__esModule",{value:!0}),e}({},Plotly);
|
|
@@ -13,6 +13,13 @@ var script$1 = {
|
|
|
13
13
|
}
|
|
14
14
|
}),
|
|
15
15
|
props: {
|
|
16
|
+
xrange: {
|
|
17
|
+
/*
|
|
18
|
+
xrange is the range of the x axis in ms.
|
|
19
|
+
*/
|
|
20
|
+
type: Array,
|
|
21
|
+
required: false
|
|
22
|
+
},
|
|
16
23
|
dtick: {
|
|
17
24
|
/*
|
|
18
25
|
dtick is the interval between ticks on the x axis in ms.
|
|
@@ -72,6 +79,14 @@ var script$1 = {
|
|
|
72
79
|
required: false,
|
|
73
80
|
default: 350
|
|
74
81
|
},
|
|
82
|
+
width: {
|
|
83
|
+
/*
|
|
84
|
+
width is the width of the plot in px.
|
|
85
|
+
*/
|
|
86
|
+
type: Number,
|
|
87
|
+
required: false,
|
|
88
|
+
default: 700
|
|
89
|
+
},
|
|
75
90
|
week: {
|
|
76
91
|
/*
|
|
77
92
|
whether the plot is used to show data of one week
|
|
@@ -123,9 +138,12 @@ var script$1 = {
|
|
|
123
138
|
if (value[i].code !== null && typeof value[i].code !== 'number') {
|
|
124
139
|
console.error(`[oeb-visualizations warn] code must be null or a number in dataItems prop item (at position ${i})`);
|
|
125
140
|
}
|
|
126
|
-
// Date must be a string containing a date
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
// Date must be a string containing a parseable date, or a number (timestamp)
|
|
142
|
+
const validDateString = typeof value[i].date === 'string' && !isNaN(Date.parse(value[i].date));
|
|
143
|
+
const validDateNumber = typeof value[i].date === 'number';
|
|
144
|
+
if (!validDateString && !validDateNumber) {
|
|
145
|
+
console.error(`[oeb-visualizations warn] date must be a string containing a date or a number in dataItems prop item (at position ${i})`);
|
|
146
|
+
console.error(`[oeb-visualizations warn] date type is ${typeof value[i].date} and the value is ${value[i].date}`);
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
// And date cannot be null
|
|
@@ -138,7 +156,13 @@ var script$1 = {
|
|
|
138
156
|
}
|
|
139
157
|
},
|
|
140
158
|
mounted() {
|
|
141
|
-
|
|
159
|
+
// Compute the tallest online bar from real measurements only.
|
|
160
|
+
// Math.max over an array containing null coerces null -> 0, so a site
|
|
161
|
+
// that was down the whole window would yield 0 and render the
|
|
162
|
+
// offline/NA bars with zero height (invisible). Ignore nulls/NaN and
|
|
163
|
+
// apply a floor so down-only sites still draw full-height bars.
|
|
164
|
+
const times = this.dataItems.map(item => item.access_time).filter(t => typeof t === 'number' && !isNaN(t));
|
|
165
|
+
this.max_access_time = times.length ? Math.max(...times) : 100;
|
|
142
166
|
var traces = this.buildOnlineTraces(this.dataItems); // generate line traces
|
|
143
167
|
|
|
144
168
|
traces = traces.concat(this.buildOfflineNATraces(this.dataItems)); // generate bar traces
|
|
@@ -149,6 +173,7 @@ var script$1 = {
|
|
|
149
173
|
showlegend: true,
|
|
150
174
|
autosize: true,
|
|
151
175
|
height: this.height,
|
|
176
|
+
width: this.width,
|
|
152
177
|
margin: {
|
|
153
178
|
l: 50,
|
|
154
179
|
r: 50,
|
|
@@ -176,7 +201,9 @@ var script$1 = {
|
|
|
176
201
|
tick0: this.xaxisTickZero(),
|
|
177
202
|
dtick: this.xaxisTickD(),
|
|
178
203
|
tickangle: this.xaxisTickAngle(),
|
|
179
|
-
tickformat: this.xaxisTickFormat()
|
|
204
|
+
tickformat: this.xaxisTickFormat(),
|
|
205
|
+
tickvals: this.sixMonths ? this.monthTickVales() : null,
|
|
206
|
+
range: this.xaxisRange()
|
|
180
207
|
},
|
|
181
208
|
yaxis: {
|
|
182
209
|
title: this.yaxisTitle,
|
|
@@ -253,7 +280,11 @@ var script$1 = {
|
|
|
253
280
|
var sum = 0;
|
|
254
281
|
var nonNullValues = 0;
|
|
255
282
|
for (let i = 0; i < data.length; i++) {
|
|
256
|
-
|
|
283
|
+
// Only genuine successful measurements belong on the online
|
|
284
|
+
// line. An error code with a recorded access_time must not
|
|
285
|
+
// sneak in (it would draw green and skew the average); treat it
|
|
286
|
+
// as a break, just like a null access_time.
|
|
287
|
+
if (data[i].access_time !== null && !this.isErrorCode(data[i].code)) {
|
|
257
288
|
// keep adding to subarray
|
|
258
289
|
subarrayTime.push(data[i].access_time);
|
|
259
290
|
subarrayDate.push(data[i].date);
|
|
@@ -380,15 +411,37 @@ var script$1 = {
|
|
|
380
411
|
traces.push(this.buildAvgAccessTimeTrace(subarrays.average_access_time));
|
|
381
412
|
return traces;
|
|
382
413
|
},
|
|
414
|
+
isErrorCode(code) {
|
|
415
|
+
/*
|
|
416
|
+
Returns true if the HTTP code denotes the server being offline.
|
|
417
|
+
Single source of truth shared by the online-line and offline-bar
|
|
418
|
+
logic so redness is decided consistently by the response code.
|
|
419
|
+
*/
|
|
420
|
+
const errorCodes = [400, 403, 404, 408, 500, 502, 503, 504];
|
|
421
|
+
return errorCodes.includes(code);
|
|
422
|
+
},
|
|
383
423
|
extractOfflineNADates(data) {
|
|
424
|
+
/*
|
|
425
|
+
This function extracts dates with access_time null.
|
|
426
|
+
Depending on the code, the server is offline or NA on those dates.
|
|
427
|
+
Arguments:
|
|
428
|
+
- data: array of objects with keys "access_time", "date" and "code". access_time and code can be null
|
|
429
|
+
|
|
430
|
+
Returns an object with keys:
|
|
431
|
+
- NA: array of dates with code null (no information available)
|
|
432
|
+
- down: array of dates with code in errorCodes (server offline)
|
|
433
|
+
Classification is driven by the HTTP code, not by access_time
|
|
434
|
+
null-ness: an error response that happened to record an access_time
|
|
435
|
+
is still "offline", and a missing code is "no information available".
|
|
436
|
+
*/
|
|
384
437
|
const resultNA = [];
|
|
385
438
|
const resultOffline = [];
|
|
386
439
|
for (let i = 0; i < data.length; i++) {
|
|
387
|
-
//
|
|
388
|
-
if (data[i].
|
|
440
|
+
// no code at all -> grey "No information available"
|
|
441
|
+
if (data[i].code === null) {
|
|
389
442
|
resultNA.push(data[i].date);
|
|
390
|
-
//
|
|
391
|
-
} else if (data[i].
|
|
443
|
+
// an error code -> red "Offline"
|
|
444
|
+
} else if (this.isErrorCode(data[i].code)) {
|
|
392
445
|
resultOffline.push(data[i].date);
|
|
393
446
|
}
|
|
394
447
|
}
|
|
@@ -489,7 +542,7 @@ var script$1 = {
|
|
|
489
542
|
return "%A<br>%d %b";
|
|
490
543
|
}
|
|
491
544
|
if (this.sixMonths) {
|
|
492
|
-
return "%b";
|
|
545
|
+
return "%d %b";
|
|
493
546
|
} else {
|
|
494
547
|
return "%d %b";
|
|
495
548
|
}
|
|
@@ -504,7 +557,7 @@ var script$1 = {
|
|
|
504
557
|
return 0;
|
|
505
558
|
}
|
|
506
559
|
if (this.sixMonths) {
|
|
507
|
-
return
|
|
560
|
+
return 45;
|
|
508
561
|
} else {
|
|
509
562
|
return 45;
|
|
510
563
|
}
|
|
@@ -534,6 +587,24 @@ var script$1 = {
|
|
|
534
587
|
} else {
|
|
535
588
|
return "instant";
|
|
536
589
|
}
|
|
590
|
+
},
|
|
591
|
+
xaxisRange() {
|
|
592
|
+
if (!this.xrange) {
|
|
593
|
+
return [this.dataItems[0], this.dataItems[this.dataItems.length - 1]];
|
|
594
|
+
} else {
|
|
595
|
+
return this.xrange;
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
monthTickVales() {
|
|
599
|
+
// First day of month of last date and previous five months
|
|
600
|
+
const lastDate = new Date(this.dataItems[this.dataItems.length - 1].date);
|
|
601
|
+
const lastMonth = lastDate.getMonth();
|
|
602
|
+
const lastYear = lastDate.getFullYear();
|
|
603
|
+
const tickValues = [];
|
|
604
|
+
for (let i = 0; i < 6; i++) {
|
|
605
|
+
tickValues.push(new Date(lastYear, lastMonth - i, 1));
|
|
606
|
+
}
|
|
607
|
+
return tickValues;
|
|
537
608
|
}
|
|
538
609
|
}
|
|
539
610
|
};function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
|
|
@@ -640,7 +711,7 @@ const __vue_inject_styles__$1 = undefined;
|
|
|
640
711
|
/* scoped */
|
|
641
712
|
const __vue_scope_id__$1 = undefined;
|
|
642
713
|
/* module identifier */
|
|
643
|
-
const __vue_module_identifier__$1 = "data-v-
|
|
714
|
+
const __vue_module_identifier__$1 = "data-v-6f184d3f";
|
|
644
715
|
/* functional template */
|
|
645
716
|
const __vue_is_functional_template__$1 = false;
|
|
646
717
|
/* style inject */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inb/oeb_visualizations",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Collection of Vue components for data visualization in OpenEBench.",
|
|
5
5
|
"author": "evamart",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"dist/*"
|
|
25
25
|
],
|
|
26
|
-
|
|
27
26
|
"vetur": {
|
|
28
27
|
"tags": "tags.json",
|
|
29
28
|
"attributes": "attributes.json"
|