2008年9月3日水曜日

Google App Engine でダイエット表の作成 (1)

追記(2008.9.6) : ダイエット表を作成するには → http://4diet.appspot.com/


Google App Engine でシンプルなアプリを作成する計画」 の続き。

 

実装方法について考える

早速実装について考える。「設定画面」については、ただのフォームなので問題なし。では、「表」の方は何で作ればいいのか?表と言えば、 HTML の table 要素。 table 要素でマス目を細かくして、CSS で枠の太さや背景となる色を設定してやればいいのかな? JavaScritp で簡単にサクっとカスタマイズしたテーブルを作成するライブラリがあればいいけれど、 よく知らないのでとりあえず JavaScript は横に置いておく。

必要なメモを探して、実装開始。

 

画面遷移

まずは画面に対応した HTML ファイルを二つ作成し、「作成」ボタンを押したら、表が表示される HTML へと遷移するようにした。同時に、フォームの設定内容を受ける Diet クラスを作成し、遷移先のテンプレートでその情報にアクセスできるようにした。

path3258

 

表の作成

「表」のページでは、まずマス目を作成しなくてはいけない。 A4 の用紙で印刷した場合に、 1 ページで納まるマス目を「印刷プレビュー」を見ながら探る。

マス目を描くには、テンプレートファイルにおいて for タグ を使う。

{% for 変数 in リスト %}
    {{ 変数.プロパティ}}
{% endfor %}

これは上手くいった。 ^^

次に、表の枠・背景の色をカスタマイズするために td 要素において各々 class 指定をし、 CSS で設定を行う。土・日曜日の列を「青・赤」に設定するには、Diet オブジェクトから「年月」情報を得て、列番号とその月の日付の曜日に合わせて class 情報を設定しなくてはいけない。(cf. Python である月の日曜日の日付のリストを取得)

for タグの中で、 if タグを使って、

{% for 変数 in リスト %}
    {% if 変数.プロパティ == 値 %}
        処理 A
    {% else %}
        処理 B
    {% endif %}
{% endfor %}

としたら、あれ?(@_@;) エラーが…。 for タグで取り出した要素オブジェクトを使って、 if タグで値の検査をすることができないようだ。うーん、使い方が悪いのかなぁ。

少し記事が古いが、Pythoneer » Django’s template syntax, enhanced ifchanged にループの中で if タグを使う場合、

{% if forloop.counter%2==0 %}
    row1
{% else %}
    row2
{% endif %}

But that doesn’t work in Django templates :-(.

とあったので、やはりダメなのかもしれない…。

 

table 要素を作成するクラス

表を作成するために、テンプレートファイルにおいてテンプレートタグを使うのは諦めた。 (+_+) もしかすると、カスタムタグを使えばいいのかもしれないけれど、 Django については Google App Engine での説明でしか読んだことがないのでわからない。 ^^; また今度ちゃんと読んでから考えよう。

仕方がないので Diet オブジェクトからフォームに入力された情報をもらって、HTML の table 要素を文字列として出力する DietTable クラスを作成することにした。

g5240

入力されたフォームの値を検証するメソッド validate()と、table 要素を出力する html_trs() を実装。「土日」の背景色を設定するだけでなく、表の「枠の太さ」も条件によって変えるようにしたら、こんなことに…。 (@_@;)

(一つの要素に複数のスタイル情報をつけるには、「CSS で複数の class を指定する」を参照。)

class DietTable:
    COLS_NUM = 32
    START_COL_NUM = 1
    
    def __init__(self, year, month, weight, target):
        self.year, self.month, self.weight, self.target = year, month, weight, target
        self.rows = range(0,38)
        self.cols = range(0,32)

    # 入力された値を検証する
    def validate(self):
        # TODO
        error = []
        
        # 「年」が数字に変換できることを確認する
        if self.year.isdigit(): self.year = int(self.year)
        else: error.append("「年」を数字で入力してください。")
        # 「月が数字に変換できることを確認する
        if self.month.isdigit(): self.month = int(self.month)
        else: error.append("「月」を数字で入力してください。")
        
        # 目標体重が適切か?
        if error: raise DietValErr, error

    # HTML で TR 要素を出力
    def html_trs(self):
        result = ""
        for i in self.rows:
            result += ""
            if i == len(self.rows) - 1:
                # 最後の行は横軸方向を太くする
                result += self.html_tds(x_axis=True)
            else:
                result += self.html_tds()
            result += ""
        return result
    
    # HTML で TD 要素を出力
    def html_tds(self, x_axis=False):
        result = ""
        # 日曜日、土曜日の列に色を付ける
        # 色はこのオブジェクトを使用するテンプレートの CSS で指定
        for i in range(0, DietTable.COLS_NUM):
            if i in [self._dayColPosition(x)
                        for x in self._dayslist(calendar.SUNDAY)]:
                # 日曜日の場合
                if i == 0: result += self._html_td("", x_axis, y_axis=True)
                else: result += self._html_td("sunday", x_axis)
            elif i in [self._dayColPosition(x)
                            for x in self._dayslist(calendar.SATURDAY)]:
                # 土曜日の場合
                if i == 0: result += self._html_td("", x_axis, y_axis=True)
                else: result += self._html_td("saturday", x_axis)
            else:
                if i == 0: result += self._html_td("", x_axis, y_axis=True)
                else: result += self._html_td("", x_axis)
        return result

    # このオブジェクトの年月における指定された曜日の日付のリストを返す
    def _dayslist(self, dayOftheWeek):
        return [x[dayOftheWeek]
            for x in calendar.monthcalendar(self.year, self.month)]

    # td 要素を指定した曜日を入れて文字列として返す
    def _html_td(self, dayOftheWeek="", x_axis=False, y_axis=False):
        if   x_axis and y_axis:
            return " "
        elif x_axis and not y_axis:
            return " "
        elif not x_axis and y_axis:
            return " "
        else:
            return " "

    # 一日の列を基準にして、指定した日付の列の相対的な位置を返す
    def _dayColPosition(self, x):
        return x + self.START_COL_NUM - 1

 

もうだめだ。翌日になって見直したら、何が書いてあるのかよくわからなくなった。このまま実装を続けるのは放棄。 ^^; だいたいこんな if if して見にくいコードなんて読みたくもない。 (+_+)


関連記事