RailsとFusionCharts Freeでデータベースを利用した動的グラフを描いてみる

前回(2010-7-6)は、RailsとFusionCharts Freeで、その仕組みを紐解きながら固定データに基づくグラフを描いてみた。今回は、Railsでデータを作成し、そのデータ(変更可能な)に基づきグラフを描く。Tutorialは、「Railsで実現する見事なグラフ」http://www.infoq.com/jp/articles/sharma-charts-in-rails にあるTimeTracker Applicationである。小生の開発環境は、Windows Vista/Git/Rails2.3.5/SQLite3である。
1.先ず、従業員とその各従業員の作業時間を記録するアプリをscaffoldを使って簡単に生成。
rails TimeTrackerApplication
script/generate scaffold Employee name:text
script/generate scaffold Timesheet log_date:datetime hours_spent:integer employee_id:integer
rake db:migrate
2.次に、2つのテーブルの関連の設定
(employee.rb) has_many:timesheets
(timesheet.rb) belongs_to:employee
3.FusionChartsの導入
(app/public)に、Download Package>Code>FusionChartsをコピー
(app/public/javascript)に、Download Package>Code>FusionCharts>FusionCharts.jsをコピー
(app/lib)に、Download Package>Code>Libraries>fusioncharts_helperをコピー
4.Employee/index/viewに、TimeSheetへ飛ぶリンクとグラフを描くリンクを設置
(app/views/employees/index.html.erb)<% @employees.each do |employee| %>
・・・・
<%= link_to 'Time Sheet', {:controller => 'timesheets', :action => 'show', :id =>employee.id} %>
<%= link_to 'Time Chart', {:action => 'view_timesheet_chart', :id =>employee.id} %>
ついでに、画面遷移のためのリンクも設置<%= link_to 'New employee', new_employee_path %><%= link_to 'Timesheets', :controller => 'timesheets', :action => 'index' %>
5.いよいよグラフを描くAction('Time Chart'へのリンクで定義した)をEmployeesControllerに記述
(app/controllers/employees_controller.rb)
def view_timesheet_chart
start_date="2010-06-07"
end_date="2010-06-12"
@employee_id = params[:id]
employee_details_with_timesheets = Employee.find_with_timesheets_in_date_range(@employee_id,start_date,end_date)
if(!employee_details_with_timesheets.nil?)
@employee_details_with_timesheets =employee_details_with_timesheets[0]
else
@employee_details_with_timesheets =nil;
end
headers["content-type"]="text/html"
end
start_dateとend_dateは、適当に設定。これを可変にするのは次の課題。
6.Employeeモデルに、上記のActionを定義する。
(app/models/employee.rb)
class Employee < ActiveRecord::Base
has_many :timesheets
def self.find_with_timesheets_in_date_range(id, start_date, end_date)
conditions="employees.id =? and employees.id=timesheets.employee_id and log_date between ? and ?"
employee_details_with_timesheets=self.find(:all, :include=>'timesheets', :conditions=> [conditions,id,start_date,end_date], :order=>'log_date asc')
return employee_details_with_timesheets
end
end
7.Action 'view_timesheet_chart'に対応するviewを新規に追加する。
(app/views/employees/view_timesheet_chart.html.erb)<%= javascript_include_tag "FusionCharts"%><%
if(!@employee_details_with_timesheets.nil?)
# The xml is obtained as a string from builder template.
str_xml = render "employees/timesheet_data"
#Create the chart - Column 3D Chart with data from strXML
render_chart '/FusionCharts/FCF_Column3D.swf','', str_xml, 'Timelog', 650, 400, false, false do-%><% end -%><%= link_to 'New employee', new_employee_path %><%= link_to 'Timesheets', :controller => 'timesheets', :action => 'index' %><%= link_to 'Show', @employee %> |<%= link_to 'Listing employees', employees_path %><% else -%>

The timesheet entries for the period 2010-06-07 to 2010-06-12 were not found.
Please enter sample data.

<%end%>
プログラムの内容を解説すると、
1行目は、Railsjavascriptの読み込みメソッド、拡張子を除いたファイル名を引数として指定
以下は、FusionChartsのdataXML methodによるグラフ描出で、データを格納するstr_xmlとグラフの描き方を指定するrender_chartを記述。
画面遷移が簡単にできるようリンクを追加し、さらに、<% else -%>以下に、指定したデータが無い場合のコメントを記述。
8.XML形式のデータを作成するための仕組みであるBuilderを使って、Timesheet tableに入力したデータをXML化、オブジェクト名はxml(詳しくは、RailsAPIリファレンスまたはhttp://builder.rubyforge.org/を参照)
(app/views/employees/timesheet_data.builder)
xml = Builder::XmlMarkup.new(:indent=>0)
options = {
:caption => 'Time Tracker Chart',
:subcaption => 'For Employee '+@employee_details_with_timesheets.name,
:yAxisName=>'Hours Spent',
:xAxisName=>'Day',
:showValues=>'1',
:formatNumberScale=>'0',
:numberSuffix=>'hrs.'
}
xml.graph(options) do
for timesheet in @employee_details_with_timesheets.timesheets do
log_day = timesheet.log_date.strftime('%A')
xml.set(:name=>log_day,:value=>timesheet.hours_spent,:color=>''+get_FC_color)
end
end
9.最後に、app/helpers/application_helper.rbに下記を追記
 require 'fusioncharts_helper'
 include FusionChartsHelper
10.なお、TimeSheetを入力する時に、Employee名をドロップダウンリストを使えるように、app/views/timesheets/のnew.html.erbとedit.html.erbでは、下記の通り記述。<% form_for(@timesheet) do |f| %>
<%= f.error_messages %>


<%= f.label :employee_id %>

<%= f.collection_select("employee_id", Employee.find(:all), :id, :name) %>

以上でプログラミング終了、Railsを立ち上げ、employeeを入力し、そのemployeeの作業時間と日付を入力、employee/index画面で、Time Chartのリンクをクリックすると、3次元の棒グラフが出来ている・・・筈であったが、
TypeError in Employees#view_timesheet_chart Showing app/views/employees/view_timesheet_chart.html.erb where line #7 raised:
can't convert nil into String
いろいろ試し、ググったら、FusionChartsのDiscussion Forumの以下のような記事をみつけた。http://www.fusioncharts.com/forum/Topic14599-46-1.aspx
Quote
The FusionCharts RoR sample application was tested on rails 2.2.2. Rails 2.3 was released afterwards.
Yes, looks like the rails render function has changed.
・・・・・・
To be explicit, you can use the :file option (which was required on Rails 2.2 and earlier)
If you’re running on Microsoft Windows, you should use the :file option to render a file, because Windows filenames do not have the same format as Unix filenames.
reference: http://guides.rubyonrails.org/layouts_and_rendering.html
・・・・
Unquote
これで、解決と思い、render :fileメソッドで入力してみたが相変わらずTypeError。原因がよくわからず困り果てて、Rails勉強会@東京で初心者セッションをやってくださるYuum3( http://d.hatena.ne.jp/yuum3/ )に相談したら、数時間で解決、ありがとうございました。
変更したところは、
(イ)8.のbuilderで、ファイル名をパーシャルにする、すなわち、app/views/employees/_timesheet_data.builder アンダーバー1本を追加、
(ロ)render :fileオプションは、上記記事にもかかわらず使わない、
(ハ)その他細かい入力ミス1〜2
Employee/index画面で、Time Seetのリンクをクリックすると、きれいな3次元棒グラフが現れた。
つぎは、日本語化や使い勝手の改善に取り組むつもり。