Google Apps Script - ⑥WebAPIとして公開

GAEで書いたスクリプトをWebAPIとして公開してみます。

WebAPI用のスクリプト作成

メールの未読件数を返すスクリプトを作成します。

ポイントは以下の通りです。

  • 1行目
    GETアクセスがきたらdoGet()関数がコールされます。
  • 3行目
    Gmailの未読件数を取得します。
  • 8行目
    JavaScriptオブジェクトをJSONに変換する関数になります。

[Google Apps Script]

1
2
3
4
5
6
7
8
9
10
11
12
13
function doGet(e){
// 未読件数を取得
var cnt = GmailApp.getInboxUnreadCount();
return createJson({count: cnt});
}

// JavaScriptオブジェクトをJSONに変換
function createJson(data){
var json_str = JSON.stringify(data);
var mime_type = ContentService.MimeType.JSON;
var json = ContentService.createTextOutput(json_str).setMimeType(mime_type);
return json;
}

デプロイ

WebAPIとして公開するためには、作成したスクリプトをデプロイする必要があります。

①画面右上の[デプロイ]⇒[新しいデプロイ]を選択します。

②[歯車マーク]⇒[ウェブアプリ]を選択します。

③[説明][次のユーザとして実行][アクセスできるユーザ]を設定し、[デプロイ]ボタンを押します。

今回はテスト用なので、[説明]なし、[次のユーザとして実行]自分(開発ユーザ)、[アクセスできるユーザ]自分のみ としました。

④デプロイが正常に完了するとアクセス用のURLが表示されます。

動作確認

デプロイ時④に表示されたURLをブラウザで表示します。

[実行結果]

json形式でメールの未読件数が表示されました。

サーバを準備することなくこんなに簡単にWebAPIを開発できるのは少し感動します。

ただ無料枠での利用ですと回数制限や突然のサービス停止などのリスクはあると思いますが。。。

Google Apps Script - ⑤JSONデータ取得

気象庁の天気予報WebAPI

気象庁の天気予報WebAPIを使うと、json形式の天気予報データが取得できます。

JSONデータのフォーマットは下記の通りです。

  • publishingOffice
    データ配信元。 基本的に「気象庁」となっています。
  • reportDatetime
    報告日時。「+09:00」という部分はタイムゾーン。
  • targetArea
    対象の地域。130000.jsonは基本的に「東京都」。
    13が東京なのは、都道府県コードがそうなっているためです。
  • headlineText
    ヘッドラインです。
  • text
    詳細な概要情報です。
    改行は「\n\n」なので、ブラウザなどに表示するときはbrタグに変更する必要があります。

エリアコードは下記のjsonで確認できます。

エリアコード一覧 - https://www.jma.go.jp/bosai/common/const/area.json

JSONデータ取得

気象庁から東京(エリアコード:130000)の天気予報JSONデータを取得するソースコードは次の通りです。

[Google Apps Script]

1
2
3
4
5
6
7
8
// メイン関数
function main() {
var response = UrlFetchApp.fetch("https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json");
var json = JSON.parse(response.getContentText())
Logger.log(json) // jsonデータ全体。
Logger.log("targetArea:" + json.targetArea) // jsonデータ内の一部(trgetArea)を参照。
Logger.log("ヘッドライン:" + json.headlineText) // jsonデータ内の一部(headlineText)を参照。
}

[実行結果]

正常に、東京の天気予報データを取得することができました。

Google Apps Script - ④外部サイトにアクセス

GASで外部サイトにアクセスします。

外部サイトにアクセス

外部サイトにアクセスするソースは以下のようになります。

[Google Apps Script]

1
2
3
4
5
6
// メイン関数
function main() {
// 外部サイトにアクセス
var response = UrlFetchApp.fetch("https://ailog.site/2021/10/30/2021/1030/");
Logger.log(response.getContentText());
}

初回実行時に、Googleアカウントの認証が必要になります。

正常に実行されるとサイトのソースがログに表示されます。

[実行結果]

外部サイトにアクセスできるということは、いろいろな情報にアクセスできるということです。

取得した情報を定期的にまとめたり、その情報に応じてメールを送ったりといろいろなサービスを構築することができるようになります。

Google Apps Script - ③カレンダーに予定追加

GASでGoogleカレンダーに予定を追加します。

カレンダーに予定追加

GASでカレンダーに予定を追加するソースは以下のようになります。

[Google Apps Script]

1
2
3
4
5
6
7
8
9
10
11
12
// メイン関数
function main() {
createSchedule("予定(テスト)", 2021, 10, 31)
}

// スケジュールを追加する関数
function createSchedule(title, year, month, day) {
// カレンダーIDからカレンダーを取得
var calendar = CalendarApp.getCalendarById("xxxxxxxx@gmail.com");
// カレンダーに終日のスケジュールを追加
calendar.createAllDayEvent(title, new Date(year, month - 1, day))
}

初回実行時に、Googleアカウントの認証が必要になります。

9行目に、予定を追加するGoogleアカウントを設定します。

GASでは、Googleカレンダーに対して下記のような操作を行うこともできます。

  • 時間指定した予定を作る
  • 予定を調べる(読み込む)
  • 予定の削除

他の機能に関しては、下記の公式リファレンスをご参照下さい。

CalendarAppのリファレンス - https://developers.google.com/apps-script/reference/calendar/calendar-app

Google Apps Script - ②メール送信

GASでメール送信を行ってみます。

メール送信

GASでメールを送信するソースは以下のようになります。

1行で簡単に書けます。

[Google Apps Script]

1
2
3
function test() {
GmailApp.sendEmail("xxxxxxxxx@ailog.site", "タイトル", "本文");
}

初回実行時に、Googleアカウントの認証が必要になります。

他のプログラミング言語でメールの送信を行う場合、smtpやポート番号や認証情報などいろいろと設定が必要で面倒ですが、GASであればGoogleアカウントを使って簡単に送信できるのでとても楽ちんです。

Google Apps Script - ①ログ出力

Google Apps Scriptを使うと、日常的な作業を自動化することができます。

またパソコンを起動していなくても、自動起動することができるのでとても便利です。

今回からはGASのサンプルをいろいろ実行してみてその可能性を探っていきたいと思います。

ログ出力

まずは最も基本的な処理としてログの出力を行ってみます。

[Google Apps Script]

1
2
3
function test() {
Logger.log("Hello World");
}

ログの出力は、処理内容の確認やデバッグでよく使用しますのでとても大切な処理になります。

pyopenjtalk - 日本語の音声合成② 読み上げ音声の作成

今回は日本語音声データセットJSUTの読み上げ音声を、音声合成フレームワークのNVIDIA/tacotron2の読み上げ音声に変換します。

必要ライブラリのインストール

読み上げ音声の変換を行うためには以下のライブラリをインストールします。

[WSLコンソール]

1
2
pip install librosa==0.8.0
pip install PySoundFile==0.9.0.post1

読み上げ音声の変換

JSUTの読み上げ音声を、NVIDIA/tacotron2の読み上げ音声に変換するソースコードは次のようになります。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import os
import librosa
import soundfile as sf

# パス
in_paths = [
'jsut_ver1.1/basic5000/',
'jsut_ver1.1/countersuffix26/',
'jsut_ver1.1/loanword128/',
'jsut_ver1.1/onomatopee300/',
'jsut_ver1.1/precedent130/',
'jsut_ver1.1/repeat500/',
'jsut_ver1.1/travel1000/',
'jsut_ver1.1/utparaphrase512/',
'jsut_ver1.1/voiceactress100/']
out_path = 'wav/'

# 出力フォルダの準備
os.makedirs(out_path, exist_ok=True)

# wavを22KHzに変換
def convert(in_path, filename):
y, sr = librosa.core.load(in_path + 'wav/' + filename, sr=22050, mono=True)
sf.write(out_path + filename, y, sr, subtype="PCM_16")

# wavの変換
count = 0
for in_path in in_paths:
filenames = os.listdir(in_path + 'wav/')
for filename in filenames:
print(str(count) + '/7696')
count += 1
convert(in_path, filename)

上記のソースコードを実行すると次のようなログが表示されます。

[実行結果]

1
2
3
4
5
6
7
8
・・・(途中略)・・・
7689/7696
7690/7696
7691/7696
7692/7696
7693/7696
7694/7696
7695/7696

wavフォルダが作成されその中にNVIDIA/tacotron2用の読み上げ音声が出力されます。

フォルダ容量は1.53GBほどになりました。


次回は、音声合成の学習を行い・・・・たかったのですが、どうしてもうまくいかなかったのでまた別内容の記事を書き始めようと思います。<(_ _)>

pyopenjtalk - 日本語の音声合成① 台本の作成

日本語音声データセットJSUTを転移学習し、音声合成フレームワークのNVIDIA/tacotron2で日本語の音声合成を行います。

今回はNVIDIA/tacotron2用の台本を作成します。

JSUTデータセットの準備

下記サイトのJSUTの「台本」と「読み上げ音声データ」のデータセットを利用します。

JSUT - https://sites.google.com/site/shinnosuketakamichi/publication/jsut

WSLコンソールで下記のコマンドを実行し、ダウンロードと解凍を行います。

[WSLコンソール]

1
2
wget http://ss-takashi.sakura.ne.jp/corpus/jsut_ver1.1.zip
unzip jsut_ver1.1.zip

NVIDIA/tacotron2用の台本作成

ダウンロードしたJSUT用の台本をNVIDIA/tacotron2用の台本に変換するソースコードは以下の通りです。

[ソースコード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import os
import pyopenjtalk

# パス
in_paths = [
'jsut_ver1.1/basic5000/',
'jsut_ver1.1/countersuffix26/',
'jsut_ver1.1/loanword128/',
'jsut_ver1.1/onomatopee300/',
'jsut_ver1.1/precedent130/',
'jsut_ver1.1/repeat500/',
'jsut_ver1.1/travel1000/',
'jsut_ver1.1/utparaphrase512/',
'jsut_ver1.1/voiceactress100/']
out_path = 'filelists/'

# 出力フォルダの準備
os.makedirs(out_path, exist_ok=True)

# NVIDIA/tacotron2用の書式に変換
def convert(line):
strs = line.split(':')
strs[1] = pyopenjtalk.g2p(strs[1], kana=False)
strs[1] = strs[1].replace('pau',',')
strs[1] = strs[1].replace(' ','')
strs[1] = strs[1] + '.'
return 'wav/' + strs[0] + '.wav|' + strs[1] + '\n'

# transcriptの変換
with open(out_path + 'transcript_utf8.txt', 'w') as wf:
for in_path in in_paths:
with open(in_path + 'transcript_utf8.txt', 'r') as rf:
lines = rf.readlines()
for line in lines:
wf.write(convert(line))

上記の処理を実行すると、filelists/transcript_utf8.txtというファイルが作成されます。

このファイルがNVIDIA/tacotron2用の台本となります。


次回は、読み上げ音声の作成を行います。

pyopenjtalk - 音素表記

音素表記とは、音素が表記の単位になっている文字体系のことです。

今回は、pyopenjtalkというツールで音素表記の単位に変換してみます。

pyopenjtalkのインストール

pyopenjtalkはLinuxとMacOSに対応していますが、Windowsでは動作しません。

ですがWSLでは動作するようですので、WSLコンソールを使ってインストールしてみます。

[WSLコンソール]

1
2
sudo apt-get install cmake
sudo pip install pyopenjtalk

[実行結果]

1
2
3
・・・(途中略)・・・
Installing collected packages: tqdm, numpy, cython, pyopenjtalk
Successfully installed cython-0.29.24 numpy-1.21.3 pyopenjtalk-0.1.5 tqdm-4.62.3

最後に上記のようなログが表示されていれば、インストール完了です。

音素表記に変換

音素表記に変換するソースコードは下記のようになります。

[ソースコード]

1
2
import pyopenjtalk
print(pyopenjtalk.g2p('こんにちは'))

上記のPythonコードを実行します。

[実行結果]

1
2
3
4
Downloading: "https://github.com/r9y9/open_jtalk/releases/download/v1.11.1/open_jtalk_dic_utf_8-1.11.tar.gz"
dic.tar.gz: 100%|███████████████████████████████████████████████████████████████████| 22.6M/22.6M [03:07<00:00, 126kB/s]
Extracting tar file /usr/local/lib/python3.8/dist-packages/pyopenjtalk/dic.tar.gz
k o N n i ch i w a

「k o N n i ch i w a」という音素表記に変換することができました。

Deep Daze - ノートPCで実行(GPUなし)

Big SleepDeep Dazeを使って、テキストから画像を生成する処理を試してみました。

ただGoogle Colaboratoryの制限のために、処理の途中でセッションが切られてしまい最後まで画像を生成することができませんでした。

Google Colaboratoryの有料版を使ってテストしたいところですが、とりあえずローカル実行でどこまでできるかを確認してみます。

ローカルPCのスペック

実行するノートパソコンのスペックは下記の通りです。

  • CPU
    Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz 1.99 GHz
  • GPU
    なし
  • メモリ
    16GB
  • Pythonバージョン
    3.8.3

Big SleepはGPUなしでは実行できなかったので、Deep Dazeの方でローカル実行してみます。

Deep Dazeのインストール

次のコマンドを実行し、Deep Dazeをインストールします。

[コンソール]

1
pip install deep-daze

[実行結果]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Collecting deep-daze
Downloading deep_daze-0.10.3-py3-none-any.whl (1.4 MB)
|████████████████████████████████| 1.4 MB 6.4 MB/s
Requirement already satisfied: fire in c:\util\anaconda3\lib\site-packages (from deep-daze) (0.4.0)
Collecting siren-pytorch>=0.0.8
Downloading siren_pytorch-0.1.5-py3-none-any.whl (3.9 kB)
Requirement already satisfied: regex in c:\util\anaconda3\lib\site-packages (from deep-daze) (2020.6.8)
Collecting torch-optimizer
Downloading torch_optimizer-0.1.0-py3-none-any.whl (72 kB)
|████████████████████████████████| 72 kB ...
Requirement already satisfied: torchvision>=0.8.2 in c:\util\anaconda3\lib\site-packages (from deep-daze) (0.9.1)
Requirement already satisfied: einops>=0.3 in c:\util\anaconda3\lib\site-packages (from deep-daze) (0.3.2)
Requirement already satisfied: imageio>=2.9.0 in c:\util\anaconda3\lib\site-packages (from deep-daze) (2.9.0)
Requirement already satisfied: torch>=1.7.1 in c:\util\anaconda3\lib\site-packages (from deep-daze) (1.8.1)
Requirement already satisfied: tqdm in c:\util\anaconda3\lib\site-packages (from deep-daze) (4.57.0)
Requirement already satisfied: ftfy in c:\util\anaconda3\lib\site-packages (from deep-daze) (6.0.3)
Requirement already satisfied: six in c:\util\anaconda3\lib\site-packages (from fire->deep-daze) (1.15.0)
Requirement already satisfied: termcolor in c:\util\anaconda3\lib\site-packages (from fire->deep-daze) (1.1.0)
Collecting pytorch-ranger>=0.1.1
Downloading pytorch_ranger-0.1.1-py3-none-any.whl (14 kB)
Requirement already satisfied: pillow>=4.1.1 in c:\util\anaconda3\lib\site-packages (from torchvision>=0.8.2->deep-daze) (7.2.0)
Requirement already satisfied: numpy in c:\util\anaconda3\lib\site-packages (from torchvision>=0.8.2->deep-daze) (1.18.5)
Requirement already satisfied: typing-extensions in c:\util\anaconda3\lib\site-packages (from torch>=1.7.1->deep-daze) (3.7.4.2)
Requirement already satisfied: wcwidth in c:\util\anaconda3\lib\site-packages (from ftfy->deep-daze) (0.2.5)
Installing collected packages: siren-pytorch, pytorch-ranger, torch-optimizer, deep-daze
Successfully installed deep-daze-0.10.3 pytorch-ranger-0.1.1 siren-pytorch-0.1.5 torch-optimizer-0.1.0

インストールは問題なく終了しました。

テキストから画像生成

テキストから画像を生成します。

指定するテキストは“shattered plates on the grass”(草の上に粉々になったプレート)としました。

[コンソール]

1
imagine "shattered plates on the grass"

15:00スタート
とりあえず18時間後に生成されている画像は下記の通りです。

(まだ処理が終了していません・・・😥😥😥)


それっぽい画像になっていると思います。

GPUなしだとかなり時間はかかってしまいますが、Google Colaboratoryのように途中でセッションを切られてしまうことはないので安心です。


ちなみに、途中で動画編集ソフトを立ち上げたらメモリ不足エラーになって進捗94%のところで終了してしまいました😥😥😥

[メモリー不足のログ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
image updated at "./shattered_plates_on_the_grass.000001.jpg"
image updated at "./shattered_plates_on_the_grass.000002.jpg"
image updated at "./shattered_plates_on_the_grass.000003.jpg"
image updated at "./shattered_plates_on_the_grass.000004.jpg"
image updated at "./shattered_plates_on_the_grass.000005.jpg"
image updated at "./shattered_plates_on_the_grass.000006.jpg"
image updated at "./shattered_plates_on_the_grass.000007.jpg"
image updated at "./shattered_plates_on_the_grass.000008.jpg"
image updated at "./shattered_plates_on_the_grass.000009.jpg"
loss: -46.64: 94%|█████████████████████████████████████████████████████████████████████████████▍ | 991/1050 [15:15:47<54:31, 55.45s/it]
epochs: 0%| | 0/20 [15:15:47<?, ?it/s]
Traceback (most recent call last):
File "c:\util\anaconda3\lib\runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "c:\util\anaconda3\lib\runpy.py", line 87, in _run_code
exec(code, run_globals)
File "C:\Util\anaconda3\Scripts\imagine.exe\__main__.py", line 7, in <module>
File "c:\util\anaconda3\lib\site-packages\deep_daze\cli.py", line 151, in main
fire.Fire(train)
File "c:\util\anaconda3\lib\site-packages\fire\core.py", line 141, in Fire
component_trace = _Fire(component, args, parsed_flag_args, context, name)
File "c:\util\anaconda3\lib\site-packages\fire\core.py", line 466, in _Fire
component, remaining_args = _CallAndUpdateTrace(
File "c:\util\anaconda3\lib\site-packages\fire\core.py", line 681, in _CallAndUpdateTrace
component = fn(*varargs, **kwargs)
File "c:\util\anaconda3\lib\site-packages\deep_daze\cli.py", line 147, in train
imagine()
File "c:\util\anaconda3\lib\site-packages\torch\nn\modules\module.py", line 889, in _call_impl
result = self.forward(*input, **kwargs)
File "c:\util\anaconda3\lib\site-packages\deep_daze\deep_daze.py", line 584, in forward
_, loss = self.train_step(epoch, i)
File "c:\util\anaconda3\lib\site-packages\deep_daze\deep_daze.py", line 508, in train_step
self.scaler.scale(loss).backward()
File "c:\util\anaconda3\lib\site-packages\torch\tensor.py", line 245, in backward
torch.autograd.backward(self, gradient, retain_graph, create_graph, inputs=inputs)
File "c:\util\anaconda3\lib\site-packages\torch\autograd\__init__.py", line 145, in backward
Variable._execution_engine.run_backward(
RuntimeError: [enforce fail at ..\c10\core\CPUAllocator.cpp:75] data. DefaultCPUAllocator: not enough memory: you tried to allocate 268435456 bytes. Buy new RAM!

Buy new RAM!(新しいRAM買え)って・・・・そんなログ始めて見ました😭😭😭