init
This commit is contained in:
commit
c06dea8d7e
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
*.git
|
||||||
|
*myenv
|
||||||
38
.github/workflows/deploy.yml
vendored
Normal file
38
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: Deploy to Heroku
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.9 # Match your app's Python version
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Test Application
|
||||||
|
run: |
|
||||||
|
pytest # Assuming you have tests configured
|
||||||
|
|
||||||
|
- name: Deploy to Heroku
|
||||||
|
env:
|
||||||
|
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
|
||||||
|
HEROKU_APP_NAME: ${{ secrets.HEROKU_APP_NAME }}
|
||||||
|
run: |
|
||||||
|
echo "Deploying to Heroku..."
|
||||||
|
heroku container:login
|
||||||
|
heroku container:push web --app $HEROKU_APP_NAME
|
||||||
|
heroku container:release web --app $HEROKU_APP_NAME
|
||||||
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Use the official Python image as a base
|
||||||
|
FROM python:3.9-slim
|
||||||
|
|
||||||
|
# Set environment variables to prevent Python from writing .pyc files and buffer outputs
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the requirements file to the container
|
||||||
|
COPY requirements.txt /app/requirements.txt
|
||||||
|
|
||||||
|
# Install the Python dependencies
|
||||||
|
RUN pip install -r /app/requirements.txt
|
||||||
|
|
||||||
|
# Copy the application code to the container
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
# Expose the port the app runs on
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Ensure the application runs on Heroku's dynamic port
|
||||||
|
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port $PORT"]
|
||||||
151
IRIS.csv
Normal file
151
IRIS.csv
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
sepal_length,sepal_width,petal_length,petal_width,species
|
||||||
|
5.1,3.5,1.4,0.2,Iris-setosa
|
||||||
|
4.9,3,1.4,0.2,Iris-setosa
|
||||||
|
4.7,3.2,1.3,0.2,Iris-setosa
|
||||||
|
4.6,3.1,1.5,0.2,Iris-setosa
|
||||||
|
5,3.6,1.4,0.2,Iris-setosa
|
||||||
|
5.4,3.9,1.7,0.4,Iris-setosa
|
||||||
|
4.6,3.4,1.4,0.3,Iris-setosa
|
||||||
|
5,3.4,1.5,0.2,Iris-setosa
|
||||||
|
4.4,2.9,1.4,0.2,Iris-setosa
|
||||||
|
4.9,3.1,1.5,0.1,Iris-setosa
|
||||||
|
5.4,3.7,1.5,0.2,Iris-setosa
|
||||||
|
4.8,3.4,1.6,0.2,Iris-setosa
|
||||||
|
4.8,3,1.4,0.1,Iris-setosa
|
||||||
|
4.3,3,1.1,0.1,Iris-setosa
|
||||||
|
5.8,4,1.2,0.2,Iris-setosa
|
||||||
|
5.7,4.4,1.5,0.4,Iris-setosa
|
||||||
|
5.4,3.9,1.3,0.4,Iris-setosa
|
||||||
|
5.1,3.5,1.4,0.3,Iris-setosa
|
||||||
|
5.7,3.8,1.7,0.3,Iris-setosa
|
||||||
|
5.1,3.8,1.5,0.3,Iris-setosa
|
||||||
|
5.4,3.4,1.7,0.2,Iris-setosa
|
||||||
|
5.1,3.7,1.5,0.4,Iris-setosa
|
||||||
|
4.6,3.6,1,0.2,Iris-setosa
|
||||||
|
5.1,3.3,1.7,0.5,Iris-setosa
|
||||||
|
4.8,3.4,1.9,0.2,Iris-setosa
|
||||||
|
5,3,1.6,0.2,Iris-setosa
|
||||||
|
5,3.4,1.6,0.4,Iris-setosa
|
||||||
|
5.2,3.5,1.5,0.2,Iris-setosa
|
||||||
|
5.2,3.4,1.4,0.2,Iris-setosa
|
||||||
|
4.7,3.2,1.6,0.2,Iris-setosa
|
||||||
|
4.8,3.1,1.6,0.2,Iris-setosa
|
||||||
|
5.4,3.4,1.5,0.4,Iris-setosa
|
||||||
|
5.2,4.1,1.5,0.1,Iris-setosa
|
||||||
|
5.5,4.2,1.4,0.2,Iris-setosa
|
||||||
|
4.9,3.1,1.5,0.1,Iris-setosa
|
||||||
|
5,3.2,1.2,0.2,Iris-setosa
|
||||||
|
5.5,3.5,1.3,0.2,Iris-setosa
|
||||||
|
4.9,3.1,1.5,0.1,Iris-setosa
|
||||||
|
4.4,3,1.3,0.2,Iris-setosa
|
||||||
|
5.1,3.4,1.5,0.2,Iris-setosa
|
||||||
|
5,3.5,1.3,0.3,Iris-setosa
|
||||||
|
4.5,2.3,1.3,0.3,Iris-setosa
|
||||||
|
4.4,3.2,1.3,0.2,Iris-setosa
|
||||||
|
5,3.5,1.6,0.6,Iris-setosa
|
||||||
|
5.1,3.8,1.9,0.4,Iris-setosa
|
||||||
|
4.8,3,1.4,0.3,Iris-setosa
|
||||||
|
5.1,3.8,1.6,0.2,Iris-setosa
|
||||||
|
4.6,3.2,1.4,0.2,Iris-setosa
|
||||||
|
5.3,3.7,1.5,0.2,Iris-setosa
|
||||||
|
5,3.3,1.4,0.2,Iris-setosa
|
||||||
|
7,3.2,4.7,1.4,Iris-versicolor
|
||||||
|
6.4,3.2,4.5,1.5,Iris-versicolor
|
||||||
|
6.9,3.1,4.9,1.5,Iris-versicolor
|
||||||
|
5.5,2.3,4,1.3,Iris-versicolor
|
||||||
|
6.5,2.8,4.6,1.5,Iris-versicolor
|
||||||
|
5.7,2.8,4.5,1.3,Iris-versicolor
|
||||||
|
6.3,3.3,4.7,1.6,Iris-versicolor
|
||||||
|
4.9,2.4,3.3,1,Iris-versicolor
|
||||||
|
6.6,2.9,4.6,1.3,Iris-versicolor
|
||||||
|
5.2,2.7,3.9,1.4,Iris-versicolor
|
||||||
|
5,2,3.5,1,Iris-versicolor
|
||||||
|
5.9,3,4.2,1.5,Iris-versicolor
|
||||||
|
6,2.2,4,1,Iris-versicolor
|
||||||
|
6.1,2.9,4.7,1.4,Iris-versicolor
|
||||||
|
5.6,2.9,3.6,1.3,Iris-versicolor
|
||||||
|
6.7,3.1,4.4,1.4,Iris-versicolor
|
||||||
|
5.6,3,4.5,1.5,Iris-versicolor
|
||||||
|
5.8,2.7,4.1,1,Iris-versicolor
|
||||||
|
6.2,2.2,4.5,1.5,Iris-versicolor
|
||||||
|
5.6,2.5,3.9,1.1,Iris-versicolor
|
||||||
|
5.9,3.2,4.8,1.8,Iris-versicolor
|
||||||
|
6.1,2.8,4,1.3,Iris-versicolor
|
||||||
|
6.3,2.5,4.9,1.5,Iris-versicolor
|
||||||
|
6.1,2.8,4.7,1.2,Iris-versicolor
|
||||||
|
6.4,2.9,4.3,1.3,Iris-versicolor
|
||||||
|
6.6,3,4.4,1.4,Iris-versicolor
|
||||||
|
6.8,2.8,4.8,1.4,Iris-versicolor
|
||||||
|
6.7,3,5,1.7,Iris-versicolor
|
||||||
|
6,2.9,4.5,1.5,Iris-versicolor
|
||||||
|
5.7,2.6,3.5,1,Iris-versicolor
|
||||||
|
5.5,2.4,3.8,1.1,Iris-versicolor
|
||||||
|
5.5,2.4,3.7,1,Iris-versicolor
|
||||||
|
5.8,2.7,3.9,1.2,Iris-versicolor
|
||||||
|
6,2.7,5.1,1.6,Iris-versicolor
|
||||||
|
5.4,3,4.5,1.5,Iris-versicolor
|
||||||
|
6,3.4,4.5,1.6,Iris-versicolor
|
||||||
|
6.7,3.1,4.7,1.5,Iris-versicolor
|
||||||
|
6.3,2.3,4.4,1.3,Iris-versicolor
|
||||||
|
5.6,3,4.1,1.3,Iris-versicolor
|
||||||
|
5.5,2.5,4,1.3,Iris-versicolor
|
||||||
|
5.5,2.6,4.4,1.2,Iris-versicolor
|
||||||
|
6.1,3,4.6,1.4,Iris-versicolor
|
||||||
|
5.8,2.6,4,1.2,Iris-versicolor
|
||||||
|
5,2.3,3.3,1,Iris-versicolor
|
||||||
|
5.6,2.7,4.2,1.3,Iris-versicolor
|
||||||
|
5.7,3,4.2,1.2,Iris-versicolor
|
||||||
|
5.7,2.9,4.2,1.3,Iris-versicolor
|
||||||
|
6.2,2.9,4.3,1.3,Iris-versicolor
|
||||||
|
5.1,2.5,3,1.1,Iris-versicolor
|
||||||
|
5.7,2.8,4.1,1.3,Iris-versicolor
|
||||||
|
6.3,3.3,6,2.5,Iris-virginica
|
||||||
|
5.8,2.7,5.1,1.9,Iris-virginica
|
||||||
|
7.1,3,5.9,2.1,Iris-virginica
|
||||||
|
6.3,2.9,5.6,1.8,Iris-virginica
|
||||||
|
6.5,3,5.8,2.2,Iris-virginica
|
||||||
|
7.6,3,6.6,2.1,Iris-virginica
|
||||||
|
4.9,2.5,4.5,1.7,Iris-virginica
|
||||||
|
7.3,2.9,6.3,1.8,Iris-virginica
|
||||||
|
6.7,2.5,5.8,1.8,Iris-virginica
|
||||||
|
7.2,3.6,6.1,2.5,Iris-virginica
|
||||||
|
6.5,3.2,5.1,2,Iris-virginica
|
||||||
|
6.4,2.7,5.3,1.9,Iris-virginica
|
||||||
|
6.8,3,5.5,2.1,Iris-virginica
|
||||||
|
5.7,2.5,5,2,Iris-virginica
|
||||||
|
5.8,2.8,5.1,2.4,Iris-virginica
|
||||||
|
6.4,3.2,5.3,2.3,Iris-virginica
|
||||||
|
6.5,3,5.5,1.8,Iris-virginica
|
||||||
|
7.7,3.8,6.7,2.2,Iris-virginica
|
||||||
|
7.7,2.6,6.9,2.3,Iris-virginica
|
||||||
|
6,2.2,5,1.5,Iris-virginica
|
||||||
|
6.9,3.2,5.7,2.3,Iris-virginica
|
||||||
|
5.6,2.8,4.9,2,Iris-virginica
|
||||||
|
7.7,2.8,6.7,2,Iris-virginica
|
||||||
|
6.3,2.7,4.9,1.8,Iris-virginica
|
||||||
|
6.7,3.3,5.7,2.1,Iris-virginica
|
||||||
|
7.2,3.2,6,1.8,Iris-virginica
|
||||||
|
6.2,2.8,4.8,1.8,Iris-virginica
|
||||||
|
6.1,3,4.9,1.8,Iris-virginica
|
||||||
|
6.4,2.8,5.6,2.1,Iris-virginica
|
||||||
|
7.2,3,5.8,1.6,Iris-virginica
|
||||||
|
7.4,2.8,6.1,1.9,Iris-virginica
|
||||||
|
7.9,3.8,6.4,2,Iris-virginica
|
||||||
|
6.4,2.8,5.6,2.2,Iris-virginica
|
||||||
|
6.3,2.8,5.1,1.5,Iris-virginica
|
||||||
|
6.1,2.6,5.6,1.4,Iris-virginica
|
||||||
|
7.7,3,6.1,2.3,Iris-virginica
|
||||||
|
6.3,3.4,5.6,2.4,Iris-virginica
|
||||||
|
6.4,3.1,5.5,1.8,Iris-virginica
|
||||||
|
6,3,4.8,1.8,Iris-virginica
|
||||||
|
6.9,3.1,5.4,2.1,Iris-virginica
|
||||||
|
6.7,3.1,5.6,2.4,Iris-virginica
|
||||||
|
6.9,3.1,5.1,2.3,Iris-virginica
|
||||||
|
5.8,2.7,5.1,1.9,Iris-virginica
|
||||||
|
6.8,3.2,5.9,2.3,Iris-virginica
|
||||||
|
6.7,3.3,5.7,2.5,Iris-virginica
|
||||||
|
6.7,3,5.2,2.3,Iris-virginica
|
||||||
|
6.3,2.5,5,1.9,Iris-virginica
|
||||||
|
6.5,3,5.2,2,Iris-virginica
|
||||||
|
6.2,3.4,5.4,2.3,Iris-virginica
|
||||||
|
5.9,3,5.1,1.8,Iris-virginica
|
||||||
|
414
Untitled.ipynb
Normal file
414
Untitled.ipynb
Normal file
File diff suppressed because one or more lines are too long
85
main.py
Normal file
85
main.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from fastapi import FastAPI, Form, Request
|
||||||
|
from fastapi.responses import HTMLResponse, JSONResponse
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
import logging
|
||||||
|
import joblib
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
handlers=[logging.StreamHandler()]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger("FastAPI Iris Predictor")
|
||||||
|
|
||||||
|
try:
|
||||||
|
model = joblib.load("model.pkl")
|
||||||
|
logger.info("Model loaded successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to load the model: %s", e)
|
||||||
|
raise RuntimeError("Model loading failed.") from e
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# Mount static folder for serving static files like CSS, HTML
|
||||||
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
|
|
||||||
|
|
||||||
|
species_mapping = {0: "Iris Setosa", 1: "Iris Versicolor", 2: "Iris Virginica"}
|
||||||
|
|
||||||
|
@app.get("/", response_class=HTMLResponse)
|
||||||
|
async def home():
|
||||||
|
try:
|
||||||
|
# Serve the home page
|
||||||
|
with open("static/index.html", "r") as file:
|
||||||
|
logger.info("Home page served.")
|
||||||
|
return file.read()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Error serving home page: %s", e)
|
||||||
|
return HTMLResponse(content="Error loading the home page.", status_code=500)
|
||||||
|
|
||||||
|
@app.get("/predict", response_class=HTMLResponse)
|
||||||
|
async def predict():
|
||||||
|
# Serve the prediction page
|
||||||
|
try:
|
||||||
|
with open("static/predict.html", "r") as file:
|
||||||
|
logger.info("Prediction page served.")
|
||||||
|
return file.read()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Error serving prediction page: %s", e)
|
||||||
|
return HTMLResponse(content="Error loading the prediction page.", status_code=500)
|
||||||
|
|
||||||
|
@app.post("/predict")
|
||||||
|
async def predict_species(
|
||||||
|
sepal_length: float = Form(...),
|
||||||
|
sepal_width: float = Form(...),
|
||||||
|
petal_length: float = Form(...),
|
||||||
|
petal_width: float = Form(...)
|
||||||
|
):
|
||||||
|
feature_names = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
|
||||||
|
try:
|
||||||
|
input_df = pd.DataFrame([[sepal_length, sepal_width, petal_length, petal_width]], columns=feature_names)
|
||||||
|
logger.info("Input data received: %s", input_df)
|
||||||
|
|
||||||
|
# Predict using the model
|
||||||
|
prediction = model.predict(input_df)[0]
|
||||||
|
|
||||||
|
# Get the species name
|
||||||
|
species = species_mapping.get(prediction, "Unknown")
|
||||||
|
logger.info("Prediction made: %s", species)
|
||||||
|
|
||||||
|
return { "prediction": species}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Error making prediction: %s", e)
|
||||||
|
return JSONResponse(content={"error": "Failed to make a prediction"}, status_code=500)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
port = int(os.getenv("PORT", 8000)) # Use Heroku's PORT
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=port)
|
||||||
|
|
||||||
10
requirements.txt
Normal file
10
requirements.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
fastapi
|
||||||
|
joblib
|
||||||
|
numpy
|
||||||
|
pandas
|
||||||
|
pydantic
|
||||||
|
pytest
|
||||||
|
httpx
|
||||||
|
uvicorn
|
||||||
|
scikit-learn
|
||||||
|
python-multipart
|
||||||
18
static/index.html
Normal file
18
static/index.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Iris Prediction Home</title>
|
||||||
|
<link rel="stylesheet" href="/static/styles.css">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Welcome to Iris Species Predictor</h1>
|
||||||
|
<p>This application predicts the species of an Iris flower based on its features.</p>
|
||||||
|
<a href="/predict" class="button">Go to Prediction</a>
|
||||||
|
<p>© Meta Brains. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
57
static/predict.html
Normal file
57
static/predict.html
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Iris Prediction</title>
|
||||||
|
<link rel="stylesheet" href="/static/styles.css">
|
||||||
|
<script>
|
||||||
|
// JavaScript function to handle the form submission
|
||||||
|
async function submitForm(event) {
|
||||||
|
event.preventDefault(); // Prevent the default form submission
|
||||||
|
|
||||||
|
// Get the form data
|
||||||
|
const formData = new FormData(event.target);
|
||||||
|
|
||||||
|
// Prepare the data to send to the FastAPI endpoint
|
||||||
|
const data = new URLSearchParams();
|
||||||
|
for (const [key, value] of formData.entries()) {
|
||||||
|
data.append(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the data to FastAPI via POST request
|
||||||
|
const response = await fetch("/predict", {
|
||||||
|
method: "POST",
|
||||||
|
body: data
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the JSON response and update the result on the page
|
||||||
|
const result = await response.json();
|
||||||
|
document.getElementById("prediction").innerText = "Predicted Species: " + result.prediction;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Predict Iris Species</h1>
|
||||||
|
<form onsubmit="submitForm(event)">
|
||||||
|
<label for="sepal_length">Sepal Length (cm):</label>
|
||||||
|
<input type="number" step="0.1" name="sepal_length" id="sepal_length" required>
|
||||||
|
|
||||||
|
<label for="sepal_width">Sepal Width (cm):</label>
|
||||||
|
<input type="number" step="0.1" name="sepal_width" id="sepal_width" required>
|
||||||
|
|
||||||
|
<label for="petal_length">Petal Length (cm):</label>
|
||||||
|
<input type="number" step="0.1" name="petal_length" id="petal_length" required>
|
||||||
|
|
||||||
|
<label for="petal_width">Petal Width (cm):</label>
|
||||||
|
<input type="number" step="0.1" name="petal_width" id="petal_width" required>
|
||||||
|
|
||||||
|
<button type="submit" class="button">Predict</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Display prediction result here -->
|
||||||
|
<div id="prediction"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
65
static/styles.css
Normal file
65
static/styles.css
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #87CEEB, #FFDEE9);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
margin: 50px auto;
|
||||||
|
max-width: 600px;
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
form label {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
width: 80%;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
background: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
27
test_integration.py
Normal file
27
test_integration.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import pytest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from main import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
def test_home_endpoint():
|
||||||
|
"""Test if the home page loads successfully."""
|
||||||
|
response = client.get("/")
|
||||||
|
assert response.status_code == 200, "Failed to load the home page."
|
||||||
|
|
||||||
|
def test_predict_page_endpoint():
|
||||||
|
"""Test if the prediction page loads successfully."""
|
||||||
|
response = client.get("/predict")
|
||||||
|
assert response.status_code == 200, "Failed to load the prediction page."
|
||||||
|
|
||||||
|
def test_predict_species_endpoint():
|
||||||
|
"""Test if the predict endpoint works."""
|
||||||
|
payload = {
|
||||||
|
"sepal_length": 5.1,
|
||||||
|
"sepal_width": 3.5,
|
||||||
|
"petal_length": 1.4,
|
||||||
|
"petal_width": 0.2
|
||||||
|
}
|
||||||
|
response = client.post("/predict", data=payload)
|
||||||
|
assert response.status_code == 200, "Failed to predict species."
|
||||||
|
assert "prediction" in response.json(), "Response missing prediction field."
|
||||||
15
test_unit.py
Normal file
15
test_unit.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import pytest
|
||||||
|
import pandas as pd
|
||||||
|
from main import model, species_mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_model_prediction():
|
||||||
|
# Arrange: Create a test input DataFrame
|
||||||
|
input_df = pd.DataFrame([[5.1, 3.5, 1.4, 0.2]], columns=["sepal_length", "sepal_width", "petal_length", "petal_width"])
|
||||||
|
|
||||||
|
# Act: Perform prediction
|
||||||
|
prediction = model.predict(input_df)[0]
|
||||||
|
species = species_mapping.get(prediction, "Unknown")
|
||||||
|
|
||||||
|
# Assert: Check prediction result
|
||||||
|
assert species == "Iris Setosa", "The prediction did not return the expected result."
|
||||||
Loading…
x
Reference in New Issue
Block a user