* Implement basic HelpScout integration
* Set 127.0.0.1 as a default customer IP in `Plans.with_prices/2`
* Use `is_nil/1` instead of `... == nil` (h/t @aerosol)
* Use `Path.join/1,2` to build API URLs a bit more safely (h/t @aerosol)
* Check for locked sites entirely within query logic
* Move conditional start of HelpScout vault to compile-time
* Include customer_id in error sent to Sentry
* Use `Plug.Crypto.secure_compare/2` for constant-time signature comparison
* Refactor token request function
* Use `Path.join/1` in one more spot
* Use route helper to build CRM URL
- `:verification` flag is no longer needed,
however we'll keep the old "awaiting first pageview" flow
for Community Edition where verification is not available
by default
- `:traffic_drop_notifications` have been tested and should
be enabled for everyone
* Expose current visitors 12h aggregate
* Remove unused site association
* Distinct drop/spike notification factories
* Rename modules accordingly + implement drop handling
* Rename periodic oban service
* Implement drop email
* Rest of the owl
* Update changelog
* Update moduledoc
* Update moduledoc
* Min threshold to 1
* Threshold 1
* Remove merge artifact
* Put panel behind a feature flag
* Format
Some users are reporting wrong results for stats querying from events table
but with session filters.
This seems to be caused by sampling: Namely both tables end up sampled and the
incorrect sample rate seems to get used.
This is sadly impossible to test for in our test suite, but reveals itself in
production with the following query:
```sql
SELECT
toUInt64 (round(uniq (se0.user_id) * any(_sample_factor))) AS visitors
FROM
events_v2 AS se0 SAMPLE 20000000
INNER JOIN (
SELECT
sss0.session_id AS session_id,
any(_sample_factor) AS _sample_factor
FROM
sessions_v2 AS sss0 SAMPLE 20000000
WHERE
(sss0.site_id = _CAST (8195000, 'Int64'))
AND (sss0.timestamp >= _CAST (1720497600, 'DateTime'))
AND (sss0.start < _CAST (1720584000, 'DateTime'))
AND (sss0.entry_page IN ('/'))
AND (sss0.sign = 1)
GROUP BY
sss0.session_id
) AS ss1 ON se0.session_id = ss1.session_id
WHERE
(se0.site_id = _CAST (8195000, 'Int64'))
AND (se0.timestamp >= _CAST (1720497600, 'DateTime'))
AND (se0.timestamp < _CAST (1720584000, 'DateTime'))
```
By removing the inner query SAMPLE clause the result changes >50%
Related old PR: https://github.com/plausible/analytics/pull/2962
This will likely require more work in the future so looking forward to that -
after my vacation.
* Refactor Expression.dimension to accept q
* Handle quarter- and half-hour timezones
Previously APIv2 output didn't start at a full hour for these time zones
and main graph was blank
The core reasoning is that ClickHouse `timeSlots` is not time-zone
aware and works off of unix epoch - meaning that in time zones which
have an offset of 5:45 the "hours" reported would start at minute :45.
The fix is kind of silly - we now divide each hour into 4 and handle
things that way.
Related basecamp issue: https://3.basecamp.com/5308029/buckets/36789884/card_tables/cards/7590936581
* Fix test typo
Some comparisons return __blank__ values. After recent time series fix,
this blew up when trying to parse __blank__ as a date.
This fixes the issue and adds a test for a period with __blank__ values
Co-authored-by: RobertJoonas <56999674+RobertJoonas@users.noreply.github.com>
Previously, revenue metrics were reported as 0.0$ when imports were included with
APIv2. This is because the metrics were not properly included/forwarded by the
imports query.
Not sure how long this bug has persisted, but eh.
* fix(suggestions): Properly provide suggestions
Previously, if a filter was applied then filter modal suggestions would
_always_ apply the previous suggestion. Now when say applying a `page`
filter suggestions we remove the previous `page` filter.
* Improve readability
* Fix filter ["contains", "visit:country", ["E"]]
This blew up since FixedString columns dont natively support matching functions for some reason
* Dont blow up for 3-letter country codes, instead fail validation
* Allow sending numbers for visit:city along with strings
This simplifies querying as we pass the list of cities as numbers to clients
* use &is_integer/1
* Remove redundancy
* Create a new BreakdownModal component and use it for Entry Pages
* Add search functionality into the new component
* Adjust FilterLink component and use it in BreakdownModal
* pass addSearchFilter fn through props
* pass fn props as useCallback
* add a function doc to BreakdownModal
* refactor: create a Metric class
* Fixup: use Metric class for defining BreakdownModal metrics
* keep revenueAvailable state in the Dashboard component
* move query context into a higher-order component
* fix react key error in BreakdownModal
* use BreakdownModal in PropsModal
* adjust EntryPagesModal to use query context
* fix variable name typo
* fixup: BreakdownModal function doc
* use BreakdownModal in SourcesModal
* use Breakdown modal in ReferrerDrilldownModal
* use BreakdownModal in PagesModal
* use BreakdownModal in ExitPagesModal
* replace ModalTable with LocationsModal and use BreakdownModal in it
* use BreakdownModal in Conversions
* make sure next pages are loaded with 'detailed: true'
* replace loading spinner logic in BreakdownModal
* fix two flaky tests
* unfocus search input element on Escape keyup event
* ignore Escape keyup handling when search disabled
* Review suggestion: remove redundant state
* do not fetch data on every search input change
* use longer variable names
* do not define renderIcon callbacks conditionally
* deconstruct props in function header of BreakdownModal
* refactor searchEnabled being true by default
* Remove a dead method
* Move select_event/session_metrics to within QueryBuilder
* Make a method private
* Move page_regex util around
* Move utc_boundaries helper around
* Fixups for utc_boundaries
* Add legacy notices
* Move Stats.query method around
* include_sentry_replay_info consistently
* Move filters out of select_group_fields
* Collapse conditions under select_group_fields
* Shorten some imported methods
* Use dimension over dim in group_by
* Separate SQL query building from imported.ex
* props.ex -> legacy_dimensions.ex
* Move some query building out of Query.ex
* Remove unneeded method
* put_filter -> add_filter
* Remove some query setters
* Moduledoc
* Split out validations and import tests from query_test
* Move tests around
* Split event:goal tests from query_test
* Remove redundant filters
* Remove dead code
* Split special metrics tests from query_test
* Legacy module
* Move fragments module under Plausible.Stats.SQL
* Introduce select_merge_as macro
This simplifies some select_merge calls
* Simplify select_join_fields
* Remove a needless dynamic
* wrap_select_columns macro
* Move metrics from base.ex to expression.ex
* Move WhereBuilder under Plausible.Stats.SQL
* Moduledoc
* Improved macros
* Wrap more code
* select_merge_as more
* Move defp to the end
* include.time_labels parsing
* include.time_labels in result
Note that the previous implementation of the labels from TimeSeries.ex was broken
* Apply consistent function in imports and timeseries.ex
* Remove boilerplate
* WIP: Limited support for timeseries-with-querybuilder
* time:week dimension
* cleanup: property -> dimension
* Make querying with time series work
* Refactor: Move special metrics (percentage, conversion rate) to own module
* Explicitly format datetimes
* Consistent include_imported in special metrics
* Solve week-related crash
* conversion_rate hacking
* Keep include_imported consistent after splitting the query
* Simplify do_decide_tables
* Handle time dimensions in imports cleaner
* Allow time dimensions in custom property queries
* time:week handling continued
* cast_revenue_metrics_to_money
* fix `full_intervals` support
* Handle minute/realtime graphs
* experimental_session_count? with timeseries
This becomes required as we try to include visits from sessions by default
* Support hourly data in imports
* Update bounce_rate in more csv tests
* Update some time-series query tests
* Fix for meta.warning being included incorrectly
* Simplify imported.ex
* experimental_session_count flag removal
* moduledoc
* Split interval and time modules
* Move fragments module under Plausible.Stats.SQL
* Introduce select_merge_as macro
This simplifies some select_merge calls
* Simplify select_join_fields
* Remove a needless dynamic
* wrap_select_columns macro
* Move metrics from base.ex to expression.ex
* Move WhereBuilder under Plausible.Stats.SQL
* Moduledoc
* Improved macros
* Wrap more code
* select_merge_as more
* Move defp to the end
* wrap_alias
* Refactor and unify auth plugs for Stats and Sites APIs
* Expose get site Sites API endpoint to all API keys
* Test the new plug
* Add test for endpoint with modified scope
* Fix typos
Co-authored-by: hq1 <hq@mtod.org>
* Rename plug for consistency (h/t @aerosol)
---------
Co-authored-by: hq1 <hq@mtod.org>
* Revert "Revert "APIv2: Replace breakdown module with QueryBuilder (#4283)" (#4292)"
This reverts commit ef5e0e0382.
* Allow querying events and pageviews from sessions table
This is not strictly accurate, especially with shorter time frames, but
is useful for a fallback mechanism. I'll figure out something around
shorter time frames in the future.
See also: https://github.com/plausible/analytics/pull/4292
* Only query events and pageviews in legacy breakdowns
* WIP: Breakdown using QueryBuilder
* Revert "Remove problematic test"
This reverts commit b442bb5d1f.
* Get more breakdown tests passing
* Preload goals, sort when dealing with time_on_page
* Handle conversion_rate in breakdowns
* Simplify ordering by using selected_as consistently for dimensions
* Get breakdown tests passing
* Strings to atoms in keys for StatsController.transform_keys calls to work
* Handle revenue metrics removal
* Add test for nil-removal case
* Include percentage metric
* Fix and test with imported locations
* Fixup time-on-page
* Fix country/region automatic filters
* Handle multiple imports (os/browser version) in importsv2
* Filter goals
* Default to ordering by page as well
* Calculate conversion rate on sessions if needed
* Order by event dimensions - handles event:page special case
* Update tests
* Update more tests, handle goal=0 case in imports
* Handle event:goal breakdowns correctly with filters
* Revenue to money
* Improved table deciding
* Also update event:page filters on event:page breakdown
* bounce_rate to 0
Previous behavior relied on two queries being made - new query leads to 0 naturally
* Update pagination test
* dont count non-pageviews as path goal completions
* Make revenue logic breakdown-specific
Its hard to fit into the new schema and likely needs a rethink for apiv2
* Retain previous behavior for TimeSeries module
* Get GA4 test passing
Most failures are related to ordering, pageviews shouldnt be read off of sessions
* Clean up old methods
* Simplify imported.ex
* Dont crash on garbage filters
* Reflect ordering-related change in test
* Fix test data
* Update table_decider
* Re-simplify get_revenue_tracking_currency
* Revert revenue changes
* Use Query.set
* Remove a TODO
* csv importer: no pageviews
Pageviews were incorrectly fetched from sessions table before, causing issues
* csv importer tweaking
* Remove use Plausible
* to_existing_atom