ちゅらデータ Advent Calendar 2023 (シリーズ1) 1日目の記事です。
オフラインイベントであると便利な名札をたくさん作る仕組みを作ったという話です。
はじめに
早いもので創業から6周年を迎えたちゅらデータ株式会社は、おかげさまで社員数が80人近くになり、先日「ゆがふBizタワー浦添港川」というなんかカッコいいオフィスビルに移転することができました。
さっそく10月末頃に新オフィスにて「6周年祭」を開催し、普段は日本全国でフルリモートワークしているメンバーも含めて多くが沖縄の新オフィスに集結するという、それはそれは楽しい催しでした。
「えっと... どちらさまですか?」
時は遡り「6周年祭」の開催一ヶ月前、ひとつの懸念が発生しました。
- かなりメンバーが増えてきて全員の名前を覚えるのも困難
- 普段はフルリモートワークのメンバーも多くコミュニケーションもチャットが主体
この状況で「全員が一同に会する」と、一体何が起きるかお分かりですか?
「はじめまして... えっと、どちらさまですか?」
という微妙なコミュニケーションが各所で多発するに決まっています。
普段なかなか会えない同僚とオフラインコミュニケーションができる貴重な機会に「あー、コイツ誰やっけな...」という謎の読み合いに思考リソースを費やすのは無駄としか言いようがりません。そんな事を考えていてはおいしい酒が飲めない。
名札を作ろう!
解決策は至って簡単で、尋ねなくても誰か分かるようにすればいいんです。名札を作りましょう。
普段チャット主体のコミュニケーションをしている弊社では、人々は名前よりもむしろ Slack アイコンで人を見分けています(要出典)。そのため名札には名前だけでなく Slack アイコンも入れるのは MUST です。(個人的なこだわり)
で、はじめは「そんなんパワポかなんかでレイアウト組んで印刷すれば終わりやろ」と思ったんですよ。でも同じ名刺を100枚印刷するのとはワケが違って、1枚ずつ異なる情報を配置していかないといけないのってかなり苦行だな?と気づきます。30人分くらいまでだったら手作業で頑張ったんですが今回は70人分だったので早々に諦めました。
こういう印刷タスクのことを印刷業界では「バリアブル印刷」というらしいです。じゃあ「データだけ揃えて業者に印刷してもらえばいいや」と見積もりをとったら「8万円」だそうで。うーむ... 1日のイベントでしか使わない名札に1枚あたり1000円超かけるのはさすがに厳しい。そもそもバリアブル印刷の主な用途は社員証や会員証なのでプラスチックカードへの印刷が基本となってしまい、ペラい紙などに安価にバリアブル印刷してくれる業者は探しても見つかりませんでした。
名札を自作しよう!
プログラミングの出番です。
我々はソフトウェアエンジニアなので、単純作業の繰り返しであればだいたいは自動化することができます。簡易的なバリアブル印刷の仕組みを実装して単純作業繰り返しの苦行から開放されましょう。
1. 参加メンバーの Slack アイコン一覧を取得する
まず、Slack API を使って Slack ワークスペースのメンバー一覧をダウンロードしてきます。
※ users:read
スコープを持つ Slack API Token は何らかの方法で入手しておいてください。(割愛)
$ curl https://slack.com/api/users.list -d "token=${SLACK_API_TOKEN}" > users.json
おもむろに Jupyter を立ち上げて、内容を確認してみます。(IPython でも可)
import json
with open("users.json", "rt") as f:
data = json.load(f)
len(data["members"]) #=> 685
人数が多すぎるので、おそらくゲストユーザや deactivated なユーザや Bot ユーザなどが入り混じっています。このまま全ユーザのアイコンを取得するとえらいことになるので、イベントの参加者だけに絞り込みます。
イベント参加メンバーのメールアドレス一覧は気合いで入手したので、これをもとに絞り込んでいきます。
# イベント参加メンバーのメールアドレス一覧
participant_emails = ["foo@example.com", ... ]
members_map = {d["profile"]["email"]: d for d in data["members"] if "email" in d["profile"]}
participants = [members_map[email] for email in participant_emails]
len(participants) #=> 70
このユーザ情報にはプロフィール画像の URL も含まれていて、この URL には認証無しでアクセスすることができます。画像サイズに応じて複数種類ありますが、 image_1024
がいい感じにデカくて良さそうなのでこれを使用して人数分のプロフィール画像をダウンロードしてきます。
for participant in participants:
if "image_1024" not in participant["profile"]:
# プロフィール画像を設定していないメンバーのメールアドレスを表示する
print(participant["profile"]["email"])
continue
url = participant["profile"]["image_1024"]
name = participant["profile"]["email"].split("@")[0]
ext = url.split(".")[-1]
wget_opts = f"{url} -O ./icons/{name}.{ext}"
! wget $wget_opts
※ 最後の行は Python コードではなくて、Jupyter や IPython では !
からはじまる行はシェルコマンドとして実行してくれるというやつです。ダウンロード処理を Python で書くのが面倒かったので...。
これで、イベント参加メンバーの Slack アイコンを ./icons/
内にもりもりダウンロードすることができました。
2. 印刷用のレイアウトを組む
(2024.11 追記) Web アプリとして公開しました
以降で紹介している印刷レイアウトを組むしくみを Web アプリとして公開したので、既にアイコン画像が集められているのであればこれを使うだけで印刷できます。
印刷するためのレイアウトを組んで、画像を1枚ずつ埋め込んだものを生成する必要があります。
何かそれ用の便利なツールがあるのかもしれないですが、よくわからなかったので Web ページとして作ることにしました。
/
├ index.html
├ index.css
├ index.js
└ icons/
├ foo.png
├ bar.jpg
:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" />
<link rel="stylesheet" href="index.css" />
<script src="index.js" defer></script>
</head>
<body></body>
</html>
.page {
width: 210mm; /* A4 用紙の横幅 */
height: 297mm; /* A4 用紙の縦幅 */
page-break-before: always; /* 改ページする */
}
.card {
display: inline-block;
width: 105mm; /* 名札1枚あたりの横幅 */
height: 74.2mm; /* 名札1枚あたりの縦幅 */
border: 1px #ccc solid;
vertical-align: top;
box-sizing: border-box;
}
.card > img {
width: 40mm; /* 名札に配置するアイコン画像の横幅 */
height: 40mm; /* 名札に配置するアイコン画像の縦幅 */
margin: 6mm;
border-radius: 20%; /* 角を丸くする */
}
// アイコン画像ファイル名一覧
const filenames = ['foo.png', 'bar.jpg', ...]
// 1ページ内に含める名札の枚数
const cardsPerPage = 8
// アイコン画像ファイルごとに要素を生成して追加していく
let page
for (let i = 0; i < filenames.length; i++) {
if (i % cardsPerPage === 0) page = addElement('page', document.body)
const card = addElement('card', page)
const img = document.createElement('img')
img.src = `icons/${filenames[i]}`
card.appendChild(img)
}
// 指定のクラス名を持つ div 要素を生成して指定の要素内に追加する関数
function addElement(className, parent) {
const element = document.createElement('div')
element.className = className
return parent.appendChild(element)
}
各要素の大きさは各自いい感じに設定してください。
3. 印刷して切る
自宅のプリンタでいい感じのサイズになるように印刷していきます。
CSS でのサイズ指定に mm
単位を使用して A4 用紙ちょうどのサイズで作っているので、印刷するときは「余白なし」を指定すれば印刷倍率は「既定 (100%)」のままでもいい感じに納まるはずです。ただし「余白なし」設定は「margin が無い想定で印刷レイアウトを組んでくれる」というだけであって、実際にフチまで印刷できるかどうかは使用するプリンタに依存するため、プリンタの限界を把握した上でフチ付近には要素を配置しないようなデザインにしておく必要があります。
4. ネームホルダーを用意する
やらかしました。
横着をしてイベントの前日ギリギリになってからホームセンターに行ったところ、同じサイズのネームホルダー x70 は売ってませんでした。
「やべぇ、どうしよ...」と思いつつダメ元で帰り道にあったダイソーに寄ったところ、ありました。
しかも10枚入りで100円(税抜)、めっちゃ安い。
頼りになる俺達のダイソー!!!
実はもともと名刺くらいのサイズで印刷して使おうと思ってたのですが、急遽このダイソーネームホルダーサイズで印刷し直すなどしました。
この記事を真似される皆様におかれましては、ネームホルダーは早めに用意しておいた方がいい という点にご留意ください。
当日
イベント会場の入口付近に名札作成スペースを設置して、あとは勝手に持っていってもらうだけです。
名前は自分で書いてもらう方式にしました。あらかじめ印刷することもできましたが、「人から呼ばれたい名前」は必ずしも本名や Slack 表示名とは限らないので、 呼ばれたい名前を自分で書いてもらったほうがよいだろう ということでそうしました。
頑張った甲斐あって名札は割と好評で、また他のイベントでも使いまわせそうな仕組みが作れたのでまぁ良かったかなと思います。
ちゅらデータ Advent Calendar 2023 (シリーズ1)、明日は @yoshiya_sugimoto さん!!!