Actualizar los datos recuperados por una función personalizada en Google Sheet

92

Escribí un script de Google Apps personalizado que recibirá idy obtendrá información de un servicio web (un precio).

Utilizo este script en una hoja de cálculo y funciona bien. Mi problema es que estos precios cambian y mi hoja de cálculo no se actualiza.

¿Cómo puedo obligarlo a volver a ejecutar el script y actualizar las celdas (sin revisar manualmente cada celda)?

tbkn23
fuente
1
Sí, aquí hay una explicación que hice sobre esto: stackoverflow.com/questions/9022984/…
Henrique G. Abreu
Leí tu explicación como parte de mi investigación. Muy útil, gracias. Agregué el enlace a mi respuesta.
tbkn23
Para aquellos que se encuentran con un comportamiento similar (definido y lógico, pero a veces desafortunado), podría ser útil votar a favor de esta solicitud de función en Google Issue Tracker: issuetracker.google.com/issues/36763858 .
Timothy Johns
Aquí hay una respuesta simple que hice.
Antenas

Respuestas:

92

Ok, parece que mi problema fue que Google se comporta de una manera extraña: no vuelve a ejecutar el script siempre que los parámetros del script sean similares, usa resultados almacenados en caché de las ejecuciones anteriores. Por lo tanto, no se vuelve a conectar a la API y no recupera el precio, simplemente devuelve el resultado del script anterior que se almacenó en caché.

Vea más información aquí: https://code.google.com/p/google-apps-script-issues/issues/detail?id=888

y aquí: Script para resumir los datos que no se actualizan

Mi solución fue agregar otro parámetro a mi script, que ni siquiera uso. Ahora, cuando llame a la función con un parámetro que sea diferente a las llamadas anteriores, tendrá que volver a ejecutar el script porque el resultado de estos parámetros no estará en la caché.

Entonces, cada vez que llamo a la función, para el parámetro adicional paso "$ A $ 1". También creé un elemento de menú llamado actualizar, y cuando lo ejecuto, pone la fecha y hora actual en A1, por lo tanto, todas las llamadas al script con $ A $ 1 como segundo parámetro tendrán que recalcularse. Aquí hay un código de mi script:

function onOpen() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var entries = [{
    name : "Refresh",
    functionName : "refreshLastUpdate"
  }];
  sheet.addMenu("Refresh", entries);
};

function refreshLastUpdate() {
  SpreadsheetApp.getActiveSpreadsheet().getRange('A1').setValue(new Date().toTimeString());
}

function getPrice(itemId, datetime) {
  var headers =
      {
        "method" : "get",
        "contentType" : "application/json",
        headers : {'Cache-Control' : 'max-age=0'}
      };

  var jsonResponse = UrlFetchApp.fetch("http://someURL?item_id=" + itemId, headers);
  var jsonObj = eval( '(' + jsonResponse + ')' );
  return jsonObj.Price;
  SpreadsheetApp.flush();
}   

Y cuando quiero poner el precio del artículo con ID 5 en una celda, uso la siguiente fórmula:

=getPrice(5, $A$1)

Cuando quiero actualizar los precios, simplemente hago clic en el elemento de menú "Actualizar" -> "Actualizar". Recuerde que debe volver a cargar la hoja de cálculo después de cambiar la onOpen()secuencia de comandos.

tbkn23
fuente
4
¿Por qué no usar now () como parámetro adicional?
Avanzado
5
Verá, al igual que mi función no se volverá a evaluar porque sus parámetros no han cambiado (es decir, los valores de las celdas), ahora () tampoco se volverá a evaluar ya que no tiene parámetros, por lo que su valor de retorno será no cambiar y, por lo tanto, los parámetros de mi función no cambiarán. Además, usar now () haría que mi función se reevaluara todo el tiempo, lo cual es un poco pesado considerando que genera varias llamadas HTTP ...
tbkn23
2
Buen descubrimiento. Tuve este problema al usar rangos con nombre como entrada. Usando su respuesta, descubrí que a menudo es suficientemente bueno pasar una suma ficticia sobre el rango de entradas, como en "suma (B: D)", donde las filas BD están en el rango buscado por la función personalizada. Cambiar cualquier celda activará la suma para cambiar y la función personalizada para actualizar. Por cierto, parece que la función personalizada ni siquiera tiene que declarar el parámetro ignorado.
Shawn Hoover
1
Es una pena que esta siga siendo la mejor solución que encontré para este problema. En lugar de que Google simplemente nos permita deshabilitar el caché para llamadas a funciones particulares, aumentamos arbitrariamente lo que se almacena en el caché solo para asegurarnos de que no obtengamos un valor almacenado en caché ... Aún así, gracias por la publicación,
GrayedFox
Intente usar un parámetro para su función personalizada como este ejemplo
Aerials
33

Sé que esta es una pregunta un poco vieja. Pero este método no requiere ninguna acción del usuario excepto realizar un cambio.

Lo que hice fue similar a tbkn23.

La función que quiero reevaluar tiene un parámetro adicional no utilizado, $ A $ 1. Entonces la llamada a la función es

=myFunction(firstParam, $A$1)

Pero en el código, la firma de la función es

function myFunction(firstParam)

En lugar de tener una función de actualización, he usado la función onEdit (e) como esta

function onEdit(e)
{
   SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Math.random());
}

Esta función se activa siempre que se edita cualquier celda de la hoja de cálculo. Entonces, ahora edita una celda, se coloca un número aleatorio en A1, esto actualiza la lista de parámetros como sugirió tbkn23, lo que hace que la función personalizada sea reevaluada.

Cepillo Lexi
fuente
5
Funciona genial. La desventaja es que el historial de deshacer (ctrl + z) está en mal estado.
Jon
1
Buen truco con un inconveniente molesto como señaló Jon ... Espero que alguien lo mejore :-)
Enissay
Pequeña y pedante advertencia con esta respuesta; en el caso increíblemente improbable Math.random devuelve el mismo número, en esa ocasión no se actualizará, ya que ese número en particular ya se ha almacenado en caché.
Woody Payne
12

Esto es muy tarde y no sé si sería útil, pero en realidad hay una configuración aquí que puede NOW()actualizar automáticamente

ingrese la descripción de la imagen aquí

Thaina
fuente
1
NOW()es una función incorporada, no una función personalizada.
Rubén
@ Rubén El punto es que podría incluir la función NOW () en cualquier función personalizada que desee actualizar
Thaina
13
En este momento, los argumentos de las funciones personalizadas deben ser deterministas, en otras palabras, no se asignan NOW () como argumento. Ver developers.google.com/apps-script/guides/sheets/functions
Rubén
3
Lanza un error si intenta usar NOW () dentro de una función personalizada
Stefano Giacone
6

Si su función personalizada está dentro de una columna específica, simplemente ordene su hoja de cálculo por esa columna.

La acción de ordenar fuerza una actualización de los datos, que invoca su función personalizada para todas las filas de esa columna a la vez.

gripe
fuente
1

Este puede ser un hilo muy antiguo, pero puede ser útil para alguien que intente hacer esto, como lo estaba haciendo yo.

Trabajando con el script de Lexi tal como está, parece que ya no funciona con las Hojas de cálculo actuales, pero si agrego la variable ficticia en mi función como parámetro (no es necesario usarla dentro de la función), de hecho lo hará Obligar a Google Sheets a actualizar la página nuevamente.

Entonces, una declaración como: function myFunction (firstParam, dummy) y luego llamarlo sería como se ha sugerido. Eso funcionó para mí.

Además, si es una molestia que una variable aleatoria aparezca en todas las hojas que edita, una solución fácil para limitar a una hoja es la siguiente:

function onEdit(e)
{
  e.source.getSheetByName('THESHEETNAME').getRange('J1').setValue(Math.random());
}
AvengingAB
fuente
1
@ Rubén es muy parecido pero con una buena molestia; en lugar de usar la hoja activa, usa un nombre de hoja definido, mejorando su historial
Jonas D.
1

Lógica de script:

  • Las funciones personalizadas no se actualizan a menos que cambien los argumentos.
  • Cree un activador onChange para cambiar todos los argumentos de todas las funciones personalizadas en la hoja de cálculo usando textFinder

Retazo:

/*@customfunction*/
function sheetNames(e) {
  return SpreadsheetApp.getActive()
    .getSheets()
    .map(function(sheet) {
      return sheet.getName();
    });
}

/*Create a installable trigger to listen to grid changes on the sheet*/
function onChange(e) {
  if (!/GRID/.test(e.changeType)) return; //Listen only to grid change
  SpreadsheetApp.getActive()
    .createTextFinder('=SHEETNAMES\\([^)]*\\)')
    .matchFormulaText(true)
    .matchCase(false)
    .useRegularExpression(true)
    .replaceAllWith(
      '=SHEETNAMES(' + (Math.floor(Math.random() * 500) + 1) + ')'
    );
}

Leer:

El maestro
fuente
1

Como se menciono anteriormente:

Las funciones personalizadas no se actualizan a menos que cambien los argumentos.

La posible solución es crear una casilla de verificación en una sola celda y usar esta celda como argumento para la función personalizada:

  1. Cree una casilla de verificación: seleccione una celda libre, por ejemplo, [A1], vaya a [Insertar]> [Casilla de verificación]
  2. Haga de esta celda un argumento: =myFunction(A1)
  3. Haga clic en la casilla de verificación para actualizar la fórmula
Max Makhrov
fuente
-3

Si ha escrito una función personalizada y la ha utilizado en su hoja de cálculo como fórmula, cada vez que abra la hoja de cálculo o se modifique cualquier celda de referencia, la fórmula se volverá a calcular.

Si solo desea seguir mirando la hoja de cálculo y desea que sus valores cambien, considere agregar un disparador temporizado que actualice las celdas. Lea más sobre los desencadenantes aquí.

Srik
fuente
10
Eso sería genial, excepto que no funciona ... Recargar la página no actualiza los valores. Además, al eliminar la celda y volver a ingresar la misma llamada de función, aún se mantiene el valor anterior. Si llamo a la misma función exactamente desde otra celda, mostrará el nuevo valor, pero no en la celda anterior.
tbkn23