From 6045547ea5dc6545f4ecbed649687e71846ef7e7 Mon Sep 17 00:00:00 2001 From: Jeremy Cunningham <34543464+jpcunningh@users.noreply.github.com> Date: Sat, 1 Feb 2020 01:18:41 -0600 Subject: [PATCH 1/8] fix brushing loop (#5499) --- lib/client/chart.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/chart.js b/lib/client/chart.js index 71d68bbc761..7a9b57bdd5d 100644 --- a/lib/client/chart.js +++ b/lib/client/chart.js @@ -602,8 +602,6 @@ function init (client, d3, $) { }; function scrollUpdate () { - scrolling = false; - var nowDate = scrollNow; var currentBrushExtent = scrollBrushExtent; @@ -669,6 +667,8 @@ function init (client, d3, $) { // console.log('Redrawing brush due to update: ', currentBrushExtent); chart.theBrush.call(chart.brush.move, currentBrushExtent.map(chart.xScale2)); + + scrolling = false; } chart.scroll = function scroll (nowDate) { From c86b8901a286815bda6532fd053bef0a7e273b39 Mon Sep 17 00:00:00 2001 From: Jonas Hummelstrand Date: Mon, 3 Feb 2020 08:06:25 +0100 Subject: [PATCH 2/8] Update README.md (#5480) Clarified that the "bridge" plugin is for Dexcom Share ("Dexcom" didn't appear at all in the read me), fixed a few typos, and added line 444 about the BRIDGE_SERVER variable. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8586e0b5a3b..62bd048f059 100644 --- a/README.md +++ b/README.md @@ -433,14 +433,15 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs/ or * `BASAL_RENDER` (`none`) - Possible values are `none`, `default`, or `icicle` (inverted) ##### `bridge` (Share2Nightscout bridge) - Glucose reading directly from the Share service, uses these extended settings: - * `BRIDGE_USER_NAME` - Your user name for the Share service. + Glucose reading directly from the Dexcom Share service, uses these extended settings: + * `BRIDGE_USER_NAME` - Your username for the Share service. * `BRIDGE_PASSWORD` - Your password for the Share service. - * `BRIDGE_INTERVAL` (`150000` *2.5 minutes*) - The time to wait between each update. + * `BRIDGE_INTERVAL` (`150000` *2.5 minutes*) - The time (in milliseconds) to wait between each update. * `BRIDGE_MAX_COUNT` (`1`) - The number of records to attempt to fetch per update. * `BRIDGE_FIRST_FETCH_COUNT` (`3`) - Changes max count during the very first update only. * `BRIDGE_MAX_FAILURES` (`3`) - How many failures before giving up. - * `BRIDGE_MINUTES` (`1400`) - The time window to search for new data per update (default is one day in minutes). + * `BRIDGE_MINUTES` (`1400`) - The time window to search for new data per update (the default value is one day in minutes). + * `BRIDGE_SERVER` (``) - The default blank value is used to fetch data from Dexcom servers in the US. Set to (`EU`) to fetch from European servers instead. ##### `mmconnect` (MiniMed Connect bridge) Transfer real-time MiniMed Connect data from the Medtronic CareLink server into Nightscout ([read more](https://github.com/mddub/minimed-connect-to-nightscout)) From c0f6b2245cfd18d715905876df693d100e8c37c5 Mon Sep 17 00:00:00 2001 From: peterleimbach Date: Mon, 3 Feb 2020 08:09:43 +0100 Subject: [PATCH 3/8] =?UTF-8?q?Added=20period=20of=20days=20into=20headlin?= =?UTF-8?q?e=20of=20glucose=20distribution=20and=20percen=E2=80=A6=20(#542?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added period of days into headline of glucose distribution and percentil chart report I make screencopies of the glucose distribution and percential chart report for my diabtes consultant and had to manually add the period of days to the report everytime because it was not shown in the report itself. I added the period of days this with a small number of lines of code and think this is helpful for other too. * removed comments as requested removed comments as requested * Camelcase for new variables reportPlugins, firstDay, lastDay, countDays * forget to save the change of reportPlugins in percentile.js --- lib/report_plugins/glucosedistribution.js | 12 ++++++--- lib/report_plugins/percentile.js | 31 ++++++++++++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/report_plugins/glucosedistribution.js b/lib/report_plugins/glucosedistribution.js index f97bd034765..21d5213e9c9 100644 --- a/lib/report_plugins/glucosedistribution.js +++ b/lib/report_plugins/glucosedistribution.js @@ -19,9 +19,9 @@ glucosedistribution.html = function html (client) { var ret = '

' + translate('Glucose distribution') + - ' (' + - ' ' + - ' )' + + ' (' + + '' + + ')' + '

' + '' + '' + @@ -122,7 +122,11 @@ glucosedistribution.report = function report_glucosedistribution (datastorage, s var data = datastorage.allstatsrecords; var days = datastorage.alldays; - $('#glucosedistribution-days').text(days + ' ' + translate('days total')); + var reportPlugins = Nightscout.report_plugins; + var firstDay = reportPlugins.utils.localeDate(sorteddaystoshow[sorteddaystoshow.length - 1]); + var lastDay = reportPlugins.utils.localeDate(sorteddaystoshow[0]); + + $('#glucosedistribution-days').text(days + ' ' + translate('days total') + ', ' + firstDay + ' - ' + lastDay); for (var i = 0; i < 24; i++) { $('#glucosedistribution-' + i).unbind('click').click(onClick); diff --git a/lib/report_plugins/percentile.js b/lib/report_plugins/percentile.js index 803860d5e47..9694fd51faf 100644 --- a/lib/report_plugins/percentile.js +++ b/lib/report_plugins/percentile.js @@ -15,11 +15,17 @@ module.exports = init; percentile.html = function html(client) { var translate = client.translate; var ret = - '

' + translate('Glucose Percentile report') + '

' - + '
' - + '
' - + '
' - ; + '

' + + translate('Glucose Percentile report') + + ' (' + + '' + + ')' + + '

' + + '
' + + '
' + + '
' + ; + return ret; }; @@ -36,16 +42,23 @@ percentile.report = function report_percentile(datastorage, sorteddaystoshow, op var translate = client.translate; var ss = require('simple-statistics'); - var minutewindow = 30; //minute-window should be a divisor of 60 - + var minutewindow = 30; //minute-window should be a divisor of 60 + var data = datastorage.allstatsrecords; - + var bins = []; var filterFunc = function withinWindow(record) { var recdate = new Date(record.displayTime); return recdate.getHours() === hour && recdate.getMinutes() >= minute && recdate.getMinutes() < minute + minutewindow; }; - + + var reportPlugins = Nightscout.report_plugins; + var firstDay = reportPlugins.utils.localeDate(sorteddaystoshow[sorteddaystoshow.length - 1]); + var lastDay = reportPlugins.utils.localeDate(sorteddaystoshow[0]); + var countDays = sorteddaystoshow.length; + + $('#percentile-days').text(countDays + ' ' + translate('days total') + ', ' + firstDay + ' - ' + lastDay); + for (var hour = 0; hour < 24; hour++) { for (var minute = 0; minute < 60; minute = minute + minutewindow) { var date = new Date(); From 82f00763d65dd9d6d1726b10831d54a5e96b7d87 Mon Sep 17 00:00:00 2001 From: Sulka Haro Date: Wed, 5 Feb 2020 08:16:26 +0200 Subject: [PATCH 4/8] Move app caching to a service worker (#5504) * Move app caching to a webworker * Code cleanup * Code cleanup * Make Codacy happy * More parentheses --- app.js | 16 +- views/adminindex.html | 10 +- views/clockviews/shared.html | 2 +- views/foodindex.html | 10 +- views/index.html | 1386 +++++++++++++++++----------------- views/nightscout.appcache | 37 - views/profileindex.html | 10 +- views/reportindex.html | 16 +- views/service-worker.js | 107 +++ 9 files changed, 844 insertions(+), 750 deletions(-) delete mode 100644 views/nightscout.appcache create mode 100644 views/service-worker.js diff --git a/app.js b/app.js index b718e3772e9..16ee906007c 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,7 @@ const bodyParser = require('body-parser'); const path = require('path'); const fs = require('fs'); +const ejs = require('ejs'); function create (env, ctx) { var app = express(); @@ -92,6 +93,15 @@ function create (env, ctx) { } app.locals.cachebuster = cacheBuster; + app.get("/sw.js", (req, res) => { + res.setHeader('Content-Type', 'application/javascript'); + res.send(ejs.render(fs.readFileSync( + require.resolve(`${__dirname}/views/service-worker.js`), + { encoding: 'utf-8' }), + { locals: app.locals} + )); + }); + if (ctx.bootErrors && ctx.bootErrors.length > 0) { app.get('*', require('./lib/server/booterror')(ctx)); return app; @@ -178,12 +188,6 @@ function create (env, ctx) { app.use("/clock", clockviews); - app.get("/appcache/*", (req, res) => { - res.render("nightscout.appcache", { - locals: app.locals - }); - }); - app.use('/api', bodyParser({ limit: 1048576 * 50 }), apiRoot); diff --git a/views/adminindex.html b/views/adminindex.html index c9bb631cfeb..9b4ff750d2e 100644 --- a/views/adminindex.html +++ b/views/adminindex.html @@ -26,10 +26,10 @@ - + - + <% include preloadCSS %> @@ -39,8 +39,8 @@ <%- include('partials/authentication-status') %> - - - + + + diff --git a/views/clockviews/shared.html b/views/clockviews/shared.html index cb27b137dfb..beac7dc0f2e 100644 --- a/views/clockviews/shared.html +++ b/views/clockviews/shared.html @@ -40,7 +40,7 @@
- + - - + + + diff --git a/views/index.html b/views/index.html index 153e187b7f7..156aa331325 100644 --- a/views/index.html +++ b/views/index.html @@ -1,696 +1,716 @@ - - manifest="appcache/nightscout-<%= locals.cachebuster %>.appcache" - <% } %>> - - - - - - - Nightscout - - - - - - - - - - - - - - - - - - - - - - - - - -<% include preloadCSS %> - - -
-
-

-

Loading the client

-
-
-
-
-
-
-
-
- <%- include('partials/toolbar') %> - -
- -
-
-
- - -
-
-
- -
-
-
---
-
-
-
-
-
- -
    -
  • Hours:
  • -
  • 2
  • -
  • 3
  • -
  • 4
  • -
  • 6
  • -
  • 12
  • -
  • 24
  • -
  • ...
  • -
-
- -
-
-
-
+ + <% include preloadCSS %> + + + +
+
+

+

Loading the client

+
+
+
+
+
+
+
+
+ <%- include('partials/toolbar') %> + +
+ +
+
+
+ - -
-
- -
- Settings -
-
Units
-
-
-
-
-
Date format
-
-
-
-
-
Language
-
- -
-
-
-
Scale
-
- -
-
-
-
Render Basal
-
- -
-
-
-
Enable Alarms
-
-
-
-
-
- - - mins -
-
- - - mins -
-
-
-
-
Night Mode
-
-
-
-
Edit Mode
-
-
-
-
Show Raw BG Data
-
-
-
-
-
-
Custom Title
-
-
-
-
Theme
-
-
-
-
-
-
Show Plugins
-
-
- - - - <%- include('partials/authentication-status') %> - -
- About -
-
version
-
head
-

-

License: AGPL
-
Copyright © 2017 Nightscout contributors
-

- -
- + +
+
+
+ +
+
+
---
+
+
- -
-
-
- Log a Treatment - - - -
- Targets - - -
- -
- Glucose Reading - - - - -
-
- - - -
- - - - - - - - - - -
- - - -
- Event Time - - - - -
- - -
- +
+
+ +
    +
  • Hours:
  • +
  • 2
  • +
  • 3
  • +
  • 4
  • +
  • 6
  • +
  • 12
  • +
  • 24
  • +
  • ...
  • +
+
+ +
+
+
+
+
+ +
+
+ +
+ Settings +
+
Units
+
+
+
+
+
Date format
+
+
+
+
+
Language
+
+ +
+
+
+
Scale
+
+ +
+
+
+
Render Basal
+
+ +
+
+
+
Enable Alarms
+
+
+
+
+
+ + + mins +
+
+ + + mins +
+
+
+
+
Night Mode
+
+
+
+
Edit Mode
+
+
+
+
Show Raw BG Data
+
+
+
+
+
+
Custom Title
+
+
+
+
Theme
+
+
+
+
+
+
Show Plugins
+
+
+ + + + <%- include('partials/authentication-status') %> + +
+ About +
+
version
+
head
+

+

License: AGPL
+
Copyright © 2017 Nightscout contributors
+

+ +
+ +
+ +
+
+
+ Log a Treatment + + + +
+ Targets + + +
+ +
+ Glucose Reading + + + + +
+
+ + + +
+ + + + + + + + + + +
+ + + +
+ Event Time + + + +
-
- -
- - Bolus Wizard - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - BG: - - - - -
- - - - - - - - - - 0.00 -
- Quickpick: -
- -
- - or - - Add from database - - -
-
-
- - - Carbs: - - g - - - -
- - - COB: - - g - - - -
- - - IOB: - - 0.00 -
- - Other correction: - - -
- - Rounding: - - 0.00 -
- - Calculation is in target range. -
- - - Insulin needed: - - 0.00 -
- - Carbs needed: - - - -
- - Basal rate: - - -
-
- -
- + + + + + +
+
+
+ + Bolus Wizard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + BG: + + + + +
+ + + + + + + + + + 0.00 +
+ Quickpick:
- - - - +
- - - - -
- Event Time: - - - - - - + + or + + Add from database + + +
- - - - - - -
- - -
- - +
+ + + Carbs: + + g + + + +
+ + + COB: + + g + + + +
+ + + IOB: + + 0.00 +
+ + Other correction: + + +
+ + Rounding: + + 0.00 +
+ + Calculation is in target range. +
+ + + Insulin needed: + + 0.00 +
+ + Carbs needed: + + + +
+ + Basal rate: + + +
+
+ +
+ +
+ + + + +
+ + + + +
+ Event Time: + + + + + +
+ +
+ + + + +
+ +
+ + +
+ + + + + + + + - - - - diff --git a/views/nightscout.appcache b/views/nightscout.appcache deleted file mode 100644 index 3823f894e5f..00000000000 --- a/views/nightscout.appcache +++ /dev/null @@ -1,37 +0,0 @@ -CACHE MANIFEST - -/images/launch.png -/images/apple-touch-icon-57x57.png -/images/apple-touch-icon-60x60.png -/images/apple-touch-icon-72x72.png -/images/apple-touch-icon-76x76.png -/images/apple-touch-icon-114x114.png -/images/apple-touch-icon-120x120.png -/images/apple-touch-icon-144x144.png -/images/apple-touch-icon-152x152.png -/images/apple-touch-icon-180x180.png -/images/favicon-32x32.png -/images/android-chrome-192x192.png -/images/favicon-96x96.png -/images/favicon-16x16.png -/manifest.json -/images/favicon.ico -/images/mstile-144x144.png -/css/ui-darkness/jquery-ui.min.css?v=<%= locals.cachebuster %> -/css/jquery.tooltips.css?v=<%= locals.cachebuster %> -/audio/alarm.mp3 -/audio/alarm2.mp3 -/css/ui-darkness/images/ui-icons_ffffff_256x240.png -/css/ui-darkness/images/ui-icons_cccccc_256x240.png -/css/ui-darkness/images/ui-bg_inset-soft_25_000000_1x100.png -/css/ui-darkness/images/ui-bg_gloss-wave_25_333333_500x100.png -/css/main.css?v=<%= locals.cachebuster %> -/bundle/js/bundle.app.js?v=<%= locals.cachebuster %> -/bundle/js/bundle.clock.js?v=<%= locals.cachebuster %> -/bundle/js/bundle.report.js?v=<%= locals.cachebuster %> -/socket.io/socket.io.js?v=<%= locals.cachebuster %> -/js/client.js?v=<%= locals.cachebuster %> -/images/logo2.png - -NETWORK: -* diff --git a/views/profileindex.html b/views/profileindex.html index 55a98f19dc4..d915d0c8cc8 100644 --- a/views/profileindex.html +++ b/views/profileindex.html @@ -25,10 +25,10 @@ - + - + <% include preloadCSS %> @@ -166,8 +166,8 @@ <%- include('partials/authentication-status') %> - - - + + + diff --git a/views/reportindex.html b/views/reportindex.html index 546e89daf75..8d02bf08f58 100644 --- a/views/reportindex.html +++ b/views/reportindex.html @@ -23,9 +23,9 @@ - - - + + + <% include preloadCSS %> @@ -122,10 +122,10 @@ <%- include('partials/authentication-status') %> - - - - - + + + + + \ No newline at end of file diff --git a/views/service-worker.js b/views/service-worker.js new file mode 100644 index 00000000000..50e720b1aba --- /dev/null +++ b/views/service-worker.js @@ -0,0 +1,107 @@ +'use strict'; + +var CACHE = '<%= locals.cachebuster %>'; + +const CACHE_LIST = [ + '/', + '/images/launch.png', + '/images/apple-touch-icon-57x57.png', + '/images/apple-touch-icon-60x60.png', + '/images/apple-touch-icon-72x72.png', + '/images/apple-touch-icon-76x76.png', + '/images/apple-touch-icon-114x114.png', + '/images/apple-touch-icon-120x120.png', + '/images/apple-touch-icon-144x144.png', + '/images/apple-touch-icon-152x152.png', + '/images/apple-touch-icon-180x180.png', + '/images/favicon-32x32.png', + '/images/android-chrome-192x192.png', + '/images/favicon-96x96.png', + '/images/favicon-16x16.png', + '/manifest.json', + '/images/favicon.ico', + '/images/mstile-144x144.png', + '/css/ui-darkness/jquery-ui.min.css', + '/css/jquery.tooltips.css', + '/audio/alarm.mp3', + '/audio/alarm2.mp3', + '/css/ui-darkness/images/ui-icons_ffffff_256x240.png', + '/css/ui-darkness/images/ui-icons_cccccc_256x240.png', + '/css/ui-darkness/images/ui-bg_inset-soft_25_000000_1x100.png', + '/css/ui-darkness/images/ui-bg_gloss-wave_25_333333_500x100.png', + '/css/main.css', + '/bundle/js/bundle.app.js', + '/bundle/js/bundle.clock.js', + '/bundle/js/bundle.report.js', + '/socket.io/socket.io.js', + '/js/client.js', + '/images/logo2.png' +]; + +// Open a cache and use `addAll()` with an array of assets to add all of them +// to the cache. Return a promise resolving when all the assets are added. +function precache() { + return caches.open(CACHE).then(function (cache) { + return cache.addAll(CACHE_LIST); + }); +} + +// Open the cache where the assets were stored and search for the requested +// resource. Notice that in case of no matching, the promise still resolves +// but it does with `undefined` as value. +function fromCache(request) { + return caches.open(CACHE).then(function (cache) { + return cache.match(request).then(function (matching) { + return matching || Promise.reject('no-match'); + }); + }); +} + +// Update consists in opening the cache, performing a network request and +// storing the new response data. +function update(request) { + return caches.open(CACHE).then(function (cache) { + return fetch(request).then(function (response) { + return cache.put(request, response); + }); + }); +} + +// On install, cache some resources. +self.addEventListener('install', function(evt) { + //console.log('The service worker is being installed.'); + evt.waitUntil(precache()); +}); + +function inCache(request) { + let found = false; + CACHE_LIST.forEach( function (e) { + if (request.url.endsWith(e)) { + found = true; + } + }); + return found; +} + +self.addEventListener('fetch', function(evt) { + if (!evt.request.url.startsWith(self.location.origin) || CACHE === 'developmentMode' || !inCache(evt.request) || evt.request.method !== 'GET') { + //console.log('Skipping cache for ', evt.request.url); + return void evt.respondWith(fetch(evt.request)); + } + //console.log('Returning cached for ', evt.request.url); + evt.respondWith(fromCache(evt.request)); + evt.waitUntil(update(evt.request)); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return cacheNames.filter((cacheName) => CACHE !== cacheName); + }).then((unusedCaches) => { + //console.log('DESTROYING CACHE', unusedCaches.join(',')); + return Promise.all(unusedCaches.map((unusedCache) => { + return caches.delete(unusedCache); + })); + }).then(() => self.clients.claim()) + ); +}); \ No newline at end of file From b1ec21cd3fa48f2fc558f09611013c790472d7ff Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 4 Feb 2020 22:27:56 -0800 Subject: [PATCH 5/8] Added indexes to 'entries' and 'treatments' along with other updates (#5463) * Added compound indexes for treatments and entries collections. Updated ensureIndex to createIndex in mongo-strage.js as ensureIndex has been deprecated. Finally, updated testing/populate.js to be compatible with more recent versions of the node driver, as well as fixing a path issue. * Fixed missing end quote in lib/server/treatments.js. Changed all newly added double quotes to single quote to match style guide. * Removed indexes that referenced key600. --- lib/server/entries.js | 11 +++++++++-- lib/server/treatments.js | 1 + lib/storage/mongo-storage.js | 6 +++--- testing/populate.js | 7 +++++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/server/entries.js b/lib/server/entries.js index d7258f13b79..02dd115bb95 100644 --- a/lib/server/entries.js +++ b/lib/server/entries.js @@ -140,7 +140,15 @@ function storage(env, ctx) { api.query_for = query_for; api.getEntry = getEntry; api.aggregate = require('./aggregate')({ }, api); - api.indexedFields = [ 'date', 'type', 'sgv', 'mbg', 'sysTime', 'dateString' ]; + api.indexedFields = [ + 'date' + , 'type' + , 'sgv' + , 'mbg' + , 'sysTime' + , 'dateString' + , { 'type' : 1, 'date' : -1, 'dateString' : 1 } + ]; return api; } @@ -160,4 +168,3 @@ storage.queryOpts = { // expose module storage.storage = storage; module.exports = storage; - diff --git a/lib/server/treatments.js b/lib/server/treatments.js index 580e5cf0c45..3edd00a155e 100644 --- a/lib/server/treatments.js +++ b/lib/server/treatments.js @@ -133,6 +133,7 @@ function storage (env, ctx) { , 'percent' , 'absolute' , 'duration' + , { 'eventType' : 1, 'duration' : 1, 'created_at' : 1 } ]; api.remove = remove; diff --git a/lib/storage/mongo-storage.js b/lib/storage/mongo-storage.js index 275cde6eb8a..fbbc328d0e2 100644 --- a/lib/storage/mongo-storage.js +++ b/lib/storage/mongo-storage.js @@ -37,11 +37,11 @@ function init (env, cb, forceNewConnection) { console.log('Error connecting to MongoDB: %j - retrying in ' + timeout/1000 + ' sec', err); setTimeout(connect_with_retry, timeout, i+1); } else if (err.message) { - throw new Error('MongoDB connection string '+env.storageURI+' seems invalid: '+err.message) ; + throw new Error('MongoDB connection string '+env.storageURI+' seems invalid: '+err.message) ; } } else { console.log('Successfully established a connected to MongoDB'); - + var dbName = env.storageURI.split('/').pop().split('?'); dbName=dbName[0]; // drop Connection Options mongo.db = client.db(dbName); @@ -66,7 +66,7 @@ function init (env, cb, forceNewConnection) { mongo.ensureIndexes = function ensureIndexes (collection, fields) { fields.forEach(function (field) { console.info('ensuring index for: ' + field); - collection.ensureIndex(field, function (err) { + collection.createIndex(field, { 'background': true }, function (err) { if (err) { console.error('unable to ensureIndex for: ' + field + ' - ' + err); } diff --git a/testing/populate.js b/testing/populate.js index 34c307b7759..c4d9e1422a1 100644 --- a/testing/populate.js +++ b/testing/populate.js @@ -3,19 +3,22 @@ var mongodb = require('mongodb'); var env = require('./../env')(); -var util = require('./helpers/util'); +var util = require('./util'); main(); function main() { var MongoClient = mongodb.MongoClient; - MongoClient.connect(env.storageURI, function connected(err, db) { + MongoClient.connect(env.storageURI, { "useUnifiedTopology" : true, "useNewUrlParser" : true }, function connected(err, client) { console.log('Connecting to mongo...'); if (err) { console.log('Error occurred: ', err); throw err; } + + var db = client.db(); + populate_collection(db); }); } From 765d7f8ea56e617504eb66f8da69b659b919630c Mon Sep 17 00:00:00 2001 From: Sulka Haro Date: Sat, 8 Feb 2020 11:22:12 +0200 Subject: [PATCH 6/8] Fix: Round interporlated mg/dL value to an integer --- lib/data/treatmenttocurve.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/data/treatmenttocurve.js b/lib/data/treatmenttocurve.js index afa17b397ec..714fb9049e6 100644 --- a/lib/data/treatmenttocurve.js +++ b/lib/data/treatmenttocurve.js @@ -41,7 +41,7 @@ module.exports = function fitTreatmentsToBGCurve (ddata, env, ctx) { calcedBG = mgdlAfter; } - return calcedBG || 180; + return Math.round(calcedBG) || 180; } function mgdlValue (entry) { From 44ab2a79dcc509d89c94e96aef07c284fa08f57a Mon Sep 17 00:00:00 2001 From: Dominik Dzienia Date: Mon, 10 Feb 2020 07:56:38 +0100 Subject: [PATCH 7/8] Plugin to show database size (% of available space or in MiB) (#5496) * Database size plugin - pill that displays current mongoDB database size * Enabled dbsize by default * Fixed bug with dbsize not shownig when size is (rounded) 0% but real bytes > 0 * Cleanup & update to iconfont generation manual * Changed how warning/urgent levels are configured - from absolute MiB to percentage of DBSIZE_MAX --- README.md | 19 ++ assets/fonts/Nightscout Plugin Icons.json | 87 ++++++ assets/fonts/README.md | 29 ++ lib/client/receiveddata.js | 3 + lib/data/dataloader.js | 18 ++ lib/data/ddata.js | 2 + lib/language.js | 28 +- lib/plugins/dbsize.js | 163 +++++++++++ lib/plugins/index.js | 1 + lib/settings.js | 4 +- static/css/main.css | 23 +- tests/dbsize.test.js | 320 ++++++++++++++++++++++ 12 files changed, 693 insertions(+), 4 deletions(-) create mode 100644 assets/fonts/Nightscout Plugin Icons.json create mode 100644 assets/fonts/README.md create mode 100644 lib/plugins/dbsize.js create mode 100644 tests/dbsize.test.js diff --git a/README.md b/README.md index 62bd048f059..ba921dc2b98 100644 --- a/README.md +++ b/README.md @@ -525,6 +525,25 @@ For remote overrides, the following extended settings must be configured: Enabled [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) so other websites can make request to your Nightscout site, uses these extended settings: * `CORS_ALLOW_ORIGIN` (`*`) - The list of sites that are allow to make requests +##### `dbsize` (Database Size) + Show size of Nightscout Database, as a percentage of declared available space or in MiB. + + Many deployments of Nightscout use free tier of MongoDB on Heroku, which is limited in size. After some time, as volume of stored data grows, it may happen that this limit is reached and system is unable to store new data. This plugin provides pill that indicates size of Database and shows (when configured) alarms regarding reaching space limit. + + **IMPORTANT:** This plugin can only check how much space database already takes, _but cannot infer_ max size available on server for it. To have correct alarms and realistic percentage, `DBSIZE_MAX` need to be properly set - according to your mongoDB hosting configuration. + + **NOTE:** It may happen that new data cannot be saved to database (due to size limits) even when this plugin reports that storage is not 100% full. MongoDB pre-allocate data in advance, and database file is always made bigger than total real data size. To avoid premature alarms, this plugin refers to data size instead of file size, but sets warning thresholds low. It may happen, that file size of database will take 100% of allowed space but there will be plenty of place inside to store the data. But it may also happen, with file size is at its max, that data size will be ~70% of file size, and there will be no place left. That may happen because data inside file is fragmented and free space _holes_ are too small for new entries. In such case calling `db.repairDatabase()` on mongoDB is recommended to compact and repack data (currently only doable with third-party mongoDB tools, but action is planned to be added in _Admin Tools_ section in future releases). + + All sizes are expressed as integers, in _Mebibytes_ `1 MiB == 1024 KiB == 1024*1024 B` + + * `DBSIZE_MAX` (`496`) - Maximal allowed size of database on your mongoDB server, in MiB. You need to adjust that value to match your database hosting limits - default value is for standard Heroku mongoDB free tier. + * `DBSIZE_WARN_PERCENTAGE` (`60`) - Threshold to show first warning about database size. When database reach this percentage of `DBSIZE_MAX` size - pill will show size in yellow. + * `DBSIZE_URGENT_PERCENTAGE` (`75`) - Threshold to show urgent warning about database size. When database reach this percentage of `DBSIZE_MAX` size, it is urgent to do backup and clean up old data. At this percentage info pill turns red. + * `DBSIZE_ENABLE_ALERTS` (`false`) - Set to `true` to enable notifications about database size. + * `DBSIZE_IN_MIB` (`false`) - Set to `true` to display size of database in MiB-s instead of default percentage. + + This plugin should be enabled by default, if needed can be diasabled by adding `dbsize` to the list of disabled plugins, for example: `DISABLE="dbsize"`. + #### Extended Settings Some plugins support additional configuration using extra environment variables. These are prefixed with the name of the plugin and a `_`. For example setting `MYPLUGIN_EXAMPLE_VALUE=1234` would make `extendedSettings.exampleValue` available to the `MYPLUGIN` plugin. diff --git a/assets/fonts/Nightscout Plugin Icons.json b/assets/fonts/Nightscout Plugin Icons.json new file mode 100644 index 00000000000..65874c15679 --- /dev/null +++ b/assets/fonts/Nightscout Plugin Icons.json @@ -0,0 +1,87 @@ +{ + "metadata": { + "name": "Nightscout Plugin Icons", + "lastOpened": 0, + "created": 1580075608590 + }, + "iconSets": [ + { + "selection": [ + { + "order": 2, + "id": 0, + "name": "database", + "prevSize": 32, + "code": 59649, + "tempChar": "" + } + ], + "id": 2, + "metadata": { + "name": "Plugin Icons", + "importSize": { + "width": 16, + "height": 18 + } + }, + "height": 1024, + "prevSize": 32, + "icons": [ + { + "id": 0, + "paths": [ + "M455.111 0c-251.449 0-455.111 101.831-455.111 227.556s203.662 227.556 455.111 227.556 455.111-101.831 455.111-227.556-203.662-227.556-455.111-227.556zM0 341.333v170.667c0 125.724 203.662 227.556 455.111 227.556s455.111-101.831 455.111-227.556v-170.667c0 125.724-203.662 227.556-455.111 227.556s-455.111-101.831-455.111-227.556zM0 625.778v170.667c0 125.724 203.662 227.556 455.111 227.556s455.111-101.831 455.111-227.556v-170.667c0 125.724-203.662 227.556-455.111 227.556s-455.111-101.831-455.111-227.556z" + ], + "attrs": [ + {} + ], + "width": 910, + "isMulticolor": false, + "isMulticolor2": false, + "grid": 0, + "tags": [ + "plugins" + ] + } + ], + "invisible": false, + "colorThemes": [] + } + ], + "preferences": { + "showGlyphs": true, + "showQuickUse": true, + "showQuickUse2": true, + "showSVGs": true, + "fontPref": { + "prefix": "plugicon-", + "metadata": { + "fontFamily": "pluginicons", + "majorVersion": 1, + "minorVersion": 0 + }, + "metrics": { + "emSize": 1024, + "baseline": 6.25, + "whitespace": 50 + }, + "embed": false, + "showSelector": false, + "showMetrics": false, + "showMetadata": false, + "showVersion": false + }, + "imagePref": { + "prefix": "icon-", + "png": true, + "useClassSelector": true, + "color": 0, + "bgColor": 16777215, + "classSelector": ".icon" + }, + "historySize": 50, + "showCodes": true, + "gridSize": 16 + }, + "uid": -1 +} \ No newline at end of file diff --git a/assets/fonts/README.md b/assets/fonts/README.md new file mode 100644 index 00000000000..b6b98ea4322 --- /dev/null +++ b/assets/fonts/README.md @@ -0,0 +1,29 @@ +How to upgrade icons in icon-fonts on Nightscout +================================================ + +This guide is fol developers regarding how to add new icon to Nightscout. + +Nightscout use icon fonts to render icons. Each icon is glyph (like - letter, or more like emoji character) inside custom made font file. +That way we have nice, vector icons, that are small, scalable, looks good on each platform, and are easy to embed inside CSS. + +To extend existing icon set.: + +1. Prepare minimalist, black & white icon in SVG tool of choice, and optimize it (you can use Inkscape) to be small in size and render good at small sizes. +2. Use https://icomoon.io/app and import accompanied JSON project file (`Nightscout Plugin Icons.json`) +3. Add SVG as new glyph. Remember to take care to set proper character code and CSS name +4. Save new version of JSON project file and store in this folder +5. Generate font, download zip file and unpack it to get `fonts/pluginicons.svg` and `fonts/pluginicons.woff` +6. Update `statc/css/main.css` file + * In section of `@font-face` with `font-family: 'pluginicons'` + * update part after `data:application/font-woff;charset=utf-8;base64,` with Base64-encoded content of just generated `pluginicons.woff` font + * update part after `data:application/font-svg;charset=utf-8;base64,` with Base64-encoded content of just generated `pluginicons.svg` font + * copy/update all entries `.plugicon-****:before { content: "****"; }` from generated font `style.css` into `statc/css/main.css` +7. Do not forget to update `Nightscout Plugin Icons.json` in this repo (´download updated project from icomoon.io) + +Hints +----- + +* You can find many useful online tools to encode file into Base64, like: https://base64.guru/converter/encode/file +* Do not split Base64 output - it should be one LONG line +* Since update process is **manual** and generated fonts & updated CSS sections are **binary** - try to avoid **git merge conflicts** by speaking with other developers if you plan to add new icon +* When in doubt - check `git log` and reach last contributor for guidelines :) diff --git a/lib/client/receiveddata.js b/lib/client/receiveddata.js index e0adf13fb3b..c167d9463a3 100644 --- a/lib/client/receiveddata.js +++ b/lib/client/receiveddata.js @@ -128,6 +128,9 @@ function receiveDData (received, ddata, settings) { } } + if (received.dbstats && received.dbstats.fileSize) { + ddata.dbstats = received.dbstats; + } } //expose for tests diff --git a/lib/data/dataloader.js b/lib/data/dataloader.js index 1c00f998d73..b0eafdd4fe0 100644 --- a/lib/data/dataloader.js +++ b/lib/data/dataloader.js @@ -69,6 +69,7 @@ function init(env, ctx) { // clear treatments, we're going to merge from more queries ddata.treatments = []; + ddata.dbstats = {}; async.parallel([ loadEntries.bind(null, ddata, ctx) @@ -79,6 +80,7 @@ function init(env, ctx) { , loadFood.bind(null, ddata, ctx) , loadDeviceStatus.bind(null, ddata, env, ctx) , loadActivity.bind(null, ddata, ctx) + , loadDatabaseStats.bind(null, ddata, ctx) ], loadComplete); }; @@ -399,5 +401,21 @@ function loadDeviceStatus(ddata, env, ctx, callback) { }); } +function loadDatabaseStats(ddata, ctx, callback) { + ctx.store.db.stats(function mongoDone (err, result) { + if (err) { + console.log("Problem loading database stats"); + } + if (!err && result) { + ddata.dbstats = { + dataSize: result.dataSize + , indexSize: result.indexSize + , fileSize: result.fileSize + }; + } + callback(); + }); +} + module.exports = init; diff --git a/lib/data/ddata.js b/lib/data/ddata.js index 9fa470e3f16..b5226124ccf 100644 --- a/lib/data/ddata.js +++ b/lib/data/ddata.js @@ -17,6 +17,7 @@ function init () { , devicestatus: [] , food: [] , activity: [] + , dbstats: {} , lastUpdated: 0 }; @@ -51,6 +52,7 @@ function init () { results.mbgs = ddata.mbgs; results.food = ddata.food; results.treatments = ddata.treatments; + results.dbstats = ddata.dbstats; return results; diff --git a/lib/language.js b/lib/language.js index c105c5239d7..ede0a734add 100644 --- a/lib/language.js +++ b/lib/language.js @@ -15100,7 +15100,33 @@ function init() { , pl: 'Tłuszcz ogółem' ,ru: 'Всего жиров' ,he: 'כל שומנים' - } + }, + 'Database Size': { + pl: 'Rozmiar Bazy Danych' + }, + 'Database Size near its limits!': { + pl: 'Rozmiar bazy danych zbliża się do limitu!' + }, + 'Database size is %1 MiB out of %2 MiB. Please backup and clean up database!': { + pl: 'Baza danych zajmuje %1 MiB z dozwolonych %2 MiB. Proszę zrób kopię zapasową i oczyść bazę danych!' + }, + 'Database file size': { + pl: 'Rozmiar pliku bazy danych' + }, + '%1 MiB of %2 MiB (%3%)': { + pl: '%1 MiB z %2 MiB (%3%)' + }, + 'Data size': { + pl: 'Rozmiar danych' + }, + 'virtAsstDatabaseSize': { + en: '%1 MiB that is %2% of available database space' + ,pl: '%1 MiB co stanowi %2% przestrzeni dostępnej dla bazy danych' + }, + 'virtAsstTitleDatabaseSize': { + en: 'Database file size' + ,pl: 'Rozmiar pliku bazy danych' + } }; language.translations = translations; diff --git a/lib/plugins/dbsize.js b/lib/plugins/dbsize.js new file mode 100644 index 00000000000..b2b85b83e7a --- /dev/null +++ b/lib/plugins/dbsize.js @@ -0,0 +1,163 @@ +'use strict'; + +var _ = require('lodash'); +var levels = require('../levels'); + +function init (ctx) { + var translate = ctx.language.translate; + + var dbsize = { + name: 'dbsize' + , label: translate('Database Size') + , pluginType: 'pill-status' + , pillFlip: true + }; + + dbsize.getPrefs = function getPrefs (sbx) { + return { + warnPercentage: sbx.extendedSettings.warnPercentage ? sbx.extendedSettings.warnPercentage : 60 + , urgentPercentage: sbx.extendedSettings.urgentPercentage ? sbx.extendedSettings.urgentPercentage : 75 + , max: sbx.extendedSettings.max ? sbx.extendedSettings.max : 496 + , enableAlerts: sbx.extendedSettings.enableAlerts + , inMib: sbx.extendedSettings.inMib + }; + }; + + dbsize.setProperties = function setProperties (sbx) { + sbx.offerProperty('dbsize', function setDbsize () { + return dbsize.analyzeData(sbx); + }); + }; + + dbsize.analyzeData = function analyzeData (sbx) { + + var prefs = dbsize.getPrefs(sbx); + + var recentData = sbx.data.dbstats; + + var result = { + level: undefined + , display: prefs.inMib ? '?MiB' : '?%' + , status: undefined + }; + + var maxSize = (prefs.max > 0) ? prefs.max : 100 * 1024; + var currentSize = (recentData && recentData.fileSize ? recentData.fileSize : 0) / (1024 * 1024); + var totalDataSize = (recentData && recentData.dataSize) ? recentData.dataSize : 0; + totalDataSize += (recentData && recentData.indexSize) ? recentData.indexSize : 0; + totalDataSize /= 1024 * 1024; + + var sizePercentage = Math.floor((currentSize * 100.0) / maxSize); + var dataPercentage = Math.floor((totalDataSize * 100.0) / maxSize); + + result.totalDataSize = totalDataSize; + result.dataPercentage = dataPercentage; + result.notificationLevel = levels.INFO; + result.details = { + fileSize: parseFloat(currentSize.toFixed(2)) + , maxSize: parseFloat(maxSize.toFixed(2)) + , dataSize: parseFloat(totalDataSize.toFixed(2)) + , sizePercentage: sizePercentage + }; + + // failsafe to have percentage in 0..100 range + var boundWarnPercentage = Math.max(0, Math.min(100, parseInt(prefs.warnPercentage))); + var boundUrgentPercentage = Math.max(0, Math.min(100, parseInt(prefs.urgentPercentage))); + + var warnSize = Math.floor((boundWarnPercentage/100) * maxSize); + var urgentSize = Math.floor((boundUrgentPercentage/100) * maxSize); + + if ((totalDataSize >= urgentSize)&&(boundUrgentPercentage > 0)) { + result.notificationLevel = levels.URGENT; + } else if ((totalDataSize >= warnSize)&&(boundWarnPercentage > 0)) { + result.notificationLevel = levels.WARN; + } + + result.display = prefs.inMib ? parseFloat(totalDataSize.toFixed(0)) + 'MiB' : dataPercentage + '%'; + result.status = levels.toStatusClass(result.notificationLevel); + + return result; + }; + + dbsize.checkNotifications = function checkNotifications (sbx) { + var prefs = dbsize.getPrefs(sbx); + + if (!prefs.enableAlerts) { return; } + + var prop = sbx.properties.dbsize; + + if (prop.dataPercentage && prop.notificationLevel && prop.notificationLevel >= levels.WARN) { + sbx.notifications.requestNotify({ + level: prop.notificationLevel + , title: levels.toDisplay(prop.notificationLevel) + ' ' + translate('Database Size near its limits!') + , message: translate('Database size is %1 MiB out of %2 MiB. Please backup and clean up database!', { + params: [prop.details.dataSize, prop.details.maxSize] + }) + , pushoverSound: 'echo' + , group: 'Database Size' + , plugin: dbsize + , debug: prop + }); + } + }; + + dbsize.updateVisualisation = function updateVisualisation (sbx) { + var prop = sbx.properties.dbsize; + + var infos = [{ + label: translate('Data size') + , value: translate('%1 MiB of %2 MiB (%3%)', { + params: [prop.details.dataSize, prop.details.maxSize, prop.dataPercentage] + }) + } + , { + label: translate('Database file size') + , value: translate('%1 MiB of %2 MiB (%3%)', { + params: [prop.details.fileSize, prop.details.maxSize, prop.details.sizePercentage] + }) + } + ]; + + sbx.pluginBase.updatePillText(dbsize, { + value: prop && prop.display + , labelClass: 'plugicon-database' + , pillClass: prop && prop.status + , info: infos + , hide: !(prop && prop.totalDataSize && prop.totalDataSize >= 0) + }); + }; + + function virtAsstDatabaseSizeHandler (next, slots, sbx) { + if (sbx.properties.dbsize.display) { + var response = translate('virtAsstDatabaseSize', { + params: [ + sbx.properties.dbsize.details.dataSize + , sbx.properties.dbsize.dataPercentage + ] + }); + next(translate('virtAsstTitleDatabaseSize'), response); + } else { + next(translate('virtAsstTitleDatabaseSize'), translate('virtAsstUnknown')); + } + } + + dbsize.virtAsst = { + intentHandlers: [ + { + // for backwards compatibility + intent: 'DatabaseSize' + , intentHandler: virtAsstDatabaseSizeHandler + } + , { + intent: 'MetricNow' + , metrics: ['database size', 'file size', 'db size', 'data size'] + , intentHandler: virtAsstDatabaseSizeHandler + } + ] + }; + + return dbsize; + +} + +module.exports = init; diff --git a/lib/plugins/index.js b/lib/plugins/index.js index 2568b634afd..5a6cb2822da 100644 --- a/lib/plugins/index.js +++ b/lib/plugins/index.js @@ -49,6 +49,7 @@ function init (ctx) { , require('./boluscalc')(ctx) // fake plugin to show/hide , require('./profile')(ctx) // fake plugin to hold extended settings , require('./speech')(ctx) + , require('./dbsize')(ctx) ]; var serverDefaultPlugins = [ diff --git a/lib/settings.js b/lib/settings.js index d1f18b0e9c5..8ac1c5ed768 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -119,7 +119,7 @@ function init () { } //TODO: getting sent in status.json, shouldn't be - settings.DEFAULT_FEATURES = ['bgnow', 'delta', 'direction', 'timeago', 'devicestatus', 'upbat', 'errorcodes', 'profile']; + settings.DEFAULT_FEATURES = ['bgnow', 'delta', 'direction', 'timeago', 'devicestatus', 'upbat', 'errorcodes', 'profile', 'dbsize']; var wasSet = []; @@ -260,7 +260,7 @@ function init () { function adjustShownPlugins () { var showPluginsUnset = settings.showPlugins && 0 === settings.showPlugins.length; - settings.showPlugins += ' delta direction upbat'; + settings.showPlugins += ' delta direction upbat dbsize'; if (settings.showRawbg === 'always' || settings.showRawbg === 'noise') { settings.showPlugins += ' rawbg'; } diff --git a/static/css/main.css b/static/css/main.css index 8e6882dde9d..25c6bae0f0e 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -9,7 +9,21 @@ font-style: normal; } - [class^="icon-"]:before, [class*=" icon-"]:before { +/* + Icon font for additional plugin icons. + Please read assets/fonts/README.md about update process +*/ +@font-face { + font-family: 'pluginicons'; + /* Plugin Icons font files content (from WOFF and SVG icon files, base64 encoded) */ + src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAWAAAsAAAAABTQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgDxIE8mNtYXAAAAFoAAAAVAAAAFQXVdKJZ2FzcAAAAbwAAAAIAAAACAAAABBnbHlmAAABxAAAAUgAAAFIFA4eR2hlYWQAAAMMAAAANgAAADYXVLrVaGhlYQAAA0QAAAAkAAAAJAdQA8ZobXR4AAADaAAAABQAAAAUCY4AAGxvY2EAAAN8AAAADAAAAAwAKAC4bWF4cAAAA4gAAAAgAAAAIAAJAFxuYW1lAAADqAAAAbYAAAG2DBt7mXBvc3QAAAVgAAAAIAAAACAAAwAAAAMCxwGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA6QEDwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADgAAAAKAAgAAgACAAEAIOkB//3//wAAAAAAIOkB//3//wAB/+MXAwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAwAA/8ADjgPAABsAOgBZAAABIgcOAQcGFRQXHgEXFjMyNz4BNzY1NCcuAScmARUUFx4BFxYzMjc+ATc2PQEUBw4BBwYjIicuAScmNREVFBceARcWMzI3PgE3Nj0BFAcOAQcGIyInLgEnJjUBx15TU3skJCQke1NTXl5TU3wjJCQjfFNT/dskJHtTU15eU1N8IyQkI3xTU15eU1N7JCQkJHtTU15eU1N8IyQkI3xTU15eU1N7JCQDwBISPikpMC8pKj0SEhISPSopLzApKT4SEv6rqy8qKT4SEhISPikqL6svKik+EhISEj4pKi/+46owKSk+EhISEj4pKTCqLykqPhESEhE+KikvAAAAAAEAAAABAABgRbaTXw889QALBAAAAAAA2lO7LAAAAADaU7ssAAD/wAOOA8AAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAA44AAQAAAAAAAAAAAAAAAAAAAAUEAAAAAAAAAAAAAAACAAAAA44AAAAAAAAACgAUAB4ApAABAAAABQBaAAMAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEACwAAAAEAAAAAAAIABwCEAAEAAAAAAAMACwBCAAEAAAAAAAQACwCZAAEAAAAAAAUACwAhAAEAAAAAAAYACwBjAAEAAAAAAAoAGgC6AAMAAQQJAAEAFgALAAMAAQQJAAIADgCLAAMAAQQJAAMAFgBNAAMAAQQJAAQAFgCkAAMAAQQJAAUAFgAsAAMAAQQJAAYAFgBuAAMAAQQJAAoANADUcGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwcGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzcGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzUmVndWxhcgBSAGUAZwB1AGwAYQBycGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format('woff'), + url(data:application/font-svg;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiA+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQo8bWV0YWRhdGE+R2VuZXJhdGVkIGJ5IEljb01vb248L21ldGFkYXRhPg0KPGRlZnM+DQo8Zm9udCBpZD0icGx1Z2luaWNvbnMiIGhvcml6LWFkdi14PSIxMDI0Ij4NCjxmb250LWZhY2UgdW5pdHMtcGVyLWVtPSIxMDI0IiBhc2NlbnQ9Ijk2MCIgZGVzY2VudD0iLTY0IiAvPg0KPG1pc3NpbmctZ2x5cGggaG9yaXotYWR2LXg9IjEwMjQiIC8+DQo8Z2x5cGggdW5pY29kZT0iJiN4MjA7IiBob3Jpei1hZHYteD0iNTEyIiBkPSIiIC8+DQo8Z2x5cGggdW5pY29kZT0iJiN4ZTkwMTsiIGdseXBoLW5hbWU9ImRhdGFiYXNlIiBob3Jpei1hZHYteD0iOTEwIiBkPSJNNDU1LjExMSA5NjBjLTI1MS40NDkgMC00NTUuMTExLTEwMS44MzEtNDU1LjExMS0yMjcuNTU2czIwMy42NjItMjI3LjU1NiA0NTUuMTExLTIyNy41NTYgNDU1LjExMSAxMDEuODMxIDQ1NS4xMTEgMjI3LjU1Ni0yMDMuNjYyIDIyNy41NTYtNDU1LjExMSAyMjcuNTU2ek0wIDYxOC42Njd2LTE3MC42NjdjMC0xMjUuNzI0IDIwMy42NjItMjI3LjU1NiA0NTUuMTExLTIyNy41NTZzNDU1LjExMSAxMDEuODMxIDQ1NS4xMTEgMjI3LjU1NnYxNzAuNjY3YzAtMTI1LjcyNC0yMDMuNjYyLTIyNy41NTYtNDU1LjExMS0yMjcuNTU2cy00NTUuMTExIDEwMS44MzEtNDU1LjExMSAyMjcuNTU2ek0wIDMzNC4yMjJ2LTE3MC42NjdjMC0xMjUuNzI0IDIwMy42NjItMjI3LjU1NiA0NTUuMTExLTIyNy41NTZzNDU1LjExMSAxMDEuODMxIDQ1NS4xMTEgMjI3LjU1NnYxNzAuNjY3YzAtMTI1LjcyNC0yMDMuNjYyLTIyNy41NTYtNDU1LjExMS0yMjcuNTU2cy00NTUuMTExIDEwMS44MzEtNDU1LjExMSAyMjcuNTU2eiIgLz4NCjwvZm9udD48L2RlZnM+PC9zdmc+) format('svg'); + font-weight: normal; + font-style: normal; +} + + [class^="icon-"]:before, [class*=" icon-"]:before, + [class^="plugicon-"]:before, [class*=" plugicon-"]:before { font-family: "nsicons"; font-style: normal; font-weight: normal; @@ -43,6 +57,10 @@ /* Uncomment for 3D effect */ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } + +[class^="plugicon-"]:before, [class*=" plugicon-"]:before { + font-family: "pluginicons"; +} .icon-volume:before { content: '\e800'; } .icon-plus:before { content: '\e801'; } @@ -65,6 +83,9 @@ .icon-chart-line:before { content: '\f201'; } .icon-hourglass:before { content: '\f254'; } +/* Plugin Icons id-s (copy from generated icon style.css) */ +.plugicon-database:before { content: "\e901"; } + html, body { margin: 0; padding: 0; diff --git a/tests/dbsize.test.js b/tests/dbsize.test.js new file mode 100644 index 00000000000..0a66bb3ad6d --- /dev/null +++ b/tests/dbsize.test.js @@ -0,0 +1,320 @@ +'use strict'; + +require('should'); + +describe('Database Size', function() { + + var dataInRange = { dbstats: { dataSize: 1024 * 1024 * 137, indexSize: 1024 * 1024 * 48, fileSize: 1024 * 1024 * 256 } }; + var dataWarn = { dbstats: { dataSize: 1024 * 1024 * 250, indexSize: 1024 * 1024 * 100, fileSize: 1024 * 1024 * 360 } }; + var dataUrgent = { dbstats: { dataSize: 1024 * 1024 * 300, indexSize: 1024 * 1024 * 150, fileSize: 1024 * 1024 * 496 } }; + + var env = require('../env')(); + + it('display database size in range', function(done) { + var sandbox = require('../lib/sandbox')(); + var ctx = { + settings: {} + , language: require('../lib/language')() + }; + ctx.language.set('en'); + ctx.levels = require('../lib/levels'); + + var sbx = sandbox.clientInit(ctx, Date.now(), dataInRange); + + sbx.offerProperty = function mockedOfferProperty (name, setter) { + name.should.equal('dbsize'); + var result = setter(); + result.display.should.equal('37%'); + result.status.should.equal('current'); + done(); + }; + + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx); + + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('display database size warning', function(done) { + var sandbox = require('../lib/sandbox')(); + var ctx = { + settings: {} + , language: require('../lib/language')() + }; + ctx.language.set('en'); + ctx.levels = require('../lib/levels'); + + var sbx = sandbox.clientInit(ctx, Date.now(), dataWarn); + + sbx.offerProperty = function mockedOfferProperty (name, setter) { + name.should.equal('dbsize'); + var result = setter(); + result.display.should.equal('70%'); + result.status.should.equal('warn'); + done(); + }; + + var dbsize = require('../lib/plugins/dbsize')(ctx); + + dbsize.setProperties(sbx); + + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('display database size urgent', function(done) { + var sandbox = require('../lib/sandbox')(); + var ctx = { + settings: {} + , language: require('../lib/language')() + }; + ctx.language.set('en'); + ctx.levels = require('../lib/levels'); + + var sbx = sandbox.clientInit(ctx, Date.now(), dataUrgent); + + sbx.offerProperty = function mockedOfferProperty (name, setter) { + name.should.equal('dbsize'); + var result = setter(); + result.display.should.equal('90%'); + result.status.should.equal('urgent'); + done(); + }; + + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx); + + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('display database size warning notiffication', function(done) { + var sandbox = require('../lib/sandbox')(); + var ctx = { + settings: {} + , language: require('../lib/language')() + , notifications: require('../lib/notifications')(env, ctx) + }; + ctx.notifications.initRequests(); + ctx.language.set('en'); + ctx.levels = require('../lib/levels'); + + var sbx = sandbox.clientInit(ctx, Date.now(), dataWarn); + sbx.extendedSettings = { 'enableAlerts': 'TRUE' }; + + var dbsize = require('../lib/plugins/dbsize')(ctx); + + dbsize.setProperties(sbx); + dbsize.checkNotifications(sbx); + + var notif = ctx.notifications.findHighestAlarm('Database Size'); + notif.level.should.equal(ctx.levels.WARN); + notif.title.should.equal('Warning Database Size near its limits!'); + notif.message.should.equal('Database size is 350 MiB out of 496 MiB. Please backup and clean up database!'); + done(); + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('display database size urgent notiffication', function(done) { + var sandbox = require('../lib/sandbox')(); + var ctx = { + settings: {} + , language: require('../lib/language')() + , notifications: require('../lib/notifications')(env, ctx) + }; + ctx.notifications.initRequests(); + ctx.language.set('en'); + ctx.levels = require('../lib/levels'); + + var sbx = sandbox.clientInit(ctx, Date.now(), dataUrgent); + sbx.extendedSettings = { 'enableAlerts': 'TRUE' }; + + var dbsize = require('../lib/plugins/dbsize')(ctx); + + dbsize.setProperties(sbx); + dbsize.checkNotifications(sbx); + + var notif = ctx.notifications.findHighestAlarm('Database Size'); + notif.level.should.equal(ctx.levels.URGENT); + notif.title.should.equal('Urgent Database Size near its limits!'); + notif.message.should.equal('Database size is 450 MiB out of 496 MiB. Please backup and clean up database!'); + done(); + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('set a pill to the database size in percent', function(done) { + var ctx = { + settings: {} + , pluginBase: { + updatePillText: function mockedUpdatePillText (plugin, options) { + options.value.should.equal('90%'); + options.labelClass.should.equal('plugicon-database'); + options.pillClass.should.equal('urgent'); + done(); + } + } + , language: require('../lib/language')() + }; + ctx.language.set('en'); + + var sandbox = require('../lib/sandbox')(); + var sbx = sandbox.clientInit(ctx, Date.now(), dataUrgent); + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx); + dbsize.updateVisualisation(sbx); + + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('set a pill to the database size in MiB', function(done) { + var ctx = { + settings: { + extendedSettings: { + empty: false + , dbsize: { + inMib: true + } + } + } + , pluginBase: { + updatePillText: function mockedUpdatePillText (plugin, options) { + options.value.should.equal('450MiB'); + options.labelClass.should.equal('plugicon-database'); + options.pillClass.should.equal('urgent'); + done(); + } + } + , language: require('../lib/language')() + }; + ctx.language.set('en'); + + var sandbox = require('../lib/sandbox')(); + var sbx = sandbox.clientInit(ctx, Date.now(), dataUrgent); + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx.withExtendedSettings(dbsize)); + dbsize.updateVisualisation(sbx); + + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('configure warn level percentage', function(done) { + + var ctx = { + settings: { + extendedSettings: { + empty: false + , dbsize: { + warnPercentage: 30 + } + } + } + , pluginBase: { + updatePillText: function mockedUpdatePillText (plugin, options) { + options.value.should.equal('37%'); + options.pillClass.should.equal('warn'); + done(); + } + } + , language: require('../lib/language')() + }; + ctx.language.set('en'); + + var sandbox = require('../lib/sandbox')(); + var sbx = sandbox.clientInit(ctx, Date.now(), dataInRange); + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx.withExtendedSettings(dbsize)); + dbsize.updateVisualisation(sbx); + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('configure urgent level percentage', function(done) { + + var ctx = { + settings: { + extendedSettings: { + empty: false + , dbsize: { + warnPercentage: 30 + , urgentPercentage: 36 + } + } + } + , pluginBase: { + updatePillText: function mockedUpdatePillText (plugin, options) { + options.value.should.equal('37%'); + options.pillClass.should.equal('urgent'); + done(); + } + } + , language: require('../lib/language')() + }; + ctx.language.set('en'); + + var sandbox = require('../lib/sandbox')(); + var sbx = sandbox.clientInit(ctx, Date.now(), dataInRange); + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx.withExtendedSettings(dbsize)); + dbsize.updateVisualisation(sbx); + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('hide the pill if there is no info regarding database size', function(done) { + var ctx = { + settings: {} + , pluginBase: { + updatePillText: function mockedUpdatePillText (plugin, options) { + options.hide.should.equal(true); + done(); + } + } + , language: require('../lib/language')() + }; + ctx.language.set('en'); + + var sandbox = require('../lib/sandbox')(); + var sbx = sandbox.clientInit(ctx, Date.now(), {}); + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx); + dbsize.updateVisualisation(sbx); + }); + + // ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~. + + it('should handle virtAsst requests', function(done) { + + var ctx = { + settings: {} + , language: require('../lib/language')() + }; + ctx.language.set('en'); + + var sandbox = require('../lib/sandbox')(); + var sbx = sandbox.clientInit(ctx, Date.now(), dataUrgent); + var dbsize = require('../lib/plugins/dbsize')(ctx); + dbsize.setProperties(sbx); + + dbsize.virtAsst.intentHandlers.length.should.equal(2); + + dbsize.virtAsst.intentHandlers[0].intentHandler(function next (title, response) { + title.should.equal('Database file size'); + response.should.equal('450 MiB that is 90% of available database space'); + + dbsize.virtAsst.intentHandlers[1].intentHandler(function next (title, response) { + title.should.equal('Database file size'); + response.should.equal('450 MiB that is 90% of available database space'); + + done(); + }, [], sbx); + + }, [], sbx); + + }); + +}); From 65b9f38f88e5d72bceeebb9da3d897723fa390c3 Mon Sep 17 00:00:00 2001 From: Petr Ondrusek <34578008+PetrOndrusek@users.noreply.github.com> Date: Tue, 11 Feb 2020 08:32:22 +0100 Subject: [PATCH 8/8] Trying to fix random fail of APIv3 tests (#5519) * APIv3: isolating documents from tests (not allowing clashes of calculated identifiers) * removing unused async keyword --- tests/api.alexa.test.js | 1 + tests/api3.create.test.js | 2 +- tests/api3.generic.workflow.test.js | 2 +- tests/api3.patch.test.js | 2 +- tests/api3.read.test.js | 2 +- tests/api3.update.test.js | 1 + 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/api.alexa.test.js b/tests/api.alexa.test.js index 8c2f5916cf6..440050eb2cc 100644 --- a/tests/api.alexa.test.js +++ b/tests/api.alexa.test.js @@ -7,6 +7,7 @@ const bodyParser = require('body-parser'); require('should'); describe('Alexa REST api', function ( ) { + this.timeout(10000); const apiRoot = require('../lib/api/root'); const api = require('../lib/api/'); before(function (done) { diff --git a/tests/api3.create.test.js b/tests/api3.create.test.js index ba0cce83ba3..5ea3ab1a869 100644 --- a/tests/api3.create.test.js +++ b/tests/api3.create.test.js @@ -16,7 +16,7 @@ describe('API3 CREATE', function() { self.validDoc = { date: (new Date()).getTime(), app: testConst.TEST_APP, - device: testConst.TEST_DEVICE, + device: testConst.TEST_DEVICE + ' API3 CREATE', eventType: 'Correction Bolus', insulin: 0.3 }; diff --git a/tests/api3.generic.workflow.test.js b/tests/api3.generic.workflow.test.js index f94238d6747..7cfbc53f618 100644 --- a/tests/api3.generic.workflow.test.js +++ b/tests/api3.generic.workflow.test.js @@ -19,7 +19,7 @@ describe('Generic REST API3', function() { insulin: 1, date: (new Date()).getTime(), app: testConst.TEST_APP, - device: testConst.TEST_DEVICE + device: testConst.TEST_DEVICE + ' Generic REST API3' }; self.identifier = opTools.calculateIdentifier(self.docOriginal); self.docOriginal.identifier = self.identifier; diff --git a/tests/api3.patch.test.js b/tests/api3.patch.test.js index 3967e53cf12..36dccc94bfa 100644 --- a/tests/api3.patch.test.js +++ b/tests/api3.patch.test.js @@ -15,7 +15,7 @@ describe('API3 PATCH', function() { date: (new Date()).getTime(), utcOffset: -180, app: testConst.TEST_APP, - device: testConst.TEST_DEVICE, + device: testConst.TEST_DEVICE + ' API3 PATCH', eventType: 'Correction Bolus', insulin: 0.3 }; diff --git a/tests/api3.read.test.js b/tests/api3.read.test.js index de29b26d000..d9f73ebf13a 100644 --- a/tests/api3.read.test.js +++ b/tests/api3.read.test.js @@ -15,7 +15,7 @@ describe('API3 READ', function() { self.validDoc = { date: (new Date()).getTime(), app: testConst.TEST_APP, - device: testConst.TEST_DEVICE, + device: testConst.TEST_DEVICE + ' API3 READ', uploaderBattery: 58 }; self.validDoc.identifier = opTools.calculateIdentifier(self.validDoc); diff --git a/tests/api3.update.test.js b/tests/api3.update.test.js index 35995d27236..481827b05d6 100644 --- a/tests/api3.update.test.js +++ b/tests/api3.update.test.js @@ -17,6 +17,7 @@ describe('API3 UPDATE', function() { date: (new Date()).getTime(), utcOffset: -180, app: testConst.TEST_APP, + device: testConst.TEST_DEVICE + ' API3 UPDATE', eventType: 'Correction Bolus', insulin: 0.3 };