Spaces:
Sleeping
Sleeping
merge: Merge feat/database into dev
Browse files- DB_SCHEMA.md +45 -0
- app/core/config.py +16 -0
- app/core/database.py +21 -0
- app/models/models.py +45 -0
- app/models/schemas.py +38 -0
- scripts/create_db.py +10 -0
DB_SCHEMA.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Database Schema Documentation
|
| 2 |
+
|
| 3 |
+
The application uses a single table `prediction_logs` to store all prediction requests and results.
|
| 4 |
+
|
| 5 |
+
## Table: `prediction_logs`
|
| 6 |
+
|
| 7 |
+
| Column Name | Type | Description |
|
| 8 |
+
| :--- | :--- | :--- |
|
| 9 |
+
| `id` | Integer | Primary Key, Auto-increment |
|
| 10 |
+
| `timestamp` | DateTime | Timestamp of the prediction (UTC) |
|
| 11 |
+
| `prediction` | Integer | Predicted class (0 or 1) |
|
| 12 |
+
| `probability` | Float | Probability of the positive class (optional) |
|
| 13 |
+
| `age` | Integer | Age of the employee |
|
| 14 |
+
| `genre` | String | Gender (M/F) |
|
| 15 |
+
| `revenu_mensuel` | Integer | Monthly income |
|
| 16 |
+
| `statut_marital` | String | Marital status |
|
| 17 |
+
| `departement` | String | Department |
|
| 18 |
+
| `poste` | String | Job title |
|
| 19 |
+
| `nombre_experiences_precedentes` | Integer | Number of previous companies |
|
| 20 |
+
| `nombre_heures_travailless` | Integer | Hours worked |
|
| 21 |
+
| `annee_experience_totale` | Integer | Total working years |
|
| 22 |
+
| `annees_dans_l_entreprise` | Integer | Years at current company |
|
| 23 |
+
| `annees_dans_le_poste_actuel` | Integer | Years in current role |
|
| 24 |
+
| `satisfaction_employee_environnement` | Integer | Environment satisfaction (1-4) |
|
| 25 |
+
| `note_evaluation_precedente` | Integer | Previous performance rating |
|
| 26 |
+
| `niveau_hierarchique_poste` | Integer | Job level |
|
| 27 |
+
| `satisfaction_employee_nature_travail` | Integer | Job involvement (1-4) |
|
| 28 |
+
| `satisfaction_employee_equipe` | Integer | Relationship satisfaction (1-4) |
|
| 29 |
+
| `satisfaction_employee_equilibre_pro_perso` | Integer | Work-life balance (1-4) |
|
| 30 |
+
| `note_evaluation_actuelle` | Integer | Current performance rating |
|
| 31 |
+
| `heure_supplementaires` | String | Overtime (Yes/No) |
|
| 32 |
+
| `augementation_salaire_precedente` | String | Percent salary hike |
|
| 33 |
+
| `nombre_participation_pee` | Integer | Stock option level |
|
| 34 |
+
| `nb_formations_suivies` | Integer | Training times last year |
|
| 35 |
+
| `nombre_employee_sous_responsabilite` | Integer | Number of subordinates |
|
| 36 |
+
| `distance_domicile_travail` | Integer | Distance from home |
|
| 37 |
+
| `niveau_education` | Integer | Education level (1-5) |
|
| 38 |
+
| `domaine_etude` | String | Education field |
|
| 39 |
+
| `ayant_enfants` | String | Has children (Yes/No) |
|
| 40 |
+
| `frequence_deplacement` | String | Business travel frequency |
|
| 41 |
+
| `annees_depuis_la_derniere_promotion` | Integer | Years since last promotion |
|
| 42 |
+
| `annes_sous_responsable_actuel` | Integer | Years with current manager |
|
| 43 |
+
|
| 44 |
+
## Relationships
|
| 45 |
+
No relationships as this is a single-table logging schema for this POC.
|
app/core/config.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
class Settings:
|
| 4 |
+
PROJECT_NAME: str = "ML Prediction API"
|
| 5 |
+
PROJECT_VERSION: str = "1.0.0"
|
| 6 |
+
|
| 7 |
+
# Database
|
| 8 |
+
DATABASE_URL: str = os.getenv("DATABASE_URL", "postgresql://postgres:pwd@localhost:5432/prediction_db")
|
| 9 |
+
|
| 10 |
+
# Security
|
| 11 |
+
API_KEY: str = os.getenv("API_KEY", "default_insecure_key")
|
| 12 |
+
|
| 13 |
+
# Model
|
| 14 |
+
MODEL_PATH: str = os.getenv("MODEL_PATH", "02_xgb_model_tuned.pkl")
|
| 15 |
+
|
| 16 |
+
settings = Settings()
|
app/core/database.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sqlalchemy import create_engine
|
| 2 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 3 |
+
from sqlalchemy.orm import sessionmaker
|
| 4 |
+
from app.core.config import settings
|
| 5 |
+
|
| 6 |
+
# Handle cases where DATABASE_URL might start with postgres:// (Heroku style) instead of postgresql://
|
| 7 |
+
SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL
|
| 8 |
+
if SQLALCHEMY_DATABASE_URL and SQLALCHEMY_DATABASE_URL.startswith("postgres://"):
|
| 9 |
+
SQLALCHEMY_DATABASE_URL = SQLALCHEMY_DATABASE_URL.replace("postgres://", "postgresql://", 1)
|
| 10 |
+
|
| 11 |
+
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
| 12 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
| 13 |
+
|
| 14 |
+
Base = declarative_base()
|
| 15 |
+
|
| 16 |
+
def get_db():
|
| 17 |
+
db = SessionLocal()
|
| 18 |
+
try:
|
| 19 |
+
yield db
|
| 20 |
+
finally:
|
| 21 |
+
db.close()
|
app/models/models.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sqlalchemy import Column, Integer, String, Float, DateTime
|
| 2 |
+
from sqlalchemy.sql import func
|
| 3 |
+
from app.core.database import Base
|
| 4 |
+
|
| 5 |
+
class PredictionLog(Base):
|
| 6 |
+
__tablename__ = "prediction_logs"
|
| 7 |
+
|
| 8 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 9 |
+
timestamp = Column(DateTime(timezone=True), server_default=func.now())
|
| 10 |
+
|
| 11 |
+
# Input features
|
| 12 |
+
age = Column(Integer)
|
| 13 |
+
genre = Column(String)
|
| 14 |
+
revenu_mensuel = Column(Integer)
|
| 15 |
+
statut_marital = Column(String)
|
| 16 |
+
departement = Column(String)
|
| 17 |
+
poste = Column(String)
|
| 18 |
+
nombre_experiences_precedentes = Column(Integer)
|
| 19 |
+
nombre_heures_travailless = Column(Integer)
|
| 20 |
+
annee_experience_totale = Column(Integer)
|
| 21 |
+
annees_dans_l_entreprise = Column(Integer)
|
| 22 |
+
annees_dans_le_poste_actuel = Column(Integer)
|
| 23 |
+
satisfaction_employee_environnement = Column(Integer)
|
| 24 |
+
note_evaluation_precedente = Column(Integer)
|
| 25 |
+
niveau_hierarchique_poste = Column(Integer)
|
| 26 |
+
satisfaction_employee_nature_travail = Column(Integer)
|
| 27 |
+
satisfaction_employee_equipe = Column(Integer)
|
| 28 |
+
satisfaction_employee_equilibre_pro_perso = Column(Integer)
|
| 29 |
+
note_evaluation_actuelle = Column(Integer)
|
| 30 |
+
heure_supplementaires = Column(String)
|
| 31 |
+
augementation_salaire_precedente = Column(String)
|
| 32 |
+
nombre_participation_pee = Column(Integer)
|
| 33 |
+
nb_formations_suivies = Column(Integer)
|
| 34 |
+
nombre_employee_sous_responsabilite = Column(Integer)
|
| 35 |
+
distance_domicile_travail = Column(Integer)
|
| 36 |
+
niveau_education = Column(Integer)
|
| 37 |
+
domaine_etude = Column(String)
|
| 38 |
+
ayant_enfants = Column(String)
|
| 39 |
+
frequence_deplacement = Column(String)
|
| 40 |
+
annees_depuis_la_derniere_promotion = Column(Integer)
|
| 41 |
+
annes_sous_responsable_actuel = Column(Integer)
|
| 42 |
+
|
| 43 |
+
# Output
|
| 44 |
+
prediction = Column(Integer)
|
| 45 |
+
probability = Column(Float, nullable=True)
|
app/models/schemas.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
from typing import Optional
|
| 3 |
+
|
| 4 |
+
class InputSchema(BaseModel):
|
| 5 |
+
age: int
|
| 6 |
+
genre: str
|
| 7 |
+
revenu_mensuel: int
|
| 8 |
+
statut_marital: str
|
| 9 |
+
departement: str
|
| 10 |
+
poste: str
|
| 11 |
+
nombre_experiences_precedentes: int
|
| 12 |
+
nombre_heures_travailless: int
|
| 13 |
+
annee_experience_totale: int
|
| 14 |
+
annees_dans_l_entreprise: int
|
| 15 |
+
annees_dans_le_poste_actuel: int
|
| 16 |
+
satisfaction_employee_environnement: int
|
| 17 |
+
note_evaluation_precedente: int
|
| 18 |
+
niveau_hierarchique_poste: int
|
| 19 |
+
satisfaction_employee_nature_travail: int
|
| 20 |
+
satisfaction_employee_equipe: int
|
| 21 |
+
satisfaction_employee_equilibre_pro_perso: int
|
| 22 |
+
note_evaluation_actuelle: int
|
| 23 |
+
heure_supplementaires: str
|
| 24 |
+
augementation_salaire_precedente: str
|
| 25 |
+
nombre_participation_pee: int
|
| 26 |
+
nb_formations_suivies: int
|
| 27 |
+
nombre_employee_sous_responsabilite: int
|
| 28 |
+
distance_domicile_travail: int
|
| 29 |
+
niveau_education: int
|
| 30 |
+
domaine_etude: str
|
| 31 |
+
ayant_enfants: str
|
| 32 |
+
frequence_deplacement: str
|
| 33 |
+
annees_depuis_la_derniere_promotion: int
|
| 34 |
+
annes_sous_responsable_actuel: int
|
| 35 |
+
|
| 36 |
+
class PredictionOutput(BaseModel):
|
| 37 |
+
prediction: int
|
| 38 |
+
probability: Optional[float] = None
|
scripts/create_db.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from app.core.database import Base, engine
|
| 2 |
+
from app.models.models import PredictionLog
|
| 3 |
+
|
| 4 |
+
def create_tables():
|
| 5 |
+
print("Creating database")
|
| 6 |
+
Base.metadata.create_all(bind=engine)
|
| 7 |
+
print("Tables created")
|
| 8 |
+
|
| 9 |
+
if __name__ == "__main__":
|
| 10 |
+
create_tables()
|