Fortran＋FastAPIで数値解析webAPIを生やす

# Fortran＋FastAPIで数値解析webAPIを生やす

Fortranユーザの方々は主に科学技術計算を行うアプリケーションを実装されているかと思います．
アプリケーションをブラウザ/API経由で行えると，外部のアプリケーションとの連携ができて嬉しいです．
ここでは，FastAPIによりFortranにwebAPIを生やす方法を概説します．

### 1. Fortranの入出力をJSONに対応させ，コマンドライン引数でJSON形式ファイルを指定できるようにする．

WebAPI経由でFortranに入力データを受け渡す際には，JSON形式を使うと便利です．

```Fortran
use your_module
implicit none

type(Your_object) :: your_object
character(256) :: json_file_name

call get_command_argument(number=1,value=json_file_name)
your_object = your_object%initialize(json_file=json_file_name)

!!! call some operations !!!
call your_object%execute_something()

call your_object%export_json_file(json_flle=trim(json_file_name)//".result.json")

end
```
### 2. FastAPIでオンラインフォームを作成し，ユーザからの入力を受け付ける（必須ではない）．
(content headerにbootstrapを使用していますが，省略しています．)

```python

@app.get("/your_api/createjsonfile")
async def get_json_form():
    content = """
<h2>Online json editor </h2><br>
<form class="form-group" action="/your_api/download_new_json" method="get">

    <div class="col-auto">
        arg1 (integer) 
        <input name="arg1" type="text"  class="form-control" value="1">
    </div>

    <div class="col-auto">
        arg2 (real32)
        <input name="arg2" type="text"  class="form-control" value="1.00">
    </div>
...
    <div class="col-auto">
        Download from here!
        <input type="submit" value="Download">
    </div>

</form>

    """
    return HTMLResponse(content=content)


```

### 3. FastAPIでアクセスポイントを作成し，フォームから情報を受け取り保存する．

あらかじめ，working directoryでテンプレート（以下の```your_template.json```）を作成しておく．

```python
@app.get("/your_api/download_new_json/")
async def download_new_json(arg1: str, arg2: str):

    current = Path()
    filename = "your_template.json"
    file_path = current / filename
    
    with open(file_path, 'r') as fcc_file:
        fcc_data = json.load(fcc_file)
        fcc_data["arg1"] = int(arg1)
        fcc_data["arg2"] = float(arg2)
        
    new_filename = "download_"+str(uuid.uuid4())+"_bridge.json"
    f = open(new_filename,"w")
    f.write((json.dumps(fcc_data, indent=4)))
    f.close()

    file_path = current / new_filename
    response = FileResponse(
        path=file_path,
        filename=f"{new_filename}"
        )
    
    return response

```

### 4. FastAPIでjsonファイルのuploadを受け，ビルド済みの実行ファイルを呼び出し，処理を行い，jsonファイルをローカルに保存してから，結果ファイルのダウンロードページへを遷移する．

(Fortran側の結果ファイルの名前を，[入力ファイル名].result.jsonとしたことに留意)

```python

@app.post("/your_api/execute_fortran/")
async def create_upload_files(files: List[UploadFile] = File(...),
    ):
    for file in files:
        filename = 'uploaded_'+str(uuid.uuid4()) +'.json'
        f = open(filename, 'wb+')
        print(type(file.file) )
        fileobj = file.file
        shutil.copyfileobj(fileobj, f)
        f.close()
        sts = subprocess.Popen("./a.out " + filename , shell=True)
    content = """
<html>

<body>


<form class="row g-3" action="/your_api/downloadfile/" method="get">
    <div class="col-auto">
        <input type="hidden" name="filename" value="""+filename+".result.json"+ """>
        <input type="submit" class="btn btn-primary mb-2" value="Download output file as .json">
    </div>
</form>

</body>
</html>
    """    
    return HTMLResponse(content=content)



@app.get("/your_api/downloadfile")
async def get_file(filename: str):
    current = Path()
    if not filename.endswith(".json"):
        return {"status": "error"}
    file_path = current / filename
    
    response = FileResponse(
        path=file_path,
        filename=f"download_{filename}"
        )
    
    return response


```

# APIのデプロイ

こちらを参照してください．

https://zenn.dev/ibaraki/articles/1c5c70f4b83042

# 実装例

### [ボックスカルバート自動生成API](https://plantfem.org:5555/culvert_creator)

# 補足
上記の通り実装したAPIはあくまで簡易的なものです．
公開するためには，アクセス制御を行ったり，認証を実装したりするなどの工夫が要ります．
LANやVPN内での運用を推奨します．