From 9008fc92fa900250b41b1d9ad394baf3465f64ca Mon Sep 17 00:00:00 2001 From: Sami Ekblad Date: Thu, 29 Jun 2023 11:26:45 +0000 Subject: [PATCH] Store scores to a Google Sheet --- .../application/data/FeedbackSheet.java | 127 ++++++++++++++++++ .../application/views/empty/FeedbackView.java | 5 + 2 files changed, 132 insertions(+) create mode 100644 demo/src/main/java/com/example/application/data/FeedbackSheet.java diff --git a/demo/src/main/java/com/example/application/data/FeedbackSheet.java b/demo/src/main/java/com/example/application/data/FeedbackSheet.java new file mode 100644 index 00000000..3ed85990 --- /dev/null +++ b/demo/src/main/java/com/example/application/data/FeedbackSheet.java @@ -0,0 +1,127 @@ +package com.example.application.data; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.services.sheets.v4.Sheets; +import com.google.api.services.sheets.v4.SheetsScopes; +import com.google.api.services.sheets.v4.model.ValueRange; +import com.google.auth.oauth2.ServiceAccountCredentials; + +/* Class to store feedack into a Google Sheet. + * + * This needs a Google Service Account to be set up first and give 'editor' + * permissions to that account in the specified sheet. + * + * + */ +public class FeedbackSheet { + + private static final String APPLICATION_NAME = "NPS Feedback App"; + + private static final List AUTH_SCOPES = List.of(SheetsScopes.SPREADSHEETS); + + private static final String RANGE = "Sheet1"; + + private static final GsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); + + private String credentialsFilePath; + private ServiceAccountCredentials loadedCredentials; + private Sheets loadedService; + + private String sheetId; + + public FeedbackSheet(String sheetId, String credentialsFilePath) { + this.sheetId = sheetId; + this.credentialsFilePath = credentialsFilePath; + } + + /** + * Append a user score to the spreadsheet. + * + * Adds a new row in the first sheet in a Google Spreadsheet + * with timestamp userId and given NPS score. + * + * 2023-06-26 11:09:44,1687640813, 10 + * + * @param anonymousUserId Unique but anonymous I + * @param score NPS score + */ + public void append(String anonymousUserId, int score) { + try { + List> values = Arrays.asList( + Arrays.asList(LocalDateTime.now().toString(), anonymousUserId, score)); + + ValueRange newEntry = new ValueRange().setValues(values); + Sheets service = getSheetsService(); + service.spreadsheets().values() + .append(this.sheetId, RANGE, newEntry) + .setValueInputOption("USER_ENTERED") + .setAccessToken(getCredentials().getAccessToken().getTokenValue()) + .execute(); + } catch (Exception e) { + throw new RuntimeException("Failed to append Google Sheet", e); + } + } + + /** + * Initialize and get Google Sheets service. + * + * @return Sheets service to perform operations. + * @throws IOException + * @throws GeneralSecurityException + */ + public Sheets getSheetsService() throws IOException, GeneralSecurityException { + if (this.loadedService == null) { + NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + this.loadedService = new Sheets.Builder(httpTransport, JSON_FACTORY, request -> { + }) + .setApplicationName(APPLICATION_NAME) + .build(); + } + return this.loadedService; + } + + /** + * Load and create new ServiceAccountCredential instance. + * + * Loads the credentials from json file specified by + * gapi.credentials.file.path. + * Once successfully loaded, calling this will return the same credential + * instance. + * + * @return An authorized Credential object. + * @throws IOException If the credentials.json file cannot be found. + */ + protected ServiceAccountCredentials getCredentials() throws IOException { + + if (this.loadedCredentials != null) { + if (this.loadedCredentials.getAccessToken().getExpirationTime().before(new Date())) { + this.loadedCredentials.refresh(); + } + return this.loadedCredentials; + } + + InputStream in = new FileInputStream(credentialsFilePath); + if (in == null) { + throw new FileNotFoundException("Credentials not found: " + credentialsFilePath); + } + + this.loadedCredentials = (ServiceAccountCredentials) ServiceAccountCredentials + .fromStream(new FileInputStream(credentialsFilePath)) + .createScoped(AUTH_SCOPES); + this.loadedCredentials.refresh(); + return this.loadedCredentials; + } + +} diff --git a/demo/src/main/java/com/example/application/views/empty/FeedbackView.java b/demo/src/main/java/com/example/application/views/empty/FeedbackView.java index 1ca5bb30..0636e6a3 100644 --- a/demo/src/main/java/com/example/application/views/empty/FeedbackView.java +++ b/demo/src/main/java/com/example/application/views/empty/FeedbackView.java @@ -32,6 +32,11 @@ public FeedbackView() { header.setText("Thank You"); replace(nps, thankYou); add(closeLink); + + // Store the results into a Google Sheet + FeedbackSheet feedbackSheet = new FeedbackSheet("1aTfU2_XuZU-HgUhSBu4_oB_gB4hhro-RzNsdN9_8YX8", + "/workspaces/nps/.devcontainer/local/service_account_credentials.json"); + feedbackSheet.append("" + UI.getCurrent().hashCode(), e.getValue()); }); // Styling