Import archive redmine_agile-1_6_3-light
This commit is contained in:
parent
82b5e37e67
commit
7c5d7b4292
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -51,7 +51,7 @@ class AgileBoardsController < ApplicationController
|
||||
include SortHelper
|
||||
include IssuesHelper
|
||||
helper :timelog
|
||||
include RedmineAgile::AgileHelper
|
||||
include RedmineAgile::Helpers::AgileHelper
|
||||
helper :checklists if RedmineAgile.use_checklist?
|
||||
|
||||
def index
|
||||
@ -81,10 +81,10 @@ class AgileBoardsController < ApplicationController
|
||||
retrieve_agile_query_from_session
|
||||
old_status = @issue.status
|
||||
@issue.init_journal(User.current)
|
||||
@issue.safe_attributes = auto_assign_on_move? ? params[:issue].merge(:assigned_to_id => User.current.id) : params[:issue]
|
||||
checking_params = params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params
|
||||
|
||||
saved = checking_params['issue'] && checking_params['issue'].inject(true) do |total, attribute|
|
||||
@issue.safe_attributes = configured_params['issue']
|
||||
|
||||
saved = configured_params['issue'] && configured_params['issue'].inject(true) do |total, attribute|
|
||||
if @issue.attributes.include?(attribute.first)
|
||||
total &&= @issue.attributes[attribute.first].to_i == attribute.last.to_i
|
||||
else
|
||||
@ -138,6 +138,16 @@ class AgileBoardsController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def configured_params
|
||||
return @configured_params if @configured_params
|
||||
|
||||
issue_params = params[:issue]
|
||||
issue_params[:parent_issue_id] = issue_params[:parent_id] && issue_params.delete(:parent_id) if issue_params[:parent_id]
|
||||
issue_params[:assigned_to_id] = User.current.id if auto_assign_on_move?
|
||||
|
||||
@configured_params = params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params
|
||||
end
|
||||
|
||||
def auto_assign_on_move?
|
||||
RedmineAgile.auto_assign_on_move? && @issue.assigned_to.nil? &&
|
||||
!params[:issue].keys.include?('assigned_to_id') &&
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -45,7 +45,7 @@ class AgileChartsController < ApplicationController
|
||||
include SortHelper
|
||||
include IssuesHelper
|
||||
helper :timelog
|
||||
include RedmineAgile::AgileHelper
|
||||
include RedmineAgile::Helpers::AgileHelper
|
||||
|
||||
def show
|
||||
retrieve_charts_query
|
||||
@ -85,7 +85,7 @@ class AgileChartsController < ApplicationController
|
||||
private
|
||||
|
||||
def render_data(options = {})
|
||||
agile_chart = RedmineAgile::Charts::AGILE_CHARTS[@chart]
|
||||
agile_chart = RedmineAgile::Charts::Helper::AGILE_CHARTS[@chart]
|
||||
data = agile_chart[:class].data(@issues, options) if agile_chart
|
||||
|
||||
if data
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -53,7 +53,7 @@ class AgileChartsQuery < AgileQuery
|
||||
end
|
||||
|
||||
def chart
|
||||
@chart ||= RedmineAgile::Charts.valid_chart_name_by(options[:chart])
|
||||
@chart ||= RedmineAgile::Charts::Helper.valid_chart_name_by(options[:chart])
|
||||
end
|
||||
|
||||
def chart=(arg)
|
||||
@ -69,10 +69,10 @@ class AgileChartsQuery < AgileQuery
|
||||
end
|
||||
|
||||
def interval_size
|
||||
if RedmineAgile::AgileChart::TIME_INTERVALS.include?(options[:interval_size])
|
||||
if RedmineAgile::Charts::AgileChart::TIME_INTERVALS.include?(options[:interval_size])
|
||||
options[:interval_size]
|
||||
else
|
||||
RedmineAgile::AgileChart::DAY_INTERVAL
|
||||
RedmineAgile::Charts::AgileChart::DAY_INTERVAL
|
||||
end
|
||||
end
|
||||
|
||||
@ -94,8 +94,8 @@ class AgileChartsQuery < AgileQuery
|
||||
self.date_from = params[:date_from] || (params[:query] && params[:query][:date_from])
|
||||
self.date_to = params[:date_to] || (params[:query] && params[:query][:date_to])
|
||||
self.chart = params[:chart] || (params[:query] && params[:query][:chart]) || params[:default_chart] || RedmineAgile.default_chart
|
||||
self.interval_size = params[:interval_size] || (params[:query] && params[:query][:interval_size]) || RedmineAgile::AgileChart::DAY_INTERVAL
|
||||
self.chart_unit = params[:chart_unit] || (params[:query] && params[:query][:chart_unit]) || RedmineAgile::Charts::UNIT_ISSUES
|
||||
self.interval_size = params[:interval_size] || (params[:query] && params[:query][:interval_size]) || RedmineAgile::Charts::AgileChart::DAY_INTERVAL
|
||||
self.chart_unit = params[:chart_unit] || (params[:query] && params[:query][:chart_unit]) || RedmineAgile::Charts::Helper::UNIT_ISSUES
|
||||
|
||||
self
|
||||
end
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -120,7 +120,7 @@ class AgileQuery < Query
|
||||
end
|
||||
|
||||
def chart_unit
|
||||
@chart_unit ||= RedmineAgile::Charts.valid_chart_unit_by(options[:chart], options[:chart_unit])
|
||||
@chart_unit ||= RedmineAgile::Charts::Helper.valid_chart_unit_by(options[:chart], options[:chart_unit])
|
||||
end
|
||||
|
||||
def chart_unit=(value)
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -13,7 +13,7 @@
|
||||
</span>
|
||||
</legend>
|
||||
<div id='agile_chart'>
|
||||
<%= render_agile_chart(RedmineAgile::Charts.valid_chart_name_by(params[:chart] || RedmineAgile.default_chart), @version.fixed_issues) %>
|
||||
<%= render_agile_chart(RedmineAgile::Charts::Helper.valid_chart_name_by(params[:chart] || RedmineAgile.default_chart), @version.fixed_issues) %>
|
||||
</div>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
@ -24,7 +24,7 @@
|
||||
<% end %>
|
||||
|
||||
<%= javascript_tag do %>
|
||||
var chartsWithUnits = <%= raw RedmineAgile::Charts::CHARTS_WITH_UNITS.to_json %>
|
||||
var chartsWithUnits = <%= raw RedmineAgile::Charts::Helper::CHARTS_WITH_UNITS.to_json %>
|
||||
$(document).ready(function() {
|
||||
toggleChartUnit($('#chart_by_select').val(), 'chart-unit-row');
|
||||
});
|
||||
|
@ -29,7 +29,7 @@
|
||||
<tr id="interval-size">
|
||||
<td><%= l(:label_agile_interval_size) %></td>
|
||||
<td>
|
||||
<%= select_tag 'interval_size', options_for_select(RedmineAgile::AgileChart::TIME_INTERVALS.map { |i| [l(:"label_agile_#{i}"), i] }, @query.interval_size) %>
|
||||
<%= select_tag 'interval_size', options_for_select(RedmineAgile::Charts::AgileChart::TIME_INTERVALS.map { |i| [l(:"label_agile_#{i}"), i] }, @query.interval_size) %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -58,7 +58,7 @@
|
||||
<% end %>
|
||||
|
||||
<%= javascript_tag do %>
|
||||
var chartsWithUnits = <%= raw RedmineAgile::Charts::CHARTS_WITH_UNITS.to_json %>
|
||||
var chartsWithUnits = <%= raw RedmineAgile::Charts::Helper::CHARTS_WITH_UNITS.to_json %>
|
||||
$(document).ready(function() {
|
||||
toggleChartUnit($('#chart').val(), 'chart-unit-row');
|
||||
/* Hide chart_period checkbox so that it couldn't be unchecked */
|
||||
|
@ -630,7 +630,9 @@ function recalculateSprintHours() {
|
||||
hours = parseFloat($(issue).data(dataAttr));
|
||||
versionEstimationSum += hours;
|
||||
});
|
||||
$(elem).find('.sprint-estimate').text('(' + versionEstimationSum.toFixed(2) + unit + ')');
|
||||
if (versionEstimationSum > 0) {
|
||||
$(elem).find('.sprint-estimate').text('(' + versionEstimationSum.toFixed(2) + unit + ')');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -576,6 +576,16 @@ table.list.issues-board.sticky {
|
||||
margin-top: -2.3em;
|
||||
}
|
||||
|
||||
.controller-agile_boards .observed-desc {
|
||||
margin-top: 0;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.agile-sprint-description {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.agile-board-fullscreen .controller-agile_boards .query-totals {
|
||||
margin: 0px;
|
||||
position: fixed;
|
||||
|
@ -198,6 +198,8 @@ en:
|
||||
label_agile_board_totals_velocity: Velocity
|
||||
label_agile_board_totals_interval: Interval
|
||||
label_agile_board_totals_remaining: Remaining
|
||||
label_agile_board_totals_description: Sprint goal
|
||||
|
||||
|
||||
label_agile_sprint_list_active: Active sprints
|
||||
label_agile_sprint_list_future: Future sprints
|
||||
|
@ -208,7 +208,7 @@ pt-BR:
|
||||
label_agile_board_backlog: Backlog
|
||||
label_agile_board_backlog_column: Colunas do Backlog
|
||||
label_agile_board_search_backlog_issues: Pesquisar tarefas do backlog
|
||||
label_agile_version_plural: Vesões
|
||||
label_agile_version_plural: Versões
|
||||
label_agile_sprint_add: Adicionar novo Sprint
|
||||
label_agile_version_add: Adicionar Versão
|
||||
|
||||
|
@ -189,6 +189,7 @@ ru:
|
||||
label_agile_board_totals_spent_time: Потраченное время
|
||||
label_agile_board_totals_percent_done: Готовность (%)
|
||||
label_agile_board_totals_velocity: Скорость закрытия
|
||||
label_agile_board_totals_description: Цель спринта
|
||||
|
||||
label_agile_sprint_list_active: Активные спринты
|
||||
label_agile_sprint_list_future: Следующие спринты
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,9 +1,21 @@
|
||||
== Redmine Agile plugin changelog
|
||||
|
||||
Redmine Agile plugin - Agile board plugin for redmine
|
||||
Copyright (C) 2011-2021 RedmineUP
|
||||
Copyright (C) 2011-2022 RedmineUP
|
||||
http://www.redmineup.com/
|
||||
|
||||
== 2022-03-30 v1.6.3
|
||||
|
||||
* Redmine 5.0 compatibility
|
||||
* Added sprint description to a board view
|
||||
* Changed Manage board permissions behavior
|
||||
* Fixed default board setting
|
||||
* Fixed card load error
|
||||
* Fixed empty sprint for default board
|
||||
* Fixed saved query bug for agile board
|
||||
* Fixed story points are not being added up in "Sprints" subtab of "Backlog" tab
|
||||
* Fixed error with empty sprint_id in AgileQuery
|
||||
|
||||
== 2021-09-07 v1.6.2
|
||||
|
||||
* Added notification message for not available operations on Backlog board
|
||||
|
11
init.rb
11
init.rb
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -17,11 +17,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
requires_redmine_crm version_or_higher: '0.0.43' rescue raise "\n\033[31mRedmine requires newer redmine_crm gem version.\nPlease update with 'bundle update redmine_crm'.\033[0m"
|
||||
requires_redmine_crm version_or_higher: '0.0.56' rescue raise "\n\033[31mRedmine requires newer redmine_crm gem version.\nPlease update with 'bundle update redmine_crm'.\033[0m"
|
||||
|
||||
require 'redmine'
|
||||
|
||||
AGILE_VERSION_NUMBER = '1.6.2'
|
||||
AGILE_VERSION_NUMBER = '1.6.3'
|
||||
AGILE_VERSION_TYPE = "Light version"
|
||||
|
||||
if ActiveRecord::VERSION::MAJOR >= 4 && !defined?(FCSV)
|
||||
@ -68,4 +68,7 @@ Redmine::Plugin.register :redmine_agile do
|
||||
end
|
||||
end
|
||||
|
||||
require 'redmine_agile'
|
||||
if Rails.configuration.respond_to?(:autoloader) && Rails.configuration.autoloader == :zeitwerk
|
||||
Rails.autoloaders.each { |loader| loader.ignore(File.dirname(__FILE__) + '/lib') }
|
||||
end
|
||||
require File.dirname(__FILE__) + '/lib/redmine_agile'
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -17,22 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
require 'redmine_agile/hooks/views_layouts_hook'
|
||||
require 'redmine_agile/hooks/views_issues_hook'
|
||||
require 'redmine_agile/hooks/views_versions_hook'
|
||||
require 'redmine_agile/hooks/controller_issue_hook'
|
||||
require 'redmine_agile/patches/issue_patch'
|
||||
|
||||
require 'redmine_agile/helpers/agile_helper'
|
||||
|
||||
require 'redmine_agile/charts/agile_chart'
|
||||
require 'redmine_agile/charts/burndown_chart'
|
||||
require 'redmine_agile/charts/work_burndown_chart'
|
||||
require 'redmine_agile/charts/charts'
|
||||
require 'redmine_agile/patches/issue_drop_patch'
|
||||
|
||||
module RedmineAgile
|
||||
|
||||
ISSUES_PER_COLUMN = 10
|
||||
@ -64,7 +48,7 @@ module RedmineAgile
|
||||
end
|
||||
|
||||
def default_chart
|
||||
Setting.plugin_redmine_agile['default_chart'] || Charts::BURNDOWN_CHART
|
||||
Setting.plugin_redmine_agile['default_chart'] || Charts::Helper::BURNDOWN_CHART
|
||||
end
|
||||
|
||||
def estimate_units
|
||||
@ -135,3 +119,22 @@ module RedmineAgile
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
REDMINE_AGILE_REQUIRED_FILES = [
|
||||
'redmine_agile/hooks/views_layouts_hook',
|
||||
'redmine_agile/hooks/views_issues_hook',
|
||||
'redmine_agile/hooks/views_versions_hook',
|
||||
'redmine_agile/hooks/controller_issue_hook',
|
||||
'redmine_agile/patches/issue_patch',
|
||||
'redmine_agile/helpers/agile_helper',
|
||||
'redmine_agile/charts/helper',
|
||||
'redmine_agile/charts/agile_chart',
|
||||
'redmine_agile/charts/burndown_chart',
|
||||
'redmine_agile/charts/work_burndown_chart',
|
||||
'redmine_agile/patches/issue_drop_patch'
|
||||
]
|
||||
|
||||
Rails.application.config.after_initialize do
|
||||
base_url = File.dirname(__FILE__)
|
||||
REDMINE_AGILE_REQUIRED_FILES.each { |file| require(base_url + '/' + file) }
|
||||
end
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -18,262 +18,264 @@
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module RedmineAgile
|
||||
class AgileChart
|
||||
include Redmine::I18n
|
||||
include Redmine::Utils::DateCalculation
|
||||
module Charts
|
||||
class AgileChart
|
||||
include Redmine::I18n
|
||||
include Redmine::Utils::DateCalculation
|
||||
|
||||
DAY_INTERVAL = 'day'.freeze
|
||||
WEEK_INTERVAL = 'week'.freeze
|
||||
MONTH_INTERVAL = 'month'.freeze
|
||||
QUARTER_INTERVAL = 'quarter'.freeze
|
||||
YEAR_INTERVAL = 'year'.freeze
|
||||
DAY_INTERVAL = 'day'.freeze
|
||||
WEEK_INTERVAL = 'week'.freeze
|
||||
MONTH_INTERVAL = 'month'.freeze
|
||||
QUARTER_INTERVAL = 'quarter'.freeze
|
||||
YEAR_INTERVAL = 'year'.freeze
|
||||
|
||||
TIME_INTERVALS = [DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, YEAR_INTERVAL].freeze
|
||||
TIME_INTERVALS = [DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, YEAR_INTERVAL].freeze
|
||||
|
||||
attr_reader :line_colors
|
||||
attr_reader :line_colors
|
||||
|
||||
def initialize(data_scope, options = {})
|
||||
@options = options
|
||||
@data_scope = data_scope
|
||||
@data_from ||= options[:data_from]
|
||||
@data_to ||= options[:data_to]
|
||||
@interval_size = options[:interval_size] || DAY_INTERVAL
|
||||
initialize_chart_periods
|
||||
@step_x_labels = @period_count > 18 ? @period_count / 12 + 1 : 1
|
||||
@fields = chart_fields_by_period
|
||||
@weekend_periods = weekend_periods
|
||||
@estimated_unit = options[:estimated_unit] || ESTIMATE_HOURS
|
||||
@line_colors = {}
|
||||
end
|
||||
def initialize(data_scope, options = {})
|
||||
@options = options
|
||||
@data_scope = data_scope
|
||||
@data_from ||= options[:data_from]
|
||||
@data_to ||= options[:data_to]
|
||||
@interval_size = options[:interval_size] || DAY_INTERVAL
|
||||
initialize_chart_periods
|
||||
@step_x_labels = @period_count > 18 ? @period_count / 12 + 1 : 1
|
||||
@fields = chart_fields_by_period
|
||||
@weekend_periods = weekend_periods
|
||||
@estimated_unit = options[:estimated_unit] || ESTIMATE_HOURS
|
||||
@line_colors = {}
|
||||
end
|
||||
|
||||
def data
|
||||
{ title: '', y_title: '', labels: [], datasets: [] }
|
||||
end
|
||||
def data
|
||||
{ title: '', y_title: '', labels: [], datasets: [] }
|
||||
end
|
||||
|
||||
def self.data(data_scope, options = {})
|
||||
new(data_scope, options).data
|
||||
end
|
||||
def self.data(data_scope, options = {})
|
||||
new(data_scope, options).data
|
||||
end
|
||||
|
||||
protected
|
||||
protected
|
||||
|
||||
def current_date_period
|
||||
return @current_date_period if @current_date_period
|
||||
def current_date_period
|
||||
return @current_date_period if @current_date_period
|
||||
|
||||
for_future = RedmineAgile.chart_future_data? ? @options[:date_to].present? : false
|
||||
date_period = (@date_to <= Date.today || for_future ? @period_count : (@period_count - (@date_to - Date.today).to_i / @scale_division)).round
|
||||
@current_date_period ||= date_period > 0 ? date_period : 0
|
||||
end
|
||||
for_future = RedmineAgile.chart_future_data? ? @options[:date_to].present? : false
|
||||
date_period = (@date_to <= Date.today || for_future ? @period_count : (@period_count - (@date_to - Date.today).to_i / @scale_division)).round
|
||||
@current_date_period ||= date_period > 0 ? date_period : 0
|
||||
end
|
||||
|
||||
def due_date_period
|
||||
@date_from = @date_from.to_date
|
||||
@date_to = @date_to.to_date
|
||||
due_date = (@due_date && @due_date > @date_from) ? @due_date : @date_from
|
||||
@due_date_period ||= (@due_date ? @period_count - (@date_to - due_date.to_date).to_i : @period_count - 1)
|
||||
@due_date_period = @due_date_period > 0 ? @due_date_period : 1
|
||||
end
|
||||
def due_date_period
|
||||
@date_from = @date_from.to_date
|
||||
@date_to = @date_to.to_date
|
||||
due_date = (@due_date && @due_date > @date_from) ? @due_date : @date_from
|
||||
@due_date_period ||= (@due_date ? @period_count - (@date_to - due_date.to_date).to_i : @period_count - 1)
|
||||
@due_date_period = @due_date_period > 0 ? @due_date_period : 1
|
||||
end
|
||||
|
||||
def date_short_period?
|
||||
(@date_to.to_date - @date_from.to_date).to_i <= 31
|
||||
end
|
||||
def date_short_period?
|
||||
(@date_to.to_date - @date_from.to_date).to_i <= 31
|
||||
end
|
||||
|
||||
def date_effort(issues, effort_date)
|
||||
cumulative_left = 0
|
||||
total_left = 0
|
||||
total_done = 0
|
||||
issues.each do |issue|
|
||||
done_ratio_details = issue.journals.map(&:details).flatten.select { |detail| 'done_ratio' == detail.prop_key }
|
||||
details_today_or_earlier = done_ratio_details.select { |a| a.journal.created_on.localtime.to_date <= effort_date }
|
||||
def date_effort(issues, effort_date)
|
||||
cumulative_left = 0
|
||||
total_left = 0
|
||||
total_done = 0
|
||||
issues.each do |issue|
|
||||
done_ratio_details = issue.journals.map(&:details).flatten.select { |detail| 'done_ratio' == detail.prop_key }
|
||||
details_today_or_earlier = done_ratio_details.select { |a| a.journal.created_on.localtime.to_date <= effort_date }
|
||||
|
||||
last_done_ratio_change = details_today_or_earlier.sort_by { |a| a.journal.created_on }.last
|
||||
ratio = if issue.closed? && issue.closed_on.localtime.to_date <= effort_date
|
||||
100
|
||||
elsif last_done_ratio_change
|
||||
last_done_ratio_change.value
|
||||
elsif (done_ratio_details.size > 0) || (issue.closed? && issue.closed_on > effort_date)
|
||||
0
|
||||
else
|
||||
issue.done_ratio.to_i
|
||||
end
|
||||
last_done_ratio_change = details_today_or_earlier.sort_by { |a| a.journal.created_on }.last
|
||||
ratio = if issue.closed? && issue.closed_on.localtime.to_date <= effort_date
|
||||
100
|
||||
elsif last_done_ratio_change
|
||||
last_done_ratio_change.value
|
||||
elsif (done_ratio_details.size > 0) || (issue.closed? && issue.closed_on > effort_date)
|
||||
0
|
||||
else
|
||||
issue.done_ratio.to_i
|
||||
end
|
||||
|
||||
if @estimated_unit == 'hours'
|
||||
cumulative_left += (issue.estimated_hours.to_f * ratio.to_f / 100.0)
|
||||
total_left += (issue.estimated_hours.to_f * (100 - ratio.to_f) / 100.0)
|
||||
total_done += (issue.estimated_hours.to_f * ratio.to_f / 100.0)
|
||||
if @estimated_unit == 'hours'
|
||||
cumulative_left += (issue.estimated_hours.to_f * ratio.to_f / 100.0)
|
||||
total_left += (issue.estimated_hours.to_f * (100 - ratio.to_f) / 100.0)
|
||||
total_done += (issue.estimated_hours.to_f * ratio.to_f / 100.0)
|
||||
else
|
||||
cumulative_left += (issue.story_points.to_f * ratio.to_f / 100.0)
|
||||
total_left += (issue.story_points.to_f * (100 - ratio.to_f) / 100.0)
|
||||
total_done += (issue.story_points.to_f * ratio.to_f / 100.0)
|
||||
end
|
||||
end
|
||||
[total_left, cumulative_left, total_done]
|
||||
end
|
||||
|
||||
def use_subissue_done_ratio
|
||||
!Setting.respond_to?(:parent_issue_done_ratio) || Setting.parent_issue_done_ratio == 'derived' || Setting.parent_issue_done_ratio.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scope_by_created_date
|
||||
@data_scope.
|
||||
where("#{Issue.table_name}.created_on >= ?", @date_from).
|
||||
where("#{Issue.table_name}.created_on < ?", @date_to.to_date + 1).
|
||||
where("#{Issue.table_name}.created_on IS NOT NULL").
|
||||
group("#{Issue.table_name}.created_on").
|
||||
count
|
||||
end
|
||||
|
||||
def scope_by_closed_date
|
||||
@data_scope.
|
||||
open(false).
|
||||
where("#{Issue.table_name}.closed_on >= ?", @date_from).
|
||||
where("#{Issue.table_name}.closed_on < ?", @date_to.to_date + 1).
|
||||
where("#{Issue.table_name}.closed_on IS NOT NULL").
|
||||
group("#{Issue.table_name}.closed_on").
|
||||
count
|
||||
end
|
||||
|
||||
# options
|
||||
# color - Line color in RGB format (e.g '255,255,255') (random)
|
||||
# fill - Fille background under line (false)
|
||||
# dashed - Draw dached line (solid)
|
||||
# nopoints - Doesn't show points on line (false)
|
||||
|
||||
def dataset(dataset_data, label, options = {})
|
||||
color = options[:color] || [rand(255), rand(255), rand(255)].join(',')
|
||||
dataset_color = "rgba(#{color}, 1)"
|
||||
{
|
||||
type: (options[:type] || 'line'),
|
||||
data: dataset_data,
|
||||
label: label,
|
||||
fill: (options[:fill] || false),
|
||||
backgroundColor: "rgba(#{color}, 0.2)",
|
||||
borderColor: dataset_color,
|
||||
borderDash: (options[:dashed] ? [5, 5] : []),
|
||||
borderWidth: (options[:dashed] ? 1.5 : 2),
|
||||
pointRadius: (options[:nopoints] ? 0 : 3),
|
||||
pointBackgroundColor: dataset_color,
|
||||
tooltips: { enable: false }
|
||||
}
|
||||
end
|
||||
|
||||
def initialize_chart_periods
|
||||
raise Exception "Dates can't be blank" if [@date_to, @date_from].any?(&:blank?)
|
||||
period_count
|
||||
scale_division
|
||||
end
|
||||
|
||||
def issues_count_by_period(issues_scope)
|
||||
data = [0] * @period_count
|
||||
issues_scope.each do |c|
|
||||
next if c.first.localtime.to_date > @date_to.to_date
|
||||
period_num = ((@date_to.to_date - c.first.localtime.to_date).to_i / @scale_division).to_i
|
||||
data[period_num] += c.last unless data[period_num].blank?
|
||||
end
|
||||
data.reverse
|
||||
end
|
||||
|
||||
def issues_avg_count_by_period(issues_scope)
|
||||
count_by_date = {}
|
||||
issues_scope.each {|x, y| count_by_date[x.localtime.to_date] = count_by_date[x.localtime.to_date].to_i + y }
|
||||
data = [0] * @period_count
|
||||
count_by_date.each do |x, y|
|
||||
next if x.to_date > @date_to.to_date
|
||||
period_num = ((@date_to.to_date - x.to_date).to_i / @scale_division).to_i
|
||||
if data[period_num]
|
||||
data[period_num] = y unless data[period_num].to_i > 0
|
||||
data[period_num] = (data[period_num] + y) / 2.0
|
||||
end
|
||||
end
|
||||
data.reverse
|
||||
end
|
||||
|
||||
def chart_fields_by_period
|
||||
chart_dates_by_period.map { |d| chart_field_by_date(d) }
|
||||
end
|
||||
|
||||
def chart_field_by_date(date)
|
||||
case @interval_size
|
||||
when YEAR_INTERVAL
|
||||
date.year
|
||||
when QUARTER_INTERVAL, MONTH_INTERVAL
|
||||
month_abbr_name(date.month) + ' ' + date.year.to_s
|
||||
else
|
||||
cumulative_left += (issue.story_points.to_f * ratio.to_f / 100.0)
|
||||
total_left += (issue.story_points.to_f * (100 - ratio.to_f) / 100.0)
|
||||
total_done += (issue.story_points.to_f * ratio.to_f / 100.0)
|
||||
date.day.to_s + ' ' + month_name(date.month)
|
||||
end
|
||||
end
|
||||
[total_left, cumulative_left, total_done]
|
||||
end
|
||||
|
||||
def use_subissue_done_ratio
|
||||
!Setting.respond_to?(:parent_issue_done_ratio) || Setting.parent_issue_done_ratio == 'derived' || Setting.parent_issue_done_ratio.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scope_by_created_date
|
||||
@data_scope.
|
||||
where("#{Issue.table_name}.created_on >= ?", @date_from).
|
||||
where("#{Issue.table_name}.created_on < ?", @date_to.to_date + 1).
|
||||
where("#{Issue.table_name}.created_on IS NOT NULL").
|
||||
group("#{Issue.table_name}.created_on").
|
||||
count
|
||||
end
|
||||
|
||||
def scope_by_closed_date
|
||||
@data_scope.
|
||||
open(false).
|
||||
where("#{Issue.table_name}.closed_on >= ?", @date_from).
|
||||
where("#{Issue.table_name}.closed_on < ?", @date_to.to_date + 1).
|
||||
where("#{Issue.table_name}.closed_on IS NOT NULL").
|
||||
group("#{Issue.table_name}.closed_on").
|
||||
count
|
||||
end
|
||||
|
||||
# options
|
||||
# color - Line color in RGB format (e.g '255,255,255') (random)
|
||||
# fill - Fille background under line (false)
|
||||
# dashed - Draw dached line (solid)
|
||||
# nopoints - Doesn't show points on line (false)
|
||||
|
||||
def dataset(dataset_data, label, options = {})
|
||||
color = options[:color] || [rand(255), rand(255), rand(255)].join(',')
|
||||
dataset_color = "rgba(#{color}, 1)"
|
||||
{
|
||||
type: (options[:type] || 'line'),
|
||||
data: dataset_data,
|
||||
label: label,
|
||||
fill: (options[:fill] || false),
|
||||
backgroundColor: "rgba(#{color}, 0.2)",
|
||||
borderColor: dataset_color,
|
||||
borderDash: (options[:dashed] ? [5, 5] : []),
|
||||
borderWidth: (options[:dashed] ? 1.5 : 2),
|
||||
pointRadius: (options[:nopoints] ? 0 : 3),
|
||||
pointBackgroundColor: dataset_color,
|
||||
tooltips: { enable: false }
|
||||
}
|
||||
end
|
||||
|
||||
def initialize_chart_periods
|
||||
raise Exception "Dates can't be blank" if [@date_to, @date_from].any?(&:blank?)
|
||||
period_count
|
||||
scale_division
|
||||
end
|
||||
|
||||
def issues_count_by_period(issues_scope)
|
||||
data = [0] * @period_count
|
||||
issues_scope.each do |c|
|
||||
next if c.first.localtime.to_date > @date_to.to_date
|
||||
period_num = ((@date_to.to_date - c.first.localtime.to_date).to_i / @scale_division).to_i
|
||||
data[period_num] += c.last unless data[period_num].blank?
|
||||
end
|
||||
data.reverse
|
||||
end
|
||||
|
||||
def issues_avg_count_by_period(issues_scope)
|
||||
count_by_date = {}
|
||||
issues_scope.each {|x, y| count_by_date[x.localtime.to_date] = count_by_date[x.localtime.to_date].to_i + y }
|
||||
data = [0] * @period_count
|
||||
count_by_date.each do |x, y|
|
||||
next if x.to_date > @date_to.to_date
|
||||
period_num = ((@date_to.to_date - x.to_date).to_i / @scale_division).to_i
|
||||
if data[period_num]
|
||||
data[period_num] = y unless data[period_num].to_i > 0
|
||||
data[period_num] = (data[period_num] + y) / 2.0
|
||||
def weekend_periods
|
||||
periods = []
|
||||
@period_count.times do |m|
|
||||
period_date = ((@date_to.to_date - 1 - m * @scale_division) + 1)
|
||||
periods << @period_count - m - 1 if non_working_week_days.include?(period_date.cwday)
|
||||
end
|
||||
end
|
||||
data.reverse
|
||||
end
|
||||
|
||||
def chart_fields_by_period
|
||||
chart_dates_by_period.map { |d| chart_field_by_date(d) }
|
||||
end
|
||||
|
||||
def chart_field_by_date(date)
|
||||
case @interval_size
|
||||
when YEAR_INTERVAL
|
||||
date.year
|
||||
when QUARTER_INTERVAL, MONTH_INTERVAL
|
||||
month_abbr_name(date.month) + ' ' + date.year.to_s
|
||||
else
|
||||
date.day.to_s + ' ' + month_name(date.month)
|
||||
end
|
||||
end
|
||||
|
||||
def weekend_periods
|
||||
periods = []
|
||||
@period_count.times do |m|
|
||||
period_date = ((@date_to.to_date - 1 - m * @scale_division) + 1)
|
||||
periods << @period_count - m - 1 if non_working_week_days.include?(period_date.cwday)
|
||||
end
|
||||
periods.compact
|
||||
end
|
||||
|
||||
def chart_data_pairs(chart_data)
|
||||
chart_data.inject([]) { |accum, value| accum << value }
|
||||
data_pairs = []
|
||||
for i in 0..chart_data.count - 1
|
||||
data_pairs << [chart_dates_by_period[i], chart_data[i]]
|
||||
end
|
||||
data_pairs
|
||||
end
|
||||
|
||||
def chart_dates_by_period
|
||||
return @chart_dates_by_period if @chart_dates_by_period
|
||||
|
||||
period = period_count > 1 ? period_count - 1 : period_count
|
||||
@chart_dates_by_period ||= period.times.inject([]) do |accum, m|
|
||||
period_date = ((@date_to.to_date - 1 - m * @scale_division) + 1)
|
||||
accum << if @interval_size == WEEK_INTERVAL
|
||||
period_date.at_beginning_of_week.to_date
|
||||
else
|
||||
period_date.to_date
|
||||
end
|
||||
end.reverse
|
||||
end
|
||||
|
||||
def month_abbr_name(month)
|
||||
l('date.abbr_month_names')[month]
|
||||
end
|
||||
|
||||
def trendline(y_values)
|
||||
size = y_values.size
|
||||
x_values = (1..size).to_a
|
||||
sum_x = 0
|
||||
sum_y = 0
|
||||
sum_xx = 0
|
||||
sum_xy = 0
|
||||
y_values.zip(x_values).each do |y, x|
|
||||
sum_xy += x * y
|
||||
sum_xx += x * x
|
||||
sum_x += x
|
||||
sum_y += y
|
||||
periods.compact
|
||||
end
|
||||
|
||||
slope = 1.0 * ((size * sum_xy) - (sum_x * sum_y)) / ((size * sum_xx) - (sum_x * sum_x))
|
||||
intercept = 1.0 * (sum_y - (slope * sum_x)) / size
|
||||
def chart_data_pairs(chart_data)
|
||||
chart_data.inject([]) { |accum, value| accum << value }
|
||||
data_pairs = []
|
||||
for i in 0..chart_data.count - 1
|
||||
data_pairs << [chart_dates_by_period[i], chart_data[i]]
|
||||
end
|
||||
data_pairs
|
||||
end
|
||||
|
||||
line_values = x_values.map { |x| predict(x, slope, intercept) }
|
||||
line_values.select { |val| val >= 0 }
|
||||
end
|
||||
def chart_dates_by_period
|
||||
return @chart_dates_by_period if @chart_dates_by_period
|
||||
|
||||
def predict(x, slope, intercept)
|
||||
slope * x + intercept
|
||||
end
|
||||
period = period_count > 1 ? period_count - 1 : period_count
|
||||
@chart_dates_by_period ||= period.times.inject([]) do |accum, m|
|
||||
period_date = ((@date_to.to_date - 1 - m * @scale_division) + 1)
|
||||
accum << if @interval_size == WEEK_INTERVAL
|
||||
period_date.at_beginning_of_week.to_date
|
||||
else
|
||||
period_date.to_date
|
||||
end
|
||||
end.reverse
|
||||
end
|
||||
|
||||
def period_count
|
||||
@period_count ||= ((@date_to.to_time - @date_from.to_time) / time_divider).round + 1
|
||||
end
|
||||
def month_abbr_name(month)
|
||||
l('date.abbr_month_names')[month]
|
||||
end
|
||||
|
||||
def scale_division
|
||||
@scale_division ||= time_divider / 1.day
|
||||
end
|
||||
def trendline(y_values)
|
||||
size = y_values.size
|
||||
x_values = (1..size).to_a
|
||||
sum_x = 0
|
||||
sum_y = 0
|
||||
sum_xx = 0
|
||||
sum_xy = 0
|
||||
y_values.zip(x_values).each do |y, x|
|
||||
sum_xy += x * y
|
||||
sum_xx += x * x
|
||||
sum_x += x
|
||||
sum_y += y
|
||||
end
|
||||
|
||||
def time_divider
|
||||
@interval_size == QUARTER_INTERVAL ? 3.months : 1.send(@interval_size)
|
||||
slope = 1.0 * ((size * sum_xy) - (sum_x * sum_y)) / ((size * sum_xx) - (sum_x * sum_x))
|
||||
intercept = 1.0 * (sum_y - (slope * sum_x)) / size
|
||||
|
||||
line_values = x_values.map { |x| predict(x, slope, intercept) }
|
||||
line_values.select { |val| val >= 0 }
|
||||
end
|
||||
|
||||
def predict(x, slope, intercept)
|
||||
slope * x + intercept
|
||||
end
|
||||
|
||||
def period_count
|
||||
@period_count ||= ((@date_to.to_time - @date_from.to_time) / time_divider).round + 1
|
||||
end
|
||||
|
||||
def scale_division
|
||||
@scale_division ||= time_divider / 1.day
|
||||
end
|
||||
|
||||
def time_divider
|
||||
@interval_size == QUARTER_INTERVAL ? 3.months : 1.send(@interval_size)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -18,95 +18,97 @@
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module RedmineAgile
|
||||
class BurndownChart < AgileChart
|
||||
attr_accessor :burndown_data, :cumulative_burndown_data
|
||||
module Charts
|
||||
class BurndownChart < AgileChart
|
||||
attr_accessor :burndown_data, :cumulative_burndown_data
|
||||
|
||||
def initialize(data_scope, options = {})
|
||||
@date_from = options[:date_from] && options[:date_from].to_date ||
|
||||
[data_scope.minimum("#{Issue.table_name}.created_on"),
|
||||
data_scope.minimum("#{Issue.table_name}.start_date")].compact.map(&:to_date).min
|
||||
def initialize(data_scope, options = {})
|
||||
@date_from = options[:date_from] && options[:date_from].to_date ||
|
||||
[data_scope.minimum("#{Issue.table_name}.created_on"),
|
||||
data_scope.minimum("#{Issue.table_name}.start_date")].compact.map(&:to_date).min
|
||||
|
||||
@date_to = options[:date_to] && options[:date_to].to_date ||
|
||||
[options[:due_date],
|
||||
data_scope.maximum("#{Issue.table_name}.updated_on")].compact.map(&:to_date).max
|
||||
@date_to = options[:date_to] && options[:date_to].to_date ||
|
||||
[options[:due_date],
|
||||
data_scope.maximum("#{Issue.table_name}.updated_on")].compact.map(&:to_date).max
|
||||
|
||||
@due_date = options[:due_date].to_date if options[:due_date]
|
||||
@show_ideal_effort = options[:date_from] && options[:date_to]
|
||||
@due_date = options[:due_date].to_date if options[:due_date]
|
||||
@show_ideal_effort = options[:date_from] && options[:date_to]
|
||||
|
||||
super data_scope, options
|
||||
super data_scope, options
|
||||
|
||||
@fields = [''] + @fields
|
||||
@y_title = l(:label_agile_charts_number_of_issues)
|
||||
@graph_title = l(:label_agile_charts_issues_burndown)
|
||||
@line_colors = { :work => '80,122,170', :ideal => '102,102,102', :total => '80,122,170' }
|
||||
end
|
||||
|
||||
def data
|
||||
return false unless calculate_burndown_data.any?
|
||||
|
||||
datasets = [dataset(@burndown_data, l(:label_agile_actual_work_remaining), :fill => true, :color => line_colors[:work])]
|
||||
if @show_ideal_effort
|
||||
datasets << dataset(ideal_effort(@cumulative_burndown_data.first), l(:label_agile_ideal_work_remaining),
|
||||
:color => line_colors[:ideal], :dashed => true, :nopoints => true)
|
||||
end
|
||||
if @show_ideal_effort && (@cumulative_burndown_data != @burndown_data)
|
||||
datasets << dataset(@cumulative_burndown_data, l(:label_agile_total_work_remaining),
|
||||
:color => line_colors[:total], :dashed => true)
|
||||
@fields = [''] + @fields
|
||||
@y_title = l(:label_agile_charts_number_of_issues)
|
||||
@graph_title = l(:label_agile_charts_issues_burndown)
|
||||
@line_colors = { :work => '80,122,170', :ideal => '102,102,102', :total => '80,122,170' }
|
||||
end
|
||||
|
||||
{
|
||||
:title => @graph_title,
|
||||
:y_title => @y_title,
|
||||
:labels => @fields,
|
||||
:datasets => datasets,
|
||||
:show_tooltips => [0, 2]
|
||||
}
|
||||
end
|
||||
def data
|
||||
return false unless calculate_burndown_data.any?
|
||||
|
||||
def self.data(data_scope, options = {})
|
||||
if options[:chart_unit] == Charts::UNIT_HOURS
|
||||
WorkBurndownChart.new(data_scope, options.merge(estimated_unit: ESTIMATE_HOURS)).data
|
||||
elsif options[:chart_unit] == Charts::UNIT_STORY_POINTS
|
||||
WorkBurndownChart.new(data_scope, options.merge(estimated_unit: ESTIMATE_STORY_POINTS)).data
|
||||
else
|
||||
super
|
||||
datasets = [dataset(@burndown_data, l(:label_agile_actual_work_remaining), :fill => true, :color => line_colors[:work])]
|
||||
if @show_ideal_effort
|
||||
datasets << dataset(ideal_effort(@cumulative_burndown_data.first), l(:label_agile_ideal_work_remaining),
|
||||
:color => line_colors[:ideal], :dashed => true, :nopoints => true)
|
||||
end
|
||||
if @show_ideal_effort && (@cumulative_burndown_data != @burndown_data)
|
||||
datasets << dataset(@cumulative_burndown_data, l(:label_agile_total_work_remaining),
|
||||
:color => line_colors[:total], :dashed => true)
|
||||
end
|
||||
|
||||
{
|
||||
:title => @graph_title,
|
||||
:y_title => @y_title,
|
||||
:labels => @fields,
|
||||
:datasets => datasets,
|
||||
:show_tooltips => [0, 2]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def ideal_effort(start_remaining)
|
||||
data = [0] * (due_date_period - 1)
|
||||
active_periods = (RedmineAgile.exclude_weekends? && date_short_period?) ? due_date_period - @weekend_periods.select { |p| p < due_date_period }.count : due_date_period
|
||||
avg_remaining_velocity = start_remaining.to_f / active_periods.to_f
|
||||
sum = start_remaining.to_f
|
||||
data[0] = sum
|
||||
(1..due_date_period - 1).each do |i|
|
||||
sum -= avg_remaining_velocity unless (RedmineAgile.exclude_weekends? && date_short_period?) && @weekend_periods.include?(i - 1)
|
||||
data[i] = (sum * 100).round / 100.0
|
||||
def self.data(data_scope, options = {})
|
||||
if options[:chart_unit] == Helper::UNIT_HOURS
|
||||
WorkBurndownChart.new(data_scope, options.merge(estimated_unit: ESTIMATE_HOURS)).data
|
||||
elsif options[:chart_unit] == Helper::UNIT_STORY_POINTS
|
||||
WorkBurndownChart.new(data_scope, options.merge(estimated_unit: ESTIMATE_STORY_POINTS)).data
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
data[due_date_period] = 0
|
||||
data
|
||||
end
|
||||
|
||||
def calculate_burndown_data
|
||||
created_by_period = issues_count_by_period(scope_by_created_date)
|
||||
closed_by_period = issues_count_by_period(scope_by_closed_date)
|
||||
protected
|
||||
|
||||
total_issues = @data_scope.count
|
||||
total_issues_before = @data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).count
|
||||
total_closed_before = @data_scope.open(false).where("#{Issue.table_name}.closed_on < ?", @date_from).count
|
||||
def ideal_effort(start_remaining)
|
||||
data = [0] * (due_date_period - 1)
|
||||
active_periods = (RedmineAgile.exclude_weekends? && date_short_period?) ? due_date_period - @weekend_periods.select { |p| p < due_date_period }.count : due_date_period
|
||||
avg_remaining_velocity = start_remaining.to_f / active_periods.to_f
|
||||
sum = start_remaining.to_f
|
||||
data[0] = sum
|
||||
(1..due_date_period - 1).each do |i|
|
||||
sum -= avg_remaining_velocity unless (RedmineAgile.exclude_weekends? && date_short_period?) && @weekend_periods.include?(i - 1)
|
||||
data[i] = (sum * 100).round / 100.0
|
||||
end
|
||||
data[due_date_period] = 0
|
||||
data
|
||||
end
|
||||
|
||||
sum = total_issues_before
|
||||
cumulative_created_by_period = created_by_period.first(current_date_period).map { |x| sum += x }
|
||||
sum = total_closed_before
|
||||
cumulative_closed_by_period = closed_by_period.first(current_date_period).map { |x| sum += x }
|
||||
def calculate_burndown_data
|
||||
created_by_period = issues_count_by_period(scope_by_created_date)
|
||||
closed_by_period = issues_count_by_period(scope_by_closed_date)
|
||||
|
||||
burndown_by_period = [0] * (current_date_period)
|
||||
cumulative_created_by_period.each_with_index { |e, i| burndown_by_period[i] = e - cumulative_closed_by_period[i] }
|
||||
first_day_open_issues = @data_scope.where("#{Issue.table_name}.created_on < ?", @date_from + 1).count - total_closed_before
|
||||
@cumulative_burndown_data = [total_issues - total_closed_before] + cumulative_closed_by_period.map { |c| total_issues - c }
|
||||
@burndown_data = [first_day_open_issues] + burndown_by_period
|
||||
total_issues = @data_scope.count
|
||||
total_issues_before = @data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).count
|
||||
total_closed_before = @data_scope.open(false).where("#{Issue.table_name}.closed_on < ?", @date_from).count
|
||||
|
||||
sum = total_issues_before
|
||||
cumulative_created_by_period = created_by_period.first(current_date_period).map { |x| sum += x }
|
||||
sum = total_closed_before
|
||||
cumulative_closed_by_period = closed_by_period.first(current_date_period).map { |x| sum += x }
|
||||
|
||||
burndown_by_period = [0] * (current_date_period)
|
||||
cumulative_created_by_period.each_with_index { |e, i| burndown_by_period[i] = e - cumulative_closed_by_period[i] }
|
||||
first_day_open_issues = @data_scope.where("#{Issue.table_name}.created_on < ?", @date_from + 1).count - total_closed_before
|
||||
@cumulative_burndown_data = [total_issues - total_closed_before] + cumulative_closed_by_period.map { |c| total_issues - c }
|
||||
@burndown_data = [first_day_open_issues] + burndown_by_period
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,98 +0,0 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# redmine_agile is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module RedmineAgile
|
||||
module Charts
|
||||
BURNDOWN_CHART = 'burndown_chart'.freeze
|
||||
ISSUES_BURNDOWN_CHART = 'issues_burndown'.freeze
|
||||
WORK_BURNDOWN_SP_CHART = 'work_burndown_sp'.freeze
|
||||
WORK_BURNDOWN_HOURS_CHART = 'work_burndown_hours'.freeze
|
||||
|
||||
AGILE_CHARTS = {
|
||||
BURNDOWN_CHART => { name: :label_agile_chart_burndown, class: BurndownChart,
|
||||
aliases: [ISSUES_BURNDOWN_CHART, WORK_BURNDOWN_HOURS_CHART, WORK_BURNDOWN_SP_CHART] },
|
||||
}.freeze
|
||||
|
||||
CHARTS_WITH_UNITS = [
|
||||
BURNDOWN_CHART,
|
||||
].freeze
|
||||
|
||||
UNIT_ISSUES = 'issues'.freeze
|
||||
UNIT_STORY_POINTS = 'story_points'.freeze
|
||||
UNIT_HOURS = 'hours'.freeze
|
||||
|
||||
CHART_UNITS = {
|
||||
UNIT_ISSUES => :label_issue_plural,
|
||||
UNIT_STORY_POINTS => :label_agile_story_points,
|
||||
UNIT_HOURS => :label_agile_hours
|
||||
}.freeze
|
||||
|
||||
CHART_UNIT_BY_ALIAS = {
|
||||
ISSUES_BURNDOWN_CHART => UNIT_ISSUES,
|
||||
WORK_BURNDOWN_HOURS_CHART => UNIT_HOURS,
|
||||
WORK_BURNDOWN_SP_CHART => UNIT_STORY_POINTS,
|
||||
}
|
||||
|
||||
def self.valid_chart?(name) !!AGILE_CHARTS[name] end
|
||||
|
||||
def self.chart_by_alias(alias_name)
|
||||
chart_name = nil
|
||||
AGILE_CHARTS.each do |chart, value|
|
||||
if value[:aliases] && value[:aliases].include?(alias_name)
|
||||
chart_name = chart
|
||||
break
|
||||
end
|
||||
end
|
||||
chart_name
|
||||
end
|
||||
|
||||
def self.valid_chart_name_by(old_chart_name)
|
||||
if valid_chart?(old_chart_name)
|
||||
old_chart_name
|
||||
elsif (chart_by_alias = chart_by_alias(old_chart_name))
|
||||
chart_by_alias
|
||||
else
|
||||
BURNDOWN_CHART
|
||||
end
|
||||
end
|
||||
|
||||
def self.valid_chart_unit?(name) !!CHART_UNITS[name] end
|
||||
|
||||
def self.chart_with_units?(old_chart_name)
|
||||
CHARTS_WITH_UNITS.include? valid_chart_name_by(old_chart_name)
|
||||
end
|
||||
|
||||
def self.valid_chart_unit_by(old_chart_name, chart_unit)
|
||||
if chart_with_units?(old_chart_name)
|
||||
if chart_by_alias(old_chart_name)
|
||||
CHART_UNIT_BY_ALIAS[old_chart_name]
|
||||
elsif valid_chart_unit?(chart_unit)
|
||||
chart_unit
|
||||
else
|
||||
UNIT_ISSUES
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.chart_unit_label_by(alias_name)
|
||||
chart_unit = CHART_UNIT_BY_ALIAS[alias_name]
|
||||
CHART_UNITS[chart_unit]
|
||||
end
|
||||
end
|
||||
end
|
100
lib/redmine_agile/charts/helper.rb
Normal file
100
lib/redmine_agile/charts/helper.rb
Normal file
@ -0,0 +1,100 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# redmine_agile is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module RedmineAgile
|
||||
module Charts
|
||||
class Helper
|
||||
BURNDOWN_CHART = 'burndown_chart'.freeze
|
||||
ISSUES_BURNDOWN_CHART = 'issues_burndown'.freeze
|
||||
WORK_BURNDOWN_SP_CHART = 'work_burndown_sp'.freeze
|
||||
WORK_BURNDOWN_HOURS_CHART = 'work_burndown_hours'.freeze
|
||||
|
||||
AGILE_CHARTS = {
|
||||
BURNDOWN_CHART => { name: :label_agile_chart_burndown, class: BurndownChart,
|
||||
aliases: [ISSUES_BURNDOWN_CHART, WORK_BURNDOWN_HOURS_CHART, WORK_BURNDOWN_SP_CHART] },
|
||||
}.freeze
|
||||
|
||||
CHARTS_WITH_UNITS = [
|
||||
BURNDOWN_CHART,
|
||||
].freeze
|
||||
|
||||
UNIT_ISSUES = 'issues'.freeze
|
||||
UNIT_STORY_POINTS = 'story_points'.freeze
|
||||
UNIT_HOURS = 'hours'.freeze
|
||||
|
||||
CHART_UNITS = {
|
||||
UNIT_ISSUES => :label_issue_plural,
|
||||
UNIT_STORY_POINTS => :label_agile_story_points,
|
||||
UNIT_HOURS => :label_agile_hours
|
||||
}.freeze
|
||||
|
||||
CHART_UNIT_BY_ALIAS = {
|
||||
ISSUES_BURNDOWN_CHART => UNIT_ISSUES,
|
||||
WORK_BURNDOWN_HOURS_CHART => UNIT_HOURS,
|
||||
WORK_BURNDOWN_SP_CHART => UNIT_STORY_POINTS,
|
||||
}
|
||||
|
||||
def self.valid_chart?(name) !!AGILE_CHARTS[name] end
|
||||
|
||||
def self.chart_by_alias(alias_name)
|
||||
chart_name = nil
|
||||
AGILE_CHARTS.each do |chart, value|
|
||||
if value[:aliases] && value[:aliases].include?(alias_name)
|
||||
chart_name = chart
|
||||
break
|
||||
end
|
||||
end
|
||||
chart_name
|
||||
end
|
||||
|
||||
def self.valid_chart_name_by(old_chart_name)
|
||||
if valid_chart?(old_chart_name)
|
||||
old_chart_name
|
||||
elsif (chart_by_alias = chart_by_alias(old_chart_name))
|
||||
chart_by_alias
|
||||
else
|
||||
BURNDOWN_CHART
|
||||
end
|
||||
end
|
||||
|
||||
def self.valid_chart_unit?(name) !!CHART_UNITS[name] end
|
||||
|
||||
def self.chart_with_units?(old_chart_name)
|
||||
CHARTS_WITH_UNITS.include? valid_chart_name_by(old_chart_name)
|
||||
end
|
||||
|
||||
def self.valid_chart_unit_by(old_chart_name, chart_unit)
|
||||
if chart_with_units?(old_chart_name)
|
||||
if chart_by_alias(old_chart_name)
|
||||
CHART_UNIT_BY_ALIAS[old_chart_name]
|
||||
elsif valid_chart_unit?(chart_unit)
|
||||
chart_unit
|
||||
else
|
||||
UNIT_ISSUES
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.chart_unit_label_by(alias_name)
|
||||
chart_unit = CHART_UNIT_BY_ALIAS[alias_name]
|
||||
CHART_UNITS[chart_unit]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -18,52 +18,54 @@
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module RedmineAgile
|
||||
class WorkBurndownChart < BurndownChart
|
||||
def initialize(data_scope, options = {})
|
||||
super data_scope, options
|
||||
if @estimated_unit == 'hours'
|
||||
@y_title = l(:label_agile_charts_number_of_hours)
|
||||
@graph_title = l(:label_agile_charts_work_burndown_hours)
|
||||
else
|
||||
@y_title = l(:label_agile_charts_number_of_story_points)
|
||||
@graph_title = l(:label_agile_charts_work_burndown_sp)
|
||||
module Charts
|
||||
class WorkBurndownChart < BurndownChart
|
||||
def initialize(data_scope, options = {})
|
||||
super data_scope, options
|
||||
if @estimated_unit == 'hours'
|
||||
@y_title = l(:label_agile_charts_number_of_hours)
|
||||
@graph_title = l(:label_agile_charts_work_burndown_hours)
|
||||
else
|
||||
@y_title = l(:label_agile_charts_number_of_story_points)
|
||||
@graph_title = l(:label_agile_charts_work_burndown_sp)
|
||||
end
|
||||
|
||||
@line_colors = { work: '0,153,0', ideal: '102,102,102', total: '0,153,0' }
|
||||
end
|
||||
|
||||
@line_colors = { work: '0,153,0', ideal: '102,102,102', total: '0,153,0' }
|
||||
end
|
||||
protected
|
||||
|
||||
protected
|
||||
def calculate_burndown_data
|
||||
data_scope = @data_scope
|
||||
data_scope = data_scope.where("#{Issue.table_name}.rgt - #{Issue.table_name}.lft = 1") if use_subissue_done_ratio && @estimated_unit == 'hours'
|
||||
|
||||
def calculate_burndown_data
|
||||
data_scope = @data_scope
|
||||
data_scope = data_scope.where("#{Issue.table_name}.rgt - #{Issue.table_name}.lft = 1") if use_subissue_done_ratio && @estimated_unit == 'hours'
|
||||
if @estimated_unit == 'hours'
|
||||
all_issues = data_scope.where("#{Issue.table_name}.estimated_hours IS NOT NULL").
|
||||
eager_load([:journals, :status, { journals: { details: :journal } }])
|
||||
cumulative_total_hours = data_scope.sum("#{Issue.table_name}.estimated_hours").to_f
|
||||
else
|
||||
all_issues = data_scope.where("#{AgileData.table_name}.story_points IS NOT NULL").
|
||||
joins(:agile_data).eager_load([:journals, :status, { journals: { details: :journal } }])
|
||||
cumulative_total_hours = data_scope.joins(:agile_data).sum("#{AgileData.table_name}.story_points").to_f
|
||||
end
|
||||
|
||||
if @estimated_unit == 'hours'
|
||||
all_issues = data_scope.where("#{Issue.table_name}.estimated_hours IS NOT NULL").
|
||||
eager_load([:journals, :status, { journals: { details: :journal } }])
|
||||
cumulative_total_hours = data_scope.sum("#{Issue.table_name}.estimated_hours").to_f
|
||||
else
|
||||
all_issues = data_scope.where("#{AgileData.table_name}.story_points IS NOT NULL").
|
||||
joins(:agile_data).eager_load([:journals, :status, { journals: { details: :journal } }])
|
||||
cumulative_total_hours = data_scope.joins(:agile_data).sum("#{AgileData.table_name}.story_points").to_f
|
||||
data = chart_dates_by_period.select { |d| d <= Date.today }.map do |date|
|
||||
issues = all_issues.select { |issue| issue.created_on.localtime.to_date <= date }
|
||||
total_hours_left, cumulative_total_hours_left = date_effort(issues, date)
|
||||
[total_hours_left, cumulative_total_hours - cumulative_total_hours_left]
|
||||
end
|
||||
tail_values = data.last ? [data.last] * (current_date_period - data.size) : []
|
||||
data = first_period_effort(all_issues, chart_dates_by_period.first, cumulative_total_hours) + data + tail_values
|
||||
@burndown_data, @cumulative_burndown_data = data.transpose
|
||||
end
|
||||
|
||||
data = chart_dates_by_period.select { |d| d <= Date.today }.map do |date|
|
||||
issues = all_issues.select { |issue| issue.created_on.localtime.to_date <= date }
|
||||
total_hours_left, cumulative_total_hours_left = date_effort(issues, date)
|
||||
[total_hours_left, cumulative_total_hours - cumulative_total_hours_left]
|
||||
private
|
||||
|
||||
def first_period_effort(issues_scope, start_date, cumulative_total_hours)
|
||||
issues = issues_scope.select { |issue| issue.created_on.localtime.to_date <= start_date }
|
||||
total_left, cumulative_left = date_effort(issues, start_date - 1)
|
||||
[[total_left, cumulative_total_hours - cumulative_left]]
|
||||
end
|
||||
tail_values = data.last ? [data.last] * (current_date_period - data.size) : []
|
||||
data = first_period_effort(all_issues, chart_dates_by_period.first, cumulative_total_hours) + data + tail_values
|
||||
@burndown_data, @cumulative_burndown_data = data.transpose
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def first_period_effort(issues_scope, start_date, cumulative_total_hours)
|
||||
issues = issues_scope.select { |issue| issue.created_on.localtime.to_date <= start_date }
|
||||
total_left, cumulative_left = date_effort(issues, start_date - 1)
|
||||
[[total_left, cumulative_total_hours - cumulative_left]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -20,134 +20,136 @@
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module RedmineAgile
|
||||
module AgileHelper
|
||||
module Helpers
|
||||
module AgileHelper
|
||||
|
||||
def retrieve_agile_query_from_session
|
||||
if session[:agile_query]
|
||||
if session[:agile_query][:id]
|
||||
@query = AgileQuery.find_by_id(session[:agile_query][:id])
|
||||
return unless @query
|
||||
else
|
||||
@query = AgileQuery.new(get_query_attributes_from_session)
|
||||
end
|
||||
if session[:agile_query].has_key?(:project_id)
|
||||
@query.project_id = session[:agile_query][:project_id]
|
||||
else
|
||||
@query.project = @project
|
||||
end
|
||||
@query
|
||||
else
|
||||
@query = AgileQuery.new(:name => "_")
|
||||
end
|
||||
end
|
||||
|
||||
def retrieve_agile_query
|
||||
if !params[:query_id].blank?
|
||||
cond = "project_id IS NULL"
|
||||
cond << " OR project_id = #{@project.id}" if @project
|
||||
@query = AgileQuery.where(cond).find(params[:query_id])
|
||||
raise ::Unauthorized unless @query.visible?
|
||||
@query.project = @project
|
||||
session[:agile_query] = {:id => @query.id, :project_id => @query.project_id}
|
||||
sort_clear
|
||||
elsif api_request? || params[:set_filter] || session[:agile_query].nil? || session[:agile_query][:project_id] != (@project ? @project.id : nil)
|
||||
unless @query
|
||||
@query = AgileQuery.new(:name => "_", :project => @project)
|
||||
@query.build_from_params(params)
|
||||
else
|
||||
@query.project = @project if @project
|
||||
end
|
||||
save_query_attribures_to_session(@query)
|
||||
else
|
||||
# retrieve from session
|
||||
@query = nil
|
||||
if session[:agile_query] && !session[:agile_query][:id] && !params[:project_id]
|
||||
@query = AgileQuery.new(get_query_attributes_from_session)
|
||||
end
|
||||
|
||||
@query ||= AgileQuery.find_by_id(session[:agile_query][:id]) if session[:agile_query][:id]
|
||||
@query ||= AgileQuery.new(get_query_attributes_from_session)
|
||||
@query.project = @project
|
||||
save_query_attribures_to_session(@query)
|
||||
end
|
||||
end
|
||||
|
||||
def options_card_colors_for_select(selected, options={})
|
||||
color_base = [[l(:label_agile_color_no_colors), "none"],
|
||||
[l(:label_issue), "issue"],
|
||||
[l(:label_tracker), "tracker"],
|
||||
[l(:field_priority), "priority"],
|
||||
[l(:label_spent_time), "spent_time"],
|
||||
[l(:field_assigned_to), "user"]]
|
||||
color_base << [l(:field_project), 'project'] if (@project && @project.children.any?) || !@project
|
||||
options_for_select(color_base.compact, selected)
|
||||
end
|
||||
|
||||
def options_charts_for_select(selected)
|
||||
container = []
|
||||
RedmineAgile::Charts::AGILE_CHARTS.each { |k, v| container << [l(v[:name]), k] }
|
||||
selected_chart = RedmineAgile::Charts.chart_by_alias(selected) || selected
|
||||
options_for_select(container, selected_chart)
|
||||
end
|
||||
|
||||
def grouped_options_charts_for_select(selected)
|
||||
grouped_options = {}
|
||||
container = []
|
||||
|
||||
RedmineAgile::Charts::AGILE_CHARTS.each do |chart, value|
|
||||
if RedmineAgile::Charts::CHARTS_WITH_UNITS.include?(chart)
|
||||
group = l(value[:name])
|
||||
grouped_options[group] = []
|
||||
value[:aliases].each do |alias_name|
|
||||
grouped_options[group] << ["#{group} (#{l(RedmineAgile::Charts.chart_unit_label_by(alias_name))})", alias_name]
|
||||
def retrieve_agile_query_from_session
|
||||
if session[:agile_query]
|
||||
if session[:agile_query][:id]
|
||||
@query = AgileQuery.find_by_id(session[:agile_query][:id])
|
||||
return unless @query
|
||||
else
|
||||
@query = AgileQuery.new(get_query_attributes_from_session)
|
||||
end
|
||||
if session[:agile_query].has_key?(:project_id)
|
||||
@query.project_id = session[:agile_query][:project_id]
|
||||
else
|
||||
@query.project = @project
|
||||
end
|
||||
@query
|
||||
else
|
||||
container << [l(value[:name]), chart]
|
||||
@query = AgileQuery.new(:name => "_")
|
||||
end
|
||||
end
|
||||
|
||||
grouped_options_for_select(grouped_options, selected) + options_for_select(container, selected)
|
||||
end
|
||||
def retrieve_agile_query
|
||||
if !params[:query_id].blank?
|
||||
cond = "project_id IS NULL"
|
||||
cond << " OR project_id = #{@project.id}" if @project
|
||||
@query = AgileQuery.where(cond).find(params[:query_id])
|
||||
raise ::Unauthorized unless @query.visible?
|
||||
@query.project = @project
|
||||
session[:agile_query] = {:id => @query.id, :project_id => @query.project_id}
|
||||
sort_clear
|
||||
elsif api_request? || params[:set_filter] || session[:agile_query].nil? || session[:agile_query][:project_id] != (@project ? @project.id : nil)
|
||||
unless @query
|
||||
@query = AgileQuery.new(:name => "_", :project => @project)
|
||||
@query.build_from_params(params)
|
||||
else
|
||||
@query.project = @project if @project
|
||||
end
|
||||
save_query_attribures_to_session(@query)
|
||||
else
|
||||
# retrieve from session
|
||||
@query = nil
|
||||
if session[:agile_query] && !session[:agile_query][:id] && !params[:project_id]
|
||||
@query = AgileQuery.new(get_query_attributes_from_session)
|
||||
end
|
||||
|
||||
def options_chart_units_for_select(selected = nil)
|
||||
container = []
|
||||
RedmineAgile::Charts::CHART_UNITS.each { |k, v| container << [l(v), k] }
|
||||
selected_unit = RedmineAgile::Charts::CHART_UNIT_BY_ALIAS[selected] || selected
|
||||
options_for_select(container, selected_unit)
|
||||
end
|
||||
@query ||= AgileQuery.find_by_id(session[:agile_query][:id]) if session[:agile_query][:id]
|
||||
@query ||= AgileQuery.new(get_query_attributes_from_session)
|
||||
@query.project = @project
|
||||
save_query_attribures_to_session(@query)
|
||||
end
|
||||
end
|
||||
|
||||
def render_agile_chart(chart_name, issues_scope)
|
||||
render partial: "agile_charts/chart",
|
||||
locals: { chart: chart_name, issues_scope: issues_scope, chart_unit: params[:chart_unit] }
|
||||
end
|
||||
def options_card_colors_for_select(selected, options={})
|
||||
color_base = [[l(:label_agile_color_no_colors), "none"],
|
||||
[l(:label_issue), "issue"],
|
||||
[l(:label_tracker), "tracker"],
|
||||
[l(:field_priority), "priority"],
|
||||
[l(:label_spent_time), "spent_time"],
|
||||
[l(:field_assigned_to), "user"]]
|
||||
color_base << [l(:field_project), 'project'] if (@project && @project.children.any?) || !@project
|
||||
options_for_select(color_base.compact, selected)
|
||||
end
|
||||
|
||||
def upgrade_to_pro_agile_chart_link(chart_name, query = @query, current_chart = @chart)
|
||||
link_to l("label_agile_charts_#{chart_name}"), '#',
|
||||
onclick: "showModal('upgrade-to-pro', '557px');",
|
||||
class: ('selected' if query.is_a?(AgileChartsQuery) && query.new_record? && current_chart == chart_name)
|
||||
end
|
||||
def options_charts_for_select(selected)
|
||||
container = []
|
||||
RedmineAgile::Charts::Helper::AGILE_CHARTS.each { |k, v| container << [l(v[:name]), k] }
|
||||
selected_chart = RedmineAgile::Charts::Helper.chart_by_alias(selected) || selected
|
||||
options_for_select(container, selected_chart)
|
||||
end
|
||||
|
||||
private
|
||||
def grouped_options_charts_for_select(selected)
|
||||
grouped_options = {}
|
||||
container = []
|
||||
|
||||
def get_query_attributes_from_session
|
||||
attributes = { name: '_',
|
||||
filters: session[:agile_query][:filters],
|
||||
group_by: session[:agile_query][:group_by],
|
||||
column_names: session[:agile_query][:column_names],
|
||||
color_base: session[:agile_query][:color_base] }
|
||||
(attributes[:options] = session[:agile_query][:options] || {}) if Redmine::VERSION.to_s > '2.4'
|
||||
attributes
|
||||
end
|
||||
RedmineAgile::Charts::Helper::AGILE_CHARTS.each do |chart, value|
|
||||
if RedmineAgile::Charts::Helper::CHARTS_WITH_UNITS.include?(chart)
|
||||
group = l(value[:name])
|
||||
grouped_options[group] = []
|
||||
value[:aliases].each do |alias_name|
|
||||
grouped_options[group] << ["#{group} (#{l(RedmineAgile::Charts::Helper.chart_unit_label_by(alias_name))})", alias_name]
|
||||
end
|
||||
else
|
||||
container << [l(value[:name]), chart]
|
||||
end
|
||||
end
|
||||
|
||||
def save_query_attribures_to_session(query)
|
||||
session[:agile_query] = { project_id: query.project_id,
|
||||
filters: query.filters,
|
||||
group_by: query.group_by,
|
||||
color_base: (query.respond_to?(:color_base) && query.color_base),
|
||||
column_names: query.column_names }
|
||||
(session[:agile_query][:options] = query.options) if Redmine::VERSION.to_s > '2.4'
|
||||
grouped_options_for_select(grouped_options, selected) + options_for_select(container, selected)
|
||||
end
|
||||
|
||||
def options_chart_units_for_select(selected = nil)
|
||||
container = []
|
||||
RedmineAgile::Charts::Helper::CHART_UNITS.each { |k, v| container << [l(v), k] }
|
||||
selected_unit = RedmineAgile::Charts::Helper::CHART_UNIT_BY_ALIAS[selected] || selected
|
||||
options_for_select(container, selected_unit)
|
||||
end
|
||||
|
||||
def render_agile_chart(chart_name, issues_scope)
|
||||
render partial: "agile_charts/chart",
|
||||
locals: { chart: chart_name, issues_scope: issues_scope, chart_unit: params[:chart_unit] }
|
||||
end
|
||||
|
||||
def upgrade_to_pro_agile_chart_link(chart_name, query = @query, current_chart = @chart)
|
||||
link_to l("label_agile_charts_#{chart_name}"), '#',
|
||||
onclick: "showModal('upgrade-to-pro', '557px');",
|
||||
class: ('selected' if query.is_a?(AgileChartsQuery) && query.new_record? && current_chart == chart_name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_query_attributes_from_session
|
||||
attributes = { name: '_',
|
||||
filters: session[:agile_query][:filters],
|
||||
group_by: session[:agile_query][:group_by],
|
||||
column_names: session[:agile_query][:column_names],
|
||||
color_base: session[:agile_query][:color_base] }
|
||||
(attributes[:options] = session[:agile_query][:options] || {}) if Redmine::VERSION.to_s > '2.4'
|
||||
attributes
|
||||
end
|
||||
|
||||
def save_query_attribures_to_session(query)
|
||||
session[:agile_query] = { project_id: query.project_id,
|
||||
filters: query.filters,
|
||||
group_by: query.group_by,
|
||||
color_base: (query.respond_to?(:color_base) && query.color_base),
|
||||
column_names: query.column_names }
|
||||
(session[:agile_query][:options] = query.options) if Redmine::VERSION.to_s > '2.4'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActionView::Base.send :include, RedmineAgile::AgileHelper
|
||||
ActionView::Base.send :include, RedmineAgile::Helpers::AgileHelper
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -17,9 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_agile. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
require_dependency 'issue'
|
||||
require_dependency 'agile_data'
|
||||
|
||||
module RedmineAgile
|
||||
module Patches
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -53,8 +53,8 @@ class AgileChartsControllerTest < ActionController::TestCase
|
||||
|
||||
EnabledModule.create(project: @project, name: 'agile')
|
||||
|
||||
@charts = RedmineAgile::Charts::AGILE_CHARTS.keys
|
||||
@charts_with_units = RedmineAgile::Charts::CHARTS_WITH_UNITS
|
||||
@charts = RedmineAgile::Charts::Helper::AGILE_CHARTS.keys
|
||||
@charts_with_units = RedmineAgile::Charts::Helper::CHARTS_WITH_UNITS
|
||||
end
|
||||
|
||||
def test_get_show
|
||||
@ -73,7 +73,7 @@ class AgileChartsControllerTest < ActionController::TestCase
|
||||
|
||||
def test_charts_with_chart_unit
|
||||
@charts_with_units.each do |chart|
|
||||
RedmineAgile::Charts::CHART_UNITS.each do |chart_unit, label|
|
||||
RedmineAgile::Charts::Helper::CHART_UNITS.each do |chart_unit, label|
|
||||
check_chart chart: chart, project_id: @project.identifier, chart_unit: chart_unit
|
||||
end
|
||||
end
|
||||
@ -81,7 +81,7 @@ class AgileChartsControllerTest < ActionController::TestCase
|
||||
|
||||
def test_charts_by_different_time_intervals
|
||||
@charts.each do |chart|
|
||||
RedmineAgile::AgileChart::TIME_INTERVALS.each do |interval|
|
||||
RedmineAgile::Charts::AgileChart::TIME_INTERVALS.each do |interval|
|
||||
check_chart chart: chart, project_id: @project.identifier, interval_size: interval
|
||||
end
|
||||
end
|
||||
@ -89,7 +89,7 @@ class AgileChartsControllerTest < ActionController::TestCase
|
||||
|
||||
def test_charts_by_different_periods_and_time_intervals
|
||||
@charts.each do |chart|
|
||||
RedmineAgile::AgileChart::TIME_INTERVALS.each do |interval|
|
||||
RedmineAgile::Charts::AgileChart::TIME_INTERVALS.each do |interval|
|
||||
params = {
|
||||
chart: chart,
|
||||
project_id: @project.identifier,
|
||||
@ -135,7 +135,7 @@ class AgileChartsControllerTest < ActionController::TestCase
|
||||
|
||||
def test_charts_with_version_and_chart_unit
|
||||
@charts_with_units.each do |chart|
|
||||
RedmineAgile::Charts::CHART_UNITS.each do |chart_unit, label|
|
||||
RedmineAgile::Charts::Helper::CHART_UNITS.each do |chart_unit, label|
|
||||
should_get_render_chart chart: chart, version_id: 2, chart_unit: chart_unit
|
||||
end
|
||||
end
|
||||
@ -152,7 +152,7 @@ class AgileChartsControllerTest < ActionController::TestCase
|
||||
)
|
||||
new_version.fixed_issues << issue.reload
|
||||
|
||||
should_get_render_chart chart: RedmineAgile::Charts::BURNDOWN_CHART, project_id: @project.identifier, version_id: new_version.id
|
||||
should_get_render_chart chart: RedmineAgile::Charts::Helper::BURNDOWN_CHART, project_id: @project.identifier, version_id: new_version.id
|
||||
end
|
||||
|
||||
def test_get_show_chart_with_open_target_version
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
@ -38,7 +38,7 @@ class BurndownChartTest < ActiveSupport::TestCase
|
||||
test_case_issues = test_case[:issues].call
|
||||
test_case[:inerval_data].each do |case_interval|
|
||||
puts "BurndownChartTest case - #{case_interval[:name]}"
|
||||
chart_data = RedmineAgile::BurndownChart.data(test_case_issues, case_interval[:options])
|
||||
chart_data = RedmineAgile::Charts::BurndownChart.data(test_case_issues, case_interval[:options])
|
||||
assert_equal case_interval[:result], extract_values(chart_data)
|
||||
end
|
||||
test_case_issues.destroy_all
|
||||
|
@ -3,7 +3,7 @@
|
||||
# This file is a part of Redmin Agile (redmine_agile) plugin,
|
||||
# Agile board plugin for redmine
|
||||
#
|
||||
# Copyright (C) 2011-2021 RedmineUP
|
||||
# Copyright (C) 2011-2022 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_agile is free software: you can redistribute it and/or modify
|
||||
|
Loading…
x
Reference in New Issue
Block a user