Project

General

Profile

Patch #245 ยป tracker_based_gantt_calendar.diff

Ehren Foss, 2008-02-03 11:02

View differences:

app/controllers/trackers_controller.rb (working copy)
18 18
class TrackersController < ApplicationController
19 19
  layout 'base'
20 20
  before_filter :require_admin
21
  
22
  helper :issues
23
  helper IssuesHelper
24
  helper :projects
25
  helper ProjectsHelper
21 26

  
22 27
  def index
23 28
    list
......
81 86
    end
82 87
    redirect_to :action => 'list'
83 88
  end  
89

  
90
  def gantt
91
    @trackers = Tracker.find(:all, :order => 'position')
92
    retrieve_selected_tracker_ids(@trackers)
93

  
94
    if params[:year] and params[:year].to_i >0
95
      @year_from = params[:year].to_i
96
      if params[:month] and params[:month].to_i >=1 and params[:month].to_i <= 12
97
        @month_from = params[:month].to_i
98
      else
99
        @month_from = 1
100
      end
101
    else
102
      @month_from ||= Date.today.month
103
      @year_from ||= Date.today.year
104
    end
105

  
106
    zoom = (params[:zoom] || User.current.pref[:gantt_zoom]).to_i
107
    @zoom = (zoom > 0 && zoom < 5) ? zoom : 2
108
    months = (params[:months] || User.current.pref[:gantt_months]).to_i
109
    @months = (months > 0 && months < 25) ? months : 6
110

  
111
    # Save gantt paramters as user preference (zoom and months count)
112
    if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
113
      User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
114
      User.current.preference.save
115
    end
116

  
117
    @date_from = Date.civil(@year_from, @month_from, 1)
118
    @date_to = (@date_from >> @months) - 1
119

  
120
    @events = []
121
    @events += Issue.find(:all,
122
                          :order => "start_date, due_date",
123
                          :include => [:tracker, :status, :assigned_to, :priority, :project],
124
                          :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null and #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')}))", @date_from, @date_to, @date_from, @date_to, @date_from, @date_to]
125
                           ) unless @selected_tracker_ids.empty?
126
#    @events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @date_from, @date_to])
127
    @events.sort! {|x,y| x.start_date <=> y.start_date }
128

  
129
    if params[:format]=='pdf'
130
      @options_for_rfpdf ||= {}
131
      @options_for_rfpdf[:file_name] = "#{@project.identifier}-gantt.pdf"
132
      render :template => "projects/gantt.rfpdf", :layout => false
133
    elsif params[:format]=='png' && respond_to?('gantt_image')
134
      image = gantt_image(@events, @date_from, @months, @zoom)
135
      image.format = 'PNG'
136
      send_data(image.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png")
137
    else
138
      render :template => "trackers/gantt.rhtml"
139
    end
140
  end
141

  
142
  def calendar
143
    @trackers = Tracker.find(:all, :order => 'position')
144
    retrieve_selected_tracker_ids(@trackers)
145

  
146
    if params[:year] and params[:year].to_i > 1900
147
      @year = params[:year].to_i
148
      if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
149
        @month = params[:month].to_i
150
      end
151
    end
152
    @year ||= Date.today.year
153
    @month ||= Date.today.month
154
    @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
155

  
156
    events = []
157
    events += Issue.find(:all,
158
                         :include => [:tracker, :status, :assigned_to, :priority, :project],
159
                         :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?)) AND #{Issue.table_name}.tracker_id IN (#{@selected_tracker_ids.join(',')})", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
160
                           ) unless @selected_tracker_ids.empty?
161
#    events += @project.versions.find(:all, :conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
162
    @calendar.events = events
163

  
164
    render :layout => false if request.xhr?
165
  end
166

  
167

  
168

  
169
private
170
 def retrieve_selected_tracker_ids(selectable_trackers)
171
    if ids = params[:tracker_ids]
172
      @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
173
    else
174
      @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
175
    end
176
  end
84 177
end
app/views/trackers/gantt.rhtml (revision 0)
1
<% zoom = 1
2
@zoom.times { zoom = zoom * 2 }
3

  
4
subject_width = 330
5
header_heigth = 18
6

  
7
headers_height = header_heigth
8
show_weeks = false
9
show_days = false
10

  
11
if @zoom >1
12
    show_weeks = true
13
    headers_height = 2*header_heigth
14
    if @zoom > 2
15
        show_days = true
16
        headers_height = 3*header_heigth
17
    end
18
end
19

  
20
g_width = (@date_to - @date_from + 1)*zoom
21
g_height = [(20 * @events.length + 6)+150, 206].max
22
t_height = g_height + headers_height
23
%>
24

  
25
<div class="contextual">
26
</div>
27

  
28
<h2><%= l(:label_gantt) %></h2>
29

  
30
<% form_tag(params.merge(:month => nil, :year => nil, :months => nil)) do %>
31
<table width="100%">
32
<tr>
33
<td align="left">
34
    <input type="text" name="months" size="2" value="<%= @months %>" />
35
    <%= l(:label_months_from) %>
36
    <%= select_month(@month_from, :prefix => "month", :discard_type => true) %>
37
    <%= select_year(@year_from, :prefix => "year", :discard_type => true) %>
38
    <%= hidden_field_tag 'zoom', @zoom %>
39
    <%= submit_tag l(:button_submit), :class => "button-small" %>    
40
</td>
41

  
42
<td align="right">
43
<%= if @zoom < 4
44
    link_to image_tag('zoom_in.png'), {:zoom => (@zoom+1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
45
  else
46
    image_tag 'zoom_in_g.png'
47
  end %>
48
<%= if @zoom > 1
49
    link_to image_tag('zoom_out.png'),{:zoom => (@zoom-1), :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects]}
50
  else
51
    image_tag 'zoom_out_g.png'
52
  end %>
53
</td>
54
</tr>
55
</table>
56
<% end %>
57

  
58
<% cache(:year => @year_from, :month => @month_from, :months => @months, :zoom => @zoom, :tracker_ids => @selected_tracker_ids, :subprojects => params[:with_subprojects], :lang => current_language) do %>
59

  
60
<table width="100%" style="border:0; border-collapse: collapse;">
61
<tr>
62
<td style="width:<%= subject_width %>px;">
63

  
64
<div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
65
<div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
66
<div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
67
<%
68
#
69
# Tasks subjects
70
#
71
top = headers_height + 8
72
@events.each do |i| %>
73
    <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>    
74
    <% if i.is_a? Issue %>
75
      	<%= link_to_issue i %><%= " (#{i.project.name})" unless @project && @project == i.project %>:
76
      	<%=h i.subject %>
77
  	<% else %>
78
      	<%= link_to_version i, :class => "icon icon-package" %>
79
  	<% end %>  	
80
  	</small></div>
81
    <% top = top + 20
82
end %>
83
</div>
84
</td>
85
<td>
86

  
87
<div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
88
<div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
89
<% 
90
#
91
# Months headers
92
#
93
month_f = @date_from
94
left = 0
95
height = (show_weeks ? header_heigth : header_heigth + g_height)
96
@months.times do 
97
	width = ((month_f >> 1) - month_f) * zoom - 1
98
	%>
99
	<div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
100
	<%= link_to "#{month_f.year}-#{month_f.month}", { :year => month_f.year, :month => month_f.month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }, :title => "#{month_name(month_f.month)} #{month_f.year}"%>
101
	</div>
102
	<% 
103
	left = left + width + 1
104
	month_f = month_f >> 1
105
end %>
106

  
107
<% 
108
#
109
# Weeks headers
110
#
111
if show_weeks
112
	left = 0
113
	height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
114
	if @date_from.cwday == 1
115
	    # @date_from is monday
116
        week_f = @date_from
117
	else
118
	    # find next monday after @date_from
119
		week_f = @date_from + (7 - @date_from.cwday + 1)
120
		width = (7 - @date_from.cwday + 1) * zoom-1
121
		%>
122
		<div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
123
		<% 
124
		left = left + width+1
125
	end %>
126
	<%
127
	while week_f <= @date_to
128
		width = (week_f + 6 <= @date_to) ? 7 * zoom -1 : (@date_to - week_f + 1) * zoom-1
129
		%>
130
		<div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
131
		<small><%= week_f.cweek if width >= 16 %></small>
132
		</div>
133
		<% 
134
		left = left + width+1
135
		week_f = week_f+7
136
	end
137
end %>
138

  
139
<% 
140
#
141
# Days headers
142
#
143
if show_days
144
	left = 0
145
	height = g_height + header_heigth - 1
146
	wday = @date_from.cwday
147
	(@date_to - @date_from + 1).to_i.times do 
148
	width =  zoom - 1
149
	%>
150
	<div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
151
	<%= day_name(wday).first %>
152
	</div>
153
	<% 
154
	left = left + width+1
155
	wday = wday + 1
156
	wday = 1 if wday > 7
157
	end
158
end %>
159

  
160
<%
161
#
162
# Tasks
163
#
164
top = headers_height + 10
165
@events.each do |i| 
166
  if i.is_a? Issue 
167
	i_start_date = (i.start_date >= @date_from ? i.start_date : @date_from )
168
	i_end_date = (i.due_date <= @date_to ? i.due_date : @date_to )
169
	
170
	i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
171
	i_done_date = (i_done_date <= @date_from ? @date_from : i_done_date )
172
	i_done_date = (i_done_date >= @date_to ? @date_to : i_done_date )
173
	
174
	i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
175
	
176
	i_left = ((i_start_date - @date_from)*zoom).floor 	
177
	i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2                  # total width of the issue (- 2 for left and right borders)
178
	d_width = ((i_done_date - i_start_date)*zoom).floor - 2                     # done width
179
	l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
180
	%>
181
	<div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
182
	<% if l_width > 0 %>
183
	    <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
184
	<% end %>
185
	<% if d_width > 0 %>
186
	    <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
187
	<% end %>
188
	<div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
189
	<%= i.status.name %>
190
	<%= (i.done_ratio).to_i %>%
191
	</div>
192
	<% # === tooltip === %>
193
	<div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
194
	<span class="tip">
195
    <%= render_issue_tooltip i %>
196
	</span></div>
197
<% else 
198
    i_left = ((i.start_date - @date_from)*zoom).floor
199
    %>
200
    <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
201
	<div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
202
	<strong><%= i.name %></strong>
203
	</div>
204
<% end %>
205
	<% top = top + 20
206
end %>
207

  
208
<% end # cache 
209
%>
210

  
211
<%
212
#
213
# Today red line (excluded from cache)
214
#
215
if Date.today >= @date_from and Date.today <= @date_to %>
216
    <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
217
<% end %>
218

  
219
</div>
220
</td>
221
</tr>
222
</table>
223

  
224
<table width="100%">
225
<tr>
226
<td align="left"><%= link_to ('&#171; ' + l(:label_previous)), :year => (@date_from << @months).year, :month => (@date_from << @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
227
<td align="right"><%= link_to (l(:label_next) + ' &#187;'), :year => (@date_from >> @months).year, :month => (@date_from >> @months).month, :zoom => @zoom, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] %></td>
228
</tr>
229
</table>
230

  
231
<div class="contextual"><%= l(:label_export_to) %>
232
<%= link_to 'PDF', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'pdf'}, :class => 'icon icon-pdf' %>
233
<%= link_to 'PNG', {:zoom => @zoom, :year => @year_from, :month => @month_from, :months => @months, :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects], :format => 'png'}, :class => 'icon icon-image' if respond_to?('gantt_image') %>
234
</div>
235

  
236
<% content_for :sidebar do %>
237
    <h3><%= l(:label_gantt) %></h3>
238
    <% form_tag(params.merge(:tracker_ids => nil, :with_subprojects => nil)) do %>
239
    <% @trackers.each do |tracker| %>
240
      <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
241
    <% end %>
242
    <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
243
    <% end %>
244
<% end %>
app/views/trackers/calendar.rhtml (revision 0)
1
<% cache(:year => @year, :month => @month, :tracker_ids => @selected_tracker_ids, :subprojects => params[:with_subprojects], :lang => current_language) do %>
2
<h2><%= l(:label_calendar) %>: <%= "#{month_name(@month).downcase} #{@year}" %></h2>
3

  
4
<table width="100%">
5
<tr><td align="left">
6
    <%= link_to_remote ('&#171; ' + (@month==1 ? "#{month_name(12)} #{@year-1}" : "#{month_name(@month-1)}")), 
7
                        {:update => "content", :url => { :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }},
8
                        {:href => url_for(:action => 'calendar', :year => (@month==1 ? @year-1 : @year), :month =>(@month==1 ? 12 : @month-1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects])}
9
                        %>
10
</td><td align="right">
11
    <%= link_to_remote ((@month==12 ? "#{month_name(1)} #{@year+1}" : "#{month_name(@month+1)}") + ' &#187;'), 
12
                        {:update => "content", :url => { :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects] }},
13
                        {:href => url_for(:action => 'calendar', :year => (@month==12 ? @year+1 : @year), :month =>(@month==12 ? 1 : @month+1), :tracker_ids => @selected_tracker_ids, :with_subprojects => params[:with_subprojects])}
14
                        %>
15
</td></tr>
16
</table>
17

  
18
<%= render :partial => 'common/calendar', :locals => {:calendar => @calendar} %>
19

  
20
<%= image_tag 'arrow_from.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_day) %><br />
21
<%= image_tag 'arrow_to.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_end_day) %><br />
22
<%= image_tag 'arrow_bw.png' %>&nbsp;&nbsp;<%= l(:text_tip_task_begin_end_day) %><br />
23
<% end %>
24

  
25
<% content_for :sidebar do %>
26
    <h3><%= l(:label_calendar) %></h3>
27
    
28
    <% form_tag() do %>
29
    <p><%= select_month(@month, :prefix => "month", :discard_type => true) %>
30
    <%= select_year(@year, :prefix => "year", :discard_type => true) %></p>
31
    
32
    <% @trackers.each do |tracker| %>
33
      <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %></label><br />
34
    <% end %>
35
    <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
36
    <% end %>
37
<% end %>
    (1-1/1)