From e2702877f6aa900e4fe05c520df84bdd96539c6e Mon Sep 17 00:00:00 2001 From: Jacek Grzybowski Date: Tue, 14 Apr 2015 14:11:46 +0200 Subject: squash --- app/helpers/queries_helper.rb | 2 + app/models/issue_query.rb | 57 ++++++++- app/models/query.rb | 6 +- config/locales/en.yml | 4 + config/locales/pl.yml | 4 + public/javascripts/application.js | 66 ++++++++++- public/stylesheets/application.css | 20 +++- test/fixtures/issues.yml | 174 +++++++++++++++++++-------- test/fixtures/journal_details.yml | 55 ++++++++- test/fixtures/journals.yml | 53 ++++++++- test/unit/query_issue_history_test.rb | 213 ++++++++++++++++++++++++++++++++++ 11 files changed, 593 insertions(+), 61 deletions(-) create mode 100644 test/unit/query_issue_history_test.rb diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 8f0c5ae..ac3d548 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -26,6 +26,8 @@ module QueriesHelper query.available_filters.map do |field, field_options| if field_options[:type] == :relation group = :label_related_issues + elsif field =~ /_id_was\z/ + group = :label_history elsif field =~ /^(.+)\./ # association filters group = "field_#{$1}" diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index c58711b..a152372 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -168,6 +168,7 @@ class IssueQuery < Query add_available_filter "tracker_id", :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] } + add_available_filter "priority_id", :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } @@ -208,6 +209,15 @@ class IssueQuery < Query :values => categories.collect{|s| [s.name, s.id.to_s] } end + add_available_filter "status_id_was", + :type => :list, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] } + add_available_filter "priority_id_was", + :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } + add_available_filter "tracker_id_was", + :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] } + add_available_filter "assigned_to_id_was", + :type => :list, :values => assigned_to_values unless assigned_to_values.empty? + add_available_filter "subject", :type => :text add_available_filter "created_on", :type => :date_past add_available_filter "updated_on", :type => :date_past @@ -289,9 +299,25 @@ class IssueQuery < Query end end + def history_filters + filters.select { |k,_| k =~ /_was\z/ } + end + + def history_join_clause + return nil if history_filters.blank? + history_filters.keys.map do |f| + "INNER JOIN journals AS j_#{f} ON j_#{f}.journalized_id = issues.id AND j_#{f}.journalized_type = 'Issue'" + + "INNER JOIN journal_details AS jd_#{f} ON jd_#{f}.journal_id = j_#{f}.id" + end.join(' ') + end + # Returns the issue count def issue_count - Issue.visible.joins(:status, :project).where(statement).count + Issue.visible. + joins(:status, :project). + joins(history_join_clause). + where(statement). + count rescue ::ActiveRecord::StatementInvalid => e raise StatementInvalid.new(e.message) end @@ -304,6 +330,7 @@ class IssueQuery < Query # Rails3 will raise an (unexpected) RecordNotFound if there's only a nil group value r = Issue.visible. joins(:status, :project). + joins(history_join_clause). where(statement). joins(joins_for_order_statement(group_by_statement)). group(group_by_statement). @@ -328,6 +355,7 @@ class IssueQuery < Query scope = Issue.visible. joins(:status, :project). + joins(history_join_clause). where(statement). includes(([:status, :project] + (options[:include] || [])).uniq). where(options[:conditions]). @@ -360,6 +388,7 @@ class IssueQuery < Query Issue.visible. joins(:status, :project). + joins(history_join_clause). where(statement). includes(([:status, :project] + (options[:include] || [])).uniq). references(([:status, :project] + (options[:include] || [])).uniq). @@ -401,6 +430,32 @@ class IssueQuery < Query raise StatementInvalid.new(e.message) end + # works for status_id_was & priority_id_was + def sql_for_history_field(field, operator, values) + field_name = field.sub('_was', '') + select_values = values[0..-3] + select_values = substitute_me_in_history_values(select_values) if field_name == 'assigned_to_id' + date_from, date_to = values[-2..-1] + journal_queries = [] + journal_queries << "jd_#{field}.prop_key = '#{field_name}'" + journal_queries << sql_for_field(field_name, operator, select_values, "jd_#{field}", "old_value") + date_range_query = sql_for_history_date_range(field, date_from, date_to) + journal_queries << date_range_query if date_range_query + journal_queries.join(" AND ") + end + + def substitute_me_in_history_values(values) + idx = values.index {|v| v == 'me'} + return values unless idx + values[idx] = User.current.logged? ? User.current.id.to_s : '0' + values + end + + def sql_for_history_date_range(field, from, to) + from, to = parse_date(from), parse_date(to) + from || to ? date_clause("j_#{field}", 'created_on', from, to, false) : nil + end + def sql_for_watcher_id_field(field, operator, value) db_table = Watcher.table_name "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " + diff --git a/app/models/query.rb b/app/models/query.rb index 0e6064f..e091fa7 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -584,6 +584,8 @@ class Query < ActiveRecord::Base if field =~ /cf_(\d+)$/ # custom field filters_clauses << sql_for_custom_field(field, operator, v, $1) + elsif field =~ /_was\z/ && respond_to?("sql_for_history_field") + filters_clauses << sql_for_history_field(field, operator, v) elsif respond_to?("sql_for_#{field}_field") # specific statement filters_clauses << send("sql_for_#{field}_field", field, operator, v) @@ -712,9 +714,9 @@ class Query < ActiveRecord::Base end end when "o" - sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false})" if field == "status_id" + sql = "#{db_table}.#{db_field} IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false})" if field == "status_id" when "c" - sql = "#{queried_table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_true})" if field == "status_id" + sql = "#{db_table}.#{db_field} IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_true})" if field == "status_id" when "> ' ); select = tr.find('td.values select'); - if (values.length > 1) { select.attr('multiple', true); } + if ( isHistoryFilter(field) ) { + // history filters have 2 additional values in array + // corresponding to `from` and `to` dates + if (values.length > 3) { select.attr('multiple', true); } + } else { + if (values.length > 1) { select.attr('multiple', true); } + } + for (i = 0; i < filterValues.length; i++) { var filterValue = filterValues[i]; var option = $('