From bba8b0a69ce1ac5c5d53d5ba29ff21b5011470d7 Mon Sep 17 00:00:00 2001 From: Benjamin Drieu Date: Mon, 6 Dec 2021 11:34:55 +0100 Subject: [PATCH] Implement natural date + fixes --- extension.js | 40 +++++---- prefs.js | 4 +- ...ll.extensions.monito@drieu.org.gschema.xml | 4 +- servers/genericserver.js | 82 +++++++++++++++++++ servers/icinga.js | 4 + servers/icinga2.js | 2 +- servers/icinga2api.js | 22 ++++- 7 files changed, 136 insertions(+), 22 deletions(-) diff --git a/extension.js b/extension.js index 646303b..70ae376 100644 --- a/extension.js +++ b/extension.js @@ -62,8 +62,8 @@ const column_definitions = { has_been_acknowledged: { label: _('Ack'), width: 50, expand: false, align: Clutter.ActorAlign.CENTER, style: 'font-weight:bold;' }, last_check: { label: _('Last check'), width: 200, expand: false, type: 'date' }, attempts: { label: _('Attempts'), width: 50, expand: false, }, - status_information: { label: _('Information'), width: 600, expand: true, }, - actions: { label: 'Actions', width: 100, expand: true, special: 'actions' }, + status_information: { label: _('Information'), width: 600, expand: false, }, + actions: { label: 'Actions', width: 120, expand: false, special: 'actions' }, }; @@ -144,16 +144,19 @@ class Indicator extends PanelMenu.Button { _iconBin.child = _icon; this._buttonMenu.actor.add_actor(_iconBin); - this._mainLabel = new St.Label ( { style_class: 'monito-title', text: 'Monito Checker', x_expand: true } ); + this._mainLabel = new St.Label ( { style_class: 'monito-title', text: _('Monito Checker'), x_expand: true } ); this._buttonMenu.actor.add_actor(this._mainLabel); - this._prefsButton = this._createButton ( 'preferences-system-symbolic', 'Preferences', this._onPreferencesActivate ); + this._prefsButton = this._createButton ( 'preferences-system-symbolic', _('Preferences'), this._onPreferencesActivate ); this._buttonMenu.actor.add_child (this._prefsButton); - this._updateButton = this._createButton ( 'emblem-synchronizing-symbolic', 'Reload', this.updateStatus ); // Implement this - this._buttonMenu.actor.add_child (this._updateButton ); + if ( this.serverLogic && this.serverLogic.canRecheck ) + { + this._recheckButton = this._createButton ( 'system-run-symbolic', _('Recheck all'), this.recheckAll ); + this._buttonMenu.actor.add_child (this._recheckButton ); + } - this._reloadButton = this._createButton ( 'view-refresh-symbolic', 'Reload', this.updateStatus ); + this._reloadButton = this._createButton ( 'view-refresh-symbolic', _('Reload view'), this.updateStatus ); this._buttonMenu.actor.add_child (this._reloadButton ); let _intermediate = new PopupMenu.PopupBaseMenuItem ( { @@ -194,6 +197,12 @@ class Indicator extends PanelMenu.Button { 'UNKNOWN': 0 }; } + recheckAll ( ) + { + this.spinChildOf ( this._recheckButton ); + this.serverLogic.recheckAll ( this._recheckButton ); + } + updateStatus ( ) { this.spinChildOf ( this._reloadButton ); @@ -262,7 +271,7 @@ class Indicator extends PanelMenu.Button { _iconBin.child = this.sortIcons [ colName ]; let _button = new St.Button ( { - x_align: Clutter.ActorAlign.START, + x_align: Clutter.ActorAlign.FILL, y_align: Clutter.ActorAlign.CENTER, width: col.width, reactive: true, @@ -294,24 +303,28 @@ class Indicator extends PanelMenu.Button { if ( ! col [ 'special' ] ) _child = new St.Label ( { style_class: 'monito-label', + width: col.width, text: text.toString(), x_align: ( col.align ? col.align : Clutter.ActorAlign.START ), style: ( col.style ? col.style : '' ) } ); else if ( col.special == 'actions' && this.serverLogic.canRecheck ) { - _child = new St.BoxLayout ( { x_expand: false, - vertical: false } ); + _child = new St.BoxLayout ( { x_expand: true, + vertical: false, + width: col.width, } ); let _button = new St.Button ( { style_class: 'button small-button', x_expand: true, x_align: Clutter.ActorAlign.END, y_align: Clutter.ActorAlign.CENTER, + width: 24, + height: 24, can_focus: true, } ); _button.service = text; _button.connect ( 'clicked', Lang.bind ( this, this._onRecheckButtonClick ) ); _button.child = new St.Icon ( { - icon_name: 'view-refresh-symbolic', + icon_name: 'system-run-symbolic', icon_size: 16, width: 16, height: 16, @@ -320,7 +333,6 @@ class Indicator extends PanelMenu.Button { } let _bin = new St.Bin({ -// style: 'background: purple', track_hover: true, width: col.width, x_expand: col.expand, @@ -374,7 +386,7 @@ class Indicator extends PanelMenu.Button { } let headerBox = new St.BoxLayout({ - hover: true, + track_hover: true, x_expand: true }); this._box.add_child(headerBox); @@ -389,7 +401,7 @@ class Indicator extends PanelMenu.Button { this._box.add_child(scrollBox); let tableBox = new St.BoxLayout({ - hover: true, + track_hover: true, vertical: true, x_expand: true, }); diff --git a/prefs.js b/prefs.js index 9916e0f..cd4803b 100644 --- a/prefs.js +++ b/prefs.js @@ -281,8 +281,8 @@ function createPrefWidgets ( noteBook, type, isActive ) else if ( prefEntry.key == 'type' ) { [ { name: 'Icinga', value: 'Icinga server' }, - { name: 'Icinga2', value: 'Icinga2 server (using Icingaweb2)' }, - { name: 'Icinga2API', value: 'Icinga2 server (using API)' } ].forEach((item) => { + { name: 'Icinga2API', value: 'Icinga2 server (using API, prefered)' }, + { name: 'Icinga2', value: 'Icinga2 server (using Icingaweb2, limited)' } ].forEach((item) => { this.prefWidgets[prefEntry.key].insert ( -1, item.name, item.value ); } ); } diff --git a/schemas/org.gnome.shell.extensions.monito@drieu.org.gschema.xml b/schemas/org.gnome.shell.extensions.monito@drieu.org.gschema.xml index 3bcc7ff..45f9530 100644 --- a/schemas/org.gnome.shell.extensions.monito@drieu.org.gschema.xml +++ b/schemas/org.gnome.shell.extensions.monito@drieu.org.gschema.xml @@ -22,8 +22,8 @@ - - + + diff --git a/servers/genericserver.js b/servers/genericserver.js index 7620040..0b6689e 100644 --- a/servers/genericserver.js +++ b/servers/genericserver.js @@ -224,4 +224,86 @@ class GenericServer { } return 0; } + + formatDate ( date ) + { + if ( typeof date == 'string' || date instanceof String ) + { + date = Date.parse ( date ) / 1000; + log ( date ); + } + + return this.timeAgo ( new Date ( date * 1000 ) ); + } + + getFormattedDate(date, prefomattedDate = false, hideYear = false) + { + const MONTH_NAMES = [ + 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', + 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.' + ]; + + const day = date.getDate(); + const month = MONTH_NAMES[date.getMonth()]; + const year = date.getFullYear(); + const hours = date.getHours(); + let minutes = date.getMinutes(); + + if (minutes < 10) { + // Adding leading zero to minutes + minutes = _(`0${ minutes }`); + } + + if (prefomattedDate) { + // Today at 10:20 + // Yesterday at 10:20 + return _(`${ prefomattedDate } at ${ hours }:${ minutes }`); + } + + if (hideYear) { + // 10. January at 10:20 + return _(`${ month } ${ day } at ${ hours }:${ minutes }`); + } + + // 10. January 2017. at 10:20 + return _(`${ year } ${ month } ${ day }`); + } + + timeAgo ( dateParam ) + { + if (!dateParam) { + return null; + } + + const date = typeof dateParam === 'object' ? dateParam : new Date(dateParam); + const DAY_IN_MS = 86400000; // 24 * 60 * 60 * 1000 + const today = new Date(); + const yesterday = new Date(today - DAY_IN_MS); + const seconds = Math.round((today - date) / 1000); + const minutes = Math.round(seconds / 60); + const isToday = today.toDateString() === date.toDateString(); + const isYesterday = yesterday.toDateString() === date.toDateString(); + const isThisYear = today.getFullYear() === date.getFullYear(); + + + if (seconds < 5) { + return 'now'; + } else if (seconds < 60) { + return `${ seconds } seconds ago`; + } else if (seconds < 90) { + return 'a minute ago'; + } else if (minutes < 60) { + return `${ minutes } minutes ago`; + } else if (isToday) { + return this.getFormattedDate(date, 'today'); // Today at 10:20 + } else if (isYesterday) { + return this.getFormattedDate(date, 'yesterday'); // Yesterday at 10:20 + } else if (isThisYear) { + return this.getFormattedDate(date, false, true); // 10. January at 10:20 + } + + return this.getFormattedDate(date); // 10. January 2017. at 10:20 + } + + } diff --git a/servers/icinga.js b/servers/icinga.js index 3084190..8af93fd 100644 --- a/servers/icinga.js +++ b/servers/icinga.js @@ -88,7 +88,11 @@ class Icinga extends GenericServer { else { let json = JSON.parse ( _data ); + this.status = json.status; + log ( this.status ); + for ( var entry of this.status.service_status ) + entry.last_check = this.formatDate ( entry.last_check ); } this.extension.refreshUI ( this ); diff --git a/servers/icinga2.js b/servers/icinga2.js index 5d72b87..f90fed0 100644 --- a/servers/icinga2.js +++ b/servers/icinga2.js @@ -71,7 +71,7 @@ class Icinga2 extends GenericServer { attempts: entry.service_attempt, has_been_acknowledged: parseInt(entry.service_acknowledged), status_information: entry.service_output, - last_check: new Date ( parseInt(entry.service_last_state_change) * 1000 ) . toString(), + last_check: this.formatDate(parseInt(entry.service_last_state_change)), } ); } } diff --git a/servers/icinga2api.js b/servers/icinga2api.js index 60b4394..b884fa3 100644 --- a/servers/icinga2api.js +++ b/servers/icinga2api.js @@ -62,10 +62,25 @@ class Icinga2API extends GenericServer { message.request_body.flatten(); message.button = entry.button; -// log ( '> Body: ' + message.request_body.data ); + message.request_headers.append ( 'Accept', 'application/json' ); +// log ( message.request_headers ); + + this.authenticateAndSend ( message, this.handleCMDMessage ); + } + + recheckAll ( button ) + { + let message = Soup.form_request_new_from_hash ( 'POST', this.urlcgi + '/actions/reschedule-check', { } ); + + let params = '{ "type": "Service", "force": true, "pretty": true }'; + + message.request_body.truncate(); + message.request_body.append ( params ); + message.request_body.flatten(); + message.button = button; message.request_headers.append ( 'Accept', 'application/json' ); - log ( message.request_headers ); +// log ( message.request_headers ); this.authenticateAndSend ( message, this.handleCMDMessage ); } @@ -84,6 +99,7 @@ class Icinga2API extends GenericServer { for ( var entry of json.results ) { +// log ( JSON.stringify(entry) ); this.status.service_status.push ( { status: _statuses [ entry.attrs.state ], host_name: entry.attrs.host_name, @@ -91,7 +107,7 @@ class Icinga2API extends GenericServer { has_been_acknowledged: parseInt(entry.attrs.acknowledgement), attempts: '%d/%d'.format(entry.attrs.check_attempt,entry.attrs.max_check_attempts), status_information: entry.attrs.last_check_result.output, - last_check: new Date ( entry.attrs.last_state_change * 1000 ) . toString(), + last_check: this.formatDate(parseInt(entry.attrs.last_check)), } ); } }