2020年5月12日火曜日

ブログのテーマ変えた

前のテーマは4年くらい使っていたのだけど、フォントが大きすぎるのと、デザインが今時じゃなくなってきたので、変えてみました。シンプルで落ち着くデザインなので、しばらくこれで。

kintoneのデータをSQL文で加工する

最近kintone漬け。
といっても、新しい機能を追加していくような楽しい作業ではなく、運用中のアプリの不具合対応(デバッグ)と細かい改良ばかりで、少し疲れてきた。

そんな中で、kintoneのデータの加工・集計をラクにやる方法はないものか? と調べていたら、
kintone でSQLを使う
という記事を発見。
alasqlというJavascript用データベースを使うと、kintoneのデータをSQLで集計できるじゃん。これすごい。
SUM(Group By)で合計値を求めたり、複数のアプリのデータをJoinで組み合わせたりできる。
(逆に言うと、こんな簡単なことがkintone標準機能ではできなかったりするのだが…)

"kintone alasql"でググるとそこそこ記事が出てくるので、もしかしてkintoneガチ勢には当たり前の話?

早速、工数管理アプリに入っている工数明細をプロジェクト単位・個人単位でSUMする画面を作ってみたので、備忘のために記事に残しておく。

元データはこんな感じ。

Figure.1 集計元のkintoneアプリ
設計方針

データは、event.recordsではなくAPIで取得する(event.recordsは最大でも100件しかデータが取れないので、集計目的では使えない)。 APIは生で叩くのではなく、kintone-rest-api-clientを利用。(getAllRecordsWithCursorが最強。みんな使うといいと思う。)

データの表示にはjQuery DataTablesを利用する。DataTablesを使って多機能な一覧画面を作ろう! – cybozu developer network とかが参考になる。

動作環境

アプリ設定画面の「JavaScript / CSSでカスタマイズ」にて以下URLを指定。

https://js.cybozu.com/jquery/3.4.0/jquery.min.js
https://js.cybozu.com/datatables/v1.10.19/js/jquery.dataTables.min.js
https://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.js
https://js.cybozu.com/datatables/v1.10.19/css/jquery.dataTables.min.css

また、http://alasql.org/ から alasql.min.js をDLし、PC用のJavaScriptファイルにアップロードしておく(alasqlのzipをダウンロードして解凍し、distディレクトリから取り出せば良い)。

もう1点、DataTablesの見栄えをkintoneに近づけるため、DataTablesをkintoneライクにする魔法のコード - Qiitaからkintone-datatables.cssをDLし、PC用のCSSファイルにアップロードしておくとよい(作者の方に感謝)。

サンプルコード HTML

アプリに一覧「プロジェクト別集計」を作成しておく。
レコード一覧の表示形式を「カスタマイズ」にし、以下のHTMLを記述。

サンプルコード js
/*
 * project_summary.js
 * 工数管理アプリのデータをプロジェクト別・個人別に集計
 * 
 * required: jquery.dataTables.min.js
 * https://js.cybozu.com/datatables/v1.10.19/js/jquery.dataTables.min.js
 * 
 * required: kintone-rest-api-client
 * https://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.js
 * 
 * requeired: alasql.min.js
 * http://alasql.org/
 */
(function() {
  "use strict";
  kintone.events.on(['app.record.index.show'], function(event){
    if(event.viewName !== 'プロジェクト別集計') return;

    // Initialize kintone-rest-api-client
    const client = new KintoneRestAPIClient();

    var myTable;

    var appId = kintone.app.getId();
    var query = kintone.app.getQuery();
    // APIでデータ取得
    client.record.getAllRecordsWithCursor({
      app: appId,
      query: query,
    }).then(function(response){

      // kintoneのrecords(json)を、alasqlで扱える配列に変換
      function convertToRows(records) {
        var rows = records.map(function(record){
          var keys = Object.keys(record);
          var row = {};
          keys.map(function(key){
            switch (key) {
              case '担当者氏名':
                // '担当者氏名'フィールドからname(=氏名)を取り出す
                row['担当者氏名'] = record[key].value[0]['name'];
                break;
              case '作業担当部署':
                // '作業担当部署'フィールドからname(=部署名)を取り出す
                row['作業担当部署'] = record[key].value[0]['name'];
                break;
              default:
                // 他のフィールドはvalueを取り出す。数値の場合はNumberに変換
                row[key] = record[key].type === 'NUMBER' ? Number(record[key].value) : record[key].value;
            }
          });
          return row;
        });
        return rows;
      }
      var recordSet = convertToRows(response);

      // sum([工数]) by alasql
      var sqlResult = alasql(
          "SELECT [プロジェクトコード], [作業担当部署], [担当者氏名], SUM([工数計_人月]) AS [工数計_人月] FROM ? GROUP BY [プロジェクトコード], [作業担当部署], [担当者氏名]", 
        [recordSet]
      );

      // 表に表示するカラム(alasqlの出力結果の項目名)
      var columns = [
        'プロジェクトコード',
        '作業担当部署',
        '担当者氏名',
        '工数計_人月',
      ];

      // 表のタイトルを好きにいじれるよう、別変数で定義。このサンプルではあまり意味がない。
      var titles = {
        プロジェクトコード: 'プロジェクトコード',
        作業担当部署: '作業担当部署',
        担当者氏名: '担当者氏名',
        工数計_人月: '工数計',
      };

      // DataTables初期化
      myTable = $('#myTable').DataTable({
        // DataTableの設定値はお好みで。
        destroy: true,
        pageLength: 100,
        lengthChange: false,
        searching: false,
        info: false,
        data: sqlResult.map(function(record){
          return columns.reduce(function(data, column){
            data[column] = record[column];
            return data;
          }, {
            レコード番号: record['レコード番号']
          });
        }),
        columns: columns.map(function(column){
          return {
            title: this[column],
            data: column
          }
        }, titles)
      });
    });
  });
})();
動作イメージ

サンプルの動作結果はこんな感じ。

Figure.2 カスタマイズ結果


画像にモザイクがかかりまくっていて何がなんだか分からないが、結果はとても良好。パフォーマンスも今のところ申し分ない。

今回はSUMしか使っていないが、そのうちJoinも使ってみたい。

2020年2月3日月曜日

kintoneのデータをスプレッドシートで編集する(Tabulator編)

最近、kintoneでエンジニアの工数管理みたいなことをしている。
工数データをスプレッドシート型のUIでサクサク入力したくて、HandsontablesTabulatorの両方を試してみた。
Handsontablesを使う方法は、cybozu developer network上にHandsontableを使ってkintoneをExcelライクに入力しよう その1という記事が載っていたりするのだけど、Handsontable自体が最近有償化されてしまったのと、いろいろ実装がうまくいかないところがあって、断念。
一方のTabulatorは、そこそこいい感じに動いているので、備忘のためまとめておく。
ちなみに、今回はレコードの追加や複製、削除には対応していない。あくまで既存のレコードの編集のみ実装している(追加や複製はそのうちやるかも)。
要件(やりたいこと)
kintoneの工数管理アプリに、以下のような案件別・担当者別の日々の工数情報が入っているとする。

プロジェクト番号客先名案件名担当者名年月1日工数2日工数31日工数
12345678○×商事株式会社メールシステム構築山田太郎2020/010.520
12345678○×商事株式会社メールシステム構築鈴木花子2020/01030.5
23456789▲△自動車株式会社仮想化基盤構築田中勘太郎2020/012.51.57

これをスプレッドシートで入力・編集したい。下に合計行もあるとうれしい。
準備①:kintoneの"一覧"を準備
kintoneの「アプリの設定」画面にて、一覧"Tabulator"を作成(※好きな名前でよい)。
レコード一覧の表示形式を「カスタマイズ」にして、以下のようなHTMLを設定しておく。
準備②:Javascriptの準備
kintoneの「アプリの設定」画面の「JavaScript / CSSでカスタマイズ」にて、以下をリンクしておく。
PC用のJavaScriptファイル
https://js.cybozu.com/jquery/3.4.0/jquery.min.js
https://unpkg.com/tabulator-tables@4.5.3/dist/js/tabulator.min.js
PC用のCSSファイル
https://unpkg.com/tabulator-tables@4.5.3/dist/css/tabulator.min.css
これで準備OK。
サンプルコード
以下のコードを「PC用のJavaScriptファイル」にアップロードする。
/**
 * kintone_tabulator.js
 * kintone上でスプレッドシートによるデータ表示・更新を実現
 * Tabulatorにて実装
 * 
 * @author dsp74118
 * @version 1.0 2020.02.03
 *
 */
(function() {

  "use strict";

  // レコード更新用データを生成する関数
  var setParams = function(record) {
    var result = {};
    for (var prop in record) {
      // note: アップデートしないフィールドを除外する処理をここに入れるとよい
      result[prop] = record[prop];
    }
  }

  // 一覧ビュー表示用のイベントハンドラ
  kintone.events.on(['app.record.index.show'], function(event) {
    // 「Tabulator」一覧のみ処理
    if (event.viewName !== "Tabulator") return;
    var records = event.records;

    // 列の定義
    var columns = [
      {title: "プロジェクト番号", field: "プロジェクト番号.value"},
      {title: "担当者氏名", field: "担当者氏名.value"},
      {title: "年月", field: "年月.value"},
      {title: "客先名", field: "客先名.value"},
      {title: "案件名", field: "案件名.value"},
    ];
    for (var i = 1; i <= 31; i++) {
      columns.push({
        title: i + "日工数",
        field: "_" + i + "日工数.value",
        align: "right",
        editor: true,
        bottomCalc: "sum",
        validator: [{type: validateManhours, parameters: {}}]
      });
    }

    // Tabulator初期化
    var tab = new Tabulator('#spreadsheet', {
//      layoutを指定指定すると表示が表示が崩れるためコメントアウト
//      layout: "fitData",
      data: records,
      // 左右矢印キーでセル移動
      keybindings:{
          "navLeft" : "37",
          "navRight" : "39",
      },
      columns: columns,

      // 更新処理
      cellEdited:function(cell) {
        console.log(cell);
        var id = cell._cell.row.data['レコード番号']['value'];

        // データを更新用に加工し配列に格納
        var updateRecords = [];
        updateRecords.push({
          id: id,
          record: setParams(cell._cell.row.data),
        });

        // 更新用Requestを作成
        var requests = [];
        requests.push({
          method: "PUT",
          api: "/k/v1/records.json",
          payload: {
            app: kintone.app.getId(),
            records: updateRecords
          }
        });

        // bulkrequestで一括で更新
        // 失敗した場合はロールバックされる
        var self = this;
        kintone.api('/k/v1/bulkRequest', 'POST', {requests: requests},
          function(resp) {
            console.dir(requests);
            console.dir(resp);
            console.log('Data updated successfully.');
            self.redraw();
          },
          function(resp) {
            console.dir(resp);
            console.log('Data update fail.');
          }
        );
      },
    });
  });
})();
event.recordsをそのままTabulatorに食わせられるので、コードがすごくシンプル! これはうれしい。
kintoneのデータ更新にはbulkrequestを使ったけど、複数セル同時編集はできないはずなので、bulkrequestではなく普通のrecords.jsonで良いかも。

注意点がいくつか。
  • layout: "fitData" で列幅を自動調整させると、横スクロール時に表示がめちゃくちゃ乱れるので、やめた。
  • 列の設定で frozen: true にて左側に特定列を固定すると、これまた横スクロール時に表示がめちゃくちゃ乱れるので、やめた。
このあたり、Tabulator側の問題な気がする(kintoneのCSSとの相性かも?)。まだ追及はできていない。
結果
こんな感じ。Excelと同等の使用感を再現することは無理だけど、矢印キーで縦横無尽に移動しながら、工数をExcel風に入力できるようにはなっている。
※このブログ記事用に別途アプリを作ることが難しい状況なので、実際に職場で作っているアプリのスクショにモザイクかけまくったものを掲載することをご容赦ください。
 ちなみにアプリは開発中のもので、データはテストデータです。悪しからず。

2019年10月29日火曜日

kintone APIで取り出したJSONデータの重複排除をする

最近、私の所属組織ではkintoneで案件(プロジェクト)管理をしている。
JavaScriptでごりごりカスタマイズしていたりするのだけど、そんな中で備忘録を残しておいたほうがよさそうなネタがあったので投稿。

要件(やりたいこと)
kintoneのプロジェクト一覧アプリに、以下のような情報が入っているとする。

プロジェクト番号客先名案件名担当者名
12345678○×商事株式会社メールシステム構築山田太郎
12345678○×商事株式会社メールシステム構築鈴木花子
23456789▲△自動車株式会社仮想化基盤構築田中勘太郎
※kintoneのアプリは正規化ができないので、1つの案件を複数名で担当する場合、上記のようにプロジェクト番号、客先名、案件名は重複して登録される。

このうち、プロジェクト番号、客先名、案件名の3つをkintone APIで取り出し、重複排除したい(=一意なデータとして取り出したい)というのが今回の要件。

まず、上記のプロジェクト一覧アプリをkintone APIで取り出すと、recordsに以下のようなJSONデータ(オブジェクト値の配列)が入ってくる。
[
 {
  プロジェクト番号: {type: "SINGLE_LINE_TEXT", value: "12345678"},
  客先名: {type: "SINGLE_LINE_TEXT", value: "○×商事株式会社"},
  案件名: {type: "SINGLE_LINE_TEXT", value: "メールシステム構築"}
 },
 {
  プロジェクト番号: {type: "SINGLE_LINE_TEXT", value: "12345678"},
  客先名: {type: "SINGLE_LINE_TEXT", value: "○×商事株式会社"},
  案件名: {type: "SINGLE_LINE_TEXT", value: "メールシステム構築"}
 },
 {
  プロジェクト番号: {type: "SINGLE_LINE_TEXT", value: "23456789"},
  客先名: {type: "SINGLE_LINE_TEXT", value: "▲△自動車株式会社"},
  案件名: {type: "SINGLE_LINE_TEXT", value: "仮想化基盤構築"}
 },
 {
  …
 }
]
これの重複排除をするにはどうすればよいか?
オブジェクト値の配列なので、一工夫必要である(Javascriptにおいてはオブジェクト値を単純に比較演算子やindexOfで評価できないため)。

結論
StackOverflowに頼ってしまいました。
How to remove duplicates from multidimensional array?
このQAは多次元配列のdedupeに関してだが、オブジェクト値の配列にも転用可能。

以下、コード。
StackOverflowのコードをまんまパクらせていただいてるけど、本記事は自分への備忘なのでご勘弁。
オブジェクト値をstringifyし、それをインデックスにした別配列(itemsFound)を使って重複チェックする。

なお、kintoneからのデータ取得には、APIの生使用ではなくkintoneUtilityを利用。
(function() {
  "use strict";
  kintone.events.on(['app.record.index.show'], function(event){
    kintoneUtility.rest.getAllRecordsByQuery({
      app: xx, //プロジェクト一覧アプリのID
      fields: [
        'プロジェクト番号',
        '客先名',
        '案件名',
      ],
    }).then(function(response){
      // 重複排除を行う関数
      function multiDimensionalUnique(arr) {
        var uniques = [];
        var itemsFound = {};
        for(var i = 0, l = arr.length; i < l; i++) {
            var stringified = JSON.stringify(arr[i]);
            if(itemsFound[stringified]) { continue; }
            uniques.push(arr[i]);
            itemsFound[stringified] = true;
        }
        return uniques;
      }

      // ここから本処理
      // kintoneUtilityで取得したデータを重複排除する
      var projects = multiDimensionalUnique(response.records);
      console.dir(projects);

      // 後続処理(略)

    });
    return event;
  });
})();

2019年9月9日月曜日

PMシンポジウム2019参加レポート

2019/9/5~9/6の2日間、恒例のPMシンポジウムに参加してきたので、いつものように(近年ずっとサボってましたが)レポートします。
9/5 基調講演1
好き嫌いの復権
一橋大学教授 楠木 建氏
もはやお馴染み感もある楠木教授。PMシンポジウムでの講演も常連になりつつあるが、実は私の所属企業の社内イベントにもお越しいただいて講演していただいたことがある(私はその時は地方勤務でしたので受講できなかったが)。
今回は「好き嫌いの復権」というタイトル。現代のビジネスにおいては「良し悪し」が尺度として使われ、個人の「好き嫌い」は無視される傾向があるが、楠木先生はそんな世間に警鐘を鳴らし、「好き嫌い」こそが競争戦略において重要だと説く。なぜなら、経営には「センス」が必要であり、センスの源泉は「良し悪し」よりも「好き嫌い」であるからだという。
このあたりで一流国内企業の著名な経営者の方々の面白語録が披露される。今回は全体的にアパレルネタで攻めてきており、"LifeWear"でおなじみの某社Y社長、月旅行に行く予定のM社長、某セレクトショップ運営会社のT社長などの発言が引用され、場内は笑いに包まれる。こういうユーモアで聴衆の心を一気に掴んでしまうところが楠木先生の上手いところ。
楠木先生曰く「スキルは訓練や教育で伸ばせるが、センスはそうはいかない」とのことで、これには私も同意するのだが、ではセンスとは先天的なもので、誰にも伸ばしようがないものなのか? それともどうにかして伸ばすことができるのか? センスのない自分としては、この辺りをいろいろ模索してみたいところである。

という具合で、前半は「経営=センス=好き嫌い」というお話であったが、後半は「ビジネスパーソンの仕事との向き合い方」へと話題が移っていく。趣味と仕事を区別するとき、一般的には「趣味=好き嫌い」「仕事=良し悪し」で語られるが、楠木先生は逆に「仕事こそ、好き嫌いが大切」と説く。好きな仕事なら続けられるし、努力も不要(好きなことを頑張るのは「努力」ではない、という理屈)。「負け」もないという。
(このあたりで、某週刊誌の記者が某芸能人のマンションを24時間張り込んでいた時の逸話が飛び出す。その雑誌の編集長?に「君たちはブラック企業を告発するような記事も書いているのに、自分たちの働き方こそ24時間の張り込みがあったりしてブラックではないか」と指摘したら「この仕事(=他人のスキャンダルを記事にすること)が好きなんだからいいのだ」と反論されたという。実例としてはいいんだか悪いんだか。)
この論説は、「仕事を好きになりなさい」と諭された気分であるが、これは私自身よりも後輩(特に若手)に伝えたいメッセージであった。若手にもいろいろいるので一絡げにするつもりはないのだが、自分の目の届く範囲の若手の中にも、「今の仕事を好きでやっていそうな人」と、「収入を得るために、嫌いだけど仕方なく仕事している人」の2種類がいるように思う。後者を頭ごなしに否定するつもりはないのだけど(収入を得ることがモチベーションだという米国的な考え方もあるので)、ただ、それって幸せなのかなあ? とは思う。
ちなみに自分の持論は「好きなことを仕事にする必要はないが、自分の仕事を好きになったほうがよい」である。もっというと、自分は働くのは嫌い(できれば遊んで暮らしたい)なのだが、働かないと生きていけないので仕方なく働いている。んで、どうせ働くのであれば、やってる仕事を好きになったほうが幸せだよね、という考え方で日々過ごしている。

最後に「他人の好き嫌いを尊重し、自分の好き嫌いを押し付けない」ことが成熟した社会の姿である、とのメッセージで講演は締めくくられた。ダイバーシティ<インクルージョン。

9/5 基調講演2
AI、ロボット、IoTを社会に活かす
株式会社hapi-robo st 代表取締役社長/ハウステンボス株式会社取締役CTO 富田 直美氏
富田氏は、ハウステンボスを立て直したことで有名な澤田社長の片腕ともいえる人物。その辺りを絡めて、ロボットやドローンがどうやって人を幸せにするか? というテーマで講演する…予定だったはずが、楠木教授の講演内容に引っ張られて前半がグダグダに…。(かなりアツくなってたので、可愛い性格の方だなあと感じた。御年71歳とのことだが、まだまだ若くて純粋な気持ちをお持ちのようです!)
講演終盤に"temi"というパーソナルロボット https://www.robotemi.com/ をご紹介しておられ、「これをやるのが私の夢」と語っておられた。以前、社内で新事業創出みたいな取り組みに参加させられたした時に、似たようなことを考えていたので、少し興味あり。
9/5 特別講演
行動経済学とプロジェクトマネジメント
慶應義塾大学 経済学部教授 星野 崇宏氏
今回のシンポジウムで1, 2を争うすばらしい内容だったが、講演資料がダウンロードできないので所感がうまくまとめられない(汗)。
行動経済学とは、ものすごくざっくり言うと、経済学に心理学を合わせたような学問であり、「合理的でない・利己的でない」人の行動を経済学に活かしていくことを目指す研究分野である、と理解している(違ったらすみません)。
行動経済学を突き詰めていくと、プロジェクトマネジメントにおいてプロジェクトメンバーを動かすことはもちろん、ビジネスにおいて顧客をはじめとするステークホルダーを動かす上でも必ず役立つはず! と感じた…のだが、短い講演時間中に山ほどキーワードや手法が紹介されてメモが追いつかず、いまいち整理できていない。書籍なりセミナーなりで今後も追いかけたいところである。ひとつ言えるのは、人間は案外動物的な判断基準で物事を決めている、ということか。例えば「何かをするたびに毎回ご褒美をあげてると、人はご褒美のためだけに行動するようになってしまう」などなど。
9/6 特別講演
新しい価値を生む方法論としてのForesight Creation
大阪ガス 行動観察研究所所長 松波 晴人氏
個人的に今回のシンポジウムでNo.1の講演。
"Foresight Creation"とは大阪ガス行動観察研究所がまとめた「新価値創造の方法論」であり、そこに必要とされる能力を「8つの玉」に整理した、というのが主旨。
新価値を創造するための発想を得るには、大きな母数からデータを収集して統計を取るよりも、少数のターゲットに密着して観察するほうが有効だという。この考え方には正直感心してしまった。例えば、高齢者向けの新たなサービスを考えるという取り組みにおいては、ある高齢者に1日密着してその行動を観察した。すると、その観察結果から「高齢者は何かサービスを受けたいというよりも、逆に自分が他人にサービスを提供したいと考えている」という新たな仮説に行き着き、「高齢者が他社に貢献するサービス」を検討するに至ったのだそう。
アンケートやインタビューといった手法では顕在化した(見えやすい)ニーズや機会、課題しか捉えることができないが、行動観察を行うことで新たな気付きが得られ、潜在ニーズを掘り起こすことができるというわけだ。

そして、新しい価値を作り出すにあたっては、「リフレーム」が大切だと説く。リフレームとは、それまで常識とされていた解釈やソリューションの枠組みを、新しい視点や発想で作り直すこと。
e.g.)
動物園の常識:「形態展示」→旭山動物園の「行動展示」
音楽の常識:「音質」→ウォークマンの「外で音楽を楽しむ」

この「リフレーム」が、先に挙げた「8つの玉」の1つ。他にも「統合」や「先見力」などの玉があるそうだ。

講演の後半は、いかに新しい価値を意思決定するか? という方向に話題が進む。ここがまた面白かった。
組織には、イノベーションを阻害する「3つの壁」があるという。
1. 意志決定の壁(新ビジネスがno risk high returnであることを証明しろと言われる)
2. リソースの壁(会社から「人も時間も金もないけど頑張れ」と言われる)
3. 横連携の壁(他部署に「ウチの部署は関係ございません」と突き放される)
この3つ、あるあるすぎる…。どこの会社も一緒ってことかと。
特に1.については、松波氏は「no risk high returnなビジネスは無いので、その中でいかに意志決定するかが重要」という。なにせ、イノベーションは新たな価値を生み出すものなので、どの案が成功するかを事前に目利きすることは非常に困難である。よって、案の是非を判断するにあたっては、「市場で正解かどうか」を基準とするのではなく、「個人の思い」から始めることになる(この点は楠木教授の「好き嫌い」の話にも通ずるものがある! 1日目と2日目で話がリンクした瞬間)。

続けて紹介された「イノベーションが経る3つのステップ」も必笑。
①無視される(関わってもらえない)
②怒られる(そんなもの上手くいくわけないだろうと反対される)
③「それが上手くいくことは最初から分かっていた」と言われる(手柄を横取りされる)
これまた、あるあるすぎる。

これらの「イノベーションを阻害する壁や組織文化」を解決するには、「出島」を作り、既存組織の影響を受けずに取り組むのがよいという。その出島として、行動観察研究所主導でForesight Creationの塾というか共創ラボのようなものを立ち上げ、業種問わずに参加者(参加法人?)を募る予定とのこと。そこではForesight Creationについて学べるだけでなく、そこに集まった人たちとともに実際にイノベーションを発想し(オープンイノベーションですね)、さらにはそれを実現させる場まで提供するつもりだという。これ、非常に興味がある。うちの会社からもぜひ代表者を送り込み、8つの玉を学んできてほしいし、何かを産み出してきてほしい(というか私が行きたい)。

全体を通して
今年は抜群に満足度が高い2日間であった。特に、一橋大学 楠木教授と大阪ガス 松波氏の講演が自分の中でガチッとリンクしたのが最高に気持ちいい。自分のこれからのビジネスに(そして人生に)どう取り込むか? マジで考えないとあかんな。

2019年7月11日木曜日

知らない間にBlogger上でSyntaxHighlighterが正しく動作しなくなっていたので直した

約2年間ブログを放置している間に、Bloggerがhttpsに対応したことによって、SyntaxHighlighterが動かなくなっていた。(どうも去年かららしいので、1年以上そのまま放置していたことになる。恥ずかしい。)
というわけで、SyntaxHighlighterをBloggerに安全に導入するためにあれこれした記録 | にわ会。を参考にして修正しておいた。Firebaseは初体験。

Vimperator, muttatorからcVim, Dorando keyconfig, Quick Folder Moveに乗り換えた

最近、社内SNSへの発信ばかりで、ブログ更新が完全に滞っておりましたが、久々に投稿。

私はキーボード派なので、FirefoxアドオンのVimperatorとThunderbirdアドオンのmuttatorを長年愛用していたのだけど、近年のFirefoxおよびThunderbirdの方針転換により、いずれのアドオンも動作しなくなってしまった。
以来、代用品を探していたのだけど、Vimperatorの代わりはChrome+cVim, muttatorの代わりはDorando keyconfigとQuick Folder Moveに落ち着いた。
備忘のため、現状の設定を記録しておく。

cVim
cVimは、ChromeをVimライクなキーバインドにしてくれるChrome拡張。
とはいえ、デフォルト設定だと色々気に入らないところが出てくるので、好みに合わせて設定を変えてやる必要がある。
設定方法は、拡張設定画面の「cvimrc」セクションに、vimrc風に設定を書き込んでいく形。
現状の私のcvimrcは以下の通り。
map s :tabnew google<Space>
map l nextTab
map h previousTab
map u lastClosedTab
map d closeTab
map <BS> goBack
" <C-Left>/<C-Right> move current tab position
map <C-Left> moveTabLeft
map <C-Right> moveTabRight
" Yank current selected word and search it
map <C-g> :execute vP<CR>
" quickmark t (xxx system) : Type "got" to open
let qmark t = ["https://example.com"]

少し解説。
s入力したキーワードをGoogleで検索
h, lタブの移動
u最後に閉じたタブを開き直す
dタブを閉じる
Ctrl+←, Ctrl+→現在のタブの位置を左右に動かす
Ctrl+g選択中の文字列をGoogleで検索
最後のqmarkは、実際には私がよくアクセスするサイトが登録してある。この設定は"t"にURLが登録されており、"got"とタイプするとそのサイトが開く。qmarkはaからzまで26個登録できる(はず)。

Dorando keyconfig
Dorando keyconfigはThunderbirdのキーバインドをカスタマイズする拡張。
デフォルトだと全然Vimライクなバインドではないので、設定をいじる必要がある。
Thunderbirdのプロファイルフォルダにあるpref.jsを開くと、
user_pref("extensions.dorandoKeyConfig
で始まる行があり、ここをいじっていく。
現在の私の設定は以下のようになっている。
Mutattorの代わりにDorando keyconfigを使う - 有馬総一郎のブログをかなり参考にさせていただいた(というか、現時点ではほとんどそのまんま)。

user_pref("extensions.dorandoKeyConfig.main.key_delete", "][D][");
user_pref("extensions.dorandoKeyConfig.main.key_forward", "][F][");
user_pref("extensions.dorandoKeyConfig.main.key_markAllRead", "shift][T][");
user_pref("extensions.dorandoKeyConfig.main.key_markJunk", "shift][!][");
user_pref("extensions.dorandoKeyConfig.main.key_markNotJunk", "!][][");
user_pref("extensions.dorandoKeyConfig.main.key_newMessage2", "][C][");
user_pref("extensions.dorandoKeyConfig.main.key_nextMsg", "][J][");
user_pref("extensions.dorandoKeyConfig.main.key_nextUnreadMsg", "shift][J][");
user_pref("extensions.dorandoKeyConfig.main.key_openMessage", "][O][");
user_pref("extensions.dorandoKeyConfig.main.key_previousMsg", "][K][");
user_pref("extensions.dorandoKeyConfig.main.key_previousUnreadMsg", "shift][K][");
user_pref("extensions.dorandoKeyConfig.main.key_replyall", "][R][");
user_pref("extensions.dorandoKeyConfig.main.xxx_key1_前のフォルダ", "control][P][][gFolderTreeView.selection.timedSelect(gFolderTreeView.selection.currentIndex - 1, 500);][chrome://messenger/content/messenger.xul");
user_pref("extensions.dorandoKeyConfig.main.xxx_key1_次のフォルダ", "control][N][][gFolderTreeView.selection.timedSelect(gFolderTreeView.selection.currentIndex + 1, 500);][chrome://messenger/content/messenger.xul");
user_pref("extensions.dorandoKeyConfig.main.xxx_key1_送信済みのメール", "alt][S][][let folder = GetDefaultAccountRootFolder().getFolderWithFlags(Ci.nsMsgFolderFlags.SentMail);\ngFolderTreeView.selectFolder(folder, true);\n][chrome://messenger/content/messenger.xul");
一部解説。
j, k次のメール、前のメール(上下移動)
Shift+j, Shift+k次の未読メール、前の未読メール
oメールを(タブで)開く
dメール削除
f転送
r全員返信
Shift+tフォルダを既読にする
c新規メール
Ctrl+n, Ctrl+p次のフォルダ、前のフォルダ
Alt+s送信済みトレイに移動

上記の設定には含まれていない(デフォルト設定)が、mでメールを未読に戻せる。
JavaScriptでコードを書いて色々できるようなので、時間が取れたら色々試していきたい。

Quick Folder Move
Quick Folder Moveはメールを別フォルダに移動したりコピーするためのThunderbird拡張で、移動先フォルダをインクリメンタルサーチできるのが特徴。
muttatorの"s"の代用。私のThunderbirdはかなり細かくフォルダ分けされているので、これがないと生きていけないと思う。
Shift+mメールの移動
Shift+yメールのコピー
Shift+g指定フォルダにジャンプ

今回は以上です。