Skip to content

Commit

Permalink
Update the Google Sheets API calls to v4
Browse files Browse the repository at this point in the history
This PR updates the API calls to Google Sheets to use v4 instead of v3
which was shut down August 2.

Fixes #217, Fixes #218, and Fixes #222 with some caveats, see below.

Full disclosure: I have no idea what I'm doing really with JS, so there
are likely to be improvements to be made.

Potentially Outstanding Issues:
* Uses my personal API Key. Its restricted to Google Sheets API only,
  but I would prefer it be someone else's. Not really a problem right now.
* Each Sheet is loaded in full through a GET request, returned entierly
  as JSON. This results in the load being a bit slow (and a fair amount
  of data comes down, the Official is 33MB of plain text returned for
  example. The load only happens once per sheet.
* I left in all the old code where I could in case improvements can be
  made.
* I do not know JS, so any improvements, especially in the Promises
  area are likely very welcome.
  • Loading branch information
Lnaden committed Aug 21, 2021
1 parent def374d commit 76b8a79
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 53 deletions.
88 changes: 75 additions & 13 deletions app/services/sheet-loader.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

angular.module("app").factory("googleSheetLoader", googleSheetService);

googleSheetService.$inject = ["$q"];
function googleSheetService($q) {
googleSheetService.$inject = ["$q", "$http"];
function googleSheetService($q, $http) {
// Loads a published google sheet by ID. Example usage:
// googleSheetService(sheetId).then(function (data) {})

Expand All @@ -27,7 +27,7 @@
// 2: Empty cells will be omitted from the individual objects lacking entries for those
// cells
return {
loadIndex: partialLoader.bind(null, $q),
loadIndex: partialLoader.bind(null, $q, $http),
};
}

Expand All @@ -36,6 +36,20 @@
var jsonpcount = 0;
var sheets = {};

// LNN: Version to setup the GoogleSheets v4 API
function getSheetsJsonFromAPI($q, $http, url){

var deferred = $q.defer();

$http.get(url)
.success(function (data){
deferred.resolve(data)
});

return deferred.promise

};

// Get AJAX using jsonp
function getSheetsJsonp($q, url) {
var callbackName = "__jsonpcallback" + jsonpcount++;
Expand Down Expand Up @@ -84,19 +98,67 @@
});
}

function loadIndex($q, id) {
var url = "https://spreadsheets.google.com/feeds/worksheets/" + id + "/public/full";
// LNN: Uses the v4 of the Google Sheets API to return the same format as the v3 code
function parseLineAPI4Data(ws, data) {
// Slice the column headers
var columnHeaders = {};

// Iterate over each row from the 1st data (2nd row) onward
data.slice(1).forEach(function (line) {
var parsedObject = {};
// Iterate over each column of the line, hidden under a "values" key
line.values.forEach(function (cellData, columnIndex) {
// Cell data in formattedValue
// "effectiveValue" is a function of cell data type, e.g 'stringValue' or 'numberValue'
// formattedValue seems to be a consistent string
var val = cellData.formattedValue;
// Grab the column name
var col = data[0].values[columnIndex].formattedValue;

parsedObject[col] = val;

});
ws.push(parsedObject);
});
}

function loadIndex($q, $http, id) {
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + id + "?key=AIzaSyCrZ0HaEQGNvEChWhgCUE2DwzImwQVaW6U&includeGridData=True"

// Step 1: Get a list of all the worksheets in the spreadsheet
return getSheetsJsonp($q, url)
// return getSheetsJsonp($q, url) // Old Method
return getSheetsJsonFromAPI($q, $http, url)
.then(function (indexData) {
return {
timestamp: indexData.timestamp,
loadSheets: loadSheets.bind(null, $q, indexData.data),
};
});
return $q.all(indexData).then(function (indexData) {
return {
// Sheets API does not get last modified timestamp. The Drive API might
// Assume need fresh for now to get it working, may change later (LNN: August 20, 2021)
// timestamp: indexData.timestamp,
timestamp: new Date(30000, 0), // Assuming some ludicrous year to force reload
// loadSheets: loadSheets.bind(null, $q, indexData), // Old method
loadSheets: loadSheetsFromData.bind(null, indexData),
};
});
});
}

function loadSheetsFromData(indexData) {
// No need for promise here since we already got data from index
var worksheets = {};
indexData.sheets.forEach(function (worksheet) {
console.log(worksheet)
var name = worksheet.properties.title; // Sheet Title
var ws = worksheets[name] = [];
// Sheet data in [data][0][rowData]
parseLineAPI4Data(ws, worksheet.data[0].rowData)
});
console.log("Building sheets")
console.log(worksheets)
return worksheets;
}



function loadSheets($q, indexData) {
var worksheetPromises = [];
var worksheets = {};
Expand Down Expand Up @@ -128,13 +190,13 @@
}

// Cache results for each id
function load($q, id, args) {
function load($q, $http, id, args) {
args = args || {};
if ( args.noCache ) {
delete sheets[id];
}

sheets[id] = sheets[id] || loadIndex($q, id);
sheets[id] = sheets[id] || loadIndex($q, $http, id);

return sheets[id];
}
Expand Down
24 changes: 11 additions & 13 deletions app/services/sheet-manager.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,17 @@ function loadLive(args) {
if ( remoteTimestamp > timestamp ) {
if (logging) console.log("We're out of date", timestamp);
// We're out of date, load remotely
return sheetIndex.loadSheets()
.then(function (sheetData) {
if (logging) console.log("Sheet data loaded", sheetData);
updateTimestamp({
store: store,
sheetId: sheetId,
timestamp: remoteTimestamp,
});

var cacheId = generateCacheId(sheetId);
store.set(cacheId, sheetData);
return sheetData;
});
var sheetData = sheetIndex.loadSheets() // No longer a promise, can access directly
if (logging) console.log("Sheet data loaded", sheetData);
updateTimestamp({
store: store,
sheetId: sheetId,
timestamp: remoteTimestamp,
});

var cacheId = generateCacheId(sheetId);
store.set(cacheId, sheetData);
return sheetData;
} else {
if (logging) console.log("We're fresh", timestamp);
// Our data are fresh, load from cache
Expand Down
2 changes: 1 addition & 1 deletion build/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
<script src="thirdparty/dirPagination/dirPagination.js"></script>
<script src="thirdparty/angular-local-storage/angular-local-storage.js"></script>

<script src="js/app-a04a87739c.js"></script>
<script src="js/app-cc8a7134e5.js"></script>
</body>
</html>
112 changes: 86 additions & 26 deletions build/js/app-a04a87739c.js → build/js/app-cc8a7134e5.js
Original file line number Diff line number Diff line change
Expand Up @@ -2888,8 +2888,8 @@

angular.module("app").factory("googleSheetLoader", googleSheetService);

googleSheetService.$inject = ["$q"];
function googleSheetService($q) {
googleSheetService.$inject = ["$q", "$http"];
function googleSheetService($q, $http) {
// Loads a published google sheet by ID. Example usage:
// googleSheetService(sheetId).then(function (data) {})

Expand All @@ -2912,7 +2912,7 @@
// 2: Empty cells will be omitted from the individual objects lacking entries for those
// cells
return {
loadIndex: partialLoader.bind(null, $q),
loadIndex: partialLoader.bind(null, $q, $http),
};
}

Expand All @@ -2921,6 +2921,20 @@
var jsonpcount = 0;
var sheets = {};

// LNN: Version to setup the GoogleSheets v4 API
function getSheetsJsonFromAPI($q, $http, url){

var deferred = $q.defer();

$http.get(url)
.success(function (data){
deferred.resolve(data)
});

return deferred.promise

};

// Get AJAX using jsonp
function getSheetsJsonp($q, url) {
var callbackName = "__jsonpcallback" + jsonpcount++;
Expand Down Expand Up @@ -2969,19 +2983,67 @@
});
}

function loadIndex($q, id) {
var url = "https://spreadsheets.google.com/feeds/worksheets/" + id + "/public/full";
// LNN: Uses the v4 of the Google Sheets API to return the same format as the v3 code
function parseLineAPI4Data(ws, data) {
// Slice the column headers
var columnHeaders = {};

// Iterate over each row from the 1st data (2nd row) onward
data.slice(1).forEach(function (line) {
var parsedObject = {};
// Iterate over each column of the line, hidden under a "values" key
line.values.forEach(function (cellData, columnIndex) {
// Cell data in formattedValue
// "effectiveValue" is a function of cell data type, e.g 'stringValue' or 'numberValue'
// formattedValue seems to be a consistent string
var val = cellData.formattedValue;
// Grab the column name
var col = data[0].values[columnIndex].formattedValue;

parsedObject[col] = val;

});
ws.push(parsedObject);
});
}

function loadIndex($q, $http, id) {
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + id + "?key=AIzaSyCrZ0HaEQGNvEChWhgCUE2DwzImwQVaW6U&includeGridData=True"

// Step 1: Get a list of all the worksheets in the spreadsheet
return getSheetsJsonp($q, url)
// return getSheetsJsonp($q, url) // Old Method
return getSheetsJsonFromAPI($q, $http, url)
.then(function (indexData) {
return {
timestamp: indexData.timestamp,
loadSheets: loadSheets.bind(null, $q, indexData.data),
};
});
return $q.all(indexData).then(function (indexData) {
return {
// Sheets API does not get last modified timestamp. The Drive API might
// Assume need fresh for now to get it working, may change later (LNN: August 20, 2021)
// timestamp: indexData.timestamp,
timestamp: new Date(30000, 0), // Assuming some ludicrous year to force reload
// loadSheets: loadSheets.bind(null, $q, indexData), // Old method
loadSheets: loadSheetsFromData.bind(null, indexData),
};
});
});
}

function loadSheetsFromData(indexData) {
// No need for promise here since we already got data from index
var worksheets = {};
indexData.sheets.forEach(function (worksheet) {
console.log(worksheet)
var name = worksheet.properties.title; // Sheet Title
var ws = worksheets[name] = [];
// Sheet data in [data][0][rowData]
parseLineAPI4Data(ws, worksheet.data[0].rowData)
});
console.log("Building sheets")
console.log(worksheets)
return worksheets;
}



function loadSheets($q, indexData) {
var worksheetPromises = [];
var worksheets = {};
Expand Down Expand Up @@ -3013,13 +3075,13 @@
}

// Cache results for each id
function load($q, id, args) {
function load($q, $http, id, args) {
args = args || {};
if ( args.noCache ) {
delete sheets[id];
}

sheets[id] = sheets[id] || loadIndex($q, id);
sheets[id] = sheets[id] || loadIndex($q, $http, id);

return sheets[id];
}
Expand Down Expand Up @@ -3072,19 +3134,17 @@ function loadLive(args) {
if ( remoteTimestamp > timestamp ) {
if (logging) console.log("We're out of date", timestamp);
// We're out of date, load remotely
return sheetIndex.loadSheets()
.then(function (sheetData) {
if (logging) console.log("Sheet data loaded", sheetData);
updateTimestamp({
store: store,
sheetId: sheetId,
timestamp: remoteTimestamp,
});

var cacheId = generateCacheId(sheetId);
store.set(cacheId, sheetData);
return sheetData;
});
var sheetData = sheetIndex.loadSheets() // No longer a promise, can access directly
if (logging) console.log("Sheet data loaded", sheetData);
updateTimestamp({
store: store,
sheetId: sheetId,
timestamp: remoteTimestamp,
});

var cacheId = generateCacheId(sheetId);
store.set(cacheId, sheetData);
return sheetData;
} else {
if (logging) console.log("We're fresh", timestamp);
// Our data are fresh, load from cache
Expand Down

0 comments on commit 76b8a79

Please sign in to comment.