Posts Tagged ‘fac2010’

    はてなブックマーク - PHPでのAmazonAPIの使い方
    このエントリーをはてなブックマークに追加

    3度目のアドベントカレンダーという、まさかの誕生日プレゼントをもらってしまいました・・・。
    小さいころの夢はお母さんを冥王星につれていくことだった、本日誕生日の萩原です。
    (bitshiftersの皆さん、昨日はありがとうございました。)

    アドベントカレンダーよりもっと別のプレゼントください。

    PHPでのAPI使い方の紹介

    さて、最近AmazonAPIを使って商品検索をする機会がありましたので、その使い方を簡単に紹介したいと思います。

    まずはじめにAmazonから自分のアクセスキーとシークレットキーを取得してきます。

    後は、実際にコードを。
    私はCakePHP使いなのでPHPを使って商品情報を取得します。
    前々回先頭のCを小文字にしてしまったら弊社小山から苦情が来ました・・・すみません・・・

    $access_key_id = 'アクセスキー';
    $secret_access_key = 'シークレットキー';
    // RFC3986 形式で URL エンコードする関数
    function urlencode_rfc3986($str) {
    return str_replace('%7E', '~', rawurlencode($str));
    }
    // 基本的なリクエストを作成します
    $baseurl = 'http://ecs.amazonaws.jp/onca/xml';
    $params = array();
    $params['Service'] = 'AWSECommerceService';
    $params['AWSAccessKeyId'] = $access_key_id;
    $params['Version'] = '2009-03-31'; //←バージョンを入れてください。
    $params['Operation'] = 'ItemSearch'; // ← ItemSearch オペレーションの例
    $params['SearchIndex'] = 'All';
    $params['Keywords'] = $word; // ← 検索ワード
    $params['ItemPage'] = $page;
    // Timestamp パラメータを追加します
    // - 時間の表記は ISO8601 形式、タイムゾーンは UTC(GMT)
    $params['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
    // パラメータの順序を昇順に並び替えます
    ksort($params);
    // canonical string を作成します
    $canonical_string = '';
    foreach ($params as $k => $v) {
    $canonical_string .= '&'.urlencode_rfc3986($k).'='.urlencode_rfc3986($v);
    }
    $canonical_string = substr($canonical_string, 1);
    // 署名を作成します
    // - 規定の文字列フォーマットを作成
    // - HMAC-SHA256 を計算
    // - BASE64 エンコード
    $parsed_url = parse_url($baseurl);
    $string_to_sign = "GET\n{$parsed_url['host']}\n{$parsed_url['path']}\n{$canonical_string}";
    $signature = base64_encode(hash_hmac('sha256', $string_to_sign, $secret_access_key, true));
    // URL を作成します
    // - リクエストの末尾に署名を追加
    $url = $baseurl.'?'.$canonical_string.'&Signature='.urlencode_rfc3986($signature);
    //XMLで情報を取得。
    $xml = @simplexml_load_file($url);

     

    注意する点

    まず、一度に取得できる商品の件数は10件固定です。
    一度に飛ばせるリクエストの数は2件までなのでまとめて20件は表示することが可能です。

    私は今回5件ずつ取得したかったので、10件取得して前後5件ずつに分けることで対応しました。
    ちょっと無駄にデータを取ってきてしまうのが気にはなりましたけどね。

    次に、開発は基本皆さん仮想環境で開発を行っていると思います。
    その際、時間がずれていることがよくあるかと思いますが、時間がずれすぎているとAmazonからアクセスを拒否されます。
    多分10~15分くらいずれていると検索できません。
    私は取りあえずの対応で、cronで5分ごとに時間を合わせることで対応しました。(正確に合わせる必要はなかったので)

    後は、xmlを取得する際、エラー回避することを忘れないようにしましょう。
    エラーが出てしまうと、XMLの取得に使用するURLがそのまま出てしまうのですが、上記を見てもらえたらわかるとおりURLにはそのままアクセスキーとシークレットキーが書いてあります。ですので、エラーが出たら即情報が漏れてしまいます。

    今回は、デモでやっているのでとりあえず”@”をつけています。

    まとめ

    今回、こういったAPIを利用することは初めてだったので色々苦労しました。
    よくあることとは思いますが、Amazonに条件を投げて帰ってくるパターンは変えられないので、そのなかで自分がほしい情報に編集するのが少々面倒でした。
    また、XMLをいじることも初めてでしたし、javascriptでも苦労したことがあったのですが、これに関してはまた機会があれば。

    毎度おなじみどうでもいい化学豆知識

    炭素原子が三重結合までしかできないのは皆さん(多分)ご存知かと思いますが、タングステンやクロムなどは四重結合をすることがあります。
    まぁ、だからなんだと言われても困るんですけど。

    それでは、また。

    { 2010.12.15 }

    無限ループのRSpec

      はてなブックマーク - 無限ループのRSpec
      このエントリーをはてなブックマークに追加

      はじめましての人ははじめまして。
      たぶん、Fusic社員の中野です。

      Advent Calendarに当たってない社員はいるのに、もう2回も当たってしまいました。ルーレットバグってんじゃね?
      さて、今回は実際に活用したRSpecのTipsについて書いていこうかと思います。

      RSpecはRubyのテスト用フレームワークです。(RSpecを知らない人はごめんなさい。詳しい説明は省きます。)
      例えば、こんな感じでテストを書いていきます。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      class Hoge
        def run
          puts foo
        end
        def foo; "foo" end
      end
       
      describe Hoge do
        it "#run" do
          hoge = Hoge.new
          hoge.should_receive(:foo).and_return("foo") # 一度だけHoge#fooがコールされるよ                                                                                                                                                           
          hoge.run
        end
      end
      # >> foo
      # >> .
      # >>  
      # >> Finished in 0.00048 seconds
      # >> 1 example, 0 failures

      それでは、Hoge#runの中に無限ループがあるときはどうするのか?

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      
      class Hoge
        def run
          while true do 
            puts foo
          end
        end
        def foo; "foo" end
      end
       
      describe Hoge do
        it "#run" do
          hoge = Hoge.new
          hoge.should_receive(:foo).and_return("foo")
          hoge.run
        end
      end

      もし、これをそのままテストすると、Hoge#runの中の無限ループが実行されて処理が帰ってきません。
      無限ループを終わらせる判定があれば、テストは可能なので・・・。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      class Hoge
        def run
          while bar do
            puts foo
          end
        end
        def foo; "foo" end
        def bar; true end
      end
       
      describe Hoge do
        it "#run" do
          hoge = Hoge.new
          hoge.stub!(:bar).and_return(true, true, true, false)
          hoge.should_receive(:foo).exactly(3).times.and_return("foo")
          hoge.run
        end
      end
      # >> foo
      # >> foo
      # >> foo
      # >> .
      # >>  
      # >> Finished in 0.00085 seconds
      # >> 1 example, 0 failures

      という感じにしてあげたら、(擬似的な)無限ループを使ったテストができると思います。

      それでは、次の人にバトンタッチ〜。

        はてなブックマーク - PostgreSQLでCase式とGroup化
        このエントリーをはてなブックマークに追加

        まさか3度目は無いだろうと完全に油断していた。。。。
        Advent Calendar3度目の登場の安元です。
        年末ジャンボを買うなら今だ!と無責任にけしかける社員がいっぱいる楽しい会社です。

        さて、前回、前々回とSQLについて書いております。今回も趣向も変えずにSQLです。
        PHPでもRubyでもフレームワークを利用されることがほとんどで
        「SQLなんて書かねーよ!!」というかたも多いのではないでしょうか?

        弊社でもフレームワーク導入の前後ではSQLに携わる機会は激減しているのが現状です。
        しかし業務系のシステムでは、集計や統計を必要とすることがよくありますので、
        「SQLでデータまとめる」という選択肢を持っておくと、プログラムがぐっとシンプルになることもあります。

        というわけで、今回はGroup ByとCase式を絡めて利用してみましょう。

        年齢ごとの集計

        今回利用するのは以下のようなテーブルです
        性別と誕生日が格納されているシンプルなテーブル構成です。
        実務ではユーザーテーブルなんかでよくある構成ですね。
        レコード数は全部で3525件準備しました。

        
         id | sex |  birthday
        ----+-----+------------
          1 | f   | 1920-08-08
          2 | f   | 1927-07-28
          3 | f   | 1920-03-28
          4 | f   | 1921-03-21
          5 | f   | 1913-12-23
        (10 rows)
        

        まずはシンプルに年齢ごとの集計をとってみましょう
        (12月1日時点での年齢をもとに集計します)

        これで「年齢」ごとの集計はできました。
        しかし実務では、「年齢」ごとの集計では情報の粒度が細かすぎることが多いです。

        年代ごとの集計

        では、10代・20代のように年代ごとの集計をしてみましょう。
        使うのは、Case式です。
        まずはSQLを見てみましょう

        特徴的なのは、
        SELECT句の最初のCase式で条件分けをしていることと
        COUNT関数の中でのCase式の利用いる部分ですね

        SELECT句のCase式はGROUP BYにも書くことができますが、
        SELECT句に先に記述し「AS」で別名を付けることで、
        GROUP BY、ORDERともに別名を利用でき、SQLを短くすることができます。

        Case式の中でやっていることは年齢を年代に置き換える作業です

        date_part('year', age('2010-12-1',birthday)) between 1 and 19 then 10

        分解してみましょう
        1:age関数を利用して、誕生日からの経過年・月・日が取得できます

        age('2010-12-1',birthday)

        2:取得した年月日から、年だけを取得します。これが年齢になりますね

        date_part('year', age('2010-12-1',birthday))

        3:最後にbetweenを利用して、「年代」に置き換えています

        case
        when date_part('year', age('2010-12-1',birthday)) between 1 and 19 then 10
        end

        このようにCase式を使うと、「条件に一致する複数のデータを一つのデータにまとめ直す」ことができるのが大きな利点です。

        性別・年代ごとの集計

        それでは最後に、先ほどの年代別の集計を性別ごとに分けてみましょう。
        問い合わせ結果が横に伸びるということは、SELECT句の項目が増えることになります。
        今回はシンプルにCase式の各条件に性別条件を付加しました

        結果はこのようになります

        age_group | men | women
        -----------+-----+-------
        20 | 0 | 1
        30 | 14 | 192
        40 | 45 | 663
        50 | 56 | 905
        60 | 47 | 743
        70 | 9 | 367
        80 | 21 | 278
        90 | 4 | 141
        100 | 1 | 36
        (9 rows)

        とてもシンプルな結果が返ってきました。
        Case式は応用の幅が広いです。
        思いもつかなかった活用法がWEB紹介されていることもありますから、
        集計に行き詰ったら、Case式の活用を検討してみるといいことがあるかもしれません。