astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

ABalytics.js + Google Analyticsを使ったABテスト

ABalytics.js + Google Analyticsを使ったABテスト

こんにちは。デザイン部でフロントエンドエンジニアをしているkitoです。
今回は、ABalytics.jsを使ったABテストについて書きたいと思います。

ABテストは、アプリケーションのUIを改善していく手がかりとしてとても有効です。
しかし、いざテスト実施しようとするとABテストツールは高額なものが多くコスト面で問題になることがあります。 今回は、ABalytics.js + Google Analyticsを使い無料でABテストが出来る方法をご紹介します。

ABalytics.jsとは

ABalytics.js?誤字じゃないの?と思われるかもしれませんが、誤字ではなくABalytics.jsというピュアなJavaScriptで書かれたオープンソースのABテストライブラリのことです。
htmlやJavaScriptの知識は必要になりますが、シンプルな使い方が可能でカスタマイズして使うこともできます。
ABalytics.jsは、Google Analyticsと連携しており、管理画面からテスト結果を確認するのでGoogle Analyticsの導入は必須です。

使い方

それでは、簡単な使い方からみていきましょう。 まずexpress.jsのインストールしましょう。Express.jsについてはこちらの記事をご覧ください。index.ejsを作成してabalytics.jsをロードします。あとで使うのでbootstrapをCDNからロードします。

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="/javascripts/ab_test.js"></script>

次にABalytics.jsの設定を記述するjsファイルを作成します。名前は任意で、ここではab_test.jsとします。 まずはじめにトラッキングコードを記述します。トラッキングコードは、Google Analyticsの管理画面からプロパティを作成すると自動的に生成されます。

f:id:astamuse:20170509111114p:plain

また、ビューの設定から登録完了画面などのABテストの結果を判定するための「目標」の設定が必要になります。

f:id:astamuse:20170509111122p:plain

次にab_test.jsに戻り、上で取得したトラッキングコードを最上部に記述します。

  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
 
  ga('create', 'UA-XXXXX-1', 'sample');
  ga('send', 'pageview');
  

ABalyticsを初期化して、ABテストの設定を記述していきます。 ABalytics.jsはブラウザのcookieで、A案とB案の出し分けを判断しています。 下記コードにある「variant」は、cookieの名前の末尾つき「ABalytics_variant」ということになります。こちらも任意の文字を設定してください。

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
 
ga('create', 'UA-XXXXX-1', 'sample');
ga('send', 'pageview');
  
ABalytics.init({
    variant: [
    {
        name: 'variant1_name',
        "lead": "variant1"
    },
    {
        name: 'variant2_name',
        "lead": "variant2"
    }
]},1);

ga('send', 'pageview');

variantのプロパティにあるname: 'variant1_name'は、 Google Analyticsの管理画面でテスト結果を検証する際に必要になります。こちらも任意の文字が設定できます。 "lead": "variant1"についてですが、"lead"はテスト対象となるhtmlのクラス名、右側の"variant1"はABalytics.jsで変更される要素です。 数字の1は、カスタムディメンションのindex番号になります。事前に以下の画面からカスタムディメンションを作成して設定します。ちなみに、カスタムディメンションの最大数は20個(プレミアム アカウントの場合は 200 個)です。

f:id:astamuse:20170509111126p:plain

index.ejsに戻り、bodyタグの中に以下を記述します。

<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">ABalytics.js example</a>
        </div>
    </div>
</nav>
<div class="container">
    <div class="starter-template">
        <div class="lead">
            This content will be replaced by ABalytics
        </div>
    </div>
</div>
<script type="text/javascript">
    window.onload = function () {
        ABalytics.applyHtml();
    };
</script>

ABalytics.jsによって、leadクラス以下のテキストが”variant1”と”variant2”かのどちらかに50%の確率でランダムに置き換わります。

<div class="lead">
    This content will be replaced by ABalytics
</div>

f:id:astamuse:20170509111134p:plain
まず確認すべきことは、chromeのデベロッパーツールを開きcookieのABalytics_variantのvalueが0だった場合variant1と表示されているか、valueが1だった場合variant2と表示されているかを確認しましょう。 cookieのvalueを変更してみて確認してみると良いと思います。

f:id:astamuse:20170509111151p:plain

Google Analyticsの管理画面で、ABテストの結果を確認します。設置してから1日ぐらい経ってから確認してみると良いでしょう。 場所は、少しわかりにくいですが、左のナビゲーションからカスタム→ユーザー定義と進みます。 右カラムに下記のように (not set)というリンクがあるのでクリックします。

f:id:astamuse:20170509111138p:plain

「セカンダリディメンション」というプルダウンメニューから、設定したカスタムディメンション名をクリックすると、ABテストの結果が表示されます。

f:id:astamuse:20170509111145p:plain

まとめ

ABalytics.jsは、テキストだけではなく画像やDOM要素などのテストもカスタマイズすれば可能です。
例えば、複雑なDOM要素のテストは、cookieの値によって表示と非表示を切りかえることで実現できます。スマホ画面とPC画面で別々のテストを実施することもできます。
導入をご検討してみてはいかがでしょう。 アスタミューゼでは、エンジニア・デザイナーを募集中です。ご興味のある方は遠慮なく採用サイトからご応募ください。お待ちしています。

マクロって言ったり、関数って言ったり、Functionだったりする何か。~Google Spread Sheet~

f:id:astamuse:20170425195524j:plain

お久しぶりでございます。scalaでバックエンドを開発しているaxtstar(@axtstart)でございます。

今回は、弊社の勉強会で話したネタを少しアレンジして記載します。

Google Spread Sheet

前々回のエントリーでExcelマクロには○○とか話をしていてナンですが、 業務では結構G Suite上のGoogle Spread Sheetなどを通して、仕様やデータを検証することも多く、 ローカルでのExcelでの作業より、G Suite上での作業の方が徐々に多くなってきています。

GoogleSpreadSheetを使った業務改善の話はこちらのエントリーでも 取り上げていますが、ここではもっとプリミティブなtips的なSpreadSheetの関数を紹介してみたいと思います。

ImportRange

こちらは他のSpreadSheetのデータを参照することができる関数です。

元のSheetを汚したくない場合などによく利用しています。 データのレンジ範囲をセル上に書けば、その範囲を全部拾って来ます。 Excelにはこのような、記載したセル以外に値を展開する関数というのはみたことがありませんでした。

例えば、ターゲットのスプレッドシートが下記のようなURLの場合、

https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxx/

f:id:astamuse:20170423112752p:plain

対象スプレッドシートのスプレッドキー(上記の場合:xxxxxxxxxxxxxxxxx)とシート・レンジを指定します。

=ImportRange("xxxxxxxxxxxxxxxxx","シート6!A1:F12")

1度だけ、アクセスの許可をする必要があります。

f:id:astamuse:20170422234819p:plain

なお、範囲上に展開する関数は、他のデータが展開先に存在する場合、エラーになります。

f:id:astamuse:20170422235031p:plain

すると、下記のように、C4:H15の範囲にシート6!A1:F12の内容が展開されます。

f:id:astamuse:20170423121306p:plain

上記を日直の持ち回り表というようにみていただくと、 追加でExcelライクな関数を設定することで次の日直を自動で算出することが可能です。 (この場合はGさんが次の日直) まぁ少し例としては微妙ですが。。。

f:id:astamuse:20170423130055p:plain

SpreadSheet API

またGoogleスプレッドシートはAPIからアクセスすることができますので、 メッセージに応じて、slack上に日直を表示するというような、botを作ることもできます。

f:id:astamuse:20170423235020p:plain

こちらにslack botの scala で実装したサンプルを上げておきました。

スクリプトエディタ

また、ほんのちょっとした処理をスクリプトエディタで記載するのもいい手です。

例えば、文字列をURLエンコードする関数は、存在しないのですが、 スクリプトエディタだと↓簡単に記述できます。

下記のようにスクリプトエディタを開き、

f:id:astamuse:20170425185221p:plain

こちらに

f:id:astamuse:20170425185442p:plain

下記を張り付ければOK。

function encode(value) {
  return encodeURIComponent(value);
}

function decode(value) {
  return decodeURIComponent(value);
}

スクリプトエディタ*1は、JavaScript1.6をBaseにつくられており、 Array、Date、RegExp、MathといったObjectが利用可能です。 ただし、Googleのサーバで動作するため、Window APIは利用不可。

ImportXML

URLを指定することでXMLをパースして取得することができます。 実際にはHTMLでもかなり取得してくれます。 ただ、認証など入力することはできないため、ログインが必要なデータは取得できないようです。

例えば下記で、本ブログのキーワードタグを取得できます。

=ImportXML("http://lab.astamuse.co.jp/","//ul[@class='hatena-urllist']")

f:id:astamuse:20170423181907p:plain

こちらを先ほど作成したencode関数と下記関数で、キーワードタグに対して

 
関数用途
Transpose 転置
=Transpose(ImportXML("http://lab.astamuse.co.jp/","//ul[@class='hatena-urllist']"))
REGEXEXTRACT 正規表現によるデータの取得
=REGEXEXTRACT(A2,"( \([1-9]+\))")
Substitute 文字列置換
=Substitute(A2,B2,"")
encode URLエンコード。スクリプトエディタで作成した関数
=encode(C3)

URLを↓下記のように生成できます。

f:id:astamuse:20170423190011p:plain

さらに生成したURLにImportXmlを適用し、タイトルを取得してみました↓

f:id:astamuse:20170423193443p:plain

ただあまり処理数が多いと以下のようにエラーになります。

f:id:astamuse:20170423231554p:plain

今回はこの辺で。

最後に

いかがでしたか?ローカルで動作するExcelとは違った機能を紹介してみました。

またここでは紹介はしなかったですが、Gmail等のGoogleのサービスへの連携も比較的簡単にできるため重宝しています。

アスタミューゼでは現在、エンジニア・デザイナーを募集中です。 興味のある方はぜひ 採用サイト からご応募ください。

参考にしたサイトなど

Google スプレッドシートの関数リスト

Google Apps Script ドキュメント

Google API Client Libraries JAVA編

*1:正確にはGoogle Apps Script

複数サイトを運営する上でのフロントエンドのちょっとしたノウハウ

こんにちは。 デザイン部でフロントエンドエンジニアをしているkitoです。
弊社は、転職ナビというドメインの異なるサイトを300サイト以上運営しています。レアケースであるとは思いますが、運営上のノウハウの一部をご紹介したいと思います。
今回はスタイルシートを量産するフローを書かせて頂きます。

jsonファイルから複数のcssを作成する

転職ナビは数百サイトありますが、ひとつのアプリケーションでほぼ同じテンプレートが使われ、静的なアセットは原則として共有されています。
しかし、それぞれのサイトに固有のスタイルシートを適用しなければならない場面がどうしてもあります。 弊社では、サイト固有のスタイルシートをjsonファイルのデータから量産することでその問題に対処していますが、そのjsonの元ファイルはコミュニケーション上使い勝手のよいcsvファイルでデータが管理されています。 従って、まずcsvファイルをjsonに変換します。

下記のようなcsvファイル、prefecture.csvがあったとします。これをprefecture.jsonに変換します。
domainは文字通りサイトドメインで、R,G,BはそれぞれサイトカラーのRGBの値です。

domain R G B name
hokkaido 0 11 111 北海道
aomori 32 66 66 青森
iwate 4 15 43 岩手
miyagi 55 180 8 宮城
akita 10 20 30 秋田
yamagata 90 0 0 山形
fukushima 10 10 10 福島
ibaraki 40 30 12 茨城
tochigi 68 11 54 栃木
gunma 250 100 40 群馬

csvtojsonというモジュールを使いたいので下記コマンドでインストールしてください。

npm install --save csvtojson

以下作成していくディレクトリは以下のようになります。

├── package.json
├── converter.js
├── template.js
├── prefecture.json
├── Gruntfile.js
├── dev
│   └── unique
│        ├── akita
│        │   └── styles
│        │        ├── sp_unique.scss
│        │        └── unique.scss
│        ├── aomori
│        │   └── styles
│        │        ├── sp_unique.scss
│        │        └── unique.scss
│        以下略
├── tasks
│   └── prefecture_styles.js
├── unique
│   ├── akita
│   │   └── styles
│   │        ├── sp_unique.css
│   │        └── unique.css
│   ├── aomori
│   │   └── styles
│   │        ├── sp_unique.css
│   │        └── unique.css
│   以下略
└── node_modules

converter.jsを作成して下記のコードを実行してください。csvファイルをjsonに変換します。

var fs = require('fs');
var csv = require("csvtojson");
var converter = csv({});
var csvFile = './prefecture.csv';
var jsonFile = './prefecture.json';

fs.createReadStream(csvFile).pipe(converter);
converter.on("end_parsed", function (jsonArray) { 
  fs.writeFile( jsonFile, JSON.stringify( jsonArray, null, '    ' ),'utf8');
  console.log('csv to json is complete !');
});

下記のようなprefecture.jsonが作成されたと思います。

[
    {
        "domain": "hokkaido",
        "R": 0,
        "G": 11,
        "B": 111,
        "name": "北海道"
    },
    {
        "domain": "aomori",
        "R": 32,
        "G": 66,
        "B": 66,
        "name": "青森"
    },
    以下略
]

弊社はsassを導入しているのでprefecture.jsonからgruntを通じてsassを作成し、それを元にcssを作成するフローになります。
二度手間のように感じるかもしれませんが、他のsassをimportしたり、ユニークなカラーを変数に設定できたりと、使い勝手がよいからです。

sassを量産するprefecture_styles.jsというGruntプラグインを作成します。
tasksディレクトリに、prefecture_styles.jsを作成して下記コードを記入してください。prefecture.jsonと後で作成するテンプレートを元にsassを量産します。

module.exports = function(grunt) {
    var fs = require('fs');
    var json = grunt.config('prefecture_styles').json;
    var data = JSON.parse(fs.readFileSync(json, 'utf8'));

    grunt.registerTask('prefecture_styles', 'task', function() {

        var dir = grunt.config('prefecture_styles').dir;
        var stylesDir = grunt.config('prefecture_styles').stylesDir;
        var name = grunt.config('prefecture_styles').name;
        var template = grunt.config('prefecture_styles').template;
        var sasstmp = require(template);

        Object.keys(data).forEach(function(key, index) {
            var domain = data[key]["domain"]
            var R = data[key]["R"]
            var G = data[key]["G"]
            var B = data[key]["B"]
            var sassObj = new sasstmp(R,G,B);

            name.forEach(function(val, index) {
                grunt.file.write(dir + domain + stylesDir + name[0],sassObj.pc , function(err) {});
                grunt.file.write(dir + domain + stylesDir + name[1],sassObj.sp , function(err) {});
            });
        });
        console.log('generated sass!')
    });
};

grunt.config('prefecture_styles').〇〇で、Gruntfile.jsから設定を読み込んでいます。
jsonはスタイルシートの値が入ったjsonファイル(ここではprefecture.json)、dirはベースとなるディレクトリ、stylesDirはsassが入るディレクトリ名、nameはsassの名前、templateは量産するsassのテンプレートになります。
Object.keys(data).forEachでprefecture.jsonから値を取り出して、それぞれ変数に代入し、grunt.file.writeでpc用とsp用のsassを作成&記述しています。

元となるテンプレート、template.jsを作成します。

var sasstmp = function(R,G,B) {
  this.pc = '@charset "utf-8";\n' + '$uniqueSiteColor:rgb(' + R + ',' + G + ',' + B + ');\n' + '.siteColor{ background-color:rgb(' + R + ',' + G + ',' + B + ')};\n';
  this.sp = '@charset "utf-8";\n' + '$uniqueSiteColor:rgb(' + R + ',' + G + ',' + B + ');\n' + '.siteColor{ background-color:rgb(' + R + ',' + G + ',' + B + ')};\n';
}

module.exports = sasstmp;

次にGruntfile.jsの設定に移ります。
(gruntをインストールについては割愛します。)
下記gruntプラグインをインストールしてください。

npm install grunt-contrib-compass --save-dev
npm install grunt-contrib-clean --save-dev

grunt-contrib-compassはsassをcssにコンパイルするプラグイン、grunt-contrib-cleanはファイルを削除をするプラグインです。

Gruntfile.jsを下記のように記述してください。 実際に業務で使うときのGruntfile.jsは、sassの変更をwatchしたりwebpackを使ってjsをバンドルしたりと他のタスクを追加することになりますが、今回は必要最小限の構成にしてあります。

var grunt = require('grunt');

module.exports = function(grunt) {

    grunt.initConfig({

        pkg: grunt.file.readJSON('package.json'),

        clean: {
            build: {
                src: [
                    '.sass-cache',
                    './dev/build/unique/'
                ]
            },
            release: {
                src: [
                    '.sass-cache',
                    './unique/**/styles/*.css'
                ]
            }
        },

        prefecture_styles: {
            dir: './dev/unique/',
            json: './prefecture.json',
            template: '../template.js',
            stylesDir: '/styles/',
            name: [
                'unique.scss',
                'sp_unique.scss'
            ]
        },

        compass: {
            prod: {
                options: {
                    sassDir: './dev/unique',
                    cssDir: './unique',
                    environment: 'production'
                }
            }
        }
    });


    //ローカルにあるprefecture_styles.jsを読み込んでいる。
    grunt.task.loadTasks('tasks');

    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-compass');
    grunt.loadNpmTasks('grunt-contrib-clean');

    grunt.registerTask('build', ['clean:release', ' prefecture_styles', 'compass:prod']);

};

grunt.initConfig({})の中に prefecture_styles.jsの設定を記述します。
compass:{}には prefecture_styles.js作成したsassをcssにコンパイルする設定を行っています。
grunt.task.loadTasks('tasks')は、コメントにあるように先程作成したprefecture_styles.jsをロードしています。
Nodeモジュールとして公開しておけば、grunt.loadNpmTasks(' prefecture_styles')で読み込めるでしょう。

下記でGruntを実行してください。

grunt build

uniqueディレクトリが作成され、それぞれのドメイン名以下に、unique.css、sp_unique.cssが作成されていれば成功です。
cssを量産するタスクを実施する際に、本稿を参考にして頂ければ幸いです。今回は以上になります。

アスタミューゼでは、エンジニア・デザイナーを募集中です。ご興味のある方は遠慮なく採用サイトからご応募ください。お待ちしています。

Copyright © astamuse company, ltd. all rights reserved.