[git]既に作成したディレクトリ名を変更

既にgit管理しているプロジェクトのディレクトリ名を変えたい時があります。そんな時は、gitコマンドでディレクトリ名を変更します。

対象ディレクトリへ移動(cd)し、

$ git mv before/ after/

ちなみに、既にmv before/ after/ をしていたり、finderなどから名前を変更してしまったりしていると、たとえ元に戻したとしてもエラーを吐きます。

$ mv before/ after/
$ git mv xxx_frontend yyy_frontend
$ fatal: bad source, source=before, destination=after/

初期段階だったので、gitごと消してgit initしました。

$ rm -rf .git/
$ mv before after
$ git init

参考

https://qiita.com/b4b4r07/items/cac4abd9ae66537e2833

[Nuxt.js]FusionCharts導入覚書 – Plain javascript編

Nuxt.jsでFusionChartsを入れたので、その覚書。

最初はVueフレームワークで導入を試みようとしたのですが、エラーが消えなかったので断念。VueとNuxtの超えられない壁を感じたのですが、githubにnuxtで動かしている人もいて、多分nuxtでも動かせるんだろうけど、私の知識不足により実現できていません

先輩の偉大な助けにより、vue dependencyをプラグイン化することで解決しました。ありがたやありがたや。

[Nuxt.js]FusionCharts導入覚書 – Vueフレームワーク編

ちなみに該当githubはこちら。プロジェクトをDLして動かしたら、選択できる地図が現れました。気になる方は動かしてみてください。

https://github.com/ICJIA/fusioncharts-test

Plain JavaScriptで動かす

というわけで、シンプルにPlain JavaScriptで動かすことにしました。

公式のGet Startedはこちらです。

https://www.fusioncharts.com/dev/getting-started/plain-javascript/your-first-chart-using-plain-javascript

前提条件

npmでfusionchartsをインストールする場合は、Node.jsのインストールが必要です。

インストール

CDN、ローカルファイル、npmのそれぞれでインストールする方法があります。

CDNの場合は、nuxt.config.jsのhead: { script: [ {src: ”,}, ],}部分にcdnソースを2つ、盛り込みましょう。


  head: {
    script: [
      // Step 1 - Include the fusioncharts core library
      {
        src: 'https://cdn.fusioncharts.com/fusioncharts/latest/fusioncharts.js',
      },
      // Step 2 - Include the fusion theme
      {
        src: 'https://cdn.fusioncharts.com/fusioncharts/latest/themes/fusioncharts.theme.fusion.js',
      },
    ],

はい。

私はnpmでインストールしました。

既存のnuxt.jsプロジェクトディレクトリで、npmを叩くだけ。先に最新のwebpackを入れておきます。

$ npm install webpack webpack-cli --save-dev
$ npm install fusioncharts

テンプレートを用意

pages>fusioncharts>index.vueを作成し、templateを用意。必要なdependencyをimportします。

<template>
  <div>
    <div id="chart-container">FusionCharts XT will load here!</div>
  </div>
</template>

<script>
// Include the core fusioncharts file from core
import FusionCharts from 'fusioncharts/core'
// Include the chart from viz folder
import Column2D from 'fusioncharts/viz/column2d'
// Include the fusion theme
import FusionTheme from 'fusioncharts/themes/es/fusioncharts.theme.fusion'
// Add the chart and theme as dependency
// E.g. FusionCharts.addDep(ChartType)
FusionCharts.addDep(Column2D)
FusionCharts.addDep(FusionTheme)
</script>

これで準備完了です。

データを用意

チャートを作成するにはデータが必要ですね。例として、ここでは世界の石油保有国とその量を示した2D chart を表現することにしましょう(公式に沿ってるだけですよ)。

<script></script>内importの下に、chartDataを定義します。x軸となるデータラベルをlabel、y軸となるデータバリューをvalueとして、オブジェクトにします。この辺はconstants/define.jsとかに書いてindex.vueでimportした方がnuxtっぽいけど。


// Preparing the chart data
const chartData = [
  {
    label: "Venezuela",
    value: "290"
  },
  {
    label: "Saudi",
    value: "260"
  },
  {
    label: "Canada",
    value: "180"
  },
  {
    label: "Iran",
    value: "140"
  },
  {
    label: "Russia",
    value: "115"
  },
  {
    label: "UAE",
    value: "100"
  },
  {
    label: "US",
    value: "30"
  },
  {
    label: "China",
    value: "30"
  }
];

チャート設定

データが用意できたので、いよいよ次はチャートの設定部分です。以下では公式の例をそのまま載せていますが、掲載されているオプションを元に色々カスタマイズができます。

// Create a JSON object to store the chart configurations
const chartConfig = {
  // チャートのタイプ
  type: "column2d",
  // Set the container object
  renderAt: "chart-container",
  // Specify the width of the chart
  width: "100%",
  // Specify the height of the chart
  height: "400",
  // Set the type of data
  dataFormat: "json",
  dataSource: {
    chart: {
      // Set the chart caption
      caption: "Countries With Most Oil Reserves [2017-18]",
      // Set the chart subcaption
      subCaption: "In MMbbl = One Million barrels",
      // Set the x-axis name
      xAxisName: "Country",
      // Set the y-axis name
      yAxisName: "Reserves (MMbbl)",
      numberSuffix: "K",
      // Set the theme for your chart
      theme: "fusion"
    },
    // Chart Data from Step 2
    data: chartData
  }
};

FusionChartsをmoutedで描画

最後に、FusionChartsをMountedします。

export default {
  mounted() {
    FusionCharts.ready(function () {
      console.log('passed FunctionChart')
      const fusioncharts = new FusionCharts(chartConfig)
      fusioncharts.render()
    })
    console.log('mounted!')
  },
}

npx nuxtした際の見た目

FusionCharts

[Nuxt.js]2.9以下 Google material icon(マテリアルアイコン)を使う

みんな大好きGoogle fontsではアイコンも利用できますね。私は少し前までfontawesomeを使用していたのですが、最近はちょっと非推奨の環境が増えてきたので、Google fontsのアイコンに鞍替えしました。

以下はnuxt.js2.9以下で実装するための手順です。それ以降はこちらの記事にまとめました。

[Nuxt.js]2.9以上 Google material icon(マテリアルアイコン)を使う

参考

https://developers.google.com/fonts/docs/material_icons

https://fonts.google.com/icons?selected=Material+Icons

nuxt.config.jsにリンクを追加

nuxt.config.jsにGoogle Material Iconsのリンクを追加します。


    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      {
        rel: 'stylesheet',
        href: 'https://fonts.googleapis.com/icon?family=Material+Icons',
      },
    ],

Google fontsサイトから使いたいアイコンを取得

下記サイトでアイコンを検索し、任意のアイコンを選択します。

https://fonts.google.com/icons?selected=Material+Icons

選択すると右側にコードが出ますので。

material icon

コピーします。

ページやコンポーネントにペースト

試しにheader.vueへ入れてみました。

header.vue
header.vue

表示結果

※左端のシェアアイコンです

[Nuxt.js]コンポーネントが表示されない(nuxt.config.jsにパスは通した?)

久しぶりにNuxtプロジェクト作成していつも通りコンポーネント作成したらコンポーネントが表示されなくて困ったので備忘録。Atomic Design(アトミックデザイン)など、componentsフォルダの中に分類のためのフォルダを作成している人のための記事です。

結論から言うと、

  • 新バージョンではコンポーネント名に内包されているフォルダ名をつけなければいけない(例:Atomsフォルダに入っているButton.vueコンポーネントは<AtomsButton/>と表記)
  • それが嫌ならnuxt.config.jsでパス通してね(下記に記載)

ということでした。

全部公式に載ってます。

https://nuxtjs.org/docs/directory-structure/components/#nested-directories

当方環境

  • @nuxt/cli v2.15.8
  • MacOS 12.1

nuxt.config.jsでパスを通す

以下公式からの引用ですが、

components/
  base/
      foo/
         CustomButton.vue

このような階層構造を持つコンポーネントをほかのコンポーネントやページで使用する際、コンポーネントの記載はこのようになります。

<BaseFooCustomButton />

でもいちいち上位フォルダの名前記載するの面倒ですよね。というわけパスを通します。
nuxt.config.jsを開き、componentsの箇所に追記します。

以下、Atomic Designを採用している場合の例です。

  components: {
    dirs: [
      '~/components',
      '~/components/atoms',
      '~/components/molecules',
      '~/components/organisms',
    ],
  },

これで、今まで(?)通り、コンポーネントのタグ名は<Button />のみでOKとなりました!

 

めでたしめでたし。

 

 

[備忘録]Google Analytics4(GA4)とUniversal Analytics(UA)の違い

免責:

  • GA4は本格的な導入が始まって間もないため、今後管理画面の内容や名称が変わる可能性があります。本記載は2022年5月22日現在のものに則ります。
  • 私は英語表記を採用しているため、画面の表記と文章内での名称が異なる場合があります。できるだ本文内では日本語での説明を行うように注力していますが、お含みおきください。

そもそもUAとGA4の違いってなんでしょう

 

レポート

  • UA…ABC分析
  • GA4…ライフサイクル(集客->エンゲージメント->収益化->維持)

UAはABC分析

ABC分析のABCは、Acquisition(集客)、Behavior(行動)、Conversion(コンバージョン)を指しています。その名の通り、集客がどれくらいできているか?その後のユーザ行動はどのようになっているか?そして顧客はどれくらいコンバージョンに至っているか?ということを分析するのが、ABC分析で、それがUniversal Analyticsのレポートの根幹を成していました。

GA4はその先まで分析

対して、GA4は一味違います。トップのメニューはよりシンプルになったように見えますが、レポートを開いてみると、リアルタイム分析の下に、ライフサイクルがAquisition(集客)、Engagement(エンゲージメント)、Monetization(収益化)、Retention(維持率)…と続木、そしてユーザー分析メニューが配置されています。

このことからもわかるように、GA4はコンバージョンをゴールとしていません。

購入や登録してもらったら終わり、ではなく、その後のユーザーのコミットメント、つまり顧客維持(LTV/Life Time Value)まで分析することで、より顧客満足度をアップさせられる施策を目指せるツールとなっているのです。

レポートの基本的な考え方

GA4のレポートは、全部で19ほど。大きくは

  • レポート
  • 広告

の2種類ですが、レポート内のカテゴリとしては先ほどのメニューからもわかるとおり、

  • ライフサイクル
  • ユーザー

の2つに分類できます。

 

標準レポート

UAのみならず、GA4でもディメンションと指標の意味を知っておくことはレポートを読み取る上で重要です。

GA4ではレポートの数が減っているように見えますが、実はディメンションの切り替えを行うことで、レポートの表示が変わる仕組みとなっています。

▼ユーザー>ユーザー属性>ユーザー属性の詳細

ディメンションには、国や地域、言語、年齢、性別などが用意されています。ですので、ディメンションを読み取る際には国別の、言語別の、年齢別の、性別ごとの、などと、ことばを置き換えて解釈していただくとわかりやすいと思います。

つまり、それぞれのレポートはデフォルトのディメンションが初期設定として採用されていますが、ユーザー属性の詳細からディメンションを切り替えることで、多様なレポートを参照することができるのです。

印のついたそれぞれの項目について、ディメンションを変更できる(イベントやコンバージョンなど、項目によっては単一のディメンションのみ採用可能)。

各項目において使用できるディメンションは公式にまとめられていますので、必要に応じて参照してみてください。

 

探索

アナリティクスに訪れるたびに期間やディメンションなど設定を切り替えたりするのは面倒ですから、UAではカスタムレポート機能を使用していた方も多いのではないでしょうか。

このUAで備わっていたカスタムレポートは、GA4でExplore(探索)にまとめられています。

探索
サイドメニュー3つめ

また探索のレポートには6つのテンプレートがあり、用途に応じてレポートを選べます。GA4の大きな強みの1つと言えると思います。

テンプレート
選べる6種類

 

セグメント

UAでは、レポートページ上部に大きく鎮座していたセグメントですが、

セグメント(UA)
セグメント(UA)

GA4ではこのセグメントが探索にまとめられています。

探索メニューから任意のフォーム形式を選ぶと、その中にセグメントのセクションを確認できます。

探索内のセグメント
探索内のセグメント

 

ユーザーの識別

GA4ではユーザーごとの分析が強化された、とはよく聞く話かもしれませんが、そのベースとなっているのが、ユーザーの識別方法です。

UAもGA4も、ユーザーの識別にはCookieを使用していますが、それはブラウザごとに保存されるため、同じユーザーの異なるブラウザでのアクセスは特定できない仕様です。User ID(ログインID)を利用したユーザー特定のみ、User ID ビューで実現していました。

一方、GA4では

  • User ID(ログインID)
  • Google シグナル
  • Cookie

という3つの要素を利用し、確度の高い順にユーザーを識別しているため、たとえば同じユーザーが複数のデバイスを使用してアクセスしている場合でも、同一ユーザーとして識別できるようになる可能性が高くなります。

一連の行動はそれぞれ別個のセッションとして記録されていますが、Google アナリティクス 4 なら、以下の 3 種類の識別情報を組み合わせることにより、デバイスをまたいだ 1 つのユーザー ジャーニーとして認識できます。

[GA4] レポート用識別子

設定方法

サイドメニューの設定>プロパティ>ユーザーの識別子を選択。

デフォルトの設定にもよるかもしれませんが、Googleシグナルは有効にされていない場合があります。

Reporting Identtity(ユーザーの識別子)
Reporting Identtity(ユーザーの識別子)

有効にするには、

  1. アナリティクスの管理画面にて管理をクリック
  2. Googleシグナルを有効するプロパティに移動
  3. [プロパティ]列で[データ設定]>[データ収集]に移動し、画像におけるGet startedボタンをクリック。
  4. 案内に沿って許可します。

どうやらプロパティごとに許可しないといけないみたいですね。

こちらを許可した場合、エンドユーザーに対しては、Googleとデータを共有している旨をプラポリなどに記しましょう。

Google シグナルのデータ収集設定下にも、確認ボタンが仕込まれています。

※GoogleシグナルをONにしても、Google広告をユーザーがOpt-inしていなければGoogleシグナルによるデータ収集は行えません

データモデル

GAがどのような形でデータが存在しているのかを理解することで、GA4とUAの違いが少し見えてきます。言ってしまえば、データ構造のことですね。

GA4では、イベントとパラメータというデータ構造を持っています。パラメータと聞くと分かりにくいかもしれませんが、イベントの「属性」と置き換えてしまっても構いません。

たとえば、add_payment_info(ユーザーの支払い情報)送信に関して持たせられるパラメータは、

  • 通貨(currency)
  • 金銭的価値(value)
  • クーポンコード(coupon)
  • 支払い方法(payment_type)
  • 購入商品(item)

などがあります。その中にさらにデータ構造を入れ込むこともできて、購入商品パラメータの中に商品idや商品名を含ませることもできます。こういう感じ↓

gtag("event", "add_payment_info", {
  currency: "USD",
  value: 7.77,
  coupon: "SUMMER_FUN",
  payment_type: "Credit Card",
  items: [
    {
      item_id: "SKU_12345",
      item_name: "Stan and Friends Tee",
      affiliation: "Google Merchandise Store",
      coupon: "SUMMER_FUN",
      currency: "USD",
      discount: 2.22,
      index: 0,
      item_brand: "Google",
      item_category: "Apparel",
      item_category2: "Adult",
      item_category3: "Shirts",
      item_category4: "Crew",
      item_category5: "Short sleeve",
      item_list_id: "related_products",
      item_list_name: "Related Products",
      item_variant: "green",
      location_id: "L_12345",
      price: 9.99,
      quantity: 1
    }
  ]
});

参照:Google アナリティクス 4 イベント

上の例では、イベント「add_payment_info」に対して、currency: “USD”,   value: 7.77,などのパラメータが紐づいています。

UAでは、ページビューやイベント、タイミングなどがヒット数として1階層の構造でカウントされていましたが、GA4ではこのヒットが「イベント」として複数の構造でまとめられるようになったということです。こうしたデータを「構造化データ」と言います。

参考:

https://www.seerinteractive.com/blog/ga4s-custom-dimension-parameter-data-model/

https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data?hl=ja

主なイベントには下記のようなものがあります。

  • first_visit … 初回訪問発生時に送信
  • session_start … セッション開始時に送信
  • page_view … ページビュー発生時
  • user_engagement … ユーザーがページフォーカスしていた時間を取得
  • scroll … 90%スクロール発生時に送信
  • click … 外部リンクへのクリック発生時に送信
  • view_search_result … サイト内検索が実行された時に送信

各イベントに対して、パラメータがあります。個々のイベント×パラメータ(key:value)の組み合わせは下記から確認できます。

https://support.google.com/analytics/answer/9234069?hl=ja

[GA4] 自動的に収集されるイベント
[GA4] 自動的に収集されるイベント
GA4ではExplore(探索)の機能が充実した分、自分好みのフィルタを自由にカスタマイズできるようになりました。そうなると、たとえば

  1. ディメンション:市区町村、国
  2. 指標:イベント数
  3. フィルタ:市区町村「Chiyoda City」
  4. フィルタ2:…
  5. フィルタ3:…

データ構造を理解していると、上記における1. ディメンション2. 指標は選択後、3.フィルタの設定を適切に行えるようになります。実際の探索画面に沿うとこんな感じです。

変数(緑色のボックス部分)から任意のディメンション、指標を登録し、右側のタブの設定(青ボックス部分)へドラッグ&ドロップ。

タブの設定の下を見ると、さらにフィルタ項目があるのが分かります。選択すると、現在タブの設定で登録されている変数が現れます。

任意の項目を選択し、フィルタ追加します。

上の例では市区町村だったので、

市区町村:Chiyoda City

のようにイベント名とプロパティの組み合わせがわかりやすかったですが、これが例えばイベント名をディメンションに設定した際、

イベント名:page_view

 

ユーザーを中長期的にわたって利用してくれるかどうかで分析するツール

 

 

活用方法

 

 

 

Webサイト制作 開発環境構築(Local環境)の流れ

備忘録です。

環境・前提

下記の環境・前提で開発しています。

  • Mac OS Monterey 12.1
  • Google Chrome  101.0.4951.54(Official Build) (arm64)
  • VSCode
  • Git管理

免責

仕事としてのWebサイト制作業務経験は多くなく、初学者です。

ドメイン名・サーバの確認

※ドメイン名とサーバがどのようにローカル環境に関わってくるか

 

ディレクトリを作成

デスクトップやDocumentsの中など、任意の場所にプロジェクトフォルダを作成します。

ネーミングは[projectname_frontend]などのようにしています。

例:

  • NPO法人Xのフロント作成の場合 … npox_frontend
  • 株式会社Yのバックエンド作成の場合 … y_backend

VSCodeで開く

VSCodeを起動し、⌘+Oから作成したプロジェクトフォルダを選択。

まだ何もない状態

New Fileを選択したらindex.htmlを作成します。

作成したhtml内で半角!を入力し、tabキーを押下するとhtmlのフォーマットが出てくるはずです。

あるいはHTMLテンプレートのBoilerplateを使っても良いと思います。

https://html5boilerplate.com/

langをjaに変更、title入力など現時点でわかる箇所を入力し、保存(⌘+S)。

全体の骨組みだけ先にマークアップしておきます。

VSCodeの場合は(htmlファイル内で)下記画像の状態でEnterキーを押すとHTMLに変換してくれます。省エネ!

VSCode成形前
VSCode成形後

ざっくり枠組み

<div id="wrap" class="st-wrap">
  <header id="header" class="sp-header">
     <h1 class="sp-header-logo">
       <a href="">
         <img src="" alt="" />
       </a>
     </h1>
     <p>test view</p>
  </header>

  <main id="container" class="sp-container"></main>

  <div class="sp-pagetop js-scrolltop" style="display: none"></div>

  <footer id="footer" class="sp-footer"></footer>
</div>
ちなみに保存したプロジェクトを素早くブラウザで確認するためのプラグインがあります。下記にまとめましたので、気になる方はぜひ。
https://nolyc.net/programming/vscode-live-server/

git管理

チームで分業したりしているとgit管理が必要になります。そうでなくても、一通りコーディングを終えた後にやっぱり前のバージョンに戻したい!という時があるので、私はどのプロジェクトでもgit管理はしておくことにしています。
会社ではbacklogを使用していますが、個人の方であればgithub(非公開)で管理するなどすれば良いと思います。
githubでのやり方は下記にまとめました。
https://nolyc.net/programming/git-connect-localtoremote/
上記記事ではSourceTreeを使用したGit管理方法を記載していますが、何もSourceTreeを使用せずともGit管理はVSCode上でできます。
https://code.visualstudio.com/docs/editor/versioncontrol
ここでは詳しい操作は省きますが、気になる方はググってみてください。
私が行った手順は以下の通りです。

VSCode上でのGit初期化

赤のアンダーラインが実行コードです

VSCode上でのGit初期化
VSCode上でのGit初期化
  1. VSCode上で⌘ + jでターミナルを開き
  2. cd ディレクトリパス(パスはVSCodeのフォルダ名を選択しOption + ⌘ +Cで取得できる)で該当ディレクトリに移動
  3. gitを初期化(git init)
  4. ブランチ名を変更(git branch)
  5. 現在のワークツリーの内容を全てindexに追加(git add *)
  6. コミット(git commit -m ‘first commit’)

GitHubへのリモートリポジトリへのpush方法は先ほど紹介した記事「[git]すでに作成したフォルダをgit管理してリモートと紐付け」にも書きました。

 

以降、随時アップデートしていく予定です。

 

 

WordPress プラグインYoast SEOのフィルター一覧

ただの備忘録。

WordPressのプラグインYoast SEOを使っているのですが、便利機能故にカスタマイズしにくい…と思いきや、ちょっとしたカスタマイズのためにTeam Yoastからフィルターが提供されていました。

フィルター一覧が載っています↓

http://hookr.io/plugins/yoast-seo/4.7.1/filters/

例えば私は別のプラグイン、Polylangで多言語サイトを作成しているのですが、固定ページには個別のtitle, descriptionを設定する箇所が管理画面下の方に用意してされているものの、

TOPページは管理画面サイドメニューのSEO>検索での見え方 から設定する仕様となっているため、英語ページのTOP(/en)でも日本語のmeta情報が採用されてしまうのです。

なので、上記サイトHOOKERからそれらしき単語を検索し(meta description関係はdescで出てくる)、欲しいフィルターかどうかチェックします。

以下、英語のトップページでのみtitleとmeta descriptionを変える場合のコードです。function.phpに記載しました。

function get_current_url() {
return(is_ssl()? 'https':'http').'://'.$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"];
}
$current_uri = get_current_url();
$home_uri = home_url('/');

// define the wpseo_title callback 
function my_en_title($title) {
return 'Welcome to the new environment|Hellow World!';
}
// define the wpseo_metadesc callback 
function filter_wpseo_metadesc( $trim ) { 
// make filter magic happen here... 
return 'Hellow world is a whole new approach to internet environments, one designed with the knowledge and know-how based on xxx years....'; 
}; 

if($current_uri == $home_uri.'en/' || $current_uri == $home_uri.'en') {
add_filter('wpseo_title', 'my_en_title');
add_filter( 'wpseo_metadesc', 'filter_wpseo_metadesc', 10, 1 ); 
}

参照: http://hookr.io/plugins/yoast-seo/4.7.1/filters/wpseo_metadesc/

 

 

GAS(Google Apps Script)でGoogleフォームを作成-選ばれた選択肢でメール送信先を変更

つい最近、Google Apps Script(以下GAS)というGoogleフォームを操作・カスタマイズできる機能を知ったので、備忘録。

GAS公式 https://developers.google.com/apps-script

今回やりたいこと

  • Googleフォームで用意した選択肢の内容によって、連絡先を変更する。

店舗をいくつか持っている会社があるとしましょう。ユーザからの問い合わせは選択された店舗の担当者に対応してもらいたいので、回答は店舗担当者に送信されるようにカスタマイズします。

 

そもそもGASとはなんぞ

公式の日本語ドキュメントがないので(ないよね?)アレルギー起こしている人も多いのではないかと思います。

冒頭ではGoogle Formを操作できる〜と申し上げましたが、公式を見ると他の色々なGoogleアプリを操作・カスタマイズできる機能のようですね。以下公式から引用。

Google Apps Script is a rapid application development platform that makes it fast and easy to create business applications that integrate with Google Workspace. You write code in modern JavaScript and have access to built-in libraries for favorite Google Workspace applications like Gmail, Calendar, Drive, and more. There’s nothing to install—we give you a code editor right in your browser, and your scripts run on Google’s servers.

GメールやGoogleカレンダー、ドライブ、といったGoogleのワークスペースと統合できるアプリ開発のプラットフォーム、とありますね。アプリのインストール不要、ブラウザ上で開発できて、みんな大好きJavaScriptで書けるので便利だよ!というわけです。

ご丁寧に、JavaScriptをご存じない方にはレッスンの案内もされています。

Codecademy https://www.codecademy.com/catalog/language/javascript

GASで具体的に何ができるのか

こちらも公式からの引用ですが、

  • GoogleドキュメントやGoogleスプレッドシート、Googleフォームでのオリジナルメニュー、ダイアログ、サイドバーの作成
  • Googleスプレッドシートでのオリジナル機能、マクロの作成
  • Webアプリの開発…Googleサイトでの独立した、あるいは埋め込みのアプリを作ってローンチ
  • Google AdSenseやGoogleカレンダー、Googleドライブ、Gメール、Googleマップとの連携
  • Google Workspace Marketplaceでアドオンを作成して公開

(意訳ですので参考程度でお願いします)

Googleフォームを作成

できることは色々あれど、今回はこのGASを使ってGoogleフォームをカスタマイズ。

  • Googleフォームで用意した選択肢の内容によって、連絡先を変更する

のですが、細分化するとこんな感じ

  1. Googleフォームの送信
  2. 回答者にメール送信
  3. 管理者側へのメール送信(回答の選択肢によってメール振り分け)

1,2まではGAS使わなくてもできますね。ただ、GASで用意しておくと同じフォームを秒で作れるようになるのでその点でも便利です。

Google フォームを作成

今回は、Googleフォームで受け取った値を元に色々操作してみたいと思いますので、一旦Googleフォームを作成して、返ってくる値を確かめてみようと思います。

フォームの内容はこんな感じ

冒頭のメールアドレス設定はタブの設定>回答>メールアドレスを収集するのトグルをONにするとできます。

GASを作成

フォームのメニュー3点リーダからスクリプトメニューを選択します。

初期状態はこれ。

function myFunction(e) {

}

function myFunction() {}とありますね。この{}の中に、カスタマイズしたい処理を書いていきます。myFunctionの部分は、好きな名前に変更してもOKです。今回はフォーム送信時に、連絡のない店舗へお知らせが届くようにしたいのでautoNotifyとします。

ここで()内に引数(今回はeとします。eventのeですが、なんでもOKです)を入れておくと、送信ボタンが押された際に、回答データがeに入り、あらかじめ用意されている関数onSubmitに渡されます。では実際にどのような内容が入っているのか確認してみましょう。

function autoNotify(e) {
  console.log(e)
}

Google フォームとGASを紐付け

Googleフォームの送信ボタンが押された時にスクリプトが実行されるよう、Google フォームとGASを紐付けします。

トリガーを設定

左のサイドメニュー時計アイコンのトリガーを選択し、

トリガーを追加。

トリガーの設定内容は以下の通り。設定できたら保存ボタンを押下します。

  • 実行する関数を選択 -> autoNotify
  • 実行するデプロイを選択 -> Head
  • イベントのソースを選択 -> フォームから
  • イベントの種類を選択 -> フォーム送信時
  • エラー通知設定 -> 毎日通知を受け取る

保存ボタンを押すとGASを承認するためのポップアップが出ますので、任意のGoogleアカウントを選択して次に進んでください。

これでトリガーが設定できました。

フォームが送信された時に入っている値を見てみよう

先ほどGASで書いたeの部分を確認してみましょう。実際にフォームを入力して、送信します。

フォーム画面右上の送信ボタンから、フォーム回答のための共有リンクを取得できます。

個人の別Googleアカウントからフォームを入力し、回答を送信しました。

GASの編集画面に戻り、左サイドメニュー実行数から、ログを確認できます。

オブジェクトで色々な値が取れていますね。

ちなみにconsole.logではなくLogger.logを使うとオブジェクトが返ってきていることしかわかりません。

console.logとLogger.logの使い分けは下の記事で詳しく解説していただいていました(謝謝!)。

GASでログ出力する2つの方法(Logger.logとconsole.log)の紹介と使い分け

回答データeはこのようにオブジェクトで返ってきているので、その返ってきている値から必要な情報を取り出し、処理を書きます。

今回は、

2. 回答者にメール送信
3. 管理者側へのメール送信(回答の選択肢によってメール振り分け)

をするので、回答者のメールアドレスと、選択された店舗情報を取り出す必要があります。回答内容は、Googleフォーム送信ボタンを押して返ってきた情報(e)の中の、response内、getItemResponses部分から取得できます(e.response.getItemResponses)。

{
toString: [Function],
authMode: {
toString: [Function: toString],
name: [Function: toString],
toJSON: [Function: toString],
ordinal: [Function: ordinal],
compareTo: [Function: compareTo],

// ちょっと省略

FULL: [Circular]
},
response: {
toString: [Function],
submit: [Function],
getId: [Function],
getTimestamp: [Function],
getRespondentEmail: [Function],
getItemResponses: [Function],
getGradableItemResponses: [Function],
getResponseForItem: [Function],
getGradableResponseForItem: [Function],
withItemResponse: [Function],
withItemGrade: [Function],
toPrefilledUrl: [Function],
getEditResponseUrl: [Function]
},

// だいぶ省略

triggerUid: '11025413'
}

フォームのレスポンス(e.response)から値を取り出すメソッドは公式にまとめられています。

https://developers.google.com/apps-script/reference/forms/form-response

getItemResponses() ItemResponse[] Gets all item responses contained in a form response, in the same order that the items appear in the form.

*フォームから受け取った値の全てのresponseアイテムを取得。値はフォームに入っているのと同様の順番で格納されています。

便利ですね〜

では早速これで値を取り出して見てみましょう。

GASのエディタに戻り、コードを書き換えます。

function autoNotify(e) {
  // 質問内容と回答を取得
  const itemResponse = e.response.getItemResponses();

  console.log(itemResponse);
}

スクリプトを保存(⌘+s)したら、フォームをもう一度送信し、GASメニューのログを確認します。

[{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
},
{
toString: [Function],
getScore: [Function],
setScore: [Function],
getFeedback: [Function],
getResponse: [Function],
getItem: [Function],
setFeedback: [Function]
}
]

配列の中に、それぞれの質問内容と回答の情報がオブジェクトに格納されています。

1つ目の質問・回答群から順に、配列の[0],[1],[2]…と格納されています。

最初のメールアドレスはフォーム作成時の設定内のトグルから作成したため、e.response.getItemResponsesとは別の場所に格納されています(後述)。

 

フォームで送信された質問と回答をログ出力してみる

では、空の配列 answerListを用意して、フォームから得られた質問と回答を入れてみましょう。

function autoNotify(e) {
  // 質問内容と回答を取得
  const itemResponse = e.response.getItemResponses();

  let answerList = [];

  // 答えた質問の数だけ繰り返し
  for (let i=0; i<itemResponse.length; i++) {
    let question = itemResponse[i].getItem().getTitle();
    let answer = itemResponse[i].getResponse();

    // 回答が入力されていればその値を、そうでなければ未回答を入れる
    answer ? answerList.push(question + ':' + answer)
      : answerList.push(question + ':' + '未回答');
  }
console.log(answerList);
}

スクリプトを保存(⌘+s)したら、フォームをもう一度送信し、GASメニューのログを確認します。

いい感じですね!

回答者にメール送信

フォームの回答ボタンが押されたらまず回答者にお礼メールを送りたいですね。

メール送信には、GmailAppというGメールアプリへのアクセスを提供するクラスを使用します。メソッド一覧はこちら

https://developers.google.com/apps-script/reference/gmail/gmail-app

上記リファレンスのsendEmailメソッドを使います。

まずはsubjectに件名、bodyに本文を定義します。

// 回答者にメール送信
// 回答者のメールアドレスを取得
const recipient = e.response.getRespondentEmail();
// 件名
const subject = '【回答者側 自動通知】お問い合わせ受付完了'
 
// 本文
const body = 'お問い合わせ受付が完了しました。\n\n内容にお間違いがないかご確認頂きますようお願いいたします。\n\n============\n' + answerList.join('\n') + '\n============\n';

その下にメソッドを追加

Gmailapp.sendEmail(recipient, subject, body);

実はGASのスクリプト内でGmailApp.sendEmail(...と入力すると文法も出てきてくれます。便利!

再びフォームを入力してデバッグしましょう!

スクリプトを保存(⌘+s)したら、フォームをもう一度送信し、スクリプトを保存(⌘+s)したら、入力したメールアドレスに届いたメールを確認します。

私はこの際、マニフェストを登録しておらず引っかかりました。ログから下記のようなエラーが出る場合は、マニフェストを追記しましょう。

やり方は以下の記事にまとめました。

GASのメール送信ができない…それならマニフェスト設定だ(appsscript.json)

のですが、調べていたらどうやらGmailAppよりMailAppの方が良さそうだ(配信速度が速いとかなんとか)…ということでメール送信部分は下記のようにしました。

Mailapp.sendEmail(recipient, subject, body);

管理者側へのメール送信(回答の選択肢によってメール振り分け)

さて、メール送信ができたところで今度は、設問1で選ばれた値によって、メール送信先の店舗先を変える…という機能を追加します。

設問1

設問1はitemResponse[0]に格納されています。getResponseメソッドでユーザーの選択した回答を取得し、mailGetShopに代入しました。Array.isArrayで、返ってきた値が配列であることを確認します。

// 管理者側へのメール送信(回答の選択肢によってメール振り分け)
let mailGetShop = itemResponse[0].getResponse();

// console.log(mailGetShop); // ['◯◯工務店','□□ハウス']
// console.log(Array.isArray(mailGetShop)); // true

今回は試しに'◯◯工務店','□□ハウス'の2店舗にチェックを入れて、テストをします。店舗ごとに、名前とメールアドレスがセットになったオブジェクトを作成し、配列shopListに代入。

今回は'◯◯工務店','□□ハウス'の2つがチェックされたので、hogehoge@hogemail.comunchi@hogemail.comにメールが配信されるようにしたいです。

let adminRecipient = [];
const shopList = [
{'◯◯工務店':'hogehoge@hogemail.com'},
{'□□ハウス':'unchi@hogemail.com'},
{'△△不動産':'nekoinochi@hogemail.com'}
];

shopListの数だけ、mailGetShopに入っている値を検索します。やり方はいろいろありましょうが…私は、indexOfで検索した値がなかった場合は-1を返すという特性を利用して、trueだった時にmailGetShopのメールアドレスをadminRecipientへpushする方法を取りました。


for (let i=0; i<shopList.length;i++) {
let target = Object.keys(shopList[i]).toString();
let result = mailGetShop.indexOf(target);
0<=result ? adminRecipient.push(Object.values(shopList[i])): null;
}
console.log(adminRecipient);  // ['hogehoge@hogemail.com','unchi@hogemail.com']

次は、adminRecipient内のアドレスへお知らせメールを送信します。これは先ほどの、回答者へのメール送信と同様ですね。

// 管理者へのメール文
let adminSubject = "【管理者側 自動通知】Googleフォームで新規回答を受け付けました";

let adminBody = 'Googleフォームで新規回答を受け付けました。\n\n内容を確認しご対応をお願いいたします。\n\n============\n'
+ answerList.join('\n')
+ '\n============\n';

//管理者にメール送信
MailApp.sendEmail(strAdminRecipient, adminSubject, adminBody);

ただ、先ほど得られたメールアドレスですが、配列の方がその後色々いじりやすいかと思っていたものの、MailApp.sendEmail()のrecipient(第一引数)はカンマ区切りのStringで渡さなければいけないらしいです。

recipient String the addresses of the recipients, separated by comma

https://developers.google.com/apps-script/reference/mail/mail-app#sendemailrecipient,-subject,-body

というわけで、MailApp.sendEmail(strAdminRecipient, adminSubject, adminBody);の前に以下を1行足します。

let strAdminRecipient = adminRecipient.join(',');

コードがかけたら例に倣って保存し、フォームを送信してデバッグ内容を確認します。

無事通りましたか?通ってくれ!

 

GASのメール送信ができない…それならマニフェスト設定だ(appsscript.json)

Googleフォームで入力されたメールアドレス宛にメールを自動送信するよう設定したく、GAS(Google Apps Script)でGmailApp.sendEmailを使おうとしたら権限がないって蹴られちゃいました。

Exception: スクリプトにはその操作を行う権限がありません。その操作を行うには「(https://www.googleapis.com/auth/gmail.send || https://www.googleapis.com/auth/gmail.compose || https://www.googleapis.com/auth/gmail.modify || https://mail.google.com/ || https://www.googleapis.com/auth/gmail.addons.current.action.compose)」権限が必要です。
at autoNotify(コード:27:12)

気を取り直してMailApp.sendEmailを使っても同じようなエラー。console.logでメールアドレス自体は取れているけれど、メール送信ができていません。

Exception: MailApp.sendEmail を呼び出す権限がありません。必要な権限: https://www.googleapis.com/auth/script.send_mail
at autoNotify(コード:28:11)

https://www.googleapis.com/auth/script.send_mail

You are receiving this error either because your input OAuth2 scope name is invalid or it refers to a newer scope that is outside the domain of this legacy API.

This API was built at a time when the scope name format was not yet standardized. This is no longer the case and all valid scope names (both old and new) are catalogued at https://developers.google.com/identity/protocols/oauth2/scopes. Use that webpage to lookup (manually) the scope name associated with the API you are trying to call and use it to craft your OAuth2 request.

APIが別ドメインなのでOAuth的に良くないよ、ってことなんでしょうか。

兎にも角にもGAS上でマニフェストを設定することにします。

appsscript.jsonにoauthScopesを追加

左サイドメニューのプロジェクトの設定>

【GAS】GmailAppに対するマニフェスト設定>全般設定から「appsscript.json」マニフェスト ファイルをエディタで表示するにチェックを入れます。

エディタに戻るとappscript.jsonが可視化されるようになりました。

ここにoauthScopesを加えて、指摘のあったapiを配列で追加します。

{
  "timeZone": "America/New_York",
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.send_mail",
    "https://www.googleapis.com/auth/gmail.modify"
  ],
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
}

公式のリファレンスはこちら

https://developers.google.com/apps-script/concepts/scopes

トリガーを削除・再登録

appsscript.jsonを保存し、トリガーを登録し直します。

削除したら再登録。再登録の際は認証を求められます。しめしめ。

ついでに、GmailAppよりMailAppの方が配信速度が速いらしいのでコードもMailApp.sendEmail()に書き換え。

再度フォームを送信して、ログと、メールが送信できているか確認。

メールOK!ログ、OK!

終わりに。

appsscript.jsonにoauthScopes追加したスコープなのですが、実はどのスコープを追加すればいいのか記載してある箇所がわからなかったため、参考サイトを元に追加しました。詳しい方いたら教えてください。私は上記2つのスコープ追加でメール送信ができました。

参考:

https://qiita.com/hisayuki/items/725110707d8abc8796d8

 

[git]すでに作成したフォルダをgit管理してリモートと紐付け

すでにローカル上で作成したプロジェクトのディレクトリをgit管理にし、リモートと紐付ける際の手順を自分用にまとめます。

環境・使用ツール

  • Mac OS Monterey 12.1
  • VSCode
  • Sourcetree
  • GitHub

まずはローカルフォルダをgit管理

Finderで対象ディレクトリを右クリックし、Optionボタンを押すと”directory name”のパス名をコピーという選択肢が現れて便利です。terminalの移動ではこれを選択し、cdの後に貼り付け。git initします。

cd /Users/neko/Documents/my_project
git init

SourceTreeの設定

New…からAdd Existing Local Repositoryを選び、対象ディレクトリを選択するだけ。

リモートリポジトリを設定

まずはGitHubでリポジトリを作成

GitHubでは右上の+ボタンからNew Repositoryを選択。

任意のリポジトリ名、設定を行い、Create repositoryボタンを押下。

ぶっちゃけここで出てくる画面にリモートリポジトリの追加方法は書いてありますが、必要なのはこの情報です。HTTPS方式でやるか、SSH方式でやるかでURLのやり方は変わります。

  • HTTPS
git remote add origin https://github.com/userName/test-repository.git
  • SSH
git remote add origin git@github.com:userName/test-repository.git

ディレクトリ内全てをステージング・ファイル追加

一旦、ディレクトリ内全てのファイルをステージングし、Git管理下にします。

git add *

そしてコミット

git commit -m 'first Commit.'

プッシュ

git push origin master

HTTPSとSSHのやり方を混ぜ込んでしまった人(俺)はここでエラーが出ます。URLを修正しましょう。

現在のリモートURLを確認し、新しいリモートURLに変更

git remote -v
git remote set-url origin {new url}

参考:gitのremote urlを変更する(レポジトリ移行時)

 

弊社ではSourcetreeを使っているため、この後はSourcetree BranchメニューにてRemoteにmasterブランチを作成し、ローカルにdevelopブランチ、feature/20220401_functionブランチを作成しました。

補足

GitHub使われている方、git push origin branch名すると、今はエラーが出ます。どうやら2021年8月にパスワード認証が廃止されたそう。パーソナルアクセストークンが必要です。

参考:GitHubでhttpsのパスワード認証が廃止された。Please use a personal access token instead.