Показаны сообщения с ярлыком perl. Показать все сообщения
Показаны сообщения с ярлыком perl. Показать все сообщения

вторник, 10 августа 2010 г.

Ода eclipse

Очень часто люди задаются вопросом, чем пользоваться для разработки программного обеспечения. Однозначного ответа тут нет. У меня очень долго не складывались отношения с eclipse, так как либо он тормозил, так как компьютеры были слишком слабыми, либо он падал, так как что-то с чем-то не срасталось, либо у меня на тот момент оказывались более удобные инструменты. В целом, использование eclipse идет немного мимо желания шире использоват vim. И тем не менее, я сейчас пользуюсь eclipse, и нисколько не жалею. Пользуюсь на маленьком asus eee 901, на котором как утверждается - невозможно им пользоваться. Оказалось - возможно. Главное, очистить как можно больше рабочего пространства. Мое выглядит приблизительно вот так:

Вполне душевно.

Самое вкусное естественно в том, что за время глючной жизни затмения под него было разработано множество плагинов, да и сам он оброс полезным функционалом. Перечислю те, которые сильно помогают мне:
  • Vrapper - делает eclipse похожим на vim. До полного сходства далеко, но зато весь функционал остается в непосредственной доступности.
  • Pydev - теперь мы можем редактировать и отлаживать python.
  • EPIC - если вы еще не забыли о языке perl, этот плагин вам также понадобится. Меня он тут очень выручил, когда выяснилось, что модуль получения котировок вышел из строя.
  • MercurialEclipse - интеграция с mercurial.

Итого, у меня почти vim, я могу править и отлаживать все что мне нужно, все это собрано в одном месте, да и в версии eclipse helios добавили хорошие возможности по созданию ссылок в рабочем пространстве, что сильно упрощает жизнь, очень удобно. В экран влезает, работает достаточно стабильно. Вот и встает вопрос, как же подружить свою любовь к vim и удобность работы с eclipse. Пока ответа нет (eclim не предлагать, хочу его использовать, но я так понимаю - придется его пилить, то, что я попробовал - это слишком мало, по сравнению с тем, что дает мне vrapper), я иду на компромисы и изучаю возможности и того и другого, не отдавая однозначного предпочтения. В конце концов я уверен, что прийду к какому-то хорошему варианту, лучше, чем текущий.

А пока, спасибо eclipse за повышение моей продуктивности.

четверг, 5 августа 2010 г.

Новая версия Finance::Quote::Sberbank 0.04

Представляю релиз очередной версии (0.04) модуля для получения котировок ОМС Сбербанка. Загрузить и посмотреть можно здесь.

Изменения:
  • убрана работа со временем (у людей из других часовых поясов после работы скрипта в окружении не будет московской временной зоны);
  • поиск смещения, чтобы меньше зависеть от изменений формата Сбербанком.

Обновление рекомендуется, так как старая версия с текущим форматом Сбербанка просто не работает.

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

понедельник, 9 ноября 2009 г.

Все говно, кроме мочи

В очередной раз нашел этому подтверждение в интервью с Brad Fitzpatrick: "...The implementation of libraries that look like they could be beatiful are shit...".

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

В эти выходные наконец таки реализовал скрипт для сборки нового ядра и почистки старых. Экономия нервных клеток на этой операции будет оценена не скоро, но думаю - будет приятно. Мне кажется, именно в этом суть программистского подхода к делу, все рутинные операции учить делать машину. Если мы делаем это больше чем один раз, это уже надо автоматизировать. Очередной пункт в личном todo-листе выполнен. Выложу тут и код этого сокровища в следующий раз. А пока оставшиеся пункты:
1. python-скрипт для заполнения ежедневных отчетов на работе
2. perl-скрипт Finance::Quote::Sberbank - исправить накопившиеся ошибки (есть 9-месячный багрепорт, а это ужасно)
3. локализовать openerp
4. lfs-нуть нетбук
5. найти или написать штуку для проверки торговой стратегии на архивных данных
6. написать собственно штуку для торговой стратегии
7. сделать информеры на svn репозитории в виде notify в gnome

И еще много чего, что мне хочется сделать. Каждый день я сижу где-то до 2-х часов ночи, стараясь сделать сегодня чуть-чуть больше чем вчера. И главное - ни один день не должен пройти без познания или создания чего-то нового. Это настоящее счастье - узнавать новое.

среда, 28 октября 2009 г.

МНОГО редактирования

Бывает, откопаешь что-то свое гениальное, и аж стыдно становится:


my $bproc = 0;
my $bfunc = 0;
my $bout = 0;
my $bbody = 0;
my $inselect = 0;

sub checkpar {
$bout = 1 if(/\sout\s/ and $bproc == 1);
if($bbody == 1) {
print "declare\n" if (!/^begin$/);
$bbody = 0;
}
if(/\)/ and $bproc == 1) {
$bproc = 0;
$bbody = 1;
print;
print " returns void" if($bout == 0);
print " as \$\$" ;
}
elsif(/\)\s*return\s+(\S+)/ and $bfunc == 1) {
$bfunc = 0;
$bbody = 1;
s/return/returns/;
print;
print " as \$\$" ;
}
else {
print;
}
print "\n";
}

while(<>) {
s/\r\n//;
next if /^\s*(as|is)\s*$/;
$inselect++ if(/select/);
if($inselect != 0) {
s/^(\s*)([a-zA-Z_.0-9]+)(\s+)([a-zA-Z_.0-9]+)(\s*,\s*)$/$1$2$3as $4$5/;
s/^(\s*,\s*)([a-zA-Z_.0-9]+)(\s+)([a-zA-Z_.0-9]+)(\s*)$/$1$2$3as $4$5/;
}
$inselect-- if(/from/);
s/^(\s*from T_OBJ o), ([^\s]+\s+)([^\s]+)$/$1 left join $2$3 on o.ID=$3.ID/;
s/ and \S\.ID \(\+\)= \S\.ID//;
s/varchar2/varchar/g;
s/number/numeric/g;
s/nvl\(/coalesce\(/g;
s/c_pkgcore\.gt_cursor/refcursor/gi;
s/"ADD"/ADD/g;
s/substr\(t_pkgVOC\.fGet([^(]+)\(t_pkgVOC\.fGetVOC\(([^)]+)\), ([^)]+)\), 1, \d+\)/t_pkgVOC__fGetVOC$1\($2, $3\) as/g;
s/substr\(c_pkgCore\.fGetSysUser([^(]+)\(([^)]+)\), 1, \d+\)/c_pkgCore__fGetUser$1\($2\) as/ig;
s/substr\((.*), (\d+), (\d+)\)/substring\($1 from $2 for $3\)/g;
s/substr\((.*), (\d+)\)/substring\($1 from $2\)/g;
s/t_pkgVOC\./t_pkgVOC__/gi;
s/t_pkgObj\./t_pkgObj__/gi;
s/t_pkgView\./t_pkgView__/gi;
s/c_pkgCore\./c_pkgCore__/gi;
s/SQL%NOTFOUND/not found/gi;
if(/function\s+([^(\s]*)([^\n]*)/) {
$bfunc = 1;
$_ = "/* $1 */\ncreate or replace function t_pkgRpt__$1$2";
}
if(/procedure\s+([^(\s]*)([^\n]*)/) {
$bproc = 1;
$_ = "/* $1 */\ncreate or replace function t_pkgRpt__$1$2";
}
if(!/^\s+$/) {
checkpar;
}
}
print "\$\$ language plpgsql;\n\n";

воскресенье, 2 августа 2009 г.

Дело GnuCash живет и побеждает - модуль Finance::Quote для ММВБ (Micex)

Именно так можно охарактеризовать мое настроение после письма Андрея, с просьбой опубликовать разработанный им модуль получения котировок ММВБ для GnuCash.

Публикую: http://atsave.narod.ru/micex-pm/index.html.

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

Поскольку обнаружилось, что у народа возникают вопросы по публикации своих модулей на CPAN, в ближайшее время постараюсь вспомнить свой опыт регистрации и изложить его в виде статьи.

Даешь больше модулей для GnuCash!

З.Ы. 21.01.2011 - поменялась ссылка на сайт автора, обновил. Попробую опубликовать на CPAN для облегчения инсталляции, тем более, что мой родной модуль что-то в последнее время поломался...

понедельник, 25 мая 2009 г.

GnuCash - лечим получение котировок валют.

С недавних пор (где-то с 12 мая) мой любимый gnucash перестал получать котировки валют. Как всегда программный отказ удачно совпал с сильными колебаниями на валютном рынке, но мои активы вполне себе спокойно поживали, что меня удивило, но с некоторым запозданием.

Все это конечно ужасно, но поправимо. Меня всегда удивляли люди, которые ругали опенсурс за ошибки. Увидел ошибку - докажи, что сам чего-то стоишь: поправь ее. Ну или баг-репорт отправь.

Итак, как вылечить gnucash? Ответ как всегда в модуле Finance::Quote. Надо его немного поправить.

Ищем, куда спрятался Quote.pm в нашей локальной инсталяции (мы же не будем править то, что ставим дистрибутивом):

kilork@phoenix ~ $ find ~/perl -name Quote.pm
/home/kilork/perl/lib64/perl5/site_perl/5.8.8/Finance/Quote.pm


Правим его. Тут особых подробностей не напишу, кроме того, что освоил debug perl под emacs (M+x perldb). Сами авторы дают резервный вариант для получения котировок валют, но почему-то, чтобы им воспользоваться, приходится попрограммировать. Я для вас поставлю свой вариант исправления, который работает у меня. В upstream пока погожу слать, подожду версии 1.17 Finance::Quote. Ну а если вдруг кому пригодится, то смотрите, как я поправил.

Ищем оригинал (каюсь, не сделал бэкапа, поэтому сравниваем с тем, что идет в дистрибутиве):

phoenix kilork # equery f Finance-Quote | grep Quote.pm
/usr/lib64/perl5/vendor_perl/5.8.8/Finance/Quote.pm


Сравниваем:

kilork@phoenix ~ $ diff -u {/usr/lib64/perl5/vendor,/home/kilork/perl/lib64/perl5/site}_perl/5.8.8/Finance/Quote.pm

--- /usr/lib64/perl5/vendor_perl/5.8.8/Finance/Quote.pm 2009-05-23 23:26:49.000000000 +0400
+++ /home/kilork/perl/lib64/perl5/site_perl/5.8.8/Finance/Quote.pm 2009-05-25 00:56:28.000000000 +0400
@@ -42,9 +42,9 @@
$VERSION $TIMEOUT %MODULES %METHODS $AUTOLOAD
$YAHOO_CURRENCY_URL $USE_EXPERIMENTAL_UA/;

-$YAHOO_CURRENCY_URL = "http://uk.finance.yahoo.com/currency/convert?amt=1&submit=Convert&";
+#$YAHOO_CURRENCY_URL = "http://uk.finance.yahoo.com/currency/convert?amt=1&submit=Convert&";
# If the above URL ever fails, try rewriting this module to use the URL below.
-# $YAHOO_CURRENCY_URL = "http://uk.finance.yahoo.com/q?s=USDCAD%3DX";
+$YAHOO_CURRENCY_URL = "http://uk.finance.yahoo.com/q?s=";# USDCAD%3DX";

@ISA = qw/Exporter/;
@EXPORT = ();
@@ -241,16 +241,11 @@

my $ua = $this->user_agent;

- my $data = $ua->request(GET "${YAHOO_CURRENCY_URL}from=$from&to=$to")->content;
- my $te = HTML::TableExtract->new( headers => ['Symbol', 'Bid', 'Ask'] );
- $te->parse(decode_utf8($data)); # The web page returns utf8 content which gives
- # a warning when parsing $data in HTML::Parser
-
- # Make sure there's a table to parse.
- return undef unless ($te->tables);
-
- my $row = ($te->rows())[0];
- my ($exchange_rate) = $$row[1];
+ my $data = $ua->request(GET "${YAHOO_CURRENCY_URL}$from${to}%3DX")->content;
+ if($data !~ /Bid:.+>(\d+.\d+)<.+Ask:/g) {
+ return undef;
+ }
+ my $exchange_rate = $1;
$exchange_rate =~ s/,// ; # solve a bug when conversion rate
# involves thousands. yahoo inserts
# a comma when thousands occur


Данное решение считаю костылем, жду 1.17 с исправлением досадной ошибочки, или 1.16.1. Надеюсь, костыль не будет передаваться из рук в руки, и пригодится только мне :)

пятница, 13 февраля 2009 г.

Работа с GnuCash. Часть 5. Finance::Quote. Придание модулю Sberbank полезных функций.

UPD: Внимание! Последняя самая актуальная и полная версия руководства по GnuCash на русском языке в различных форматах доступна по адресу http://kilork.org/gnucash.html.

Для разнообразия решил писать этот пост в emacs. Несколько странно, поскольку делаю это уже где-то в 4-й раз, привыкаю к emacs, потом начинаешь пользоваться "классическим редактором", и emacs забывается. А зря. На eee есть правда существенный недостаток - справа нет ctrl, что делает пользование несколько осложненным. В четвертый раз вместо забоя пользуюсь backspace, что свидетельствует о наличии застарелых рудиментарных атавизмов, а ведь одна из главных идей emacs - отказ от лишних и утомительных движений, а тянуться к backspace между прочим очень утомительно. Все, хватит о emacs, вернемся к нашим баранчикам.

Чтобы сделать что-то полезное, зайдем на сайт Сбербанка на страничку с котировками ОМС. Текст странички сохраняем на диск. Файл с котировками в формате xls аналогично. Начнем разбираться с последним. Для работы с excel файлами используется модуль Spreadsheet::Read. Все тонкости работы с excel нам не нужны, достаточно с первой странички получить значение из ячейки с известным адресом. Делается это следующим образом:

#!/usr/bin/perl
use Spreadsheet::Read;

my $xls = ReadData("dm1002.xls");

print "$xls->[1]{'A1'}\n";


Этот код выведет первую ячейку, с первой страницы.

Нам надо существенно больше. Найти котировки счетов и вытащить цену покупки/продажи. Но простота обращения с модулем облегчит нам задачу. Теперь надо разобраться со страничкой. Если взглянуть на реализации других модулей для Finance::Quote, то можно понять, что люди предпочитают HTML::TableExtract. Нам же только вытащить ссылку с первой строчкой, так что сойдут и более простые методы. Котировки в Сбербанке идут по месяцам. Страница для января 2009 года это:

http://sbrf.ru/ru/valkprev/archive_1/index.php?year114=2009&month114=01


Получаемая страница содержит котировки с датами, идут они в обратном порядке. Пример HTML кода с котировками:

<li class="xls"><a href="/common/img/uploaded/c_list/sdmet/download/2009/01/dm0131.xls" target="_blank">31 января 2009 года</a><!-- --> (с 00 часов 00 мин.)</li>


Для отбора нам понадобится регулярное выражение. Наверно это самый сложный для понимания кусок, но к счастью, писать регулярные выражения существенно проще, чем читать. Итак, на основе предыдущего текста:

<li class="xls"><a href="(.*\/(\d{4})\/(\d{2})\/dm(\d{2})(\d{2})(?:\_\d+)?\.xls)"[^\(]*\(.+(\d{2}).+(\d{2})


Еще один кусок кода. Пора объединить все это в единое целое. В модуле Finance::Quote::Sberbank находим метод sberbank. Сейчас он имеет следующий вид:

sub sberbank {
my $quoter = shift;
my @stocks = @_;
}


Для получения текста страничек нам понадобится user agent. К счастью, он есть у нас, итак, первая строка:

my $ua = $quoter->user_agent;


Теперь можно и запросить страничку.

my $year = 2009;
my $mon = 1;
my $url = "http://sbrf.ru/ru/valkprev/archive_1/index.php?year114=${year}&month114=${mon}";
my $response = $ua->request(GET $url);


Важно стараться сделать какую-никакую, но обработку ошибок, в случае с запросом странички, стоит проверить успешность операции. А если неуспешно, нам надо правильно выставить статусы ошибок. Статусы ошибок записываются в @stocks следующим образом:
unless ($response->is_success) {
foreach my $stock (@stocks) {
$info{$stock, "success"} = 0;
$info{$stock, "errormsg"} = "HTTP failure";
}
return wantarray() ? %info : \%info;
}


Т.е. для каждого запрашиваемого инструмента ставим статус success = 0 и errormsg = HTTP failure. Какая-то обработка ошибок. Теперь надо разобрать содержимое на предмет интересующих нас ссылок:
my $link = "";
my $content = $response->content;
if($content =~ /<li class="xls"><a href="(.*\/(\d{4})\/(\d{2})\/dm(\d{2})(\d{2})(?:\_\d+)?\.xls)"[^\(]*\(.+(\d{2}).+(\d{2})/g) {
$link = $1;
}


Получили ссылку на файлик xls, теперь надо бы его получить, и наконец уже зачитать наши котировки. Единственное, надо бы определиться как мы их назовем. Например, вот так:
Золото SBRF.AU
Серебро SBRF.AG
Платина SBRF.PT
Палладий SBRF.PD


Итак, ссылка найдена, и мы получаем котировки:

if($link) {
$url = "http://sbrf.ru/".$link;
$response = $ua->request(GET $url);
unless($response->is_success) {
foreach my $stock (@stocks) {
$info{$stock, "success"} = 0;
$info{$stock, "errormsg"} = "HTTP failure";
}
return wantarray() ? %info : \%info;
}
$content = $response->content;


Загружаем его как xls-файл:

my $xls = ReadData($content);


Теперь нам надо найти начало котировок ОМС. Это место начинается со строки, определяемой следующим регулярным выражением:

/\d+\. Котировки продажи и покупки драгоценных металлов в обезличенном виде/


Пробегаем примитивным поиском:

my $start = 1;
while($xls->[1]{"A$start"} !~ /\d+\. Котировки продажи и покупки драгоценных металлов в обезличенном виде/ && $start < 100) {
$start++;
}


Определим хэш значений для поиска, в соответствии с определенными выше кодами для различных ОМС:

my %map = (
'Золото' => 'SBRF.AU',
'Серебро' => 'SBRF.AG',
'Платина' => 'SBRF.PT',
'Палладий' => 'SBRF.PD'
);


Начиная с этого момента нам осталось только пробежать по оставшимся строкам в файле, найти наши котировки и заполнить соответствующие части в info:

while($xls->[1]{"A$start"}) {
$start++;
my $name = $xls->[1]{"A$start"};
if($name) {
my $stock = $map{$name};
next unless($stock);
$info{$stock, "symbol"} = $stock;
$info{$stock, "name"} = $name;
$info{$stock, "currency"} = "RUB";
$info{$stock, "method"} = "sberbank";
$info{$stock, "bid"} = $xls->[1]{"E$start"};
$info{$stock, "ask"} = $xls->[1]{"D$start"};
$info{$stock, "last"} = $info{$stock, "bid"};
$quoter->store_date(\%info, $stock, {today => 1});
$info{$stock, "success"} = 1;
}
}


Теперь осталось только сделать тесты. Здесь на помощь приходит модуль Test::More. Открываем t/Finance-Quote-Sberbank.t и пишем там такое:

use encoding 'utf8';
use Test::More;

plan tests => 5;

use_ok('Finance::Quote');
use_ok('Finance::Quote::Sberbank');

my $quoter = Finance::Quote->new("Sberbank");

ok(defined $quoter, "created");

my %info = $quoter->fetch("sberbank", "SBRF.PD");

ok(%info, "fetched");

ok($info{"SBRF.PD", "name"}, "palladium");



Запускаем тесты в терминале и если все получилось, то видим следующее:

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ make test
cp lib/Finance/Quote/Sberbank.pm blib/lib/Finance/Quote/Sberbank.pm
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/Finance-Quote-Sberbank....ok                                               
All tests successful.
Files=1, Tests=5,  1 wallclock secs ( 1.05 cusr +  0.06 csys =  1.11 CPU)


Это хорошо, теперь можно смело делать make install и запускать gnucash с загрузкой нашего свежего модуля:

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ make install
Manifying blib/man3/Finance::Quote::Sberbank.3pm
Installing /home/kilork/perl/share/perl/5.8.8/Finance/Quote/Sberbank.pm
Installing /home/kilork/perl/man/man3/Finance::Quote::Sberbank.3pm
Writing /home/kilork/perl/lib/perl/5.8.8/auto/Finance/Quote/Sberbank/.packlist
Appending installation info to /home/kilork/perl/lib/perl/5.8.8/perllocal.pod
kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ FQ_LOAD_QUOTELET="-defaults Sberbank" gnucash
gnc.bin-Message: main: binreloc relocation support was disabled at configure time.

Found Finance::Quote version 1.15


Заходим в редактор цен, жмем "Получить котировки", убеждаемся, что все получилось:



Рис 5.1. Получение котировок новым модулем Sberbank.

В заключение, полный текст модуля и скриншот набора текста в emacs:

package Finance::Quote::Sberbank;

use 5.008008;
use strict;
use warnings;
use encoding 'utf8';

use HTTP::Request::Common;
use Spreadsheet::Read;

require Exporter;

our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration use Finance::Quote::Sberbank ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(

) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(

);

our $VERSION = '0.01';


# Preloaded methods go here.
sub methods { return ( sberbank => \&sberbank ); }

{
my @labels = qw/name last bid ask date isodate currency/;

sub labels { return ( sberbank => \@labels ); }
}

sub sberbank {
my $quoter = shift;
my @stocks = @_;
my %info;

my $ua = $quoter->user_agent;
my $year = 2009;
my $mon = 1;
my $url = "http://sbrf.ru/ru/valkprev/archive_1/index.php?year114=${year}&month114=${mon}";
my $response = $ua->request(GET $url);
unless ($response->is_success) {
foreach my $stock (@stocks) {
$info{$stock, "success"} = 0;
$info{$stock, "errormsg"} = "HTTP failure";
}
return wantarray() ? %info : \%info;
}
my $link = "";
my $content = $response->content;
if($content =~ /<li class="xls"><a href="(.*\/(\d{4})\/(\d{2})\/dm(\d{2})(\d{2})(?:\_\d+)?\.xls)"[^\(]*\(.+(\d{2}).+(\d{2})/g) {
$link = $1;
}
if($link) {
$url = "http://sbrf.ru/".$link;
$response = $ua->request(GET $url);
unless($response->is_success) {
foreach my $stock (@stocks) {
$info{$stock, "success"} = 0;
$info{$stock, "errormsg"} = "HTTP failure";
}
return wantarray() ? %info : \%info;
}
$content = $response->content;
my $xls = ReadData($content);
my $start = 1;
while($xls->[1]{"A$start"} !~ /\d+\. Котировки продажи и покупки драгоценных металлов в обезличенном виде/ && $start < 100) {
$start++;
}
my %map = (
'Золото' => 'SBRF.AU',
'Серебро' => 'SBRF.AG',
'Платина' => 'SBRF.PT',
'Палладий' => 'SBRF.PD'
);
while($xls->[1]{"A$start"}) {
$start++;
my $name = $xls->[1]{"A$start"};
if($name) {
my $stock = $map{$name};
next unless($stock);
$info{$stock, "symbol"} = $stock;
$info{$stock, "name"} = $name;
$info{$stock, "currency"} = "RUB";
$info{$stock, "method"} = "sberbank";
$info{$stock, "bid"} = $xls->[1]{"E$start"};
$info{$stock, "ask"} = $xls->[1]{"D$start"};
$info{$stock, "last"} = $info{$stock, "bid"};
$quoter->store_date(\%info, $stock, {today => 1});
$info{$stock, "success"} = 1;
}
}
}
return wantarray() ? %info : \%info;
}
1;
__END__




Рис 5.2. Набор этого поста в emacs.

Очередная часть могучего труда готова. Удачи и успехов в освоении gnucash!

пятница, 30 января 2009 г.

Работа с GnuCash. Часть 4. Finance::Quote. Разработка собственных модулей получения котировок.

UPD: Внимание! Последняя самая актуальная и полная версия руководства по GnuCash на русском языке в различных форматах доступна по адресу http://kilork.org/gnucash.html.

В предыдущей части мы вплотную подошли к необходимости автоматизировать процесс получения котировок. И gnucash предоставляет нам для этого отлично средство - Finance::Quote. Finance::Quote - это perl-модуль, и этим очень многое сказано. В комплекте поставки версии 0.15 множество разнообразных подмодулей. Но нужных нам, естественно, все равно нет. Написаны модули на perl. Множество уже в страхе будут бежать без оглядки, так как perl ассоциируется у всех с чем-то сложным и совершенно непонятным. Скажу, так оно и есть. Но в данном случае, модули очень просто написаны, откомментированы. Я убежден, что для того, чтобы написать собственный модуль под Finance::Quote нужны минимальные познания в программировании, не требуется глубокое знание perl, и нужно только желание достичь цели. В состав Finance::Quote входит документация о разработке собственных модулей получения котировок. Используя это руководство мы сделаем простой модуль для получения котировок ОМС Сбербанка. Процесс создания разобьем на шаги. Начнем с того, что у нашего пользователя нет опыта работы с perl. Прекрасно, тогда переходим к созданию нашего модуля для новичков в perl.

Создание модуля.

Мне кажется, очень много для популярности того или иного языка делают вовсе не его качества именно как языка, а какие-то маленькие удобные фенечки, или большие удобные фенечки. В perl главная большая удобная фенечка - это CPAN, гигантский архив готовых модулей, которые ставятся через единый интерфейс, с зависимостями, тестами, и прочим. Очень большая удобная фенечка. Есть фенечки поменьше. Например, утилитки для генерации каркаса модуля. Открываем терминал, идем в папочку проектов (у себя назову ее ~/Desktop/test):

kilork@pantogan:~$ mkdir Desktop/test
kilork@pantogan:~$ cd Desktop/test
kilork@pantogan:~/Desktop/test$ 


Надо определиться с названием. Я обычно весьма много времени трачу на придумывание названия. Как говорил небезизвестный персонаж, как вы яхту назовете, так она и поплывет. Поэтому название надо очень тщательно выбирать. Наш модуль будет жить в пространстве имен Finance::Quote и относится к Сбербанку, назовем его Finance::Quote::Sberbank. Итак, воспользуемся командой h2xs:

kilork@pantogan:~/Desktop/test$ h2xs -AX -n Finance::Quote::Sberbank
Defaulting to backwards compatibility with perl 5.8.8
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.

Writing Finance-Quote-Sberbank/lib/Finance/Quote/Sberbank.pm
Writing Finance-Quote-Sberbank/Makefile.PL
Writing Finance-Quote-Sberbank/README
Writing Finance-Quote-Sberbank/t/Finance-Quote-Sberbank.t
Writing Finance-Quote-Sberbank/Changes
Writing Finance-Quote-Sberbank/MANIFEST


Как видно, одна маленькая команда насоздавала нам множество файликов. Один, очевидно, сам каркас модуля. Затем идет генератор Makefile'ов. Файлик с README, файлик тестов. Лог изменений Changes. И MANIFEST. Чудеса, да и только, почти вся работа сделана за нас этой маленькой чудесной командой. Главное - не давать никому знать об этом.

Сгенерируем Makefile. Укажем дополнительный параметр, говорящий о нашем желании ставить модули в локальную директорию пользователя.

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ perl Makefile.PL PREFIX=~/perl
Checking if your kit is complete...
Looks good
Writing Makefile for Finance::Quote::Sberbank


Отлично, теперь мы можем собирать, тестировать, инсталлировать, и собирать дистрибутив следующими простыми командами:

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ make # собираем
cp lib/Finance/Quote/Sberbank.pm blib/lib/Finance/Quote/Sberbank.pm
Manifying blib/man3/Finance::Quote::Sberbank.3pm

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ make test # тестируем
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/Finance-Quote-Sberbank....ok                                               
All tests successful.
Files=1, Tests=1,  0 wallclock secs ( 0.08 cusr +  0.01 csys =  0.09 CPU)

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ make install # инсталлируем
Installing /home/kilork/perl/share/perl/5.8.8/Finance/Quote/Sberbank.pm
Installing /home/kilork/perl/man/man3/Finance::Quote::Sberbank.3pm
Writing /home/kilork/perl/lib/perl/5.8.8/auto/Finance/Quote/Sberbank/.packlist
Appending installation info to /home/kilork/perl/lib/perl/5.8.8/perllocal.pod

kilork@pantogan:~/Desktop/test/Finance-Quote-Sberbank$ make dist # собираем дистрибутив
rm -rf Finance-Quote-Sberbank-0.01
/usr/bin/perl "-MExtUtils::Manifest=manicopy,maniread" \
-e "manicopy(maniread(),'Finance-Quote-Sberbank-0.01', 'best');"
mkdir Finance-Quote-Sberbank-0.01
mkdir Finance-Quote-Sberbank-0.01/lib
mkdir Finance-Quote-Sberbank-0.01/lib/Finance
mkdir Finance-Quote-Sberbank-0.01/lib/Finance/Quote
mkdir Finance-Quote-Sberbank-0.01/t
Generating META.yml
tar cvf Finance-Quote-Sberbank-0.01.tar Finance-Quote-Sberbank-0.01
Finance-Quote-Sberbank-0.01/
Finance-Quote-Sberbank-0.01/MANIFEST
Finance-Quote-Sberbank-0.01/lib/
Finance-Quote-Sberbank-0.01/lib/Finance/
Finance-Quote-Sberbank-0.01/lib/Finance/Quote/
Finance-Quote-Sberbank-0.01/lib/Finance/Quote/Sberbank.pm
Finance-Quote-Sberbank-0.01/t/
Finance-Quote-Sberbank-0.01/t/Finance-Quote-Sberbank.t
Finance-Quote-Sberbank-0.01/Changes
Finance-Quote-Sberbank-0.01/README
Finance-Quote-Sberbank-0.01/Makefile.PL
Finance-Quote-Sberbank-0.01/META.yml
rm -rf Finance-Quote-Sberbank-0.01
gzip --best Finance-Quote-Sberbank-0.01.tar


Команд немного, все очень просто, на текущем этапе нам понадобятся команды make и make test. Затем make install, если надо будет использовать модуль из gnucash или других программ, и наконец - make dist, если нам вдруг захочется все это дело выложить на CPAN (мне захотелось, можно поискать Sberbank на http://search.cpan.org).

Каркас модуля мы создали, подготовили среду к сборке и тестированию, приступаем к реализации модуля для Finance::Quote.

Реализация модуля для Finance::Quote.

Открываем Sberbank.pm в нашем любимом текстовом редакторе. Текст снабжен неплохими комментариями, так что понятно назначение той или иной секции в сгенерированном файле. Нам надо удалить все лишнее, и реализовать методы для Finance::Quote. Самое забавное, что реализуя модуль под Finance::Quote нам вовсе не надо что-то знать о Finance::Quote, не надо даже упоминать ее в нашем модуле. Достаточно поддержать нужный интерфейс и подложить модуль в правильное место.

Находим строку "# Preloaded methods go here.". Отсюда мы и начнем определять наши методы. Первый метод, который нам понадобится, это метод methods. Этот метод будет вызван Finance::Quote для получения списка нестандартных методов, определяемых нашим модулем. Это как раз та строчка, которую мы увидим, когда подключим наш модуль в gnucash. Кроме этого нам понадобится метод, который вернет список поддерживаемых нами атрибутов labels, ну и собственно, главный метод, в котором мы и будем лазить на сайт Sberbank'а, и забивать значения атрибутов. Пока определим только стандартные методы, и пустышку для главного метода. Вот, что у меня получилось:

sub methods { return ( sberbank => \&sberbank ); }

{
my @labels = qw/name last bid ask date isodate currency/;

sub labels { return ( sberbank => \@labels ); }
}

sub sberbank {
my $quoter = shift;
my @stocks = @_;
}


Получился метод sberbank с двумя параметрами, загадочный $quoter, и не менее загадочные @stocks. Метод ничего не возвращает, а вообще говоря - должен. Тем не менее, это уже вполне минимально подходящий для использования модуль Finance::Quote.

Делаем make, make test, make install. Запускаем gnucash командой:
kilork@pantogan:~/Desktop$ FQ_LOAD_QUOTELET="-defaults Sberbank" gnucash


У меня открылся наш последний пример из 3-й части руководства. Есть золото, но котировки надо вбивать руками. Теперь мы попробуем настроить получение котировок с помощью нашего новомодного модуля. Пустышки правда пока.

Идем в редактор ценных бумаг, находим там наше золото, выбираем "Правка", на открывающеся форме настройки ставим галочку "Получить онлайн котировки", устанавливаем радио-переключатель в положение "Неизвестный", в выпадающем списке находим "sberbank" и выбираем. Жмем "Ок".



Рис 4.1. Настройка получения онлайн котировок.

Мы подключили наш модуль к нашему инструменту - золоту. Теперь убедимся, что пока ничего не работает. Идем в редактор цен, жмем "Получить котировки", получаем вполне ожиаемый результат.



Рис 4.2. Получение котировок с новым "пустым" модулем приводит к ошибке.

Уже чего-то мы достигли, наш модуль виден gnucash, и выдает даже какую-то ошибку при попытке запроса. В следующей части мы постараемся научить его делать более полезные вещи.

пятница, 16 января 2009 г.

CPAN Finance::Quote::Sberbank - Модуль для получения котировок ОМС Сбербанка

Выложил свои труды на CPAN. Теперь любой желающий сможет это установить.

kilork@pantogan:~$ cpan

cpan> i Finance::Quote::Sberbank
Strange distribution name [Finance::Quote::Sberbank]
Module id = Finance::Quote::Sberbank
CPAN_USERID KILORK (Alexander Korolyoff )
CPAN_VERSION 0.01
CPAN_FILE K/KI/KILORK/Finance-Quote-Sberbank-0.01.tar.gz
MANPAGE Finance::Quote::Sberbank - Obtain quotes from Sberbank (Savings Bank of the Russian Federation)
INST_FILE /home/kilork/perl/share/perl/5.8.8/Finance/Quote/Sberbank.pm
INST_VERSION 0.01


cpan> install Finance::Quote::Sberbank


Сразу, правда, в gnucash это не заработает - требуется немного магии. Магия заключается в том, что надо поправить сам Finance::Quote и добавить туда загрузку модуля, выглядит это приблизительно так:

kilork@pantogan:~/perl/share/perl/5.8.8/Finance$ diff Quote.pm~ Quote.pm -u
--- Quote.pm~ 2008-10-26 11:15:50.000000000 +0300
+++ Quote.pm 2009-01-08 11:13:02.000000000 +0300
@@ -170,7 +170,7 @@
DWS FTPortfolios Fidelity FinanceCanada Fool HEX IndiaMutual
LeRevenu ManInvestments NZX Platinum SEB StockHouseCanada
TSP TSX Tdefunds Tdwaterhouse Tiaacref Troweprice Trustnet Union
- USFedBonds VWD ZA Cominvest Finanzpartner
+ USFedBonds VWD ZA Cominvest Finanzpartner Sberbank
Yahoo::Asia Yahoo::Australia Yahoo::Brasil
Yahoo::Europe Yahoo::NZ Yahoo::USA/; }


Т.е. просто дописываем модуль в список загружаемых. В данном примере подразумевается, что cpan для пользователя настроен на локальную директорию в папке пользователя, и туда же установлены модули Finance::Quote и Finance::Quote::Sberbank. Подробнее о настройке установки модулей в локальную директорию можно посмотреть здесь. Я пока сомневаюсь, что мой модуль включат в официальный дистрибутив, так что пока так.

UPDATE: Внимательно прочитал модуль, можно сделать гораздо проще, надо назначить переменную окружения:
FQ_LOAD_QUOTELET="-defaults Sberbank"

В таком случае можно ничего не править, будут загружены как модули по-умолчанию, так и наш новый модуль. Как передавать переменную окружения уже отдельный вопрос. Можно переопределить в профиле, или просто передавать через командную строку:

kilork@mexidol ~$ FQ_LOAD_QUOTELET="-defaults Sberbank" gnucash finans



Итак, если мы все сделали правильно, то в gnucash->Инструменты->"Редактор ценных бумаг" можно будет настроить следующее:




Теперь, надеюсь более подробно описать собственно сам процесс разработки новых модулей для gnucash. Цепочка отработана.

UPDATE2: Добавил отдельный сайт для работы с котировками ОМС Сбербанка - пользовательский и программный интерфейс: http://kilork.blogspot.com/2012/01/sberbank-quotes.html.