uokadaの見逃し三振は嫌いです

ここで述べられていることは私の個人的な意見に基づくものであり、私が所属する組織には一切の関係はありません。

pdbでデバッグするやつの補足

racchai.hatenablog.com

初心者向けのpdbの紹介記事を見かけたのでバット読んだけど、 このやり方も書いて欲しいなと思ってこのエントリを書いてみた。

#!/usr/bin/env python

import sys

def log(args):
    print args

def main(args):
    log(args)

#import pdb;pdb.set_trace()
main(sys.argv)

import pdb している行をコメントアウトしたスクリプトを用意しました。

% python2.7 -mpdb sample.py
> /private/tmp/sample.py(3)<module>()
-> import sys
(Pdb) b 12  # <= 12行目のmain()にブレイクポイントをセットします。
Breakpoint 1 at /private/tmp/sample.py:12
(Pdb) c     # <= ブレイクポイントまでスクリプトを実行します。
> /private/tmp/sample.py(12)<module>()
-> main(sys.argv)
(Pdb) l
  7
  8    def main(args):
  9        log(args)
 10
 11    #import pdb;pdb.set_trace()
 12 B->   main(sys.argv)

スクリプトの起動時に -m pdb で渡すメリットとしてはスクリプトを編集する必要がないところです。
本番環境などで改変が禁止されているスクリプトをどうしてもデバッグしたいときにはこの方法が有効です。

お粗末な内容ですが補足でした。

Intel Edisonをイジってみた

正月前後にIoTに興味をもってずっとものを買わずに来たんだが3月末に思い切ってIntel Edisonを買ってみた。

このブログを参考に秋葉原のマルツ電気と秋月電子にいってものを揃えたのが3月。
今日やっとOSアップデートとwi-fiの設定を完了したw

ついでに mraa ってライブラリを動かしたいと思ってこのブログとgithubリポジトリを見つつ設定を入れた。

% echo "src mraa-upm http://iotdk.intel.com/repos/3.0/intelgalactic/opkg/i586" > /etc/opkg/mraa-upm.conf
% opkg update
% opkg install mraa

サンプルコードを動かしてみる。 opkgで入ってるバージョンと同じものが表示されてるので正常に動いてそう。

% opkg list mraa
mraa - 0.10.1 - mraa built using CMake

%  cat sample.js
var m  = require('mraa');
console.log("MARR Version: " + m.getVersion());

% node sample.js
MARR Version: v0.10.1

ずっと時間が取れずにサボってたのでGWに本格的に開発出来るように今から予習を進めていく次第です。

Intel EdisonではじめるIoTプロトタイピング

Intel EdisonではじめるIoTプロトタイピング

自分は正月ぐらいにこの本を買ってペラペラめくって読んでます。
Amazonのレビューにもある通り本の内容はすでに古くなってきているのでこの1冊ですべて解決出来るわけではないけど、 何を買ってくればいいかなどおおまかな流れをつかむのにはこの本を読んでおくのがいいと思う。

それ以外はクラスメソッドのブログを読んで解決していくのがいいかと思う。

たまゆら 汐入卒業式~ちひろちゃん、ともちゃん卒業おめでとう、なので~に行ってきた

「卒業式」詳細情報 | たまゆら〜卒業写真〜

①汐入卒業式~ちひろちゃん、ともちゃん卒業おめでとう、なので~
日時: 2016年4月10 日 18:00開場 19:00開演 21:00終了(予定)
場所: 横須賀芸術劇場
料金: 6,480円[税込]

関東ではラストの大きなイベントだったのでこの1年間たまゆらを追っかけてきた自分としてはやっぱり行きたいなと思い行ってきました。

グッズの物販はそんなにほしいもの無かったけどイラスト集は買って良かったと思ってる。
たまゆらのほとんどのイラストが掲載されててこれはたまゆらファンなら買い一択な一品です。

イベント本編はいつもの劇団たまゆらのミニコントで始まり、
寿美菜子"画伯"が書いた作品に声をあてる無茶振りだったりサトジュン監督が扮する誰かに告白するゲームだったりと会場は終始爆笑の連続でした。

最後の卒業証書授与式では東山さんの涙が特に印象的で裏にこんなエピソードあったのかって
たくさんの気づきがありました。

「寂しければ寂しいほどそれだけ楽しかった」たまゆらの最後はこの一言に尽きると思います。 この1年間ちゃんとたまゆらを追っかけてきてどのイベントも笑いあり爆笑ありの少し涙ありでどれもいい思い出ばかりでとても満足させてもらいました。

4/24に竹原でやる最後のイベントは行けないが東京の空から見守りたいと思います。

我流togetterまとめ作成方法

最近、業務でtogetterまとめを作る機会が何度かあったので 自分なりのまとめ作成方法をメモ代わりに公開しておく。

まず、ヤフーのリアルタイム検索からハッシュタグで検索してtweetを拾ってくる。
リアルタイム検索

リアルタイム検索を使うのは特に理由はなかったはずだけど、スパムっぽいツィートが弾かれてるからだった気もする。

Firefox + Firebugでコンソールを使って下のスニペットを実行してtweetのURL一覧を取得。

$("#TSm p.lt").each(function(){
  var link = $(this).children("a")[1];
  var h = link.href;
  var twURL = decodeURIComponent(h.split("*")[2])
  console.log(twURL);
});

2016/06/05 追記

twitterからぶっこ抜きするにはこんなコードでOK
もうちょっとキレイな書き方あるはずだけどとりあえずこれで。

var elements = $("a.tweet-timestamp");
var n = elements.length;
for(var i = 0 ; i < n; i++){ 
  var link = $("a.tweet-timestamp")[i].getAttribute("href");
  link = "https://twitter.com" + link;
  console.log(link);
}

2016/09/26 追記

いつの間にかフォーマット変えられてたのでちょっと変更してURLの一部を抜き出す形式に変更。

$("#TSm p.lt").each(function(){
  var link = $(this).children("a")[1];
  var h = link.href;
  var RU = h.split("/")[9].slice(3);
  // var rawURL = decodeURIComponent(RU.replace('-', '=').replace('_', "/").replace(".", "+"));
  var rawURL = RU;
  rawURL = rawURL.replace(/\-/g, '=');
  rawURL = rawURL.replace("_", '/');
  rawURL = rawURL.replace(".", '+');  
  // console.log(decodeURIComponent(rawURL));
  console.log(window.atob(decodeURIComponent(rawURL)));
});

2018/01/06 追記

twitterの方が取れなくなっていたのでちょっと修正。

var x = "";
$$(".tweet-timestamp").forEach(
  function(el, i){
    x += el.href + "<br/>\n";
    console.error(el.href);
});
document.write(x);

querySelectorAll() を使うコードに変更。

次に、togetterのまとめ作成画面から画像のY字っぽいボタンをクリック。

f:id:uokada:20160328024142p:plain

クリックして出てきたフォームに先ほど取得したURL一覧を入力して完了。 f:id:uokada:20160328024144p:plain

あとは、時間順に並べて重複、RTを削っ荒いまとめの完成。
余裕があればここからデコレーションしたり、ハッシュタグがついてないけど関係ありそうなまとめを拾ってきて完成。

デコレーションはRTが多いtweetは優先的に赤字で大きくしたいと思っているのだが今のtogetterのUIだと難しいので、 今後の課題かなと思ってる。

最近読んだ本 2016/03

10年後、生き残る理系の条件

10年後、生き残る理系の条件

著者のtwitter観てて面白い人だったので購入。
いつもtwitterで主張しておられることをまとめた1冊って印象。

対象の読者層としては就活生から入社数年目までの若手が対象ってところだと感じた。

日本企業の社員は、なぜこんなにもモチベーションが低いのか?

日本企業の社員は、なぜこんなにもモチベーションが低いのか?


あとは今月はこの辺を読んでる。

自分の写真整理するためのGoスクリプトを書いた。

みなさん大掃除は済みましたか? 自分は今パソコンの中を大掃除中です。

今年たくさん写真とったおかげでMACのディスクをかなり圧迫していてちょうどそれの整理をしています。

自分のカメラは1枚の写真に対してORFとJPGファイルを2つ生成するような設定となっています。 ただ、ORFファイルがあればJPGは要らないな〜と思ったので今回まとめて整理出来るスクリプトを書きました。

ディレクトリを引数にとってそのディレクトリ以下を探索して、
JPGとORFファイルを重複して持っているものは削除するというものです。

package main

import (
    "fmt"
    "os"
    "path/filepath"
    "regexp"
    //    "os"
    "flag"
    "log"
    "os/exec"
)

const (
    DEBUG     = false
    EXTENSION = ".JPG"
    WALK_PATH = "/Users/hogehoge/Dropbox/Pictures/写真 Library.photoslibrary/"
    TRASH_BOX = "~/.Trash"
)

const raw = `
on run argv
  tell application "Finder"
    repeat with f in argv
      move (f as POSIX file) to trash
    end repeat
  end tell
end run
`

var trash = filepath.Join(os.Getenv("HOME"), ".Trash")

func go2Trash(f string) error {
    bin, err := exec.LookPath("osascript")
    if err != nil {
        err = fmt.Errorf("not yet supported")
        return err
    }

    if _, err = os.Stat(trash); err != nil {
        err = fmt.Errorf("not yet supported")
        return err
    }

    path, err := filepath.Abs(f)
    if err != nil {
        return err
    }

    params := []string{"-e", raw, path}
    cmd := exec.Command(bin, params...)
    if err = cmd.Run(); err != nil {
        return err
    }
    return nil
}

func WalkFunc(path string, info os.FileInfo, err error) error {
    if err != nil {
        fmt.Println(err)
        return nil
    }
    if info.IsDir() {
        return nil
    }

    if ext := filepath.Ext(path); ext == EXTENSION {
        pat, err := regexp.Compile(EXTENSION + "$")
        if err != nil {
            fmt.Println(err)
            return nil
        }
        orfpath := pat.ReplaceAllString(path, ".ORF")

        if _, berr := os.Stat(orfpath); berr == nil {
            fmt.Println("ORF File Exist: ", path)
            if dryrun {
                // if dry-run mode, file isn't deleted.
                log.Println("next file will be deleted: ", path)
                return nil
            }
            // File Remove Process
            if rename {
                derr := go2Trash(path)
                if derr != nil {
                    log.Println(err)
                    return err
                }
            } else {
                derr := os.Remove(path)
                if derr != nil {
                    log.Println(err)
                    return err
                }
            }
        }
    }
    return nil
}

var path string
var rename, dryrun bool

func init() {
    flag.StringVar(&path, "path", WALK_PATH, "Pictures filepath.")
    flag.BoolVar(&dryrun, "n", false, "dry run flag")
    flag.BoolVar(&rename, "trash", true, "go to trashbox")
    flag.Parse()
    debugp(fmt.Sprintf("dryrun: %s", dryrun))
    debugp(fmt.Sprintf("trash: %s", rename))

    if path != WALK_PATH {
        tmp_p, err := filepath.Abs(path)
        if err != nil {
            log.Fatalln(err)
        }
        path = tmp_p
    }
    debugp(path)
}

func debugp(debugObj interface{}) {
    if DEBUG {
        fmt.Println(debugObj)
    }
}

func main() {
    f := WalkFunc
    err := filepath.Walk(path, f)
    if err != nil {
        fmt.Println(err)
    }
}

機能は以下のとおり。

  • ドライラン
  • ゴミ箱に入れずに直接削除モード

あとはこのスクリプトをビルドしてドライランで削除されるファイルを確認。 問題がなければドライランオプションを外して削除という流れです。

$ go build -o walker
$ ./walker -path=~/Dropbox/Pictures/写真\ Library.photoslibrary -n
<中略>
$ ./walker -path=~/Dropbox/Pictures/写真\ Library.photoslibrary
〜〜〜〜〜〜
ORF File Exist:  /path/to/2015/09/20/20150920-181504/P9200882.JPG
ORF File Exist:  /path/to/2015/09/20/20150920-181504/P9200883.JPG
ORF File Exist:  /path/to/2015/09/20/20150920-181504/P9200886.JPG
ORF File Exist:  /path/to/2015/09/20/20150920-181504/P9200889.JPG
〜〜〜〜〜〜

こんなスクリプトでもうまく動きましたが実行してみてると1つ課題が見つかりました。 コマンド実行でゴミ箱に送ってる部分が遅いのです。
ファイル1000個ぐらいで15分程度なので耐えられない遅さではないとは思いますが、 go で並列化出来るといいんだろうなと感じました。


余談ですが、ゴミ箱に入れる方法が分からなかったのでその部分は gomi を参考にさせてもらいました。
(AppleScript使って実現してるんですね。)

Link

navigator.sendBeaconを動かしてみた!!

What is navigator.sendBeacon?

navigator.sendBeaconってなんのこっちゃって感じなのでこれを読むと良い。

Beacon
日本語版: Beacon (日本語訳)

何がうれしいの!?って感じですが、例えばリンクをクリックした時にXMLHttpRequestでサーバーに情報を送っているようなケースで
サーバーのレスポンスが悪かったりするとサーバーにデータを送る前に遷移が完了してしまって取りこぼしてしまって正確なデータの収集が出来ないようなケースがあります。 また、サーバーからのレスポンスを待って遷移させるようなケースではサーバー側のレスポンスが悪いとページ遷移の妨げになりユーザーエクスペリエンスの低下につながります。

これをBeacon APIを使うことでユーザー・アクションを妨げることなくブラウザのバックグラウンドで処理を実行して確実にサーバーにデータを送れるようになります。
そして、送ったデータは広告なりアクセス解析なりなんなりと利用すればいいと思います。

サーバーを書いてみる

まずはサーバーを書いてみます。

package main

import (
    "encoding/base64"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    apachelog "github.com/gorilla/handlers"
    "os"
)

const (
    body string = `
<html>
<script>
  // emit non-blocking beacon to record state update
  function reportEvent(eventData) {
    navigator.sendBeacon('/collector', eventData);
  }

  // emit non-blocking beacon to record click on a link
  function reportClick(url) {
    var clickData = JSON.stringify({
      clickTo: url,
      clickAt: performance.now()
    });
    navigator.sendBeacon('/collector', clickData);
  }

  // emit non-blocking beacon when page transitions to background state
  document.addEventListener('visibilitychange', function() {
    if (document.visiblityState === 'hidden') {
      var analyticsData = buildSessionReport();
      navigator.sendBeacon('/collector', analyticsData);
    }
  });
</script>

<body>
 <a href='http://www.w3.org/' onclick='reportClick(this)'>
 <button onclick="reportEvent('button event!')">Click me</button>
</body>
</html>
`
    base64GifPixel = "R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="
)

type BaseHandler struct{}

func (bh *BaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, body)
}

type BeaconHandler struct {
    BeaconImage []byte
    RequireTLS  bool
}

func (bh *BeaconHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    log.Println("send beacon")
    //if bh.RequireTLS  r.TLS == nil {
    // log.Printf("WARNING: Please Requst with https.\n")
    //}
    if s, err := ioutil.ReadAll(r.Body); err == nil {
        log.Printf("DEBUG: %#v\n", string(s))
    } else {
        log.Printf("DEBUG: Error %#v\n", err)
    }
    output := string(bh.BeaconImage)
    w.Header().Set("Content-Type", "image/gif")
    //w.Header().Set("Content-Length", string(len(output)))
    io.WriteString(w, output)
    return
}

func main() {
    port := 8000
    mux := http.NewServeMux()

    // set demo page handler
    bh := &BaseHandler{}
    mux.Handle("/hey", bh)

    // Convert image && set handdler
    output, _ := base64.StdEncoding.DecodeString(base64GifPixel)
    beacon := &BeaconHandler{
        BeaconImage: output,
    }
    mux.Handle("/collector",
        apachelog.CombinedLoggingHandler(os.Stderr, beacon))

    // Extra
    mux.Handle("/", http.RedirectHandler("/hey", http.StatusFound))


    fmt.Printf("Listen: %d\n", port)
    addr := fmt.Sprintf(":%d", port)
    srv := http.Server{
        Addr:     addr,
        Handler:  mux,
        ErrorLog: nil,
        //ReadTimeout:  10 * time.Second,
        //WriteTimeout: 10 * time.Second,
    }
    err := srv.ListenAndServe()
    if err != nil {
        log.Fatal(err)
    }
}

検証用のサーバースクリプトを用意したのでとりあえず、サーバーを起動してみよう。

% go run server.go
Listen: 8000

ブラウザからアクセスしてみる

navigator.sendBeaconが実装されているブラウザはあまり多くない。
caniuseで確認してみるとFirefoxChromeには実装されているがそれ以外のサポート状況はあまりよろしくない。
モバイル界隈も最新のAndroidのブラウザはサポートされているがiOS safariがサポートされていない状況となっている。
個人的にiOS safariがサポートされていないのは痛い状況で日本のWEB界隈だとiOS:Androidが7:3ぐらいなので7に実装されてないとnavigator.sendBeaconを利用する側のモチベーションに影響するんじゃないかと思う。
また、Can I useのページにも日本におけるサポートブラウザの比率が掲載されているがまだ 38.87% ということで半分にも満たない。
普及にはiOS Safariで実装がされることが必須のように思う。

Can I use... Support tables for HTML5, CSS3, etc

自分はFirefox 42からこのURLにアクセス。
アクセスしたらボタンを押して遷移してW3Cのページに移動しよう。
移動したら、コンソールに戻ってみたください。 下のようなログが落ちてるはずです。

% go run server.go
Listen: 8000

2015/12/13 02:34:45 send beacon
2015/12/13 02:34:45 DEBUG: "event!"
2015/12/13 02:34:45 send beacon
2015/12/13 02:34:45 DEBUG: "{\"clickTo\":{},\"clickAt\":2601.465}"

ここには詳細は書いてませんが仕様にはPOSTでリクエストすることとなってますので比較的大きなデータでも受け渡すことが可能です。

Beacon-Age ヘッダフィールドというものが定義されています。 これはユーザーアクションからサーバーに送るまで非同期で処理されるためアクションを起こした正確な時間をサーバー側で取得するためのヘッダーとなっています。 ただ、tcpdump使ってhttpの通信みた限りChrome,Firefoxともに実装されていないので頭の片隅に入ってればいいんじゃないかなと。

Link