Skip to content

Commit 12d818a

Browse files
authored
Refactoring preparation for DashboardQueryParser (#5929)
* rename QueryParser to ApiQueryParser * move utc_time_range construction to querybuilder * input_date_range format * rename 30m atom to realtime_30m * move build_comparison_date_range into do_build
1 parent 98632ae commit 12d818a

18 files changed

Lines changed: 274 additions & 204 deletions

File tree

lib/plausible/segments/filters.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule Plausible.Segments.Filters do
33
This module contains functions that enable resolving segments in filters.
44
"""
55
alias Plausible.Segments
6-
alias Plausible.Stats.{Filters, QueryParser}
6+
alias Plausible.Stats.{Filters, ApiQueryParser}
77

88
@max_segment_filters_count 10
99

@@ -48,7 +48,7 @@ defmodule Plausible.Segments.Filters do
4848
segments,
4949
%{},
5050
fn %Segments.Segment{id: id, segment_data: segment_data} ->
51-
case QueryParser.parse_filters(segment_data["filters"]) do
51+
case ApiQueryParser.parse_filters(segment_data["filters"]) do
5252
{:ok, filters} -> {id, filters}
5353
_ -> {id, nil}
5454
end
Lines changed: 42 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
1-
defmodule Plausible.Stats.QueryParser do
1+
defmodule Plausible.Stats.ApiQueryParser do
22
@moduledoc false
33

44
use Plausible
55

66
alias Plausible.Stats.{Filters, Metrics, DateTimeRange, JSONSchema}
77

8-
def parse(site, schema_type, params, now \\ nil) when is_map(params) do
9-
now = now || Plausible.Stats.Query.Test.get_fixed_now()
10-
date = now |> DateTime.shift_zone!(site.timezone) |> DateTime.to_date()
8+
def parse(schema_type, params) when is_map(params) do
9+
now = Plausible.Stats.Query.Test.get_fixed_now()
10+
input_date_range = Map.get(params, "date_range")
1111

1212
with :ok <- JSONSchema.validate(schema_type, params),
13-
{:ok, date, now} <- parse_date(site, Map.get(params, "date"), date, now),
14-
{:ok, raw_time_range} <-
15-
parse_time_range(site, Map.get(params, "date_range"), date, now),
16-
utc_time_range = raw_time_range |> DateTimeRange.to_timezone("Etc/UTC"),
13+
{:ok, input_date_range} <- parse_input_date_range(input_date_range),
1714
{:ok, metrics} <- parse_metrics(Map.fetch!(params, "metrics")),
1815
{:ok, filters} <- parse_filters(params["filters"]),
1916
{:ok, dimensions} <- parse_dimensions(params["dimensions"]),
2017
{:ok, order_by} <- parse_order_by(params["order_by"]),
2118
{:ok, pagination} <- parse_pagination(params["pagination"]),
22-
{:ok, include} <- parse_include(params["include"], site) do
19+
{:ok, include} <- parse_include(params["include"]) do
2320
{:ok,
2421
Plausible.Stats.ParsedQueryParams.new!(%{
2522
now: now,
26-
utc_time_range: utc_time_range,
23+
input_date_range: input_date_range,
2724
metrics: metrics,
2825
filters: filters,
2926
dimensions: dimensions,
@@ -42,6 +39,13 @@ defmodule Plausible.Stats.QueryParser do
4239

4340
def parse_date_range_pair(_site, unknown), do: {:error, "Invalid date_range '#{i(unknown)}'."}
4441

42+
defp date_range_from_date_strings(site, from, to) do
43+
with {:ok, from_date} <- Date.from_iso8601(from),
44+
{:ok, to_date} <- Date.from_iso8601(to) do
45+
{:ok, DateTimeRange.new!(from_date, to_date, site.timezone)}
46+
end
47+
end
48+
4549
defp parse_metrics(metrics) when is_list(metrics) do
4650
parse_list(metrics, &parse_metric/1)
4751
end
@@ -155,94 +159,43 @@ defmodule Plausible.Stats.QueryParser do
155159
{:ok, []}
156160
end
157161

158-
defp parse_date(site, date_string, _date, _now) when is_binary(date_string) do
159-
with {:ok, date} <- Date.from_iso8601(date_string),
160-
{:ok, datetime} <- DateTime.new(date, ~T[00:00:00], site.timezone) do
161-
{:ok, date, datetime}
162-
else
163-
{:gap, just_before, _just_after} -> just_before
164-
{:ambiguous, first_datetime, _second_datetime} -> first_datetime
165-
_ -> {:error, "Invalid date '#{date_string}'."}
166-
end
167-
end
168-
169-
defp parse_date(_site, _date_string, date, now) do
170-
{:ok, date, now}
171-
end
172-
173-
defp parse_time_range(_site, date_range, _date, now) when date_range in ["realtime", "30m"] do
174-
duration_minutes =
175-
case date_range do
176-
"realtime" -> 5
177-
"30m" -> 30
178-
end
179-
180-
first_datetime = DateTime.shift(now, minute: -duration_minutes)
181-
last_datetime = DateTime.shift(now, second: 5)
182-
183-
{:ok, DateTimeRange.new!(first_datetime, last_datetime)}
184-
end
185-
186-
defp parse_time_range(site, "day", date, _now) do
187-
{:ok, DateTimeRange.new!(date, date, site.timezone)}
188-
end
189-
190-
defp parse_time_range(site, "month", date, _now) do
191-
last = date |> Date.end_of_month()
192-
first = last |> Date.beginning_of_month()
193-
{:ok, DateTimeRange.new!(first, last, site.timezone)}
194-
end
195-
196-
defp parse_time_range(site, "year", date, _now) do
197-
last = date |> Plausible.Times.end_of_year()
198-
first = last |> Plausible.Times.beginning_of_year()
199-
{:ok, DateTimeRange.new!(first, last, site.timezone)}
200-
end
201-
202-
defp parse_time_range(site, "all", date, _now) do
203-
start_date = Plausible.Sites.stats_start_date(site) || date
162+
defp parse_input_date_range("realtime"), do: {:ok, :realtime}
163+
defp parse_input_date_range("30m"), do: {:ok, :realtime_30m}
164+
defp parse_input_date_range("day"), do: {:ok, :day}
165+
defp parse_input_date_range("month"), do: {:ok, :month}
166+
defp parse_input_date_range("year"), do: {:ok, :year}
167+
defp parse_input_date_range("all"), do: {:ok, :all}
204168

205-
{:ok, DateTimeRange.new!(start_date, date, site.timezone)}
206-
end
207-
208-
defp parse_time_range(site, shorthand, date, _now) when is_binary(shorthand) do
169+
defp parse_input_date_range(shorthand) when is_binary(shorthand) do
209170
case Integer.parse(shorthand) do
210-
{n, "d"} when n > 0 and n <= 5_000 ->
211-
last = date |> Date.add(-1)
212-
first = date |> Date.add(-n)
213-
{:ok, DateTimeRange.new!(first, last, site.timezone)}
214-
215-
{n, "mo"} when n > 0 and n <= 100 ->
216-
last = date |> Date.shift(month: -1) |> Date.end_of_month()
217-
first = date |> Date.shift(month: -n) |> Date.beginning_of_month()
218-
{:ok, DateTimeRange.new!(first, last, site.timezone)}
219-
220-
_ ->
221-
{:error, "Invalid date_range #{i(shorthand)}"}
171+
{n, "d"} when n > 0 and n <= 5_000 -> {:ok, {:last_n_days, n}}
172+
{n, "mo"} when n > 0 and n <= 100 -> {:ok, {:last_n_months, n}}
173+
_ -> {:error, "Invalid date_range #{i(shorthand)}"}
222174
end
223175
end
224176

225-
defp parse_time_range(site, [from, to], _date, _now) when is_binary(from) and is_binary(to) do
226-
case date_range_from_date_strings(site, from, to) do
227-
{:ok, date_range} -> {:ok, date_range}
228-
{:error, _} -> date_range_from_timestamps(from, to)
177+
defp parse_input_date_range([from, to]) when is_binary(from) and is_binary(to) do
178+
case parse_date_strings(from, to) do
179+
{:ok, dates} -> {:ok, dates}
180+
{:error, _} -> parse_timestamp_strings(from, to)
229181
end
230182
end
231183

232-
defp parse_time_range(_site, unknown, _date, _now),
233-
do: {:error, "Invalid date_range #{i(unknown)}"}
184+
defp parse_input_date_range(unknown) do
185+
{:error, "Invalid date_range #{i(unknown)}"}
186+
end
234187

235-
defp date_range_from_date_strings(site, from, to) do
188+
defp parse_date_strings(from, to) do
236189
with {:ok, from_date} <- Date.from_iso8601(from),
237190
{:ok, to_date} <- Date.from_iso8601(to) do
238-
{:ok, DateTimeRange.new!(from_date, to_date, site.timezone)}
191+
{:ok, {:date_range, from_date, to_date}}
239192
end
240193
end
241194

242-
defp date_range_from_timestamps(from, to) do
195+
defp parse_timestamp_strings(from, to) do
243196
with {:ok, from_datetime, _offset} <- DateTime.from_iso8601(from),
244197
{:ok, to_datetime, _offset} <- DateTime.from_iso8601(to) do
245-
{:ok, DateTimeRange.new!(from_datetime, to_datetime)}
198+
{:ok, {:datetime_range, from_datetime, to_datetime}}
246199
else
247200
_ -> {:error, "Invalid date_range '#{i([from, to])}'."}
248201
end
@@ -307,14 +260,14 @@ defmodule Plausible.Stats.QueryParser do
307260
defp parse_order_direction([_, "desc"]), do: {:ok, :desc}
308261
defp parse_order_direction(entry), do: {:error, "Invalid order_by entry '#{i(entry)}'."}
309262

310-
def parse_include(include, site) when is_map(include) do
263+
def parse_include(include) when is_map(include) do
311264
with {:ok, include} <- atomize_include_keys(include) do
312-
update_comparisons_date_range(include, site)
265+
parse_comparison_date_range(include)
313266
end
314267
end
315268

316-
def parse_include(nil, _site), do: {:ok, nil}
317-
def parse_include(include, _site), do: {:error, "Invalid include '#{i(include)}'."}
269+
def parse_include(nil), do: {:ok, nil}
270+
def parse_include(include), do: {:error, "Invalid include '#{i(include)}'."}
318271

319272
defp atomize_include_keys(map) do
320273
expected_keys =
@@ -329,13 +282,13 @@ defmodule Plausible.Stats.QueryParser do
329282
end
330283
end
331284

332-
defp update_comparisons_date_range(%{comparisons: %{date_range: date_range}} = include, site) do
333-
with {:ok, parsed_date_range} <- parse_time_range(site, date_range, nil, nil) do
285+
defp parse_comparison_date_range(%{comparisons: %{date_range: date_range}} = include) do
286+
with {:ok, parsed_date_range} <- parse_input_date_range(date_range) do
334287
{:ok, put_in(include, [:comparisons, :date_range], parsed_date_range)}
335288
end
336289
end
337290

338-
defp update_comparisons_date_range(include, _site), do: {:ok, include}
291+
defp parse_comparison_date_range(include), do: {:ok, include}
339292

340293
defp parse_pagination(pagination) when is_map(pagination) do
341294
{:ok,

lib/plausible/stats/filters/filters.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Plausible.Stats.Filters do
44
"""
55

66
alias Plausible.Stats.Query
7-
alias Plausible.Stats.QueryParser
7+
alias Plausible.Stats.ApiQueryParser
88
alias Plausible.Stats.Filters.LegacyStatsAPIFilterParser
99

1010
@visit_props [
@@ -75,7 +75,7 @@ defmodule Plausible.Stats.Filters do
7575
end
7676

7777
def parse(filters) when is_list(filters) do
78-
{:ok, parsed_filters} = QueryParser.parse_filters(filters)
78+
{:ok, parsed_filters} = ApiQueryParser.parse_filters(filters)
7979
parsed_filters
8080
end
8181

lib/plausible/stats/interval.ex

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule Plausible.Stats.Interval do
77
`week`, and `month`.
88
"""
99

10-
alias Plausible.Stats.DateTimeRange
10+
alias Plausible.Stats.{DateTimeRange, Query}
1111

1212
@type t() :: String.t()
1313
@type(opt() :: {:site, Plausible.Site.t()} | {:from, Date.t()}, {:to, Date.t()})
@@ -23,24 +23,16 @@ defmodule Plausible.Stats.Interval do
2323
interval in @intervals
2424
end
2525

26-
@spec default_for_period(period()) :: t()
26+
@spec default_for_query(Query.t()) :: t()
2727
@doc """
28-
Returns the suggested interval for the given time period.
28+
Returns the suggested interval (i.e. the time dimension) for the given query.
2929
"""
30-
def default_for_period(period) do
31-
case period do
32-
period when period in ["realtime", "30m"] -> "minute"
33-
"day" -> "hour"
34-
period when period in ["custom", "7d", "28d", "30d", "91d", "month"] -> "day"
35-
period when period in ["6mo", "12mo", "year"] -> "month"
36-
end
37-
end
30+
def default_for_query(query)
3831

39-
@spec default_for_date_range(DateTimeRange.t()) :: t()
40-
@doc """
41-
Returns the suggested interval for the given `DateTimeRange` struct.
42-
"""
43-
def default_for_date_range(%DateTimeRange{first: first, last: last}) do
32+
def default_for_query(%Query{
33+
input_date_range: :all,
34+
utc_time_range: %DateTimeRange{first: first, last: last}
35+
}) do
4436
cond do
4537
Plausible.Times.diff(last, first, :month) > 0 ->
4638
"month"
@@ -53,6 +45,17 @@ defmodule Plausible.Stats.Interval do
5345
end
5446
end
5547

48+
def default_for_query(%Query{} = query) do
49+
case query.input_date_range do
50+
period when period in [:realtime, :realtime_30m] -> "minute"
51+
:day -> "hour"
52+
{:last_n_days, _} -> "day"
53+
period when period in [:custom, :month] -> "day"
54+
{:last_n_months, _} -> "month"
55+
:year -> "month"
56+
end
57+
end
58+
5659
@valid_by_period %{
5760
"realtime" => ["minute"],
5861
"day" => ["minute", "hour"],

lib/plausible/stats/legacy/legacy_dimensions.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
defmodule Plausible.Stats.Legacy.Dimensions do
22
@moduledoc """
3-
Deprecated module. See QueryParser for list of valid dimensions
3+
Deprecated module. See ApiQueryParser for list of valid dimensions
44
"""
55
@event_props ["event:page", "event:name", "event:goal", "event:hostname"]
66
@session_props [

0 commit comments

Comments
 (0)