diff --git a/app/controllers/agile_boards_controller.rb b/app/controllers/agile_boards_controller.rb index 8d940b2..1f6b824 100755 --- a/app/controllers/agile_boards_controller.rb +++ b/app/controllers/agile_boards_controller.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 @@ -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') && diff --git a/app/controllers/agile_charts_controller.rb b/app/controllers/agile_charts_controller.rb index 5fd0ebe..101b76d 100644 --- a/app/controllers/agile_charts_controller.rb +++ b/app/controllers/agile_charts_controller.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 @@ -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 diff --git a/app/controllers/agile_charts_queries_controller.rb b/app/controllers/agile_charts_queries_controller.rb index 6dc6fa0..fcebd28 100644 --- a/app/controllers/agile_charts_queries_controller.rb +++ b/app/controllers/agile_charts_queries_controller.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 diff --git a/app/controllers/agile_journal_details_controller.rb b/app/controllers/agile_journal_details_controller.rb index 3d2c6c0..8fdc24c 100644 --- a/app/controllers/agile_journal_details_controller.rb +++ b/app/controllers/agile_journal_details_controller.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 diff --git a/app/helpers/agile_boards_helper.rb b/app/helpers/agile_boards_helper.rb index 5a11b6f..2a97ab2 100644 --- a/app/helpers/agile_boards_helper.rb +++ b/app/helpers/agile_boards_helper.rb @@ -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 diff --git a/app/helpers/agile_charts_helper.rb b/app/helpers/agile_charts_helper.rb index 679997a..4484997 100644 --- a/app/helpers/agile_charts_helper.rb +++ b/app/helpers/agile_charts_helper.rb @@ -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 diff --git a/app/helpers/agile_support_helper.rb b/app/helpers/agile_support_helper.rb index aba1c22..b063766 100644 --- a/app/helpers/agile_support_helper.rb +++ b/app/helpers/agile_support_helper.rb @@ -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 diff --git a/app/models/agile_charts_query.rb b/app/models/agile_charts_query.rb index 05d8a2d..63a5aae 100644 --- a/app/models/agile_charts_query.rb +++ b/app/models/agile_charts_query.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 @@ -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 diff --git a/app/models/agile_data.rb b/app/models/agile_data.rb index 87db483..8a159a2 100755 --- a/app/models/agile_data.rb +++ b/app/models/agile_data.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 diff --git a/app/models/agile_query.rb b/app/models/agile_query.rb index 2b07d26..79bff3d 100644 --- a/app/models/agile_query.rb +++ b/app/models/agile_query.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 @@ -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) diff --git a/app/models/agile_statuses_collector.rb b/app/models/agile_statuses_collector.rb index b2b0673..21a679f 100644 --- a/app/models/agile_statuses_collector.rb +++ b/app/models/agile_statuses_collector.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 diff --git a/app/views/agile_charts/_versions_show.html.erb b/app/views/agile_charts/_versions_show.html.erb index 79c9725..a7b179d 100644 --- a/app/views/agile_charts/_versions_show.html.erb +++ b/app/views/agile_charts/_versions_show.html.erb @@ -13,7 +13,7 @@
- <%= 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) %>
<% 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'); }); diff --git a/app/views/agile_charts/show.html.erb b/app/views/agile_charts/show.html.erb index a92392f..1afa732 100644 --- a/app/views/agile_charts/show.html.erb +++ b/app/views/agile_charts/show.html.erb @@ -29,7 +29,7 @@ <%= l(:label_agile_interval_size) %> - <%= 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) %> @@ -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 */ diff --git a/assets/javascripts/redmine_agile.js b/assets/javascripts/redmine_agile.js index 956bcae..58a385d 100755 --- a/assets/javascripts/redmine_agile.js +++ b/assets/javascripts/redmine_agile.js @@ -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 + ')'); + } }); } diff --git a/assets/stylesheets/redmine_agile.css b/assets/stylesheets/redmine_agile.css index d8ce691..5327767 100755 --- a/assets/stylesheets/redmine_agile.css +++ b/assets/stylesheets/redmine_agile.css @@ -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; diff --git a/config/locales/en.yml b/config/locales/en.yml index 19a38ca..7bc0d18 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 5580ab7..4b5ee97 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -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 diff --git a/config/locales/ru.yml b/config/locales/ru.yml index c7e21a0..ee75f8d 100755 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -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: Следующие спринты diff --git a/config/routes.rb b/config/routes.rb index 21dc7b9..10be89d 100755 --- a/config/routes.rb +++ b/config/routes.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 diff --git a/db/migrate/001_create_issue_status_orders.rb b/db/migrate/001_create_issue_status_orders.rb index c345ce3..2b34c2c 100755 --- a/db/migrate/001_create_issue_status_orders.rb +++ b/db/migrate/001_create_issue_status_orders.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 diff --git a/db/migrate/002_create_agile_colors.rb b/db/migrate/002_create_agile_colors.rb index 1e705e8..42497e2 100644 --- a/db/migrate/002_create_agile_colors.rb +++ b/db/migrate/002_create_agile_colors.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 diff --git a/db/migrate/003_rename_issue_status_orders.rb b/db/migrate/003_rename_issue_status_orders.rb index c21e129..6889a83 100644 --- a/db/migrate/003_rename_issue_status_orders.rb +++ b/db/migrate/003_rename_issue_status_orders.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 diff --git a/db/migrate/004_rename_agile_ranks.rb b/db/migrate/004_rename_agile_ranks.rb index de7b86f..1d509c4 100644 --- a/db/migrate/004_rename_agile_ranks.rb +++ b/db/migrate/004_rename_agile_ranks.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 diff --git a/db/migrate/005_add_story_points_to_agile_ranks.rb b/db/migrate/005_add_story_points_to_agile_ranks.rb index 16ef93d..3f3db2d 100644 --- a/db/migrate/005_add_story_points_to_agile_ranks.rb +++ b/db/migrate/005_add_story_points_to_agile_ranks.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 diff --git a/db/migrate/006_create_agile_sprints.rb b/db/migrate/006_create_agile_sprints.rb index 59cac7f..2d015c3 100644 --- a/db/migrate/006_create_agile_sprints.rb +++ b/db/migrate/006_create_agile_sprints.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 diff --git a/db/migrate/007_add_sprint_id_to_agile_data.rb b/db/migrate/007_add_sprint_id_to_agile_data.rb index 5fc4159..dd54ff4 100644 --- a/db/migrate/007_add_sprint_id_to_agile_data.rb +++ b/db/migrate/007_add_sprint_id_to_agile_data.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 diff --git a/db/migrate/008_add_sharing_to_agile_sprint.rb b/db/migrate/008_add_sharing_to_agile_sprint.rb index 0df6426..d2ff55f 100644 --- a/db/migrate/008_add_sharing_to_agile_sprint.rb +++ b/db/migrate/008_add_sharing_to_agile_sprint.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 diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 7345bda..ce2058d 100755 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -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 diff --git a/init.rb b/init.rb index c6ff7ee..b10f42b 100755 --- a/init.rb +++ b/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 . -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' diff --git a/lib/redmine_agile.rb b/lib/redmine_agile.rb index ee45db0..5ddd2b6 100755 --- a/lib/redmine_agile.rb +++ b/lib/redmine_agile.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,22 +17,6 @@ # You should have received a copy of the GNU General Public License # along with redmine_agile. If not, see . - - -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 diff --git a/lib/redmine_agile/charts/agile_chart.rb b/lib/redmine_agile/charts/agile_chart.rb index 0a7c1db..1f84a8b 100644 --- a/lib/redmine_agile/charts/agile_chart.rb +++ b/lib/redmine_agile/charts/agile_chart.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 @@ -18,262 +18,264 @@ # along with redmine_agile. If not, see . 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 diff --git a/lib/redmine_agile/charts/burndown_chart.rb b/lib/redmine_agile/charts/burndown_chart.rb index 6a21a57..e29e55f 100644 --- a/lib/redmine_agile/charts/burndown_chart.rb +++ b/lib/redmine_agile/charts/burndown_chart.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 @@ -18,95 +18,97 @@ # along with redmine_agile. If not, see . 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 diff --git a/lib/redmine_agile/charts/charts.rb b/lib/redmine_agile/charts/charts.rb deleted file mode 100644 index a2a478e..0000000 --- a/lib/redmine_agile/charts/charts.rb +++ /dev/null @@ -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 . - -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 diff --git a/lib/redmine_agile/charts/helper.rb b/lib/redmine_agile/charts/helper.rb new file mode 100644 index 0000000..dc1916e --- /dev/null +++ b/lib/redmine_agile/charts/helper.rb @@ -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 . + +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 diff --git a/lib/redmine_agile/charts/work_burndown_chart.rb b/lib/redmine_agile/charts/work_burndown_chart.rb index 0c8019e..19b20d0 100644 --- a/lib/redmine_agile/charts/work_burndown_chart.rb +++ b/lib/redmine_agile/charts/work_burndown_chart.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 @@ -18,52 +18,54 @@ # along with redmine_agile. If not, see . 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 diff --git a/lib/redmine_agile/helpers/agile_helper.rb b/lib/redmine_agile/helpers/agile_helper.rb index cdc4be9..cae2787 100644 --- a/lib/redmine_agile/helpers/agile_helper.rb +++ b/lib/redmine_agile/helpers/agile_helper.rb @@ -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 . 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 diff --git a/lib/redmine_agile/hooks/controller_issue_hook.rb b/lib/redmine_agile/hooks/controller_issue_hook.rb index 06cc964..769949d 100644 --- a/lib/redmine_agile/hooks/controller_issue_hook.rb +++ b/lib/redmine_agile/hooks/controller_issue_hook.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 diff --git a/lib/redmine_agile/hooks/views_issues_hook.rb b/lib/redmine_agile/hooks/views_issues_hook.rb index d4d7ab2..79154f4 100755 --- a/lib/redmine_agile/hooks/views_issues_hook.rb +++ b/lib/redmine_agile/hooks/views_issues_hook.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 diff --git a/lib/redmine_agile/hooks/views_layouts_hook.rb b/lib/redmine_agile/hooks/views_layouts_hook.rb index ef723bc..42eef3f 100644 --- a/lib/redmine_agile/hooks/views_layouts_hook.rb +++ b/lib/redmine_agile/hooks/views_layouts_hook.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 diff --git a/lib/redmine_agile/hooks/views_versions_hook.rb b/lib/redmine_agile/hooks/views_versions_hook.rb index 67af1e1..1fc662d 100644 --- a/lib/redmine_agile/hooks/views_versions_hook.rb +++ b/lib/redmine_agile/hooks/views_versions_hook.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 diff --git a/lib/redmine_agile/patches/issue_drop_patch.rb b/lib/redmine_agile/patches/issue_drop_patch.rb index db476e5..f152d84 100644 --- a/lib/redmine_agile/patches/issue_drop_patch.rb +++ b/lib/redmine_agile/patches/issue_drop_patch.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 diff --git a/lib/redmine_agile/patches/issue_patch.rb b/lib/redmine_agile/patches/issue_patch.rb index 8caca54..60c1395 100755 --- a/lib/redmine_agile/patches/issue_patch.rb +++ b/lib/redmine_agile/patches/issue_patch.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,9 +17,6 @@ # You should have received a copy of the GNU General Public License # along with redmine_agile. If not, see . -require_dependency 'issue' -require_dependency 'agile_data' - module RedmineAgile module Patches diff --git a/lib/redmine_agile/patches/issues_controller_patch.rb b/lib/redmine_agile/patches/issues_controller_patch.rb index 5e4d94c..63a6dd6 100644 --- a/lib/redmine_agile/patches/issues_controller_patch.rb +++ b/lib/redmine_agile/patches/issues_controller_patch.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 diff --git a/lib/redmine_agile/patches/project_patch.rb b/lib/redmine_agile/patches/project_patch.rb index 2f4a628..7665c3d 100644 --- a/lib/redmine_agile/patches/project_patch.rb +++ b/lib/redmine_agile/patches/project_patch.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 diff --git a/lib/redmine_agile/patches/projects_helper_patch.rb b/lib/redmine_agile/patches/projects_helper_patch.rb index 653c009..d1d0e33 100644 --- a/lib/redmine_agile/patches/projects_helper_patch.rb +++ b/lib/redmine_agile/patches/projects_helper_patch.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 diff --git a/lib/redmine_agile/patches/queries_controller_patch.rb b/lib/redmine_agile/patches/queries_controller_patch.rb index 085b7d6..4f8c08c 100644 --- a/lib/redmine_agile/patches/queries_controller_patch.rb +++ b/lib/redmine_agile/patches/queries_controller_patch.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 diff --git a/test/functional/agile_boards_controller_test.rb b/test/functional/agile_boards_controller_test.rb index 6619a26..700f513 100755 --- a/test/functional/agile_boards_controller_test.rb +++ b/test/functional/agile_boards_controller_test.rb @@ -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 diff --git a/test/functional/agile_charts_controller_test.rb b/test/functional/agile_charts_controller_test.rb index 0752633..47a903b 100644 --- a/test/functional/agile_charts_controller_test.rb +++ b/test/functional/agile_charts_controller_test.rb @@ -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 diff --git a/test/functional/agile_journal_details_controller_test.rb b/test/functional/agile_journal_details_controller_test.rb index 5d3db81..98d86c7 100644 --- a/test/functional/agile_journal_details_controller_test.rb +++ b/test/functional/agile_journal_details_controller_test.rb @@ -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 diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index f124644..c81e632 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -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 diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 67d3a32..3a68eb2 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -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 diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index 71aa90c..0fd206e 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -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 diff --git a/test/integration/api_test/agile_data_test.rb b/test/integration/api_test/agile_data_test.rb index bae9bcd..4656ab7 100644 --- a/test/integration/api_test/agile_data_test.rb +++ b/test/integration/api_test/agile_data_test.rb @@ -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 diff --git a/test/integration/common_views_test.rb b/test/integration/common_views_test.rb index eb3936c..83f7004 100644 --- a/test/integration/common_views_test.rb +++ b/test/integration/common_views_test.rb @@ -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 diff --git a/test/test_helper.rb b/test/test_helper.rb index 1c345e1..69b348b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -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 diff --git a/test/ui/agile_board_ui_test.rb b/test/ui/agile_board_ui_test.rb index b27389f..e71b77d 100644 --- a/test/ui/agile_board_ui_test.rb +++ b/test/ui/agile_board_ui_test.rb @@ -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 diff --git a/test/unit/agile_charts_query_test.rb b/test/unit/agile_charts_query_test.rb index a9fde49..d2827a3 100644 --- a/test/unit/agile_charts_query_test.rb +++ b/test/unit/agile_charts_query_test.rb @@ -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 diff --git a/test/unit/agile_data_test.rb b/test/unit/agile_data_test.rb index 13bf20a..01f4fc9 100644 --- a/test/unit/agile_data_test.rb +++ b/test/unit/agile_data_test.rb @@ -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 diff --git a/test/unit/charts/burndown_chart_test.rb b/test/unit/charts/burndown_chart_test.rb index e051a10..5fd3960 100644 --- a/test/unit/charts/burndown_chart_test.rb +++ b/test/unit/charts/burndown_chart_test.rb @@ -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 diff --git a/test/unit/helpers/agile_boards_helper_test.rb b/test/unit/helpers/agile_boards_helper_test.rb index accb368..e6595b8 100644 --- a/test/unit/helpers/agile_boards_helper_test.rb +++ b/test/unit/helpers/agile_boards_helper_test.rb @@ -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