bump version of wakatime package to 2.0.0
This commit is contained in:
parent
533aaac313
commit
7e678a38bd
5 changed files with 166 additions and 20 deletions
|
@ -3,6 +3,18 @@ History
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
||||||
|
2.0.0 (2014-05-25)
|
||||||
|
++++++++++++++++++
|
||||||
|
|
||||||
|
- offline time logging using sqlite3 to queue editor events
|
||||||
|
|
||||||
|
|
||||||
|
1.0.2 (2014-05-06)
|
||||||
|
++++++++++++++++++
|
||||||
|
|
||||||
|
- ability to set project from command line argument
|
||||||
|
|
||||||
|
|
||||||
1.0.1 (2014-03-05)
|
1.0.1 (2014-03-05)
|
||||||
++++++++++++++++++
|
++++++++++++++++++
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
WakaTime
|
WakaTime
|
||||||
========
|
========
|
||||||
|
|
||||||
Fully automatic time tracking for your text editor.
|
Fully automatic time tracking for programmers.
|
||||||
|
|
||||||
This is the common interface for the WakaTime api. You shouldn't need to directly use this package unless you are creating a new plugin.
|
This is the common interface for the WakaTime api. You shouldn't need to directly use this package unless you are creating a new plugin or your text editor's plugin asks you to install the wakatime-cli interface.
|
||||||
|
|
||||||
Go to http://wakatime.com to install the plugin for your text editor.
|
Go to http://wakatime.com to install the plugin for your text editor.
|
||||||
|
|
||||||
|
@ -11,4 +11,10 @@ Go to http://wakatime.com to install the plugin for your text editor.
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
https://wakatime.com/help/plugins/installing-plugins
|
pip install wakatime
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
https://wakatime.com/
|
||||||
|
|
|
@ -11,13 +11,10 @@ setup(
|
||||||
name='wakatime',
|
name='wakatime',
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
license='BSD 3 Clause',
|
license='BSD 3 Clause',
|
||||||
description=' '.join([
|
description='Interface to the WakaTime api.',
|
||||||
'Action event appender for WakaTime, a time',
|
|
||||||
'tracking api for text editors.',
|
|
||||||
]),
|
|
||||||
long_description=open('README.rst').read(),
|
long_description=open('README.rst').read(),
|
||||||
author='Alan Hamlett',
|
author='Alan Hamlett',
|
||||||
author_email='alan.hamlett@gmail.com',
|
author_email='alan@wakatime.com',
|
||||||
url='https://github.com/wakatime/wakatime',
|
url='https://github.com/wakatime/wakatime',
|
||||||
packages=packages,
|
packages=packages,
|
||||||
package_dir={'wakatime': 'wakatime'},
|
package_dir={'wakatime': 'wakatime'},
|
||||||
|
@ -28,7 +25,7 @@ setup(
|
||||||
'console_scripts': ['wakatime = wakatime.__init__:main'],
|
'console_scripts': ['wakatime = wakatime.__init__:main'],
|
||||||
},
|
},
|
||||||
classifiers=(
|
classifiers=(
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 5 - Production/Stable',
|
||||||
'Environment :: Console',
|
'Environment :: Console',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: BSD License',
|
'License :: OSI Approved :: BSD License',
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
__title__ = 'wakatime'
|
__title__ = 'wakatime'
|
||||||
__version__ = '1.0.1'
|
__version__ = '2.0.0'
|
||||||
__author__ = 'Alan Hamlett'
|
__author__ = 'Alan Hamlett'
|
||||||
__license__ = 'BSD'
|
__license__ = 'BSD'
|
||||||
__copyright__ = 'Copyright 2013 Alan Hamlett'
|
__copyright__ = 'Copyright 2014 Alan Hamlett'
|
||||||
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
@ -39,6 +39,7 @@ except ImportError:
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages'))
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'packages'))
|
||||||
|
from .queue import Queue
|
||||||
from .log import setup_logging
|
from .log import setup_logging
|
||||||
from .project import find_project
|
from .project import find_project
|
||||||
from .stats import get_file_stats
|
from .stats import get_file_stats
|
||||||
|
@ -148,9 +149,14 @@ def parseArguments(argv):
|
||||||
parser.add_argument('--plugin', dest='plugin',
|
parser.add_argument('--plugin', dest='plugin',
|
||||||
help='optional text editor plugin name and version '+
|
help='optional text editor plugin name and version '+
|
||||||
'for User-Agent header')
|
'for User-Agent header')
|
||||||
|
parser.add_argument('--project', dest='project_name',
|
||||||
|
help='optional project name; will auto-discover by default')
|
||||||
parser.add_argument('--key', dest='key',
|
parser.add_argument('--key', dest='key',
|
||||||
help='your wakatime api key; uses api_key from '+
|
help='your wakatime api key; uses api_key from '+
|
||||||
'~/.wakatime.conf by default')
|
'~/.wakatime.conf by default')
|
||||||
|
parser.add_argument('--disableoffline', dest='offline',
|
||||||
|
action='store_false',
|
||||||
|
help='disables offline time logging instead of queuing logged time')
|
||||||
parser.add_argument('--ignore', dest='ignore', action='append',
|
parser.add_argument('--ignore', dest='ignore', action='append',
|
||||||
help='filename patterns to ignore; POSIX regex syntax; can be used more than once')
|
help='filename patterns to ignore; POSIX regex syntax; can be used more than once')
|
||||||
parser.add_argument('--logfile', dest='logfile',
|
parser.add_argument('--logfile', dest='logfile',
|
||||||
|
@ -191,6 +197,8 @@ def parseArguments(argv):
|
||||||
args.ignore.append(pattern)
|
args.ignore.append(pattern)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
if args.offline and configs.has_option('settings', 'offline'):
|
||||||
|
args.offline = configs.getboolean('settings', 'offline')
|
||||||
if not args.verbose and configs.has_option('settings', 'verbose'):
|
if not args.verbose and configs.has_option('settings', 'verbose'):
|
||||||
args.verbose = configs.getboolean('settings', 'verbose')
|
args.verbose = configs.getboolean('settings', 'verbose')
|
||||||
if not args.verbose and configs.has_option('settings', 'debug'):
|
if not args.verbose and configs.has_option('settings', 'debug'):
|
||||||
|
@ -225,8 +233,8 @@ def get_user_agent(plugin):
|
||||||
return user_agent
|
return user_agent
|
||||||
|
|
||||||
|
|
||||||
def send_action(project=None, branch=None, stats={}, key=None, targetFile=None,
|
def send_action(project=None, branch=None, stats=None, key=None, targetFile=None,
|
||||||
timestamp=None, isWrite=None, plugin=None, **kwargs):
|
timestamp=None, isWrite=None, plugin=None, offline=None, **kwargs):
|
||||||
url = 'https://wakatime.com/api/v1/actions'
|
url = 'https://wakatime.com/api/v1/actions'
|
||||||
log.debug('Sending action to api at %s' % url)
|
log.debug('Sending action to api at %s' % url)
|
||||||
data = {
|
data = {
|
||||||
|
@ -268,24 +276,45 @@ def send_action(project=None, branch=None, stats={}, key=None, targetFile=None,
|
||||||
}
|
}
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
exception_data['traceback'] = traceback.format_exc()
|
exception_data['traceback'] = traceback.format_exc()
|
||||||
log.error(exception_data)
|
if offline:
|
||||||
|
queue = Queue()
|
||||||
|
queue.push(data, plugin)
|
||||||
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
|
log.warn(exception_data)
|
||||||
|
else:
|
||||||
|
log.error(exception_data)
|
||||||
except:
|
except:
|
||||||
exception_data = {
|
exception_data = {
|
||||||
sys.exc_info()[0].__name__: str(sys.exc_info()[1]),
|
sys.exc_info()[0].__name__: str(sys.exc_info()[1]),
|
||||||
}
|
}
|
||||||
if log.isEnabledFor(logging.DEBUG):
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
exception_data['traceback'] = traceback.format_exc()
|
exception_data['traceback'] = traceback.format_exc()
|
||||||
log.error(exception_data)
|
if offline:
|
||||||
|
queue = Queue()
|
||||||
|
queue.push(data, plugin)
|
||||||
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
|
log.warn(exception_data)
|
||||||
|
else:
|
||||||
|
log.error(exception_data)
|
||||||
else:
|
else:
|
||||||
if response.getcode() == 201:
|
if response.getcode() == 201:
|
||||||
log.debug({
|
log.debug({
|
||||||
'response_code': response.getcode(),
|
'response_code': response.getcode(),
|
||||||
})
|
})
|
||||||
return True
|
return True
|
||||||
log.error({
|
if offline:
|
||||||
'response_code': response.getcode(),
|
queue = Queue()
|
||||||
'response_content': response.read(),
|
queue.push(data, plugin)
|
||||||
})
|
if log.isEnabledFor(logging.DEBUG):
|
||||||
|
log.warn({
|
||||||
|
'response_code': response.getcode(),
|
||||||
|
'response_content': response.read(),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
log.error({
|
||||||
|
'response_code': response.getcode(),
|
||||||
|
'response_content': response.read(),
|
||||||
|
})
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -313,7 +342,7 @@ def main(argv=None):
|
||||||
project_name = None
|
project_name = None
|
||||||
if project:
|
if project:
|
||||||
branch = project.branch()
|
branch = project.branch()
|
||||||
project_name = project.name()
|
project_name = args.project_name or project.name()
|
||||||
|
|
||||||
if send_action(
|
if send_action(
|
||||||
project=project_name,
|
project=project_name,
|
||||||
|
@ -321,6 +350,15 @@ def main(argv=None):
|
||||||
stats=stats,
|
stats=stats,
|
||||||
**vars(args)
|
**vars(args)
|
||||||
):
|
):
|
||||||
|
queue = Queue()
|
||||||
|
while True:
|
||||||
|
action = queue.pop()
|
||||||
|
if action is None:
|
||||||
|
break
|
||||||
|
if not send_action(project=action['project'], targetFile=action['file'], timestamp=action['time'],
|
||||||
|
branch=action['branch'], stats={'language': action['language'], 'lines': action['lines']},
|
||||||
|
key=args.key, isWrite=action['is_write'], plugin=action['plugin'], offline=args.offline):
|
||||||
|
break
|
||||||
return 0 # success
|
return 0 # success
|
||||||
|
|
||||||
return 102 # api error
|
return 102 # api error
|
||||||
|
|
93
packages/wakatime/wakatime/queue.py
Normal file
93
packages/wakatime/wakatime/queue.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
wakatime.queue
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Queue for offline time logging.
|
||||||
|
http://wakatime.com
|
||||||
|
|
||||||
|
:copyright: (c) 2014 Alan Hamlett.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Queue(object):
|
||||||
|
DB_FILE = os.path.join(os.path.expanduser('~'), '.wakatime.db')
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
exists = os.path.exists(self.DB_FILE)
|
||||||
|
conn = sqlite3.connect(self.DB_FILE)
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute('''CREATE TABLE IF NOT EXISTS action (
|
||||||
|
file text,
|
||||||
|
time real,
|
||||||
|
project text,
|
||||||
|
language text,
|
||||||
|
lines integer,
|
||||||
|
branch text,
|
||||||
|
is_write integer,
|
||||||
|
plugin text)
|
||||||
|
''')
|
||||||
|
return (conn, c)
|
||||||
|
|
||||||
|
|
||||||
|
def push(self, data, plugin):
|
||||||
|
conn, c = self.connect()
|
||||||
|
action = {
|
||||||
|
'file': data.get('file'),
|
||||||
|
'time': data.get('time'),
|
||||||
|
'project': data.get('project'),
|
||||||
|
'language': data.get('language'),
|
||||||
|
'lines': data.get('lines'),
|
||||||
|
'branch': data.get('branch'),
|
||||||
|
'is_write': 1 if data.get('is_write') else 0,
|
||||||
|
'plugin': plugin,
|
||||||
|
}
|
||||||
|
c.execute('INSERT INTO action VALUES (:file,:time,:project,:language,:lines,:branch,:is_write,:plugin)', action)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
tries = 3
|
||||||
|
wait = 0.1
|
||||||
|
action = None
|
||||||
|
conn, c = self.connect()
|
||||||
|
loop = True
|
||||||
|
while loop and tries > -1:
|
||||||
|
try:
|
||||||
|
c.execute('BEGIN IMMEDIATE')
|
||||||
|
c.execute('SELECT * FROM action LIMIT 1')
|
||||||
|
row = c.fetchone()
|
||||||
|
if row is not None:
|
||||||
|
c.execute('''DELETE FROM action WHERE
|
||||||
|
file=? AND time=? AND project=? AND language=? AND
|
||||||
|
lines=? AND branch=? AND is_write=?''', row[0:7])
|
||||||
|
conn.commit()
|
||||||
|
if row is not None:
|
||||||
|
action = {
|
||||||
|
'file': row[0],
|
||||||
|
'time': row[1],
|
||||||
|
'project': row[2],
|
||||||
|
'language': row[3],
|
||||||
|
'lines': row[4],
|
||||||
|
'branch': row[5],
|
||||||
|
'is_write': True if row[6] is 1 else False,
|
||||||
|
'plugin': row[7],
|
||||||
|
}
|
||||||
|
loop = False
|
||||||
|
except sqlite3.Error, e:
|
||||||
|
log.debug(str(e))
|
||||||
|
sleep(wait)
|
||||||
|
tries -= 1
|
||||||
|
conn.close()
|
||||||
|
return action
|
Loading…
Reference in a new issue