lookout-ahoy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +99 -0
- data/Rakefile +24 -0
- data/app/assets/images/lookout/apple-touch-icon.png +0 -0
- data/app/assets/images/lookout/favicon-16x16.png +0 -0
- data/app/assets/images/lookout/favicon-32x32.png +0 -0
- data/app/assets/images/lookout/logo.png +0 -0
- data/app/assets/images/lookout/safari-pinned-tab.png +0 -0
- data/app/assets/images/lookout/safari-pinned-tab.svg +199 -0
- data/app/assets/javascript/lookout/application.js +2 -0
- data/app/assets/javascript/lookout/controllers/application.js +9 -0
- data/app/assets/javascript/lookout/controllers/application_controller.js +33 -0
- data/app/assets/javascript/lookout/controllers/combobox_controller.js +371 -0
- data/app/assets/javascript/lookout/controllers/details_modal_controller.js +18 -0
- data/app/assets/javascript/lookout/controllers/dropdown_label_controller.js +39 -0
- data/app/assets/javascript/lookout/controllers/filter/item_controller.js +12 -0
- data/app/assets/javascript/lookout/controllers/filter_form_controller.js +13 -0
- data/app/assets/javascript/lookout/controllers/filter_modal_controller.js +45 -0
- data/app/assets/javascript/lookout/controllers/frame_link_controller.js +20 -0
- data/app/assets/javascript/lookout/controllers/funnel_chart_controller.js +159 -0
- data/app/assets/javascript/lookout/controllers/index.js +4 -0
- data/app/assets/javascript/lookout/controllers/interval_controller.js +15 -0
- data/app/assets/javascript/lookout/controllers/line_chart_controller.js +251 -0
- data/app/assets/javascript/lookout/controllers/predicate_select_controller.js +10 -0
- data/app/assets/javascript/lookout/controllers/properties_controller.js +8 -0
- data/app/assets/javascript/lookout/controllers/property_filter_controller.js +45 -0
- data/app/assets/javascript/lookout/controllers/realtime_controller.js +30 -0
- data/app/assets/javascript/lookout/controllers/sparkline_controller.js +64 -0
- data/app/assets/javascript/lookout/controllers/tile_controller.js +33 -0
- data/app/assets/javascript/lookout/controllers/toggle_controller.js +17 -0
- data/app/assets/javascript/lookout/helpers/chart_utils.js +156 -0
- data/app/assets/javascript/lookout/helpers/countries.js +2261 -0
- data/app/assets/javascript/lookout/helpers/number_formatters.js +55 -0
- data/app/assets/manifest/lookout/manifest.js +2 -0
- data/app/components/lookout/combobox_component.html.erb +33 -0
- data/app/components/lookout/combobox_component.rb +13 -0
- data/app/components/lookout/comparison_link_component.html.erb +17 -0
- data/app/components/lookout/comparison_link_component.rb +44 -0
- data/app/components/lookout/dropdown_button_component.html.erb +16 -0
- data/app/components/lookout/dropdown_button_component.rb +14 -0
- data/app/components/lookout/dropdown_link_component.html.erb +17 -0
- data/app/components/lookout/dropdown_link_component.rb +19 -0
- data/app/components/lookout/filter/dropdown_component.html.erb +50 -0
- data/app/components/lookout/filter/dropdown_component.rb +51 -0
- data/app/components/lookout/filter/modal_component.html.erb +16 -0
- data/app/components/lookout/filter/modal_component.rb +13 -0
- data/app/components/lookout/filter/select_component.html.erb +25 -0
- data/app/components/lookout/filter/select_component.rb +64 -0
- data/app/components/lookout/filter/tag_component.html.erb +13 -0
- data/app/components/lookout/filter/tag_component.rb +14 -0
- data/app/components/lookout/filter/tag_container_component.html.erb +4 -0
- data/app/components/lookout/filter/tag_container_component.rb +6 -0
- data/app/components/lookout/previous_next_component.html.erb +8 -0
- data/app/components/lookout/previous_next_component.rb +11 -0
- data/app/components/lookout/stats/comparable_container_component.html.erb +25 -0
- data/app/components/lookout/stats/comparable_container_component.rb +103 -0
- data/app/components/lookout/stats/container_component.html.erb +23 -0
- data/app/components/lookout/stats/container_component.rb +28 -0
- data/app/components/lookout/sticky_nav_component.html.erb +32 -0
- data/app/components/lookout/sticky_nav_component.rb +24 -0
- data/app/components/lookout/table_component.html.erb +16 -0
- data/app/components/lookout/table_component.rb +48 -0
- data/app/components/lookout/tables/devices_table_component.rb +11 -0
- data/app/components/lookout/tables/dynamic_table.rb +13 -0
- data/app/components/lookout/tables/dynamic_table_component.rb +207 -0
- data/app/components/lookout/tables/goals_table_component.rb +17 -0
- data/app/components/lookout/tables/header_component.html.erb +6 -0
- data/app/components/lookout/tables/header_component.rb +18 -0
- data/app/components/lookout/tables/headers/header_component.html.erb +5 -0
- data/app/components/lookout/tables/headers/header_component.rb +16 -0
- data/app/components/lookout/tables/properties_table_component.rb +27 -0
- data/app/components/lookout/tables/row_component.html.erb +4 -0
- data/app/components/lookout/tables/rows/row_component.html.erb +6 -0
- data/app/components/lookout/tables/rows/row_component.rb +40 -0
- data/app/components/lookout/tile_component.html.erb +24 -0
- data/app/components/lookout/tile_component.rb +24 -0
- data/app/components/lookout/tooltip_component.html.erb +3 -0
- data/app/components/lookout/tooltip_component.rb +18 -0
- data/app/controllers/lookout/application_controller.rb +83 -0
- data/app/controllers/lookout/campaigns_controller.rb +19 -0
- data/app/controllers/lookout/devices_controller.rb +20 -0
- data/app/controllers/lookout/entry_pages_controller.rb +19 -0
- data/app/controllers/lookout/exit_pages_controller.rb +19 -0
- data/app/controllers/lookout/exports_controller.rb +14 -0
- data/app/controllers/lookout/filters/base_controller.rb +15 -0
- data/app/controllers/lookout/filters/goals_controller.rb +9 -0
- data/app/controllers/lookout/filters/locations_controller.rb +11 -0
- data/app/controllers/lookout/filters/operating_systems/names_controller.rb +13 -0
- data/app/controllers/lookout/filters/operating_systems/versions_controller.rb +13 -0
- data/app/controllers/lookout/filters/pages/actions_controller.rb +13 -0
- data/app/controllers/lookout/filters/pages/entry_pages_controller.rb +14 -0
- data/app/controllers/lookout/filters/pages/exit_pages_controller.rb +15 -0
- data/app/controllers/lookout/filters/properties/names_controller.rb +29 -0
- data/app/controllers/lookout/filters/properties/values_controller.rb +15 -0
- data/app/controllers/lookout/filters/screens_controller.rb +11 -0
- data/app/controllers/lookout/filters/sources_controller.rb +11 -0
- data/app/controllers/lookout/filters/utms_controller.rb +10 -0
- data/app/controllers/lookout/funnels_controller.rb +8 -0
- data/app/controllers/lookout/goals_controller.rb +7 -0
- data/app/controllers/lookout/locations/cities_controller.rb +22 -0
- data/app/controllers/lookout/locations/countries_controller.rb +22 -0
- data/app/controllers/lookout/locations/maps_controller.rb +24 -0
- data/app/controllers/lookout/locations/regions_controller.rb +22 -0
- data/app/controllers/lookout/properties_controller.rb +73 -0
- data/app/controllers/lookout/realtimes_controller.rb +7 -0
- data/app/controllers/lookout/roots_controller.rb +6 -0
- data/app/controllers/lookout/sources_controller.rb +21 -0
- data/app/controllers/lookout/stats/base_controller.rb +148 -0
- data/app/controllers/lookout/stats/bounce_rates_controller.rb +12 -0
- data/app/controllers/lookout/stats/total_pageviews_controller.rb +10 -0
- data/app/controllers/lookout/stats/total_visits_controller.rb +10 -0
- data/app/controllers/lookout/stats/unique_visitors_controller.rb +11 -0
- data/app/controllers/lookout/stats/views_per_visits_controller.rb +11 -0
- data/app/controllers/lookout/stats/visit_durations_controller.rb +10 -0
- data/app/controllers/lookout/stats_controller.rb +7 -0
- data/app/controllers/lookout/top_pages_controller.rb +20 -0
- data/app/decorators/lookout/application_decorator.rb +58 -0
- data/app/decorators/lookout/campaign_decorator.rb +27 -0
- data/app/decorators/lookout/city_decorator.rb +24 -0
- data/app/decorators/lookout/country_decorator.rb +38 -0
- data/app/decorators/lookout/device_decorator.rb +27 -0
- data/app/decorators/lookout/entry_page_decorator.rb +7 -0
- data/app/decorators/lookout/exit_page_decorator.rb +7 -0
- data/app/decorators/lookout/page_decorator.rb +27 -0
- data/app/decorators/lookout/region_decorator.rb +28 -0
- data/app/decorators/lookout/source_decorator.rb +27 -0
- data/app/decorators/lookout/top_page_decorator.rb +7 -0
- data/app/helpers/lookout/application_helper.rb +124 -0
- data/app/models/concerns/lookout/compare_mode.rb +19 -0
- data/app/models/concerns/lookout/limitable.rb +17 -0
- data/app/models/concerns/lookout/range_options.rb +8 -0
- data/app/models/lookout/comparison_mode.rb +72 -0
- data/app/models/lookout/export.rb +48 -0
- data/app/models/lookout/filter_parser.rb +82 -0
- data/app/models/lookout/range_from_params.rb +78 -0
- data/app/models/lookout/rangeable.rb +7 -0
- data/app/models/lookout/widget.rb +15 -0
- data/app/presenters/lookout/dashboard_presenter.rb +53 -0
- data/app/presenters/lookout/funnel_presenter.rb +75 -0
- data/app/presenters/lookout/goals_presenter.rb +72 -0
- data/app/queries/concerns/lookout/comparable_queries.rb +25 -0
- data/app/queries/concerns/lookout/comparable_query.rb +152 -0
- data/app/queries/concerns/lookout/lazy_comparable_query.rb +42 -0
- data/app/queries/lookout/application_query.rb +186 -0
- data/app/queries/lookout/campaign_query.rb +14 -0
- data/app/queries/lookout/city_query.rb +14 -0
- data/app/queries/lookout/country_query.rb +10 -0
- data/app/queries/lookout/device_query.rb +10 -0
- data/app/queries/lookout/entry_pages_query.rb +18 -0
- data/app/queries/lookout/event_query.rb +42 -0
- data/app/queries/lookout/exit_pages_query.rb +19 -0
- data/app/queries/lookout/region_query.rb +14 -0
- data/app/queries/lookout/source_query.rb +11 -0
- data/app/queries/lookout/stats/average_views_per_visit_query.rb +20 -0
- data/app/queries/lookout/stats/average_visit_duration_query.rb +34 -0
- data/app/queries/lookout/stats/base_query.rb +18 -0
- data/app/queries/lookout/stats/bounce_rates_query.rb +33 -0
- data/app/queries/lookout/stats/total_pageviews_query.rb +9 -0
- data/app/queries/lookout/stats/total_visitors_query.rb +9 -0
- data/app/queries/lookout/stats/unique_visitors_query.rb +9 -0
- data/app/queries/lookout/stats/views_per_visit_query.rb +17 -0
- data/app/queries/lookout/stats/visit_duration_query.rb +19 -0
- data/app/queries/lookout/top_page_query.rb +13 -0
- data/app/queries/lookout/visit_query.rb +42 -0
- data/app/views/lookout/campaigns/index.html+details.erb +4 -0
- data/app/views/lookout/campaigns/index.html.erb +3 -0
- data/app/views/lookout/devices/_table.html.erb +2 -0
- data/app/views/lookout/devices/index.html+details.erb +4 -0
- data/app/views/lookout/devices/index.html.erb +3 -0
- data/app/views/lookout/entry_pages/index.html+details.erb +4 -0
- data/app/views/lookout/entry_pages/index.html.erb +3 -0
- data/app/views/lookout/exit_pages/index.html+details.erb +4 -0
- data/app/views/lookout/exit_pages/index.html.erb +3 -0
- data/app/views/lookout/funnels/index.html.erb +7 -0
- data/app/views/lookout/funnels/show.html.erb +15 -0
- data/app/views/lookout/goals/index.html.erb +4 -0
- data/app/views/lookout/layouts/application.html.erb +144 -0
- data/app/views/lookout/layouts/shared/_tile_loader.html.erb +5 -0
- data/app/views/lookout/layouts/shared/_widget_disabled.html+details.erb +3 -0
- data/app/views/lookout/layouts/shared/_widget_disabled.html.erb +3 -0
- data/app/views/lookout/locations/cities/index.html+details.erb +4 -0
- data/app/views/lookout/locations/cities/index.html.erb +3 -0
- data/app/views/lookout/locations/countries/index.html+details.erb +5 -0
- data/app/views/lookout/locations/countries/index.html.erb +3 -0
- data/app/views/lookout/locations/maps/_simple_map.html.erb +26 -0
- data/app/views/lookout/locations/maps/show.html.erb +106 -0
- data/app/views/lookout/locations/regions/index.html+details.erb +4 -0
- data/app/views/lookout/locations/regions/index.html.erb +3 -0
- data/app/views/lookout/properties/_form.html.erb +6 -0
- data/app/views/lookout/properties/index.html.erb +3 -0
- data/app/views/lookout/properties/show.html.erb +6 -0
- data/app/views/lookout/realtimes/show.html.erb +9 -0
- data/app/views/lookout/roots/_filters.html.erb +80 -0
- data/app/views/lookout/roots/show.html.erb +191 -0
- data/app/views/lookout/sources/index.html+details.erb +4 -0
- data/app/views/lookout/sources/index.html.erb +3 -0
- data/app/views/lookout/stats/base/index.html.erb +40 -0
- data/app/views/lookout/stats/show.html.erb +15 -0
- data/app/views/lookout/top_pages/index.html+details.erb +4 -0
- data/app/views/lookout/top_pages/index.html.erb +3 -0
- data/config/routes.rb +69 -0
- data/lib/generators/lookout/install_generator.rb +31 -0
- data/lib/generators/lookout/migration_generator.rb +21 -0
- data/lib/generators/lookout/templates/config.rb.tt +185 -0
- data/lib/generators/lookout/templates/migration.rb.tt +7 -0
- data/lib/lookout/active_record.rb +108 -0
- data/lib/lookout/ahoy/event_methods.rb +75 -0
- data/lib/lookout/ahoy/visit_methods.rb +24 -0
- data/lib/lookout/configuration.rb +58 -0
- data/lib/lookout/database_adapter.rb +168 -0
- data/lib/lookout/engine.rb +47 -0
- data/lib/lookout/filter_configuration/filter.rb +16 -0
- data/lib/lookout/filter_configuration/filter_collection.rb +48 -0
- data/lib/lookout/filters_configuration.rb +77 -0
- data/lib/lookout/funnels.rb +44 -0
- data/lib/lookout/goals.rb +51 -0
- data/lib/lookout/period_collection.rb +115 -0
- data/lib/lookout/predicate_label.rb +7 -0
- data/lib/lookout/railtie.rb +9 -0
- data/lib/lookout/version.rb +3 -0
- data/lib/lookout.rb +78 -0
- metadata +673 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class ApplicationController < ActionController::Base
|
|
3
|
+
include Pagy::Backend
|
|
4
|
+
include CompareMode
|
|
5
|
+
include RangeOptions
|
|
6
|
+
include Rangeable
|
|
7
|
+
|
|
8
|
+
layout 'lookout/layouts/application'
|
|
9
|
+
|
|
10
|
+
def period
|
|
11
|
+
params[:period] || Lookout.config.ranges.default
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# show the details frame
|
|
15
|
+
before_action :use_details_frame
|
|
16
|
+
|
|
17
|
+
# act like an spa without being an spa
|
|
18
|
+
before_action :act_like_an_spa
|
|
19
|
+
|
|
20
|
+
rescue_from Widget::WidgetDisabled do |e|
|
|
21
|
+
respond_to do |f|
|
|
22
|
+
f.turbo_stream do
|
|
23
|
+
render(partial: "lookout/layouts/shared/widget_disabled", locals: { frame: e.frame })
|
|
24
|
+
end
|
|
25
|
+
f.html do
|
|
26
|
+
render(partial: "lookout/layouts/shared/widget_disabled", locals: { frame: e.frame })
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def use_details_frame
|
|
34
|
+
if request.headers['Turbo-Frame'] == 'details'
|
|
35
|
+
request.variant = :details
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def act_like_an_spa
|
|
40
|
+
if request.format.html? && request.headers['Turbo-Frame'].blank?
|
|
41
|
+
if request.path != root_path
|
|
42
|
+
requested_params = Rails.application.routes.recognize_path(request.path).except(:controller, :action)
|
|
43
|
+
params.merge!(requested_params)
|
|
44
|
+
unless params[:debug]
|
|
45
|
+
render template: 'lookout/roots/show'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def visit_query
|
|
52
|
+
VisitQuery.call(params)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def event_query
|
|
56
|
+
EventQuery.call(params)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Only paginate details requests requests
|
|
60
|
+
def paginate(collection)
|
|
61
|
+
if paginate?
|
|
62
|
+
pagy, results = pagy(collection, page: params[:page])
|
|
63
|
+
@pagination = pagy
|
|
64
|
+
return results
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
collection
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def paginate?
|
|
71
|
+
request.variant.include?(:details)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def cached(*names)
|
|
75
|
+
if Lookout.cache.class == ActiveSupport::Cache::NullStore
|
|
76
|
+
return yield
|
|
77
|
+
end
|
|
78
|
+
Lookout.cache.fetch("lookout:#{names.join(":")}:#{request.query_parameters.sort.map { |k,v| "#{k}-#{v}" }.join(":")}", expire_in: Lookout.config.cache[:ttl]) do
|
|
79
|
+
yield
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class CampaignsController < ApplicationController
|
|
3
|
+
include Limitable
|
|
4
|
+
|
|
5
|
+
before_action do
|
|
6
|
+
if Widget.disabled?(:campaigns, params[:campaigns_type])
|
|
7
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :sources)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
results = cached(:campaigns, params[:campaigns_type]) do
|
|
13
|
+
CampaignQuery.call(params).limit(limit)
|
|
14
|
+
end
|
|
15
|
+
@campaigns = paginate(results).map { |campaign| CampaignDecorator.new(campaign, self) }
|
|
16
|
+
@campaign_type = params[:campaigns_type]&.titleize&.gsub("Utm", "UTM")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class DevicesController < ApplicationController
|
|
3
|
+
include Limitable
|
|
4
|
+
|
|
5
|
+
before_action do
|
|
6
|
+
if Widget.disabled?(:devices, params[:devices_type])
|
|
7
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :devices)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
results = cached(:devices, params[:devices_type]) do
|
|
13
|
+
DeviceQuery.call(params)
|
|
14
|
+
.limit(limit)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
@devices = results.map { |device| DeviceDecorator.new(device, self) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class EntryPagesController < ApplicationController
|
|
3
|
+
include Limitable
|
|
4
|
+
|
|
5
|
+
before_action do
|
|
6
|
+
if Widget.disabled?(:entry_pages)
|
|
7
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :pages)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
results = cached(:entry_pages) do
|
|
13
|
+
EntryPagesQuery.call(params).limit(limit)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@pages = paginate(results).map { |page| EntryPageDecorator.new(page, self) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class ExitPagesController < ApplicationController
|
|
3
|
+
include Limitable
|
|
4
|
+
|
|
5
|
+
before_action do
|
|
6
|
+
if Widget.disabled?(:exit_pages)
|
|
7
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :pages)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
results = cached(:exit_pages) do
|
|
13
|
+
ExitPagesQuery.call(params)
|
|
14
|
+
.limit(limit)
|
|
15
|
+
end
|
|
16
|
+
@pages = paginate(results).map { |page| ExitPageDecorator.new(page, self) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class ExportsController < ApplicationController
|
|
3
|
+
skip_before_action :act_like_an_spa
|
|
4
|
+
|
|
5
|
+
def show
|
|
6
|
+
export = Export.new(params, self).build
|
|
7
|
+
file = export.to_zip
|
|
8
|
+
send_data file.read,
|
|
9
|
+
type: 'application/zip',
|
|
10
|
+
disposition: 'attachment',
|
|
11
|
+
filename: "Lookout export #{request.host} #{range[0].to_date} to #{(range[1] || Time.current).to_date}.zip"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
class BaseController < ApplicationController
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def serialize(value)
|
|
7
|
+
{ text: (value.presence || Lookout.none.text), value: (value.presence || Lookout.none.value) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def visit_query
|
|
11
|
+
VisitQuery.call(params)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
class LocationsController < BaseController
|
|
4
|
+
def index
|
|
5
|
+
query = visit_query.all
|
|
6
|
+
|
|
7
|
+
render json: query.select("distinct #{params[:type]}").where.not(params[:type] => nil).group(params[:type]).order(Arel.sql "count(*) desc").limit(50).pluck(params[:type]).map { |city| serialize(city) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
module OperatingSystems
|
|
4
|
+
class NamesController < BaseController
|
|
5
|
+
def index
|
|
6
|
+
query = visit_query.all
|
|
7
|
+
|
|
8
|
+
render json: query.select("distinct os").where.not(os: nil).group(:os).order(Arel.sql "count(*) desc").pluck(:os).map { |city| serialize(city) }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
module OperatingSystems
|
|
4
|
+
class VersionsController < BaseController
|
|
5
|
+
def index
|
|
6
|
+
query = visit_query.all
|
|
7
|
+
|
|
8
|
+
render json: query.select("distinct os_version").where.not(os_version: nil).group(:os_version).order(Arel.sql "count(*) desc").pluck(:os_version).map { |city| serialize(city) }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
module Pages
|
|
4
|
+
class EntryPagesController < BaseController
|
|
5
|
+
def index
|
|
6
|
+
query = event_query.all.distinct("entry_pages.url").select("entry_pages.url as url")
|
|
7
|
+
|
|
8
|
+
render json: query.map { |row| serialize(row.url) }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
module Pages
|
|
4
|
+
class ExitPagesController < BaseController
|
|
5
|
+
def index
|
|
6
|
+
query = event_query.distinct("exit_pages.url").select("exit_pages.url as url")
|
|
7
|
+
|
|
8
|
+
render json: query.map { |row| serialize(row.url) }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
module Properties
|
|
4
|
+
class NamesController < BaseController
|
|
5
|
+
def index
|
|
6
|
+
keys = extract_property_keys
|
|
7
|
+
render json: keys.map { |key| serialize(key) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def extract_property_keys
|
|
13
|
+
if Lookout::DatabaseAdapter.postgresql?
|
|
14
|
+
::Ahoy::Event
|
|
15
|
+
.select("jsonb_object_keys(properties) as keys")
|
|
16
|
+
.distinct
|
|
17
|
+
.pluck("jsonb_object_keys(properties)")
|
|
18
|
+
else
|
|
19
|
+
# SQLite: use json_each to extract keys
|
|
20
|
+
::Ahoy::Event
|
|
21
|
+
.from("#{::Ahoy::Event.table_name}, json_each(#{::Ahoy::Event.table_name}.properties)")
|
|
22
|
+
.select("DISTINCT json_each.key as keys")
|
|
23
|
+
.pluck("json_each.key")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
module Properties
|
|
4
|
+
class ValuesController < BaseController
|
|
5
|
+
def index
|
|
6
|
+
param_key = params[:q].to_unsafe_h.detect { |k,v| k.ends_with?("_i_cont") && k.starts_with?("properties.") }[0]
|
|
7
|
+
key = param_key.delete_prefix("properties.").delete_suffix("_i_cont")
|
|
8
|
+
query = event_query.all.distinct.select("properties->>'#{key}'").pluck(Arel.sql "properties->>'#{key}'")
|
|
9
|
+
|
|
10
|
+
render json: query.map { |element| serialize(element) }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
class ScreensController < BaseController
|
|
4
|
+
def index
|
|
5
|
+
query = visit_query.all
|
|
6
|
+
|
|
7
|
+
render json: query.select("distinct device_type").where.not(device_type: nil).group(:device_type).order(Arel.sql "count(*) desc").pluck(:device_type).map { |city| serialize(city) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
class SourcesController < BaseController
|
|
4
|
+
def index
|
|
5
|
+
query = visit_query.all
|
|
6
|
+
|
|
7
|
+
render json: query.select("distinct referring_domain").where.not(referring_domain: nil).group(:referring_domain).order(Arel.sql "count(*) desc").pluck(:referring_domain).map { |city| serialize(city) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Filters
|
|
3
|
+
class UtmsController < BaseController
|
|
4
|
+
def index
|
|
5
|
+
query = visit_query.select("#{params[:type]}", "count(#{params[:type]}) as total").group(params[:type]).order(Arel.sql "count(#{params[:type]}) desc").pluck(params[:type]).map { |city| serialize(city) }
|
|
6
|
+
render json: query
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Locations
|
|
3
|
+
class CitiesController < ApplicationController
|
|
4
|
+
include Lookout::Limitable
|
|
5
|
+
|
|
6
|
+
before_action do
|
|
7
|
+
if Widget.disabled?(:locations, :cities)
|
|
8
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :geography)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def index
|
|
13
|
+
results = cached(:cities) do
|
|
14
|
+
CityQuery.call(params)
|
|
15
|
+
.limit(limit)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@cities = paginate(results).map { |city| CityDecorator.new(city, self) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Locations
|
|
3
|
+
class CountriesController < ApplicationController
|
|
4
|
+
include Limitable
|
|
5
|
+
|
|
6
|
+
before_action do
|
|
7
|
+
if Widget.disabled?(:locations, :countries)
|
|
8
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :geography)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def index
|
|
13
|
+
results = cached(:countries) do
|
|
14
|
+
CountryQuery.call(params)
|
|
15
|
+
.limit(limit)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@countries = paginate(results).map { |country| CountryDecorator.new(country, self) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Locations
|
|
3
|
+
class MapsController < ApplicationController
|
|
4
|
+
include Limitable
|
|
5
|
+
|
|
6
|
+
before_action do
|
|
7
|
+
if Widget.disabled?(:locations, :map)
|
|
8
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :geography)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def show
|
|
13
|
+
if request.variant.include?(:details)
|
|
14
|
+
results = CountryQuery.call(params)
|
|
15
|
+
results = results.limit(limit)
|
|
16
|
+
@countries = paginate(results).map { |country| CountryDecorator.new(country, self) }
|
|
17
|
+
render template: 'lookout/locations/countries/index'
|
|
18
|
+
else
|
|
19
|
+
@countries = visit_query.where.not(country: nil).group("country").count
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
module Locations
|
|
3
|
+
class RegionsController < ApplicationController
|
|
4
|
+
include Limitable
|
|
5
|
+
|
|
6
|
+
before_action do
|
|
7
|
+
if Widget.disabled?(:locations, :regions)
|
|
8
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :geography)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def index
|
|
13
|
+
results = cached(:regions) do
|
|
14
|
+
RegionQuery.call(params)
|
|
15
|
+
.limit(limit)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@regions = paginate(results).map { |region| RegionDecorator.new(region, self) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class PropertiesController < ApplicationController
|
|
3
|
+
before_action do
|
|
4
|
+
@options = extract_property_keys.map { |key| [Base64.urlsafe_encode64(key), key]}.to_h
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def index
|
|
8
|
+
# Auto-select 'url' property if it exists, otherwise first property
|
|
9
|
+
if @options.any?
|
|
10
|
+
# Prefer 'url' property
|
|
11
|
+
url_key = @options.find { |key, value| value == 'url' }&.first
|
|
12
|
+
default_key = url_key || @options.keys.first
|
|
13
|
+
|
|
14
|
+
redirect_to property_path(id: default_key, **request.query_parameters), status: :see_other
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show
|
|
19
|
+
value = Base64.urlsafe_decode64(params[:id])
|
|
20
|
+
json_extract = Lookout::DatabaseAdapter.json_extract_text("properties", value)
|
|
21
|
+
coalesce_expr = "COALESCE(#{json_extract}, '(none)')"
|
|
22
|
+
|
|
23
|
+
# Handle percentage calculation differently for SQLite vs PostgreSQL
|
|
24
|
+
percentage_calc = if Lookout::DatabaseAdapter.postgresql?
|
|
25
|
+
"(COUNT(DISTINCT visit_id)/COUNT(*)::numeric) * 100"
|
|
26
|
+
else
|
|
27
|
+
"(CAST(COUNT(DISTINCT visit_id) AS REAL) / COUNT(*)) * 100"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@properties = event_query
|
|
31
|
+
.select(
|
|
32
|
+
"#{coalesce_expr} AS label",
|
|
33
|
+
"COUNT(*) AS events_count",
|
|
34
|
+
"COUNT(DISTINCT visit_id) AS unique_visitors_count",
|
|
35
|
+
"#{percentage_calc} as percentage"
|
|
36
|
+
)
|
|
37
|
+
.group(coalesce_expr)
|
|
38
|
+
.order(Arel.sql "COUNT(*) desc")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
helper_method :has_property?
|
|
44
|
+
def has_property?(value)
|
|
45
|
+
searching_properties[value]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
helper_method :selected_property?
|
|
49
|
+
def selected_property?(value)
|
|
50
|
+
encoded = Base64.urlsafe_encode64(value, padding: false)
|
|
51
|
+
encoded == params[:id]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def searching_properties
|
|
55
|
+
JSON.parse(params.dig("q", "properties_json_cont") || '{}')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def extract_property_keys
|
|
59
|
+
if Lookout::DatabaseAdapter.postgresql?
|
|
60
|
+
::Ahoy::Event
|
|
61
|
+
.select("jsonb_object_keys(properties) as keys")
|
|
62
|
+
.distinct
|
|
63
|
+
.pluck("jsonb_object_keys(properties)")
|
|
64
|
+
else
|
|
65
|
+
# SQLite: use json_each to extract keys
|
|
66
|
+
::Ahoy::Event
|
|
67
|
+
.from("#{::Ahoy::Event.table_name}, json_each(#{::Ahoy::Event.table_name}.properties)")
|
|
68
|
+
.select("DISTINCT json_each.key as keys")
|
|
69
|
+
.pluck("json_each.key")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Lookout
|
|
2
|
+
class SourcesController < ApplicationController
|
|
3
|
+
include Limitable
|
|
4
|
+
|
|
5
|
+
before_action do
|
|
6
|
+
if Widget.disabled?(:sources)
|
|
7
|
+
raise Widget::WidgetDisabled.new("Widget disabled", :sources)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index
|
|
12
|
+
results = cached(:sources) do
|
|
13
|
+
SourceQuery.call(params)
|
|
14
|
+
.limit(limit)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
@sources = paginate(results).map { |source| Lookout::SourceDecorator.new(source, self) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|