var Reporting = function (core, target, data, returnCB) {
'use strict';
var events = new EventManager(),
template = new Template(),
element = null,
elements = null,
squares = null,
uid = getUid(),
hasLocations = false,
schedDlg = new ReportDialog('sched'),
campaignsDropDownList = new DropdownObj([{ title: 'Stats: All Campaigns', value: 'all' }], 'all', 'royal', 'royal', 'royal'),
downloadDropdownOpts = new DropdownObj([
{ title: 'Download', value: '-', placeholder: true },
{ title: 'Excel Spreadsheet', value: 'xlsx' },
{ title: 'JSON', value: 'json' },
{ title: 'CSV (zip)', value: 'zip' },
{ title: 'Schedule Report', value: 'SR' }
], '-', 'cool', 'cool', 'cool', true),
apiManager = new ApiManager(getApiManagerObj(), core.userID, init),
graphInitialized = false;
this.exit = exit;
function getApiManagerObj() {
var week = { args: 7 };
return {
'profiles': null,
'adsList': null,
'adGroupsList': null,
'reporting': week,
'reporting30': { 'source': 'reporting/' + core.userID + '/30?noAds=true', 'noID': true },
'campaignsList': null,
'campaignsSpent': week
};
}
function getUid() { return core.userType !== 'demo' || !isDefined(core.targetAccount) ? core.userID : core.targetAccount; }
function init(error, d) {
d = cleanRTBObject(getUid(), d);
hasLocations = isArray(d.profiles.locations) && !!d.profiles.locations.length;
loop(d.campaignsList, function (_, v) {
campaignsDropDownList.data.push({ title: v.name, value: v.id });
});
initIndex(d);
if (returnCB) returnCB();
}
function getAdvStats() {
if (!data.fromDate || !data.toDate) {
return;
}
var url = new URL(location.protocol + "//" + location.host);
url.pathname += "api/v1/advertiserStats/" + core.userID + "/";
url.pathname += data.fromDate + "/";
url.pathname += data.toDate;
new HttpRequest("GET", url.href, null, "json", "json", updateAdvStats);
}
function updateAdvStats(e, statusCode) {
if (statusCode !== 200 || !e.success) {
console.error(e.errors)
return;
}
var d = e.data,
obj = {};
for (var date in d) {
var dateObj = d[date],
gi = new GraphItem(dateObj.imps, dateObj.clicks, dateObj.convs);
// Set graph item for this date
obj[date] = gi;
}
data.graph = getGraphData(obj);
refreshGraph(data);
}
function Template() {
var graph = '
' + downloadReport + graph + cmpListDD + sbd + tabbed + note;
this.general = '
';
this.ads = '
';
this.campaigns = '
';
this.totalSquare = '
{{ mainValue }}
{{ subTitle }}:
{{ subValue }}
';
this.miniSquare = '
{{ mainValue }}
';
this.notificationSquare = '
{{# . }}{{ timestamp }}
{{ message }}
{{/ . }}
';
}
function initIndex(d, filterCampaignId) {
var loaded = false;
if (!!elements) { elements.exit(); target.removeChild(element); }
campaignsDropDownList.initialValue = !filterCampaignId ? 'all' : filterCampaignId;
element = getElementFromString(template.index);
elements = new Elements(element, {
//'index' : {},
'tabbed': {},
'tabbedWindow': {},
'general': {},
'ads': {},
'campaigns': {},
'segments': {},
'impressions': {},
'clicks': {},
'conversions': {},
'graphContainer': { 'selector': '.graphContainer' },
'toggle': {},
'fromInput': { 'selector': '.fromInput' },
'toInput': { 'selector': '.toInput' },
'incDomains': new Checkbox(element.querySelector('.incDomains'), function(v) { data.incDomains = v; }),
'fromDate': {},
'toDate': {},
'cmpList': { 'target': new Dropdown(element.querySelector('.cmpListDD'), campaignsDropDownList, setStats) },
'downloadAnchor': { 'target': new Dropdown(element.querySelector('.downloadAnchor'), downloadDropdownOpts, downloadReport) }
});
var e = elements,
localData = filterDataByCampaign(d, filterCampaignId);
e.tabbed = element.querySelector('.tabbed');
data = new Data(localData, filterCampaignId);
squares = new Squares(localData, filterCampaignId);
e.general = getElementFromString(template.general);
e.ads = getElementFromString(template.ads);
e.campaigns = getElementFromString(template.campaigns);
e.fromDate = new CalendarInput(e.fromInput, { "value": getLastWeek() }, updateFromDate);
e.toDate = new CalendarInput(e.toInput, null, updateToDate);
initGraph(e);
// Maybe we should do this if an error occurs on load of adv stats
// e.graphContainer.classList.add('noDisplay');
initTabbedWindow();
target.appendChild(element);
function setStats(v) {
if (!loaded) { loaded = true; return; }
initIndex(d, v === 'all' ? null : v);
}
}
function initTabbedWindow() {
var e = elements;
elements.tabbedWindow = new TabbedContainer(e.tabbed, getTabbedObj(e));
elements.toggle = new TabbedContainer(e.graphContainer, getGraphsObj(e));
}
function initGeneral() {
var g = elements.general,
obj = {
'squares': [
{ 'column': 0, 'size': 'one', 'title': 'Impressions', 'element': getSquare('totalAdsShown') },
{ 'column': 1, 'size': 'one', 'title': 'Clicks', 'element': getSquare('totalClicks') },
{ 'column': 2, 'size': 'one', 'title': 'Conversions', 'element': getSquare('totalConversions') },
{ 'column': 3, 'size': 'one', 'title': 'Spent', 'element': getSquare('weekSpent') },
{ 'column': 4, 'size': 'one', 'title': 'Revenue', 'element': getSquare('totalRevenue') },
{ 'column': 0, 'size': 'one', 'title': 'CPM', 'element': getSquare('CPM') },
{ 'column': 1, 'size': 'one', 'title': 'CPC', 'element': getSquare('CPC') },
{ 'column': 2, 'size': 'one', 'title': 'CPA', 'element': getSquare('CPA') },
{ 'column': 3, 'size': 'one', 'title': 'CTR', 'element': getSquare('CTR') },
{ 'column': 4, 'size': 'one', 'title': 'ROI', 'element': getSquare('ROI') }
]
};
if (hasLocations) {
obj.squares.push({ 'column': 0, 'size': 'one', 'title': '# In Store Visits', 'element': getSquare('storeVisits') });
}
setTimeout(getCSClosure(g, obj), 1);
}
function initAds() {
var g = elements.ads,
obj = {
'squares': [
{ 'column': 0, 'size': 'one', 'title': 'Impressions', 'element': getSquare('totalAdsShown') },
{ 'column': 1, 'size': 'one', 'title': '# Active Ads', 'element': getSquare('numberOfActiveAds') },
{ 'column': 2, 'size': 'one', 'title': '# Ad Groups', 'element': getSquare('numberOfAdGroups') }
]
};
setTimeout(getCSClosure(g, obj), 1);
}
function initCampaigns() {
var g = elements.campaigns,
obj = {
'squares': [
{ 'column': 0, 'size': 'one', 'title': 'Impressions', 'element': getSquare('totalAdsShown') },
{ 'column': 1, 'size': 'one', 'title': 'Clicks', 'element': getSquare('totalClicks') },
{ 'column': 2, 'size': 'one', 'title': 'Conversions', 'element': getSquare('totalConversions') },
{ 'column': 3, 'size': 'one', 'title': '# Live Campaigns', 'element': getSquare('numberOfLiveCampaigns') },
{ 'column': 4, 'size': 'one', 'title': '# Paused Campaigns', 'element': getSquare('numberOfPausedCampaigns') },
{ 'column': 0, 'size': 'two', 'title': 'Spent', 'element': getSquare('totalSpent') }
]
};
setTimeout(getCSClosure(g, obj), 1);
}
function initGraph(e) {
if (graphInitialized) {
return;
}
e.impressions = getElementFromString('
');
e.clicks = getElementFromString('
');
e.conversions = getElementFromString('
');
graphInitialized = true;
}
function refreshGraph(data) {
new Chart(elements.impressions.getContext('2d')).Line(data.graph.impressions);
new Chart(elements.clicks.getContext('2d')).Line(data.graph.clicks);
new Chart(elements.conversions.getContext('2d')).Line(data.graph.conversions);
}
function Data(d, cid) {
var newObj = {};
if (!!cid) {
var dataTarget = d.reporting30.campaignLevel[cid];
for (var date in dataTarget) {
var dateObj = dataTarget[date],
gi = new GraphItem();
newObj[date] = gi;
}
}
this.graph = newObj;
}
function Squares(d, cid) {
var reporting = d.reporting,
adsList = d.adsList || [],
campaignsList = d.campaignsList || [],
activeAds = getActiveAdsObj(adsList),
adGroups = getAdGroupsObj(adsList, d.adGroupsList),
activeCampaigns = getActiveListObj(campaignsList),
pendingCampaigns = getPendingListObj(campaignsList),
totalAdsShown = getTotalAdsShown(reporting, cid), // total in 7 days
totalClicks = getTotalClicks(reporting, cid), // ^
totalConversions = getTotalConversions(reporting, cid), // ^
averageCTR = getAvgCTR(),
totalSpent = getTotalSpent(),
weekSpent = getWeekSpent(d, cid),
totalRevenue = getAdvStats('spent', 2),
numberOfCampaignDays = reporting.numDays,
storeVisits = getAdvStats('inStoreVisits');
this.numberOfActiveAds = getSquareObj(template.totalSquare, activeAds.count, '% of total ads', activeAds.percent + '%');
this.numberOfAdGroups = getSquareObj(template.totalSquare, adGroups.count, 'Avg ads per group', adGroups.average);
this.totalAdsShown = getSquareObj(template.totalSquare, totalAdsShown.total, 'Daily average', totalAdsShown.average);
this.totalClicks = getSquareObj(template.totalSquare, totalClicks.total, 'Daily average', totalClicks.average);
this.totalConversions = getSquareObj(template.totalSquare, totalConversions.total, 'Daily average', totalConversions.average);
this.numberOfLiveCampaigns = getSquareObj(template.totalSquare, activeCampaigns.count, '% of total campaigns', activeCampaigns.percent + '%');
this.numberOfPausedCampaigns = getSquareObj(template.totalSquare, pendingCampaigns.count, '% of total campaigns', pendingCampaigns.percent + '%');
this.weekSpent = getSquareObj(template.totalSquare, '$' + weekSpent, 'Daily average', '$' + (weekSpent / 7).toFixed(2));
this.totalSpent = getSquareObj(template.totalSquare, '$' + totalSpent, 'Daily average', '$' + (totalSpent / 7).toFixed(2));
this.totalRevenue = getSquareObj(template.totalSquare, '$' + totalRevenue, 'Daily average', '$' + getSpentPerDay(totalRevenue, numberOfCampaignDays));
this.storeVisits = getSquareObj(template.miniSquare, storeVisits);
this.CPM = getSquareObj(template.miniSquare, !!totalAdsShown.total ? '$' + ((weekSpent * 1000) / totalAdsShown.total).toFixed(3) : '$0.00');
this.CPC = getSquareObj(template.miniSquare, !!totalClicks.total ? '$' + (weekSpent / totalClicks.total).toFixed(3) : '$0.00');
this.CPA = getSquareObj(template.miniSquare, !!totalConversions.total ? (weekSpent / totalConversions.total).toFixed(3) : 0);
this.CTR = getSquareObj(template.miniSquare, averageCTR + '%');
this.ROI = getSquareObj(template.miniSquare, (!!weekSpent ? totalRevenue / weekSpent : 0).toFixed(3));
function getAdvStats(name, fixed) {
if (!!cid || !d.advStats) return 0;
var v = d.advStats[name];
if (isNaN(v)) return 0;
return fixed > 0 ? v.toFixed(fixed) : v;
}
function getTotalSpent() {
if (!!cid && d.campaignsSpent[cid] && 'spent' in d.campaignsSpent[cid]) {
return d.campaignsSpent[cid].spent.toFixed(3);
}
return getAdvStats('spent', 3);
}
function getAvgCTR() {
if (!totalClicks.total || !totalAdsShown.total) return '0';
return ((totalClicks.total / totalAdsShown.total) * 100).toFixed(3);
}
}
function isValidGraphData(graph) {
if (!isDefined(graph)) {
return false;
}
return graph.impressions.datasets[0].data.length > 0;
}
function getCSClosure(g, obj) { return function (g, obj) { return function () { new ColumnSquares(g, obj); }; }(g, obj); }
function getWeekSpent(d, cid) {
var a = d.campaignsSpent,
v = 0;
if (!!cid) {
if (!!a[cid] && a[cid].spent) v = a[cid].spent;
} else {
for (var k in a) {
if (!!a[k] && !!a[k].spent) v += a[k].spent;
}
}
return Math.round(v * 100) / 100;
}
function getSpentPerDay(s, days) { return Math.round((s / days) * 100) / 100; }
function getTotalAdsShown(d, cid) {
var uL = !!cid ? d.campaignLevel[cid] : d.userLevel[uid],
list = [];
for (var key in uL) {
list.push(uL[key].impressions);
}
return getTotalObject(list);
}
function getTotalClicks(d, cid) {
var uL = !!cid ? d.campaignLevel[cid] : d.userLevel[uid],
list = [];
for (var key in uL) { list.push(uL[key].clicks); }
return getTotalObject(list);
}
function getTotalConversions(d, cid) {
var uL = !!cid ? d.campaignLevel[cid] : d.userLevel[uid],
list = [];
for (var key in uL) { list.push(uL[key].conversions); }
return getTotalObject(list);
}
function getTotal(array) {
var v = 0;
forEach(array, p);
return v;
function p(t) { v += t; }
}
function getAverage(array) {
var v = 0;
forEach(array, p);
return Math.round((v / array.length) * 10000) / 10000;
function p(t) { v += t; }
}
function getTotalObject(a) { return { 'total': getTotal(a), 'average': getAverage(a) }; }
function getActiveAdsObj(list) {
var count = 0,
total = list.length;
forEach(list, p);
return { 'count': count, 'percent': count != 0 ? Math.floor((count / total) * 100) : 0 };
function p(t) {
if (!!t.data && t.data.active === true) {
count++;
}
}
}
function getAdGroupsObj(adsList, adGroupsList) {
var count = adGroupsList.length,
adCountList = [];
forEach(adGroupsList, p);
return { 'count': count, 'average': getAverage(adCountList) };
function p(t, d, i) { adCountList[i] = getAdCount(t.id); }
function getAdCount(adGroupID) {
var aCount = 0;
forEach(adsList, isAdMatch);
return aCount;
function isAdMatch(ad) { if (ad.group == adGroupID) aCount++; }
}
function getAverage(array) {
var len = array.length,
val = 0;
for (var i = 0; i < len; i++) { val += array[i]; }
return Math.floor((val / len) * 10) / 10;
}
}
function getActiveListObj(list) {
var count = 0,
total = list.length;
forEach(list, p);
return { 'count': count, 'percent': count != 0 ? Math.floor((count / total) * 100) : 0 };
function p(t) { if (t.active === true) count++; }
}
function getPendingListObj(list) {
var count = 0,
total = list.length;
forEach(list, p);
return { 'count': count, 'percent': count != 0 ? Math.floor((count / total) * 100) : 0 };
function p(t) { if (t.active === false && t.rejected != true) count++; }
}
function getSquareObj(template, mV, sT, sV) { return { 'template': template, 'data': { 'mainValue': mV, 'subTitle': sT, 'subValue': sV } }; }
function getSquare(k) {
var s = squares[k];
if (!!s && !!s.template) {
return getElementFromString(render(s.template, isDefined(s.data) ? s.data : {}));
} else {
return null;
}
}
function GraphItem(impressions, clicks, conversions) {
this.impressions = impressions;
this.clicks = clicks;
this.conversions = conversions;
}
function getGraphData(days, cid) {
var impressions = getValues(days, 'impressions'),
clicks = getValues(days, 'clicks'),
conversions = getValues(days, 'conversions');
return {
'impressions': {
labels: impressions.labels,
datasets: [
{
fillColor: 'rgba(151,187,205,0.5)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
pointStrokeColor: '#fff',
data: impressions.data
}
]
},
'clicks': {
labels: clicks.labels,
datasets: [
{
fillColor: 'rgba(151,187,205,0.5)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
pointStrokeColor: '#fff',
data: clicks.data
}
]
},
'conversions': {
labels: conversions.labels,
datasets: [
{
fillColor: 'rgba(151,187,205,0.5)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
pointStrokeColor: '#fff',
data: conversions.data
}
]
}
};
function getDate(s) {
var v = s.split('-');
return { 'year': v[0], 'day': v[2], 'month': getMonthName(v[1]) };
}
function getValues(d, key) {
var data = [],
labels = [];
for (var k in d) {
var t = d[k],
date = getDate(k);
data.push(t[key]);
labels.push(date.month + ' ' + date.day + ' ' + date.year);
}
return { 'data': data, 'labels': labels };
}
}
function getTabbedObj(e) {
return {
'data': [
{ 'title': 'General', 'element': e.general, 'initFn': initGeneral },
{ 'title': 'Ads', 'element': e.ads, 'initFn': initAds },
{ 'title': 'Campaigns', 'element': e.campaigns, 'initFn': initCampaigns }
]
};
}
function getGraphsObj(e) {
return {
'data': [
{ 'title': 'Impressions', 'element': e.impressions },
{ 'title': 'Clicks', 'element': e.clicks },
{ 'title': 'Conversions', 'element': e.conversions }
],
'config': {
'className': 'toggle'
}
};
}
function filterDataByCampaign(d, cid) {
if (!cid) return d;
var o = {
campaignsList: [],
reporting: {
adLevel: d.reporting.adLevel,
userLevel: d.reporting.userLevel,
campaignLevel: {},
}
};
loop(d, function (k, v) { if (k !== 'reporting' && k !== 'campaignsList') o[k] = v; });
loop(d.campaignsList, function (_, v) { if (v.id === cid) { o.campaignsList.push(v); return false; } });
o.reporting.campaignLevel[cid] = d.reporting.campaignLevel[cid];
return o;
}
function updateFromDate(e) {
if (!e) {
return;
}
data.fromDate = e.year + '-' + getFormattedDate(e.monthValue) + '-' + getFormattedDate(e.date);
getAdvStats();
}
function updateToDate(e) {
if (!e) {
return;
}
data.toDate = e.year + '-' + getFormattedDate(e.monthValue) + '-' + getFormattedDate(e.date);
getAdvStats();
}
function getFormattedDate(v) { if (v > 9) return v; else return '0' + v; }
function getDownloadSource(d, fmt) {
var url = '/api/v1/statsReportRange/' + core.userID + '/' + d.fromDate + '/' + d.toDate + '/' + d.fromDate + '_' + d.toDate + '_report.' + fmt;
if(!d.incDomains) url += '?noDomains=true';
return url;
}
function isValidReportRange() { return data.fromDate !== '' && data.toDate !== ''; }
function downloadReport(e) {
preventDefault(e);
if (e === '-') return;
if (e === 'SR') {
schedDlg.show(null, function (data, canceled) {
if (canceled) return true;
new HttpRequest('POST', '/api/v1/scheduledReports/' + core.userID,
new scheduledReportOpts(core.userID, null, 'advertiser', data.frequency, data.format, data.recipient), 'json', 'json', done);
return true;
});
return;
}
if (isValidReportRange()) {
ga('send', 'event', 'Stats', 'Download report', 'AID ' + core.userID + ': ' + data.fromDate + ' to ' + data.toDate);
new HttpRequest('GET', getDownloadSource(data, e), null, null, 'json', done);
} else {
core.notifications.setError('Error: please set a valid date range.');
}
function done(v) {
if (v.status === 'success') return pop('Pop!', 'Downloads', '/dashboard/downloads');
if (v.error) return core.notifications.setError('Error: ' + v.error);
}
}
function getLastWeek() {
var d = new Date();
d.setHours(-(24 * 7) + d.getHours());
return d;
}
function exit() {
if (apiManager) apiManager.abort();
if (isDefined(element)) removeChild(target, element);
if (events) events.reset();
if (elements) elements.exit();
housekeeping();
}
function housekeeping() { core = target = data = returnCB = events = template = elements = data = squares = apiManager = null; }
};