Slack

目的があってツールを導入するもんだという前提はあるとして、弊社では上司の一存でSlackが導入された。上司には目的があるんだけどそれを広く共有している感じではない。結果、皆は言われたからインストールしてみた程度の認識に見える。積極的にSlackを使ってみて在り方を模索したほうが良いと思ってる。Slackは情報共有を加速させるもので、そもそも情報共有するという文化/意識がないと意味がない。

Natural sort order for PostgreSQL

Natural sort orderPHPではnatsort関数で実現できるソート方法です。
例えばsort関数でソートすると

<?php
$a = ['file1', 'file2,' 'file10'];
sort($a); // => ['file1', 'file10', 'file2']

となるところ、natsort関数では、

<?php
$a = ['file1', 'file2,' 'file10'];
natsort($a); // => ['file1', 'file2', 'file10']

となる。通常文字列ソートすると辞書式順序に従うのだが、ユーザが任意のコンテンツ名に連番付けて整頓するようなサービスでは、Natural orderは有用だ。

PostgreSQLでNatural order

PostgreSQLではどうすんのかなと調べてみたら、標準関数などでは用意がないが、関数を書いた人を見つけた。

gist.github.com

SELECT * FROM files FROM filename;
-- -> filename: file1, file10, file2

SELECT * FROM files FROM naturalsort(filename);
-- -> filename: file1, file2, file10

PHPのnatsort関数と全く同じ結果になるわけではないようだけど、ほとんどこれで十分なのでは。

naturalsort関数の動作する仕組み

まずFROM句を取り出して動きを確認してみる。

SELECT regexp_matches('2file', '0*([0-9]+)|([^0-9]+)', 'g');
regexp_matches
{2,null}
{null,file}

数字とそれ以外に分解して行を作っている。0*で0パティング(01,02,…10,11とか)を無視する。

 SELECT string_agg(convert_to(coalesce(r[2],length(length(r[1])::text)::text || length(r[1])::text || r[1]),'SQL_ASCII'),'\x00')
 FROM regexp_matches('2file', '0*([0-9]+)|([^0-9]+)', 'g') r;
string_agg
112 file

(112,fileの間は\x00
string_aggで、regexp_matchesで出力された複数行を`\x00'で区切った1つの文字列にしている。
集約前に、convert_toの引数で各行に何をしているか、以下のようになる。

regexp_matches convert_to(…)
{2,null} 112
{null,file} file

112とは、length(length('2')) || length('2') || '2' の結果出力される数字だ。数字の桁数を連結している。最終的にはstring_aggで集約した文字列"112 file"がORDER句のソートに使われるわけだが、112という数字を作る意味は何か、数字の例で確認すると分かりやすい。

length(length(text)) length(text) text
1 1 1
1 1 2
1 1 3
1 2 10
1 2 20
1 3 100
1 3 200

textの桁数が変わらないうち(1,2,3,…9)は、上位桁が変わらない。辞書式順序では、単純にtextだけで比較されることになる。
textの桁数が上がる(10,100)と、上位桁であるlength(text)の数が上がる。辞書式順序では、まずlength(text)(textの桁数)で比較され、同じであれば次にtextで比較される。
これで辞書式順序でも意図した大小関係で比較できるわけだ。

textの数を大きくしてみよう。

length(length(text)) length(text) text
1 9 999999999
2 10 1000000000
2 11 10000000000
3 100 1e99
4 1000 1e999
5 10000 1e9999
6 1e5 1e99999
9 1e8 1e99999999
10 1e9 1e999999999

length(text)が2桁以上になればlength(length(text))が上がる。text=1e99999999、length(length(text))=9までは問題なく動作する。text=1e999999999、length(length(text))=10となると、辞書式順序ではlength(length(text))=2より小さいとみなされ、正しく動作しない。が、1e999999999なんて大きな数を扱うことはないのではないか。

普通、辞書式順序で数字をソートする時は、対象の数字の最大桁数を調べて0パティングすると思うが、あらかじめ桁数が分かっていない場合は、制限があることを分かった上でこの手法を使うのは便利そう。

元のGistコードではエラーが発生したのでフォークした。Postgres 8.4(string_aggが無い)で動かす例も追記してある。 gist.github.com

FireFoxでdragoverイベントが発生しない

ドラッグアンドドロップできるファイルソートUIを作った の続き。

前回作ったデモサイト、FireFoxで動かないことに気づいた。dragoverイベントが発生していない。dragstartハンドラ内でDataTransferにsetDataしないと、dragstart以降のイベントが発生しないみたい。
また、dragoverでpreventDefaultしないと、dragover以降のイベントが発生しないブラウザがあるようだ。

修正コミット: dragever evet not fired in FireFox · kawausokun/reordering@32f1265 · GitHub
参考にした: 意外と知らないHTML5 API - Drag & Drop APIとは | CodeGrid

さらに、この変更以降、ドラッグ中のマウス横に「+」アイコンが表示されるようになってしまった。DataTransferに値を設定することで、dropEfectが変わってしまったのかもしれない。dropEfectをちゃんと設定してやる必要がある。

修正コミット: drop effect move · kawausokun/reordering@52ce123 · GitHub

ドラッグアンドドロップできるファイルソートUIを作った

f:id:kawauso7c:20170816192849g:plain

検証サイト: https://kawausokun.github.io/reordering/
ソース: GitHub - kawausokun/reordering: Reordering UI (experimental)

ファイル・フォルダをソートできるようなUIが必要になった。
単一のリストでのソートなら特に面倒なことはないが、フォルダを考慮する必要があると難易度が上がる。操作者が並び替えたいのかフォルダに突っ込みたいのかを判定しないといけない。
Fancytree - Example Browser のUIを参考にした。

基本的な考え方

一般的なドラッグアンドドロップによるソートUIは、ドラッグ中に即時ソートするものも多い。(例: Vue.jsのリストレンダリングとhtml5のドラッグ&ドロップの実装 - Qiita)
しかしファイルをフォルダに入れる時などは、ドロップ先が移動したり変化したりすると、狙ったところに移動させにくくなる。なので、Fancytreeのように, ドラッグ中は挿入先を示すマークを表示し、ドラッグ終了時にファイルを移動させる方が良い。

f:id:kawauso7c:20170816194847p:plain

挿入場所は以下のように分類できる。

  • ドロップ先ファイル・フォルダの直前に挿入
  • ドロップ先ファイル・フォルダの直後に挿入
  • ドロップ先フォルダに挿入

ドロップ先がフォルダの時、前後に挿入したいのかフォルダに入れたいのかを判定する必要がある。ドロップ先の要素内での、カーソルの座標で決定することにする。

f:id:kawauso7c:20170816201608p:plain

ドロップ先要素の上部にカーソルがある場合は「直前に挿入」、下部なら「直後に挿入」、真ん中なら「フォルダに挿入」とみなす。
ドロップ先がファイルなら「直前に挿入」と「直後に挿入」だけ。

検証サイトでドラッグ中に表示される"y"はドロップ先要素内でのカーソルのY座標、"mode"は挿入場所を示している。

実装

Vue.jsで実装している。
Y座標さえ計算できれば、挿入場所を判定し、ドロップ先要素にクラスを付与することで、挿入先を示すマークをCSSで表現できる。

注意

  • 現状フォルダの中にフォルダは挿入しない想定。やるとしたらファイル・フォルダをコンポーネント化し再帰する実装とするだろう。
  • 製品とするならもう一歩使い勝手向上を狙いたい。例えばファイルが入ったフォルダがリストの最後にある場合に、そのフォルダの下に挿入できない。

愛されすぎたぬいぐるみたち

愛されすぎたぬいぐるみたち

愛されすぎたぬいぐるみたち

何年間、何十年間、人と生活を共にしてきたぬいぐるみを写した写真集。
歴史を感じさせる痛みや汚れは愛された証拠。
頭部と脊髄のみとなったキリンのぬいぐるみは壮絶。スカルサーペントかドロヘドロか。サバンナで肉食獣に食われたのか。それでも一応ぬいぐるみ。

作者のページからも見られる。MUCHLOVEDシリーズ。 www.marknixon.com

最近のタスク管理

仕事柄、複数のプロジェクトから発生するたくさんのタスクを並行してこなしている。タスク管理の手法なんてのは定番が決まっていて、単純なTODOリストやGTD等、ネットや本で調べて真似するのがいいだろう。(最初から自己流を混ぜるのは、本質が分からなくなるので良くない)

GTDは単なる複雑なタスクリストではない。
全てのタスクを洗い出し頭の中を空にすること、定期的に振り返りの時間を設けることが一番良いポイントだと思っていて、生産性を上げ、創造的な仕事に繋げるように仕組み作られている。
自分もしばらくGTDの流儀に従っていた(3段トレイまで用意した)んだけど、習慣化の難しさがあり、飽きがきた。振り返りをしなくなって、死蔵タスクばかりになった。振り返りは意外と頭に負荷がかかる。せっかく頭を空にしたのにまた見返すなんて!

今はちょっと考え方を変えて、自分でタスク全てを管理しないようにしている。つまり、自分以外の他人ができるタスクならすぐに他人に振る。または会社のチームで使っているRedmineやカンバン等の作業管理ツールにタスクを追いやるよう努力する。チームのスタックに積んでおけば、チームメンバーの誰かか自分がいつか消化するだろう。まあつまりそういうスタックが存在して管理されている必要があるんだけど。
できるだけ他所に押し付けた上で残った自分で優先的にやるタスクはTrelloで管理し、リストの数はなるべく少なくするようにしている。

  • Inbox : 思いついたことをひたすら放り込む
  • TODO Today : 今日やること。朝これを作ってから作業を開始する。

あとは仕事用とプライベート用のリストで分けたり。
しばらくこんな感じでうまくいっている気がする。創造的な仕事ができているかは怪しいな。

次この方法も飽きて面倒臭くなったら、「忘れるようなタスクは重要ではない」を採用して全て忘れる予定。

メールをフィルタリング

会社のメールはGmailを使用しているんだけど、自分に関係のないメールが流れすぎてそろそろ鬱陶しいので、フィルタリングする方法を調べてみた。結果、ラベルとフィルタ機能を使用して、文中に「◯◯さん」(自分の名前)が含まれるメールのみを集めるラベル My Inbox と、それ以外を集める -{label:my-inbox} (My Inboxラベルがついていない)という条件を付与したラベル Team Inbox を作った。仕事中は My Inbox を開きっぱなしにして常に確認できるようにし、Team Inbox は数時間おきに確認することにする。

自分にとって重要なメールを絞り込むために、今回はホワイトリストを作ったが、ブラックリストにしたほうがメール数は増えるが漏れは無くなりそう。改めて受信トレイに並ぶメールの種類を確認してみると、明らかにチーム用メーリスに流すべきでない、皆が知る必要が無いものがあるのが分かった。そちらを根本から断つのもいいかもしれない。

これでディープワークできるわー