├── .DS_Store ├── README.rdoc ├── app ├── controllers │ └── parking_lot_chart_controller.rb ├── helpers │ └── parking_lot_chart_helper.rb └── views │ └── parking_lot_chart │ └── index.html.erb ├── config ├── .DS_Store ├── locales │ ├── de.yml │ ├── en.yml │ ├── ja.yml │ └── ko.yml └── routes.rb ├── init.rb ├── lang ├── en.yml └── ja.yml ├── lib └── parking_lot_chart_data.rb └── test ├── functional └── parking_lot_chart_controller_test.rb └── test_helper.rb /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daipresents/redmine_parking_lot_chart/4096b34b308b44a9798ef764707e9114f8ccbeb5/.DS_Store -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = redmine_parking_lot_chart 2 | アジャイルな見積りと計画づくりに登場する、テーマやストーリーの完了状況を可視化したツールをRedmineにそそぎこむ。 3 | (Parking lot chart appears in the Agile estimating and planning. This chart makes the theme and the story visible. I try to create redmine plugin for agile development tool.) 4 | 5 | ロードマップという機能が存在するが、縦に長いページだと状況がわかりずらい。モバイルページと同じく、1画面でできるだけ多くの情報をわかりやすく表示するためにプラグインを作ってみる。 6 | (Redmine Roadmap is wonderful feature. but if threre are a lot of version, roadmap page is became very long page. So parking lot chart plugin offers the feature to display a lot of information on one screen.) 7 | 8 | 作者は英語が得意ではありません。 9 | (The author is not good at English.Sorry..) 10 | 11 | == Details 12 | - プロジェクト内のバージョンをカード形式で表示します。(The version in the project is displayed by the card format.) 13 | 14 | == Plugin Installation for Rails.version >= '3.0' 15 | - Download this plugin. 16 | - Unpack and move into RAILS_ROOT/plugins. 17 | - Restart Redmine. 18 | - Login and configure the plugin (Administration > Roles and permissions > Permissions report) 19 | - Configure the project (Project > Settings > Modules). 20 | - Click to Parking Lot Chart menu. 21 | 22 | == Plugin Installation for Rails.version < '3.0' 23 | - Download this plugin. 24 | - Unpack and move into RAILS_ROOT/vendor/plugins. 25 | - Rename the plugin directory to parking_lot_chart 26 | - Restart Redmine. 27 | - Login and configure the plugin (Administration > Roles and permissions > Permissions report) 28 | - Configure the project (Project > Settings > Modules). 29 | - Click to Parking Lot Chart menu. 30 | 31 | 32 | == Note 33 | - This plugin works only Redmine 0.9.0 or higher. 34 | 35 | == License 36 | This plugin is licensed under GNU General Public License version 3. 37 | 38 | -------------------------------------------------------------------------------- /app/controllers/parking_lot_chart_controller.rb: -------------------------------------------------------------------------------- 1 | require 'parking_lot_chart_data' 2 | 3 | class ParkingLotChartController < ApplicationController 4 | unloadable 5 | menu_item :parking_lot_chart 6 | before_filter :find_project, :find_issues_open_status, :find_all_versions 7 | 8 | DEBUG = false 9 | 10 | def index 11 | if DEBUG 12 | @today = Date::new(2010, 3, 8) 13 | else 14 | @today = Date.today 15 | end 16 | versions = find_versions 17 | @chart_data = [] 18 | versions.each do |version| 19 | data = ParkingLotChartData.new 20 | data.id = version.id 21 | data.name = version.name 22 | if version.estimated_hours 23 | data.estimated_hours = round(version.estimated_hours) 24 | else 25 | data.estimated_hours = 0 26 | end 27 | data.open_issues_count = version.open_issues_count 28 | data.closed_issues_count = version.closed_issues_count 29 | data.effective_date = version.effective_date 30 | data.status = version.status 31 | if version.effective_date 32 | if data.open_issues_count == 0 && 0 < data.closed_issues_count 33 | data.issues_status = "closed" 34 | elsif version.effective_date < @today 35 | data.issues_status = "late" 36 | elsif version.effective_date <= (@today + 2) 37 | data.issues_status = "pinch" 38 | elsif (data.effective_date - 6) <= @today 39 | data.issues_status = "one more week" 40 | else 41 | data.issues_status = "relax" 42 | end 43 | else 44 | data.issues_status = "late" 45 | end 46 | data.open_issues_pourcent = calc_open_issues_pourcent(version.id, version.estimated_hours) 47 | data.closed_issues_pourcent = 100 - data.open_issues_pourcent 48 | @chart_data.push(data) 49 | end 50 | end 51 | 52 | def calc_open_issues_pourcent(version_id, total_hours) 53 | sum = 0 54 | @open_statuses.each do |status| 55 | sum += Issue.sum(:estimated_hours, :conditions => {:fixed_version_id => version_id, :status_id => status.id}) 56 | end 57 | 58 | if sum == 0 59 | return 0 60 | else 61 | return round(sum / total_hours * 100) 62 | end 63 | end 64 | 65 | def round(value) 66 | if value.nil? || value == 0 67 | return 0 68 | else 69 | return ((value * 10.0).round / 10.0).to_f 70 | end 71 | end 72 | 73 | def find_versions 74 | unless params[:status] 75 | return find_open_versions 76 | else 77 | if params[:status] == "closed" 78 | return find_closed_versions 79 | elsif params[:status] == "locked" 80 | return find_locked_versions 81 | elsif params[:status] == "no_effective_date" 82 | return find_no_effectvie_date_versions 83 | elsif params[:status] == "all" 84 | return @versions 85 | else 86 | return find_open_versions 87 | end 88 | end 89 | end 90 | 91 | def find_open_versions 92 | return @versions.select{|version| version.status == "open"} 93 | end 94 | 95 | def find_closed_versions 96 | return @versions.select{|version| version.status == "closed"} 97 | end 98 | 99 | def find_locked_versions 100 | return @versions.select{|version| version.status == "locked"} 101 | end 102 | 103 | def find_no_effectvie_date_versions 104 | return @project.versions.select{|version| !version.effective_date} 105 | end 106 | 107 | def find_all_versions 108 | @versions = @project.versions.select(&:effective_date).sort_by(&:effective_date) 109 | end 110 | 111 | private 112 | def find_project 113 | render_error(l(:parking_lot_chart_project_not_found, :project_id => 'parameter not found.')) and return unless params[:project_id] 114 | begin 115 | @project = Project.find(params[:project_id]) 116 | rescue ActiveRecord::NotFound 117 | render_error(l(:parking_lot_chart_project_not_found, :project_id => params[:project_id])) and return unless @project 118 | end 119 | end 120 | 121 | private 122 | def find_issues_open_status 123 | @open_statuses = IssueStatus.find_all_by_is_closed(false) 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /app/helpers/parking_lot_chart_helper.rb: -------------------------------------------------------------------------------- 1 | module ParkingLotChartHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/views/parking_lot_chart/index.html.erb: -------------------------------------------------------------------------------- 1 | <% html_title "#{@project.name} - " + l(:parking_lot_chart) -%> 2 | 3 | <% content_for :header_tags do %> 4 | 17 | <% end %> 18 | 19 | <% content_for :sidebar do -%> 20 |

<%= l(:parking_lot_chart_menu_info) %>

21 |
<%= l(:parking_lot_chart_menu_today, :today => @today) %>
22 |
23 |
    <%= l(:parking_lot_chart_menu_color_late) %>
24 |
    <%= l(:parking_lot_chart_menu_color_pinch) %>
25 |
    <%= l(:parking_lot_chart_menu_color_1week) %>
26 |
    <%= l(:parking_lot_chart_menu_color_relax) %>
27 |
    <%= l(:parking_lot_chart_menu_color_closed) %>
28 |
29 |

<%= l(:parking_lot_chart_menu_filter) %>

30 | <%= " style='font-weight:bold'" %><% end %>> 31 | <%= link_to l(:parking_lot_chart_open_version), {:controller => 'parking_lot_chart', :action => 'index', :project_id => @project.identifier} %>
32 | 33 | <%= " style='font-weight:bold'" %><% end %>> 34 | <%= link_to l(:parking_lot_chart_locked_version), {:controller => 'parking_lot_chart', :action => 'index', :project_id => @project.identifier, :status => 'locked'} %>
35 | 36 | <%= " style='font-weight:bold'" %><% end %>> 37 | <%= link_to l(:parking_lot_chart_closed_version), {:controller => 'parking_lot_chart', :action => 'index', :project_id => @project.identifier, :status => 'closed'} %>
38 | 39 |

40 | <%= " style='font-weight:bold'" %><% end %>> 41 | <%= link_to l(:parking_lot_chart_all_version), {:controller => 'parking_lot_chart', :action => 'index', :project_id => @project.identifier, :status => 'all'} %> 42 | 43 | <%= " style='font-weight:bold'" %><% end %>> 44 | <%= link_to l(:parking_lot_chart_no_effective_date_version), {:controller => 'parking_lot_chart', :action => 'index', :project_id => @project.identifier, :status => 'no_effective_date'} %>
45 | 46 | <% end -%> 47 | 48 | <% @chart_data.each do |data| %> 49 | <% 50 | if data.issues_status == 'closed' 51 | style = "background-color:#cccccc;" 52 | elsif data.issues_status == 'late' 53 | style = "background-color:#ff6666;" 54 | elsif data.issues_status == 'pinch' 55 | style = "background-color:#ff9900;" 56 | elsif data.issues_status == 'one more week' 57 | style = "background-color:#ffff66;" 58 | elsif data.issues_status == 'relax' 59 | style = "background-color:#ccffff;" 60 | else 61 | style = "background-color:#ccffff;" 62 | end 63 | %> 64 |

65 |
66 | <%= link_to data.name, {:controller => 'versions', :action => 'show', :id => data.id} %> 67 |
68 |
69 | <%= l(:parking_lot_chart_estimated_hours) %>:<%= data.estimated_hours %>
70 | <%= link_to l(:parking_lot_chart_closed) + data.closed_issues_count.to_s, {:controller => 'issues', :action => 'index', :project_id => @project.identifier, :fixed_version_id => data.id, :set_filter => 1, :status_id => "c"} %> 71 | / 72 | <%= link_to l(:parking_lot_chart_open) + data.open_issues_count.to_s, {:controller => 'issues', :action => 'index', :project_id => @project.identifier, :fixed_version_id => data.id, :set_filter => 1, :status_id => "o"} %>
73 | <%= l(:parking_lot_chart_effective_date) %>:<% if data.effective_date %><%= data.effective_date %><% else %>---<% end %> 74 |
75 |
76 | 77 | 78 | <% if data.closed_issues_count == 0 || data.open_issues_count + data.closed_issues_count == 0 %> 79 | 80 | <% elsif data.closed_issues_count >= 1 && data.open_issues_count == 0 %> 81 | 82 | <% else %> 83 | 84 | 85 | <% end %> 86 | 87 |
88 |
89 |
90 | <% end %> 91 | 92 |
93 | -------------------------------------------------------------------------------- /config/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daipresents/redmine_parking_lot_chart/4096b34b308b44a9798ef764707e9114f8ccbeb5/config/.DS_Store -------------------------------------------------------------------------------- /config/locales/de.yml: -------------------------------------------------------------------------------- 1 | de: 2 | parking_lot_chart: "Parkplatz Diagramm" 3 | parking_lot_chart_closed: "Geschlossen" 4 | parking_lot_chart_open: "Offen" 5 | parking_lot_chart_estimated_hours: "Geschätzte Dauer" 6 | parking_lot_chart_effective_date: "Stichtag" 7 | 8 | parking_lot_chart_menu_info: "Information" 9 | parking_lot_chart_menu_filter: "Filter" 10 | parking_lot_chart_menu_today: "Heute: %{today}" 11 | parking_lot_chart_menu_color_late: "In Verzug" 12 | parking_lot_chart_menu_color_pinch: "Verzögert" 13 | parking_lot_chart_menu_color_1week: "Nächste Woche" 14 | parking_lot_chart_menu_color_relax: "Alles In Ordnung" 15 | parking_lot_chart_menu_color_closed: "Geschlossen" 16 | 17 | parking_lot_chart_all_version: "Alle Versionen" 18 | parking_lot_chart_open_version: "Sprint Versionen" 19 | parking_lot_chart_locked_version: "Gesperrte Versionen" 20 | parking_lot_chart_closed_version: "Abgeschlossene Versionen" 21 | parking_lot_chart_no_effective_date_version: "Ohne Stichtag" 22 | 23 | parking_lot_chart_project_not_found: "Projekt nicht gefunden. project_id:%{project_id}" 24 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | parking_lot_chart: "Parking Lot Chart" 3 | parking_lot_chart_closed: "Closed" 4 | parking_lot_chart_open: "Open" 5 | parking_lot_chart_estimated_hours: "Estimated Hours" 6 | parking_lot_chart_effective_date: "Effective Date" 7 | 8 | parking_lot_chart_menu_info: "Information" 9 | parking_lot_chart_menu_filter: "Filter" 10 | parking_lot_chart_menu_today: "Today:%{today}" 11 | parking_lot_chart_menu_color_late: "Delay" 12 | parking_lot_chart_menu_color_pinch: "Pinch" 13 | parking_lot_chart_menu_color_1week: "Another week" 14 | parking_lot_chart_menu_color_relax: "Everything OK?" 15 | parking_lot_chart_menu_color_closed: "Closed" 16 | 17 | parking_lot_chart_all_version: "All Version" 18 | parking_lot_chart_open_version: "Sprint Version" 19 | parking_lot_chart_locked_version: "Locked Version" 20 | parking_lot_chart_closed_version: "Closed Version" 21 | parking_lot_chart_no_effective_date_version: "No Effective Date" 22 | 23 | parking_lot_chart_project_not_found: "Project not found. project_id:%{project_id}" 24 | -------------------------------------------------------------------------------- /config/locales/ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | parking_lot_chart: "パーキングロットチャート" 3 | parking_lot_chart_closed: "完了" 4 | parking_lot_chart_open: "未完了" 5 | parking_lot_chart_estimated_hours: "予定工数" 6 | parking_lot_chart_effective_date: "期日" 7 | 8 | parking_lot_chart_menu_info: "Information" 9 | parking_lot_chart_menu_filter: "フィルタ" 10 | parking_lot_chart_menu_today: "今日は%{today}です。" 11 | parking_lot_chart_menu_color_late: "遅延発生" 12 | parking_lot_chart_menu_color_pinch: "ピンチ" 13 | parking_lot_chart_menu_color_1week: "残り1週間を切りました" 14 | parking_lot_chart_menu_color_relax: "残り1週間以上です" 15 | parking_lot_chart_menu_color_closed: "完了" 16 | 17 | parking_lot_chart_all_version: "すべてのバージョン" 18 | parking_lot_chart_open_version: "スプリント中のバージョン" 19 | parking_lot_chart_locked_version: "ロック中のバージョン" 20 | parking_lot_chart_closed_version: "完了したバージョン" 21 | parking_lot_chart_no_effective_date_version: "期限のないバージョン" 22 | 23 | parking_lot_chart_project_not_found: "プロジェクトが存在しません。プロジェクトID:%{project_id}" 24 | 25 | -------------------------------------------------------------------------------- /config/locales/ko.yml: -------------------------------------------------------------------------------- 1 | # Translation by Ki Won Kim (http://xyz37.blog.me, xyz37@naver.com) 2 | ko: 3 | parking_lot_chart: "격자 차트" 4 | parking_lot_chart_closed: "완료됨" 5 | parking_lot_chart_open: "진행" 6 | parking_lot_chart_estimated_hours: "평가 시간" 7 | parking_lot_chart_effective_date: "유효 일자" 8 | 9 | parking_lot_chart_menu_info: "정보" 10 | parking_lot_chart_menu_filter: "필터" 11 | parking_lot_chart_menu_today: "오늘:%{today}" 12 | parking_lot_chart_menu_color_late: "지연됨" 13 | parking_lot_chart_menu_color_pinch: "막바지" 14 | parking_lot_chart_menu_color_1week: "다른 주(week)" 15 | parking_lot_chart_menu_color_relax: "모두 정상?" 16 | parking_lot_chart_menu_color_closed: "완료됨" 17 | 18 | parking_lot_chart_all_version: "모든 버전" 19 | parking_lot_chart_open_version: "진행중인 버전" 20 | parking_lot_chart_locked_version: "잠긴 버전" 21 | parking_lot_chart_closed_version: "완료된 버전" 22 | parking_lot_chart_no_effective_date_version: "유효 일자 없음" 23 | 24 | parking_lot_chart_project_not_found: "프로젝트가 없습니다. 프로젝트 식별자:%{project_id}" 25 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | if Rails.version < '3.0' 2 | ActionController::Routing::Routes.draw do |map| 3 | map.connect 'parking_lot_chart/:action', :controller => 'parking_lot_chart' 4 | end 5 | else # Rails 3 6 | RedmineApp::Application.routes.draw do 7 | match 'parking_lot_chart/:action', :controller => 'parking_lot_chart' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require 'redmine' 2 | 3 | Redmine::Plugin.register :redmine_parking_lot_chart do 4 | name 'Redmine Parking Lot Chart plugin' 5 | author 'Dai Fujihara' 6 | description 'Parking lot chart appears in the Agile estimating and planning. This chart makes the theme and the story visible. I try to create redmine plugin for agile development tool.' 7 | author_url 'http://daipresents.com/' 8 | url 'http://daipresents.com/2010/redmine_parking_lot_chart_plugin/' 9 | 10 | requires_redmine :version_or_higher => '1.1.3' 11 | version '0.0.7' 12 | 13 | project_module :parking_lot_chart do 14 | permission :parking_lot_chart_view, :parking_lot_chart => :index 15 | end 16 | 17 | menu :project_menu, :parking_lot_chart, { :controller => 'parking_lot_chart', :action => 'index' }, 18 | :caption => :parking_lot_chart, :after => :roadmap, :param => :project_id 19 | end 20 | -------------------------------------------------------------------------------- /lang/en.yml: -------------------------------------------------------------------------------- 1 | parking_lot_chart: "Parking Lot Chart" 2 | parking_lot_chart_closed: "Closed" 3 | parking_lot_chart_open: "Open" 4 | parking_lot_chart_estimated_hours: "Estimated Hours" 5 | parking_lot_chart_effective_date: "Effective Date" 6 | 7 | parking_lot_chart_menu_info: "Information" 8 | parking_lot_chart_menu_filter: "Filter" 9 | parking_lot_chart_menu_today: "Today:{{today}}" 10 | parking_lot_chart_menu_color_late: "Delay" 11 | parking_lot_chart_menu_color_pinch: "Pinch" 12 | parking_lot_chart_menu_color_1week: "Anothre week" 13 | parking_lot_chart_menu_color_relax: "Everything OK?" 14 | parking_lot_chart_menu_color_closed: "Closed" 15 | 16 | parking_lot_chart_all_version: "All Version" 17 | parking_lot_chart_open_version: "Sprint Version" 18 | parking_lot_chart_locked_version: "Locked Version" 19 | parking_lot_chart_closed_version: "Closed Version" 20 | parking_lot_chart_no_effective_date_version: "No Effective Date" 21 | 22 | parking_lot_chart_project_not_found: "Project not found. project_id:{{project_id}}" -------------------------------------------------------------------------------- /lang/ja.yml: -------------------------------------------------------------------------------- 1 | parking_lot_chart: "パーキングロットチャート" 2 | parking_lot_chart_closed: "完了" 3 | parking_lot_chart_open: "未完了" 4 | parking_lot_chart_estimated_hours: "予定工数" 5 | parking_lot_chart_effective_date: "期日" 6 | 7 | parking_lot_chart_menu_info: "Information" 8 | parking_lot_chart_menu_filter: "フィルタ" 9 | parking_lot_chart_menu_today: "今日は{{today}}です。" 10 | parking_lot_chart_menu_color_late: "遅延発生" 11 | parking_lot_chart_menu_color_pinch: "ピンチ" 12 | parking_lot_chart_menu_color_1week: "残り1週間を切りました" 13 | parking_lot_chart_menu_color_relax: "残り1週間以上です" 14 | parking_lot_chart_menu_color_closed: "完了" 15 | 16 | parking_lot_chart_all_version: "すべてのバージョン" 17 | parking_lot_chart_open_version: "スプリント中のバージョン" 18 | parking_lot_chart_locked_version: "ロック中のバージョン" 19 | parking_lot_chart_closed_version: "完了したバージョン" 20 | parking_lot_chart_no_effective_date_version: "期限のないバージョン" 21 | 22 | parking_lot_chart_project_not_found: "プロジェクトが存在しません。プロジェクトID:{{project_id}}" 23 | 24 | -------------------------------------------------------------------------------- /lib/parking_lot_chart_data.rb: -------------------------------------------------------------------------------- 1 | class ParkingLotChartData 2 | def initialize 3 | 4 | end 5 | 6 | attr_accessor :id, :name, :estimated_hours, 7 | :open_issues_count, :closed_issues_count, :effective_date, :status, 8 | :open_issues_pourcent, :closed_issues_pourcent, :issues_status 9 | end 10 | -------------------------------------------------------------------------------- /test/functional/parking_lot_chart_controller_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class ParkingLotChartControllerTest < ActionController::TestCase 4 | # Replace this with your real tests. 5 | def test_truth 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Load the normal Rails helper 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper') 3 | 4 | # Ensure that we are using the temporary fixture path 5 | Engines::Testing.set_fixture_path 6 | --------------------------------------------------------------------------------