2014年3月30日日曜日

Androidのメモ帳アプリを試してみた

スマホに限らず、ガラケーの時からメモ帳は割りと大事。

ところがスマホになってからメモ帳の機能が豪華になってきた。
豪華なだけならいいのだけれど、ネストが深くなってメモとしてシンプルではなくなってしまっている。

カテゴリ分けとかリマインダーとかどうでもいいから、最小限のステップで入力ができて最小限の深さで再表示できるのがベスト。

以下試してみた。

2014年3月28日金曜日

ポーリングだっていいじゃない同盟

ポーリングってダサいイメージがあるけど、ダサいだけなら構わない。ダサい以上のデメリットがあるかどうか考えてみよう。

2014年3月10日月曜日

ログサーバが大事な理由

Webのシステムってログをどうするかってことが後回しになりがちだけど、超重要。

ログサーバがあるのとないのでは大違い。ログサーバのいいところをガンガン書くからね。


2014年3月7日金曜日

2014年3月5日水曜日

Gmailにも僕の既読状況がわかる仕組みを入れてみた

LINEやチャットのメリットは「相手が読んでいるのかどうかがわかる」ってとこ。メールはそのテンよくわからない。PCの前にいるのか席を外しているのかで状況が変わってくる。そこでメールにも、未読/既読状況をわかる仕組みを考えて作ってみた。

シナリオ

  • 登場人物
    • Aさんと私の二人
  • Aさんは私にメールを送信する
  • 私はちょうど打ち合わせ中でメールが見れてない
  • Aさんは私の受信箱の未読状況を確認する
  • 受信箱の未読状況を見る限り、ここ1時間はメールを見れていなさそう
  • 2時間前までは返信をしているので今日は活動していそう

実行画面

私の受信箱の状況です。

ソースコード

function doGet(e) {
  Logger.log("start");
  Logger.log(Session.getEffectiveUser().getEmail());
  var buff = "";
  var aryInbox = getInboxStatus();

  //未読のメッセージをビルド
  var aryOut = Array();
  var dateList = aryInbox.unread;
  for(var i=0;i<dateList.length;i++){
    strDate = formatDate(dateList[i]);
  if(typeof aryOut[strDate] === 'undefined'){
      aryOut[strDate] = new Array();
    }
    aryOut[strDate].push(formatTime(dateList[i]));
  }
  buff += "◆未読("+dateList.length+")\n";
  for(var k in aryOut){
    buff += k + aryOut[k].join(",") + "\n";
  }  
  //既読未返信のメッセージをビルド
  var aryOut = Array();
  var dateList = aryInbox.working;
  for(var i=0;i<dateList.length;i++){
    strDate = formatDate(dateList[i]);
    if(typeof aryOut[strDate] === 'undefined'){
      aryOut[strDate] = new Array();
    }
    aryOut[strDate].push(formatTime(dateList[i]));
  }
  buff += "\n◆既読未返信("+dateList.length+")\n";
  for(var k in aryOut){
    buff += k + aryOut[k].join(",") + "\n";
  }  

  //返信状況をビルド
  var aryReply = getReplyStatus();
  buff += "\n◆返信状況最近" + aryReply.length + "件 返信日時 <- 元時刻(リードタイムH)\n"
  for(var i=0;i<aryReply.length;i++){
    var row = aryReply[i];
    buff += formatDate(row.sent) + " " + formatTime(row.sent) + " <- " + formatTime(row.reply);
    buff += " (" + Math.floor((row.sent.getTime()-row.reply.getTime())/ 3600000) + "H)\n";
  }

  buff += "\n※ 未読と既読は受信トレイのメインカテゴリのみ対象。返信状況はスレッド単位です。"
  var output  = ContentService.createTextOutput(buff);
  return output;  
}

// 受信トレイのメインタブのみを対象に未読と既読のメッセージを取得する
function getInboxStatus(){
  var myMail =Session.getEffectiveUser().getEmail();
  var aryUnread = Array();
  var aryWorking = Array();

  var threads = GmailApp.search('category:primary', 0, 20); //メインタブ
  //var threads = GmailApp.search('is:inbox', 0, 20); //テスト用
  for (var i = 0; i < threads.length; i++) {
    messages = threads[i].getMessages();  
    lastMessage= messages[messages.length-1]; //最後のメッセージだけを見る
    if(lastMessage.getFrom().indexOf("<"+myMail+">")>=0){  
    }else{
      if(lastMessage.isUnread()){
        aryUnread.push(lastMessage.getDate());
      }else{
        aryWorking.push(lastMessage.getDate());
      }
    } 
  }
  return {unread:aryUnread,working:aryWorking};
}

//返信状況を返す
function getReplyStatus(){
  var myMail =Session.getEffectiveUser().getEmail();
  var aryReply = Array();

  //送信済みメールの返信時刻と返信先のメールの送信時刻を見る
  var threads = GmailApp.search('is:sent', 0,10);
  for (var i = 0; i < threads.length; i++) {
    messages = threads[i].getMessages();
    if(messages.length==1){ //メッセージが一件の時は処理の対象外
      continue;
    }
    for(var j=1;j<messages.length;j++){
      //自分からのメールの一つ前が自分以外のメールの時を返信扱いにする
      if(messages[j].getFrom().indexOf("<"+myMail+">")>=0 && messages[j-1].getFrom().indexOf("<"+myMail+">")<0){
        aryReply.push({sent:messages[j].getDate(),reply:messages[j-1].getDate()});
      }
    }
  }
  //Logger.log(aryReply);
  return aryReply;
}

// 日付のフォーマット mm/dd(aaa)で表記
function formatDate(date){
  var weekDays = ["日", "月", "火", "水", "木", "金", "土"];
  var strMonth = ("00"+(date.getMonth()+1)).slice(-2);
  var strDate = ("00"+date.getDate()).slice(-2);
  var strDay = weekDays[date.getDay()];
  return strMonth + "/" + strDate + "(" + strDay + ")";
}

//時刻のフォーマット(hh:mm)で表記
function formatTime(date){
  var strHour =  ("00"+date.getHours()).slice(-2);
  var strMinites = ("00"+date.getMinutes()).slice(-2);
  return strHour + ":" + strMinites ;
}

//テスト
function testRunner(){
  var d = new Date();
  Logger.log(formatDate(d));
  Logger.log(formatTime(d));
  var aryInbox = getInboxStatus();
  Logger.log(aryInbox);
  var aryReply = getReplyStatus();
  Logger.log(aryReply);
}

解説

  • getInboxStatus()で受信箱のメインカテゴリの中を検索
  • メインカテゴリだけにしているのは、メルマガや通知メールをのぞいた返信が必要そうなメールだけを対象にしたいから
  • getReplyStatus()は、「送信済み」で検索したスレッドを対象に検索
  • 本当はメッセージ単位で操作をしたいが、GmailのAPIがスレッド単位でしか検索できなかった
  • スレッドからメッセージに分解するときに、たまに4~6秒かかる。これは制約として仕方がない
  • スレッドの数かける数秒とすると処理時間がかかる。キャッシュした方がよいのかもしれないが、「メールを受信したらキャッシュ削除」というトリガが作れないので、キャッシュクリアのタイミングがいまいち。
  • 自分からのメールか相手からのメールかの判断をするフラグがなかったので、Session.getEffectiveUser().getEmail();で、このアプリの実行ユーザのメールアドレスがFROMに含まれているかどうかで判断している。
  • このWebアプリのURLにリクエストを出すのは不特定多数の人間だが、アプリは私の権限で動く。それがEffectiveUserってやつ。
  • GASではプレーンなHTMLを出すプレームワークがなかったので、プレーンテキストで表示するだけにしている。HTMLだとJSでUI作るからちょっと遅いので単なるレポートはプレーンテキストで表示。かなり割り切った。

2014年3月4日火曜日

一番しっくり来る通知ってなんだろうということを考える

Webサービスでかなり大事なKPIが継続率。有料バナー使って人を集めてきても、再来訪してもらわなければすげーもったいない。コンテンツの更新通知なんかをメルマガで出したりしているけど、いろんな選択肢があるはず。それ考えてみよう。