【第4部】Googleフォームの抄録を自動整形してDocumentとPDFを出力する方法【Google Apps Script活用】

仕事

Googleフォームで学会演題を受付けている方にとって、登録された抄録を自動で整形し、統一された形式で出力できるととても便利です。

さらに、出力したPDFを演題投稿者本人にメールで送信して「内容や体裁に不備がないか確認してもらう」ことができれば、事務作業が大きく効率化できます。

今回はそのような演題受付業務を効率化するGoogle Apps Scriptの活用法をご紹介します。


今回できること

  • Googleフォームで集めた演題情報を整形し、Googleドキュメントに自動で出力
  • 同時にPDFも生成し、Googleドライブに保存
  • 演題が修正された場合には、古いファイルを削除して最新の内容に自動で更新
  • PDFファイルを演題登録者にメールで送信して、内容確認を依頼

事前準備

スプレッドシートの準備

Googleフォームと連携したスプレッドシートには、以下のような項目が必要です(例):

  • 演題受付番号(例:0001YIA
  • タイムスタンプ
  • 演題タイトル、抄録本文
  • 筆頭演者の氏名・ふりがな・所属・所属番号
  • 共同演者の情報
  • メールアドレス
  • 送信ステータス(初期値は「未送信」)
  • 送信時刻

送信ステータス送信時刻の列は、PDFを送信済みかどうかを管理するために使います。


Googleドライブのフォルダを準備する

Googleドライブに専用の保存用フォルダを1つ作成してください。その中に、PDF専用のサブフォルダを自動で作成します。

フォルダの作り方:

  1. Googleドライブを開く
  2. 「+ 新規」→「フォルダ」を選び、たとえば「演題ファイル」と名前をつけて作成
  3. 作成したフォルダを開いてURLを確認します
    例:https://drive.google.com/drive/folders/YOUR_FOLDER_ID_HERE

このようなURLの場合はhttps://drive.google.com/drive/folders/の後のYOUR_FOLDER_ID_HEREがフォルダIDです。

後でコードの中に入れる場所があります。


スクリプトエディタでの設定方法

Googleスプレッドシートのメニューから「拡張機能」→「Apps Script」を選択し、以下のコードを貼り付けます。


スクリプト(GoogleドキュメントとPDFを生成する)

function generateAbstractDocs() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('フォームの回答 1');
const data = sheet.getDataRange().getValues();
const header = data[0];

const entryCol = header.findIndex(col => col.includes("演題受付番号"));
const timestampCol = header.findIndex(col => col.includes("タイムスタンプ"));
const titleCol = header.findIndex(col => col.includes("演題タイトル"));
const abstractCol = header.findIndex(col => col.includes("抄録の本文"));
const lastNameCol = header.findIndex(col => col.includes("筆頭演者(発表者)の姓"));
const firstNameCol = header.findIndex(col => col.includes("筆頭演者の名前"));
const lastKanaCol = header.findIndex(col => col.includes("姓のふりがな"));
const firstKanaCol = header.findIndex(col => col.includes("名前のふりがな"));
const affiliationCol = header.findIndex(col => col.includes("筆頭演者の所属機関名"));
const affilNumCol = header.findIndex(col => col.includes("筆頭演者") && col.includes("所属番号"));

const additionalAffils = [];
for (let i = 2; i <= 10; i++) {
const idx = header.findIndex(col => col.includes(`所属${i}の 所属機関名`));
if (idx !== -1) additionalAffils.push(idx);
}

// ▼ ここを自分のフォルダIDに置き換えてください ▼
const folderId = "YOUR_FOLDER_ID_HERE"; // ← ここを差し替え
const parentFolder = DriveApp.getFolderById(folderId);
const pdfFolder = parentFolder.getFoldersByName("PDF").hasNext()
? parentFolder.getFoldersByName("PDF").next()
: parentFolder.createFolder("PDF");

for (let i = 1; i < data.length; i++) {
const row = data[i];
const entryId = row[entryCol];
const timestamp = row[timestampCol];
if (!entryId || !timestamp) continue;

const formattedTimestamp = Utilities.formatDate(new Date(timestamp), Session.getScriptTimeZone(), 'yyyyMMdd_HHmmss');
const basePrefix = entryId.match(/^\d{4}/)?.[0];
const baseName = `${entryId}_abstract_${formattedTimestamp}`;

// 古いファイルを削除
deleteOldFiles(parentFolder, basePrefix);
deleteOldFiles(pdfFolder, basePrefix);

// 抄録情報を整形
const fullName = row[lastNameCol] + "\u00A0" + row[firstNameCol];
const kana = row[lastKanaCol] + "\u00A0" + row[firstKanaCol];
const nameWithKana = `〇${fullName}(${kana})`;

const affilNums = extractNumbers(row[affilNumCol] || "");
const affilNumsSup = affilNums.map(toSup).join(toSup(","));
const authorEntries = [`${nameWithKana}${affilNumsSup}`];

for (let j = 1; j <= 19; j++) {
const last = row[header.findIndex(col => col.includes(`共同演者${j}人目の姓`))];
const first = row[header.findIndex(col => col.includes(`共同演者${j}人目の名前`))];
const nums = extractNumbers(row[header.findIndex(col => col.includes(`共同演者${j}人目所属番号`))] || "");
if (last || first) authorEntries.push(`${last}\u00A0${first}${nums.map(toSup).join(toSup(","))}`);
}

const authorLine = authorEntries.join("、");
const affilText = [toSup("1") + " " + (row[affiliationCol] || "")]
.concat(additionalAffils.map((idx, i) => toSup((i + 2).toString()) + " " + (row[idx] || "")))
.filter(x => x.includes(" ")).join("\n");

// Googleドキュメント作成
const doc = DocumentApp.create(baseName);
const body = doc.getBody();
body.appendParagraph(`【${entryId}】`).setFontSize(16).setBold(true);
body.appendParagraph(" ").setFontSize(11);
body.appendParagraph(row[titleCol] || "").setFontSize(14).setBold(true);
body.appendParagraph(" ").setFontSize(11);
body.appendParagraph(authorLine).setFontSize(11).setBold(true);
body.appendParagraph(" ").setFontSize(11);
body.appendParagraph(affilText).setFontSize(11);
body.appendParagraph(" ").setFontSize(11);
body.appendParagraph(row[abstractCol] || "").setFontSize(11);
doc.saveAndClose();

const file = DriveApp.getFileById(doc.getId());
parentFolder.addFile(file);
DriveApp.getRootFolder().removeFile(file);

// PDF出力
const pdfBlob = file.getAs(MimeType.PDF).setName(baseName + ".pdf");
pdfFolder.createFile(pdfBlob);
}
}

function toSup(str) {
const superscript = {
'0': '⁰', '1': '¹', '2': '²', '3': '³', '4': '⁴',
'5': '⁵', '6': '⁶', '7': '⁷', '8': '⁸', '9': '⁹', ',': '˒'
};
return str.split('').map(c => superscript[c] || c).join('');
}

function extractNumbers(str) {
return (str.match(/\d+/g) || []);
}

function deleteOldFiles(folder, prefix) {
const files = folder.getFiles();
while (files.hasNext()) {
const file = files.next();
if (file.getName().startsWith(prefix)) {
file.setTrashed(true);
}
}
}

補足:上付き文字と演題番号の整形ルール

  • 所属番号は上付き文字で表示(例:〇山田 太郎¹)
  • 所属が複数ある場合は、カンマも上付き(例:¹˒²)
  • 演題番号は 0001YIA のように 4桁の通し番号+発表区分コードとしています

次回は…

このようにして生成されたPDFファイルを、演題登録者に自動でメール送信する方法を紹介します!
スプレッドシートにあるメールアドレスをもとに、1件ずつ、文面も調整して、各個人で異なる情報は自動で取得して、送信できるようになります。

コメント

タイトルとURLをコピーしました