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 = '
', fromDate = 'from:
', toDate = 'to:
', incDomains = 'Include Domains:
', downloadReport = '
Download detailed report:

' + fromDate + toDate + incDomains + '
', cmpListDD = '
', sbd = '

7-day stats breakdown

', // Silent.. but deadly. frrrt. tabbed = '
', note = '

The data may be cached and slightly less than actual values.

'; this.index = '
' + 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; } };