
Wir hatten zwei Google-Formulare mit jeweils 75 Fragen, fünf Geschäftsbenutzer, die diese Formulare aktiv bearbeitet haben, sowie ein Google-Skript, mit dem das Formular in JSON exportiert wurde. Nicht, dass es schwierig wäre, es jedes Mal mit den Händen auszuführen, aber wenn Sie einmal damit begonnen haben, Ihre Arbeit zu automatisieren, gehen Sie in diesem Hobby zu Ende.
In der offiziellen Dokumentation wird sich der Teufel das Bein brechen, daher werden wir uns unter der Katze den Remote-Download und das Starten von Google Apps Script über die REST-API mit Python genauer ansehen.
Einleitung
In Doctor Near entwickeln wir eine Plattform für Chat-Bots, in der Google-Formulare zur Beschreibung von Szenarien verwendet werden. Dementsprechend möchte ich von den Formularen auf Knopfdruck einen JSON erhalten, der Knoten (Formpunkte) und Metadaten zu ihnen (Übergänge zwischen Knoten, Knotentypen, deren Namen) enthält. Es scheint, dass der Wunsch einfach ist, aber Google unterstützt diese Funktionalität nicht und Sie müssen diesen "Exporteur" mit Ihren eigenen Händen zusammenbauen. Betrachten Sie die Schritte, um es zu erstellen.
SCHRITT 1. Google Apps Script
Google bietet die Möglichkeit, mit seinen Diensten (Tabellen, Dokumente, Formulare) über Google Apps Script zu interagieren - Skripte, die in Google Script (.gs) geschrieben sind. Dieser Artikel enthält keine Analyse der Google-Skriptsprache. Ich werde daher ein Beispiel für ein fertiges Skript geben, mit dem JSON aus einem vorhandenen Google-Formular erstellt wird. Der Benutzercode
Steven Schmatz wurde aus dem Github als Grundlage genommen, wofür ich ihm meinen Dank aussprechen möchte.
Was passiert im Code:
- getFormMetadata- Funktion - Gibt JSON mit Formularmetadaten zurück
- itemToObject- Funktion - konvertiert das form.item- Objekt mit den erforderlichen Feldern in JSON
- sendEmail- Funktion - sendet eine JSON-Datei an die angegebene E-Mail im Text
- Hauptfunktion - gibt den resultierenden JSON zurück
- Die Variable form_url in der Hauptfunktion ist die Adresse unseres Google-Formulars
SCHRITT 2. Das Skript testen
Im Moment kann die Leistung des Skripts wie folgt überprüft werden:
- Erstellen Sie Ihr eigenes App-Skript-Projekt
- Kopieren Sie den Code hinein
- Anstelle von <YOUR_FORM_URL> ersetzen wir die Adresse unseres Formulars aus dem Formular docs.google.com/forms/d/FORM_IDENTIFICATOR/edit
- Kommentieren Sie den Aufruf von sendEmail in der Hauptfunktion aus
- Anstelle von <YOUR_EMAIL> ersetzen wir die E-Mail-Adresse, an die JSON gesendet werden soll
- Speichern Sie das Projekt
- Führen Sie die Hauptfunktion aus
- Wenn dies die erste Ausführung des Skripts ist, werden Sie vom System über die Notwendigkeit informiert, dem Skript die Berechtigung zum Senden von E-Mails von Ihrer Adresse aus zu erteilen. Hab keine Angst. Dies ist die Standardprozedur, die zum Testen des Skripts erforderlich ist. Gehe durch "Berechtigungen überprüfen" -> wähle dein Konto aus -> "Erweitert" -> "Gehe zu PROJECT_NAME-Projekt (unsicher)" -> "Zulassen"
- Warten auf das Skript zu arbeiten
- Schauen Sie in das Postfach und sehen Sie die JSON-Datei in Textform
Alles wäre in Ordnung, aber die weitere Verwendung der erhaltenen Daten erfordert manuelles Kopieren aus der Mail, Verarbeiten dieses Texts (z. B. in Python) und Speichern der resultierenden Datei. Es klingt nicht zu produktionsreif. Wir automatisieren den Start dieses Skripts und erhalten das Ergebnis über die Google Apps-Skript-API. Zunächst richten wir jedoch unser Google-Projekt entsprechend ein.
Achtung: Um zu verstehen, was gerade passiert, werde ich im Folgenden nur auf zwei Seiten verweisen. Es wird daher empfohlen, diese in benachbarten Registerkarten zu öffnen:
- Skript / Skriptbearbeitungsseite - „Seite 1“
- Google Cloud Platform-Seite - "Seite 2"
SCHRITT 3. Google Cloud Platform konfigurieren
Wir gehen zur Google Cloud Platform (Seite 2) und erstellen ein neues Projekt. Es ist erforderlich, ein neues Projekt zu erstellen, da der Standardstatus des Projekts "Standard" lautet und für unsere Zwecke "Standart" erforderlich ist. Weitere Details finden Sie
hier (Punkt 3).
Wir kehren zu Seite 2 zurück, gehen zur Registerkarte "API and Services" und dann zu "OAuth Access Request Window". Stellen Sie den Benutzertyp auf "Extern".
Geben Sie im angezeigten Fenster den "Anwendungsnamen" ein.
Öffnen Sie die Homepage auf der Google Cloud-Plattform. Kopieren Sie aus dem Block "Projektinformationen" die Projektnummer.
Gehen Sie zu Seite 1. Öffnen Sie das zuvor erstellte Skript. Gehen Sie im geöffneten Skriptbearbeitungsfenster zu „Ressourcen“ -> „Cloud Platform-Projekt“. Geben Sie im Feld „Projekt ändern“ die zuvor kopierte Projektnummer ein. Dieses Skript ist nun dem erstellten Projekt zugeordnet.
SCHRITT 4. Python-REST-API
Es ist Zeit, das Skript mithilfe der
REST-API zu automatisieren. Als Sprache wurde Python verwendet.
Anmeldung für Apps Script API
Der Code muss Zugriff auf das Projekt haben, daher ist die Anmeldung in der Apps Script-API die erste und sehr wichtige Prozedur. Öffnen Sie Seite 2 -> „API and Services“ -> „Credentials“ -> „Create Credentials“ -> „OAuth Client Identifier“ -> „Other Types“. Wir nennen unseren Bezeichner, gehen Sie zu ihm. Wählen Sie auf der Registerkarte "Anmeldeinformationen" die Option "JSON-Datei herunterladen". Dadurch wird die Schlüsseldatei für den Zugriff vom Code auf das Projekt in Google geladen. Wir legen diese Datei im Anmeldeinformationsordner ab.
Jetzt müssen Sie die Berechtigung erteilen, die API (in unserem Fall die Apps Script-API) als Teil dieses Projekts zu verwenden. Gehen Sie dazu zu "API und Dienste" -> "Bibliothek" -> geben Sie "Apps Script API" in die Suche ein und klicken Sie auf "Aktivieren".
Anwendungen, die mit Google interagieren, verfügen über eine Reihe von Berechtigungen, die der Nutzer beim Starten erteilen muss. Diese Kopie hängt von den Funktionen ab, die von einem bestimmten Skript verwendet werden. Sie finden sie auf Seite 1 im Skriptbearbeitungsfenster unter „Datei“ -> „Projekteigenschaften“ -> „Bereiche“. Diese Berechtigungen sollten für die zukünftige Verwendung im Code gespeichert werden.
In diesem Fall sieht die Login-Funktion folgendermaßen aus:
import pickle import os.path from googleapiclient.discovery import build from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request def login(config): try: creds = None
Dieser Codeblock ist das Standardverfahren für den Einstieg in Google App Script.
Wir verwenden ein Authentifizierungstoken und erstellen bei der Anmeldung entweder ein neues Token oder verwenden ein vorhandenes.
Zur Vereinfachung wurde eine JSON-Konfigurationsdatei mit der folgenden Form erstellt:
{ "SCOPES": ["https://www.googleapis.com/auth/forms", "https://www.googleapis.com/auth/script.send_mail"], "credentials_path": "credentials/", "credentials_file": "google_test_project.json", "token_file": "token.pickle" }
Wichtig: Das Token wird für die Authentifizierung mit einem bestimmten Berechtigungsumfang erstellt. Mit anderen Worten, wenn Sie den Berechtigungsumfang ändern, sollten Sie das Token löschen und bei der Anmeldung ein neues erstellen.
Skriptcode für Remote-Aktualisierung
Jetzt lernen wir, wie man den Skriptcode remote aktualisiert, diesen Code dann ausführt und das Ergebnis erhält. Zusätzlich zu dem Code, den wir im Google Editor ausführen, gibt es auch eine
Manifestdatei , die die Startrechte, Bereitstellungseinstellungen usw. enthält. Weitere Informationen zu seiner Struktur finden Sie
hier .
Um die von Google für Ihr Skript erstellte Standardmanifestdatei anzuzeigen, rufen Sie den Skripteditor unter "Ansicht" -> "Manifestdatei anzeigen" auf. Das Manifest wird in der Liste der Dateien angezeigt, die sich auf dieses Skript beziehen.
Die Rede über das Manifest war nicht ohne Grund: Für die Remote-Skriptaktualisierung muss der Code sowohl der Dateien (* .gs) als auch des Manifests (appscript.json) heruntergeladen werden.
Lesen Sie zunächst den Code der .gs-Datei, die Sie bereitstellen möchten:
with open('export-google-form.gs', 'r') as f: sample_code = f.read()
Kopieren Sie nun das automatisch generierte Manifest und ändern Sie es ein wenig für unsere Zwecke. Die Dokumentation beschreibt die Struktur der Manifestdatei ziemlich ausführlich, so dass ich auf diesen Punkt nicht näher eingehen werde. Damit das Skript funktioniert, müssen Sie dem Standardmanifest den Abschnitt "executionApi" hinzufügen, der für die Remoteausführung des Skripts über die API erforderlich ist. In diesem Abschnitt geben wir den Personenkreis an, der in der Lage ist, das Programm auszuführen. Ich habe den Start für alle erlaubt, die die Autorisierung bestanden haben, die der Kennung "ANYONE" entspricht:
MANIFEST = ''' { "timeZone": "America/New_York", "exceptionLogging": "STACKDRIVER", "executionApi": { "access": "ANYONE" } } '''.strip()
Der Hauptteil der Aktualisierungsanforderung sollte ein Array von Dateien mit der folgenden Struktur enthalten:
- name : Name der Datei, die auf dem Server erstellt werden soll, ohne Erweiterung
- Typ : Dateityp (JSON für Manifest, SERVER_JS für .gs)
- Quelle : Dateicode
request = { 'files': [{ 'name': 'hello', 'type': 'SERVER_JS', 'source': sample_code }, { 'name': 'appsscript', 'type': 'JSON', 'source': MANIFEST } ] }
Schließlich muss die Aktualisierungsanforderung selbst den Hauptteil (oben beschriebene Anforderung) und die Skript-ID enthalten. Letzteres erhalten Sie, indem Sie im Skripteditor unter "Datei" -> "Projekteigenschaften" die "Skript-ID" kopieren:
script_id = 'qwertyuiopQWERTYUIOPasdfghjkl123456789zxcvbnmASDFGHJKL54'
Für das Serviceobjekt, das als Ergebnis der Anmeldung abgerufen wurde, erhalten wir das Feld projects () und rufen die Methode updateContent () auf. Anschließend rufen wir die Methode execute () für das empfangene HttpRequest-Objekt auf:
service.projects().updateContent( body=request, scriptId=script_id ).execute()
Derzeit führt das Ausführen des Codes jedoch zu einem Fehler:
"error": { "code": 403, "message": "Request had insufficient authentication scopes.", "status": "PERMISSION_DENIED" }
Wie Sie sehen, gibt es nicht genügend Berechtigungen für den Authentifizierungs-Fischadler, auf den wir bereits hingewiesen haben. Wir wenden uns der offiziellen Dokumentation der API zu, der
updateContent- Methode, mit der wir das Skript remote aktualisiert haben. Die Dokumentation besagt, dass für die Verwendung dieser Methode der Zugriff auf script.projects aktiviert werden muss:
https://www.googleapis.com/auth/script.projects
Fügen Sie es unserer Konfigurationsdatei im Bereich SCOPES hinzu. Wie ich oben geschrieben habe, ist es beim Ändern des Fischadlers erforderlich, das automatisch generierte Token zu löschen.
Großartig! Im Moment haben wir gelernt, Google-Skript aus der Ferne zu aktualisieren. Es bleibt zu laufen und das Ergebnis der Ausführung zu erhalten.
Skript ausführen
Die Skriptstartanforderung enthält die
Skript- ID und den Text mit der folgenden Struktur:
- function : Name der Funktion, die ausgeführt werden soll
- parameters : (optional) Eine Reihe von Parametern eines primitiven Typs (Zeichenfolge, Array ...), die an die Funktion übergeben werden
- sessionState : (optional) ist nur für Android-Anwendungen erforderlich
- devMode : (optional) True, wenn der Benutzer der Eigentümer des Skripts ist und dann die neueste Version gestartet wird, als diejenige, die mit der Apps Script-API bereitgestellt wurde. (standardmäßig - Falsch)
Um die URL des Google-Formulars im Skript nicht zusammenzufügen, übergeben wir
form_url als Argument an die
Hauptfunktion .
Achtung Beim Testen des Skripts hat die
Hauptfunktion nichts akzeptiert. Daher ändern wir die ersten Codezeilen in der .gs-Datei wie folgt:
function main(form_url) { var form = FormApp.openByUrl(form_url); .......
Da unsere Anwendung nicht für Android ist und wir die Eigentümer des Skripts sind, sieht der Text folgendermaßen aus:
body = { "function": "main", "devMode": True, "parameters": form_url }
Führen Sie das Skript aus und schreiben Sie das Ergebnis der Ausführung in die entsprechende Variable:
resp = service.scripts().run(scriptId=script_id, body=body).execute()
Speichern bzw. in eine Datei mit praktischer JSON-Formatierung:
import json with open('habr_auto.json', 'w', encoding='utf-8') as f: json.dump(resp['response']['result'], f, ensure_ascii=False, indent=4)
Achtung Aufgrund der Tatsache, dass die script.run () -Anforderung über den Socket auf das Ergebnis wartet und das Timeout um die Ausführungszeit überschritten wird, tritt ein Fehler des folgenden Typs auf:
socket.timeout: The read operation timed out
Um dieses Verhalten zu vermeiden, empfehle ich, zu Beginn des Programms ein Limit für die Open-Socket-Zeit festzulegen, das offensichtlich ausreicht, um auf die Ausführung des Skripts zu warten. In meinem Fall reichen 120 Sekunden:
import socket socket.setdefaulttimeout(120)
Voila! Eine praktische Pipeline zum Remote-Aktualisieren und Starten von Google-Skripten ist verfügbar. Der vollständige Code für den Start vom Terminal ist in meinem
Github angegeben .
Außerdem werde ich den Code der Hauptfunktionen unten geben
login.py from pprint import pprint import pickle import os.path from googleapiclient.discovery import build from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request def login(config): try: creds = None
update_script.py from pprint import pprint import json import sys from googleapiclient import errors from google_habr_login import login MANIFEST = ''' { "timeZone": "America/New_York", "exceptionLogging": "STACKDRIVER", "executionApi": { "access": "ANYONE" } } '''.strip() def update_project(service, script_id, script_file_name):
export_form.py from pprint import pprint import socket import json import sys from googleapiclient import errors from google_habr_login import login socket.setdefaulttimeout(120)
Zu Beginn müssen Sie die JSON-Datei mit den Google-Zugriffsschlüsseln im Ordner mit den Anmeldeinformationen und die Konfigurations-JSON im selben Verzeichnis wie die Skripts ablegen.
Wenn wir dann das Skript aus der Ferne aktualisieren möchten, rufen Sie im Terminal Folgendes auf:
python update_script.py <config_file_name> <script_id> <script_file_name>
In diesem Fall:
- config_file_name - Name der Konfigurations-JSON-Datei
- script_id - Skript-ID
- script_file_name - Der Name der .gs-Datei, die auf Google hochgeladen wird
Rufen Sie zum Ausführen des Skripts Folgendes auf:
python export_form.py <config_file_name> <result_file_name> <script_id> <google_form_url>
In diesem Fall:
- config_file_name - Name der Konfigurations-JSON-Datei
- result_file_name - Der Name der JSON-Datei, in die das Formular entladen wird
- script_id - Skript-ID
- google_form_url - Google-Formular-URL
Vielen Dank für Ihre Aufmerksamkeit und das Warten auf Ihre Vorschläge und Kommentare :)