Djangoでカレンダー その3
先日、CSSを使ってカレンダー(1日分)を作りましたが、それをDjangoのカレンダーアプリに
組み込んでみました。
python manage.py runserver
で開発用のサーバを起動し、ブラウザでカレンダーのURLを入力すると、
以下のような感じで表示されました。
ビュー関数はこんな感じです。
def daily(request, year, month, day): ''' 1日単位のカレンダーを表示するビュー。 year: 年 month: 月 day: 日 ''' # 日付のデータを検索 taskList = Task.objects.filter(task_date__year=year, task_date__month=month, task_date__day=day) taskList = taskList.order_by('time_start') # ユーザ名(ニックネーム)をキーとした辞書にタスクを振り分ける userDicts = {} for task in taskList: userTasks = userDicts.get(task.user_setting.nickname) if userTasks == None: userTasks = [] userDicts[task.user_setting.nickname] = userTasks userTasks.append(task) d = {'year': year, 'month': month, 'day': day, 'task_list': userDicts} return render_to_response('daily.html', d)
テンプレートはこんな感じで書いてあります。
{% extends "base.html" %} {% load calendar_templates %} {% block content %} <div id="container"> <div id="header1">{{year}} / {{month}} / {{day}}</div> {# 00:00 〜 23:00の時刻を表示 #} <div id="timeframe"> <div class="timelabel">Time</div> <div class="timeline">00:00</div> <div class="timeline">01:00</div> <div class="timeline">02:00</div> <div class="timeline">03:00</div> <div class="timeline">04:00</div> <div class="timeline">05:00</div> <div class="timeline">06:00</div> <div class="timeline">07:00</div> <div class="timeline">08:00</div> <div class="timeline">09:00</div> <div class="timeline">10:00</div> <div class="timeline">11:00</div> <div class="timeline">12:00</div> <div class="timeline">13:00</div> <div class="timeline">14:00</div> <div class="timeline">15:00</div> <div class="timeline">16:00</div> <div class="timeline">17:00</div> <div class="timeline">18:00</div> <div class="timeline">19:00</div> <div class="timeline">20:00</div> <div class="timeline">21:00</div> <div class="timeline">22:00</div> <div class="timeline">23:00</div> </div> {# ユーザごとのタスクリストを取得して処理 #} {% for user, tasks in task_list.items %} {# ユーザ数をもとに、スタイルシートの列幅を指定 #} <div class="userframe width_user_x{{task_list.keys|length}}"> {# ユーザ名(ニックネーム)を表示 #} <div class="userlabel">{{user}}</div> {# 変数task_rowsに時系列のタスク情報を取得する #} {% daily_task_rows tasks as task_rows %} {# タスク情報の内容をもとに、時刻ごとのタスクを表示する #} {% for hours, tsk in task_rows %} {% if tsk %} {# 時刻にタスクが割り当てられている場合 #} <div class="taskfield timeunit{{hours|cut:"."}} active"> {{tsk}}({{tsk.time_start|time:"H:i"}} 〜 {{tsk.time_end|time:"H:i"}}) </div> {% else %} {# 時刻の場合にタスクが割り当てられていない場合 #} <div class="taskfield timeunit{{hours|cut:"."}} normal"></div> {% endif %} {% endfor %} </div> {% endfor %} <div class="clear"><hr/></div> </div> {% endblock content %}
上記のこの部分、
{# ユーザ数をもとに、スタイルシートの列幅を指定 #} <div class="userframe width_user_x{{task_list.keys|length}}">
ユーザ数が増えると、1ユーザ分の幅を狭くする必要があると思い、
ユーザ数ごとの幅をCSSで定義しておいてそれを切り替えるようにしました。。
CSSには以下のように書いてあります。
.width_user_x1 { width:90%; } .width_user_x2 { width:45%; } .width_user_x3 { width:30%; } .width_user_x4 { width:22.5%; } .width_user_x5 { width:18%; }
本来はこんな書き方はしないのかも知れませんが、この方法しか思いつきませんでした。。(笑)
また、上記のこの部分、
{# 変数task_rowsに時系列のタスク情報を取得する #} {% daily_task_rows tasks as task_rows %}
daily_task_rowsというカスタムタグを作って呼び出しています。
daily_task_rowsはどんなものかというと、こんな感じです。
(もっと簡潔にかければよかったんですが。。)
@register.tag(name='daily_task_rows') def do_daily_task_rows(parser, token): try: tag, args = token.contents.split(None, 1) except ValueError: tag = token.contents.split()[0] msg = '%r tag requires exactly two arguments' % tag raise template.TemplateSyntaxError, msg match = re.search(r'(.*?) as (\w+)', args) if not match: msg = '%r tag had invalid arguments' % tag raise template.TemplateSyntaxError, msg tasks, varName = match.groups() return DailyTaskRowsNode(tasks, varName) class DailyTaskRowsNode(template.Node): def __init__(self, tasks, varName): self.tasks = tasks self.varName = varName def render(self, context): ''' 00:00〜23:00の各時刻へのタスクの割り当て状態のリストを作成し、 self.varNameで指定された変数へ設定する。 割り当て状態のリストはタプルのリストとなっており、それぞれのタプルの情報 は以下のようになっている。 (タスクの継続時間, タスクのインスタンス) 継続時間 :最小値は0.5(30分)〜。 タスクのインスタンス:タスクが割り当てられていない場合にはNone ''' rows = [] tasks = resolve_variable(self.tasks, context) if not tasks: context[self.varName] = rows return '' # 最後のタスクまでの処理を行う prevEd = 0 for t in tasks: # タスクの開始、終了時刻を取得(単位:秒) taskSt, taskEd = t.get_range_as_seconds() if taskSt < prevEd: continue # タスク開始時刻の端数(30秒未満)を切り捨てる odd = taskSt % 1800 if odd: taskSt -= odd # タスク終了時刻の端数(30秒未満)を切り上げる odd = taskEd % 1800 if odd: taskEd += 1800 - odd # 直前のタスクの終了時刻から、現在処理中のタスク開始時刻 # までの時刻を求め、30分を1単位として、いくつぶんになるか # 計算。 emptySec = taskSt - prevEd halfHours = emptySec / 1800 # 直前のタスクの終了時刻から、現在処理中のタスク開始時刻 # までの時刻を1時間単に変換する。 hours = halfHours / 2 # 1時間単位で空の行を設定する rows += [('1.0', None) for h in range(hours)] # 30分の端数が出た場合のケア if halfHours % 2: rows.append(('0.5', None)) # タスクの継続時間(秒数)を求める duration = taskEd - taskSt # タスクの継続時間が30分を1として、いくつぶんになるか計算。 # 30分未満の場合は切り上げする。 halfHours = duration / 1800 rows.append((float(halfHours/2.0), t)) prevEd = taskEd # 最後のタスクが**:30で終わっていた場合、 # 30分の行を補う。 odd = prevEd % 3600 if odd: rows.append(('0.5', None)) prevEd += 3600 - odd # 最後のタスクから24:00までの空白の時間の処理 hours = 24 - (prevEd / 3600) rows += [('1.0', None) for h in range(hours)] context[self.varName] = rows return ''
少し長くなりました。。それにソース中のコメント文章がぐちゃぐちゃ。。
次回は1ヶ月単位のカレンダーの予定です。