初Reactでも能力Amplifyしてスマートな(?)チーム分けアプリを作ってみる


突然ですが現withコロナ時代においてチーム分け時が必要な時はないでしょうか。

例えば最近増えましたリモート会議ですと一人しか同時に話せないのでzoomのブレイクアウトルーム使うなどチームを分割することは良くあるのかなと思ってます
(ちなみに自分の所属するチームでは毎朝、朝会でランダムに2、3人のチームを組んで開発しております。Be Full Stackです。)

普通のチームわけであればrandomに割り振ればいいかと思いますが、下記のような状況で困ることがありました

  • なるべく均等に分けたいグループがある(たとえば下記)
    • 研究室MTGで各学年が均等に分かれて欲しい
    • 飲み会で1つのチームに各世代の人がいて欲しい
    • マルチファンクショナルなスクラムチームを結成する時に各分野(インフラ、バックエンド、フロントエンド、デザイナー、QA)の人がそれぞれれのチームに入って欲しい)
  • ランダムだと前回と同じ人と同じチームになってなかなか同じチームになれない人がいる

このようなことが何回かあって毎回手動で調整するのが面倒だなと思い、楽するために苦労するエンジニア精神でこの2つの問題を解消するwebアプリ作ってみました。

↓作ったもの
https://www.memo-team-gen.com/

ソースコード (拙いですが公開しました。aws-exports.js などセキュリティ的にまずそうなのは除いております。)
https://github.com/LittleWat/memorizing-team-generator-public

普段の業務はモバイルアプリなのでiOSやAndroid,バックエンドをいじることはあってもフロントをいじることはほとんどなく、フロントエンドは 状態だったのですがこれを良い機会にということでググり力 で開発しました。

時代はjsよりtsということで、初めて typescript を使ったので設定周りでつまづきまくりました。が、何とかバックもフロントも開発できバックもフロントもイケるts(js)の旨味を享受しました。typescript初めてではありましたが、null安全で型推論が多少ありmapはじめコレクションの処理がサポートされていたりしてswift, kotlinと似ており個人的に書きやすかったです。

いろいろググると、React, Vue, Angularがフロントエンド の三大レームワークとしてあり、Angularは後方互換性を消しまくってて開発辛いみたいな記事を散見したので選択肢から外し、ReactVueで迷い 世界的にメジャーなのは React ということで React の学習から入りました。(のちのち Vue のチュートリアルも行ったのですが、今回のようにRedux使わなくても良さそうな小さいアプリだったらVueのほうが楽かもと感じました。)

では開発の流れや内容についてご紹介したいと思います。

開発の流れ

Reactの学習

適当にググって出るチュートリアルをやっているとReactは書き方がclassのものとfunction(Hooks使うもの)のものの大きく二種類があるのを知り、Hooksがナウいことをしったので何かまとまっている良さげな記事ないかなと調べておりますと下記の良記事に遭遇できました。

こちらで紹介されていた本(pdf)を買って勉強しました。

こちらの本、体系的にまとまっててお世話になりました
Boothのpdfの本ですが、紙の本は時代の流れについていけてない(hooks以前のものが多い)ので流行りすたり(競争)の激しいDeepLearing研究時代を彷彿とさせるものを感じました。

またUIのライブラリはbootstrapか何か使おうか迷っていましたが、material-ui がぱっと見充実しており、GoTのキャラが使われていたためmaterial-uiを使うことにしました。話それますが、GoTのキャラはDeepLearning研究でもこちらなどちらほら題材として使われていましたね。

開発

ナウい(お財布に優しい)構成で行きたいなと思い、firebaseは以前iOSアプリで一通り触ってみたので、今回は別のものがいいなということでawsのamplifyでサーバレスにいくことにしました。

フロントエンド

amplify hosting で行いました。masterにpushするとデプロイされる仕組みも簡単に構築でき、ドメイン名の取得も簡単に行えました。amplify楽で良いですね。

バックエンド

amplify apiGraphQLRestfulAPIの二種類選べます。

ナウさを求める私はGraphQLで行おうと思い、下記のAWS公式のチュートリアルを行いました。

このチュートリアル、実装物も具体的で図解もあり、分かりやすく勉強になりました(おすすめです!!)
AppSync使えば簡単にElastic Seachと連携できるというのも知りました。強いですね。

がしかし、実際のチームわけのモデルをしたところ、お金のかかる(CapacityUnitの料金が倍になる)DynamoDBのGlobasSecondaryIndexが自動で貼られてしまったり、思ったように動かなかったので断念しました。(さらなるGraphQLの勉強の必要性を感じました。。)

仕方がないので、ここで方向転換して普通の RestfulAPIでいくことにしました。

こちらもいろいろ選べたのですが、expressで開発することにしました。
フロントは create-react-app でtypescirptが選べたのですが、expressはデフォルトが jsでバックエンドもどうせなら統一して tsでいきたいなと思ったのですが、割と設定でハマったので後述してます。結論として何とかjsに変換してデプロイはできました。
(amplify、デフォルトを jstsか選べるようになることを期待してます。(自分でコミットせぇという話かもですが汗))

DynamoDBのテーブル設計

Dynamo(NoSQL)なのでRDSではアンチパターンとされる配列を格納しても良いかと思い、配列を格納する設計にしました。

設計にはNoSQL Workbench という下記のようなツールを用いました。

このツール、dynamoのlocalやawsに簡単に設計したものを反映でき、テーブル定義のjsonも保存できて使いやすかったです^^

チームわけのアルゴリズム

randomsmart か選べるようにしました。

スマートなと言ってますが、実は100回ランダムにチーム生成してその中から スコア(frequency)が低いものを選ぶというシンプルな方法です。

スコア(frequency, freq.)の計算ですが、これまでのチームわけの履歴を見て最近チームになった人がいればいるほど高いみたいな計算してます。

簡単に数式で示しますと

 w_{ij} = 
\begin{eqnarray}
\left\{
\begin{array}{l}
1 (member_i とmember_j が同じチームの時) \\
0 (member_i とmember_j が異なるチームの時)
\end{array}
\right.
\end{eqnarray} \\
N: 過去の何回分まで見るか(max10回分見るようにしてます) \\
M: メンバーの総数

と置くと下記のような計算をしてます。

  freq. = \sum_{k=1}^N \sum_{i=1}^M \sum_{j=1}^M \frac{1}{k}w_{ij} \\

ソースコード的には下記のあたりです。
https://github.com/LittleWat/memorizing-team-generator-public/blob/939748cd58a5f4be415b9a65b1b417407d92f946/amplify/backend/function/memoteamgenec8c5c26/src/ts/service/adjacent-matrix-computer.ts#L58

具体例で説明しますと下記のように均等に分けたいグループがあるときに

単純に直近10回で何回一緒のチームになったかが下記の隣接行列で、5回一緒になった人が数組ありますが、

下記の重み付けされた隣接行列で100(最大値を100にスケールしました)の組みは[麻生太郎、松本さん]ペアなので

次回は[麻生太郎、松本さん]が同じチームには絶対ならないだろうということでgenerationすると下記のようになり

たしかに[麻生太郎、松本さん]は同じチームにならず、いい感じでチーム分けできていることが伝わるかと思います。

ぜひ使っていただいて何かフィードバックいただけると幸いです。

Analytics

amplify add analytics で一瞬でAmazon Pinpointが使えるようになります。簡単ですね。。
GoogleAnalyticsやFirebaseAnalyticsの置き換えになりそうでしょうか。

まとめ

  • Amplifyは現在進行形で発展中(awsのUIもupdateされてますね。)
  • 個人開発は全部自分でやらないといけないので良い勉強になる(慣れない技術を使うと苦労も多いですが発見が多く楽しい)
  • 隣接行列の可視化のライブラリ探してて偶然見つけたapexcharts楽しい

将来的にやりたいこと

  • ランダム生成はログイン不要にしたい
  • 生成結果をslack通知

などありますがやるかは不明です。。

ハマったこと

いろいろあるのですが、誰かの参考になればと思い念のため残しておきます。

backendのfunctionを勝手にいじってうまくdeployできない

同じく困った人がいた模様

下記、私がミスしていたファイル構成(srctsファイルを置いた。)

➜ memoteamgenec8c5c26 git:(add-lambda-team-gen-api) ✗ tree . -L 2
.
├── amplify.state
├── dist
│   ├── app.js
│   ├── app.js.map
│   ├── config
│   ├── index.js
│   ├── index.js.map
│   ├── latest-build
│   ├── latest-build.zip
│   ├── models
│   ├── repository
│   ├── service
│   └── util
├── function-parameters.json
├── memoteamgenec8c5c26-cloudformation-template.json
├── package-lock.json
├── package.json
├── parameters.json
├── src
│   ├── app.ts
│   ├── config
│   ├── event.json
│   ├── index.ts
│   ├── models
│   ├── repository
│   ├── service
│   ├── swagger.yml
│   └── util
└── tsconfig.json

あるべき姿(デフォルトの姿): (srcjsファイル, package.jsonを置く。)

➜ memorizing-team-generator git:(add-lambda-team-gen-api) ✗ tree amplify/backend/function/tmp             
amplify/backend/function/tmp
├── amplify.state
├── function-parameters.json
├── parameters.json
├── src
│   ├── app.js
│   ├── event.json
│   ├── index.js
│   └── package.json
└── tmp-cloudformation-template.json

結局 srcの下に tsディレクトリを切って下記のようにtsconfig.jsonを編集すると動いたが、ディレクトリ構造が気持ち悪いことになりました...
何か良い方法ないのですかね

tsconfig.json
{
  "compilerOptions": {
    "outDir": ".",
    "sourceMap": true,
    "module": "commonjs",
    "target": "es6",
    "lib": ["es2017", "dom"],
    "moduleResolution": "node",
    "removeComments": true,
    "allowJs": true,
    "checkJs": true,
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./ts",
    "paths": {
      "*": ["node_modules/*", "ts/types/*"]
    }
  },
  "include": [
    "ts/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

responsive designにできない

こちらは今もハマってます。ただのフロントエンドスキル不足故の問題です。。cssの勉強などします。。