blueprint-api / tools.py
mohamedhuggig's picture
Upload 16 files
a44ef87 verified
import logging
import math
import base64
import io
import ezdxf
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as patches
logger = logging.getLogger("tools")
UNIT_PRICES = {"concrete_m3": 1000, "steel_ton": 35000}
# ---------- تحديث أسعار المواد من قاعدة البيانات ----------
def update_unit_prices_from_db(project_id):
from database import SessionLocal, ProjectSettings
db = SessionLocal()
try:
settings = db.query(ProjectSettings).filter(ProjectSettings.project_id == project_id).first()
if settings:
global UNIT_PRICES
UNIT_PRICES["concrete_m3"] = settings.concrete_price
UNIT_PRICES["steel_ton"] = settings.steel_price
except:
pass
finally:
db.close()
# --- دوال التصميم الإنشائي ---
def structural_beam_analysis(length, load):
try:
L = length
w = load * 10
M = (w * L**2) / 8
scenarios = [
_calc_beam(L, M, 250, int((L/12.0)*1000), "💰 Economic"),
_calc_beam(L, M, 250, int((L/18.0)*1000), "🪶 Lightweight"),
_calc_beam(L, M, 300, int((L/10.0)*1000), "🛡️ Safe")
]
return {"success": True, "scenarios": scenarios}
except Exception as e:
return {"success": False, "error": str(e)}
def _calc_beam(L, M, w, d, label):
d_eff = d - 40
if d_eff <= 0:
d_eff = 100
As_req = (M * 1e6) / (0.87 * 360 * d_eff)
bars = math.ceil(As_req / 201)
vol = (w/1000) * (d/1000) * L
steel_w = (201 * bars * L * 7.85) / 1000000
cost = (vol * UNIT_PRICES['concrete_m3']) + (steel_w * UNIT_PRICES['steel_ton'])
return {
"label": label,
"width_mm": w,
"depth_mm": d,
"steel_bars": f"{bars} T16",
"cost_egp": round(cost),
"raw_values": {
"type": "beam",
"depth_mm": d,
"length_m": L,
"steel_ratio_percent": round((As_req/(w*d_eff))*100, 2)
}
}
def slab_analysis(length_short, length_long, thickness=0):
try:
ratio = length_long / length_short
type_str = "Two-Way" if ratio < 2 else "One-Way"
t = thickness if thickness > 0 else (length_short * 1000 / 30)
load = 15.0
M = (load * length_short**2) / 8
As_req = (M * 1e6) / (0.87 * 360 * (t-20))
return {
"success": True,
"results": {
"explanation": f"🔲 **بلاطة {type_str}**: السمك المطلوب {t:.0f} مم، الحديد التقريبي {As_req/201:.2f} قضيب/متر",
"raw_values": {"type": "slab", "thickness_mm": t}
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def column_analysis(axial_load, height):
try:
Pu = axial_load * 1.5 * 10
Ag = Pu / (0.4 * 30)
side = math.sqrt(Ag) * 1000
return {
"success": True,
"results": {
"explanation": f"🏗️ **عمود**: المقاس المقترح {side:.0f}x{side:.0f} مم للحمل {axial_load} ك.ن",
"raw_values": {"type": "column"}
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def foundation_analysis(load, soil_capacity):
try:
area = (load * 1.5) / (soil_capacity * 10)
side = math.sqrt(area)
thickness = side * 0.5
return {
"success": True,
"results": {
"explanation": f"🧱 **قاعدة**: الأبعاد {side:.2f}x{side:.2f} متر، سمك {thickness*100:.0f} سم",
"raw_values": {"type": "foundation"}
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def retaining_wall_analysis(height, soil_load):
try:
base_thickness = 0.1 * height + 0.3
base_width = 0.5 * height
steel_main = f"16mm كل 200 مم"
return {
"success": True,
"results": {
"explanation": f"🧱 **جدار استنادي**: ارتفاع {height} م، عرض القاعدة {base_width:.2f} م، سمك القاعدة {base_thickness:.2f} م، تسليح رئيسي {steel_main}",
"raw_values": {"type": "retaining_wall", "height": height, "base_width": base_width, "base_thickness": base_thickness}
}
}
except Exception as e:
return {"success": False, "error": str(e)}
def stair_analysis(floor_height, riser=0.17, tread=0.30):
try:
number_of_risers = floor_height / riser
number_of_treads = number_of_risers - 1
flight_length = number_of_treads * tread
thickness = flight_length * 0.03
return {
"success": True,
"results": {
"explanation": f"🪜 **سلم**: عدد القوائم {number_of_risers:.0f}، طول الباي {flight_length:.2f} م، السمك التقريبي {thickness*100:.0f} مم",
"raw_values": {"type": "stair", "risers": number_of_risers, "flight_length": flight_length, "thickness_mm": thickness*1000}
}
}
except Exception as e:
return {"success": False, "error": str(e)}
# --- دوال الحصر (BOQ) ---
def boq_steel_calculator(d, l, c):
wt = (d**2 / 162.2 * l * c) / 1000
cost = wt * UNIT_PRICES['steel_ton']
return {"success": True, "item": {"description": f"Steel {d}mm", "unit": "Ton", "quantity": round(wt,3), "total_price": round(cost)}}
def boq_concrete_calculator(v):
cost = v * UNIT_PRICES['concrete_m3']
return {"success": True, "item": {"description": "Concrete", "unit": "M3", "quantity": v, "total_price": round(cost)}}
# --- الرسم و CAD ---
def generate_beam_dxf(w, d, bars):
try:
doc = ezdxf.new()
msp = doc.modelspace()
msp.add_lwpolyline([(0,0), (w,0), (w,d), (0,d)], close=True)
for i in range(bars):
x = 40 + (w - 80) / (bars + 1) * (i + 1)
msp.add_circle((x, 50), 8)
stream = io.BytesIO()
doc.write(stream)
stream.seek(0)
return base64.b64encode(stream.read()).decode('utf-8')
except:
return None
def draw_beam_section(w, d, bars):
try:
fig, ax = plt.subplots(1, figsize=(6,4))
ax.add_patch(patches.Rectangle((0,0), w, d, facecolor='#f0f0f0', edgecolor='black'))
if bars > 0:
sp = (w - 80) / (bars + 1)
for i in range(bars):
ax.add_patch(patches.Circle((40 + sp*(i+1), 50), 10, color='blue'))
ax.set_aspect('equal')
ax.set_xlim(0, w)
ax.set_ylim(0, d)
ax.set_title(f"Beam Section {w}x{d} mm")
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=100)
buf.seek(0)
img = base64.b64encode(buf.read()).decode('utf-8')
plt.close()
return img
except:
return None
def get_site_checklist(work_type):
data = {
"نجارة": ["مراجعة أبعاد", "رأسية الأعمدة"],
"حدادة": ["أقطار الحديد", "التوشيك"],
"صب": ["التربيط", "الدمك"],
"عمود": ["الرأسية", "أماكن الوصلات"],
"قاعدة": ["النظافة", "الغطاء الخرساني"]
}
items = data.get(work_type, ["عام"])
text = f"📋 **تشك ليست {work_type}:**\n"
for i, item in enumerate(items, 1):
text += f"{i}. {item}\n"
return {"success": True, "items": items, "text": text}