Add navigation for chat history
This commit is contained in:
parent
5ce0c65ce4
commit
b0bf74337c
|
@ -732,6 +732,64 @@ class Logger:
|
||||||
# attributes set to None because of the MAX() function
|
# attributes set to None because of the MAX() function
|
||||||
return self.con.execute(sql, tuple(jids)).fetchone().time
|
return self.con.execute(sql, tuple(jids)).fetchone().time
|
||||||
|
|
||||||
|
def get_first_date_that_has_logs(self, account, jid):
|
||||||
|
"""
|
||||||
|
Get the timestamp of the first message we received for the jid.
|
||||||
|
|
||||||
|
:param account: The account
|
||||||
|
|
||||||
|
:param jid: The jid for which we request the first timestamp
|
||||||
|
|
||||||
|
returns a timestamp or None
|
||||||
|
"""
|
||||||
|
jids = self._get_family_jids(account, jid)
|
||||||
|
|
||||||
|
kinds = map(str, [KindConstant.STATUS,
|
||||||
|
KindConstant.GCSTATUS])
|
||||||
|
|
||||||
|
sql = '''
|
||||||
|
SELECT MIN(time) as time FROM logs
|
||||||
|
NATURAL JOIN jids WHERE jid IN ({jids})
|
||||||
|
AND kind NOT IN ({kinds})
|
||||||
|
'''.format(jids=', '.join('?' * len(jids)),
|
||||||
|
kinds=', '.join(kinds))
|
||||||
|
|
||||||
|
# fetchone() returns always at least one Row with all
|
||||||
|
# attributes set to None because of the MIN() function
|
||||||
|
return self.con.execute(sql, tuple(jids)).fetchone().time
|
||||||
|
|
||||||
|
def get_date_has_logs(self, account, jid, date):
|
||||||
|
"""
|
||||||
|
Get single timestamp of a message we received for the jid
|
||||||
|
in the time range of one day.
|
||||||
|
|
||||||
|
:param account: The account
|
||||||
|
|
||||||
|
:param jid: The jid for which we request the first timestamp
|
||||||
|
|
||||||
|
:param date: datetime.datetime instance
|
||||||
|
example: datetime.datetime(year, month, day)
|
||||||
|
|
||||||
|
returns a timestamp or None
|
||||||
|
"""
|
||||||
|
jids = self._get_family_jids(account, jid)
|
||||||
|
|
||||||
|
kinds = map(str, [KindConstant.STATUS,
|
||||||
|
KindConstant.GCSTATUS])
|
||||||
|
|
||||||
|
delta = datetime.timedelta(
|
||||||
|
hours=23, minutes=59, seconds=59, microseconds=999999)
|
||||||
|
|
||||||
|
sql = '''
|
||||||
|
SELECT time
|
||||||
|
FROM logs NATURAL JOIN jids WHERE jid IN ({jids})
|
||||||
|
AND time BETWEEN ? AND ?
|
||||||
|
'''.format(jids=', '.join('?' * len(jids)))
|
||||||
|
|
||||||
|
return self.con.execute(sql, tuple(jids) +
|
||||||
|
(date.timestamp(),
|
||||||
|
(date + delta).timestamp())).fetchone()
|
||||||
|
|
||||||
def get_room_last_message_time(self, account, jid):
|
def get_room_last_message_time(self, account, jid):
|
||||||
"""
|
"""
|
||||||
Get the timestamp of the last message we received in a room.
|
Get the timestamp of the last message we received in a room.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.20.0 -->
|
<!-- Generated with glade 3.20.2 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.20"/>
|
<requires lib="gtk+" version="3.20"/>
|
||||||
<object class="GtkApplicationWindow" id="history_window">
|
<object class="GtkApplicationWindow" id="history_window">
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<object class="GtkEntry" id="query_entry">
|
<object class="GtkEntry" id="query_entry">
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="invisible_char">●</property>
|
<property name="invisible_char">●</property>
|
||||||
<property name="placeholder_text">Enter name / JID of contact or groupchat</property>
|
<property name="placeholder_text" translatable="yes">Enter name / JID of contact or groupchat</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
|
@ -102,11 +102,13 @@
|
||||||
<object class="GtkPaned" id="hpaned">
|
<object class="GtkPaned" id="hpaned">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
|
<property name="margin_bottom">5</property>
|
||||||
<property name="position">165</property>
|
<property name="position">165</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="vbox2">
|
<object class="GtkBox" id="vbox2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_right">5</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="spacing">6</property>
|
<property name="spacing">6</property>
|
||||||
<child>
|
<child>
|
||||||
|
@ -123,7 +125,98 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<placeholder/>
|
<object class="GtkButtonBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
<property name="layout_style">expand</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button_first_day">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<signal name="clicked" handler="_change_date" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">go-first-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button_previous_day">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<signal name="clicked" handler="_change_date" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">go-previous-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button_next_day">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<signal name="clicked" handler="_change_date" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">go-next-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button_last_day">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<signal name="clicked" handler="_change_date" swapped="no"/>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">go-last-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
|
|
|
@ -71,6 +71,10 @@ class HistoryWindow:
|
||||||
self.window = xml.get_object('history_window')
|
self.window = xml.get_object('history_window')
|
||||||
self.window.set_application(app.app)
|
self.window.set_application(app.app)
|
||||||
self.calendar = xml.get_object('calendar')
|
self.calendar = xml.get_object('calendar')
|
||||||
|
self.button_first_day = xml.get_object('button_first_day')
|
||||||
|
self.button_previous_day = xml.get_object('button_previous_day')
|
||||||
|
self.button_next_day = xml.get_object('button_next_day')
|
||||||
|
self.button_last_day = xml.get_object('button_last_day')
|
||||||
scrolledwindow = xml.get_object('scrolledwindow')
|
scrolledwindow = xml.get_object('scrolledwindow')
|
||||||
self.history_textview = conversation_textview.ConversationTextview(
|
self.history_textview = conversation_textview.ConversationTextview(
|
||||||
account, used_in_history_window = True)
|
account, used_in_history_window = True)
|
||||||
|
@ -311,17 +315,25 @@ class HistoryWindow:
|
||||||
|
|
||||||
self.jids_to_search = [info_jid]
|
self.jids_to_search = [info_jid]
|
||||||
|
|
||||||
# select logs for last date we have logs with contact
|
# Get first/last date we have logs with contact (for log navigation)
|
||||||
|
self.first_log = app.logger.get_first_date_that_has_logs(
|
||||||
|
self.account, self.jid)
|
||||||
|
self.first_day = self._get_date_from_timestamp(self.first_log)
|
||||||
|
self.last_log = app.logger.get_last_date_that_has_logs(
|
||||||
|
self.account, self.jid)
|
||||||
|
self.last_day = self._get_date_from_timestamp(self.last_log)
|
||||||
|
|
||||||
|
# Select logs for last date we have logs with contact
|
||||||
self.calendar.set_sensitive(True)
|
self.calendar.set_sensitive(True)
|
||||||
last_log = \
|
gtk_month = gtkgui_helpers.make_python_month_gtk_month(
|
||||||
app.logger.get_last_date_that_has_logs(self.account, self.jid)
|
self.last_day.month)
|
||||||
|
self.calendar.select_month(gtk_month, self.last_day.year)
|
||||||
|
self.calendar.select_day(self.last_day.day)
|
||||||
|
|
||||||
date = time.localtime(last_log)
|
self.button_previous_day.set_sensitive(True)
|
||||||
|
self.button_next_day.set_sensitive(True)
|
||||||
y, m, d = date[0], date[1], date[2]
|
self.button_first_day.set_sensitive(True)
|
||||||
gtk_month = gtkgui_helpers.make_python_month_gtk_month(m)
|
self.button_last_day.set_sensitive(True)
|
||||||
self.calendar.select_month(gtk_month, y)
|
|
||||||
self.calendar.select_day(d)
|
|
||||||
|
|
||||||
self.search_entry.set_sensitive(True)
|
self.search_entry.set_sensitive(True)
|
||||||
self.search_entry.grab_focus()
|
self.search_entry.grab_focus()
|
||||||
|
@ -341,6 +353,10 @@ class HistoryWindow:
|
||||||
self.checkbutton.set_sensitive(False)
|
self.checkbutton.set_sensitive(False)
|
||||||
self.calendar.set_sensitive(False)
|
self.calendar.set_sensitive(False)
|
||||||
self.calendar.clear_marks()
|
self.calendar.clear_marks()
|
||||||
|
self.button_previous_day.set_sensitive(False)
|
||||||
|
self.button_next_day.set_sensitive(False)
|
||||||
|
self.button_first_day.set_sensitive(False)
|
||||||
|
self.button_last_day.set_sensitive(False)
|
||||||
|
|
||||||
self.results_window.set_property('visible', False)
|
self.results_window.set_property('visible', False)
|
||||||
|
|
||||||
|
@ -379,6 +395,60 @@ class HistoryWindow:
|
||||||
for date in log_days:
|
for date in log_days:
|
||||||
widget.mark_day(date.day)
|
widget.mark_day(date.day)
|
||||||
|
|
||||||
|
def _get_date_from_timestamp(self, timestamp):
|
||||||
|
# Conversion from timestamp to date
|
||||||
|
log = time.localtime(timestamp)
|
||||||
|
y, m, d = log[0], log[1], log[2]
|
||||||
|
date = datetime.datetime(y, m, d)
|
||||||
|
return(date)
|
||||||
|
|
||||||
|
def _change_date(self, widget):
|
||||||
|
# Get day selected in calendar
|
||||||
|
y, m, d = self.calendar.get_date()
|
||||||
|
py_m = gtkgui_helpers.make_gtk_month_python_month(m)
|
||||||
|
_date = datetime.datetime(y, py_m, d)
|
||||||
|
|
||||||
|
if widget is self.button_first_day:
|
||||||
|
gtk_m = gtkgui_helpers.make_python_month_gtk_month(
|
||||||
|
self.first_day.month)
|
||||||
|
self.calendar.select_month(gtk_m, self.first_day.year)
|
||||||
|
self.calendar.select_day(self.first_day.day)
|
||||||
|
return
|
||||||
|
elif widget is self.button_last_day:
|
||||||
|
gtk_m = gtkgui_helpers.make_python_month_gtk_month(
|
||||||
|
self.last_day.month)
|
||||||
|
self.calendar.select_month(gtk_m, self.last_day.year)
|
||||||
|
self.calendar.select_day(self.last_day.day)
|
||||||
|
return
|
||||||
|
elif widget is self.button_previous_day:
|
||||||
|
end_date = self.first_day
|
||||||
|
timedelta = datetime.timedelta(days=-1)
|
||||||
|
if end_date >= _date:
|
||||||
|
return
|
||||||
|
elif widget is self.button_next_day:
|
||||||
|
end_date = self.last_day
|
||||||
|
timedelta = datetime.timedelta(days=1)
|
||||||
|
if end_date <= _date:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Iterate through days until log entry found or
|
||||||
|
# supplied end_date (first_log / last_log) reached
|
||||||
|
logs = None
|
||||||
|
while logs is None:
|
||||||
|
_date = _date + timedelta
|
||||||
|
if _date == end_date:
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
logs = app.logger.get_date_has_logs(
|
||||||
|
self.account, self.jid, _date)
|
||||||
|
except exceptions.PysqliteOperationalError as e:
|
||||||
|
dialogs.ErrorDialog(_('Disk Error'), str(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
gtk_month = gtkgui_helpers.make_python_month_gtk_month(_date.month)
|
||||||
|
self.calendar.select_month(gtk_month, _date.year)
|
||||||
|
self.calendar.select_day(_date.day)
|
||||||
|
|
||||||
def _get_string_show_from_constant_int(self, show):
|
def _get_string_show_from_constant_int(self, show):
|
||||||
if show == ShowConstant.ONLINE:
|
if show == ShowConstant.ONLINE:
|
||||||
show = 'online'
|
show = 'online'
|
||||||
|
|
Loading…
Reference in New Issue