New --category argument
This commit is contained in:
parent
6022f1f531
commit
1fe41b6677
9 changed files with 284 additions and 143 deletions
|
@ -1,10 +1,11 @@
|
||||||
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
||||||
[--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify]
|
[--entity-type ENTITY_TYPE] [--category CATEGORY]
|
||||||
[--project PROJECT] [--alternate-project ALTERNATE_PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--language LANGUAGE] [--hostname HOSTNAME]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--disable-offline] [--hide-filenames] [--exclude EXCLUDE]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude EXCLUDE] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
wakatime: error: Missing api key. Find your api key from wakatime.com/settings.
|
[--config CONFIG] [--verbose] [--version]
|
||||||
|
wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key.
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
||||||
[--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify]
|
[--entity-type ENTITY_TYPE] [--category CATEGORY]
|
||||||
[--project PROJECT] [--alternate-project ALTERNATE_PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--language LANGUAGE] [--hostname HOSTNAME]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--disable-offline] [--hide-filenames] [--exclude EXCLUDE]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude EXCLUDE] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
wakatime: error: Missing api key. Find your api key from wakatime.com/settings.
|
[--config CONFIG] [--verbose] [--version]
|
||||||
|
wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key.
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
||||||
[--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify]
|
[--entity-type ENTITY_TYPE] [--category CATEGORY]
|
||||||
[--project PROJECT] [--alternate-project ALTERNATE_PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--language LANGUAGE] [--hostname HOSTNAME]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--disable-offline] [--hide-filenames] [--exclude EXCLUDE]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude EXCLUDE] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
|
[--config CONFIG] [--verbose] [--version]
|
||||||
wakatime: error: argument --timeout: invalid int value: 'abc'
|
wakatime: error: argument --timeout: invalid int value: 'abc'
|
||||||
|
|
|
@ -1,62 +1,68 @@
|
||||||
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN]
|
||||||
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
[--time time] [--lineno LINENO] [--cursorpos CURSORPOS]
|
||||||
[--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify]
|
[--entity-type ENTITY_TYPE] [--category CATEGORY]
|
||||||
[--project PROJECT] [--alternate-project ALTERNATE_PROJECT]
|
[--proxy PROXY] [--no-ssl-verify] [--project PROJECT]
|
||||||
[--language LANGUAGE] [--hostname HOSTNAME]
|
[--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE]
|
||||||
[--disable-offline] [--hide-filenames] [--exclude EXCLUDE]
|
[--hostname HOSTNAME] [--disable-offline] [--hide-filenames]
|
||||||
[--include INCLUDE] [--include-only-with-project-file]
|
[--exclude EXCLUDE] [--include INCLUDE]
|
||||||
[--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL]
|
[--include-only-with-project-file] [--extra-heartbeats]
|
||||||
[--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version]
|
[--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT]
|
||||||
|
[--config CONFIG] [--verbose] [--version]
|
||||||
|
|
||||||
Common interface for the WakaTime api.
|
Common interface for the WakaTime api.
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--entity FILE absolute path to file for the heartbeat; can also be a
|
--entity FILE Absolute path to file for the heartbeat. Can also be a
|
||||||
url, domain, or app when --entity-type is not file
|
url, domain, or app when --entity-type is not file.
|
||||||
--key KEY your wakatime api key; uses api_key from
|
--key KEY Your wakatime api key; uses api_key from
|
||||||
~/.wakatime.cfg by default
|
~/.wakatime.cfg by default.
|
||||||
--write when set, tells api this heartbeat was triggered from
|
--write When set, tells api this heartbeat was triggered from
|
||||||
writing to a file
|
writing to a file.
|
||||||
--plugin PLUGIN optional text editor plugin name and version for User-
|
--plugin PLUGIN Optional text editor plugin name and version for User-
|
||||||
Agent header
|
Agent header.
|
||||||
--time time optional floating-point unix epoch timestamp; uses
|
--time time Optional floating-point unix epoch timestamp. Uses
|
||||||
current time by default
|
current time by default.
|
||||||
--lineno LINENO optional line number; current line being edited
|
--lineno LINENO Optional line number. This is the current line being
|
||||||
|
edited.
|
||||||
--cursorpos CURSORPOS
|
--cursorpos CURSORPOS
|
||||||
optional cursor position in the current file
|
Optional cursor position in the current file.
|
||||||
--entity-type ENTITY_TYPE
|
--entity-type ENTITY_TYPE
|
||||||
entity type for this heartbeat. can be one of "file",
|
Entity type for this heartbeat. Can be "file",
|
||||||
"domain", or "app"; defaults to file.
|
"domain", or "app". Defaults to "file".
|
||||||
--proxy PROXY optional proxy configuration. Supports HTTPS and SOCKS
|
--category CATEGORY Category of this heartbeat activity. Can be "coding",
|
||||||
|
"building", "debugging", "running tests", "browsing",
|
||||||
|
or "code reviewing". Defaults to "coding".
|
||||||
|
--proxy PROXY Optional proxy configuration. Supports HTTPS and SOCKS
|
||||||
proxies. For example: https://user:pass@host:port or
|
proxies. For example: https://user:pass@host:port or
|
||||||
socks5://user:pass@host:port or domain\user:pass
|
socks5://user:pass@host:port or domain\user:pass
|
||||||
--no-ssl-verify disables SSL certificate verification for HTTPS
|
--no-ssl-verify Disables SSL certificate verification for HTTPS
|
||||||
requests. By default, SSL certificates are verified.
|
requests. By default, SSL certificates are verified.
|
||||||
--project PROJECT optional project name
|
--project PROJECT Optional project name.
|
||||||
--alternate-project ALTERNATE_PROJECT
|
--alternate-project ALTERNATE_PROJECT
|
||||||
optional alternate project name; auto-discovered
|
Optional alternate project name. Auto-discovered
|
||||||
project takes priority
|
project takes priority.
|
||||||
--language LANGUAGE optional language name; if valid, takes priority over
|
--language LANGUAGE Optional language name. If valid, takes priority over
|
||||||
auto-detected language
|
auto-detected language.
|
||||||
--hostname HOSTNAME hostname of current machine.
|
--hostname HOSTNAME Hostname of current machine.
|
||||||
--disable-offline disables offline time logging instead of queuing
|
--disable-offline Disables offline time logging instead of queuing
|
||||||
logged time
|
logged time.
|
||||||
--hide-filenames obfuscate filenames; will not send file names to api
|
--hide-filenames Obfuscate filenames. Will not send file names to api.
|
||||||
--exclude EXCLUDE filename patterns to exclude from logging; POSIX regex
|
--exclude EXCLUDE Filename patterns to exclude from logging. POSIX regex
|
||||||
syntax; can be used more than once
|
syntax. Can be used more than once.
|
||||||
--include INCLUDE filename patterns to log; when used in combination
|
--include INCLUDE Filename patterns to log. When used in combination
|
||||||
with --exclude, files matching include will still be
|
with --exclude, files matching include will still be
|
||||||
logged; POSIX regex syntax; can be used more than once
|
logged. POSIX regex syntax. Can be used more than
|
||||||
|
once.
|
||||||
--include-only-with-project-file
|
--include-only-with-project-file
|
||||||
disables tracking folders unless they contain a
|
Disables tracking folders unless they contain a
|
||||||
.wakatime-project file; defaults to false
|
.wakatime-project file. Defaults to false.
|
||||||
--extra-heartbeats reads extra heartbeats from STDIN as a JSON array
|
--extra-heartbeats Reads extra heartbeats from STDIN as a JSON array
|
||||||
until EOF
|
until EOF.
|
||||||
--log-file LOG_FILE defaults to ~/.wakatime.log
|
--log-file LOG_FILE Defaults to ~/.wakatime.log.
|
||||||
--api-url API_URL heartbeats api url; for debugging with a local server
|
--api-url API_URL Heartbeats api url. For debugging with a local server.
|
||||||
--timeout TIMEOUT number of seconds to wait when sending heartbeats to
|
--timeout TIMEOUT Number of seconds to wait when sending heartbeats to
|
||||||
api; defaults to 60 seconds
|
api. Defaults to 60 seconds.
|
||||||
--config CONFIG defaults to ~/.wakatime.cfg
|
--config CONFIG Defaults to ~/.wakatime.cfg.
|
||||||
--verbose turns on debug messages in log file
|
--verbose Turns on debug messages in log file.
|
||||||
--version show program's version number and exit
|
--version show program's version number and exit
|
||||||
|
|
|
@ -244,7 +244,7 @@ class ArgumentsTestCase(TestCase):
|
||||||
|
|
||||||
self.assertEquals(int(str(e.exception)), AUTH_ERROR)
|
self.assertEquals(int(str(e.exception)), AUTH_ERROR)
|
||||||
self.assertEquals(sys.stdout.getvalue(), '')
|
self.assertEquals(sys.stdout.getvalue(), '')
|
||||||
expected = 'error: Missing api key. Find your api key from wakatime.com/settings.'
|
expected = 'error: Missing api key. Find your api key from wakatime.com/settings/api-key.'
|
||||||
self.assertIn(expected, sys.stderr.getvalue())
|
self.assertIn(expected, sys.stderr.getvalue())
|
||||||
|
|
||||||
log_output = u("\n").join([u(' ').join(x) for x in logs.actual()])
|
log_output = u("\n").join([u(' ').join(x) for x in logs.actual()])
|
||||||
|
@ -272,7 +272,7 @@ class ArgumentsTestCase(TestCase):
|
||||||
|
|
||||||
self.assertEquals(int(str(e.exception)), AUTH_ERROR)
|
self.assertEquals(int(str(e.exception)), AUTH_ERROR)
|
||||||
self.assertEquals(sys.stdout.getvalue(), '')
|
self.assertEquals(sys.stdout.getvalue(), '')
|
||||||
expected = 'error: Invalid api key. Find your api key from wakatime.com/settings.'
|
expected = 'error: Invalid api key. Find your api key from wakatime.com/settings/api-key.'
|
||||||
self.assertIn(expected, sys.stderr.getvalue())
|
self.assertIn(expected, sys.stderr.getvalue())
|
||||||
|
|
||||||
log_output = u("\n").join([u(' ').join(x) for x in logs.actual()])
|
log_output = u("\n").join([u(' ').join(x) for x in logs.actual()])
|
||||||
|
@ -488,6 +488,97 @@ class ArgumentsTestCase(TestCase):
|
||||||
self.assertOfflineHeartbeatsSynced()
|
self.assertOfflineHeartbeatsSynced()
|
||||||
self.assertSessionCacheSaved()
|
self.assertSessionCacheSaved()
|
||||||
|
|
||||||
|
@log_capture()
|
||||||
|
def test_valid_categories(self, logs):
|
||||||
|
logging.disable(logging.NOTSET)
|
||||||
|
|
||||||
|
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = CustomResponse()
|
||||||
|
|
||||||
|
with TemporaryDirectory() as tempdir:
|
||||||
|
|
||||||
|
entity = 'tests/samples/codefiles/emptyfile.txt'
|
||||||
|
shutil.copy(entity, os.path.join(tempdir, 'emptyfile.txt'))
|
||||||
|
entity = os.path.realpath(os.path.join(tempdir, 'emptyfile.txt'))
|
||||||
|
config = 'tests/samples/configs/good_config.cfg'
|
||||||
|
now = u(int(time.time()))
|
||||||
|
|
||||||
|
valid_categories = [
|
||||||
|
'coding',
|
||||||
|
'building',
|
||||||
|
'debugging',
|
||||||
|
'running tests',
|
||||||
|
'browsing',
|
||||||
|
'code reviewing',
|
||||||
|
]
|
||||||
|
|
||||||
|
for category in valid_categories:
|
||||||
|
args = ['--entity', entity, '--category', category, '--config', config, '--time', now]
|
||||||
|
|
||||||
|
self.resetMocks()
|
||||||
|
retval = execute(args)
|
||||||
|
|
||||||
|
self.assertEquals(retval, SUCCESS)
|
||||||
|
self.assertNothingPrinted()
|
||||||
|
self.assertNothingLogged(logs)
|
||||||
|
|
||||||
|
heartbeat = {
|
||||||
|
'entity': u(entity),
|
||||||
|
'time': float(now),
|
||||||
|
'type': 'file',
|
||||||
|
'category': category,
|
||||||
|
'cursorpos': None,
|
||||||
|
'language': 'Text only',
|
||||||
|
'lines': 0,
|
||||||
|
'is_write': False,
|
||||||
|
'dependencies': [],
|
||||||
|
'user_agent': ANY,
|
||||||
|
}
|
||||||
|
self.assertHeartbeatSent(heartbeat)
|
||||||
|
|
||||||
|
self.assertHeartbeatNotSavedOffline()
|
||||||
|
self.assertOfflineHeartbeatsSynced()
|
||||||
|
self.assertSessionCacheSaved()
|
||||||
|
|
||||||
|
@log_capture()
|
||||||
|
def test_invalid_category(self, logs):
|
||||||
|
logging.disable(logging.NOTSET)
|
||||||
|
|
||||||
|
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = CustomResponse()
|
||||||
|
|
||||||
|
with TemporaryDirectory() as tempdir:
|
||||||
|
|
||||||
|
entity = 'tests/samples/codefiles/emptyfile.txt'
|
||||||
|
shutil.copy(entity, os.path.join(tempdir, 'emptyfile.txt'))
|
||||||
|
entity = os.path.realpath(os.path.join(tempdir, 'emptyfile.txt'))
|
||||||
|
config = 'tests/samples/configs/good_config.cfg'
|
||||||
|
now = u(int(time.time()))
|
||||||
|
category = 'foobar'
|
||||||
|
|
||||||
|
args = ['--entity', entity, '--category', category, '--config', config, '--time', now]
|
||||||
|
retval = execute(args)
|
||||||
|
|
||||||
|
self.assertEquals(retval, SUCCESS)
|
||||||
|
self.assertNothingPrinted()
|
||||||
|
self.assertNothingLogged(logs)
|
||||||
|
|
||||||
|
heartbeat = {
|
||||||
|
'entity': u(entity),
|
||||||
|
'time': float(now),
|
||||||
|
'type': 'file',
|
||||||
|
'category': None,
|
||||||
|
'cursorpos': None,
|
||||||
|
'language': 'Text only',
|
||||||
|
'lines': 0,
|
||||||
|
'is_write': False,
|
||||||
|
'dependencies': [],
|
||||||
|
'user_agent': ANY,
|
||||||
|
}
|
||||||
|
self.assertHeartbeatSent(heartbeat)
|
||||||
|
|
||||||
|
self.assertHeartbeatNotSavedOffline()
|
||||||
|
self.assertOfflineHeartbeatsSynced()
|
||||||
|
self.assertSessionCacheSaved()
|
||||||
|
|
||||||
@log_capture()
|
@log_capture()
|
||||||
def test_old_alternate_language_argument_still_supported(self, logs):
|
def test_old_alternate_language_argument_still_supported(self, logs):
|
||||||
logging.disable(logging.NOTSET)
|
logging.disable(logging.NOTSET)
|
||||||
|
|
|
@ -276,7 +276,7 @@ class MainTestCase(utils.TestCase):
|
||||||
|
|
||||||
self.assertEquals(int(str(e.exception)), AUTH_ERROR)
|
self.assertEquals(int(str(e.exception)), AUTH_ERROR)
|
||||||
self.assertEquals(sys.stdout.getvalue(), '')
|
self.assertEquals(sys.stdout.getvalue(), '')
|
||||||
expected = 'error: Invalid api key. Find your api key from wakatime.com/settings.'
|
expected = 'error: Invalid api key. Find your api key from wakatime.com/settings/api-key.'
|
||||||
self.assertIn(expected, sys.stderr.getvalue())
|
self.assertIn(expected, sys.stderr.getvalue())
|
||||||
|
|
||||||
log_output = u("\n").join([u(' ').join(x) for x in logs.actual()])
|
log_output = u("\n").join([u(' ').join(x) for x in logs.actual()])
|
||||||
|
|
|
@ -144,6 +144,10 @@ class TestCase(unittest.TestCase):
|
||||||
def getLogOutput(self, logs):
|
def getLogOutput(self, logs):
|
||||||
return u("\n").join([u(' ').join(x) for x in logs.actual()])
|
return u("\n").join([u(' ').join(x) for x in logs.actual()])
|
||||||
|
|
||||||
|
def resetMocks(self):
|
||||||
|
for key in self.patched:
|
||||||
|
self.patched[key].reset_mock()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isPy35OrNewer(self):
|
def isPy35OrNewer(self):
|
||||||
if sys.version_info[0] > 3:
|
if sys.version_info[0] > 3:
|
||||||
|
|
|
@ -52,100 +52,122 @@ def parse_arguments():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# define supported command line arguments
|
# define supported command line arguments
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description='Common interface for the ' +
|
||||||
description='Common interface for the WakaTime api.')
|
'WakaTime api.')
|
||||||
parser.add_argument('--entity', dest='entity', metavar='FILE',
|
parser.add_argument('--entity', dest='entity', metavar='FILE',
|
||||||
action=FileAction,
|
action=FileAction,
|
||||||
help='absolute path to file for the heartbeat; can also be a '+
|
help='Absolute path to file for the heartbeat. Can ' +
|
||||||
'url, domain, or app when --entity-type is not file')
|
'also be a url, domain, or app when ' +
|
||||||
|
'--entity-type is not file.')
|
||||||
parser.add_argument('--file', dest='file', action=FileAction,
|
parser.add_argument('--file', dest='file', action=FileAction,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
|
parser.add_argument('--key', dest='key', action=StoreWithoutQuotes,
|
||||||
help='your wakatime api key; uses api_key from '+
|
help='Your wakatime api key; uses api_key from ' +
|
||||||
'~/.wakatime.cfg by default')
|
'~/.wakatime.cfg by default.')
|
||||||
parser.add_argument('--write', dest='is_write',
|
parser.add_argument('--write', dest='is_write', action='store_true',
|
||||||
action='store_true',
|
help='When set, tells api this heartbeat was ' +
|
||||||
help='when set, tells api this heartbeat was triggered from '+
|
'triggered from writing to a file.')
|
||||||
'writing to a file')
|
|
||||||
parser.add_argument('--plugin', dest='plugin', action=StoreWithoutQuotes,
|
parser.add_argument('--plugin', dest='plugin', action=StoreWithoutQuotes,
|
||||||
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('--time', dest='timestamp', metavar='time',
|
parser.add_argument('--time', dest='timestamp', metavar='time',
|
||||||
type=float, action=StoreWithoutQuotes,
|
type=float, action=StoreWithoutQuotes,
|
||||||
help='optional floating-point unix epoch timestamp; '+
|
help='Optional floating-point unix epoch timestamp. ' +
|
||||||
'uses current time by default')
|
'Uses current time by default.')
|
||||||
parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
|
parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes,
|
||||||
help='optional line number; current line being edited')
|
help='Optional line number. This is the current ' +
|
||||||
parser.add_argument('--cursorpos', dest='cursorpos', action=StoreWithoutQuotes,
|
'line being edited.')
|
||||||
help='optional cursor position in the current file')
|
parser.add_argument('--cursorpos', dest='cursorpos',
|
||||||
parser.add_argument('--entity-type', dest='entity_type', action=StoreWithoutQuotes,
|
action=StoreWithoutQuotes,
|
||||||
help='entity type for this heartbeat. can be one of "file", '+
|
help='Optional cursor position in the current file.')
|
||||||
'"domain", or "app"; defaults to file.')
|
parser.add_argument('--entity-type', dest='entity_type',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Entity type for this heartbeat. Can be ' +
|
||||||
|
'"file", "domain", or "app". Defaults to "file".')
|
||||||
|
parser.add_argument('--category', dest='category',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Category of this heartbeat activity. Can be ' +
|
||||||
|
'"coding", "building", "debugging", ' +
|
||||||
|
'"running tests", "browsing", or ' +
|
||||||
|
'"code reviewing". Defaults to "coding".')
|
||||||
parser.add_argument('--proxy', dest='proxy', action=StoreWithoutQuotes,
|
parser.add_argument('--proxy', dest='proxy', action=StoreWithoutQuotes,
|
||||||
help='optional proxy configuration. Supports HTTPS '+
|
help='Optional proxy configuration. Supports HTTPS '+
|
||||||
'and SOCKS proxies. For example: '+
|
'and SOCKS proxies. For example: '+
|
||||||
'https://user:pass@host:port or '+
|
'https://user:pass@host:port or '+
|
||||||
'socks5://user:pass@host:port or ' +
|
'socks5://user:pass@host:port or ' +
|
||||||
'domain\\user:pass')
|
'domain\\user:pass')
|
||||||
parser.add_argument('--no-ssl-verify', dest='nosslverify',
|
parser.add_argument('--no-ssl-verify', dest='nosslverify',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='disables SSL certificate verification for HTTPS '+
|
help='Disables SSL certificate verification for HTTPS '+
|
||||||
'requests. By default, SSL certificates are verified.')
|
'requests. By default, SSL certificates are ' +
|
||||||
|
'verified.')
|
||||||
parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
|
parser.add_argument('--project', dest='project', action=StoreWithoutQuotes,
|
||||||
help='optional project name')
|
help='Optional project name.')
|
||||||
parser.add_argument('--alternate-project', dest='alternate_project', action=StoreWithoutQuotes,
|
parser.add_argument('--alternate-project', dest='alternate_project',
|
||||||
help='optional alternate project name; auto-discovered project '+
|
action=StoreWithoutQuotes,
|
||||||
'takes priority')
|
help='Optional alternate project name. ' +
|
||||||
parser.add_argument('--alternate-language', dest='alternate_language', action=StoreWithoutQuotes,
|
'Auto-discovered project takes priority.')
|
||||||
|
parser.add_argument('--alternate-language', dest='alternate_language',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--language', dest='language', action=StoreWithoutQuotes,
|
parser.add_argument('--language', dest='language',
|
||||||
help='optional language name; if valid, takes priority over '+
|
action=StoreWithoutQuotes,
|
||||||
'auto-detected language')
|
help='Optional language name. If valid, takes ' +
|
||||||
parser.add_argument('--hostname', dest='hostname', action=StoreWithoutQuotes, help='hostname of '+
|
'priority over auto-detected language.')
|
||||||
'current machine.')
|
parser.add_argument('--hostname', dest='hostname',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Hostname of current machine.')
|
||||||
parser.add_argument('--disable-offline', dest='offline',
|
parser.add_argument('--disable-offline', dest='offline',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
help='disables offline time logging instead of queuing logged time')
|
help='Disables offline time logging instead of ' +
|
||||||
|
'queuing logged time.')
|
||||||
parser.add_argument('--disableoffline', dest='offline_deprecated',
|
parser.add_argument('--disableoffline', dest='offline_deprecated',
|
||||||
action='store_true', help=argparse.SUPPRESS)
|
action='store_true',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--hide-filenames', dest='hide_filenames',
|
parser.add_argument('--hide-filenames', dest='hide_filenames',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='obfuscate filenames; will not send file names to api')
|
help='Obfuscate filenames. Will not send file names ' +
|
||||||
|
'to api.')
|
||||||
parser.add_argument('--hidefilenames', dest='hidefilenames',
|
parser.add_argument('--hidefilenames', dest='hidefilenames',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--exclude', dest='exclude', action='append',
|
parser.add_argument('--exclude', dest='exclude', action='append',
|
||||||
help='filename patterns to exclude from logging; POSIX regex '+
|
help='Filename patterns to exclude from logging. ' +
|
||||||
'syntax; can be used more than once')
|
'POSIX regex syntax. Can be used more than once.')
|
||||||
parser.add_argument('--include', dest='include', action='append',
|
parser.add_argument('--include', dest='include', action='append',
|
||||||
help='filename patterns to log; when used in combination with '+
|
help='Filename patterns to log. When used in ' +
|
||||||
'--exclude, files matching include will still be logged; '+
|
'combination with --exclude, files matching ' +
|
||||||
'POSIX regex syntax; can be used more than once')
|
'include will still be logged. POSIX regex ' +
|
||||||
|
'syntax. Can be used more than once.')
|
||||||
parser.add_argument('--include-only-with-project-file',
|
parser.add_argument('--include-only-with-project-file',
|
||||||
dest='include_only_with_project_file',
|
dest='include_only_with_project_file',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='disables tracking folders unless they contain '+
|
help='Disables tracking folders unless they contain '+
|
||||||
'a .wakatime-project file; defaults to false')
|
'a .wakatime-project file. Defaults to false.')
|
||||||
parser.add_argument('--ignore', dest='ignore', action='append',
|
parser.add_argument('--ignore', dest='ignore', action='append',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--extra-heartbeats', dest='extra_heartbeats',
|
parser.add_argument('--extra-heartbeats', dest='extra_heartbeats',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='reads extra heartbeats from STDIN as a JSON array until EOF')
|
help='Reads extra heartbeats from STDIN as a JSON ' +
|
||||||
parser.add_argument('--log-file', dest='log_file', action=StoreWithoutQuotes,
|
'array until EOF.')
|
||||||
help='defaults to ~/.wakatime.log')
|
parser.add_argument('--log-file', dest='log_file',
|
||||||
|
action=StoreWithoutQuotes,
|
||||||
|
help='Defaults to ~/.wakatime.log.')
|
||||||
parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
|
parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
|
parser.add_argument('--api-url', dest='api_url', action=StoreWithoutQuotes,
|
||||||
help='heartbeats api url; for debugging with a local server')
|
help='Heartbeats api url. For debugging with a ' +
|
||||||
|
'local server.')
|
||||||
parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
|
parser.add_argument('--apiurl', dest='apiurl', action=StoreWithoutQuotes,
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
parser.add_argument('--timeout', dest='timeout', type=int, action=StoreWithoutQuotes,
|
parser.add_argument('--timeout', dest='timeout', type=int,
|
||||||
help='number of seconds to wait when sending heartbeats to api; '+
|
action=StoreWithoutQuotes,
|
||||||
'defaults to 60 seconds')
|
help='Number of seconds to wait when sending ' +
|
||||||
|
'heartbeats to api. Defaults to 60 seconds.')
|
||||||
parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
|
parser.add_argument('--config', dest='config', action=StoreWithoutQuotes,
|
||||||
help='defaults to ~/.wakatime.cfg')
|
help='Defaults to ~/.wakatime.cfg.')
|
||||||
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
parser.add_argument('--verbose', dest='verbose', action='store_true',
|
||||||
help='turns on debug messages in log file')
|
help='Turns on debug messages in log file.')
|
||||||
parser.add_argument('--version', action='version', version=__version__)
|
parser.add_argument('--version', action='version', version=__version__)
|
||||||
|
|
||||||
# parse command line arguments
|
# parse command line arguments
|
||||||
|
@ -172,14 +194,14 @@ def parse_arguments():
|
||||||
args.key = default_key
|
args.key = default_key
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
parser.error('Missing api key. Find your api key from wakatime.com/settings.')
|
parser.error('Missing api key. Find your api key from wakatime.com/settings/api-key.')
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise SystemExit(AUTH_ERROR)
|
raise SystemExit(AUTH_ERROR)
|
||||||
|
|
||||||
is_valid = not not re.match(r'^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$', args.key, re.I)
|
is_valid = not not re.match(r'^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$', args.key, re.I)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
try:
|
try:
|
||||||
parser.error('Invalid api key. Find your api key from wakatime.com/settings.')
|
parser.error('Invalid api key. Find your api key from wakatime.com/settings/api-key.')
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
raise SystemExit(AUTH_ERROR)
|
raise SystemExit(AUTH_ERROR)
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Heartbeat(object):
|
||||||
time = None
|
time = None
|
||||||
entity = None
|
entity = None
|
||||||
type = None
|
type = None
|
||||||
|
category = None
|
||||||
is_write = None
|
is_write = None
|
||||||
project = None
|
project = None
|
||||||
branch = None
|
branch = None
|
||||||
|
@ -58,6 +59,18 @@ class Heartbeat(object):
|
||||||
if self.type not in ['file', 'domain', 'app']:
|
if self.type not in ['file', 'domain', 'app']:
|
||||||
self.type = 'file'
|
self.type = 'file'
|
||||||
|
|
||||||
|
self.category = data.get('category')
|
||||||
|
allowed_categories = [
|
||||||
|
'coding',
|
||||||
|
'building',
|
||||||
|
'debugging',
|
||||||
|
'running tests',
|
||||||
|
'browsing',
|
||||||
|
'code reviewing'
|
||||||
|
]
|
||||||
|
if self.category not in allowed_categories:
|
||||||
|
self.category = None
|
||||||
|
|
||||||
if not _clone:
|
if not _clone:
|
||||||
exclude = self._excluded_by_pattern()
|
exclude = self._excluded_by_pattern()
|
||||||
if exclude:
|
if exclude:
|
||||||
|
@ -155,6 +168,7 @@ class Heartbeat(object):
|
||||||
'time': self.time,
|
'time': self.time,
|
||||||
'entity': self._unicode(self.entity),
|
'entity': self._unicode(self.entity),
|
||||||
'type': self.type,
|
'type': self.type,
|
||||||
|
'category': self.category,
|
||||||
'is_write': self.is_write,
|
'is_write': self.is_write,
|
||||||
'project': self._unicode(self.project),
|
'project': self._unicode(self.project),
|
||||||
'branch': self._unicode(self.branch),
|
'branch': self._unicode(self.branch),
|
||||||
|
@ -170,9 +184,10 @@ class Heartbeat(object):
|
||||||
return self.dict().items()
|
return self.dict().items()
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return u('{time}-{type}-{project}-{branch}-{entity}-{is_write}').format(
|
return u('{time}-{type}-{category}-{project}-{branch}-{entity}-{is_write}').format(
|
||||||
time=self.time,
|
time=self.time,
|
||||||
type=self.type,
|
type=self.type,
|
||||||
|
category=self.category,
|
||||||
project=self._unicode(self.project),
|
project=self._unicode(self.project),
|
||||||
branch=self._unicode(self.branch),
|
branch=self._unicode(self.branch),
|
||||||
entity=self._unicode(self.entity),
|
entity=self._unicode(self.entity),
|
||||||
|
|
Loading…
Reference in a new issue