AbeerJadoon's picture
Update app.py
728b1c2 verified
Raw
History Blame Contribute Delete
10.6 kB
import streamlit as st
st.set_page_config(
page_title="Smart Construction App: IoT-Based Real-Time Material Tracking, Quantification, and Pricing",
layout="wide" # This removes the sidebar and uses a wide layout
)
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from google.oauth2.service_account import Credentials
from googleapiclient.discovery import build
import json
import requests
from datetime import datetime, timedelta
import pytz # For timezone adjustments
import io
import time
# Google Sheets Constants
SPREADSHEET_ID = "1NxEFF5xQFTgDC2exGbb37RixtVjlDJ9H20nyQcJMZhw"
SHEET_NAME = "Weighing_Sheet"
RANGE = "A1:D1000"
# Blynk Token and API URL
BLYNK_AUTH_TOKEN = "AGlSvlX_z72VFvwZBgpHTZOKKDudEtjz"
BLYNK_API_URL = f"https://blynk.cloud/external/api/get?token={BLYNK_AUTH_TOKEN}&V4"
# Google Credentials from secrets
json_data = st.secrets["GOOGLE_CREDENTIALS_JSON"]
credentials = Credentials.from_service_account_info(json.loads(json_data))
# Set your local timezone (adjust as needed)
LOCAL_TZ = pytz.timezone("Asia/Karachi")
def fetch_google_sheet():
"""Fetch data from Google Sheets."""
try:
service = build('sheets', 'v4', credentials=credentials)
sheet = service.spreadsheets()
result = sheet.values().get(spreadsheetId=SPREADSHEET_ID, range=f"{SHEET_NAME}!{RANGE}").execute()
values = result.get('values', [])
if not values:
st.warning("No data found in the Google Sheet.")
return pd.DataFrame()
return pd.DataFrame(values[1:], columns=values[0])
except Exception as e:
st.error(f"Error reading Google Sheets: {e}")
return pd.DataFrame()
def fetch_live_blynk_data():
"""Fetch live weight data from Blynk."""
try:
response = requests.get(BLYNK_API_URL)
if response.status_code == 200:
return float(response.text)
else:
st.error(f"Failed to fetch data from Blynk: {response.status_code}")
return 0.0
except Exception as e:
st.error(f"Error fetching Blynk data: {e}")
return 0.0
def main():
# Create Tabs
tabs = st.tabs(["Home", "Weight Monitoring", "Reports & Summaries"])
# Home Tab Layout
with tabs[0]:
# Adjust layout: Title, Subheader, Tagline on the left, Image on the right
col1, col2 = st.columns([0.5, 0.5]) # Adjust column widths as needed
with col1:
st.title("Smart Construction App")
st.subheader("IoT-Based Real-Time Material Tracking, Quantification, and Pricing")
st.markdown("""
**Your Trusted Partner for Reliable, Real-Time, and Innovative Construction Material Quantity Monitoring, Quantification, and Costing Solutions. 🚀🤝**
""")
# Add Project Overview below
st.subheader("Project Overview")
st.markdown("""
- Track construction materials in real time
- Ensure accurate costing and quantification
- Leverage IoT-based solutions for enhanced efficiency
- Reduce material wastage and improve accountability
""")
with col2:
try:
st.image("placeholder_logo.jpg", use_container_width=True)
except Exception:
st.warning("Image not found. Please upload 'placeholder_logo.jpg'.")
# Benefits and Problems Section Side-by-Side
st.subheader("Problems & Benefits")
col1, col2 = st.columns(2) # Create two equal-width columns
# Benefits Section (Left Column)
with col1:
st.subheader("Problems")
col3, col4 = st.columns([0.2, 0.8]) # Adjust logo and text widths as needed
with col3:
st.image("problems_logo.png", use_container_width=True)
with col4:
st.markdown("""
- Manual tracking errors
- Lack of real-time updates
- Inefficient resource management
- Overrun costs
""")
# Problems Section (Right Column)
with col2:
st.subheader("Benefits")
col5, col6 = st.columns([0.2, 0.8]) # Adjust logo and text widths as needed
with col5:
st.image("benefits_logo.png", use_container_width=True)
with col6:
st.markdown("""
- Real-time monitoring
- Improved accuracy
- Enhanced productivity
- Cost-effective solutions
""")
# Weight Monitoring Tab
with tabs[1]:
st.header("Real-Time Weight Monitoring")
with st.container():
material_type = st.selectbox("Select Material Type", ["Sand", "Crush", "Aggregate", "Pan", "Soil", "Other"])
# Placeholders for live updates
weight_gauge_placeholder = st.empty()
weight_time_plot_placeholder = st.empty()
if "time_series" not in st.session_state:
st.session_state.time_series = []
st.session_state.weight_series = []
if st.button("Start Monitoring"):
while True:
# Fetch live data
live_weight = fetch_live_blynk_data()
# Update Time and Weight Series (Keep data for the last 2 hours)
current_time = datetime.now(LOCAL_TZ)
st.session_state.time_series.append(current_time)
st.session_state.weight_series.append(live_weight)
two_hours_ago = current_time - timedelta(hours=2)
st.session_state.time_series = [t for t in st.session_state.time_series if t >= two_hours_ago]
st.session_state.weight_series = st.session_state.weight_series[-len(st.session_state.time_series):]
# Update Weight Gauge
gauge_fig = go.Figure(go.Indicator(
mode="gauge+number",
value=live_weight,
title={'text': "Weight (Kg)"},
gauge={
'axis': {'range': [0, 20000]},
'bar': {'color': "darkblue"},
}
))
weight_gauge_placeholder.plotly_chart(gauge_fig, use_container_width=True, key=f"gauge_{len(st.session_state.time_series)}")
# Update Weight vs Time Plot
weight_time_fig = go.Figure()
weight_time_fig.add_trace(go.Scatter(
x=[t.strftime("%H:%M:%S") for t in st.session_state.time_series],
y=st.session_state.weight_series,
mode='lines+markers',
name='Weight'))
weight_time_fig.update_layout(title="Weight vs. Time",
xaxis_title="Time",
yaxis_title="Weight (Kg)")
weight_time_plot_placeholder.plotly_chart(weight_time_fig, use_container_width=True, key=f"time_{len(st.session_state.time_series)}")
time.sleep(30)
st.experimental_rerun()
# Reports & Summaries Tab
with tabs[2]:
st.header("Reports and Summaries")
# User Inputs for Material Type and Rate in the Reports & Summaries Tab
material_type = st.selectbox("Select Material Type", ["Sand", "Crush", "Aggregate", "Pan", "Soil", "Other"], key="material_type_summary")
material_rate = st.number_input("Rate per Cubic Feet (PKR)", min_value=0.0, step=0.1, key="rate_summary")
# Fetch Google Sheets data
data = fetch_google_sheet()
if not data.empty:
# Ensure numeric columns are converted properly
data['Loading Weight'] = pd.to_numeric(data['Loading Weight'], errors='coerce').fillna(0)
data['Unloading Weight'] = pd.to_numeric(data['Unloading Weight'], errors='coerce').fillna(0)
# Add 'Type of Material' column if not already present
if 'Type of Material' not in data.columns:
data['Type of Material'] = material_type # Assign user-selected material type
# Compute additional columns
data['Difference in Weight'] = data['Loading Weight'] - data['Unloading Weight'].shift(-1, fill_value=0)
data['Received Weight'] = data['Difference in Weight']
data['Cummulative Wt'] = data['Received Weight'].cumsum()
data['Kg to Cft'] = data['Received Weight'] / 40
data['Qty in Cft'] = data['Kg to Cft']
data['Rate of Material'] = material_rate # Add the Rate of Material column
data['Price per Load'] = data['Qty in Cft'] * material_rate
data['Cummulative Price'] = data['Price per Load'].cumsum()
# Display the updated DataFrame
st.dataframe(data)
# Generate Summary
if st.button("Generate Summary Report"):
summary = data.groupby('Type of Material').agg({
'Qty in Cft': 'sum',
'Rate of Material': 'first', # Show the rate of material
'Price per Load': 'sum'
}).reset_index()
# Add a Total Row
total_row = pd.DataFrame({
'Type of Material': ['Total'],
'Qty in Cft': [summary['Qty in Cft'].sum()],
'Rate of Material': [None], # Total row does not need a specific rate
'Price per Load': [summary['Price per Load'].sum()]
})
summary = pd.concat([summary, total_row], ignore_index=True)
st.write("Summary Report:")
st.dataframe(summary)
# Allow users to download the summary as an Excel file
buffer = io.BytesIO()
with pd.ExcelWriter(buffer, engine='xlsxwriter') as writer:
data.to_excel(writer, index=False, sheet_name='Detailed Report')
summary.to_excel(writer, index=False, sheet_name='Summary')
st.download_button("Download Report", data=buffer.getvalue(), file_name="Material_Summary_Report.xlsx")
# Footer
st.markdown("<p style='text-align: right; color: gray;'>Created by Abeer Ahmed Jadoon</p>", unsafe_allow_html=True)
if __name__ == "__main__":
main()