Odśwież dane pobrane przez niestandardową funkcję w Arkuszu Google

92

Napisałem niestandardowy skrypt Google Apps, który otrzyma plik id i pobiera informacje z usługi internetowej (cena).

Używam tego skryptu w arkuszu kalkulacyjnym i działa dobrze. Mój problem polega na tym, że te ceny się zmieniają, a mój arkusz kalkulacyjny nie jest aktualizowany.

Jak mogę zmusić go do ponownego uruchomienia skryptu i zaktualizowania komórek (bez ręcznego przeglądania każdej komórki)?

tbkn23
źródło
1
Tak, oto wyjaśnienie, które zrobiłem: stackoverflow.com/questions/9022984/…
Henrique G. Abreu,
Przeczytałem twoje wyjaśnienie jako część moich badań. Bardzo pomocne, dzięki. Dodałem link do mojej odpowiedzi.
tbkn23
Dla tych, którzy napotykają podobne (zdefiniowane i logiczne, ale czasami niefortunne) zachowanie, może pomóc głosowanie za tą prośbą o funkcję w Google Issue Tracker: issuetracker.google.com/issues/36763858 .
Timothy Johns
Oto prosta odpowiedź, którą zrobiłem.
Anteny

Odpowiedzi:

92

Ok, wydaje mi się, że moim problemem było to, że Google zachowuje się dziwnie - nie uruchamia ponownie skryptu, o ile parametry skryptu są podobne, używa buforowanych wyników z poprzednich uruchomień. W związku z tym nie łączy się ponownie z interfejsem API i nie pobiera ponownie ceny, po prostu zwraca wynik poprzedniego skryptu, który został zapisany w pamięci podręcznej.

Zobacz więcej informacji tutaj: https://code.google.com/p/google-apps-script-issues/issues/detail?id=888

i tutaj: skrypt podsumowujący dane, które nie są aktualizowane

Moim rozwiązaniem było dodanie kolejnego parametru do mojego skryptu, którego nawet nie używam. Teraz, gdy wywołasz funkcję z parametrem innym niż poprzednie wywołania, będzie ona musiała ponownie uruchomić skrypt, ponieważ wynik dla tych parametrów nie będzie w pamięci podręcznej.

Więc ilekroć wywołuję tę funkcję, jako dodatkowy parametr przekazuję „$ A $ 1”. Utworzyłem również element menu o nazwie odświeżanie, a kiedy go uruchamiam, umieszcza bieżącą datę i godzinę w A1, stąd wszystkie wywołania skryptu z $ A $ 1 jako drugim parametrem będą musiały zostać przeliczone ponownie. Oto kod z mojego skryptu:

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();
}   

A kiedy chcę umieścić w komórce cenę przedmiotu o identyfikatorze 5, używam następującego wzoru:

=getPrice(5, $A$1)

Kiedy chcę odświeżyć ceny, po prostu klikam opcję „Odśwież” -> „Odśwież”. Pamiętaj, że po zmianie onOpen()skryptu musisz ponownie załadować arkusz kalkulacyjny .

tbkn23
źródło
4
dlaczego nie użyć now () jako dodatkowego parametru?
Zaawansowany
5
Widzisz, podobnie jak moja funkcja nie zostanie ponownie obliczona, ponieważ jej parametry nie uległy zmianie (tj. Wartości komórek), funkcja now () również nie zostanie ponownie oszacowana, ponieważ nie ma parametrów, stąd jej wartość zwrócona będzie nie ulegną zmianie, więc parametry mojej funkcji się nie zmienią. Ponadto użycie funkcji now () spowodowałoby, że moja funkcja cały czas ponownie oceniałaby, co jest nieco trudne, biorąc pod uwagę, że generuje kilka wywołań HTTP ...
tbkn23
2
Dobre znalezisko. Miałem ten problem podczas używania nazwanych zakresów jako danych wejściowych. Korzystając z Twojej odpowiedzi, doszedłem do wniosku, że często wystarczy podać fikcyjną sumę przez zakres danych wejściowych, jak w przypadku „suma (B: D)”, gdzie wiersze BD znajdują się w zakresie wyszukanym przez funkcję niestandardową. Zmiana dowolnej komórki spowoduje zmianę sumy i odświeżenie funkcji niestandardowej. Nawiasem mówiąc, wygląda na to, że funkcja niestandardowa nie musi nawet deklarować ignorowanego parametru.
Shawn Hoover
1
Szkoda, że ​​to nadal najlepsze rozwiązanie tego problemu, jakie znalazłem. Zamiast pozwalać Google tylko na wyłączenie pamięci podręcznej dla określonych wywołań funkcji, arbitralnie zwiększamy zawartość pamięci podręcznej, aby upewnić się, że nie otrzymamy wartości w pamięci podręcznej ... Mimo to, dziękuję za wiadomość,
GrayedFox
Spróbuj użyć parametru dla swojej funkcji niestandardowej, takiego jak ten przykład
Aerials
33

Wiem, że to trochę stare pytanie. Ale ta metoda nie wymaga żadnych działań użytkownika poza wprowadzeniem zmiany.

To, co zrobiłem, było podobne do tbkn23.

Funkcja, którą chcę ponownie ocenić, ma dodatkowy nieużywany parametr $ A $ 1. Więc wywołanie funkcji to

=myFunction(firstParam, $A$1)

Ale w kodzie jest podpis funkcji

function myFunction(firstParam)

Zamiast mieć funkcję Refresh, użyłem funkcji onEdit (e) w ten sposób

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

Ta funkcja jest uruchamiana za każdym razem, gdy edytowana jest dowolna komórka w arkuszu kalkulacyjnym. Więc teraz edytujesz komórkę, losowa liczba jest umieszczana w A1, co odświeża listę parametrów zgodnie z sugestią tbkn23, powodując ponowne oszacowanie funkcji niestandardowej.

Pędzel Lexi
źródło
5
Działa świetnie. Wadą jest to, że historia cofania (ctrl + z) jest pomieszana.
Jon
1
Niezła sztuczka z irytującym minusem, jak wskazał Jon ... Mam nadzieję, że ktoś to poprawi :-)
Enissay
Małe i pedantyczne zastrzeżenie z tą odpowiedzią; w niezwykle nieprawdopodobnym przypadku Math.random zwraca tę samą liczbę, w tym przypadku nie będzie aktualizowana, ponieważ ta konkretna liczba została już zapisana w pamięci podręcznej.
Woody Payne
12

Jest bardzo późno i nie wiem, czy byłoby to przydatne, ale w rzeczywistości są tutaj ustawienia, które można NOW()aktualizować automatycznie

wprowadź opis obrazu tutaj

Thaina
źródło
1
NOW()jest funkcją wbudowaną, a nie funkcją niestandardową.
Rubén
@ Rubén Chodzi o to, że możesz dołączyć funkcję NOW () do dowolnej funkcji niestandardowej, którą chcesz zaktualizować
Thaina
13
W tym momencie argumenty funkcji niestandardowych powinny być deterministyczne, innymi słowy, nie uwzględniają TERAZ () jako argumentu. Zobacz developers.google.com/apps-script/guides/sheets/functions
Rubén
3
Zgłasza
6

Jeśli Twoja funkcja niestandardowa znajduje się w określonej kolumnie, po prostu uporządkuj arkusz kalkulacyjny według tej kolumny.

Akcja porządkowania wymusza odświeżenie danych, co wywołuje funkcję niestandardową dla wszystkich wierszy tej kolumny jednocześnie.

grypa
źródło
1

To może być bardzo stary wątek, ale może być przydatny dla kogoś, kto próbuje to zrobić, tak jak przed chwilą.

Pracując ze skryptu Lexi w obecnym stanie, nie wydawało się już działać z bieżącymi Arkuszami, ale jeśli dodam zmienną zastępczą do mojej funkcji jako parametr (nie ma potrzeby używania jej w funkcji), rzeczywiście wymuś ponowne odświeżenie strony w arkuszach Google.

Tak więc, zadeklaruj taką funkcję jak: function myFunction (firstParam, dummy), a następnie wywołaj ją zgodnie z sugestią. To zadziałało dla mnie.

Ponadto, jeśli pojawianie się zmiennej losowej na wszystkich arkuszach, które edytujesz, jest uciążliwe, prostym sposobem na ograniczenie do jednego arkusza jest następujący:

function onEdit(e)
{
  e.source.getSheetByName('THESHEETNAME').getRange('J1').setValue(Math.random());
}
AvengingAB
źródło
1
@ Rubén jest bardzo podobny, ale z dobrą uciążliwością; zamiast używać aktywnego arkusza, używa zdefiniowanej nazwy arkusza, poprawiając twoją historię
Jonas D.
1

Logika skryptów:

  • Funkcje niestandardowe nie są aktualizowane, chyba że zmieniają się argumenty.
  • Utwórz wyzwalacz onChange, aby zmienić wszystkie argumenty wszystkich funkcji niestandardowych w arkuszu kalkulacyjnym za pomocą funkcji textFinder

Skrawek:

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

Czytać:

Mistrz
źródło
1

Jak już wspomniano:

Funkcje niestandardowe nie są aktualizowane, chyba że zmieniają się argumenty.

Możliwym rozwiązaniem jest utworzenie pola wyboru w pojedynczej komórce i użycie tej komórki jako argumentu funkcji niestandardowej:

  1. Utwórz pole wyboru: wybierz wolną komórkę, np. [A1], przejdź do [Wstaw]> [Pole wyboru]
  2. Uczyń tę komórkę argumentem: =myFunction(A1)
  3. Kliknij pole wyboru, aby odświeżyć formułę
Max Makhrov
źródło
-3

Jeśli napisałeś funkcję niestandardową i użyłeś jej w arkuszu kalkulacyjnym jako formuły, to za każdym razem, gdy otwierasz arkusz kalkulacyjny lub modyfikowana jest dowolna komórka odniesienia, formuła jest ponownie obliczana.

Jeśli chcesz po prostu wpatrywać się w arkusz kalkulacyjny i chcesz zmienić jego wartości, rozważ dodanie wyzwalacza czasowego, który zaktualizuje komórki. Przeczytaj więcej o wyzwalaczach tutaj

Srik
źródło
10
Byłoby świetnie, gdyby nie to, że nie działa ... Ponowne załadowanie strony nie powoduje odświeżenia wartości. Co więcej, usunięcie komórki i ponowne wprowadzenie tego samego wywołania funkcji nadal zachowuje starą wartość. Jeśli wywołam tę samą funkcję dokładnie z innej komórki, pokaże nową wartość, ale nie w starej komórce.
tbkn23