シフト表を自動生成してLINEで見られるようにしたい②
Googlespreadsheetでオリジナルカレンダー(勤務表)作ってみた①に引き続き、作ったカレンダーをLINEで見られるようにしていきます。
1.毎月のカレンダーを完成させたら新しいシートを作成する
前回作ったオリジナルカレンダーは自動で更新ができて楽ちんです。先生の予定が変わって書き換えることもできます。しかし月を変えて更新をするとせっかく編集したものが消えてしまうデメリットも持っています。
そこで、完成したカレンダーは新しいシートに保存することにしました。これがLINEと繋がる大事なシートになってきます。
Appscriptで新しくスクリプトを作成します。
(.gs.gsになっているのがバレる…。)
シート名は必要に応じて変えます。
私は医師の勤務リストをDoctorlist
、医師の予定が入ったカレンダーをDoctorCalendar
としました。
新しいシートは2022年6月
のようにシート名を付けて、シートが既にあったら更新だけされる仕組みになっています。
新しいシートを作るのと同時にリスト作成ができないので、コードの書き方は悪そうです。
シートがないと1度はシート作成の実行、2度目にリスト作成の実行になります。
下の方は同じことを繰り返していますが、for文を考えるのに疲れたのでゴリ押しコードになっています。
function sample24() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
// spreadsheetのIDはURLのd/~/editの間の部分です。下のXXXの部分。
// https://docs.google.com/spreadsheets/d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/edit
var sp = SpreadsheetApp.openById("自分のspreadsheetのID")
var sheet02 = sp.getSheetByName("DoctorCalendar")
var year = sheet02.getRange("B3").getDisplayValue()
var day = sheet02.getRange("C3").getDisplayValue()
var SheetName = year+"年"+day+"月";
var sheet = SpreadsheetApp.getActive().getSheetByName(SheetName);
if(sheet==null){
var sheet = ss.insertSheet(SheetName);
}else{
var sheet01 = sp.getSheetByName(SheetName)
var sheet02 = sp.getSheetByName("DoctorCalendar")
sheet01.getRange("A1").setValue("date")
sheet01.getRange("B1").setValue("am")
sheet01.getRange("C1").setValue("pm")
// 日付を入れる
var date = new Date(sheet02.getRange("C6").getValues())
sheet01.getRange("A2").setValue(date);
for(var i=0; i<30; i++){
date.setDate(date.getDate()+1);
sheet01.getRange(3+i,1).setValue(date);
}
for (h = 0; h<7; h++){
// 午前の先生
var am = sheet02.getRange(8,3+h,7,1).getValues();
var value = am.join("\n");
sheet01.getRange(2+h,2).setValue(value)
// 午後の先生
var pm = sheet02.getRange(16,3+h,7,1).getValues();
var value01 = pm.join("\n");
sheet01.getRange(2+h,3).setValue(value01)
}
for (h = 0; h<7; h++){
// 午前の先生
var am = sheet02.getRange(25,3+h,7,1).getValues();
var value = am.join("\n");
sheet01.getRange(9+h,2).setValue(value)
// 午後の先生
var pm = sheet02.getRange(33,3+h,7,1).getValues();
var value01 = pm.join("\n");
sheet01.getRange(9+h,3).setValue(value01)
}
for (h = 0; h<7; h++){
// 午前の先生
var am = sheet02.getRange(42,3+h,7,1).getValues();
var value = am.join("\n");
sheet01.getRange(16+h,2).setValue(value)
// 午後の先生
var pm = sheet02.getRange(50,3+h,7,1).getValues();
var value01 = pm.join("\n");
sheet01.getRange(16+h,3).setValue(value01)
}
for (h = 0; h<7; h++){
// 午前の先生
var am = sheet02.getRange(59,3+h,7,1).getValues();
var value = am.join("\n");
sheet01.getRange(23+h,2).setValue(value)
// 午後の先生
var pm = sheet02.getRange(67,3+h,7,1).getValues();
var value01 = pm.join("\n");
sheet01.getRange(23+h,3).setValue(value01)
}
for (h = 0; h<7; h++){
// 午前の先生
var am = sheet02.getRange(76,3+h,7,1).getValues();
var value = am.join("\n");
sheet01.getRange(30+h,2).setValue(value)
// 午後の先生
var pm = sheet02.getRange(84,3+h,7,1).getValues();
var value01 = pm.join("\n");
sheet01.getRange(30+h,3).setValue(value01)
}
}
}
挿入→図形描画から完成ボタンを作成し、スクリプトを割り当てます。
これで先生の予定変更をした後のカレンダーリストを保持することができます。
2.steinを使ってspreadsheetをAPI化する
Steinを利用します。
普通に検索するとファッションサイトが表示されるので上記からどうぞ。
spreadsheetのURLを入れるだけでAPIにしてくれるので超便利です。
ライブラリもあるので、追加、削除、閲覧などの方法は調べます。
作成出来たらAPI URL
をコピーして控えておきます。
3.LINEと連携する
実物は今日の担当医以外にも、今月の予定表や専門外来の案内など機能を付けているのですが、コードが長すぎるので今回は今日の担当医と曜日で呼び出す部分だけにします。
LINEはVScode上で、JavaScript、node.jsを使って書いています。
リッチメニューに「今日の担当医は?」を入れ込んでいて、その後はクイックリプライを仕込んでいます。
今回はリッチメニューがないので、自分で「今日の担当医は?」とメッセージを送って返答があれば成功です!
LINEのSDK入れたり…の部分は、こちらを読んでセッティングしてください。
めちゃ分かりやすいので、何度も読んで助けてもらっている記事です。
参考:1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest
moment.jsというパッケージを入れているので、ターミナルで以下をインストールします。
$ npm i moment
'use strict';
const express = require('express');
const line = require('@line/bot-sdk');
var mom = require('moment')
const PORT = process.env.PORT || 3000;
const channel = {
channelSecret: '自分のチャンネルシークレット',
channelAccessToken: '自分のアクセストークン'
};
const app = express();
app.post('/webhook', line.middleware(channel), (req, res) => {
console.log(req.body.events);
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result));
});
const client = new line.Client(channel);
// ------------------------------------------------
// スプレットシートAPI
const SteinStore = require("stein-js-client");
const store = new SteinStore("steinで作成したAPIのURL");
async function handleEvent(event) {
var replyText = "";
var msg = event.message.text;
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
// 〇年〇月を取得する
var date = new Date();
const formatDate = (date)=>{
let formatted_date = date.getFullYear() + "年" + (date.getMonth() + 1) + "月"
return formatted_date;
}
console.log(formatDate(date));
// spreadsheetのシートをどの範囲で取得するか指示する
var data = await store.read("Doctorlist", { limit: 45, offset: 0 })
var data01 = await store.read(formatDate(date), { limit: 31, offset: 0 })
// console.log(data);
// console.log(msg);
// 取得する期間を今月の初めから月末までに指定
var today = mom().format('YYYY/MM/DD');
// 〇〇先生と送られてきたら〇〇(名前の部分)をspreadsheetから探すようにする
var checkname = data.filter(value => value.name.match(msg.replace("先生","")));
var checkdayofweek = data.filter(value => value.dayofweek.match(msg.replace("曜日","")));
var checktoday = data01.filter(value => value.date.match(today));
// console.log(checktoday);
// クイックリプライ
var quickReply ={ // ②
"items": [
{
"type": "action",
"action": {
"type": "message",
"label": "先生はいついる?",
"text": "先生はいついる?"
}
},
{
"type": "action", // ③
"action": {
"type": "message",
"label": "曜日の先生を探す",
"text": "曜日の先生を探す"
}
}
]
};
// 先生の時間を探す
if(checkname.length>0 && msg !== "水" && msg !== "木" && msg !== "金"){
var test = [];
var test02 = [];
for (var i=0; i<checkname.length; i++){
var speciality = checkname[i].speciality;
var dayofweek = checkname[i].dayofweek;
var classification = checkname[i].classification || "";
var amstart = checkname[i].amstart || "";
var amend = checkname[i].amend || "";
var pmstart = checkname[i].pmstart || "";
var pmend = checkname[i].pmend || "";
test[i] = "\n【"+dayofweek+"曜日】"+classification+"外来\n"+"午前"+amstart+"-"+amend+"\n午後"+pmstart + "-" + pmend
test02[i] = speciality
}
var name = checkname[0].name;
var doctorinfo01 = "専門:"+Array.from(new Set(test02));
var doctorinfo02 =name+"先生\n"+doctorinfo01+test+"\nです。";
replyText = doctorinfo02
}
// 曜日の先生を探す
else if(checkdayofweek.length>0){
var test03 = [];
for (var i=0; i<checkdayofweek.length; i++){
var name = checkdayofweek[i].name;
test03[i] = "\n"+name+"先生"
}
var d = checkdayofweek[0].dayofweek;
var doctorweek = d +"曜日の先生は"+Array.from(new Set(test03))+"\nです。"
replyText = doctorweek
}
else if(msg === "日曜日" || msg === "日" ){
replyText = "日曜日、祝日はお休みです。"
}
// 今日の担当医
else if(msg==="今日の担当医は?"){
var am = checktoday[0].am.replace(/(\n)\1+/g, '$1');
var pm = checktoday[0].pm.replace(/(\n)\1+/g, '$1');
var todaydoctor = "今日の担当医は\n午前\n"+am+"\n午後\n"+pm+"です。"
replyText = todaydoctor
}
else if(event.message.text == "こんにちは")
return client.replyMessage(event.replyToken, quickReply);
// 先生は誰?
else if(msg==="曜日の先生を探す"){
return client.replyMessage(event.replyToken, {
type: 'text',
text: "何曜日に来院しますか?",
"quickReply": { // ②
"items": [
{
"type": "action", // ③
"action": {
"type": "message",
"label": "月曜日",
"text": "月曜日"
}
},
{
"type": "action",
"action": {
"type": "message",
"label": "火曜日",
"text": "火曜日"
}
},
{
"type": "action", // ③
"action": {
"type": "message",
"label": "水曜日",
"text": "水曜日"
}
},
{
"type": "action", // ③
"action": {
"type": "message",
"label": "木曜日",
"text": "木曜日"
}
},
{
"type": "action",
"action": {
"type": "message",
"label": "金曜日",
"text": "金曜日"
}
},
{
"type": "action", // ③
"action": {
"type": "message",
"label": "土曜日",
"text": "土曜日"
}
},
{
"type": "action", // ③
"action": {
"type": "message",
"label": "日曜日",
"text": "日曜日"
}
}
]}
})
}
// 先生はいついるのか?
else if(msg==="先生はいついる?"){
replyText = "気になる先生の名前を送ってください。"+"\nA先生\nB先生 …など"
}
// -----------------------------------------------
else{
replyText = "もう一度やり直してください"
}
return client.replyMessage(event.replyToken, {
type: 'text',
text: replyText,
"quickReply": quickReply
}
);
};
app.listen(PORT);
console.log(`Server running at ${PORT}`);
おわりに
読んでコピペすればできる記事を書く予定でしたが、説明が少ない記事になってしまいました。
同じ名字の先生はどちらか一方しか出てこないなどまだ不備はありますが、完成に向けて取り組んでいきます。
理事長に「これ、すごいっすね!」って言っていただけたので一旦満足です!
読んでいただいてありがとうございました。
参考
Stein/docs
JavaScriptで配列から値を検索し、取得や判定ができるfindの使い方
配列から条件に合うものはfind()で見つけよう。
[JavaScript] 配列の存在チェック(空判定)は if (array.length) {...} でいいよって話
【GAS】スプレッドシートの値を別のスプレッドシートにコピペするGAS
Google Apps Script(GAS)入門 スプレッドシートのセル取得・書き込み方法(getValue,setValue)
JavaScript で配列から重複を削除する
Google Apps Scriptで隣のセルを取得、選択、アクティブセルを隣に移動する
js で完全一致をまとめてやりたい時
INDEXとMATCH関数の組み合わせで行列の交差値が一瞬で取得出来るよ
【javascript】連想配列で”key”だけや”value”だけを取得する方法
【JavaScript入門】連想配列(Dictionary)の取得/追加/ソートまとめ