Skip to content

Commit

Permalink
Merge pull request #436 from hasgeek/funnel-analytics
Browse files Browse the repository at this point in the history
Better visualizations to display funnel analysis for job posts
  • Loading branch information
shreyas-satish authored Apr 16, 2018
2 parents ee2faa2 + 3e3a978 commit 41450f7
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 70 deletions.
3 changes: 1 addition & 2 deletions hasjob/models/jobpost.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.dialects.postgresql import TSVECTOR
import tldextract
from coaster.auth import current_auth
from coaster.sqlalchemy import make_timestamp_columns, Query, JsonDict, StateManager
from baseframe import cache, _, __
from baseframe.utils import is_public_email_domain
Expand Down Expand Up @@ -426,7 +425,7 @@ def tag_content(self):
Markup('<div>') + Markup(escape(self.headline)) + Markup('</div>'),
Markup('<div>') + Markup(self.description) + Markup('</div>'),
Markup('<div>') + Markup(self.perks) + Markup('</div>')
))
))

@staticmethod
def viewcounts_key(jobpost_id):
Expand Down
140 changes: 124 additions & 16 deletions hasjob/static/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -933,9 +933,8 @@ tr > div {
position: relative;
vertical-align: top;
display: block;
font-family: "McLaren", sans-serif;
overflow: visible;
padding: 24px 18px;
padding: 24px 0 0;
word-wrap: break-word;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
Expand All @@ -956,17 +955,11 @@ tr > div {
.stickie label {
font-weight: normal;
}
.stickie .count {
font-size: 90%;
color: #816894;
display: block;
}
.stickie .annotation {
font-size: 75%;
display: block;
position: absolute;
color: #816894;
font-family: "McLaren", sans-serif;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
Expand All @@ -981,19 +974,127 @@ tr > div {
left: 0.5em;
width: 75%;
}
.stickie .bottom-right {
right: 0.5em;
bottom: 0.1em;
width: 70%;
text-align: right;
.stickie .headline,
.stickie .pay {
display: block;
padding: 0 18px;
position: static;
}
.stickie .bottom-left {
left: 0.5em;
bottom: 0.1em;
.stickie .headline {
font-family: "McLaren", sans-serif;
}
.stickie .star {
position: static;
float: left;
width: 20%;
margin-left: 0.5em;
}
.stickie .new {
color: #df5e0e;
}
.stickie .company-name {
position: static;
float: right;
width: 70%;
text-align: right;
margin-right: 0.5em;
}
.stickie .count {
margin-top: 5px;
color: #816894;
display: flex;
}
.stickie .count-items {
flex: 1;
font-size: 9px;
}
.stickie .count-text {
display: inline-block;
width: calc(100% - 6px);
}
.stickie .count-items.impressions {
flex: 1.5;
}
.stickie .count-arrow {
float: right;
}
.stickie .count-text,
.stickie .count-arrow {
font-size: 10px;
}
@media (min-width: 480px) {
.stickie .count-text,
.stickie .count-arrow {
font-size: 14px;
}
}
@media (min-width: 768px) {
.stickie .count-text,
.stickie .count-arrow {
font-size: 7px;
}
.stickie .count-arrow {
margin-top: 2px;
}
}
@media (min-width: 1200px) {
.stickie .count-text,
.stickie .count-arrow {
font-size: 8px;
}
.stickie .count-arrow {
margin-top: 1px;
}
}
.stickie .count-background {
display: flex;
height: 7px;
margin-top: 4px;
border-radius: 0 0 2px 2px;
overflow: hidden;
padding: 0;
list-style: none;
}
.stickie .count-background .background {
flex: 1;
height: 100%;
margin: 0;
padding: 0;
}
.stickie .count-background .impressions.background {
flex: 1.5;
position: relative;
z-index: 4;
}
.stickie .count-background .viewed.background {
position: relative;
z-index: 3;
}
.stickie .count-background .opened.background {
position: relative;
z-index: 2;
}
.stickie .count-background .applied.background {
position: relative;
z-index: 1;
}
.stickie .count-background .background.arrow:before {
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-style: solid;
border-width: 2px 2px 0 0;
border-color: #ffffa2;
position: absolute;
top: 0;
right: -2px;
vertical-align: top;
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.stickie .pinned {
text-indent: -10000px;
display: block;
Expand Down Expand Up @@ -1047,6 +1148,11 @@ tr > div {
left: 1.5em;
right: 1.5em;
}
.stickie.grouped.under .count-background {
position: absolute;
bottom: 0;
width: 100%;
}
.no-touch li:hover > .stickie.grouped.under {
top: 0px;
left: 15px;
Expand Down Expand Up @@ -1103,6 +1209,7 @@ tr > div {

.stickie.special {
background-color: #f6f6f6;
padding: 24px 18px;
}

.stickie.org {
Expand Down Expand Up @@ -1283,6 +1390,7 @@ tr > div {
text-align: center;
width: auto;
margin: 24px 0;
padding: 24px 18px;
}

#i40x {
Expand Down
105 changes: 103 additions & 2 deletions hasjob/static/js/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//window.Hasjob initialized in layout.html

Hasjob.Util = {
updateGA: function(){
updateGA: function() {
/*
Resets the path in the tracker object and updates GA with the current path.
To be called after updating the URL with pushState or replaceState.
Expand All @@ -12,6 +12,17 @@ Hasjob.Util = {
window.ga('set', 'page', path);
window.ga('send', 'pageview');
}
},
createCustomEvent: function(eventName) {
// Raise a custom event
if (typeof(window.Event) === "function") {
var customEvent = new Event(eventName);
} else {
// 'Event' constructor is not supported by IE
var customEvent = document.createEvent('Event');
customEvent.initEvent(eventName, true, true);
}
return customEvent;
}
};

Expand Down Expand Up @@ -73,7 +84,7 @@ window.Hasjob.JobPost = {

window.Hasjob.StickieList = {
init: function(){
var stickielist = this;
window.dispatchEvent(Hasjob.Util.createCustomEvent('onStickiesInit'));
},
loadmore: function(config){
var stickielist = this;
Expand All @@ -94,6 +105,7 @@ window.Hasjob.StickieList = {
$('ul#stickie-area').append(data.trim());
stickielist.loadmoreRactive.set('loading', false);
stickielist.loadmoreRactive.set('error', false);
window.dispatchEvent(Hasjob.Util.createCustomEvent('onStickiesPagination'));
},
error: function() {
stickielist.loadmoreRactive.set('error', true);
Expand Down Expand Up @@ -153,11 +165,97 @@ window.Hasjob.StickieList = {
$('#main-content').html(data);
window.Hasjob.Filters.refresh();
NProgress.done();
window.dispatchEvent(Hasjob.Util.createCustomEvent('onStickiesRefresh'));
}
});
history.replaceState({reloadOnPop: true}, '', window.location.href);
history.pushState({reloadOnPop: true}, '', searchUrl);
window.Hasjob.Util.updateGA();
},
createGradientColourScale: function(funnelName, maxValue) {
/*
Creates a linear colour gradient with canvas of width equal to maxValue. The canvas indicates a scale from 0 to maxValue.
Takes
'funnelName' - conversion funnel's name.
'maxValue' - max value of conversion funnel across job posts of last 30 days
*/

var canvas = document.createElement("canvas");
canvas.id = funnelName;
canvas.width = maxValue;
canvas.height = 10;

var context = canvas.getContext('2d');
context.rect(0, 0, canvas.width, canvas.height);
var grd = context.createLinearGradient(0, 0, canvas.width, canvas.height);

grd.addColorStop(1, '#DF3499');
grd.addColorStop(0.7, '#E05F26');
grd.addColorStop(0.5, '#DF5C2A');
grd.addColorStop(0.1, '#F1D564');
grd.addColorStop(0, '#FFFFA2');

context.fillStyle = grd;
context.fill();
//Store the canvas context and end colour of the conversion funnel, later to be used by window.Hasjob.Util.setFunnelColour for picking the colour for a conversion funnel value for a job post.
window.Hasjob.Config[funnelName] = {};
window.Hasjob.Config[funnelName].canvasContext = context;
window.Hasjob.Config[funnelName].maxColour = '#DF3499';
},
setGradientColour: function(funnelName, value, elementId) {
/*
Picks the colour for the value from the colour gradient canvas based on a scale of 0 to maxValue.
Takes 'funnelName', value, elementId'
'funnelName' - conversion funnel's name.
'value' - conversion funnel value for the job post
'elementId' - id attribute of the element of which background colour is to be set
*/

//rgba - RGBA values at a particular point in the canvas.
var rgba = window.Hasjob.Config[funnelName].canvasContext.getImageData(value, 1, 1, 1).data;
if (rgba[0] > 255 || rgba[1] > 255 || rgba[2] > 255) {
// rgb value is invalid, hence return white
colourHex ="#FFFFFF";
} else if (rgba[0] == 0 && rgba[1] == 0 && rgba[2] == 0) {
// value greater than maxValue hence return the last colour of the gradient
colourHex = window.Hasjob.Config[funnelName].maxColour;
} else {
// Get the colour code in hex from RGB values returned by getImageData
colourHex = "#" + (("000000" + (rgba[0] << 16) | (rgba[1] << 8) | rgba[2]).toString(16)).slice(-6);
}
// Set the background colour of the element
var element = document.getElementById(elementId);
element.classList.add("funnel-color-set");
element.style.backgroundColor = colourHex;
},
renderGradientColour: function() {
$('.js-funnel').each(function() {
if(!$(this).hasClass("funnel-color-set")) {
Hasjob.StickieList.setGradientColour($(this).data('funnel-name'), $(this).data('funnel-value'), $(this).attr('id'));
}
});
},
createGradientColour: function() {
Hasjob.StickieList.createGradientColourScale('impressions', Hasjob.Config.MaxCounts.max_impressions);
Hasjob.StickieList.createGradientColourScale('views', Hasjob.Config.MaxCounts.max_views);
Hasjob.StickieList.createGradientColourScale('opens', Hasjob.Config.MaxCounts.max_opens);
Hasjob.StickieList.createGradientColourScale('applied', Hasjob.Config.MaxCounts.max_applied);
},
initFunnelViz: function() {
window.addEventListener('onStickiesInit', function (e) {
Hasjob.StickieList.createGradientColour();
Hasjob.StickieList.renderGradientColour();
}, false);

window.addEventListener('onStickiesRefresh', function (e) {
Hasjob.StickieList.renderGradientColour();
}, false);

window.addEventListener('onStickiesPagination', function (e) {
Hasjob.StickieList.renderGradientColour();
}, false);
}
};

Expand Down Expand Up @@ -589,6 +687,9 @@ $(function() {
window.Hasjob.Filters.init();
window.Hasjob.JobPost.handleStarClick();
window.Hasjob.JobPost.handleGroupClick();
if (window.Hasjob.Config.MaxCounts) {
window.Hasjob.StickieList.initFunnelViz();
}

var getCurrencyVal = function() {
return $("input[type='radio'][name='currency']:checked").val();
Expand Down
1 change: 1 addition & 0 deletions hasjob/static/sass/_40x.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
text-align: center;
width: auto;
margin: 24px 0;
padding: 24px 18px;
}
#i40x {
font-size: 150px;
Expand Down
Loading

0 comments on commit 41450f7

Please sign in to comment.