Google Apps Sctriptで作る参考文献管理ツール① 登録編
割と短期スパンでこんにちは。もう3月末とか正気ですかね…?ついこの間、春休みが始まったばかりのような気すらするのにあっという間に新学期です。冬眠してたんですかね、あまりにも時間が過ぎ去ってる。
さて、今回は先日の備品登録・検索システムを眺めていて「これ、何か別の作る時に使えそうだよなあ」と思っていました。
そして後日、割とたくさんの文献を読む必要があり、いつものように1つ1つメモを書いていたんですが突然面倒になって…?!ということで、文献とWebサイトをメモとともに管理できるようにツールを作ります。
では、いってみよー
はじめに
動機
「参考情報の管理めんどくさーー!!」って思ったから。論文を見る・参考になるWebサイトを見るといった場合に大体メモを書くんですが、とっても面倒だし検索できないし、何より紙媒体で書いているので忘れる・失くすということが割と多発。
大体こんな感じで書いてる。
8cm四方くらいなのでそりゃあどっかこっか紛れるし消える。1文献1〜2枚で書くのですぐ増える。
つまり動機は「これ、どうにかならんかな…」です。
ここで書くこと
簡単にいうと、めっちゃくちゃ詰まった部分です。GASの仕様的な部分が割と大きいかもしれない。
ツールとしてやっている動作はすごく単純なものばかりで備忘録しなくてもいい程度のものなので。
ツールの動きと外装
外装
外装はこんな感じ。
PC画面でのレイアウト
スマホでのレイアウト
登録する対象は、論文やファイルとWebサイトに分けています。論文やファイルはファイルそのものとタイトル、発行年、キーワード、概要、新規性・特徴、メモ。Webページはサイト名・記事タイトル、URL、キーワード、概要、参考になった情報、メモ。それぞれはタブで切り替え可能になっていて、スワイプにも対応してます。
ツールの動き
論文・ファイル
ファイルを選択、タイトルや内容について入力して送信すると、Google Driveの指定のフォルダに選択されたファイルを「発行年_タイトル」とリネームしアップロード。入力した情報はSpreadSheetに格納・蓄積される、という流れになってます。
内部的には
- ユーザがフォームへファイルやテキストを入力する
- submit時にgoogle.script.runでGASの自作関数を呼び出し、this.parentNodeでフォームの情報を直接渡す
- ファイルが選択されていない場合は処理しない
- ファイルオブジェクトをcreatefileに渡し、アップロード処理をする
- ファイルをリネームし、URLを取得
- 入力データ含め全てをSpreadSheetに書き込む
- 通常のsubmit関数を実行し、データを送信
- doPost関数がindex.htmlを返却
という処理。誰でも思いつくようなのをぽちぽちやっただけって感じです。
Webサイト
入力したらそのまま何もせずにSpreadSheetに投げ込む。以上。マジでなんもしてないので書けることもない。ないったらないない。
詰まったところ
ファイルが送信できない
知識不足でやっちゃったやつ。
初めてちゃんと認識したんだけど、MIME typeの設定つまりHTTPヘッダーのcontent typeの設定がmultipart/form-dataだとファイルを送信できるらしい。ただ、ちょっと仕様が理解できなかったのとファイルエンコードのやり方がわからなかったので、google.script.runで引数としてformの要素を投げて処理する形で実装しました。
発覚の状況
実行したら「Exception: パラメータ(String)が DriveApp.Folder.createFile のメソッドのシグネチャと一致しません。」と出た。この時点では「あれ?POSTでフォームデータ送ってるはずなんだけど指定間違ったかな?」と思ってた。他の送信する要素が全部文字列だったので、そっちと指定ミスしたかなと。でもコードを確認したら指定のミスはしてなかった。もしかして…?とtypeofをかけてconsole.logでファイルオブジェクト(が送信されてると思い込んでた)要素を指定して出力。その結果、Stringだった。でもって受け取った情報をそのまま出力させたらファイル名だった。
なるほどね〜??
原因
GASがPOSTするときのデフォルトcontent-typeがapplication/jsonだったため、ファイル自体の送信ができていなかった。
解決策
通常、content-typeを明示的にmultipart/form-dataに設定するすることでBace64やバイナリデータとしてファイルを取得できる。取得したファイルのバイナリデータなどはエンコードすることでファイルオブジェクトとして利用できる。
が、私にはちょっとエンコード周りのやり方がわからなかったので、別の方法で解決しました。
<!-- index.html --> <form method="post" action="https://script.google.com/a/macros/xxx/xxx/exec" onsubmit="return false;"> <!-- 中略 --> <input type="submit" class="submitbutton" value="記録" onclick="google.script.run.postFile(this.parentNode);submit()"> </form>
// コード.gs function postFile(ref){ let fileObj=Folder.createFile(ref.file); }
submitした時にGASの関数であるpostFileを呼び出してフォームのデータをそのまま渡す、渡されたデータをGASが処理する、と言う形です。その後に通常のsubmit処理がされる(Webサイト側の送信後の処理とindex.htmlの返却はdoPostに書いてる)。
この方式にするとちゃんとオブジェクトが取得できる。
その後は普通にファイルオブジェクトとして扱えば良い。
PC用のCSSがスマホにも反映される
GASの仕様に翻弄されたもの。
なんとなーくmediaクエリ設定したのにちゃんと表示されなくない?とは思ってたので、試行錯誤してた分ものすごく気力を吸い取られた。
発覚の状況
基本的にモバイルファーストで作ることが多いんだけど、PCのレイアウト完全に変えたい時は通常、headにviewportの設定をかけて、mediaクエリでブレイクポイントを決めて変更する感じだと思います。
<head> <meta name=”viewport” content=”width=device-width, initial-scale=1> </head>
@media screen and (min-width:480px){ //スタイル }
これをちゃんとかけても、外装で紹介したようなPC・タブレットだとクリック可能な感じのメニュー部分が2カラム、スマホだとメニューも1カラムみたいにならず、片方のレイアウトを組むともう片方も変わる組む変わる組む変わる…の繰り返し。mediaクエリ、効いてなくない…?
原因
GASではmetaタグをindex.html内に記述しても解釈されない。
解決策
.gs内でHTMLサービスをevaluateした時にaddMetaTagでmetaタグを記述する。マジでこれだけ。ほんと、ドキュメントに最初目を通した時に気づかなかったのがすごく悲しい。
おわりに
単純な仕組みの割に知識不足で詰まったのでなんとも言えない気持ちになりました。まあ今度からは疑えるはずなので、はずなので…
次は検索部分についてかなあ…まだ作り途中だからちょっと先になるけど
では、このへんでー