Diversions: repeat & no_response
This commit is contained in:
parent
28fcd50d0f
commit
aa1385baed
6 changed files with 378 additions and 34 deletions
27
README.md
27
README.md
|
@ -1,15 +1,14 @@
|
||||||
# Hugvey / Pillow Talk
|
# Hugvey / Pillow Talk
|
||||||
|
|
||||||
Panpoticon
|
- Panpoticon
|
||||||
: Fancy nickname for the web interface that allows altering the story and running the individual Hugveys
|
+ Fancy nickname for the web interface that allows altering the story and running the individual Hugveys
|
||||||
Voice
|
- Voice
|
||||||
: Lyrebird voice syntehsis API wrapper. Set the oAuth token using a token generated [here](http://hugvey.rubenvandeven.com/oauth.php)
|
+ Lyrebird voice syntehsis API wrapper. Set the oAuth token using a token generated [here](http://hugvey.rubenvandeven.com/oauth.php)
|
||||||
Client
|
- Client
|
||||||
: Individual Hugveys that stream their mic output and play audiofiles trough the Panopticon. Communication with the server is done through zmq
|
+ Individual Hugveys that stream their mic output and play audiofiles trough the Panopticon. Communication with the server is done through zmq
|
||||||
: Connect with them trough hugvey1.local etc (1-25).
|
+ Connect with them trough hugvey1.local etc (1-25).
|
||||||
Central Command/server
|
- Central Command/server
|
||||||
: One server to rule them all. Start individual threads/subprocesses for the individual Hugveys. The Panopticon is started when starting the server.
|
+ One server to rule them all. Start individual threads/subprocesses for the individual Hugveys. The Panopticon is started when starting the server.
|
||||||
|
|
||||||
|
|
||||||
## Server
|
## Server
|
||||||
|
|
||||||
|
@ -28,6 +27,14 @@ To run it: `python hugvey_client.py -c client_config.yml`
|
||||||
|
|
||||||
The Panopticon uses gulp to compile SASS into CSS, and to set up browser-sync for css & js. For now, no js user facing dependencies are managed trough node/npm.
|
The Panopticon uses gulp to compile SASS into CSS, and to set up browser-sync for css & js. For now, no js user facing dependencies are managed trough node/npm.
|
||||||
|
|
||||||
|
After starting the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd www
|
||||||
|
gulp
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
create and load Python virtualenv
|
create and load Python virtualenv
|
||||||
|
|
153
hugvey/story.py
153
hugvey/story.py
|
@ -48,7 +48,7 @@ class Message(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def initFromJson(message, data, story):
|
def initFromJson(message, data, story):
|
||||||
msg = message(data['@id'], data['text'])
|
msg = message(data['@id'], data['text'])
|
||||||
msg.isStart = data['start'] if 'start' in data else False
|
msg.isStart = data['beginning'] if 'beginning' in data else False
|
||||||
msg.afterrunTime = data['afterrun'] if 'afterrun' in data else 0.
|
msg.afterrunTime = data['afterrun'] if 'afterrun' in data else 0.
|
||||||
if 'audio' in data:
|
if 'audio' in data:
|
||||||
msg.audioFile = data['audio']['file']
|
msg.audioFile = data['audio']['file']
|
||||||
|
@ -241,14 +241,24 @@ class Condition(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if 'onlyIfNoReply' in self.vars and self.vars['onlyIfNoReply']:
|
if 'onlyIfNoReply' in self.vars and self.vars['onlyIfNoReply']:
|
||||||
if story.currentReply and story.currentReply.hasUtterances():
|
if story.currentReply and story.currentReply is not None and story.currentReply.hasUtterances():
|
||||||
logger.log(LOG_BS, f'Only if no reply has text! {story.currentReply.getText()}')
|
logger.log(LOG_BS, f'Only if no reply has text! {story.currentReply.getText()}')
|
||||||
# 'onlyIfNoReply': only use this timeout if participants doesn't speak.
|
# 'onlyIfNoReply': only use this timeout if participants doesn't speak.
|
||||||
return False
|
return False
|
||||||
# else:
|
# else:
|
||||||
# logger.debug('Only if no reply has no text yet!')
|
# logger.debug('Only if no reply has no text yet!')
|
||||||
|
|
||||||
return now - story.lastMsgFinishTime >= float(self.vars['seconds'])
|
hasMetTimeout = now - story.lastMsgFinishTime >= float(self.vars['seconds'])
|
||||||
|
if not hasMetTimeout:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# update stats:
|
||||||
|
story.stats['timeouts'] +=1
|
||||||
|
if story.currentReply is None or not story.currentReply.hasUtterances():
|
||||||
|
story.stats['silentTimeouts'] +=1
|
||||||
|
story.stats['consecutiveSilentTimeouts'] += 1
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def _hasMetReplyContains(self, story) -> bool:
|
def _hasMetReplyContains(self, story) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -327,6 +337,7 @@ class Direction(object):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.msgFrom = msgFrom
|
self.msgFrom = msgFrom
|
||||||
self.msgTo = msgTo
|
self.msgTo = msgTo
|
||||||
|
#: :type self.conditions: list(Condition)
|
||||||
self.conditions = []
|
self.conditions = []
|
||||||
self.conditionMet = None
|
self.conditionMet = None
|
||||||
|
|
||||||
|
@ -359,29 +370,94 @@ class Diversion(object):
|
||||||
An Diversion. Used to catch events outside of story flow.
|
An Diversion. Used to catch events outside of story flow.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, id):
|
def __init__(self, id, type: str, params: dict):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.conditions = []
|
self.params = params
|
||||||
|
self.finaliseMethod = None
|
||||||
|
if type == 'no_response':
|
||||||
|
self.method = self._divergeIfNoResponse
|
||||||
|
self.finaliseMethod = self._returnAfterNoResponse
|
||||||
|
self.counter = 0
|
||||||
|
if type == 'repeat':
|
||||||
|
self.method = self._divergeIfRepeatRequest
|
||||||
|
self.regex = re.compile(self.params['regex'])
|
||||||
|
|
||||||
def addCondition(self, condition: Condition):
|
if not self.method:
|
||||||
self.conditions.append(condition)
|
raise Exception("No valid type given for diversion")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def initFromJson(diversionClass, data, story):
|
def initFromJson(diversionClass, data, story):
|
||||||
diversion = diversionClass(data['@id'])
|
diversion = diversionClass(data['@id'], data['type'], data['params'])
|
||||||
if 'conditions' in data:
|
|
||||||
for conditionId in data['conditions']:
|
|
||||||
c = story.get(conditionId)
|
|
||||||
diversion.addCondition(c)
|
|
||||||
return diversion
|
return diversion
|
||||||
|
|
||||||
|
|
||||||
def getLogSummary(self):
|
def getLogSummary(self):
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
# 'time': self.replyTime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def divergeIfNeeded(self, story, msgFrom, msgTo):
|
||||||
|
"""
|
||||||
|
Validate if condition is met for the current story state
|
||||||
|
Returns True when diverging
|
||||||
|
"""
|
||||||
|
return await self.method(story, msgFrom, msgTo)
|
||||||
|
|
||||||
|
async def finalise(self, story):
|
||||||
|
""""
|
||||||
|
Only used if the Diversion sets the story.currentDiversion
|
||||||
|
"""
|
||||||
|
if not self.finaliseMethod:
|
||||||
|
return False
|
||||||
|
await self.finaliseMethod(story)
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def _divergeIfNoResponse(self, story, msgFrom, msgTo):
|
||||||
|
"""
|
||||||
|
Participant doesn't speak for x consecutive replies (has had timeout)
|
||||||
|
"""
|
||||||
|
':type story: Story'
|
||||||
|
if story.currentDiversion:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if story.stats['diversions']['no_response'] + 1 == self.params['timesOccured'] and story.stats['consecutiveSilentTimeouts'] >= int(self.params['consecutiveSilences']):
|
||||||
|
story.stats['diversions']['no_response'] += 1
|
||||||
|
msg = story.get(self.params['msgId'])
|
||||||
|
if msg is None:
|
||||||
|
logger.critical(f"Not a valid message id for diversion: {self.params['msgId']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"Diverge: No response {self.id} {story.stats}")
|
||||||
|
self.returnMessage = msgTo
|
||||||
|
await story.setCurrentMessage(msg)
|
||||||
|
story.currentDiversion = self
|
||||||
|
return True
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
async def _returnAfterNoResponse(self, story):
|
||||||
|
logger.info(f"Finalise diversion: {self.id}")
|
||||||
|
story.stats['consecutiveSilentTimeouts'] = 0 # reset counter after diverging
|
||||||
|
if self.params['returnAfterStrand']:
|
||||||
|
await story.setCurrentMessage(self.returnMessage)
|
||||||
|
|
||||||
|
async def _divergeIfRepeatRequest(self, story, msgFrom, msgTo):
|
||||||
|
"""
|
||||||
|
Participant asks if message can be repeated.
|
||||||
|
"""
|
||||||
|
if story.currentReply is None or story.currentReply.isSpeaking():
|
||||||
|
return
|
||||||
|
|
||||||
|
r = self.regex.search(story.currentReply.getText())
|
||||||
|
if r is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"Diverge: request repeat {self.id}")
|
||||||
|
story.stats['diversions']['repeat'] += 1
|
||||||
|
await story.setCurrentMessage(msgFrom)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
storyClasses = {
|
storyClasses = {
|
||||||
'Msg': Message,
|
'Msg': Message,
|
||||||
|
@ -450,9 +526,11 @@ class Story(object):
|
||||||
self.commands = [] # queue of commands to send
|
self.commands = [] # queue of commands to send
|
||||||
self.log = [] # all nodes/elements that are triggered
|
self.log = [] # all nodes/elements that are triggered
|
||||||
self.currentMessage = None
|
self.currentMessage = None
|
||||||
|
self.currentDiversion = None
|
||||||
self.currentReply = None
|
self.currentReply = None
|
||||||
self.timer = Stopwatch()
|
self.timer = Stopwatch()
|
||||||
self.isRunning = False
|
self.isRunning = False
|
||||||
|
self.diversions = []
|
||||||
self.variables = {}
|
self.variables = {}
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
|
@ -511,6 +589,8 @@ class Story(object):
|
||||||
logger.debug(self.elements)
|
logger.debug(self.elements)
|
||||||
logger.debug(self.directionsPerMsg)
|
logger.debug(self.directionsPerMsg)
|
||||||
|
|
||||||
|
self.diversions = [el for el in self.elements.values() if type(el) == Diversion]
|
||||||
|
|
||||||
if currentId:
|
if currentId:
|
||||||
self.currentMessage = self.get(currentId)
|
self.currentMessage = self.get(currentId)
|
||||||
if self.currentMessage:
|
if self.currentMessage:
|
||||||
|
@ -529,6 +609,7 @@ class Story(object):
|
||||||
for var in msg.variables:
|
for var in msg.variables:
|
||||||
self.registerVariable(var, msg)
|
self.registerVariable(var, msg)
|
||||||
|
|
||||||
|
|
||||||
logger.info(f'has variables: {self.variables}')
|
logger.info(f'has variables: {self.variables}')
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
@ -536,6 +617,7 @@ class Story(object):
|
||||||
# self.startTime = time.time()
|
# self.startTime = time.time()
|
||||||
# currently active message, determines active listeners etc.
|
# currently active message, determines active listeners etc.
|
||||||
self.currentMessage = None
|
self.currentMessage = None
|
||||||
|
self.currentDiversion = None
|
||||||
self.lastMsgTime = None
|
self.lastMsgTime = None
|
||||||
self.lastSpeechStartTime = None
|
self.lastSpeechStartTime = None
|
||||||
self.lastSpeechEndTime = None
|
self.lastSpeechEndTime = None
|
||||||
|
@ -549,6 +631,12 @@ class Story(object):
|
||||||
|
|
||||||
self.stats = {
|
self.stats = {
|
||||||
'timeouts': 0,
|
'timeouts': 0,
|
||||||
|
'silentTimeouts': 0,
|
||||||
|
'consecutiveSilentTimeouts': 0,
|
||||||
|
'diversions': {
|
||||||
|
'no_response': 0,
|
||||||
|
'repeat': 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for msg in self.getMessages():
|
for msg in self.getMessages():
|
||||||
|
@ -613,11 +701,19 @@ class Story(object):
|
||||||
# self.hugvey.google.resume()
|
# self.hugvey.google.resume()
|
||||||
|
|
||||||
if self.currentMessage.id not in self.directionsPerMsg:
|
if self.currentMessage.id not in self.directionsPerMsg:
|
||||||
logger.info("THE END!")
|
if self.currentDiversion is not None:
|
||||||
self.stop()
|
logger.info("end of diversion")
|
||||||
return
|
await self.currentDiversion.finalise(self)
|
||||||
|
self.currentDiversion = None
|
||||||
|
else:
|
||||||
|
logger.info("THE END!")
|
||||||
|
self.stop()
|
||||||
|
return
|
||||||
|
|
||||||
if e['event'] == 'speech':
|
if e['event'] == 'speech':
|
||||||
|
# participants speaks, reset counter
|
||||||
|
self.stats['consecutiveSilentTimeouts'] = 0
|
||||||
|
|
||||||
# message is still playing:
|
# message is still playing:
|
||||||
if self.currentMessage and not self.lastMsgFinishTime and self.previousReply and self.previousReply.forMessage.interruptCount < 4:
|
if self.currentMessage and not self.lastMsgFinishTime and self.previousReply and self.previousReply.forMessage.interruptCount < 4:
|
||||||
timeDiff = self.timer.getElapsed() - self.previousReply.forMessage.getFinishedTime()
|
timeDiff = self.timer.getElapsed() - self.previousReply.forMessage.getFinishedTime()
|
||||||
|
@ -626,7 +722,7 @@ class Story(object):
|
||||||
logger.warn("Interrupt message, replay {}".format(self.previousReply.forMessage.id))
|
logger.warn("Interrupt message, replay {}".format(self.previousReply.forMessage.id))
|
||||||
self.currentReply = self.previousReply
|
self.currentReply = self.previousReply
|
||||||
self.previousReply.forMessage.interruptCount += 1
|
self.previousReply.forMessage.interruptCount += 1
|
||||||
self.currentMessage = await self.setCurrentMessage(self.previousReply.forMessage)
|
self.currentMessage = await self.setCurrentMessage(self.previousReply.forMessage, self.previousReply)
|
||||||
|
|
||||||
# log if somebody starts speaking
|
# log if somebody starts speaking
|
||||||
# TODO: implement interrupt
|
# TODO: implement interrupt
|
||||||
|
@ -641,6 +737,7 @@ class Story(object):
|
||||||
|
|
||||||
|
|
||||||
async def _processDirections(self, directions):
|
async def _processDirections(self, directions):
|
||||||
|
':type directions: list(Direction)'
|
||||||
for direction in directions:
|
for direction in directions:
|
||||||
for condition in direction.conditions:
|
for condition in direction.conditions:
|
||||||
if condition.isMet(self):
|
if condition.isMet(self):
|
||||||
|
@ -650,9 +747,22 @@ class Story(object):
|
||||||
self.addToLog(condition)
|
self.addToLog(condition)
|
||||||
self.addToLog(direction)
|
self.addToLog(direction)
|
||||||
self.currentMessage.setFinished(self.timer.getElapsed())
|
self.currentMessage.setFinished(self.timer.getElapsed())
|
||||||
await self.setCurrentMessage(direction.msgTo)
|
isDiverging = await self._processDiversions(direction.msgFrom, direction.msgTo)
|
||||||
|
if not isDiverging:
|
||||||
|
await self.setCurrentMessage(direction.msgTo)
|
||||||
return direction
|
return direction
|
||||||
|
|
||||||
|
async def _processDiversions(self, msgFrom, msgTo) -> bool:
|
||||||
|
"""
|
||||||
|
Process the diversions on stack. If diverging, return True, else False
|
||||||
|
"""
|
||||||
|
diverge = False
|
||||||
|
for diversion in self.diversions:
|
||||||
|
d = await diversion.divergeIfNeeded(self, msgFrom, msgTo)
|
||||||
|
if d:
|
||||||
|
diverge = True
|
||||||
|
return diverge
|
||||||
|
|
||||||
def addToLog(self, node):
|
def addToLog(self, node):
|
||||||
self.log.append((node, self.timer.getElapsed()))
|
self.log.append((node, self.timer.getElapsed()))
|
||||||
|
|
||||||
|
@ -689,7 +799,10 @@ class Story(object):
|
||||||
|
|
||||||
logger.info("Stop renderer")
|
logger.info("Stop renderer")
|
||||||
|
|
||||||
async def setCurrentMessage(self, message):
|
async def setCurrentMessage(self, message, useReply = None):
|
||||||
|
"""
|
||||||
|
Use Reply allows to pre-initiate a reply to use with the message. This is used eg. when doing an interruption.
|
||||||
|
"""
|
||||||
if self.currentMessage and not self.lastMsgFinishTime:
|
if self.currentMessage and not self.lastMsgFinishTime:
|
||||||
logger.info("Interrupt playback {}".format(self.currentMessage.id))
|
logger.info("Interrupt playback {}".format(self.currentMessage.id))
|
||||||
# message is playing
|
# message is playing
|
||||||
|
@ -704,7 +817,7 @@ class Story(object):
|
||||||
|
|
||||||
# if not reset:
|
# if not reset:
|
||||||
self.previousReply = self.currentReply # we can use this for interrptions
|
self.previousReply = self.currentReply # we can use this for interrptions
|
||||||
self.currentReply = self.currentMessage.reply
|
self.currentReply = useReply #self.currentMessage.reply
|
||||||
# else:
|
# else:
|
||||||
# # if we press 'save & play', it should not remember it's last reply to that msg
|
# # if we press 'save & play', it should not remember it's last reply to that msg
|
||||||
# self.previousReply = self.currentReply # we can use this for interrptions
|
# self.previousReply = self.currentReply # we can use this for interrptions
|
||||||
|
|
|
@ -187,10 +187,14 @@ img.icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
right: 0px; }
|
right: 0px; }
|
||||||
|
#story #msg #diversions .diversion {
|
||||||
|
background: pink; }
|
||||||
#story #nodes g:hover circle,
|
#story #nodes g:hover circle,
|
||||||
#story .selectedMsg circle {
|
#story .selectedMsg circle {
|
||||||
stroke: lightgreen;
|
stroke: lightgreen;
|
||||||
stroke-width: 27; }
|
stroke-width: 27; }
|
||||||
|
#story .diversion circle {
|
||||||
|
fill: pink !important; }
|
||||||
#story .controlDown #nodes g:hover circle,
|
#story .controlDown #nodes g:hover circle,
|
||||||
#story .secondaryMsg circle {
|
#story .secondaryMsg circle {
|
||||||
stroke: lightgreen;
|
stroke: lightgreen;
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
<span id="current_lang"></span>
|
<span id="current_lang"></span>
|
||||||
<div id="btn-save" class="btn">Save</div>
|
<div id="btn-save" class="btn">Save</div>
|
||||||
<div id="btn-addMsg" class="btn">Create message</div>
|
<div id="btn-addMsg" class="btn">Create message</div>
|
||||||
|
<div id="btn-diversions" class="btn">View Diversions</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="msg"></div>
|
<div id="msg"></div>
|
||||||
<svg id='graph' viewbox="-640 -512 1280 1024"
|
<svg id='graph' viewbox="-640 -512 1280 1024"
|
||||||
|
|
|
@ -229,6 +229,7 @@ class Graph {
|
||||||
|
|
||||||
document.getElementById( 'btn-save' ).addEventListener( 'click', function( e ) { graph.saveJson(); } );
|
document.getElementById( 'btn-save' ).addEventListener( 'click', function( e ) { graph.saveJson(); } );
|
||||||
document.getElementById( 'btn-addMsg' ).addEventListener( 'click', function( e ) { graph.createMsg(); } );
|
document.getElementById( 'btn-addMsg' ).addEventListener( 'click', function( e ) { graph.createMsg(); } );
|
||||||
|
document.getElementById( 'btn-diversions' ).addEventListener( 'click', function( e ) { graph.showDiversions(); } );
|
||||||
}
|
}
|
||||||
|
|
||||||
clickMsg( msg ) {
|
clickMsg( msg ) {
|
||||||
|
@ -270,6 +271,197 @@ class Graph {
|
||||||
return `http://localhost:8888/voice?text=${encodeURIComponent(msg['text'])}&variable=${isVariable}&filename=0`;
|
return `http://localhost:8888/voice?text=${encodeURIComponent(msg['text'])}&variable=${isVariable}&filename=0`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNumericId(prefix) {
|
||||||
|
let id, i = 0;
|
||||||
|
let hasId= function(a, id) {
|
||||||
|
for(let i of a){
|
||||||
|
if(i['@id'] == id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
id = prefix + i;
|
||||||
|
i++;
|
||||||
|
} while(hasId(this.data, id))
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
createDiversion(type) {
|
||||||
|
let div = {
|
||||||
|
"@id": this.getNumericId(this.language_code.substring( 0, 2 ) + `-div-${type}#`),
|
||||||
|
'@type': 'Diversion',
|
||||||
|
'type': type,
|
||||||
|
'params': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type == 'no_response') {
|
||||||
|
div['params']['consecutiveSilences'] = 3;
|
||||||
|
div['params']['timesOccured'] = 0;
|
||||||
|
div['params']['returnAfterStrand'] = true;
|
||||||
|
div['params']['msgId'] = "";
|
||||||
|
}
|
||||||
|
else if(type == 'repeat') {
|
||||||
|
div['params']['regex'] = "can you repeat that\\?";
|
||||||
|
} else {
|
||||||
|
console.log("invalid type", type);
|
||||||
|
alert('invalid type for diversion');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.push( div );
|
||||||
|
this.updateFromData();
|
||||||
|
this.build();
|
||||||
|
|
||||||
|
this.showDiversions();
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteDiversion(div) {
|
||||||
|
this._rmNode( div );
|
||||||
|
this.showDiversions( );
|
||||||
|
}
|
||||||
|
|
||||||
|
showDiversions( ) {
|
||||||
|
let msgEl = document.getElementById( 'msg' );
|
||||||
|
msgEl.innerHTML = "";
|
||||||
|
|
||||||
|
let divsNoResponse =[], divsRepeat = [];
|
||||||
|
for(let div of this.diversions) {
|
||||||
|
if(div['type'] == 'no_response') {
|
||||||
|
let returnAttrs = {
|
||||||
|
'type': 'checkbox',
|
||||||
|
'on': {
|
||||||
|
'change': (e) => div['params']['returnAfterStrand'] = e.target.checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(div['params']['returnAfterStrand']) {
|
||||||
|
returnAttrs['checked'] = 'checked';
|
||||||
|
}
|
||||||
|
let msgOptions = [crel('option',"")];
|
||||||
|
let starts = this.messages.filter( m => m.hasOwnProperty('start') && m['start'] == true);
|
||||||
|
for(let startMsg of starts) {
|
||||||
|
let optionParams = {};
|
||||||
|
if(div['params']['msgId'] == startMsg['@id']) {
|
||||||
|
optionParams['selected'] = 'selected';
|
||||||
|
}
|
||||||
|
msgOptions.push(crel('option', optionParams , startMsg['@id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
divsNoResponse.push(crel(
|
||||||
|
'div', {
|
||||||
|
'class': 'diversion',
|
||||||
|
'on': {
|
||||||
|
'mouseover': function(e) {
|
||||||
|
if(div['params']['msgId'])
|
||||||
|
document.getElementById(div['params']['msgId']).classList.add('selectedMsg');
|
||||||
|
},
|
||||||
|
'mouseout': function(e) {
|
||||||
|
if(div['params']['msgId'])
|
||||||
|
document.getElementById(div['params']['msgId']).classList.remove('selectedMsg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
crel('h3', div['@id']),
|
||||||
|
crel(
|
||||||
|
'div', {
|
||||||
|
'class':'btn btn--delete',
|
||||||
|
'on': {
|
||||||
|
'click': (e) => this.deleteDiversion(div)
|
||||||
|
}
|
||||||
|
}, 'Delete diversion'),
|
||||||
|
crel('label', 'Consecutive Silences',
|
||||||
|
crel('input', {
|
||||||
|
'type': 'number',
|
||||||
|
'value': div['params']['consecutiveSilences'],
|
||||||
|
'on': {
|
||||||
|
'change': (e) => div['params']['consecutiveSilences'] = parseInt(e.target.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
crel('label', 'On n-th instance',
|
||||||
|
crel('input', {
|
||||||
|
'type': 'number',
|
||||||
|
'value': div['params']['timesOccured'],
|
||||||
|
'on': {
|
||||||
|
'change': (e) => div['params']['timesOccured'] = parseInt(e.target.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
crel('label', 'Return to point of departure afterwards',
|
||||||
|
crel('input', returnAttrs)
|
||||||
|
),
|
||||||
|
crel('label', 'Go to (start message)',
|
||||||
|
crel('select', {'on': {
|
||||||
|
'change': (e) => div['params']['msgId'] = e.target.value
|
||||||
|
}}, ...msgOptions)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if(div['type'] == 'repeat'){
|
||||||
|
divsRepeat.push(crel(
|
||||||
|
'div', {'class': 'diversion'},
|
||||||
|
crel('h3', div['@id']),
|
||||||
|
crel(
|
||||||
|
'div', {
|
||||||
|
'class':'btn btn--delete',
|
||||||
|
'on': {
|
||||||
|
'click': (e) => this.deleteDiversion(div)
|
||||||
|
}
|
||||||
|
}, 'Delete diversion'),
|
||||||
|
crel('label', 'Regex',
|
||||||
|
crel('input', {
|
||||||
|
'type': 'text',
|
||||||
|
'value': div['params']['regex'],
|
||||||
|
'on': {
|
||||||
|
'change': (e) => div['params']['regex'] = e.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(divsNoResponse, divsRepeat);
|
||||||
|
|
||||||
|
let divEl = crel(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
'id': 'diversions'
|
||||||
|
},
|
||||||
|
crel('h1', 'Configure Diversions'),
|
||||||
|
crel('div',
|
||||||
|
crel('h2', 'In case of No Response'),
|
||||||
|
...divsNoResponse,
|
||||||
|
crel('div',
|
||||||
|
{
|
||||||
|
'class': 'btn',
|
||||||
|
'on': {
|
||||||
|
'click': (e) => this.createDiversion('no_response')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'New case for no_response'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
crel('div',
|
||||||
|
crel('h2', 'Request repeat'),
|
||||||
|
...divsRepeat,
|
||||||
|
crel('div',
|
||||||
|
{
|
||||||
|
'class': 'btn',
|
||||||
|
'on': {
|
||||||
|
'click': (e) => this.createDiversion('repeat')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'New case for repeat'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
msgEl.appendChild(divEl);
|
||||||
|
}
|
||||||
|
|
||||||
showMsg( msg ) {
|
showMsg( msg ) {
|
||||||
let msgEl = document.getElementById( 'msg' );
|
let msgEl = document.getElementById( 'msg' );
|
||||||
msgEl.innerHTML = "";
|
msgEl.innerHTML = "";
|
||||||
|
@ -291,6 +483,17 @@ class Graph {
|
||||||
if ( msg['start'] == true ) {
|
if ( msg['start'] == true ) {
|
||||||
startAttributes['checked'] = 'checked';
|
startAttributes['checked'] = 'checked';
|
||||||
}
|
}
|
||||||
|
let beginningAttributes = {
|
||||||
|
'name': msg['@id'] + '-beginning',
|
||||||
|
// 'readonly': 'readonly',
|
||||||
|
'type': 'checkbox',
|
||||||
|
'on': {
|
||||||
|
'change': this.getEditEventListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( msg['beginning'] == true ) {
|
||||||
|
beginningAttributes['checked'] = 'checked';
|
||||||
|
}
|
||||||
|
|
||||||
let params = {};
|
let params = {};
|
||||||
if(msg.hasOwnProperty('params')) {
|
if(msg.hasOwnProperty('params')) {
|
||||||
|
@ -357,6 +560,10 @@ class Graph {
|
||||||
crel( 'span', 'Start' ),
|
crel( 'span', 'Start' ),
|
||||||
crel( 'input', startAttributes )
|
crel( 'input', startAttributes )
|
||||||
),
|
),
|
||||||
|
crel( 'label',
|
||||||
|
crel( 'span', 'Beginning' ),
|
||||||
|
crel( 'input', beginningAttributes )
|
||||||
|
),
|
||||||
|
|
||||||
crel( 'label',
|
crel( 'label',
|
||||||
crel( 'span', 'Audio' ),
|
crel( 'span', 'Audio' ),
|
||||||
|
|
|
@ -314,6 +314,12 @@ img.icon{
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#diversions{
|
||||||
|
.diversion{
|
||||||
|
background: pink;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#nodes g:hover circle,
|
#nodes g:hover circle,
|
||||||
|
@ -322,6 +328,12 @@ img.icon{
|
||||||
stroke-width: 27;
|
stroke-width: 27;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.diversion{
|
||||||
|
circle{
|
||||||
|
fill: pink !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.controlDown #nodes g:hover circle,
|
.controlDown #nodes g:hover circle,
|
||||||
.secondaryMsg circle {
|
.secondaryMsg circle {
|
||||||
stroke: lightgreen;
|
stroke: lightgreen;
|
||||||
|
|
Loading…
Reference in a new issue