本記事について
Plotlyでガントチャートを作成する方法と、それをDjangoのテンプレートに配置して表示する方法を書きます。
先に結論
-
plotly.figure_factory.create_gantt()
を読み込む -
fig = ff.create_gantt(df)
でガントチャートのオブジェクトを作成する -
plotly.offline.offline.plot()
を読み込む -
gantt = plot(fig, output_type='div', include_plotlyjs=False)
でグラフとグラフを描画するスクリプトを含むhtmlコードの文字列を取得する -
autoescape
とendautoescape
でエスケープ機能をoffにした上で、4.で取得した文字列をテンプレートにわたす
Plotlyとは
オンラインデータの分析や視覚化のツールを開発している会社で、JavaScriptやPython、Rで使えるグラフ作成ライブラリを提供しています。
Python用のライブラリを使えば、例えば以下のリンク先のようなグラフを描画することができます。
Plotly Python Open Source Graphing Library
ガントチャートの作成方法
以下のリンク先に例が記載されています。
Gantt Charts in Python
import plotly.figure_factory as ff
# 日時のフォーマットは `2000-01-01 00:00:00`
df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]
fig = ff.create_gantt(df)
fig.show()
データフレームもしくはリスト型のデータを使います。
キーがTast
,Start
,Finish
の辞書型が、ガントチャートのバー1本分に相当し、それをリスト(変数fig
とする)に格納します
ff.create_gantt()
の第一引数に変数fig
セットすることで、ガントチャートのオブジェクトが作成されます。
.show()
はブラウザを起動して、引数に入れたオブジェクトのガントチャートを描画します。
又、create_gantt()
には様々な引数があり、グラフをカスタマイズすることが可能です。
(カラーバーを表示したり、色を変えたり、太さを変えたりなどなど)
create_gantt(df, colors=None, index_col=None, show_colorbar=False, reverse_colors=False, title='Gantt Chart', bar_width=0.2, showgrid_x=False, showgrid_y=False, height=600, width=None, tasks=None, task_names=None, data=None, group_tasks=False, show_hover_fill=True)
help(ff.create_gantt)で説明が見られます。
作成したグラフをHTMLとして取得する
上記の.show()
の代わりにplotly.offline.offline.plot()
を使うことで、HTML形式のファイルを作成したり、divで囲まれたHTMLのコードを出力してくれます。Djangoのテンプレートに配置したい場合は、こちらの関数を使います。
from plotly.offline import plot
df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]
fig = ff.create_gantt(df)
plot_fig = plot(fig, output_type='div', include_plotlyjs=False)
print(plot_fig)
plot()
の引数
Chart StudiotというGUIでグラフをカスタマイズできるサービスへのリンクを表示するか否か・アニメーションを自動再生するかなど、色々ある
plot(figure_or_data, show_link=False, link_text='Export to plot.ly', validate=True, output_type='file', include_plotlyjs=True, filename='temp-plot.html', auto_open=True, image=None, image_filename='plot_image', image_width=800, image_height=600, config=None, include_mathjax=False, auto_play=True, animation_opts=None)
Djangoでplotlyを使う際は、主にinclude_plotlyjs
とoutput_type
を設定します
output_type
グラフと、グラフを生成するスクリプトを含むHTMLを、どのように出力するかの設定。
output_type='div'
グラフとグラフを生成するスクリプトを含むHTMLのコード(divに囲まれている)が、文字列で出力される
output_type=file
グラフとグラフを生成するスクリプトを含むHTMLファイルが生成される
include_plotlyjs
plotly.jsライブラリのソースコードをどのように読み込むか設定できます。
include_plotlyjs=False
plotly.js CDNを参照するコードは含まれない
output_type = 'div'
で出力したコードが、plotly.jsを読み込んでいるHTMLドキュメント内に配置されれば利用可能
include_plotlyjs=con
plotly.js CDNを参照するスクリプトタグが出力に含まれまれる。
include_plotlyjs=True
plotly.jsのソースコートが丸々含まれる。3MB程度になる。
Djangoで使うとき
Djangoはbase.html
などの基盤となるテンプレートにcss
やjavascript
の読み込みを記述し、他のテンプレートにおいて{% extends 'app/base.html' %}
としてbase.htmlを引き継ぎます。そのため、基盤となるテンプレートにplotly.jsを読み込む旨記述し、include_plotlyjs=False
とするのがよいかなと思います。
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
又、output_type='div'
で取得したHTMLコードの文字列をテンプレートに渡せば、そのテンプレート内でグラフを描画できます。
ただし、テンプレートタグの{% autoescape off %}
と{% endautoescape %}
を使い、エスケープ機能をoffにしないと正しく描画されません。
{% extends 'blog/base.html' %}
{% block content %}
{% autoescape off %}
{{ plot_gantt }} <!-- HTMLコードの文字列が格納されたコンテキスト -->
{% endautoescape %}
まとめ
-
plotly.figure_factory.create_gantt()
を読み込む -
fig = ff.create_gantt(df)
でガントチャートのオブジェクトを作成する -
plotly.offline.offline.plot()
を読み込む -
gantt = plot(fig, output_type='div', include_plotlyjs=False)
でグラフとグラフを描画するスクリプトを含むhtmlコードの文字列を取得する -
autoescape
とendautoescape
でエスケープ機能をoffにした上で、4.で取得した文字列をテンプレートにわたす
プロジェクトを立ち上げそれに向けてタスクを作成するというアプリケーションを作っており、そこで使ってみた。
すでに完了したタスクのスケジュールを取得し、ガントチャートを作成、テンプレートに配置してみる
# Projectsと、その小モデルTasks
class Projects(models.Model):
# 略
class Tasks(models.Model):
STATUS_LIST = ((0, '待機'), (1, '取組中'), (2, '完了'))
project = models.ForeignKey(Projects, on_delete=models.CASCADE, related_name='project_task')
taskName = models.CharField(max_length=100, verbose_name='タスク')
status = models.IntegerField(choices=STATUS_LIST, verbose_name='状況')
createdDate = models.DateTimeField(default=now, null=True, blank=True, verbose_name='開始日')
deadline = DateTimeField(blank=True,null=True, verbose_name='締め切り')
finishedDate = DateTimeField(blank=True,null=True, verbose_name='達成した日')
def projects_detail_view(request, p_pk):
project = Projects.objects.get(pk=p_pk)
# 完了済のタスクを取得し、開始日の昇順でタスク一覧を取得
tasks = project.project_task.filter(status=2).order_by('createdDate')
df = []
for task in tasks:
df.append(
dict(Task=task.taskName,
# create_ganttのStartとFinishで使うため、フォーマットを整形
Start="{0:%Y-%m-%d %H:%M:%S}".format(task.createdDate),
Finish="{0:%Y-%m-%d %H:%M:%S}".format(task.finishedDate),))
fig = ff.create_gantt(df, title='これまでの軌跡', bar_width=0.5, showgrid_x=True, showgrid_y=False,)
plot_html = plot(fig, output_type='div', include_plotlyjs=False)
context = {
'plot_html': plot_html,
}
return render(request, 'detail.html', context)
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
{% autoescape off %}
{{ plot_html }}
{% endautoescape %}
タスク名が長いため、グラフを圧迫している。