Upload 6 files
#1
by
Krishna086
- opened
- database.py +36 -0
- inventory.py +65 -0
- inventory_model.pkl +3 -0
- prediction.py +43 -0
- requirements.txt +7 -0
- styles.css +6 -0
database.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime
|
2 |
+
from sqlalchemy.ext.declarative import declarative_base
|
3 |
+
from sqlalchemy.orm import sessionmaker
|
4 |
+
|
5 |
+
Base = declarative_base() # Define base class for ORM models
|
6 |
+
|
7 |
+
class Product(Base):
|
8 |
+
__tablename__ = "products"
|
9 |
+
id = Column(Integer, primary_key=True) # Primary key for products
|
10 |
+
name = Column(String, nullable=False)
|
11 |
+
current_stock = Column(Float, nullable=False)
|
12 |
+
safety_stock = Column(Float, nullable=False)
|
13 |
+
lead_time = Column(Float, nullable=False)
|
14 |
+
monthly_demand = Column(Float, nullable=False)
|
15 |
+
product_class = Column(String, nullable=False)
|
16 |
+
|
17 |
+
class Transaction(Base):
|
18 |
+
__tablename__ = "transactions"
|
19 |
+
id = Column(Integer, primary_key=True) # Primary key for transactions
|
20 |
+
product_id = Column(Integer, nullable=False)
|
21 |
+
quantity = Column(Float, nullable=False)
|
22 |
+
transaction_type = Column(String, nullable=False)
|
23 |
+
timestamp = Column(DateTime, server_default="CURRENT_TIMESTAMP")
|
24 |
+
|
25 |
+
engine = create_engine("sqlite:///:memory:") # In-memory SQLite database
|
26 |
+
SessionLocal = sessionmaker(bind=engine) # Session factory for database access
|
27 |
+
|
28 |
+
def initialize_database():
|
29 |
+
Base.metadata.create_all(engine) # Create all tables
|
30 |
+
|
31 |
+
def get_db():
|
32 |
+
db = SessionLocal()
|
33 |
+
try:
|
34 |
+
yield db
|
35 |
+
finally:
|
36 |
+
db.close() # Ensure database session is closed
|
inventory.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy.orm import Session
|
2 |
+
from database import Product, Transaction
|
3 |
+
import uuid
|
4 |
+
from datetime import datetime
|
5 |
+
|
6 |
+
def add_product(db: Session, name: str, current_stock: float, safety_stock: float, lead_time: float, monthly_demand: float, product_class: str):
|
7 |
+
product = Product(name=name, current_stock=current_stock, safety_stock=safety_stock, lead_time=lead_time, monthly_demand=monthly_demand, product_class=product_class) # Create new product
|
8 |
+
db.add(product)
|
9 |
+
db.commit()
|
10 |
+
db.refresh(product)
|
11 |
+
return product
|
12 |
+
|
13 |
+
def get_all_products(db: Session):
|
14 |
+
return db.query(Product).all() # Fetch all products
|
15 |
+
|
16 |
+
def get_product_by_id(db: Session, product_id: int):
|
17 |
+
product = db.query(Product).filter(Product.id == product_id).first()
|
18 |
+
if not product:
|
19 |
+
raise ValueError("Product not found")
|
20 |
+
return product
|
21 |
+
|
22 |
+
# In-memory session-based functions
|
23 |
+
def add_product_memory(session_products: list, name: str, current_stock: float, safety_stock: float, lead_time: float, monthly_demand: float, product_class: str):
|
24 |
+
if any(p["name"].lower() == name.lower() for p in session_products):
|
25 |
+
raise ValueError("Product name already exists in your session")
|
26 |
+
product = {"id": str(uuid.uuid4()), "name": name, "current_stock": current_stock, "safety_stock": safety_stock, "lead_time": lead_time, "monthly_demand": monthly_demand, "product_class": product_class} # Create new in-memory product
|
27 |
+
session_products.append(product)
|
28 |
+
return product
|
29 |
+
|
30 |
+
def update_product_memory(session_products: list, product_id: str, name: str, current_stock: float, safety_stock: float, lead_time: float, monthly_demand: float, product_class: str):
|
31 |
+
if any(p["name"].lower() == name.lower() and p["id"] != product_id for p in session_products):
|
32 |
+
raise ValueError("Product name already exists in your session")
|
33 |
+
for product in session_products:
|
34 |
+
if product["id"] == product_id:
|
35 |
+
product.update({"name": name, "current_stock": current_stock, "safety_stock": safety_stock, "lead_time": lead_time, "monthly_demand": monthly_demand, "product_class": product_class})
|
36 |
+
return product
|
37 |
+
raise ValueError("Product not found")
|
38 |
+
|
39 |
+
def delete_product_memory(session_products: list, product_id: str):
|
40 |
+
for i, product in enumerate(session_products):
|
41 |
+
if product["id"] == product_id:
|
42 |
+
session_products.pop(i)
|
43 |
+
return
|
44 |
+
raise ValueError("Product not found")
|
45 |
+
|
46 |
+
def add_stock_memory(session_products: list, session_transactions: list, product_id: str, quantity: float):
|
47 |
+
for product in session_products:
|
48 |
+
if product["id"] == product_id:
|
49 |
+
product["current_stock"] += quantity
|
50 |
+
session_transactions.append({"id": str(uuid.uuid4()), "product_id": product_id, "quantity": quantity, "transaction_type": "ADD", "timestamp": datetime.now()}) # Record stock addition
|
51 |
+
return
|
52 |
+
raise ValueError("Product not found")
|
53 |
+
|
54 |
+
def remove_stock_memory(session_products: list, session_transactions: list, product_id: str, quantity: float):
|
55 |
+
for product in session_products:
|
56 |
+
if product["id"] == product_id:
|
57 |
+
if product["current_stock"] < quantity:
|
58 |
+
raise ValueError("Insufficient stock")
|
59 |
+
product["current_stock"] -= quantity
|
60 |
+
session_transactions.append({"id": str(uuid.uuid4()), "product_id": product_id, "quantity": quantity, "transaction_type": "REMOVE", "timestamp": datetime.now()}) # Record stock removal
|
61 |
+
return
|
62 |
+
raise ValueError("Product not found")
|
63 |
+
|
64 |
+
def get_transactions_memory(session_transactions: list):
|
65 |
+
return session_transactions # Fetch all transactions
|
inventory_model.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e4adb914d0431d5874a0caa2d83ba4abc1a487082dcd39abfc7fb4a396bf878f
|
3 |
+
size 10678545
|
prediction.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import joblib
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
from scipy.stats import poisson
|
5 |
+
|
6 |
+
model = joblib.load("inventory_model.pkl") # Load trained model
|
7 |
+
|
8 |
+
INDUSTRIAL_PARAMS = {
|
9 |
+
"service_levels": {"A": 2.33, "B": 1.65, "C": 1.28},
|
10 |
+
"risk_thresholds": {"Low": 15, "Medium": 40, "High": 100}
|
11 |
+
}
|
12 |
+
|
13 |
+
def predict_stockout(order_quantity: float, lead_time: float, safety_stock: float, product_class: str) -> str:
|
14 |
+
if not (0 <= order_quantity <= 10000 and 0 <= lead_time <= 90 and 0 <= safety_stock <= 10000):
|
15 |
+
return "Error: Inputs out of range (Demand: 0-10,000, Lead Time: 0-90 days, Safety Stock: 0-10,000)."
|
16 |
+
if any(x < 0 for x in [order_quantity, lead_time, safety_stock]):
|
17 |
+
return "Error: Negative values not allowed."
|
18 |
+
if product_class not in INDUSTRIAL_PARAMS["service_levels"]:
|
19 |
+
return "Error: Invalid product class. Use A, B, or C."
|
20 |
+
|
21 |
+
daily_demand = order_quantity / 30
|
22 |
+
expected_demand = daily_demand * lead_time
|
23 |
+
poisson_risk = max(0, min(1, 1 - poisson.cdf(safety_stock, max(1, expected_demand))))
|
24 |
+
|
25 |
+
safety_stock_impact = safety_stock / (order_quantity + 1e-6)
|
26 |
+
lead_time_impact = lead_time / 90
|
27 |
+
order_quantity_impact = order_quantity / 10000
|
28 |
+
|
29 |
+
ml_input = pd.DataFrame({"safety_stock_impact": [safety_stock_impact], "lead_time_impact": [lead_time_impact], "order_quantity_impact": [order_quantity_impact]})
|
30 |
+
ml_risk = max(0, min(1, model.predict(ml_input)[0]))
|
31 |
+
|
32 |
+
weight_poisson = min(1, max(0, 1 - (order_quantity / 1000)))
|
33 |
+
weight_ml = 1 - weight_poisson
|
34 |
+
blended_risk = (weight_poisson * poisson_risk + weight_ml * ml_risk)
|
35 |
+
risk_percentage = np.clip(blended_risk * 100, 0, 100)
|
36 |
+
|
37 |
+
thresholds = INDUSTRIAL_PARAMS["risk_thresholds"]
|
38 |
+
if risk_percentage < thresholds["Low"]:
|
39 |
+
return f"<div class='risk-low'>Low Risk (Class {product_class})<br>Risk: {risk_percentage:.1f}%<br>No action needed</div>"
|
40 |
+
elif risk_percentage < thresholds["Medium"]:
|
41 |
+
return f"<div class='risk-medium'>Medium Risk (Class {product_class})<br>Risk: {risk_percentage:.1f}%<br>Monitor closely</div>"
|
42 |
+
else:
|
43 |
+
return f"<div class='risk-high'>High Risk (Class {product_class})<br>Risk: {risk_percentage:.1f}%<br>Replenish inventory</div>" # Return risk prediction HTML
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit==1.38.0
|
2 |
+
pandas==2.1.4
|
3 |
+
scikit-learn==1.3.2
|
4 |
+
joblib==1.3.2
|
5 |
+
numpy==1.26.0
|
6 |
+
scipy==1.11.4
|
7 |
+
sqlalchemy==2.0.35
|
styles.css
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.risk-low { color: #28a745; font-weight: bold; }
|
2 |
+
.risk-medium { color: #ffc107; font-weight: bold; }
|
3 |
+
.risk-high { color: #dc3545; font-weight: bold; }
|
4 |
+
.alert-high { color: #dc3545; font-weight: bold; border: 2px solid #dc3545; padding: 12px; margin: 8px; background-color: #ffe6e6; }
|
5 |
+
.alert-medium { color: #ffc107; font-weight: bold; border: 2px solid #ffc107; padding: 12px; margin: 8px; background-color: #fff3cd; }
|
6 |
+
.footer { font-size: small; color: grey; text-align: center; margin-top: 20px; }
|