Skip to content

Commit

Permalink
[feature] Entity specific analytics (#1389)
Browse files Browse the repository at this point in the history
* [feature] Entity specific analytics 

Addresses: #1292
<img width="1101" alt="Screen Shot 2023-01-04 at 8 07 53 AM" src="https://user-images.githubusercontent.com/35935196/210561884-9f2220bf-0c69-45ff-bef4-d9f9dc974e3b.png">

### Determining the visibility threshold for section views 

Threshold indicates at what percentage of the target's visibility the tracking should be executed. 0.75 threshold indicates that the tracking is done when 75% of the section is visible in the screen.

However for larger sections, 75% of the content can never be visible. Therefore we need to adjust the threshold.

A good rule of thumb is if the section is double the screen size, adjust the threshold to slightly less than 0.5 and if the section is 4 times the screen size, adjust threshold to less than  0.25, and so on

REF:

https://stackoverflow.com/questions/66296057/intersectionobserver-does-not-work-on-small-screens-for-long-sections-js/

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#thresholds


Co-authored-by: Pralish Kayastha <[email protected]>
Co-authored-by: Pralish Kayastha <[email protected]>
  • Loading branch information
3 people authored Jan 31, 2023
1 parent b43d050 commit a1c806e
Show file tree
Hide file tree
Showing 30 changed files with 1,163 additions and 24 deletions.
1 change: 1 addition & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ RUN apk --no-cache update \
&& apk --no-cache upgrade \
&& apk add --no-cache \
build-base \
python2 \
ttf-ubuntu-font-family \
git \
postgresql-dev \
Expand Down
1 change: 1 addition & 0 deletions app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../javascripts
39 changes: 39 additions & 0 deletions app/assets/javascripts/comfy/admin/cms/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//= require moment
//= require bootstrap-daterangepicker

$(function() {
$('[data-toggle="tooltip"]').tooltip();

$('#reportrange').daterangepicker({
opens: 'left',
locale: {
format: 'YYYY/MM/DD'
},
startDate: $("#start_date").val() || moment().startOf('month'),
endDate: $("#end_date").val() || moment().endOf('month'),
ranges: {
[moment().format('MMMM YYYY')]: [moment().startOf('month'), moment().endOf('month')],
'3 months': [moment().startOf('month').subtract(2, 'months'), moment().endOf('month')],
'6 months': [moment().startOf('month').subtract(5, 'months'), moment().endOf('month')],
'1 year': [moment().startOf('month').subtract(11, 'months'), moment().endOf('month')]
}
}, function(start, end, label) {
$('#start_date').val(start.format('YYYY-MM-DD'));
$('#end_date').val(end.format('YYYY-MM-DD'));
$('#interval').val(label);
cb();
$("#analytics_filter").submit();
});

cb();
});

function cb() {
if (!$('#start_date').val() && !$('#end_date').val()) {
$('#reportrange span').html(moment().format('MMMM YYYY'));
} else if ($('#interval').val() == 'Custom Range') {
$('#reportrange span').html(moment($("#start_date").val()).format('MMMM D, YYYY') + ' - ' + moment($("#end_date").val()).format('MMMM D, YYYY'));
} else {
$('#reportrange span').html($('#interval').val());
}
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@import "./actiontext.scss";
@import 'select2/dist/css/select2.css';
@import './direct_upload.scss';
@import 'bootstrap-daterangepicker/daterangepicker';

.alert-file_url {
display: none;
Expand Down
172 changes: 172 additions & 0 deletions app/assets/stylesheets/dashboard.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,175 @@
// Place all the styles related to the dashboard controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/

.vr-analytics {
color: #293845;

&-title {
font-size: 32px;
line-height: 44px;
font-weight: bold;
}

&-sub-title {
font-size: 24px;
line-height: 33px;
text-transform: capitalize;
}

&-count,
&-percent-change,
&-event-label {
color: #535F6A;
font-size: 16px;
line-height: 22px;
}

&-count {
margin-right: 10px;

@media (min-width: 768px) {
margin-left: 24px;
}
}

&-count-lg {
font-size: 24px;
line-height: 33px;
margin-right: 10px;
}

&-tooltips {
height: 16px;
width: 16px;
border-radius: 50%;
background-color: #D4D7DA;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
margin-left: 8px;
cursor: default;
}

&-percent-change {
.positive {
color: #63C32D;
}

.negative {
color: #E93E3E;
}
}

&-filter-dropdown-container {
& > button {
color: #BD34D1;
font-size: 16px;
font-weight: 600;
}
}

&-filters {
&-pages {
border: none;
outline: none;
-moz-appearance:none; /* Firefox */
-webkit-appearance:none; /* Safari and Chrome */
appearance:none;
background: transparent;
background-image: url("data:image/svg+xml;utf8,<svg fill='%23bd34d1' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
background-repeat: no-repeat;
background-position-x: 98.5%;
background-position-y: 50%;
}

&-pages,
&-ranges > span,
&-ranges > i {
color: #BD34D1;
font-size: 16px;
font-weight: 600;
cursor: pointer;
}

@media (max-width: 767px) {
&-pages,
&-ranges {
width: 100%;
border: 1px solid #E5E7E8;
border-radius: 5px;
padding: 9px 16px;
margin-bottom: 8px;
}
}

}

&-card {
box-shadow: -4px 12px 24px #29384514;
border: 1px solid #E9EAEC;
border-radius: 5px;
position: relative;

& > a {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}

&-section {
padding: 16px;
}

&-resource-poster-conatiner {
margin-right: 10px;

& > img {
height: 48px;
width: 85px;
object-fit: cover;
}
}

&-resource-title {
font-weight: bold;
position: relative;
pointer-events: none;
z-index: 1;

& > a {
color: #293845;
pointer-events: all;
}
}

&-resource-duration {
color: #535F6A;
}
}

&-section {
padding: 32px 0;

&-header {
margin-bottom: 24px;
}
}

&-page-visit-events {
&-donut-chart {
padding: 16px;
}
}


&-blank-states {
color: grey;
padding: 0 25px 40px;
font-size: 14px;
}
}
26 changes: 25 additions & 1 deletion app/controllers/comfy/admin/api_namespaces_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'will_paginate/array'

class Comfy::Admin::ApiNamespacesController < Comfy::Admin::Cms::BaseController
before_action :set_api_namespace, only: %i[ show edit update destroy discard_failed_api_actions rerun_failed_api_actions export export_api_resources duplicate_with_associations duplicate_without_associations export_without_associations_as_json export_with_associations_as_json social_share_metadata api_action_workflow ]
before_action :set_api_namespace, except: %i[index new create import_as_json]

before_action :ensure_authority_for_creating_api, only: %i[ new create import_as_json]
before_action :ensure_authority_for_viewing_all_api, only: :index
Expand All @@ -12,6 +12,7 @@ class Comfy::Admin::ApiNamespacesController < Comfy::Admin::Cms::BaseController
before_action :ensure_authority_for_allow_exports_in_api, only: %i[ export export_api_resources export_without_associations_as_json export_with_associations_as_json ]
before_action :ensure_authority_for_allow_duplication_in_api, only: %i[ duplicate_with_associations duplicate_without_associations ]
before_action :ensure_authority_for_allow_social_share_metadata_in_api, only: %i[ social_share_metadata ]
before_action :ensure_authority_to_manage_analytics, only: :analytics_metadata
before_action :ensure_authority_for_full_access_for_api_actions_only_in_api, only: %i[ api_action_workflow discard_failed_api_actions rerun_failed_api_actions ]

# GET /api_namespaces or /api_namespaces.json
Expand Down Expand Up @@ -219,6 +220,25 @@ def social_share_metadata
end
end


def analytics_metadata
respond_to do |format|
if @api_namespace.update(analytics_metadata_params)
format.html do
flash[:notice] = 'Analytics Metadata successfully updated.'
redirect_to @api_namespace
end
format.json { render :show, status: :ok, location: @api_namespace }
else
format.html do
flash[:error] = @api_namespace.errors.full_messages
render :edit, status: :unprocessable_entity
end
format.json { render json: @api_namespace.errors, status: :unprocessable_entity }
end
end
end

def api_action_workflow
respond_to do |format|
if @api_namespace.update(api_action_workflow_params)
Expand Down Expand Up @@ -269,4 +289,8 @@ def api_action_workflow_params
def api_namespace_social_share_metadata_params
params.require(:api_namespace).permit(social_share_metadata: [:title, :description, :image])
end

def analytics_metadata_params
params.require(:api_namespace).permit(analytics_metadata: [:title, :author, :thumbnail])
end
end
53 changes: 53 additions & 0 deletions app/controllers/comfy/admin/v2/dashboard_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
class Comfy::Admin::V2::DashboardController < Comfy::Admin::Cms::BaseController
include AhoyEventsHelper

before_action :ensure_authority_to_manage_analytics

def dashboard
@start_date = params[:start_date]&.to_date || Date.today.beginning_of_month
@end_date = params[:end_date]&.to_date || Date.today.end_of_month
date_range = @start_date.beginning_of_day..@end_date.end_of_day

@visits = Ahoy::Visit.where(started_at: @start_date.beginning_of_day..@end_date.end_of_day)

Ahoy::Event::EVENT_CATEGORIES.values.each do |event_category|
if event_category == Ahoy::Event::EVENT_CATEGORIES[:page_visit]
events = Ahoy::Event.where(name: 'comfy-cms-page-visit').joins(:visit)
else
events = Ahoy::Event.jsonb_search(:properties, { category: event_category }).joins(:visit)
end
events = events.jsonb_search(:properties, { page_id: params[:page] }) if params[:page].present?
instance_variable_set("@previous_period_#{event_category}_events", events.where(time: previous_period(params[:interval], @start_date, @end_date)))
instance_variable_set("@#{event_category}_events", events.where(time: date_range))
end

# legacy and system events does not have category
# separating out 'comfy-cms-page-visit' event since we have a seprate section
@legacy_and_system_events = Ahoy::Event.where.not('properties::jsonb ? :key', key: 'category').where.not(name: 'comfy-cms-page-visit').joins(:visit)
@previous_period_legacy_and_system_events = @legacy_and_system_events.where(time: previous_period(params[:interval], @start_date, @end_date))
@legacy_and_system_events = @legacy_and_system_events.where(time: date_range)
end


private

def previous_period(interval, start_date, end_date)
today = Date.current
interval = interval || today.strftime('%B %Y')

case interval
when "#{today.strftime('%B %Y')}"
today.prev_month.beginning_of_month.beginning_of_day..today.prev_month.end_of_month.end_of_day
when "3 months"
(start_date - 3.months).beginning_of_month.beginning_of_day..(start_date - 1.month).end_of_month.end_of_day
when "6 months"
(today - 6.months).beginning_of_month.beginning_of_day..(start_date - 1.month).end_of_month.end_of_day
when "1 year"
(today - 12.months).beginning_of_month.beginning_of_day..(start_date - 1.month).end_of_month.end_of_day
else

days_diff = (end_date - start_date).to_i
(start_date - (days_diff + 1).days).beginning_of_day..(start_date - 1.day).end_of_day
end
end
end
7 changes: 5 additions & 2 deletions app/helpers/api_forms_helper.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
module ApiFormsHelper
def render_form(id)
def render_form(id, options = {})
# usage in cms {{ cms:helper render_form, 1 }} here 1 is the id
# usage in rails = render_form @api_form.id
@api_form = ApiForm.find_by(id: id)
if @api_form
@api_namespace = @api_form.api_namespace
render partial: 'comfy/admin/api_forms/render'
data = options['data'] || {}

data["violet-track-form-submit"] = "true" unless data["violet-track-form-submit"].present?
render partial: 'comfy/admin/api_forms/render', locals: { data: data }
end
end

Expand Down
Loading

0 comments on commit a1c806e

Please sign in to comment.