UMLモデリングを通して、一歩一歩作ってみる 「UMLモデリング練習帳アプリ」#2 Flask適用


はじめに

UMLモデリングを通して、一歩一歩作ってみる 「UMLモデリング練習帳アプリ」第二回目です。
今回は、前回作ったものをWebアプリとして実装すること考えてみたいと思います。

前回はこちら
(https://qiita.com/tomohiro_odan/items/48a96141a2a96916f3b2)

作るものを考える

では、ざっくり作るものを考えましょう
前回は、CUIベースでplantUMLを利用した作成UMLモデルと解答UMLモデルを比較するプログラムを作成しました。
「でもなぁ…サービスなんだから、UMLモデル作成も含めて、Webブラウザ上でやりたいよねー。」
そんな要望きたということにします。
…ということで、今回はWebブラウザ上でできる仕組みを考えていきます。

概念

今回も、UMLモデル学習者がUMLモデルを作成し、与えられた問題に対して、作り切れたかを確認することを考えます。(詳細は前回...)

ユースケース

それでは、そういった概念の中でどこをUMLモデリング練習帳アプリの範囲として開発していくか考えます。
ユースケースを考えてみると、「UC1.UMLモデルを作成する」、「UC2.UMLモデルを確認する」の二つを考えていました。
今回は、考えたユースケースは、ブラウザ上で実施したいので、「UC1.UMLモデルを作成する」を新たに含め、どちらも「UMLモデリング練習帳アプリの範囲」としました。

ユースケースのシナリオ(シーケンス)

続いて、ユースケースのシナリオ(シーケンス)を検討していきます。

前回からの変更点は、「UMLモデルを作成する()」を「UMLモデリング練習帳アプリ」で行うことです。
また、それに伴い、ユースケースのシナリオとして、UMLモデリング練習帳アプリは、下記の二つの機能を追加で開発することになりそうです。

  • 今回追加で開発するUMLモデリング練習帳アプリ機能
    • UMLモデリング問題表示
    • UMLモデル記述

ユースケースと機能

先ほどの分析結果からユースケースとアプリの機能の対応は以下となりました。

作るものを決めよう

さて、作るべき機能が明確になったので作るものを決めていきましょう。
Pythonを開発言語としていますが、今回はWebブラウザ上のアプリを想定しています。
そのため、配置や実装に加えて、画面について考える必要がありそうです。
今回は、簡素で使いやすいflaskを利用することにしました。

画面

遷移

今回は単純な1つの画面(index.html)だけで実現することを考えることにしました。

イメージ

画面では、UMLモデル記述用の一つのテキストフィールドとモデル確認用のボタンを持つものを考えました。
結果を受けて、最初は何も表示されていませんが、解答結果表示を行う想定です。
(問題文表示を書き忘れてますが、上部に表示する想定です…)

配置

前回は、main.pyというファイルに全て作成していましたが、今回は、server.pyというファイルに機能を実装することにしました。
UMLモデリング学習者は、Google ChromeなどのウェブブラウザでそのURLにアクセスします。そのため、server.pyは、'localhost:8000'にアクセスできるようにします。
モデル確認ボタンを押すと、画面からUMLモデル記述が書かれたテキストエリアの内容を読み取り、解答UMLモデルスクリプトの記述と比較し、UMLモデルの画像ファイルを出力し画面上に表示します。
また、今回から構築が容易なCentos7上で動作を想定としました。


※→は、「参照や呼び出し元→参照や呼び出し先」

[フォルダ構成]
server.py(「UMLモデリング練習帳アプリ」サーバースクリプト)
|_static(画像、CSS、Javascriptなどの静的ファイル置き場)
|    |_plantuml.png(ダミーUMLモデル画像)
|    |_forminput.png(出力UMLモデル画像)
|
|_templates(HTMLのテンプレート置き場)
|    |_index.html(トップページhtml)
|
|_answers(解答UMLモデルスクリプト置き場)
|    |_usecase001a.pu(解答UMLモデルスクリプト)
|
|_run_plantuml.sh(UMLモデル画像の出力用shellファイル)
|_forminut.pu(出力UMLモデルスクリプト)

実装

それでは実装していきます。
今回もかなりべた書き…。

[server.py]
#! /usr/bin/env python
# -*- coding: utf-8 -*-

from flask import Flask, render_template, request
import subprocess
import sys
import difflib as diff

app = Flask(__name__)

# キャッシュをなくす(入れないと画像が古いままになる)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0

# 最初のページ
@app.route('/', methods=['GET'])
def index():
    return render_template('index.html', \
      image_pass = "/static/plantumlimage.png" )

# UC2.UMLモデルを確認する
# モデル確認を実行する
@app.route('/', methods=['POST'])
def model_conform():

    # UMLモデル解析
    # plantumlファイル作成
    file = "forminput.pu"
    fileobj = open(file, "w", encoding = "utf_8")
    fileobj.write("@startuml{static/forminput.png}\n") 
    fileobj.write(str(request.form['modelingform'])+"\n")
    fileobj.write("@enduml\n")   
    fileobj.close()

    # 差分出力
    with open("./forminput.pu","r") as f:
        submit = f.readlines()

    with open("./answers/usecase0001a.pu","r") as f:
        answer = f.readlines()

    # 差分をunified形式で取得
    difflist = list(diff.unified_diff(submit, answer, fromfile=str("submit model"), tofile=str("answer model")))

    # 確認結果を表示
    if len(difflist) == 0 :
        result = "確認結果:OK"
    else :
        result = "確認結果:NG"

    # png画像出力
    subprocess.Popen(["pwd"], shell=True)
    completed_process = subprocess.run(["./run_plantuml.sh"], shell=True)

    # 解答結果表示   
    return render_template('index.html', \
      image_pass = "/static/forminput.png", \
      check_result = result, \
      diffs= difflist)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)
[index.html]
<!-- index.html -->
<!DOCTYPE html>
<html lang="ja">
  <head>
  </head>
  <body>

    <h1>UMLモデリング練習帳アプリfrom index.html</h1>

    <label for="title">問題①</label><br>
    <p>下記の文章からユースケース図を作成しなさい。</p>
    <p>UMLモデル学習者は、UMLモデルを作成する。</p>

    <form action="/" method="POST">
      <textarea name="modelingform" id="modelingform">{{ request.form['modelingform'] }}</textarea><br>
      <input type="submit" value="モデル確認"><br>
    </form>

    <p><img src={{ image_pass }} ></p>

    <p>{{ check_result }}</p>

    {% for diff in diffs %}
      <p>{{ diff }}</p>
    {% endfor %} 

  </body>
</html>
[usecase0001a.pu]
@startuml{static/forminput.png}
usecase UC1.UMLモデルを作成する
UMLモデリング学習者 --> (UC1.UMLモデルを作成する)
@enduml
[run_plantuml.sh]
前回と同様のため省略。
[forminput.pu]
初期は、何も記載されていないので省略。

動きをみてみよう

実装し終えたので、今度は動作を確認しましょう。

実行結果

plantuml,Graphvizは利用できるように環境変数を登録しておきます。
アプリを起動し、動作を確認してみましょう。

$ export FLASK_APP=server.py
$ flask run --host=0.0.0.0 --port=8000

出力結果

確認結果がOKの場合

画像が古いやつですが、イメージ的に下記のような内容になります。差がない場合は、「確認結果:OK」のみが表示されます。

確認結果がNGの場合

「確認結果:NG」と提出したUMLモデルスクリプトと解答UMLモデルスクリプトとの差分が表示されます。
ちなみに、前回は補足できていませんでしたが、差分はunified形式で行の差分が出力されます。

以上で、確認結果が出力できることを確認できました。

次回に向けて

今回はwebブラウザで実現できるようにしてみました。なんとなくアプリらしくなってきました。
しかし…

「これだと1問しかできないじゃん。出題者が問題を登録したりできるようにしたいよ。」

と言われてしましました。

ということで次回は、UMLモデリング出題者というアクターが新たに登場し、問題を登録・削除・編集できるようにすることに取り組んでいきます。

参考文献

以上。