application」カテゴリーアーカイブ

[kz] PlantUMLのセットアップからハンズオンまで

こんにちは、kazscapeです。

本日は、PlantUMLをさわってみます。

PlantUMLとは

「PlantUML」とは、ソフトウェア開発の業務で作成するUML(Unified Modeling Language))ダイアグラムというさまざまな図を、ExcelやVisioといった図形描画のツールで書くのではなく、テキストをつらつら書くことで図が作成できるオープンソースの開発言語です。
書くことができるダイアグラムの種類には、

  • シーケンス図
  • ユースケース図
  • クラス図
  • アクティビティ図(古い文法はこちら)
  • コンポーネント図
  • 状態遷移図(ステートマシン図)
  • オブジェクト図
  • 配置図
  • タイミング図
  • ワイヤーフレーム
  • アーキテクチャ図
  • 仕様及び記述言語 (SDL)
  • Ditaa
  • ガントチャート
  • マインドマップ
  • WBS図(作業分解図)
  • AsciiMath や JLaTeXMath による、数学的記法

があります。

セットアップ

セットアップ環境

今回はWindowsやMacで使えるクロスプラットフォームなエディタであるVisual Studio Codeの拡張機能としてPlantUMLをセットアップしました。
この記事では、Macにセットアップしましたが、概ねWindowsでもあまり違いはないと思います。
セットアップした環境は、

  • macOS Mojave ver.10.14.4
  • Visual Studio Code ver.1.33.1
  • Java JDK ver.11.0.2
  • Graphviz ver.2.40.1

になります。
さらに、Visual Studo Codeの拡張機能(Extension)として、

  • PlantUML ver.2.10.9

をいれました。

Visual Studio Codeのインストール

Visual Studio Codeのダウンロードサイトから、Mac用のものをダウンロードします。

VSCode-darwin-stable.zipというzipファイルがダウンロードできるので、解凍したVisual Studio Code.appをアプリケーションに移動します。
起動するとWelcome画面が表示されます。さっそく拡張機能をインストールします。
左のサイドバーにある拡張機能(Extension)を選択します。

左上のSearch Extensions in MarketplacePlantUMLと入力して、検索されたPlantUMLを選択します。

Installボタンを押して、

InstallボタンがUninstallになれば、インストールされています。

Javaのインストール

Javaのインストールは、Mac用のパッケージマネージャーであるHomebrewでインストールします。
Homebrewがインストールされていないようでしたら、前回の投稿を参考にしてください。
ターミナルを起動して、HomebrewでJavaをインストールします。
version 12のJavaを入れると、Previewの際にWarningが出るので、version 11をインストールします。
※これについては、PlantUMLのForumでも話題になっているので、解決したらversion 12に入れ替えることにします。
過去のversionをインストールできるように、homebrew-cask-versionsを導入します。

brew tap homebrew/cask-versions

そして、Javaのversion 11をインストールします。

$ brew cask install java11

バージョンを確認します。

$ /usr/libexec/java_home -V

実行結果

Matching Java Virtual Machines (1):
    11.0.2, x86_64:    "OpenJDK 11.0.2"    /Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home

さらに、Javaへのpathを通す必要があります。
ホームディレクトリに.bash_profileがない場合は、

$ touch ~/.bash_profile

で作成をします。
.bash_ProfileにJavaへのpathを記載します。
バージョンの確認の実行結果で表示された、Javaがインストールされたディレクトリを使って、

export PATH=$PATH:/Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home/bin
export JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home

と記載します。
sourceコマンドでbash_profileの更新を反映します。

$ source ~/.bash_profile

pathが設定されているか確認します。

$ echo $PATH
$ echo $JAVA_HOME

graphvizのインストール

PlantUMLで書かれたダイアグラム作成のための言語を描画するためのパッケージです。
Homebrewでインストールします。

$ brew install graphviz

PlantUML ハンズオン

ここまででPlantUMLでダイアグラムを書く準備が整ったので、PDFガイドを使って、ハンズオンしてみます。
例えば、シーケンス図をこんな風に書いてみます。

@startuml
Alice -> Bob: Authentication Request
alt successful case
Bob -> Alice: Authentication Accepted
else some kind of failure
Bob -> Alice: Authentication Failure
group My own label
Alice -> Log : Log attack start
loop 1000 times
Alice -> Bob: DNS Attack
end
Alice -> Log : Log attack end
end
else Another type of failure
Bob -> Alice: Please repeat
end
@enduml

これを、.puという拡張子で保存して、Alt+Dでプレビュー表示すると、

こんな風に表示してくれます。
描画されたダイアグラムはCommand PaletteでExport Current Diagramから、

pngやpdfでExportすることができます。

PDFガイドを一通りやってみると、さまざまな応用ができることが理解でき、幅広くダイアグラムが書けることがわかります。

まとめ

PlantUMLは慣れこそ必要ですが、ソフトウェア開発だけでなく、Blogにちょっとした図を掲載したいときなどにも使えそうです。
Excelなどで書いた場合、図の中に記載した文字列は検索できないですが、PlantUMLならテキストですから検索が可能になります。
また、ダイアグラムのために書いたコードは、GitHubなどで管理をするとDiffが取りやすく、バージョン管理にも優れています。
使い方に慣れて、さまざまシーンで活用したいですね。

今日はちょっとテクニカルなお話でした。

ではっ!


[kz] Google Spreadsheetの英文を音声にしてFlashcardsに連携するプログラム

こんにちは、@kazscapeです。

ゴールデンウィークですね。なにしてますか?

私はプログラムなんぞをつくっています。

ゴールデンウィーク前半に作ったプログラムはこちら!

”Google Spreadsheetに書いた英文をGoogle翻訳で音声にして、それをGoogle Driveにアップロードして、暗記帳アプリの「Flashcards Deluxe」に連携する”

がんばって作りましたのでご紹介

Google Spreadsheetの準備

以前の記事でご紹介した単語帳アプリ「Flashcards Deluxe

このアプリ、Google Spreadsheetと連携できるんですね。

「Flashcards Deluxe」とGoogle Driveを連携させるためには、Google Driveに「Flashcards Deluxe」というフォルダを作成して、その配下にSpreadsheetを作成しないといけないこと、さらに音声ファイルは「作成したSpreadsheet名 + <半角スペース> + “Media”」というフォルダに保存しなければならないことに注意してください。

google-drive-for-flashcards-001

今回、「Just Look ‘n Learn English Picture Dictionary」というSpreadsheetを作成したので、「Just Look ‘n Learn English Picture Dictionary Media」というフォルダを作ってあります。

google-drive-for-flashcards-002

Google Spreadsheetを「Flashcards Deluxe」で読み込めるフォーマットは、

  • Text1:いわゆる暗記帳のおもて
  • Text2:いわゆる暗記帳のうら
  • Category1:”Chapter 1″だったり、分類やグループなど
  • Sound 2:音声ファイル

といった項目名を1行目に記載して認識してもらいます

そこで、まずは、Google Spreadsheetをこんなふうに用意します。

google-spreadsheet-for-flashcards-001

Text1には単語を、Text2には英文を。Category1にはグループを入れてあります。Sound2はこのあと作るプログラムで自動で入れてもらうので空欄です。

Googleの認証情報を作ります

今回は、Google Spreadsheetを更新して、Google Driveに音声ファイルを保存するプログラムを作りますが、私以外の誰でも彼でもが好き勝手にそんなことができてはセキュリティずぶずぶですよね。そこで、「私がやってることですよ!登録してるでしょ、私!」的な認証情報を作ります。

まずは、これから作ろうとするプログラムが適切に認証されたプログラムであることを証明するための「認証情報」を取得します。認証情報の取得は「Google API Console」で取得します。

Googleのアカウントをお持ちでなかったら、作ってくださいね。

まずはプロジェクトを作成します。「プロジェクト」なんて、たいそうなものではありませんが。。。

google-api-v4-quickstart-001

自分が作ったプログラムに反応してくれるAPIが有効化されました。続けて「認証情報」を作ります。

google-api-v4-quickstart-002

「プロジェクトへの認証情報の追加」という画面になりますが、一旦、「キャンセル」してください。

google-api-v4-quickstart-003

先に認証方法の「OAuth」の同意をしますので、「OAuth同意画面」のタブを選択してください。

google-api-v4-quickstart-004

「OAuth同意画面」で必要な情報を入力していきます。メールアドレスはGoogleアカウントが自動で表示されます。「サービス名」には適当な名前を入れてください。あとの項目は省略可なので入力してもしなくてもよいです。「保存」ボタンで先に進んでください。

google-api-v4-quickstart-005

「認証情報」のタブに戻るので、ここで「認証情報を作成」に進み、「OAuthクライアントID」を選んでください。

google-api-v4-quickstart-006

今回作ろうとしているプログラムは、表示されている具体的なものとは一致しませんので「その他」を選択。名前は適当な名前を入力して、「作成」ボタンで先に進みます。

google-api-v4-quickstart-007

ここまで進むと「クライアントID」と「クライアントシークレット」という認証情報が取得できます。

google-api-v4-quickstart-008

認証情報は作成しただけでなく、ダウンロードしてください。JSONという形式のファイルがダウンロードできますので、これから作ろうとしているプログラムと同じ場所に保存してください。そして、ファイル名は変更しなくてもいいですが、私は「client_secret.json」というファイル名に変更しておきました。

google-api-v4-quickstart-009

さぁ、プログラミングです。

今回は、最近、勉強している「Python」というプログラミング言語でプログラムを作成してみます。

やりたいことは、

  1. Google Spreadsheetに音声ファイル名を追加すること
  2. Google Spreadsheetに記載されている英文をGoogle翻訳で音声ファイル(MP3)を作成すること
  3. 作成された音声ファイルをGoogle Driveに保存すること

です。

作成したプログラムはこちら。


from __future__ import print_function
import os
import shutil
import httplib2

from googleapiclient import discovery
from googleapiclient.http import MediaFileUpload
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from gtts import gTTS
from datetime import datetime as dt

SHEET_SCOPES = 'https://www.googleapis.com/auth/spreadsheets'
DRIVE_SCOPES = 'https://www.googleapis.com/auth/drive.file'
SHEET_JSON_NAME = 'sheet2flashcards.json'
DRIVE_JSON_NAME = 'upload2gdrive.json'
SHEET_ID = <SpreadsheetのID>
DRIVE_ID = <Goggle DriveのID
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Google API Python Client'
MAX_COLUMN = 4
TEXT1 = 0
TEXT2 = 1
SOUND2 = 3

try:
    import argparse
    parser = argparse.ArgumentParser(parents=[tools.argparser])
    parser.add_argument('--update',
                        action='store_true',
                        default=False,
                        help='update all contents (default: False)')
    flags = parser.parse_args()
except ImportError:
    flags = None

# 認証
def get_credentials(scopes, json_name):
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir, json_name)

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, scopes)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

# Google SpreadsheetにMP3のファイル名を追加
def append_mp3name_to_gsheet():
    credentials = get_credentials(SHEET_SCOPES, SHEET_JSON_NAME)
    http = credentials.authorize(httplib2.Http())
    discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?'
                    'version=v4')
    service = discovery.build('sheets', 'v4', http=http,
                              discoveryServiceUrl=discoveryUrl)
                    
    result = service.spreadsheets().values().get(
        spreadsheetId=SHEET_ID, range='A2:D').execute()
    values = result.get('values', [])

    if not values:
        print('No data found.')
    else:
        for row in values:
            if len(row) < MAX_COLUMN:
                row.append(row[TEXT1] + '.mp3')

        value_range_body = {"range":"A2:D",
                            "majorDimension":"ROWS",
                            "values":values}
        response = service.spreadsheets().values().update(
                        spreadsheetId=SHEET_ID, range="A2:D",
                        valueInputOption='USER_ENTERED',
                        body=value_range_body).execute()
    return values

# MP3ファイルをGoogle Driveに追加(--updateオプションの場合は総入れ替え)
def mp3_to_gdrive(lists):
    credentials = get_credentials(DRIVE_SCOPES, DRIVE_JSON_NAME)
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('drive', 'v3', http=http)
    
    datetime = dt.now()
    dirname = datetime.strftime('%Y%m%d%H%M%S')
    os.mkdir(dirname)

    query_parents = "'" + DRIVE_ID + "'" + " in parents"

    for row in lists:
        query_mp3name = "name = " + "'" + row[SOUND2] + "'"
        results = service.files().list(
            q = query_parents + " and trashed = False and " +
                query_mp3name, 
            fields = "files(id, name)").execute()
        results = results.get('files', [])

        if not results:
            tts = gTTS(text = row[TEXT2],lang = 'en',slow = False)
            tts.save(os.path.join(dirname, row[SOUND2]))

            file_metadata = {'name':row[SOUND2], 'parents':[DRIVE_ID]}
            media_body = MediaFileUpload(os.path.join(dirname, row[SOUND2]),
                                        mimetype='audio/mp3')
            
            results = service.files().create(body=file_metadata, 
                                    media_body=media_body).execute()
        else:
            if flags.update == True:
                tts = gTTS(text = row[TEXT2], lang = 'en', slow = False)
                tts.save(os.path.join(dirname, row[SOUND2]))

                file_metadata = {'name':row[SOUND2], 'addParents':[DRIVE_ID]}
                media_body = MediaFileUpload(os.path.join(dirname, row[SOUND2]),
                                            mimetype='audio/mp3')
            
                for item in results:
                    file_id = item['id']
                    results = service.files().update(fileId=file_id,
                                        body=file_metadata, 
                                        media_body=media_body).execute()

    shutil.rmtree(dirname)
                                    
def main():
    lists = append_mp3name_to_gsheet()
    mp3_to_gdrive(lists)

if __name__ == '__main__':
    main()

「SHEET_ID」と「DRIVE_ID」はご自身のものに書き換えてください。

「SHEET_ID」は、

google-spreadsheet-for-flashcards-002

「DRIVE_ID」は、

google-drive-for-flashcards-003

それぞれ、URLのところの一部の箇所にあります。

Google SpreadSheetに単語、英文、カテゴリーを入力して、このプログラムを起動すると、音声ファイルを作成してくれて、所定のGoogle Driveに音声ファイルを保存してくれます。

起動方法は

python gtts2flashcards.py

もちろんPythonが動く環境であることが前提です。

「–update」オプションをつけて

python gtts2flashcards.py --update

と起動すると、既存のMP3ファイルも洗い替えてくれるようにしました。

どんなふうな結果になるかというと

Spreadsheetの空だった「Sound 2」の項目に、

google-spreadsheet-for-flashcards-003

音声ファイル名が追加されます。

google-spreadsheet-for-flashcards-004

そして、Mediaのフォルダには、音声ファイルが保存されます。

google-drive-for-flashcards-004

Flashcards Deluxeへの読み込み

ここまでくれば、あとはFlashcards Delexe側で単語帳を読み込むことができます。

単語帳の追加元からGoogle Driveを選択します。

flashcards-deluxe-mp3-download-001

今回作ったSpreadsheetをダウンロードします。

flashcards-deluxe-mp3-download-002

単語を選択すると、

flashcards-deluxe-mp3-download-003

英文が表示されて、その下に再生ボタンが現れます。

flashcards-deluxe-mp3-download-004

再生ボタンを押すと、Google翻訳で作成した音声ファイルが再生されるようになりました。

出来上がりです。

まとめ

ちょっとマニアックな記事でしたでしょうか?

こういうのを作っている時が楽しいんですね。夢中になって作ってました。

これで出先のちょっとした時間でSpreadsheetを更新して、プログラムを動かせば、単語帳を増やせるようになりました。

GoogleのAPIにはこのほかに、カレンダー用のAPIもあるので、次はカレンダーの更新にチャレンジしてみたいと思います。

ゴールデンウィーク前半の成果でした。

ではっ!