ガントチャートとかカレンダー

Ruby on Railsプラグインを探す。カレンダープラグインは以前につかったものしか見つからない。使った瞬間は感動するんだけど、複数日を跨ぐ表示には向いていないというか出来そうに無い。
 
そしてガントチャートを探すが、見つかるのはredMineの記事ばかり。
 
オイラがクレクレといっているのはプラグインなのだ。
 
 
仕方ないので以前に入れてみたredMineのコードを眺める。

app/controllers/projects_controller.rb
app/views/projects/calender.rhtml
app/views/projects/gantt.rhtml

modesとhelpersにもイロイロ入っているけど、大まかなところはOK
 
今欲しい機能は、redMineのプロジェクトのカレンダーだけで十分な気がしてきた。なので解読と改造を始める!(出来るのか?
 

やりたいこと

Rentalというテーブルに「品目」「ユーザー名」「貸出日時」「返却日時」とか入っている。
日付を元にした縦長の表があるけど、状況を確認しずらいのでカレンダーっぽく表示させたい。
 

コードの確認

必要なのか不必要なのか考えてみる

app/controllers/projects_controller.rb#calendar

ここね。

@trackers = Tracker.find(:all, :order => 'position')
retrieve_selected_tracker_ids(@trackers)

positionを元に

  def retrieve_selected_tracker_ids(selectable_trackers)
    if ids = params[:tracker_ids]
      @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
    else
      @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
    end
  end

たぶん要らない。
 
次の年月の作成のところ

    if params[:year] and params[:year].to_i > 1900
      @year = params[:year].to_i
      if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
        @month = params[:month].to_i
      end    
    end

1900年とか凄い確認をしている。このまま引用!

 

    @year ||= Date.today.year
    @month ||= Date.today.month
    
    @date_from = Date.civil(@year, @month, 1)
    @date_to = (@date_from >> 1)-1
    # start on monday
    @date_from = @date_from - (@date_from.cwday-1)
    # finish on sunday
    @date_to = @date_to + (7-@date_to.cwday)  

civilって何?http://www.ruby-lang.org/ja/man/html/Date.htmlを見る。

civil([year[, mon[, mday[, start]]]])
暦日付に相当する日付オブジェクトを生成します。

このクラスでは、紀元前の年を天文学の流儀で勘定します。 1年の前は零年、零年の前は-1年、のようにします。月、および月の日は負、または正の数でなければなりません (負のときは最後からの序数)。零であってはなりません。

最後の引数は、グレゴリオ暦をつかい始めた日をあらわすユリウス日です。グレゴリオ暦の指定として真、ユリウス暦の指定として偽を与えることもできます。省略した場合は、Date::ITALY (1582年10月15日) になります。

うむ。

cwday
暦週の日 (曜日) を返します (1-7、月曜は1)。

うむ。
 

@events = []
@project.issues_with_subprojects(params[:with_subprojects]) do
 @events += Issue.find(:all, 
  :include => [:tracker, :status, :assigned_to, :priority, :project], 
  :conditions => ["((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?)) and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", @date_from, @date_to, @date_from, @date_to]
  ) unless @selected_tracker_ids.empty?
end

まずは「issues_with_subprojects」って何?
 
app/models/project.rb

  def issues_with_subprojects(include_subprojects=false)
    conditions = nil
    if include_subprojects && !active_children.empty?
      ids = [id] + active_children.collect {|c| c.id}
      conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
    end
    conditions ||= ["#{Issue.table_name}.project_id = ?", id]
    Issue.with_scope :find => { :conditions => conditions } do 
      yield
    end 
  end

検索するプロジェクトを調べているっぽい。単純な構成を想定しているので、ここは要らない。
普通のfindを使うことにしよう。
 
conditionsの中

#{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", 
と
unless @selected_tracker_ids.empty?

この条件もイラネ、unlessは無くても大丈夫・・・だと思う。
 
views/projects/calendar.rhtmlも使いまわししたいので、変数とかはなるべく変更しないことにしよう!
 

コントローラ側

作ってみた。

  def rentalcalendar
    @page_title = "貸出の月間表示"
    if params[:year] and params[:year].to_i > 1900
      @year = params[:year].to_i
      if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
        @month = params[:month].to_i
      end    
    end
    @year ||= Date.today.year
    @month ||= Date.today.month
    
    @date_from = Date.civil(@year, @month, 1)
    @date_to = (@date_from >> 1)-1
    # start on monday
    @date_from = @date_from - (@date_from.cwday-1)
    # finish on sunday
    @date_to = @date_to + (7-@date_to.cwday)        
    
    @events = []
    @events += Rental.find(:all, 
    :conditions =>["lending>=? and rtning<=?", @date_from, @date_to],
    :order=>"lending, rtning")
    
    @ending_events_by_days = @events.group_by {|event| event.rtning}
    @starting_events_by_days = @events.group_by {|event| event.lending}
    
    render :layout => false if request.xhr?
  end

ポイントは

  • 予定の開始日:start_dateからlending
  • 予定の終了日:due_dateからrtning


findとconditionsのところがカスタマイズするところかな?(まだViewを作っていないので動くのか?)

ビュー側

イロイロ不要なものがある
たいへんだ・・・
 
要らない部分はどんどん削除!!

<h2><%= @page_title %> <%= @year %>年<%= @month %>月</h2>
<% form_tag do %>
<table width="100%">
<tr>
	
<th align="left" style="width:15%">
<%= link_to ('&#171; ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")),
 :action=>'rentalcalendar', :year=>(@month==1 ? @year-1 : @year), :month=>(@month==1 ? 12 : @month-1) %>
</th>

<th align="center" style="width:55%">
    <%= select_month(@month, :prefix => "month", :discard_type => true) %>
    <%= select_year(@year, :prefix => "year", :discard_type => true) %>
    <%= submit_tag ("変更"), :class => "button-small" %>
</th>

<th align="right" style="width:15%">
<%= link_to ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' &#187;'),
 :action=>'rentalcalendar', :year=>(@month==12 ? @year+1 : @year), :month=>(@month==12 ? 1 : @month+1) %>
</th>

</tr>
</table>
<% end %>

<table class="list with-cells">
<thead>
<tr class='dayName'>
<th> </th>
<% 1.upto(7) do |d| %>
    <th style='width:14%'><%= day_name(d) %></th>
<% end %>
</tr>
</thead>
<tbody>
<tr style="height:100px">
<% day = @date_from
while day <= @date_to

if day.cwday == 1 %>
<th><%= day.cweek %></th>
<% end %>	
<td valign="top" class="<%= day.month==@month ? "even" : "odd" %>"
 style="width:14%; <%= Date.today == day ? 'background:#FDFED0;' : '' %>">
<p class="textright">
	<%= link_to((day==Date.today ? "<b>#{day.day}</b>" : day.day),
 :controller => 'support/rental', :action=>'search', :rtning_from=>day, :rtning_to=>day ) %>
</p>	
<% ((@ending_events_by_days[day] || []) +
 (@starting_events_by_days[day] || [])).uniq.each do |i| %>
    <div class="tooltip">
	<%= if day == i.lending.to_date and day == i.rtning.to_date
	    image_tag('patrols/arrow_bw.png')
	elsif day == i.lending.to_date
	    image_tag('patrols/arrow_from.png') 
	elsif day == i.rtning.to_date
	    image_tag('patrols/arrow_to.png') 
	end %>
	<small><%= link_to h(i.title), :controller => 'support/rental', :action=>'edit', :id=>i %>
	 <%=h i.member_name.sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)') %></small>
	</div>
<% end %>
</td>
<%= '</tr><tr style="height:100px">' if day.cwday >= 7 and day!=@date_to %>
<%
day = day + 1

end %>
</tr>
</tbody>
</table>
<%= image_tag 'patrols/arrow_from.png' %>&nbsp;&nbsp;この日からの貸出<br />
<%= image_tag 'patrols/arrow_to.png' %>&nbsp;&nbsp;この日に返却<br />
<%= image_tag 'patrols/arrow_bw.png' %>&nbsp;&nbsp;当日に貸出と返却<br />
  • Calendar Helperを使っているのでスタイルは使いまわし
  • link_to_remoteはIE7でよい結果にならなかったのでlink_toに変更
  • 言語系は直接仕込む
  • 画像はインスパイア
  • ヘルパーを追加作成
  #redMineからのインスパイア(パクリ
  def month_name(month)
    month_arrays=["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"]
    month_arrays[month-1]
  end

  def day_name(day)
    day_arrays=["月曜日","火曜日","水曜日","木曜日","金曜日","土曜日","日曜日"]
    day_arrays[day-1]
  end

ひとまず動いているからOKとしよう。
 
完成してからの感想:僕が欲しいビューはこんなんじゃない!