diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-12-09 17:57:18 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-12-09 17:57:18 +0000 |
commit | f8895a7cdd9c53f3335825a45be1d53862693370 (patch) | |
tree | 2d75806f7fda54ccc9118cb1f69ff26c6fb541cd /app | |
parent | 10998c9bae314057b5c2adf97418618f68efb3d7 (diff) | |
download | redmine-f8895a7cdd9c53f3335825a45be1d53862693370.tar.gz redmine-f8895a7cdd9c53f3335825a45be1d53862693370.zip |
Adds TimeEntryQuery for listing time entries.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10967 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/timelog_controller.rb | 37 | ||||
-rw-r--r-- | app/models/query.rb | 48 | ||||
-rw-r--r-- | app/models/time_entry_query.rb | 65 | ||||
-rw-r--r-- | app/views/timelog/_date_range.html.erb | 45 |
4 files changed, 141 insertions, 54 deletions
diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb index a0c694bc4..a2dcfac7d 100644 --- a/app/controllers/timelog_controller.rb +++ b/app/controllers/timelog_controller.rb @@ -36,24 +36,14 @@ class TimelogController < ApplicationController include TimelogHelper helper :custom_fields include CustomFieldsHelper + helper :queries def index - sort_init 'spent_on', 'desc' - sort_update 'spent_on' => ['spent_on', "#{TimeEntry.table_name}.created_on"], - 'user' => 'user_id', - 'activity' => 'activity_id', - 'project' => "#{Project.table_name}.name", - 'issue' => 'issue_id', - 'hours' => 'hours' + @query = TimeEntryQuery.build_from_params(params, :name => '_') + scope = time_entry_scope - retrieve_date_range - - scope = TimeEntry.visible.spent_between(@from, @to) - if @issue - scope = scope.on_issue(@issue) - elsif @project - scope = scope.on_project(@project, Setting.display_subprojects_issues?) - end + sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria) + sort_update(@query.sortable_columns) respond_to do |format| format.html { @@ -100,8 +90,10 @@ class TimelogController < ApplicationController end def report - retrieve_date_range - @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to) + @query = TimeEntryQuery.build_from_params(params, :name => '_') + scope = time_entry_scope + + @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope) respond_to do |format| format.html { render :layout => !request.xhr? } @@ -291,6 +283,17 @@ private end end + # Returns the TimeEntry scope for index and report actions + def time_entry_scope + scope = TimeEntry.visible.where(@query.statement) + if @issue + scope = scope.on_issue(@issue) + elsif @project + scope = scope.on_project(@project, Setting.display_subprojects_issues?) + end + scope + end + # Retrieves the date range based on predefined ranges or specific from/to param dates def retrieve_date_range @free_period = false diff --git a/app/models/query.rb b/app/models/query.rb index 81eeb9f3b..814d11846 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -117,7 +117,13 @@ class Query < ActiveRecord::Base "><t+"=> :label_in_the_next_days, "t+" => :label_in, "t" => :label_today, + "ld" => :label_yesterday, "w" => :label_this_week, + "lw" => :label_last_week, + "l2w" => [:label_last_n_weeks, :count => 2], + "m" => :label_this_month, + "lm" => :label_last_month, + "y" => :label_this_year, ">t-" => :label_less_than_ago, "<t-" => :label_more_than_ago, "><t-"=> :label_in_the_past_days, @@ -135,8 +141,8 @@ class Query < ActiveRecord::Base :list_status => [ "o", "=", "!", "c", "*" ], :list_optional => [ "=", "!", "!*", "*" ], :list_subprojects => [ "*", "!*", "=" ], - :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "t", "w", ">t-", "<t-", "><t-", "t-", "!*", "*" ], - :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "w", "!*", "*" ], + :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ], + :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ], :string => [ "=", "~", "!", "!~", "!*", "*" ], :text => [ "~", "!~", "!*", "*" ], :integer => [ "=", ">=", "<=", "><", "!*", "*" ], @@ -173,6 +179,11 @@ class Query < ActiveRecord::Base self end + # Builds a new query from the given params and attributes + def self.build_from_params(params, attributes={}) + new(attributes).build_from_params(params) + end + def validate_query_filters filters.each_key do |field| if values_for(field) @@ -195,7 +206,7 @@ class Query < ActiveRecord::Base # filter requires one or more values (values_for(field) and !values_for(field).first.blank?) or # filter doesn't require any value - ["o", "c", "!*", "*", "t", "w"].include? operator_for(field) + ["o", "c", "!*", "*", "t", "ld", "w", "lw", "l2w", "m", "lm", "y"].include? operator_for(field) end if filters end @@ -218,7 +229,7 @@ class Query < ActiveRecord::Base # Returns a hash of localized labels for all filter operators def self.operators_labels - operators.inject({}) {|h, operator| h[operator.first] = l(operator.last); h} + operators.inject({}) {|h, operator| h[operator.first] = l(*operator.last); h} end # Returns a representation of the available filters for JSON serialization @@ -245,7 +256,7 @@ class Query < ActiveRecord::Base @all_projects_values = values end - def add_filter(field, operator, values) + def add_filter(field, operator, values=nil) # values must be an array return unless values.nil? || values.is_a?(Array) # check if field is defined as an available filter @@ -612,12 +623,39 @@ class Query < ActiveRecord::Base when "t" # = today sql = relative_date_clause(db_table, db_field, 0, 0) + when "ld" + # = yesterday + sql = relative_date_clause(db_table, db_field, -1, -1) when "w" # = this week first_day_of_week = l(:general_first_day_of_week).to_i day_of_week = Date.today.cwday days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6) + when "lw" + # = last week + first_day_of_week = l(:general_first_day_of_week).to_i + day_of_week = Date.today.cwday + days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) + sql = relative_date_clause(db_table, db_field, - days_ago - 7, - days_ago - 1) + when "l2w" + # = last 2 weeks + first_day_of_week = l(:general_first_day_of_week).to_i + day_of_week = Date.today.cwday + days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week) + sql = relative_date_clause(db_table, db_field, - days_ago - 14, - days_ago - 1) + when "m" + # = this month + date = Date.today + sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month) + when "lm" + # = last month + date = Date.today.prev_month + sql = date_clause(db_table, db_field, date.beginning_of_month, date.end_of_month) + when "y" + # = this year + date = Date.today + sql = date_clause(db_table, db_field, date.beginning_of_year, date.end_of_year) when "~" sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'" when "!~" diff --git a/app/models/time_entry_query.rb b/app/models/time_entry_query.rb new file mode 100644 index 000000000..c2fea35fe --- /dev/null +++ b/app/models/time_entry_query.rb @@ -0,0 +1,65 @@ +# Redmine - project management software +# Copyright (C) 2006-2012 Jean-Philippe Lang +# +# 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. + +class TimeEntryQuery < Query + + self.queried_class = TimeEntry + + self.available_columns = [ + QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true), + QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"]), + QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true), + QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true), + QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"), + QueryColumn.new(:comments), + QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours"), + ] + + def initialize(attributes=nil, *args) + super attributes + self.filters ||= {} + add_filter('spent_on', '*') unless filters.present? + end + + def available_filters + return @available_filters if @available_filters + @available_filters = { + "spent_on" => { :type => :date_past, :order => 0 } + } + @available_filters.each do |field, options| + options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) + end + @available_filters + end + + def default_columns_names + @default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours] + end + + # Accepts :from/:to params as shortcut filters + def build_from_params(params) + super + if params[:from].present? && params[:to].present? + add_filter('spent_on', '><', [params[:from], params[:to]]) + elsif params[:from].present? + add_filter('spent_on', '>=', [params[:from]]) + elsif params[:to].present? + add_filter('spent_on', '<=', [params[:to]]) + end + self + end +end diff --git a/app/views/timelog/_date_range.html.erb b/app/views/timelog/_date_range.html.erb index 17a34b99b..a0c180fc8 100644 --- a/app/views/timelog/_date_range.html.erb +++ b/app/views/timelog/_date_range.html.erb @@ -1,42 +1,23 @@ -<fieldset id="date-range" class="collapsible"> -<legend onclick="toggleFieldset(this);"><%= l(:label_date_range) %></legend> -<div> -<p> -<%= label_tag "period_type_list", l(:description_date_range_list), :class => "hidden-for-sighted" %> -<%= radio_button_tag 'period_type', '1', !@free_period, :onclick => '$("#from,#to").attr("disabled", true);$("#period").removeAttr("disabled");', :id => "period_type_list"%> -<%= select_tag 'period', options_for_period_select(params[:period]), - :onchange => 'this.form.submit();', - :onfocus => '$("#period_type_1").attr("checked", true);', - :disabled => @free_period %> -</p> -<p> -<%= label_tag "period_type_interval", l(:description_date_range_interval), :class => "hidden-for-sighted" %> -<%= radio_button_tag 'period_type', '2', @free_period, :onclick => '$("#from,#to").removeAttr("disabled");$("#period").attr("disabled", true);', :id => "period_type_interval" %> -<%= l(:label_date_from_to, - :start => ((label_tag "from", l(:description_date_from), :class => "hidden-for-sighted") + - text_field_tag('from', @from, :size => 10, :disabled => !@free_period) + calendar_for('from')), - :end => ((label_tag "to", l(:description_date_to), :class => "hidden-for-sighted") + - text_field_tag('to', @to, :size => 10, :disabled => !@free_period) + calendar_for('to'))).html_safe %> -</p> -</div> +<div id="query_form_content" class="hide-when-print"> +<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>"> + <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend> + <div style="<%= @query.new_record? ? "" : "display: none;" %>"> + <%= render :partial => 'queries/filters', :locals => {:query => @query} %> + </div> </fieldset> -<p class="buttons"> +</div> + +<p class="buttons hide-when-print"> <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %> - <%= link_to l(:button_clear), {:controller => controller_name, :action => action_name, :project_id => @project, :issue_id => @issue}, :class => 'icon icon-reload' %> + <%= link_to l(:button_clear), {:project_id => @project, :issue_id => @issue}, :class => 'icon icon-reload' %> </p> <div class="tabs"> -<% url_params = @free_period ? { :from => @from, :to => @to } : { :period => params[:period] } %> +<% query_params = params.slice(:f, :op, :v, :sort) %> <ul> - <li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue }), + <li><%= link_to(l(:label_details), query_params.merge({:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue }), :class => (action_name == 'index' ? 'selected' : nil)) %></li> - <li><%= link_to(l(:label_report), url_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project, :issue_id => @issue}), + <li><%= link_to(l(:label_report), query_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project, :issue_id => @issue}), :class => (action_name == 'report' ? 'selected' : nil)) %></li> </ul> </div> - -<%= javascript_tag do %> -$('#from, #to').change(function(){ - $('#period_type_interval').attr('checked', true); $('#from,#to').removeAttr('disabled'); $('#period').attr('disabled', true); -}); -<% end %> |