Большинство разработчиков знакома с таким продуктом, как визуализатор code_swarm (на google code). Как минимум каждый третий наверняка выгружал для него лог и создавал видео, которое визуализирует процесс разработки приложения, в котором видно активность программистов. Ну и конечно каждый второй видел видео подобного рода. Практически все эти видео делались на срезе отношения программист⇨файл.
В этой статье будет описан процесс формирования лога в срезе отношения файл⇨строка, то есть с генерированное видео будет демонстрировать активность работы над файлом.
Кому это интересно под прошу под кат.
В статье будет использованы:
Готовый работающий результат находиться здесь.
Данные будем брать из diff файла, который большей частью похож на классический файл, но к нему прикрепленный еще и коммиты из репозитория. Файл имеет такой вид:
Подробно объяснять не буду, как работает программа на awk. Скажу только в общих черта:
Создайте файл с названием my.conf со следующим содержимым:
Вот его содержимое (наиболее актуальная версия файла):
Что это значит (модель «притяжение»):
Что это значит (модель «пчелы и соты»):
Все это конечно больше напоминает игру. Для меня по крайней мере этим оно и является. Скажем так, что данные вещи добавляют разнообразия в труд программиста.
Делайте клон моего репа
В этой статье будет описан процесс формирования лога в срезе отношения файл⇨строка, то есть с генерированное видео будет демонстрировать активность работы над файлом.
Кому это интересно под прошу под кат.
В статье будет использованы:
- Git — VCS
- code_swarm — визуализатор истории репозиториев.
- gource — визуализатор истории репозиториев.
- Эмулятор среды linux в Windows или UNIX OS (с git уже идет для win эмулятор msysgit)
- MEncoder — свободный кодировщик видео
- ffmpeg — программа для конвертации видео с использованием различных кодеков.
Готовый работающий результат находиться здесь.
Скрипт генерации лога для code_swarm
Для того что бы code_swarm сумел проанализировать историю, ее ему надо подать в определенном формате. Формат файла xml и имеет он следующий вид:
- <?xml version="1.0"?>
- <file_events>
- <event date="" author="" filename="" action="" comment=""/>
- </file_events>
По сути в code_swarm можно демонстрировать любую статистику, которая изменяется по времени и у которой есть объект, который что-то и субъект над которым производиться действие. В классическом случае, когда выгружается лог для code_swarm, допустим таким платформой как showteamwork, объектом является программист, субъектом файл. В нашем случае объектом будет файл а субъектом строка, которую добавили или удалили.Данные будем брать из diff файла, который большей частью похож на классический файл, но к нему прикрепленный еще и коммиты из репозитория. Файл имеет такой вид:
1142998387000:John Resig<br>&ajax/ajax.js<br>new file mode 100644<br>+// AJAX Plugin<br>+// Docs Here:<br>+// http://jquery.com/docs/ajax/<br>+if ( typeof XMLHttpRequest == 'undefined' && typeof window.ActiveXObject == 'function') {<br>+var XMLHttpRequest = function() {<br>+return new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') = 0) ?<br>-Microsoft.XMLHTTP : Msxml2.XMLHTTP);<br>-};<br>-}<br>+.xml = function( type, url, data, ret ) {<br>+var xml = new XMLHttpRequest();<br>+if ( xml ) {<br>+xml.open(type || GET, url, true);<br>+if ( data )<br>+xml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');<br>+if ( ret )<br>+xml.onreadystatechange = function() {<br>
Данный файл выгружается командой:git log -U0 --diff-filter=AMD --reverse --pretty="%at000:%cn" -10 | \<br> grep -v "^\(-\{3\}\|+\{3\}\) " | \<br> grep -v "^[+-][ ]*$" | \<br> grep -v "^[+-]$" | \<br> grep -v "^[ ]*$" | \<br> sed -e "s/diff .* b\//\&/g" \<br> -e "s/^+[ ]\+/+/g" \<br> -e "s/^-[ ]\+/-/g" \<br> -e "s/[ ]\+$//g" \<br> -e "s/^$//g" \<br> -e 's/\\/\\\\/g' \<br> -e "s/[\"\`<>$]//g"
Думаю стоит описать что и для чего здесь:- log — вывести лог
- -U0 — добавить к логу еще и дифф изменении с 0-ым количеством строк контекста, только измененные строки
- --diff-filter=AMD — выводить только файлы со статусами A: добавлен, M: изменен, D: удален.
- --reverse — обратная сортировка по дате.
- --pretty="%at000:%cn" — формат лога %at—дата, %cn—имя коммитера.
- -10 — только 10 последних коммитов.
- grep запрещаем вывод строк, которые начинаются с 3 + или — с пробелом, все пустые строки, все строки начинающиеся с + или — но они пустые.
- sed преобразовываем строки: удаляем все не нужные пробелы после +/-, делаем строки безопасными для shell.
#!/bin/sh
generate() {
if test -t 1; then
exec >$logfile
fi
echo -e "<?xml version=\"1.0\"?>\n<file_events>"
echo "generating ..." >&2
awk -v typegen=$1 '
BEGIN {
split("\b\b\b\b\b. . . . . \b- \b\b- \b\b- \b\b- \b\b- \b= = = = =", st, " ")
ist=0
_ord_init()
typehash=0
if( typegen == "ch_code") {
typehash=1
}
else if( typegen == "crypt" ) {
typehash=2
}
}
function _ord_init(low, high, i, t)
{
low = sprintf("%c", 7)
if (low == "\a") {
low = 0
high = 127
} else if (sprintf("%c", 128 + 7) == "\a") {
low = 128
high = 255
} else {
low = 0
high = 255
}
for (i = low; i <= high; i++) {
t = sprintf("%c", i)
_ord_[t] = i
}
}
function ord(str, c) {
c = substr(str, 1, 1);
return _ord_[c];
}
/^[0-9]/ {
sub(/:.*/, "");
d=$0;
next;
}
/^&/ {
sub(/&/, "");
f=$0
substr($0, 2, length($0) - 1);
next;
}
/^\+/ { a="A"; }
/^-/ { a="D"; }
/^[\+-]/ {
sub(/[\+-]/, "")
str=""
if( typehash == 1) {
for(i=1; i<length($0); i++){
str = str "" ord(substr($0, i, 1))
}
gsub(/32|16/, "/sd", str)
str = substr(str, 0, length(str)-2) "." substr(str, length(str)-1, 2);
}
else {
cmd="echo \"" $0 "\" | md5sum | cut -f1 -d \" \" | sed -e \"s@[32|16]@/sd@g;\" -e \"s/\\(..\\)\$/.\\1/\""
if ( typehash == 2 )
cmd="C:/Perl/bin/perl -e \"print crypt($ARGV[0], $ARGV[1])\" \"" $0 "\" \"1/5l58j/jk\""
cmd | getline str;
close(cmd);
}
if (str != "")
print "<event date=\""d"\" author=\""f"\" filename=\""str"\" action=\""a"\" comment=\"\"/>"
system("echo -ne \"" st[ist++] "\" >&2")
if (ist > 16) ist=0
}
' $gitdiff
echo -ne "\b\b\b\b\b\b\b\b\b\b\b\bcompleted!" >&2
echo "</file_events>"
rm $gitdiff
}
prepare_git() {
git log -U0 --diff-filter=AMD --reverse --pretty="%at000:%cn" $1 | \
grep -v "^\(-\{3\}\|+\{3\}\) " | \
grep -v "^[+-][ ]*$" | \
grep -v "^[+-]$" | \
grep -v "^[ ]*$" | \
sed -e "s/diff .* b\//\&/g" \
-e "s/^+[ ]\+/+/g" \
-e "s/^-[ ]\+/-/g" \
-e "s/[ ]\+$//g" \
-e "s/^$//g" \
-e 's/\\/\\\\/g' \
-e "s/[\"\`<>$]//g" > $gitdiff
}
fileaction="$(date +%j%H%M%s)"
typehash=md5
[ -n "$1" ] && typehash=$1 || echo -e "первым параметром передается тип функции для" + \
"генерации хеша строки\nдоступны следующие значения:\n" + \
"\t\tmd5 — по-умолчанию\n\t\tcrypt\n\t\tch_code\nusing: $0 crypt" >&2
echo "Будет применена функция: "$typehash >&2
[ -n "$2" ] && countcommit=$2 || echo -e "вторым параметром передается лимит коммитов\n" + \
"git log --help\nпараметер:\t-<n>\n\t\tLimits the number of commits to show.\nusing: $0 crypt -10" >&2
echo -n "Будет использован лимит: " >&2
[ -n "$2" ] && echo $2' коммитов' >&2 || echo "все коммиты" >&2
gitdiff=$fileaction".temp"
logfile=$fileaction"actions.xml"
prepare_git $countcommit
generate $typehash
Подробно объяснять не буду, как работает программа на awk. Скажу только в общих черта:
- Если строка начинается на цифру то значит, это время коммита, извлекаем и запоминаем это значение и сразу переходим к следующей строке
- Если строка начинается со знака & значит следующие символы это имя файла, извлекаем и переходим к следующей строке
- Если строка начинается со знака +, значит действие типа A, продолжаем анализ строки
- Если строка начинается со знака -, значит действие типа D, продолжаем анализ строки
- Если строка начинается со знака -/+, то обработать строку и вывести ее в STDOUT, перейти к следующей строке
- md5 — с использованием утилиты md5sum, затем в сумме все числа 32 или 16 заменяются символами /sd, а перед двумя последними добавляется символ точка. Это делается что бы в визуализаторе gource строилось дерево
- crypt — с использованием функции perl crypt которая шифрует входящую по ключу и возвращает результат.
- ch_code — просто преобразует все символы в цифровое значение и заменяет все числа 32 или 16 символами /sd.
- тип преобразования строк — этот параметр отвечает за преобразование строки, принимает значения приведенные выше, без его указания по умолчанию будет использоваться тип md5.
- количество коммитов — этот параметр передается в функцию генерации дифа, чтоб ограничить количество выводимых коммитов, передавать надо следующую конструкцию -num, где num число коммитов. Если его не, то берутся все коммиты.
$ echo "{путь к файлу скрипта генерации лога} \$@" > /bin/genlogcs
Собственно по генерации активности все.Конфиг для code_swarm
Теперь поговорим о конфиге для code_swarm.Для начала я собрал code_swarm из исходников, полученный файл можно скачать от сюда. Ложите его себе в директорию, где находиться code_swarm в каталог dist.Создайте файл с названием my.conf со следующим содержимым:
#Цвет для элементов заканчивающихся на число и букву
ColorAssign1="DigitLetter", ".*[0-9][a-z]", 43, 170, 215, 43, 170, 215
#Для элементов заканчивающихся на букву и цифру
ColorAssign2="LetterDigit", ".*[a-z][0-9]", 255, 134, 51, 255, 134, 51
#Букву и букву
ColorAssign3="LetterLetter", ".*[a-z][a-z]", 43, 110, 214, 43, 110, 214
#Цифру и цифру
ColorAssign4="DigitDigit", ".*[0-9][0-9]", 41, 242, 185, 41, 242, 185
Width=1280
Height=720
InputFile=data/my/data/actions.xml
PhysicsEngineConfigDir=physics_engine
PhysicsEngineSelection=PhysicsEngineOrderly
ParticleSpriteFile=src/particle.png
Font=Helvetica
FontSize=16
BoldFontSize=16
#MillisecondsPerFrame=2254085
MaxThreads=4
Background=0,0,0
TakeSnapshots=true
SnapshotLocation=data/my/png/cs-#####.png
DrawNamesSharp=true
DrawNamesHalos=true
DrawFilesSharp=false
DrawFilesFuzzy=true
DrawFilesJelly=false
ShowLegend=true
ShowHistory=true
ShowDate=true
ShowEdges=false
ShowDebug=false
EdgeLength=36
EdgeDecrement=-2
FileDecrement=-1
PersonDecrement=-1
FileSpeed=7.0
PersonSpeed=2.0
FileMass=2.0
PersonMass=10.0
EdgeLife=250
FileLife=200
PersonLife=255
HighlightPct=5
UseOpenGL=false
ShowUserName=true
IsInputSorted=false
Этот файл нам пригодится в дальнейшем.Скрипт генерации видео визуализации активности
Чтоб долго не объяснять что и где создавать и какая структура каталога должна быть, скажу просто в директории с code_swarm, в каталоге data создайте каталог my со структурой которая приведена здесь. Взять надо следующее:- Каталог data со всем содержимым
- Каталог tools со всем содержимым
- Каталог generator_logs, в который мы положим скрипт для генерации файла активности для code_swarm. Собственно, он там и находиться.
- Файл gen_log, о нем речь пойдет дальше, этот файл генерирует лог для gource из лога для code_swarm
- my.config, который описан выше
- sort_log, это скрипт для сортировки лога для gource
- run.bat, о нем речь пойдет дальше
Скрипт gen_log
Так как интересно посмотреть результат работы над файлами не только в code_swarm, но и в gource я сделал скрипт, который генерирует лог для него. Данный скрип называется gen_log (наиболее актуальная версия файла):#!/bin/sh
uses(){
echo -e 'using\n$0 file_codeswarm.xml'
}
generatelog(){
echo "genereting... "
state=("\\" "|" "/" "—")
i=0
if [ -f "$1" ]; then
result=${1%.*}'.log'
echo -n > $result
#Выводим только строки в которых есть слово event
grep -e "event " $1 | \
#удаляем все символы табуляции и пробелы
#удаляем все <event и />
sed -e "s/^[ ]*//;s/^<event //g;s|/>$||g" | \
while read line
do
date=""
#валидируем строку, появляются 4 переменные
eval $line;
#проверяем не пустали переменная date, если нет производим вывод
[ -n "$date" ] && [ "`echo -n $data | wc -c`" -gt "10" ] && date=`echo $data | sed -e "s/^\(.\{10\}\).*/\1/"`
[ -n "$date" ] && echo "$date|$author|$action|$filename" >> $result
#отображение процесса генерации.
echo -ne "\b${state[$i]}"
(( i += 1 ))
[[ $i -eq 5 ]] && i=0
done
echo -ne "\bcompleted!"
else
echo -e "file log code_swarm not exsits!\n$1"
fi
}
[ -n "$1" ] && generatelog $1 || uses
В этом скрипте используется полезная функция eval. Она исполняет текст как будто вы его ввели в командную строку. Такой подход удобен в нашем случае так как входная строка имеет следующий вид:date="1142998387000" author="ajax/ajax.js" filename="c9/sd/sd9db4/sd/sd/sdb945/sdb89a/sd/sd7/sd/sdfbfdf.04" action="A" comment=""
Как вы понимаете система обработает эту строчку и у нас появиться 5 переменных date, author, filename, action, comment (спасибо bliznezz). Эти переменные заливаются в файл со следующим форматом:date|author|action|filename
Правда у gource можно уже настроить этот формат. Формат обработки файла находиться в файле {gource_home}/data/gource.styleСкрипт run.bat
Теперь соберем все в общий файл, который будет обрабатывать сгенерированный вами, при помощи команды genlogcs, файл активности, который вы положили в каталог {code_swarm_home}/data/my/data/actions.xml.Вот его содержимое (наиболее актуальная версия файла):
call sh gen_log ./data/actions.xml
call sh sort_log ./data/actions.log > data\gource.log
pushd png
del *.png
popd
pushd ..\..
call run.bat data\my\my.config
popd
pushd png
call "..\tools\nt\mencoder" mf://*.png -mf fps=19:type=png -ovc x264 -x264encopts pass=1:bitrate=1000 -oac copy -audiofile "..\data\audio.wav" -o "..\results\result.avi"
popd
pushd "tools\gource"
call gource.exe --hide filenames,dirnames --user-scale 2 --output-framerate 25 --stop-position 1 --highlight-all-users --seconds-per-day 1 --output-ppm-stream "..\..\results\resultgource.ppm" "..\..\data\gource.log"
popd
pushd "tools\nt"
call ffmpeg -y -b 9000K -f image2pipe -vcodec ppm -i "..\..\results\resultgource.ppm" -fpre "..\ll.ffpreset" -i "..\..\results\resultgource.ppm" -vcodec libx264 "..\..\results\resultgource.avi"
call mencoder "..\..\results\resultgource.avi" -ovc x264 -x264encopts pass=1:bitrate=10000 -ofps 19 -speed 2 -o "..\..\results\resultgource.fps"
call mencoder "..\..\results\resultgource.fps" -ovc x264 -x264encopts pass=1:bitrate=10000 -oac copy -audiofile "..\..\data\audio.wav" -o "..\..\results\resultgource.avi"
popd
del results\resultgource.ppm
del results\resultgource.fps
del data\actions.log
Данный скрипт выполняет следующие действия:- сортирует его командой sort -k1 -t "|", но так как выполняем под виндой я ее в другой файл поместил, а то ругается в винде. Сортировать необходимо, так как gource работает корректно только с отсортированными данными.
- Запускает code_swarm с конфигом, который мы описали в my.config. В результате работы code_swarm генерирует большое колво png файлов
- Превращаем png файлы в видео по средствам mencoder, при этом прикрепляем звуковую дорожку, продолжительность видео можно регулировать параметром -mf fps=19:type=png, где 19 это по идее должно быть отношение между колвом файлов png и продолжительностью аудио трека в секундах. Но это мне не нравиться и я по этому использую приемлемое для меня значение.
- Потом запускается gource и выгружает результат в ppm. Предупреждаю файл будет очень большой в несколько гигабайт, так что путь выгрузки указывайте учитывая это.
- Затем перегоняем этот ppm файл при помощи утилиты ffmpeg в файл avi, но он получается очень большой и долгий. Ускоряем при помощи mencoder до тех же 19 fps. И затем снова запускаем mencoder для того чтоб прикрепить к нему звуковую дорожку. В результате получаем файл всего в несколько 10 мегабайт.
Результат выполнения, с типом генерации md5
Вот мои результаты с репозитория jquery.code_swarm
Что это значит (модель «притяжение»):
- Объект притягивает к себе субъекты.
- Объекты отталкивают друг дргуга
- Объекты притягиваются друг к другу если используют один и тотже субъект
- Субъекты которые часто используется увеличивается в размере и в яркости.
gource
Что это значит (модель «пчелы и соты»):
- Объект — пчела
- Субъект — соты
- Пчелы строят соты, Зеленый луч — добавление, Красный — удаление.
Итоги
Эта статья в первую очередь подчеркивает то, что с помощью визуализаторов code_swarm и gource можно обработать любую статистику, у которой есть переменная время, главное в правильном виде подать им эту статистику.Все это конечно больше напоминает игру. Для меня по крайней мере этим оно и является. Скажем так, что данные вещи добавляют разнообразия в труд программиста.
Делайте клон моего репа
$ git clone git://github.com/artzub/code_swarm-gource-my-conf.git test
и выкладывайте свои результаты мне очень интересно.Литература
- Эффективное программирование на языке awk — хороший материал по awk.
- ОС UNIX руководство пользователя
- man ffmpeg
- Основы использования MEncoder и другие главы из документации по MPlayer
- git log — документация по выгрузки лога
- Как бороться с ошибкой: couldn't commit memory for cygwin heap?
- Awk парсинг xml
- Shell, замена всех символов строки их кодами