echons's picture
Update titles
594d078
raw
history blame
25.5 kB
import streamlit as st
import plotly.express as px
import pandas as pd
import streamlit_authenticator as stauth
import yaml
from yaml.loader import SafeLoader
import datetime
# from langchain.chat_models import ChatAnthropic
# from langchain.callbacks.base import BaseCallbackHandler
# from langchain.prompts import PromptTemplate
# from langchain.chains import LLMChain
# Function to fetch simulated fly situation data
def get_fly_situation(canteen):
if canteen == "Deck":
# Sample fly situation data
fly_situation = {
"temperature": 28,
"humidity": 60,
"fly_count": 9,
"last_updated": "2023-11-10 12:00:00"
}
delta1 = '0.2'
delta2 = '2'
delta3 = '1'
elif canteen == "Frontier":
# Sample fly situation data
fly_situation = {
"temperature": 28.1,
"humidity": 62,
"fly_count": 21,
"last_updated": "2023-11-10 12:00:00"
}
delta1 = '0.1'
delta2 = '1'
delta3 = '3'
return fly_situation, delta1, delta2, delta3
# Function to generate a sample fly situation dataset with time series
def get_fly_situation_history(canteen):
if canteen == "Deck":
# Sample fly situation time series data
fly_situation_history = [
{"timestamp": "2023-11-10 11:00:00", "fly_count": 2, "sensor":1},
{"timestamp": "2023-11-10 11:05:00", "fly_count": 1, "sensor": 1},
{"timestamp": "2023-11-10 11:10:00", "fly_count": 2, "sensor": 1},
{"timestamp": "2023-11-10 11:15:00", "fly_count": 2, "sensor": 1},
{"timestamp": "2023-11-10 11:20:00", "fly_count": 3, "sensor": 1},
{"timestamp": "2023-11-10 11:25:00", "fly_count": 1, "sensor": 1},
{"timestamp": "2023-11-10 11:30:00", "fly_count": 2, "sensor": 1},
{"timestamp": "2023-11-10 11:35:00", "fly_count": 1, "sensor": 1},
{"timestamp": "2023-11-10 11:40:00", "fly_count": 3, "sensor": 1},
{"timestamp": "2023-11-10 11:45:00", "fly_count": 1, "sensor": 1},
{"timestamp": "2023-11-10 11:50:00", "fly_count": 2, "sensor": 1},
{"timestamp": "2023-11-10 11:55:00", "fly_count": 3, "sensor": 1},
{"timestamp": "2023-11-10 12:00:00", "fly_count": 1, "sensor": 1},
{"timestamp": "2023-11-10 11:00:00", "fly_count": 1, "sensor": 2},
{"timestamp": "2023-11-10 11:05:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:10:00", "fly_count": 3, "sensor": 2},
{"timestamp": "2023-11-10 11:15:00", "fly_count": 1, "sensor": 2},
{"timestamp": "2023-11-10 11:20:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:25:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:30:00", "fly_count": 1, "sensor": 2},
{"timestamp": "2023-11-10 11:35:00", "fly_count": 3, "sensor": 2},
{"timestamp": "2023-11-10 11:40:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:45:00", "fly_count": 1, "sensor": 2},
{"timestamp": "2023-11-10 11:50:00", "fly_count": 3, "sensor": 2},
{"timestamp": "2023-11-10 11:55:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 12:00:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:00:00", "fly_count": 3, "sensor": 3},
{"timestamp": "2023-11-10 11:05:00", "fly_count": 1, "sensor": 3},
{"timestamp": "2023-11-10 11:10:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:15:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:20:00", "fly_count": 1, "sensor": 3},
{"timestamp": "2023-11-10 11:25:00", "fly_count": 3, "sensor": 3},
{"timestamp": "2023-11-10 11:30:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:35:00", "fly_count": 1, "sensor": 3},
{"timestamp": "2023-11-10 11:40:00", "fly_count": 1, "sensor": 3},
{"timestamp": "2023-11-10 11:45:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:50:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:55:00", "fly_count": 3, "sensor": 3},
{"timestamp": "2023-11-10 12:00:00", "fly_count": 6, "sensor": 3},
]
elif canteen == "Frontier":
# Sample fly situation time series data
fly_situation_history = [
{"timestamp": "2023-11-10 11:00:00", "fly_count": 2, "sensor":1},
{"timestamp": "2023-11-10 11:05:00", "fly_count": 5, "sensor": 1},
{"timestamp": "2023-11-10 11:10:00", "fly_count": 6, "sensor": 1},
{"timestamp": "2023-11-10 11:15:00", "fly_count": 4, "sensor": 1},
{"timestamp": "2023-11-10 11:20:00", "fly_count": 5, "sensor": 1},
{"timestamp": "2023-11-10 11:25:00", "fly_count": 2, "sensor": 1},
{"timestamp": "2023-11-10 11:30:00", "fly_count": 5, "sensor": 1},
{"timestamp": "2023-11-10 11:35:00", "fly_count": 6, "sensor": 1},
{"timestamp": "2023-11-10 11:40:00", "fly_count": 7, "sensor": 1},
{"timestamp": "2023-11-10 11:45:00", "fly_count": 8, "sensor": 1},
{"timestamp": "2023-11-10 11:50:00", "fly_count": 10, "sensor": 1},
{"timestamp": "2023-11-10 11:55:00", "fly_count": 9, "sensor": 1},
{"timestamp": "2023-11-10 12:00:00", "fly_count": 8, "sensor": 1},
{"timestamp": "2023-11-10 11:00:00", "fly_count": 1, "sensor": 2},
{"timestamp": "2023-11-10 11:05:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:10:00", "fly_count": 3, "sensor": 2},
{"timestamp": "2023-11-10 11:15:00", "fly_count": 2, "sensor": 2},
{"timestamp": "2023-11-10 11:20:00", "fly_count": 3, "sensor": 2},
{"timestamp": "2023-11-10 11:25:00", "fly_count": 4, "sensor": 2},
{"timestamp": "2023-11-10 11:30:00", "fly_count": 6, "sensor": 2},
{"timestamp": "2023-11-10 11:35:00", "fly_count": 7, "sensor": 2},
{"timestamp": "2023-11-10 11:40:00", "fly_count": 8, "sensor": 2},
{"timestamp": "2023-11-10 11:45:00", "fly_count": 10, "sensor": 2},
{"timestamp": "2023-11-10 11:50:00", "fly_count": 9, "sensor": 2},
{"timestamp": "2023-11-10 11:55:00", "fly_count": 8, "sensor": 2},
{"timestamp": "2023-11-10 12:00:00", "fly_count": 6, "sensor": 2},
{"timestamp": "2023-11-10 11:00:00", "fly_count": 3, "sensor": 3},
{"timestamp": "2023-11-10 11:05:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:10:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:15:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:20:00", "fly_count": 1, "sensor": 3},
{"timestamp": "2023-11-10 11:25:00", "fly_count": 3, "sensor": 3},
{"timestamp": "2023-11-10 11:30:00", "fly_count": 5, "sensor": 3},
{"timestamp": "2023-11-10 11:35:00", "fly_count": 7, "sensor": 3},
{"timestamp": "2023-11-10 11:40:00", "fly_count": 6, "sensor": 3},
{"timestamp": "2023-11-10 11:45:00", "fly_count": 3, "sensor": 3},
{"timestamp": "2023-11-10 11:50:00", "fly_count": 2, "sensor": 3},
{"timestamp": "2023-11-10 11:55:00", "fly_count": 1, "sensor": 3},
{"timestamp": "2023-11-10 12:00:00", "fly_count": 7, "sensor": 3},
]
return fly_situation_history
# Function to get dataframe of camera locations
def get_camera_locations(canteen):
if canteen == 'Frontier':
camera_locations = pd.DataFrame({
"latitude": [1.2963134225592299, 1.2965099487866827, 1.296561127489237],
"longitude": [103.78033553238319, 103.78067954132742, 103.7807614482189],
"size": [1 for i in range(3)]
})
elif canteen == 'Deck':
camera_locations = pd.DataFrame({
"latitude": [1.2948580016451805, 1.2947091254796532, 1.2944617283028779],
"longitude": [103.77238596429575, 103.77266955821814, 103.77246151634456],
"size": [1 for i in range(3)]
})
return camera_locations
def get_pheremone_levels(sensor):
pheremone_levels_history = [
{"timestamp": "2023-11-10 11:00:00", "pheremone_level": 75, "sensor":1},
{"timestamp": "2023-11-10 11:05:00", "pheremone_level": 75, "sensor": 1},
{"timestamp": "2023-11-10 11:10:00", "pheremone_level": 74, "sensor": 1},
{"timestamp": "2023-11-10 11:15:00", "pheremone_level": 74, "sensor": 1},
{"timestamp": "2023-11-10 11:20:00", "pheremone_level": 74, "sensor": 1},
{"timestamp": "2023-11-10 11:25:00", "pheremone_level": 74, "sensor": 1},
{"timestamp": "2023-11-10 11:30:00", "pheremone_level": 73, "sensor": 1},
{"timestamp": "2023-11-10 11:35:00", "pheremone_level": 72, "sensor": 1},
{"timestamp": "2023-11-10 11:40:00", "pheremone_level": 71, "sensor": 1},
{"timestamp": "2023-11-10 11:45:00", "pheremone_level": 65, "sensor": 1},
{"timestamp": "2023-11-10 11:50:00", "pheremone_level": 63, "sensor": 1},
{"timestamp": "2023-11-10 11:55:00", "pheremone_level": 62, "sensor": 1},
{"timestamp": "2023-11-10 12:00:00", "pheremone_level": 58, "sensor": 1},
{"timestamp": "2023-11-10 11:00:00", "pheremone_level": 95, "sensor": 2},
{"timestamp": "2023-11-10 11:05:00", "pheremone_level": 91, "sensor": 2},
{"timestamp": "2023-11-10 11:10:00", "pheremone_level": 91, "sensor": 2},
{"timestamp": "2023-11-10 11:15:00", "pheremone_level": 90, "sensor": 2},
{"timestamp": "2023-11-10 11:20:00", "pheremone_level": 90, "sensor": 2},
{"timestamp": "2023-11-10 11:25:00", "pheremone_level": 90, "sensor": 2},
{"timestamp": "2023-11-10 11:30:00", "pheremone_level": 90, "sensor": 2},
{"timestamp": "2023-11-10 11:35:00", "pheremone_level": 90, "sensor": 2},
{"timestamp": "2023-11-10 11:40:00", "pheremone_level": 87, "sensor": 2},
{"timestamp": "2023-11-10 11:45:00", "pheremone_level": 84, "sensor": 2},
{"timestamp": "2023-11-10 11:50:00", "pheremone_level": 80, "sensor": 2},
{"timestamp": "2023-11-10 11:55:00", "pheremone_level": 73, "sensor": 2},
{"timestamp": "2023-11-10 12:00:00", "pheremone_level": 72, "sensor": 2},
{"timestamp": "2023-11-10 11:00:00", "pheremone_level": 41, "sensor": 3},
{"timestamp": "2023-11-10 11:05:00", "pheremone_level": 41, "sensor": 3},
{"timestamp": "2023-11-10 11:10:00", "pheremone_level": 40, "sensor": 3},
{"timestamp": "2023-11-10 11:15:00", "pheremone_level": 40, "sensor": 3},
{"timestamp": "2023-11-10 11:20:00", "pheremone_level": 39, "sensor": 3},
{"timestamp": "2023-11-10 11:25:00", "pheremone_level": 38, "sensor": 3},
{"timestamp": "2023-11-10 11:30:00", "pheremone_level": 38, "sensor": 3},
{"timestamp": "2023-11-10 11:35:00", "pheremone_level": 35, "sensor": 3},
{"timestamp": "2023-11-10 11:40:00", "pheremone_level": 34, "sensor": 3},
{"timestamp": "2023-11-10 11:45:00", "pheremone_level": 33, "sensor": 3},
{"timestamp": "2023-11-10 11:50:00", "pheremone_level": 33, "sensor": 3},
{"timestamp": "2023-11-10 11:55:00", "pheremone_level": 30, "sensor": 3},
{"timestamp": "2023-11-10 12:00:00", "pheremone_level": 26, "sensor": 3},
]
return pheremone_levels_history
# # Streaming LLM output class
# class StreamHandler(BaseCallbackHandler):
# # Referenced from: https://discuss.streamlit.io/t/langchain-stream/43782
# def __init__(self, container, initial_text='', display_method='markdown'):
# self.container = container
# self.text = initial_text
# self.display_method = display_method
# def on_llm_new_token(self, token: str, **kwargs) -> None:
# self.text += token
# display_function = getattr(self.container, self.display_method, None)
# if display_function is not None:
# display_function(self.text)
# else:
# raise ValueError(f'Invalid display_method: {self.display_method}')
# Start of Streamlit Apps
st.set_page_config(layout="centered")
hide_streamlit_style = '''
<style>
#MainMenu {visibility: show;}
footer {visibility: hidden;}
</style>
'''
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
# Import configuration file for user authentication
with open('credentials.yaml') as file:
config = yaml.load(file, Loader=SafeLoader)
# Create an authentication object.
authenticator = stauth.Authenticate(
config['credentials'],
config['cookie']['name'],
config['cookie']['key'],
config['cookie']['expiry_days']
)
# List of advanced users
advanced_users = ['advanced']
# Landing page if user not logged in
if st.session_state['authentication_status'] is None:
# Landing page copy and banner
st.markdown('<h1 style="text-align: left;">Fly Situation Monitoring App 🪰</h1>', unsafe_allow_html=True)
st.markdown('<h4 style="text-align: left;">Keeping You Informed, Keeping Flies at Bay</h4>', unsafe_allow_html=True)
st.write('\n')
st.write('\n')
# Loging log-in details
name, authentication_status, username = authenticator.login('', 'main')
# If log-in failed
if st.session_state['authentication_status'] is False:
st.error('Username/password is incorrect.')
st.write('\n')
st.write('\n')
# App if user is logged in and authenticated
if st.session_state['authentication_status']:
# Streamlit app start
st.title("Fly Situation Monitoring App 🪰")
st.markdown("Keeping You Informed, Keeping Flies at Bay")
st.write('\n')
# User selects a canteen
canteen = st.selectbox("Select a Canteen:", options=["Deck", "Frontier"])
st.write('\n')
# If user is a student, show basic app layout
if not st.session_state['username'] in advanced_users:
# Tabs
tab1, tab2 = st.tabs(["Current", "History"])
# Tab 1: Fly Situation
with tab1:
st.header("Current Fly Situation")
# Get data
fly_situation, delta1, delta2, delta3 = get_fly_situation(canteen)
# Display key information using cards
col1_fly_curr, col2_fly_curr, col3_fly_curr = st.columns(3)
col1_fly_curr.metric("Temperature", str(fly_situation["temperature"]) + " °C", delta=delta1)
col2_fly_curr.metric("Humidty", str(fly_situation["humidity"]) + " %", delta=delta2)
col3_fly_curr.metric("Fly Count", str(fly_situation["fly_count"]), delta=delta3, delta_color="inverse")
st.caption("Last updated at " + fly_situation["last_updated"] + " (5 min intervals)")
# Alert level
if fly_situation["fly_count"] > 20:
alert_level = "High 🔴"
alert_colour = "red"
elif fly_situation["fly_count"] > 10:
alert_level = "Moderate 🟠"
alert_colour = "orange"
else:
alert_level = "Low 🟢"
alert_colour = "green"
st.markdown(f"<h2 style='color:{alert_colour}; text-align: left'>Alert Level: {alert_level}</h3>", unsafe_allow_html=True)
st.markdown('---')
# Camera locations
st.header("Smart Sensor Locations")
camera_locations = get_camera_locations(canteen)
st.map(camera_locations, size='size', zoom=18)
st.markdown('---')
# Feedback
st.header("Feedback")
# Gather feedback
feedback_col1, feedback_col2 = st.columns(2)
with feedback_col1:
user_feedback = st.text_area("Provide Feedback on the Fly Situation:")
with feedback_col2:
uploaded_files = st.file_uploader("Upload a Photo", accept_multiple_files=True, type=['jpg', 'png'])
for uploaded_file in uploaded_files:
st.image(uploaded_file)
if st.button("Submit Feedback"):
st.success("Feedback submitted successfully!")
st.write('\n')
st.write('\n')
st.write('\n')
# Tab 2: History
with tab2:
st.subheader("Fly Count Over Time")
# Get history data
fly_situation_history = get_fly_situation_history(canteen)
# Create a DataFrame for the time series data
df = pd.DataFrame(fly_situation_history)
sum_by_timestamp = df.groupby('timestamp')['fly_count'].sum().reset_index()
sum_by_timestamp["timestamp"] = pd.to_datetime(sum_by_timestamp["timestamp"])
# Plot the time series using Plotly Express
fig = px.line(sum_by_timestamp, x="timestamp", y="fly_count", labels={"fly_count": "Fly Count", "timestamp": "Timestamp"})
st.plotly_chart(fig)
# Question-and-Answer
with st.form("form"):
prompt = st.text_input("Ask a Question:")
submit = st.form_submit_button("Submit")
if prompt:
with st.spinner("Generating..."):
pass
# Logout
logout_col1, logout_col2 = st.columns([6,1])
with logout_col2:
st.write('\n')
st.write('\n')
st.write('\n')
authenticator.logout('Logout', 'main')
# Footer Credits
st.markdown('##')
st.markdown("---")
st.markdown("Created with ❤️ by HS2912 W4 Group 2")
else:
# Tabs
tab1, tab2, tab3 = st.tabs(["Current", "History", "Control System"])
# Tab 1: Fly Situation
with tab1:
st.header("Current Fly Situation")
# Get current data
fly_situation, delta1, delta2, delta3 = get_fly_situation(canteen)
# Display key information using cards
col1_fly_curr, col2_fly_curr, col3_fly_curr = st.columns(3)
col1_fly_curr.metric("Temperature", str(fly_situation["temperature"]) + " °C", delta=delta1)
col2_fly_curr.metric("Humidty", str(fly_situation["humidity"]) + " %", delta=delta2)
col3_fly_curr.metric("Fly Count", str(fly_situation["fly_count"]), delta=delta3, delta_color="inverse")
st.caption("Last updated at " + fly_situation["last_updated"] + " (5 min intervals)")
# Alert
if fly_situation["fly_count"] > 20:
alert_level = "High 🔴"
alert_colour = "red"
elif fly_situation["fly_count"] > 10:
alert_level = "Moderate 🟠"
alert_colour = "orange"
else:
alert_level = "Low 🟢"
alert_colour = "green"
st.markdown(f"<h2 style='color:{alert_colour}; text-align: left'>Alert Level: {alert_level}</h3>", unsafe_allow_html=True)
st.markdown('---')
# Camera locations
st.header("Smart Sensor Locations")
camera_locations = get_camera_locations(canteen)
st.map(camera_locations, size='size', zoom=18)
st.markdown('---')
# Feedback
st.header("Feedback")
# Gather feedback
feedback_col1, feedback_col2 = st.columns(2)
with feedback_col1:
user_feedback = st.text_area("Provide Feedback on the Fly Situation:")
with feedback_col2:
uploaded_files = st.file_uploader("Upload a Photo", accept_multiple_files=True, type=['jpg', 'png'])
for uploaded_file in uploaded_files:
st.image(uploaded_file)
if st.button("Submit Feedback"):
st.success("Feedback submitted successfully!")
st.write('\n')
st.write('\n')
st.write('\n')
# Tab 2: History
with tab2:
# Fly count over time
st.subheader("Fly Count Over Time")
# Select sensor
selected_sensor = st.selectbox("Select Sensor:", ["All", "Sensor 1", "Sensor 2", "Sensor 3"])
# Get history data
fly_situation_history = get_fly_situation_history(canteen)
# Create a DataFrame for the time series data
df = pd.DataFrame(fly_situation_history)
if selected_sensor != "All":
df = df[df["sensor"]==int(selected_sensor[-1])]
sum_by_timestamp = df.groupby('timestamp')['fly_count'].sum().reset_index()
sum_by_timestamp["timestamp"] = pd.to_datetime(sum_by_timestamp["timestamp"])
# Plot the time series using Plotly Express
fig = px.line(sum_by_timestamp, x="timestamp", y="fly_count", labels={"fly_count": "Fly Count", "timestamp": "Timestamp"})
st.plotly_chart(fig)
# Pheremones level
st.subheader("Pheremone Level Over Time")
selected_sensor_level = st.selectbox("Select Sensor:", ["Sensor 1", "Sensor 2", "Sensor 3"])
# Get history data
sensor_pheremone_history = get_pheremone_levels(selected_sensor_level)
pheremone_df = pd.DataFrame(sensor_pheremone_history)
pheremone_df = pheremone_df[pheremone_df["sensor"] == int(selected_sensor_level[-1])]
pheremone_df['timestamp'] = pd.to_datetime(pheremone_df['timestamp'])
# Plot the time series using Plotly Express
fig = px.line(pheremone_df, x="timestamp", y="pheremone_level", labels={"pheremone_level": "Pheremone Level", "timestamp": "Timestamp"})
st.plotly_chart(fig)
# Question-and-Answer
with st.form("form"):
prompt = st.text_input("Ask a Question:")
submit = st.form_submit_button("Submit")
if prompt:
with st.spinner("Generating..."):
pass
# Tab 3: Control System
with tab3:
# Enable/disable automatic pest control system
st.header("System Settings")
automatic_control_enabled = st.toggle("Enable Automatic Pest Control", value=True)
st.write('\n')
st.write('\n')
if not automatic_control_enabled:
disabled = False
else:
disabled = True
# Camera
st.subheader("Smart Camera/Sensors")
sensor1 = st.toggle("Enable Sensor 1", value=True, disabled=disabled, key='deck_sensor_1')
sensor2 = st.toggle("Enable Sensor 2", value=True, disabled=disabled, key='deck_sensor_2')
sensor3 = st.toggle("Enable Sensor 3", value=True, disabled=disabled, key='deck_sensor_3')
st.write('\n')
# Audio
st.subheader("Audio")
# Accoustic
accoustic = st.selectbox("Accoustic Audio", ["Audio 1", "Audio 2", "Audio 3"], disabled=disabled)
st.write('\n')
# Pheremones
# Time interval for pheremones discharge in minutes)
st.subheader("Pheremones")
pheremones_interval = st.slider("Pheremones Discharge Interval (minutes)", min_value=5, max_value=60, value=15, step=5, disabled=disabled)
st.write('\n')
# Alerts
st.subheader('Alerts')
# Pest activity threshold for alerts
pest_activity_threshold = st.slider("Fly Count Threshold to Send Out Alerts", min_value=0, max_value=100, value=30, step=5, disabled=disabled)
st.write('\n')
# Instant alerts for pest sightings or unusual activity
st.markdown('<h5>Instant alert</h3>', unsafe_allow_html=True)
if st.button("Send Pest Alert", disabled=disabled):
st.success("Pest alert sent!")
st.write('\n')
# Notifications for upcoming preventive measures or scheduled treatments
st.markdown('<h5>Schedule notification for upcoming treatment day</h3>', unsafe_allow_html=True)
upcoming_event_date = st.date_input("Schedule Date", disabled=disabled)
upcoming_event_time = st.time_input("Set time for alert", disabled=disabled)
if st.button("Schedule Notification", disabled=disabled):
st.success(f"Notification scheduled for {upcoming_event_date} {upcoming_event_time}")
# Logout
logout_col1, logout_col2 = st.columns([6,1])
with logout_col2:
st.write('\n')
st.write('\n')
st.write('\n')
authenticator.logout('Logout', 'main')
# Footer Credits
st.markdown('##')
st.markdown("---")
st.markdown("Created with ❤️ by HS2912 W4 Group 2")