1 of 141

2 of 141

Module 7: Open Source Collaborative Development

Session 7C - Further Web Development with Streamlit

Sammi Rosser

“All That Jazz”

11th June 2024

facility location problems

3 of 141

Housekeeping

  • Registering your projects
    • No project registered = no support…�
  • Impact
    • https://github.com/hsma-chief-elf/PIMS
    • If it’s something you did with the skills you learned on HSMA, we want to know!�
  • Please welcome Jemma!�
  • Hackathon

4 of 141

Recap

Last week we built a couple of Streamlit apps�

  • A calculator that takes user inputs and outputs some values
    • Maybe you have a simple calculator that exists in an Excel spreadsheet

    • Creating a streamlit app
      • removes the risk of people having old versions of the spreadsheet
      • Avoids people breaking it by inputting values incorrectly
      • And allows you to better constrain things, respond to outputs, incorporate images and
      • Instantly makes it multi-platform�

5 of 141

Recap

Last week we built a couple of Streamlit apps�

  • A tool that takes a range of user inputs and uploaded files and does something you couldn’t do without Python
    • Our app was focussed around wordclouds
    • But maybe we want to do something else Python specific, like give people an easier way to use a machine learning model we’ve trained
      • remember this from ARM in the NLP hackathon?

6 of 141

Recap

Last week we built a couple of Streamlit apps�

  • A dashboard that allows you to leverage Python’s data visualisation and mapping capabilities
    • This can allow us to replace non-FOSS apps with solutions with auditable code
    • Also can be reused and adapted more easily, or code shared across multiple apps�
  • Even if you’re not using Streamlit for dashboards, being able to arrange and display graphs, maps and metrics are a big part of many apps

7 of 141

Plan for this week

We’re going to mainly focus on building an app for running a discrete event simulation

A lot of projects go down a DES route

Web apps are a very popular way to allow stakeholders to interact with these models

The principles translate well to a wide range of the techniques we’ve covered on the course!�

8 of 141

Plan for this week

They also allow us to practice�

    • translating more complex code into a Streamlit app �
    • a range of more advanced concepts
      • Preventing the app running every time an input changes
      • Multipage apps
      • Session state
      • Caching
      • Partial reruns
      • Memory management�
    • deploying apps to the web

9 of 141

Streamlit Recap

Let’s recap the fundamentals of building a Streamlit app.

Time to switch to VSCode!

10 of 141

Next Steps: A DES App

So now we hopefully remember the key parts of building and running a Streamlit app, we want to start thinking about how we can join this up with a DES model.

What might our output look like?

11 of 141

What we’re aiming for

Let’s remind ourselves of what a DES app might look like.

12 of 141

What we’re aiming for

We can provide users with a range of outputs.

13 of 141

What we’re aiming for

And we can start to build up to avoiding overwhelm while giving power users more control over the inputs...

14 of 141

What we’re aiming for

And the level of output they are comfortable with.

15 of 141

What we’re aiming for

16 of 141

DES Recap

We will need to replace some of the inputs to our DES model with sliders, dropdowns and more.

We’ll also want to remind ourselves what we get out of our Streamlit models so we can provide some tables or graphs that our users could view and download.

So let’s go back several months and remind ourselves what precisely DES is and how a DES model is structured.

17 of 141

What is Discrete Event Simulation?

Discrete Event Simulation (DES) is a way of modelling �queueing problems

Healthcare in particular involves a lot of queuing problems!

  • Diagnosis Pathways
  • Emergency Departments
  • Mental Health Treatment

18 of 141

What is Discrete Event Simulation?

Entities

👨🏽‍⚕️👨🏻‍⚕️👩🏿‍⚕️👩🏼‍⚕️🛏️🛏️🛏️

🤒😷🤕🤧🤢

flow through a pathway

using resources along the way

Arrive

Sign in with receptionist

Triaged

See nurse

See doctor

Additional Tests

Discharge

19 of 141

Coding a DES: SimPy

SimPy is a Python package that allows us to create powerful Discrete Event Simulation (DES) models.

It provides a series of building blocks for us to put together into highly customised discrete event simulations.

  • Simulation environment
  • Resources

20 of 141

Structuring Simpy Models

There are multiple ways to structure a simpy model!

It’s a good idea to use object-oriented programming (OOP).�

  • You can do simpy without OOP
    • But more complex models start getting very messy very quickly.�

21 of 141

Classes

We strongly recommend structuring SimPy models in an Object Oriented way. Specifically, we’re going to have 4 different classes.

g Class <- our PARAMETERS

This is a special class that will store our global level parameters for the model.

Unlike most OOP cases, we won’t create an instance of this class (hence the lower case) - we’ll just refer to the blueprint directly.

Model <- our PATHWAYS

This is the big one that represents the system that we are modelling (our clinic in this case).

It’ll have our generators, our patient journeys and more, and it’s where our SimPy environment (where everything lives) will be kept.

We’ll also have a method for one run of the model.

Patient <- our ENTITY

This class will represent our entity - patients.

Patients will carry with them information that we can record to and / or read from (e.g. an ID, how long they spent queuing, their condition etc).

If we had more than one entity, we’d need a class for each.

Trial <- what we run

This class will represent a batch of runs of our simulation, and will have methods to run a trial, extract results etc.

22 of 141

Runs and Trials

A single run of a model for a simulated period of time is known as a run.

A batch of multiple runs with the same parameter values is known as a trial.

23 of 141

The g Class

When making a Streamlit app of your DES model, most of the changes to your DES code will be in the g class.

You can basically leave the other three classes untouched!

Let’s head over to the code and take a look at how we’d adapt a model.

g Class

This is a special class that will store our global level parameters for the model.

24 of 141

The g Class

Here’s our original g class.

It looks like we need to make use of some number inputs or some sliders.

25 of 141

The g Class

We will create an input for every parameter - here are two examples.

Before

After

Now we can change how our g class is written.

26 of 141

st.sidebar

We could put this in a column of our app - but a sidebar can be a nice way of organising inputs too.�https://bergam0t.github.io/streamlit_book/layout_sidebar.html

with st.sidebar:

st.header("I'm a sidebar")

st.write("We can use inputs our sidebar too.")

name = st.text_input("What's your name?", value=None)

Once we write a line of code that is not indented, this signals the beginning of code that will just appear in the main area of the streamlit app.

27 of 141

User Inputs: Common Examples

Note that the variable names below are just examples - you can use any variable name that makes sense for your app.

my_number = st.number_input(“Enter a number”)

my_short_text = st.text_input(“Enter your name”)

my_long_text = st.text_area(“Enter a story”)

my_slider = st.slider(“Choose a number between 1 and 100”)

my_selection = st.selectbox(“Select an option from the list”, [“Red”, “Blue”, “Green”])

Many widgets have multiple additional options like minimum and maximum values or default values - take a look at the Streamlit documentation.

https://docs.streamlit.io/develop/api-reference/widgets

The streamlit book also covers several key input types.

https://bergam0t.github.io/streamlit_book/text_numeric_and_selection_inputs.html

28 of 141

Displaying outputs

We can then use a range of different outputs to display tables and plots.

The main ones will be

st.dataframe()

st.plotly_chart()

st.metric()

29 of 141

Loading and displaying a dataframe

Dataframes are then just displayed with

st.dataframe(my_df)

If you want it to use the full space, use

st.dataframe(my_df, use_container_width=True)

30 of 141

Altering how the dataframe columns are displayed

We use the ‘column API’ to adjust the way column names and values are displayed.

You can use this for as many or as few columns as you like.

Check the documentation to see all available column types: https://docs.streamlit.io/develop/api-reference/data/st.column_config

31 of 141

Displaying charts

Charts can be built in the normal way, then displayed using the various streamlit functions.

e.g.

Plotly plots: st.plotly_plot(your_plotly_fig)

https://bergam0t.github.io/streamlit_book/interactive_charts.html

32 of 141

Displaying metrics

Metrics allow you to display a value and an optional comparison.

https://bergam0t.github.io/streamlit_book/metrics.html

33 of 141

Arranging your dashboard using columns

For columns, you need to use the st.columns() function.

https://bergam0t.github.io/streamlit_book/layout_columns.html

�Inside the bracket, put the number of columns you want.

On the left of your equals, create the same number of variables as the number of columns you have asked streamlit for.

E.g.

col1, col2, col3 = st.columns(3)

col_a, col_b = st.columns(2)

Indent code to make it appear inside a given column.

with col1:

st.dataframe(my_dataframe)

with col2:

st.pyplot(fig)

34 of 141

Arranging your dashboard using tabs

For tabs, you need to use the st.tabs() function.

https://bergam0t.github.io/streamlit_book/layout_tabs.html

�Inside the bracket, put a list of as many strings as you want tabs.

On the left of your equals, create the same number of variables as the number of tabs you have asked streamlit for.

E.g.

tab1, tab2, tab3 = st.tabs([“My Graph”, “My Map”, “My dataframe”])

tab_a, tab_b = st.tabs([“Thing 1”, “Thing 2”])

Indent code to make it appear inside a given tab.

with tab1:

st.dataframe(my_dataframe)

with tab2:

st.pyplot(fig)

35 of 141

Arranging your dashboard using expanders

For collapsible sections, you need to use the st.expander() function.

https://bergam0t.github.io/streamlit_book/layout_expander.html

�Indent code to make it appear inside a given expander, passing the expander function the wording you want to display when the expander is in its collapsed state.

with st.expander(“Click to view the hidden dataframe”):

st.dataframe(my_dataframe)

36 of 141

Letting your dashboard use the full page width

As the first thing in your app after your initial library imports, use the st.set_page_config() function.

To this, pass the layout=”wide”

import streamlit as st

import pandas as pd

st.set_page_config(layout=”wide”)

st.title(“My app’s title”)

df = pd.read_csv(“my_df.csv”)

st.write(df)

etc.

37 of 141

Downloading dataframes

Dataframes can be downloaded in the following way.

https://bergam0t.github.io/streamlit_book/file_downloads_tabular.html

For a dataframe my_df:

st.download_button(

"Click here to download the dataframe as a csv file",

data=my_df.to_csv(index=False).encode('utf-8'),

file_name=f"output.csv",

mime="text/csv"

)

38 of 141

Downloading charts

Downloading charts uses a very similar syntax, but the layout varies slightly for different file types.

Interactive charts (plotly): https://bergam0t.github.io/streamlit_book/file_downloads_images_charts.html#plotly

st.plotly_chart(fig)

fig.write_html("plotly_chart.html")

with open("plotly_chart.html", "rb") as file:

st.download_button(

label='Download This Plot as an Interactive HTML file',

data=file,

file_name='my_plotly_plot.html',

mime='text/html'

)

39 of 141

Exercise 1: Fulfil your DEStiny

You’ve been provided with the more advanced version of the discrete event simulation from session 2B and your task is to create an app for running this.

I’ve updated this code slightly to add in some extra logging steps that will give you more potential for plots.

Before you start trying to adapt the code, run the provided code in an interactive window and take a look at the outputs it prints.

40 of 141

Exercise 1: Fulfil your DEStiny

Here is a diagram of the model!

41 of 141

Exercise 1: Fulfil your DEStiny

Adapt this code into a Streamlit app.

(NOTE - for now I’d recommend copying the DES code into your app or just adapting the code file into a Streamlit app. Importing the individual classes is a little more complex and covered in the next part!)

Your app should allow people to change

  • The patient inter-arrival time
  • The call inter-arrival time
  • The mean activity times for registration, GP consults, booking tests and calls
  • The number of receptionists and GPs
  • The probability of booking tests
  • The simulation duration (in minutes or days)
  • The number of simulation runs

You can place these parameters anywhere (e.g. main screen, sidebar).

It should then display some (or all) of the charts and summary values from the original file

42 of 141

Exercise 1: Fulfil your DEStiny

You can also explore some additional ways to enhance your app.

  • Make use of columns, tabs, the sidebar, expanders, headings, subheadings and dividers to change how your inputs and outputs are laid out. �
  • Create some additional plots in plotly express (or your preferred plotting library), or enhance the given plots
    • Try thinking about how you might indicate to users what is a ‘good’ model run or a ‘bad’ model run�
  • Add buttons to download each table as a csv
    • Note that there’s a limitation of streamlit that will result in your app reloading each time you click the button - we’ll demonstrate a new feature that also helps fix this issue in the next part!

43 of 141

Part 2

Now we’re going to take a look at some features that will help you organise larger apps, as well as making them look how you want them to!

  • Controlling app execution with a run button

  • Splitting out streamlit app code from your model code

  • Multipage apps

  • Changing App Colours

  • Logos

  • Advanced Theming with custom CSS

44 of 141

Run Buttons

We can prevent the app from running until we have finished changing all our parameters!

The first line makes a button.

The next lines then will only execute when the user presses the button.

45 of 141

Good Organisation Habits

It’s a good idea to try and separate your code out.

It’s easier to keep track of what’s going on (for you and other people!)

Let’s take a look at doing that.

46 of 141

Good Organisation Habits

Here I’ve split my code into

app.py

and

des_classes.py

I’ve also created a blank file

called �__init__.py �that is important for allowing us to import functions and classes from one file into another.

You don’t need to put anything at all in the __init__.py file - it’s just so Python understands that there’s files in this folder that can be imported from.

47 of 141

Good Organisation Habits

In my des_classes.py file I have the simpy, pandas and random imports.

I might also have additional packages like re and numpy here in more complex simulation models.

It defines the following classes:

  • g
  • Patient
  • Model
  • Trial

In this case, I put the code for the plots into the app (user interface) file discussed on the next slide - though I could split those out into functions in their own file too!

48 of 141

Good Organisation Habits

In my app.py file I import streamlit and anything else I use specifically in this file, like plotly and pandas.

Then I import the g and Trial classes from our new file.

Notice I don’t need to use .py after the file name when importing.

Now instead of setting up the g class using the variables relating to our inputs, I instead overwrite the values of the g class.

49 of 141

Module 7: Open Source Collaborative Development

Session 7C - Further Web Development with Streamlit

Sammi Rosser

“All That Jazz”

50 of 141

Multipage Apps

As your app becomes bigger and more complex, it can be beneficial to split it out into multiple pages.

51 of 141

Multipage Apps

Each page of a multipage app can be quite separate from the others.

This is good as our apps get more complex - it can separate out things we don’t want to run at the same time.

Used well, it can also make it easier for our users to interact with the app and find what they’re looking for.

The main downside is that you can’t just create a variable on one page and use it on another

  • Passing data between pages is very doable, but it does require a few extra steps that we’ll go into later.

52 of 141

Multipage Apps

There are a few supported ways to set up multipage apps.

We’re going to look at Streamlit’s favoured way, which has the benefits of being very flexible.

Note that some of Streamlit’s documentation uses the old method, which is still fine but looks a bit different from this!

  1. We set up each page of our app as a separate .py file�
  2. We set up an additional page that won’t be displayed itself, but handles the order of pages in our sidebar and the moving of users around the pages. ��We’ll generally call this app.py �(though we can call it anything!)�
  3. When running the app, we run �streamlit run app.py

53 of 141

Multipage Apps: Example app.py

The key parts of your app.py are

  • st.navigation
    • This will contain a list of st.page objects in the order you want them to appear in the sidebar, along with the title you want them to have�
  • The output of st.navigation needs to be save to a variable, and we then call the .run() method of this variable

54 of 141

Multipage Apps: Icons

We can also use an optional ‘icon’ parameter to add an icon into the navigation bar for some of all of our pages.

Just replace the bit after the / with the relevant name from here: https://mui.com/material-ui/material-icons/

55 of 141

Theming with config.toml

There are a few parts of Streamlit’s theming that we can officially change using a configuration file.

This file needs to be called config.toml and lives inside a subfolder called .streamlit

The config.toml file contains a variable number of parameters.

It can determine whether the default colourscheme is light or dark, and whether the default streamlit colours are overridden.

56 of 141

Populating config.toml

You can create a template config.toml from within streamlit, then paste the output into a config.toml file you create yourself.

57 of 141

Logos

Streamlit has basic support for small logos in your app.

It will look for a file you specify relative to the currently running streamlit file

  • here, for example, it looks for a folder called ‘resources’ and then looks for the ‘hsma_logo.png’ file

58 of 141

Logos

The logo will appear in the top left of your app.

If it’s a multi page app, it will appear at the top of the sidebar.

Ideally, your logo should be close to 24 pixels tall by 240 pixels wide.

59 of 141

Logos

It is possible to include larger logos through more advanced tricks, but they are fragile and prone to breaking as changes are made to the Streamlit library.

If you want to experiment with this yourself, take a look at the add_logo() function in this file: https://github.com/hsma-programme/Teaching_DES_Concepts_Streamlit/blob/main/helper_functions.py

60 of 141

Custom CSS

To customise the look of your app much more than this, we need to turn to custom css.

CSS stands for ‘cascading style sheets’ and is a language used for theming much of the web!

We can create our own .css file and force Streamlit to load it in.

61 of 141

Custom CSS

What does the .css file look like??

62 of 141

Custom CSS - Result

Without style.css imported

With style.css imported

63 of 141

Exercise 2: DEStiny’s Child

It’s time to enhance our DES app a bit! We’ll work on this for 25 minutes.

Take a copy of your original version first and store it somewhere safe!

Do these steps in the order listed, and take the time to make sure it’s working after each step.

  • Add a run button (slide 44)�
  • Split your code out into separate files for the web app and the DES classes, then import the DES classes where you need them and update how the g class values are set�(slides 45-48)

  • Turn your app into a multipage app (slides 50-54)
    • Make the first page give a brief explanation of what the app actually is
    • Make the second page allow people to run the simulation

Bonus:

  • Create a custom colourscheme for your app using a config.toml file in a .streamlit subfolder (slides 55-56)

  • Add the HSMA logo to your app - it’s provided in the exercise_2 folder (slides 57-59)

  • Import the file style.css into your app to change the font of your app (slides 60-62)

64 of 141

Part 3

In the next section, we’re going to tackle some advanced features that will help you as your app starts to become more complex.

Caching

    • This helps increase the speed of your app

Session State

    • This helps us manage information across multiple app runs and pages

Callbacks

    • This is another way in which to trigger changes to session state or other parts of the app in response to user input

Partial Reruns (Fragments)

    • This prevents your whole app rerunning in response to a user interaction

65 of 141

Caching

We often want to load data files into Streamlit.

There are two main issues we might run into.

  1. Each time you rerun something in your app (e.g. filtering a dataframe by using a drop-down select box), then the code to load the dataframe gets run again too.
    • This can make your app feel really sluggish with larger data files�
  2. When we reach the stage of deploying our app to the web, we have to be a bit more conscious of how much memory our app is using when being accessed by multiple people simultaneously
    • By default, the data will be loaded in separately for each user - even though it’s identical
    • This can quickly lead to memory requirements ballooning (and your app crashing!)

66 of 141

Caching

By using the

@st.cache_data

decorator, Streamlit will intelligently handle the loading in of datasets to minimize unnecessary reloads and memory use.

To switch from doing a standard data import to using caching, we

  • Turn our data import into a function that returns the data
  • Add the @st.cache_data decorator directly above the function
  • Call the function in our code to load the data in, assigning the output (the dataframe) to a variable

We can then just use our dataframe like normal - Streamlit will handle the awkward bits.

67 of 141

Caching

Without Caching

Loading in the same dataset with caching

68 of 141

Caching Types: Data vs Resources

Caching can also be used for other large files - for example, if you wanted to load in a trained machine learning model that’s the same for all users who will interact with your app.

As a general rule:

@st.cache_data

@st.cache_resource

  • Trained machine learning models�
  • Database connections�
  • Some charts
  • Pandas dataframes�
  • Strings, integers, floats�
  • Most charts

https://docs.streamlit.io/develop/concepts/architecture/caching#deciding-which-caching-decorator-to-use

It’s not always obvious which to use, so head to the Streamlit documentation if you’re a bit unsure.

69 of 141

More Advanced Caching

While using caching for the initial load of a dataframe, it can also make sense to use it for other long-running functions.

Like normal functions, functions for caching can accept parameters.

Streamlit will be able to look at the parameters passed in and tell whether it should �

  • use a cached version of the data (because the parameters are the same as a previous instance that’s already in its cache) �
  • run the function (because it’s a new set of parameters that it doesn’t already have a saved output for in its cache)

* There are a few complexities around parameters that you may want to look into if using this in your own app: https://docs.streamlit.io/develop/concepts/architecture/caching#excluding-input-parameters

70 of 141

Caching Settings: TTL

When you choose to cache something, you can add in some additional settings to make the cache as effective as possible for your particular situation.

ttl (time to live)

TTL determines how long to keep cached data before rerunning it.

For example, if data is likely to change over time, you could set the ttl parameter to set the number of seconds to keep cached data for.

@st.cache_data(ttl=3600)

def your_function_here():

This also prevents your cache from becoming very large.

71 of 141

Caching Settings: max_entries

When you choose to cache something, you can add in some additional settings to make the cache as effective as possible for your particular situation.

max_entries

Once the cache contains the maximum number of objects you have specified, the oldest ones will be deleted to make way for new ones.

@st.cache_data(max_entries=10)

def your_function_here():

This also prevents your cache from becoming very large.

72 of 141

Session State

One limitation we’ve mentioned of multipage apps is that variables do not persist across the different pages.

Once you switch pages, all of that information is lost!

However - you can use session state to remember things across multiple pages or multiple runs of the same app.

It doesn’t help with remembering information when leaving or fully reloading the page.

73 of 141

Session State - Initialisation

First, we need to initialise the variable with a default value if it doesn’t already exist.

You need to do this on every page of your app where you will do either of the following:

  • Update the value
  • Display the value

74 of 141

Session State - Setting

Session state can be used to store a range of things.

It can store the input given in a user input widget:

Or the result of a calculation (whether that’s a number, a dataframe, a graph, or something else)

The thing you’re saving to the state could itself optionally use something stored in the session state!

75 of 141

Session State - Using

You can then access the session state key regardless of the page you are using it on!

Just remember - you need to check for whether the key exists in the session state anywhere you are setting or using the key, and set a default value for it if it doesn’t already exist.

When your app is deployed, a user could open the app on a page that means they are trying to view the stored session state key before they’ve had a chance to actually input a value - so think about how you could use conditional logic (if/elif/else) to handle this gracefully.

76 of 141

Callbacks

Callbacks are custom functions that will execute when a user interacts with an input.

They are often used in conjunction with session state.

For example - we could increment a counter every time the user clicks on a button.

In standard Streamlit, this would be impossible because it would forget that you clicked the button every time you did so!

Callbacks + session state get around that problem.

77 of 141

Callbacks - Code

To begin, we initialise our session state variable like before.

Next, we define our callback function.

We associate the callback with the relevant input, using the ‘on_click’ or ‘on_change’ parameter as appropriate.

Finally, we use our session state variable as we want to!

78 of 141

Session State and Callbacks

While the ways I’ve mentioned here should work in most simple cases, you may want to take a look into the documentation to fully get your head around the power of state and callbacks.

https://docs.streamlit.io/develop/api-reference/caching-and-state/st.session_state

* It’s technically better practice to use the on_change callback if you’re storing the value of an input widget in session state - but just assigning it directly works in most cases

79 of 141

Partial Reruns (fragments)

Partial reruns are a fairly new feature in Streamlit that allow you to break out of the traditional top-to-bottom rerun pattern of a Streamlit app.

It requires you to group together the inputs that should affect a given output into a single function, adding the @st.fragment decorator.

You then just call this function in your code to add in all of the relevant bits, and Streamlit handles the running!

80 of 141

Partial Reruns (fragments)

Here we have an example without the use of partial reruns.

The whole app reruns every time you change an input, even though the inputs only affect the graph directly underneath them.

81 of 141

Partial Reruns (fragments)

Here, we’ve split the two sets of inputs and outputs into two separate functions with the @st.fragment decorator. Notice how only half the screen fades out when an input is used!

82 of 141

Partial Reruns (fragments)

Function 1

@st.fragment

def this_bar_chart():

species = st.selectbox(...)

st.plotly_chart(...)

with col_1:

this_bar_chart()

Function 2

@st.fragment

def this_scatterplot():

input_1 = st.selectbox(...)

input_2 = st.selectbox(...)

input_3 = st.selectbox(...)

st.plotly_chart(...)

with col_2:

this_scatterplot()

83 of 141

Exercise 3: DESpite Everything, It’s Still You

Let’s now make our app really powerful! We’re going to work with a new Streamlit app that works out the estimated demand for an area given its demographic features*, make it more efficient, and then incorporate it into our main app.

We’ll first take a look at the new Streamlit app we’re going to work with.�

* this is just a dummy app to show what’s possible - it’s not using a proper method to estimate the demand!

84 of 141

Exercise 3: DESpite Everything, It’s Still You

  • The LSOA demographics file that new app loads in is really big!
    • Switch to loading it in using the @st.cache_data decorator (slides 65-67)�
  • There’s a long-running calculation in the second half of that page that isn’t anything to do with the map.
    • Use the @st.fragment decorator so that changing the parameters of the map doesn’t trigger this calculation to rerun (slides 79-82)�
  • Add this new app as an extra page in your multipage DES app from the previous exercise (slides 50-54)�
  • Use session state to save the caller and patient IAT figures from that new page (slides 72-75)
    • then remove the ability for the user to specify the IAT for callers and patients
    • replace the IAT used for the simulation with the IAT that you saved into the session state

�Challenge Activity

Try saving some key outputs from each model run to the session state and use this to display a comparison of the outputs across these multiple runs�

85 of 141

Deployment

Now it’s time for the final step

(of the first iteration of your tool)

Getting it out there so other people can use it!

86 of 141

Deployment Methods

There are a couple of key routes you might be interested in for Streamlit app deployment

  • Streamlit community cloud
  • Stlite�
  • Cloud platforms (e.g. Heroku, Azure)�
  • Enterprise platforms (Posit Cloud, Plotly Dash Enterprise)

We’ll focus on this today

87 of 141

How Does a Deployed Web App Work?

88 of 141

Are you being served?

“A server is a computer or system that provides resources, data, services, or programs to other computers, known as clients, over a network.”

Any computer that’s connected to an intranet or the internet can act as a server - they may just vary in how good they are at the job and how exactly they go about it!

89 of 141

Are you being served?

https://commons.wikimedia.org/wiki/File:A_view_of_the_server_room_at_The_National_Archives.jpg

https://en.wikipedia.org/wiki/Mini_PC#/media/File:2009_Taipei_IT_Month_Day1_Acer_Aspire_Revo.jpg

It doesn’t just have to be sitting in a big server room that your organisation may (or may not) have…

Or even a microcomputer…

It can be any old computer…

90 of 141

Are you being served?

Or maybe it’s on someone else’s server, running in its own little virtual machine.

91 of 141

Streamlit Community Cloud

Streamlit community cloud is a completely free hosting platform that’s great for getting some apps out there.

If your app uses data that is not sensitive, then community cloud is often the easiest option available to you.

92 of 141

Streamlit Community Cloud

Any app that is hosted on github can be deployed in just a few clicks.

It gets its own customisable, persistent URL that can be shared with users.

93 of 141

Streamlit Community Cloud

There are a few limitations:

  • The little virtual computer your app runs in only gets 1gb of RAM
    • For more memory-hungry apps, or apps where a lot of people will use it simultaneously, that can be a problem�
  • If no-one uses your app for a few days, it will ‘go to sleep’
    • This just means it might take a minute or so to ‘wake up’ the first time someone uses it in a while�
  • All apps are hosted in the USA - there’s no way you can get around this at present (September 2024)

94 of 141

Streamlit Community Cloud

You might be able to publish more than you think!

Talk to people in your organisation about how sensitive the data actually is.

Are the default parameters for a DES actually sensitive?

What about travel time data at LSOA level?

95 of 141

Streamlit Community Cloud

https://efit-tool.streamlit.app/?utm_medium=oembed

96 of 141

Streamlit Community Cloud

https://hsma4cardiac.streamlit.app/

97 of 141

Streamlit Community Cloud

IT’S ALWAYS BEST TO ASK - don’t get yourself in trouble!

DONE

DONE

98 of 141

Requirements and Python Versions

  • If your repository contains a requirements.txt, this will be noticed by Streamlit Community Cloud and used to set up your environment when building your app�
  • When first uploading your app you’ll get the option (in the advanced settings) to define the version of python
    • Once the app is deployed, this can’t be changed; you would have to delete and redeploy

99 of 141

Local Hosting

  • If you need to deploy locally, you probably want to make friends with someone in IT or data engineering

  • At a minimum, you need a computer that’s always on that can have a Python process always running and is only accessible on an internal network
    • That’s not very robust, but it might be enough for a proof of concept
    • Prepare yourself for some babysitting…
    • You’ll probably need some help dealing with ports�

100 of 141

Docker

  • A better next step would be Docker
    • But we’ll wait until a masterclass for that…

101 of 141

Bringing it closer to home

  • In recent years, Pyodide was released and opened up a new world of opportunities for web apps
    • It’s a web-based version of Python that runs Python code within a browser�
  • Instead of requiring a server that does the computation for each user, we instead just need a server to send out the scripts, and each user’s browser does the hard work�
  • This has a couple of benefits
    • Less server power needed as concurrent users aren’t such a burden
    • Data doesn’t leave the user’s browser

102 of 141

Pyodide: Benefits and Downsides

  • This has a couple of benefits
    • Less server power needed as concurrent users aren’t such a burden
    • Data doesn’t leave the user’s browser�
  • There are a few downsides
    • Loading is slower as a number of packages need to be downloaded every time
    • Most computation is 5-20x slower than running natively in Python on a server
    • There are some limitations on the packages you can use
      • Need to be available on PyPi
      • The package - and all its dependencies - need to have a pure python wheel�(a special pre-built format that makes package installs quicker and easier)

103 of 141

Stlite

The stlite framework brings the power of pyodide to Streamlit.

With (fairly) minimal modification, your web app becomes something that runs in the user’s browser!

Paired with github pages - which can be entirely free hosting - you can create websites that can deal with high concurrent traffic.

104 of 141

Stlite

We won’t go into details of stlite, but there’s an example of a complex multipage stlite app in this repository:

https://github.com/hsma-programme/Teaching_DES_Concepts_Streamlit

The key difference is having an index.html file that controls various aspects.

The stlite github provides a simpler single-page example too:

https://github.com/whitphx/stlite

105 of 141

Stlite → .exe

In fact, you can take stlite even further and use it to create an executable file!

(though you may well end up with even more resistance from your organisation to this approach…)

It’s a bit more complex and may require getting some additional software approved to be able to do this.

If this sounds of interest to you, take a look at the following links:

106 of 141

All aboard the Enterprise

If you’re lucky enough to be working in a progressive organisation, you may have access to a service like Posit Connect, Plotly Dash Enterprise or Snowflake.

These services massively simplify the deployment and hosting of Streamlit apps, along with a series of bells and whistles like permission management/user authentication support.

https://en.wikipedia.org/wiki/USS_Enterprise_%28NCC-1701-A%29

It usually means someone else has already dealt with (most of) the messy stuff around secure sharing of data.

107 of 141

All aboard the Enterprise

For example, from posit connect, you can deploy Streamlit apps with just a few lines - as well as Shiny apps (in R or Python), Quarto documents (with scheduled refresh), Dash apps…

https://posit.co/blog/what-can-you-publish-with-posit-connect/

108 of 141

Name Your Price

Price - and/or the way pricing is calculated - can often be a barrier for organisations.

E.g. the ‘enhanced’ tier of Posit Connect

However - the efficiency savings by working with a tool like this have the potential to be huge.

Posit in particular have some history of working with NHS organisations to make pricing available that will work.

109 of 141

Anyone using these?

Shout out or pop it in the chat!

110 of 141

Recap: Version Control

We’re now going to go through the process of uploading our app to the community cloud.

This will involve a few key steps

  1. Moving our app code into a new folder�
  2. Adding a requirements.txt file to that folder�
  3. Publishing that folder on Github as a repository�
  4. Joining our Github account to Streamlit community cloud�
  5. Enjoying our published app!

111 of 141

requirements.txt

If your app only uses Streamlit, pandas and numpy, then you don’t strictly need to provide a requirements.txt file - though it’s still good practice to do so as it ensures you are in control of the versions in use and your app is less likely to break over time!

Otherwise, place a requirements.txt file in one of two places:

  • The root folder of your repository
  • The folder where your app file is stored

Exercise 4 contains a simple requirements file that should cover the main files from today, but you’ll want to use the main one from the environment folder if you want to deploy your wordcloud app from last week.

112 of 141

RECAP: Creating a repository

We have two broad options when we are working with repositories - create our own or work on someone else’s.

We’ll explore the latter more later. For now, let’s look at how we create a brand new repository. This is what we want to do when we start a new coding project.

First, we need to create a new folder that will store our repository on our local drive (note - you may run into issues if you try to do this on cloud storage).

113 of 141

RECAP: Creating a repository

Now let’s go into VSCode, and click the Source Control button on the left sidebar. This will open a panel asking if we want to open a folder or clone a repository.

For now, we’ll open a folder - the one we created a moment ago. Find the folder on your computer, double click into it, and then push the Select Folder button. You’ll be asked if you trust the authors of the files in the folder. Hopefully you trust yourself…

114 of 141

RECAP: Initialising a repository

Now we’ve selected the folder, we need to initialise the repository. This basically means that we create a new Git repository based on the files in our directory.

Our directory is currently empty - but you can do this for a folder that already has files in it. If there are files already there, Git will note that they exist but it won’t begin tracking them for future changes until we tell it to do so.

If we were using a command line interface, we’d call the command git init. But as we’re using VSCode, we simply click the Initialize Repository button that now appears in our Source Control panel.

115 of 141

RECAP: Untracked Files

Let’s add some code to our repository. I’m going to create a new .py file and save it in this directory, with the following code in it :

Once the code is saved you’ll notice two things :

  1. The Source Control panel on the left has updated to include my_calc.py under a list named Changes
  2. Next to the name of the file in the list, and next to the name on the tab at the top, there is the letter U.

The U indicates that this file is Untracked. This means that Git knows that the file exists in the repository, but it’s not currently tracking changes made to it.

116 of 141

RECAP: Staging

To tell Git we want it to start tracking changes, we need to tell it we want to include it in the next commit. To do that, we need to first stage the file. Recall the flow we talked about earlier :

117 of 141

RECAP: Staging

We can stage multiple files at once before committing our changes. But for now, we’re just going to stage our one and only file.

In a command line interface, we’d use git add. In VSCode, we simply click the + button next to the file(s) we want to stage in the Source Control panel.

Once we do this, we see that the file is added to the Staged Changes list in the panel. This means the file will be included in the next commit.

118 of 141

RECAP: Discarding Changes

If we want to discard changes we’ve made (ie reverse the staging), we can click the - button next to the file (or group of files).

If we have made changes to multiple files, we can stage all files that have changes at once by clicking the + button next to the “Changes” title of the list

119 of 141

RECAP: Commit

Once a file(s) has been staged, we can, when we’re ready, commit the changes to the repository. To do this, we need to supply a commit message. This is a short description of what the commit represents (ie what you’ve added / changed etc).

Commit messages should be concise, but also specific, so that someone else (or you, later down the road) can understand what this “snapshot” of changes represents.

Tip (thanks Tom) : you might consider splitting your commit message into two parts – 1) the type of commit you’re making (eg an initial upload of a file, a bug fix, upload of some documentation etc) and 2) the description of changes.

e.g “INIT: add my_calc.py” clearly tells us that this is an initial upload of a file, and the file that was uploaded was my_calc.py. Feel free to use your own style, but make sure it is consistent and that someone else could understand it!

120 of 141

RECAP: Commit

To commit all of our staged changes in VSCode, we simply type our commit message in the message box at the top of the Source Control panel, and then click the big “Commit” button.

(If you forget to add a message before clicking the button, this is where you’ll be taken to your chosen editor and will need to enter your message on a non commented line - eg line 12 on the screenshot below)

121 of 141

RECAP: Commit

Once you’ve committed your staged changes, you’ll see that (mostly) everything disappears from your Source Control panel. That’s because there are now no staged changes waiting to be committed. This is known as having a clean working tree.

Note : if you have changes that have not yet been staged, or untracked files, they’ll still appear here - you won’t have a clean working tree if that’s the case. The tree will also become “dirty” again as soon as you start modifying and / or adding files to a clean working tree.

122 of 141

RECAP: Publishing an existing repo

Let’s first look at how we’d publish an existing local repository (in our example, the one we’ve created today) on GitHub using VSCode.

To do this, we’re going to publish our main branch on GitHub.

Using the big “Publish Branch” button in the Source Control Panel.

I bet you guessed didn’t you?

We’ll make sure we’re in the main branch of our repo first, and then click the button.

123 of 141

RECAP: Publishing an existing repo

When we do this, if this is our first time using GitHub from VSCode, the following will appear. We need to click “Allow”.

124 of 141

RECAP: Publishing an existing repo

We’ll then get some authentication messages from GitHub that we’ll need to follow, including using our passkey. Note : I have my passkey setup so that it is linked to my Windows PIN authentication, so my authentication may be different to yours (and likely won’t appear during the session as I’m already authenticated). We’ll also be asked whether we want to publish to a private or public repository - I’m going to make mine public. By default, the name of the repo will be the same as the local one - you can change it, but I’d recommend you don’t.

In this case, make it public.

125 of 141

RECAP: Publishing an existing repo

When we go to publish the branch, if this is our first time publishing, we’ll get more authentication requests will need to go through and allow (don’t worry, this only happens once).

126 of 141

RECAP: Publishing an existing repo

Eventually, after a lot of authentication, the branch will be published, and when we go back to VSCode, we’ll see the following :

Git Fetch grabs updates from a repository but doesn’t merge them (it basically says “Here’s some updates, do you want to do something with them?”). My advice is to say “No” to VSCode doing this periodically, at least initially in your Git journeys (I’ve also heard reports that the VSCode periodic fetch can be a bit problematic)

127 of 141

Publishing to Community Cloud

Now we want to head over to the streamlit site: https://streamlit.io/

Choose ‘sign up’

128 of 141

Publishing to Community Cloud

Click ‘Continue to sign-in’

129 of 141

Publishing to Community Cloud

Sign up using any of the provided options

You will need to link up with your github account in a later step regardless of which option you choose here

You may need to enter a code in the next step to verify your email address, and enter some additional details to finish setting up your account.

130 of 141

Publishing to Community Cloud

Click ‘create app’

131 of 141

Publishing to Community Cloud

You’ll now be asked to connect to your GitHub account

132 of 141

Publishing to Community Cloud

Click

‘Authorize Streamlit’

133 of 141

Publishing to Community Cloud

Choose ‘Yup, I have an app’

134 of 141

Publishing to Community Cloud

Fill in the details as appropriate - various drop-downs will appear to help you.

This needs to be the name of the file you’ve been running in the terminal to launch your app during testing

When you’re done, click ‘deploy’ and enjoy your new app!

135 of 141

Publishing to Community Cloud

In the advanced settings, you can change the Python version your app runs on.

You can’t change this later!

You can also provide ‘secrets’ here, which is useful for things like database connections.

(still, be careful about what you share!)

136 of 141

Publishing to Community Cloud

When you’re done, click ‘deploy’ and enjoy your new app!

137 of 141

Publishing to Community Cloud

Once you’ve got all of this set up, in future, an easy way to publish to the community cloud is to run you app from the command line (after first publishing the repository on github) and choose ‘deploy’ from the top right.

This will pre-fill most of those details for you.

138 of 141

Streamlit Community Cloud

Once you’ve made a few apps, you’ll be provided with a list of all of your apps so you can manage various aspects of them.

139 of 141

Streamlit Community Cloud

Clicking on the three dots at the far right allows you to change various settings.

140 of 141

Streamlit Community Cloud

You can even have one ‘private’ app (though bear in mind that it is still published/hosted on external servers)

141 of 141

Exercise 4: Take Me To The Clouds Above

The final task of the day is deploying an app.

  • Create a new repository and upload one of the apps you’ve created in the last few sessions to Github using the skills from session 7A (or borrow the sample app and requirements.txt from the exercise_4 folder)�
  • Deploy your app to the streamlit community cloud
    • Include a requirements.txt file in your repository
      • The one in exercise_4 contains simpy and plotly, so you could deploy your DES app�(you could use the one provided in the environment folder, but there may be more packages in there than strictly necessary)�
  • Share your link in your PSG channel�

If you finish this,

  • Try uploading some more apps (maybe your wordcloud app from last week)
  • Go back to any exercises from these two sessions you want to spend more time on
  • Start building an app of your choice!
  • Or try deploying your app using serverless Streamlit instead using the instructions here: https://bergam0t.github.io/streamlit_book/stlite_github_pages.html