Files changed (6) hide show
  1. database.py +36 -0
  2. inventory.py +65 -0
  3. inventory_model.pkl +3 -0
  4. prediction.py +43 -0
  5. requirements.txt +7 -0
  6. 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; }