商談をTrello + Google Colaboratoryで管理（その2）

このシリーズでは、商談の管理をTrelloで行いその分析をGoogle Colaboratoryで行うことを考えていきます。

* 1回目の記事（https://qiita.com/yazn/items/9368cc9e442eae85e5da ）
* 2回目の記事（本記事）
* 3回目の記事（https://qiita.com/yazn/items/f1e0d05f14db49473b1f ）

#1. 前回のおさらいと本記事でのゴール
前回の記事（[商談をTrello + Google Colaboratoryで管理（その1）](https://qiita.com/yazn/items/9368cc9e442eae85e5da) ）では、以下のトピックを扱いました。

* 商談フェーズ管理の運用例について
* 商談フェーズ管理を行うために、ここではTrelloとGoogle Colaboratoryを使って行うこと
* 必要なボードを作成
* API keyやtokenを取得し、Pythonからボードの情報を取得すること


本記事では以下をゴールとします。

* Google Colaboratoryを準備し、ボード情報を取得します。
* 商談レポートを作成

商談レポートは以下のような画像を生成することをゴールとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984649/d9a8831e-e0aa-51fe-042d-369623cb9fe9.png)
※THE MODEL/福田康隆（2019）より引用

#2. Google Colaboratoryの準備
Google Colaboratoryにアクセスし、ログインします。（https://colab.research.google.com/notebooks/ ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984649/04d2c50c-5a51-5b6b-ce1c-ea7377daa67f.png)

ノートブックを作成し適当な名前をつけます。
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984649/e0efda2e-11ac-1b07-d37d-4ab46d85b9dd.png">

#3. Google Colaboratoryにpy-trelloをインストール
Google Colaboratoryにはpy-trelloがインストールされていないようですので、インストールします。

```
!pip install py-trello
```

#4. ボードを取得
前回記事を参考にし、ボードを取得します。ここではopps_boardをその変数とします。

```py
from trello import TrelloClient

trello_client = TrelloClient(
    # 先程取得したキーとトークンを記載
    api_key = '',
    token = ''
)
# 対象のボードである'Opportunities'ボードを取得（ボードの名前がユニークでない場合は困る）
opps_board = [x for x in trello_client.list_boards() if x.name == 'Opportunities'][0]
```

#5. 商談レポート作成
前回のボード・カードの運用の設計を少し拡張することにしました。カードのコメントに「Next Action」を記載することにして、営業担当者がどの活動を次に実行するかを把握できるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984649/948064a8-86c3-d068-16fb-4849444351c9.png)

「Next Action」が複数ある場合、最新のコメントのみを商談レポートに含めるようにします。

Notebookから商談レポートを取得するコードを以下のようにしました。

```py
import pandas as pd
import numpy as np
from datetime import datetime, timezone

# py-trelloオブジェクトのうち案件カードを抽出しdataframeを作成
opp_df = pd.DataFrame({'opp_card': [x for x in opps_board.get_cards() if not x.name.startswith('.')]})

# カード名の列を追加
opp_df['card_name'] = opp_df.apply(lambda x: x['opp_card'].name, axis=1)

# 顧客名、案件名 列の追加
_df = opp_df['card_name'].str.split('_', expand=True)
_df.columns = ['顧客名', '案件名']
opp_df = pd.concat([opp_df, _df], axis=1)

# フェーズ列の追加
opp_df['フェーズ'] = opp_df.apply(lambda x: x['opp_card'].get_list().name[:5], axis=1)

# クローズ時期や金額の記載があるdescriptionの列を追加
opp_df['desctiption'] = opp_df.apply(lambda x: x['opp_card'].description, axis=1)

# 金額列の追加
def get_budget(s):
  s_list = s.split('\n')
  for i in range(len(s_list)):
    if '# 予算' in s_list[i]:
      return s_list[i + 1]
opp_df['金額'] = opp_df.apply(lambda x: get_budget(x['desctiption']), axis=1)

# 受注予定日列を追加
def get_close_date(s):
  s_list = s.split('\n')
  for i in range(len(s_list)):
    if '# クローズ時期' in s_list[i]:
      return s_list[i + 1]
opp_df['受注予定日'] = opp_df.apply(lambda x: get_close_date(x['desctiption']), axis=1)

# カード作成日の追加
opp_df['created_date'] = opp_df.apply(lambda x: x['opp_card'].created_date, axis=1)

# カード移動日の追加（カード移動していなければNaT）
opp_df['moved_date'] = opp_df.apply(lambda x: x['opp_card'].listCardMove_date()[0][2] if len(x['opp_card'].listCardMove_date()) else np.nan, axis=1)

# 商談日数の追加
opp_df['商談日数'] = datetime.now() - opp_df['created_date'].dt.tz_convert(None)

# 滞留日数
opp_df['滞留日数'] = datetime.now() - opp_df['moved_date'].dt.tz_convert(None) 
opp_df['滞留日数'] = opp_df.apply(lambda x: x['商談日数'] if pd.isnull(x['滞留日数']) else x['滞留日数'], axis=1)

# ネクストステップ列の追加
opp_df['comments'] = opp_df.apply(lambda x: x['opp_card'].comments, axis=1) 
def get_next_action(c):
  def _get_next_action(t):
    t_list = t.split('\n')
    for i in range(len(t_list)):
      if '# Next Action' in t_list[i]:
        return t_list[i + 1]
  date_text_list = [(x['date'], x['data']['text']) for x in c]
  date_text_list.sort(key=lambda x: x[0], reverse=True)
  for x in date_text_list:
    r = _get_next_action(x[1])
    if r:
      return r
opp_df['ネクストステップ'] = opp_df.apply(lambda x: get_next_action(x['comments']), axis=1)

opp_df[['顧客名', '案件名', 'フェーズ', '金額', '商談日数', '滞留日数', 'ネクストステップ']]
```

結果は以下のようになっており、求めたい表が得られることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984649/09f52d4a-85d1-f081-ec2b-0439ea9706b0.png)

#6. Notebookの全容
今回の記事で扱ったNotebookの全容を貼り付けます。ぜひ楽しんでください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984649/62712b49-4c99-c44c-bc57-8ea333607d77.png)

#7. まとめと次回予告
本記事では以下のトピックを扱いました。

* Google ColaboratoryでNotebookを作成
* 前回作ったボードからボードの情報を取得
* 商談レポートを自動生成

次回は以下のトピックを扱おうと考えてます。

* Plotly Dashを使って打ち合わせで使える情報を可視化

次回記事: https://qiita.com/yazn/items/f1e0d05f14db49473b1f