2015年12月12日土曜日

Elasticsearch/KibanaをインストールしたDockerコンテナを作った

OS:Ubuntu 14.04
java8/Elasticsearch2.1.0/Kibana4.3.0で構築。
https://github.com/maar4569/dockerfiles-sample/tree/master/elastic

さらにネットワークまわり(主にIPアドレスだが)を編集するコマンドを追加したのが
これ。
https://github.com/maar4569/dockerfiles-sample/blob/master/elastic_dev/Dockerfile

これは前者のイメージをベースに作成したDockerイメージ。

2015年12月11日金曜日

スペースを含むkey/value形式のデータを抽出する

splunkではkey=val形式のイベントを検出すると自動的に抽出する。
しかしvalの値にスペースを含むと正しく抽出することができない。

例えば、ダブルクォテーションで括られていれば抽出可能だが、
無い場合抽出できない。

msg="hello world" =>hello worldを抽出

msg=hello world   => helloだけ抽出。

正しく抽出するにはtransform.confのREGEXを使用して抽出する。

例えば以下のようなイベントが存在するとする。


この時以下のように設定する。
props.conf

[mysourctype]
REPORT-mykvformat=my_kv

transforms.conf
[my_kv]
REGEX=¥s(?<_KEY_1>¥w+)=(?<_VAL_1>¥w+)¥s¥w+=

splunkを再起動する。

機械学習で使うライブラリ(numpy/scipy/matplotlib)をインストールしたDockerコンテナを作った

OSはCentOS 7ベースで、
numpy、scipy、matplotlibをインストールしたDockerコンテナを作成。

Dockerfileは以下のとおり。
https://github.com/maar4569/dockerfiles-sample/blob/master/machinelearning/Dockerfile

最低限のライブラリをインストールしただけなので、コードを各場合は必要に応じてインストールすること。

2015年8月10日月曜日

指定した時間の範囲で集計処理する

まとめ
・指定した時間の範囲を生成するにはgentimesを使う。
・gentimesで生成した時間の範囲をmapコマンドに渡してループ処理する。


使用するシーン
通常1日は0時から始まり23:59:59までと考えるが、業務によって1日の始まりを0:00AM以外で規程することがある。
例えば2:00AMを1日の始まりとした場合、2015/08/01を2015/08/01 02:00:00 〜 2015/08/02 01:59:59として考える。
 
timechartで集計すると1日の始まりは0:00AMとなるので、期待する結果が得られない。
この時gentimesコマンドを使用すると1日の始まりを2:00AMと規程して、時間の範囲を作成することができる。unixtimeとhuman readableな時間が返る。



このうちstarttime、endtimeフィールドをトークンでmapコマンドに渡す事により、
各時間の範囲のサーチができる。

以下は5/25から7/25日のデータを25日を基点に集計したサンプル。(starttimeは)
最初の時間の範囲は5/25から6/24、2行目は6/25から7/24となっている。


上記のstarttimeフィールドとendtimeフィールドをmapコマンドにトークンで渡すと、
gentimesコマンドで取得した時間の範囲で集計している。


おわりに
mapコマンドにわたすトークンは時間でなくてもよいので、何かのリストを渡してループ処理したい場合など応用範囲は広いのではと思う。

2015年5月20日水曜日

Splunkでサマリインデックスを使う

まとめ
・サマリインデックスを使うことで大量データであっても効率よくレポートが作成できる。
・レポートでは先頭にsiが付くsixxxxコマンドを使う。


サンプルデータを登録・設定する
まず以下のようなデータがインデックスに登録されていくものとする。
販売履歴のようなデータ。
フィールドは「タイムススタンプ、アイテム、価格」

<やりたいこと>
・週次で売上履歴のサマリを取得したい。ここでは価格の合計値。
・集計対象は前週の日曜日から土曜日とする。

<インデックスの登録されていくデータ>
2015-03-01 13:00:00,book,100
2015-03-02 11:00:00,book,200
2015-03-03 16:00:00,book,300
2015-03-04 10:00:00,eraser,400
2015-03-05 09:00:00,note,500
2015-03-06 08:00:00,note,600
2015-03-07 07:00:00,book,700
2015-03-08 16:00:00,book,800
2015-03-09 13:00:00,book,900
2015-03-10 14:00:00,book,1000
2015-03-11 22:00:00,pen,1100
2015-03-12 23:00:00,book,1200
2015-03-13 10:00:00,note,1300
2015-03-14 12:00:00,note,1400
2015-03-15 16:00:00,book,1500
2015-03-16 13:00:00,book,100
2015-03-17 11:00:00,book,200
2015-03-18 16:00:00,book,300
2015-03-19 10:00:00,eraser,400
2015-03-20 09:00:00,note,500
2015-03-21 08:00:00,note,600
2015-03-22 07:00:00,book,700
2015-03-23 16:00:00,book,800
2015-03-24 13:00:00,book,900
2015-03-25 14:00:00,book,1000
2015-03-26 22:00:00,pen,1100
2015-03-27 23:00:00,book,1200
2015-03-28 10:00:00,note,1300
2015-03-29 12:00:00,note,1400
2015-03-30 16:00:00,book,1500
2015-03-31 16:00:00,book,1600


<事前の条件>
インデックス名
  demo_alert

フィールド定義
$SPLUNK_HOME$/etc/system/local/props.confで以下のように定義しておく。
定義後はsplunkdの再起動を忘れない。

 [demo_alert]
NO_BINARY_CHECK = true
SHOULD_LINEMERGE = false
category = Custom
pulldown_type = true
FIELD_NAMES = time,item,amount
disabled = false
TIME_FORMAT=%Y-%m-%d %H:%M:%S
MAX_DAYS_HENCE=7

サマリインデックスを作成する
サマリインデックスは通常のインデックスを作成する場合と同じ方法。
サマリインデックスを作成するための特別な手順はない。

インデックス名はdemo_alert_sumとして作成する。

レポートを作成する
ここで毎週スケジューリングして週次の集計値を作成するための
設定を行う。サマリインデックスに集計値を格納する部分もここで行う。
1.[設定]-[サーチ、アラート、レポート]を選択する

2.[新規追加]ボタンをクリックする。


3.サーチ文字列を設定する。ここで記述するコマンドはサマリインデックス用の
コマンドを使う。先頭にsiが付くコマンド。例えばsistatsなど。

3-1.サーチ文字列を設定する
index="demo_alert" | sistats sum(amount)

ここでは、amountの合計を取得する。

開始時刻と完了時刻が-w@w0、@w0となっている。
これはサーチ対象とする時間の範囲を指定している。この表現を細かく見ていく。

まず開始時刻だが、[-w@w0]は先週の日曜日を示す。@より前の部分[-w]は先週、@移行の部分w0は日曜日を示す。(0が日曜で、1が月曜、、、、6が土曜日となる。)

一方[@w0]は今週の日曜日を示す。今週を示すのであれば@より前の部分の定義は不要だ。

開始時刻と完了時刻は以上・未満の関係になるため、
先週日曜の00:00:00から先週土曜の23:59:59までを意味する。

時間に関する表現は以下のURLを参照。
http://docs.splunk.com/Documentation/Splunk/6.2.2/SearchReference/Commontimeformatvariables

 3-2.[このサーチをスケジュールする]にチェックを入れる。


3-3.スケジュールタイプで[基本]を選択する
3-4.実行間隔を選択する。ここでは[週(毎土曜日の午前0時)]を選択する。
  選択肢はいくつか用意されているが、任意のタイミングで実施したい場合は、
  cronと同じ形式で設定することも可能。








サマリインデックスを確認する

スケジュールどおりに動作し、サマリインデックス[demo_alert_sum]を開くと、下記のように集計値が保存される。

1行目はXからXまでのフィールド「amount」の集計で3600
2行目はXからXまでのフィールド「amount」の集計で3600
3行目はXからXまでのフィールド「amount」の集計で7200

上記のログデータとつき合わせて確認するとちゃんと週次で集計されていることが
分かる。

格納されている情報を見るとわかるが、集計元のイベントは無くsixxxxコマンドで集計した統計情報だけである。



集計対象の時間はinfo_min_time,info_max_timeフィールドに値が登録される。
unixtimeなのでわかりにくいので、わかりやすく変換してみる。

unixtimeを変換し、変換した値を新たなフィールドとして追加するためには、evalコマンドとsrftimeコマンドを使用する。
例)
eval info_min_time2=strftime("%Y-%m-%d %H %M %S")

それぞれの集計データが
2015/3/8(日) 00:00:00以上、2015/3/15(日) 00:00:00未満
2015/3/15(日) 00:00:00以上、2015/3/22(日) 00:00:00未満
の範囲のデータを対象にしていたことがわかる。


終わりに
大量データから集計値を作成する場合、長期間に渡るデータを1回の操作で抽出するのは、
非効率的だし、処理をするのに多くのリソースを必要とする。要は作成に時間がかかりすぎる。ユーザ要件の中で例えば1ヶ月に1回のレポート作成があるのならば、
週次や日次で集計データを作成しておくと効率的にレポートを作成することができる。
レポートとサマリインデックスを併用するとそれが実現できる。





2014年11月26日水曜日

AMIにMechanizeをインストールする

AMIのruby環境はあまりパッケージがインストールされていないので、mechanizeのインストールにつまずくことがある。
 なので、ここに一連の手順を残しておく。

rubyバージョン ruby 2.0.0p481






1.yum install ruby-devel

2.yum install gcc-c++.noarch

3.yum install libxslt-devel.x86_64

4.yum install patch.x86_64
  以下のような nokogoriやmechanizeをインストールするときに
  以下のようなメッセージが出で失敗する事があるので
  必ずインストールする。


  patch.logを見ると、パッチコマンドが見つからないという趣旨のエラーが出力されている。

  Running 'patch' for libxml2 2.9.2... ERROR, review '/home/ec2-user/.gem/ruby  /2.0/gems/nokogiri-1.6.4.1/ext/nokogiri/tmp/x86_64-redhat-linux-gnu/ports  /libxml2/2.9.2/patch.log' to see what happened.
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.
 
5.gem install nokogiri

6.gem install mechanize

以上

2014年10月20日月曜日

ZaimApp for Splunk

Zaimに入力データをJson形式でインポートすればデータ解析ができる
SplunkのAppを作ってみた

Zaimは分析能力が乏しいことから分析能力が非常にすばらしいSplunkでの
利用を検討。

基本的にはJsonフォーマットによるインポートだが
CSVファイルでもinputs.confを調整すればいけるはず。

まずはベータとして。
https://github.com/maar4569/splunk_zaimapp

2014年5月9日金曜日

Splunkをインストールする

Splunkとは何か。Splunkは 主にセキュリティのインシデント分析(SIEM)を
中心としたログデータの検索・分析システム。
CSVやJSONなどテキスト形式のデータであれば何でも取り込める。データベースのように
スキーマを定義する必要が無く、Splunkが自動的に判別する。検索スピードもとても速い。この手の分析ツールは高価で大企業しか使えないイメージがあるが無償版も提供している。1日500MB未満のデータ取り込みであれば無料で使えるので、個人で使うのであれば十分過ぎる。
 
とりあえず使ってみるために以下のチュートリアルを参考にインストールした。
 http://docs.splunk.com/Documentation/Splunk/latest/SearchTutorial/InstallSplunk
 
OSはCentOS 6.2
 
オペレーションは以下のとおり。
 
1.ルートに切り替える。
 su root ( splunk do not needs root user! )

2.Splunkを展開。とりあえず/optへ。

  tar xvzf splunk-6.0.2-196940-Linux-x86_64.tgz -C /opt
 
  詳細は以下のURL
    http://docs.splunk.com/Documentation/Splunk/6.0.3/Installation/InstallonLinux

3.Splunkを開始するためにシェルを作っておく。

  3-1.cd /opt

  3-2.vim run_splunk.sh

         cd /splunk/bin
         ./splunk start

  3-3.chmod +x run_splunk.sh

4.splunkを開始する。
   4-1.  ./run_splunk.sh
            installration processing, you need to agree user licencse.
    
   4-2.  splunk web site is http://hostname:8000
   

5.splunkにサインする。
   デフォルト認証情報は以下のとおり。
           user admin
           pass changeme
 パスワードの変更を求められるので変える。

以上で完了。簡単過ぎ。
      
 

2014年3月6日木曜日

PostgreSQL9.3に更新してからDBのサイズが取得できなくなった

仕事でPostgreSQL8.3を使っていたがいろいろ問題があって9.3にアップする事に。
DB監視でサイズ情報を取得していたが動かなくなってしまいさあ大変。
ということで調べてみた。

これが今まで使っていたクエリ(の抜粋)
select
  relname,
  to_char(pg_relation_size(relname)),
  to_char(pg_total_relation_size(relname)),
  relfilenode
where
 pg_class
where
 relowner = xxxx and
 relkind = 'r';

こんな情報がありました。明示的なキャストが必要みたい。でも8.3だぞ?

 http://www.sraoss.co.jp/technology/postgresql/8.3/



select
  relname,
  to_char(pg_relation_size(relname::text)),
  to_char(pg_total_relation_size(relname::text)),
  relfilenode
where
 pg_class
where
 relowner = xxxx and
 relkind = 'r';

実際に明示的に記述することで取得できるようになった。
実務ではプログラミングから離れているため、このようなちょっとしたことでも
キャッチアップできていない。そんな自分に腹が立つ。。。

2013年12月24日火曜日

車に追突してきた容疑者が見つかった

 11月に家族と車で買い物に出かけた。信号待ちで止まっていたところ後ろから追突された。ハザードを出して車から出ようとしたところ、急発進して私の車の横を駆け抜け信号無視して逃げていった。車の特徴・ナンバーを覚えていた私は、頭にきて警察に通報、もろもろの処理をして、1か月ほど経過したところだった。

 警察からは容疑者が謝りたい、弁償したいとのことだった。ならばということで電話で話をしようということで待っているのだがかかってこない。
正直、車の破損についてはどうでもいい。 いい大人が逃げる、そして謝ると言って謝りの連絡がないことに憤りを感じている。本当はこんないい加減な人間と話す時間さえももったいないくらいなので、話すことさえためらわれる。しかし言いたい事は言っておきたいので待ってることにする。


はあ、全く人間的に問題のある大人が多すぎてうんざりする。

2013年12月19日木曜日

[Ruby]Zaim API V2に対応した認証用ライブラリを作ってみた

家計簿ソフトZaim(https://zaim.net)が提供するWebAPIの認証部分を
omniauthを参考に作ってみた。

ソースコードは以下のサイトにアップロード。
https://github.com/maar4569/omniauth-zaimv2

参考にしたのは以下のライブラリ。(v1用のライブラリ。)
mururuさんありがとうございます。

https://github.com/mururu/omniauth-zaim


以前Google Apps ScriptからZaim APIをたたこうとして失敗し涙したが、
今回は成功。

Railsに組み込む場合、コールバック用のコントローラに
以下のような記述をしておくと、verify APIのレスポンスが得られる。

#サンプル
def callback
  auth = request.env['omniauth.auth']
  @user_id = auth.info.id
  @name = auth.info.name
  @input_count = auth.info.input_count
  @day_count = auth.info.day_count
  @repeat_count = auth.info.repeat_count
  @day = auth.info.day
  @week = auth.info.week
  @currency_code = auth.info.currency_code"
end

また、カテゴリに関する情報を取得する場合は以下のコードで取得できる。
 api_url = auth.extra.access_token.consumer.options[:site] + "/v2/home/category"
 raw_info = MultiJson.load(auth.extra.access_token.get(api_url).body)['categories']
 p "categries =>  #{raw_info}"

ジャンルなども基本的には同じ要領で取得する。

Zaimの情報を分析して家計を助けるぞー

2013年11月27日水曜日

EC2インスタンスにRuby1.9.3をインストールする。

Amazon EC2のRHEL6の無料インスタンスにRuby1.9.3をyumでインストールしたかったが、初期設定のレポジトリには1.8.x1しかなかったため、パッケージをビルドしてインストールすることを試みた。

1.まずビルドに必要なパッケージをインストールする。
  対象は以下のとおり。
  zlib-devel
  openssl-devel
  readline-devel
  tk-devel
  libyaml-devel
  libffi-devel
  rpm-build

  注意
  libyaml-develをインストールしたときに、以下のようなエラーが発生するかもしれな
  い。
  Error:  Multilib version problems found. This often means that the root
       cause is something else and multilib version checking is just
       pointing out that there is a problem.

  要は複数のバージョンがあるので問題ありということ。
  元々インストールされていたのはlibyaml.i686、必要なのはlibyaml-devel.x86_64
      なので元々あったほうをアンインストールし、libyaml-devel.x86_64をインストール
  した。
      sudo yum remove libyaml.i686
      sudo yum install   libyaml-devel.x86_64
 
2.home直下にrpmbuildディレクトリを作成する。

3.rpmbuildディレクトリの下に以下5つのディレクトリを作成する。
  BUILD  RPMS  SOURCES  SPECS  SRPMS

4.SOURCESディレクトリに移動する。
  cd ~/rpmbuild/SOURCES

5.ruby1.9.3のアーカイブをダウンロードする。
  wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p448.tar.gz

6.SPECSディレクトリに移動する。
  cd ~/rpmbuild/SPECS

7.specファイルをダウンロードする。
 wget https://raw.github.com/imeyer/ruby-1.9.3-rpm/master/ruby19.spec

8.ビルドを実行する。
  rpmbuild -bb ruby19.spec

9. rpmファイルが作成されていることを確認する。
 ~/rpmbuild/RPMS/x86_64/ruby-1.9.3p448-1.el6.x86_64.rpm

10.~/rpmbuild/RPMS/x86_64ディレクトリに移動する。
   cd ~/rpmbuild/RPMS/x86_64

11.パッケージをインストールする。
  sudo rpm -ivh ruby-1.9.3p448-1.el6.x86_64.rpm

12.インストールできたか確認する。
  ruby -v

  OK。1.9.3が入った!

  ruby 1.9.3p448 (2013-06-27 revision 41675) [x86_64-linux]




2013年11月20日水曜日

AWSでWindowsのFTPサーバ(パッシブモード)を構築する

仕事でWindowsサーバをFTPサーバとする必要があったため、AWS上で構築してみた。
その作業のまとめ。
前提として
 ・WindowsサーバのEC2インスタンスを作成しておくこと。
 ・そのインスタンスにFTPサービスをインストールしておくこと。
 ・RDPで接続できること。
が必要。

今回は動作確認のため同一VPC内に2つのWindowsサーバインスタンスを作成した。
FTPクライアントとFTPサービスのような位置づけ。
FTPプロトコルの利用範囲だが、
「インターネットからのFTPは許さない。指定VPC内のみ許可する」
ものとする。

1.まずマネジメントコンソールからVPCを選択する。
2.Security Groupsを選択する。

3.右ペインから用意したインスタンスが使うVPCを選択する。

4.下のペインで以下のように設定する。
 Create a new Rule  -> Custom TCP Rule
   Port range             ->  21
   Source                   -> VPCのGroup ID。 (ここではsg-4ce9e12e)
 ポイントはSourceにVPCのGroup IDを入れる事である。これでVPC内でしかFTPが
 使えないようになる。VPCの名前はDetailsタブのGroupIDに書かれている。


5.Apply Rule Changesをクリックする。これをクリックしないと設定が反映されない。

6.手順4同様に今度はFTPの制御用ポートを設定する。WindowsサーバのWindowsファイアウォールのポリシーを見てみると1024-65535が制御用ポートとして設定されているのでVPCでも同じ設定をする。
 Create a new Rule  -> Custom TCP Rule
   Port range             ->  1024-65535
   Source                   -> VPCのGroup ID


7.これでFTPサーバに接続できればOK。
   ex)ftp://サーバIPアドレス/hoge
   *hogeディレクトリはあらかじめサーバ側に作成しておくと望ましい。

2013年10月30日水曜日

CentOSでSambaを使う。

CentOS6.2でsambaをインストールしてファイル共有サーバを構築する。

1.sambaをインストール
  yum install samba.x64_86

2.sambaがインストールされたことを確認する。
  yum list installed samba
  ->samba.x64_86 3.6.9-151.el6_4.1

4./etc/samba/smb.confに以下の設定を追加
[global]
        dos charset = CP932
        display charset = UTF-8
        workgroup = VMLAN
        netbios name = VMSAMBA
[public] <-フォルダ名がpublicとして公開される。
        path = /var/samba/shared <-後で実体のフォルダを作る
        writable = Yes
        write list = smbguest <-後でアカウントを作る。

5.sharedフォルダの作成
  mkdir /var/samba
  mkdir /var/samba/shared

6.sharedフォルダを読み書きOKにする。
  chmod 777 /var/samba/shared

7.アクセス用アカウントを作成する
  /usr/sbin/useradd -m smbguest

8.アクセス用アカウントにパスワードを設定する
  pdbedit -a smbguest

9.サービスを起動する
  rc.d/init.d/smb start
  rc.d/init.d/nmb start
  できればOS起動時に自動自動するようにしたほうがよい。

10.FWでSMB関連ポートを開く
   vi /etc/sysconfig/iptables で以下の設定を追加する。
 
   -A INPUT -p tcp --dport 139 -j ACCEPT
   -A INPUT -p udp --dport 137 -j ACCEPT
   -A INPUT -p udp --dport 138 -j ACCEPT
   -A INPUT -p tcp --dport 445 -j ACCEPT

   参考:http://futurismo.biz/archives/1390

11.しかしpublicフォルダにアクセスできない。
   selinuxによるセキュリティがかかっている。
   semanageコマンドでアクセスできるようにする。
 
  semanageコマンドが無い場合があるのでその場合は追加インストールする。
  /usr/bin/yum provides /usr/sbin/semanage

  ここは結構はまるところ。

12.以下のコマンドを実行
  semanage fcontext -a -t samba_share_t "/var/samba/shared"
  restorecon -RF "/var/samba/shared"

13.Windowsマシンからアクセスする。
  ¥¥サーバ名(またはIPアドレス)¥shared

14.ログイン要求されるのでsmbguestで認証する。

15.アクセスでき晴れて書き込みできる状態に。

2013年10月10日木曜日

GoogleSpreadSheetでToDoタスクを作り、期限が近づいたらメールで通知する。

GoogleSpreadSheetは非常に便利だ。

Excelと同じように表計算やマクロ機能(Google Apps Script)を持っているが
何と言っても一番はスケジューラ機能だ。
スプレッドシートの内容を、指定した時間にメールで通知するなんてことも可能。
これはExcelだけではなかなかできないと思う。

という訳で自分の仕事を円滑に進められるようにするためにということで、
GoogleSpreadSheetを多様しているのだが、中でもToDoリストとしての活用は
いろいろな方でも使えるものだと思うのでサンプルコードを書いてみた。

コードはGitHubにアップしているのでそれを参考にしていただければ思う。
https://github.com/maar4569/notifyToDoWithGAS

そのサンプルを元にToDoアプリを作ってみようと思う。

このToDoアプリができること。
・利用者はスプレッドシートにToDoを書く。
・ToDoの締め切り日が近づいてくるとメールで通知してくれる。
 N日前ですよとか、締め切り当日ですよとか、過ぎてますよとか。

作成手順
1.GitHubからコードをダウンロードしておく。
2.まずはGoogleドライブ上に新規のスプレッドシート「ToDoList」を作成する。
  2-1.シート名を「ToDo」に変更する。
  2-2.ToDoListに1行目のカラムに「task」「deadline」「notes」「deleted」と記述する。
 task やるべきことを書いておく。
 deadline 期日を書く。(yyyy/mm/dd形式で)
 notes 補足事項があれば書いておく。
 deleted メールで通知する必要が無くなった場合「1」と書いておくと通知しなくなる。
     つまりもう済んだタスクは1と書いておく。書かないとずっとメールで通知して
     くることになる。

  2-3.2行名以降にはやるべきtaskを書く。


3.ダウンロードしたコードを挿入する。
  3-1.空のプロジェクトを選択。 

  3-2.[ファイル]-[新規作成]-[スクリプトファイル]を選択。

  3-3.main.gsというファイル名に変更し、ダウンロードしたコードをコピペする。

4.関数 notifyDoDoを探し、変数に設定する。(赤い字のところを変更する)
function notifyToDo(){
  //Spread Sheet Config
  var sheetName      = "ToDo";  <-シート名を設定する。手順2.1でToDoにしたのでここは編集しなくてもよい。
  var spreadSheetKey = "your spread sheet Key"; <-スプレッドシートのキーを設定する
スプレッドシートを開いたときのURLで下のアドレスバーの黒塗りの箇所に該当する文字列を登録する。

  //mail config
  var subject        = "[MyToDoリストからの通知]ステータスのお知らせ";  <-メールのタイトルを設定する
  var recipients     = new Array("mailaddress"); <-メールアドレスを設定する。複数ある場合は、カンマ区切りで設定する。ex)aaa@x.com,bbb@x.com
  //notification config
  var threshold      = 4;   <-期限の4日間になったら通知する

5.関数notifyToDoを実行する。

6.SpreadSheet、Gmailの利用承認を求められる。
  6-1.「続行」をクリックする。


  6-2.「承認する」をクリックする。
   これによってこのアプリがSpreadSheetへのアクセスとメール送信が許可される。


7.トリガーを設定する。
 これは定期的にToDoの状況をメールで通知するようにするためである。

  7-1.「リソース」から「現在のプロジェクトのトリガー」を選択。

  7-2.「トリガーが設定されていません。・・・」をクリックする。

  7-3.トリガーを設定する。
          実行は、notifyTodo関数
          イベントは、時間主導型、日タイマー、午後4時から5時。
          これで毎日午後4〜5時の間にタスクのステータスがメールで通知される。



8.トリガーで設定した時間になるとメールで通知される。(めでたしめでたし)


 


2013年10月8日火曜日

コマ大数学科 特別集中講座

ビートたけしがやっていたTV番組「たけしのコマ大数学科」からスピンアウトした書籍。
Eテレの「サイエンスZERO」でおなじみの竹内薫さんとの対談本である。

竹内薫さんはというと、「99・9%は仮説 思いこみで判断しないための考え方」 で
有名な方だ(と私は勝手に思っている)が、
日常にあふれている様々な事が、実は科学では厳密に解明できておらず仮説に基づいて成立しているということを、いくつかの事例に基づいて書いていて、トリビア的な要素が知的好奇心をそそる。

そんなお二人が数学を通して、仕事や人生など(おおげさな言い方だが)の価値観に
影響を与えてきた事をつれづれなるままに語っている。

実際読んでみるとビートたけしって本当に数学が好きなんだなぁというのがひしひしと
伝わってくる。余暇のあるときに数学の本を買ってきて問題を解いてみたり、孫と一緒に算数の勉強をしたり、小学生向けの英語の算数の教科書で問題を解きつつ英語の勉強をしてみたりと、いくら理系の人たちといえど趣味で数学の問題を解くなんて相当なマニアでないかぎりやらないだろう。しかしそこに楽しさ・美しさを見い出しているビートたけし
を知りさらに人間的な魅力を感じた次第だ。

「問題を作るっていうのは、自分用の教科書を作るのと同じなんですよ。だから次に同じような問題が出ても解けちゃう。数学の問題集1冊に対して5冊くだい自分用のノートを作り、それを眺めたりしてるうちにどんどんキレイに問題が解けるようになってくるんですね。」

「問題があって答えを出すなんていう形式の問題はよくないね。逆に「答えから問題を考える」なんて方法があったっていい。どんな計算をしても答えが5というだけなら、カンニングしてもいいし、鉛筆転がしだって答えさえ合ってたら合格だ。それより、どうやって5という解答を導きだしたかという家庭が大切でおもしろいのにね」


これってビジネスの世界でも同じ「課題設定をどうするか」っていうことと同じじゃないか!と思った。ある程度の年齢になってくると与えられる立場から自分が仕事を与えたり、作る立場になる。

例えば売り上げを2倍にするという答え(結果)を作る(達成する)場合、それにたどり着くまでには結果に至るにあたっての問題(課題)とそれを解き明かす方法(プロセス)を正しく設定できなければならない。

まぁできれば売り上げも上がって会社の評価もあがるんだけど難しいんだよね。
でもこれもたけしが自分のノートを作るといっていたように思考の訓練が必要なのだ。

与えられているコト(問題)をこなす(解答する)だけで満足していないか?
自らが作り出すこと、すなわち課題を設定する事し問題を解決していくことを
怠っていないか?
I'll give it a shot.
今からでもやってみよう、と思う。

それにしても平成教育委員会は本当に面白い番組だったなぁ。
あんな知的なクイズ番組後にも先にもないと思う。
あの番組が自分が小学生の頃から存在していたらもっと勉強が好きになったのになぁ。

ZaimとGoogleSpreadSheetが連携できない。

Zaimでは外部のサービスと連携するためにAPIを公開している。
認証はoAuth1.0aに対応しているのだが、GoogleAppsScriptと相性が悪いのか、
oAuthエラーというエラーが発生し認証に失敗する。

丸1日費やしたが失敗の原因がわからないのでひとまず撤収。
もし「できる!知ってるよ!」という方がいたら教えてください。
私が書いたコードを一応のせておきます。

var oAuthConfig = function(serviceName,accessTokenURL,requestTokenUrl,authorizationUrl){
  var m_methods = {};
  var m_oAuthConfig      = UrlFetchApp.addOAuthService(serviceName);
  m_oAuthConfig.setAccessTokenUrl( accessTokenURL );
  m_oAuthConfig.setRequestTokenUrl( requestTokenUrl );
  m_oAuthConfig.setAuthorizationUrl( authorizationUrl );
 
  var setConsumerKeyAndSecret = function(key,secret){
    ScriptProperties.setProperty('consumerKey', key);
    ScriptProperties.setProperty('consumerSecret', secret);
    m_oAuthConfig.setConsumerKey(ScriptProperties.getProperty('consumerKey'));
    m_oAuthConfig.setConsumerSecret(ScriptProperties.getProperty('consumerSecret'));
  }
  m_methods.setConsumerKeyAndSecret = setConsumerKeyAndSecret;
 
  var getConfig = function(){ return m_oAuthConfig; }
  m_methods.getConfig = getConfig;
 
  var authConfigDump = function(){
    Logger.log('AccessTokenURL->' + m_oAuthConfig.getAccessTokenUrl());
    Logger.log('AuthorizationUrl->'+ m_oAuthConfig.getAuthorizationUrl());
    Logger.log('Method->' + m_oAuthConfig.getMethod());
    Logger.log('ParameterLocation->' + m_oAuthConfig.getParamLocation());
    Logger.log('RequestTokenURL->' + m_oAuthConfig.getRequestTokenUrl());
    Logger.log('ServiceName->' + m_oAuthConfig.getServiceName());
  }
  m_methods.authConfigDump = authConfigDump;
 
  //create nonce from SHA-1 Digest 
  var getNonce = function(){
    var now = new Date();
    var nonce = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1,now.getTime(),Utilities.Charset.US_ASCII);
    var strNonce = bytes2StringDigest(nonce);
    Logger.log('nonce(bytes2StringDigest)->'  + strNonce);
    return strNonce;
  }
  m_methods.getNonce = getNonce;
 
  return m_methods;
}
var urlString = function(url){
  var m_methods = {};
  var m_response = UrlFetchApp.getRequest(url);
  var m_methodName = m_response.method;
  var m_url        = m_response.url;

  var getMethod = function(){return m_methodName;}
  m_methods.getMethod = getMethod;
  var getUrl    = function(){ return encodeURIComponent( m_url.split('?')[0] );}
  m_methods.getUrl = getUrl;
  var getQueryStrings   = function(){ return encodeURIComponent( m_url.split('?')[1] ); }
  m_methods.getQueryStrings = getQueryStrings;
 
  var dump = function(){
    for(var i in m_response) {
       Logger.log(i + ": " + m_response[i]);
    }   
  }
  m_methods.dump = dump;
  return m_methods;
}
var getOAuthSignature = function(url,common_param_oauth){
  //HASHMAC Signature
  var urlStr = urlString(url);
  var sign_url   = encodeURIComponent(urlStr.getUrl());
  var sign_param = encodeURIComponent(urlStr.getQueryStrings());
  var sign_source = 'GET' + '&'+ sign_url + '&' +
  //  'oauth_body_hash'        + encodeURIComponent('=') + common_param_oauth.oauth_body_hash + '&' +
      'oauth_consumer_key'     + encodeURIComponent('=') + common_param_oauth.oauth_consumer_key + '&' +
        'oauth_nonce'            + encodeURIComponent('=') + common_param_oauth.oauth_nonce  +'&' +
          'oauth_signature_method' + encodeURIComponent('=') + common_param_oauth.oauth_signature_method  +'&' +
            'oauth_timestamp'        + encodeURIComponent('=') + common_param_oauth.oauth_timestamp + '&' +
              'oauth_version'          + encodeURIComponent('=') + '1.0';
  //if (sign_param != undefined){ sign_source = sign_source + '&' +  sign_param }
  Logger.log('SIGN_SOURCE->' + sign_source);
  //create Signature(HMAC SHA-1) and encode BASE64.
  var tmpSignature = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1,sign_source,common_param_oauth.oauth_consumer_key);
  common_param_oauth.oauth_signature = Utilities.base64Encode(tmpSignature);
  Logger.log('Signature(bytecode)->' + tmpSignature );
  Logger.log('Signature(base64)->' + common_param_oauth.oauth_signature );
 
  return common_param_oauth.oauth_signature;
}
/////////////////////
//MAIN
/////////////////////
function main(){
  var serviceName      = 'Zaim';
  var accessTokenUrl   = "https://api.zaim.net/v2/auth/access";
  var requestTokenUrl  = "https://api.zaim.net/v2/auth/request";
  var authorizationUrl = "https://www.zaim.net/users/auth";
  var key              = 'Your consumerKey';
  var secret           = 'Your consumerSecret';
  var methodName       = 'get';         //default get
  var paramLocation    = 'auth-header'; //Allowed values are 'post-body', 'uri-query' and 'auth-header'.
  var tergetUrl = 'https://api.zaim.net/v2/home/user/verify';
  var response='';
  try{
    var oAuthConf = oAuthConfig(serviceName,accessTokenUrl,requestTokenUrl,authorizationUrl);
    oAuthConf.setConsumerKeyAndSecret(key,secret);
    oAuthConf.authConfigDump();
    oAuthConf.getConfig().setMethod( methodName );
    oAuthConf.getConfig().setParamLocation( paramLocation );
    oAuthConf.authConfigDump();
   
    Logger.log('key->' + ScriptProperties.getProperty('consumerKey'));
    Logger.log('secret->' + ScriptProperties.getProperty('consumerSecret'));
    var now = new Date();
    var common_param_oauth =
    {
      'oauth_consumer_key'     : ScriptProperties.getProperty('consumerKey'),
      'oauth_signature'        : '', //see after code.
      'oauth_signature_method' : 'HMAC-SHA1',
      'oauth_token'            : '',
      //'oauth_timestamp'        : now.getTime(),
      //'oauth_nonce'            : oAuthConf.getNonce(),
      'oauth_timestamp'        : '',
      'oauth_nonce'            : '',
      'oauth_version'          : '1.0'
      //'oauth_body_hash'        : ''
    };
    getOAuthSignature(tergetUrl,common_param_oauth );
    Logger.log('COMMON_PARAM_OAUTH->' + Utilities.jsonStringify(common_param_oauth));
   
    var options =
    {
      "oAuthServiceName" : serviceName,
      "oAuthUseToken" : "always",
      "method" : methodName,
      //"headers" : '',
      //"payload" : common_param_oauth,
      "muteHttpExceptions" : true
    };
    Logger.log('options->' + Utilities.jsonStringify(options));
    //var response = UrlFetchApp.fetch( tergetUrl  );
    response = UrlFetchApp.fetch( tergetUrl , options );
  }catch(e){
    Logger.log('main failure.' + e.message);
  }
}

2013/11/27
再チャレンジしたが認証エラーでやはりNG。
上のソースコードはかなり間違いがあるのだが、再度oauth_signatureの作成方法(アルゴリズム、パラメータのソート、URIエンコード・・・)などを
oauth1.0の仕様書に基づきトライしてもダメ。
ということでGoogle Apps Scriptとの連携は一旦あきらめる。



2013年10月4日金曜日

JavaScriptに連想配列(ハッシュテーブル)は存在しない

最近JavaScriptの勉強を兼ねて、仕事(Windowsサーバのシステム)で
JScriptを利用するようにしている。(残念ながらLinux環境にない。。。)
Webで調べながら「JavaScriptでハッシュを実現するにはこうすればいいのね」と
得意げに 以下のようなコードを書いていた。

var hash = {};
hash[ 'apple' ] = 100;
hash[ 'lemon' ] = 200;
for(key in hash){
  var ret = hash[key];
}
retにはそれぞれ100,200が返る。

しかし実はJavaScriptにハッシュなど存在しなかった。
それを知ったのはつい最近のことである。

JavaScriptで配列を実現できるのはArray()だけだ。
Array()による配列の実現は以下のとおりである。
var list = []; //空の配列を生成。 var list = new Array()と同義。
list[0] = 100;
list[1] = 200;
for(var i=0 ; i<list.length ; i++){
  var ret = list[i];
}

じゃあどうして前者のコードはあたかもハッシュテーブルのように
見えるのだろうか?そしてハッシュテーブルとは別物なのか?
var hash = {}; は
var hash = new Object();と同義である。
つまりhashは空の新しいObject型のオブジェクトを生成していることを表す。
 
では以下のコードは何を意味するか?
hash[ 'apple' ] = 100;
これはhashオブジェクトにappleプロパティを設定し、値を100とする
ことを意味する。
hash.apple = 100と同じ事である。
hash['lemon'] = 200も同じだ。

見た目はhash配列にappleをキーとして値に100を設定しているように見えるが、
全く違うのだ。

それでは以下の部分はどうだろう。
for(key in hash){
  var ret = hash[key];
} in演算子は対象のオブジェクトが指定された名前のプロパティを
保持しているかどうかを確認する。(true/falseが返る。)
forと組み合わせる事によって、指定オブジェクトの各プロパティ名を
取得していくことができる。

だから
hash[key]は、hashオブジェクトのkeyプロパティに設定された値を
取得することを意味する。
上記の例でいうとプロパティにapple、lemonを追加したから、
hash['apple']、hash['lemon']の値を取得することになる。
hash.apple、hash.lemonでもかまわない。

補足だがプロパティが数値の場合は、ドットを利用したアクセスはできない。
hash['1000'] -> OK
hash.1000    -> NG エラーになる。

つまり、JavaScriptにおけるハッシュテーブルに似た仕組みの裏側は、
オブジェクトにプロパティを設定するという行為である。

考察
コードを書いて少しでも動いてしまうと、
同じようなケースに遭遇したときに、
動作原理について知らないまま
経験に基づいてコードを書いてしまうことが少なくない。
理論ではなく経験則に基づいてということだ。

これは非常に危うい。
プログラムの複雑さが増し問題にぶちあたった時に、
原理を知らないからそれに基づいて問題解決に取り組む事ができないからだ。
当然経験も無いので何から手をつけたらいいかわからなくなる。

しかし原理を知らないまま書く事が悪いといっている訳ではない。
どのような動作をするかを知るためには、
体で理解すること、反復練習をすること
つまり何度も同じようなコードを自分の手で書くことで、
体にしみ込ませる事が必要だと思うからだ。

重要なのは、体がわかるようになり余裕が出てきた時に
なぜそのような動作になるかを知ることだと思う。
経験と原理両面から知る事で深い理解につながる。

算数・数学などの公式なども一緒だと思う。
公式の意味するところは最初はわからないが、
問題を数多くこなす事につれ体と頭が理解するようになる。

人間何かを学ぶためには、理論と経験の両輪で考えなければならない。

2013年10月3日木曜日

ziprubyを使ったzip圧縮ライブラリ

ziprubyを利用したライブラリを作成。

使い方は以下のとおり。
require relative '******/ziparchiver.rb'

objZip = ZipArchiver.new( 'zipファイルのフルパス' )
objZip.createZip( '対象のフォルダまたはファイルのパス' )
  *フォルダを指定するとサブディレクトリも含めて圧縮する。

objZip.encrypt( 'zipファイルのパスワード' ) <-任意で設定

https://github.com/maar4569/Ziparchiver

植物はすごい 生き残りをかけた仕組みと工夫

 植物は動くことがなければ、コミュニケーションを取る事もできない。
そのような特徴を持っているとしても、
いかにして自分を守りながら子孫を増やせばよいかを遺伝子の中に
組み込んできた。そのやり方も様々。

例えば、栗と柿を例に取ると一見全く違う戦略だが目的は一緒というところが面白い。

栗は過剰なほどに防衛する。あのトゲトゲの中に、固い皮の鎧をまとっている。
仮に皮を剝く事ができたとしても、渋皮という下着をつけ、これでも食うの?
といった様子。超ディフェンシブ。

一方、柿といえば種が成熟するまでは渋柿で「食べちゃいやよ」オーラを振りまくが、
ほどよく成長すると、食べても渋みを感じ無いようにし、甘みで誘惑してを出して
「私を食べて、そして遠くへ連れてって」と言わんばかりの様子。
(渋柿をかじると渋く感じるのはタンニンが溶けだしてくるからだそうな。果実が成熟するとアルコールでおなじみアセトアルデヒドが生成されタンニンを不溶性にするとのこと)
こちらは反対に超オフェンシブ。

守る戦略と、攻めてリスク分散をはかる戦略。
育ってきた環境によってそれぞれが選択した戦略なのだろう。

ビジネスでも一緒だと思う。
自分たちが置かれている環境をよく見極め戦略を考えなければならない。
植物は気の遠くなるような時間をかけていまの戦略を選択したが、
ビジネスではそうはいかないのがまた難しい。