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

# はじめに

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

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

# 作るものを考える

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

## 概念

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

![conceptual model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/e4ae8f0e-75c0-1ea9-56d5-22de49adf0bc.png)

## ユースケース

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

![usecase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/1abda662-bf8c-5862-a236-aa7c39c864bf.png)


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

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

![UMLモデリング練習帳 シーケンス図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/bd0620dc-f199-8896-421d-3686886cf014.png)

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

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

## ユースケースと機能

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

<img width="569" alt="SnapCrab_NoName_2020-3-1_23-25-13_No-00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/324872f5-1719-c2da-2090-1843a5c5f003.png">

# 作るものを決めよう

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

## 画面

### 遷移

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

![screen_statemachine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/b8a60182-1f64-893a-5b0d-4838b2e3fe83.png)

### イメージ

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

<img width="397" alt="index.html.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/f160f523-270b-c292-21ef-d9c86aae400f.png">


## 配置

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

![deployment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/58b9e55c-4472-d864-45ce-0c64523cd3c2.png)
※→は、「参照や呼び出し元→参照や呼び出し先」

```
[フォルダ構成]
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」のみが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/5a244820-7b32-2816-21fe-3e3411bb393f.png)


#### 確認結果がNGの場合

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/220327/68ef95f1-e3ff-9923-36ee-0a5ac682ea76.png)

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

# 次回に向けて

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

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

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

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

# 参考文献

* plantUML
  * https://plantuml.com/ja/

以上。