タイタニックから学ぶタイムマネジメント

この記事は ハンズラボ [Advent Calendar 2017](https://qiita.com/advent-calendar/2017/handslab) 14日目の記事です。

こんにちは、今回はタイタニックを題材にタイムマネジメントについて考えてみたいと思います。


# タイムマネジメントを意識したきっかけ

私はGoogle Cloud Next '17 in Tokyoで、イアン リーダーさんのセッション
「[時間を有効に使うためのテクノロジーの活用方法](https://cloudnext.withgoogle.com/tokyo/schedule#target=-f6b4f6df-805a-4224-af7b-a3c81d8a71ef)」
に感銘を受けて、そこからタイムマネジメントに興味を持つようになりました。
ぜひリンク先の動画を見てみてください。


# タイタニックのジャックの生き方
タイタニックに登場する富裕層は毎日決まったルーティンで生活していて、
同じことの繰り返しの日々にローズは飽き飽きしてしまいます。
そんな中、自由の象徴として現れたのがジャックです。
ジャックは常に今という時間を大切に生きています。
食事会でジャックが自分の生き方を語るシーンがあります。

> 朝、目を覚ますとまた未知の1日が始まる。
何が起こるか。
橋の下で眠ることもあれば、
世界一の豪華客船でシャンパン
人生は贈り物
ムダにはしたくない
どんなカードが配られても
それも人生
毎日を大切に


つまり、ローズはタイムマネジメントが難しい状態。
ジャックは自分の時間をコントロールできるのでタイムマネジメントで理想的な状態です。

# 未知の1日が始まるタスク管理アプリ

TODOアプリやカレンダーアプリでタスク管理をすると、確かに時間の管理はできますが、
時間に縛られているような気持ちになってしまいます。
自分主体で未知の1日が始められるTODOアプリを作れないかなーと思っています。
どんなアプリが良いかはまだまとまっていませんが、

- タスクを登録すると、スケジュールを考えてくれる。
- その日のコンディションによって、タスクをレコメンドしてくれる。

のような自分のタスク整理の手助けしてくれるものが将来的にできたらと思います。
今回はAmazon Lexを試してみて、TODOのタスク作成部分だけ作ってみました。

### Amazon Lexでタスクを作成
<img width="414" alt="Simulator Screen Shot - iPhone 8 Plus - 2017-12-14 at 03.19.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/308f4b82-0e52-65b7-0154-d920ea1e2e80.png">

### Amazon Lexでタスクを作成　バリデーション付きバージョン
lambdaで特定の語句をエラーにして弾くこともできます。
<img width="414" alt="Simulator Screen Shot - iPhone 8 Plus - 2017-12-14 at 03.20.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/126805d6-8990-9df5-e9eb-127a7c90e98d.png">

## Amazon Lexの設定
###　起動文言
<img width="526" alt="スクリーンショット 2017-12-14 2.18.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/7db5d4e7-566d-f6d9-99ec-4a66cfebe58e.png">
###　lambdaを指定
<img width="419" alt="スクリーンショット 2017-12-14 2.18.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/98eb2b36-bf9e-7abe-372e-2feafa177e1c.png">
###　TaskTitleというSlotを作成する
<img width="822" alt="スクリーンショット 2017-12-14 2.18.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/eb8e3e1e-f80d-4a19-fe1a-3ffde0c48fce.png">
###　確認文言
<img width="493" alt="スクリーンショット 2017-12-14 2.18.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/777e5654-2163-ef7b-7731-65884d3d2d63.png">
###　lambdaを指定
<img width="493" alt="スクリーンショット 2017-12-14 2.18.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/210767/0c36f956-65c0-8f84-fbaf-1eb8d4644572.png">

### lambda
```python
import time
import os
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

def get_slots(intent_request):
    return intent_request['currentIntent']['slots']

def elicit_slot(session_attributes, intent_name, slots, slot_to_elicit, message):
    return {
        'sessionAttributes': session_attributes,
        'dialogAction': {
            'type': 'ElicitSlot',
            'intentName': intent_name,
            'slots': slots,
            'slotToElicit': slot_to_elicit,
            'message': message
        }
    }


def close(session_attributes, fulfillment_state, message):
    response = {
        'sessionAttributes': session_attributes,
        'dialogAction': {
            'type': 'Close',
            'fulfillmentState': fulfillment_state,
            'message': message
        }
    }

    return response

def delegate(session_attributes, slots):
    return {
        'sessionAttributes': session_attributes,
        'dialogAction': {
            'type': 'Delegate',
            'slots': slots
        }
    }

def build_validation_result(is_valid, violated_slot, message_content):
    if message_content is None:
        return {
            "isValid": is_valid,
            "violatedSlot": violated_slot,
        }

    return {
        'isValid': is_valid,
        'violatedSlot': violated_slot,
        'message': {'contentType': 'PlainText', 'content': message_content}
    }

def validate_create_task(task_title):
    if task_title is not None:
        if task_title in ["English","english"]:
            return build_validation_result(False, 'TaskTitle', 'No kidding! You said the same thing last year.')

    return build_validation_result(True, None, None)

def create_task(intent_request):
    task_title = get_slots(intent_request)["TaskTitle"]
    source = intent_request['invocationSource']

    if source == 'DialogCodeHook':
        slots = get_slots(intent_request)

        validation_result = validate_create_task(task_title)
        if not validation_result['isValid']:
            slots[validation_result['violatedSlot']] = None
            return elicit_slot(intent_request['sessionAttributes'],
                               intent_request['currentIntent']['name'],
                               slots,
                               validation_result['violatedSlot'],
                               validation_result['message'])

        output_session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {}

        return delegate(output_session_attributes, get_slots(intent_request))

    return close(intent_request['sessionAttributes'],
                 'Fulfilled',
                 {'contentType': 'PlainText',
                  'content': 'Thanks, your task {} has been created. Make it count!'.format(task_title)})

def dispatch(intent_request):
    logger.debug('dispatch userId={}, intentName={}'.format(intent_request['userId'], intent_request['currentIntent']['name']))

    intent_name = intent_request['currentIntent']['name']

    if intent_name == 'CreateTask':
        return create_task(intent_request)

    raise Exception('Intent with name ' + intent_name + ' not supported')

def lambda_handler(event, context):
    os.environ['TZ'] = 'America/New_York'
    time.tzset()
    logger.debug('event.bot.name={}'.format(event['bot']['name']))

    return dispatch(event)

```

##　デモアプリ作成
Mobile Hubを使うと数クリックで簡単にLexと連携したデモアプリを作ることができます。

# まとめ
対話型インターフェースに大きな可能性を感じました。
今後もタスク管理のより良い形を模索していきたいと思います。



[ハンズラボ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/handslab) 明日15日目は@r_plusです！