Build A Streamlit Web App From Scratch (incl. NoSQL Database + interactive Sankey chart) 🚀

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hey guys, In this video, we will build the following web application from scratch using Python. In particular, we will be using the open-source framework streamlit. The beauty of streamlit is that you can create entire web applications without knowing HTML, CSS or Javascript. On this website, you can keep track of your income & expenses. Just select a period, and enter your numbers in the different income and expenses categories. Additionally, you could also insert a comment down here. After clicking the save data button, your inputs will be saved to a NoSQL database. If you now navigate to the data visualization tab, you can retrieve a period from your database and plot it in an interactive Sankey chart. In this chart, you can easily see how you spent your money. While coding out this solution, I will show you some very cool tipps & tricks in streamlit, like this navigation menu and how to create your custom colour theme for your app. Additionally, you will learn how to hook up your streamlit app with a NoSQL database and, of course, how to create an interactive Sankey chart. Even if you do not plan to create an income and expense app, I am sure you will get a ton of value from this video which you can apply for your next streamlit project. OK, and without further ado, let us get started. First things first, we need to install the required packages. Therefore, open up your terminal or command prompt, type 'pip install streamlit and plotly' and hit enter. We will use plotly to create the Sankey chart. With that in place, let me open up a blank Python file. As the first step, I will import streamlit as 'st' and 'plotly graph objects' as go. After that, I will create a settings area at the top of the script. Here I will define some basic settings. As shown in the intro, I want to keep track of my income and expenses in this web app. Therefore, I will create two lists for the different income sources and expenses. Later in the video, we will use a for loop to iterate over the lists to generate the actual input fields. Next, I will define the currency and page title. For the extra pop, I like to use an emoji for the page icon. Just head over to the following website. I will also leave the link to this website in the script. Pick any emoji you like and use this shortcode in your streamlit app. In my case, I have picked the following emoji. After that, I will add a setting for the layout. By default, the streamlit app will be centred. However, you could also change this value to wide if you want. With those variables in place, let us set up the basic page configurations. In particular, I will use our settings for the page title, page_icon and layout. After the page configurations, I will insert a title onto the page. For this example, I will simply take our page title, add a space and concatenate it with our page icon. OK, before going further, let us see what our web app looks like. Therefore go back to your terminal. Ensure that you are in the same directory as your python file. In my case, my python script is located on the desktop in a folder called 'income_expense_tracker'. To start the app, type run streamlit, followed by the name of your python file. After pressing enter, streamlit should open up your default browser and display your app. Alright, here it is—our website with the page title, icon and title. By the way, streamlit will pick your default system settings. As I am using dark mode on my machine, I have a black background and white text. So, do not worry if you have a white background with black font. We will customize the theme anyway later in the video. OK, and with that said, let us carry on building the web app. In the next section, I will define the drop-down values for selecting the period. So, I want a drop-down list with the years and months. I could now go ahead and write all months in a list. However, we could make our lives easier by using the built-in calendar & and DateTime modules. Both modules are already included in your Python installation. So, instead of hardcoding the years, I can get the current year by using 'DateTime.today' and extracting the year. If you want, you could also add the next year by adding one. Or use the previous year by subtracting one. To create the list of months, you can use 'month_name' on the calendar object. The first item in the list will be empty, so January starts from index position 1. OK, now we have everything we need to create the actual input form. Above our input form, I want to inform the user that the data entry should be done in the currency, which we have defined in our settings. Then I will create the form by typing 'st.form' followed by a key. Each form must have its own key. Yet, this key is not displayed to the user in the interface. Optionally, you could also add the option to clear all input fields after submitting the form. Within our form, I will set up two columns. In the first column, I will insert a select box to select the months. Later, when submitting the form, we want to retrieve the month the user has selected. We can do this with the help of the 'key' argument. The second column will be very similar, but this time I want to display a select box to pick a year from our list. As our web app is still running, I can now simply save the script and refresh the browser to see our updates. OK, and here we have our header with the drop lists for the months and years. Additionally, streamlit shows us a warning, as we have not implemented a submit button yet. But we will take care of that later in the video. For now, I just wanted to check if the select boxes are working. To visually separate the period selection from the next input fields, I will add a divider using three dashes. Next, I will group our income categories in an expander element. Now, I will show you a nifty trick to create the different input fields for the income categories. You just need to iterate over each item in our income list. Within each iteration, I will make a streamlit number input. For the label, I am using an f string. Additionally, I can also set the minimum value and format. In my case, streamlit should convert the input to integers. By default, the number input widget has two buttons to increase or decrease the number. For this feature, you can also set the step size. And last but not least, you also want to define a key. For the key, you need to use a unique value. In our case, we could just simply pick the income name. And that is all there is to it. Let me quickly do the same for the expenses. So for each expense in expenses, I will create a number input widget. And with that in place, let me save the script again and refresh the page. Now we have our two expander elements. If I open the income bracket, we can see the input fields for the different categories. Likewise, we will have the same for the expenses. As mentioned, we could set the values by using the following buttons. Now that we have this in place, I will add an expander for the comments. To input a comment, I will use the streamlit text area widget and leave the label empty. Optionally, you could also set a placeholder. After our comment section, let us take care of the submit button. First, I visually separate it by using a divider again. Then I will assign the 'st.form_submit_button' to a variable called 'submitted'. When this button is clicked, it will return True. So, we can check if the button was clicked by typing 'if submitted'. In that case, we want to send all values from our form to a database. But before doing that, we need to get the values from each input field. We can do this by accessing the key of each widget, which is stored in the Session state. So, as an example, I want to save the period in the following format. Like a dictionary, we can get the values from the session state by using the key. In my case, I will concatenate the year and month with an underscore. I will follow a slightly different approach to get the values from our income section. Instead of typing out all income categories, I will use a dictionary comprehension. So, as you might remember, we used the income category name for the key. This means I can simply loop over our income list and get the value for each item by using the session state. And to map the value to the respective category, I am using a dictionary. I hope it is not too confusing for you. I will also print out the income & expense dictionaries to make it clearer. But before doing that, let me remind myself that we want to insert those values later into a database instead of printing them to our app. Also, to let the user know that the data has been saved, I will include a success message. Now that we have this in place let me refresh our website. To test it out, I will insert random values for the income and expenses and provide a short comment. If I click the Save Data button, we can see our income and expenses in a dictionary. As the next step, let us plot the data using a Sankey chart. As before, I will set up a header and another form. Later in the app, I want a selection box that only displays the periods which we have saved to our database. As I haven't set up the database yet, I will insert another 'to do'. However, to continue with our script, I will just hardcode the period for now. So, in the selection box, I will simply type 'March 2022'. As I said, we will fix this hardcoded period later. After selecting the period, I want to have another submit button with the label 'Plot Period'. If that button gets clicked, I want to get the data for the selected period from our database. Once again, as we do not have our database yet, I will simply type out an example. So, we will have a comment and two dictionaries – the income and expenses. Before plotting the numbers, I will create simple metrics like the total income by summing up the income values. Likewise, I will do the same for the expenses. The remaining budget will be simple the total income minus the total expenses. I want to display those numbers in three separate columns. In column 1, I will use the widget 'metric', followed by an f-string, where I am concatenating the total income with our currency. I will do the same in the other two columns. So in the second column, I will write the total expenses, and after that, I will display the remaining budget and the comment for the respective month. And before we continue, let us see how it looks. After a quick refresh, we can now select a period. So far, we have only our hard-coded value, so March 2022. If I now plot this period, we can see our metrics. Let me maximize the window so we can see our three columns. OK, cool. Now that we have that, let us create the Sankey chart. To explain the creation of the Sankey chart better, I have created a Jupyter Notebook. The only purpose of this notebook is to explain how we can create the chart. Afterwards, we will switch back to our Python file. To get our feet wet, let us start with a simple example. After importing plotly as go, I will execute the following cell. The hardest part to understand are the following lists. Let me explain. The label is quite straightforward. We have got here two different incomes and expenses. In between, I have added an extra item called 'Total Income'. Next, we have the source list. In this list, we are defining the starting point for each item in the list. So, 'income' is in index position zero. And the source will also be from index position "0", so the same index. Income should flow to 'Total income'. As total 'Total income' is as index position 2, the target will be two. And for the value, I have picked 10 USD. Next, we will have 'Other Income' at index position 1. So, the source will be one, and the target will also be 'Total Income', so 2. For the value, I have also picked 2. I hope I have not confused you too much, but stick with me. We also need to map 'Rent' and 'Food'. Unlike the 'Income' & 'Other Income', they should start from 'Total Income', so the source will be two in both cases. And the target should be their own index position. So, three and four. For the values, I have picked 6 for rent and 4 for Food. OK, this was the harder part. Once we have our lists, we will create and convert them into a dictionary. Then, we can also apply some styling within the node, like the padding and thickness. And after that, we can create the Sankey chart of the links and nodes. And finally, we can plot it. OK, so this was the simple example. Let us now move to our specific use case. As you remember, we have got an income and expense dictionary. The challenge is now to create the lists dynamically with those dictionaries. So, for the income and expenses above, the lists should look like this, without hard coding any numbers. Let us start with the label and value list. For the label, I will simply list all keys from the income and expenses and combine it with 'Total Income' as seen before. The values are also straightforward. Instead of the keys, I will get the values. The source and target, however, will be a bit more tricky. The first part of the source list will be our income. As before, the source should be the respective index position for each element. And in the second part of the source list, the expenses should start from the total income. For our example, the source list will look like this. Now that we have got this, I will do something very similar. All the incomes should have the Total income as the target. The expenses, on the other hand, will have their own index position as their target. I know this might seem confusing, and I have not explained the different list comprehensions in depth. However, I will also upload this Jupyter Notebook to Github so you can play around with the figures. Also, I am sure there might be better and easier ways to create those lists. Let me know in the comments if you have a better idea. And with that said, this is how our final script looks. We can now create the Sankey chart for as many income and expense categories as we want. Just a quick note: I have also changed the colour here. And with that, let me switch back to my Python file and write down all the lines to create the chart. The margin is the only thing I have changed compared to the jupyter notebook. By default, every plotly chart has a certain number of margins, which we can overwrite in the update layout method. To display the chart, I am using "st.plotly_chart" followed by our figure, and I want to use the entire container width. Alright, let's give our website a refresh to check out our chart. If I click the plot period button, we will have our metrics and the Sankey chart. Let me maximize the window so that we can see the chart better. OK, and here you have it. A nice interactive chart where we have a clear overview of our income and expenses. So far, we have put everything on one page, so the input fields and visualization. For the extra touch, I would like to add a navigation bar at the top of our app. For this, we will need another package. So back in my terminal, I will type 'pip install streamlit-option-menu' and hit enter. Once you have installed this package, go back to your python file and import 'option menu' from 'streamlit option menu'. After importing this package, I will create the navigation menu below our title. By the way, I already have a separate video on how to use this package. In that video, I will explain the different options more deeply. With that said, let me initialize the option_menu. For this example, I do not want to have a menu title. For the navigation options, I will choose 'Data Entry' and 'Data Visualization'. For each option, I can also insert an icon. For the data entry, I will use a pencil, and for the data visualization, I will pick a chart icon. To check out all available icons, head over to the icons section on bootstrap. Here you can search for an icon. Once you find one you like, click on it and use this name in your streamlit option menu. OK, back in the script, I will set the orientation to horizontal. And with that, let me save the file and refresh the web app. Great, now we have got our navigation menu. So far, those buttons do not do anything. We just store the user selection in a variable called 'selected'. To only display the data entry form, I will add an if condition, which checks if we have selected "Data Entry". Afterwards, I just need to indent the following lines by one level. Next, let us do the same for the Data Visualization. Just insert the if condition and indent the code afterwards. OK, now it should work. After refreshing the page, we can only see the data entry form. If we click on Data Visualization, we switch to our plotting section. OK, now that we have this also in place, let us customize the styling of our app. I will first hide some default streamlit elements, like the menu icon, the colourful header and the footer note. To do this, I will write some CSS code to set the visibility of those elements to 'hidden'. To inject this CSS code into our app, I use a markdown widget and set 'unsafe_allow_html' to True. So, if I refresh the page now, you will notice that the colourful header and the menu icon have disappeared. Next, let us create a custom colour theme for our app. Therefore, go back to the root directory of your app. Here, you want to create a new folder called '.streamlit'. Inside this folder, create a file with the name 'config.toml'. Open this file with your preferred text editor and create a new section called 'theme'. Under this section, you can set the different colours. Let me shortly type out all available options here. I think it might be easier if you grab this code from my GitHub repo instead of typing it out yourself. For each option, you can also see what it will change in the comments. So, feel free to adjust those colours to your needs and liking. One important hint: Nothing will change if you save this file and refresh your app. You actually need to rerun your app to apply the custom theme. So, back in my terminal, I will stop the app by pressing CTRL + C on my keyboard, and then I can rerun it. After your app has been loaded, you can see the changes. OK, this is looking great so far. The only thing left to do is to set up the database to store and retrieve our input values. There are many great databases available, but I like the NoSQL database from deta. Just head over to their website at "deta.sh". The database from deta is super easy to set up, and it is free. To create an account, click on 'Join Deta' and follow their sign-up procedure. Once logged in, you will see a page which looks like this. Deta offers a couple of different services, like a cloud environment for your app, a database and file storage. For our app, we are only interested in the database. Therefore, click on 'Base' and then 'Python'. Here you can find a quick guide on how to get started. The first thing you want to do is to install the deta SDK for Python. Therefore, open up your terminal or command prompt type 'pip install deta' and press enter. Once installed, go back to your dashboard, and click on 'Settings' and 'Create Key'. As mentioned here, this project key will only be shown once, and you should store it in a safe place because anybody with this key could access your database. With that said, let me copy this key to my clipboard and go back to the root directory of my app. To keep our code organized, I will create a dedicated Python file which will interact with the database. I will call this file 'database.py'. In this file, import deta from deta. Then, I will store the key we just have copied in a variable. This is not really a secure place. Everybody could see your project key if you uploaded this file to GitHub. We will take care of this issue in just a moment. But for now, let us move on and initialize a deta object with our project key. We can now use this object to create or connect to a database. Simply type 'deta.Base' followed by the name of your database. I will call mine 'monthly reports'. And with that, we are ready to use our database. There are just three things I want to do: Insert the values for a specific period into our database, fetch all periods to fill our selection box, and lastly, I want to get all values from a particular period to plot the data. So let me quickly explain what I type here. To insert a period, I have defined four parameters. The period name, the incomes, expenses and the comment. To insert those values, simply type 'put' on the database instance variable and provide the values in a dictionary. The critical part here is the key. For the key, I have picked the period. So, this will be our unique identifier. In just a moment, I will show you an example of this. To fetch all periods, you can type 'DB.fetch'. This will return an instance of a FetchResponse class with the following properties. In our case, we want to get back all items. And finally, we can also retrieve only a specific period using 'get'. To test and explain the functions, I have created the following Jupyter notebook. As before, this notebook is just for testing our code. For the actual app, we do not need this notebook. So, after running the first cells, let us go through an example. We want to store four things when submitting data in the web app. The period, comment, income and expenses. And the data looks like this. So, later in the app, we can use our 'insert period' function. Let me run this cell and go back to my deta dashboard to test it. If you refresh the page, you should see your database on the left side. OK, and here it is – our first entry. If you click on the expenses, you can see that deta saved the data in a JSON object. The neat thing is that you can manipulate any entries directly from your dashboard. For example, let me change the blog value to 500. Afterwards, I can save the changes. If I now go back to my notebook and fetch all periods, I can see the updated value for the Blog. Instead of fetching all periods, you can also get back a single period. OK, now that you have seen how this works, let me go back to the deta dashboard and delete our example entry. We could also validate this by trying to get back the April data one more time. This time, we will back an empty response. This is all working fine, but there is one thing I want to change. If we go back to your 'database.py' file, we have still the hard-coded project key here. Let us now move this key into an environment variable. For this, I will use a small helper package, which you can install by running 'pip install python-dotenv'. Once you have installed this package, go back to your script and import os, a core python module, and from dotenv, import load_dotenv. After those imports, we can load the environment variables by typing load_dotenv, followed by the file path. In just a moment, we will create this '.env' file. Later, we will need our key in that '.env' file. So, let me copy it to the clipboard. Instead of our hard-coded key, I will now write 'os.getenv' and then 'Deta_Key'. OK, now let us create this env file in the root directory of the app. You can simply create a text file and rename it to '.env'. In this file, type 'Deta_key' followed by the project key and save it. Now that we have our .env file, you want to avoid uploading it to GitHub or any other platform. Therefore, also ensure to create a gitignore file. I usually use the service from gitignore.io. Just head over to their website, type python and click on create. Then copy the whole text, go back to your project folder, create a new text file and name it ".gitignore". In this text file, paste the text you just copied. If you scroll up a bit, you will see that git will ignore any environment files. OK, now onto the last step. Let us now connect our streamlit app with our database. Therefore, I will import our local database file as DB. Afterwards, let me navigate to our 'to-dos'. We can now use our 'insert period' function to insert our values, with the period, incomes, expenses and the comment. With that in place, I can delete the following two lines. As the next step, let us get all the periods from the database. For this, I will create a small section at the top of the script. To get all periods, let me fetch all items from our database. I am only interested in the key, so the period name. Therefore I am using a list comprehension. Frankly speaking, I could have implemented this function also directly in my database.py file. But anyways, from this function, I will now return a list of all periods. So, if I scroll down a bit, I can replace our hard-coded period name with our function and remove the 'to_do' marker. Once the user selects a period, we want to get the data from our database. Previously, I have inserted here some dummy data. So, let me delete the following lines and instead, I will retrieve the data using the selected period. This will return a Python dictionary. From this dictionary, I will now retrieve the different values for the comment, expenses and income. OK, and that is all there is to it. Let me save the script and refresh our app. To test it out, let me input some random data into our entry form. Additionally, I will write a short comment, and then I can press the save data button. If I now switch over to the data visualization tab, we should see the entry for January 2022. And indeed, in the drop-down menu, we have our entry. So far, we only have the January data available. If I select it and hit the 'Plot Period' button, we have our metrics and the Sankey chart. So, let me add more data to our database. Back in the entry form, I will insert some values for March 2022. Once done, I will press the save data button again. As a result, we will now have two entries in our selection box, which we can plot. Also, if we go back to our deta dashboard, we will see our entries. Ok, now that we know that everything is working you might be asking yourself how to deploy this web application to the internet without exposing our database key to the public. And this depends on the hosting service you are choosing. For example, if you are using Heroku, you can configure the environment variables via the command-line interface tool or the Heroku dashboard. If you want to deploy it to the streamlit cloud, I will leave the link to the following blog article in the description below. In this article, you can find a step-by-step guide on how to store and retrieve the environment variables using streamlit the cloud. OK, guys, and that is all for this tutorial. If you have any questions, let me know in the comment. Thanks for watching, and see you in the next video.
Info
Channel: Coding Is Fun
Views: 92,971
Rating: undefined out of 5
Keywords: coding is fun, streamlit tutrial, interactive python app, web app using python, website in python, web applicaiton in python, deta database, no sql database, NoSQL Database, NoSQL database streamlit, streamlit with database, database tutorial, python tutorial, sankey chart, create sankey chart, plotly sankey chart, sankey plot, income expense app, streamlit project, streamlit python, python web app, python streamlit
Id: 3egaMfE9388
Channel Id: undefined
Length: 30min 5sec (1805 seconds)
Published: Sun Jun 26 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.