Skip to content

jryusuf/rule_engine_project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rule Engine API Project

Overview

This project implements a flexible rule engine exposed via a FastAPI REST API. The core engine executes decision logic defined in graph-based data structures, allowing for different sets of rules (e.g., planning permission, discount eligibility) to be loaded and evaluated dynamically.

The primary example implemented is a set of rules for determining UK planning permission requirements for fences, gates, and walls, based on criteria like location, height, and specific planning constraints. The system is designed with separation of concerns, testability, and containerized deployment in mind.

Features

  • REST API: FastAPI endpoint (/planning) to evaluate planning logic.
  • Data Validation: Pydantic models for robust request/response validation and OpenAPI documentation.
  • Rule Engine: Generic Python engine (engine.py) executes logic defined as a graph (Python dictionary).
  • Decoupled Logic: Logic graph definitions are stored separately (e.g., planning_logic.py, discount_logic.py).
  • Factory Pattern: A factory (factory.py) loads the appropriate logic graph on demand, decoupling the API/service layer from the definition source.
  • Configurable Logic: Graphs include configuration for start nodes and default outcomes for error handling.
  • Structured Logging: Centralized logging configuration (logging_config.py) outputs to both console and a rotating file.
  • Containerization: Dockerfile provided for building a container image.
  • Persistent Logs: Docker volume mounting allows logs to persist outside the container lifecycle.
  • Testing: Includes unit tests for the engine/factory and integration tests for the API endpoint using pytest and TestClient.
  • Modular Structure: Organized by core engine logic (src) and API features (api/planning).

Technology Stack

  • Language: Python 3.10+
  • Web Framework: FastAPI
  • Data Validation: Pydantic
  • ASGI Server: Uvicorn
  • Testing: Pytest, FastAPI TestClient, HTTPX
  • Containerization: Docker
  • Standard Libraries: logging, importlib, os, sys, pathlib

Prerequisites

  • Python (3.10 or later recommended)
  • pip (Python package installer)
  • Docker Desktop or Docker Engine/CLI

Project Setup (Development)

  1. Clone the Repository:

    git clone https://github.com/jryusuf/rule_engine_project
    cd rule_engine_project
  2. Create and Activate Virtual Environment:

    # Linux/macOS
    python3 -m venv venv
    source venv/bin/activate
    
    # Windows
    python -m venv venv
    .\venv\Scripts\activate
  3. Install Dependencies:

    pip install -r requirements.txt

Running the Application (Development)

To run the FastAPI application locally for development:

  1. Ensure your virtual environment is activated.

  2. Navigate to the project root directory (rule_engine_project/).

  3. Run Uvicorn:

    uvicorn api.server:app --reload --app-dir .
    • --reload: Enables auto-reloading on code changes.
    • --app-dir .: Helps Uvicorn find the api package correctly from the root.
  4. The API will typically be available at http://127.0.0.1:8000.

  5. Access interactive documentation:

    • Swagger UI: http://127.0.0.1:8000/docs
    • ReDoc: http://127.0.0.1:8000/redoc

Running Tests

  1. Ensure your virtual environment is activated and development dependencies are installed.
  2. Navigate to the project root directory (rule_engine_project/).
  3. Run pytest:
    pytest
    Or for more verbose output:
    pytest -v

Deployment (Docker)

The application is designed to be deployed as a Docker container.

  1. Build the Docker Image: Navigate to the project root directory and run:

    docker build -t planning-engine-api .

    (Replace planning-engine-api with your desired image tag).

  2. Run the Docker Container: To run the container and map the API port and log volume:

    # Option A: Using a named volume for logs (Recommended)
    docker volume create api_logs # Optional: create volume beforehand
    docker run -d -p 8001:8000 \
      -v api_logs:/app/logs \
      --name planning-api-container \
      planning-engine-api
    
    # Option B: Using a host directory mount for logs
    # mkdir logs # Create logs directory in project root first if it doesn't exist
    # docker run -d -p 8001:8000 \
    #   -v "$(pwd)/logs":/app/logs \
    #   --name planning-api-container \
    #   planning-engine-api
    • -d: Run in detached mode.
    • -p 8001:8000: Map port 8001 on the host to port 8000 in the container. Access the API via http://localhost:8001.
    • -v api_logs:/app/logs or -v "$(pwd)/logs":/app/logs: Mounts a volume to persist logs written to /app/logs inside the container.
    • --name planning-api-container: Assigns a name to the container.
    • planning-engine-api: The name of the image to run.
  3. Accessing the API:

    • Root: http://localhost:8001/
    • Docs: http://localhost:8001/docs
    • Endpoint: POST http://localhost:8001/planning/evaluate
  4. Managing the Container:

    • View Logs: docker logs planning-api-container
    • Stop: docker stop planning-api-container
    • Remove: docker rm planning-api-container (Logs in the volume will persist)

Design Decisions & Architecture

  • Separation of Concerns:
    • The core rule engine logic (src/rule_engine_core) is kept separate from the API framework (api). This makes the engine potentially reusable in other contexts (e.g., CLI tool, different web framework).
    • Within the API, concerns are further separated into Routers (HTTP handling), Services (business logic orchestration), and Models (data structure definition).
  • Feature-Based API Structure: The api directory is organized by feature (e.g., api/planning/). This promotes modularity and makes it easier to add new, distinct rule evaluation endpoints in the future by creating new feature folders.
  • Data-Driven Logic Graph:
    • The core decision logic is defined as Python dictionaries (planning_logic.py, discount_logic.py) representing a graph structure. This separates the logic definition from the execution code.
    • Nodes have types (start, decision, terminal).
    • Decision nodes use Python lambda functions for concise condition evaluation against input data.
    • Connections define the flow based on decision outcomes.
    • A config section within the graph specifies the start node and a default outcome for error handling.
  • Factory Pattern (factory.py):
    • A LogicFactory is used to load the appropriate logic graph dictionary based on a name (e.g., "planning").
    • This decouples the Service layer (which needs a graph) from the specifics of how and from where the graph is loaded (currently Python modules, but could be changed to JSON, YAML, database within the factory).
    • Includes caching to improve performance by avoiding redundant loading.
  • Generic Engine (engine.py):
    • The execute_logic_graph_with_default function is designed to be generic. It takes any valid graph dictionary and input data and traverses the defined logic.
    • It handles different node types and evaluates conditions dynamically.
  • Centralized Logging (logging_config.py):
    • Logging is configured once using a dedicated utility function called at application startup (api/server.py).
    • Outputs to both console (for docker logs) and a rotating file (/app/logs/api.log inside the container).
    • File logging uses a volume mount in Docker to persist logs across container restarts/recreations.
  • Testing Strategy:
    • Unit Tests (tests/test_engine.py, tests/test_factory.py): Test the core engine and factory components in isolation.
    • Integration Tests (tests/test_api_planning.py): Use FastAPI's TestClient to test the full request/response cycle for the API endpoint, including routing, validation, service interaction, and engine execution, without needing a running server or Docker.
  • Containerization (Docker):
    • Provides a consistent, isolated environment for running the application.
    • Simplifies deployment and dependency management.
    • Uses multi-stage builds (implicitly by copying requirements first) to optimize build caching.
    • Configured for persistent logging using volumes.

About

Containerized REST API for a construction planning permission requirements in the UK

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published