blog
« サーバーサイドJavaScriptで簡単Webアプリ開発 - Webフロント開発編(3) |  TOPへ  | 動画アップロードシステムをNode.js、MongoDB、Expressで作ってみた »
RingoJS

「Webフロント開発編(3)」に続き、最後の開発工程になるGoogle Calendar APIをたたいてmemberテーブルとマッチする検索キーワードが存在した場合にメール通知を行うバッチ処理プログラムを開発します

バッチ処理の作成

Google Calendarに接続するための各種jarファイルの取得

ダウンロード&解答して以下のjarファイルを$RINGO_HOME/libにおきます

  • gdata-calendar-2.0.jar
  • gdata-client-1.0.jar
  • gdata-core-1.0.jar
  • guava-r09.jar
http://code.google.com/p/gdata-java-client/downloads/からgdata-src.java-1.45.0.zip 
http://code.google.com/p/guava-libraries/downloads/listからguava-r09.zip 
よりダウンロードして解凍する
バックコマンドプログラムの雛型

バッチ処理を行うbatch.jsの雛型を作成します。RingoJSのルール的にコマンドで動作させるバッチ用のプログラムは、if (require.main === module) { でくくるようです。

[batch.js]

/**
 * メイン処理
 */
var main = function() {
  //ここにメイン処理を記述する
};

/**
 * mainファンクションを呼び出す
 */
if (require.main === module) {
  try {
      main();      
    }
  } catch (error) {
    print(error);
  }
}
MemberオブジェクトにgetBatchInfo()メソッドを作成

status=activeの対象データーをオブジェクトで返します。これも前に実装したようにO/Rマッパを使って実装します

[model.js]

  /**
   * バッチで使用する対象データーをDBから取得する
   */
  getBatchInfo: function() {
    var result = this.loadObject.query()
        .equals('status', 'active').select();
    return result;
  }
mainファンクションの処理フロー

memberテーブルより登録されている勉強会キーワードをgetBatchInfo()を使用して、取得してループ毎に処理したいと思います。mainファンクションに必要なデーターとして、「メールアドレス」、「ユニークID」、「勉強会キーワード」を渡したいと思います

[batch.js]

/**
 * mainファンクションを呼び出す
 */
if (require.main === module) {
  try {
    var results = Member.getBatchInfo();
    for each(var result in results) {
      main(result.email, result.uid, result.keyword);      
    }
  } catch (error) {
    print(error);
  }
}
config.jsに対象となるカレンダーのURLを追記する

IT勉強会のURLをconfig.jsで定義しておきます

[config.js]

exports.calendarUrl = 'http://www.google.com/calendar/feeds/fvijvohm91uifvd9hratehf65k%40group.calendar.google.com/public/basic';
必要なファイルのrequireとインスタンスの生成

GDATA関連のクラスはファイル先頭でインスタンスを生成してしまいます。mainファンクション内で生成しても問題ないと思います

[batch.js]

var {Response} = require('ringo/webapp/response');
var {Member} = require('./model.js'); 
var config = require('./config.js');
var {Service} = require('./service.js');

//// {}くくって、DateTimeオブジェクトをセット
var {DateTime} = com.google.gdata.data;

//// カレンダー検索クエリオブジェクトをセット
var cservice = com.google.gdata.client.calendar.CalendarService("");

//// config.jsに定義されたIT勉強会のURLを取得して、URLオブジェクトでラップ
var eventFeedUrl = java.net.URL(config.calendarUrl);
var query = com.google.gdata.client.calendar.CalendarQuery(eventFeedUrl);
var events = com.google.gdata.data.calendar.CalendarEventFeed().class;
検索対象期間を定義する

とりあえず、現在~3か月後を対象にするため、60日間を定義したいと思います

[config.js]

exports.durationDays = 60;
検索期間をコンバートするメソッドの追加

JavaのSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")クラスを使って、gdataのフォーマットに指定します。いったんミリ秒にしてからフォーマットかけています

[service.js]

var simpleFormat = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

/**
 * 検索対象期間のDateTimeを取得する
 */
exports.Service.searchTerm = {
  sd: simpleFormat, 
  start: function() {
    return this.sd.format(new Date());
  },
  end: function() {
    var duration = config.durationDays * 24 * 60 * 60 * 1000;
    var now = new Date().getTime();
    return this.sd.format(new Date(now + duration));
  }
};
カレンダーへ検索ワードを投げて、結果取得

ここは特に説明のしようがなくて、GDATAのAPI仕様どおりにパラメーターをセットするだけです

[batch.js]

/**
 * メイン処理
 */
var main = function(email, uid, keyword) {
  var entry = "", content = "", text = "";
  query.setFullTextQuery(keyword);
  query.setMinimumStartTime(
    DateTime.parseDateTime(Service.searchTerm.start()));
  query.setMaximumStartTime(
    DateTime.parseDateTime(Service.searchTerm.end()));

  var resultFeed = cservice.query(query, events);
検索結果が空だった場合

resultFeedのエントリー数を調べて0だった場合は処理を中断します

[batch.js]

  var count = resultFeed.getEntries().size();

  if(count === 0) {return;}
バッチ用メールテンプレート作成

いままで作成してきたメールテンプレートと同じように作成するのですが、今回はループして同じテンプレートを何回もよみだしますので、batch.tplとは別にbatchPart.tplという繰り返し用のテンプレートにわけています

[skins/email/batchPart.tpl]

----------------------------------------
タイトル:<% studyTitle %>
<% plainText %>

[skins/email/batch.tpl]

バッチで使用します、メールフォーマットの全体テンプレートになります。textBodyにbatchPart.tplを使って生成されたデータが渡されてきます

IT勉強会サーチメール

このキーワードにマッチした勉強会は以下のとおりです

[<% keyword %>]

<% textBody %>


====================================================
今後、このメールが不要な方は、下記URLをクリックして下さい
<% withdrawUrl %>
====================================================
バッチ用メールフォーマットメソッド追加

skins/email/batchPart.tpl用メソッドになります

[service.js]

   batchPart : function(studyTitle, plainText) {
    return Response.skin(module.resolve
                         ('skins/email/batchPart.tpl'),
                         { studyTitle: studyTitle,
                           plainText: plainText}).body;

skins/email/batch.tpl用メソッドになります。退会用のリンクもメールに添付するため、withdrawというアクションを用意します。withdrawについては、後ほど作成します

[service.js]

  batch : function(uid, keyword, text) {
    return Response.skin(module.resolve
                         ('skins/email/batch.tpl'),
                         { keyword: keyword,
                           withdrawUrl: config.fqdn 
                           + "/withdraw?uid=" + uid,
                           textBody: text}).body;
  },
メール用整形メソッドの追加

GData calendarのSummaryデーターを人間の目でみて読みやすい形に変換します。

[service.js]

  /**
   * GData calendarのSummary的な値を読みやすい形に加工する
   */
  modifySummary : function(content) {
    var CR = "\r\n";
    return content.replace(' JST ', CR)
        .replace('予定のステータス: 確定', '')
        .replace('予定の説明", "詳細内容') + CR;       
  }
};
検索でマッチしたデータをループして、Service.modifySummary()に渡して、メールで読みやすい形式に変換します

resultFeed.getEntries().get(i)でマッチしたentryオブジェクトの中から1個取得します。entry.getTextContent().getContent().getPlainText()で取得した検索結果をテキスト形式にコンバートします。

[model.js]

  for (var i = 0; i < count; i++) {
    entry = resultFeed.getEntries().get(i);
    text += Service.textFormat.batchPart(
      entry.getTitle().getPlainText(),
      Service.modifySummary(entry.getTextContent()
                            .getContent()
                            .getPlainText())
    );
  }
メール送信

上記で作成した Service.textFormat.batch()に必要な値を渡してメールを送信します

[model.js]

  var textFormat = Service.textFormat.batch(uid, keyword, text);
  Service.sendMail(email, textFormat);

以下のようなメールが飛べば、成功です。

RingoJS

退会処理

退会処理メソッドの追加

uidをキーにmemberテーブルにマッチするデータがあれば、statusカラムをactiveからinactiveにupdateします。inactiveになることで次回のバッチ処理の対象外とします。

[model.js]

  /**
   * 退会処理
   */
  withdraw: function(uid) {
    var result = this.loadObject.query()
        .equals('uid', uid).select();
    result[0].status = 'inactive';
    result[0].save();  
  },
withdrawアクションの追加

Member.withdraw()をよびだしてステータスをアップデートし、メールを送信するだけのシンプルな処理になります。特に退会用のhtmlテンプレートは用意せず、登録完了時に使用した、skins/complete.htmlを再利用し、退会用メッセージを引数に渡すだけにしたいと思います。

[action.js]

/**
 * withdraw page
 */
exports.withdraw = {};
exports.withdraw.GET = function(req) {
  var uid = req.params['uid'];
  if (uid === "") {
    return Response.skin(module.resolve('skins/complete.html'), 
    { title: 'エラー',
      errorMessage: 'URLに問題があります'});
  }

 Member.withdraw(uid);
  return Response.skin(module.resolve('skins/complete.html'), {
    title: '退会が完了しました',
    headMessage: '今後、IT 勉強会サーチメールは送信されなくなります'
  });

以下のページが表示されれば、無事退会処理されたことになります

RingoJS

簡単Webアプリ開発完了

以上がバッチ処理機能の開発になります。簡略化された処理もありますが、「サーバーサイドJavaScriptで簡単Webアプリ開発」ひととりやりたいことは網羅できたと思いますので、完了としたいと思います。このブログで使用したソースコード一式はGitHubにUPしてありますので、もしよろしければ見てみてください。


 
投稿日:2011/06/17 | カテゴリ:JavaScript | コメント・TrackBack:(0)



Trackback URL

http://blog.fukaoi.org/2011/06/17/server-side-javascript-batch?tb=y&entry_id=38

コメントはこちらからどうぞ

 
 
              
入力された内容
確認内容として表示されます