🍊
Pleasanter x Google 連携 : Google Form を Pleasanter で集計する
2022 個人アドベントカレンダー の記事です。
やること
前回 Form 作ったんで、取り込みます。
まあ基本的に列構造が合致してるはずなので CSV でインポートしたほうが早いとは思います。
そうはいっても自動化したいとかってなるんだろうなーと。
あと Form の送信イベントで発動させるので Form にスクリプトを書かないといけないです。
(ここも、あまり汎用的でないところ)
作り
- フォーム送信時イベントで Pleasanter API にリクエストします。
- 一括でいいなら CSV 取り込みのほうがマシなので。
Google Apps Script のコード
-
OnFormSubmit
をフォーム送信のトリガーとして設定してください。
const onFormSubmit = (e) => {
const formResponse = e.response;
const formName = e.source.getTitle();
const answer = formResponse
.getItemResponses()
.reduce(
(a, c) => a.concat([[c.getItem().getTitle(), c.getResponse()]]),
[],
);
const { siteId: id, body: dic } = getInfo(formName);
const data = answer.reduce((a, c) => ({ ...a, [dic[c[0]]]: c[1] }), {});
insertTo(id, data);
};
const apikey = 'Api キー';
const host = 'https://demo.pleasanter.org/api/items/';
const post = (host, id, action, body = {}) => {
const url = `${host}/${id}/${action}`;
const param = {
contentType: 'application/json',
method: 'post',
payload: JSON.stringify({ ...body, ApiVersion: 1.1, ApiKey: apikey }),
muteHttpExceptions: true,
};
const res = UrlFetchApp.fetch(url, param);
if (res.getResponseCode() !== 200) return undefined;
return JSON.parse(res.getContentText());
};
const getInfo = (title) => {
const id = 7167230;
const filter = {
View: {
ColumnFilterHash: {
ClassA: title,
},
},
};
const dat = post(host, id, 'get', filter).Response?.Data;
if (dat.length !== 1) {
throw new Error('could not find valid site information');
}
const record = dat[0];
return {
siteId: record.NumHash.NumA,
body: JSON.parse(record.Body).reduce(
(a, c) => ({ ...a, [c.LabelText]: c.ColumName }),
{},
),
};
};
const insertTo = (id, data) => {
console.log(buildData(data));
const res = post(host, id, 'create', buildData(data));
if (res.StatusCode !== 200) {
throw new Error(JSON.stringify(res));
}
console.log(res);
};
const buildData = (data) =>
Object.entries(data).reduce((a, [k, v]) => assignTo(a, k, v), {});
const needsHash = (str) =>
/^(Class|Num|Description|Date|Check)[A-Z0-9]+$/.test(str);
const matchHash = (str) =>
/^(Class|Num|Description|Date|Check)[A-Z0-9]+$/.exec(str);
const assignTo = (obj, key, value) => {
if (!needsHash(key)) return { ...obj, [key]: value };
const varType = matchHash(key)[1];
if (varType === 'Date' && value === '') return { ...obj };
if (varType === 'Class' && Array.isArray(value))
value = JSON.stringify(value);
const sub = { [key]: value };
const bundleKey = `${varType}Hash`;
return { ...obj, [bundleKey]: { ...obj[bundleKey], ...sub } };
};
詰まったところ
- Google Form のチェックボックス
チェックボックスは Pleasanter で言うところの、「複数選択」を有効にした「分類型」だった。
→ 前回記事で手直しが必要になった。
- Pleasanter API の動きとして、不正な JSON になるとデバッグが厳しい
Google Form での日付型は、ハイフンで区切られた表記("2022-12-18")だが、これは Pleasanter でも正しくパースできた。
一方、未入力の場合 '' になり、これは Pleasanter では受け入れられない値となる(不正な JSON として扱われる)。ので、空配列のときスキップする手当を場当たりに実施。
また、前記のとおり Google Form のチェックボックス型は、複数選択可能なので文字列の配列でデータ保持される。
これをこのまま Pleasanter に送ると不正な文字列になるので、一旦 JSON.stringify
して ClassX のキーに文字列の値として引き渡さないといけない。ので、stringify を場当たりに追加。
おわりに
- 前回とセットで Form 連携はひととおりできた。
- 連携機能をアプリ的に作ったので ID 依存する箇所が、アプリとして使うテーブルのサイト ID のみに限定されているのが良いと考えている。
- Form 連携したテーブルを何度も作ると、それらテーブルのサイト ID は変動してしまう。もし、これら各テーブルのスクリプトに機能を作り込んでいると Google Apps Script には、都度対応するサイト ID を指定しないといけなくなる。
- 細かいところでは Pleasanter のタイトルが Google Form の場合、うまく生かせてないなーという印象。
- 項目として無効化すればいいんだけれども。
Discussion