> This post continues our series about deploying [machine learning](https://saturncloud.io/glossary/machine-learning) models with Saturn Cloud - if you missed it, [read Part 1 here](https://saturncloud.io/blog/deploy-ml-models-1/) and [Part 2 here](https://saturncloud.io/blog/deploy-ml-models-2/).

## Our Toolkit

- [Saturn Cloud](https://www.saturncloud.io/) (so you can deploy easily!)
- [Flask](https://flask.palletsprojects.com/en/2.0.x/)
- [Plotly](https://plotly.com/) ( [python](https://saturncloud.io/glossary/python) and JS)
- [Scikit-learn](https://scikit-learn.org/stable/index.html) (for our model)

#### See it in Action!

If you have a [Saturn Cloud account](https://www.saturncloud.io/docs/getting-started/signing_up/), you can [see the Flask web application](https://flask-webapp-deploy.community.saturnenterprise.io/) running live now, or [see the Flask REST API](https://flask-demo-api-deploy.community.saturnenterprise.io/).

## Flask

To build a deployment in Flask, we’ll be using [Python](https://saturncloud.io/glossary/python) scripts instead of the [Jupyter](https://saturncloud.io/glossary/Jupyter) notebooks that Voila uses. As a result, there’s more flexibility and possibility available — especially if you start integrating additional frontend frameworks. For this project, we have two choices about how to proceed:

- A bare REST API
- A web app (displaying an interface like we made with Voila in Part 2)

If we choose the web app, we’ll be developing with Python, HTML, CSS, and some Javascript.

## Application

As with Voila, we want to take the functions we wrote and put them into a script — instead of `.ipynb`, this will be just `.py`. The majority of our work can be contained in this Python script, which we’ll call `app.py`.

```python
def run_workflow():
    df = load_data()
    X, y = split_data(df)

modobj, modscore = trainmodel(X, y)
    return modobj, modscore, df
```

Now, inside our script, we initialize Flask, call our model function, and get everything set up.

```python
app = Flask(__name__)
modobj, modscore, df = train_model()
```

### Index

In the `app.py` script, we’ll have a function that creates this landing page.

```python
@app.route("/")
def index():
    return render_template('index.html',
    modscore = round(modscore,3),
    cred_list = creds,
    tuit_min = min(round(df['tuition'].astype(float), 1)),
    tuit_max = max(round(df['tuition'].astype(float), 1)),
    adm_min = min(df['ADM_RATE_ALL'].astype(float)),
    adm_max = max(df['ADM_RATE_ALL'].astype(float)))
```

What does the HTML template need to look like? It can start with the most basic HTML page outline.

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Demo Model Deployment</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='main.css') }}" />
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
  </head>
  <body>
    {% block content %}
	    <! -- This is where our dynamic content will appear -->
    {% endblock %}
  </body>
</html>
```

### Interactivity

If you want to make it possible for the user to submit options via the UI, then add a form and selectors to this page .

```html
<form action="/result">
  <div class="panel">
    <ul>
	   <li>Credential:
	       <select name='cred' method="GET" action="/">
	         {% for credtype in cred_list %}
	         <option value= "{{credtype}}" SELECTED>{{credtype}}</option>"
	         {% endfor %}
	       </select>
	   </li>
    </ul>
  </div>
  <div style="margin-bottom: 2rem;">
    <input type="submit" value="Submit" class="button"/>
    <input type="reset" value="Reset Values" class="button" />
  </div>
</form>
```

### Results

```python
@app.route('/result')
def result():
    variables = {'SAT_AVG_ALL':'sat',
                'CREDDESC':'cred',
                'CIPDESC_new':'cip',
                'CONTROL':'coltype',
                'REGION':'region',
                'tuition':'tuit',
                'LOCALE':'locale',
                'ADM_RATE_ALL':'adm',
        }
    for i in variables:
        if variables[i] in request.args:
            if variables[i]  == 'adm':
                variables[i] = request.args.get(variables[i], '', type= float)
            elif (variables[i] == 'tuit') | (variables[i] == 'sat'):
                variables[i] = request.args.get(variables[i], '', type= int)
            else:
                variables[i] = request.args.get(variables[i], '')
        else:
            return f"Error: No {variables[i]} field provided. Please specify {variables[i]}."

newdf = pd.DataFrame([variables])

[[prediction]] = modobj.predict(newdf)
    pred_final = prediction if prediction > 0 else np.nan
```

### REST API

```python
variables['predicted_earn'] = pred_final
return jsonify(variables)
```

### UI Endpoint

```python
p3 = plotly_hist(df=df, degreetype = variables['CREDDESC'],
         prediction=pred_final, majorfield=variables['CIPDESC_new'])
graphJSON = json.dumps(p3, cls=plotly.utils.PlotlyJSONEncoder)

return render_template(
    'result.html',
    pred_final = round(pred_final, 2),
    graphJSON=graphJSON
)
```

### Deploy

```python
if __name__ == "__main__":
  app.run(host='0.0.0.0', port=8000)
```

Our file structure will look something like this:

```fallback
flask_app/
├── app.py
├── templates/
│   ├── design.html
│   └── index.html
│   └── results.html
```

## Conclusion

With that, you have deployed a model! This technique can work for lots of different types of model, and if you’d like to try it on Saturn Cloud, we would love to have you join us. Check out our [getting started documentation](https://saturncloud.io/docs/) for more guides.
