mirror of
https://github.com/plausible/analytics.git
synced 2024-11-26 23:27:54 +03:00
Add Year to Date option in datepicker
This commit is contained in:
parent
8616dd46fb
commit
7f58e6be4e
@ -9,12 +9,14 @@ import {
|
||||
formatDay,
|
||||
formatDayShort,
|
||||
formatMonthYYYY,
|
||||
formatYear,
|
||||
formatISO,
|
||||
isToday,
|
||||
lastMonth,
|
||||
nowForSite,
|
||||
isSameMonth,
|
||||
isThisMonth,
|
||||
isThisYear,
|
||||
parseUTCDate,
|
||||
isBefore,
|
||||
isAfter,
|
||||
@ -86,12 +88,17 @@ function renderArrow(query, site, period, prevDate, nextDate) {
|
||||
}
|
||||
|
||||
function DatePickerArrows({site, query}) {
|
||||
if (query.period === "month") {
|
||||
if (query.period === "year") {
|
||||
const prevDate = formatISO(shiftMonths(query.date, -12));
|
||||
const nextDate = formatISO(shiftMonths(query.date, 12));
|
||||
|
||||
return renderArrow(query, site, "year", prevDate, nextDate);
|
||||
} else if (query.period === "month") {
|
||||
const prevDate = formatISO(shiftMonths(query.date, -1));
|
||||
const nextDate = formatISO(shiftMonths(query.date, 1));
|
||||
|
||||
return renderArrow(query, site, "month", prevDate, nextDate);
|
||||
} if (query.period === "day") {
|
||||
} else if (query.period === "day") {
|
||||
const prevDate = formatISO(shiftDays(query.date, -1));
|
||||
const nextDate = formatISO(shiftDays(query.date, 1));
|
||||
|
||||
@ -141,53 +148,44 @@ class DatePicker extends React.Component {
|
||||
if (e.key === "ArrowLeft") {
|
||||
const prevDate = formatISO(shiftDays(query.date, -1));
|
||||
const prevMonth = formatISO(shiftMonths(query.date, -1));
|
||||
const prevYear = formatISO(shiftMonths(query.date, -12));
|
||||
|
||||
if (
|
||||
query.period === "day" &&
|
||||
!isBefore(parseUTCDate(prevDate), insertionDate, query.period)
|
||||
) {
|
||||
if (query.period === "day" && !isBefore(parseUTCDate(prevDate), insertionDate, query.period)) {
|
||||
newSearch.period = "day";
|
||||
newSearch.date = prevDate;
|
||||
} else if (
|
||||
query.period === "month" &&
|
||||
!isBefore(parseUTCDate(prevMonth), insertionDate, query.period)
|
||||
) {
|
||||
} else if (query.period === "month" && !isBefore(parseUTCDate(prevMonth), insertionDate, query.period)) {
|
||||
newSearch.period = "month";
|
||||
newSearch.date = prevMonth;
|
||||
} else if (query.period === "year" && !isBefore(parseUTCDate(prevYear), insertionDate, query.period)) {
|
||||
newSearch.period = "year";
|
||||
newSearch.date = prevYear;
|
||||
}
|
||||
} else if (e.key === "ArrowRight") {
|
||||
const now = nowForSite(this.props.site)
|
||||
const nextDate = formatISO(shiftDays(query.date, 1));
|
||||
const nextMonth = formatISO(shiftMonths(query.date, 1));
|
||||
const nextYear = formatISO(shiftMonths(query.date, 12));
|
||||
|
||||
if (
|
||||
query.period === "day" &&
|
||||
!isAfter(
|
||||
parseUTCDate(nextDate),
|
||||
nowForSite(this.props.site),
|
||||
query.period
|
||||
)
|
||||
) {
|
||||
if (query.period === "day" && !isAfter(parseUTCDate(nextDate), now, query.period)) {
|
||||
newSearch.period = "day";
|
||||
newSearch.date = nextDate;
|
||||
} else if (
|
||||
query.period === "month" &&
|
||||
!isAfter(
|
||||
parseUTCDate(nextMonth),
|
||||
nowForSite(this.props.site),
|
||||
query.period
|
||||
)
|
||||
) {
|
||||
} else if (query.period === "month" && !isAfter(parseUTCDate(nextMonth), now, query.period)) {
|
||||
newSearch.period = "month";
|
||||
newSearch.date = nextMonth;
|
||||
} else if (query.period === "year" && !isAfter(parseUTCDate(nextYear), now, query.period)) {
|
||||
newSearch.period = "year";
|
||||
newSearch.date = nextYear;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({open: false});
|
||||
|
||||
const keys = ['d', 'r', 'w', 'm', 'y', 't', 's'];
|
||||
const redirects = [{date: false, period: 'day'}, {period: 'realtime'}, {date: false, period: '7d'}, {date: false, period: 'month'}, {date: false, period: '12mo'}, {date: false, period: '30d'}, {date: false, period: '6mo'}];
|
||||
const keys = ['d', 'r', 'w', 'm', 'y', 't', 's', 'a'];
|
||||
const redirects = [{date: false, period: 'day'}, {period: 'realtime'}, {date: false, period: '7d'}, {date: false, period: 'month'}, {date: false, period: 'year'}, {date: false, period: '30d'}, {date: false, period: '6mo'}, {date: false, period: 'all'}];
|
||||
|
||||
if (keys.includes(e.key.toLowerCase())) {
|
||||
console.log(e.key.toLowerCase())
|
||||
console.log(redirects[keys.indexOf(e.key.toLowerCase())])
|
||||
navigateToQuery(history, query, {...newSearch, ...(redirects[keys.indexOf(e.key.toLowerCase())])});
|
||||
} else if (e.key.toLowerCase() === 'c') {
|
||||
this.setState({mode: 'calendar', open: true}, this.openCalendar);
|
||||
@ -253,6 +251,11 @@ class DatePicker extends React.Component {
|
||||
return 'Last 6 months'
|
||||
} if (query.period === '12mo') {
|
||||
return 'Last 12 months'
|
||||
} if (query.period === 'year') {
|
||||
if (isThisYear(site, query.date)) {
|
||||
return 'Year to Date'
|
||||
}
|
||||
return formatYear(query.date)
|
||||
} if (query.period === 'all') {
|
||||
return 'All time'
|
||||
} if (query.period === 'custom') {
|
||||
@ -293,9 +296,10 @@ class DatePicker extends React.Component {
|
||||
'Realtime': 'R',
|
||||
'Last 7 days': 'W',
|
||||
'Month to Date': 'M',
|
||||
'Last 12 months': 'Y',
|
||||
'Year to Date': 'Y',
|
||||
'Last 6 months': 'S',
|
||||
'Last 30 days': 'T',
|
||||
'All time': 'A',
|
||||
};
|
||||
|
||||
return (
|
||||
@ -336,8 +340,8 @@ class DatePicker extends React.Component {
|
||||
{ this.renderLink('month', 'Last month', {date: lastMonth(this.props.site)}) }
|
||||
</div>
|
||||
<div className="py-1 border-b border-gray-200 dark:border-gray-500 date-option-group">
|
||||
{this.renderLink("6mo", "Last 6 months")}
|
||||
{this.renderLink("12mo", "Last 12 months")}
|
||||
{this.renderLink("year", "Year to Date")}
|
||||
</div>
|
||||
<div className="py-1 date-option-group">
|
||||
{this.renderLink("all", "All time")}
|
||||
|
@ -3,7 +3,7 @@ import { Link, withRouter } from 'react-router-dom'
|
||||
import {formatDay, formatMonthYYYY, nowForSite, parseUTCDate} from './util/date'
|
||||
import * as storage from './util/storage'
|
||||
|
||||
const PERIODS = ['realtime', 'day', 'month', '7d', '30d', '6mo', '12mo', 'all', 'custom']
|
||||
const PERIODS = ['realtime', 'day', 'month', '7d', '30d', '6mo', '12mo', 'year', 'all', 'custom']
|
||||
|
||||
export function parseQuery(querystring, site) {
|
||||
const q = new URLSearchParams(querystring)
|
||||
|
@ -36,6 +36,10 @@ export function formatMonthYYYY(date) {
|
||||
return `${MONTHS[date.getMonth()]} ${date.getFullYear()}`;
|
||||
}
|
||||
|
||||
export function formatYear(date) {
|
||||
return `Year of ${date.getFullYear()}`;
|
||||
}
|
||||
|
||||
export function formatDay(date) {
|
||||
var weekday = DAYS_ABBREV[date.getUTCDay()];
|
||||
if (date.getFullYear() !== (new Date()).getFullYear()) {
|
||||
@ -76,6 +80,10 @@ export function isThisMonth(site, date) {
|
||||
return formatMonthYYYY(date) === formatMonthYYYY(nowForSite(site))
|
||||
}
|
||||
|
||||
export function isThisYear(site, date) {
|
||||
return date.getFullYear() === nowForSite(site).getFullYear()
|
||||
}
|
||||
|
||||
export function isBefore(date1, date2, period) {
|
||||
/* assumes 'day' and 'month' are the only valid periods */
|
||||
if (date1.getFullYear() !== date2.getFullYear()) {
|
||||
|
@ -8,6 +8,29 @@ defmodule Plausible.Stats.Query do
|
||||
|
||||
@default_sample_threshold 20_000_000
|
||||
|
||||
def shift_back(%__MODULE__{period: "year"} = query, site) do
|
||||
# Querying current year to date
|
||||
{new_first, new_last} =
|
||||
if Timex.compare(Timex.now(site.timezone), query.date_range.first, :year) == 0 do
|
||||
diff =
|
||||
Timex.diff(
|
||||
Timex.beginning_of_year(Timex.now(site.timezone)),
|
||||
Timex.now(site.timezone),
|
||||
:days
|
||||
) - 1
|
||||
|
||||
{query.date_range.first |> Timex.shift(days: diff),
|
||||
Timex.now(site.timezone) |> Timex.to_date() |> Timex.shift(days: diff)}
|
||||
else
|
||||
diff = Timex.diff(query.date_range.first, query.date_range.last, :days) - 1
|
||||
|
||||
{query.date_range.first |> Timex.shift(days: diff),
|
||||
query.date_range.last |> Timex.shift(days: diff)}
|
||||
end
|
||||
|
||||
Map.put(query, :date_range, Date.range(new_first, new_last))
|
||||
end
|
||||
|
||||
def shift_back(%__MODULE__{period: "month"} = query, site) do
|
||||
# Querying current month to date
|
||||
{new_first, new_last} =
|
||||
@ -146,6 +169,23 @@ defmodule Plausible.Stats.Query do
|
||||
|> maybe_include_imported(site, params)
|
||||
end
|
||||
|
||||
def from(site, %{"period" => "year"} = params) do
|
||||
end_date =
|
||||
parse_single_date(site.timezone, params)
|
||||
|> Timex.end_of_year()
|
||||
|
||||
start_date = Timex.beginning_of_year(end_date)
|
||||
|
||||
%__MODULE__{
|
||||
period: "year",
|
||||
date_range: Date.range(start_date, end_date),
|
||||
interval: Map.get(params, "interval", "month"),
|
||||
filters: parse_filters(params),
|
||||
sample_threshold: Map.get(params, "sample_threshold", @default_sample_threshold)
|
||||
}
|
||||
|> maybe_include_imported(site, params)
|
||||
end
|
||||
|
||||
def from(site, %{"period" => "all"} = params) do
|
||||
end_date =
|
||||
today(site.timezone)
|
||||
|
@ -60,6 +60,18 @@ defmodule Plausible.Stats.QueryTest do
|
||||
assert q.interval == "month"
|
||||
end
|
||||
|
||||
test "parses year to date format" do
|
||||
q = Query.from(@site, %{"period" => "year"})
|
||||
|
||||
assert q.date_range.first ==
|
||||
Timex.now(@site.timezone) |> Timex.to_date() |> Timex.beginning_of_year()
|
||||
|
||||
assert q.date_range.last ==
|
||||
Timex.now(@site.timezone) |> Timex.to_date() |> Timex.end_of_year()
|
||||
|
||||
assert q.interval == "month"
|
||||
end
|
||||
|
||||
test "parses all time" do
|
||||
q = Query.from(@site, %{"period" => "all"})
|
||||
|
||||
|
@ -159,6 +159,28 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
|
||||
assert Enum.sum(plot) == 4
|
||||
end
|
||||
|
||||
test "displays visitors for calendar year with imported data", %{conn: conn, site: site} do
|
||||
populate_stats(site, [
|
||||
build(:pageview, timestamp: ~N[2021-01-01 00:00:00]),
|
||||
build(:pageview, timestamp: ~N[2021-12-31 00:00:00]),
|
||||
build(:imported_visitors, date: ~D[2021-01-01]),
|
||||
build(:imported_visitors, date: ~D[2021-12-31])
|
||||
])
|
||||
|
||||
conn =
|
||||
get(
|
||||
conn,
|
||||
"/api/stats/#{site.domain}/main-graph?period=year&date=2021-12-31&with_imported=true"
|
||||
)
|
||||
|
||||
assert %{"plot" => plot} = json_response(conn, 200)
|
||||
|
||||
assert Enum.count(plot) == 12
|
||||
assert List.first(plot) == 2
|
||||
assert List.last(plot) == 2
|
||||
assert Enum.sum(plot) == 4
|
||||
end
|
||||
|
||||
test "displays visitors for all time with just native data", %{conn: conn, site: site} do
|
||||
use Plausible.Repo
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user