Compare commits

...

13 Commits
8.1.2 ... 8.3.2

Author SHA1 Message Date
be09b34d44 v8.3.2 2018-10-06 20:40:57 -07:00
e1ee1c1216 changes for v8.3.2 2018-10-06 20:40:37 -07:00
a37061924b Send buffered heartbeats to API every 30 seconds. 2018-10-06 20:40:12 -07:00
da01fa268b v8.3.1 2018-10-05 00:07:11 -07:00
c279418651 changes for v8.3.1 2018-10-05 00:06:46 -07:00
5cf2c8f7ac upgrade wakatime-cli to v10.4.1 2018-10-05 00:06:02 -07:00
d1455e77a8 v8.3.0 2018-10-03 00:55:02 -07:00
8499e7bafe changes for v8.3.0 2018-10-03 00:54:40 -07:00
abc26a0864 upgrade wakatime-cli to v10.4.0 2018-10-03 00:47:20 -07:00
71ad97ffe9 v8.2.0 2018-09-30 22:03:01 -07:00
3ec5995c99 changes for v8.2.0 2018-09-30 22:02:53 -07:00
195cf4de36 upgrade wakatime-cli to v10.3.0 2018-09-30 21:48:20 -07:00
b39eefb4f5 cross-platform Popen with hidden window 2018-09-30 21:29:04 -07:00
8 changed files with 182 additions and 20 deletions

View File

@ -3,6 +3,39 @@ History
-------
8.3.2 (2018-10-06)
++++++++++++++++++
- Send buffered heartbeats to API every 30 seconds.
8.3.1 (2018-10-05)
++++++++++++++++++
- Upgrade wakatime-cli to v10.4.1.
- Send 50 offline heartbeats to API per request with 1 second delay in between.
8.3.0 (2018-10-03)
++++++++++++++++++
- Upgrade wakatime-cli to v10.4.0.
- Support logging coding activity to remote network drive files on Windows
platform by detecting UNC path from drive letter.
`wakatime#72 <https://github.com/wakatime/wakatime/issues/72>`_
8.2.0 (2018-09-30)
++++++++++++++++++
- Prevent opening cmd window on Windows when running wakatime-cli.
`#91 <https://github.com/wakatime/sublime-wakatime/issues/91>`_
- Upgrade wakatime-cli to v10.3.0.
- Re-enable detecting projects from Subversion folder on Windows platform.
- Prevent opening cmd window on Windows when detecting project from Subversion.
- Run tests on Windows using Appveyor.
8.1.2 (2018-09-20)
++++++++++++++++++

View File

@ -7,7 +7,7 @@ Website: https://wakatime.com/
==========================================================="""
__version__ = '8.1.2'
__version__ = '8.3.2'
import sublime
@ -18,6 +18,7 @@ import json
import os
import platform
import re
import subprocess
import sys
import time
import threading
@ -25,7 +26,7 @@ import traceback
import urllib
import webbrowser
from datetime import datetime
from subprocess import Popen, STDOUT, PIPE
from subprocess import STDOUT, PIPE
from zipfile import ZipFile
try:
import _winreg as winreg # py2
@ -42,6 +43,8 @@ except ImportError:
is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)
is_win = platform.system() == 'Windows'
if is_py2:
def u(text):
@ -91,8 +94,22 @@ else:
))
class Popen(subprocess.Popen):
"""Patched Popen to prevent opening cmd window on Windows platform."""
def __init__(self, *args, **kwargs):
startupinfo = kwargs.get('startupinfo')
if is_win or True:
try:
startupinfo = startupinfo or subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
except AttributeError:
pass
kwargs['startupinfo'] = startupinfo
super(Popen, self).__init__(*args, **kwargs)
# globals
HEARTBEAT_FREQUENCY = 2
ST_VERSION = int(sublime.version())
PLUGIN_DIR = os.path.dirname(os.path.realpath(__file__))
API_CLIENT = os.path.join(PLUGIN_DIR, 'packages', 'wakatime', 'cli.py')
@ -103,8 +120,11 @@ LAST_HEARTBEAT = {
'file': None,
'is_write': False,
}
LAST_HEARTBEAT_SENT_AT = 0
PYTHON_LOCATION = None
HEARTBEATS = queue.Queue()
HEARTBEAT_FREQUENCY = 2 # minutes between logging heartbeat when editing same file
SEND_BUFFER_SECONDS = 30 # seconds between sending buffered heartbeats to API
# Log Levels
@ -433,11 +453,18 @@ def append_heartbeat(entity, timestamp, is_write, view, project, folders):
}
# process the queue of heartbeats in the future
seconds = 4
set_timeout(process_queue, seconds)
set_timeout(lambda: process_queue(timestamp), SEND_BUFFER_SECONDS)
def process_queue():
def process_queue(timestamp):
global LAST_HEARTBEAT_SENT_AT
# Prevent sending heartbeats more often than SEND_BUFFER_SECONDS
now = int(time.time())
if timestamp != LAST_HEARTBEAT['time'] and LAST_HEARTBEAT_SENT_AT > now - SEND_BUFFER_SECONDS:
return
LAST_HEARTBEAT_SENT_AT = now
try:
heartbeat = HEARTBEATS.get_nowait()
except queue.Empty:

View File

@ -1,7 +1,7 @@
__title__ = 'wakatime'
__description__ = 'Common interface to the WakaTime api.'
__url__ = 'https://github.com/wakatime/wakatime'
__version_info__ = ('10', '2', '4')
__version_info__ = ('10', '4', '1')
__version__ = '.'.join(__version_info__)
__author__ = 'Alan Hamlett'
__author_email__ = 'alan@wakatime.com'

View File

@ -11,11 +11,15 @@
import codecs
import os
import platform
import subprocess
import sys
is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)
is_win = platform.system() == 'Windows'
if is_py2: # pragma: nocover
@ -98,3 +102,21 @@ try:
from .packages import simplejson as json
except (ImportError, SyntaxError): # pragma: nocover
import json
class Popen(subprocess.Popen):
"""Patched Popen to prevent opening cmd window on Windows platform."""
def __init__(self, *args, **kwargs):
startupinfo = kwargs.get('startupinfo')
if is_win or True:
try:
startupinfo = startupinfo or subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
except AttributeError:
pass
kwargs['startupinfo'] = startupinfo
if 'env' not in kwargs:
kwargs['env'] = os.environ.copy()
kwargs['env']['LANG'] = 'en-US' if is_win else 'en_US.UTF-8'
subprocess.Popen.__init__(self, *args, **kwargs)

View File

@ -46,11 +46,11 @@ Default is 2MB.
"""
MAX_FILE_SIZE_SUPPORTED = 2000000
""" Default number of offline heartbeats to sync before exiting."""
""" Default limit of number of offline heartbeats to sync before exiting."""
DEFAULT_SYNC_OFFLINE_ACTIVITY = 100
""" Number of heartbeats per api request.
Even when sending more heartbeats, this is the number of heartbeats sent per
individual https request to the WakaTime API.
"""
HEARTBEATS_PER_REQUEST = 10
HEARTBEATS_PER_REQUEST = 50

View File

@ -10,8 +10,9 @@
import os
import logging
import re
from subprocess import PIPE
from .compat import u, json
from .compat import u, json, is_win, Popen
from .exceptions import SkipHeartbeat
from .project import get_project_info
from .stats import get_file_stats
@ -85,6 +86,7 @@ class Heartbeat(object):
return
if self.type == 'file':
self.entity = format_file_path(self.entity)
self._format_local_file()
if not self._file_exists():
self.skip = u('File does not exist; ignoring this heartbeat.')
return
@ -236,6 +238,84 @@ class Heartbeat(object):
return (self.entity and os.path.isfile(self.entity) or
self.args.local_file and os.path.isfile(self.args.local_file))
def _format_local_file(self):
"""When args.local_file empty on Windows, tries to map args.entity to a
unc path.
Updates args.local_file in-place without returning anything.
"""
if self.type != 'file':
return
if not is_win:
return
if self._file_exists():
return
self.args.local_file = self._to_unc_path(self.entity)
def _to_unc_path(self, filepath):
drive, rest = self._splitdrive(filepath)
if not drive:
return filepath
stdout = None
try:
stdout, stderr = Popen(['net', 'use'], stdout=PIPE, stderr=PIPE).communicate()
except OSError:
pass
else:
if stdout:
cols = None
for line in stdout.strip().splitlines()[1:]:
line = u(line)
if not line.strip():
continue
if not cols:
cols = self._unc_columns(line)
continue
start, end = cols.get('local', (0, 0))
if not start and not end:
break
local = line[start:end].strip().split(':')[0].upper()
if not local.isalpha():
continue
if local == drive:
start, end = cols.get('remote', (0, 0))
if not start and not end:
break
remote = line[start:end].strip()
return remote + rest
return filepath
def _unc_columns(self, line):
cols = {}
current_col = u('')
newcol = False
start, end = 0, 0
for char in line:
if char.isalpha():
if newcol:
cols[current_col.strip().lower()] = (start, end)
current_col = u('')
start = end
newcol = False
current_col += u(char)
else:
newcol = True
end += 1
if start != end and current_col:
cols[current_col.strip().lower()] = (start, -1)
return cols
def _splitdrive(self, filepath):
if filepath[1:2] != ':' or not filepath[0].isalpha():
return None, filepath
return filepath[0].upper(), filepath[2:]
def _excluded_by_pattern(self):
return should_exclude(self.entity, self.args.include, self.args.exclude)

View File

@ -14,6 +14,7 @@ from __future__ import print_function
import logging
import os
import sys
import time
import traceback
pwd = os.path.dirname(os.path.abspath(__file__))
@ -76,6 +77,7 @@ def execute(argv=None):
if retval == SUCCESS:
queue = Queue(args, configs)
for offline_heartbeats in queue.pop_many(args.sync_offline_activity):
time.sleep(1)
retval = send_heartbeats(offline_heartbeats, args, configs)
if retval != SUCCESS:
break

View File

@ -12,10 +12,10 @@
import logging
import os
import platform
from subprocess import Popen, PIPE
from subprocess import PIPE
from .base import BaseProject
from ..compat import u, open
from ..compat import u, open, Popen
try:
from collections import OrderedDict
except ImportError: # pragma: nocover
@ -70,24 +70,22 @@ class Subversion(BaseProject):
if not self._is_mac() or self._has_xcode_tools():
stdout = None
try:
os.environ['LANG'] = 'en_US'
stdout, stderr = Popen([
self._find_binary(), 'info', os.path.realpath(path)
], stdout=PIPE, stderr=PIPE).communicate()
stdout, stderr = Popen(
[self._find_binary(), 'info', os.path.realpath(path)],
stdout=PIPE,
stderr=PIPE,
).communicate()
except OSError:
pass
else:
if stdout:
for line in stdout.splitlines():
line = u(line)
line = line.split(': ', 1)
line = u(line).split(': ', 1)
if len(line) == 2:
info[line[0]] = line[1]
return info
def _find_project_base(self, path, found=False):
if platform.system() == 'Windows':
return False # pragma: nocover
path = os.path.realpath(path)
if os.path.isfile(path):
path = os.path.split(path)[0]