Skip to content

Instantly share code, notes, and snippets.

@Tomokatsu-Sakamoto
Last active February 1, 2023 13:54
Show Gist options
  • Save Tomokatsu-Sakamoto/b627294f7fbe9b97f5a075609b8930b7 to your computer and use it in GitHub Desktop.
Save Tomokatsu-Sakamoto/b627294f7fbe9b97f5a075609b8930b7 to your computer and use it in GitHub Desktop.
"use strict"; // 変数の宣言を強要する
const OUTPUT_VALUE = 0; // 書き出す内容に単位を付けるか?(0:付ける、2:付けない)
// 関数 currentRowProc で使用する定数(あとで解析する)
const READ_COLUMNS = 10; // 読み込むセル数
const WRITE_COLUMN1 = 5; // 書き込むセル位置(平均キータイプ数)
const WRITE_COLUMN2 = 6; // 書き込むセル位置(ミスタイプ数)
// 関数 onFormSubmit で使用する定数(トリガー関数で解析する)
const SHEET_NAME = '解析結果'; // 解析結果を保存するシート名
/**
* トリガー関数として、フォームからの回答を解析して、別シートに解析結果とあわせて出力する
*
* ※この関数を実行するときには、SHEET_NAME の設定が必要で、当該シートが存在していなければならない
*/
function onFormSubmit(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
let outData = e.values; // フォームからの回答結果
let res = procRowData(outData); // 回答結果を解析する
outData.push(res[OUTPUT_VALUE + 1]);
outData.push(res[OUTPUT_VALUE + 2]);
sheet.appendRow(outData); // 出力用のシートに解析結果とともに書き出す
}
/**
* 現在のカーソル行を処理して、既定の列に解析結果を書き込む
*
* ※この関数を実行するときには、READ_COLUMNS、WRITE_COLUMN1、WRITE_COLUMN2 の設定が必要
*/
function currentRowProc() {
let sheet = SpreadsheetApp.getActiveSheet(); // 現在開いているスプレッドシート
let row = sheet.getActiveRange().getRow();
let datas = sheet.getRange(row, 1, 1, READ_COLUMNS).getValues();
let res = procRowData(datas[0]); // 現在のカーソル行を処理する
sheet.getRange(row, WRITE_COLUMN1).setValue(res[OUTPUT_VALUE + 1]);
sheet.getRange(row, WRITE_COLUMN2).setValue(res[OUTPUT_VALUE + 2]);
}
/**
* 与えられた行データをもとにして、
*  ① アップロードされた画像ファイルの抽出(どの列に URL が保存されているか?)
*  ② 画像ファイルの解析(与えられた画像ファイルをテキスト化)
*  ③ 解析結果から目的の値を抽出
*  ④ 処理結果を配列として戻す
*/
function procRowData(rowData) {
let fileId = ''; // 画像ファイルのファイル ID
// ① アップロードされた画像ファイルの抽出(どの列に URL が保存されているか?)
for (let i = 0; i < rowData.length; i++) {
let url = String(rowData[i]); // 文字列として扱うために変換
let idx = url.indexOf('https:'); // 'https:' が含まれていれば、アップロードされた画像ファイルと判断
if (idx != -1) {
let urlSplit = url.split(/id=/);
fileId = urlSplit[1];
break;
}
}
return imageReadOCR(fileId); // ②〜④ は別の関数で処理
}
/**
* 指定されたファイル ID の画像ファイルを読み込んで、「平均キータイプ数」「ミスタイプ数」を配列として戻す
*/
function imageReadOCR(id) {
/**
* 参考:
* GAS x OCRで画像から文字を簡単に抜き出す方法【Google Apps Scriptチュートリアル】 | ふくのブログ
* https://fuku-fk.com/gas-ocr-text/
*/
const image = Drive.Files.copy( // 画像ファイルを OCR 機能でテキスト化
{ title: "tmp" },
id,
{ "ocr": true, "ocrLanguage": "ja" }
);
const text = DocumentApp.openById(image.id).getBody().getText();
let strSplit = text.split(/\n| /); // テキスト化した内容を分割して配列に格納
console.log(strSplit, strSplit.length);
// 「平均キータイプ数」を探し出す
let val1, val2, val3, val4, idx;
for (let i = 0; i < strSplit.length; i++) { // 基準とする '回/秒' を探す
let base = String(strSplit[i]); // 文字列として扱うために変換
if (base.indexOf('回/秒') != -1) { // '回/秒' が含まれている要素を探す
idx = i;
break; // 見つけた!
}
}
console.log(idx);
val1 = String(strSplit[idx]);
val3 = val1.replace('回/秒', ''); // 単位を取り除く
if (val3 == '') { // 数値と単位が分かれているケース
val1 = strSplit[idx - 1] + val1;
val3 = strSplit[idx - 1];
}
// 「ミスタイプ数」を探し出す
do {
idx++; // 次の '回' を含む要素を探す
let base = String(strSplit[idx]); // 文字列として扱うために変換
if (base.indexOf('回') != -1) { // '回/秒' が含まれている要素を探す
break;
}
} while (idx < strSplit.length);
val2 = String(strSplit[idx]);
val4 = val2.replace('回', ''); // 単位を取り除く
if (val4 == '') { // 数値と単位が分かれているケース
val2 = strSplit[idx - 1] + val2;
val4 = strSplit[idx - 1];
}
let ret = [ // 処理結果を戻すための配列
idx, // 0: 基準となるミスタイプの位置
val1, // 1: [単位あり] 平均キータイプ数
val2, // 2: [単位あり] ミスタイプ数
val3, // 3: [単位なし] 平均キータイプ数
val4, // 4: [単位なし] ミスタイプ数
];
Drive.Files.remove(image.id); // 作業ファイルを削除
console.log(ret);
return ret; // 処理で得た値を戻す
}
/**
* 既存データをまとめて処理するための関数
*/
function sheetAllProc() {
let sheet = SpreadsheetApp.getActiveSheet(); // 現在開いているスプレッドシート
for (let i = 2; i < sheet.getMaxRows(); i++) {
sheet.setActiveSelection(sheet.getRange(i, 1));
currentRowProc();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment