LINUX.ORG.RU

Where has my time gone?

 ,


13

1

Release early, release often!

В продолжение тем Time Card и Консольный тайм-трекер хочу представить общественности свой собственный велосипед.

Отличается от других велосипедов квадратными колёсами, тем, что время сам считает.

Вобщем, что меня не устраивало в других подобных программах, так то, что надо в ручную заводить, что когда делать начал и не забывать потом ещё всё это вовремя терминировать. А я человек очень ленивый и забывчивый. Поэтому сдружиться с такими программами я не смог.

Так родилась идея всё это дело автоматизировать. Т.е. программа сама следит за тем, какие окна активны и записывает время проведённое в том или ином приложении. К концу рабочего дня можно разобрать на что же было потрачено время и записать уже всё это по-хорошему в систему учёта времени или ещё куда.

Когда срабатывает скринсейвер счёт прекращается. Неактивные таски забываются после 8-и часов (плюс-минус рабочий день).

Всё это дело бежит просто на заднем фоне и предоставляет (пока ещё рудиментарные) результаты на http://localhost:8001/ Записи дампятся каждую минуту на диск, т.ч. рестартов тоже не боится.

Работает с EWMH компатибельными WM. (Для xmonad нужна особая магия, которую я не осилил.) UPD: внизу по треду есть решение.

Т.ч. приглашаю, кому не лень, потестить мой быдлокод и поделиться идеями и предложениями. А так же поругать и поучить. Надеюсь мой маленький проект окажется не безинтересным.

Установка: go get github.com/dim13/gone

ref: https://github.com/dim13/gone

UPD: в планах добавть ещё какую-нибудь fuzzy logic для группировки окон по названиям.

★★★★★

Последнее исправление: CYB3R (всего исправлений: 14)

Ещё бы оно не падало. Вот стектрейс, если нада

panic: runtime error: index out of range

goroutine 4 [running]:
runtime.panic(0x6f0600, 0xb071d7)
	/home/ox/opt/go/src/pkg/runtime/panic.c:266 +0xb6
main.Xorg.active(0xc2100c5000, 0x131, 0xc2100bd090, 0xc2100b6530, 0xc2100d91e0, ...)
	/home/ox/go/src/github.com/dim13/gone/gone.go:78 +0xf4
main.Xorg.winName(0xc2100c5000, 0x131, 0xc2100bd090, 0xc2100b6530, 0xc2100d91e0, ...)
	/home/ox/go/src/github.com/dim13/gone/gone.go:108 +0x47
main.Xorg.update(0xc2100c5000, 0x131, 0xc2100bd090, 0xc2100b6530, 0xc2100d91e0, ...)
	/home/ox/go/src/github.com/dim13/gone/gone.go:126 +0x53
main.Tracker.collect(0xc2100af3f0)
	/home/ox/go/src/github.com/dim13/gone/gone.go:181 +0x4fc
created by main.main
	/home/ox/go/src/github.com/dim13/gone/gone.go:289 +0x56

goroutine 1 [IO wait]:
net.runtime_pollWait(0x7f2e803d77d0, 0x72, 0x0)
	/home/ox/opt/go/src/pkg/runtime/netpoll.goc:116 +0x6a
net.(*pollDesc).Wait(0xc2100d5290, 0x72, 0x7f2e803d60c0, 0xb)
	/home/ox/opt/go/src/pkg/net/fd_poll_runtime.go:81 +0x34
net.(*pollDesc).WaitRead(0xc2100d5290, 0xb, 0x7f2e803d60c0)
	/home/ox/opt/go/src/pkg/net/fd_poll_runtime.go:86 +0x30
net.(*netFD).accept(0xc2100d5230, 0x800688, 0x0, 0x7f2e803d60c0, 0xb)
	/home/ox/opt/go/src/pkg/net/fd_unix.go:382 +0x2c2
net.(*TCPListener).AcceptTCP(0xc210000b38, 0x47d7cb, 0x7f2e80246dd8, 0x47d7cb)
	/home/ox/opt/go/src/pkg/net/tcpsock_posix.go:233 +0x47
net.(*TCPListener).Accept(0xc210000b38, 0x7f2e803d7ab0, 0xc210000b70, 0xc210068580, 0x0)
	/home/ox/opt/go/src/pkg/net/tcpsock_posix.go:243 +0x27
net/http.(*Server).Serve(0xc210035cd0, 0x7f2e803d68d8, 0xc210000b38, 0x0, 0x0)
	/home/ox/opt/go/src/pkg/net/http/server.go:1622 +0x91
net/http.(*Server).ListenAndServe(0xc210035cd0, 0xc210035cd0, 0x7fe6a8)
	/home/ox/opt/go/src/pkg/net/http/server.go:1612 +0xa0
net/http.ListenAndServe(0x74c8a0, 0x5, 0x0, 0x0, 0xc2100d91b0, ...)
	/home/ox/opt/go/src/pkg/net/http/server.go:1677 +0x6d
main.main()
	/home/ox/go/src/github.com/dim13/gone/gone.go:299 +0x183

goroutine 6 [chan send]:
github.com/BurntSushi/xgb.(*Conn).generateXIds(0xc2100c5000)
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:236 +0x197
created by github.com/BurntSushi/xgb.NewConnDisplay
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:110 +0x1a4

goroutine 5 [sleep]:
time.Sleep(0xdf8475800)
	/home/ox/opt/go/src/pkg/runtime/time.goc:31 +0x31
main.func·001()
	/home/ox/go/src/github.com/dim13/gone/gone.go:294 +0x71
created by main.main
	/home/ox/go/src/github.com/dim13/gone/gone.go:296 +0x64

goroutine 7 [chan send]:
github.com/BurntSushi/xgb.(*Conn).generateSeqIds(0xc2100c5000)
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:258 +0x65
created by github.com/BurntSushi/xgb.NewConnDisplay
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:111 +0x1bb

goroutine 8 [runnable]:
github.com/BurntSushi/xgb.(*Conn).sendRequests(0xc2100c5000)
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:303 +0x65
created by github.com/BurntSushi/xgb.NewConnDisplay
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:112 +0x1d2

goroutine 9 [IO wait]:
net.runtime_pollWait(0x7f2e803d7878, 0x72, 0x0)
	/home/ox/opt/go/src/pkg/runtime/netpoll.goc:116 +0x6a
net.(*pollDesc).Wait(0xc2100bf0d0, 0x72, 0x7f2e803d60c0, 0xb)
	/home/ox/opt/go/src/pkg/net/fd_poll_runtime.go:81 +0x34
net.(*pollDesc).WaitRead(0xc2100bf0d0, 0xb, 0x7f2e803d60c0)
	/home/ox/opt/go/src/pkg/net/fd_poll_runtime.go:86 +0x30
net.(*netFD).Read(0xc2100bf070, 0xc210080780, 0x20, 0x20, 0x0, ...)
	/home/ox/opt/go/src/pkg/net/fd_unix.go:204 +0x2a0
net.(*conn).Read(0xc2100b9060, 0xc210080780, 0x20, 0x20, 0x20, ...)
	/home/ox/opt/go/src/pkg/net/net.go:122 +0xc5
io.ReadAtLeast(0x7f2e803d7a30, 0xc2100b9060, 0xc210080780, 0x20, 0x20, ...)
	/home/ox/opt/go/src/pkg/io/io.go:288 +0xf6
io.ReadFull(0x7f2e803d7a30, 0xc2100b9060, 0xc210080780, 0x20, 0x20, ...)
	/home/ox/opt/go/src/pkg/io/io.go:306 +0x71
github.com/BurntSushi/xgb.(*Conn).readResponses(0xc2100c5000)
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:370 +0x1f6
created by github.com/BurntSushi/xgb.NewConnDisplay
	/home/ox/go/src/github.com/BurntSushi/xgb/xgb.go:113 +0x1e9

goroutine 11 [IO wait]:
net.runtime_pollWait(0x7f2e803d7728, 0x72, 0x0)
	/home/ox/opt/go/src/pkg/runtime/netpoll.goc:116 +0x6a
net.(*pollDesc).Wait(0xc2100d5d10, 0x72, 0x7f2e803d60c0, 0xb)
	/home/ox/opt/go/src/pkg/net/fd_poll_runtime.go:81 +0x34
net.(*pollDesc).WaitRead(0xc2100d5d10, 0xb, 0x7f2e803d60c0)
	/home/ox/opt/go/src/pkg/net/fd_poll_runtime.go:86 +0x30
net.(*netFD).Read(0xc2100d5cb0, 0xc210103000, 0x1000, 0x1000, 0x0, ...)
	/home/ox/opt/go/src/pkg/net/fd_unix.go:204 +0x2a0
net.(*conn).Read(0xc210000b70, 0xc210103000, 0x1000, 0x1000, 0x0, ...)
	/home/ox/opt/go/src/pkg/net/net.go:122 +0xc5
net/http.(*liveSwitchReader).Read(0xc2100685a8, 0xc210103000, 0x1000, 0x1000, 0xb07d80, ...)
	/home/ox/opt/go/src/pkg/net/http/server.go:204 +0xa5
io.(*LimitedReader).Read(0xc210101280, 0xc210103000, 0x1000, 0x1000, 0x0, ...)
	/home/ox/opt/go/src/pkg/io/io.go:398 +0xbb
bufio.(*Reader).fill(0xc21005c9c0)
	/home/ox/opt/go/src/pkg/bufio/bufio.go:91 +0x110
bufio.(*Reader).ReadSlice(0xc21005c9c0, 0xc21013700a, 0x0, 0x0, 0x0, ...)
	/home/ox/opt/go/src/pkg/bufio/bufio.go:274 +0x204
bufio.(*Reader).ReadLine(0xc21005c9c0, 0x0, 0x0, 0x0, 0x0, ...)
	/home/ox/opt/go/src/pkg/bufio/bufio.go:305 +0x63
net/textproto.(*Reader).readLineSlice(0xc2100cdab0, 0x7f2e803cd5e0, 0x661fc0, 0x7f2e7cb37ce8, 0x424072, ...)
	/home/ox/opt/go/src/pkg/net/textproto/reader.go:55 +0x61
net/textproto.(*Reader).ReadLine(0xc2100cdab0, 0xc210045270, 0x0, 0xc210104000, 0x0)
	/home/ox/opt/go/src/pkg/net/textproto/reader.go:36 +0x27
net/http.ReadRequest(0xc21005c9c0, 0xc210045270, 0x0, 0x0)
	/home/ox/opt/go/src/pkg/net/http/request.go:526 +0x88
net/http.(*conn).readRequest(0xc210068580, 0x0, 0x0, 0x0)
	/home/ox/opt/go/src/pkg/net/http/server.go:575 +0x1bb
net/http.(*conn).serve(0xc210068580)
	/home/ox/opt/go/src/pkg/net/http/server.go:1123 +0x3b4
created by net/http.(*Server).Serve
	/home/ox/opt/go/src/pkg/net/http/server.go:1644 +0x28b

Debasher ★★★★★
()
Ответ на: комментарий от Debasher

Get32 предполагает, что длина слайса - 4 байта, а xproto.GetProperty вернула что-то не то, видимо.

anonymous
()
Ответ на: комментарий от Debasher

Спасибо, чуть позже гляну, где там проблема. Сейчас не дома.

beastie ★★★★★
() автор топика
Ответ на: комментарий от zinfandel

Но из tmux или tty FireFox не вытянешь. =) И зачем эти обходные пути, когда можно спросить у X11 напрямую?

beastie ★★★★★
() автор топика
Ответ на: комментарий от zinfandel

Добавил лог «сдохших» тасков в «gone.log», «ресет» функцию и «дамп» актуального состояния в json: http://localhost:8001/gone.json

Переименовал также внутренний дамп из «dump.gob» в «gone.gob», т.ч., если важно прежнее состояние, то mv {dump,gone}.gob. Так же защитил внутренний дамп от «внезапной смерти» прародителя.

beastie ★★★★★
() автор топика
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от grouzen

Это не мое предложение было, а товарища кибера. Я просто продолжил его идею.

zinfandel ★★
()

пара вопросов:
1) оно что, на go? почему не православный Си?
2) если у меня на заднем плане висит окно firefox, но в это время на переднем я 5 часов подряд смотрел видео в mplayer, оно время firefox будет учитывать? надеюсь, что НЕТ, ибо иначе оно ненужно

reprimand ★★★★★
()
Ответ на: комментарий от anonymous

что значит активные окна? они все активные
меня интересуют те, которые на переднем плане

reprimand ★★★★★
()
Ответ на: комментарий от reprimand
  1. Да, на Go. Выше уже писал, мне просто стало лень опять в дебри Xlib нырять (хоть Xlib я и очень люблю и уважаю). Ну и скорость разработки и ясность кода тоже роль съиграли. Прототип был написан за 2 вечера (с обдумыванием и копанием в пакетах), экспорт в json за 5 минут.
  2. Оно ловит изменения фокуса и засчитывает время тому, кто фокус потерял. Т.е. в твоём примере время будет считаться mplayer'у.
beastie ★★★★★
() автор топика
Ответ на: комментарий от Debasher

А если обновить и собрать с этим патчем, что выводит?

diff --git a/gone.go b/gone.go
index 254c461..968fcf7 100644
--- a/gone.go
+++ b/gone.go
@@ -79,6 +79,14 @@ func (x Xorg) active() xproto.Window {
 	if err != nil {
 		return x.root
 	}
+	if p == nil {
+		log.Println("OOPS: nil reply")
+		return x.root
+	}
+	if len(p.Value) < 4 {
+		log.Println("OOPS: slice length < 4: %v", p.Value)
+		return x.root
+	}
 	return xproto.Window(xgb.Get32(p.Value))
 }
 
anonymous
()
Ответ на: комментарий от beastie

мне просто стало лень опять в дебри Xlib нырять

а что, для Go есть какая-то более высокоуровневая либа?
и да, есть же еще xcb, говорят, неплохой

засчитывает время тому, кто фокус потерял

У тебя какая-то коллизия в п.2. Может, «перестает засчитывать время тому, кто фокус потерял»?

reprimand ★★★★★
()
Ответ на: комментарий от anonymous

Второй log.Println заменить на log.Printf.

anonymous
()

$ go get github.com/dim13/gone
package github.com/dim13/gone: mkdir /usr/lib/go/src/pkg/github.com: permission denied

Без прав superuser не хочет устанавливать. Бред.

$ sudo go get github.com/dim13/gone
# github.com/dim13/gone
/usr/lib/go/src/pkg/github.com/dim13/gone/gone.go:312: undefined: sort.Reverse
/usr/lib/go/src/pkg/github.com/dim13/gone/gone.go:313: undefined: sort.Reverse
$ gone
Команда 'gone' не найдена

я понимаю, что ты крутой программист, но может ты еще расскажешь как запускать?

reprimand ★★★★★
()
Ответ на: комментарий от reprimand
$ mkdir /home/$USER/gocode
$ export GOPATH=/home/$USER/gocode
$ go get github.com/dim13/gone

А перед этим - обновить лохматый Go 1.0 хотя бы до 1.1.

anonymous
()
Ответ на: комментарий от anonymous

А перед этим - обновить лохматый Go 1.0 хотя бы до 1.1.

для этого надо либо качать исходники и опять компилировать, либо обновить весь дистр, а то базовых репах только 1.0.2

короче манал я такой велосипедизм. Нет чтобы написать программу нормальным образом, на Си, юниксвейно, и иже с ним - НЕТ, НАДО ДЕЛАТЬ ЧЕРЕЗ ЗАДНИЦУ.


проклинаю теперь тебя, beastie. Нужная программа, но реализация уровня:

собственный велосипед
мой быдлокод

молодец. Очередное поделие в /dev/null

удачи.

reprimand ★★★★★
()
Последнее исправление: reprimand (всего исправлений: 1)
Ответ на: комментарий от reprimand

Меня проклинаешь? Ню-ню. Что я тебе плохого сделал? =)

Я тебя полюбил, я тебя научу, как поставить свежий Go:

Есть маленький пакет, называется godeb. Процедура следующая:

# ставим заплесневелый Go из реп (нужен из-за chicken-and-egg проблемы)
sudo apt-get install golang
# настраиваем себе environment
mkdir -p $HOME/gocode/{bin,pkg,src}
cat <<EOF>> $HOME/.profile
export GOPATH=$HOME/gocode
export PATH=$PATH:$GOPATH/bin
EOF
. $HOME/.profile
# ставим godeb
go get github.com/niemeyer/godeb
# сносим Go из реп, но больше ненужен
sudo apt-get remove golang
sudo apt-get autoremove
# и ставим свежий Go
godeb install
# и вуаля, у нас стоит свежий Go пакет
go env
beastie ★★★★★
() автор топика
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от beastie

Меня проклинаешь? Ню-ню. Что я тебе плохого сделал? =)

это было образно. Человек выставляет свою работу на всеобщее обозрение и предлагает его установить/использовать/протестировать. В итоге я должен еще маяться подготовкой в виде того, что ты мне только что написал. Зачем мне это делать? Дай мне бинарник БЕЗ тонны зависимостей и БЕЗ требований на последние версии библиотек и glibc6-99.999, и я его запущу через ./

Страннота всё это. Ладно, сейчас попробую.

reprimand ★★★★★
()
Ответ на: комментарий от reprimand

А вообще, я фигею, дорогая редакция. Пришёл, обматерил, сломал хорошую вещь! (c) И гордо удалился. =)

beastie ★★★★★
() автор топика
Ответ на: комментарий от reprimand

Так дело то добровольне. Не можешь — не делай, не знаешь как — спроси.

На этом месте, кстати, особое спасибо Anonymous'у, который очень активно помогает, указывает на недочёты и присылает патчи! Большое спасибо!

beastie ★★★★★
() автор топика
Ответ на: комментарий от beastie

А вообще, я фигею, дорогая редакция

я всего-лишь сообщил то, что увидел. Ничего более

Пришёл, обматерил, сломал хорошую вещь

никого не материл и ничего не ломал. Удалялся для выполнения того, что ты написал

reprimand ★★★★★
()
Ответ на: комментарий от beastie

Так дело то добровольне

естественно добровольное. Потому и пишу.

Не можешь — не делай

ну да, одни велосипеды для запуска других велосипедов не работают. Не могу, да-да.

не знаешь как — спроси

уже спросил. ЧЯДНТ?

reprimand ★★★★★
()

под freebsd всё взлетело с пол оборота
поставил go, export путей, и все заработало
правда, index.html не находило, вручную скопировал в папку с бинарником

работает :)

(теперь косо смотрю на linux зоопарк)

reprimand ★★★★★
()
Ответ на: комментарий от reprimand

Да, что-то там с версиями не срослось. Похоже, что go из реп совсем протух. Для godeb есть и уже готовые бинарные сборки.

Если интерес ещё не совсем пропал, попробуй пожалуйста с бинарным вариантом.

beastie ★★★★★
() автор топика
Ответ на: комментарий от beastie

Эту проблему на днях пофиксили, но в ветку master исправление не попало. Ставить godeb надо отсюда (как написано в README):

go get gopkg.in/niemeyer/godeb.v1/cmd/godeb
anonymous
()

Спасибо, годно. Постестим :)

P.S. То ли ЛОР переклинело, то ли огнелис, несколько сообщений вместо одного добавились.

unfo ★★★★★
()
Последнее исправление: unfo (всего исправлений: 1)
Ответ на: комментарий от beastie

интерес не пропал
я просто забью на linux, и так со временем собирался отказаться от него полностью в пользу freebsd

reprimand ★★★★★
()

Балуясь, добавил «весёленький» pie chart. Может у кого ещё какие идеи будут на тему графического представления?

beastie ★★★★★
() автор топика

В новой функции forget доступ к t не охраняется мьютексом. Нужно охранять доступ к t и к полям v.Seen и v.Spent (поскольку ссылка v тоже шареная).

anonymous
()
Ответ на: комментарий от anonymous

Вызов forget() уже окружен мутексами в соответствующих caller. Т.ч. не вижу, где тут может быть проблема.

beastie ★★★★★
() автор топика
Ответ на: комментарий от beastie

Ага, точно. Если еще и внутри forget (logDelete) лочить, получится deadlock.

anonymous
()

Хотелось бы ещё возможность объединять несколько тасков в один, в который бы шло суммарное время. Для полного счастья не хватает возможности указать, например, чтобы все подтаски у файерфокса суммировались в «работу», а все от хромиума шло в «дурака валяние», ну и там сублайм в «работу», имакс в «домашние поделки » и т.п.

Weres ★★★
()
Ответ на: комментарий от anonymous

У меня ещё не вылазило. Надо будет посмотреть. Я заюзал просто готовый JS от гугля, но в дебри его не заглядывал.

beastie ★★★★★
() автор топика
Ответ на: комментарий от Weres

Частично это уже есть. Наример FF спамит в класс «Navigator», а хром в класс «chromium».

Я тоже хочу как-нибудь объединить таски. Но ещё не придумал как.

Например в xterm обычно идёт в начале имя хоста, а потом путь.

FF — приписывает в конец строки 'Mozilla Firefox' (или 'Iceweasel').

GVIM и Skype отличаются тем, что добавляют «звёздочку» где-то посередине в зависимости от того сохранён ли файл или увидено ли сообщение соответственно.

Т.е. это надо будет или отдельные костыли под каждый класс или придумать какой-нибудь AI, что бы сам это различал. =)

beastie ★★★★★
() автор топика
Ответ на: комментарий от srrmk

Не проверял. Использую стандартный. Но должен. Каких-то особых трюков нет. Проверь пожалуйста, если не сложно.

beastie ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.