---
title: 'Google Sheet Api (2)'
disqus: hackmd
---
###### tags: `Sheet API`
Google Sheet Api (2)
===
[TOC]
## 筆記目的
在前篇筆記中已經完成串接Google Ad APi,這個筆記則是更深入的去理解一下google
sheet api的架構,讓人可以完成read write delete update 等動作
具體請參考:
* [A1 notation](https://developers.google.com/sheets/api/guides/concepts#expandable-1)
* [基本寫入使用postman](https://justcode.ikeepstudying.com/2017/05/java-google-sheet-api-v4-%E5%85%A5%E9%97%A8%E4%BA%8C-%EF%BC%9A%E5%9F%BA%E6%9C%AC%E5%86%99%E5%85%A5/)
## 程式碼部分
第一篇文章並沒有將共同會重複使用的部分獨立成為class,這篇文章將會示範如何將共同使用的部分獨立出來,使用的方式很簡單,在main function裡面使用`Sheets googlesheetservice=GoogleSheetsAuthorizedServiceAgent.getAuthorizedSheetsService();`之後拿到service之後便可以對
### 專案架構

### GoogleSheetsAuthorizedServiceAgent
```java=
package com.google.sheet.agent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.sheets.v4.Sheets;
import com.google.api.services.sheets.v4.SheetsScopes;
public class GoogleSheetsAuthorizedServiceAgent {
private static final String APPLICATION_NAME = "ComprehensiveServiceCenter-GoogleSheetsAPI";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final String TOKENS_DIRECTORY_PATH = "tokens";
/*
* https://developers.google.com/sheets/api/quickstart/java
*/
/**
* Global instance of the scopes required by this quickstart.
* If modifying these scopes, delete your previously saved tokens/ folder.
* 這邊可以控制呼叫Sheet API 的使用權限
*/
private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.DRIVE);
/*
* 此份檔案得從Google Api console 後台 開啟Google Sheets服務後就可以取得該份檔案
* 將此份檔案放在 /src/main/resources 即可
*/
private static final String CREDENTIALS_FILE_PATH = "/credentialsforsheet.json";
/**
* Creates an authorized Credential object.
* @param HTTP_TRANSPORT The network HTTP Transport.
* @return An authorized Credential object.
* @throws IOException If the credentials.json file cannot be found.
*/
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
//Google 官方範例提供方式
InputStream in = GoogleSheetsAuthorizedServiceAgent.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
/*
* 呼叫使用Drive Api 會去認證授權給定一組Token 並會儲存下來一份檔案(StoredCredential)
* 但是要注意,因為這個StoredCredential 當第一次呼叫時 都得手動連結一個 授權網址 如下範例
* Please open the following address in your browser:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=908400561361-9lsm2ilr9f066tcndks1v0nkofp4tibb.apps.googleusercontent.com&redirect_uri=http://localhost:55423/Callback&response_type=code&scope=https://www.googleapis.com/auth/drive
* 因此當要佈署到linux上時,得一併將測試環境已經授權過的StoredCredential 一併copy過去對應的路徑上
* 否則得手動再授權一次, 且該授權動作還會callback 因為callback URL都是=localhost 因此無法成功 ==> 結論還是乖乖複製吧
*/
File tokenDirectory = new java.io.File(TOKENS_DIRECTORY_PATH);
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(tokenDirectory))
.setAccessType("offline")
.build();
//Google官方sample,屬於指定port的方式 當取得google確認授權後的callback url:port
// Build flow and trigger user authorization request.
// GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
// HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
// .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
// .setAccessType("offline")
// .build();
// LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
// return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
//也可以不指定port,隨機配給一組port
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}
public static Sheets getAuthorizedSheetsService() throws GeneralSecurityException, IOException {
// Build a new authorized API client service.
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Sheets service = new Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
return service;
}//End function
}//End class
```
### main for read
```java=
package com.google.sheet;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import com.google.api.services.sheets.v4.Sheets;
import com.google.api.services.sheets.v4.model.ValueRange;
import com.google.sheet.agent.GoogleSheetsAuthorizedServiceAgent;
public class SheetTest {
public static void main(String[] args) throws GeneralSecurityException, IOException {
Sheets googlesheetservice=GoogleSheetsAuthorizedServiceAgent.getAuthorizedSheetsService();
final String spreadsheetId = "1z078C9BH5rjQ01SYhlWLNXrscflCaua3lVx3dPi3l1w";
final String range = "Class Data!A2:B";
ValueRange response = googlesheetservice.spreadsheets().values()
.get(spreadsheetId, range)
.execute();
List<List<Object>> values = response.getValues();
if (values == null || values.isEmpty()) {
System.out.println("No data found.");
} else {
System.out.println("Name, score");
for (List row : values) {
// Print columns A and E, which correspond to indices 0 and 4.
System.out.printf("%s, %s\n", row.get(0), row.get(1));
} //end for loop
} //end for else
}//end for main
}
```
### main for write and update
```java=
package com.google.sheet;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import com.google.api.services.sheets.v4.Sheets;
import com.google.api.services.sheets.v4.model.UpdateValuesResponse;
import com.google.api.services.sheets.v4.model.ValueRange;
import com.google.sheet.agent.GoogleSheetsAuthorizedServiceAgent;
public class SheetTest {
public static void main(String[] args) throws GeneralSecurityException, IOException {
Sheets googlesheetservice=GoogleSheetsAuthorizedServiceAgent.getAuthorizedSheetsService();
final String spreadsheetId = "1z078C9BH5rjQ01SYhlWLNXrscflCaua3lVx3dPi3l1w";
final String range = "Class Data!A2:B4";
List<List<Object>> values1 = Arrays.asList(
Arrays.asList("2020-06-25", 300.0),
Arrays.asList("20200625", 8888)
);
ValueRange body = new ValueRange()
.setValues(values1);
UpdateValuesResponse result =
googlesheetservice.spreadsheets().values().update(spreadsheetId, range, body)
.setValueInputOption("RAW")
.execute();
}//end for main
}//end for SheetTest class
```
## write spreadsheet function
```java=
private String writeDataInSpreadsheetByUpdateMode(Sheets sheetsService, String spreadsheetId, String range, List<List<Object>> updateValues) throws InterruptedException {
//將預備寫入陣列的值寫入到Spreadsheet
/**
* 要將值寫入到Spreadsheet必須符合特定的格式,詳細請參考底下Google API網頁
* https://developers.google.com/sheets/api/guides/values
* 由於底下是寫入單欄但不同列的值所以格式必須符合
*
* List<List<Object>> values = Arrays.asList(
* Arrays.asList("A"),//第1列
* Arrays.asList("B"),//第2列
* Arrays.asList("C"),//第3列
* ...........
* );
*/
String replyStatus = "";
boolean retry = true;
int retryCount = 0;
while(retry) {
try {
if(sheetsService == null) {sheetsService = GoogleSheetsAuthorizedServiceAgent.getAuthorizedSheetsService();}
ValueRange finishBody = new ValueRange()
.setValues(updateValues);
UpdateValuesResponse updateFinishedResult =
sheetsService.spreadsheets().values().update(spreadsheetId, range, finishBody)
.setValueInputOption("RAW")
.execute();
logger.info("writeDataInSpreadsheetByUpdateMode<" + spreadsheetId + "," + range + "> Finish....");
retry = false;
replyStatus = "";
} catch (GoogleJsonResponseException jgre) {
replyStatus = EXECUTION_STATUS_SpreadsheetByUpdateMode_GoogleJsonResponseException;
logger.error("writeDataInSpreadsheetByUpdateMode()...GoogleJsonResponseException Happened...");
logger.error(jgre.toString());
if(jgre.toString().indexOf("RESOURCE_EXHAUSTED") !=-1) {
logger.warn("Sheets service Quota exceeded....getTargetCampaignUpdateRangeFromSpreadsheetOfSettingTable()");
retryCount++;
retry = (retryCount <= MaxRetryCount) ? true : false;
if(retry) {
retrySleepDuration = MathematicsBasicOperationAgent.getIntegerRandomNumberInRange(retrySleepMinimumSeconds, retrySleepMaximumSeconds);
logger.error("[Retry Count: " + retryCount + "] will sleep " + retrySleepDuration + " secs then try again");
Thread.sleep(retrySleepDuration * 1000L);
}
}
} catch(Exception e) {
replyStatus = EXECUTION_STATUS_SpreadsheetByUpdateMode_Exception;
logger.error("writeDataInSpreadsheetByUpdateMode()...Exception Happened...");
logger.error(e.toString());
}//End catch
}//End while
return replyStatus;
}//End
```
## get usefulrange code example
```java=
private List<List<Object>> getListObjectDataOfAppendMode(
SpreadsheetFramePropertiesDataBean currentCampaign_SpreadsheetFramePropertiesDataBean,
List<CommonPerformanceReportBean> currentCampaignPer,
HashMap<String, CommonPerformanceReportBean> currentCampaignPerViewByDateMap,
HashMap<String, GAds_DailyCampaignSettingEntity> currentCampaign_DailySettingMap,
String aiKpiType) {
List<List<Object>> result = new ArrayList<List<Object>>();
// List<List<Object>> appendValues =
// Arrays.asList(
// Arrays.asList("2020-06-25", 300.0, "Cell_C2", "", "Cell_E2"),
// Arrays.asList("20200625", 8888, "Cell_C3", "", ""),
// Arrays.asList(),
// Arrays.asList("2020/06/25", 666.6, "Cell_C5", "", "Cell_E5", "Cell_F5")
// );
List<GoogleSpreadsheet_Ai_CPV_DataBean> cpvDataResultList = null;
List<GoogleSpreadsheet_Ai_CPC_DataBean> cpcDataResultList = null;
List<GoogleSpreadsheet_Ai_CPA_DataBean> cpaDataResultList = null;
// CommonPerformanceReportBean commonPerformanceReportBean = null;
GAds_DailyCampaignSettingEntity campaignDailySettingEntity = null;
if(aiKpiType.equalsIgnoreCase("CPV")) {
cpvDataResultList = (List<GoogleSpreadsheet_Ai_CPV_DataBean>) currentCampaign_SpreadsheetFramePropertiesDataBean.getResult();
HashMap<String, GoogleSpreadsheet_Ai_CPV_DataBean> cpvDataResultMap = convertGoogleSpreadsheet_Ai_CPV_DataBeanListIntoDateViewMap(cpvDataResultList);
for(CommonPerformanceReportBean eachPer : currentCampaignPer) {
if(!cpvDataResultMap.containsKey(eachPer.getDataDate())) {
campaignDailySettingEntity = (currentCampaign_DailySettingMap.containsKey(eachPer.getDataDate().replaceAll("-", ""))) ? currentCampaign_DailySettingMap.get(eachPer.getDataDate().replaceAll("-", "")) : null;
Double dailyBudget = (campaignDailySettingEntity != null) ? campaignDailySettingEntity.getDailyBudget(): 0.0;
Double spendRatio = getSpendRatioValue(dailyBudget, eachPer.getCosts());
result.add(
Arrays.asList(
eachPer.getDataDate(),
dailyBudget,
eachPer.getCosts(),
spendRatio,
eachPer.getImpressions(),
eachPer.getVideoViews(),
eachPer.getAverageCpv(),
MathematicsBasicOperationAgent.getDoubleByDivision(eachPer.getVideoViewRate(), 100.0, 4)
)
);
}
}
} else if(aiKpiType.equalsIgnoreCase("CPC")) {
cpcDataResultList = (List<GoogleSpreadsheet_Ai_CPC_DataBean>) currentCampaign_SpreadsheetFramePropertiesDataBean.getResult();
HashMap<String, GoogleSpreadsheet_Ai_CPC_DataBean> cpcDataResultMap = convertGoogleSpreadsheet_Ai_CPC_DataBeanListIntoDateViewMap(cpcDataResultList);
for(CommonPerformanceReportBean eachPer : currentCampaignPer) {
if(!cpcDataResultMap.containsKey(eachPer.getDataDate())) {
campaignDailySettingEntity = (currentCampaign_DailySettingMap.containsKey(eachPer.getDataDate().replaceAll("-", ""))) ? currentCampaign_DailySettingMap.get(eachPer.getDataDate().replaceAll("-", "")) : null;
Double dailyBudget = (campaignDailySettingEntity != null) ? campaignDailySettingEntity.getDailyBudget(): 0.0;
Double spendRatio = getSpendRatioValue(dailyBudget, eachPer.getCosts());
Double impressionShare = getImpressionShareRelatedMetricsValue("impressionshare", eachPer);
Double absoluteTopImpressionPercentage = getImpressionShareRelatedMetricsValue("absolutetopimpressionpercentage", eachPer);
Double budgetLostImpressionShare = getImpressionShareRelatedMetricsValue("budgetlostimpressionshare", eachPer);
Double rankLostImpressionShare = getImpressionShareRelatedMetricsValue("ranklostimpressionshare", eachPer);
result.add(
Arrays.asList(
eachPer.getDataDate(),
dailyBudget,
eachPer.getCosts(),
spendRatio,
eachPer.getImpressions(),
eachPer.getClicks(),
eachPer.getAverageCpc(),
MathematicsBasicOperationAgent.getDoubleByDivision(eachPer.getCtr(), 100.0, 4),
impressionShare,
absoluteTopImpressionPercentage,
budgetLostImpressionShare,
rankLostImpressionShare
)
);
}
}
} else if(aiKpiType.equalsIgnoreCase("CPA")) {
cpaDataResultList = (List<GoogleSpreadsheet_Ai_CPA_DataBean>) currentCampaign_SpreadsheetFramePropertiesDataBean.getResult();
HashMap<String, GoogleSpreadsheet_Ai_CPA_DataBean> cpaDataResultMap = convertGoogleSpreadsheet_Ai_CPA_DataBeanListIntoDateViewMap(cpaDataResultList);
for(CommonPerformanceReportBean eachPer : currentCampaignPer) {
if(!cpaDataResultMap.containsKey(eachPer.getDataDate())) {
campaignDailySettingEntity = (currentCampaign_DailySettingMap.containsKey(eachPer.getDataDate().replaceAll("-", ""))) ? currentCampaign_DailySettingMap.get(eachPer.getDataDate().replaceAll("-", "")) : null;
Double dailyBudget = (campaignDailySettingEntity != null) ? campaignDailySettingEntity.getDailyBudget(): 0.0;
Double spendRatio = getSpendRatioValue(dailyBudget, eachPer.getCosts());
result.add(
Arrays.asList(
eachPer.getDataDate(),
dailyBudget,
eachPer.getCosts(),
spendRatio,
eachPer.getImpressions(),
eachPer.getClicks(),
MathematicsBasicOperationAgent.getDoubleByDivision(eachPer.getCtr(), 100.0, 4),
eachPer.getConversions(),
eachPer.getCostPerConversion(),
MathematicsBasicOperationAgent.getDoubleByDivision(eachPer.getConversionsFromInteractionsRate(), 100.0, 4),
eachPer.getAverageCpc()
)
);
}
}
} else {
}
return result;
}//End
```