シャッフル主任の進捗報告

興味のあるものを作ります。進捗を不定期にご報告します。

そうだ、教祖になろう。出エジプト記 第3章4節 Lambdaでサーバサイドを実装する

山の神 海の神 今年も本当にありがとう


前回の第3章3節 Lambdaでサーバサイドを実装できないUnicodeエンコーディングという後顧の憂いを断ちましたので、いよいよプログラミング祭の始まりです。

まずは、ざっくりクラス図を描きました。
読む人と返すために整える人。
あとでもうちょっと複雑化する気もしますが、今はこれでいいでしょう。

f:id:chief-shuffle:20191210210708p:plain

白い褌ひきしめた 裸若衆に雪が舞う


まずはGoogleスプレッドシートからランダムで1行取得するクラスです。
空でない行数をカウントして、ヘッダ行数を除いて2行目〜最大行目のうち、1レコードを辞書オブジェクトで返します。
取ったままの配列でもいいんですが、一応。

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '.'))
import random
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import settings


class LifeReader():

    def __init__(self, ):

    def random(self, ):
    
        credentials = ServiceAccountCredentials.from_json_keyfile_dict(
            settings.GSPREAD['account-key'], settings.GSPREAD['scope'])
        gclient = gspread.authorize(credentials)
        spreadsheet = gclient.open(settings.GSPREAD['spredsheet-title'])

        worksheet = spreadsheet.sheet1
        count = len(worksheet.col_values(2)) - 1

        row = random.randint(2, count + 1)
        values = worksheet.row_values(row)

        return {
            'who': values[1],
            'birth-min': values[2],
            'birth-max': values[3],
            'birth-step': values[4],
            'birth-unit': values[5],
            'way-of-life': values[6],
            'cause-of-death': values[7],
            'death-min': values[8],
            'death-max': values[9],
            'death-step': values[10],
            'death-unit': values[11]
        }

ちなみにブログのソースコードのスタイルを変えてみましたが、見やすいでしょうか。

次に1レコードをもらってクライアントに渡すレスポンスを作るクラス。

import random


class ResponseFormatter():

    def __init__(self, ):

    def format(
        self,
        record,
    ):
        list = []

        try:
            birth = '今から{}{}'.format(
                random.randrange(int(record['birth-max']),
                                 int(record['birth-min']),
                                 int(record['birth-step'])),
                record['birth-unit'])
        except:
            birth = record['birth-min']

        try:
            death = '{}{}'.format(
                random.randrange(int(record['death-min']),
                                 int(record['death-max']),
                                 int(record['death-step'])),
                record['death-unit'])
        except:
            death = record['death-min']

        list.append('あなたは{}に生まれ変わりました。'.format(record['who']))
        list.append('{}は{}に生まれました。'.format(record['who'], birth))
        list.append('{}生き'.format(record['way-of-life']))
        list.append('{}{}死にました。'.format(record['cause-of-death'], death))

        return list

正直こんな規模ならモジュール分割しなくてもいいんでしょうが、クラス化すると処理が擬人化されるみたいで可愛いですよね。

ね。

コードの匂いのしみこんだ 倅その手が宝物


settings.pyに諸々の定数を寄せました。
あとでProd用とStage用で設定分けるのもいいですね。

GSPREAD = {
    'account-key': {
        # Googleスプレッドシートのサービスアカウントキー
    },
    'scope': ['https://www.googleapis.com/auth/drive'],
    'spredsheet-title': 'シャッフル再生教'
}

あとはメイン処理。
2クラスを呼ぶだけです。

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '../site-packages'))
import datetime
import json
import gspread
from oauth2client.service_account import ServiceAccountCredentials
sys.path.append(os.path.join(os.path.dirname(__file__), '.'))
import settings
from lifereader import LifeReader
from responseformatter import ResponseFormatter


def lambda_handler(event, context):

    reader = LifeReader()
    record = reader.random()

    formatter = ResponseFormatter()
    response = formatter.format(record)

    return {
        'statusCode': 200,
        'body': json.dumps(response, ensure_ascii=False)
    }

実行してみます。 きちんと要素4の配列でレスポンスが返ってます。

f:id:chief-shuffle:20191210212249j:plain

祭の後のうら寂しさ。
興奮冷めやらない感覚ですが、ひとまずこれで終わりです。
画像を返したり、Googleスプレッドシートを論理削除したりは別の機会に。