add version 0.1.2

This commit is contained in:
Alan Hamlett 2013-06-25 00:57:15 -07:00
parent 30e9697622
commit 4346a055e3
5 changed files with 395 additions and 3 deletions

185
plugin/wakatime.py Normal file
View file

@ -0,0 +1,185 @@
#!/usr/bin/env python
import os
import sys
import argparse
import platform
import urllib2
import json
import base64
import uuid
import time
import re
from collections import OrderedDict
from httplib import BadStatusLine, IncompleteRead
from urllib2 import HTTPError, URLError
import logging as log
# Config
version = '0.1.2'
user_agent = 'vim-wakatime/%s (%s)' % (version, platform.platform())
def project_from_path(path):
project = git_project(path)
if project:
return project
return None
def tags_from_path(path):
tags = []
if os.path.exists(path):
tags.extend(git_tags(path))
tags.extend(mercurial_tags(path))
return list(set(tags))
def git_project(path):
config_file = find_git_config(path)
if config_file:
folder = os.path.split(os.path.split(os.path.split(config_file)[0])[0])[1]
if folder:
return folder
return None
def find_git_config(path):
path = os.path.realpath(path)
log.info(path)
if os.path.isfile(path):
path = os.path.split(path)[0]
if os.path.isfile(os.path.join(path, '.git', 'config')):
return os.path.join(path, '.git', 'config')
split_path = os.path.split(path)
if split_path[1] == '':
return None
return find_git_config(split_path[0])
def parse_git_config(config):
sections = OrderedDict()
try:
f = open(config, 'r')
except IOError as e:
log.exception("Exception:")
else:
with f:
section = None
for line in f.readlines():
line = line.lstrip()
if len(line) > 0 and line[0] == '[':
section = line[1:].split(']', 1)[0]
temp = section.split(' ', 1)
section = temp[0].lower()
if len(temp) > 1:
section = ' '.join([section, temp[1]])
sections[section] = OrderedDict()
else:
try:
(setting, value) = line.split('=', 1)
except ValueError:
setting = line.split('#', 1)[0].split(';', 1)[0]
value = 'true'
setting = setting.strip().lower()
value = value.split('#', 1)[0].split(';', 1)[0].strip()
sections[section][setting] = value
f.close()
return sections
def git_tags(path):
tags = []
config_file = find_git_config(path)
if config_file:
sections = parse_git_config(config_file)
for section in sections:
if section.split(' ', 1)[0] == 'remote' and 'url' in sections[section]:
tags.append(sections[section]['url'])
return tags
def mercurial_tags(path):
tags = []
return tags
def svn_tags(path):
tags = []
return tags
def log_action(**kwargs):
kwargs['User-Agent'] = user_agent
log.info(json.dumps(kwargs))
def send_action(key, instance, action, task, timestamp, project, tags):
url = 'https://www.wakati.me/api/v1/actions'
data = {
'type': action,
'task': task,
'time': time.time(),
'instance_id': instance,
'project': project,
'tags': tags,
}
if timestamp:
data['time'] = timestamp
request = urllib2.Request(url=url, data=json.dumps(data))
request.add_header('User-Agent', user_agent)
request.add_header('Content-Type', 'application/json')
request.add_header('Authorization', 'Basic %s' % base64.b64encode(key))
log_action(**data)
response = None
try:
response = urllib2.urlopen(request)
except HTTPError as ex:
log.error("%s:\ndata=%s\nresponse=%s" % (ex.getcode(), json.dumps(data), ex.read()))
if log.getLogger().isEnabledFor(log.DEBUG):
log.exception("Exception for %s:\n%s" % (data['time'], json.dumps(data)))
except (URLError, IncompleteRead, BadStatusLine) as ex:
log.error("%s:\ndata=%s\nmessage=%s" % (ex.__class__.__name__, json.dumps(data), ex))
if log.getLogger().isEnabledFor(log.DEBUG):
log.exception("Exception for %s:\n%s" % (data['time'], json.dumps(data)))
if response:
log.debug('response_code=%s response_content=%s' % (response.getcode(), response.read()))
if response and (response.getcode() == 200 or response.getcode() == 201):
return True
return False
def parse_args(argv):
parser = argparse.ArgumentParser(description='Log time to the wakati.me api')
parser.add_argument('--key', dest='key', required=True,
help='your wakati.me api key')
parser.add_argument('--action', dest='action', required=True,
choices=['open_file', 'ping', 'close_file', 'write_file', 'open_editor', 'quit_editor', 'minimize_editor', 'maximize_editor', 'start', 'stop'])
parser.add_argument('--task', dest='task', required=True,
help='path to file or named task')
parser.add_argument('--instance', dest='instance', required=True,
help='the UUID4 representing the current editor')
parser.add_argument('--time', dest='timestamp', metavar='time', type=float,
help='optional floating-point timestamp in seconds')
parser.add_argument('--verbose', dest='verbose', action='store_true',
help='turns on debug messages in logfile')
parser.add_argument('--version', action='version', version=version)
return parser.parse_args(argv)
def main(argv):
args = parse_args(argv)
level = log.INFO
if args.verbose:
level = log.DEBUG
del args.verbose
log.basicConfig(filename='/var/log/wakatime.log', format='%(asctime)s vim-wakatime/'+version+' %(levelname)s %(message)s', datefmt='%Y-%m-%dT%H:%M:%SZ', level=level)
tags = tags_from_path(args.task)
project = project_from_path(args.task)
send_action(project=project, tags=tags, **vars(args))
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

171
plugin/wakatime.vim Normal file
View file

@ -0,0 +1,171 @@
" ============================================================================
" File: wakatime.vim
" Description: invisible time tracker using Wakati.Me
" Maintainer: Wakati.Me <support@wakatime.com>
" Version: 0.0.1
" ============================================================================
" Init {{{
" Check Vim version
if v:version < 700
echoerr "This plugin requires vim >= 7."
finish
endif
" Check for Python support
if !has('python')
echoerr "This plugin requires Vim to be compiled with Python support."
finish
endif
" Check for required user-defined settings
if !exists("g:wakatime_api_key")
if filereadable(expand("$HOME/.wakatime"))
for s:line in readfile(expand("$HOME/.wakatime"))
let s:setting = split(s:line, "=")
if s:setting[0] == "api_key"
let g:wakatime_api_key = s:setting[1]
endif
endfor
endif
if !exists("g:wakatime_api_key")
finish
endif
endif
" Only load plugin once
if exists("g:loaded_wakatime")
finish
endif
let g:loaded_wakatime = 1
" Backup & Override cpoptions
let s:old_cpo = &cpo
set cpo&vim
let s:plugin_directory = expand("<sfile>:p:h")
" Set a nice updatetime value, if updatetime is too short
if &updatetime < 60 * 1000 * 2
let &updatetime = 60 * 1000 * 15 " 15 minutes
endif
python << ENDPYTHON
import vim
import uuid
import time
instance_id = str(uuid.uuid4())
vim.command('let s:instance_id = "%s"' % instance_id)
ENDPYTHON
" }}}
" Function Definitions {{{
function! s:initVariable(var, value)
if !exists(a:var)
exec 'let ' . a:var . ' = ' . "'" . substitute(a:value, "'", "''", "g") . "'"
return 1
endif
return 0
endfunction
function! s:GetCurrentFile()
return expand("%:p")
endfunction
function! s:api(type, task)
exec "silent !python " . s:plugin_directory . "/wakatime.py --key" g:wakatime_api_key "--instance" s:instance_id "--action" a:type "--task" a:task . " &"
endfunction
function! s:api_with_time(type, task, time)
exec "silent !python " . s:plugin_directory . "/wakatime.py --key" g:wakatime_api_key "--instance" s:instance_id "--action" a:type "--task" a:task "--time" printf("%f", a:time) . " &"
endfunction
function! s:getchar()
let c = getchar()
if c =~ '^\d\+$'
let c = nr2char(c)
endif
return c
endfunction
" }}}
" Event Handlers {{{
function! s:bufenter()
let task = s:GetCurrentFile()
call s:api("open_file", shellescape(task))
endfunction
function! s:bufleave()
let task = s:GetCurrentFile()
call s:api("close_file", shellescape(task))
endfunction
function! s:vimenter()
let task = s:GetCurrentFile()
call s:api("open_editor", shellescape(task))
endfunction
function! s:vimleave()
let task = s:GetCurrentFile()
call s:api("quit_editor", shellescape(task))
endfunction
function! s:bufwrite()
let task = s:GetCurrentFile()
call s:api("write_file", shellescape(task))
endfunction
function! s:cursorhold()
let s:away_task = s:GetCurrentFile()
python vim.command("let s:away_start=%f" % (time.time() - (float(vim.eval("&updatetime")) / 1000.0)))
autocmd Wakatime CursorMoved,CursorMovedI * call s:cursormoved()
endfunction
function! s:cursormoved()
autocmd! Wakatime CursorMoved,CursorMovedI *
python vim.command("let away_end=%f" % time.time())
let away_unit = "minutes"
let away_duration = (away_end - s:away_start) / 60
if away_duration > 59
let away_duration = away_duration / 60
let away_unit = "hours"
endif
if away_duration > 59
let away_duration = away_duration / 60
let away_unit = "days"
endif
let answer = input(printf("You were away %.f %s. Add time to current file? (y/n)", away_duration, away_unit))
if answer != "y"
call s:api_with_time("minimize_editor", shellescape(s:away_task), s:away_start)
call s:api_with_time("maximize_editor", shellescape(s:away_task), away_end)
let s:away_start = 0
else
call s:api("ping", shellescape(s:away_task))
endif
"redraw!
endfunction
" }}}
" Autocommand Events {{{
augroup Wakatime
autocmd!
autocmd BufEnter * call s:bufenter()
autocmd BufLeave * call s:bufleave()
autocmd VimEnter * call s:vimenter()
autocmd VimLeave * call s:vimleave()
autocmd BufWritePost * call s:bufwrite()
autocmd CursorHold,CursorHoldI * call s:cursorhold()
augroup END
" }}}
" Restore cpoptions
let &cpo = s:old_cpo