0

I am trying to create a custom pdf web app on Google Apps Script, replacing values into my template Google Document. I generate numbers in the numGenerator function, and I want to return these numbers too for later use in my python file.

function numGenerator(digits){
   let listOfNums = [];
   const powerOfTen = Math.pow(10, digits);
   
   //Addition
   for (i=0; i<6; i++) {
     let num = getRandomNumber(0, powerOfTen);
     sendToList(num, listOfNums);
   }
   //Subtraction
   for (i=6; i<14; i+=2) {
     let num1 = getRandomNumber(0, powerOfTen);
     let num2 = getRandomNumber(num1, powerOfTen);
     sendToList(num2, listOfNums);
     sendToList(num1, listOfNums);
   }
   //Multiplication
   for (i=14; i<22; i+=2) {
     let num1 = getRandomNumber(2, (powerOfTen/10));
     let num2 = getRandomNumber(2, 20);
     sendToList(num1, listOfNums);
     sendToList(num2, listOfNums);
   }
   //Division
   for (i=22; i<30; i+=2) {
     let num2 = getRandomNumber(2, (powerOfTen/10));
     let num1 = getRandomNumber(2, (num2/10));     
     sendToList(num1*num2, listOfNums);
     sendToList(num2, listOfNums);
   }
   return listOfNums;
 }

function createDocument(doc_id, digits) {
  const listOfNums = numGenerator(digits)
  var TEMPLATE_ID = '#########';  // Google Doc template
  var documentId = DriveApp.getFileById(TEMPLATE_ID).makeCopy().getId();
  
  drivedoc = DriveApp.getFileById(documentId);
  drivedoc.setName("Mental Math Starter Exercise" + doc_id); // Copy created
  
  doc = DocumentApp.openById(documentId);
  
  var body = doc.getBody();
  
  body.replaceText('<ID>', doc_id);
  body.replaceText('<digits>', digits)
  
  for (i=1; i<=30; i++){
    body.replaceText('<num' + i + '>', listOfNums[i-1])  // Generated numbers inserted
  }
  
  drivedoc.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.EDIT);

  return [listOfNums, "https://docs.google.com/document/d/" + documentId + "/export?format=pdf"]; // This might be a problem
}

function doGet(e) {
  var invoice_id = e.parameter.invoice_id; // fetches parameters
  var digits = e.parameter.digits // fetches parameters
  var url = createDocument(invoice_id, digits); // url should be a list here
  return (url[0], ContentService.createTextOutput(url[1])); //Should have returned the listOfNums and the document here
}

Before I tried to return two values from the function, the following Python code worked fine, downloading the custom worksheet.

import requests
import random
from PyPDF2 import PdfFileMerger
import os

url = "https://script.google.com/macros/s/AKfycbyWXUQkpxyxDtj8lsOZtdaCBqRJUMZ1f9jXOFaTN82HtGIMIFc/exec?invoice_id={" \
      "}&digits={}"

def createPDF(digits):

    merger = PdfFileMerger()

    for digit in digits:
        id_num = random.randint(0, 10000)
        print("processing ", id_num, digit)
        docURL = requests.get(url.format(id_num, digit))
        print("file generated")
        document = requests.get(docURL.content)
        print("file downloaded")
        listOfNums = document.content[0]
        with open("worksheet{}.pdf".format(id_num), "wb") as f:
            f.write(document.content[1])
            # merger.append(response.content)
        merger.append("worksheet{}.pdf".format(id_num))
        os.remove("worksheet{}.pdf".format(id_num))

    merger.write("result.pdf")
    merger.close()
    print("Worksheet bundle downloaded")

However, now that I'm trying to access both the document and the listOfNums, I get the following error:

  File "/Users/vkhanna/PycharmProjects/MachineLearning/pdfRequest.py", line 22, in createPDF
    f.write(document.content[1])
TypeError: a bytes-like object is required, not 'int'

How can I successfully return both the listOfNums and the document? I know I'm missing something simple.

TheMaster
  • 45,448
  • 6
  • 62
  • 85

1 Answers1

2

Part 1. Python

You should be careful with the public contract of your functions, especially when developing in multiple languages. Let us go step by step and see why the error happens.

First, the Response object has a content property, which is the "content of the response, in bytes", so far so good. Next, the write method accepts "either to a string (in text mode) or a bytes object (in binary mode)". Finally, trying to read the value by index from bytes data type results in int (as a byte is an integer).

All the above result in the error you get. write expected bytes or str, but got an int. You could use a json method or a text property of the Response instead to work directly on JSON data, the exact implementation is up to you.


Part 2. Google Apps Script

Even if you fix the client, there are still problems with your Web App. First, you cannot return multiple values from a function in JavaScript. Although, as mentioned, the comma operator's behaviour may trick you into thinking that this is allowed. The problem is that only the last argument will be returned, to demonstrate:

const test = () => {
    const u = 1, l = 2;
    return u, l;
};

console.log(test());

Secondly, you should either return a TextOutput or an HtmlOutput from a doGet or doPost trigger function to get a correct response back (your response was correct by accident).

  • I can expand on the problems later, but that should get you started - at least fixing the client-side code (in terms of communicating with a web app as a backend service) should address the immediate concern (you don't need `listOfNums` now) – Oleg Valter is with Ukraine Jul 11 '20 at 11:27