Edited at
kintoneDay 18

自動トーナメント作成無料Webサービス Challonge と kintone を連携してみました

More than 1 year has passed since last update.

皆さんこんにちは!こちらkintone Advent Calendar 2016 18日目の記事になります(少し遅くなってすみません。。。)。

初記事投稿ということでよろしくお願いします!m(_ _)m

私は普段 kintone のデベロッパーでありつつも兼業プロゲーマーとしても活動しております。

プロゲーマーとしての活動の一貫としてゲームの大会イベントを主催することも多いのですが

大会イベントを企画する中で私はある悩みを持ってました。

そう。トーナメントの作成作業がとてもめんどくさかったのです。

Excelで作ったトーナメントや図に参加者の名前を手書きで書き込んでそれを回してコールで呼び出して・・・。

大会が終わったらそれを集計してHPに載せて・・・。

そんな悩みを解決してくれたとても便利なトーナメント作成サービス、Challonge と kintone の

連携に挑戦してみたので紹介させていただきます。

忘年会シーズンでちょっとしたイベントを開く際には是非ご利用ください!


事前準備


Challongeとは?

Challongeは上述の通り、トーナメント作成サービスです。

主にゲームシーンで活用されており、海外を中心に徐々に認知が広がっているようです。

サンプル http://challonge.com/ja/battlegateway13



トーナメントの形式はもちろん、細かいスコアや参加者のシード権や並び順も自由に変更できます。

詳しくはこちらのブログで紹介されておりますので是非お試しください。

https://keykakko.wordpress.com/2013/03/14/challonge-%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9/


やりたいこと

Challonge APIドキュメントより色々できることがわかりますが

今回はkintoneのアプリでトーナメントを作れるアプリを作成し、kintoneから


  • トーナメントの作成

  • 参加者の追加&ランダム並び替え

  • トーナメントの開始

  • トーナメントの終了

  • トーナメントの結果集計

をやってみます。


Challongeのアカウントを取得する

今回はChallongeのAPIをkintoneから呼び出すために、アカウントを取得してAPIトークンを確認します。

1.「Sign Up」からアカウント作成します。ユーザ名、メールアドレス、パスワード入力で簡単に作成できます。

2.自分のアカウントにログインし、設定画面からAPIキーを発行し、APIキーを控えておきます。


kintoneのアプリを作成する

下記のアプリを新規作成します。フィールドコードがtournament_から始まるものはこのあとのJSカスタマイズで利用します。

フィールド名
フィールドコード
フィールドタイプ

運営者
作成者
作成者

トーナメント開催日時
tournament_start_at
日時

公開設定
tournament_private
ラジオボタン

トーナメント名
tournament_name
文字列一行

トーナメントurl(半角英数、アンダースコアのみ)
tournament_url
リンク(Webアドレス)

トーナメントフルURL(編集不要)
tournament_full_challonge_url
リンク(Webアドレス)

トーナメントタイプ
tournament_type
ドロップダウン

トーナメント説明
tournament_description
文字列一行

トーナメント参加者
tournament_participants
ユーザー選択

テーブル
Table
テーブル

テーブル「ランク」
tournament_rank
数値

テーブル「参加者」
tournament_user
aligned

最後にプロセス管理を設定して完了です。

これで準備は整いました。あとはJavaScriptカスタマイズをやるだけです!


JSカスタマイズ


トーナメントの作成

レコード保存前イベントを使ってトーナメントを作成します。外部のAPIの呼び出しになるので、kintone.proxy()を使っています。

ここから登場する「api_key」項目はすべてChallongeで取得したAPI_TOKENを入力します。

利用API

POST https://api.challonge.com/v1/tournaments.json

https://api.challonge.com/ja/v1/documents/tournaments/create

    //レコード保存前イベント

kintone.events.on('app.record.create.submit', function(event) {
var record = event.record;
var private_value = "";
if (record.tournament_private.value === "公開") {
private_value = false;
} else if (record.tournament_private.value === "非公開") {
private_value = true;
}
var params = {
'api_key': INPUT_YOUR_API_TOKEN,
'tournament': {
'name': record.tournament_name.value,
'tournament_type': record.tournament_type.value,
'private': private_value,
'url': record.tournament_url.value,
'description': record.tournament_description.value,
'start_at': record.tournament_start_at.value
}
};
var headers = {
'Content-Type': 'application/json'
};
var url = 'https://api.challonge.com/v1/tournaments.json';
return kintone.proxy(url, 'POST', headers, params).then(function(args) {
//success
if (JSON.parse(args[0]).errors) {
alert(JSON.parse(args[0]).errors);
return false;
}
record['tournament_full_challonge_url']['value'] = JSON.parse(args[0]).tournament.full_challonge_url;
return event;
}, function(error) {
//error
return false;
});
});


参加者の追加&ランダム並び替え

続いてaddParticipants()関数とrandomizeParticipants()関数を作成します。

これらはボタンからの入力で操作できるようにいじります。

利用API

POST https://api.challonge.com/v1/tournaments/{tournament}/participants/bulk_add.json

https://api.challonge.com/ja/v1/documents/participants/bulk_add

POST https://api.challonge.com/v1/tournaments/{tournament}/participants/randomize.json

https://api.challonge.com/ja/v1/documents/participants/randomize

    //参加者を加える

function addParticipants() {
var record = kintone.app.record.get();
var participants_array = [];
for (var i = 0; i < record.record.tournament_participants.value.length; i++) {
participants_array.push(
{
'name': record.record.tournament_participants.value[i].name
}
);
}

var params = {
'api_key': INPUT_YOUR_API_TOKEN,
'participants': participants_array
};
var headers = {
'Content-Type': 'application/json'
};
var url = 'https://api.challonge.com/v1/tournaments/' + record.record.tournament_url.value +
'/participants/bulk_add.json';
return kintone.proxy(url, 'POST', headers, params).then(function(args) {
//success
if (JSON.parse(args[0]).errors) {
alert(JSON.parse(args[0]).errors);
return false;
}
alert("参加者を追加しました。");
window.location.reload();
}, function(error) {
//error
return false;
});
}
//参加者を並び替える
function randomizeParticipants() {
var record = kintone.app.record.get();
var params = {
'api_key': INPUT_YOUR_API_TOKEN
};
var headers = {
'Content-Type': 'application/json'
};
var url = 'https://api.challonge.com/v1/tournaments/' + record.record.tournament_url.value +
'/participants/randomize.json';
return kintone.proxy(url, 'POST', headers, params).then(function(args) {
//success
if (JSON.parse(args[0]).errors) {
alert(JSON.parse(args[0]).errors);
return false;
}
alert("並び替えが完了しました。");
window.location.reload();
}, function(error) {
//error
return false;
});
}


トーナメントの開始/終了

次にトーナメントの開始の関数startTournament()とトーナメントの終了の関数endTournament()を作ります。

これらの関数はプロセス管理のステータスを回したときに動作することを想定しています。

POST https://api.challonge.com/v1/tournaments/{tournament}/start.json

https://api.challonge.com/ja/v1/documents/tournaments/start

POST https://api.challonge.com/v1/tournaments/{tournament}/finalize.json

https://api.challonge.com/ja/v1/documents/tournaments/finalize

    //トーナメントを開始する

function startTournament(record) {
var params = {
'api_key': INPUT_YOUR_API_TOKEN
};
var headers = {
'Content-Type': 'application/json'
};
var url = 'https://api.challonge.com/v1/tournaments/' + record.tournament_url.value + '/start.json';
return kintone.proxy(url, 'POST', headers, params).then(function(args) {
//success
if (JSON.parse(args[0]).errors) {
alert(JSON.parse(args[0]).errors);
return false;
}
alert("トーナメントを開始しました");
}, function(error) {
//error
return kintone.Promise.reject(error);
});
}

//トーナメントを終了する
function endTournament(record) {
var params = {
'api_key': INPUT_YOUR_API_TOKEN
};
var headers = {
'Content-Type': 'application/json'
};
var url = 'https://api.challonge.com/v1/tournaments/' + record.tournament_url.value + '/finalize.json';
return kintone.proxy(url, 'POST', headers, params).then(function(args) {
//success
if (JSON.parse(args[0]).errors) {
alert(JSON.parse(args[0]).errors);
return false;
}
alert("トーナメントが終了しました。");
}, function(error) {
//error
return kintone.Promise.reject(error);
});
}


トーナメントの結果集計

最後に集計用の関数getFinalRank()関数で取得した値をputRecordResult()関数でテーブルに更新をかけます。

これらもボタンからの入力で操作できるようにいじります。

利用API

GET https://api.challonge.com/v1/tournaments/{tournament}/participants.{json|xml}

https://api.challonge.com/ja/v1/documents/participants/bulk_add

    //最終結果を取得する。

function getFinalRank() {
var record = kintone.app.record.get();
var api_key = '?api_key=' + INPUT_YOUR_API_TOKEN;
var headers = {
'Content-Type': 'application/json'
};
var url = 'https://api.challonge.com/v1/tournaments/' + record.record.tournament_url.value +
'/participants.json' + api_key;
return kintone.proxy(url, 'GET', headers, {}).then(function(args) {
//success
if (JSON.parse(args[0]).errors) {
alert(JSON.parse(args[0]).errors);
return false;
}
return JSON.parse(args[0]);
}, function(error) {
//error
return kintone.Promise.reject(error);
});
}

//最終結果をレコードに保存する。
function putRecordResult(result_data) {
var result_array = [];
for (var i = 0; i < result_data.length; i++) {
result_array.push(
{
"value": {
"tournament_user": {
"value": result_data[i].participant.name
},
"tournament_rank": {
"value": result_data[i].participant.final_rank
}
}
}
);
}
//配列をソート
result_array.sort(
function(a, b) {
var aName = a['value']["tournament_rank"]['value'];
var bName = b['value']["tournament_rank"]['value'];
if (aName < bName) return -1;
if (aName > bName) return 1;
return 0;
}
);
var body = {
"app": kintone.app.getId(),
"id": kintone.app.record.getId(),
"record": {
"Table": {
"value": result_array
}
}
};
return kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', body).then(function(resp) {
// success
alert("トーナメントの最終結果をレコードに保存しました。");
window.location.reload();
}, function(error) {
// error
return kintone.Promise.reject(error);
});
}


その他

レコード詳細イベント、プロセス管理のイベントに上記の関数を書いていきます。

レコード詳細画面ではせっかくなのでボタンで埋め込む以外にiframeで画面操作をできるようにしました。

    //レコード詳細画面が表示された時のイベント

kintone.events.on('app.record.detail.show', function(event) {
if (document.getElementById("addParticipants") !== null) {
return;
}
//参加者を追加するボタン
var menuButton = document.createElement("button");
menuButton.id = "addParticipants";
menuButton.innerHTML = "参加者を追加する";
menuButton.onclick = function() {
if (window.confirm("参加者を追加しますか?")) {
addParticipants()
.catch(function(error) {
alert(error);
});
}
};

//参加者をランダムに並び替えるボタン
var menuButton2 = document.createElement("button");
menuButton2.id = "randomizeParticipants";
menuButton2.innerHTML = "ランダム振分";
menuButton2.onclick = function() {
if (window.confirm("参加者の位置をランダムに変えますか?")) {
randomizeParticipants()
.catch(function(error) {
alert(error);
});
}
};

//結果をレコードに追加するボタン
var menuButton3 = document.createElement("button");
menuButton3.id = "getFinalRank";
menuButton3.innerHTML = "結果をレコードに追加する";
menuButton3.onclick = function() {
if (window.confirm("結果をレコード内のサブテーブルに追加しますか?")) {
getFinalRank()
.then(putRecordResult)
.catch(function(error) {
alert(error);
});
}
};

var el = kintone.app.record.getHeaderMenuSpaceElement();
var tournament_element = '<iframe src="https://challonge.com/ja/' + event.record.tournament_url.value +
'/module?show_final_results=1" width="100%"' +
'height="500" frameborder="0" scrolling="auto" allowtransparency="true"></iframe>';
el.innerHTML = tournament_element;
kintone.app.record.getHeaderMenuSpaceElement().appendChild(menuButton);
kintone.app.record.getHeaderMenuSpaceElement().appendChild(menuButton2);
kintone.app.record.getHeaderMenuSpaceElement().appendChild(menuButton3);
return;
});

//プロセス管理のアクションイベント
kintone.events.on('app.record.detail.process.proceed', function(event) {
var record = event.record;
return new kintone.Promise(function(resolve, reject) {
if (event.nextStatus.value === "進行中") {
resolve(startTournament(record));
} else if (event.nextStatus.value === "完了") {
resolve(endTournament(record));
} else {
resolve(event);
}
}).catch(function(error) {
alert(error);
return false;
});
});


実際に使ってみた

それでは実際に上のカスタマイズを追加したアプリを見ていきましょう!


新規レコード作成

参加者やトーナメント名称などを入力していきます。

トーナメントフルURLとテーブルは入力不要です。

保存時にトーナメントが作成されます。この時点では参加者がいないので何も表示されていません。


参加者を追加する

「参加者を追加する」ボタンをクリックしてユーザー選択フィールドのメンバーを参加者に追加します。

ユーザーの表示名を使っています。参加者を追加したらプロセス管理を進めましょう!


結果を入力する

入力画面はiframeで埋め込んでいるだけなので、Challongeでログインしていれば結果入力もこの画面からできてしまいます!

コメント等で結果報告してもらいながら運営者で進めていきましょう。


トーナメント終了して結果報告

結果をすべて入力し終わったら最後にプロセス管理を進めて「結果をレコードに追加する」ボタンをクリックして終了です。



ちゃんと通知も行く設定にしてみました。


終わりに

思ったより長くなってしまい遅くなりました。記事を書くってなかなか大変です・・・。

余談ですが、参加者の一括登録のAPIのプロパティがリファレンスだと「participant」なんですが

実際は「participants」が正しいようでなかなかハマりました。。。要注意。

また、Challongeで作成したトーナメントは基本全公開なのですが、プライベート設定をするとリンクを直接叩かない限り

見えなくなるようです。

細かいところをまだまだいじっていないので今後バージョンアップしていきたいと思います。

今回のサンプルで作ったトーナメント

http://challonge.com/ja/kintone_top