backend, bridge: asynchronous history

quick_frontend: use of asynchronous history
This commit is contained in:
Goffi 2011-11-08 01:08:11 +01:00
commit f1966d9692
2 changed files with 94 additions and 79 deletions

View file

@ -34,7 +34,6 @@ from sat.core.default_config import default_config
from sat.tools.sqlite import SqliteStorage
SAVEFILE_PARAM_XML="/param" #xml parameters template
SAVEFILE_HISTORY="/history"
SAVEFILE_PRIVATE="/private" #file used to store misc values (mainly for plugins)
SAVEFILE_DATABASE="/sat.db"
@ -501,12 +500,10 @@ class Memory:
self.presenceStatus={}
self.lastResource={} #tmp, will be refactored with bdd integration
self.subscriptions={}
self.history={} #used to store chat history (key: short jid)
self.private={} #used to store private value
self.server_features={} #used to store discovery's informations
self.server_identities={}
self.config = self.parseMainConf()
host.set_const('savefile_history', SAVEFILE_HISTORY)
host.set_const('savefile_private', SAVEFILE_PRIVATE)
host.set_const('savefile_database', SAVEFILE_DATABASE)
database_file = os.path.expanduser(self.getConfig('','local_dir')+
@ -547,8 +544,6 @@ class Memory:
"""Load parameters and all memory things from file/db"""
param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+
self.host.get_const('savefile_param_xml'))
history_file = os.path.expanduser(self.getConfig('','local_dir')+
self.host.get_const('savefile_history'))
private_file = os.path.expanduser(self.getConfig('','local_dir')+
self.host.get_const('savefile_private'))
@ -564,16 +559,6 @@ class Memory:
info (_("No params template, using default template"))
self.params.load_default_params()
#history
if os.path.exists(history_file):
try:
with open(history_file, 'r') as history_pickle:
self.history=pickle.load(history_pickle)
debug(_("history loaded"))
except:
error (_("Can't load history !"))
#private
if os.path.exists(private_file):
try:
@ -604,16 +589,11 @@ class Memory:
#TODO: need to encrypt files (at least passwords !) and set permissions
param_file_xml = os.path.expanduser(self.getConfig('','local_dir')+
self.host.get_const('savefile_param_xml'))
history_file = os.path.expanduser(self.getConfig('','local_dir')+
self.host.get_const('savefile_history'))
private_file = os.path.expanduser(self.getConfig('','local_dir')+
self.host.get_const('savefile_private'))
self.params.save_xml(param_file_xml)
debug(_("params saved"))
with open(history_file, 'w') as history_pickle:
pickle.dump(self.history, history_pickle)
debug(_("history saved"))
with open(private_file, 'w') as private_pickle:
pickle.dump(self.private, private_pickle)
debug(_("private values saved"))
@ -645,39 +625,12 @@ class Memory:
@param name: Name of the profile"""
return self.params.deleteProfile(name)
def addToHistory(self, me_jid, from_jid, to_jid, type, message):
me_short=me_jid.userhost()
from_short=from_jid.userhost()
to_short=to_jid.userhost()
def addToHistory(self, from_jid, to_jid, message, timestamp=None, profile="@NONE@"):
assert(profile!="@NONE@")
return self.storage.addToHistory(from_jid, to_jid, message, timestamp, profile)
if from_jid==me_jid:
key=to_short
else:
key=from_short
if not self.history.has_key(me_short):
self.history[me_short]={}
if not self.history[me_short].has_key(key):
self.history[me_short][key]={}
self.history[me_short][key][int(time.time())] = (from_jid.full(), message)
def getHistory(self, from_jid, to_jid, size):
ret={}
if not self.history.has_key(from_jid):
error(_("source JID not found !"))
#TODO: throw an error here
return {}
if not self.history[from_jid].has_key(to_jid):
error(_("dest JID not found !"))
#TODO: throw an error here
return {}
stamps=self.history[from_jid][to_jid].keys()
stamps.sort()
for stamp in stamps[-size:]:
ret[stamp]=self.history[from_jid][to_jid][stamp]
return ret
def getHistory(self, from_jid, to_jid, limit=0, between=True):
return self.storage.getHistory(jid.JID(from_jid), jid.JID(to_jid), limit, between)
def setPrivate(self, key, value):
"""Save a misc private value (mainly useful for plugins)"""

View file

@ -24,6 +24,7 @@ from logging import debug, info, warning, error
from twisted.enterprise import adbapi
from twisted.internet import defer
import os.path
import time
class SqliteStorage():
"""This class manage storage with Sqlite database"""
@ -44,7 +45,7 @@ class SqliteStorage():
info(_("The database is new, creating the tables"))
database_creation = [
"CREATE TABLE profiles (id INTEGER PRIMARY KEY ASC, name TEXT, UNIQUE (name))",
"CREATE TABLE historic (id INTEGER PRIMARY KEY ASC, profile_id INTEGER, source TEXT, dest TEXT, source_res TEXT, dest_res TEXT, timestamp DATETIME, message TEXT, FOREIGN KEY(profile_id) REFERENCES profiles(id))",
"CREATE TABLE history (id INTEGER PRIMARY KEY ASC, profile_id INTEGER, source TEXT, dest TEXT, source_res TEXT, dest_res TEXT, timestamp DATETIME, message TEXT, FOREIGN KEY(profile_id) REFERENCES profiles(id))",
"CREATE TABLE param_gen (category TEXT, name TEXT, value TEXT, PRIMARY KEY (category,name))",
"CREATE TABLE param_ind (category TEXT, name TEXT, profile_id INTEGER, value TEXT, PRIMARY KEY (category,name,profile_id), FOREIGN KEY(profile_id) REFERENCES profiles(id))"]
for op in database_creation:
@ -162,6 +163,59 @@ class SqliteStorage():
d.addErrback(lambda ignore: error(_("Can't set individual parameter (%(category)s/%(name)s) for [%(profile)s] in database" % {"category":category, "name":name, "profile":profile})))
return d
#History
def addToHistory(self, from_jid, to_jid, message, timestamp=None, profile=None):
"""Store a new message in history
@param from_jid: full source JID
@param to_jid: full dest JID
@param message: message
@param timestamp: timestamp in seconds since epoch, or None to use current time
"""
assert(profile!=None)
d = self.dbpool.runQuery("INSERT INTO history(source, source_res, dest, dest_res, timestamp, message, profile_id) VALUES (?,?,?,?,?,?,?)",
(from_jid.userhost(), from_jid.resource, to_jid.userhost(), to_jid.resource, timestamp or int(time.time()),
message, self.profiles[profile]))
d.addErrback(lambda ignore: error(_("Can't save following message in history: from [%(from_jid)s] to [%(to_jid)s] ==> [%(message)s]" %
{"from_jid":from_jid.full(), "to_jid":to_jid.full(), "message":message})))
return d
def getHistory(self, from_jid, to_jid, limit=0, between=True):
"""Store a new message in history
@param from_jid: source JID (full, or bare for catchall
@param to_jid: dest JID (full, or bare for catchall
@param size: maximum number of messages to get, or 0 for unlimited
"""
def sqliteToDict(result):
result_dict = {}
for row in result:
timestamp, source, source_res, dest, dest_res, message = row
result_dict[timestamp] = ("%s/%s" % (source, source_res) if source_res else source,
"%s/%s" % (dest, dest_res) if dest_res else dest,
message)
return result_dict
query_parts = ["SELECT timestamp, source, source_res, dest, dest_res, message FROM history WHERE"]
values = []
if between:
query_parts.append("(source=? OR source=?) AND (dest=? or dest=?)")
values.extend([from_jid.userhost(), to_jid.userhost(), to_jid.userhost(), from_jid.userhost()])
else:
query_parts.append("source=? AND dest=?")
values.extend([from_jid.userhost(), to_jid.userhost()])
if from_jid.resource:
query_parts.append("AND source_res=?")
values.append(from_jid.resource)
if to_jid.resource:
query_parts.append("AND dest_res=?")
values.append(to_jid.resource)
if limit:
query_parts.append("LIMIT ?")
values.append(limit)
d = self.dbpool.runQuery(" ".join(query_parts), values)
return d.addCallback(sqliteToDict)
##Helper methods##
def __getFirstResult(self, result):