ChatGPT,チュートリアル

前準備

  1. App Inventor日本語版でプロジェクトメニューから「選択したプロジェクトをローカルコンピュータにエクスポート(.aia)」を実行し、ローカルコンピュータに.aiaファイルをダウンロードします。
  2. ローカルコンピュータでダウンロードしたファイルの.aiaを.zipに変更し、このファイルを解凍します。
  3. フォルダーの中にScreen1.bky, Screen1.scm, project.propertiesの三つのファイルがあります。これらをGPT-4に送り込みます。

GPT-4とのやり取り

プロンプト

ソースコードは長いので省略しますが実際はファイル名の後にコピペしています。日本語はあまり得意ではないので英語でやりとりして最後に日本語に翻訳してもらいます。ここで使ったのはChatGPTアプリのファイルです。

Do you understand what the App Inventor project files doing below?

project.properties:

Screen1.scm:

Screen1.bky:

返答

Yes, these project files appear to be part of an app created using the MIT App Inventor, a visual programming environment that allows users to create mobile apps for Android.

Let’s briefly explain what each of the file is doing:

  • project.properties: This is a properties file for the project that contains information about the source code, app name, default file scope, main form, color scheme, sizing, assets location, theme, version name and version code. This file also contains some other properties such as if the app uses the device’s location or not.
  • Screen1.scm: This file defines the components on the “Screen1" of the app. It appears to have one “TextBox" (named TextBox1) for user input, a “Button" (Button1) to submit the input, a “Label" (Label3) to display the inputted prompt, another “Label" (Label2) to display the response, and a “Web" component (Web1) for making web requests to OpenAI’s API.
  • Screen1.bky: This is the logic of the app written in Blockly, a web-based, graphical programming language.
    • When the “Button1" is clicked, it checks if the TextBox is not empty. If it’s not, the program sets the request headers of the Web component, including the Authorization header using a global variable API_KEY. It then makes a POST request to the OpenAI’s API endpoint (https://api.openai.com/v1/chat/completions) with a JSON body including the model to use (“gpt-3.5-turbo") and the user’s input message.
    • When the Web component receives the text from the OpenAI’s API, it parses the JSON response to retrieve the response message from the model and displays it in the “Label2". It also updates the “Label3" to show the user’s inputted prompt and clears the TextBox for the next input.

In summary, this app allows a user to enter a prompt in the TextBox. Upon pressing the Button, the app makes a POST request to the OpenAI’s API, using the GPT-3.5-turbo model, to generate a response to the prompt. The prompt and the response are then displayed on the screen.

正しく理解しているようなので、チュートリアルを書いてもらいます。

プロンプト

Can you write a tutorial to make this App Inventor App?

返答

Sure, here is a step-by-step tutorial on how to recreate this App Inventor App. This app basically acts as a simple interface for the OpenAI GPT-3 API. You type in a prompt in a textbox, press a button, and the GPT-3 API is queried with your prompt. The response is then shown in the app.

Prerequisites

  • You should have an MIT App Inventor account. If you don’t have one, you can create it at: MIT App Inventor.
  • You should have a valid OpenAI API Key.

Step 1: Start a new project

  1. Log into your App Inventor account.
  2. Click on “Start a new project" and give it a name (e.g., “ChatGPT").

Step 2: Set up the screen

  1. Click on “Screen1" in the component section.
  2. In the properties panel on the right, change the Title to “Text2ChatGPT".

Step 3: Add components

In the palette panel on the left, add the following components:

  1. User Interface: TextBox (rename to TextBox1), Button (rename to Button1), and two Labels (rename to Label2 and Label3 respectively).
  2. Connectivity: Web (rename to Web1).

Step 4: Set up the components

  1. TextBox1: Set the “Hint" to “Enter your prompt here".
  2. Button1: Set the “Text" to “Submit".
  3. Label3: Set the “Text" to “Prompt".
  4. Label2: Set the “Text" to “Response".

Step 5: Define variables

  1. In the Blocks panel, click on “Variables" and then on “Initialize global name to".
  2. Name the variable “API_KEY" and set it to your OpenAI API key.

Step 6: Define blocks for Button1.Click event

When the Button1 is clicked, it needs to call the GPT-3 API with the text entered in TextBox1.

  1. Drag and drop the “when Button1.Click do" block.
  2. If TextBox1’s text is not empty, set the Web1’s request headers and make a POST request to the GPT-3 API.
    • Set the “Content-Type" header to “application/json".
    • Set the “Authorization" header to “Bearer " + API_KEY.
    • The body of the POST request should be a JSON string that includes the model, messages, and temperature attributes.

Step 7: Define blocks for Web1.GotText event

When the API returns a response, the “when Web1.GotText do" block is executed.

  1. In this block, parse the JSON response, extract the content of the GPT-3 response, and set it as the text for Label2.
  2. Also, set the text for Label3 to the text of TextBox1 (the initial user prompt).
  3. Lastly, clear the text in TextBox1.

With this, your app is complete. Note that the actual blocks and their configuration may vary depending on your specific requirements and use case. Also, be sure to replace the placeholder “API_KEY" with your actual OpenAI API key. Please handle this key securely, as misuse could lead to charges on your OpenAI account.

Important: Remember to comply with OpenAI’s use case policy while using the API. For any extensive use, you may need to apply for OpenAI’s approval. The app created here is for educational purposes and does not handle errors that might occur during the API call. You may need to handle those scenarios as per your needs.

プロンプト

Translate it into Japanese

返答

もちろんです。以下に、このApp Inventorアプリを作成するためのステップバイステップのチュートリアルを日本語で示します。このアプリは、OpenAI GPT-3 APIのシンプルなインターフェースとして機能します。テキストボックスにプロンプトを入力し、ボタンを押すと、GPT-3 APIにあなたのプロンプトを問い合わせます。その後、応答がアプリに表示されます。

必要条件

  • MIT App Inventorのアカウントを持っていること。持っていない場合は、こちらで作成できます: MIT App Inventor
  • 有効なOpenAI APIキーを持っていること。

ステップ1: 新しいプロジェクトを開始する

  1. App Inventorアカウントにログインします。
  2. “新しいプロジェクトを開始する"をクリックし、名前を付けます(例:"ChatGPT")。

ステップ2: スクリーンの設定

  1. コンポーネントセクションで"Screen1″をクリックします。
  2. 右側のプロパティパネルで、タイトルを"Text2ChatGPT"に変更します。

ステップ3: コンポーネントの追加

左側のパレットパネルから、以下のコンポーネントを追加します:

  1. ユーザーインターフェース: TextBox(名前をTextBox1に変更)、Button(名前をButton1に変更)、二つのLabels(それぞれ名前をLabel2とLabel3に変更)。
  2. 接続: Web(名前をWeb1に変更)。

ステップ4: コンポーネントの設定

  1. TextBox1: “Hint"を"ここにプロンプトを入力してください"に設定します。
  2. Button1: “Text"を"送信"に設定します。
  3. Label3: “Text"を"プロンプト"に設定します。
  4. Label2: “Text"を"レスポンス"に設定します。

ステップ5: 変数の定義

  1. ブロックパネルで"Variables"をクリックし、"Initialize global name to"をクリックします。
  2. 変数名を"API_KEY"とし、その値をあなたのOpenAI APIキーに設定します。

ステップ6: Button1.Clickイベントのブロックを定義する

Button1がクリックされると、TextBox1に入力されたテキストでGPT-3 APIを呼び出す必要があります。

  1. “when Button1.Click do"ブロックをドラッグします。
  2. 次に、"set Web1.Url to"ブロックを追加し、その値を"https://api.openai.com/v1/engines/davinci-codex/completions"に設定します。
  3. “set Web1.Headers to"ブロックを追加し、その値を"create empty list"ブロックと"add items to list"ブロックで作成したリストに設定します。リストには"Authorization"と"Bearer “+API_KEY、"Content-Type"と"application/json"を追加します。
  4. “call Web1.PostText"ブロックを追加し、そのテキストを以下のJSON文字列に設定します:{ "prompt": "ここにプロンプト", "max_tokens": 60 }。"ここにプロンプト"はTextBox1のテキストに置き換えます。

ステップ7: Web1.GotTextイベントのブロックを定義する

Web1.GotTextイベントは、Web1.PostTextが完了したときに発生します。このイベントは、APIからのレスポンスを処理するために使用されます。

  1. “when Web1.GotText do"ブロックをドラッグします。
  2. “set Label2.Text to"ブロックを追加し、その値を"get responseContent"ブロックに設定します。

以上で、App InventorでOpenAI GPT-3 APIを使用するためのシンプルなアプリを作成するための手順が終わりです。アプリをテストして、OpenAI GPT-3と対話できることを確認しましょう。

GPTは間違ったことを言うことがあるので、最後に必ず内容を確認してください。

チュートリアル

MITから2023年6月13日に公開されたnb193には実験中のカテゴリーにChatBotとImageBotという2つのコンポーネントが追加されていて、OpenAIの生成AIにアクセスできるようになっています。日本語化プロジェクトではnb193の日本語版を作る作業をしていましたが、ソースコードを読んだところ、これらのコンポーネントには以下の問題があることがわかりました。

  • これらコンポーネントはMITのサーバー経由でOpenAIのAPIキーを使わなくても生成AIが使えるようになっており(ただしかなりきつい制限ありMIT以外のApp InventorからはこのMITのサーバーを利用できない
  • これらコンポーネントでは利用者が取得したOpenAIのAPIキーを使用することもできるようになっているが、その場合でもMITのサーバーにアクセスする。利用者はOpenAIのAPIキーをMITに取られる危険性がある

これらの事実から日本語化プロジェクトはOpenAIのGPT-4を使って独自に開発していたApp Inventor版「ChatGPTと話そう(その1)」アプリの作り方のチュートリアルを公開し、日本の利用者の皆さんがOpenAIの生成AIにアクセスできるようにすることにしました。このコードは直接OpenAIにAPIキーとプロンプトをSSL暗号化を使って送信するのでAPIキーが途中で取られる心配はありません。

せっかくなのでこのチュートリアルの文章はGPT-4に書いてもらってから加筆・修正しました。細かいところまではGPT-4はうまくは書けないですが大筋は良さそうです。チュートリアルの書いてもらい方はこちらです。

OpenAIのAPIキーの取り方は色々なところで説明があります。例えばこちらなど。

このアプリのaiaファイルはこのリンクからダウンロードできます

ステップ1: 新しいプロジェクトを開始する

  1. App Inventorアカウントにログインします。
  2. “新しいプロジェクトを開始する"をクリックし、名前を付けます(例:"ChatGPT")。

ステップ2: スクリーンの設定

  1. コンポーネントセクションで"Screen1″をクリックします。
  2. 右側のプロパティパネルで、タイトルを"ChatGPT"に変更します。

ステップ3: コンポーネントの追加

左側のパレットパネルから、以下のコンポーネントを追加します:

  1. ユーザーインターフェース: テキストボックス(テキストボックス1)、ボタン(ボタン1)、二つのラベル(それぞれラベル1とラベル2)。
  2. 接続: Web(Web1)。

ステップ4: コンポーネントの設定

  1. テキストボックス1: “ヒント"を"プロンプトを入れてください"に設定し、幅を画面いっぱいに表示にします。
  2. ボタン1: “テキスト"を"送信"に設定します。
  3. ラベル1: “テキスト"を"プロンプト"に設定します。
  4. ラベル2: “テキスト"を"返答"に設定します。

ステップ5: 変数の定義

  1. ブロック編集で"変数"をクリックし、"グローバル変数を次の値で初期化"をクリックします。
  2. 変数名を"API_KEY"とし、その値をあなたのOpenAI APIキーに設定します。ここではダミーのAPIキーを入れてあります。

ステップ6: ボタン1.クリックされたらイベントのブロックを定義する

ボタン1がクリックされると、テキストボックス1に入力されたテキストでGPT-3 APIを呼び出す必要があります。

  1. “いつもボタン1クリックされたら実行する"ブロックをドラッグします。
  2. このブロックに、" 設定Web1.URLを"ブロックを追加し、その値を"https://api.openai.com/v1/engines/davinci-codex/completions"に設定します。
  3. “設定Web1.リクエストヘッダを"ブロックを追加し、その値を"リストを作成"ブロックで作成したリストに設定します。リストには"Authorization"と"Bearer “+API_KEY、"Content-Type"と"application/json"を追加します。詳しくは下の図を見てください。
  4. “呼び出すWeb1.テキストをPost"ブロックを追加し、そのテキストを以下のJSON文字列に設定します。このブロックをいつもボタン1クリックされたら実行する"ブロックに入れます。

ステップ7: いつもWeb1.テキストに受け取ったらイベントのブロックを定義する

“いつもWeb1.テキストに受け取ったら"イベントは、"呼び出すWeb1.テキストをPost"が完了したときに発生します。このイベントは、APIからのレスポンスを処理するために使用されます。

  1. “いつもWeb1.テキストに受け取ったら"ブロックをドラッグします。
  2. “設定ラベル2.テキストを"ブロックを追加し、その値をOpenAIから受け取ったJSONデータを"呼び出すWeb1.Jsonデコード"を使ってdictinaryに変換した後、連想配列(dictionary)にある"ペアの値を取得:キーパス"ブロックを使って返答を取り出します。[と]を空白に置換しないとWeb1.Jsonデコードでうまく処理できませんでした。
  3. “設定ラベル1.テキストを"ブロックを追加しテキストボックス1のテキストを設定します。
  4. “設定テキストボックス1.テキストを"ブロックを追加し空白テキストを設定します。

このアプリはAPIキーを入れればこのままで動きますが、API呼び出しの詳細に興味のある方は以下のページを参考にしてください。

https://platform.openai.com/docs/api-reference/making-requests

ChatGPT,チュートリアル

GPT-4を使ってApp InventorアプリのチュートリアルページからiPhoneアプリを作ることを試み、既に5種類のiPhoneアプリを作ることに成功しました。しかし、一々チュートリアルページを作るのは面倒なので、App Inventorアプリの情報をチュートリアルページを使う以外でGPT-4に渡すことができないかと検討していましたが、やっと見つかりました。ここではApp Inventor版「 ChatGPTと話そう!(その1)」 に音声入力・出力機能を付け加えて、「ChatGPTと話そう!(その2)」を作ります。ただし英語でGPT-4プロンプトを入力しないとまともな返答をしてくれませんでした。

前準備

  1. App Inventor日本語版でApp Inventor版 ChatGPTと話そう!(その1)プロジェクトを選択してプロジェクトメニューから「選択したプロジェクトをローカルコンピュータにエクスポート(.aia)」を実行し、ローカルコンピュータに.aiaファイルをダウンロードします。
  2. ローカルコンピュータでダウンロードしたファイルの.aiaを.zipに変更し、このファイルを解凍します。
  3. フォルダーの中にScreen1.bky, Screen1.scm, project.propertiesの三つのファイルがあります。これらをGPT-4に送り込みます。

GPT-4とのやり取り

プロンプト

ソースコードは長いので省略しますが実際はファイル名の後にコピペしています。

Do you understand what the App Inventor project files doing below?

project.properties:

Screen1.scm:

Screen1.bky:

返答

These are MIT App Inventor project files which define an app that interacts with the OpenAI GPT-3 model to provide a chat interface. Let’s break down what each file is doing:

  1. project.properties: This file contains metadata about the project, such as the app name, version, primary and accent colors, location usage, etc. It also defines the main screen of the application and the location of source files, assets, and build files.
  2. Screen1.scm: This file describes the components of Screen1 in the form of a JSON object. It specifies the UI components and their properties such as Text Box, Button, Labels, and Web components.
  3. Screen1.bky: This file is written in Blockly, a visual programming language developed by Google. It defines the functionality of the application. Here’s a high-level overview of what this file is doing:
    • The app takes input text from TextBox1.
    • When Button1 is clicked, if the input text is not empty, a web request is made to the OpenAI API. The headers for this request are set to include the appropriate Content-Type and Authorization (with a global API key). The body of the POST request is formed as per the OpenAI API specifications, including the input text as the message from the user to the GPT-3 model.
    • When the response from the API (Web1.GotText event) is received, it is decoded from JSON and processed. The content field is extracted from the choices array, which contains the GPT-3 model’s generated response. This response text is then displayed in Label2. The original input text is also displayed in Label3, and TextBox1 is cleared.

Please note that these are general interpretations based on the information provided. The precise behavior of the app might vary and is subject to the entire codebase and the runtime environment.

正しく理解しているようなので、音声入力・出力機能を追加してもらいます。

プロンプト

Can you add voice input and voice output functions and generate new project.propertie, Screen1.scm, and Screen1.bky?

返答

To add voice input and output functionality, we will need to add the SpeechRecognizer and TextToSpeech components to our App Inventor project. I will include them in the Screen1.scm file.

As for the Screen1.bky file, you will need to modify your button click event to use the SpeechRecognizer.GetText() method, and in the event where the SpeechRecognizer.AfterGettingText(), send that text to the Web API. Also, we will handle the Web.GotText() event to extract the response, and feed that into the TextToSpeech.Speak() method.

Note that the API key and other critical values in the Blockly file are placeholders and should be replaced with the actual values.

Here is what your modified files might look like:

project.properties:

#
#Wed Jun 07 18:19:49 UTC 2023
source=../src
name=Text2ChatGPTWorking
defaultfilescope=App
main=appinventor.ai_tam0130.Text2ChatGPTWorking.Screen1
color.accent=&HFFFF4081
sizing=Responsive
assets=../assets
theme=Classic
showlistsasjson=True
useslocation=False
aname=Text2ChatGPT
actionbar=False
color.primary=&HFF3F51B5
build=../build
versionname=1.0
versioncode=1
color.primary.dark=&HFF303F9F

Screen1.scm:

#|
$JSON
{"authURL":["ai2.tmsoftwareinc.com"],"YaVersion":"221","Source":"Form","Properties":{"$Name":"Screen1","$Type":"Form","$Version":"30","AppName":"Text2ChatGPT","Title":"Text2ChatGPT","Uuid":"0","$Components":[
{"$Name":"TextBox1","$Type":"TextBox","$Version":"6","Width":"-2","Hint":"\u30d7\u30ed\u30f3\u30d7\u30c8\u3092\u5165\u308c\u3066\u304f\u3060\u3055\u3044","Uuid":"-1671791960"},
{"$Name":"Button1","$Type":"Button","$Version":"7","Text":"\u9001\u4fe1","Uuid":"2041796963"},
{"$Name":"Label3","$Type":"Label","$Version":"5","Text":"\u30d7\u30ed\u30f3\u30d7\u30c8","Uuid":"1877175895"},
{"$Name":"\u30e9\u30d9\u30eb1","$Type":"Label","$Version":"5","Uuid":"1111020969"},
{"$Name":"Label2","$Type":"Label","$Version":"5","Text":"\u8fd4\u7b54","Uuid":"-1790405606"},
{"$Name":"Web1","$Type":"Web","$Version":"8","Url":"https:\/\/api.openai.com\/v1\/chat\/completions","Uuid":"1099905226"},
{"$Name":"SpeechRecognizer1","$Type":"SpeechRecognizer","$Version":"2","Uuid":"1"},
{"$Name":"TextToSpeech1","$Type":"TextToSpeech","$Version":"5","Uuid":"2"}
]}}
|#

Screen1.bky:

This file is too large to show here completely, but here are the key changes you would need to make.

For the Button1.Click event, instead of immediately sending the text from the TextBox1 to the API, you would first call SpeechRecognizer1.GetText(). This will start the speech recognition process.

Next, you will need to handle the SpeechRecognizer1.AfterGettingText event. In this event, you would call the same logic that was originally in the Button1.Click event, except you would replace TextBox1.Text with the result variable from the SpeechRecognizer1.AfterGettingText event.

Finally, in the Web1.GotText event, you would add a line at the end to call TextToSpeech1.Speak(result), where result is the final message string you extracted from the JSON response.

Please remember to replace the placeholders with actual values. Blockly XML format is intricate, so you might want to use the visual interface of MIT App Inventor to make these changes.

この手順に従ってApp Inventor版 ChatGPTと話そう!(その1)を変更すると問題無く動きました。

ChatGPT,チュートリアル

音声入力・出力が動いた「Ionic版 ChatGPTと話そう!(その2)」のソースコードをChatGPTに与えてApp Inventorアプリに変換してもらおうとしました。GPT-4だけで動くものを作るのはApp Inventorの知識不足で難しそうなので、GPT-4の返答を参考にしながら人間が作るのが効率が良さそうです。ここではそのように作りました。

改訂)App Inventor版 ChatGPTと話そう!(その1)の情報をGPT-4に渡すことができないかと検討していましたが、やっと見つかりました。ただし英語でプロンプトを入力しないとまともな返答をしてくれません。詳細はこちらから

GPT-4とのやり取り

プロンプト

ソースコードは長いので省略しますが実際はファイル名の後にコピペしています。

次のIonicプログラムと同じ機能を持つApp Inventorアプリを作る方法をステップ1からステップ3にしたがって教えてください。初心者でもわかるように詳しくお願いします。初心者でもわかるように詳しくお願いします。

ステップ1:細かい機能の説明
ステップ2:作り方の概略
ステップ3:初めての人でもわかるような詳しい作り方。ChatGPTとの通信部分はAPIのことを知らない人にもわかるように説明してください

home.page.html

home.page.ts

返答

提供していただいたIonicプログラムは、ユーザが音声入力を開始・停止し、その音声内容をテキストとしてOpenAIのChatGPT APIに送信し、その結果を音声として再生するというものです。この機能をApp Inventorで実装する方法を以下のステップで説明します。

ただし、App InventorはIonicなどの一部のフレームワークに比べて機能面で制限があるため、全ての機能を再現することは難しいかもしれません。特にOpenAIのChatGPT APIへの直接的な接続はサポートされていないため、代わりの方法を提案します。

ステップ1:細かい機能の説明

まずIonicプログラムがどのような機能を持っているか説明します。

  • ユーザがテキストエリアにテキストを入力できます。
  • 音声入力開始ボタンをクリックすると、音声認識が開始されます。
  • 音声入力停止ボタンをクリックすると、音声認識が停止されます。
  • 送信ボタンをクリックすると、入力されたテキスト(音声認識結果)がOpenAIのChatGPT APIに送信されます。
  • ChatGPT APIからのレスポンスはテキストとして画面に表示され、また音声として再生されます。

ステップ2:作り方の概略

次に、これらの機能をApp Inventorで再現するための概略を説明します。

  1. ユーザインターフェースを設定します。テキストボックス、ボタンなどのコンポーネントを配置します。
  2. 音声認識を開始するためのボタンのイベントハンドラを設定します。App InventorのSpeechRecognizerコンポーネントを利用します。
  3. 音声認識の結果をテキストボックスに表示するように設定します。
  4. 送信ボタンのイベントハンドラを設定します。ここでは、Webコンポーネントを使用して外部APIに接続します。
  5. レスポンスをテキストとして表示し、音声として再生するように設定します。TextToSpeechコンポーネントを使用します。

ステップ3:初めての人でもわかるような詳しい作り方

以下に、具体的な手順を詳細に説明します。ただし、OpenAIのAPIを直接使用することができないので、その代わりに任意の外部APIを使用する例を示します。なお、実際にはAPIの利用には適切なキーが必要です。

  1. App Inventorを開き、新しいプロジェクトを作成します。
  2. ユーザインターフェースの設定:
    • Designer画面で、左側のPaletteからTextBoxを選択し、Viewerの任意の位置にドラッグアンドドロップします。これがユーザの入力を受け取るテキストボックスとなります。
    • 同様に、PaletteからButtonを3つ追加します。それぞれのボタンには、"音声入力開始"、"音声入力停止"、"送信"といった適切なテキストを設定します。
    • PaletteからLabelを選択し、Viewerに追加します。これがAPIのレスポンスを表示するためのものです。
  3. 音声認識の設定:
    • PaletteからSpeechRecognizerを選択し、Viewerに追加します。このコンポーネントは非表示です。
    • “音声入力開始"ボタンをクリックした時のイベントハンドラを設定します。Blocks画面で、SpeechRecognizer1.GetTextブロックを追加します。
  4. 音声認識の結果を表示する設定:
    • Blocks画面で、SpeechRecognizer1.AfterGettingTextイベントを選択します。
    • TextBox1のTextに結果を設定するためのブロックを追加します。これにより、音声認識の結果がテキストボックスに表示されます。
  5. 外部APIへの送信設定:
    • PaletteからWebを選択し、Viewerに追加します。このコンポーネントは非表示です。
    • “送信"ボタンをクリックした時のイベントハンドラを設定します。ここでは、Web1.UrlWeb1.Getブロックを使用して外部APIに接続します。
  6. レスポンスの表示と音声再生の設定:
    • PaletteからTextToSpeechを選択し、Viewerに追加します。このコンポーネントは非表示です。
    • Web1.GotTextイベントを設定します。このイベントは、WebコンポーネントがAPIからレスポンスを受け取った時に発生します。
    • Label1のTextにレスポンスを設定し、TextToSpeechコンポーネントを使用してレスポンスを音声として再生します。

以上が、IonicプログラムをApp Inventorで実装する方法です。なお、OpenAIのAPIを直接使用する代わりに、自分で作成した中間サーバーを利用する方法もあります。その中間サーバーでOpenAIのAPIと通信を行い、App Inventorからはその中間サーバーにリクエストを送るという形になります。

この返答だけで作るのは難しそうなので音声入力・出力部分はこの返答を参考にしながら「ChatGPTと話そう!(その1)」に音声入力・出力機能を追加しました。音声入力は自動的に停止するので音声入力停止ボタンは機能していません。

デザイン編集画面
ブロック編集画面

ChatGPT,チュートリアル

ChatGPT (GPT-4)を使って作ろうとした、ChatGPTに入力したプロンプトを送って返答を画面に表示するApp Inventorアプリ「ChatGPTと話そう!(その1)」です。

ChatGPT (GPT-4)は万能ではありません。間違ったことを教えることもあるので、その都度修正してもらう必要があります。ここでは結局ChatGPT (GPT-4)はApp Inventorアプリを作れなかったので、人間が作りました。どうもGPT-4はApp Inventorの知識はあまりないようですね。

クリックして動いているところを見てください

GPT-4とのやり取り

プロンプト 「iPhone版 ChatGPTと話そう!(その1)」とほぼ同じものです

次の機能を持つアンドロイドアプリをApp Inventorで作る方法をステップ1からステップ3にしたがって教えてください。App Inventorの初心者でもわかるように詳しくお願いします。

機能:
1. 入力したテキストをChatGPTに送信。モデルはgpt-3.5-turbo
2. 戻ってきた返信を画面に表示

ステップ1:細かい機能の説明
ステップ2:作り方の概略
ステップ3:初めての人でもわかるような詳しい作り方。ChatGPTとの通信部分はAPIのことを知らない人にもわかるように説明してください

返答

ステップ1:細かい機能の説明 このアンドロイドアプリでは、次の機能があります。

  1. ユーザーがテキストを入力できるテキストボックス
  2. テキストが入力された後、送信ボタンをクリックすると、入力されたテキストがChatGPT(gpt-3.5-turbo)に送信される
  3. ChatGPTから返信を受け取り、画面上に表示する

ステップ2:作り方の概略

  1. MIT App Inventorで新しいプロジェクトを作成
  2. 必要なコンポーネントをデザイナー画面に配置
  3. 各コンポーネントをブロックエディタで関連付ける
  4. ChatGPT APIに接続するための設定

ステップ3:初めての人でもわかるような詳しい作り方

  • MIT App Inventorのウェブサイト(http://appinventor.mit.edu/)にアクセスし、新しいプロジェクトを作成します。
  • デザイナー画面で以下のコンポーネントを配置します。
    • テキストボックス(入力)
    • ボタン(送信ボタン)
    • ラベル(返信の表示)
    • Web コンポーネント(APIと通信)
  • ブロックエディタで以下の手順に従ってブロックを作成します。
    a. 送信ボタンがクリックされたら、入力されたテキストを取得し、Web コンポーネントでOpenAIのAPIに送信するブロックを作成します。
    b. APIからの返信を受け取ったら、返信の内容をラベルに表示するブロックを作成します
  • ChatGPT APIに接続するための設定を行います。
    a. OpenAIのウェブサイト(https://beta.openai.com/signup/)でAPIキーを取得します。
    b. App Inventorのデザイナー画面で、Web コンポーネントを選択し、PropertiesパネルでURLを設定します。URLは「https://api.openai.com/v1/engines/gpt-3.5-turbo/completions」となります。
    c. ブロックエディタで、Web コンポーネントの「set Web.Headers」ブロックを使用して、APIキーを含むヘッダー情報を設定します。ヘッダー情報は次のようになります。
  • “Content-Type": “application/json"
  • “Authorization": “Bearer あなたのAPIキー"
    d. 送信ボタンがクリックされたときのブロックで、入力されたテキストをChatGPT APIに送信するように設定します。具体的には、「set Web.Body」ブロックを使用して、送信するテキストを次のようなJSON形式に設定します。
    – {“prompt": “ここに入力されたテキスト", “max_tokens": 60}
    e. 'Web.GotText’イベントのブロックで、APIからの返信(responseContent)を取得し、ラベルに表示するように設定します。

これらの手順を踏むことで、入力されたテキストをChatGPTに送信し、返信を画面に表示するアンドロイドアプリを作成できます。

ただし、App Inventorは直接的な外部APIとの連携に制限があります。そのため、上記の方法は理想的なシナリオを示していますが、実際にはOpenAIのAPIと直接通信するためには、サーバー側のスクリプト(PHP, Node.js, Pythonなど)が必要となります。そのスクリプトはOpenAIのAPIを呼び出し、結果をApp Inventorに返します。

App Inventorが直接APIを呼び出すことが難しい場合は、Firebaseや他のクラウドサービスを使用して中間サーバーを作成し、そこからOpenAIのAPIを呼び出す方法もあります。このアプローチを取る場合は、その実装は少し複雑になりますが、一般的なプログラミング知識とFirebaseまたは選択したクラウドサービスの基本的な理解があれば実現可能です。

ChatGPTはApp Inventor日本語版のことは知らないようで「」の中が英語になっているので日本語化プロジェクトで言語を英語に切り替えて作ってみます。

3.a.がよくわからないので聞いてみます。

プロンプト

これわからない「Web コンポーネントでOpenAIのAPIに送信するブロックを作成します。」

返答

App Inventorでは、Webコンポーネントを使用して外部のWebサービスと通信することができます。しかし、直接的な外部APIとの連携には制限があり、WebコンポーネントはHTTP GETとPOSTリクエストをサポートしていますが、必要なヘッダーやボディの詳細な設定など、複雑なAPIリクエストの制御が難しいです。

OpenAIのAPIに接続するためには、通常はHTTP POSTリクエストを使用し、リクエストヘッダーにAPIキーを含め、リクエストボディにパラメータをJSON形式で含めます。しかし、App InventorのWebコンポーネントではこれが直接的には難しいため、ここでは簡略化した説明をしています。

理想的なシナリオでは、Webコンポーネントの「.Web1.PostText」ブロックを使用してAPIにテキストデータを送信し、「when Web1.GotText」ブロックを使用してAPIからの応答を受け取ります。

しかし、実際にはこの直接的な方法ではOpenAIのAPIと通信することが難しいため、通常は中間のサーバー(バックエンド)を使用してApp InventorとOpenAIのAPIとの間の通信を橋渡しします。このバックエンドはPHP、Node.js、Pythonなどのサーバーサイドのプログラミング言語で書かれ、OpenAIのAPIを呼び出し、その結果をApp Inventorに返します。

このようなバックエンドを使用する場合、App Inventorはそのバックエンドに対してリクエストを送り、バックエンドがそのリクエストを受け取ってOpenAIのAPIに転送し、APIからの応答をApp Inventorに返します。この方法では、App Inventorから直接OpenAIのAPIに接続する必要はなく、バックエンドがその役割を果たします。これは、App Inventorの制限を回避する一般的な方法です。

なお、この方法を使用するためには、サーバーサイドのプログラミングに関する基本的な知識が必要になります。また、バックエンドをホストするためのサーバーも必要となります。

どうもGPT-4はWebコンポーネントのことをよく知らないようですね。確認してみましょう。

プロンプト

Webコンポーネントにset RequestHeadersがあるんじゃないの?これ使えないの?

返答

申し訳ありません、誤解を与えてしまいました。あなたの指摘は正しいです。App InventorのWebコンポーネントは、確かにset RequestHeadersを使ってリクエストヘッダーを設定することが可能です。これにより、"Content-Type"と"Authorization"などのヘッダー情報を設定できます。ただし、"Authorization"にはあなたのOpenAI APIキーを含める必要があります。

以下に具体的なブロックの作成手順を示します。

  1. 「set Web1.Url」ブロックにOpenAIのAPIのURL(https://api.openai.com/v1/engines/davinci-codex/completions)を設定します。
  2. 「set Web1.RequestHeaders」ブロックを使ってヘッダーを設定します。このブロックは、リストからマップ(キーと値のペア)に変換する「make a dictionary」ブロックと組み合わせて使用します。"Content-Type"は “application/json"、"Authorization"は “Bearer {あなたのAPIキー}"とします。
  3. 「set Web1.PostText」ブロックを使ってリクエストボディを設定し、その後APIにPOSTリクエストを送信します。リクエストボディはJSON形式で、"prompt"にユーザーが入力したテキストを、"max_tokens"に返答の最大トークン数(例えば60)を設定します。
  4. 「when Web1.GotText」ブロックを使ってAPIからの応答を受け取ります。応答はJSON形式なので、「get value for key “choices" of dictionary」ブロックを使って"choices"キーの値を取り出し、さらにその中の最初の要素の"text"キーの値を取り出して表示します。

なお、OpenAIのAPIは2021年9月時点の情報であり、最新のAPIの仕様についてはOpenAIの公式ドキュメンテーションをご覧ください。

3の「set Web1.PostText」ブロックがありませんので聞いてみます。

プロンプト

「set Web1.PostText」ブロックなんて無いよ

返答

申し訳ありません、誤りがありました。App InventorのWebコンポーネントには「set Web1.PostText」ブロックは存在しません。正確には、「Web1.PostText」ブロックを使用してAPIへのPOSTリクエストを送信します。

また、POSTリクエストのボディにJSON形式のデータを含めるためには、「join」ブロックを使用してJSON形式の文字列を作成し、それを「Web1.PostText」ブロックの引数として渡します。

以下に、具体的な手順を修正して示します。

  1. 「set Web1.Url」ブロックにOpenAIのAPIのURL(https://api.openai.com/v1/engines/davinci-codex/completions)を設定します。
  2. 「set Web1.RequestHeaders」ブロックを使ってヘッダーを設定します。このブロックは、リストからマップ(キーと値のペア)に変換する「make a dictionary」ブロックと組み合わせて使用します。"Content-Type"は “application/json"、"Authorization"は “Bearer {あなたのAPIキー}"とします。
  3. 「join」ブロックを使ってリクエストボディをJSON形式の文字列として作成します。"prompt"にユーザーが入力したテキストを、"max_tokens"に返答の最大トークン数(例えば60)を設定します。そして、「Web1.PostText」ブロックを使ってこのJSON形式の文字列をPOSTリクエストのボディとしてAPIに送信します。
  4. 「when Web1.GotText」ブロックを使ってAPIからの応答を受け取ります。応答はJSON形式なので、「get value for key “choices" of dictionary」ブロックを使って"choices"キーの値を取り出し、さらにその中の最初の要素の"text"キーの値を取り出して表示します。

再度、この情報があなたのApp Inventorプロジェクトに役立つことを願っています。

前に行っていることと違うことを言い出したり、混乱してきたみたいなのでこの辺りでやめにします。どうもGPT-4はApp Inventorの知識はあまりないようですね。
仕方無いのでアプリは私が作りました。
デザイン編集画面
ブロック編集画面

チュートリアル

App Inventor 2の更新の手順を紹介します

MITからnb187aがリリースされたので日本語版でnb186aをnb187aに更新する手順を紹介します。自分でApp Inventor 2のサーバーを立ち上げたい人は参考にしてください。作業開始後nb187b、nb187cとリリースが続いたので、途中でバージョンが変わっています。今回のMITによる更新はかなり大幅なもので、更新の仕方が分かっていたわけではないので何度も行ったりきたりしながら手探りで作業を進めていて、15日間もかかった今までになく大変な更新作業だったので日付も入れて備忘録も兼ねています。「実録App Inventor 2日本語版更新」なのでこのまま手順をフォローするのはやめてくださいね。

利用者の方はApp Inventor 2日本語版が使えて当然と思っていると思いますが、更新作業はとても手間のかかる作業で、このような地道な作業をする人がいるためにApp Inventor 2日本語版が使えていることをご理解いただき、少しでもいいのでプロジェクト支援を検討してください。プロジェクトをご支援いただける方は下のボタンをクリックしてお願いします。

 

目次

    1. 2021年8月23日
      1. gitでnb187aをマージしてからCONFLICTを解決
      2. DockerコンテナでApp Inventor 2をビルド
      3. ビルド失敗原因の究明
    2. 2021年8月24日
    3. 2021年8月25日
    4. 2021年8月26日
      1. パソコンubuntu 18.04でデプロイを試す
    5. 2021年8月28日
      1. gitでnb187bをマージしてからCONFLICTを解決
    6. 2021年8月29日
      1. デプロイ前に動作確認
    7. 2021年8月30,31日
      1. 多言語変更部分調査と日本語追加
    8. 2021年9月1,2日
      1. ステージング環境にGUIデプロイ
      2. bootstrap.shにgcloudを追加
      3. ステージング環境でフルテスト
      4. 一部メニューを日本語化
    9. 2021年9月3日
      1. GitHubにプッシュ
    10. 2021年9月10日
      1. 本番環境にデプロイ
    11. 2021年9月12,13日
      1. 日本語未表示バグ

    2021年8月23日

    gitでnb187aをマージしてからCONFLICTを解決

    gitが使えるコンピュータに作業用フォルダーを作ってそこで以下のコマンドを実行します。日本語化プロジェクトではmacOS 10.15.7のiMacで作業しています。

    $ git clone https://github.com/tmsoftwareinc/appinventor-sources.git
    $ cd appinventor-sources/
    $ git remote add upstream https://github.com/mit-cml/appinventor-sources.git
    $ git checkout appinventorjapan
    $ git fetch upstream
    

    次にnb187aのコミットIDを見つけます。我々はSourceTreeを使いました。nb187aのコミットIDは 93a23aa575f74880d392f4032c3070bbbe365546 です。それではnb187aをマージしましょう。

    $ git merge 93a23aa575f74880d392f4032c3070bbbe365546
    Auto-merging appinventor/docs/html/reference/components/userinterface.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/userinterface.html
    Auto-merging appinventor/docs/html/reference/components/storage.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/storage.html
    Auto-merging appinventor/docs/html/reference/components/maps.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/maps.html
    Auto-merging appinventor/docs/html/reference/components/legomindstorms.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/legomindstorms.html
    Auto-merging appinventor/docs/html/reference/components/layout.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/layout.html
    Auto-merging appinventor/docs/html/reference/components/connectivity.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/connectivity.html
    Auto-merging appinventor/docs/html/reference/components/animation.html
    CONFLICT (content): Merge conflict in appinventor/docs/html/reference/components/animation.html
    Removing appinventor/blocklyeditor/src/msg/zh_tw/_messages.js
    Removing appinventor/blocklyeditor/src/msg/zh_cn/_messages.js
    Removing appinventor/blocklyeditor/src/msg/sv/_messages.js
    Removing appinventor/blocklyeditor/src/msg/ru/_messages.js
    Removing appinventor/blocklyeditor/src/msg/pt_br/_messages.js
    Removing appinventor/blocklyeditor/src/msg/pt/_messages.js
    Removing appinventor/blocklyeditor/src/msg/pl/_messages.js
    Removing appinventor/blocklyeditor/src/msg/nl/_messages.js
    Removing appinventor/blocklyeditor/src/msg/lt/_messages.js
    Removing appinventor/blocklyeditor/src/msg/ko_kr/_messages.js
    Removing appinventor/blocklyeditor/src/msg/it_it/_messages.js
    Removing appinventor/blocklyeditor/src/msg/hu/_messages.js
    Removing appinventor/blocklyeditor/src/msg/fr_fr/_messages.js
    Removing appinventor/blocklyeditor/src/msg/es_es/_messages.js
    Removing appinventor/blocklyeditor/src/msg/en/_messages.js
    Removing appinventor/blocklyeditor/src/msg/de/_messages.js
    CONFLICT (modify/delete): appinventor/blocklyeditor/src/language_switch.js deleted in 93a23aa575f74880d392f4032c3070bbbe365546 and modified in HEAD. Version HEAD of appinventor/blocklyeditor/src/language_switch.js left in tree.
    Removing appinventor/blocklyeditor/plovrConfigAI3.js
    Auto-merging appinventor/blocklyeditor/ploverConfig.js
    CONFLICT (content): Merge conflict in appinventor/blocklyeditor/ploverConfig.js
    Auto-merging appinventor/appengine/war/index.html
    CONFLICT (content): Merge conflict in appinventor/appengine/war/index.html
    Auto-merging appinventor/appengine/war/WEB-INF/appengine-web.xml
    Auto-merging appinventor/appengine/src/com/google/appinventor/client/languages.json
    Auto-merging appinventor/appengine/src/com/google/appinventor/client/Ode.java
    Auto-merging appinventor/appengine/src/com/google/appinventor/YaClient.gwt.xml
    CONFLICT (content): Merge conflict in appinventor/appengine/src/com/google/appinventor/YaClient.gwt.xml
    Auto-merging appinventor/appengine/build.xml
    CONFLICT (content): Merge conflict in appinventor/appengine/build.xml
    Automatic merge failed; fix conflicts and then commit the result.
    

    たくさんCONFLICTが出てきました。これをひとつずつ解決していきます。すべて解決できたらコミットします。

    DockerコンテナでApp Inventor 2をビルド

    App Inventor 2をビルドする環境を入れたdockerコンテナを使ってビルドします。日本語化プロジェクトでは以下のDockerfileで作ったコンテナを使っています。かなり以前に作ったのでソフトのバージョンは古いものになっています。

    
    FROM ubuntu:18.04
    
    # Build with
    #    docker build -t ai2 .
    RUN apt-get update
    RUN apt-get install -y curl unzip
    # Install prerequisites
    # python3 is need for i18n.py and pre-installed
    RUN apt-get install -y python
    RUN apt-get install -y openjdk-8-jdk
    RUN apt-get install -y lib32z1
    RUN apt-get install -y lib32ncurses5
    RUN apt-get install -y lib32stdc++6
    # Install other useful tools
    RUN apt-get install -y git vim ant sudo android-tools-adb
    # Install phantomJS
    RUN apt-get install -y phantomjs
    # Clean up
    RUN apt-get clean
    RUN apt-get purge
    # Add user
    RUN useradd -ms /bin/bash ubuntu
    # Install Google App Engine SDK
    RUN curl 'https://storage.googleapis.com/appengine-sdks/featured/appengine-java-sdk-1.9.72.zip' > /tmp/appengine.zip && unzip -d /home/ubuntu /tmp/appengine.zip && rm /tmp/appengine.zip
    # Install Google cloud SDK
    RUN curl 'https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-239.0.0-linux-x86_64.tar.gz' > /tmp/gcloud.tgz && cd /home/ubuntu && tar zxvf /tmp/gcloud.tgz 
    EXPOSE 8888 5037
    USER ubuntu
    ENV HOME /home/ubuntu
    ENV PATH $PATH:$HOME/google-cloud-sdk/bin
    
    

    dockerコンテナを起動します。

    docker restart コンテナ名
    docker attach コンテナ名
    

    コミットしたApp Inventor 2をコンテナにコピーします。macOSの~/Publicフォルダーをコンテナの/home/exportにマウントしているので簡単です。/home/ubuntu/appinventor-sourcesを作ります。

    それでは以下の手順でビルドしましょう。

    cd appinventor-sources/
    git submodule update --init
    cd appinventor/
    ant clean
    ant MakeAuthKey
    ant
    

    出力が延々と続きますが、結局以下のメッセージが出てビルドに失敗しました。

    BUILD FAILED
    /home/ubuntu/appinventor-sources/appinventor/build.xml:16: The following error occurred while executing this line:
    /home/ubuntu/appinventor-sources/appinventor/appengine/build.xml:581: Java returned: 137
    
    Total time: 4 minutes 7 seconds
    

    ここからエラーの原因を突き止める作業に入ります。まず確認のためにから英語版のnb187aをコンテナでビルドしてみます。

    rm appinventor-sources/
    git clone https://github.com/mit-cml/appinventor-sources.git
    cd appinventor-sources/
    git submodule update --init
    cd appinventor/
    ant clean
    ant MakeAuthKey
    ant

    マージした日本語版と同じエラーでビルドに失敗しました。

    YaClientApp:
        [mkdir] Created dir: /home/ubuntu/appinventor-sources/appinventor/appengine/build/extra
         [java] Compiling module com.google.appinventor.YaClient-dev
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to DEBUG or WARN to see all errors.
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource'
         [java]       Rebinding com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/editor/youngandroid/blockly.js
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport'
         [java]       Rebinding com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/utils/html5dnd.js
         [java]    Compiling 1 permutation
         [java]       Compiling permutation 0...
    
    BUILD FAILED
    /home/ubuntu/appinventor-sources/appinventor/build.xml:16: The following error occurred while executing this line:
    /home/ubuntu/appinventor-sources/appinventor/appengine/build.xml:581: Java returned: 137
    
    Total time: 3 minutes 16 seconds
    
    

    ビルド失敗原因の究明

    ここまでで分かったことは、

    1) nb186aまでは英語版、日本語版共にビルドに成功
    2) nb187aだと英語版、日本語版共にビルドに失敗
    3) MITではnb187aがビルドできている
    4) エラーメッセージからは原因はわからない

    これらの事実から仮説を立て検証します。

    1) nb187a日本語版に問題がある
       英語版でのビルドにも失敗しているので無し
    2) dockerコンテナの環境に問題がある
       可能性あり

    dockerコンテナの環境に問題があると言っても確実にビルドできる環境がわからないとどうしようもないので、今まで真面目に読む必要が無かったGitHub(https://github.com/mit-cml/appinventor-sources)のREADMEを読み直してみます。すると簡単にビルドするにはvagrantを使うといいよ、との記述が。nb187a英語版で早速やってみます。作業に使っているのはmacOS 10.15.7, 16GBメモリーの27インチiMac(2017)です。

    https://www.vagrantup.com/からvagrant_2.2.18_x86_64.dmgをダウンロードしてインストール
    https://www.virtualbox.org/wiki/DownloadsからVirtualBox-6.1.26-145957-OSX.dmgをダウンロードしてインストール
    vagrant plugin install vagrant-vbguest
    適当なフォルダー、例えば~/Documents/AI2_Repositories/tmp/を作って 
    mac> git clone https://github.com/mit-cml/appinventor-sources.git
    mac> cd appinventor-sources
    mac> vagrant up
    mac> vagrant ssh
    これでVirutalBoxのubuntu 18.04にログインできるのでビルドします
    ubuntu> ant

    以下のメッセージが出てビルドに失敗しました。

    BlocklyCompile:
         [java] Exception in thread "main" java.lang.IllegalArgumentException: File not found at: /vagrant/appinventor/blocklyeditor/../lib/closure-library/closure/goog
         [java] 	at com.google.common.base.Preconditions.checkArgument(Preconditions.java:88)
         [java] 	at org.plovr.Manifest.getInputs(Manifest.java:393)
         [java] 	at org.plovr.Manifest.getFiles(Manifest.java:369)
         [java] 	at org.plovr.Manifest.getFiles(Manifest.java:363)
         [java] 	at org.plovr.Manifest.getAllDependencies(Manifest.java:324)
         [java] 	at org.plovr.Manifest.getInputsInCompilationOrder(Manifest.java:193)
         [java] 	at org.plovr.Manifest.getCompilerArguments(Manifest.java:177)
         [java] 	at org.plovr.CompileRequestHandler.compile(CompileRequestHandler.java:94)
         [java] 	at org.plovr.cli.BuildCommand.runCommandWithOptions(BuildCommand.java:58)
         [java] 	at org.plovr.cli.BuildCommand.runCommandWithOptions(BuildCommand.java:31)
         [java] 	at org.plovr.cli.AbstractCommandRunner.runCommand(AbstractCommandRunner.java:41)
         [java] 	at org.plovr.cli.Command.execute(Command.java:47)
         [java] 	at org.plovr.cli.Main.mainWithExitCode(Main.java:56)
         [java] 	at org.plovr.cli.Main.main(Main.java:30)
    
    BUILD FAILED
    /vagrant/appinventor/build.xml:16: The following error occurred while executing this line:
    /vagrant/appinventor/build-common.xml:299: The following error occurred while executing this line:
    /vagrant/appinventor/blocklyeditor/build.xml:118: Java returned: 1

    2021年8月24日

    エラーメッセージを読んでみるとclosure-libraryが無いせいで失敗しているようです。これはREADMEの後の方に書いてありますが、git submodule update –init が必要みたいなので、やってみます。ちなみにdockerコンテナでビルドするときは git submodule update –init は手順に入っていました。

    ubuntu> git submodule update --init
    ubuntu> ant
    BUILD SUCCESSFUL
    Total time: 5 minutes 32 seconds

    やっとビルドに成功しました。これでdockerコンテナでビルドできない原因の調査ができます。

    GitHubからcloneしたApp Inventorには次のVagrantfileがあり、vagrant upした時にこのファイルからコンテナを作ります。ベースになっているのはubuntu/bionic64=ubuntu 18.04でdockerコンテナで使っているものと同じです。

    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    
    # All Vagrant configuration is done below. The "2" in Vagrant.configure
    # configures the configuration version (we support older styles for
    # backwards compatibility). Please don't change it unless you know what
    # you're doing.
    
    Vagrant.configure("2") do |config|
    
      config.vm.box = "ubuntu/bionic64"
    
      config.vm.boot_timeout = 400
    
      config.vm.provider "virtualbox" do |v|
        v.name = "ForAppinventor2-bionic64"
        v.memory = "4096"
        v.customize ["modifyvm", :id, "--usb", "on"]
        # fix for slow network
        v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
        v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
        v.customize ["modifyvm", :id, "--nictype1", "virtio"]
      end
    
      config.vm.provision :shell, path: "bootstrap.sh"
    
      config.vm.network :forwarded_port, guest: 8888, host: 8888
      config.vm.network :forwarded_port, guest: 9876, host: 9876
      config.vm.network :forwarded_port, guest: 9990, host: 9990
    
    end

    そして次のbootstrap.shを参照して様々なソフトをインストールします。

    #!/usr/bin/env bash
    
    dpkg --add-architecture i386
    
    # Install dependencies
    apt-get update
    apt-get upgrade -y
    apt-get install -y libc6:i386 libstdc++6:i386 glibc-doc:i386 gcc-5-base:i386 gcc-6-base:i386 libgcc1:i386 \
         openjdk-8-jdk zip unzip ant lib32z1 adb phantomjs
    
    # Install App Engine
    mkdir -p /opt/appengine
    cd /opt/appengine
    wget --no-verbose -O /tmp/appengine.zip https://storage.googleapis.com/appengine-sdks/featured/appengine-java-sdk-1.9.68.zip
    unzip -o /tmp/appengine.zip
    
    # Configure shell
    echo "export PATH=$PATH:/opt/appengine/appengine-java-sdk-1.9.68/bin" >> /home/vagrant/.bashrc
    echo "cd /vagrant/appinventor" >> /home/vagrant/.bashrc
    
    # Configure java
    update-java-alternatives -s java-1.8.0-openjdk-amd64
    
    # Make the auth key in advance
    cd /vagrant/appinventor
    sudo -u vagrant ant MakeAuthKey
    
    # Helper script for starting App Inventor dev server
    cat < /usr/local/bin/start_appinventor
    ant RunLocalBuildServer &> buildserver.log &
    BUILDSERVER=$!
    dev_appserver.sh -p 8888 -a 0.0.0.0 appengine/build/war
    kill -9 -- -$BUILDSERVER
    EOF
    chmod +x /usr/local/bin/start_appinventor

    ここでインストールしているのと同じソフトをインストールしたdockerコンテナを作ってみましょう。

    2021年8月25日

    ubuntu 18.04から新しくdockerコンテナを作り、bootstrap.shにあるコマンドを順に実行していきます。全部インストールできたら英語版nb187aを次の手順でビルドしてみます。

    git clone https://github.com/mit-cml/appinventor-sources.git
    cd appinventor-sources/
    git submodule update --init
    cd appinventor/
    ant clean
    ant MakeAuthKey
    ant

    結果は残念ながら失敗でした。

    YaClientApp:
        [mkdir] Created dir: /home/ubuntu/appinventor-sources/appinventor/appengine/build/extra
         [java] Aug 25, 2021 9:06:30 PM java.util.prefs.FileSystemPreferences$1 run
         [java] INFO: Created user preferences directory.
         [java] Compiling module com.google.appinventor.YaClient-dev
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to DEBUG or WARN to see all errors.
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource'
         [java]       Rebinding com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/editor/youngandroid/blockly.js
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport'
         [java]       Rebinding com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/utils/html5dnd.js
         [java]    Compiling 1 permutation
         [java]       Compiling permutation 0...
         [java]    Compile of permutations succeeded
         [java]    Compilation succeeded -- 225.904s
    
    BUILD FAILED
    /home/ubuntu/appinventor-sources/appinventor/build.xml:16: The following error occurred while executing this line:
    

    この後たまたまあったubuntu 18.04をインストールしたパソコンでビルドを試してみたところ、ビルドに成功しました。だとするとdockerの問題としか考えられなくなりました。そうこうしているうちにnb187bがリリースされます。これでdockerコンテナでもビルドできるようになったことを期待します。

    2021年8月26日

    ダメでした。英語版nb187bでもdockerコンテナでは全く同じエラーでビルドに失敗しました。

    ここまでくると精神的にも時間的にもきつくなってきたので、今まで長年使ってきたdockerコンテナを諦めてパソコンubuntu 18.04でデプロイまでやろうということになりました。この判断がビルド失敗の原因を発見することに間接的につながります。dockerコンテナを追求していたらデプロイできなかったと思います。ユーザーの皆さんごめんなさい、App Inventor 2日本語版は今後最新版に更新できなくなりました、理由はMITがわけのわからない更新をしたためです、ってメールで許してもらえるかな?とまで考えました。

    パソコンubuntu 18.04でデプロイを試す

    App Inventor 2はGoogleのクラウドサービスのApp Engineを使っていて、GUI部分はこのApp Engineにデプロイすることになります。デプロイするにはビルドできたら以下コマンドを使います。gcloudはGoogle Cloudコマンドラインツールです。https://cloud.google.com/sdk/docs/quickstart-linuxを参考にインストールします。

    gcloud app deploy appinventor-sources/appinventor/appengine/build/war/WEB-INF/appengine-web.xml

    はい、失敗しました。ここまでくると失敗しても、あっまた、って感じです。

    Beginning deployment of service [default]...
    ERROR: (gcloud.app.deploy) Cannot upload file
    [/tmp/tmp27ys7jo_/tmp6n000ge4/ode/4F75BDCDDD4332BEA20CD44DCAF54733.cache.js],
    which has size [46468038] (greater than maximum allowed size of
    [33554432]). Please delete the file or add to the skip_files entry in
    your application .yaml file and try again.
    

    このエラーは見覚えがあります。nb185aのデプロイの時に同じエラーが起きてかわしています。https://community.appinventor.mit.edu/t/deploy-app-inventor-failed-on-google-app-engine/2574/4 にかわし方があるので、これにしたがってソースを変更します。と思ってソースをみると既に変更されていました。もちろん条件付きで。いろいろソースを探っていくと、appengine/build.xmlとblocklyeditor/build.xmlに<property name="release" value="false"/>とあります。これですね。falseをtrueに変えてビルドです。これでデプロイできる、と思ったらさすがMIT、そう簡単には終わらせてくれません。

    YaClientApp:
        [mkdir] Created dir: /home/tam/Projects/AppInventor/AI2_Repositories/AI2_git_20210825/appinventor-sources/appinventor/appengine/build/extra
         [java] Compiling module com.google.appinventor.YaClient
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to DEBUG or WARN to see all errors.
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource'
         [java]       Rebinding com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/editor/youngandroid/blockly.js
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport'
         [java]       Rebinding com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/utils/html5dnd.js
         [java]    Compiling 34 permutations
         [java]       Compiling permutation 0...
    
    BUILD FAILED
    /home/tam/Projects/AppInventor/AI2_Repositories/AI2_git_20210825/appinventor-sources/appinventor/build.xml:16: The following error occurred while executing this line:
    /home/tam/Projects/AppInventor/AI2_Repositories/AI2_git_20210825/appinventor-sources/appinventor/appengine/build.xml:581: Java returned: 137

    vagrantならさすがにビルドできるはず、と思ってリリースモードでビルドしたところ、失敗。ここまでくるとMITに嫌がらせをされているんではないかという感じです。実際MIT以外でApp Inventor 2を動かせないようにしたんじゃないかと思いました。

    YaClientApp:
        [mkdir] Created dir: /vagrant/appinventor/appengine/build/extra
         [java] Aug 26, 2021 7:27:55 PM java.util.prefs.FileSystemPreferences$1 run
         [java] INFO: Created user preferences directory.
         [java] Compiling module com.google.appinventor.YaClient
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to DEBUG or WARN to see all errors.
         [java]    Ignored 3 units with compilation errors in first pass.
         [java] Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource'
         [java]       Rebinding com.google.appinventor.client.editor.youngandroid.BlocklyPanel.BlocklySource
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/editor/youngandroid/blockly.js
         [java]    Computing all possible rebind results for 'com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport'
         [java]       Rebinding com.google.appinventor.client.utils.HTML5DragDrop.HTML5DragDropSupport
         [java]          Invoking generator com.google.gwt.query.rebind.JsniBundleGenerator
         [java]             JsniBundleGenerator - importing external javascript: com/google/appinventor/client/utils/html5dnd.js
         [java]    Compiling 34 permutations
         [java]       Compiling permutation 0...
         [java]       Process output
         [java]          #
         [java]          # There is insufficient memory for the Java Runtime Environment to continue.
         [java]          # Native memory allocation (mmap) failed to map 60817408 bytes for committing reserved memory.
         [java]          # An error report file with more information is saved as:
         [java]          # /vagrant/appinventor/appengine/hs_err_pid24224.log
         [java]          [ERROR] OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000db600000, 60817408, 0) failed; error='Cannot allocate memory' (errno=12)
         [java]       Process output
         [java]          [ERROR] OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x000000008ee00000, 524288, 0) failed; error='Cannot allocate memory' (errno=12)
         [java]          #
         [java]          # There is insufficient memory for the Java Runtime Environment to continue.
         [java]          # Native memory allocation (mmap) failed to map 524288 bytes
         [java]       Process output
         [java]          Compiling
         [java]             Compiling permutation 5...
         [java]       Process output
         [java]          Compiling
         [java]             Compiling permutation 1...
         [java]       Process output
         [java]          #
         [java]          # There is insufficient memory for the Java Runtime Environment to continue.
         [java]          # Native memory allocation (mmap) failed to map 116391936 bytes for committing reserved memory.
         [java]          [ERROR] OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000094300000, 116391936, 0) failed; error='Cannot allocate memory' (errno=12)
         [java]          # An error report file with more information is saved as:
         [java]          # /vagrant/appinventor/appengine/hs_err_pid24233.log
         [java]       Compiling permutation 8...
         [java]          #
         [java]          [ERROR] OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000f3980000, 30932992, 0) failed; error='Cannot allocate memory' (errno=12)
         [java]          # There is insufficient memory for the Java Runtime Environment to continue.
         [java]          # Native memory allocation (mmap) failed to map 30932992 bytes for committing reserved memory.
         [java]          # An error report file with more information is saved as:
         [java]          # /vagrant/appinventor/appengine/hs_err_pid24238.log
         [java]       [WARN] Lost communication with remote process
         [java] java.io.EOFException
         [java] 	at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:3078)
         [java] 	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1618)
         [java] 	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503)
         [java] 	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461)
         [java] 	at com.google.gwt.dev.ExternalPermutationWorkerFactory$ExternalPermutationWorker.compile(ExternalPermutationWorkerFactory.java:154)
         [java] 	at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
         [java] 	at java.lang.Thread.run(Thread.java:748)
         [java]          Compiling
         [java]             Compiling permutation 9...
         [java]       Compiling permutation 10...
         [java]          Compiling
         [java]             Compiling permutation 11...
         [java]       Compiling permutation 12...
         [java]          Compiling
         [java]             Compiling permutation 13...
         [java]       Compiling permutation 14...
         [java]          Compiling
         [java]             Compiling permutation 15...
         [java]       Compiling permutation 16...
         [java]          Compiling
         [java]             Compiling permutation 17...
         [java]       Compiling permutation 18...
         [java]          Compiling
         [java]             Compiling permutation 19...
         [java]       Compiling permutation 20...
         [java]          Compiling
         [java]             Compiling permutation 21...
         [java]       Compiling permutation 22...
         [java]          Compiling
         [java]             Compiling permutation 23...
         [java]       Compiling permutation 24...
         [java]          Compiling
         [java]             Compiling permutation 25...
         [java]       Compiling permutation 26...
         [java]          Compiling
         [java]             Compiling permutation 27...
         [java]       Compiling permutation 28...
         [java]          Compiling
         [java]             Compiling permutation 29...
         [java]       Compiling permutation 30...
         [java]          Compiling
         [java]             Compiling permutation 31...
         [java]       Compiling permutation 32...
         [java]          Compiling
         [java]             Compiling permutation 33...
         [java]       Compiling permutation 4...
         [java]          Compiling
         [java]             Compiling permutation 3...
         [java]       Compiling permutation 2...
         [java]          [ERROR] Compile failed
         [java]          java.lang.OutOfMemoryError
         [java]          	at java.io.RandomAccessFile.readBytes(Native Method)
         [java]          	at java.io.RandomAccessFile.read(RandomAccessFile.java:377)
         [java]          	at java.io.RandomAccessFile.readFully(RandomAccessFile.java:436)
         [java]          	at java.io.RandomAccessFile.readFully(RandomAccessFile.java:416)
         [java]          	at com.google.gwt.dev.util.DiskCache.readByteArray(DiskCache.java:77)
         [java]          	at com.google.gwt.dev.util.DiskCache.readObject(DiskCache.java:94)
         [java]          	at com.google.gwt.dev.jjs.UnifiedAst.getFreshAst(UnifiedAst.java:128)
         [java]          	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:322)
         [java]          	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:272)
         [java]          	at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
         [java]          	at com.google.gwt.dev.CompilePermsServer.compilePermutation(CompilePermsServer.java:313)
         [java]          	at com.google.gwt.dev.CompilePermsServer.run(CompilePermsServer.java:276)
         [java]          	at com.google.gwt.dev.CompilePermsServer.main(CompilePermsServer.java:239)
         [java]             [ERROR] Out of memory; to increase the amount of memory, use the -Xmx flag at startup (java -Xmx128M ...)
         [java]       [ERROR] Error from external worker
         [java] java.lang.OutOfMemoryError
         [java] 	at java.io.RandomAccessFile.readBytes(Native Method)
         [java] 	at java.io.RandomAccessFile.read(RandomAccessFile.java:377)
         [java] 	at java.io.RandomAccessFile.readFully(RandomAccessFile.java:436)
         [java] 	at java.io.RandomAccessFile.readFully(RandomAccessFile.java:416)
         [java] 	at com.google.gwt.dev.util.DiskCache.readByteArray(DiskCache.java:77)
         [java] 	at com.google.gwt.dev.util.DiskCache.readObject(DiskCache.java:94)
         [java] 	at com.google.gwt.dev.jjs.UnifiedAst.getFreshAst(UnifiedAst.java:128)
         [java] 	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:322)
         [java] 	at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:272)
         [java] 	at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
         [java] 	at com.google.gwt.dev.CompilePermsServer.compilePermutation(CompilePermsServer.java:313)
         [java] 	at com.google.gwt.dev.CompilePermsServer.run(CompilePermsServer.java:276)
         [java] 	at com.google.gwt.dev.CompilePermsServer.main(CompilePermsServer.java:239)
         [java]          [ERROR] Out of memory; to increase the amount of memory, use the -Xmx flag at startup (java -Xmx128M ...)
         [java]       [ERROR] Unrecoverable exception, shutting down
         [java] com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
         [java] 	at com.google.gwt.dev.ExternalPermutationWorkerFactory$ExternalPermutationWorker.compile(ExternalPermutationWorkerFactory.java:157)
         [java] 	at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
         [java] 	at java.lang.Thread.run(Thread.java:748)
         [java]       [ERROR] Not all permutation were compiled , completed (30/34)
    
    BUILD FAILED
    /vagrant/appinventor/build.xml:16: The following error occurred while executing this line:
    /vagrant/appinventor/appengine/build.xml:581: Java returned: 1

    でもちょっと待ってくださいよ。vagrantだとエラーメッセージが長くなっている。注目したのはここ

    [java]          # There is insufficient memory for the Java Runtime Environment to continue.
    [java]          # Native memory allocation (mmap) failed to map 60817408 bytes for committing reserved memory.
    

    メモリー不足?App inventorについてきたVagrantfileでは4GBメモリーを取っています。今までビルドできていたdockerコンテナは2GBメモリーを取っています。とりあえずVagrantfile 17行目を変更してメモリーを6GBにしてリリースビルド。

    v.memory = "4096"  ->  v.memory = "6144"

    コンテナから強制ログアウトされてしまいました。iMacで不要なソフトを止めてリリースビルドやり直すも、またメモリー不足エラー。Vagrantfile 17行目を変更してメモリーを8GBにしてリリースビルド。やっと成功。パソコンubuntu 18.04は物理メモリー3GBしか積んでいないのでビルド環境に使えないことが確定。

    vagrantにGoogle CloudコマンドラインツールをインストールしてApp Engineにデプロイ。ここではステージング環境のApp Engineにデプロイしているので本番環境に影響はありません。ようやくデプロイ成功!長い道のりでした。

    2021年8月28日

    gitでnb187bをマージしてからCONFLICTを解決

    英語版nb187bのデプロイに成功したので日本語版nb187bのデプロイを試します。前回と同じ日本語化する手順にしたがって日本語版nb187bを作ります。

    ここでもしかしたらdockerコンテナもメモリー増やせばビルドできるんじゃないかと思いつき、実行。とりあえず2GBから4GBに増やして開発ビルドしたところ、なんと成功!リリースビルドはやはり失敗。

    これ以上の作業は物理メモリー16GBのiMacでは無理なのでamazonで一昨日購入した32GB RAM($187)が届いてから続きの作業。使っている27インチiMac(2017)は最大64GBまでメモリー増設可能なのです。このiMacはamazonで$1500でApp Inventor 2の作業用に購入した改修品です。安くあげるために内臓HDはFusion Driveにしたので外付け1TB SSD($176)から起動してます。27インチiMacでメモリー16GB+1TB SSDにすると$2500くらいまで軽くいきますからね。

    iMacのメモリーを48GBに増設してdockerコンテナでリリースビルドできるメモリー量を探したところ、10GBでもダメなので思い切って16GBにしたところ無事成功!今まで2GBでビルドできていたことを考えるとMITはなんということをしてくれたんだという感じです。MITはお金があるだろうから16GBくらいどうってことないかもしれないけど。

    2021年8月29日

    デプロイ前に動作確認

    デプロイする前にdockerコンテナでローカル環境で動作確認することにします。次のコマンドでサーバーを起動するとローカルマシンの8888ポートにアクセスすることでApp Inventor 2が起動できます。

    appengine-java-sdk-1.9.72/bin/dev_appserver.sh --port=8888 --address=0.0.0.0  appinventor-sources/appinventor/appengine/build/war/  

    ブラウザを立ち上げて次のURLにアクセスします。が、メニューが日本語で表示されません。

    http://127.0.0.1:8888/??locale=ja_JP 

    言語選択ドロップダウンメニューにも日本語がありません。多言語対応部分が変更されているようです。どこが変更されたか調べているうちにあっという間に時間が過ぎ去りました。

    2021年8月30,31日

    多言語変更部分調査と日本語追加

    言語選択ドロップダウンメニューを定義しているのは appinventor/appengine/src/com/google/appinventor/YaClient.gwt.xml の終わりの方ですが、ここから日本語がなくなっていました。gitでマージした時のCONFLICT解決の時に失敗して消したようです。英語の次に追加し直しました。

    <!-- English language, independent of country -->
    <extend-property name="locale" values="en"/>
    <!-- Japanese -->
    <extend-property name="locale" values="ja_JP"/>

    それでもまだメニューが日本語になりません。appinventor/blocklyeditor/src/msg/ja_jp/_messages.js でかなりの量の日本語の翻訳をしていたんですが、gitでマージした時に他の言語の*/_messages.jsが無くなって代わりにmessage_*.json(例えばappinventor/blocklyeditor/src/msg/zh_tw/_messages.jsが無くなってappinventor/blocklyeditor/src/msg/ai_blockly/messages_zh_TW.json)が追加されています。ということでappinventor/blocklyeditor/src/msg/ja_jp/_messages.jsからappinventor/blocklyeditor/src/msg/ai_blockly/messages_ja.jsonを作ったところ、メニューが日本語になりました。JavaScriptファイルをJSONファイルに変換するのにはpythonを使いました。こんな感じです。

    ja_jp/_messages.js
    Blockly.Msg.DUPLICATE_BLOCK = '複製';
    Blockly.Msg.REMOVE_COMMENT = 'コメント削除';
    Blockly.Msg.ADD_COMMENT = 'コメント追加';
    
    messages_ja.json
    "Blockly.Msg.DUPLICATE_BLOCK": "複製",
    "Blockly.Msg.REMOVE_COMMENT": "コメント削除",
    "Blockly.Msg.ADD_COMMENT": "コメント追加",

    ついでながらappengine/build.xmlとblocklyeditor/build.xmlに<property name="release" value="false"/>を変更しなくてもリリースビルドはこれでできることがわかりました。

    ant -Drelease=true noplay 

    2021年9月1,2日

    dockerコンテナでローカル環境で動作確認です。すべての日本語が表示されているか見ていくと、日本語だけではなく韓国語や中国語が???になっている所があります。よくわからないので、vagrantでリリースビルドしてみます。ところが英語版nb187bはメモリー8GBでリリースビルドできたのに今回は失敗。

    [ERROR] OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000f4780000, 34603008, 0) failed; error='Cannot allocate memory' (errno=12)
         [java]          #
         [java]          # There is insufficient memory for the Java Runtime Environment to continue.
         [java]          # Native memory allocation (mmap) failed to map 34603008 bytes for committing reserved memory.
         [java]          # An error report file with more information is saved as:
         [java]          # /vagrant/appinventor/appengine/hs_err_pid28463.log
         [java]       [WARN] Lost communication with remote process
         [java] java.io.EOFException
         [java] 	at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:3078)
         [java] 	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1618)
         [java] 	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503)
         [java] 	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461)
         [java] 	at com.google.gwt.dev.ExternalPermutationWorkerFactory$ExternalPermutationWorker.compile(ExternalPermutationWorkerFactory.java:154)
         [java] 	at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
         [java] 	at java.lang.Thread.run(Thread.java:748)
         [java]       [WARN] Lost communication with remote process

    エラーメッセージから明らかにメモリー不足なので、vagrantのメモリーを増やしていって16GBでリリースビルド成功。これでローカル環境で動作確認したところ、正常に日本語が表示できました。

    ステージング環境にGUIデプロイ

    ステージング環境にGUIをデプロイしてみます。使うコマンドはこれです。無事成功しました。ステージング環境ですべてうまく日本語表示されました。

    bootstrap.shにgcloudを追加

    新規にvagrant upをした時にGoogle Cloudコマンドラインツールもインストールされるようにbootstrap.shに次の部分を追加しました。

    # Install gcloud
    echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
    apt-get install -y apt-transport-https ca-certificates gnupg
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
    apt-get update && sudo apt-get install -y google-cloud-sdk
    apt-get install -y google-cloud-sdk-app-engine-java

    ステージング環境でフルテスト

    GUI部分は無事に動いたので、ビルドサーバーもステージング環境にデプロイしてすべての機能をテストします。まずはビルドサーバーのIPアドレス(XXX.XXX.XXX.XXX)をappinventor/appengine/war/WEB-INF/appengine-web.xml の87行の以下の部分に入力します。ビルドサーバー用のGoogle Cloud Compute EngineはUbuntu 18.04.3 LTSで動いています。

    <pproperty name="build.server.host" value="XXX.XXX.XXX.XXX:9990"></property>

    バージョン表示が綺麗になるように、gitコミットしてタグを付け直します。これをしないとバージョンがv187b_jp-1-g4c3702dcのようになります。git describe –dirty で表示されるのがGUIで表示されるバージョンです。できたらビルドします。

    git add /vagrant/appinventor/appengine/war/WEB-INF/appengine-web.xml; git commit -m "Added build server IP"
    git tag -d v187b_jp
    git tag -a v187b_jp
    git describe --dirty
    		v187b_jp
    
    ant clean
    ant -Drelease=true noplay

    ステージング環境にこのコマンドでGUIをデプロイしたら、ビルドサーバーのtarファイルを作ります。

    On vagrant
      cd appinventor/buildserver
      ant BuildDeploymentTar

    appinventor/build/buildserver/BuildServer.tar ができるので、これから次の手順で forbuild-nb187b.tgz を作りビルドサーバーにアップロードします。

    On iMac
    mkdir for-BuildServer
    cp -p appinventor-sources/appinventor/build/buildserver/BuildServer.tar for-BuildServer/
    cp -p appinventor-sources/appinventor/misc/buildserver/launch-buildserver  for-BuildServer/
    tar cfz forbuild-nb187b.tgz for-BuildServer/

    ビルドサーバー用のCompute Engineにログインして、以下コマンドでビルドサーバーを起動します。

    tar xvfz forbuild-nb187b.tgz
    Cd for-BuildServer
    tar -xf BuildServer.tar
    ./launch-buildserver --maxSimultaneousBuilds 4

    一部メニューを日本語化

    結構前のリリースから気になっていたんですが、一部のメニューがまだ日本語化されていません。SettingsとかView Trashとかです。理由はApp Inventor 2自体が多言語に対応していなかったからなんですが、今回ソースを見直してみると多言語対応しているので日本語化しました。MITのApp Inventor 2ではまだ多言語対応していません。ハングルだとこんな感じですね。

    変更したファイルは appinventor/appengine/src/com/google/appinventor/client/OdeMessages_ja_JP.properties で、次の部分を追加しました。

    showExportAndroidApk=アンドロイドアプリ(.apk)
    
    showExportAndroidAab=アンドロイドアプリバンドル(.aab)
    
    trashButton=ゴミ箱を表示
    viewTrashTabName=ゴミ箱を表示
    trashButton=ゴミ箱を表示
    myProjectsButton=私のプロジェクト
    restoreProjectButton=元に戻す
    deleteFromTrashButton=ゴミ箱から削除
    showEmptyTrashMessage=ゴミ箱は空です
    
    settingsTabName=設定
    disableAutoload=プロジェクトの自動ロードを無効化
    enableOpenDyslexic=OpenDyslexicを有効化

    これで改めてビルドしてステージング環境にデプロイしてテストします。今度はGUI部分からHelloCodiプロジェクトを開いてビルド -> アンドロイドアプリ(.apk)でビルドしてみます。成功しました!

    2021年9月3日

    GitHubにプッシュ

    うまく動いたのでGitHubにプッシュします。タグはnb187b_jpにしました。後でユーザーの方にご指摘いただいたのですが、このプッシュの時に日本語ファイルの appinventor/blocklyeditor/src/msg/ai_blockly/messages_ja.json を入れ忘れました。今回の更新はまるでスムーズにいかなかったせいか、こうしたポカが多いですね。プッシュした後でGitHubからcloneしてテストビルドは2回もしたんですけど。

    これでデプロイしてようやく終わりかと思ったらMITからnb187cがリリースされました。でもGitHubにはプッシュされません。

    2021年9月10日

    いつまで経ってもnb187_cがGitHubにはプッシュされないのでMITに問い合わせたところ、nb187cはMIT内部変更なのでプッシュしないとか。それならそうと言ってよね、待ってる人もいるんだから。

    ということでGitHubにnb187c_jpタグをプッシュして完成。後は本番機デプロイ。

    本番環境にデプロイ

    作業自体はステージング環境デプロイと同じです。違うのはApp EngineのプロジェクトとビルドサーバーのIPアドレスだけです。デプロイ後GUIとビルド -> アンドロイドアプリ(.apk)が正常に動くことを確認します。ここで漏れがあるんですけどね。

    一応完了しました!nb187c_jpリリースです。

    2021年9月12,13日

    日本語未表示バグ

    ユーザーの方からtwitter経由で組み込みブロック内の命令が日本語になっていない旨、お知らせいただきました。とってもありがたかったです。日本語化プロジェクトは皆さんのご協力無しには運営できません。この場を借りて改めてお礼申し上げます。

    原因は appinventor/blocklyeditor/src/msg/ai_blockly/messages_ja.json を入れ忘れでした。ファイルを追加してコミット、GitHubにプッシュ、タグはnb187d_jpにしました。

    これで本当に完了、ならいいけどな。

    チュートリアル

    人工知能の機械学習モデルを使って画像を精度良く分類するアプリを作りましょう

    このアプリは人工知能画像分類アプリとは異なりエクステンションを使わないので機械学習モデルの変更が簡単です

    クリックしてアプリが動いているところを見てください

    [プロジェクト]メニューから[プロジェクトを新規作成]を選択し、"WhatisThis2″と名前を付けます。

    なぜエクテンションを追加しなくていいのか?

    tensorflow.jsを使うとブラウザで機械学習モデルを利用することができます。MITで開発されたエクステンションLookExtensionはtensorflow.jを利用していますが、機械学習モデルを変更するためにはにエクステンションをビルドし直さなければならないので、簡単にはモデルを変更できません。その上、このエクステンションで使っているモデルはサイズが小さいので精度がそれほど良くないため、本格的なアプリの開発にLookExtensionを使えません。日本語化プロジェクトではこの問題を解決するために、ブラウザで使用するJavaScriptを含んだウェブページを使用するだけで機械学習モデルを使った画像分類方法を開発しました。この方法ではイメージファイルをウェブページで読み込むために外部ウェブサイトを利用していますが、ウェブページ内のJavaScriptを変更するだけでモデルを容易に変更できます。

    デザイン編集

    レイアウトとコンポーネントの配置

    下図のようにユーザーインターフェースパレットからウェブビュー、テキストボックス、レイアウトパレットから横並びレイアウトを配置し、横並びレイアウトの中にユーザーインターフェースパレットから二つのボタンをドラッグアンドドロップし撮影ボタン、推論ボタンと名前を変更して配置します。見えないコンポーネントとしてメディアパレットからカメラ、タイマーを、接続パレットから二つのWebをドラッグアンドドロップしアップロード、ファイル削除と名前を変更します。テキストボックス1のヒントは「推論結果」に、撮影ボタンのテキストは「イメージ撮影」、推論ボタンのテキストは「推論」にします。

    クリックして拡大

    JavaScriptを含んだウェブページの追加

    以下のhtmlファイルを作成し、メディアの"ファイルをアップロード"ボタンをクリックしてアセットに追加します。ファイル名はindex.htmlにしてください。以下はこのファイルの簡単な説明です。

    行3:tensowflow.jsの読み込み
    行4:MobileNetの読み込み
    行10:撮影したイメージファイルの表示タグ
    行18-24:WebGL2判別関数
    行27-30:イメージ差し替え関数setimage()。App Inventorアプリから渡されるイメージファイルのURLをイメージタグにセットします
    行33-44:推論関数classify()。推論を行い結果をApp Inventorアプリに渡します
    行47-62:初期化関数app()。WebGL2の有無を判別してbackEndを設定したあと、MobileNetモデルを読み込みます

    <html>
    <head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet"></script>
    </head>
    <body>
    <div><input type="text" id="output" size="100" value=""></div>
    
    
    <img id="img" crossorigin src="https://img.appinventor.tmsoftwareinc.com/images/tfjs.png" width="80%"/>
    
    
    
    <script >
    let net;
    
    
    var supportsWebGL2 = ( function () {
    try {
    return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'webgl2' );
    } catch( e ) {
    return false;
    }
    } )();
    
    
    function setimage() {
    var appInventorInput = window.AppInventor.getWebViewString();
    document.getElementById('img').src = appInventorInput;
    }
    
    
    async function classify() {
    window.AppInventor.setWebViewString('');
    
    
    const imgEl = document.getElementById('img');
    
    
    const result = await net.classify(imgEl);
    const ret = result[0]['className'];
    // document.getElementById("output").value = ret;
    window.AppInventor.setWebViewString(ret);
    }
    
    
    async function app() {
    if(supportsWebGL2){
    document.getElementById("output").value = 'WebGL2 is supported';
    }else{
    document.getElementById("output").value = 'WebGL2 is not supported';
    tf.setBackend('cpu');
    }
    
    
    document.getElementById("output").value = 'モデル読み込み中..';
    
    
    // Read MobileNet
    net = await mobilenet.load();
    document.getElementById("output").value = 'モデル読み込み完了';
    }
    
    
    app();
    </script>
    </body>
    </html>

    ブロック編集機能を使用したプログラミング

    アプリの動作をプログラミングするには、 ブロック編集機能にアクセスする必要があります。 画面右上のブロック編集ボタンをクリックしてブロック編集機能に行きます。

    グローバル変数

    はじめにグローバル変数を定義します。組み込みブロック内の変数をクリックして”グローバル変数 変数名 を次の値で初期化”ブロックをドラッグアンドドロップして下図のように設定してください。productionはapkファイルをビルドする時は”真”、MIT AI2 Companionで実行する時は”偽”にします。imageHostはイメージファイルをアップロードするウェブサーバーです。タイマー待ち時間は推論結果を取得するまでの待ち時間をミリ秒で指定します。その他は一時的に使う変数です。

    クリックして拡大

    スクリーン初期化イベント

    “いつもScreen1初期化したら“ブロックに以下のブロックを組み込みます。最初の三つは一時グローバル変数を設定しています。順にアップロードしたイメージファイルのURL、イメージファイルのアップロードに使うスクリプトのURL、イメージファイルの削除に使うスクリプトのURLです。次のもしブロックはapkファイルをビルドする時とMIT AI2 Companionで実行する時はアセットのパスが異なるためにウェブビューのURLに異なるパスを設定しています。最後にタイマーの待ち時間を設定し、タイマーを無効にします。

    クリックして拡大

    撮影ボタンクリックイベント

    “撮影ボタンをクリックされたら“ブロックに“テキストボックス1テキスト“ブロックに空白テキストを組み込み、“呼び出すカメラ1撮影する“ブロックと、“推論ボタン有効を“ブロックにロジックの偽ブロックを組み込んだものを組み込みます。

    撮影終了後イベント

    “いつもカメラ1撮影終了後“ブロックでグローバル変数のphotoPathに撮影したイメージを設定し、アップロードWebのURLにグローバル変数のuploadURLを設定してアップロードWebにイメージをPostしてアップロードします。同時に推論ボタンを有効にします。

    アップロード結果取得イベント

    “いつもアップロードテキストに受け取ったら“ブロックで、アップロードが成功するとアップロードしたファイル名がレスポンスコンテントに返ってくるのでグローバル変数のimageNameにこのファイル名を設定し、ウェブビュー1のWebView文字列にイメージファイルのフルURLをセットして、setimage()関数を呼び出します。これによりアップロードしたイメージがスクリーンに表示されます。

    推論ボタンクリックイベント

    “推論ボタンをクリックされたら“ブロックに、“テキストボックス1テキスト“ブロックに空白テキストを組み込み、classify()関数を呼び出し推論を実行します。同時にタイマーを有効にします。

    タイマーイベント

    classify()関数を呼び出してからウェブビュー1のWebView文字列が返ってくるのには時間がかかるので、タイマーを使ってしばらく待っています。この時間はグローバル変数のタイマー待ち時間で設定していますが、500ミリ秒(0.5秒)です。“いつもタイマー1タイマー“ブロックで、まずタイマー1を無効にして、テキストボックス1テキストにウェブビュー1のWebView文字列を表示し、ファイル削除WebのURLにグローバル変数のremoveURLにファイル名を結合したURLを設定してからファイル削除Webをメソッドで呼び出してファイルを削除します。

    これで完成です。スマホでテストしてください。分類結果は英語で下部に表示されます。

     

    クリックして拡大

    ソースコードのダウンロード

    App Inventorでこのサンプルを使用したい場合は、 ソースコードをコンピュータにダウンロードしてからApp Inventorを開き、[ プロジェクト ]をクリックして[ローカルコンピュータからプロジェクト(.aia)をインポート]を選択し、ソースコードを選択してインポートしてください。

    PHPソースコード

    イメージファイルのアップロードと削除に使っているPHPソースコードです。ご自分のウェブサーバーを使うときに参考にしてください。ブラウザのセキュリティーのためにウェブサーバーはHeaderにAccess-Control-Allow-Origin “*" を返す必要があります。Apacheウェブサーバーの場合は Header set Access-Control-Allow-Origin “*" を設定します。

    upload.php

    <?php
    $data = file_get_contents('php://input');
    $tmpfname = tempnam("/var/www/------/images", "prefix");
    if (!(file_put_contents($tmpfname,$data) === FALSE)) echo basename($tmpfname); // file could be empty
    else echo "File xfer failed.";
    ?>

    remove.php

    <?php
    if(isset($_GET['fn'])) {
        $fname = "/var/www/------/images/" . $_GET['fn'];;
        if(file_exists($fname)) {
            echo "unlinked $fname";
            unlink($fname);
        }
    }
    ?>

    チュートリアル

    人工知能の機械学習モデルを使って画像を分類するアプリを作りましょう

    クリックしてアプリが動いているところを見てください

    [プロジェクト]メニューから[プロジェクトを新規作成]を選択し、"WhatisThis"と名前を付けます。

    エクテンションの追加

    https://appinventor.tmsoftwareinc.com/download/LookExtension.aix から日本語化プロジェクトでハックしたLookExtension.aixをダウンロードし、LookExtension.aixをエクステンションパレットからImport extensionをクリックしてインポートします。LookExtension.aixはMITで開発されたエクステンションですが機械学習モデルによる推論をアンドロイドデバイス上で行うために、オリジナルのLookExtension.aixは動作しないアンドロイドデバイスが多く、日本語化プロジェクト所有のアンドロイドデバイスでは動作確認ができませんでした。MITオリジナルのLookExtension.aixが動作確認されているアンドロイドデバイスはこちらです。日本語化プロジェクトでハックしたLookExtension.aixはMITオリジナルのLookExtension.aixが動作しないデバイスではバックエンドにCPUを使用することにより推論速度は遅くなるものの、多くのアンドロイドデバイスで動作するようになっています。MITオリジナル版が動作しなくて日本語化プロジェクトハック版が動作することが確認されているアンドロイドデバイスは今のところ、Google Nexus 5、Moto G Play、Samsung Galaxy Tab 4 8.0です。このエクステンションで使っているモデルはサイズが小さいので精度はそれほど良くありません。

    クリックして拡大

    デザイン編集

    レイアウトとコンポーネントの配置

    下図のようにユーザーインターフェースパレットから状況ラベル、レイアウトパレットから縦並びレイアウトを配置し、縦並びレイアウトの中にユーザーインターフェースパレットからウェブビュー、レイアウトパレットから横並びレイアウト、その中にはユーザーインターフェースパレットから撮影ボタンを配置します。見えないコンポーネントとしてエクステンションパレットLookExtensionとメディアパレットからカメラをドラッグアンドドロップします。状況ラベルのテキストは「準備中」に、撮影ボタンのテキストは「イメージ撮影」にします。Look1のプロパティのInputModeをImageに、WebViewerをウェブビュー1にするのを忘れないでください。忘れると動きません。

    クリックして拡大

    ブロック編集機能を使用したプログラミング

    アプリの動作をプログラミングするには、 ブロック編集機能にアクセスする必要があります。 画面右上のブロック編集ボタンをクリックしてブロック編集機能に行きます。

    エラーメッセージ

    はじめにグローバル変数を定義します。組み込みブロック内の変数をクリックして”グローバル変数 変数名 を次の値で初期化”ブロックをドラッグアンドドロップします。変数名は「エラーメッセージ」にしてください。次に組み込みブロック内のリストをクリックして"リストを作成"を1個ドラッグアンドドロップしてグローバル変数ブロックに組み込みます。

    “リストを作成"の左上にギアアイコンをクリックして要素をドラッグアンドドロップして要素が合計7個になるようにします。

    “リストを作成"を7個ドラッグアンドドロップして"リストを作成"ブロックに組み込みます。あとは数学にある数字とテキストにある文字列をドラッグアンドドロップして下図のようにします。

    “いつもLook1.Error"ブロックをドラッグアンドドロップして下図のように状況ラベルにエラーメッセージを表示するようにします。"結合する"ブロックは組み込みブロック内のテキストに、"ペア検索"ブロックは組み込みブロック内のリストにあります。"取得errorCode"ブロックは"いつもLook1.Error"ブロックのオレンジ色のerrorCodeをクリックすると取れます。

    撮影ボタンクリックイベント

    “撮影ボタンをクリックされたら“ブロックに“呼び出すカメラ1撮影する“ブロックと、“縦並び1見えるを“ブロックにロジックの偽ブロックを組み込んだものを組み込みます。

    “いつもカメラ1撮影終了後“ブロックに“呼び出すLook1ClassifyImageData“ブロックと状況ラベルに「推論中」と表示するブロックを組み込みます。"取得イメージ"ブロックは"いつもカメラ1撮影終了後"ブロックのオレンジ色のイメージをクリックすると取れます。

    Look1イベント

    “いつもLook1ClasifierReady“ブロックに"撮影ボタン有効を"ブロックにロジックの真ブロックを組み込んだものと状況ラベルに「推論中」と表示するブロックを組み込みます。

    “いつもLook1GotClasification“ブロックに“縦並び1見えるを“ブロックにロジックの真ブロックを組み込んだものと状況ラベルに結果リストの1番目を表示するブロックを組み込みます。"結果"ブロックは"いつもLook1GotClasification"ブロックのオレンジ色の結果をクリックすると取れます。

     
    これで完成です。スマホでテストしてください。分類結果は英語で上部に表示されます。
    クリックして拡大

    ソースコードのダウンロード

    App Inventorでこのサンプルを使用したい場合は、 ソースコードをコンピュータにダウンロードしてからApp Inventorを開き、[ プロジェクト ]をクリックして[ローカルコンピュータからプロジェクト(.aia)をインポート]を選択し、ソースコードを選択してインポートしてください。

    チュートリアル

    新型コロナ感染者数を地図を使って検索するアプリを作ります

    このアプリは実用的でGoogle Playで公開できるレベルのアプリです。ただし、新型コロナ関係のアプリはGoogle Playが受け付けないので、このアプリはこのままでは公開できません。

    更新履歴

    • 初版 2/11/2021

    使用しているテクニック

    1. RESTful API呼び出し
    2. JSONリストの扱い
    3. JSONリストのクイックソート
    4. GPS
    5. マップ
    6. TinyDB
    7. 手続き

    ソースコード(aiaファイル)のダウンロード(有料)

    チュートリアルを読んで手でアプリを入力すれば動くものが作れますが、入力する時間がもったいないという人のためにソースコードを2980円で販売しています。下の購入ボタンをクリックしてメールアドレス、クレジットカード番号、有効期限、CVCを入力すると、ダウンロードリンクがメールで送付されます。コンピュータにソースコードをダウンロードしてからApp Inventorを開き、[ プロジェクト ]をクリックして[ローカルコンピュータからプロジェクト(.aia)をインポート]を選択し、ソースコードを選択してインポートしてください。売り上げ金はプロジェクトの運営に必要なクラウドコンピュータ使用料金、テスト用スマホ購入費用、人件費などに当てられます。プロジェクトの発展のためにご協力ください。

    プログラムの構造(流れ)

    スポンサー企業のTM Software, Inc.ではアメリカ合衆国のジョンズ・ホプキンス大学が公開している全世界の新型コロナ感染者数のデータをデータベースに取り込み、1日の新規感染者数と人口10万人あたりの新規感染者数の7日間移動平均を計算し、RESTful APIを使用してデータを自由にダウンロードできるようにしています。このアプリではこのAPIを利用して1)最新の全国コロナ感染者数データを取得して新規感染者数の7日間移動平均の上位20都道府県にマークを表示、2)表示されている地図をクリックするとその場所の新規感染者数と累積感染者数を表示、3)地名を入力するとその場所にマップをズームしてその場所の新規感染者数と累積感染者数を表示、4)GPSを利用して現在地の新規感染者数と累積感染者数を表示、します。

    APIの呼び方と返される値

    緯度と経度からその都道府県のコロナ感染者数の時系列データを取得

    呼び方
    https://covid19api.tmsoftwareinc.com/api/v1/latlngs?lat=35.685750628984465&long=139.75407754498727
    返される値:JSONリスト
    [{“country":"Japan","state":"Tokyo","county":"","city":"","cases":49539,"newcases":822,"7rate":4.19,"deaths":532,"date":"2020-12-18″},
    {“country":"Japan","state":"Tokyo","county":"","city":"","cases":48717,"newcases":678,"7rate":3.95,"deaths":532,"date":"2020-12-17″},
    {“country":"Japan","state":"Tokyo","county":"","city":"","cases":48039,"newcases":460,"7rate":3.84,"deaths":522,"date":"2020-12-16″},
    {“country":"Japan","state":"Tokyo","county":"","city":"","cases":47579,"newcases":305,"7rate":3.73,"deaths":513,"date":"2020-12-15″},

    地名から緯度と経度を取得

    呼び方
    https://covid19api.tmsoftwareinc.com/v1/geos?location=東京
    返される値:JSON
    {“lat":35.6803997,"lng":139.7690174}

    最新の全国コロナ感染者数データを取得

    呼び方
    https://covid19api.tmsoftwareinc.com/api/v1/jabounds
    返される値:JSONリスト
    [{“state":"Aichi","county":"","city":null,"lat":35.035551,"lng":137.211621,"cases":13535,"newcases":238,"date":"2020-12-18″,"sevenrate":2.67},
    {“state":"Akita","county":"","city":null,"lat":39.748679,"lng":140.408228,"cases":94,"newcases":0,"date":"2020-12-18″,"sevenrate":0.06},
    {“state":"Aomori","county":"","city":null,"lat":40.781541,"lng":140.828896,"cases":390,"newcases":7,"date":"2020-12-18″,"sevenrate":0.35},
    {“state":"Chiba","county":"","city":null,"lat":35.510141,"lng":140.198917,"cases":8663,"newcases":148,"date":"2020-12-18″,"sevenrate":1.88},
    {“state":"Ehime","county":"","city":null,"lat":33.624835,"lng":132.856842,"cases":372,"newcases":2,"date":"2020-12-18″,"sevenrate":0.19},
    {“state":"Fukui","county":"","city":null,"lat":35.846614,"lng":136.224654,"cases":337,"newcases":0,"date":"2020-12-18″,"sevenrate":0.11},

    使用しているテクニックの詳細

    RESTful API呼び出し方法

    接続パレットにあるWebコンポーネントを使います。下のようにAPI呼び出しURLに必要な引数を結合してからWebコンポーネントのURLに設定し、WebコンポーネントのGetメソッドを呼び出します。

    Getメソッドは非同期で呼び出されるので、下の「いつもテキストに受け取ったら」を使って別途受け取る結果の処理を行います。

    JSONリストの扱い方法

    「いつもテキストに受け取ったら」で受け取る「レスポンスコンテント」をWebコンポーネントのJsonTextDecodeWithDictionariesメソッドに渡して、連想配列(dictionary)のリストに変換します。

    リスト内の要素を一つ取り出すときは「リスト内の順番の要素を取得」を、ループで全ての要素を取り出すときは「それぞれの項目リスト内の」を使います。

    取り出した要素は連想配列(dictionary)なので、キーを指定して値を取得します。

    JSONリストのクイックソート方法

    リストをQuick Sortアルゴリズムを使ってソートします。Dataにはソートするリストをleftにはソートするリストの最初の順番(通常は1)をrightにはソートするリストの最後の順番(通常はリストの要素数)をcolumnにはキーを指定します。

    以下がQuick Sortを行う手続きです。これは https://appinventorplus.wordpress.com/2017/04/12/quicksort-routine-for-your-app-inventor-apps/ 掲載の手続きを少し変更した物です。オリジナルの手続きはMIT App Inventor Galleryのここにあります。

    クリックして拡大
    クリックして拡大

    GPS

    GPSによる現在地の取得にはApp Inventor 2のセンサー関連パレットにある位置センサーコンポーネントを利用します。詳細はこちらを見てください。

    マップ

    マップはApp Inventor 2のマップ関連パレットにあるコンポーネントです。詳細はこちらを見てください。

    TinyDB

    TinyDBはApp Inventor 2のストレージパレットにあるコンポーネントです。詳細はこちらを見てください。

    プロジェクトを作成

    [プロジェクト]メニューから[プロジェクトを新規作成]を選択し、”casemap”と名前を付けます。

    デザイン編集

    レイアウトとコンポーネントの配置

    下図のようにレイアウトとコンポーネントを配置します。

    クリックして拡大

     

    1. 使用するイメージファイルを2個メディアにアップロードします。ファイルはCaseMapIcon.pngcaution-4-01.pngです。それぞれをクリックしてダウンロードしてください。CaseMapIcon.pngはScreen1のプロパティのアイコンに選びます。Screen1のプロパティのアプリ名は「新型コロナ感染者数マップ」、タイトルは「新型コロナ感染者数マップ」にします。
    2. 見えないコンポーネントとして接続パレットにあるWebを3個(ドラッグアンドドロップすると自動的にWeb1, Web2, Web3という名前になります)、センサーパレットにある位置センサー、ストレージパレットにあるTinyDBをドラッグアンドドロップします。
    3. 縦並びレイアウトを配置し「縦並び外側」と名前を変更したあと、その中に上から順番に横並び日付、縦並び内側、横並びヘルプ、マップ関連パレットからマップ1、横並びボタン、横並び状況、横並びデータソースを配置します。
    4. 次に順番にレイアウトの中にコンポーネントを配置していきます。横並び日付の中には日付という名前でラベルを、縦並び内側の中には横並び現在地(中に現在地という名前でラベル)、横並び感染率(中に感染率ラベル、感染率という名前で2個のラベル)、横並び新規(中に新規ラベル、新規感染者数という名前で2個のラベル)、横並び累積(中に累積ラベル、累積感染者数という名前で2個のラベル)を配置します。各ラベルのテキストは上の図の値にしてください。
    5. 横並びヘルプの中には注意イメージという名前でイメージと、ヘルプという名前でラベルを配置します。注意イメージの画像ファイルはcaution-4-01.pngを選びます。そのままだと大きいので、高さと幅は16ピクセルにします。ヘルプのテキストは「感染者数を知りたい場所で長押ししてください」にします。
    6. マップ1の中にはマップ関連パレットからマーカー1を配置します。マップ1の中心の緯度、経度は35.685750628984465, 139.75407754498727にすると皇居が中心になります。マーカー1の緯度、軽度も同じ値にしてください。
    7. 横並びボタンの中には場所テキストボックス、場所検索ボタン、現在地ボタン、全国ボタンを配置します。場所検索ボタン、現在地ボタン、全国ボタンのテキストは上の図の値にしてください。
    8. 横並び状況の中には状況ラベルを、横並びデータソースの中にはデータソースラベルを配置します。データソースラベルのテキストは「ジョンズ・ホプキンス大学のデータを使用しています」にします。

     

    ブロック編集機能を使用したプログラミング

    グローバル変数定義

    • コロナAPI: 緯度と経度からその都道府県のコロナ感染者数の時系列データを取得するAPIのURL https://covid19api.tmsoftwareinc.com/v1/latlngs?
    • コロナAPI2: 地名から緯度と経度を取得するAPIのURL https://covid19api.tmsoftwareinc.com/v1/geos?
    • コロナAPI3: 最新の全国コロナ感染者数データを取得するAPIのURL https://covid19.tmsoftwareinc.com/api/v1/jabounds
    • 初期状況メッセージ: 状況ラベルの初期値
    • 全国緯度: 全国表示する際のマップの中心緯度
    • 全国経度: 全国表示する際のマップの中心経度
    • 全国ズーム: 全国表示する際のマップのズームレベル
    • 初期緯度: マップの中心緯度の初期値
    • 初期経度: マップの中心経度の初期値
    • 初期ズーム: マップのズームレベルの初期値
    • 初期住所: 現在地ラベルの初期値
    • データ無し: データがない時のメッセージ
    • ヘルプ: ヘルプラベルの初期値=「は新規感染者数/10万人が高い都道府県を示していて、タップするとその数値を表示します。地名を入力して場所検索ボタンをクリックするとその場所に移動して感染者情報を表示します。感染者数を知りたい場所で長押しても感染者数情報を表示できます。」
    • 新規感染者数: 新規感染者数
    • 感染率: 感染率
    • マーカーリスト: 全国表示の際に感染率の高い場所に表示するマップ内マーカーのリスト
    • 感染率マーカー: 全国表示の際に感染率の高い場所に表示するマップ内マーカーの一時的な格納変数
    • 緯度: マップの現在の中心緯度
    • 経度: マップの現在の中心経度
    • 地図ズーム: マップの現在のズームレベル
    • JSONリスト: APIから取得したJSONリストの格納変数
    • JSON要素: JSONリストから取り出したJSON要素の格納変数
    • JSONリスト2: APIから取得したJSONリストの格納変数2
    • JSON要素2: JSONリスト2から取り出したJSON要素の格納変数
    クリックして拡大

    都道府県名翻訳用連想配列定義

    APIから返される都道府県名は英語なので、それを日本語に翻訳するために連想配列を定義してグローバル変数に格納しています。 

    クリックして拡大
    クリックして拡大

    手続き

    繰り返し使用するコードや複雑なコードは手続きで定義します。

    • JSONリストのクイックソート:こちらを見てください。
    • 感染者取得:緯度と経度からその都道府県のコロナ感染者数の時系列データを取得します。

    • 全マーカー表示、全マーカー非表示:マップ内マーカーをすべて表示したり、非表示したりします。

    スクリーン初期化に伴う作業など

    1. ヘルプラベル、状況ラベル、現在地ラベルを初期化します。
    2. TinyDBから前回使用した緯度、経度、ズームレベルを取得しグローバル変数の緯度、経度、ズームに設定します。初回起動時はグローバル変数の全国緯度、全国経度、全国ズームを使用します。
    3. グローバル変数の緯度、経度、地図ズームでマップをパンします。
    4. 緯度と全国緯度が等しくない場合は初回起動ではないので、マーカーは現在地に表示し感染者取得手続きを呼び出します。そうでない場合は初回起動なのでマーカーを非表示にし、縦並び内側も非表示にして都道府県ごとの感染者数情報を表示しません。
    5. 最新の全国コロナ感染者数データを取得するAPIを呼び出します。
    クリックして拡大

    緯度と経度からその都道府県のコロナ感染者数の時系列データを取得した時の処理

    感染者数の時系列データの取得にはWeb1コンポーネントを使用してAPIを呼び出しています。結果は非同期で返ってくるので、その際の処理を定義します。

    1. 返ってきた結果をグローバル変数のJSONリストに格納します。
    2. もしリストの長さが0ならば、縦並び内側を非表示にして都道府県ごとの感染者数情報を表示しません。状況ラベルにはデータ無しと表示します。
    3. そうでなければ、縦並び内側を表示にして状況ラベルは空白にします。引き続き以下の手順を実行します。
    4. JSONリストの最初の要素をJSON要素に格納します。
    5. もしJSON要素のcountryキーの値がJapanでは無いならば現在地ラベルにcountry+state+cityを結合した文字を、countryキーの値がJapanならばstateを都道府県名翻訳用連想配列を使って日本語に翻訳して現在地ラベルに表示します。
    6. JSON要素のdateキーの値を日付ラベルに表示します。
    7. もしJSON要素の7rate(新規感染者数の7日間移動平均)キーの値が-1(未定義)で無いならばグローバル変数の感染率にその値を格納し、そうでなければ「不明」を格納します。引き続き感染率ラベルにグローバル変数の感染率を表示します。
    8. もしJSON要素のnewcases(新規感染者数)キーの値が-1(未定義)で無いならばグローバル変数の新規感染者数にその値を格納し、そうでなければ「不明」を格納します。引き続き新規感染者数ラベルにグローバル変数の新規感染者数を表示します。
    9. 累積感染者数ラベルにJSON要素のcases(累積感染者数)を表示します。
    10. TinyDBに緯度、経度、ズームレベルを格納します。
    クリックして拡大

    地名から緯度と経度を取得した時の処理

    地名からの緯度と経度の取得にはWeb2コンポーネントを使用してAPIを呼び出しています。結果は非同期で返ってくるので、その際の処理を定義します。

    1. 返ってきた結果はリストでは無いのでグローバル変数のJSON要素に格納します。
    2. もしJSON要素のlatキーの値がnot foundで無いならばグローバル変数の緯度、経度にlatキーの値、lngキーの値を設定するとともにグローバル変数の地図ズームに初期ズームの値を設定し、グローバル変数の緯度、経度、地図ズームでマップをパンします。引き続きマーカー1を緯度、経度の場所に表示し、感染者数取得手続きを呼び出します。
    3. そうでなければ状況ラベルに「この場所は見つかりませんでした」と表示します。
    クリックして拡大

    最新の全国コロナ感染者数データを取得した時の処理

    最新の全国コロナ感染者数データの取得にはWeb3コンポーネントを使用してAPIを呼び出しています。結果は非同期で返ってくるので、その際の処理を定義します。

    1. 返ってきた結果をグローバル変数のJSONリスト2に格納します。
    2. もしリストの長さが0ならば、縦並び内側を非表示にして都道府県ごとの感染者数情報を表示しません。状況ラベルにはデータ無しと表示します。
    3. そうでなければ、JSONリスト2をsevenrate(新規感染者数の7日間移動平均)で降順にクイックソートします。
    4. JSONリスト2の1番目から20番目まで以下の通りループ処理します。
    5. ローカル変数のJSON項目にJSONリスト2の現在の要素を設定します。
    6. JSON項目のdateキーの値を日付ラベルに表示します。
    7. JSON項目のlatキーの値、lngキーの値の位置にマーカーを作成してグローバル変数の感染率マーカーに格納し、マーカーのイメージをcaution-4-01.pngに設定し、マーカーのタイトルにstateキーの値を都道府県名翻訳用連想配列を使って日本語に翻訳したものとsevenrateキーの値を結合した文字列を設定します。そしてマーカーの情報ボックスを有効にします。
    8. もしも1番目(新規感染者数の7日間移動平均が国内最大)ならばマーカーの情報ボックスを表示します。
    9. 作成したマーカーを上書きして消さないようにグローバル変数のマーカーリストに感染率マーカーを追加します。
    クリックして拡大

    マップ関連の処理

    マップ1が長押しされたら緯度、経度をグローバル変数の緯度、経度に格納し、マーカー1をその場所に移動して感染者数取得手続きを呼び出します。

    マップ1の境界が変わったら(ドラッグ、パン、ズームされたら)状況ラベルに初期状況メッセージを表示します。

    ボタンクリック処理

    場所検索ボタン

    1. 入力された地名から緯度、経度を取得するAPIを呼び出します。
    クリックして拡大

    現在地ボタン

    1. 位置センサーの緯度が0でなければ(位置情報が取れていれば)位置センサーの緯度、経度をグローバル変数の緯度、経度に、グローバル変数の地図ズームに初期ズームの値を設定し、グローバル変数の緯度、経度、地図ズームでマップをパンします。引き続きマーカー1を緯度、経度の場所に表示し、感染者数取得手続きを呼び出します。
    2. そうでなければ状況ラベルに「GPS準備中」と表示します。
    クリックして拡大

    全国表示ボタン

    1. グローバル変数の全国緯度、全国経度、全国ズームでマップをパンした後、全マーカーを表示し、1番目(新規感染者数の7日間移動平均が国内最大)のマーカーの情報ボックスを表示します。
    クリックして拡大

    チュートリアル

    我々は米国在住で日本国内のクラウドファンディングサイトが利用できませんので、米国のPatreonというクラウドファンディングサービスを利用しています。英語サイトなのでわかりにくいところもあるかと思い、ご支援方法をまとめました。

    Patreonページを表示

    https://www.patreon.com/appinventorjapan をクリックして下のようなPatreonページを表示します

    ログイン

    ページ右上 Login をクリックすると下のように表示されますので、Sign upからPatreonアカウントを作るか、Continue with Google, Continue with Apple, Continue with Facebookのいずれかをクリックしてそれぞれのアカウントを使ってログインしてください。

    初めてログインした時は居住地を聞かれますので、日本にお住いの場合は Japan 、その他の国の場合はお住いの国を選んで Submit をクリックします。

    レベルを選択して支援金お支払い方法の入力

    支援金額によって特典の異なる5種類のレベルがありますので、支援したいレベルを決めて、紫色のJoinボタンをクリックします。

    支払い方法入力画面が表示されますので、一番上のChoose what you pay(支援額)に支援金額を入力します。この支援金額は選択したレベルの月額費用の最低金額が表示されていますが、それ以上の金額を支払いたい場合はここで金額をご自分で入力できます。次に、カード情報を記入し Pay With Card ボタンをクリックして決済をします。CVVはカード裏面(アメリカンエクスプレスは表面)にあるセキュリティコードです。こちらをご覧ください。Postal Codeには郵便番号をハイフン無しで入力してください。PayPalアカウントをお持ちでクレジットカード決済をご希望ではない場合にはそちらを選択してください。

     

    これで完了です。お疲れ様でした。以降毎月1日に同じ金額が課金されます。