育休中の勉強記録

イントロダクション

  • 約一か月(2/13~3/11)の育休で時間がとれたので、自分の時間を確保し、ルーティン化&記録してみました。
  • 成果や苦労したことを書こうと思います。

  • 学習目標

    • Kintoneカスタマイズスペシャリストの資格勉強
    • ランニング
    • ジム
    • 決算説明資料に関する知識習得
    • ブログ始める
    • その他の目標:決算説明資料の理解、読書

これらを、勉強記録をつけながらやりました。

自分の行動記録を日付・週番号・タスク内容・目標時間・実際使った時間を記入して集計しました

目標と成果(全体)

  • 目標値 vs 実績 vs 達成度
    • 合計
      • 目標: 102.8時間
      • 実績: 101.8時間
      • 達成度: 98.9%
      • 全体としては使いたい時間の確保はできました。項目別にバラつきがあるので下に詳細を書きます

おもな学習目標と成果(詳細)

いろいろ取り組みましたが、多く時間割いてたものをピックアップ

  • 目標値 vs 実績 vs 達成度
    • kintone資格勉強

      • 目標: 41.7時間
      • 実績: 39.5時間
      • 達成度: 94.9%
      • コメント:
        • 全体の大部分を締めます。最初はREST APIがわからなくて本当にやめようと思ったけど、とにかく時間を投入して理解しました。
        • CSVに頼らずkintoneのAPI連携で自動でのデータ取得や更新が可能になり、めちゃめちゃ仕事で業務効率化を進められています。諦めなくてよかった・・
    • 読書

      • 目標: 12.3時間
      • 実績: 18.4時間
      • 達成度: 149.1%
      • コメント:
        • 5冊くらい読みました。営業の仕組化や決算関係、あとJavaScriptに関する本。
        • 読書する目的がわった気がします。ビジネス書の読書は「いつか(または今)PJ化・todo化するかもしれない分野に関して、先に知識を入れて感覚つかむ」ことが目的かなと。なので読書は通常業務には不要だけど、今後の自分の幅を広げるためには絶対必要。だと考えました。今まで読書はなんとなく”知識つけるとうれしいし、なんか落ち着く”くらいでしたので。考え方が定まりました。
        • あと進撃の巨人が好きで、マダカスカル計画が気になってホロコーストに関する本も読みました。これはどっぷりはまり、家庭教師のトライYouTubeも見ながらめちゃ調べてました笑
    • ランニング

      • 目標: 100km(2/12~2/29)
      • 実績: 76km
      • 達成度: 76%
      • コメント:
        • 達成できず。原因は以下の二つです。
          • ①早起きできない日もあった(外以外にジムでも走ってますが、3月からナイト会員(22:00~06:00)にしたけど活用しきれず)
          • ②外出で、育児はまかせっきりになるので、”今外出していいのか”がわからなかった
        • 3月からは平日ジムで走る距離と、土日に外で走る量を調整して100km達成しました。12月にフルマラソンに出るので、走る量は確保できるよう工夫していきます。
    • ジム

      • 目標: 11.8時間
      • 実績: 8.5時間
      • 達成度: 72.3%
      • コメント:
        • 未達原因はランニングとほぼ同じです。ただナイト会員は続けます。理由は金額が安いこと以外に、他の利用者が少なくて快適なので。
    • ブログ

      • 目標: 3時間
      • 実績: 7時間
      • 達成度: 233.3%
      • コメント:
        • このブログの開始ですね。普段自分がやってることを自分で理解→言語化→発信のクセを強くしたいな。と思ったので始めました。
        • 達成度200%となってますが、初めて記事を書くとき「3時間でできるっしょ~」と考えてたら7時間かかった。というだけです笑 目標管理が時間数なのでハイ達成に見えてしまうだけです。
        • 初めて書いた記事↓
        • kintoneアソシエイトの振り返り - vchiha’s diary
    • 決算説明資料の勉強

      • 目標: 5.8時間
      • 実績: 7時間
      • 達成度: 120.0%
      • コメント:
        • これやってよかったなと思います。決算書の見方の入門書を読みながら自社と他社を見比べてました。同じような企業でも指標は違うんですよね。「企業によって状況違うんだから当たり前やん」と思うかもしれないけど、じゃあなんでそうなのか。を事実や数字で説明するの難しくないですか?イメージではそれっぽい言えるのですが。
        • 企業の利益、預金、投資内容はどんなものか、事業リスクは何か、収益の構造・ビジネスモデルは何か。を読んでいくと見えてきます。詳しい人からしたら私はまだまだだですが、かなり学びになりました。

成果

  • 一番は、kintoneからのデータ収集と更新が自動化できるスキルが付いたことです。
    • 成果より良い気づきもありました。やりたいことを見つければ多少辛くても続けられるし、達成の可能性があがる。私の場合kintoneですね。”本当にわからなくても机には座って考えよう。”で時間投入して突破できました。
  • ルーティンシートが良かったです。
    • 普段やるべきことがわかってても、後になってつい別の時間にしちゃう・・を減らせました。毎日ルーティンシートを眺めていると「あ、あれやってない。今これやってる場合じゃない」と気づきになります。1週間ごとに次週のルーティンを決めてるんですが、週が始まる前に俯瞰している自分が”今週の振り返りと来週やるべきこと”をスケジュール化しているので、その場に惑わされず進められました。
  • 気になったことを納得するまで調べられた。
    • 決算説明資料の理解促進は、集中的に調べることで身についた知識もあってよかった。これは思い切って時間捻出できる育休ならでは。GWなどまとまった時間ができるときはアリかも。

苦労したこと

  • 育児との両立
    • やっぱり両立が一番難しかったです。特にジムやランニングといった外出するものは影響大。「自分の時間は最大でも一日4時間まで」でルーティンを決めていましたが、ルーティンにあてはまるほど単純なことじゃなかったです。いつでも対応できる状態にする必要がありました。ここは反省です。自分の時間を細かい時間で区切られるようにする(タスク的な面でも、頭の切り替えでも)ことを目指します。

最後に

  • 今回は成果物よりもプロセスの記載でした。月一で更新していけたらなと思います。では

kintoneデータをスプレッドシートに自動抽出して、集計をスピーディにしました

きっかけ

kintoneカスタマイズスペシャリストの勉強(JavaScript)を始めて、APIトークンとかJSONの話が出てきました。 社内でkintoneとGASでAPI連携してデータの自取取得を試みている部署がるのを思い出して「そういえばこの前やり方聞かれたけど答えられなかったな。もし自動取得できたら自分の業務でもかなり時短できるぞ。ちょっと調べてみよう」と思ったのがきっかけです

効率化の必要性

昨年から社内ではSFAを入れ替えており、一定の進捗確認の仕組みはすでにありました。今年は「このレイアウトで数字を見たい」「商品別、販売経路別、●●別で見たい。あと時期の区切りは~~で」と様々な切り口から売上のトレンドを、しかもなるべくリアルタイムで知りたい。という要望も多くなりました。データはありますが、全部すぐに出せるほどは整ってないんですね。

対応するには、kintoneからcsv出力→スプレッドシートに張り付け→関数使って集計。が早いです。 早いんですが、複数の要望を定期的に集計。となるとまあまあ時間かかります。 ですので、自動化ができれば、私の負担が軽減されるだけでなく、営業組織全体の意思決定も迅速化にもつながるため、重要度も高いと考えました

API連携時のデータの抽出とスプレッドシート出力の実装手順

  1. GASを使用してkintoneからデータを500件ずつ取得
  2. 取得したデータをスプレッドシート出力用の配列変数に格納
  3. 500件ずつの処理を繰り返し、読み込み終了後にスプレッドシートに出力

※kintoneからのAPIでのレコード取得は一度に500件が限度なのでループさせます。
※今回は営業の案件のデータで、一つの案件に複数商品(テーブルデータ)が含まれます。商品が複数ある場合、商品ごとで一行ずつ出力します。テーブルデータ以外はコピーして同じデータを出力します
※一部の商品(テーブルデータ)は、あるフィールドのフラグごとに出力する列を分けます。
※値が空だと列数がずれることがあるので、空欄判定をして"-"を出力します。

実装コード(項目名をxxxにしたり、一部端折ってます)

function fetchRecords(startIndex, limitnumber) {
  const subdomain = "xxxxxxxxxx";
  var urlGetRecords = "https://" + subdomain + ".cybozu.com/k/v1/records.json";
  var appId = xx;
  var apiToken = 'xxxxxxxxxx';

//xxxにフィールドコード名を入れる
  var paramFields = "&fields=$id," + encodeURIComponent("xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,~~~~~~");
  var paramQuery = "&query=" + encodeURIComponent("xxx in (条件) and xxx in (条件 order by $id desc limit " + limitnumber + " offset " + startIndex);
  var url = urlGetRecords + "?app=" + appId + paramFields + paramQuery;
  var res = JSON.parse(UrlFetchApp.fetch(url, { "method": "get", "headers": { "X-Cybozu-API-Token": apiToken } }));
  return res.records;
}

function convertToJapanTime(utcTimeString) {
  var utcDate = new Date(utcTimeString);
  
  // 日本時間に変換
    var japanDate = Utilities.formatDate(utcDate, "GMT+9", "yyyy/MM/dd");
  return japanDate;
}

function getNameFromRecord(record, field) {
  var data = record[field].value;
  return (data && data.name) ? data.name : "-";
}

function myFunction4() {
  const limit = 500;
  const sheetOutpust = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('データ取得');

  // ヘッダ行の生成
  var headerRow = ["ヘッダ1", "ヘッダ2"~~~~~"ヘッダ43"];
  var output = [headerRow]; // ヘッダ行を先頭に追加

  var startIndex = 0;
  var records = fetchRecords(startIndex, limit);

  while (records.length > 0) {
      records.forEach(function (record) {
          var baseRow = [];
          //ユーザー選択や組織選択のデータ抽出の書き方
          baseRow.push(record["xxx"].value && record["xxx"].value.length > 0 ? record["xxx"].value[0].name : "-");
          //日付関連のデータ抽出の書き方
          baseRow.push(convertToJapanTime(record["xxx"].value) || "-");
          //文字列、レコード番号など単純にデータ取得する場合
          baseRow.push(record["xxx"].value || "-");
          
          //更新者・作成者の取得はこんな感じ
          var updaterName = getNameFromRecord(record, "xxx");
          var creatorName = getNameFromRecord(record, "xxx");
          baseRow.push(updaterName);
          baseRow.push(creatorName);

          //他データも同様に取得
          //...
          //...

          // テーブルの情報はあるフィールドの値で場合分けして取得。テーブルデータの数だけ行を追加。テーブルデータ以外の共通の値はコピーして同じ情報を持つ。関係ないテーブルデータは"-"とする。
          if (record["xxx"].value === "フラグ1") {
              // テーブルのデータが存在する場合
              if ("xxx" in record && record["xxx"].value.length > 0) {
                  record["テーブル"].value.forEach(function (tableData) {
                    if (tableData.value["xxx"].value !== null) {
                      var row = baseRow.slice(); // ベース行をコピー 
                      row.push(tableData.value["xxx"].value);
                      row.push(tableData.value["xxx"].value);
                      row.push(tableData.value["xxx"].value);
                      row.push(tableData.value["xxx"].value);
                      row.push(record["xxx"].value);
                      row.push("-", "-", "-", "-", "-"); 
                      row.push("-", "-", "-", "-", "-"); 
                      row.push("-", "-"); 
                      output.push(row);
                    }
                  });
              } else {
                // 何もしない
              }
          }
          else if (record["xxx"].value === "フラグ2") {
            // テーブル_1のデータが存在する場合
            if ("テーブル_1" in record && record["テーブル_1"].value.length > 0) {
                record["テーブル_1"].value.forEach(function (tableData) {
                  if (tableData.value["xxx"].value !== null) {
                    var row = baseRow.slice(); // ベース行をコピー 
                    row.push("-", "-", "-", "-", "-"); 
                    row.push(tableData.value["xxx"].value);
                    row.push(tableData.value["xxx"].value);
                    row.push(tableData.value["xxx"].value);
                    row.push(tableData.value["xxx"].value);
                    row.push(record["xxx"].value);
                    row.push("-", "-", "-", "-", "-"); 
                    row.push("-", "-"); 
                    output.push(row);
                  }
                });
            } else {
              //何もしない
            }
          }
          else if (record["xxx"].value === "フラグ3") {
            // テーブル_0のデータが存在する場合
            if ("テーブル_0" in record && record["テーブル_0"].value.length > 0) {
                record["テーブル_0"].value.forEach(function (tableData) {
                  if (tableData.value["xxx"].value !== null) {
                    var row = baseRow.slice(); // ベース行をコピー 
                    row.push("-", "-", "-", "-", "-");
                    row.push("-", "-", "-", "-", "-");
                    row.push(tableData.value["xxx"].value);
                    row.push(tableData.value["xxx"].value);
                    row.push(tableData.value["xxx"].value);
                    row.push(tableData.value["xxx"].value);
                    row.push(record["xxx"].value);
                    row.push("-", "-");
                    output.push(row);
                  }
                });
            } else {
              //何もしない
            }
          }
          
          //フラグ関係なく、テーブル情報があれば別行でデータ取得
          if ("テーブル_2" in record && record["テーブル_2"].value.length > 0) {
            record["テーブル_2"].value.forEach(function (tableData) {
              if (tableData.value["xxx"].value !== null) {
                var row = baseRow.slice(); // ベース行をコピー
                row.push("-", "-", "-", "-", "-");
                row.push("-", "-", "-", "-", "-");
                row.push("-", "-", "-", "-", "-");
                row.push(tableData.value["xxx"].value);
                row.push(tableData.value["xxx"].value);
                output.push(row);
              }
            });
          } else {
            //何もしない
          }
          
    });
    startIndex += limit;
    records = fetchRecords(startIndex, limit);
  }
    // スプレッドシートに出力
    //console.log(output);
    var sheetOutput = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('データ取得');
    sheetOutput.getRange(1, 1, output.length, output[0].length).setValues(output); // ヘッダ行も含めて出力
}

挑戦したことと学び

  • 課題・問題点

    • エラー原因がわからなくてメンタル消耗
      • JSON形式の扱い方、API連携の文法など、最初は意味不明でエラー原因読んでもさっぱりだったけど、原因わからなくてもとにかく仮説立て⇔実行でトライアンドエラー。心折れそうになったら時間をおいて再度チャレンジ。わからなくてもとりあえず机の上で60分は考える!で時間使ってなんとか乗り越えました。おかげで多少なり理解できた気がします笑
  • 学び

    • API連携のデータ取得はJSON形式が多い→汎用的な知識?
      • 一般的にAPI連携で取得するデータはJSON形式が多いようです。なのでこの経験によって、kintone以外のシステムも、API連携ができるものはデータを扱えるようになったと思います。
    • ユーザー選択は配列で格納、作成者や更新者は配列ではなく直接code・nameで保存
      • これはkintoneの仕様の話ですが、データ取得のコードの書き方で「ユーザー選択フィールドも作成者フィールドも同じっしょ」と思って実行したらエラーになりました。理由としては作成者や更新者は、配列ではないのでちょっと書き方が変わります。これは、kintoneの仕様として、複数ユーザが作成者になるはずが無い。と考えてデータ構造を変えているんだなと学びました。カスタマイズスペシャリスト資格でも必要そうな知識を知れて良かった。

成果と今後の展望

  • 成果

    • 営業組織の一部門の進捗管理を自動化し、業務効率化を達成しました。営業担当者別の契約・解約・提案状況を顧客名、商品や個数・金額など約50項目を自動で出力します。
  • 今後の展望

    • 個人:カスタマイズスペシャリストの勉強を続けるとともに、JSを用いたフィールドの表示・非表示切り替えに取り組みます。
    • 業務:営業企画で管理するものも自動化していきます。BIツールの活用や、他の業務領域での効率化を目指します。

最後に

読んでくださりありがとうございます。 現状はkintoneの記事が2つのみですが、kintone以外の取り組みも書いていこうと思いますので、よろしくお願いします。

kintoneアソシエイトの振り返り

kintoneアソシエイト受験の体験記を書いていきます 

受験のきっかけ

使いこなせたらかなり面白いことできるんじゃないか

会社でSFAとして使っていますが、SFA以外でも何か業務アプリ作れそうですし、JS使えれば自作プラグインが作れるとも知り、使いこなせればいろんな場面で業務効率化やデータ集計・分析が可能になるのでは?と思いました。

kintoneでの実績を何か形にしたい

kintone使いこなせたからすぐに営業実績に直結!とはならないので、着実にkintoneを使えるようになろう。そのためにどのくらい使えているかを見える形にしたいなとフワッと思っているところに、webでkintoneの資格を発見。

カスタマイズスペシャリストが難しそうだけど目指したいな、そのためにまずはkintoneアソシエイトらしい。と考えて受験することに決めました。

 

勉強前のkintoneスキル

実務歴

  • 7月~(6か月)

実務内容

  • 新規アプリ作成
  • 既存アプリ改修
  • メンテナンス(一括更新や設定など)

受験結果

  • 合否:合格
  • スコア :77%
  • 勉強時間:50hくらい

スコアレポート

受験計画

事前調べ

  • 合格基準の確認。
    • 70%以上の正答率が必要。相対評価ではないので、単純な理解度勝負
  • 教材の確認
    • あり。テキスト教材や動画教材、サンプル問題も豊富
  • 合格体験記をいくつか読む
    • ざっとテキスト&動画見る→サンプル問題を複数回解く。が主流。
    • 動画は約50コ
    • 合計で30h~50h程度勉強している人が多い。

試験日決め

  • 勉強開始は1月~
    • 個人的に12月まではダイエット期間(12月は月間200km走破)のため勉強開始は1月~に設定
  • 勉強時間を55hで見積もり
    • 教材のボリューム、サンプル問題のボリュームから必要な勉強時間を想定。結果約55h
  • 1月末を試験日にセット
    • 勉強時間を15h/週で設定
      • 教材もあり、勉強方法や勉強時間も、ググれば参考になる情報も多く計画が立てやすかった。

勉強方法

全体的な進め方

  • 動画見る&テキスト読む→サンプル問題を3周解く。

時間

  • 平日は1.5h、休日は3.5hで設計
  • 水曜日はジムに行きたいから休息日

 

覚え方の工夫

  •  間違えた問題、正解したけど不安に感じたものをピックアップし復習。サンプル問題は内容や結果をCSVでダウンロードできるので便利。スプシに張り付けて復習。覚えにくいところはセルに色付けて管理

復習


苦労したこと

  • 勉強時間の確保に苦戦
    • 朝4時台に起きて勉強するつもりが、仕事の残業で帰り遅い→寝るのが遅い→次の日朝起きれない→勉強時間を別日にリスケ。→休日でカバー。という負のスパイラルに。
    • 対策→スケジュールが狂ってもとにかく休日で頑張る!で気合でやりきる
  • 教材の問題にチャレンジ→急に不安に
    • サンプル問題は3周目になるとかなり時間短縮して終わるので、教材の問題(45問くらい)もやっておこと挑戦→全然正解せず。あわてて3周解く。かなり焦りました。。「本当は理解できてないからサンプル問題以外解けないんじゃないか」と急に不安に。

試験当日

試験前

不安になりながら早朝に起床し復習。スプシに溜めた不安な部分のみをひたすら復習。メンタルは「落ちたらまた受ければイイさ。自分の実力を発揮できればOK」で落ち着かせました。
再度問題を何十問も解くのはやめました。実際の試験で疲れて集中力なくなると思い。笑

試験

教材やサンプル問題と同じ問題はありませんでした。
ただ、しっかり復習しておけば解ける問題でした。教材の隅まで読んでないとわからない細かい問題は無かったかに思います。

試験後

試験終了時、ピアソンの受験会場ですぐ結果を紙でもらいました。結果は合格。受かってよかった。数時間後、メールでも結果通知が来ました。

試験を終えて

業務上意識しない範囲まで知って、kintoneを体系的に知れました。
一番良かったことは、会社でkintoneについて聞かれた際に「できる/できない」がさっと答えられ、仕事しやすくなったことです。

これは、勉強するごとに、kintoneの設計思想や仕様を自然と理解していけたんだと思ってます。

今後は、カスタマイズスペシャリストの勉強を始めます。WEB上の情報や記事が少なく不安ですがチャレンジしてみます。