├── .gitattributes ├── 9781484281109.JPG ├── Chapter_1 ├── main.py └── unit_test.py ├── Chapter_10 └── main.py ├── Chapter_2 ├── conditional_flow_1.py ├── conditional_flow_2.py ├── dataframe_demo.py ├── input_form.py ├── interactive_line_chart.py ├── interactive_map.py ├── markdown_code.py ├── markdown_header_sizes.py ├── markdown_hyperlink.py ├── markdown_image.py ├── markdown_lists.py ├── markdown_quote.py ├── markdown_table.py ├── markdown_text_styling.py ├── mutate_dataframe_arrange.py ├── mutate_dataframe_filter.py ├── mutate_dataframe_groupby.py ├── mutate_dataframe_merge.py ├── mutate_dataframe_mutate.py ├── mutate_dataframe_select.py ├── static_bar_chart.py ├── static_line_chart.py ├── try_and_except_1.py ├── try_and_except_2.py ├── webapp.bat └── without_try_and_except.py ├── Chapter_3 ├── API-__init__.py ├── API.py ├── AddPost.py ├── AddPostView.py ├── FeedView.py ├── GetFeed.py ├── Post.py ├── Views-__init__.py ├── config.toml ├── footer.py ├── main.py ├── main_page.py ├── main_page_subpage.py ├── page_1.py ├── page_2.py ├── page_config.py ├── page_organization.py ├── placeholder.py ├── progress_bar.py ├── remove_footer_menu.py ├── subpage_1_1.py └── subpage_1_2.py ├── Chapter_5 ├── df_demo.py ├── main.py └── secrets.toml ├── Chapter_6 ├── Base.py ├── Employees.py ├── PayGrades.py ├── flask_sample.py ├── main.py ├── sample_json.json ├── streamlit_api.py └── streamlit_main.py ├── Chapter_7 ├── cookie_management.py ├── session_id_demo.py └── session_state.py ├── Chapter_8 ├── .gitignore ├── API.py ├── AddEmployee.py ├── Admins.py ├── DisplayEmployees.py ├── HashingService.py ├── JWTService.py ├── Login.py ├── Middleware.py ├── flask_main.py ├── secrets.example.yaml └── streamlit_main.py ├── Contributing.md ├── LICENSE.txt └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /9781484281109.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/Web-Application-Development-with-Streamlit/d5c2b92e9d51fc0fa4f49011334224fccaf906ee/9781484281109.JPG -------------------------------------------------------------------------------- /Chapter_1/main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | 4 | def calculate_sum(n1, n2): 5 | return n1 + n2 6 | 7 | 8 | st.title("Add numbers") 9 | 10 | n1 = st.number_input("First Number") 11 | n2 = st.number_input("Second Number") 12 | 13 | if st.button("Calculate"): 14 | st.write("Summation is: " + str(calculate_sum(n1, n2))) 15 | -------------------------------------------------------------------------------- /Chapter_1/unit_test.py: -------------------------------------------------------------------------------- 1 | from main import calculate_sum 2 | from selenium import webdriver 3 | from selenium.webdriver.chrome.options import Options 4 | import time 5 | 6 | 7 | def test_user_interface(): 8 | driver_path = r"----------\chromedriver.exe" # Path to chromedriver. Can end with .sh if on (Li/U)nix environments 9 | options = Options() 10 | options.add_argument("--headless") # To not open a real chrome window 11 | with webdriver.Chrome(driver_path, chrome_options=options) as driver: 12 | url = "http://127.0.0.1:8501" 13 | driver.get(url) 14 | time.sleep(5) 15 | html = driver.page_source 16 | 17 | assert "Add numbers" in html 18 | assert "First Number" in html 19 | assert "Second Number" in html 20 | 21 | 22 | def test_logic(): 23 | assert calculate_sum(1, 1) == 2 24 | assert calculate_sum(1, -1) == 0 25 | assert calculate_sum(1, 9) == 10 26 | 27 | 28 | if __name__ == "__main__": 29 | test_logic() 30 | test_user_interface() 31 | -------------------------------------------------------------------------------- /Chapter_10/main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.title("Welcome, WWW") 4 | 5 | url = "https://www.whatismyip-address.com" 6 | 7 | script = f""" 8 | 9 | """ 10 | 11 | st.write(script, unsafe_allow_html=True) 12 | st.write(f"Check out your [public IP]({url})") 13 | -------------------------------------------------------------------------------- /Chapter_2/conditional_flow_1.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def display_name(): 4 | st.info('**Name:** %s' % (name)) 5 | 6 | name = st.text_input('Please enter your name') 7 | 8 | if not name: 9 | st.error('No name entered') 10 | 11 | if name: 12 | display_name() 13 | -------------------------------------------------------------------------------- /Chapter_2/conditional_flow_2.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def display_name(): 4 | st.info('**Name:** %s' % (name)) 5 | 6 | name = st.text_input('Please enter your name') 7 | 8 | if not name: 9 | st.error('No name entered') 10 | st.stop() 11 | 12 | display_name() 13 | -------------------------------------------------------------------------------- /Chapter_2/dataframe_demo.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import plotly.express as px 4 | 5 | df = px.data.stocks() 6 | st.title('DataFrame Demo') 7 | program = st.sidebar.selectbox('Select program',['Dataframe Demo']) 8 | code = st.sidebar.checkbox('Display code') 9 | stocks = st.multiselect('Select stocks',df.columns[1:],[df.columns[1]]) 10 | st.subheader('Stock value') 11 | 12 | # Mutating the dataframe to keep selected columns only 13 | st.write(df[['date'] + stocks].set_index('date')) 14 | 15 | # Creating a Plotly timeseries line chart 16 | fig = px.line(df, x='date', y=stocks, 17 | hover_data={"date": "|%Y %b %d"} 18 | ) 19 | 20 | st.write(fig) 21 | 22 | if code is True: 23 | st.code( 24 | """ 25 | import streamlit as st 26 | import pandas as pd 27 | import plotly.express as px 28 | 29 | df = px.data.stocks() 30 | st.title('DataFrame demo') 31 | program = st.sidebar.selectbox('Select program',['Dataframe Demo']) 32 | code = st.sidebar.checkbox('Display code') 33 | stocks = st.multiselect('Select stocks',df.columns[1:],[df.columns[1]]) 34 | st.subheader('Stock value') 35 | st.write(df[['date'] + stocks].set_index('date')) 36 | 37 | fig = px.line(df, x='date', y=stocks, 38 | hover_data={"date": "|%Y %b %d"} 39 | ) 40 | 41 | st.write(fig) 42 | """ 43 | ) 44 | -------------------------------------------------------------------------------- /Chapter_2/input_form.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | with st.form('feedback_form'): 4 | st.header('Feedback form') 5 | 6 | # Creating columns to organize the form 7 | col1, col2 = st.columns(2) 8 | with col1: 9 | name = st.text_input('Please enter your name') 10 | rating = st.slider('Please rate this app',0,10,5) 11 | with col2: 12 | dob = st.date_input('Please enter your date of birth') 13 | recommend = st.radio('Would you recommend this app to others?',('Yes','No')) 14 | 15 | submit_button = st.form_submit_button('Submit') 16 | 17 | if submit_button is True: 18 | st.write('**Name:**', name, '**Date of birth:**', dob, '**Rating:**', rating, '**Would recommend?:**', recommend) 19 | -------------------------------------------------------------------------------- /Chapter_2/interactive_line_chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import plotly.graph_objects as go 4 | 5 | df = pd.DataFrame(data={'Exam':['Exam 1','Exam 2','Exam 3'], 6 | 'Jessica':[77,76,87],'John':[56,97,95],'Alex':[87,82,93]} 7 | ) 8 | fig = go.Figure(data=[ 9 | go.Line(name='Jessica', x=df['Exam'], y=df['Jessica']), 10 | go.Line(name='John', x=df['Exam'], y=df['John']), 11 | go.Line(name='Alex', x=df['Exam'], y=df['Alex']) 12 | ]) 13 | 14 | fig.update_layout( 15 | xaxis_title='Exam', 16 | yaxis_title='Score', 17 | legend_title='Name', 18 | ) 19 | 20 | st.plotly_chart(fig) 21 | -------------------------------------------------------------------------------- /Chapter_2/interactive_map.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import plotly.graph_objects as go 4 | 5 | df = pd.DataFrame(data={'university':['Harvard University','Yale University', 6 | 'Princeton University','Columbia University','Brown University', 7 | 'Dartmouth University','University of Pennsylvania','Cornell University'], 8 | 'latitude':[42.3770,41.3163,40.3573,40.8075,41.8268,43.7044,39.9522,42.4534], 9 | 'longitude':[-71.1167,-72.9223,-74.6672,-73.9626,-71.4025,-72.2887, 10 | -75.1932,-76.4735]} 11 | ) 12 | 13 | fig = go.Figure(data=go.Scattergeo( 14 | lon = df['longitude'], 15 | lat = df['latitude'], 16 | text = df['university'], 17 | )) 18 | 19 | fig.update_layout( 20 | geo_scope='usa', 21 | ) 22 | 23 | st.plotly_chart(fig) 24 | -------------------------------------------------------------------------------- /Chapter_2/markdown_code.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown( 4 | ''' 5 | ``` 6 | def hello_world(): 7 | print('Hello world') 8 | ``` 9 | ''' 10 | ) 11 | st.markdown( 12 | ''' 13 | ```python 14 | def hello_world(): 15 | print('Hello world') 16 | ``` 17 | ''' 18 | ) 19 | -------------------------------------------------------------------------------- /Chapter_2/markdown_header_sizes.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown( 4 | ''' 5 | # Hello world 6 | ## Hello world 7 | ### Hello world 8 | #### Hello world 9 | ##### Hello world 10 | ###### Hello world 11 | ''' 12 | ) 13 | -------------------------------------------------------------------------------- /Chapter_2/markdown_hyperlink.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown('Check out [Streamlit](https://streamlit.io/)') 4 | -------------------------------------------------------------------------------- /Chapter_2/markdown_image.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown('![Image](https://avatars.githubusercontent.com/u/31454258?v=4)') 4 | -------------------------------------------------------------------------------- /Chapter_2/markdown_lists.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown( 4 | ''' 5 | - Hello world 6 | - Hello world 7 | - Hello world 8 | ''' 9 | ) 10 | st.markdown( 11 | ''' 12 | 1. Hello world 13 | 2. Hello world 14 | 3. Hello world 15 | ''' 16 | ) 17 | st.markdown( 18 | ''' 19 | - [x] Hello world 20 | - [ ] Hello world 21 | ''' 22 | ) 23 | -------------------------------------------------------------------------------- /Chapter_2/markdown_quote.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown('> Hello world') 4 | -------------------------------------------------------------------------------- /Chapter_2/markdown_table.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown( 4 | ''' 5 | Name | Score 1 | Score 2 6 | ------------ | ------------- | ------------- 7 | Jessica | 77 | 76 8 | John | 56 | 97 9 | Alex | 87 | 82 10 | ''' 11 | ) 12 | -------------------------------------------------------------------------------- /Chapter_2/markdown_text_styling.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | st.markdown('**Hello world**') 4 | st.markdown('_Hello world_') 5 | st.markdown('~~Hello world~~') 6 | -------------------------------------------------------------------------------- /Chapter_2/mutate_dataframe_arrange.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | 5 | df = pd.DataFrame( 6 | np.random.randn(4, 3), 7 | columns=('Column 1','Column 2','Column 3') 8 | ) 9 | st.subheader('Original dataframe') 10 | st.write(df) 11 | 12 | st.subheader('Mutated dataframe') 13 | st.write(df.sort_values(by='Column 1',ascending=True)) 14 | -------------------------------------------------------------------------------- /Chapter_2/mutate_dataframe_filter.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | 5 | df = pd.DataFrame( 6 | np.random.randn(4, 3), 7 | columns=('Column 1','Column 2','Column 3') 8 | ) 9 | st.subheader('Original dataframe') 10 | st.write(df) 11 | 12 | st.subheader('Mutated dataframe') 13 | st.write(df[df['Column 1'] > -1]) 14 | -------------------------------------------------------------------------------- /Chapter_2/mutate_dataframe_groupby.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | 5 | df = pd.DataFrame( 6 | np.random.randn(12, 3), 7 | columns=('Score 1','Score 2','Score 3') 8 | ) 9 | 10 | df['Name'] = pd.DataFrame(['John','Alex','Jessica','John','Alex','John', 11 | 'Jessica','John','Alex','Alex','Jessica','Jessica']) 12 | 13 | df['Category'] = pd.DataFrame(['B','A','D','C','C','A', 14 | 'B','C','B','A','A','D']) 15 | 16 | st.subheader('Original dataframe') 17 | st.write(df) 18 | 19 | st.subheader('Mutated dataframe') 20 | st.write(df.groupby(['Name','Category']).first()) 21 | -------------------------------------------------------------------------------- /Chapter_2/mutate_dataframe_merge.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | 4 | df1 = pd.DataFrame(data={'Name':['Jessica','John','Alex'], 5 | 'Score 1':[77,56,87]} 6 | ) 7 | 8 | df2 = pd.DataFrame(data={'Name':['Jessica','John','Alex'], 9 | 'Score 2':[76,97,82]} 10 | ) 11 | 12 | st.subheader('Original dataframes') 13 | st.write(df1) 14 | st.write(df2) 15 | 16 | st.subheader('Mutated dataframe') 17 | st.write(df1.merge(df2,how='inner',on='Name')) 18 | -------------------------------------------------------------------------------- /Chapter_2/mutate_dataframe_mutate.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | 5 | df = pd.DataFrame( 6 | np.random.randn(4, 3), 7 | columns=('Column 1','Column 2','Column 3') 8 | ) 9 | st.subheader('Original dataframe') 10 | st.write(df) 11 | 12 | st.subheader('Mutated dataframe') 13 | st.write(df.assign(Column_4 = lambda x: df['Column 1']*2)) 14 | -------------------------------------------------------------------------------- /Chapter_2/mutate_dataframe_select.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | 5 | df = pd.DataFrame( 6 | np.random.randn(4, 3), 7 | columns=('Column 1','Column 2','Column 3') 8 | ) 9 | st.subheader('Original dataframe') 10 | st.write(df) 11 | 12 | st.subheader('Mutated dataframe') 13 | st.write(df[['Column 1', 'Column 2']]) 14 | -------------------------------------------------------------------------------- /Chapter_2/static_bar_chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | df = pd.DataFrame(data={'Name':['Jessica','John','Alex'], 6 | 'Score 1':[77,56,87],'Score 2':[76,97,82]} 7 | ) 8 | df.set_index('Name').plot(kind='bar',stacked=False,xlabel='Name',ylabel='Score') 9 | st.pyplot(plt) 10 | 11 | -------------------------------------------------------------------------------- /Chapter_2/static_line_chart.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | 5 | df = pd.DataFrame(data={'Exam':['Exam 1','Exam 2','Exam 3'], 6 | 'Jessica':[77,76,87],'John':[56,97,95],'Alex':[87,82,93]} 7 | ) 8 | 9 | df.set_index('Exam').plot(kind='line',xlabel='Exam',ylabel='Score',subplots=True) 10 | st.pyplot(plt) 11 | -------------------------------------------------------------------------------- /Chapter_2/try_and_except_1.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | col1, col2 = st.columns(2) 4 | 5 | with col1: 6 | number_1 = st.number_input('Please enter the first number',value=0,step=1) 7 | with col2: 8 | number_2 = st.number_input('Please enter the second number',value=0,step=1) 9 | 10 | try: 11 | st.info('**%s/%s=** %s' % (number_1,number_2,number_1/number_2)) 12 | except ZeroDivisionError: 13 | st.error('Cannot divide by zero') 14 | except: 15 | pass 16 | -------------------------------------------------------------------------------- /Chapter_2/try_and_except_2.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | col1, col2 = st.columns(2) 4 | 5 | with col1: 6 | number_1 = st.number_input('Please enter the first number',value=0,step=1) 7 | with col2: 8 | number_2 = st.number_input('Please enter the second number',value=0,step=1) 9 | 10 | try: 11 | st.info('**%s/%s=** %s' % (number_1,number_2,number_1/number_2)) 12 | except Exception as e: 13 | st.error(f'Error: {e}') 14 | -------------------------------------------------------------------------------- /Chapter_2/webapp.bat: -------------------------------------------------------------------------------- 1 | call activate base 2 | cd C:/Users/U342185/Desktop/res-owfdmwebapp/ 3 | streamlit run main.py 4 | pause -------------------------------------------------------------------------------- /Chapter_2/without_try_and_except.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | col1, col2 = st.columns(2) 4 | 5 | with col1: 6 | number_1 = st.number_input('Please enter the first number',value=0,step=1) 7 | with col2: 8 | number_2 = st.number_input('Please enter the second number',value=0,step=1) 9 | 10 | st.info('**%s/%s=** %s' % (number_1,number_2,number_1/number_2)) 11 | -------------------------------------------------------------------------------- /Chapter_3/API-__init__.py: -------------------------------------------------------------------------------- 1 | from .API import API as _API 2 | 3 | api_instance = _API() 4 | -------------------------------------------------------------------------------- /Chapter_3/API.py: -------------------------------------------------------------------------------- 1 | from Models import Post 2 | import datetime 3 | 4 | 5 | class API: 6 | def __init__(self, config=None): 7 | self.config = config 8 | 9 | def add_post(self, post: Post): 10 | # POST HTTP request to backend to add the post 11 | # Returns true as in post has been added 12 | return True 13 | 14 | def get_posts(self, start_date: datetime.datetime, end_data: datetime.datetime): 15 | # GET HTTP request to backend to posts within a time period 16 | # Returns a list of Posts 17 | return [Post("Adam", "Python is a snake", datetime.datetime(year=2021, month=5, day=1)), 18 | Post("Sara", "Python is a programming language", datetime.datetime(year=2021, month=5, day=3))] 19 | -------------------------------------------------------------------------------- /Chapter_3/AddPost.py: -------------------------------------------------------------------------------- 1 | from API import api_instance 2 | from Models import Post 3 | 4 | 5 | def add_post(post: Post): 6 | if post is None or len(post.creator_name) == 0 or len(post.content) == 0: 7 | return None 8 | 9 | did_add = api_instance.add_post(post) 10 | 11 | return did_add 12 | -------------------------------------------------------------------------------- /Chapter_3/AddPostView.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import streamlit as st 3 | from Models import Post 4 | from typing import Callable 5 | 6 | 7 | class AddPostView: 8 | def __init__(self, add_post_func: Callable[[Post], bool]): 9 | user_name_text = st.text_input("Displayed name?") 10 | post_text = st.text_input("What's in your mind?") 11 | clicked = st.button("Post") 12 | if clicked: 13 | post = Post(creator_name=user_name_text, content=post_text, posting_date=datetime.datetime.now()) 14 | did_add = add_post_func(post) 15 | 16 | if did_add: 17 | st.success("Post added!") 18 | else: 19 | st.error("Error adding post") 20 | -------------------------------------------------------------------------------- /Chapter_3/FeedView.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from Models import Post 3 | from typing import Callable 4 | 5 | 6 | class FeedView: 7 | def __init__(self, get_feed_func: Callable[[], list]): 8 | posts = get_feed_func() 9 | for post in posts: 10 | _PostView(post) 11 | 12 | 13 | class _PostView: 14 | def __init__(self, post: Post): 15 | st.write(f"**{post.creator_name}**: {post.content} | _{post.posting_date}_") 16 | -------------------------------------------------------------------------------- /Chapter_3/GetFeed.py: -------------------------------------------------------------------------------- 1 | from API import api_instance 2 | import datetime 3 | 4 | 5 | def get_feed(): 6 | to_date = datetime.datetime.now() 7 | from_date = to_date - datetime.timedelta(days=1) 8 | posts = api_instance.get_posts(from_date, to_date) 9 | return posts 10 | -------------------------------------------------------------------------------- /Chapter_3/Post.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | import datetime 3 | 4 | 5 | @dataclass(init=True) 6 | class Post: 7 | creator_name: str 8 | content: str 9 | posting_date: datetime.datetime 10 | -------------------------------------------------------------------------------- /Chapter_3/Views-__init__.py: -------------------------------------------------------------------------------- 1 | from .AddPostView import AddPostView 2 | from .FeedView import FeedView 3 | -------------------------------------------------------------------------------- /Chapter_3/config.toml: -------------------------------------------------------------------------------- 1 | [theme] 2 | 3 | base = 'light' 4 | primaryColor = '#7792E3' 5 | backgroundColor = '#273346' 6 | secondaryBackgroundColor = '#B9F1C0' 7 | textColor = '#FFFFFF' 8 | font = 'sans serif' 9 | -------------------------------------------------------------------------------- /Chapter_3/footer.py: -------------------------------------------------------------------------------- 1 | st.sidebar.markdown( 2 | f'''
3 |
Hello world v 0.1
4 |
Hello world LLC.
''', 5 | unsafe_allow_html=True) 6 | -------------------------------------------------------------------------------- /Chapter_3/main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from Views import FeedView, AddPostView 3 | from Services import get_feed, add_post 4 | 5 | AddPostView(add_post) 6 | st.write("___") 7 | FeedView(get_feed) 8 | 9 | -------------------------------------------------------------------------------- /Chapter_3/main_page.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from pages.page_1 import func_page_1 3 | from pages.page_2 import func_page_2 4 | 5 | def main(): 6 | 7 | st.sidebar.subheader('Page selection') 8 | page_selection = st.sidebar.selectbox('Please select a page',['Main Page','Page 1','Page 2']) 9 | 10 | pages_main = { 11 | 'Main Page': main_page, 12 | 'Page 1': run_page_1, 13 | 'Page 2': run_page_2 14 | } 15 | 16 | # Run selected page 17 | pages_main[page_selection]() 18 | 19 | def main_page(): 20 | st.title('Main Page') 21 | 22 | def run_page_1(): 23 | func_page_1() 24 | 25 | def run_page_2(): 26 | func_page_2() 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /Chapter_3/main_page_subpage.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from pages.page_1 import func_page_1 3 | from pages.subpages_1.subpage_1_1 import func_subpage_1_1 4 | from pages.subpages_1.subpage_1_2 import func_subpage_1_2 5 | 6 | def main(): 7 | 8 | st.sidebar.subheader('Page selection') 9 | page_selection = st.sidebar.selectbox('Please select a page',['Main Page','Page 1']) 10 | 11 | pages_main = { 12 | 'Main Page': main_page, 13 | 'Page 1': run_page_1 14 | } 15 | 16 | subpages_page_1 = { 17 | 'Subpage 1.1': run_subpage_1_1, 18 | 'Subpage 1.2': run_subpage_1_2 19 | } 20 | 21 | if page_selection == 'Main': 22 | # Run selected page 23 | pages_main[page_selection]() 24 | 25 | elif page_selection == 'Page 1': 26 | st.sidebar.subheader('Subpage selection') 27 | subpage_selection_1 = st.sidebar.selectbox("Please select a subpage", tuple(subpages_page_1.keys())) 28 | # Run selected subpage 29 | subpages_page_1[subpage_selection_1]() 30 | 31 | def main_page(): 32 | st.title('Main Page') 33 | 34 | def run_page_1(): 35 | func_page_1() 36 | 37 | def run_subpage_1_1(): 38 | func_subpage_1_1() 39 | 40 | def run_subpage_1_2(): 41 | func_subpage_1_2() 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /Chapter_3/page_1.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def func_page_1(): 4 | st.title('Page 1') 5 | -------------------------------------------------------------------------------- /Chapter_3/page_2.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def func_page_2(): 4 | st.title('Page 2') 5 | -------------------------------------------------------------------------------- /Chapter_3/page_config.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from PIL import Image 3 | icon = Image.open('favicon.ico') 4 | 5 | st.set_page_config( 6 | page_title='Hello world', 7 | page_icon=icon, 8 | layout='centered', 9 | initial_sidebar_state='auto', 10 | menu_items={ 11 | 'Get Help': 'https://streamlit.io/', 12 | 'Report a bug': 'https://github.com', 13 | 'About': 'About your application: **Hello world**' 14 | } 15 | ) 16 | 17 | st.sidebar.title('Hello world') 18 | st.title('Hello world') 19 | -------------------------------------------------------------------------------- /Chapter_3/page_organization.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from datetime import datetime 3 | 4 | #Expander in sidebar 5 | st.sidebar.subheader('Expander') 6 | with st.sidebar.expander('Time'): 7 | time = datetime.now().strftime("%H:%M:%S") 8 | st.write('**%s**' % (time)) 9 | 10 | #Columns in sidebar 11 | st.sidebar.subheader('Columns') 12 | col1, col2 = st.sidebar.columns(2) 13 | with col1: 14 | option_1 = st.selectbox('Please select option 1',['A','B']) 15 | with col2: 16 | option_2 = st.radio('Please select option 2',['A','B']) 17 | 18 | #Container in sidebar 19 | container = st.sidebar.container() 20 | container.subheader('Container') 21 | option_3 = container.slider('Please select option 3') 22 | st.sidebar.warning('Elements outside of container will be displayed externally') 23 | container.info('**Option 3:** %s' % (option_3)) 24 | 25 | #Expander in main body 26 | st.subheader('Expander') 27 | with st.expander('Time'): 28 | time = datetime.now().strftime("%H:%M:%S") 29 | st.write('**%s**' % (time)) 30 | 31 | #Columns in main body 32 | st.subheader('Columns') 33 | col1, col2 = st.columns(2) 34 | with col1: 35 | option_4 = st.selectbox('Please select option 4',['A','B']) 36 | with col2: 37 | option_5 = st.radio('Please select option 5',['A','B']) 38 | 39 | #Container in main body 40 | container = st.container() 41 | container.subheader('Container') 42 | option_6 = container.slider('Please select option 6') 43 | st.warning('Elements outside of container will be displayed externally') 44 | container.info('**Option 6:** %s' % (option_6)) 45 | -------------------------------------------------------------------------------- /Chapter_3/placeholder.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from datetime import datetime 3 | 4 | st.title('Clock') 5 | clock = st.empty() 6 | 7 | while True: 8 | time = datetime.now().strftime("%H:%M:%S") 9 | clock.info('**Current time: ** %s' % (time)) 10 | 11 | if time == '16:09:50': 12 | clock.empty() 13 | st.warning('Alarm!!') 14 | break 15 | -------------------------------------------------------------------------------- /Chapter_3/progress_bar.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import wget 3 | 4 | progress_text = st.empty() 5 | progress_bar = st.progress(0) 6 | 7 | def streamlit_progress_bar(current,total,width): 8 | percent = int((current/total)*100) 9 | progress_text.subheader('Progress: {}%'.format(percent)) 10 | progress_bar.progress(percent) 11 | 12 | wget.download('file URL', 13 | bar=streamlit_progress_bar) 14 | -------------------------------------------------------------------------------- /Chapter_3/remove_footer_menu.py: -------------------------------------------------------------------------------- 1 | hide_footer_style = ''' 2 | 11 | ''' 12 | 13 | st.markdown(hide_menu_style, unsafe_allow_html=True) 14 | -------------------------------------------------------------------------------- /Chapter_3/subpage_1_1.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def func_subpage_1_1(): 4 | st.title('Subpage 1.1') 5 | -------------------------------------------------------------------------------- /Chapter_3/subpage_1_2.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | def func_subpage_1_2(): 4 | st.title('Subpage 1.2') 5 | -------------------------------------------------------------------------------- /Chapter_5/df_demo.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | 4 | df = pd.DataFrame([["Adam", "01/01/1990", 2], 5 | ["Sara", "01/01/1980", 1], 6 | ["Bob", "01/01/1970", 1], 7 | ["Alice", "01/01/2000", 3] 8 | ], columns=["Name", "DOB", "Paygrade ID"]) 9 | 10 | st.table(df) 11 | 12 | st.dataframe(df) -------------------------------------------------------------------------------- /Chapter_5/main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import psycopg2 3 | 4 | 5 | @st.cache(allow_output_mutation=True, 6 | hash_funcs={"_thread.RLock": lambda _: None}) 7 | def init_connection(): 8 | return psycopg2.connect(**st.secrets["db_postrgres"]) 9 | 10 | 11 | conn = init_connection() 12 | 13 | 14 | def run_query(query_str): 15 | cur = conn.cursor() 16 | cur.execute(query_str) 17 | data = cur.fetchall() 18 | cur.close() 19 | return data 20 | 21 | 22 | def run_query_with_context_manager(query_str): 23 | with conn.cursor() as cur: 24 | cur.execute(query_str) 25 | return cur.fetchall() 26 | 27 | 28 | query = st.text_input("Query") 29 | 30 | c1, c2 = st.columns(2) 31 | 32 | output = None 33 | 34 | with c1: 35 | if st.button("Run with context manager"): 36 | output = run_query_with_context_manager(query) 37 | with c2: 38 | if st.button("Run without context manager"): 39 | output = run_query(query) 40 | 41 | st.write(output) 42 | 43 | -------------------------------------------------------------------------------- /Chapter_5/secrets.toml: -------------------------------------------------------------------------------- 1 | [db_postrgres] 2 | host = "127.0.0.1" 3 | port = "5432" 4 | user = "postgres" 5 | password = "admin" 6 | dbname = "CompanyData" -------------------------------------------------------------------------------- /Chapter_6/Base.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | 3 | Base = declarative_base() 4 | -------------------------------------------------------------------------------- /Chapter_6/Employees.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from .Base import Base 3 | 4 | 5 | class Employees(Base): 6 | __tablename__ = 'persons' 7 | id = Column(Integer, primary_key=True) 8 | 9 | name = Column(String) 10 | date_of_birth = Column(String, default=True) 11 | paygrade_id = Column(Integer, unique=True, index=True) 12 | 13 | def to_dict(self): 14 | return { 15 | "id": self.id, 16 | "name": self.name, 17 | "date_of_birth": self.date_of_birth, 18 | "paygrade_id": self.paygrade_id 19 | } 20 | -------------------------------------------------------------------------------- /Chapter_6/PayGrades.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from .Base import Base 3 | 4 | 5 | class PayGrades(Base): 6 | __tablename__ = 'paygrades' 7 | id = Column(Integer, primary_key=True) 8 | 9 | base_salary = Column(String) 10 | reimbursement = Column(String, default=True) 11 | bonuses = Column(String) 12 | 13 | def to_dict(self): 14 | return { 15 | "id": self.id, 16 | "base_salary": self.base_salary, 17 | "reimbursement": self.reimbursement, 18 | "bonuses": self.bonuses 19 | } 20 | -------------------------------------------------------------------------------- /Chapter_6/flask_sample.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | 6 | @app.route('/server_status') 7 | def welcome_controller(): 8 | return { 9 | "message": "Welcome to your Flask Server", 10 | "status": "up", 11 | "random": 1 + 1 12 | } 13 | 14 | 15 | app.run() 16 | -------------------------------------------------------------------------------- /Chapter_6/main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from DataBase import Connection 3 | from DataBase import Employees 4 | 5 | app = Flask(__name__) 6 | 7 | 8 | @app.route('/employees') 9 | def get_all_employees(): 10 | with connection.use_session() as session: 11 | employees = session.query(Employees).all() 12 | employees = [employee.to_dict() for employee in employees] 13 | return {"data": employees} 14 | 15 | 16 | @app.route('/employee', methods=["POST"]) 17 | def add_employee(): 18 | body = request.json 19 | with connection.use_session() as session: 20 | session.add(Employees(**body)) 21 | session.commit() 22 | return {"message": "New employee added successfully"} 23 | 24 | 25 | connection = Connection("postgresql://postgres:admin@127.0.0.1:5432/CompanyData") 26 | app.run() 27 | -------------------------------------------------------------------------------- /Chapter_6/sample_json.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "Adam", 4 | "DOB": "01/01/1990", 5 | "Paygrade ID": "2" 6 | }, 7 | { 8 | "Name": "Sara", 9 | "DOB": "01/01/1980", 10 | "Paygrade ID": "1" 11 | }, 12 | { 13 | "Name": "Bob", 14 | "DOB": "01/01/1970", 15 | "Paygrade ID": "1" 16 | }, 17 | { 18 | "Name": "Alice", 19 | "DOB": "01/01/2000", 20 | "Paygrade ID": "3" 21 | } 22 | ] -------------------------------------------------------------------------------- /Chapter_6/streamlit_api.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import requests 3 | import datetime 4 | 5 | url = "http://127.0.0.1:5000" 6 | 7 | 8 | def add_employee(name, dob, paygrade): 9 | data = { 10 | "name": name, 11 | "date_of_birth": dob, 12 | "paygrade_id": paygrade 13 | } 14 | response = requests.post(url + "/employee", json=data) 15 | if response.status_code == 200: 16 | return True 17 | return False 18 | 19 | 20 | def get_employees(): 21 | response = requests.get(url + "/employees") 22 | return response.json()['data'] 23 | 24 | 25 | form = st.form("new_employee") 26 | name = form.text_input("Name") 27 | dob = str(form.date_input("DOB", min_value=datetime.datetime(year=1920, day=1, month=1))) 28 | paygrade = form.number_input("paygrade", step=1) 29 | 30 | if form.form_submit_button("Add new Employee"): 31 | if add_employee(name, dob, paygrade): 32 | st.success("Employee Added") 33 | else: 34 | st.error("Error adding employee") 35 | 36 | st.write("___") 37 | 38 | employees = get_employees() 39 | st.table(employees) 40 | -------------------------------------------------------------------------------- /Chapter_6/streamlit_main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from multiprocessing import Pool, cpu_count 3 | import threading 4 | import time 5 | 6 | 7 | def func(iterations, id): 8 | i = 0 9 | for i in range(iterations): 10 | i += 1 11 | print("Finished job id =", id) 12 | 13 | 14 | if __name__ == "__main__": 15 | pool = Pool(cpu_count()) 16 | 17 | st.title("Speed You Code!") 18 | 19 | jobs_count = 5 20 | iterations = 10 ** 3 21 | c1, c2 = st.columns(2) 22 | 23 | with c1: 24 | if st.button("multiprocess"): 25 | inputs = [(iterations, i) for i in range(jobs_count)] 26 | t11 = time.time() 27 | pool.starmap(func, inputs) 28 | t21 = time.time() 29 | st.write(f"Finished after {t21 - t11} seconds") 30 | with c2: 31 | if st.button("multithread"): 32 | threads = [threading.Thread(target=func, args=(iterations, i)) for i in range(10)] 33 | 34 | t12 = time.time() 35 | for thread in threads: 36 | thread.start() 37 | for thread in threads: 38 | thread.join() 39 | t22 = time.time() 40 | st.write(f"Finished after {t22 - t12} seconds") 41 | -------------------------------------------------------------------------------- /Chapter_7/cookie_management.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import extra_streamlit_components as stx 3 | 4 | st.title("Cookie Management Demo") 5 | st.subheader("_Featuring Cookie Manager from Extra-Streamlit-Components_") 6 | 7 | cookie_manager = stx.CookieManager() 8 | 9 | st.subheader("All Cookies:") 10 | cookies = cookie_manager.get_all() 11 | st.write(cookies) 12 | 13 | c1, c2, c3 = st.columns(3) 14 | with c1: 15 | st.subheader("Get Cookie:") 16 | cookie = st.text_input("Cookie", key="0") 17 | clicked = st.button("Get") 18 | if clicked: 19 | value = cookie_manager.get(cookie) 20 | st.write(value) 21 | with c2: 22 | st.subheader("Set Cookie:") 23 | cookie = st.text_input("Cookie", key="1") 24 | val = st.text_input("Value") 25 | if st.button("Add"): 26 | cookie_manager.set(cookie, val) 27 | with c3: 28 | st.subheader("Delete Cookie:") 29 | cookie = st.text_input("Cookie", key="2") 30 | if st.button("Delete"): 31 | cookie_manager.delete(cookie) 32 | -------------------------------------------------------------------------------- /Chapter_7/session_id_demo.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from streamlit.report_thread import get_report_ctx 3 | from streamlit.server.server import Server 4 | 5 | all_sessions = Server.get_current()._session_info_by_id 6 | 7 | session_id = get_report_ctx().session_id 8 | session_number = list(all_sessions.keys()).index(session_id) + 1 9 | 10 | st.title("Session ID #"+str(session_number)) 11 | st.header("Id of this session is: " + session_id) 12 | st.subheader("All sessions ("+str(len(all_sessions))+") :") 13 | st.write(all_sessions) 14 | -------------------------------------------------------------------------------- /Chapter_7/session_state.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | 4 | def get_state_value(key): 5 | return st.session_state.get(key) 6 | 7 | 8 | def set_state_value(key, value): 9 | st.session_state[key] = value 10 | 11 | 12 | st.title("Session State Management") 13 | c1, c2, c3 = st.columns(3) 14 | 15 | with c1: 16 | st.subheader("All") 17 | st.write(st.session_state) 18 | with c2: 19 | st.subheader("Set Key") 20 | key = st.text_input("Key", key="KeyInput1") 21 | value = st.text_input("Value") 22 | 23 | if st.button("Set"): 24 | st.session_state[key] = value 25 | st.success("Success") 26 | with c3: 27 | st.subheader("Get Key") 28 | key = st.text_input("Key", key="KeyInput2") 29 | 30 | if st.button("Get"): 31 | st.write(st.session_state.get(key)) 32 | -------------------------------------------------------------------------------- /Chapter_8/.gitignore: -------------------------------------------------------------------------------- 1 | secrets.yaml -------------------------------------------------------------------------------- /Chapter_8/API.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class API: 5 | def __init__(self, base_url: str, token: str): 6 | self.base_url = base_url 7 | self.base_headers = {"token": token} 8 | 9 | def add_employee(self, name, dob, paygrade): 10 | try: 11 | data = { 12 | "name": name, 13 | "date_of_birth": dob, 14 | "paygrade_id": paygrade 15 | } 16 | response = requests.post(self.base_url + "/employee", 17 | json=data, headers=self.base_headers) 18 | if response.status_code == 200: 19 | return True 20 | except: 21 | return False 22 | 23 | def get_employees(self): 24 | try: 25 | response = requests.get(self.base_url + "/employees", 26 | headers=self.base_headers) 27 | return response.json()['data'] 28 | except: 29 | return None 30 | 31 | def login(self, username, password): 32 | try: 33 | response = requests.post(self.base_url + "/auth/login", json={ 34 | "username": username, 35 | "password": password 36 | }) 37 | body = response.json() 38 | token = body.get("token") if type(body) == dict else None 39 | 40 | return token 41 | except: 42 | return None 43 | 44 | def is_logged_in(self): 45 | return requests.get(self.base_url + "/auth/is_logged_in", 46 | headers=self.base_headers).status_code == 200 47 | -------------------------------------------------------------------------------- /Chapter_8/AddEmployee.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from typing import Callable 3 | import datetime 4 | 5 | 6 | class AddEmployee: 7 | def __init__(self, on_submit: Callable[[str, str, int], bool]): 8 | st.header("Add a new employee") 9 | 10 | form = st.form("new_employee") 11 | name = form.text_input("Name") 12 | dob = str(form.date_input("DOB", 13 | min_value=datetime.datetime(year=1920, day=1, month=1))) 14 | paygrade = form.number_input("paygrade", step=1) 15 | 16 | if form.form_submit_button("Add new Employee"): 17 | success = on_submit(name, dob, paygrade) 18 | if success: 19 | st.success("New employee added") 20 | else: 21 | st.error("Employee not added") 22 | -------------------------------------------------------------------------------- /Chapter_8/Admins.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from .Base import Base 3 | 4 | 5 | class Admins(Base): 6 | __tablename__ = 'admins' 7 | id = Column(Integer, primary_key=True) 8 | 9 | username = Column(String) 10 | password_hash = Column(String, default=True) 11 | 12 | def to_dict(self): 13 | return { 14 | "id": self.id, 15 | "username": self.username, 16 | "password_hash": self.password_hash 17 | } 18 | -------------------------------------------------------------------------------- /Chapter_8/DisplayEmployees.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from typing import Callable 3 | 4 | 5 | class DisplayEmployees: 6 | def __init__(self, get_employees: Callable[[], list]): 7 | st.header("Current Employees") 8 | 9 | employees = get_employees() 10 | 11 | if employees is None: 12 | st.error("Error getting employees") 13 | else: 14 | st.table(employees) 15 | -------------------------------------------------------------------------------- /Chapter_8/HashingService.py: -------------------------------------------------------------------------------- 1 | import bcrypt 2 | 3 | 4 | class HashingService: 5 | def __init__(self, bcrypt_gen_salt: int = 12): 6 | self.gen_salt = bcrypt_gen_salt 7 | 8 | def hash_bcrypt(self, plain_text: bytes) -> bytes: 9 | return bcrypt.hashpw(plain_text, bcrypt.gensalt(self.gen_salt)) 10 | 11 | def check_bcrypt(self, plain_text: bytes, hashed_password: bytes) -> bool: 12 | try: 13 | return bcrypt.checkpw(plain_text, hashed_password) 14 | except: 15 | return False 16 | -------------------------------------------------------------------------------- /Chapter_8/JWTService.py: -------------------------------------------------------------------------------- 1 | from jwt import PyJWT 2 | from time import time 3 | from typing import Union 4 | 5 | 6 | class JWTService: 7 | expires_in_seconds = 2592000 8 | signing_algorithm = "HS256" 9 | 10 | def __init__(self, signing_key: str, expires_in_seconds: int = 2592000): 11 | self.signing_key = signing_key 12 | self.expires_in_seconds = expires_in_seconds 13 | 14 | def generate(self, data: dict, expires_in_seconds: int = expires_in_seconds) -> Union[str, None]: 15 | try: 16 | instance = PyJWT() 17 | 18 | curr_unix_epoch = int(time()) 19 | data['iat'] = curr_unix_epoch 20 | 21 | if isinstance(expires_in_seconds, int): 22 | data['exp'] = curr_unix_epoch + expires_in_seconds 23 | 24 | token = instance.encode(payload=data, key=self.signing_key, algorithm=self.signing_algorithm) 25 | 26 | if type(token) == bytes: 27 | token = token.decode('utf8') # Needed for some versions of PyJWT 28 | 29 | return token 30 | except BaseException as _: 31 | return None 32 | 33 | def is_valid(self, token: str, verify_time: bool = True) -> bool: 34 | try: 35 | payload = self.get_payload(token) 36 | 37 | if payload is None: 38 | return False 39 | 40 | if verify_time and 'exp' in payload and payload['exp'] < int(time()): 41 | return False 42 | 43 | return True 44 | except: 45 | return False 46 | 47 | def get_payload(self, token: str): 48 | try: 49 | instance = PyJWT() 50 | payload = instance.decode(jwt=token, key=self.signing_key, algorithms=[self.signing_algorithm]) 51 | return payload 52 | except Exception as e: 53 | print(e) 54 | return None 55 | -------------------------------------------------------------------------------- /Chapter_8/Login.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from typing import Callable 3 | 4 | 5 | class Login: 6 | def __init__(self, on_login: Callable[[str, str], bool]): 7 | st.header("Login") 8 | username = st.text_input("Username") 9 | password = st.text_input("Password",type="password") 10 | 11 | if st.button("Login"): 12 | success = on_login(username, password) 13 | if success: 14 | st.success("Login successful") 15 | else: 16 | st.error("Incorrect username and password combination") 17 | -------------------------------------------------------------------------------- /Chapter_8/Middleware.py: -------------------------------------------------------------------------------- 1 | from flask import Request 2 | from Services.JWTService import JWTService 3 | from werkzeug import exceptions 4 | 5 | 6 | class Middleware: 7 | def __init__(self, jwt_service: JWTService): 8 | self.unauthenticated_route_names = {"/api/auth/login", "/api/auth/sing_up"} 9 | self.jwt_service = jwt_service 10 | 11 | def auth(self, request: Request): 12 | is_route_unauthenticated = request.path in self.unauthenticated_route_names 13 | 14 | if is_route_unauthenticated: 15 | return None 16 | 17 | if "token" in request.headers: 18 | token = request.headers['token'] 19 | is_valid = self.jwt_service.is_valid(token) 20 | if is_valid: 21 | return None 22 | else: 23 | return exceptions.Unauthorized() 24 | 25 | return exceptions.Unauthorized() 26 | -------------------------------------------------------------------------------- /Chapter_8/flask_main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from DataBase import Connection, Employees, Admins 3 | from Services import JWTService, HashingService 4 | from Middleware import Middleware 5 | from werkzeug import exceptions 6 | import yaml 7 | 8 | app = Flask(__name__) 9 | 10 | with open("secrets.yaml") as f: 11 | yaml_dict = yaml.safe_load(f) 12 | sing_up_key = yaml_dict['sing_up_key'] 13 | jwt_secret = yaml_dict['jwt_secret'] 14 | 15 | jwt_service = JWTService(jwt_secret) 16 | middleware = Middleware(jwt_service) 17 | hashing_service = HashingService() 18 | 19 | app.before_request(lambda: middleware.auth(request)) 20 | 21 | 22 | @app.route('/api/employees') 23 | def get_all_employees(): 24 | with connection.use_session() as session: 25 | employees = session.query(Employees).all() 26 | employees = [employee.to_dict() for employee in employees] 27 | return {"data": employees} 28 | 29 | 30 | @app.route('/api/employee', methods=["POST"]) 31 | def add_employee(): 32 | body = request.json 33 | with connection.use_session() as session: 34 | session.add(Employees(**body)) 35 | session.commit() 36 | return {"message": "New employee added successfully"} 37 | 38 | 39 | @app.route('/api/auth/login', methods=["POST"]) 40 | def log_in(): 41 | username, password = request.json['username'], request.json['password'] 42 | with connection.use_session() as session: 43 | admin_account = session.query(Admins).filter( 44 | Admins.username == username).first() 45 | if admin_account is None: 46 | # Username doesn't exist. But don't inform the client with that as 47 | # they can use it to bruteforce valid usernames 48 | return exceptions.Unauthorized( 49 | description="Incorrect username/password combination") 50 | 51 | # Checking if such hash can be generated from that password 52 | is_password_correct = hashing_service.check_bcrypt( 53 | password.encode("utf8"), admin_account.password_hash.encode("utf8")) 54 | if not is_password_correct: 55 | return exceptions.Unauthorized( 56 | description="Incorrect username/password combination") 57 | 58 | token_payload = {"username": username} 59 | token = jwt_service.generate(token_payload) 60 | 61 | if token is None: 62 | return exceptions.InternalServerError(description="Login failed") 63 | 64 | return {"token": token} 65 | 66 | 67 | @app.route('/api/auth/sing_up', methods=["POST"]) 68 | def sign_up(): 69 | username, password = request.json['username'], request.json['password'] 70 | if request.headers.get("sing_up_key") != "sing_up_key": 71 | exceptions.Unauthorized(description="Incorrect Key") 72 | 73 | with connection.use_session() as session: 74 | password_hash = hashing_service.hash_bcrypt( 75 | password.encode("utf-8")).decode("utf-8") 76 | admin = Admins(username=username, password_hash=password_hash) 77 | session.add(admin) 78 | session.commit() 79 | return {"message": "Admin account created successfully"} 80 | 81 | 82 | @app.route('/api/auth/is_logged_in') 83 | def is_logged_in(): 84 | # If this controller is reached this means the 85 | # Auth middleware recognizes the passed token 86 | return {"message": "Token is valid"} 87 | 88 | 89 | connection = Connection("postgresql://postgres:admin@127.0.0.1:5432/CompanyData") 90 | app.run() 91 | -------------------------------------------------------------------------------- /Chapter_8/secrets.example.yaml: -------------------------------------------------------------------------------- 1 | # Copy the content of this file to secrets.yaml 2 | # and replace its contents with the correct values 3 | jwt_secret: "" 4 | sing_up_key: "" -------------------------------------------------------------------------------- /Chapter_8/streamlit_main.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from Views import AddEmployee, DisplayEmployees, Login 3 | from API import API 4 | import extra_streamlit_components as stx 5 | import base64, json 6 | 7 | cookie_manager = stx.CookieManager() 8 | cookies = cookie_manager.get_all() 9 | authentication_token = cookies.get("token")\ 10 | if type(cookies) == dict else cookies 11 | 12 | api = API("http://127.0.0.1:5000/api", authentication_token) 13 | 14 | 15 | def get_username_from_token(auth_token): 16 | b64 = str(auth_token).split(".")[1] 17 | b64 = b64 + "=" * (4 - (len(b64) % 4)) 18 | data = base64.b64decode(b64).decode("utf8") 19 | username = json.loads(data)['username'] 20 | return username 21 | 22 | 23 | def manage_login(username, password): 24 | token = api.login(username, password) 25 | cookie_manager.set("token", token) 26 | return token is not None 27 | 28 | 29 | st.title("Company Management Portal") 30 | 31 | if api.is_logged_in(): 32 | st.subheader(f"_Welcome " 33 | f"**{get_username_from_token(authentication_token)}**_") 34 | st.write("_____") 35 | AddEmployee(api.add_employee) 36 | st.write("___") 37 | DisplayEmployees(api.get_employees) 38 | else: 39 | Login(manage_login) 40 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Freeware License, some rights reserved 2 | 3 | Copyright (c) 2022 Rishabh Jain 4 | 5 | Permission is hereby granted, free of charge, to anyone obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to work with the Software within the limits of freeware distribution and fair use. 8 | This includes the rights to use, copy, and modify the Software for personal use. 9 | Users are also allowed and encouraged to submit corrections and modifications 10 | to the Software for the benefit of other users. 11 | 12 | It is not allowed to reuse, modify, or redistribute the Software for 13 | commercial use in any way, or for a user’s educational materials such as books 14 | or blog articles without prior permission from the copyright holder. 15 | 16 | The above copyright notice and this permission notice need to be included 17 | in all copies or substantial portions of the software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*Web Application Development with Streamlit: Develop and Deploy Secure and Scalable Web Applications to the Cloud Using a Pure Python Framework*](https://www.link.springer.com/book/10.1007/9781484281109) by Mohammad Khorasani, Mohamed Abdou, Javier Hernandez Fernandez (Apress, 2022). 4 | 5 | [comment]: #cover 6 | ![Cover image](9781484281109.JPG) 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. --------------------------------------------------------------------------------