研究室の書籍貸出簿をJavascriptとGoogle Apps Scriptと書誌情報APIで．その4

これまでの内容：
[その1：本のバーコードを読んで貸出記録をつけるシステム](https://qiita.com/fujino-fpu/items/fd4199f18f2d835098f0)
[その2：返却期限すぎても返却されていない本のリマインドメールを出すシステム](https://qiita.com/fujino-fpu/items/170de6ce8ac1cfe0c4d1)
[その3：貸出簿をつける時のエラーチェック（スプレッドシートの学生情報DBに問い合わせするシステム）](https://qiita.com/fujino-fpu/items/e1b09807b5f54eb93385)
[その4 : 返却処理のシステム](https://qiita.com/fujino-fpu/items/123dc46a54ac344bb2ac)

今回は「その4」ということで，返却処理を行うシステムを作った．

基本的な動作：
1. 本のバーコードを読み込んでISBN番号を取得
2. 取得したISBN番号から貸出簿を検索し，返却処理が未了（返却日が未記載）の記録を取得
3. 記録にある本のタイトル，著者名，借りた人の名前，学籍番号を画面表示
4. 返却OKなら，その貸出記録に返却日を記載．

というもの．
その3までの処理はスプレッドシートに紐づけたGoogleAppScriptを組んでたけど，今回は別のGoogleAppScriptを作って，そこからスプレッドシートにアクセスする方法を取った．

# 貸出記録の検索
ポイントはISBN番号から貸出簿を検索して記録を返すところ．
といことで，上記のことをするために以下のようなJavascriptとGoogleAppScriptを作った．

##Javascript
ISBNをGet送信でGoogleAppScriptに送って，レスポンスとして貸出記録を受け取るというもの．

```{javascript}
function GetBookrecord( _isbn ){
  const endpoint = "https://script.google.com/macros/s/*********/exec";
  $.ajax({
    type: 'GET',
    url: endpoint,
    dataType: 'jsonp',
    data: {
      m_isbn: _isbn
    }, 
    success: out => {
      if(out.BookTitle==""){
        alert("貸出記録を見つけられませんでした．")
      }else{
        $("#BookTitle").text(out.BookTitle);
        $("#BookAuthor").text(out.BookAuthor);
        $("#StudentName").text(out.StudentName);
        $("#StudentNo").text(out.StudentNo);
        LentDate = new Date(out.LentDate);
        $("#LentDate").text(LentDate.getFullYear()+"年"+(LentDate.getMonth()+1)+"月"+LentDate.getDate()+"日");
      }
    }
  });
}
```
##GoogleAppScript
上記と対になるGoogleAppScriptは以下のような感じ．すでにスプレッドシートに紐づいたスクリプトでdoGetを作ってるので，今回は独立したGoogleAppScriptを作ってdoGetを書き込んだ．
スプレッドシートから独立しているので，スプレッドシートへのアクセスは`getActiveSpreadsheet()`ではなく，`OpenByID()`でIDを直接打ち込む形．

```{Javascript:GoogleAppScript}
//引数
//isbn:ＩＳＢＮ番号
//返り値
//BookTitle:本の名前
//BookAuthor：本の著者
//StudentName:学生名
//StudentNo:学籍番号
//LentDate:貸出日
function doGet(e){
  var ss = SpreadsheetApp.openById('*****');
  var sheet = ss.getSheetByName("シート1");
  var lastRow=sheet.getDataRange().getLastRow(); //対象となるシートの最終行を取得 
 
  var title="";
  var author="";
  var sname = "";
  var snumber="";
  var lentdate="";
  var isbn = e.parameter.m_isbn.toString();

  for(var i = 2; i<=lastRow ; i++){
    if(sheet.getRange(i, 2).getValue() == isbn && sheet.getRange(i, 8).isBlank()){
      title = sheet.getRange(i, 3).getValue();  //貸出記録の3列目
      author= sheet.getRange(i, 4).getValue();  //貸出記録の4列目
      sname= sheet.getRange(i, 5).getValue();  //貸出記録の5列目
      snumber= sheet.getRange(i, 6).getValue();  //貸出記録の6列目
      lentdate= sheet.getRange(i, 1).getValue();  //貸出記録の1列目
      break;
    }
  }
  
  var result = {
    BookTitle: title,
    BookAuthor:author,
    StudentName:sname,
    StudentNo:snumber,
    LentDate:lentdate
  }

  //以下はJSONPを返すためのコード．
  var responseText;
  var out = ContentService.createTextOutput();
  var callback = e.parameter.callback;
  if (callback) {
      responseText = callback + "(" + JSON.stringify(result) + ")";
      out.setMimeType(ContentService.MimeType.JAVASCRIPT);
  } else {
      responseText = JSON.stringify(result);
      out.setMimeType(ContentService.MimeType.JSON);
  }
  out.setContent(responseText);
  
  return out;
  
}
```

#返却日の記録
返却日の列に日付が記載されていることを持って返却とする．ということで，これはPostで行う．
##htmlとJavascript
html側は以下の通り．
これで，ISBN，書籍名，著者名，名前，学籍番号，貸出日がGoogleAppScriptに送られる．

```{html}
（前略）
<form id="myform" name="myform" method="POST" action="https://script.google.com/macros/s/********/exec">
  <p>ＩＳＢＮ番　号：<input id ="ISBN" name="ISBN" type ="text"></input>
    <input type="button" id="ISBNcheck" value="OK"></input>
  </p>
  <p>書　　籍　　名：<input type ="text" id ="BookTitle" name="BookTitle" disabled="disabled" ></input></p>
  <p>著　　者　　名：<input type ="text" id ="BookAuthor" name="BookAuthor" disabled="disabled" ></input></p>
  <p>名　　　　　前：<input type ="text" id ="StudentName" name="StudentName" disabled="disabled" ></input></p>
  <p>学　籍　番　号：<input type ="text" id ="StudentNo" name="StudentNo" disabled="disabled" ></input></p>
  <p>貸　　出　　日：<input type ="text" id ="LentDate" name="LentDate" disabled="disabled" ></input></p>
  <p><input type="button" id="submitbtn" value="返却" ></input></p>
</form>
（中略）
<script type="text/javascript">
var btn2 = document.getElementById('submitbtn');
btn2.addEventListener('click', function(){ // ボタンをクリックした場合．
  if($("#BookTitle").val() == ""){
    alert("貸出簿の確認ができていません．返却処理を中止します．")
    return;
  }
  if(confirm("この本の返却処理を行いますか？")){
    document.myform.submit();
  }else{
    alert("中止します")
  }
});			
</script>
```

##GoogleAppScript
貸出簿検索と同じように，受け取ったformデータのISBN番号と返却日が未了かどうかで確認．

```{Javascript}
function doPost(e) {
  var ss = SpreadsheetApp.openById('*******');
  var sheet = ss.getSheetByName("シート1");
  var lastRow=sheet.getDataRange().getLastRow(); //対象となるシートの最終行を取得 
  var isbn = e.parameter.ISBN.toString();
  
  for(var i = 2; i<=lastRow ; i++){
    if(sheet.getRange(i, 2).getValue() == isbn && sheet.getRange(i, 8).isBlank()){
      var today = new Date();
      sheet.getRange(i,8).setValue(today);
      break;
    }
  }
  return ContentService.createTextOutput("返却処理を完了しました");
}
```

#完成
最終的なインタフェースとなるhtmlは以下のような感じ．

```{html:return.html}
<!DOCTYPE html>
<meta charset=utf-8>
<html lang="ja">
	<head>
		<title>藤野ゼミの本の返却フォーム</title>
		<meta name="robots" content="noindex" />
		<!--<link rel="manifest" href="manifest.json">-->
	</head>
	<body>
		<div id="container">
			<p>以下のボタンを押してカメラを起動させ，本のバーコードを撮影してください．</p>
			<p><input id="Take-Picture" type="file" accept="image/*;capture=camera" /></p>
			<p><canvas width="240" id="picture"></canvas></p>
			<p id="textbit"></p>
			<p id="Error">うまくISBN（978または979から始まる13桁の数字）が読めない場合には，以下のISBN番号に手動で入力し，横のOKボタンをクリックしてください．</p>
			<form id="myform" name="myform" method="POST" action="https://script.google.com/macros/s/******/exec">
				<p>ＩＳＢＮ番　号：<input id ="ISBN" name="ISBN" type ="text"></input>
					<input type="button" id="ISBNcheck" value="OK"></input>
				</p>
				<p>書　　籍　　名：<input type ="text" id ="BookTitle" name="BookTitle" disabled="disabled" ></input></p>
				<p>著　　者　　名：<input type ="text" id ="BookAuthor" name="BookAuthor" disabled="disabled" ></input></p>
				<p>名　　　　　前：<input type ="text" id ="StudentName" name="StudentName" disabled="disabled" ></input></p>
				<p>学　籍　番　号：<input type ="text" id ="StudentNo" name="StudentNo" disabled="disabled" ></input></p>
				<p>貸　　出　　日：<input type ="text" id ="LentDate" name="LentDate" disabled="disabled" ></input></p>
				<p><input type="button" id="submitbtn" value="返却" ></input></p>
			</form>
		</div>
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
		<script type="text/javascript">
			function GetBookrecord( _isbn ){
				const endpoint = "https://script.google.com/macros/s/******/exec";
				$.ajax({
					type: 'GET',
					url: endpoint,
					dataType: 'jsonp',
					data: {
						m_isbn: _isbn
					}, 
					success: out => {
						if(out.BookTitle==""){
							alert("貸出記録を見つけられませんでした．")
						}else{
							$("#BookTitle").val(out.BookTitle);
							$("#BookAuthor").val(out.BookAuthor);
							$("#StudentName").val(out.StudentName);
							$("#StudentNo").val(out.StudentNo);
							LentDate = new Date(out.LentDate);
							$("#LentDate").val(LentDate.getFullYear()+"年"+(LentDate.getMonth()+1)+"月"+LentDate.getDate()+"日");
						}
					}
				});
			}

			var btn1 = document.getElementById('ISBNcheck');
 			btn1.addEventListener('click', function(){ // ボタンをクリックした場合．
				var isbn = $("#ISBN").val();
				GetBookrecord(isbn);
			});

			var btn2 = document.getElementById('submitbtn');
 			btn2.addEventListener('click', function(){ // ボタンをクリックした場合．
				if($("#BookTitle").val() == ""){
					alert("貸出簿の確認ができていません．返却処理を中止します．")
					return;
				}
				if(confirm("この本の返却処理を行いますか？")){
					document.myform.submit();
				}else{
					alert("中止します")
				}
			});
		</script>
		<script type="text/javascript" src="JOB.js"></script>
		<script type="text/javascript"> //以下はほとんどバーコードを読むためのソース．
			var takePicture = document.querySelector("#Take-Picture"),
			showPicture = document.createElement("img");
			Result = document.querySelector("#textbit");
			var canvas =document.getElementById("picture");
			var ctx = canvas.getContext("2d");
			JOB.Init();
			JOB.SetImageCallback(function(result) {
				if(result.length > 0){
					var tempArray = [];
					for(var i = 0; i < result.length; i++) {
						tempArray.push(result[i].Format+" : "+result[i].Value);
						//バーコードを読み込んだ後の処理はここの部分だけ．
						const isbn = result[i].Value;
						//isbn番号をGetで送ると，貸出簿にある書名．著者名．借主，学籍，貸出日が帰ってくる．
						if(9780000000000<isbn && isbn <9799999999999) {
							$("#ISBN").val(isbn);
							GetBookrecord(isbn);
						}
						//ここまでバーコードを読み込んだ後の処理	
					}
					Result.innerHTML=tempArray.join("<br />"); 					

				}else{
					if(result.length === 0) {
						Result.innerHTML="Decoding failed.";
					}
				}
			});
			JOB.PostOrientation = true;
			JOB.OrientationCallback = function(result) {
				canvas.width = result.width;
				canvas.height = result.height;
				var data = ctx.getImageData(0,0,canvas.width,canvas.height);
				for(var i = 0; i < data.data.length; i++) {
					data.data[i] = result.data[i];
				}
				ctx.putImageData(data,0,0);
			};
			JOB.SwitchLocalizationFeedback(true);
			JOB.SetLocalizationCallback(function(result) {
				ctx.beginPath();
				ctx.lineWIdth = "2";
				ctx.strokeStyle="red";
				for(var i = 0; i < result.length; i++) {
					ctx.rect(result[i].x,result[i].y,result[i].width,result[i].height); 
				}
				ctx.stroke();
			});
			if(takePicture && showPicture) {
				takePicture.onchange = function (event) {
					var files = event.target.files;
					if (files && files.length > 0) {
						file = files[0];
						try {
							var URL = window.URL || window.webkitURL;
							showPicture.onload = function(event) {
								Result.innerHTML="";
								JOB.DecodeImage(showPicture);
								URL.revokeObjectURL(showPicture.src);
							};
							showPicture.src = URL.createObjectURL(file);
						}
						catch (e) {
							try {
								var fileReader = new FileReader();
								fileReader.onload = function (event) {
									showPicture.onload = function(event) {
										Result.innerHTML="";
										JOB.DecodeImage(showPicture);
									};
									showPicture.src = event.target.result;
								};
								fileReader.readAsDataURL(file);
							}
							catch (e) {
								Result.innerHTML = "Neither createObjectURL or FileReader are supported";
							}
						}
					}
				};
			}
		</script>
	</body>
</html>
```

#残った課題
画像を撮影するときにスマホの姿勢によって画像の向きが横向きになったりすることがあり，正しくバーコードが読めないことがある．そこんとこの修正．