Googleフォームで学会演題を受付けている方にとって、登録された抄録を自動で整形し、統一された形式で出力できるととても便利です。
さらに、出力したPDFを演題投稿者本人にメールで送信して「内容や体裁に不備がないか確認してもらう」ことができれば、事務作業が大きく効率化できます。
今回はそのような演題受付業務を効率化するGoogle Apps Scriptの活用法をご紹介します。
今回できること
- Googleフォームで集めた演題情報を整形し、Googleドキュメントに自動で出力
- 同時にPDFも生成し、Googleドライブに保存
- 演題が修正された場合には、古いファイルを削除して最新の内容に自動で更新
- PDFファイルを演題登録者にメールで送信して、内容確認を依頼
事前準備
スプレッドシートの準備
Googleフォームと連携したスプレッドシートには、以下のような項目が必要です(例):
- 演題受付番号(例:
0001YIA
) - タイムスタンプ
- 演題タイトル、抄録本文
- 筆頭演者の氏名・ふりがな・所属・所属番号
- 共同演者の情報
- メールアドレス
- 送信ステータス(初期値は「未送信」)
- 送信時刻
送信ステータス
と送信時刻
の列は、PDFを送信済みかどうかを管理するために使います。
Googleドライブのフォルダを準備する
Googleドライブに専用の保存用フォルダを1つ作成してください。その中に、PDF専用のサブフォルダを自動で作成します。
フォルダの作り方:
- Googleドライブを開く
- 「+ 新規」→「フォルダ」を選び、たとえば「演題ファイル」と名前をつけて作成
- 作成したフォルダを開いて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件ずつ、文面も調整して、各個人で異なる情報は自動で取得して、送信できるようになります。
コメント