From 4effebf67b37fb072513a8bd925d66a88f34e0c2 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Fri, 29 Apr 2016 00:16:08 +0200 Subject: [PATCH] upgrade wakatime-cli to v6.0.0 --- plugin/packages/wakatime/__about__.py | 2 +- plugin/packages/wakatime/constants.py | 1 + plugin/packages/wakatime/logger.py | 14 ++-- plugin/packages/wakatime/main.py | 115 +++++++++++++++----------- plugin/packages/wakatime/project.py | 10 +-- 5 files changed, 83 insertions(+), 59 deletions(-) diff --git a/plugin/packages/wakatime/__about__.py b/plugin/packages/wakatime/__about__.py index 85bc53d..2f2a1c1 100644 --- a/plugin/packages/wakatime/__about__.py +++ b/plugin/packages/wakatime/__about__.py @@ -1,7 +1,7 @@ __title__ = 'wakatime' __description__ = 'Common interface to the WakaTime api.' __url__ = 'https://github.com/wakatime/wakatime' -__version_info__ = ('5', '0', '1') +__version_info__ = ('6', '0', '0') __version__ = '.'.join(__version_info__) __author__ = 'Alan Hamlett' __author_email__ = 'alan@wakatime.com' diff --git a/plugin/packages/wakatime/constants.py b/plugin/packages/wakatime/constants.py index 9db227f..1c7316b 100644 --- a/plugin/packages/wakatime/constants.py +++ b/plugin/packages/wakatime/constants.py @@ -15,3 +15,4 @@ API_ERROR = 102 CONFIG_FILE_PARSE_ERROR = 103 AUTH_ERROR = 104 UNKNOWN_ERROR = 105 +MALFORMED_HEARTBEAT_ERROR = 106 diff --git a/plugin/packages/wakatime/logger.py b/plugin/packages/wakatime/logger.py index 78b38ee..3d3f166 100644 --- a/plugin/packages/wakatime/logger.py +++ b/plugin/packages/wakatime/logger.py @@ -41,10 +41,10 @@ class CustomEncoder(json.JSONEncoder): class JsonFormatter(logging.Formatter): - def setup(self, timestamp, isWrite, entity, version, plugin, verbose, + def setup(self, timestamp, is_write, entity, version, plugin, verbose, warnings=False): self.timestamp = timestamp - self.isWrite = isWrite + self.is_write = is_write self.entity = entity self.version = version self.plugin = plugin @@ -61,10 +61,10 @@ class JsonFormatter(logging.Formatter): if self.verbose: data['caller'] = record.pathname data['lineno'] = record.lineno - data['isWrite'] = self.isWrite + data['is_write'] = self.is_write data['file'] = self.entity - if not self.isWrite: - del data['isWrite'] + if not self.is_write: + del data['is_write'] data['level'] = record.levelname data['message'] = record.getMessage() if self.warnings else record.msg if not self.plugin: @@ -103,7 +103,7 @@ def setup_logging(args, version): formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z') formatter.setup( timestamp=args.timestamp, - isWrite=args.isWrite, + is_write=args.is_write, entity=args.entity, version=version, plugin=args.plugin, @@ -118,7 +118,7 @@ def setup_logging(args, version): warnings_formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z') warnings_formatter.setup( timestamp=args.timestamp, - isWrite=args.isWrite, + is_write=args.is_write, entity=args.entity, version=version, plugin=args.plugin, diff --git a/plugin/packages/wakatime/main.py b/plugin/packages/wakatime/main.py index f8aeb58..737f8d9 100644 --- a/plugin/packages/wakatime/main.py +++ b/plugin/packages/wakatime/main.py @@ -37,6 +37,7 @@ from .constants import ( CONFIG_FILE_PARSE_ERROR, SUCCESS, UNKNOWN_ERROR, + MALFORMED_HEARTBEAT_ERROR, ) from .logger import setup_logging from .offlinequeue import Queue @@ -101,13 +102,13 @@ def parseArguments(): 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 --entitytype is not file') + 'url, domain, or app when --entity-type is not file') parser.add_argument('--file', dest='file', action=FileAction, help=argparse.SUPPRESS) parser.add_argument('--key', dest='key', help='your wakatime api key; uses api_key from '+ - '~/.wakatime.conf by default') - parser.add_argument('--write', dest='isWrite', + '~/.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') @@ -122,9 +123,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('--entitytype', dest='entity_type', + parser.add_argument('--entity-type', dest='entity_type', help='entity type for this heartbeat. can be one of "file", '+ - '"url", "domain", or "app"; defaults to file.') + '"domain", or "app"; defaults to file.') parser.add_argument('--proxy', dest='proxy', help='optional https proxy url; for example: '+ 'https://user:pass@localhost:8080') @@ -153,14 +154,18 @@ def parseArguments(): 'POSIX regex syntax; can be used more than once') parser.add_argument('--ignore', dest='ignore', action='append', 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('--logfile', dest='logfile', help='defaults to ~/.wakatime.log') parser.add_argument('--apiurl', dest='api_url', help='heartbeats api url; for debugging with a local server') parser.add_argument('--timeout', dest='timeout', type=int, - help='number of seconds to wait when sending heartbeats to api') + help='number of seconds to wait when sending heartbeats to api; '+ + 'defaults to 60 seconds') parser.add_argument('--config', dest='config', - help='defaults to ~/.wakatime.conf') + help='defaults to ~/.wakatime.cfg') parser.add_argument('--verbose', dest='verbose', action='store_true', help='turns on debug messages in log file') parser.add_argument('--version', action='version', version=__version__) @@ -188,8 +193,6 @@ def parseArguments(): args.key = default_key else: parser.error('Missing api key') - if not args.entity_type: - args.entity_type = 'file' if not args.entity: if args.file: args.entity = args.file @@ -295,7 +298,7 @@ def get_user_agent(plugin): def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, - entity=None, timestamp=None, isWrite=None, plugin=None, + entity=None, timestamp=None, is_write=None, plugin=None, offline=None, entity_type='file', hidefilenames=None, proxy=None, api_url=None, timeout=None, **kwargs): """Sends heartbeat as POST request to WakaTime api server. @@ -307,7 +310,7 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, if not api_url: api_url = 'https://api.wakatime.com/api/v1/heartbeats' if not timeout: - timeout = 30 + timeout = 60 log.debug('Sending heartbeat to api at %s' % api_url) data = { 'time': timestamp, @@ -327,8 +330,8 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None, data['lineno'] = stats['lineno'] if stats.get('cursorpos'): data['cursorpos'] = stats['cursorpos'] - if isWrite: - data['is_write'] = isWrite + if is_write: + data['is_write'] = is_write if project: data['project'] = project if branch: @@ -435,7 +438,7 @@ def sync_offline_heartbeats(args, hostname): hostname=hostname, stats=json.loads(heartbeat['stats']), key=args.key, - isWrite=heartbeat['is_write'], + is_write=heartbeat['is_write'], plugin=heartbeat['plugin'], offline=args.offline, hidefilenames=args.hidefilenames, @@ -451,6 +454,45 @@ def sync_offline_heartbeats(args, hostname): return SUCCESS +def process_heartbeat(args, configs, hostname, heartbeat): + exclude = should_exclude(heartbeat['entity'], args.include, args.exclude) + if exclude is not False: + log.debug(u('Skipping because matches exclude pattern: {pattern}').format( + pattern=u(exclude), + )) + return SUCCESS + + if heartbeat.get('entity_type') not in ['file', 'domain', 'app']: + heartbeat['entity_type'] = 'file' + + if heartbeat['entity_type'] != 'file' or os.path.isfile(heartbeat['entity']): + + stats = get_file_stats(heartbeat['entity'], + entity_type=heartbeat['entity_type'], + lineno=heartbeat.get('lineno'), + cursorpos=heartbeat.get('cursorpos'), + plugin=args.plugin, + alternate_language=heartbeat.get('alternate_language')) + + project = heartbeat.get('project') or heartbeat.get('alternate_project') + branch = None + if heartbeat['entity_type'] == 'file': + project, branch = get_project_info(configs, heartbeat) + + heartbeat['project'] = project + heartbeat['branch'] = branch + heartbeat['stats'] = stats + heartbeat['hostname'] = hostname + heartbeat['timeout'] = args.timeout + heartbeat['key'] = args.key + + return send_heartbeat(**heartbeat) + + else: + log.debug('File does not exist; ignoring this heartbeat.') + return SUCCESS + + def execute(argv=None): if argv: sys.argv = ['wakatime'] + argv @@ -462,44 +504,25 @@ def execute(argv=None): setup_logging(args, __version__) try: - exclude = should_exclude(args.entity, args.include, args.exclude) - if exclude is not False: - log.debug(u('Skipping because matches exclude pattern: {pattern}').format( - pattern=u(exclude), - )) - return SUCCESS - if args.entity_type != 'file' or os.path.isfile(args.entity): + hostname = args.hostname or socket.gethostname() - stats = get_file_stats(args.entity, - entity_type=args.entity_type, - lineno=args.lineno, - cursorpos=args.cursorpos, - plugin=args.plugin, - alternate_language=args.alternate_language) + heartbeat = vars(args) + retval = process_heartbeat(args, configs, hostname, heartbeat) - project = args.project or args.alternate_project - branch = None - if args.entity_type == 'file': - project, branch = get_project_info(configs, args) + if args.extra_heartbeats: + try: + for heartbeat in json.loads(sys.stdin.readline()): + retval = process_heartbeat(args, configs, hostname, heartbeat) + except json.JSONDecodeError: + retval = MALFORMED_HEARTBEAT_ERROR - kwargs = vars(args) - kwargs['project'] = project - kwargs['branch'] = branch - kwargs['stats'] = stats - hostname = args.hostname or socket.gethostname() - kwargs['hostname'] = hostname - kwargs['timeout'] = args.timeout + if retval == SUCCESS: + retval = sync_offline_heartbeats(args, hostname) - status = send_heartbeat(**kwargs) - if status == SUCCESS: - return sync_offline_heartbeats(args, hostname) - else: - return status + return retval - else: - log.debug('File does not exist; ignoring this heartbeat.') - return SUCCESS except: log.traceback() + print(traceback.format_exc()) return UNKNOWN_ERROR diff --git a/plugin/packages/wakatime/project.py b/plugin/packages/wakatime/project.py index f3299fe..c008651 100644 --- a/plugin/packages/wakatime/project.py +++ b/plugin/packages/wakatime/project.py @@ -33,7 +33,7 @@ REV_CONTROL_PLUGINS = [ ] -def get_project_info(configs, args): +def get_project_info(configs, heartbeat): """Find the current project and branch. First looks for a .wakatime-project file. Second, uses the --project arg. @@ -50,14 +50,14 @@ def get_project_info(configs, args): plugin_name = plugin_cls.__name__.lower() plugin_configs = get_configs_for_plugin(plugin_name, configs) - project = plugin_cls(args.entity, configs=plugin_configs) + project = plugin_cls(heartbeat['entity'], configs=plugin_configs) if project.process(): project_name = project_name or project.name() branch_name = project.branch() break if project_name is None: - project_name = args.project + project_name = heartbeat.get('project') if project_name is None or branch_name is None: @@ -66,14 +66,14 @@ def get_project_info(configs, args): plugin_name = plugin_cls.__name__.lower() plugin_configs = get_configs_for_plugin(plugin_name, configs) - project = plugin_cls(args.entity, configs=plugin_configs) + project = plugin_cls(heartbeat['entity'], configs=plugin_configs) if project.process(): project_name = project_name or project.name() branch_name = branch_name or project.branch() break if project_name is None: - project_name = args.alternate_project + project_name = heartbeat.get('alternate_project') return project_name, branch_name