diff --git a/pyproject.toml b/pyproject.toml index 815e73d..c813ec2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pycatcher" -version = "0.0.63" +version = "0.0.64" description = "This package identifies outlier(s) for a given time-series dataset in simple steps. It supports day, week, month and quarter level time-series data." authors = ["Aseem Anand "] maintainers = ["Jagadish Pamarthi "] diff --git a/src/pycatcher/api_catch.py b/src/pycatcher/api_catch.py index 0eda4ee..9624656 100644 --- a/src/pycatcher/api_catch.py +++ b/src/pycatcher/api_catch.py @@ -2,7 +2,7 @@ from fastapi import FastAPI, HTTPException from pydantic import BaseModel import pandas as pd -from src.pycatcher.catch import find_outliers_iqr +from src.pycatcher.catch import (find_outliers_iqr, detect_outliers_stl, detect_outliers_today_classic) # Define the FastAPI app app = FastAPI( @@ -48,5 +48,70 @@ async def find_outliers_api(inputs: InputModel): return OutputModel(outliers=outliers_list) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@app.post("/detect_outliers_stl", response_model=OutputModel, summary="Detect outliers using STL") +async def detect_outliers_stl_api(inputs: InputModel): + """ + API endpoint to detect outliers using the STL method. + """ + try: + # Convert input data into a pandas DataFrame + df = pd.DataFrame(data=inputs.data, columns=inputs.columns) + + # Ensure the first column is in datetime format + if not pd.api.types.is_datetime64_any_dtype(df.iloc[:, 0]): + try: + df.iloc[:, 0] = pd.to_datetime(df.iloc[:, 0]) + except Exception as e: + raise HTTPException(status_code=400, detail=f"Error converting first column to datetime: {e}") + + # Call the `detect_outliers_stl` function + if not callable(detect_outliers_stl): + raise HTTPException(status_code=500, detail="`detect_outliers_stl` is not defined or not callable") + + # Call the function and get the result + outliers = detect_outliers_stl(df) + + # Check if the result is a DataFrame + if isinstance(outliers, pd.DataFrame): + outliers_list = outliers.reset_index().to_dict(orient="records") + else: + raise HTTPException(status_code=400, detail="No outliers detected or an error occurred.") + + return OutputModel(outliers=outliers_list) + + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + +@app.post("/detect_outliers_today_classic", response_model=OutputModel, summary="Detect today's outliers using " + "Classic method") +async def detect_outliers_today_classic_api(inputs: InputModel): + """ + API endpoint to detect today's outliers using the Classic method. + """ + try: + df = pd.DataFrame(data=inputs.data, columns=inputs.columns) + + if not pd.api.types.is_datetime64_any_dtype(df.iloc[:, 0]): + try: + df.iloc[:, 0] = pd.to_datetime(df.iloc[:, 0]) + except Exception as e: + raise HTTPException(status_code=400, detail=f"Error converting first column to datetime: {e}") + + outliers = detect_outliers_today_classic(df) + + if isinstance(outliers, pd.DataFrame): + outliers_list = outliers.reset_index().to_dict(orient="records") + elif isinstance(outliers, str): # Handle the "No Outliers Today!" case + outliers_list = [{"message": outliers}] + else: + raise HTTPException(status_code=400, detail="Unexpected output from the function.") + + return OutputModel(outliers=outliers_list) + except Exception as e: raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/tests/test_api_catch.py b/tests/test_api_catch.py index fbc2d30..d05d967 100644 --- a/tests/test_api_catch.py +++ b/tests/test_api_catch.py @@ -2,6 +2,9 @@ from src.pycatcher.api_catch import app import pandas as pd +# Create a TestClient instance for the FastAPI app +client = TestClient(app) + def test_find_outliers_api(): # Create a TestClient instance for the FastAPI app client = TestClient(app) @@ -40,4 +43,61 @@ def test_find_outliers_api(): f"Expected outliers {expected_outliers}, but got {response_data['outliers']}" ) +def test_detect_outliers_stl_api(): + # Prepare test data + data = { + 'ID': [1, 2, 3, 4, 5], + 'Value': [10, 12, 14, 100, 15] + } + df = pd.DataFrame(data) + payload = { + "data": df.values.tolist(), + "columns": df.columns.tolist() + } + + response = client.post("/detect_outliers_stl", json=payload) + + # Expected output + expected_outliers = [ + {'ID': '1970-01-01T00:00:00', 'Value': 100.0, 'index': 3} + ] + + assert response.status_code == 200 + response_data = response.json() + assert "outliers" in response_data + + # Validate the outliers detected + assert response_data["outliers"] == expected_outliers, ( + f"Expected outliers {expected_outliers}, but got {response_data['outliers']}" + ) + +def test_detect_outliers_today_classic_api(): + # Prepare test data + data = { + 'ID': [1, 2, 3, 4, 5], + 'Value': [10, 12, 14, 100, 15] + } + + df = pd.DataFrame(data) + payload = { + "data": df.values.tolist(), + "columns": df.columns.tolist() + } + + response = client.post("/detect_outliers_today_classic", json=payload) + + # Expected output + expected_outliers = [ + {'message': 'No Outliers Today!'} + ] + + assert response.status_code == 200 + response_data = response.json() + assert "outliers" in response_data + + # Validate the outliers detected + assert response_data["outliers"] == expected_outliers, ( + f"Expected outliers {expected_outliers}, but got {response_data['outliers']}" + ) + print("Test passed: The API detected outliers correctly.")