Refactor get_last_conversation_lines()

- use NATURAL JOIN in SQL query instead of multiple SELECT via
_build_contact_where

- make code more concise

- update method documentation
This commit is contained in:
Philipp Hörist 2017-07-15 03:54:13 +02:00
parent 5e63461e6d
commit 40ba449f47
2 changed files with 74 additions and 34 deletions

View File

@ -1411,14 +1411,14 @@ class ChatControl(ChatControlBase):
# number of messages that are in queue and are already logged, we want
# to avoid duplication
pending_how_many = len(gajim.events.get_events(self.account, jid,
pending = len(gajim.events.get_events(self.account, jid,
['chat', 'pm']))
if self.resource:
pending_how_many += len(gajim.events.get_events(self.account,
pending += len(gajim.events.get_events(self.account,
self.contact.get_full_jid(), ['chat', 'pm']))
rows = gajim.logger.get_last_conversation_lines(
jid, pending_how_many, self.account)
self.account, jid, pending)
local_old_kind = None
self.conv_textview.just_cleared = True

View File

@ -68,6 +68,9 @@ class KindConstant(IntEnum):
CHAT_MSG_SENT = 6
ERROR = 7
def __str__(self):
return str(self.value)
@unique
class ShowConstant(IntEnum):
ONLINE = 0
@ -160,6 +163,10 @@ class Logger:
isolation_level='IMMEDIATE')
os.chdir(back)
self.con.row_factory = self.namedtuple_factory
# DB functions
self.con.create_function("get_timeout", 0, self._get_timeout)
self.cur = self.con.cursor()
self.set_synchronous(False)
@ -183,6 +190,18 @@ class Logger:
self.open_db()
self.get_jids_already_in_db()
@staticmethod
def _get_timeout():
"""
returns the timeout in epoch
"""
timeout = gajim.config.get('restore_timeout')
now = int(time.time())
if timeout > 0:
timeout = now - (timeout * 60)
return timeout
def commit(self):
try:
self.con.commit()
@ -249,6 +268,22 @@ class Logger:
return True
return False
@staticmethod
def _get_family_jids(account, jid):
"""
Get all jids of the metacontacts family
:param account: The account
:param jid: The JID
returns a list of JIDs'
"""
family = gajim.contacts.get_metacontacts_family(account, jid)
if family:
return [user['jid'] for user in family]
return [jid]
def get_jid_id(self, jid, typestr=None):
"""
jids table has jid and jid_id logs table has log_id, jid_id,
@ -593,44 +628,49 @@ class Logger:
exceptions.PysqliteOperationalError) as error:
self.dispatch('DB_ERROR', error)
def get_last_conversation_lines(self, jid, pending_how_many, account):
def get_last_conversation_lines(self, account, jid, pending):
"""
Accept how many rows to restore and when to time them out (in minutes)
(mark them as too old) and number of messages that are in queue and are
already logged but pending to be viewed, returns a list of tuples
containg time, kind, message, subject list with empty tuple if nothing
found to meet our demands
Get recent messages
Pending messages are already in queue to be printed when the
ChatControl is opened, so we dont want to request those messages.
How many messages are requested depends on the 'restore_lines'
config value. How far back in time messages are requested depends on
_get_timeout().
:param account: The account
:param jid: The jid from which we request the conversation lines
:param pending: How many messages are currently pending so we dont
request those messages
returns a list of namedtuples
"""
try:
self.get_jid_id(jid)
except exceptions.PysqliteOperationalError:
# Error trying to create a new jid_id. This means there is no log
restore = gajim.config.get('restore_lines')
if restore <= 0:
return []
where_sql, jid_tuple = self._build_contact_where(account, jid)
# How many lines to restore and when to time them out
restore_how_many = gajim.config.get('restore_lines')
if restore_how_many <= 0:
return []
timeout = gajim.config.get('restore_timeout') # in minutes
kinds = map(str, [KindConstant.SINGLE_MSG_RECV,
KindConstant.SINGLE_MSG_SENT,
KindConstant.CHAT_MSG_RECV,
KindConstant.CHAT_MSG_SENT,
KindConstant.ERROR])
now = int(float(time.time()))
if timeout > 0:
timeout = now - (timeout * 60) # before that they are too old
jids = self._get_family_jids(account, jid)
sql = '''
SELECT time, kind, message, subject, additional_data
FROM logs NATURAL JOIN jids WHERE jid IN ({jids}) AND
kind IN ({kinds}) AND time > get_timeout()
ORDER BY time DESC, log_line_id DESC LIMIT ? OFFSET ?
'''.format(jids=', '.join('?' * len(jids)),
kinds=', '.join(kinds))
# so if we ask last 5 lines and we have 2 pending we get
# 3 - 8 (we avoid the last 2 lines but we still return 5 asked)
try:
self.cur.execute('''
SELECT time, kind, message, subject, additional_data FROM logs
WHERE (%s) AND kind IN (%d, %d, %d, %d, %d) AND time > %d
ORDER BY time DESC LIMIT %d OFFSET %d
''' % (where_sql, KindConstant.SINGLE_MSG_RECV,
KindConstant.CHAT_MSG_RECV, KindConstant.SINGLE_MSG_SENT,
KindConstant.CHAT_MSG_SENT, KindConstant.ERROR, timeout,
restore_how_many, pending_how_many), jid_tuple)
messages = self.cur.fetchall()
messages = self.con.execute(
sql, (*jids, restore, pending)).fetchall()
except sqlite.DatabaseError:
self.dispatch('DB_ERROR',
exceptions.DatabaseMalformed(LOG_DB_PATH))