Exract timeline to sepearte page
This commit is contained in:
parent
997eeff663
commit
a286bbf1fa
5 changed files with 204 additions and 144 deletions
|
@ -425,6 +425,7 @@ class HugveyState(object):
|
||||||
self.status = status
|
self.status = status
|
||||||
lightOn = status in [self.STATE_AWAITING, self.STATE_PAUSE, self.STATE_GONE]
|
lightOn = status in [self.STATE_AWAITING, self.STATE_PAUSE, self.STATE_GONE]
|
||||||
self.setLightStatus(lightOn)
|
self.setLightStatus(lightOn)
|
||||||
|
self.eventLogger.info(f"status: {self.status}")
|
||||||
|
|
||||||
|
|
||||||
def config(self, hostname, ip):
|
def config(self, hostname, ip):
|
||||||
|
@ -605,6 +606,7 @@ class HugveyState(object):
|
||||||
|
|
||||||
def shutdown(self, definitive = False):
|
def shutdown(self, definitive = False):
|
||||||
self.logger.info(f"Start shutdown sequence {definitive}")
|
self.logger.info(f"Start shutdown sequence {definitive}")
|
||||||
|
self.eventLogger.critical(f"error: shutting down")
|
||||||
if self.streamer:
|
if self.streamer:
|
||||||
self.streamer.stop()
|
self.streamer.stop()
|
||||||
if self.story:
|
if self.story:
|
||||||
|
|
|
@ -7,13 +7,12 @@
|
||||||
<script src="/js/moment.min.js"></script>
|
<script src="/js/moment.min.js"></script>
|
||||||
<script src="/js/d3.v5.min.js"></script>
|
<script src="/js/d3.v5.min.js"></script>
|
||||||
<script src="/js/crel.min.js"></script>
|
<script src="/js/crel.min.js"></script>
|
||||||
<script src="/js/vis.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="/css/vis.min.css"></link>
|
|
||||||
<link rel="stylesheet" href="/css/styles.css"></link>
|
<link rel="stylesheet" href="/css/styles.css"></link>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class='showTimeline dark'>
|
<body class='dark'>
|
||||||
<div id="interface" class='showStatus'>
|
<div id="interface" class='showStatus'>
|
||||||
<div id='status'>
|
<div id='status'>
|
||||||
<div id='overview'>
|
<div id='overview'>
|
||||||
|
@ -29,7 +28,6 @@
|
||||||
:class="['flag-icon', lang.code]"></span> {{lang.code}}</li>
|
:class="['flag-icon', lang.code]"></span> {{lang.code}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class='btn' id='toggleTimeline'>Timeline</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class='hugvey' v-for="hv in hugveys"
|
<div class='hugvey' v-for="hv in hugveys"
|
||||||
:class="[{'hugvey--on': hv.status != 'off'},'hugvey--' + hv.status]"
|
:class="[{'hugvey--on': hv.status != 'off'},'hugvey--' + hv.status]"
|
||||||
|
@ -99,7 +97,6 @@
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div id='timeline'></div>
|
|
||||||
</div>
|
</div>
|
||||||
<script type='application/javascript' src="/js/hugvey_console.js"></script>
|
<script type='application/javascript' src="/js/hugvey_console.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -53,7 +53,6 @@ class Panopticon {
|
||||||
return panopticon.change_language(hv.id, lang_code);
|
return panopticon.change_language(hv.id, lang_code);
|
||||||
},
|
},
|
||||||
showHugvey: function(hv) {
|
showHugvey: function(hv) {
|
||||||
document.body.classList.remove('showTimeline');
|
|
||||||
panopticon.hugveys.selectedId = hv.language ? hv.id : null;
|
panopticon.hugveys.selectedId = hv.language ? hv.id : null;
|
||||||
panopticon.updateSelectedHugvey();
|
panopticon.updateSelectedHugvey();
|
||||||
}
|
}
|
||||||
|
@ -65,16 +64,7 @@ class Panopticon {
|
||||||
this.socket = new ReconnectingWebSocket( "ws://localhost:8888/ws", null, { debug: false, reconnectInterval: 3000 } );
|
this.socket = new ReconnectingWebSocket( "ws://localhost:8888/ws", null, { debug: false, reconnectInterval: 3000 } );
|
||||||
this.graph = new Graph();
|
this.graph = new Graph();
|
||||||
|
|
||||||
this.eventDataSet = new vis.DataSet([
|
|
||||||
{content: '.', start: new Date(), type: 'point', group: 1}
|
|
||||||
]);
|
|
||||||
this.timeline = false;
|
|
||||||
|
|
||||||
document.getElementById('toggleTimeline').addEventListener('click', function(){
|
|
||||||
document.body.classList.toggle('showTimeline');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
this.socket.addEventListener( 'open', ( e ) => {
|
this.socket.addEventListener( 'open', ( e ) => {
|
||||||
this.send( { action: 'init' } );
|
this.send( { action: 'init' } );
|
||||||
} );
|
} );
|
||||||
|
@ -110,112 +100,9 @@ class Panopticon {
|
||||||
if(this.hugveys.selectedId) {
|
if(this.hugveys.selectedId) {
|
||||||
this.updateSelectedHugvey();
|
this.updateSelectedHugvey();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.timeline) {
|
|
||||||
console.log('init timeline');
|
|
||||||
let groups = [];
|
|
||||||
for(let hid of msg['hugvey_ids']) {
|
|
||||||
groups.push({id: parseInt(hid), content: 'Hugvey #'+hid});
|
|
||||||
this.eventDataSet.add({content: 'initiate', start: new Date(), type: 'point', group: parseInt(hid)})
|
|
||||||
}
|
|
||||||
let dataGroups = new vis.DataSet(groups);
|
|
||||||
let options = {
|
|
||||||
// 'rollingMode': {'follow': true, 'offset': .8 }
|
|
||||||
};
|
|
||||||
console.log('groups', dataGroups, groups, options);
|
|
||||||
|
|
||||||
this.timeline = new vis.Timeline(document.getElementById('timeline'), this.eventDataSet, dataGroups, options);
|
|
||||||
//
|
|
||||||
|
|
||||||
let startDate = new Date();
|
|
||||||
startDate.setMinutes(startDate.getMinutes()-1);
|
|
||||||
let endDate = new Date();
|
|
||||||
endDate.setMinutes(endDate.getMinutes()+20);
|
|
||||||
setTimeout(function(){
|
|
||||||
panopticon.timeline.setWindow(startDate, endDate);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
console.log(startDate, endDate);
|
|
||||||
this.timelineInterval = setInterval(function(){
|
|
||||||
// skip movement if not visible
|
|
||||||
if(!document.body.classList.contains('showTimeline'))
|
|
||||||
return;
|
|
||||||
panopticon.timeline.moveTo(new Date());
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'log':
|
case 'log':
|
||||||
let hv_id = parseInt(msg['id']);
|
|
||||||
if(this.timeline) {
|
|
||||||
// {'action': 'log', 'id':hugvey_id, 'type': items[0], 'info', 'args'}
|
|
||||||
let d, parts;
|
|
||||||
switch(msg['type']){
|
|
||||||
case 'message':
|
|
||||||
// info: en-njsm8bwbd "Are you up for a conversation?"
|
|
||||||
parts = msg['info'].trim().split(' ');
|
|
||||||
let msgId = parts.shift();
|
|
||||||
let msgUuid = parts.shift();
|
|
||||||
let msgEvent = parts.shift();
|
|
||||||
let msgContent = parts.join(' ');
|
|
||||||
let mId = 'm-'+msgUuid+'-'+hv_id;
|
|
||||||
d = this.eventDataSet.get(mId);
|
|
||||||
console.log(msgId, msgEvent, msgContent);
|
|
||||||
if(d !== null && msgEvent == 'done'){
|
|
||||||
d['end'] = new Date();
|
|
||||||
this.eventDataSet.update(d);
|
|
||||||
console.log('update', d);
|
|
||||||
} else {
|
|
||||||
this.eventDataSet.add({id: mId, content: msgContent, title: `${msgContent} (${msgId})`, start: new Date(), group: hv_id, 'className': 'message'});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'speaking':
|
|
||||||
// start/content/end
|
|
||||||
parts = msg['info'].trim().split(' ');
|
|
||||||
let info = parts.shift();
|
|
||||||
let id = parts.shift();
|
|
||||||
let content = parts.join(' ');
|
|
||||||
let scId = 'sc-'+id+'-'+hv_id;
|
|
||||||
|
|
||||||
if(info.startsWith('start')){
|
|
||||||
this.eventDataSet.add({content: info, start: new Date(), type: 'point', group: hv_id, 'className': 'speech'});
|
|
||||||
}
|
|
||||||
if(info.startsWith('content')){
|
|
||||||
d = this.eventDataSet.get(scId);
|
|
||||||
if(d !== null){
|
|
||||||
console.log('alter');
|
|
||||||
d['content'] = content;
|
|
||||||
d['end']= new Date();
|
|
||||||
d['title'] = content;
|
|
||||||
this.eventDataSet.update(d);
|
|
||||||
} else {
|
|
||||||
console.log('add');
|
|
||||||
this.eventDataSet.add({id: scId, content: content, title: content, start: new Date(), group: hv_id, 'className': 'speech'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(info.startsWith('end')){
|
|
||||||
d = this.eventDataSet.get(scId);
|
|
||||||
if(d !== null){
|
|
||||||
d['end'] = new Date();
|
|
||||||
this.eventDataSet.update(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'story':
|
|
||||||
// 'info': 'start'/'finished'
|
|
||||||
this.eventDataSet.add({content: msg['type'] +': ' + msg['info'] + ': ' + msg['args'], start: new Date(), type: 'point', group: hv_id, 'className': 'story'});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'condition':
|
|
||||||
case 'direction':
|
|
||||||
// don't draw these :-0
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.eventDataSet.add({content: msg['type'] +': ' + msg['info'] + ': ' + msg['args'], start: new Date(), type: 'point', group: hv_id});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
@ -224,11 +111,6 @@ class Panopticon {
|
||||||
updateSelectedHugvey() {
|
updateSelectedHugvey() {
|
||||||
let hv = null;
|
let hv = null;
|
||||||
|
|
||||||
if(document.body.classList.contains('showTimeline')) {
|
|
||||||
// skip at all if no hugvey is visbile anyway due to the timeline
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.hugveys.selectedId) {
|
if(this.hugveys.selectedId) {
|
||||||
hv = this.getHugvey(this.hugveys.selectedId);
|
hv = this.getHugvey(this.hugveys.selectedId);
|
||||||
if(hv.language && this.graph.language_code != hv.language) {
|
if(hv.language && this.graph.language_code != hv.language) {
|
||||||
|
@ -2055,24 +1937,4 @@ class Graph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
//class Timeline {
|
//
|
||||||
// constructor(el, hugvey_ids) {
|
|
||||||
// this.el = el;
|
|
||||||
// this.logbook = []
|
|
||||||
// this.hugvey_ids = hugvey_ids;
|
|
||||||
//
|
|
||||||
// this.el.innerHTML = "";
|
|
||||||
// for(id of this.hugvey_ids) {
|
|
||||||
// this.el.appendChild(crel(
|
|
||||||
// 'div', {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// log(msg) {
|
|
||||||
//// {"action": "log", "id": "3", "type": "story", "info": " start"}
|
|
||||||
// console.log('log!', msg);
|
|
||||||
// }
|
|
||||||
//}
|
|
146
www/js/hugvey_timeline.js
Normal file
146
www/js/hugvey_timeline.js
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
var ws = new ReconnectingWebSocket( "ws://localhost:8888/ws", null, { debug: false, reconnectInterval: 3000 } );
|
||||||
|
|
||||||
|
//request close before unloading
|
||||||
|
window.addEventListener('beforeunload', function(){
|
||||||
|
ws.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.addEventListener( 'close', function( e ) {
|
||||||
|
console.log( 'Closed connection' );
|
||||||
|
} );
|
||||||
|
|
||||||
|
class Timeline{
|
||||||
|
constructor(ws, el, nr) {
|
||||||
|
this.ws = ws;
|
||||||
|
this.el = el;
|
||||||
|
this.count = nr;
|
||||||
|
this.eventDataSet = new vis.DataSet([
|
||||||
|
{content: '.', start: new Date(), type: 'point', group: 1}
|
||||||
|
]);
|
||||||
|
console.log('init timeline');
|
||||||
|
|
||||||
|
let groups = [];
|
||||||
|
for(let hid = 1; hid<=this.count; hid++) {
|
||||||
|
groups.push({id: parseInt(hid), content: 'Hugvey #'+hid});
|
||||||
|
this.eventDataSet.add({content: 'initiate', start: new Date(), type: 'point', group: parseInt(hid)})
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataGroups = new vis.DataSet(groups);
|
||||||
|
let options = {
|
||||||
|
// 'rollingMode': {'follow': true, 'offset': .8 }
|
||||||
|
};
|
||||||
|
console.log('groups', dataGroups, groups, options);
|
||||||
|
|
||||||
|
this.timeline = new vis.Timeline(this.el, this.eventDataSet, dataGroups, options);
|
||||||
|
|
||||||
|
let tl = this.timeline;
|
||||||
|
let startDate = new Date();
|
||||||
|
startDate.setMinutes(startDate.getMinutes()-1);
|
||||||
|
let endDate = new Date();
|
||||||
|
endDate.setMinutes(endDate.getMinutes()+20);
|
||||||
|
setTimeout(function(){
|
||||||
|
tl.setWindow(startDate, endDate);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
this.moveInterval = setInterval(function(){
|
||||||
|
// skip movement if not visible
|
||||||
|
tl.moveTo(new Date());
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
ws.addEventListener( 'message', this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(e) {
|
||||||
|
console.log('handle', e, this);
|
||||||
|
if(e.type == 'message') {
|
||||||
|
this.wsOnMessage(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wsOnMessage(e) {
|
||||||
|
let msg = JSON.parse( e.data );
|
||||||
|
|
||||||
|
if ( typeof msg['action'] === 'undefined' ) {
|
||||||
|
console.error( "not a valid message: " + e.data );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(msg['action'] != 'log') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(msg, this);
|
||||||
|
|
||||||
|
let hv_id = parseInt(msg['id']);
|
||||||
|
// {'action': 'log', 'id':hugvey_id, 'type': items[0], 'info', 'args'}
|
||||||
|
let d, parts;
|
||||||
|
switch(msg['type']){
|
||||||
|
case 'message':
|
||||||
|
// info: en-njsm8bwbd "Are you up for a conversation?"
|
||||||
|
parts = msg['info'].trim().split(' ');
|
||||||
|
let msgId = parts.shift();
|
||||||
|
let msgUuid = parts.shift();
|
||||||
|
let msgEvent = parts.shift();
|
||||||
|
let msgContent = parts.join(' ');
|
||||||
|
let mId = 'm-'+msgUuid+'-'+hv_id;
|
||||||
|
d = this.eventDataSet.get(mId);
|
||||||
|
console.log(msgId, msgEvent, msgContent);
|
||||||
|
if(d !== null && msgEvent == 'done'){
|
||||||
|
d['end'] = new Date();
|
||||||
|
this.eventDataSet.update(d);
|
||||||
|
console.log('update', d);
|
||||||
|
} else {
|
||||||
|
this.eventDataSet.add({id: mId, content: msgContent, title: `${msgContent} (${msgId})`, start: new Date(), group: hv_id, 'className': 'message'});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'speaking':
|
||||||
|
// start/content/end
|
||||||
|
parts = msg['info'].trim().split(' ');
|
||||||
|
let info = parts.shift();
|
||||||
|
let id = parts.shift();
|
||||||
|
let content = parts.join(' ');
|
||||||
|
let scId = 'sc-'+id+'-'+hv_id;
|
||||||
|
|
||||||
|
if(info.startsWith('start')){
|
||||||
|
this.eventDataSet.add({content: info, start: new Date(), type: 'point', group: hv_id, 'className': 'speech'});
|
||||||
|
}
|
||||||
|
if(info.startsWith('content')){
|
||||||
|
d = this.eventDataSet.get(scId);
|
||||||
|
if(d !== null){
|
||||||
|
console.log('alter');
|
||||||
|
d['content'] = content;
|
||||||
|
d['end']= new Date();
|
||||||
|
d['title'] = content;
|
||||||
|
this.eventDataSet.update(d);
|
||||||
|
} else {
|
||||||
|
console.log('add');
|
||||||
|
this.eventDataSet.add({id: scId, content: content, title: content, start: new Date(), group: hv_id, 'className': 'speech'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(info.startsWith('end')){
|
||||||
|
d = this.eventDataSet.get(scId);
|
||||||
|
if(d !== null){
|
||||||
|
d['end'] = new Date();
|
||||||
|
this.eventDataSet.update(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'story':
|
||||||
|
// 'info': 'start'/'finished'
|
||||||
|
this.eventDataSet.add({content: msg['type'] +': ' + msg['info'] + ': ' + msg['args'], start: new Date(), type: 'point', group: hv_id, 'className': 'story'});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'condition':
|
||||||
|
case 'direction':
|
||||||
|
// don't draw these :-0
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.eventDataSet.add({content: msg['type'] +': ' + msg['info'] + ': ' + msg['args'], start: new Date(), type: 'point', group: hv_id});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tl = new Timeline(ws, document.getElementById('line'), 25);
|
53
www/timeline.html
Normal file
53
www/timeline.html
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Pillow Talk - Timeline</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/vis.min.css"></link>
|
||||||
|
<script src="/js/reconnecting-websocket.js"></script>
|
||||||
|
<script src="/js/vis.min.js"></script>
|
||||||
|
<style type="text/css">
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
background: #333;
|
||||||
|
color:#999;
|
||||||
|
font-family:sans-serif;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
#line{
|
||||||
|
width: 180%;
|
||||||
|
}
|
||||||
|
.vis-item{
|
||||||
|
color:#999;
|
||||||
|
}
|
||||||
|
.vis-item.message {
|
||||||
|
background: lightgray;
|
||||||
|
color:#333;
|
||||||
|
}
|
||||||
|
.vis-item.message.vis-range{
|
||||||
|
background-color: darkgray;
|
||||||
|
border-color: green;
|
||||||
|
}
|
||||||
|
.vis-item.speech {
|
||||||
|
background-color: greenyellow;
|
||||||
|
border-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vis-text,.vis-labelset .vis-label {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.vis-time-axis .vis-text{
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vis-grid, .vis-foreground .vis-group{
|
||||||
|
border-color:#666 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body id='timeline'>
|
||||||
|
<div id='line'></div>
|
||||||
|
|
||||||
|
<script type='application/javascript' src="/js/hugvey_timeline.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue