* local CSV exports/imports and S3 updates
* credo
* dialyzer
* refactor input columns
* fix ci minio/clickhouse tests
* Update lib/plausible_web/live/csv_export.ex
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
* fix date range filter in export_pages_q and process only pageviews
* remove toTimeZone(zero_timestamp) note
* use SiteImport.pending(), SiteImport.importing()
* escape [SiteImport.pending(), SiteImport.importing()]
* use random s3 keys for imports to avoid collisions (sometimes makes the upload get stuck)
* clamp import date ranges
* site is already in assigns
* recompute cutoff date each time
* use toDate(timestamp[, timezone]) shortcut
* show alreats on export cancel/delete and extract hint into a component
* switch to Imported.clamp_dates/4
* reprocess tables when imports are added
* recompute cutoff_date on each call
* actually use clamped_date_range on submit
* add warning message
* add expiry rules to buckets in make minio
* add site_id to imports notifications and use it in csv_importer
* try/catch safer
* return :ok
* date range is not available when no uploads
* improve ui and warning messages
* use Generic.notice
* fix flaky exports test
* begin tests
* Improve `Importer` notification payload shape
---------
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
* Update request logging
Ultimate goal is to be able to compare results with and without a flag against each other.
To do this we need logging which displays the full request url with parameters.
Example logs:
```
14:46:09.042 request_id=F8MRLSsaKB7BeIkAAAHk [info] (200) GET /api/sites took 17ms
14:46:09.175 request_id=F8MRLTKYV3G-GqEAAAZB [info] (200) GET /api/stats/dummy.site/current-visitors took 24ms
14:46:09.396 request_id=F8MRLUDfav28LIkAAAIE [info] (202) POST /api/event took 5ms
14:46:09.501 request_id=F8MRLUDS_YhftUkAAAAD [info] (200) GET /api/stats/dummy.site/sources?period=30d&date=2024-04-04&filters=%7B%7D&with_imported=true&limit=9 took 111ms
14:46:09.508 request_id=F8MRLUDhHbK8WKUAAAah [info] (200) GET /api/stats/dummy.site/main-graph?period=30d&date=2024-04-04&filters=%7B%7D&with_imported=true&metric=visitors took 117ms
14:46:09.511 request_id=F8MRLUDS1CYntK4AAAaB [info] (200) GET /api/stats/dummy.site/entry-pages?period=30d&date=2024-04-04&filters=%7B%7D&with_imported=true&limit=9 took 121ms
14:46:09.541 request_id=F8MRLTk5sIPYSn4AABoC [info] (200) GET /api/stats/dummy.site/top-stats?period=30d&date=2024-04-04&filters=%7B%7D&with_imported=true&comparison=previous_period&compare_from=undefined&compare_to=undefined&match_day_of_week=true took 278ms
```
* re-add plug
* router_dispatch -> endpoint
* Always scope import ID by site as well
* Do not schedule new import job if there are any site imports in progress
* Disable import buttons when any import is in progress
* Simplify `schedule_job/4` (h/t @RobertJoonas)
changing the copy here as I think that in some situations the "we're still counting stats" message is now shown even to those dashboards where we've stopped counting stats so best to avoid that
* Give a more semantic name to a function
* Make the LineGraph component thinner
* Move LineGraph into a separate file
* Move interval logic into interval-picker.js
This commit also fixes a bug where the interval name displayed inside
the picker component flickers the default interval when the graph is
loading.
The problem was that we were counting on graphData for returning us the
current interval: `let currentInterval = graphData?.interval`
We should always know the default interval before making the main-graph
request. Sending graphData to IntervalPicker component does not make
sense anyway.
* extract data fetching functions out of VisitorGraph component
* Return graph_metric key from Top Stats API
This commit introduces no behavioral changes - only starts returning an
additional field, allowing us to avoid the following logic in React:
1. Finding the metric names, given a stat display name. E.g.
`Unique visitors (last 30 min) -> visitors`
2. Checking if a metric is graphable or not
* Move metric state into localStorage
This commit gets rid of the internal `metric` state in the VisitorGraph
component and starts using localStorage for that instead.
This commit also chains the main-graph request into the top-stats request
callback - meaning that we'll always fetch new graph data after top stats
are updated. And we do it all in a single function.
Doing so simplifies the loading state significantly, and also helps to
make it clear, that at all times, existing top stats are required before
we can fetch the graph. That's because the metric is determined by which
Top stats are returned (for example, we can't be sure whether revenue
metrics will be returned or not).
* Make sure graph tooltip says "Converted Visitors"
* Extract a StatsExport function component
Again, instead of relying on `graphData?.interval` we can read it from
localStorage, or default to the largest interval available. The export
should not be dependant on the graph.
* Extract SamplingNotice function component
* Extract WithImportedSwitch function component
* Stop "lazy-loading" the graph and top stats
Since the container is always on top on the page, it will be visible on
the first render in any case - no matter the screen size.
* Turn VisitorGraph into a function component
* Display empty container until everything has loaded
* Do not display loading spinner on realtime ticks
* Turn Top Stats into a fn component
* fetch top stats and graph async
* Make sure revenue metrics can remain on the graph
* Add an extra check to canMetricBeGraphed
* fix typo
* remove redundant double negation
* CH Migration: exit/entry hostnames in sessions_v2
* Leave only exit_page_hostname, we already record hostnames
* Use ClickHouse DDL in favour of ecto so that cluster is included
* Compress with ZSTD(3)
* Expose Hostname filter in the dashboard dropdown
* Add `exit_page_hostname` to ClickHouse `sessions_v2` schema
* Start tracking hostname changes in sessions
* Implement hostname filter suggestions
* Enable filtering by `event:hostname`
* Add tests for filtering by hostnames
* Ensure filter suggestions work for exit pages too
* Allow overriding hostnames with `send_pageview` mix task
* Remove `:window_time_on_page` flag
It seems that we can remove it after all?
* Initialize `experimental_hostname_filter` query parameter
* Rewrite cache store behaviour with regards to session hostnames
* Work around inconsistent session merging
So that `populate_stats` can get closer to actual ingestion
* Improve top stats test
* Make it possible to filter sessions by entry/exit hostnames
* Update pages tests
* Expose `experimental_hostname_filtering` temporarily in the UI
* Untested yet: also apply experimental filtering to sources
* Introduce `hostname_filter` feature flag
* Format
* Test top sources with hostname filter + experimental flag
* Check import presence across all imports and not just the first one
Also, simplify imported data toggle rendering to not explicitly
refer to the earliest import source.
* Change imported stats toggle icon in dashboard
* Test `Imported.get_imports_date_range/1`
* Simplify failed UA/GA import email copy
* Always select and clear import ID 0 when referring to legacy imports
* Implement script for adding site import entries and adjusting end dates
* Log cases where end date computation is using fallback
* Don't log queries when running the migration to reduce noise
* Implement adjusting imported date range to actual and existing stats
* Drop redundant prefix from import list entries
* Make pageview numbers in imports list formatted for readability
* Test and improve date range cropping
* DRY UA and GA4 stats start and end date API calls
* Extend UA/GA import controller tests and improve error handling
* refactor finding longest open range without existing data
* Fix typo in test description
Co-authored-by: RobertJoonas <56999674+RobertJoonas@users.noreply.github.com>
* Rename `open_ranges` to `free_ranges`
---------
Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>
Co-authored-by: RobertJoonas <56999674+RobertJoonas@users.noreply.github.com>
* Use sessionStorage for offer e-mail report banner tracking
Keeping it within the cookie is problematic, as the banners don't
expire and overflow the cookie with data when enough new sites
are added.
Ref https://github.com/plausible/analytics/issues/3762
* Update changelog
* Extract a component
* Make is_dbip evaluate to quoted boolean
* Add validation for the events metric in main_graph
* Test the already existing events metric support in main-graph
* Put total conversions on the graph
* extract main_graph_csv function (refactor only)
* add total_conversions and conversion_rate to goal-filtered visitors.csv
* update changelog
* add conversion rate to Stats API timeseries
* make sure CR can be queried as the only metric
* add a test asserting zeros are returned
* add tests for filtering by other properties at the same time
* Remove unnecessary validation of params
1. It doesn't make to validate `interval` (and its granularity) in all
endpoints. It's only relevant for the main graph.
2. The plug (renamed to `date_validation_plug`) already makes sure that
the dates are validated. No need to call the same function again in
Top Stats and Funnel endpoints.
* add metric validation to main graph
* Add tests for main graph API
* put conversion rate on the graph
* update changelog
* Add revenue metrics into metrics.ex
* make fn private
* avoid setting graph metric to visitors in goal-filtered view
* Unify GA4 and UA import flow into one
* Clean up property and view data retrieval via Google HTTP APIs
* Turn `Map.get` into `Map.fetch!` in API response processing code
* Bump list account summaries page size limit to max of 200
* Show only views in legacy flow and fix legacy redirect after import start
* Move google analytics import actions tests to a separate module
* Extend Google Analytics controller tests
* DRY up `property?` predicate (h/t @RobertJoonas)
* ui
* fix redirect link
* improve make minio
* use implicit button form for csv export
* add exports_bucket helper
* read S3_EXPORTS_BUCKET
* supply s3_bucket in export_csv job args
* make plausible_minio use unprotected port
* move s3_csv_export queue to base queues
* Update lib/plausible_web/controllers/site_controller.ex
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
---------
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
* Implement LV date input using flatpickr
* Implement basics of GA4 import (very dirty WIP)
* Split Google HTTP API into UA and GA4 specific parts
* Add a quick way to record GA4 API responses
* Add first GA4 import fixtures with GA4 Data API responses
* Extract GA4 and UA specific logic form Google API
* Extract UA and GA4 specific actions to distinct controllers
* Add integration test for GA4 importer
* Update GA4 fixtures
* Test GA4 API
* Add debug logging and fix paginating through API results in in GA4 import
* Revert "Implement LV date input using flatpickr"
This reverts commit c696f8ee39d5702f27015c09a4f079ca124cc7bb.
* Fix note
* add metric validation + support in aggregate
* add a test ensuring comparison works
* disallow time_on_page with a goal filter
* Return time_on_page as `nil` from aggregate API
In case time_on_page cannot be calculated, we'll return it as `nil` from
the Stats API.
This is to make the behaviour consistent between breakdown and aggregate
endpoints. As for the UI, we'll still continue to report time_on_page as
0 - not changing any UI behaviour as discussed with Marko.
* add tests for time_on_page in event:page breakdown
* update changelog
* invalidate time_on_page with event:name filter
* add the ability to only query time_on_page in page breakdown
We'll need the visitors metric to get the list of pages to calculate the
time_on_page for.
* changes to the in-app footer
changes to the in-app footer to focus on product usage links rather than marketing pages
* Format
---------
Co-authored-by: Marko Saric <34340819+metmarkosaric@users.noreply.github.com>
* Remove `add_percentage`, calculate percentages in clickhouse queries
This simplifies querying logic and avoids doing extra queries and avoids
race conditions.
* Remove special none handling from breakdowns, handling percentages correctly
* Add (failing) test showing expected add_percentage behavior for user making multiple sessions
* Update add_percentage behavior to use separate subqueries
* Create a stub of site settings section for imports and exports
* Use legacy site import indication to determine UA import handling
* Add provisional logos for upcoming import sources
* Stub basics of import page
* Add very rudimentary support for multiple UA imports
* Implement imports list as live view
* Add support for opening LV modal from backend and closing from frontend
* Introduce notion of themes to `button` and `button_link` components
* Add confirmation modal on deleting import
* Swap GA4 logo
* Implement disabled state support for `button_link` component
* Disable export and non-implemented import sources
* Use native starts start date for upper boundary of import time range
* Ensure integrations view uses legacy UA import flow
* Remove unnecessary preload in SiteController
* Remove unnecessary exception for legacy imports
* Move API controller stats tests under PlausibleWeb
* Test listing imports
* Add test for explicit listener setup
* Add tests for legacy flag state in UA importer
* Add test for purging legacy import data
* Add tests for `Sites.native_stats_start_date`
* Test forgetting imports
* Add `Stats.Clickhouse.imported_pageview_counts/1` and fix test flakiness
* Show page view counts on imports list
* Add tests for static imports and exports view
* Adjust button look slightly
* Use `case` instead of `cond`
* Make feature flag customisable per site
* Fix buttons and empty state styling
* Add another import to seeds
* Use JS confirm dialog instead of modal for deletion confirmations
* Revert "Add support for opening LV modal from backend and closing from frontend"
This reverts commit 260e6c753032b451542e24be9edc2118790b5a00.
* Default `legacy` to false when inserting new import jobs
* Drop `method` attribute from `button_link` and `unstyled_link` components
* Migration: add country rules
* Add CountryRule schema
* Implement CountryRule cache
* Add country rules context interface
* Start country rules cache
* Lookup country rules on ingestion
* Remove :shields feature flag from test helpers
* Add nested sidebar menu for Shields
* Fix typo
* IP Rules: hide description on mobile view
* Prepare SiteController to handle multiple shield types
* Seed some country shield
* Implement LV for country rules
* Remove "YOU" indicator from country rules
* Fix small build
* Format
* Update typespecs
* Make docs link point at /countries
* Fix flash on top of modal for Safari
* Build the rule struct with site_id provided up-front
* Clarify why we're messaging the ComboBox component
* Re-open combobox suggestions after pressing Escape
* Update changelog
* Fix font size in country table cells
* Pass `added_by` via rule add options
* Display site's timezone timestamps in rule tooltips
* Display formatted timestamps in site's timezone
And simplify+test Timezone module; an input timestamp converted
to UTC can never be ambiguous.
* Remove no-op atom
* Display the maximum number of rules when reached
* Improve readability of remove button tests
* Credo
---------
Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
* Replace footer text
* add COPYING.txt file
* Add new logos
* Use new logos in all layouts
* New logos
* Check license key on startup
* Bypass license check when Mix.env == :dev
* Use new logos with smaller wordmarks
* Add generic logo_path/1 function
* Use new favicons everywhere
* Bypass license check in test env
* Use sha256 for license key hash
* Mix.env -> config_env()
* Use Mix.evn at compile time rather than runtime
* Mix format
---------
Co-authored-by: Uku Taht <uku.taht@gmail.com>
* improve test
* add os to os_version breakdown
...and add operating_system_versions.csv to the CSV export
* fix conversion rate for os_version breakdown
* update changelog
* fix existing CSV tests
* use case instead of cond