Skip to content

Commit

Permalink
Add in simple average calculation mode job
Browse files Browse the repository at this point in the history
* Adds temporary depth table - Could be cleaned up down thie line with
  the `work_package_hierarchies` table.
  • Loading branch information
aaron-contreras committed Sep 24, 2024
1 parent 6cbfdc8 commit 27e5d87
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program 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 2
# of the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Journal::CausedByTotalPercentCompleteModeChangedToSimpleAverage < CauseOfChange::Base
def initialize
super("total_percent_complete_mode_changed_to_simple_average")
end
end
4 changes: 4 additions & 0 deletions app/services/settings/update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def set_setting_value(name, value)
WorkPackages::Progress::ApplyTotalPercentCompleteModeChangeJob
.perform_later(mode: new_value,
cause_type: "total_percent_complete_mode_changed_to_work_weighted_average")
elsif name == :total_percent_complete_mode && old_value != "simple_average" && new_value == "simple_average"
WorkPackages::Progress::ApplyTotalPercentCompleteModeChangeJob
.perform_later(mode: new_value,
cause_type: "total_percent_complete_mode_changed_to_simple_average")
end
end

Check notice on line 59 in app/services/settings/update_service.rb

View workflow job for this annotation

GitHub Actions / rubocop

[rubocop] app/services/settings/update_service.rb#L44-L59 <Metrics/AbcSize>

Assignment Branch Condition size for set_setting_value is too high. [<3, 6, 18> 19.21/17]
Raw output
app/services/settings/update_service.rb:44:3: C: Metrics/AbcSize: Assignment Branch Condition size for set_setting_value is too high. [<3, 6, 18> 19.21/17]

Check notice on line 59 in app/services/settings/update_service.rb

View workflow job for this annotation

GitHub Actions / rubocop

[rubocop] app/services/settings/update_service.rb#L44-L59 <Metrics/PerceivedComplexity>

Perceived complexity for set_setting_value is too high. [11/8]
Raw output
app/services/settings/update_service.rb:44:3: C: Metrics/PerceivedComplexity: Perceived complexity for set_setting_value is too high. [11/8]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,30 +92,61 @@ def update_to_work_weighted_average

def update_to_simple_average
execute(<<~SQL.squish)
UPDATE temp_wp_progress_values
SET derived_done_ratio = CASE
WHEN avg_ratios.avg_done_ratio IS NOT NULL THEN ROUND(avg_ratios.avg_done_ratio)
ELSE done_ratio
END
FROM (
SELECT wp_tree.ancestor_id AS id,
AVG(CASE
WHEN wp_tree.generations = 1 THEN COALESCE(wp_progress.done_ratio, 0)
ELSE NULL
END) AS avg_done_ratio
FROM work_package_hierarchies wp_tree
LEFT JOIN temp_wp_progress_values wp_progress ON wp_tree.descendant_id = wp_progress.id
LEFT JOIN statuses ON wp_progress.status_id = statuses.id
WHERE statuses.excluded_from_totals = FALSE
GROUP BY wp_tree.ancestor_id
) avg_ratios
WHERE temp_wp_progress_values.id = avg_ratios.id
AND temp_wp_progress_values.id IN (
SELECT ancestor_id AS id
FROM work_package_hierarchies
GROUP BY id
HAVING MAX(generations) > 0
)
DO $$
DECLARE
min_depth INTEGER := 0;
max_depth INTEGER := (SELECT MAX(depth) FROM temp_work_package_depth);
current_depth INTEGER := min_depth;
BEGIN
/* Navigate work packages and perform updates bottom-up */
while current_depth <= max_depth loop
UPDATE temp_wp_progress_values wp
SET
total_p_complete = CASE
WHEN current_depth = min_depth THEN NULL
ELSE ROUND(
(
COALESCE(wp.p_complete, 0) + (
SELECT
SUM(
COALESCE(child_wp.total_p_complete, child_wp.p_complete, 0)
)
FROM
temp_wp_progress_values child_wp
WHERE
child_wp.parent_id = wp.id
)
) / (
CASE
WHEN wp.p_complete IS NOT NULL THEN 1
ELSE 0
END + (
SELECT
COUNT(1)
FROM
temp_wp_progress_values child_wp
WHERE
child_wp.parent_id = wp.id
)
)
)
END
/* Select only work packages at the curren depth */
WHERE
wp.id IN (
SELECT
id
FROM
temp_work_package_depth
WHERE
depth = current_depth
);
/* Go up a level from a child to a parent*/
current_depth := current_depth + 1;
END loop;
END $$;
SQL
end

Expand Down
72 changes: 68 additions & 4 deletions app/workers/work_packages/progress/sql_commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,31 +67,94 @@ def with_temporary_total_percent_complete_table
create_temporary_total_percent_complete_table_for_work_weighted_average_mode
when "simple_average"
create_temporary_total_percent_complete_table_for_simple_average_mode
create_temporary_depth_table
else
raise ArgumentError, "Invalid total percent complete mode: #{mode}"
end

yield
ensure
drop_temporary_total_percent_complete_table
drop_temporary_depth_table
end
end

def create_temporary_total_percent_complete_table_for_work_weighted_average_mode
execute(<<~SQL.squish)
CREATE UNLOGGED TABLE temp_wp_progress_values
AS SELECT
CREATE UNLOGGED TABLE temp_wp_progress_values AS
SELECT
id,
derived_estimated_hours as total_work,
derived_remaining_hours as total_remaining_work,
derived_done_ratio as total_p_complete
derived_done_ratio as total_p_complete
FROM work_packages
SQL
end

def create_temporary_total_percent_complete_table_for_simple_average_mode
execute(<<~SQL.squish)
CREATE UNLOGGED TABLE temp_wp_progress_values AS
SELECT
id,
parent_id,
status_id,
done_ratio AS p_complete,
NULL::INTEGER AS total_p_complete
FROM work_packages
SQL
end

def create_temporary_depth_table
execute(<<~SQL.squish)
CREATE UNLOGGED TABLE temp_work_package_depth AS
WITH RECURSIVE
work_package_depth AS (
/* Base case: Leaves (work packages with no children) */
SELECT
wp.id,
wp.parent_id,
0 AS depth
FROM
temp_wp_progress_values wp
WHERE
NOT EXISTS (
SELECT
1
FROM
temp_wp_progress_values c
WHERE
c.parent_id = wp.id
)
UNION ALL
/* Recursive case: Parents */
SELECT
wp.parent_id AS id,
wp2.parent_id,
wpd.depth + 1 AS depth
FROM
work_packages wp
INNER JOIN work_package_depth wpd ON wp.id = wpd.id
INNER JOIN temp_wp_progress_values wp2 ON wp.parent_id = wp2.id
WHERE
wp.parent_id IS NOT NULL
)
SELECT
id,
depth
FROM
work_package_depth
SQL
end

def drop_temporary_total_percent_complete_table
execute(<<~SQL.squish)
DROP TABLE temp_wp_progress_values
DROP TABLE IF EXISTS temp_wp_progress_values
SQL
end

def drop_temporary_depth_table
execute(<<~SQL.squish)
DROP TABLE IF EXISTS temp_work_package_depth
SQL
end

Expand Down Expand Up @@ -223,6 +286,7 @@ def copy_total_percent_complete_values_to_work_packages
)
RETURNING work_packages.id
SQL

results.column_values(0)
end

Expand Down
5 changes: 3 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1916,7 +1916,7 @@ en:
status_changed: "Status '%{status_name}'"
system_update: "OpenProject system update:"
total_percent_complete_mode_changed_to_work_weighted_average: "Calculation of % Complete totals now weighted by Work."

total_percent_complete_mode_changed_to_simple_average: "Calculation of % Complete totals now based on a simple average of only % Complete values."
cause_descriptions:
work_package_predecessor_changed_times: by changes to predecessor %{link}
work_package_parent_changed_times: by changes to parent %{link}
Expand Down Expand Up @@ -1948,7 +1948,8 @@ en:
This is a maintenance task and can be safely ignored.
total_percent_complete_mode_changed_to_work_weighted_average: >-
Child work packages without Work are ignored.
total_percent_complete_mode_changed_to_simple_average: >-
Work values of child work packages are ignored.
links:
configuration_guide: "Configuration guide"
get_in_touch: "You have questions? Get in touch with us."
Expand Down
6 changes: 6 additions & 0 deletions lib/open_project/journal_formatter/cause.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ def cause_description
progress_mode_changed_to_status_based_message
when "total_percent_complete_mode_changed_to_work_weighted_average"
total_percent_complete_mode_changed_to_work_weighted_average_message
when "total_percent_complete_mode_changed_to_simple_average"
total_percent_complete_mode_changed_to_simple_average_message
else
related_work_package_changed_message
end
Expand Down Expand Up @@ -149,6 +151,10 @@ def total_percent_complete_mode_changed_to_work_weighted_average_message
I18n.t("journals.cause_descriptions.total_percent_complete_mode_changed_to_work_weighted_average")
end

def total_percent_complete_mode_changed_to_simple_average_message
I18n.t("journals.cause_descriptions.total_percent_complete_mode_changed_to_simple_average")
end

def related_work_package_changed_message
related_work_package = WorkPackage.includes(:project).visible(User.current).find_by(id: cause["work_package_id"])

Expand Down
Loading

0 comments on commit 27e5d87

Please sign in to comment.