Hatena::ブログ(Diary)

yattの日記 このページをアンテナに追加 RSSフィード

2017-02-23

redashを使わなかった話、あるいは風呂敷を広げすぎない自戒


結論:新しいツールを導入しなくてもSQLの実行結果をメールで送信するだけで良かった


毎日決まった時間に動作するプログラムを書いて個人で運用しているのですが、動いた実績が日によって違うものなので、簡単に確認する手段が欲しくなりました。

そこで新しいものを触りたい欲が刺激され、redash ( https://redash.io/ ) という可視化ツールを使ってみようか迷いました。使い方に慣れておけば、他でも可視化したいという要望が出てきたときに知識を使い回せると考えたからです。

しかし、すぐにこれまでの経験が浮かび思いとどまりました。

  • だいたい環境構築でつまずいて、(ただでさえ少ない)余暇を奪う
  • こういうツールって思ったよりマシンリソースを食う
  • 実行結果を細かく修飾したくなってきて設定とかCSSいじりだしたりしそう…

↑これにかける時間でゲームしたりDVD観たりできそう(重要)


そもそも、やりたい事は「SQLの実行結果を1日に1回程度確認したい」というだけなので、実行結果をそのままメールで送る程度で十分な事に気が付きました。しかも、メールを送信するプログラムは既にあるので、SQLと簡単なシェルスクリプトを書けばすぐに実装できます。

ただの表で表示されるより棒グラフや折れ線グラフで綺麗な表示ができる方が良いといえば良いのですが、実際、別になくてもそんなに困らない。

というわけで、SQLを実行してメールを送るだけにしました。具体的には、シェルスクリプトで特定のフォルダ以下にあるSQLファイルすべてについて、psqlを--htmlオプション付きでクエリ実行し、その結果を連結してgmailにメールを送信する対応をして終わらせました。

新しい事を覚えて他にも活かせるようにしよう、というのは良いのですが、新しい事だらけだと疲れるし、目的に対してかける時間や手段が大げさになりすぎる事もよくあります。そこら辺はバランス感覚が大事というか、完璧を求めすぎないほうが良いのかなと思います。

2017-02-07

Pythonでpickleを使って永続化する、期限付きメモ化デコレータ

止まっちゃうプログラムの応急処理として、pickleでキャッシュを永続化するメモ化関数を書いたのでついでにgistにあげました。いつかまた使う気がする。

メモ化する関数がコールバック関数を受け取るタイプの関数だったので、キャッシュのキーを作る段階で関数名の文字列に置き換えちゃってます。

2015-06-01

windowsでxargs -Pがしたい

windowsでxargs -P 10 -n 2みたいなことをしたかったのですが、

  • powershellとかではそういうことができなさそう
  • cygwinは入れたくない

ということで自分で実装することにしました。

最低限必要な-P(--max-procs) -n(--max-args) {}(プレースホルダ)は実装されてます。

これで並列実行させることで7分掛かってた処理が3分になりました。

multiprocessingは本当に便利。

続きを読む

2015-05-23

python pipでインストール用ファイルをローカルに保存しておく

アプリケーションを別のサーバーに載せ替えたい等の理由で、インストールされているpythonパッケージを、別のマシンへ展開したいことがあります。

こういう時pipなら、pip freezeでパッケージ名とバージョンの一覧を出力して、他マシンでその一覧を元にinstallすると簡単です。

[HOST A]$ pip freeze > /nas/pip/requirements.txt
[HOST A]$ cat /nas/pip/requirements.txt
BeautifulSoup==3.2.1
Flask==0.10.1
Jinja2==2.7.3
...
terminado==0.5
toolz==0.7.1
tornado==4.1
wheel==0.24.0
[HOST B]$ pip install -r /nas/pip/requirements.txt

しかし、これだとfreezeで一覧にした後に、ライブラリの特定のバージョンが公開停止されたり、あるいはライブラリ自体が

公開停止されると、うまく展開できなくなるか、一部の機能が動かない(とか、変に動いたりする)はず。

それだと困るので、今の状態で、予め依存するライブラリも含め、全てローカルに保存しておきたい。

しかしPyPIから一つ一つ落としてくるのも手間です。



こういうときに便利なコマンドは無いのか…



installコマンドに--downloadというオプションがありました。

指定すると、インストール用の.tar.gzや.whlファイルをフォルダに放り込んでくれます。

ダウンロードまでで、インストールはされません。これで今あるpython環境をローカルに落としておけます。

[HOST A]$ pip freeze > /nas/pip/requirements.txt
[HOST A]$ pip install --download /nas/pip/archive -r /nas/pip/requirements.txt

あとは展開される側でそのフォルダを参照してpip installすればOKです。

[HOST B]$ pip install --no-index --find-links=file:///nas/pip/archive -r /nas/pip/requirements.txt
...()
Collecting wheel==0.24.0 (from -r ..\requirements.txt (line 34))
Installing collected packages: pyquery, pytz, pyzmq, redis, requests, selenium,
tornado, terminado, toolz, wheel
  Running setup.py install for pyquery
  Running setup.py install for redis
  Running setup.py install for selenium
  Running setup.py install for tornado
  Running setup.py install for terminado
  Running setup.py install for toolz
Successfully installed pyquery-1.2.9 pytz-2014.10 pyzmq-14.6.0 redis-2.10.3 requests-2.5.1 selenium-2.44.0 terminado-0.5 toolz-0.7.1 tornado-4.1 wheel-0.24.0

なお、cx_OracleとかscipyとかpylzmaとかのC拡張が含まれる場合は、関連するコンパイラなども持っておく必要がある気がします。

あと、pip以外からパッケージをインストールしている場合(aptとかyumで入れてる場合)、当然これでは展開できないので要注意です。

※pip 1.5.6(python2.7)で動作確認してます。

参考:

User Guide − pip 6.1.1 documentation

How to use Python's pip to download and keep the zipped files for a package? - Stack Overflow

2015-04-30

Project Eulerの日本語訳へのリンクを張るuser script(2015/4/30時点)

Project Eulerの日本語訳へのリンクを張るuser scriptを更新しました。

Chrome 42.0.2311.90 m

Tampermonkey 3.10.84

で動作確認してます。

f:id:yatt:20121105215448p:image

// ==UserScript==
// @name         ScriptEuler
// @namespace    http://d.hatena.ne.jp/yatt/
// @description  link euler project problem to http://odz.sakura.ne.jp/
// @include      https://projecteuler.net/archives*
// ==/UserScript==

(function(){
  var lst = document.getElementsByClassName('id_column')
  var prefix = 'http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%20'
  
  for (var i = 1 ; i < lst.length; i++)
  {
      var no = lst[i].innerHTML
      var a = document.createElement('a')
      a.href = prefix + no
      a.innerText = no
      
      lst[i].innerText = ''
      lst[i].appendChild(a)
  }
})()

関連