From 92948386fa408143d423c175f4d80e7a27e287aa Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Sat, 29 Aug 2015 16:16:14 -0700 Subject: [PATCH] new --entitytype command line arg to replace --notfile --- tests/samples/output/test_help_contents | 8 ++-- tests/samples/output/test_missing_config_file | 2 +- tests/test_wakatime_languages.py | 15 ++++--- ...wakatime_base.py => test_wakatime_main.py} | 34 +++++++-------- wakatime/__init__.py | 2 +- wakatime/cli.py | 2 +- wakatime/{base.py => main.py} | 31 +++++++------- wakatime/offlinequeue.py | 42 ++++++++++--------- wakatime/project.py | 4 +- wakatime/stats.py | 4 +- 10 files changed, 77 insertions(+), 67 deletions(-) rename tests/{test_wakatime_base.py => test_wakatime_main.py} (92%) rename wakatime/{base.py => main.py} (95%) diff --git a/tests/samples/output/test_help_contents b/tests/samples/output/test_help_contents index c89c924..16a29e5 100644 --- a/tests/samples/output/test_help_contents +++ b/tests/samples/output/test_help_contents @@ -1,6 +1,6 @@ usage: wakatime [-h] --file file [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] - [--notfile] [--proxy PROXY] [--project PROJECT] + [--entitytype ENTITY_TYPE] [--proxy PROXY] [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] [--hostname HOSTNAME] [--disableoffline] [--hidefilenames] [--exclude EXCLUDE] [--include INCLUDE] [--logfile LOGFILE] [--apiurl API_URL] @@ -22,9 +22,9 @@ optional arguments: --lineno LINENO optional line number; current line being edited --cursorpos CURSORPOS optional cursor position in the current file - --notfile when set, will accept any value for the file. for - example, a domain name or other item you want to log - time towards. + --entitytype ENTITY_TYPE + entity type for this heartbeat. can be one of "file", + "url", or "domain"; defaults to file. --proxy PROXY optional https proxy url; for example: https://user:pass@localhost:8080 --project PROJECT optional project name diff --git a/tests/samples/output/test_missing_config_file b/tests/samples/output/test_missing_config_file index 0284075..1c1e5b7 100644 --- a/tests/samples/output/test_missing_config_file +++ b/tests/samples/output/test_missing_config_file @@ -1,6 +1,6 @@ usage: wakatime [-h] --file file [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] - [--notfile] [--proxy PROXY] [--project PROJECT] + [--entitytype ENTITY_TYPE] [--proxy PROXY] [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] [--hostname HOSTNAME] [--disableoffline] [--hidefilenames] [--exclude EXCLUDE] [--include INCLUDE] [--logfile LOGFILE] [--apiurl API_URL] diff --git a/tests/test_wakatime_languages.py b/tests/test_wakatime_languages.py index 3e0dd0e..8c656c7 100644 --- a/tests/test_wakatime_languages.py +++ b/tests/test_wakatime_languages.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from wakatime.base import main +from wakatime.main import execute from wakatime.packages import requests import os @@ -38,7 +38,7 @@ class LanguagesTestCase(utils.TestCase): args = ['--file', entity, '--config', config, '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -48,16 +48,19 @@ class LanguagesTestCase(utils.TestCase): self.patched['wakatime.session_cache.SessionCache.save'].assert_not_called() heartbeat = { - 'language': 'Python', + 'language': u('Python'), 'lines': 26, 'entity': os.path.abspath(entity), - 'project': os.path.basename(os.path.abspath('.')), - 'dependencies': ['wakatime', 'os', 'mock', 'simplejson', 'django'], + 'project': u(os.path.basename(os.path.abspath('.'))), + 'dependencies': ANY, 'branch': os.environ.get('TRAVIS_COMMIT', ANY), 'time': float(now), 'type': 'file', } - stats = '{"cursorpos": null, "dependencies": ["wakatime", "os", "mock", "simplejson", "django"], "lines": 26, "lineno": null, "language": "Python"}' + stats = ANY + expected_dependencies = ['wakatime', 'mock', 'django', 'simplejson', 'os'] self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, stats, None) + for dep in expected_dependencies: + self.assertIn(dep, self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['dependencies']) self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called() diff --git a/tests/test_wakatime_base.py b/tests/test_wakatime_main.py similarity index 92% rename from tests/test_wakatime_base.py rename to tests/test_wakatime_main.py index 73dace9..894fc56 100644 --- a/tests/test_wakatime_base.py +++ b/tests/test_wakatime_main.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from wakatime.base import main +from wakatime.main import execute from wakatime.packages import requests import os @@ -30,7 +30,7 @@ class BaseTestCase(utils.TestCase): def test_help_contents(self): args = ['--help'] with self.assertRaises(SystemExit): - main(args) + execute(args) expected_stdout = open('tests/samples/output/test_help_contents').read() self.assertEquals(sys.stdout.getvalue(), expected_stdout) self.assertEquals(sys.stderr.getvalue(), '') @@ -45,7 +45,7 @@ class BaseTestCase(utils.TestCase): args = ['--file', 'tests/samples/twolinefile.txt', '--key', '123', '--config', 'tests/samples/sample.cfg'] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 0) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -60,7 +60,7 @@ class BaseTestCase(utils.TestCase): def test_missing_config_file(self): args = ['--file', 'tests/samples/emptyfile.txt', '--config', 'foo'] with self.assertRaises(SystemExit): - main(args) + execute(args) expected_stdout = u("Error: Could not read from config file foo\n") expected_stderr = open('tests/samples/output/test_missing_config_file').read() self.assertEquals(sys.stdout.getvalue(), expected_stdout) @@ -74,7 +74,7 @@ class BaseTestCase(utils.TestCase): self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response args = ['--file', 'tests/samples/emptyfile.txt', '--config', 'tests/samples/sample.cfg'] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 0) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -88,7 +88,7 @@ class BaseTestCase(utils.TestCase): def test_bad_config_file(self): args = ['--file', 'tests/samples/emptyfile.txt', '--config', 'tests/samples/bad_config.cfg'] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 103) self.assertIn('ParsingError', sys.stdout.getvalue()) self.assertEquals(sys.stderr.getvalue(), '') @@ -108,7 +108,7 @@ class BaseTestCase(utils.TestCase): args = ['--file', entity, '--key', '123', '--config', config, '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -126,7 +126,7 @@ class BaseTestCase(utils.TestCase): 'time': float(now), 'type': 'file', } - stats = '{"cursorpos": null, "dependencies": [], "lines": 2, "lineno": null, "language": "Text only"}' + stats = ANY self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, stats, None) self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called() @@ -142,7 +142,7 @@ class BaseTestCase(utils.TestCase): args = ['--file', entity, '--key', '123', '--config', config, '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -160,7 +160,7 @@ class BaseTestCase(utils.TestCase): 'time': float(now), 'type': 'file', } - stats = '{"cursorpos": null, "dependencies": [], "lines": 2, "lineno": null, "language": "Text only"}' + stats = ANY self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, stats, None) self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called() @@ -176,7 +176,7 @@ class BaseTestCase(utils.TestCase): '--config', 'tests/samples/paranoid.cfg', '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -194,7 +194,7 @@ class BaseTestCase(utils.TestCase): 'time': float(now), 'type': 'file', } - stats = '{"cursorpos": null, "dependencies": [], "lines": 2, "lineno": null, "language": "Text only"}' + stats = ANY self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, stats, None) self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called() @@ -210,7 +210,7 @@ class BaseTestCase(utils.TestCase): '--config', 'tests/samples/paranoid.cfg', '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -233,7 +233,7 @@ class BaseTestCase(utils.TestCase): args = ['--file', entity, '--alternate-project', 'xyz', '--config', config, '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -251,7 +251,7 @@ class BaseTestCase(utils.TestCase): 'time': float(now), 'type': 'file', } - stats = '{"cursorpos": null, "dependencies": [], "lines": 2, "lineno": null, "language": "Text only"}' + stats = ANY self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, stats, None) self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called() @@ -267,7 +267,7 @@ class BaseTestCase(utils.TestCase): args = ['--file', entity, '--project', 'xyz', '--config', config, '--time', now] - retval = main(args) + retval = execute(args) self.assertEquals(retval, 102) self.assertEquals(sys.stdout.getvalue(), '') self.assertEquals(sys.stderr.getvalue(), '') @@ -285,7 +285,7 @@ class BaseTestCase(utils.TestCase): 'time': float(now), 'type': 'file', } - stats = '{"cursorpos": null, "dependencies": [], "lines": 2, "lineno": null, "language": "Text only"}' + stats = ANY self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, stats, None) self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called() diff --git a/wakatime/__init__.py b/wakatime/__init__.py index e6793b3..adaf85b 100644 --- a/wakatime/__init__.py +++ b/wakatime/__init__.py @@ -14,4 +14,4 @@ __all__ = ['main'] -from .base import main +from .main import execute diff --git a/wakatime/cli.py b/wakatime/cli.py index 6144906..9952f3f 100644 --- a/wakatime/cli.py +++ b/wakatime/cli.py @@ -32,4 +32,4 @@ except (TypeError, ImportError): if __name__ == '__main__': - sys.exit(wakatime.main(sys.argv[1:])) + sys.exit(wakatime.execute(sys.argv[1:])) diff --git a/wakatime/base.py b/wakatime/main.py similarity index 95% rename from wakatime/base.py rename to wakatime/main.py index 4452bb2..be6e509 100644 --- a/wakatime/base.py +++ b/wakatime/main.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ - wakatime.base + wakatime.main ~~~~~~~~~~~~~ wakatime module entry point. @@ -109,9 +109,9 @@ def parseArguments(): help='optional line number; current line being edited') parser.add_argument('--cursorpos', dest='cursorpos', help='optional cursor position in the current file') - parser.add_argument('--notfile', dest='notfile', action='store_true', - help='when set, will accept any value for the file. for example, '+ - 'a domain name or other item you want to log time towards.') + parser.add_argument('--entitytype', dest='entity_type', + help='entity type for this heartbeat. can be one of "file", '+ + '"url", or "domain"; defaults to file.') parser.add_argument('--proxy', dest='proxy', help='optional https proxy url; for example: '+ 'https://user:pass@localhost:8080') @@ -168,6 +168,8 @@ def parseArguments(): args.key = default_key else: parser.error('Missing api key') + if not args.entity_type: + args.entity_type = 'file' if not args.exclude: args.exclude = [] if configs.has_option('settings', 'ignore'): @@ -263,7 +265,7 @@ def get_user_agent(plugin): def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, targetFile=None, - timestamp=None, isWrite=None, plugin=None, offline=None, notfile=False, + timestamp=None, isWrite=None, plugin=None, offline=None, entity_type='file', hidefilenames=None, proxy=None, api_url=None, **kwargs): """Sends heartbeat as POST request to WakaTime api server. """ @@ -274,9 +276,9 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, data = { 'time': timestamp, 'entity': targetFile, - 'type': 'file', + 'type': entity_type, } - if hidefilenames and targetFile is not None and not notfile: + if hidefilenames and targetFile is not None and entity_type == 'file': extension = u(os.path.splitext(data['entity'])[1]) data['entity'] = u('HIDDEN{0}').format(extension) if stats.get('lines'): @@ -379,7 +381,7 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, return False -def main(argv): +def execute(argv): sys.argv = ['wakatime'] + argv args, configs = parseArguments() @@ -395,14 +397,15 @@ def main(argv): )) return 0 - if os.path.isfile(args.targetFile) or args.notfile: + if args.entity_type != 'file' or os.path.isfile(args.targetFile): - stats = get_file_stats(args.targetFile, notfile=args.notfile, + stats = get_file_stats(args.targetFile, entity_type=args.entity_type, lineno=args.lineno, cursorpos=args.cursorpos) - project, branch = None, None - if not args.notfile: - project, branch = get_project_info(configs=configs, args=args) + project = args.project or args.alternate_project + branch = None + if args.entity_type == 'file': + project, branch = get_project_info(configs, args) kwargs = vars(args) kwargs['project'] = project @@ -428,7 +431,7 @@ def main(argv): plugin=heartbeat['plugin'], offline=args.offline, hidefilenames=args.hidefilenames, - notfile=args.notfile, + entity_type=heartbeat['type'], proxy=args.proxy, api_url=args.api_url, ) diff --git a/wakatime/offlinequeue.py b/wakatime/offlinequeue.py index 3ef3d30..09efe41 100644 --- a/wakatime/offlinequeue.py +++ b/wakatime/offlinequeue.py @@ -28,13 +28,15 @@ log = logging.getLogger('WakaTime') class Queue(object): - DB_FILE = os.path.join(os.path.expanduser('~'), '.wakatime.db') + db_file = os.path.join(os.path.expanduser('~'), '.wakatime.db') + table_name = 'heartbeat_1' def connect(self): - conn = sqlite3.connect(self.DB_FILE) + conn = sqlite3.connect(self.db_file) c = conn.cursor() - c.execute('''CREATE TABLE IF NOT EXISTS heartbeat ( - file text, + c.execute('''CREATE TABLE IF NOT EXISTS {0} ( + entity text, + type text, time real, project text, branch text, @@ -42,7 +44,7 @@ class Queue(object): stats text, misc text, plugin text) - ''') + '''.format(self.table_name)) return (conn, c) @@ -52,7 +54,8 @@ class Queue(object): try: conn, c = self.connect() heartbeat = { - 'file': u(data.get('entity')), + 'entity': u(data.get('entity')), + 'type': u(data.get('type')), 'time': data.get('time'), 'project': u(data.get('project')), 'branch': u(data.get('branch')), @@ -61,7 +64,7 @@ class Queue(object): 'misc': u(misc), 'plugin': u(plugin), } - c.execute('INSERT INTO heartbeat VALUES (:file,:time,:project,:branch,:is_write,:stats,:misc,:plugin)', heartbeat) + c.execute('INSERT INTO {0} VALUES (:entity,:type,:time,:project,:branch,:is_write,:stats,:misc,:plugin)'.format(self.table_name), heartbeat) conn.commit() conn.close() except sqlite3.Error: @@ -83,13 +86,13 @@ class Queue(object): while loop and tries > -1: try: c.execute('BEGIN IMMEDIATE') - c.execute('SELECT * FROM heartbeat LIMIT 1') + c.execute('SELECT * FROM {0} LIMIT 1'.format(self.table_name)) row = c.fetchone() if row is not None: values = [] clauses = [] index = 0 - for row_name in ['file', 'time', 'project', 'branch', 'is_write']: + for row_name in ['entity', 'type', 'time', 'project', 'branch', 'is_write']: if row[index] is not None: clauses.append('{0}=?'.format(row_name)) values.append(row[index]) @@ -97,20 +100,21 @@ class Queue(object): clauses.append('{0} IS NULL'.format(row_name)) index += 1 if len(values) > 0: - c.execute('DELETE FROM heartbeat WHERE {0}'.format(' AND '.join(clauses)), values) + c.execute('DELETE FROM {0} WHERE {1}'.format(self.table_name, ' AND '.join(clauses)), values) else: - c.execute('DELETE FROM heartbeat WHERE {0}'.format(' AND '.join(clauses))) + c.execute('DELETE FROM {0} WHERE {1}'.format(self.table_name, ' AND '.join(clauses))) conn.commit() if row is not None: heartbeat = { - 'file': row[0], - 'time': row[1], - 'project': row[2], - 'branch': row[3], - 'is_write': True if row[4] is 1 else False, - 'stats': row[5], - 'misc': row[6], - 'plugin': row[7], + 'entity': row[0], + 'type': row[1], + 'time': row[2], + 'project': row[3], + 'branch': row[4], + 'is_write': True if row[5] is 1 else False, + 'stats': row[6], + 'misc': row[7], + 'plugin': row[8], } loop = False except sqlite3.Error: diff --git a/wakatime/project.py b/wakatime/project.py index bb8b503..9998649 100644 --- a/wakatime/project.py +++ b/wakatime/project.py @@ -33,7 +33,7 @@ REV_CONTROL_PLUGINS = [ ] -def get_project_info(configs=None, args=None): +def get_project_info(configs, args): """Find the current project and branch. First looks for a .wakatime-project file. Second, uses the --project arg. @@ -52,7 +52,7 @@ def get_project_info(configs=None, args=None): project = plugin_cls(args.targetFile, configs=plugin_configs) if project.process(): - project_name = project.name() + project_name = project_name or project.name() branch_name = project.branch() break diff --git a/wakatime/stats.py b/wakatime/stats.py index db424c0..e02a0eb 100644 --- a/wakatime/stats.py +++ b/wakatime/stats.py @@ -153,8 +153,8 @@ def number_lines_in_file(file_name): return lines -def get_file_stats(file_name, notfile=False, lineno=None, cursorpos=None): - if notfile: +def get_file_stats(file_name, entity_type='file', lineno=None, cursorpos=None): + if entity_type != 'file': stats = { 'language': None, 'dependencies': [],