Open Chat
本サイトはJSPS科研費(課題番号23K02737)の助成を受けた研究成果を基に作成されています。
AIチャットボットはパソコンでの使用を強くお勧めいたします。※タブレット端末でチャットが突然消えることがあります。

【コピペで完了】学校の長期休業「動静表」を自動化!Googleスプレッドシート×GASで働き方改革


はじめに

長期休業(夏休み・冬休み)の動静表作成、まだExcelで枠線を引いて作っていませんか?
「期間が変わるたびに行を削除したり追加したり…」「職員の異動があるたびに名簿を直して…」という作業は、今日で終わりにしましょう。

今回ご紹介するのは、**「学校ごとの設定(期間や名簿)を入力してボタンを押すだけ」**で、その学校専用の動静表システムを一発作成できるツールです。

プログラミング(GAS)を使いますが、難しい知識はゼロで大丈夫。コードをコピペするだけで、明日から自校で使えます。


このツールのすごいところ

  1. どんな期間でも一発作成:日付を入れるだけで、夏休み(40日)でも冬休み(14日)でも、土日色付け済みの表が自動生成されます。
  2. 自校仕様にカスタマイズ:職員数、職名、使う記号(○、出張、年休など)は、設定シートに書き込むだけで自由に反映されます。
  3. 全員同時入力&未入力チェック:クラウド(Googleスプレッドシート)なので、職員室全員で同時に入力でき、未入力者の数も自動表示されます。
  4. 個人印刷も対応:名前を選ぶだけで、自分だけの予定表が表示・印刷できる機能付き。

このシステムでできること

  1. 自動作成機能:開始日と終了日を入れるだけで、その日数分の表を一瞬で作成(夏休み40日でも冬休み14日でも自動対応)。
  2. 入力の効率化:記号(○、年休、出張など)はプルダウンで選択。記号の凡例も自動表示。
  3. 全員同時編集:1つのシートを全員で共有。他の先生の動きもリアルタイムで分かります。
  4. 未入力チェック:「あと何人入力していないか」が自動表示されるので、管理職のチェックが楽になります。
  5. 個人印刷対応:自分の名前を選ぶだけで、自分専用の予定表が表示され、そのまま印刷できます。

作成手順(所要時間:約15分)

ステップ1:土台となる「設定」シートを作る(ここが一番大事!)

まず、Googleスプレッドシートを新規作成し、シート名を**「設定」に変更します。
このシートが
「設計図」**になります。ご自身の学校に合わせて、以下の通りに入力してください。

  • B1セル:表のタイトル(例:令和7年度 冬季休業動静表)
  • B2〜B3セル:開始日と終了日を入力
  • A列・B列(5行目〜):職名と氏名を入力(人数分だけ縦にズラッと書いてください)
  • C列・D列(5行目〜):使いたい記号とその意味を入力(例:C列に「年」、D列に「年休」)

ポイントここに入力した内容が、そのままシステムに読み込まれます。「ウチの学校は『日直』がある」「『副校長』がいる」など、自由に書き換えてOKです!

ステップ2:プログラムをコピペする

設計図ができたら、それを組み立てる「エンジン」を搭載します。

  1. メニューの [拡張機能] > [Apps Script] をクリック。
  2. コード入力画面の文字を全て消す。
  3. 以下のコードをすべてコピーして貼り付ける。

codeJavaScript

/* 学校用動静表自動作成システム */
function onOpen() {
  SpreadsheetApp.getUi().createMenu('動静表メニュー')
    .addItem('動静表システム作成・更新', 'createScheduleSheets')
    .addToUi();
}

function createScheduleSheets() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const configSheet = ss.getSheetByName('設定');
  
  if (!configSheet) { Browser.msgBox("「設定」シートが見つかりません。"); return; }

  // 1. 設定データ取得
  const title = configSheet.getRange('B1').getValue();
  const startDate = new Date(configSheet.getRange('B2').getValue());
  const endDate = new Date(configSheet.getRange('B3').getValue());
  const lastRow = configSheet.getLastRow();
  
  if (lastRow < 5) { Browser.msgBox("職員データがありません(5行目以降に入力してください)"); return; }

  // 職員データ
  const staffDataRaw = configSheet.getRange(5, 1, lastRow - 4, 2).getValues();
  const validStaff = staffDataRaw.filter(row => row[1] !== ""); 
  const staffNames = validStaff.map(r => r[1]);
  
  // 記号と説明データの取得
  const symbolRows = configSheet.getRange("C5:C").getValues().filter(String).length;
  const symbolData = configSheet.getRange(5, 3, symbolRows, 2).getValues(); 
  const symbols = symbolData.map(r => r[0]); 
  
  // 日付リスト生成
  const dateList = [];
  for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
    dateList.push(new Date(d));
  }
  const daysCount = dateList.length;
  const dayMap = ['日', '月', '火', '水', '木', '金', '土'];

  // 2. 「行事マスタ」作成
  let eventSheet = ss.getSheetByName('行事マスタ');
  if (!eventSheet) {
    eventSheet = ss.insertSheet('行事マスタ');
    eventSheet.getRange("A1:C1").setValues([['日付', '曜日', '主な行事']]).setBackground('#d9d9d9').setFontWeight('bold');
  }
  const eventData = dateList.map(date => [date, dayMap[date.getDay()]]);
  eventSheet.getRange(2, 1, eventSheet.getLastRow(), 2).clearContent();
  eventSheet.getRange(2, 1, daysCount, 2).setValues(eventData);

  // 3. 「動静表」作成
  let mainSheet = ss.getSheetByName('動静表');
  if (mainSheet) {
    if(Browser.msgBox('警告', '動静表を再作成します。入力済みのデータは初期化されますがよろしいですか?', Browser.Buttons.OK_CANCEL) == 'cancel') return;
    mainSheet.clear();
  } else {
    mainSheet = ss.insertSheet('動静表');
  }

  // レイアウト構築
  const hRow = 3; // ヘッダー
  const dRow = 6; // データ開始
  
  mainSheet.getRange(1, 1).setValue(title).setFontSize(16).setFontWeight('bold');

  // 凡例表示(右上)
  let legendCol = 6; 
  mainSheet.getRange(1, legendCol).setValue("【記号凡例】:").setFontWeight("bold").setHorizontalAlignment("right");
  legendCol++;
  symbolData.forEach(item => {
    const text = item[0] + ":" + (item[1] ? item[1] : "");
    mainSheet.getRange(1, legendCol).setValue(text).setFontSize(9)
             .setBorder(true,true,true,true,true,true).setBackground('#f3f3f3').setHorizontalAlignment("center");
    legendCol++;
  });

  mainSheet.getRange(hRow, 1, 1, 4).setValues([["月", "日", "曜日", "主な行事"]]);
  mainSheet.getRange(dRow, 1).setFormula(`=ARRAYFORMULA(IF('行事マスタ'!A2:A${daysCount+1}="","",MONTH('行事マスタ'!A2:A${daysCount+1})))`);
  mainSheet.getRange(dRow, 2).setFormula(`=ARRAYFORMULA(IF('行事マスタ'!A2:A${daysCount+1}="","",DAY('行事マスタ'!A2:A${daysCount+1})))`);
  mainSheet.getRange(dRow, 3).setFormula(`=ARRAYFORMULA('行事マスタ'!B2:B${daysCount+1})`);
  mainSheet.getRange(dRow, 4).setFormula(`=ARRAYFORMULA('行事マスタ'!C2:C${daysCount+1})`);

  let col = 5;
  validStaff.forEach(staff => {
    // 未入力カウンター&枠組み
    const checkRange = `R[${dRow-2}]C[0]:R[${dRow + daysCount - 3}]C[0]`;
    mainSheet.getRange(2, col).setFormula(`=COUNTBLANK(${checkRange})&"未"`).setFontSize(8).setFontColor('red').setHorizontalAlignment('center');
    mainSheet.getRange(3, col).setValue(staff[0]).setBackground('#efefef').mergeAcross().setHorizontalAlignment('center');
    mainSheet.getRange(4, col).setValue(staff[1]).setFontWeight('bold').mergeAcross().setHorizontalAlignment('center');
    mainSheet.getRange(5, col).setValue('午前').setHorizontalAlignment('center').setBorder(true,true,true,true,true,true);
    mainSheet.getRange(5, col+1).setValue('午後').setHorizontalAlignment('center').setBorder(true,true,true,true,true,true);
    
    const rule = SpreadsheetApp.newDataValidation().requireValueInList(symbols, true).setAllowInvalid(false).build();
    mainSheet.getRange(dRow, col, daysCount, 2).setDataValidation(rule);
    mainSheet.getRange(3, col, daysCount+3, 2).setBorder(true,true,true,true,true,true);
    col += 2;
  });

  // デザイン調整(土日色付け等)
  mainSheet.getRange(hRow, 1, 3, 4).setBackground('#d9d9d9').setFontWeight('bold').setBorder(true,true,true,true,true,true).setHorizontalAlignment('center').setVerticalAlignment('middle');
  mainSheet.getRange(dRow, 1, daysCount, col-1).setBorder(true,true,true,true,true,true);
  
  const days = eventData.map(r => r[1]);
  for (let i = 0; i < days.length; i++) {
    const r = dRow + i;
    if (days[i] === '土') mainSheet.getRange(r, 1, 1, col-1).setBackground('#fce5cd');
    if (days[i] === '日') mainSheet.getRange(r, 1, 1, col-1).setBackground('#f9cb9c');
  }
  mainSheet.setColumnWidth(4, 150);
  mainSheet.setFrozenRows(5);
  mainSheet.setFrozenColumns(4);

  // 4. 「個人印刷」作成
  let printSheet = ss.getSheetByName('個人印刷');
  if (printSheet) printSheet.clear();
  else printSheet = ss.insertSheet('個人印刷');

  printSheet.getRange("A1").setValue("氏名選択:");
  const nameRule = SpreadsheetApp.newDataValidation().requireValueInList(staffNames, true).build();
  printSheet.getRange("B1").setDataValidation(nameRule).setValue(staffNames[0]).setBackground('#fff2cc').setBorder(true,true,true,true,true,true);
  printSheet.getRange("C1").setValue("←名前を選択(入力は「動静表」で)").setFontColor("gray");
  
  printSheet.getRange("A3").setValue(title + "(個人)").setFontSize(14).setFontWeight("bold");
  printSheet.getRange("A5:E5").setValues([["月", "日", "曜日", "主な行事", "動静"]]).setBackground('#d9d9d9').setFontWeight('bold').setHorizontalAlignment('center');
  
  printSheet.getRange(6, 1).setFormula(`=ARRAYFORMULA(MONTH('行事マスタ'!A2:A${daysCount+1}))`);
  printSheet.getRange(6, 2).setFormula(`=ARRAYFORMULA(DAY('行事マスタ'!A2:A${daysCount+1}))`);
  printSheet.getRange(6, 3).setFormula(`=ARRAYFORMULA('行事マスタ'!B2:B${daysCount+1})`);
  printSheet.getRange(6, 4).setFormula(`=ARRAYFORMULA('行事マスタ'!C2:C${daysCount+1})`);
  
  // 個人データ参照式
  const f = `=ARRAYFORMULA(OFFSET('動静表'!$A$6, 0, MATCH($B$1, '動静表'!$4:$4, 0)-1, ${daysCount}, 1) & " / " & OFFSET('動静表'!$A$6, 0, MATCH($B$1, '動静表'!$4:$4, 0), ${daysCount}, 1))`;
  printSheet.getRange(6, 5).setFormula(f);
  printSheet.getRange(6, 1, daysCount, 5).setBorder(true,true,true,true,true,true).setHorizontalAlignment('center');
  printSheet.setColumnWidth(4, 200); printSheet.setColumnWidth(5, 100);

  // 凡例表示(個人用)
  printSheet.getRange("G5").setValue("【記号凡例】").setFontWeight("bold");
  printSheet.getRange("G5:H5").merge().setBackground('#efefef').setBorder(true,true,true,true,true,true).setHorizontalAlignment('center');
  let pRow = 6;
  symbolData.forEach(item => {
    printSheet.getRange(pRow, 7).setValue(item[0]).setHorizontalAlignment('center').setBorder(true,true,true,true,true,true); 
    printSheet.getRange(pRow, 8).setValue(item[1]).setBorder(true,true,true,true,true,true); 
    pRow++;
  });
  printSheet.setColumnWidth(6, 20); printSheet.setColumnWidth(7, 40); printSheet.setColumnWidth(8, 120);

  Browser.msgBox("作成完了!\n・「動静表」「個人印刷」「行事マスタ」が作成されました。\n・土日は自動で色付けされています。");
}
  1. 上部のフロッピーアイコン(保存)を押して保存します。

ステップ3:作成ボタンを押す(※初回のみ認証あり)

いよいよ作成です。「設定」シートの内容が正しいことを確認して実行しましょう。

  1. スプレッドシートに戻り、ブラウザを更新(F5)します。
  2. メニューに追加された [動静表メニュー] > [動静表システム作成・更新] をクリック。
  3. 【重要】初回のみ「認証」が必要です
    • 「承認が必要です」→ [続行]
    • アカウントを選択
    • 「このアプリはGoogleによって確認されていません」→ 左下の [詳細] をクリック
    • [(安全ではないページ)に移動] をクリック(※自作プログラムなのでこう出ますが安全です)
    • [許可] をクリック

これで、設定シートの内容を読み取って、あなたの学校用の動静表が自動生成されます!

これでスクリプトが動き出し、シートが自動作成されます。
※2回目以降、この操作は不要です。


管理職(作成者)の運用ポイント

  • 行事の入力:自動で作られる「行事マスタ」シートに入力すると、全員の表や個人表に反映されます。
  • 配布前の最終確認:もし名簿や記号を間違えていても大丈夫。「設定」シートを直して、もう一度メニューから「作成・更新」を押せば、何度でも作り直せます。(※ただし、先生方が入力を始めた後に作り直すとデータが消えるので、配布前に確定させましょう!)
  • 共有方法:スプレッドシート右上の「共有」から、職員全員に「編集者」としてリンクを送れば完了です。

最後に

このツールの最大の利点は、「設定」シートの日付を変えて実行し直すだけで、次の夏休み用シートも一瞬で作れることです。
一度仕組みを作ってしまえば、ずっと使い続けられます。ぜひご活用ください!

会員の方々はダウンロードできます

プロ・グループ会員の方はこちら

一般会員の方はここから

このスプレッドシートは、認証作業が必要です。

コメント

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