Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Filter reports#349

Merged
shalapatil merged 17 commits intodevelopfrom
filter-reports
May 18, 2022
Merged

Filter reports#349
shalapatil merged 17 commits intodevelopfrom
filter-reports

Conversation

@shalapatil
Copy link
Contributor

@shalapatil shalapatil commented Apr 28, 2022

Notion card

https://www.notion.so/saeloun/6afc9500b2cc42af86be55f37894fb09?v=185bd45b9dc24dfcad9c57c07d32a7c0&p=592b4791f2b14979890319b90a1198bf

Summary

Updated API which returns the list of reports to filter the results based on date range, clients, team members and status, if passed any.
Params looks like - ?date_range=last_month&client[]=10&client[]=1&status[]=non_billable&team_member[]=1
I am working on specs. Will add them in next commit in the same PR.

Preview

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to
    not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Checklist:

  • I have manually tested all workflows
  • I have performed a self-review of my own code
  • I have added automated tests for my code

@github-actions
Copy link

github-actions bot commented Apr 28, 2022

Current Code Coverage Percent of this PR:

94.24 %

Files having coverage below 100%

Impacted Files Coverage
/app/controllers/subscriptions_controller.rb 66.67 %
/app/controllers/invoices_controller.rb 63.64 %
/app/models/client.rb 92.68 %
/app/models/invoice.rb 96.3 %
/app/policies/subscriptions_policy.rb 66.67 %
/app/policies/timezone_policy.rb 66.67 %
/app/controllers/invoices/payments_controller.rb 61.11 %
/app/services/report/filters.rb 87.88 %
/app/services/invoice_payment/pdf_generation.rb 65.38 %
/app/services/invoice_payment/checkout.rb 43.48 %
/app/controllers/internal_api/v1/timezones_controller.rb 37.5 %
/lib/countries_info.rb 0.0 %

@supriya3105 supriya3105 requested review from gowsik-ragunath and removed request for alkesh26 May 2, 2022 07:59
@supriya3105
Copy link
Contributor

@shalapatil Please ensure that you use elastic search here on filters as per Vipul's recommendation

@rohitjoshixyz
Copy link
Contributor

Not reviewing right now, waiting for elastic search implementation

@shalapatil shalapatil requested a review from keshavbiswa May 9, 2022 07:44
Comment on lines 18 to 83
def add_filters_if_any(where_clause)
where_clause = add_date_range_filter(where_clause)
where_clause = add_clients_filter(where_clause)
where_clause = add_bill_status_filter(where_clause)
where_clause = add_team_members_filter(where_clause)
end

def add_date_range_filter(where_clause)
if params[:date_range].present?
where_clause.merge(work_date: from_date..to_date)
else
where_clause
end
end

def add_clients_filter(where_clause)
if params[:client_id].present?
clients = Client.includes(:projects).where(id: params[:client_id].split(","))
project_ids = clients.map { |client| client.project_ids }.flatten
where_clause.merge(project_id: project_ids)
else
where_clause
end
end

def add_bill_status_filter(where_clause)
if params[:status].present?
where_clause.merge(bill_status: params[:status].split(","))
else
where_clause
end
end

def add_team_members_filter(where_clause)
if params[:team_member].present?
where_clause.merge(user_id: params[:team_member].split(","))
else
where_clause
end
end

def from_date
case params[:date_range]
when "this_month"
0.month.ago.beginning_of_month
when "last_month"
1.month.ago.beginning_of_month
when "this_week"
0.weeks.ago.beginning_of_week
when "last_week"
1.weeks.ago.beginning_of_week
end
end

def to_date
case params[:date_range]
when "this_month"
0.month.ago.end_of_month
when "last_month"
1.month.ago.end_of_month
when "this_week"
0.weeks.ago.end_of_week
when "last_week"
1.weeks.ago.end_of_week
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use scopes here instead? You may use this and this for reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per slack discussion, we are agoing ahead with elasticsearch

Comment on lines 19 to 22
where_clause = add_date_range_filter(where_clause)
where_clause = add_clients_filter(where_clause)
where_clause = add_bill_status_filter(where_clause)
where_clause = add_team_members_filter(where_clause)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if it's a good idea to reuse the same argument variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we follow this practice or not. But if the variable value is going to be updated and the old value is not going to be used anywhere then there is no harm in using the same variable.

Copy link
Contributor

@rohitjoshixyz rohitjoshixyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 suggestions

end

context "when reports page's request is made with combination of filters" do
before do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use let vs @ variables https://www.betterspecs.org/#let

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aditya-vector How can we reduce the use of these instance variables? let! seems to have a limit of 10 variables cc @shalapatil

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use create_list to create lists of indentical records, and store it in one let variable. we can then use that variable for assertions. @rohitjoshixyz @shalapatil

Check https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md for definition on create_list.

Comment on lines 25 to 34
def add_date_range_filter(where_clause)
if params[:date_range].present?
where_clause.merge(work_date: from_date..to_date)
else
where_clause
end
end

def add_clients_filter(where_clause)
if params[:client_id].present?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to a service class, controller is getting very big

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, all filtering for the controller, perhaps

end

def add_bill_status_filter(where_clause)
if params[:status].present?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using diminutive return?
Eg:

return where_clause if params[:status].blank?

where_clause.merge(bill_status: params[:status].split(","))

Also, I'm inclined towards adding the presence check beforehand and not as a part of the method itself.

end

def add_team_members_filter(where_clause)
if params[:team_member].present?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def add_date_range_filter_if_any(where_clause)
return where_clause if date_range_filter.blank?

where_clause.merge(work_date: from_date..to_date)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, this can be fetched as a range directly. Why add separate methods just to fetch a range? We are anyways not using this anywhere else.

# frozen_string_literal: true

module Report
class Filters < ApplicationService

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Comment on lines 12 to 16
def process
where_clause = add_date_range_filter_if_any({})
where_clause = add_clients_filter_if_any(where_clause)
where_clause = add_bill_status_filter_if_any(where_clause)
where_clause = add_team_members_filter_if_any(where_clause)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small feedback - inclusion totally depends on reviewees' discretion,
How about we use the parameter information at a single source instead, may prove easy to extend

class Filters < ApplicationService
  
  FILTER_PARAM_LIST = %i[date_range status team_member client_id]

  attr_reader :filter_params

  def initialize(filter_params:)
    @filter_params = filter_params
  end

  def process
    FILTER_PARAM_LIST.each_with_object({}) do |filter_param_key, where_condition|
      where_condition.merge(public_send("#{date_range}_filter")) if filter_params[filter_param_key].present?
    end
  end

  # Optional: Remove `_filter` and use filter_param_key as method directly
  def date_range_filter
    { work_date: from_date..to_date }
  end

  def status_filter
    { bill_status: filter_params[:status].split(",") }
  end

  # And other filters as methods and other required methods
end

Note: This is untested, so may need small changes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cool! Updated my code with few changes!

Copy link
Contributor

@rohitjoshixyz rohitjoshixyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job with the refactoring @shalapatil 💯
Few minor suggestions, rest LGTM. @akhilgkrishnan Can you review once for the elastic search GitHub action?

params[:team_member].present?
end

def entries
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entries is too generic, please rename

Suggested change
def entries
def filtered_reports

context "when reports page's request is made without any filters" do
before do
@timesheet_entry1 = create(:timesheet_entry, project:)
TimesheetEntry.search_index.refresh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can consider adding a class method to reindex this model. Non-blocking To Do, is not a priority at all


context "when reports page's request is made with bill status filter" do
before do
@timesheet_entry1 = create(:timesheet_entry, project:, bill_status: "non_billable")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a trait to the timesheet entry factory for billable and non billable since this getting repeated

end

it "returns the time entry reports with given single status value" do
get "/internal_api/v1/reports?status[]=unbilled"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User URL helpers and query params in a hash, this is difficult to read

end

it "returns the time entry reports with given single client value" do
get "/internal_api/v1/reports?client[]=#{client.id}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for all specs, try and use URL helper for get calls

end

context "when reports page's request is made with combination of filters" do
before do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aditya-vector How can we reduce the use of these instance variables? let! seems to have a limit of 10 variables cc @shalapatil

Comment on lines +45 to +48
@timesheet_entry1 = create(:timesheet_entry, project:, work_date: last_month_start_date)
@timesheet_entry2 = create(:timesheet_entry, project:, work_date: this_week_start_date)
@timesheet_entry3 = create(:timesheet_entry, project:, work_date: this_week_start_date)
TimesheetEntry.search_index.refresh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: use let instead of instance variables for specs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using let is exceeding the limits, which is set to 10 by us. So, can't use it.

Copy link
Contributor

@keshavbiswa keshavbiswa May 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For common records we can use create_list() instead of create() to create array of records and can access the record using timesheet_entry[0], timesheet_entry[1], etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Require different values for each timsheet entry, so can't create the same ones

Copy link
Contributor

@keshavbiswa keshavbiswa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shalapatil Great work on the refactoring, loved the changes you made. Looks awesome to me !!! 💯

Copy link
Contributor Author

@shalapatil shalapatil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @keshavbiswa! :)

@shalapatil shalapatil merged commit fc00620 into develop May 18, 2022
@shalapatil shalapatil deleted the filter-reports branch May 18, 2022 05:41
vipulnsward pushed a commit that referenced this pull request Feb 15, 2026
* Close the edit members modal on saving the changes

* Add filtering options for reports list

* Add serchkick gem & use it in timesheetentries

* Add filters for reports page

* Remove model changes as not required anymore

* Extract comma separated multiple params

* Add request specs for reports index page

* Extract comma separated multiple params

* Add utility functions to service class

* Fix params parsing

* Add ES configuration in github validation

* Add optimisation

* Update Readme for elasticsearch installation

* Use ankane elasticsearch github actions

* Add suggested changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants