Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 173 additions & 128 deletions Code.gs
Original file line number Diff line number Diff line change
@@ -1,65 +1,113 @@
function getConfig(request) {
var service = getService();
var response = JSON.parse(UrlFetchApp.fetch("https://graph.facebook.com/v2.10/me/accounts", {

// Constructing the URL for Ad Account Insights API request
var url = 'https://graph.facebook.com/v19.0/me/adaccounts?fields=name,id';

// Fetching ad account data from Facebook API
var response = UrlFetchApp.fetch(url, {
method: 'GET',
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
}));
});

// Parsing the response to extract relevant data
var adAccountData = JSON.parse(response.getContentText());

// Creating the config object with options for Ad Account ID
var config = {
configParams: [
{
type: "SELECT_SINGLE",
name: "pageID",
displayName: "Page ID",
helpText: "Please select the Page ID for which you would like to retrieve the Statistics.",
name: "adAccountID",
displayName: "Ad Account ID",
helpText: "Please select the Ad Account ID for which you would like to retrieve the Statistics.",
options: []
}
],
dateRangeRequired: true
};
response.data.forEach(function(field) {

// Populating the options for Ad Account ID based on the ad account data
adAccountData.data.forEach(function(account) {
config.configParams[0].options.push({
label: field.name,
value: field.id
label: account.name,
value: account.id
});
})
});

// Return the config object
return config;
};
}


var facebookSchema = [
{
name: 'timestamp',
label: 'Timestamp',
name: 'date_start',
label: 'Date start',
dataType: 'STRING',
semantics: {
conceptType: 'DIMENSION'
}
},
{
name: 'timestampWeek',
label: 'Timestamp Week',
name: 'date_end',
label: 'Date end',
dataType: 'STRING',
semantics: {
conceptType: 'DIMENSION'
}
},
{
name: 'timestampMonth',
label: 'Timestamp Month',
name: 'campaign_name',
label: 'Campaign Name',
dataType: 'STRING',
semantics: {
conceptType: 'DIMENSION'
}
},
{
name: 'likes',
label: 'Likes Total',
name: 'clicks',
label: 'Clicks',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
},
{
name: 'cpm',
label: 'CPM',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
},
{
name: 'ctr',
label: 'CTR',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
},
{
name: 'cpc',
label: 'CPC',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
},
{
name: 'frequency',
label: 'FREQUENCY',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
},

{
name: 'impressions_daily',
label: 'Impressions',
Expand All @@ -69,149 +117,146 @@ var facebookSchema = [
}
},
{
name: 'engagements_daily',
label: 'Page Post Engagements',
name: 'purchases',
label: 'Purchase',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
},{
name: 'spend',
label: 'Spend',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC'
}
}

];


function getSchema(request) {
return {schema: facebookSchema};
};


function getData(request) {
var service = getService();


// Helper function to adjust dates
function dateDelta(dObj, num) {
if (isNaN(num)) {
var dateStart = new Date(dObj);
} else {
var dateStart = new Date(dObj);
var dateStart = new Date(dateStart.setDate(dateStart.getDate() + num));
var dateStart = new Date(dObj);
if (!isNaN(num)) {
dateStart.setDate(dateStart.getDate() + num);
}
var dd = dateStart.getDate();
var mm = dateStart.getMonth()+1; //January is 0!

var mm = dateStart.getMonth() + 1; // January is 0
var yyyy = dateStart.getFullYear();
if(dd<10){
dd='0'+dd;
}
if(mm<10){
mm='0'+mm;
}
var dateStart = yyyy + "-" + mm + "-" + dd;
return dateStart;
}

var gStartDate = new Date(request.dateRange.startDate);
var gStartDate = new Date(dateDelta(gStartDate, -1));
var gEndDate = new Date(request.dateRange.endDate);
var gEndDate = new Date(dateDelta(gEndDate, +1));
var gRange = Math.ceil(Math.abs(gEndDate - gStartDate) / (1000 * 3600 * 24));
var gBatches = Math.ceil(gRange / 92);

if (gBatches < 2) {
var batch = [{"method": "GET", "relative_url": request.configParams.pageID + "/insights/page_fans,page_impressions,page_post_engagements?since=" + dateDelta(gStartDate) + "&until=" + dateDelta(gEndDate)}];
//console.log(batch);
} else {
batch = [];
var iterRanges = gRange / gBatches;

for (i = 0; i < gBatches; i++) {
var iterStart = dateDelta(gStartDate, (iterRanges * i));
if (i == (gBatches - 1)) {
var iterEnd = dateDelta(gEndDate);
} else {
var iterEnd = dateDelta(gStartDate, (iterRanges * (i + 1)) + 1);
}
batch.push({"method": "GET", "relative_url": request.configParams.pageID + "/insights/page_fans,page_impressions,page_post_engagements?since=" + iterStart + "&until=" + iterEnd})
}
//console.log(batch);
return yyyy + "-" + (mm < 10 ? '0' + mm : mm) + "-" + (dd < 10 ? '0' + dd : dd);
}

// Fetch the data with UrlFetchApp
var url = "https://graph.facebook.com?include_headers=false&batch=" + encodeURIComponent(JSON.stringify(batch))

var response = JSON.parse(UrlFetchApp.fetch(url, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}

// Encode parameters
var actionAttributionWindows = encodeURIComponent(JSON.stringify(['7d_click'])); //change attribution parameter here. If you prefer standard meta attribution use ['7d_click','1d_view']
var fields = encodeURIComponent('impressions,ctr,cpc,frequency,cpm,clicks,spend,campaign_id,campaign_name,adset_name,action_values');
var filtering = encodeURIComponent(JSON.stringify([{ field: "action_type", operator: "IN", value: ["purchase"] }]));
var timeRange = encodeURIComponent(JSON.stringify({
since: dateDelta(request.dateRange.startDate),
until: dateDelta(request.dateRange.endDate)
}));

// Prepare the schema for the fields requested.

var baseUrl = `https://graph.facebook.com/v21.0/${request.configParams.adAccountID}/insights/`;
var url = `${baseUrl}?action_attribution_windows=${actionAttributionWindows}&level=adset&fields=${fields}&filtering=${filtering}&time_range=${timeRange}&limit=5000`;

var dataSchema = [];
var data = [];

// Populate the schema based on requested fields
request.fields.forEach(function(field) {
for (var i = 0; i < facebookSchema.length; i++) {
if (facebookSchema[i].name === field.name) {
dataSchema.push(facebookSchema[i]);
break;
facebookSchema.forEach(function(schemaField) {
if (schemaField.name === field.name) {
dataSchema.push(schemaField);
}
}
});
});
var data = [];

// Prepare the tabular
// console.log("Response: %s", response);
// response.data[0].values.forEach(function(day, i) {
response.forEach(function(resp) {
var resp = JSON.parse(resp.body);
resp.data[0].values.forEach(function(day, i){
var values = [];
dataSchema.forEach(function(field) {
switch(field.name) {
case 'timestamp':
var fbTime = day.end_time;
var myTime = fbTime.substring(0,4) + fbTime.substring(5,7) + fbTime.substring(8,10);
values.push(myTime);
break;
case 'timestampWeek':
var myTime = new Date(day.end_time);
var startTime = new Date(myTime.getFullYear(), 00, 01);
var deltaTime = Math.abs(myTime - startTime);
var weekTime = ("0" + Math.ceil((deltaTime / (1000 * 3600 * 24)) / 7)).slice(-2);
values.push(myTime.getFullYear() + weekTime);
break;
case 'timestampMonth':
var fbTime = day.end_time;
var myTime = fbTime.substring(0,4) + fbTime.substring(5,7);
values.push(myTime);
break;
case 'likes':
values.push(day.value);
break;
case 'impressions_daily':
values.push(resp.data[1].values[i].value);
break;
case 'engagements_daily':
values.push(resp.data[4].values[i].value);
break;
default:
values.push('');
}
});
data.push({
values: values
});

// Loop to handle pagination
while (url) {
console.info("Fetching data from URL: ", url); // Log current request URL
var response = UrlFetchApp.fetch(url, {
method: 'GET',
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});

var jsonResponse = JSON.parse(response.getContentText());
console.info("API Response: ", JSON.stringify(jsonResponse)); // Log API response

// Process data from the current page
jsonResponse.data.forEach(function(entry) {
var values = [];
dataSchema.forEach(function(field) {
switch (field.name) {
case 'date_start':
values.push(entry.date_start);
break;
case 'date_end':
values.push(entry.date_stop);
break;
case 'campaign_name':
values.push(entry.campaign_name);
break;
case 'clicks':
values.push(parseInt(entry.clicks));
break;
case 'cpm':
values.push(parseFloat(entry.cpm));
break;
case 'ctr':
values.push(parseFloat(entry.ctr));
break;
case 'cpc':
values.push(parseFloat(entry.cpc));
break;
case 'frequency':
values.push(parseFloat(entry.frequency));
break;
case 'impressions_daily':
values.push(parseInt(entry.impressions));
break;
case 'spend':
values.push(parseFloat(entry.spend));
break;
case 'purchases':
var purchaseValue = entry.action_values?.find(a => a.action_type === 'purchase')?.['7d_click'] || ''; // change to (a => a.action_type === 'purchase')?.value || ''; for standard 7d_click, 1d_view attribution
values.push(parseFloat(purchaseValue) || '');
break;
default:
values.push('');
}
});
data.push({ values: values });
});

//console.log("Data Schema: %s", dataSchema);
//console.log("Data: %s", data);
// Log current batch data
console.info("Processed Data Batch: ", JSON.stringify(data));


// Return the tabular data for the given request.
// Check for next page
url = jsonResponse.paging?.next || null;
console.info("Next Page URL: ", url); // Log next page URL
}

// Return the aggregated data
console.info("Final Data Schema: ", JSON.stringify(dataSchema));
console.info("Final Data Rows: ", JSON.stringify(data));
return {
schema: dataSchema,
rows: data
};
};
}

function isAdminUser() {
if (Session.getEffectiveUser().getEmail() == "#########@gmail.com") {
if (Session.getEffectiveUser().getEmail() == "xxxxx@xxxxxxx.xxx") {
return true;
}
}
Expand Down
Loading