LoginSignup
46
30

フリーランスエンジニア向けの稼働管理ツールを作ってみた【GAS】

Last updated at Posted at 2021-12-01

はじめまして、ますみです!

自己紹介.png

本記事は、「身の回りの困りごとを楽しく解決!【PR】Works Human Intelligence」のアドベントカレンダー2日目の記事です🎄

今回は、フリーランスエンジニア向けのツールとして**「自分の稼働時間を管理するツール」**の作り方と使い方を紹介します👨‍💻

私自身は4年ほどフリーランスとして、受託開発やコーチングなどの業務などをしてきましたが「実際にどれくらい各案件ごとに稼働しているのか?」を把握することは非常に重要だと3年目あたりから気付き始めました。

理由としては、以下の3つです。

  1. 自分の時給単価(市場価値)を正確に把握するため。
  2. 自分のタイムリソースのマネジメントのため。
  3. 請求業務を効率化するため。

これに関しては、また別の機会に詳しく書きたいと思います!

いずれにせよ、稼働時間の管理をしっかりとしていない(もしくは全く管理していない)フリーランスエンジニアは意外と周りにいます。そういった方々の働く上での課題を解決するために10分くらいで運用を始められる管理ツールについてまとめてみました!

最終成果物

前置きが長くなりましたが、最終成果物は以下のようなものです。
少し早めのクリスマスプレゼントとして、ソースコードも全て公開していきます🎁
成果物.jpeg

使用技術

今回使用する技術(ツール)は以下の3つです。

  1. Google Spreadsheet(スプレッドシート)
  2. Google Calendar(カレンダー)
  3. Google Apps Script(GAS、ガス)

カレンダーの勤怠情報を、GASを使って抽出して、スプレッドシートへ書き込むという流れになります。

GASが初めての方はこちらをご参照ください👍

稼働管理ツールの作り方と手順

それでは、具体的な手順を説明していきます✨

1. 新しいGoogleスプレッドシートの作成

まずGoogleへログインして、新しいスプレッドシートを作ります。
すごく雑学ですが、実はsheets.newというURLから新規シートを作成することもできます:sunglasses:

ss-editor-gas-work-calendar.001.jpeg

2. 大枠を作る

次に、取得してきた情報を見やすくするための大枠を作ります。
以下のように各行のタイトルと取得したい年月のシート名を入力します。

ss-editor-gas-work-calendar.002.jpeg

テンプレートのスプレッドシートを以下のURLに置いておきました👍

3. GASでコーディング

「拡張機能 > Apps Script」からGASの編集画面を開きます。

ss-editor-gas-work-calendar.003.jpeg

編集画面が開いたら以下の画像のように下記のコードを入力して、保存します。

ss-editor-gas-work-calendar.004.jpeg

コード.gs
const ORIGINAL_MENU_NAME                = '勤怠管理メニュー';
const GET_CALENDAR_INFO_BUTTON_NAME     = '勤怠情報の取得';
const TITLE_NAME                        = '勤怠管理';
const COMPLETE_PROCESS_TITLE            = '勤務表作成完了';
const COMPLETE_PROCESS_MESSAGE          = 'お仕事お疲れ様でした。';
const ERROR_TITLE                       = 'エラー発生';
const ERROR_MESSAGE_INVALID_SHEET_NAME  = '無効なシート名です。';
const ERROR_MESSAGE_NO_DATA             = '指定月の稼働は、現在ありません。';
const CALENDAR_ID                       = 'ENTER_HERE@group.calendar.google.com';

/*
【コーディング・ルール】
- 関数:キャメルケース(例:updateCalendarInfo())
- グローバル定数:コンスタントケース(例:ORIGINAL_MENU_NAME)
- その他の定数および変数:スネークケース(例:sheet_name)
*/

function onOpen() {
  // オリジナルメニューの追加
  // TIPS: スプレッドシートが開いたタイミングで実行されます。
  let ui = SpreadsheetApp.getUi();
  ui.createMenu(ORIGINAL_MENU_NAME)
    .addItem(GET_CALENDAR_INFO_BUTTON_NAME, 'updateCalendarInfo')
    .addToUi();
}

function updateCalendarInfo() {
  try{
    // タイトルの記入(B2セルへ記入)
    const sheet_name = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();  // 選択中(アクティブ)のシート名を取得
    const sheet_title = TITLE_NAME + "" + sheet_name + "";  // 月の情報をタイトルに付加
    SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(2,2).setValue(sheet_title); // B2セルへタイトルを書き込み

    // カレンダーの取得
    const my_calendar = CalendarApp.getCalendarById(CALENDAR_ID);

    // 選択月の初日と末日を取得
    const calc_month_string = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName() + "/01";
    const start_day = new Date(calc_month_string + " 00:00:00");
    const end_day = new Date(start_day.getFullYear(), start_day.getMonth()+1, 1);
    if (start_day == "Invalid Date"){ // エラー処理:シートの名前が正しくない時
      throw(ERROR_MESSAGE_INVALID_SHEET_NAME);
    }

    // 選択月のカレンダー内の情報(イベント)を取得
    const events = my_calendar.getEvents(start_day, end_day);
    if (events.length == 0){  // エラー処理:イベントが何もない時
      throw(ERROR_MESSAGE_NO_DATA);
    }

    // カレンダー情報の変数初期化
    let record_list = [];       // 予定情報の配列(List)
    let start_time;             // 開始時間(Date)
    let end_time;               // 終了時間(Date)
    let schedule_name = "";     // 予定の名前(String)
    let work_time = 0.0;        // 勤務時間(Float)
    let mtg_time = 0.0;         // MTG時間(Float)
    let work_time_total = 0.0;  // 勤務合計時間(Float)
    let mtg_time_total = 0.0;   // MTG合計時間(Float)

    for(const event of events){
      start_time = event.getStartTime();
      end_time = event.getEndTime();
      schedule_name = event.getTitle();
      work_time = (end_time - start_time)/(60*60*1000);
      if(schedule_name.match(/MTG/)){
        mtg_time = work_time;
      }else{
        mtg_time = 0;
      }

      const record = [
        start_time,
        end_time,
        schedule_name,
        work_time,
        mtg_time,
      ];
      record_list.push(record);

      work_time_total = work_time_total + work_time;
      mtg_time_total = mtg_time_total + mtg_time;
    }

    // レコードを書き込み
    // TIPS: 一行ずつ書き込むと時間がかかるため、このように配列に格納して、最後にまとめて記入します。
    SpreadsheetApp.getActiveSheet().getRange(4,2,record_list.length, record_list[0].length).setValues(record_list);

    // 合計時間(全勤務およびMTG)の書き込み
    SpreadsheetApp.getActiveSheet().getRange(2,5).setValue(work_time_total);  // B5セルへ勤務合計時間を書き込み
    SpreadsheetApp.getActiveSheet().getRange(2,6).setValue(mtg_time_total);   // B6セルへMTG合計時間を書き込み

    Browser.msgBox(COMPLETE_PROCESS_TITLE, COMPLETE_PROCESS_MESSAGE, Browser.Buttons.OK);
    return;
  }catch(e){
    Browser.msgBox(ERROR_TITLE, e, Browser.Buttons.OK);
    return;
  }
}

4. Google Calendarの作成とIDの埋め込み

以下の手順に沿って、勤怠管理用のGoogle Calendarを作成します。

ss-editor-gas-work-calendar.005.jpeg
ss-editor-gas-work-calendar.006.jpeg
ss-editor-gas-work-calendar.007.jpeg

カレンダーが作成できたら、設定画面から、以下の手順でカレンダーIDをコピーします。
ss-editor-gas-work-calendar.008.jpeg
ss-editor-gas-work-calendar.009.jpeg

コピーしたカレンダーIDをGASのCALENDAR_IDの箇所にペーストします。
ss-editor-gas-work-calendar.010.jpeg

5. テスト

テストは開発において非常に重要です。
一つずつしっかりと動作しているか確認していきましょう🚀

5-1. 成功パターン

以下のようにサンプルの予定を追加してみましょう。
ss-editor-gas-work-calendar.011.jpeg

「勤怠管理メニュー > 勤怠情報の取得」を選択して、処理を実行しましょう。
ss-editor-gas-work-calendar.012.jpeg

以下のように完了画面が表示されれば、成功です!
ss-editor-gas-work-calendar.013.jpeg

追加した予定がしっかりと反映されているかも確認しましょう。
今回のソースコードでは、MTGという文字列が入っている場合にMTG時間としても出力されるようにプログラミングされています。
ss-editor-gas-work-calendar.014.jpeg

5-2. 例外処理パターン①:シート名が無効な場合

続いて、シート名が2021/12のように年月のフォーマットではない場合のエラー処理を確認します。
以下のように無効な名前のシート名に変更して、失敗するか確認してみましょう。
ss-editor-gas-work-calendar.015.jpeg

以下のようにエラー発生の文章を表示されれば、OKです!
ss-editor-gas-work-calendar.016.jpeg

5-3. 例外処理パターン②:予定が何も入っていない場合

続いて、カレンダーに何も予定が入っていない場合のエラー処理を確認します。
以下のように、カレンダーに何も予定が入っていない年月のシート名に変更して、失敗するか確認してみましょう。
ss-editor-gas-work-calendar.017.jpeg

以下のようにエラー発生の文章を表示されれば、OKです!
ss-editor-gas-work-calendar.018.jpeg

おすすめの管理方法

最後に、この記事で紹介したスプレッドシートのおすすめの管理方法を紹介します。
私は、2021/tempというテンプレートのシートを作成して、それをコピーして、各月の勤務管理シート(月次勤怠表のようなもの)を生成するようにしています。
ss-editor-gas-work-calendar.019.jpeg

最後に

最後まで読んでくださり、ありがとうございました!
いかがだったでしょうか?

この記事を通して、少しでもあなたの学びに役立てば幸いです!

【仕事の相談はこちら】
お仕事の相談のある方は、下記のフォームよりお気軽にご相談ください。

問い合わせフォームはこちら

もしもメールでの問い合わせの方がよろしければ、下記のメールアドレスへご連絡ください。

info*galirage.com(*を@に変えてご送付ください)

🎁 「生成AI活用の無料相談券」もしくは「生成AIの社内ガイドライン(無料PDF)」を『公式LINE』で配布中 🎁
『生成AIを業務に活用したいけど、どうしたらいいかわからない』といった声を多くいただきます。

Galirageでは公式LINEにて、チャットやオンライン会議で「完全個別の生成AI活用無料相談会」を実施しております!
(期間限定で実施しているため、ご興味ある方はお早めに以下のLINE公式アカウントをご登録ください^^)
https://lin.ee/rvz6lMN

※ 予告なく、キャンペーンを終了する可能性がございますが、ご了承ください。

【業務内容】
具体的には、以下のお仕事を中心に受け付けております!(詳しくはこちら

  1. 受託開発(例:生成AIを使った社内システムの開発)
  2. コンサルティング(例:技術戦略のアドバイス)
  3. 講演(例:社内研修、イベント登壇)

※ 特に「生成AIを使ったシステム開発のご依頼」が多く、ご好評いただいております。

【これまでの相談事例】
以下のようなご相談が多くあります。

🔑 機密情報を漏洩させないための、生成AIのシステム構築をお願いしたい。
🤖 自社データを用いたFAQチャットボットの作り方を知りたい。
💡 ChatGPTを、自分たちの事業にどのように活かせるか、アドバイスやアイデアが欲しい。

おまけ

エンジニアの仲間(データサイエンティストも含む)を増やしたいため、公式LINEを始めました🎉

一緒に仕事をしてくれる方」「友だちとして仲良くしてくれる方」は、友だち追加をしていただけますと嬉しいです!(仲良くなった人たちを集めて、「ボードゲーム会」や「ハッカソン」や「もくもく会」もやりたいなと考えています😆)

とはいえ、みなさんにもメリットがないと申し訳ないので、特典を用意しました!

友だち追加後に、アンケートに回答してくれた方へ「エンジニア図鑑(職種20選)」のPDFをお送りします◎

46
30
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
30