Project

General

Profile

Feature #35657 » 0002-Filterable-and-Sortable-POC-implementation.patch

Takenori TAKAKI, 2021-08-27 08:15

View differences:

app/helpers/issues_helper.rb
276 276
    end
277 277
  end
278 278

  
279
  def issue_spent_time_ratio_details(issue)
280
    if issue.total_spent_time_ratio
281
      if issue.total_spent_time_ratio == issue.spent_time_ratio
282
        "#{issue.spent_time_ratio.to_s(:percentage, precision: 2)}"
283
      else
284
        "#{issue.spent_time_ratio.to_s(:percentage, precision: 2)} (#{l(:label_total)}: #{issue.total_spent_time_ratio.to_s(:percentage, precision: 2)})"
285
      end
286
    end
287
  end
288

  
279 289
  def issue_due_date_details(issue)
280 290
    return if issue&.due_date.nil?
281 291

  
app/helpers/queries_helper.rb
260 260
        item.last_notes.present? ? content_tag('div', textilizable(item, :last_notes), :class => "wiki") : ''
261 261
      when :done_ratio
262 262
        progress_bar(value)
263
      when :spent_time_ratio, :total_spent_time_ratio
264
        value ? value.to_s(:percentage, precision: 2) : ''
263 265
      when :relations
264 266
        content_tag(
265 267
          'span',
app/models/issue.rb
733 733
    end
734 734
  end
735 735

  
736
  def spent_time_ratio
737
    if estimated_hours.to_f > 0
738
      (spent_hours.to_f / estimated_hours.to_f * 100).round(2)
739
    end
740
  end
741

  
742
  def total_spent_time_ratio
743
    if total_estimated_hours.to_f > 0
744
      (total_spent_hours.to_f / total_estimated_hours.to_f * 100).round(2)
745
    end
746
  end
747

  
736 748
  def self.use_status_for_done_ratio?
737 749
    Setting.issue_done_ratio == 'issue_status'
738 750
  end
app/models/issue_query.rb
61 61
        end,
62 62
      :default_order => 'desc'),
63 63
    QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
64
    QueryColumn.new(
65
      :spent_time_ratio,
66
      :sortable =>
67
        lambda do
68
          "COALESCE((" \
69
          " SELECT ROUND(CAST(COALESCE(SUM(hours), 0) / NULLIF(#{Issue.table_name}.estimated_hours, 0) * 100 AS DECIMAL(4,2)), 2) " \
70
          " FROM #{TimeEntry.table_name}" \
71
          " WHERE issue_id = #{Issue.table_name}.id), NULL)"
72
        end,
73
      :default_order => 'desc'),
74
    QueryColumn.new(
75
      :total_spent_time_ratio,
76
      :sortable =>
77
        lambda do
78
          "COALESCE(ROUND(CAST(" \
79
            "COALESCE((SELECT SUM(hours)" \
80
              " FROM #{TimeEntry.table_name}" \
81
              " JOIN #{Project.table_name} ON #{Project.table_name}.id = #{TimeEntry.table_name}.project_id" \
82
              " JOIN #{Issue.table_name} subtasks ON subtasks.id = #{TimeEntry.table_name}.issue_id" \
83
              " WHERE (#{TimeEntry.visible_condition(User.current)})" \
84
              " AND subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)" \
85
            " / " \
86
            "NULLIF((SELECT SUM(estimated_hours)" \
87
              " FROM #{Issue.table_name} subtasks" \
88
              " WHERE #{Issue.visible_condition(User.current).gsub(/\bissues\b/, 'subtasks')}" \
89
              " AND subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)" \
90
            "* 100 AS DECIMAL(4,2)), 2), NULL)"
91
        end,
92
      :default_order => 'desc'),
64 93
    TimestampQueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on",
65 94
                             :default_order => 'desc', :groupable => true),
66 95
    TimestampQueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on",
......
203 232
    end
204 233

  
205 234
    add_available_filter "done_ratio", :type => :integer
235
    add_available_filter "spent_time_ratio", :type => :float
236
    add_available_filter "total_spent_time_ratio", :type => :float
206 237

  
207 238
    if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
208 239
      User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
......
508 539
      "WHERE issue_id = #{Issue.table_name}.id), 0) #{sql_op}"
509 540
  end
510 541

  
542
  def sql_for_spent_time_ratio_field(field, operator, value)
543
    first, second = value.first.to_f, value.second.to_f
544
    sql_op =
545
      case operator
546
      when "=", ">=", "<=" then  "#{operator} #{first}"
547
      when "><"            then  "BETWEEN #{first} AND #{second}"
548
      when "*"             then  ">= 0"
549
      when "!*"            then  "IS NULL"
550
      else
551
        return nil
552
      end
553
    "COALESCE((" +
554
      "SELECT ROUND(CAST(COALESCE(SUM(hours), 0) / NULLIF(#{Issue.table_name}.estimated_hours, 0) * 100 AS DECIMAL(4,2)), 2) " +
555
      "FROM #{TimeEntry.table_name} " +
556
      "WHERE issue_id = #{Issue.table_name}.id), NULL) #{sql_op}"
557
  end
558

  
559
  def sql_for_total_spent_time_ratio_field(field, operator, value)
560
    first, second = value.first.to_f, value.second.to_f
561
    sql_op =
562
      case operator
563
      when "=", ">=", "<=" then  "#{operator} #{first}"
564
      when "><"            then  "BETWEEN #{first} AND #{second}"
565
      when "*"             then  ">= 0"
566
      when "!*"            then  "IS NULL"
567
      else
568
        return nil
569
      end
570
    "COALESCE(ROUND(CAST(" +
571
      "COALESCE((SELECT SUM(hours)" +
572
        " FROM #{TimeEntry.table_name}" +
573
        " JOIN #{Project.table_name} ON #{Project.table_name}.id = #{TimeEntry.table_name}.project_id" +
574
        " JOIN #{Issue.table_name} subtasks ON subtasks.id = #{TimeEntry.table_name}.issue_id" +
575
        " WHERE (#{TimeEntry.visible_condition(User.current)})" +
576
        " AND subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)" +
577
      " / " +
578
      "NULLIF((SELECT SUM(estimated_hours)" +
579
        " FROM #{Issue.table_name} subtasks" +
580
        " WHERE #{Issue.visible_condition(User.current).gsub(/\bissues\b/, 'subtasks')}" +
581
        " AND subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)" +
582
      " * 100 AS DECIMAL(4,2)), 2), NULL) #{sql_op}"
583
  end
584

  
511 585
  def sql_for_watcher_id_field(field, operator, value)
512 586
    db_table = Watcher.table_name
513 587
    me, others = value.partition {|id| ['0', User.current.id.to_s].include?(id)}
app/views/issues/show.html.erb
73 73
  end
74 74
  if User.current.allowed_to?(:view_time_entries, @project) && @issue.total_spent_hours > 0
75 75
    rows.right l(:label_spent_time), issue_spent_hours_details(@issue), :class => 'spent-time'
76
    if @issue.total_spent_time_ratio.present?
77
      rows.right l(:field_spent_time_ratio), issue_spent_time_ratio_details(@issue), :class => 'spent-time'
78
    end
76 79
  end
77 80
end %>
78 81
<%= render_half_width_custom_fields_rows(@issue) %>
config/locales/en.yml
338 338
  field_onthefly: On-the-fly user creation
339 339
  field_start_date: Start date
340 340
  field_done_ratio: "% Done"
341
  field_spent_time_ratio: Spent Time Ratio
342
  field_total_spent_time_ratio: Total spent time Ratio
341 343
  field_auth_source: Authentication mode
342 344
  field_hide_mail: Hide my email address
343 345
  field_comments: Comment
(6-6/6)