diff --git a/tests/samples/output/configs_test_config_file_not_passed_in_command_line_args b/tests/samples/output/configs_test_config_file_not_passed_in_command_line_args index b417a9a..5c91cbf 100644 --- a/tests/samples/output/configs_test_config_file_not_passed_in_command_line_args +++ b/tests/samples/output/configs_test_config_file_not_passed_in_command_line_args @@ -1,10 +1,11 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] - [--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify] - [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] - [--language LANGUAGE] [--hostname HOSTNAME] - [--disable-offline] [--hide-filenames] [--exclude EXCLUDE] - [--include INCLUDE] [--include-only-with-project-file] - [--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL] - [--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version] -wakatime: error: Missing api key. Find your api key from wakatime.com/settings. + [--entity-type ENTITY_TYPE] [--category CATEGORY] + [--proxy PROXY] [--no-ssl-verify] [--project PROJECT] + [--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE] + [--hostname HOSTNAME] [--disable-offline] [--hide-filenames] + [--exclude EXCLUDE] [--include INCLUDE] + [--include-only-with-project-file] [--extra-heartbeats] + [--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT] + [--config CONFIG] [--verbose] [--version] +wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key. diff --git a/tests/samples/output/configs_test_missing_config_file b/tests/samples/output/configs_test_missing_config_file index b417a9a..5c91cbf 100644 --- a/tests/samples/output/configs_test_missing_config_file +++ b/tests/samples/output/configs_test_missing_config_file @@ -1,10 +1,11 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] - [--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify] - [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] - [--language LANGUAGE] [--hostname HOSTNAME] - [--disable-offline] [--hide-filenames] [--exclude EXCLUDE] - [--include INCLUDE] [--include-only-with-project-file] - [--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL] - [--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version] -wakatime: error: Missing api key. Find your api key from wakatime.com/settings. + [--entity-type ENTITY_TYPE] [--category CATEGORY] + [--proxy PROXY] [--no-ssl-verify] [--project PROJECT] + [--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE] + [--hostname HOSTNAME] [--disable-offline] [--hide-filenames] + [--exclude EXCLUDE] [--include INCLUDE] + [--include-only-with-project-file] [--extra-heartbeats] + [--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT] + [--config CONFIG] [--verbose] [--version] +wakatime: error: Missing api key. Find your api key from wakatime.com/settings/api-key. diff --git a/tests/samples/output/main_test_timeout_passed_via_command_line b/tests/samples/output/main_test_timeout_passed_via_command_line index 8f52543..32af829 100644 --- a/tests/samples/output/main_test_timeout_passed_via_command_line +++ b/tests/samples/output/main_test_timeout_passed_via_command_line @@ -1,10 +1,11 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] - [--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify] - [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] - [--language LANGUAGE] [--hostname HOSTNAME] - [--disable-offline] [--hide-filenames] [--exclude EXCLUDE] - [--include INCLUDE] [--include-only-with-project-file] - [--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL] - [--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version] + [--entity-type ENTITY_TYPE] [--category CATEGORY] + [--proxy PROXY] [--no-ssl-verify] [--project PROJECT] + [--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE] + [--hostname HOSTNAME] [--disable-offline] [--hide-filenames] + [--exclude EXCLUDE] [--include INCLUDE] + [--include-only-with-project-file] [--extra-heartbeats] + [--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT] + [--config CONFIG] [--verbose] [--version] wakatime: error: argument --timeout: invalid int value: 'abc' diff --git a/tests/samples/output/test_help_contents b/tests/samples/output/test_help_contents index ff4a471..8509cb7 100644 --- a/tests/samples/output/test_help_contents +++ b/tests/samples/output/test_help_contents @@ -1,62 +1,68 @@ usage: wakatime [-h] [--entity FILE] [--key KEY] [--write] [--plugin PLUGIN] [--time time] [--lineno LINENO] [--cursorpos CURSORPOS] - [--entity-type ENTITY_TYPE] [--proxy PROXY] [--no-ssl-verify] - [--project PROJECT] [--alternate-project ALTERNATE_PROJECT] - [--language LANGUAGE] [--hostname HOSTNAME] - [--disable-offline] [--hide-filenames] [--exclude EXCLUDE] - [--include INCLUDE] [--include-only-with-project-file] - [--extra-heartbeats] [--log-file LOG_FILE] [--api-url API_URL] - [--timeout TIMEOUT] [--config CONFIG] [--verbose] [--version] + [--entity-type ENTITY_TYPE] [--category CATEGORY] + [--proxy PROXY] [--no-ssl-verify] [--project PROJECT] + [--alternate-project ALTERNATE_PROJECT] [--language LANGUAGE] + [--hostname HOSTNAME] [--disable-offline] [--hide-filenames] + [--exclude EXCLUDE] [--include INCLUDE] + [--include-only-with-project-file] [--extra-heartbeats] + [--log-file LOG_FILE] [--api-url API_URL] [--timeout TIMEOUT] + [--config CONFIG] [--verbose] [--version] Common interface for the WakaTime api. optional arguments: -h, --help show this help message and exit - --entity FILE absolute path to file for the heartbeat; can also be a - url, domain, or app when --entity-type is not file - --key KEY your wakatime api key; uses api_key from - ~/.wakatime.cfg by default - --write when set, tells api this heartbeat was triggered from - writing to a file - --plugin PLUGIN optional text editor plugin name and version for User- - Agent header - --time time optional floating-point unix epoch timestamp; uses - current time by default - --lineno LINENO optional line number; current line being edited + --entity FILE Absolute path to file for the heartbeat. Can also be a + url, domain, or app when --entity-type is not file. + --key KEY Your wakatime api key; uses api_key from + ~/.wakatime.cfg by default. + --write When set, tells api this heartbeat was triggered from + writing to a file. + --plugin PLUGIN Optional text editor plugin name and version for User- + Agent header. + --time time Optional floating-point unix epoch timestamp. Uses + current time by default. + --lineno LINENO Optional line number. This is the current line being + edited. --cursorpos CURSORPOS - optional cursor position in the current file + Optional cursor position in the current file. --entity-type ENTITY_TYPE - entity type for this heartbeat. can be one of "file", - "domain", or "app"; defaults to file. - --proxy PROXY optional proxy configuration. Supports HTTPS and SOCKS + Entity type for this heartbeat. Can be "file", + "domain", or "app". Defaults to "file". + --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 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. - --project PROJECT optional project name + --project PROJECT Optional project name. --alternate-project ALTERNATE_PROJECT - optional alternate project name; auto-discovered - project takes priority - --language LANGUAGE optional language name; if valid, takes priority over - auto-detected language - --hostname HOSTNAME hostname of current machine. - --disable-offline disables offline time logging instead of queuing - logged time - --hide-filenames obfuscate filenames; will not send file names to api - --exclude EXCLUDE filename patterns to exclude from logging; POSIX regex - syntax; can be used more than once - --include INCLUDE filename patterns to log; when used in combination + Optional alternate project name. Auto-discovered + project takes priority. + --language LANGUAGE Optional language name. If valid, takes priority over + auto-detected language. + --hostname HOSTNAME Hostname of current machine. + --disable-offline Disables offline time logging instead of queuing + logged time. + --hide-filenames Obfuscate filenames. Will not send file names to api. + --exclude EXCLUDE Filename patterns to exclude from logging. POSIX regex + syntax. Can be used more than once. + --include INCLUDE Filename patterns to log. When used in combination 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 - disables tracking folders unless they contain a - .wakatime-project file; defaults to false - --extra-heartbeats reads extra heartbeats from STDIN as a JSON array - until EOF - --log-file LOG_FILE defaults to ~/.wakatime.log - --api-url API_URL heartbeats api url; for debugging with a local server - --timeout TIMEOUT number of seconds to wait when sending heartbeats to - api; defaults to 60 seconds - --config CONFIG defaults to ~/.wakatime.cfg - --verbose turns on debug messages in log file + Disables tracking folders unless they contain a + .wakatime-project file. Defaults to false. + --extra-heartbeats Reads extra heartbeats from STDIN as a JSON array + until EOF. + --log-file LOG_FILE Defaults to ~/.wakatime.log. + --api-url API_URL Heartbeats api url. For debugging with a local server. + --timeout TIMEOUT Number of seconds to wait when sending heartbeats to + api. Defaults to 60 seconds. + --config CONFIG Defaults to ~/.wakatime.cfg. + --verbose Turns on debug messages in log file. --version show program's version number and exit diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 8c99ccd..4a574e2 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -244,7 +244,7 @@ class ArgumentsTestCase(TestCase): self.assertEquals(int(str(e.exception)), AUTH_ERROR) 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()) 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(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()) log_output = u("\n").join([u(' ').join(x) for x in logs.actual()]) @@ -488,6 +488,97 @@ class ArgumentsTestCase(TestCase): self.assertOfflineHeartbeatsSynced() 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() def test_old_alternate_language_argument_still_supported(self, logs): logging.disable(logging.NOTSET) diff --git a/tests/test_main.py b/tests/test_main.py index fcd2d6a..1b70f5c 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -276,7 +276,7 @@ class MainTestCase(utils.TestCase): self.assertEquals(int(str(e.exception)), AUTH_ERROR) 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()) log_output = u("\n").join([u(' ').join(x) for x in logs.actual()]) diff --git a/tests/utils.py b/tests/utils.py index 9332022..3e98fbb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -144,6 +144,10 @@ class TestCase(unittest.TestCase): def getLogOutput(self, logs): 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 def isPy35OrNewer(self): if sys.version_info[0] > 3: diff --git a/wakatime/arguments.py b/wakatime/arguments.py index a2ff819..35aceeb 100644 --- a/wakatime/arguments.py +++ b/wakatime/arguments.py @@ -52,100 +52,122 @@ def parse_arguments(): """ # define supported command line arguments - parser = argparse.ArgumentParser( - description='Common interface for the WakaTime api.') + parser = argparse.ArgumentParser(description='Common interface for the ' + + 'WakaTime api.') parser.add_argument('--entity', dest='entity', metavar='FILE', - action=FileAction, - help='absolute path to file for the heartbeat; can also be a '+ - 'url, domain, or app when --entity-type is not file') + action=FileAction, + help='Absolute path to file for the heartbeat. Can ' + + 'also be a url, domain, or app when ' + + '--entity-type is not file.') parser.add_argument('--file', dest='file', action=FileAction, - help=argparse.SUPPRESS) + help=argparse.SUPPRESS) parser.add_argument('--key', dest='key', action=StoreWithoutQuotes, - help='your wakatime api key; uses api_key from '+ - '~/.wakatime.cfg by default') - parser.add_argument('--write', dest='is_write', - action='store_true', - help='when set, tells api this heartbeat was triggered from '+ - 'writing to a file') + help='Your wakatime api key; uses api_key from ' + + '~/.wakatime.cfg by default.') + parser.add_argument('--write', dest='is_write', action='store_true', + help='When set, tells api this heartbeat was ' + + 'triggered from writing to a file.') parser.add_argument('--plugin', dest='plugin', action=StoreWithoutQuotes, - help='optional text editor plugin name and version '+ - 'for User-Agent header') + help='Optional text editor plugin name and version ' + + 'for User-Agent header.') parser.add_argument('--time', dest='timestamp', metavar='time', - type=float, action=StoreWithoutQuotes, - help='optional floating-point unix epoch timestamp; '+ - 'uses current time by default') + type=float, action=StoreWithoutQuotes, + help='Optional floating-point unix epoch timestamp. ' + + 'Uses current time by default.') parser.add_argument('--lineno', dest='lineno', action=StoreWithoutQuotes, - help='optional line number; current line being edited') - parser.add_argument('--cursorpos', dest='cursorpos', action=StoreWithoutQuotes, - help='optional cursor position in the current file') - parser.add_argument('--entity-type', dest='entity_type', action=StoreWithoutQuotes, - help='entity type for this heartbeat. can be one of "file", '+ - '"domain", or "app"; defaults to file.') + help='Optional line number. This is the current ' + + 'line being edited.') + parser.add_argument('--cursorpos', dest='cursorpos', + action=StoreWithoutQuotes, + help='Optional cursor position in the current 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, - help='optional proxy configuration. Supports HTTPS '+ - 'and SOCKS proxies. For example: '+ - 'https://user:pass@host:port or '+ - 'socks5://user:pass@host:port or ' + - 'domain\\user:pass') + help='Optional proxy configuration. Supports HTTPS '+ + 'and SOCKS proxies. For example: '+ + 'https://user:pass@host:port or '+ + 'socks5://user:pass@host:port or ' + + 'domain\\user:pass') parser.add_argument('--no-ssl-verify', dest='nosslverify', action='store_true', - help='disables SSL certificate verification for HTTPS '+ - 'requests. By default, SSL certificates are verified.') + help='Disables SSL certificate verification for HTTPS '+ + 'requests. By default, SSL certificates are ' + + 'verified.') parser.add_argument('--project', dest='project', action=StoreWithoutQuotes, - help='optional project name') - parser.add_argument('--alternate-project', dest='alternate_project', action=StoreWithoutQuotes, - help='optional alternate project name; auto-discovered project '+ - 'takes priority') - parser.add_argument('--alternate-language', dest='alternate_language', action=StoreWithoutQuotes, - help=argparse.SUPPRESS) - parser.add_argument('--language', dest='language', action=StoreWithoutQuotes, - help='optional language name; if valid, takes priority over '+ - 'auto-detected language') - parser.add_argument('--hostname', dest='hostname', action=StoreWithoutQuotes, help='hostname of '+ - 'current machine.') + help='Optional project name.') + parser.add_argument('--alternate-project', dest='alternate_project', + action=StoreWithoutQuotes, + help='Optional alternate project name. ' + + 'Auto-discovered project takes priority.') + parser.add_argument('--alternate-language', dest='alternate_language', + action=StoreWithoutQuotes, + help=argparse.SUPPRESS) + parser.add_argument('--language', dest='language', + action=StoreWithoutQuotes, + help='Optional language name. If valid, takes ' + + 'priority over auto-detected language.') + parser.add_argument('--hostname', dest='hostname', + action=StoreWithoutQuotes, + help='Hostname of current machine.') parser.add_argument('--disable-offline', dest='offline', - action='store_false', - help='disables offline time logging instead of queuing logged time') + action='store_false', + help='Disables offline time logging instead of ' + + 'queuing logged time.') 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', - action='store_true', - help='obfuscate filenames; will not send file names to api') + action='store_true', + help='Obfuscate filenames. Will not send file names ' + + 'to api.') parser.add_argument('--hidefilenames', dest='hidefilenames', - action='store_true', - help=argparse.SUPPRESS) + action='store_true', + help=argparse.SUPPRESS) parser.add_argument('--exclude', dest='exclude', action='append', - help='filename patterns to exclude from logging; POSIX regex '+ - 'syntax; can be used more than once') + help='Filename patterns to exclude from logging. ' + + 'POSIX regex syntax. Can be used more than once.') parser.add_argument('--include', dest='include', action='append', - help='filename patterns to log; when used in combination with '+ - '--exclude, files matching include will still be logged; '+ - 'POSIX regex syntax; can be used more than once') + help='Filename patterns to log. When used in ' + + 'combination with --exclude, files matching ' + + 'include will still be logged. POSIX regex ' + + 'syntax. Can be used more than once.') parser.add_argument('--include-only-with-project-file', dest='include_only_with_project_file', action='store_true', - help='disables tracking folders unless they contain '+ - 'a .wakatime-project file; defaults to false') + help='Disables tracking folders unless they contain '+ + 'a .wakatime-project file. Defaults to false.') parser.add_argument('--ignore', dest='ignore', action='append', - help=argparse.SUPPRESS) + help=argparse.SUPPRESS) parser.add_argument('--extra-heartbeats', dest='extra_heartbeats', - action='store_true', - help='reads extra heartbeats from STDIN as a JSON array until EOF') - parser.add_argument('--log-file', dest='log_file', action=StoreWithoutQuotes, - help='defaults to ~/.wakatime.log') + action='store_true', + help='Reads extra heartbeats from STDIN as a JSON ' + + 'array until EOF.') + parser.add_argument('--log-file', dest='log_file', + action=StoreWithoutQuotes, + help='Defaults to ~/.wakatime.log.') parser.add_argument('--logfile', dest='logfile', action=StoreWithoutQuotes, - help=argparse.SUPPRESS) + help=argparse.SUPPRESS) 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, - help=argparse.SUPPRESS) - parser.add_argument('--timeout', dest='timeout', type=int, action=StoreWithoutQuotes, - help='number of seconds to wait when sending heartbeats to api; '+ - 'defaults to 60 seconds') + help=argparse.SUPPRESS) + parser.add_argument('--timeout', dest='timeout', type=int, + action=StoreWithoutQuotes, + help='Number of seconds to wait when sending ' + + 'heartbeats to api. Defaults to 60 seconds.') 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', - help='turns on debug messages in log file') + help='Turns on debug messages in log file.') parser.add_argument('--version', action='version', version=__version__) # parse command line arguments @@ -172,14 +194,14 @@ def parse_arguments(): args.key = default_key else: 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: 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) if not is_valid: 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: raise SystemExit(AUTH_ERROR) diff --git a/wakatime/heartbeat.py b/wakatime/heartbeat.py index 757838e..855aee5 100644 --- a/wakatime/heartbeat.py +++ b/wakatime/heartbeat.py @@ -31,6 +31,7 @@ class Heartbeat(object): time = None entity = None type = None + category = None is_write = None project = None branch = None @@ -58,6 +59,18 @@ class Heartbeat(object): if self.type not in ['file', 'domain', 'app']: 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: exclude = self._excluded_by_pattern() if exclude: @@ -155,6 +168,7 @@ class Heartbeat(object): 'time': self.time, 'entity': self._unicode(self.entity), 'type': self.type, + 'category': self.category, 'is_write': self.is_write, 'project': self._unicode(self.project), 'branch': self._unicode(self.branch), @@ -170,9 +184,10 @@ class Heartbeat(object): return self.dict().items() 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, type=self.type, + category=self.category, project=self._unicode(self.project), branch=self._unicode(self.branch), entity=self._unicode(self.entity),