はじめてのポートフォリオ（技術＜制作過程）

プログラミングを始めて半年を迎えます、上野栞音です。
スクールでは主にRailsアプリの作り方を教わり、現在は株式会社Wilicoでインターンとしてお世話になりながら就活中です。
プログラミングも字書きも不慣れなもんで、至らぬ点がありましたら教えてください。

今回はスクール3ヶ月目のフェーズで作成したポートフォリオについて、雑多にはなりますが色々と書き留めていこうと思います。

# ポートフォリオ概要
[ToT](http://tot.monster/)
[github](https://github.com/kanon2580/tot)
※かんたんログイン("Signin as a trial user")実装済み。

開発コミュニティ向けのQAサイトを想定して制作しました。
詳細はgithubのREADMEに。

# 開発経緯と目的
今回学習したかったことは大きく分けると2つ。

:thinking: **ユーザーのアクションを経て、どのようなデータが集められるのか。**
:thinking: **そのデータを基にどのような分析結果を返せるのか。**

もともと、**ユーザーに評価を付与→チャートで表示の機能は絶対に実装しよう**と決めていました。
きっかけとしてはAI(データの統計や分析)に興味が湧いたからなので、最初はPythonを使って何かしようと考えていたのですが却下。
理由としては、

1. 初学者が1ヶ月という期限付きで新しい技術を得てアプリを作っても、強みのあるポートフォリオにはならない気がしたから。
2. 当時教わっていたRailsについての理解が明らかに浅く、その理解度のまま別の技術に手を出すのが腑に落ちなかったから。

そんなこんなで いくつかサイトをチラ見しつつアプリの企画を練りました。
QAサイトの制作に至った理由としては、評価基準の設け方がパッとイメージできたからです。

# DB設計
:bulb:**静的(不変的)なデータのみを保管するようにするのがベスト！**
というのが今回の学びです。開発はSQLite3、本番はMySQLで実装しています。

### はじめに考えていたもの
![旧UR図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566716/03c3b603-588f-8743-adbb-a036d6a415a0.jpeg)

ユーザーの評価はユーザーテーブルに、それ以外の評価もコメントやスレッドのアクションに紐づけてアップデートする予定でしたが、途中でこんがらがってスクールのメンターさんに相談。
諸々差し置いて問題点として大きかったのが、**この設計だとユーザーが何か操作するたびにアップデートが掛かってしまうため、正常にアップデートされない可能性があることです**。(回避方も色々あるみたいですが、もっと掘り下げたいので省略)

後々算出できるデータやユーザーの動作に依存して頻繁にデータが更新されるものは、理由がない限りDBで管理しないようにするため、**分析するための材料だけ保管してチャートを描画するタイミングで算出する設計にしました。**

### 最終的な設計
![最新UR図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566716/2b9ab4d9-02ec-23a7-a9e1-f5812ffa268f.jpeg)

赤い部分をごっそり消しましたが、時間系の評価については他の評価に比べて算出するステップが一つ多い(2つのテーブルに登録されている登録日を基に差分を算出 → その差分を基に平均値算出)こともあり、集計のスピードを上げるために用途を変えてテーブルを残しています。

# 基本的な機能の実装
チャート実装を除いた部分です。チャートに時間が割きたかったこともあり、検索機能以外を1週間くらいでスケジュールを組んで実装しました。
さして難しい機能は実装してないのでアピールポイントを挙げると、
:information_desk_person:**タグ付け機能と検索機能はgemを使わずに実装しました**
改めて調べてみると「gemで出来たのでは…？」なんて思いますが、いい運動(?)になったので結果オーライ:confetti_ball:
<details><summary>以下備考</summary><div>

- タグ付け機能
    - 同じ意味なのに違う表記(rails, Railsみたいな)のデータが入ると分析の精度が下がってしまうため、**新規タグ作成の動作を重くしたかった**。
    - 基本1つのtext_fieldにカンマ区切りで書き込むようなやり方しか見つからず軽いなぁと悩んだあげく、普通に中間テーブルで結んでフォーム作った方が慣れたやり方だし早そうだと判断。
    - 実装後、Qiitaのタグ付け機能を見て目ぇひん剥きました。なるほど。この手があったか。なるほど…
- 検索機能
    - 検索対象や解決/未解決フラグごとなど**少し細かく条件を指定して検索できるように**したかった。
    - 改めて調べてみると[アドバンストモード](http://nekorails.hatenablog.com/entry/2017/05/31/173925#031-%E3%82%A2%E3%83%89%E3%83%90%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8%E3%81%AF)でいけそう。当時触ってみたけど、このモードを理解するより自力で実装した方が早いなと判断。(条件分岐はスクールの応用課題で実装済み、or検索は[このサイト](https://qiita.com/Orangina1050/items/ca4e5dc26e0a32ee3137)を参考に実装。)
</div></details>

# チャート実装

開発経緯の通り、~~今できる事~~→これやりたい！軸で企画を立ててここまで来たので、為せば成る精神で詳しい実装の目論見はほぼありませんでした。
調べてみるものの、当時jsに馴染みが無さ過ぎて悶絶寸前。

:fearful:＜**何をどうすれば これが出来るんですか…？**
自分で考えたアプリのくせに、ここに来て教室の隅で静かに絶望してました。

この時の学びとして大きかったのは、
:raised_hands:**分からない、初めて触るものは一度触ってみる大切さ**

何も分からず嘆いていた最中、スクールの同期生に相談したら[chart.jsのcodepen](https://codepen.io/chartjs/pen/YVWZbz)を教えてくれました。
ここで少し触ってみた瞬間、chart.jsが面白いほど読める。要因としては、すでに完成しているコードを触れることが大きかったと思います。
どの値がどの軸のデータに影響しているかや、どの値がどのデザインに影響しているかなどが直観的に分かり、ここに配列渡せば勝ちじゃん！とゴールを定める事が出来ました。

ちょっと無謀にも思いますが、試行錯誤も含めて工数を割くために基本的な機能 頑張ったので潔く実装に移って良かったなと思います(結果論)。

# いざ、尋常にチャート実装
jsファイルとのデータの受け渡しは、[gem 'gon'](https://github.com/gazay/gon)で行っています。
json形式に変換するのが基本ですが学習目的に含まれていないのと、これから実装するチャートの工数が読みきれずスピードを重視したかったので採用しました。

### 大体こう。
1. UserModelのロジックでチャートに渡す配列を計算するメソッド作成、Controllerで呼び出す
2. UsersControllerでgonに渡す
3. Viewでgonのタグ→jsファイルに渡す
4. jsファイル→canvasタグに渡してチャート描画

※自分が流れを掴むために搔い摘んだものです。gon周りは特にもう少し検証しながら理解を深めたい。

### UserModelのロジックは、大体こう。

コードがぼちぼち長いため、流れだけ伝わりますようにといった感じで書きます:pray:
代わりと言っては何ですが行単位でGithubのリンクを貼るので、気になる方はご覧ください。
評価基準ごとにまとめます。

#### :cactus: Questioner/Tags, Answer/Tags
ドーナツチャートの2項目です。少しデータを引っ張るロジックが違うだけなので、Questioner/Tagsを例にします。

1. [対象のユーザーが投稿したIssueに紐づくタグを算出](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L152)
2. [タグの名前だけを格納した配列を作る](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L154)
3. [対象のユーザーが投稿したIssueに紐づくタグを算出](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L158)
4. [タグの割合だけ格納した配列を作る](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L160-L162)

#### :cactus: Time to response, Time to solved, Total
バーチャートとレーダーチャートの項目です。
データを引っ張ってくるテーブルが違うだけなので、Time to responseを例にします。
**ドーナツチャート以外は基本この流れです。**細かい処理は省きます。

1. [全ユーザーの平均値を算出](https://github.com/kanon2580/tot/blob/master/app/models/user.rb#L32)  
    ・ユーザーの動作に依存してグラフの階級を変えるため、このデータを基に基準になる値を算出します。
2. [チャートの諸々を決めるのに使う値を算出](https://github.com/kanon2580/tot/blob/master/app/models/user.rb#L63-L72)  
    ・返すのは各ユーザー平均値の [ 最小値, 最大値, それを基にした階級幅 ]。  
    ・投稿されたIssueが1つも無い場合はfalseで返して例外処理。
3. [2を基に境界値を算出](https://github.com/kanon2580/tot/blob/master/app/models/user.rb#L76-L86)  
    ・〇秒～〇秒のユーザーはスコア1、〇秒～〇秒のユーザーはスコア2… の〇だけ入ったような配列です。
4. [3の境界値を基に](https://github.com/kanon2580/tot/blob/master/app/models/user.rb#L117)
5. [各階級に何人ユーザーが含まれるかを算出して配列を作りながら](https://github.com/kanon2580/tot/blob/master/app/models/user.rb#L125-L134)
6. [各階級に対象のユーザーが含まれるか否かを0,1で算出して配列を作る](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L136-L140)
7. [6の配列を基に、ユーザーのスコアを算出](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L145-L149)  
    ・3点の場合、[0,0,1,0,0,0,0,0,0,0] → each_with_indexで回すと2番目の値が1になる → 2+1でスコア算出
8. [5,7をControllerに返す](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L93)
9. ※[レーダーチャートの配列だけControllerで作ります。](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/controllers/users_controller.rb#L42)  
    ・ ControllerからModelのメソッドを呼び出す際、Model上に配列を作ろうとすると呼び出すたびに配列がリセットされるためです。  
    ・[ 5(チャートに渡す値), 7(ユーザーのスコア) ] の配列が返り値なので、この配列の[1]を拾って配列を作ります。

#### :cactus: ほか
Time to response とほぼ同じなので、差分だけまとめます。

**1.平均値**
平均値が割り出せるほど絶対値が大きくなかったため**合計値を算出しています。**チャートの値が0ばかりになって変わり映えしなかった:frowning2:

**3. 階級の算出**
階級の誤差をスコア1に寄せるため、呼び出すメソッドを変えています。
評価基準によってスコアが高くなる条件が分岐するのが肝で、
Time to ～ → 1.平均値算出の結果が**低いと高スコア** → [calculate_evaluation_datas_sort_by_**max**](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L74)
それ以外 → 1.合計値算出の結果が**高いと高スコア** → [calculate_evaluation_datas_sort_by_**min**](https://github.com/kanon2580/tot/blob/713f6e626a9b83712189a4289452ec3c1cc963a8/app/models/user.rb#L96)
大きく違う点としては、階級を決める基準が **最大値 → 最小値** であること(こちら基準で命名してます)と、**配列をreverseするタイミング**です。

### リファクタリング

制作期間が 2/15～3/15 くらいだったのですが、3月頭にβ版をデプロイして色んな方にレビューを頂きました。ありがとうございました:sob:

**UserModelのメソッド**
1. [最初は全てControllerに記述していたメソッドを](https://github.com/kanon2580/tot/commit/103729109be2108132af08484267a68b92130e53)
2. [Modelに移行して](https://github.com/kanon2580/tot/commit/273fcd5611cd7561fcfc43e487a4d86eb4a9b9b5#diff-4676c008b11a5480d73d4a6de01e45b9)
3. 共通するロジックをメソッド化(最新版)

**Viewの描画まわり**
1. [HTMLの部分テンプレートでscriptタグをrenderしていたのを](https://github.com/kanon2580/tot/commit/dd04e53df7f81f4cf5d30b4f8cf2194de8afb3fa#diff-ff905642b1c9d762bee30c8cfef62a70)
2. [jsファイルに移行して](https://github.com/kanon2580/tot/commit/d891c3e0ee2aac151c3649a27722df684aaa664c#diff-ff905642b1c9d762bee30c8cfef62a70)(リンクはcomment_tags)
3. 一つのファイルにまとめて共通するロジックをメソッド化(最新版)

### チャートまとめ

このポートフォリオにおいて最大の学びでもあるのですが、
:wave:**手を動かせば必ず答えは見つかる！**
と確信を得られた制作物でした。
今扱えるパラメータを読み、それを基にロジックを組み、足りなければパラメータを送る。これを自分自身の経験をもって得られたのはとても貴重な学びだったなと思います。

全てのチャートを最低限実装するまでの1週間くらいはコンソール画面にかじりついて模索する日々でしたが、ロジックができた瞬間の達成感が最高すぎて何だかんだ楽しかったです:v:

# さいごに
ポートフォリオについて調べていると「こういうのが有利！」ばかりで自分と同じレベル感の記事が上手いこと見つからず、望んでいた判断材料では無かったので書いてみました。

もちろん まだ改善の余地があるアプリとは思いますが、キリがないので一旦区切りにしようかなと思います。

これからポートフォリオを制作する方の目に留まり、少しでもインスピレーションの助けになれば幸いです:ramen:
あと色んな方のこんな感じの記事見たいので是非書いてください:eyes:

ありがとうございました！

> **参照**
> [Ransackで簡単に検索フォームを作る73のレシピ](http://nekorails.hatenablog.com/entry/2017/05/31/173925)
> [railsで複数ワードでの検索機能(or)とマイナス検索機能(-)を実装してみる](https://qiita.com/Orangina1050/items/ca4e5dc26e0a32ee3137)
> [chart.jsのcodepen](https://codepen.io/chartjs/pen/YVWZbz)
> [gem 'gon'](https://github.com/gazay/gon)