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 # number of messages that are in queue and are already logged, we want
# to avoid duplication # 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'])) ['chat', 'pm']))
if self.resource: 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'])) self.contact.get_full_jid(), ['chat', 'pm']))
rows = gajim.logger.get_last_conversation_lines( rows = gajim.logger.get_last_conversation_lines(
jid, pending_how_many, self.account) self.account, jid, pending)
local_old_kind = None local_old_kind = None
self.conv_textview.just_cleared = True self.conv_textview.just_cleared = True

View File

@ -68,6 +68,9 @@ class KindConstant(IntEnum):
CHAT_MSG_SENT = 6 CHAT_MSG_SENT = 6
ERROR = 7 ERROR = 7
def __str__(self):
return str(self.value)
@unique @unique
class ShowConstant(IntEnum): class ShowConstant(IntEnum):
ONLINE = 0 ONLINE = 0
@ -160,6 +163,10 @@ class Logger:
isolation_level='IMMEDIATE') isolation_level='IMMEDIATE')
os.chdir(back) os.chdir(back)
self.con.row_factory = self.namedtuple_factory 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.cur = self.con.cursor()
self.set_synchronous(False) self.set_synchronous(False)
@ -183,6 +190,18 @@ class Logger:
self.open_db() self.open_db()
self.get_jids_already_in_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): def commit(self):
try: try:
self.con.commit() self.con.commit()
@ -249,6 +268,22 @@ class Logger:
return True return True
return False 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): def get_jid_id(self, jid, typestr=None):
""" """
jids table has jid and jid_id logs table has log_id, jid_id, jids table has jid and jid_id logs table has log_id, jid_id,
@ -593,44 +628,49 @@ class Logger:
exceptions.PysqliteOperationalError) as error: exceptions.PysqliteOperationalError) as error:
self.dispatch('DB_ERROR', 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) Get recent messages
(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 Pending messages are already in queue to be printed when the
containg time, kind, message, subject list with empty tuple if nothing ChatControl is opened, so we dont want to request those messages.
found to meet our demands 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) restore = gajim.config.get('restore_lines')
except exceptions.PysqliteOperationalError: if restore <= 0:
# Error trying to create a new jid_id. This means there is no log
return [] return []
where_sql, jid_tuple = self._build_contact_where(account, jid)
# How many lines to restore and when to time them out kinds = map(str, [KindConstant.SINGLE_MSG_RECV,
restore_how_many = gajim.config.get('restore_lines') KindConstant.SINGLE_MSG_SENT,
if restore_how_many <= 0: KindConstant.CHAT_MSG_RECV,
return [] KindConstant.CHAT_MSG_SENT,
timeout = gajim.config.get('restore_timeout') # in minutes KindConstant.ERROR])
now = int(float(time.time())) jids = self._get_family_jids(account, jid)
if timeout > 0:
timeout = now - (timeout * 60) # before that they are too old 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: try:
self.cur.execute(''' messages = self.con.execute(
SELECT time, kind, message, subject, additional_data FROM logs sql, (*jids, restore, pending)).fetchall()
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()
except sqlite.DatabaseError: except sqlite.DatabaseError:
self.dispatch('DB_ERROR', self.dispatch('DB_ERROR',
exceptions.DatabaseMalformed(LOG_DB_PATH)) exceptions.DatabaseMalformed(LOG_DB_PATH))