joshmn / ahoy_captain Goto Github PK
View Code? Open in Web Editor NEWA full-featured, mountable analytics dashboard for your Rails app, powered by the Ahoy gem.
License: MIT License
A full-featured, mountable analytics dashboard for your Rails app, powered by the Ahoy gem.
License: MIT License
Hello
Thanks for this amazing project really love it
On RubyVideo I have chosen to explore a very simple stack based on SQLite and the Litestack gem.
As a poor man analytics I have implemented this but would love to replace it by Ahoy_captain. Of course I totally understand that Posgresql is the major player and I might be asking for something exotic.
Just in case you plan to support SQLite at some point I will be more than happy to provide a set or real-world data if that can help.
have a good day
not going to be fun
On my deployment these numbers are always the same even though when they should be different. I've verified the results being different for a given month when comparing these queries:
# @presenter.unique_visitors
SELECT SUM(count_visitor_token) AS total_unique_visitors
FROM (
SELECT COUNT(DISTINCT "ahoy_visits"."visitor_token") AS "count_visitor_token",
DATE_TRUNC('day', "ahoy_visits"."started_at"::timestamptz AT TIME ZONE 'America/New_York')::date AS "date_trunc_day"
FROM "ahoy_visits"
LEFT OUTER JOIN "ahoy_events" ON "ahoy_events"."visit_id" = "ahoy_visits"."id"
WHERE ("ahoy_visits"."started_at" > '2023-11-13 00:01:00' AND "ahoy_visits"."started_at" < '2023-12-13 14:20:00')
AND ("ahoy_events"."time" > '2023-11-13 00:01:00' AND "ahoy_events"."time" < '2023-12-13 14:20:00')
AND ("ahoy_visits"."started_at" IS NOT NULL)
GROUP BY DATE_TRUNC('day', "ahoy_visits"."started_at"::timestamptz AT TIME ZONE 'America/New_York')::date
) AS daily_counts;
vs
# @presenter.total_visits
WITH
"current" AS (
SELECT COUNT(DISTINCT "ahoy_visits"."id")
FROM "ahoy_visits"
LEFT OUTER JOIN "ahoy_events"
ON "ahoy_events"."visit_id" = "ahoy_visits"."id"
WHERE ("ahoy_visits"."started_at" > '2023-11-13 00:01:00'
AND "ahoy_visits"."started_at" < '2023-12-13 14:20:00')
AND ("ahoy_events"."time" > '2023-11-13 00:01:00'
AND "ahoy_events"."time" < '2023-12-13 14:20:00')
),
"compare" AS (
SELECT COUNT(DISTINCT "ahoy_visits"."id")
FROM "ahoy_visits"
LEFT OUTER JOIN "ahoy_events"
ON "ahoy_events"."visit_id" = "ahoy_visits"."id"
WHERE ("ahoy_visits"."started_at" > '2023-10-13 09:42:00'
AND "ahoy_visits"."started_at" < '2023-11-13 00:01:00')
AND ("ahoy_events"."time" > '2023-10-13 09:42:00'
AND "ahoy_events"."time" < '2023-11-13 00:01:00')
)
SELECT
current,
compare
FROM
current,
compare;
I'm having a really hard diagnosing where the problem is in the ViewComponents. At the Stats level the queries look fine which is how I derived the ones above but I got lost in ComparableContainerComponent and ran out of time for this problem today.
Hi I have a rails 7 esbuild app and am trying to get it to work with ahoy_captain
. When I visit /ahoy_captain
, I do not get any results:
I also get this error and other warnings in the console:
I followed the installation instructions to the tee (although, rails g ahoy_captain:migration
does not work).
I also checked that ahoy_visits
table has data
Any help is appreciated!
Just installed the gem, and I do have some events to show:
But the dashboard shows up with some placeholder skeletons, then immediately crashes:
I can circumvent the crash by adding return 0 if value.current.nil? || value.compared_to.nil?
before the diff calculation. Happy to open a PR unless you want the comparable itself to have safer logic.
Fresh installation, with Rails 7.1.2:
undefined local variable or method `javascript_importmap_shim_tag'
I think this is caused by the dependency you have in the .gemspec
liberally accepting any version of importmap-rails
>=1, and DHH has removed the shim
in > 2.0.
I wanted to use config.disabled_widgets in the provided initializer to remove some (to me) unnecessary widgets, but I get an error:
undefined method `disabled_widgets='
Looking at line 6 of configuration.rb it is apparent that :disabled_widgets is only readable, not writable. Changing it to be writable removes the error, but in the actual dashboard I then get a 'content missing' turbo frame error for each of the disabled widgets, instead of the widget being removed, as I would expect.
Currently, seems like folks might want to see something that provides a bit more signal if they have little/no data when first setting things up.
Suggestion: In the event of no data, render a view that gives suggestions for what events should be tracked in order to see them rendered in a given tile
Getting this error when trying to access the dashboard:
undefined method
with_option_content' for #<AhoyCaptain::DropdownLinkComponent:0x00007f25a0495f08 @title="Campaign", @classes=nil, @__vc_original_view_context=#ActionView::Base:0x00000000034bc0, @view_context=#ActionView::Base:0x00000000034bc0, @output_buffer="<div class="dropdown dropdown-end" data-controller='dropdown-label'>\n <label\n tabindex="0"\n class="cursor-pointer flex "\n data-action='click->dropdown-label#removeHidden'\n >\n <span data-dropdown-label-target="label">Campaign\n\n \n <ul class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52" data-dropdown-label-target="close">\n", @lookup_context=#<ActionView::LookupContext:0x00007f25a04e5788 @details_key=#<ActionView::TemplateDetails::Requested:0x00007f25a3b83240 @Locale=[:en], @handlers=[:raw, :erb, :html, :builder, :ruby, :jbuilder], @formats=[:html], @Variants=[], @locale_idx={:en=>0, nil=>1}, @handlers_idx={:raw=>0, :erb=>1, :html=>2, :builder=>3, :ruby=>4, :jbuilder=>5, nil=>6}, @formats_idx={:html=>0, nil=>1}, @variants_idx={nil=>0}>, @digest_cache=nil, @cache=true, @Prefixes=["ahoy_captain/roots", "ahoy_captain/application"], @details={:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}, @view_paths=#<ActionView::PathSet:0x00007f25a04e5508 @paths=[#<ActionView::FileSystemResolver:0x00007f25a36c05f0 @unbound_templates=#<Concurrent::Map:0x00007f25a36c05c8 entries=13 default_proc=nil>, @path_parser=#<ActionView::Resolver::PathParser:0x00007f25a368c6b0 @regex=/
\A
(?:(?.)/)?
(?_)?
(?.?)
(?:.(?[a-z]{2}(?:[-][A-Z]{2})?))??
(?:.(?html|text|js|css|ics|csv|vcf|vtt|png|jpeg|gif|bmp|tiff|svg|mpeg|mp3|ogg|m4a|webm|mp4|otf|ttf|woff|woff2|xml|rss|atom|yaml|multipart_form|url_encoded_form|json|pdf|zip|gzip|turbo_stream))??
(?:+(?[^.]))??
(?:.(?raw|erb|html|builder|ruby|jbuilder))?
\z
/x>, @path="/home/james/src/clubs/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c08e8 @unbound_templates=#<Concurrent::Map:0x00007f25a36c07a8 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c660, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/letter_opener_web-1.4.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c0ac8 @unbound_templates=#<Concurrent::Map:0x00007f25a36c0aa0 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c638, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/bootstrap5-kaminari-views-0.0.1/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c0d70 @unbound_templates=#<Concurrent::Map:0x00007f25a36c0d20 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c610, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/kaminari-core-1.2.2/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1040 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1018 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c5c0, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/paul_revere-3.3.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c12c0 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1298 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c598, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1568 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1518 entries=7 default_proc=nil>, @path_parser=#<ActionView::Resolver::PathParser:0x00007f25a368c570 @regex=/
\A
(?:(?.)/)?
(?)?
(?.?)
(?:.(?[a-z]{2}(?:[-_][A-Z]{2})?))??
(?:.(?html|text|js|css|ics|csv|vcf|vtt|png|jpeg|gif|bmp|tiff|svg|mpeg|mp3|ogg|m4a|webm|mp4|otf|ttf|woff|woff2|xml|rss|atom|yaml|multipart_form|url_encoded_form|json|pdf|zip|gzip|turbo_stream))??
(?:+(?[^.]))??
(?:.(?raw|erb|html|builder|ruby|jbuilder))?
\z
/x>, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/ahoy_captain-1.0.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1748 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1720 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c548, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/view_component-2.82.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c19c8 @unbound_templates=#<Concurrent::Map:0x00007f25a36c19a0 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c4f8, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/turbo-rails-1.4.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1b80 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1b58 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c430, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/active_analytics-0.2.1/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1d60 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1d38 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c408, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/actiontext-7.0.4/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c2080 @unbound_templates=#<Concurrent::Map:0x00007f25a36c2008 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c3e0, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/actionmailbox-7.0.4/app/views">]>>, @view_renderer=#<ActionView::Renderer:0x00007f25a04cb7c0 @lookup_context=#<ActionView::LookupContext:0x00007f25a04e5788 @details_key=#<ActionView::TemplateDetails::Requested:0x00007f25a3b83240 @Locale=[:en], @handlers=[:raw, :erb, :html, :builder, :ruby, :jbuilder], @formats=[:html], @Variants=[], @locale_idx={:en=>0, nil=>1}, @handlers_idx={:raw=>0, :erb=>1, :html=>2, :builder=>3, :ruby=>4, :jbuilder=>5, nil=>6}, @formats_idx={:html=>0, nil=>1}, @variants_idx={nil=>0}>, @digest_cache=nil, @cache=true, @Prefixes=["ahoy_captain/roots", "ahoy_captain/application"], @details={:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :jbuilder]}, @view_paths=#<ActionView::PathSet:0x00007f25a04e5508 @paths=[#<ActionView::FileSystemResolver:0x00007f25a36c05f0 @unbound_templates=#<Concurrent::Map:0x00007f25a36c05c8 entries=13 default_proc=nil>, @path_parser=#<ActionView::Resolver::PathParser:0x00007f25a368c6b0 @regex=/
\A
(?:(?.)/)?
(?_)?
(?.?)
(?:.(?[a-z]{2}(?:[-][A-Z]{2})?))??
(?:.(?html|text|js|css|ics|csv|vcf|vtt|png|jpeg|gif|bmp|tiff|svg|mpeg|mp3|ogg|m4a|webm|mp4|otf|ttf|woff|woff2|xml|rss|atom|yaml|multipart_form|url_encoded_form|json|pdf|zip|gzip|turbo_stream))??
(?:+(?[^.]))??
(?:.(?raw|erb|html|builder|ruby|jbuilder))?
\z
/x>, @path="/home/james/src/clubs/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c08e8 @unbound_templates=#<Concurrent::Map:0x00007f25a36c07a8 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c660, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/letter_opener_web-1.4.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c0ac8 @unbound_templates=#<Concurrent::Map:0x00007f25a36c0aa0 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c638, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/bootstrap5-kaminari-views-0.0.1/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c0d70 @unbound_templates=#<Concurrent::Map:0x00007f25a36c0d20 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c610, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/kaminari-core-1.2.2/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1040 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1018 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c5c0, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/paul_revere-3.3.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c12c0 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1298 entries=7 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c598, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1568 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1518 entries=7 default_proc=nil>, @path_parser=#<ActionView::Resolver::PathParser:0x00007f25a368c570 @regex=/
\A
(?:(?.)/)?
(?)?
(?.?)
(?:.(?[a-z]{2}(?:[-_][A-Z]{2})?))??
(?:.(?html|text|js|css|ics|csv|vcf|vtt|png|jpeg|gif|bmp|tiff|svg|mpeg|mp3|ogg|m4a|webm|mp4|otf|ttf|woff|woff2|xml|rss|atom|yaml|multipart_form|url_encoded_form|json|pdf|zip|gzip|turbo_stream))??
(?:+(?[^.]))??
(?:.(?raw|erb|html|builder|ruby|jbuilder))?
\z
/x>, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/ahoy_captain-1.0.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1748 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1720 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c548, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/view_component-2.82.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c19c8 @unbound_templates=#<Concurrent::Map:0x00007f25a36c19a0 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c4f8, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/turbo-rails-1.4.0/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1b80 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1b58 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c430, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/active_analytics-0.2.1/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c1d60 @unbound_templates=#<Concurrent::Map:0x00007f25a36c1d38 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c408, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/actiontext-7.0.4/app/views">, #<ActionView::FileSystemResolver:0x00007f25a36c2080 @unbound_templates=#<Concurrent::Map:0x00007f25a36c2008 entries=4 default_proc=nil>, @path_parser=#ActionView::Resolver::PathParser:0x00007f25a368c3e0, @path="/home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/actionmailbox-7.0.4/app/views">]>>, @cache_hits={}>, @view_flow=#<ActionView::OutputFlow:0x00007f25a04cb680 @content={}>, @virtual_path="/ahoy_captain/dropdown_link_component", @__vc_variant=nil, @current_template=nil, @__vc_content_evaluated=true, @__vc_render_in_block=#<Proc:0x00007f25a0495e40 /home/james/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/ahoy_captain-1.0.0/app/views/ahoy_captain/roots/show.html.erb:50>>`
Ruby 3.1.0
Any chance you're planning to add support MySQL databases?
I played around a bit and it's close to working out of the box except the JSONB_EXISTS
and jsonb_object_keys
methods which are Postgres specific. I have a JSON column in my database and apart from the missing methods, it seems like it could work. MySQL 8 introduced the JSON_CONTAINS_PATH
method and JSON_EXTRACT
has been available since v5. Either of those will work to check if a key exists. And the JSON_KEYS
function allows for access to the key value store.
Looks like support for MySQL databases would be possible, but it would require a bunch of refactoring to allow for choosing different database adapters. I'll tinker around a bit more myself to see if I can get a fork working for my application, but it would be great to know if others are doing anything similar!
This project looks really cool, thanks for putting it out there!
The ahoy_captain README just points to the section in the Ahoy documents that show:
class ApplicationController < ActionController::Base
after_action :track_action
protected
def track_action
ahoy.track "Ran action", request.path_parameters
end
end
What's a properly working example configuration for capturing events they way they have been in the screenshot? Are we supposed to literally track an event with the name "$view", or is that supposed to be a substitution example?
AhoyCaptain.configure
should be
AhoyCaptain.configure do |config|
Docs imply that I should run a generator to add additional indexes, but this task does not exist.
We do not use the turbo
gem in our application. We do render some templates in background jobs. After installing the ahoy_captain
gem those jobs get errors like this:
NoMethodError:
Could not render layout: undefined method `headers' for nil:NilClass
# /usr/local/bundle/gems/turbo-rails-1.4.0/app/controllers/turbo/frames/frame_request.rb:34:in `turbo_frame_request_id'
# /usr/local/bundle/gems/turbo-rails-1.4.0/app/controllers/turbo/frames/frame_request.rb:30:in `turbo_frame_request?'
# /usr/local/bundle/gems/turbo-rails-1.4.0/app/controllers/turbo/frames/frame_request.rb:24:in `block (2 levels) in <module:FrameRequest>'
# ./app/sidekiq/add_card_to_stage.rb:11:in `perform'
I'm not clear on why adding the ahoy_captain gem causes turbo
gem code to be called outside of the engine itself.
Hi,
I just noticed an issue with the overview:
As you can see the dates go from 29th of March, 31st of March, 2nd of April and then jump back to the 4th of March and continue from there.
The view is set to 30 days and I am using my own fork to have the shim fixes from here #40 and the latest ahoy_captain code.
The fork is here:
https://github.com/abuisman/ahoy_captain
I can't get the forkless version working because of the #40 issues and I am experiencing these issues: #36
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.