Atualizar dados recuperados por uma função personalizada no Planilhas Google

92

Eu escrevi um Script do Google Apps personalizado que receberá um ide buscará informações de um serviço da web (um preço).

Eu uso este script em uma planilha e ele funciona muito bem. Meu problema é que esses preços mudam e minha planilha não é atualizada.

Como posso forçá-lo a executar novamente o script e atualizar as células (sem passar manualmente por cada célula)?

tbkn23
fonte
1
Sim, aqui vai uma explicação que fiz sobre isso: stackoverflow.com/questions/9022984/…
Henrique G. Abreu
Eu li sua explicação como parte da minha pesquisa. Muito útil, obrigado. Eu adicionei o link para minha resposta.
tbkn23
Para aqueles que encontrarem comportamento semelhante (definido e lógico, mas às vezes infeliz), pode ajudar a votar a favor desta solicitação de recurso no Rastreador de problemas do Google: issuetracker.google.com/issues/36763858 .
Timothy Johns
Aqui está uma resposta simples que fiz.
Antenas de
Soluções alternativas reais: stackoverflow.com/a/56802017 e stackoverflow.com/a/61946592
TheMaster

Respostas:

93

Ok, parece que meu problema é que o Google se comporta de uma maneira estranha - ele não executa novamente o script, desde que os parâmetros do script sejam semelhantes, ele usa os resultados armazenados em cache das execuções anteriores. Portanto, ele não se reconecta à API e não busca o preço novamente, simplesmente retorna o resultado do script anterior que foi armazenado em cache.

Veja mais informações aqui: https://code.google.com/p/google-apps-script-issues/issues/detail?id=888

e aqui: Script para resumir os dados não atualizados

Minha solução foi adicionar outro parâmetro ao meu script, que eu nem mesmo uso. Agora, ao chamar a função com um parâmetro diferente das chamadas anteriores, ela terá que executar novamente o script porque o resultado para esses parâmetros não estará no cache.

Portanto, sempre que chamo a função, para o parâmetro extra, passo "$ A $ 1". Também criei um item de menu chamado refresh e, quando o executo, ele coloca a data e a hora atuais em A1, portanto, todas as chamadas para o script com $ A $ 1 como segundo parâmetro terão que ser recalculadas. Aqui está um código do meu 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();
}   

E quando quero colocar o preço do item com ID 5 em uma célula, uso a seguinte fórmula:

=getPrice(5, $A$1)

Quando quero atualizar os preços, simplesmente clico no item de menu "Atualizar" -> "Atualizar". Lembre-se de que você precisa recarregar a planilha após alterar o onOpen()script.

tbkn23
fonte
4
por que não usar now () como parâmetro adicional?
Avançado de
5
Veja, assim como minha função não será reavaliada porque seus parâmetros não mudaram (ou seja, os valores das células), now () também não será reavaliado, pois não tem parâmetros, portanto, seu valor de retorno será não muda e, portanto, os parâmetros da minha função não vão mudar. Além disso, usar now () faria com que minha função fosse reavaliada o tempo todo, o que é um pouco pesado, considerando que gera várias chamadas HTTP ...
tbkn23
2
Bom achado. Eu tive esse problema ao usar intervalos nomeados como entrada. Usando sua resposta, descobri que geralmente é bom o suficiente para passar uma soma fictícia ao longo do intervalo de entradas, como em "soma (B: D)", onde as linhas BD estão no intervalo pesquisado pela função personalizada. A alteração de qualquer célula acionará a alteração da soma e a atualização da função personalizada. A propósito, parece que a função personalizada nem mesmo precisa declarar o parâmetro ignorado.
Shawn Hoover de
1
Uma pena que esta ainda seja a melhor solução que encontrei para este problema. Em vez de o Google apenas nos permitir desabilitar o cache para chamadas de funções específicas, aumentamos arbitrariamente o que está sendo armazenado no cache apenas para garantir que não
recebamos
Tente usar um parâmetro para sua função personalizada, como este exemplo
Antenas de
33

Eu sei que esta é uma questão um pouco velha. Mas esse método não requer nenhuma ação do usuário, exceto fazer uma alteração.

O que fiz foi semelhante a tbkn23.

A função que desejo reavaliar tem um parâmetro extra não utilizado, $ A $ 1. Portanto, a chamada de função é

=myFunction(firstParam, $A$1)

Mas no código, a assinatura da função é

function myFunction(firstParam)

Em vez de ter uma função Atualizar, usei a função onEdit (e) como esta

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

Esta função é acionada sempre que qualquer célula da planilha é editada. Então, agora que você edita uma célula, um número aleatório é colocado em A1, isso atualiza a lista de parâmetros conforme sugerido por tbkn23, fazendo com que a função personalizada seja reavaliada.

Lexi Brush
fonte
5
Funciona bem. A desvantagem é que o histórico de desfazer (ctrl + z) está bagunçado.
Jon
1
Belo truque com uma desvantagem irritante como Jon apontou ... Espero que alguém o melhore :-)
Enissay
2
Uma advertência pequena e pedante com esta resposta; no evento incrivelmente improvável Math.random retorna o mesmo número, nessa ocasião ele não será atualizado, pois esse número específico já foi armazenado em cache.
Woody Payne
12

É muito tarde e não sei se seria útil, mas na verdade existem configurações aqui que você pode fazer a NOW()atualização automaticamente

insira a descrição da imagem aqui

Thaina
fonte
1
NOW()é uma função incorporada, não uma função personalizada.
Rubén
@ Rubén A questão é que você pode incluir a função NOW () em qualquer função personalizada que você deseja que seja atualizada
Thaina,
13
Neste momento, os argumentos das funções personalizadas devem ser determinísticos, em outras palavras, eles não permitem NOW () como argumento. Consulte developers.google.com/apps-script/guides/sheets/functions
Rubén
3
Lança um erro se você tentar usar NOW () dentro de uma função personalizada
Stefano Giacone
7

Se sua função personalizada estiver dentro de uma coluna específica, basta ordenar sua planilha por essa coluna.

A ação de ordenação força uma atualização dos dados, que invoca sua função personalizada para todas as linhas daquela coluna de uma vez.

gripe
fonte
só funciona se a folha já não for encomendada
Sanfer
2

Este pode ser um tópico super antigo, mas pode ser útil para alguém que está tentando fazer isso, como eu estava agora.

Trabalhando fora do script de Lexi como está, ele não parecia funcionar mais com o Sheets atual, mas se eu adicionar a variável fictícia em minha função como um parâmetro (não há necessidade de realmente usá-la dentro da função), realmente forçar o Google Sheets a atualizar a página novamente.

Portanto, uma declaração do tipo: function myFunction (firstParam, dummy) e depois chamá-la seria conforme sugerido. Isso funcionou para mim.

Além disso, se for um incômodo que uma variável aleatória apareça em todas as suas planilhas que você edita, uma solução fácil para limitar a uma planilha é a seguinte:

function onEdit(e)
{
  e.source.getSheetByName('THESHEETNAME').getRange('J1').setValue(Math.random());
}
AvengingAB
fonte
1
@Rubén é muito parecido, mas com um bom incômodo; em vez de usar a planilha ativa, ele usa um nome de planilha definido, melhorando seu histórico
Jonas D.
1

Lógica do script:

  • Funções personalizadas não são atualizadas a menos que seus argumentos sejam alterados.
  • Crie um gatilho onChange para alterar todos os argumentos de todas as funções personalizadas na planilha usando textFinder

Snippet:

/*@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) + ')'
    );
}

Ler:

O mestre
fonte
1

Conforme observado anteriormente:

Funções personalizadas não são atualizadas a menos que seus argumentos sejam alterados.

A solução possível é criar uma caixa de seleção em uma única célula e usar essa célula como um argumento para a função personalizada:

  1. Crie uma caixa de seleção: selecione a célula livre, por exemplo, [A1], vá para [Inserir]> [Caixa de seleção]
  2. Faça desta célula um argumento: =myFunction(A1)
  3. Clique na caixa de seleção para atualizar a fórmula
Max Makhrov
fonte
-3

Se você escreveu uma função personalizada e a usou em sua planilha como uma fórmula, cada vez que você abrir a planilha ou qualquer célula de referência for modificada, a fórmula será recalculada.

Se você deseja apenas continuar olhando para a planilha e deseja que seus valores mudem, considere adicionar um gatilho cronometrado que atualizará as células. Leia mais sobre gatilhos aqui

Srik
fonte
10
Isso seria ótimo, exceto que não funciona ... Recarregar a página não atualiza os valores. Além disso, excluir a célula e entrar novamente na mesma chamada de função, ainda mantém o valor antigo. Se eu chamar a mesma função exatamente de outra célula, ela mostrará o novo valor, mas não na célula antiga.
tbkn23