Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/services/forest_liana/filters_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ def parse_condition_without_smart_field(condition)
field_name = condition['field']

if @operator_date_parser.is_date_operator?(operator)
condition = @operator_date_parser.get_date_filter(operator, value)
field_schema = SchemaUtils.find_column_schema_by_name(@resource.name, field_name)
condition = @operator_date_parser.get_date_filter(operator, value, field_schema)
return "#{parse_field_name(field_name)} #{condition}"
end

Expand Down Expand Up @@ -259,10 +260,12 @@ def parse_aggregation_on_previous_interval(node, previous_condition)

def parse_previous_interval_condition(condition)
raise_empty_condition_in_filter_error unless condition
field_schema = SchemaUtils.find_column_schema_by_name(@resource.name, condition['field'])

parsed_condition = @operator_date_parser.get_date_filter_for_previous_interval(
condition['operator'],
condition['value']
condition['value'],
field_schema
)

"#{parse_field_name(condition['field'])} #{parsed_condition}"
Expand Down
63 changes: 36 additions & 27 deletions app/services/forest_liana/operator_date_interval_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,16 @@ def to_client_timezone(date)
date - @timezone_offset.hours
end

def get_date_filter(operator, value)

def format_date(field_type, value)
if field_type == 'Dateonly'
return value.strftime('%Y-%m-%d')
end

value
end

def get_date_filter(operator, value, field_schema)
return nil unless is_date_operator? operator

filter = case operator
Expand All @@ -79,18 +88,18 @@ def get_date_filter(operator, value)
when OPERATOR_PAST
"<= '#{Time.now}'"
when OPERATOR_TODAY
"BETWEEN '#{to_client_timezone(Time.now.beginning_of_day)}' " +
"AND '#{to_client_timezone(Time.now.end_of_day)}'"
"BETWEEN '#{format_date(field_schema[:type], to_client_timezone(Time.now.beginning_of_day))}' " +
"AND '#{format_date(field_schema[:type], to_client_timezone(Time.now.end_of_day))}'"
when OPERATOR_PREVIOUS_X_DAYS
ensure_integer_value(value)
"BETWEEN '" +
"#{to_client_timezone(Integer(value).day.ago.beginning_of_day)}'" +
" AND '#{to_client_timezone(1.day.ago.end_of_day)}'"
"#{format_date(field_schema[:type], to_client_timezone(Integer(value).day.ago.beginning_of_day))}'" +
" AND '#{format_date(field_schema[:type], to_client_timezone(1.day.ago.end_of_day))}'"
when OPERATOR_PREVIOUS_X_DAYS_TO_DATE
ensure_integer_value(value)
"BETWEEN '" +
"#{to_client_timezone((Integer(value) - 1).day.ago.beginning_of_day)}'" +
" AND '#{Time.now}'"
"#{format_date(field_schema[:type], to_client_timezone((Integer(value) - 1).day.ago.beginning_of_day))}'" +
" AND '#{format_date(field_schema[:type], Time.now)}'"
when OPERATOR_BEFORE_X_HOURS_AGO
ensure_integer_value(value)
"< '#{(Integer(value)).hour.ago}'"
Expand All @@ -109,35 +118,35 @@ def get_date_filter(operator, value)
to_date = PERIODS[operator][:to_date]

if to_date
from = to_client_timezone(Time.now.send("beginning_of_#{period_of_time}"))
to = Time.now
from = format_date(field_schema[:type], to_client_timezone(Time.now.send("beginning_of_#{period_of_time}")))
to = format_date(field_schema[:type], Time.now)
else
from = to_client_timezone(duration.send(period).ago
.send("beginning_of_#{period_of_time}"))
to = to_client_timezone(duration.send(period).ago
.send("end_of_#{period_of_time}"))
from = format_date(field_schema[:type], to_client_timezone(duration.send(period).ago
.send("beginning_of_#{period_of_time}")))
to = format_date(field_schema[:type], to_client_timezone(duration.send(period).ago
.send("end_of_#{period_of_time}")))
end

"BETWEEN '#{from}' AND '#{to}'"
end

def get_date_filter_for_previous_interval(operator, value)
def get_date_filter_for_previous_interval(operator, value, field_schema)
return nil unless has_previous_interval? operator

case operator
when OPERATOR_TODAY
return "BETWEEN '#{to_client_timezone(1.day.ago.beginning_of_day)}' AND " +
"'#{to_client_timezone(1.day.ago.end_of_day)}'"
return "BETWEEN '#{format_date(field_schema[:type], to_client_timezone(1.day.ago.beginning_of_day))}' AND " +
"'#{format_date(field_schema[:type], to_client_timezone(1.day.ago.end_of_day))}'"
when OPERATOR_PREVIOUS_X_DAYS
ensure_integer_value(value)
return "BETWEEN '" +
"#{to_client_timezone((Integer(value) * 2).day.ago.beginning_of_day)}'" +
" AND '#{to_client_timezone((Integer(value) + 1).day.ago.end_of_day)}'"
"#{format_date(field_schema[:type], to_client_timezone((Integer(value) * 2).day.ago.beginning_of_day))}'" +
" AND '#{format_date(field_schema[:type], to_client_timezone((Integer(value) + 1).day.ago.end_of_day))}'"
when OPERATOR_PREVIOUS_X_DAYS_TO_DATE
ensure_integer_value(value)
return "BETWEEN '" +
"#{to_client_timezone(((Integer(value) * 2) - 1).day.ago.beginning_of_day)}'" +
" AND '#{to_client_timezone(Integer(value).day.ago)}'"
"#{format_date(field_schema[:type], to_client_timezone(((Integer(value) * 2) - 1).day.ago.beginning_of_day))}'" +
" AND '#{format_date(field_schema[:type], to_client_timezone(Integer(value).day.ago))}'"
end

duration = PERIODS[operator][:duration]
Expand All @@ -146,14 +155,14 @@ def get_date_filter_for_previous_interval(operator, value)
to_date = PERIODS[operator][:to_date]

if to_date
from = to_client_timezone((duration)
.send(period).ago.send("beginning_of_#{period_of_time}"))
to = to_client_timezone((duration).send(period).ago)
from = format_date(field_schema[:type], to_client_timezone((duration)
.send(period).ago.send("beginning_of_#{period_of_time}")))
to = format_date(field_schema[:type], to_client_timezone((duration).send(period).ago))
else
from = to_client_timezone((duration * 2).send(period).ago
.send("beginning_of_#{period_of_time}"))
to = to_client_timezone((duration * 2).send(period).ago
.send("end_of_#{period_of_time}"))
from = format_date(field_schema[:type], to_client_timezone((duration * 2).send(period).ago
.send("beginning_of_#{period_of_time}")))
to = format_date(field_schema[:type], to_client_timezone((duration * 2).send(period).ago
.send("end_of_#{period_of_time}")))
end

"BETWEEN '#{from}' AND '#{to}'"
Expand Down
17 changes: 17 additions & 0 deletions app/services/forest_liana/schema_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ def self.find_model_from_collection_name(collection_name, logs = false)
model_found
end

def self.find_column_schema_by_name(collection_name, field_name)
schema = ForestLiana.apimap.find { |collection| collection.name == collection_name }
if field_name.include?(':')
relation, field_name = field_name.split(':')
relation_schema = schema.fields.find do |field|
field[:field].to_s == relation
end
foreign_collection_name, = relation_schema[:reference].split('.')

return find_column_schema_by_name(foreign_collection_name, field_name)
else
return schema.fields.find do |field|
field[:field].to_s == field_name
end
end
end

def self.tables_names
ActiveRecord::Base.connection.tables
end
Expand Down
28 changes: 26 additions & 2 deletions test/services/forest_liana/operator_date_interval_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,36 @@ class OperatorDateIntervalParserTest < ActiveSupport::TestCase
test 'OPERATOR_AFTER_X_HOURS_AGO and OPERATOR_BEFORE_X_HOURS_AGO should not take timezone into account' do
# Setting a big timezone (GMT+10) on purpose, the timezone should not be applied on the result date
operatorDateIntervalParser = OperatorDateIntervalParser.new('Australia/Sydney')
fake_field_schema = {
field: "foo",
type: "Dateonly",
is_filterable: true,
is_sortable: true,
is_read_only: false,
is_required: false,
is_virtual: false,
default_value: nil,
integration: nil,
reference: nil,
inverse_of: nil,
relationships: nil,
widget: nil,
validations: []
}

result = operatorDateIntervalParser.get_date_filter(OperatorDateIntervalParser::OPERATOR_AFTER_X_HOURS_AGO, 2)
result = operatorDateIntervalParser.get_date_filter(
OperatorDateIntervalParser::OPERATOR_AFTER_X_HOURS_AGO,
2,
fake_field_schema
)
hourComputed = result.split('> ')[1].tr('\'', '').to_datetime.hour
assert hourComputed == Time.now.utc.hour - 2

result = operatorDateIntervalParser.get_date_filter(OperatorDateIntervalParser::OPERATOR_BEFORE_X_HOURS_AGO, 2)
result = operatorDateIntervalParser.get_date_filter(
OperatorDateIntervalParser::OPERATOR_BEFORE_X_HOURS_AGO,
2,
fake_field_schema
)
hourComputed = result.split('< ')[1].tr('\'', '').to_datetime.hour
assert hourComputed == Time.now.utc.hour - 2
end
Expand Down
Loading