2014-05-26 00:27:52 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
2015-05-12 22:03:23 +00:00
|
|
|
wakatime.offlinequeue
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~
|
2014-05-26 00:27:52 +00:00
|
|
|
|
2015-05-12 22:03:23 +00:00
|
|
|
Queue for saving heartbeats while offline.
|
2014-05-26 00:27:52 +00:00
|
|
|
|
|
|
|
:copyright: (c) 2014 Alan Hamlett.
|
|
|
|
:license: BSD, see LICENSE for more details.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
from time import sleep
|
2014-12-04 19:45:18 +00:00
|
|
|
|
2017-11-09 07:12:05 +00:00
|
|
|
from .compat import json
|
2018-09-21 05:29:34 +00:00
|
|
|
from .constants import DEFAULT_SYNC_OFFLINE_ACTIVITY, HEARTBEATS_PER_REQUEST
|
2017-11-09 07:12:05 +00:00
|
|
|
from .heartbeat import Heartbeat
|
|
|
|
|
|
|
|
|
2014-06-09 20:18:30 +00:00
|
|
|
try:
|
|
|
|
import sqlite3
|
|
|
|
HAS_SQL = True
|
2015-09-29 10:11:25 +00:00
|
|
|
except ImportError: # pragma: nocover
|
2014-06-09 20:18:30 +00:00
|
|
|
HAS_SQL = False
|
2014-05-26 00:27:52 +00:00
|
|
|
|
|
|
|
|
2014-07-25 08:01:39 +00:00
|
|
|
log = logging.getLogger('WakaTime')
|
2014-05-26 00:27:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Queue(object):
|
2017-10-29 18:32:03 +00:00
|
|
|
db_file = '.wakatime.db'
|
2017-11-09 07:12:05 +00:00
|
|
|
table_name = 'heartbeat_2'
|
2015-09-29 10:11:25 +00:00
|
|
|
|
2017-11-09 07:12:05 +00:00
|
|
|
args = None
|
|
|
|
configs = None
|
|
|
|
|
|
|
|
def __init__(self, args, configs):
|
|
|
|
self.args = args
|
|
|
|
self.configs = configs
|
2014-05-26 00:27:52 +00:00
|
|
|
|
|
|
|
def connect(self):
|
2017-11-09 07:12:05 +00:00
|
|
|
conn = sqlite3.connect(self._get_db_file(), isolation_level=None)
|
2014-05-26 00:27:52 +00:00
|
|
|
c = conn.cursor()
|
2015-09-29 10:11:25 +00:00
|
|
|
c.execute('''CREATE TABLE IF NOT EXISTS {0} (
|
2017-11-09 07:12:05 +00:00
|
|
|
id text,
|
|
|
|
heartbeat text)
|
2015-09-29 10:11:25 +00:00
|
|
|
'''.format(self.table_name))
|
2014-05-26 00:27:52 +00:00
|
|
|
return (conn, c)
|
|
|
|
|
2017-11-09 07:12:05 +00:00
|
|
|
def push(self, heartbeat):
|
|
|
|
if not HAS_SQL:
|
2014-06-09 20:18:30 +00:00
|
|
|
return
|
2014-05-26 21:06:36 +00:00
|
|
|
try:
|
|
|
|
conn, c = self.connect()
|
2017-11-09 07:12:05 +00:00
|
|
|
data = {
|
|
|
|
'id': heartbeat.get_id(),
|
|
|
|
'heartbeat': heartbeat.json(),
|
2014-05-26 21:06:36 +00:00
|
|
|
}
|
2017-11-09 07:12:05 +00:00
|
|
|
c.execute('INSERT INTO {0} VALUES (:id,:heartbeat)'.format(self.table_name), data)
|
2014-05-26 21:06:36 +00:00
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
2014-06-09 20:18:30 +00:00
|
|
|
except sqlite3.Error:
|
2016-06-17 08:17:29 +00:00
|
|
|
log.traceback()
|
2014-05-26 00:27:52 +00:00
|
|
|
|
|
|
|
def pop(self):
|
2017-11-09 07:12:05 +00:00
|
|
|
if not HAS_SQL:
|
2014-06-09 20:18:30 +00:00
|
|
|
return None
|
2014-05-26 00:27:52 +00:00
|
|
|
tries = 3
|
|
|
|
wait = 0.1
|
2014-05-26 21:06:36 +00:00
|
|
|
try:
|
|
|
|
conn, c = self.connect()
|
2014-06-09 20:18:30 +00:00
|
|
|
except sqlite3.Error:
|
2016-09-02 08:50:54 +00:00
|
|
|
log.traceback(logging.DEBUG)
|
2014-05-26 21:06:36 +00:00
|
|
|
return None
|
2017-11-09 07:12:05 +00:00
|
|
|
|
|
|
|
heartbeat = None
|
|
|
|
|
2014-05-26 00:27:52 +00:00
|
|
|
loop = True
|
|
|
|
while loop and tries > -1:
|
|
|
|
try:
|
|
|
|
c.execute('BEGIN IMMEDIATE')
|
2015-09-29 10:11:25 +00:00
|
|
|
c.execute('SELECT * FROM {0} LIMIT 1'.format(self.table_name))
|
2014-05-26 00:27:52 +00:00
|
|
|
row = c.fetchone()
|
|
|
|
if row is not None:
|
2017-11-09 07:12:05 +00:00
|
|
|
id = row[0]
|
|
|
|
heartbeat = Heartbeat(json.loads(row[1]), self.args, self.configs, _clone=True)
|
|
|
|
c.execute('DELETE FROM {0} WHERE id=?'.format(self.table_name), [id])
|
2014-05-26 00:27:52 +00:00
|
|
|
conn.commit()
|
|
|
|
loop = False
|
2017-11-09 07:12:05 +00:00
|
|
|
except sqlite3.Error:
|
2016-09-02 08:50:54 +00:00
|
|
|
log.traceback(logging.DEBUG)
|
2014-05-26 00:27:52 +00:00
|
|
|
sleep(wait)
|
|
|
|
tries -= 1
|
2014-05-26 21:06:36 +00:00
|
|
|
try:
|
|
|
|
conn.close()
|
2017-11-09 07:12:05 +00:00
|
|
|
except sqlite3.Error:
|
2016-09-02 08:50:54 +00:00
|
|
|
log.traceback(logging.DEBUG)
|
2014-12-22 07:01:09 +00:00
|
|
|
return heartbeat
|
2017-11-09 07:12:05 +00:00
|
|
|
|
|
|
|
def push_many(self, heartbeats):
|
|
|
|
for heartbeat in heartbeats:
|
|
|
|
self.push(heartbeat)
|
|
|
|
|
|
|
|
def pop_many(self, limit=None):
|
|
|
|
if limit is None:
|
2018-09-21 05:29:34 +00:00
|
|
|
limit = DEFAULT_SYNC_OFFLINE_ACTIVITY
|
2017-11-09 07:12:05 +00:00
|
|
|
|
|
|
|
heartbeats = []
|
|
|
|
|
|
|
|
count = 0
|
2018-09-21 05:29:34 +00:00
|
|
|
while count < limit:
|
2017-11-09 07:12:05 +00:00
|
|
|
heartbeat = self.pop()
|
|
|
|
if not heartbeat:
|
|
|
|
break
|
|
|
|
heartbeats.append(heartbeat)
|
|
|
|
count += 1
|
2018-09-21 05:29:34 +00:00
|
|
|
if count % HEARTBEATS_PER_REQUEST == 0:
|
|
|
|
yield heartbeats
|
|
|
|
heartbeats = []
|
2017-11-09 07:12:05 +00:00
|
|
|
|
2018-09-21 05:29:34 +00:00
|
|
|
if heartbeats:
|
|
|
|
yield heartbeats
|
2017-11-09 07:12:05 +00:00
|
|
|
|
|
|
|
def _get_db_file(self):
|
|
|
|
home = '~'
|
|
|
|
if os.environ.get('WAKATIME_HOME'):
|
|
|
|
home = os.environ.get('WAKATIME_HOME')
|
|
|
|
return os.path.join(os.path.expanduser(home), '.wakatime.db')
|