Store variables in a shared database. Condition to retreive the last n variables of a specific name. Fix #61
This commit is contained in:
parent
2eeee9095f
commit
8227cceffb
4 changed files with 115 additions and 3 deletions
|
@ -26,6 +26,7 @@ from hugvey.speech.recorder import Recorder
|
|||
from pythonosc import udp_client, osc_server, dispatcher
|
||||
import copy
|
||||
from pythonosc.osc_server import AsyncIOOSCUDPServer
|
||||
from hugvey.variablestore import VariableStore
|
||||
|
||||
mainLogger = logging.getLogger("hugvey")
|
||||
|
||||
|
@ -93,6 +94,11 @@ class CentralCommand(object):
|
|||
|
||||
voice_dir = os.path.join(self.config['web']['files_dir'], 'voices')
|
||||
self.voiceStorage = VoiceStorage(voice_dir, self.languageConfig)
|
||||
varDb = os.path.join(
|
||||
self.config['voice']['record_dir'],
|
||||
'hugvey_variable_store.db'
|
||||
)
|
||||
self.variableStore = VariableStore(varDb)
|
||||
|
||||
self.panopticon = Panopticon(self, self.config, self.voiceStorage)
|
||||
|
||||
|
@ -419,6 +425,8 @@ class CentralCommand(object):
|
|||
self.catchException(self.oscListener()))
|
||||
self.tasks['redLightController'] = self.loop.create_task(
|
||||
self.catchException(self.redLightController()))
|
||||
self.tasks['variableStore'] = self.loop.create_task(
|
||||
self.catchException(self.variableStore.queueProcessor()))
|
||||
|
||||
for hid in self.hugvey_ids:
|
||||
self.tasks['voiceListener'] = self.loop.create_task(
|
||||
|
|
|
@ -142,6 +142,9 @@ class Message(object):
|
|||
asyncio.get_event_loop().create_task(self.getAudioFilePath())
|
||||
# asyncio.get_event_loop().call_soon_threadsafe(self.getAudioFilePath)
|
||||
self.logger.warn(f"started {name}")
|
||||
|
||||
def getVariableValue(self, var):
|
||||
return self.variableValues[var] if (self.variableValues[var] is not None) else self.story.configuration.nothing_text #TODO: translate nothing to each language
|
||||
|
||||
def getText(self):
|
||||
# sort reverse to avoid replacing the wrong variable
|
||||
|
@ -150,7 +153,7 @@ class Message(object):
|
|||
# self.logger.debug(f"Getting text for {self.id}")
|
||||
for var in self.variables:
|
||||
self.logger.debug(f"try replacing ${var} with {self.variableValues[var]} in {text}")
|
||||
replacement = self.variableValues[var] if (self.variableValues[var] is not None) else self.story.configuration.nothing_text #TODO: translate nothing to each language
|
||||
replacement = self.getVariableValue(var)
|
||||
text = text.replace('$'+var, replacement)
|
||||
return text
|
||||
|
||||
|
@ -330,6 +333,9 @@ class Condition(object):
|
|||
condition.method = condition._hasPlayed
|
||||
if data['type'] == "variableEquals":
|
||||
condition.method = condition._variableEquals
|
||||
if data['type'] == "variable_storage":
|
||||
condition.method = condition._hasVariableStorage
|
||||
condition.hasRan = False
|
||||
|
||||
if 'vars' in data:
|
||||
condition.vars = data['vars']
|
||||
|
@ -450,6 +456,32 @@ class Condition(object):
|
|||
)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def _hasVariableStorage(self, story) -> bool:
|
||||
if not story.lastMsgFinishTime:
|
||||
return False
|
||||
|
||||
if self.hasRan:
|
||||
# Prevent multiple runs of the same query within eg. waiting for a timeout.
|
||||
return False
|
||||
|
||||
number = int(self.vars['number'])
|
||||
varValues = story.hugvey.command.variableStore.getLastOfName(self.vars['var_name'], number)
|
||||
self.hasRan = True
|
||||
|
||||
if len(varValues) < number:
|
||||
story.logger.info(f"{self.id}: Too few instances of {self.vars['var_name']}")
|
||||
return False
|
||||
|
||||
for i in range(number):
|
||||
story.setVariableValue(
|
||||
f"stored_{self.vars['var_name']}_{i+1}",
|
||||
varValues[i],
|
||||
store=False
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def _hasMetReplyContains(self, story) -> bool:
|
||||
"""
|
||||
|
@ -1237,13 +1269,15 @@ class Story(object):
|
|||
else:
|
||||
self.variables[variableName].append(message)
|
||||
|
||||
def setVariableValue(self, name, value):
|
||||
def setVariableValue(self, name, value, store=True):
|
||||
if name not in self.variables:
|
||||
self.logger.warn(f"Set variable that is not needed in the story: {name}")
|
||||
else:
|
||||
self.logger.debug(f"Set variable {name} to {value}")
|
||||
|
||||
|
||||
self.variableValues[name] = value
|
||||
if store:
|
||||
self.hugvey.command.variableStore.addVariable(name, value, self.hugvey.id)
|
||||
|
||||
if name not in self.variables:
|
||||
return
|
||||
|
|
61
hugvey/variablestore.py
Normal file
61
hugvey/variablestore.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import sqlite3
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
mainLogger = logging.getLogger("hugvey")
|
||||
logger = mainLogger.getChild("variableStore")
|
||||
|
||||
|
||||
|
||||
class Variable:
|
||||
def __init__(self, name: str, value: str, hugveyId: int):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.hugveyId = hugveyId
|
||||
|
||||
class VariableStore:
|
||||
def __init__(self, db_filename):
|
||||
self.conn = sqlite3.connect(db_filename, check_same_thread=False)
|
||||
|
||||
# make sure the table exits.
|
||||
createSqls = ["""
|
||||
CREATE TABLE IF NOT EXISTS `variables` (
|
||||
`name` VARCHAR(255),
|
||||
`hugvey` INTEGER,
|
||||
`createdAt` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
`val` VARCHAR(1024)
|
||||
);
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS `name_time` ON `variables` (
|
||||
`name`,
|
||||
`createdAt` DESC
|
||||
);
|
||||
"""]
|
||||
cur = self.conn.cursor()
|
||||
for sql in createSqls:
|
||||
cur.execute(sql)
|
||||
self.conn.commit()
|
||||
self.q = asyncio.Queue()
|
||||
|
||||
def addVariable(self, name, value, hugveyId):
|
||||
logger.debug(f"Queing storing of {name} for {hugveyId}")
|
||||
self.q.put_nowait(Variable(name, value, hugveyId))
|
||||
|
||||
async def queueProcessor(self):
|
||||
while True:
|
||||
#: :var v: Variable
|
||||
v = await self.q.get()
|
||||
c = self.conn.cursor()
|
||||
logger.info(f"Store variable {v.name} for {v.hugveyId}: '{v.value}'")
|
||||
c.execute("INSERT INTO variables (name, hugvey, createdAt, val) VALUES (?,?, current_timestamp,?)", (v.name, v.hugveyId, v.value))
|
||||
self.conn.commit()
|
||||
c.close()
|
||||
|
||||
def getLastOfName(self, name, n = 10):
|
||||
cur = self.conn.cursor()
|
||||
logging.debug(f"Get last {n} stored variables of {name}")
|
||||
cur.execute("SELECT val FROM variables WHERE name = ? ORDER BY createdAt DESC LIMIT ?", (name, n))
|
||||
values = [v[0] for v in cur.fetchall()]
|
||||
cur.close()
|
||||
return values
|
|
@ -1607,6 +1607,11 @@ class Graph {
|
|||
'onlyIfNoReply': { 'type': 'checkbox', label: "Only if no reply", "title": "This timeout is only used if the participant doesn't say a word. If the participant starts speaking within the time of this timeout condition, only other conditions are applicable." },
|
||||
'needsReply': { 'type': 'checkbox', label: "Reply needed", "title": "If checked, the timeout is counted if met. Used by consecutive-timeouts diversions." },
|
||||
},
|
||||
'variable_storage': {
|
||||
// when matched, variable will be accessible as {store_name_1}
|
||||
'var_name': { 'label': "Variable name", 'type': 'text', 'description': "When matched, variable will be accessible as $stored_VARNAME_1, $stored_VARNAME_2.. etc (use the name given here instead of VARNAME)" },
|
||||
'number': { 'label': "Nr. of items to get", 'type': 'number', 'value': 5, 'min': 0, 'step': 1 },
|
||||
},
|
||||
'replyContains': {
|
||||
'delays.0.minReplyDuration': { 'type': 'number', 'value': 0, 'min': 0, 'step': 0.1, 'label': 'Delay 1 - reply duration', 'unit': "s", 'readonly': 'readonly' },
|
||||
'delays.0.waitTime': { 'type': 'number', 'value': 3, 'min': 0, 'step': 0.1 , 'label': 'Delay 1 - wait time', 'unit': "s" },
|
||||
|
@ -1668,6 +1673,10 @@ class Graph {
|
|||
// crel('span', {'class': 'label-unit'}, attr.hasOwnProperty('unit') ? attr['unit'] : "" )
|
||||
)
|
||||
);
|
||||
if(attr.hasOwnProperty('description')) {
|
||||
inputs.push(crel('div', {'class':'description'}, attr['description']));
|
||||
}
|
||||
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue