Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| # Load the Excel file | |
| file_path = "Estimate Refer Database.xlsx" | |
| data = pd.read_excel(file_path, sheet_name=0) | |
| # Adjust column names for clarity | |
| data.columns = [ | |
| "Subtype", "Vancouver_Low", "Vancouver_High", | |
| "Calgary_Low", "Calgary_High", "Edmonton_Low", "Edmonton_High", | |
| "Winnipeg_Low", "Winnipeg_High", "Greater Toronto Area_Low", "Greater Toronto Area_High", | |
| "Ottawa/Gatineau_Low", "Ottawa/Gatineau_High", | |
| "Montreal_Low", "Montreal_High", | |
| "Halifax_Low", "Halifax_High", | |
| "St. John’s_Low", "St. John’s_High" | |
| ] | |
| # Remove the first row of labels (already captured in column names) | |
| data = data.iloc[1:].reset_index(drop=True) | |
| # Define cities and project types | |
| cities = [ | |
| "Vancouver", "Calgary", "Edmonton", "Winnipeg", "Greater Toronto Area", | |
| "Ottawa/Gatineau", "Montreal", "Halifax", "St. John’s" | |
| ] | |
| project_types = { | |
| "Office Buildings": [ | |
| "Under 5 Storeys (Class B)", "5- 30 Storeys (Class B)", "5- 30 Storeys (Class A)", | |
| "31 - 60 Storeys (Class A)", "Interior Fitout (Class B)", "Interior Fitout (Class A)" | |
| ], | |
| "Retail": ["Strip Plaza", "Supermarket", "Big Box Store", "Enclosed Mall"], | |
| "Hotels": ["Budget", "Suite Hotel", "4-star Full Service", "Premium for Luxury"], | |
| "Parking": [ | |
| "Surface Parking", "Freestanding Parking Garages (Above Grade)", | |
| "Underground Parking Garages", "Underground Parking Garages - Premium for Unusual Circumstances" | |
| ], | |
| "Industrial Facilities": ["Warehouse", "Distribution Facility", "Urban Storage Facility"] | |
| } | |
| def calculate_estimate(city, project_type, subtype, square_footage): | |
| """Calculate the cost estimate based on inputs.""" | |
| low_column = f"{city}_Low" | |
| high_column = f"{city}_High" | |
| # Filter data for the selected subtype | |
| filtered_data = data[data["Subtype"] == subtype] | |
| if filtered_data.empty or low_column not in data.columns or high_column not in data.columns: | |
| return None, None, None | |
| low_cost = float(filtered_data[low_column].values[0]) | |
| high_cost = float(filtered_data[high_column].values[0]) | |
| # Calculate estimates | |
| low_estimate = low_cost * square_footage | |
| high_estimate = high_cost * square_footage | |
| average_estimate = (low_estimate + high_estimate) / 2 | |
| return low_estimate, high_estimate, average_estimate | |
| # Custom CSS for styling | |
| st.markdown( | |
| """ | |
| <style> | |
| .st-emotion-cache-yw8pof { | |
| padding-bottom: 20px; | |
| } | |
| .stApp { | |
| max-width: 650px; | |
| margin: auto; | |
| padding: 20px; | |
| } | |
| .stButton button { | |
| background-color: rgb(253, 190, 64); | |
| color: #ffffff; | |
| border-radius: 5px; | |
| border: none; | |
| padding: 10px 20px; | |
| margin: auto; | |
| display: block; | |
| } | |
| .stButton button:hover { | |
| background-color: #f7a930; | |
| color: #ffffff; | |
| } | |
| .result-box { | |
| background-color: #ffffff; | |
| color: #000000; | |
| text-align: center; | |
| font-size: 1.2em; | |
| padding: 20px; | |
| border-radius: 10px; | |
| margin-top: 20px; | |
| } | |
| .back-button { | |
| margin-top: 10px; | |
| text-align: left; | |
| } | |
| .email-button { | |
| background-color: rgb(253, 190, 64); | |
| color: #ffffff; /* Text color already white */ | |
| border-radius: 5px; | |
| padding: 10px 15px; | |
| text-decoration: none !important; /* Remove underline */ | |
| font-size: 1em; | |
| display: inline-block; | |
| margin-top: 20px; | |
| border: none; /* Remove default button border */ | |
| cursor: pointer; | |
| } | |
| /* Optional: Add hover effect */ | |
| .email-button:hover { | |
| background-color: rgb(245, 180, 54); | |
| text-decoration: none !important; /* Ensure no underline on hover */ | |
| } | |
| .st-emotion-cache-1104ytp a { | |
| color: rgb(255 255 255); | |
| } | |
| </style> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| # State management for displaying inputs/results | |
| if "page" not in st.session_state: | |
| st.session_state.page = "input" # Default to input form | |
| # Render the input form | |
| if st.session_state.page == "input": | |
| # Custom CSS to center-align the text labels | |
| st.markdown(""" | |
| <style> | |
| .stSelectbox label { | |
| display: block; | |
| text-align: center; | |
| } | |
| .stSlider label { | |
| display: block; | |
| text-align: center; | |
| } | |
| .stSlider > div > div > div { | |
| height: 10px; /* Make slider line bold */ | |
| } | |
| .stSlider > div > div > div > div { | |
| width: 20px; /* Make slider button larger */ | |
| height: 20px; /* Make slider button larger */ | |
| border-radius: 50%; /* Make slider button round */ | |
| background:rgb(253, 190, 64); | |
| } | |
| .stSlider > div > div > div > div > div { | |
| font-size: 18px; /* Make text on top of slider button larger */ | |
| color:rgb(253, 190, 64); | |
| font-weight: bold; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| city = st.selectbox("Select a City", cities, index=4) | |
| project_type = st.selectbox("Select a Project Type", list(project_types.keys())) | |
| subtype = st.selectbox("Select a Subtype", project_types[project_type]) | |
| square_footage = st.slider("Select Square Footage", 0, 100000, 1000) | |
| if st.button("Get Estimate"): | |
| low_estimate, high_estimate, average_estimate = calculate_estimate(city, project_type, subtype, square_footage) | |
| if low_estimate is not None: | |
| st.session_state.result = { | |
| "city": city, | |
| "project_type": project_type, | |
| "subtype": subtype, | |
| "square_footage": square_footage, | |
| "low_estimate": low_estimate, | |
| "high_estimate": high_estimate, | |
| "average_estimate": average_estimate | |
| } | |
| st.session_state.page = "result" | |
| st.rerun() | |
| else: | |
| st.error("No data available for the selected inputs.") | |
| # Render the results page | |
| elif st.session_state.page == "result": | |
| st.markdown(""" | |
| <style> | |
| .st-emotion-cache-yw8pof { | |
| padding-top: 76px; | |
| padding-bottom: 0px; | |
| } | |
| </style>""", unsafe_allow_html=True) | |
| result = st.session_state.result | |
| if st.button("Back"): | |
| st.session_state.page = "input" | |
| st.rerun() | |
| st.markdown(f""" | |
| <div class="result-box"> | |
| <b>💰 Average Estimate:</b><br> | |
| <span style="font-size: 1.5em;">${result['average_estimate']:,.2f}</span><br> | |
| Estimated Cost Range:<br> | |
| ${result['low_estimate']:,.2f} - ${result['high_estimate']:,.2f}<br> | |
| <br> | |
| <a class="email-button" href="mailto:robertor@hkcconstruction.com?subject=Estimate%20Request&body=Hello,%0A%0AI%20would%20like%20a%20detailed%20estimate.%0A%0ADetails:%0A- City: {result['city']}%0A- Project Type: {result['project_type']}%0A- Subtype: {result['subtype']}%0A- Square Footage: {result['square_footage']} sq ft%0A- Average Estimate: ${result['average_estimate']:,.2f}" target="_blank">Request Detailed Budget</a> | |
| <br><br> | |
| <p style="font-size: 1em;">To get a more detailed breakdown of your budget, contact us directly using the button above.</p> | |
| <div style="position: absolute; bottom: 10px; right: 10px;"> | |
| <span style="font-size: 0.5em; cursor: pointer;" title="The construction cost data contained herein are of a general nature only and subject to confirmation with respect to specific circumstances.">ℹ️</span> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |