Mtkhang90/SmartConEstimator
0
1import streamlit as st2import pandas as pd3import numpy as np4import torch5import openai6import os7import io8import re9from sentence_transformers import SentenceTransformer10from sklearn.metrics.pairwise import cosine_similarity11import matplotlib.pyplot as plt12 13# --- Groq API setup ---14openai.api_key = os.getenv("GROQ_API_KEY")15openai.api_base = "https://api.groq.com/openai/v1"16GROQ_MODEL = "llama3-8b-8192"17 18# --- Load Excel ---19@st.cache_data20def load_excel(file):21 xl = pd.read_excel(file, sheet_name=None)22 all_data = pd.concat(xl.values(), ignore_index=True)23 return all_data24 25# --- Chunk using regex instead of nltk ---26def chunk_data(df, max_tokens=100):27 text = "\n".join(df.astype(str).apply(lambda row: " | ".join(row), axis=1))28 # Split on period, newline, or semicolon29 sentences = re.split(r'(?<=[.;])\s+|\n+', text)30 chunks, current_chunk, current_len = [], [], 031 32 for sent in sentences:33 tokens = sent.split()34 if current_len + len(tokens) > max_tokens:35 chunks.append(" ".join(current_chunk))36 current_chunk, current_len = [], 037 current_chunk.append(sent)38 current_len += len(tokens)39 40 if current_chunk:41 chunks.append(" ".join(current_chunk))42 43 return chunks44 45# --- Embed chunks ---46@st.cache_resource47def embed_chunks(chunks):48 model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")49 embeddings = model.encode(chunks)50 return embeddings, model51 52# --- Query chunks ---53def query_embedding(user_query, chunks, embeddings, model):54 query_vec = model.encode([user_query])55 similarities = cosine_similarity(query_vec, embeddings)[0]56 top_indices = similarities.argsort()[::-1][:5]57 top_chunks = "\n\n".join([chunks[i] for i in top_indices])58 return top_chunks59 60# --- Generate Estimate from Groq ---61def generate_estimate(context, user_input):62 prompt = f"""You are a construction estimator in Pakistan. Use the following schedule of rates:63 64{context}65 66Estimate a full itemized construction BOQ for:67{user_input}68 69Include all relevant items for a complete house: excavation, foundation, RCC, masonry, plastering, flooring, doors, windows, paint, distemper, fans, lights, wiring, DBs, plumbing, sanitary fittings, water supply, cupboards, wardrobes, gate, etc.70 71Provide output in a markdown table with columns: Item No, Description, Qty, Unit, Rate, Amount in Rs.72"""73 response = openai.ChatCompletion.create(74 model=GROQ_MODEL,75 messages=[{"role": "user", "content": prompt}]76 )77 return response['choices'][0]['message']['content']78 79# --- Quantity Calculator ---80def calculate_quantities(rooms, area, baths, car_porch, living):81 return {82 "Total Area (sqft)": area,83 "No. of Rooms": rooms,84 "No. of Bathrooms": baths,85 "Living Rooms": living,86 "Car Porch Area (est.)": car_porch * 20087 }88 89# --- Floor Plan Sketch ---90def draw_floor_plan(rooms, baths, living, car_porch, area):91 total_spaces = rooms + baths + living + car_porch92 cols = int(np.ceil(np.sqrt(total_spaces)))93 rows = int(np.ceil(total_spaces / cols))94 95 fig, ax = plt.subplots(figsize=(10, 8))96 scale = np.sqrt(area) / 1097 width, height = scale, scale * 0.7598 99 labels = (["Room"] * rooms + ["Bath"] * baths +100 ["Living"] * living + ["Car Porch"] * car_porch)101 102 for i, label in enumerate(labels):103 row = i // cols104 col = i % cols105 x = col * width106 y = (rows - 1 - row) * height107 ax.add_patch(plt.Rectangle((x, y), width, height, edgecolor='black', facecolor='lightblue'))108 ax.text(x + width / 2, y + height / 2, label, ha='center', va='center', fontsize=8)109 110 ax.set_xlim(0, cols * width)111 ax.set_ylim(0, rows * height)112 ax.set_aspect('equal')113 ax.set_title(f"Tentative Floor Plan (Scale: 1 unit = {int(scale)} sqft)")114 ax.axis('off')115 116 buf = io.BytesIO()117 plt.savefig(buf, format='png')118 buf.seek(0)119 return buf120 121# --- Main App ---122def main():123 st.set_page_config(page_title="Construction Estimator", layout="centered")124 st.title("๐งฑ Construction Estimator (RAG + LLaMA 3 + Sketch)")125 126 excel_file = st.file_uploader("Upload Schedule of Rates (.xlsx or .xlsm)", type=["xlsx", "xlsm"])127 if excel_file:128 df = load_excel(excel_file)129 st.success("Excel file loaded successfully.")130 chunks = chunk_data(df)131 embeddings, model = embed_chunks(chunks)132 133 st.subheader("๐๏ธ Enter Project Details")134 rooms = st.number_input("Number of Rooms", min_value=1, value=3)135 area = st.number_input("Total Covered Area (sqft)", min_value=100, value=1200)136 baths = st.number_input("Number of Washrooms", min_value=1, value=2)137 living = st.number_input("Number of Living Rooms", min_value=0, value=1)138 car_porch = st.number_input("Number of Car Porches", min_value=0, value=1)139 140 if st.button("Generate Estimate"):141 quantities = calculate_quantities(rooms, area, baths, car_porch, living)142 user_query = f"Estimate cost for {rooms} rooms, {baths} bathrooms, {living} living rooms, total area {area} sqft, and {car_porch} car porch(es)."143 context = query_embedding(user_query, chunks, embeddings, model)144 response = generate_estimate(context, user_query)145 146 st.subheader("๐ Input Quantities")147 st.json(quantities)148 149 st.subheader("๐ธ Estimated Construction Cost (BOQ Style)")150 st.markdown(response)151 152 buf = draw_floor_plan(rooms, baths, living, car_porch, area)153 st.subheader("๐ Tentative Floor Plan Sketch")154 st.image(buf, caption="Auto-generated Line Plan", use_column_width=True)155 st.download_button("๐ฅ Download Sketch", buf, file_name="floor_plan.png", mime="image/png")156 157if __name__ == "__main__":158 main()159 