4. Python Integration & Applications
Building practical disease diagnosis systems
Python Integration with Plant Disease Ontology
1. Setting Up the Environment
# Create and activate virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Install required packages
pip install owlready2 pandas numpy jupyter2. Loading and Querying the Ontology
#| eval: true
#| echo: true
from owlready2 import *
import pandas as pd
# Load the ontology
onto_path.append("path/to/your/ontology")
onto = get_ontology("plant-disease.owl").load()
# Get all diseases
diseases = list(onto.Disease.instances())
print(f"Loaded {len(diseases)} diseases")
# Create a DataFrame of diseases and their treatments
disease_data = []
for disease in diseases:
treatments = [t.name for t in disease.treatedWith] if disease.treatedWith else []
disease_data.append({
'Disease': disease.name,
'Type': disease.__class__.__name__,
'Severity': disease.severity[0] if disease.severity else None,
'Treatments': ", ".join(treatments)
})
df_diseases = pd.DataFrame(disease_data)
df_diseases.head()3. Automated Diagnosis System
class PlantDiseaseDiagnoser:
def __init__(self, ontology_path):
self.onto = get_ontology(ontology_path).load()
self.symptom_map = self._build_symptom_map()
def _build_symptom_map(self):
"""Create a map from symptoms to possible diseases"""
symptom_map = {}
for disease in self.onto.Disease.instances():
if hasattr(disease, 'hasSymptom') and disease.hasSymptom:
for symptom in disease.hasSymptom:
if symptom not in symptom_map:
symptom_map[symptom] = []
symptom_map[symptom].append(disease)
return symptom_map
def diagnose(self, symptoms, environment=None):
"""
Diagnose plant based on symptoms and environment
Args:
symptoms: List of symptom names
environment: Dict of environmental conditions
Returns:
List of (disease, confidence) tuples
"""
disease_scores = {}
# Score diseases based on symptoms
for symptom_name in symptoms:
if symptom_name in self.symptom_map:
for disease in self.symptom_map[symptom_name]:
disease_scores[disease] = disease_scores.get(disease, 0) + 1
# Normalize scores
total_symptoms = len(symptoms)
results = []
for disease, score in disease_scores.items():
confidence = (score / total_symptoms) * 100
# Apply environment factors if provided
if environment and hasattr(disease, 'environmentalFactors'):
confidence = self._adjust_for_environment(confidence, disease, environment)
results.append((disease, min(round(confidence, 2), 100)))
return sorted(results, key=lambda x: x[1], reverse=True)
def _adjust_for_environment(self, confidence, disease, environment):
"""Adjust confidence based on environmental factors"""
# Implementation would check temperature, humidity, etc.
# against disease's preferred conditions
return confidence
# Example usage
if __name__ == "__main__":
diagnoser = PlantDiseaseDiagnoser("plant-disease.owl")
symptoms = ["YellowSpots", "WhitePowder"]
environment = {"temperature": 25, "humidity": 75}
print("Diagnosis Results:")
for disease, confidence in diagnoser.diagnose(symptoms, environment):
print(f"- {disease.name}: {confidence}% confidence")4. Integration with Machine Learning
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from owlready2 import get_ontology, default_world
class OntologyAwareClassifier:
def __init__(self, ontology_path):
self.onto = get_ontology(ontology_path).load()
self.model = RandomForestClassifier()
self.feature_names = None
def extract_features(self, plant_data):
"""Extract features using ontology reasoning"""
# This would extract relevant features based on the ontology
# For example, checking disease symptoms, plant types, etc.
features = []
for data in plant_data:
# Example feature extraction
feature_vector = [
len(data.get('symptoms', [])),
1 if 'YellowSpots' in data.get('symptoms', []) else 0,
data.get('environment', {}).get('temperature', 0),
data.get('environment', {}).get('humidity', 0)
]
features.append(feature_vector)
self.feature_names = [
'num_symptoms',
'has_yellow_spots',
'temperature',
'humidity'
]
return np.array(features)
def train(self, X, y):
"""Train the classifier"""
self.model.fit(X, y)
def predict_with_confidence(self, X):
"""Make predictions with confidence scores"""
probas = self.model.predict_proba(X)
classes = self.model.classes_
return [(classes[i], proba) for i, proba in enumerate(probas[0])]
# Example usage
if __name__ == "__main__":
# Initialize the classifier with ontology
classifier = OntologyAwareClassifier("plant-disease.owl")
# Example training data (in practice, you'd load real data)
plant_data = [
{'symptoms': ['YellowSpots', 'WhitePowder'], 'environment': {'temperature': 25, 'humidity': 70}},
# Add more training examples
]
y = ['PowderyMildew'] # Corresponding labels
# Train the model
X = classifier.extract_features(plant_data)
classifier.train(X, y)
# Make a prediction
test_data = [{'symptoms': ['YellowSpots'], 'environment': {'temperature': 24, 'humidity': 68}}]
X_test = classifier.extract_features(test_data)
predictions = classifier.predict_with_confidence(X_test)
print("\nPrediction Results:")
for disease, confidence in predictions:
print(f"- {disease}: {confidence*100:.1f}%")5. Building a Simple Web Application
#| eval: false
#| echo: true
from flask import Flask, request, jsonify, render_template_string
from owlready2 import get_ontology, default_world
import pandas as pd
app = Flask(__name__)
# Initialize the ontology and diagnostic system
onto_path.append("path/to/your/ontology")
onto = get_ontology("plant-disease.owl").load()
# HTML template for the web interface
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<title>Plant Disease Diagnosis System</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1 class="mb-4">π± Plant Disease Diagnosis</h1>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Enter Symptoms</h5>
<form id="diagnosisForm">
<div class="mb-3">
<label class="form-label">Select Symptoms:</label>
<select multiple class="form-select" id="symptoms" name="symptoms" size="8">
{% for symptom in symptoms %}
<option value="{{ symptom }}">{{ symptom }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label">Temperature (Β°C)</label>
<input type="number" class="form-control" id="temperature" name="temperature" step="0.1">
</div>
<div class="mb-3">
<label class="form-label">Humidity (%)</label>
<input type="number" class="form-control" id="humidity" name="humidity" min="0" max="100">
</div>
<button type="submit" class="btn btn-primary">Diagnose</button>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Diagnosis Results</h5>
<div id="results">
<p class="text-muted">Submit symptoms to see diagnosis results.</p>
</div>
</div>
</div>
</div>
</div>
<div class="mt-4">
<h4>Recent Diagnoses</h4>
<table class="table table-striped" id="history">
<thead>
<tr>
<th>Time</th>
<th>Symptoms</th>
<th>Diagnosis</th>
<th>Confidence</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
$('#diagnosisForm').on('submit', function(e) {
e.preventDefault();
const symptoms = $('#symptoms').val();
const temperature = $('#temperature').val();
const humidity = $('#humidity').val();
$.ajax({
url: '/diagnose',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
symptoms: symptoms,
environment: {
temperature: parseFloat(temperature),
humidity: parseFloat(humidity)
}
}),
success: function(response) {
let resultsHtml = '';
if (response.diagnoses && response.diagnoses.length > 0) {
resultsHtml = '<div class="list-group">';
response.diagnoses.forEach(diag => {
resultsHtml += `
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">${diag.disease}</h6>
<span class="badge bg-${diag.confidence > 70 ? 'success' : diag.confidence > 40 ? 'warning' : 'danger'} rounded-pill">
${diag.confidence}%
</span>
</div>
<p class="mb-1">${diag.description || 'No description available'}</p>
<small>Recommended treatments: ${diag.treatments.join(', ') || 'None specified'}</small>
</div>`;
});
resultsHtml += '</div>';
// Add to history
const now = new Date().toLocaleTimeString();
$('#history tbody').prepend(`
<tr>
<td>${now}</td>
<td>${symptoms.join(', ')}</td>
<td>${response.diagnoses[0].disease}</td>
<td>${response.diagnoses[0].confidence}%</td>
</tr>
`);
} else {
resultsHtml = '<div class="alert alert-warning">No matching diseases found for these symptoms.</div>';
}
$('#results').html(resultsHtml);
},
error: function() {
$('#results').html('<div class="alert alert-danger">Error processing your request. Please try again.</div>');
}
});
});
});
</script>
</body>
</html>
"""
# Get all symptoms for the dropdown
symptoms = sorted(list(set(
symptom.name for disease in onto.Disease.instances()
if hasattr(disease, 'hasSymptom') and disease.hasSymptom
for symptom in disease.hasSymptom
)))
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE, symptoms=symptoms)
@app.route('/diagnose', methods=['POST'])
def diagnose():
data = request.get_json()
symptoms = data.get('symptoms', [])
environment = data.get('environment', {})
# Simple diagnosis logic (extend with your actual diagnosis logic)
diagnoses = []
for disease in onto.Disease.instances():
if hasattr(disease, 'hasSymptom'):
disease_symptoms = [s.name for s in disease.hasSymptom]
matching_symptoms = set(symptoms) & set(disease_symptoms)
if matching_symptoms:
confidence = (len(matching_symptoms) / len(symptoms)) * 100
# Get treatment information if available
treatments = []
if hasattr(disease, 'treatedWith'):
treatments = [t.name for t in disease.treatedWith]
# Get description if available
description = ""
if hasattr(disease, 'comment') and disease.comment:
description = disease.comment[0] if isinstance(disease.comment, list) else str(disease.comment)
diagnoses.append({
'disease': disease.name,
'confidence': round(confidence, 2),
'treatments': treatments,
'description': description
})
# Sort by confidence (highest first)
diagnoses.sort(key=lambda x: x['confidence'], reverse=True)
return jsonify({
'diagnoses': diagnoses
})
if __name__ == '__main__':
app.run(debug=True)5.1 Extending the Ontology with New Disease Cases
Adding Fusarium Wheat Blast to the Ontology
Letβs walk through extending our plant disease ontology to include Fusarium Wheat Blast, a serious fungal disease affecting wheat crops. This example demonstrates how to integrate a new disease with its symptoms, treatments, and relationships.
#| eval: true
#| echo: true
from owlready2 import *
import datetime
# Load the existing ontology
onto = get_ontology("plant-disease.owl").load()
with onto:
# Define the pathogen if it doesn't exist
if not onto.search_one(iri="*#Magnaporthe_oryzae"):
# Create the pathogen
magnaporthe = onto.Fungus("Magnaporthe_oryzae")
magnaporthe.scientificName = "Magnaporthe oryzae"
magnaporthe.commonName = ["Rice blast fungus", "Wheat blast fungus"]
magnaporthe.description = "A plant-pathogenic fungus that causes rice blast and wheat blast diseases."
else:
magnaporthe = onto.search_one(iri="*#Magnaporthe_oryzae")
# Create the disease
wheat_blast = onto.Disease("Fusarium_Wheat_Blast")
wheat_blast.label = ["Wheat Blast", "Wheat Blast Disease"]
wheat_blast.scientificName = "Pyricularia oryzae Triticum pathotype"
wheat_blast.altName = ["Wheat Blast", "Brusone"]
wheat_blast.description = "A devastating fungal disease of wheat caused by Magnaporthe oryzae Triticum pathotype."
# Add disease symptoms
symptoms = [
("Bleached_Spike", "Bleached spike symptoms with white to light gray lesions"),
("Elliptical_Lesions", "Elliptical or diamond-shaped lesions with gray centers"),
("Grain_Abortion", "Partial or complete grain abortion"),
("Leaf_Blast", "Lesions on leaves with dark borders")
]
symptom_instances = []
for symptom_name, desc in symptoms:
symptom = onto.Symptom(symptom_name.replace(" ", "_"))
symptom.label = [symptom_name]
symptom.description = desc
symptom_instances.append(symptom)
wheat_blast.hasSymptom = symptom_instances
# Add causal relationships
wheat_blast.causedBy = [magnaporthe]
# Add affected plant parts
wheat_blast.affectsPlantPart = [
onto.search_one(iri="*#Spike"),
onto.search_one(iri="*#Leaf"),
onto.search_one(iri="*#Grain")
]
# Add environmental conditions that favor the disease
favorable_conditions = onto.EnvironmentalCondition("Favorable_WheatBlast_Conditions")
favorable_conditions.hasTemperature = [onto.Temperature(value=25, unit="Β°C")] # Optimal around 25Β°C
favorable_conditions.hasHumidity = [onto.Humidity(value=90, unit="%")] # High humidity
favorable_conditions.hasRainfall = [onto.Rainfall(value=10, unit="mm/day")] # Rainy conditions
wheat_blast.favoredBy = [favorable_conditions]
# Add management practices
management = onto.ManagementPractice("WheatBlast_Management")
management.includesPractice = [
"Use of resistant varieties",
"Fungicide application at booting stage",
"Crop rotation with non-host crops",
"Destruction of infected crop residues"
]
wheat_blast.managedBy = [management]
# Add chemical controls
fungicides = [
("Azoxystrobin", "QCL (Quinone outside inhibitor)", "Preventative and curative"),
("Tebuconazole", "DMI (Demethylation inhibitor)", "Protectant")
]
for name, mode, action in fungicides:
fungicide = onto.Fungicide(name.replace(" ", "_"))
fungicide.modeOfAction = mode
fungicide.actionType = action
wheat_blast.treatedWith.append(fungicide)
# Add geographic distribution
distribution = onto.GeographicDistribution("WheatBlast_Distribution")
distribution.occursIn = ["South America", "South Asia", "Africa"]
distribution.firstReported = "1985"
wheat_blast.hasDistribution = [distribution]
# Add references
reference = onto.ScientificReference("WheatBlast_Ref1")
reference.title = "Wheat Blast: A New Fungal Inhabitant to Bangladesh Threatening World Wheat Production"
reference.authors = ["Tofazzal Islam", "et al."]
reference.year = "2016"
reference.journal = "Plant Pathology Journal"
wheat_blast.hasReference = [reference]
# Save the updated ontology
onto.save("plant-disease-updated.owl")Validating the New Disease Entry
After adding the new disease, itβs important to validate the ontology for consistency and run reasoners to infer new knowledge.
#| eval: true
#| echo: true
from owlready2 import sync_reasoner
# Load the updated ontology
onto = get_ontology("plant-disease-updated.owl").load()
# Run the reasoner to infer new knowledge
with onto:
print("Running reasoner...")
sync_reasoner()
# Check for inconsistencies
inconsistent = list(default_world.inconsistent_classes())
print(f"Inconsistent classes: {len(inconsistent)}")
for cls in inconsistent:
print(f" - {cls}")
# Save the inferred ontology
onto.save("plant-disease-inferred.owl")
# Query the new disease information
wheat_blast = onto.search_one(iri="*#Fusarium_Wheat_Blast")
print(f"\nDisease: {wheat_blast.label[0]}")
print(f"Pathogen: {wheat_blast.causedBy[0].label[0]}")
print("\nSymptoms:")
for symptom in wheat_blast.hasSymptom:
print(f" - {symptom.label[0]}: {symptom.description}")
print("\nManagement Practices:")
for practice in wheat_blast.managedBy[0].includesPractice:
print(f" - {practice}")
print("\nRecommended Fungicides:")
for fungicide in wheat_blast.treatedWith:
print(f" - {fungicide.name} ({fungicide.modeOfAction}): {fungicide.actionType}")Visualizing the New Disease in the Ontology
To better understand how the new disease fits into the existing ontology, we can generate a visualization of its relationships.
#| eval: true
#| echo: true
from owlready2 import get_ontology
import pydot
from IPython.display import Image, display
def visualize_ontology(ontology_path, focus_class, depth=2):
"""Generate a visualization of the ontology focusing on a specific class"""
# Load the ontology
onto = get_ontology(ontology_path).load()
# Create a new directed graph
graph = pydot.Dot(graph_type='digraph', rankdir='LR')
# Add nodes and edges for the focus class and its relationships
focus_node = pydot.Node(focus_class.name, style='filled', fillcolor='lightblue')
graph.add_node(focus_node)
# Get all properties of the focus class
for prop in dir(onto[focus_class.name]):
if not prop.startswith('_') and not prop in ['get_iri', 'is_a']:
try:
values = getattr(onto[focus_class.name], prop)
if values: # Only process if there are values
if not isinstance(values, list):
values = [values]
for value in values:
# Skip empty values or literals
if value is None or isinstance(value, (str, int, float, bool)):
continue
# Create a node for the value
if hasattr(value, 'name'):
value_node = pydot.Node(value.name, shape='box')
graph.add_node(value_node)
graph.add_edge(pydot.Edge(focus_node, value_node, label=prop))
except:
continue
# Save and display the graph
output_path = f"{focus_class.name.lower()}_ontology.png"
graph.write_png(output_path)
display(Image(filename=output_path))
# Visualize the Wheat Blast disease in the ontology
visualize_ontology("plant-disease-updated.owl", onto.Fusarium_Wheat_Blast)Practical Exercise: Adding Another Disease
As an exercise, try adding a new disease to the ontology following the same pattern. For example, you could add βBanana Fusarium Wilt TR4β with the following characteristics:
- Pathogen: Fusarium odoratissimum (Fusarium oxysporum f. sp. cubense Tropical Race 4)
- Symptoms:
- Yellowing of older leaves
- Wilting and collapse of leaves
- Reddish-brown vascular discoloration
- Affected Parts: Stem, vascular system
- Management:
- Use of disease-free planting material
- Soil disinfection
- Quarantine measures
- Distribution: Southeast Asia, Africa, Middle East, Latin America
6. Advanced Features and Integrations
6.1 Real-time Sensor Data Integration
import requests
from datetime import datetime
class SensorDataIntegrator:
def __init__(self, ontology):
self.onto = ontology
self.sensor_data = {}
def fetch_environmental_data(self, location_id):
"""Fetch environmental data from IoT sensors or weather API"""
# Example: Fetch from OpenWeatherMap API
# In production, replace with actual sensor data or API calls
try:
# This is a mock implementation
self.sensor_data = {
'temperature': 24.5, # in Β°C
'humidity': 65.2, # in %
'soil_moisture': 42.8, # in %
'light_intensity': 850, # in Β΅mol/mΒ²/s
'timestamp': datetime.now().isoformat()
}
return True
except Exception as e:
print(f"Error fetching sensor data: {e}")
return False
def update_ontology_with_sensor_data(self):
"""Update the ontology with the latest sensor readings"""
with self.onto:
# Create or update environmental conditions in the ontology
env = self.onto.Environment("current_environment")
env.hasTemperature = [self.onto.Temperature(value=self.sensor_data['temperature'], unit="Β°C")]
env.hasHumidity = [self.onto.Humidity(value=self.sensor_data['humidity'], unit="%")]
env.hasSoilMoisture = [self.onto.SoilMoisture(value=self.sensor_data['soil_moisture'], unit="%")]
env.timestamp = [self.sensor_data['timestamp']]
# Sync changes
sync_reasoner()
return env6.2 Batch Processing for Large Datasets
import pandas as pd
from concurrent.futures import ThreadPoolExecutor
class BatchProcessor:
def __init__(self, ontology_path, batch_size=100):
self.ontology = get_ontology(ontology_path).load()
self.batch_size = batch_size
def process_csv_batch(self, csv_path):
"""Process plant observation data from CSV in batches"""
results = []
# Read CSV in chunks
for chunk in pd.read_csv(csv_path, chunksize=self.batch_size):
chunk_results = self._process_chunk(chunk)
results.extend(chunk_results)
# Sync with ontology after each batch
with self.ontology:
sync_reasoner()
return pd.DataFrame(results)
def _process_chunk(self, chunk):
"""Process a single batch of data"""
results = []
for _, row in chunk.iterrows():
# Process each row (example: create individuals in the ontology)
plant_id = f"plant_{row['id']}"
plant = self.ontology.Plant(plant_id)
# Add properties from CSV
if 'species' in row:
plant.hasSpecies = [row['species']]
if 'health_status' in row:
plant.hasHealthStatus = [row['health_status']]
results.append({
'plant_id': plant_id,
'status': 'processed',
'timestamp': datetime.now().isoformat()
})
return results7. Deployment and Scaling
7.1 Containerization with Docker
# Dockerfile for Plant Disease Diagnosis System
FROM python:3.9-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Environment variables
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
ENV ONTOLOGY_PATH=/data/plant-disease.owl
# Create data directory
RUN mkdir -p /data
# Expose port
EXPOSE 5000
# Run the application
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]7.2 Kubernetes Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: plant-disease-diagnosis
labels:
app: plant-disease
spec:
replicas: 3
selector:
matchLabels:
app: plant-disease
template:
metadata:
labels:
app: plant-disease
spec:
containers:
- name: app
image: your-username/plant-disease-diagnosis:latest
ports:
- containerPort: 5000
volumeMounts:
- name: ontology-volume
mountPath: /data
env:
- name: FLASK_ENV
value: "production"
- name: ONTOLOGY_PATH
value: "/data/plant-disease.owl"
volumes:
- name: ontology-volume
persistentVolumeClaim:
claimName: ontology-pvc
---
# Service
apiVersion: v1
kind: Service
metadata:
name: plant-disease-service
spec:
selector:
app: plant-disease
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: LoadBalancer8. Practical Exercises
Exercise 1: Implement a Caching Layer
from functools import lru_cache
from datetime import datetime, timedelta
class OntologyCache:
def __init__(self, ontology_path, ttl_hours=24):
self.ontology_path = ontology_path
self.ttl = timedelta(hours=ttl_hours)
self.last_loaded = None
self._ontology = None
@property
def ontology(self):
if self._should_reload():
self._load_ontology()
return self._ontology
def _should_reload(self):
if self._ontology is None or self.last_loaded is None:
return True
return datetime.now() - self.last_loaded > self.ttl
def _load_ontology(self):
print("Loading ontology...")
self._ontology = get_ontology(self.ontology_path).load()
self.last_loaded = datetime.now()
@lru_cache(maxsize=128)
def get_disease_by_name(self, disease_name):
"""Get disease by name with caching"""
with self.ontology:
disease = self.ontology.search_one(iri=f"*#{disease_name}")
if disease:
return {
'name': disease.name,
'symptoms': [s.name for s in disease.hasSymptom] if hasattr(disease, 'hasSymptom') else [],
'treatments': [t.name for t in disease.treatedWith] if hasattr(disease, 'treatedWith') else []
}
return NoneExercise 2: Implement a REST API with FastAPI
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn
app = FastAPI(title="Plant Disease Diagnosis API")
class SymptomRequest(BaseModel):
symptoms: List[str]
environment: Optional[dict] = {}
crop_type: Optional[str] = None
class DiseasePrediction(BaseModel):
disease: str
confidence: float
treatments: List[str]
description: Optional[str] = None
@app.post("/api/diagnose", response_model=List[DiseasePrediction])
async def diagnose_disease(request: SymptomRequest):
"""
Diagnose plant disease based on symptoms and environmental conditions
"""
try:
# Initialize the diagnoser (from previous examples)
diagnoser = PlantDiseaseDiagnoser("plant-disease.owl")
# Get predictions
predictions = diagnoser.diagnose(
symptoms=request.symptoms,
environment=request.environment
)
# Format response
results = []
for disease, confidence in predictions:
treatments = [t.name for t in disease.treatedWith] if hasattr(disease, 'treatedWith') else []
description = disease.comment[0] if hasattr(disease, 'comment') and disease.comment else None
results.append(DiseasePrediction(
disease=disease.name,
confidence=confidence,
treatments=treatments,
description=description
))
return results
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)9. Conclusion and Next Steps
Key Takeaways
- Flexible Integration: Pythonβs OWLReady2 library provides powerful tools for working with OWL ontologies
- Scalability: The system can be scaled using batch processing and caching strategies
- Deployment: Containerization and orchestration enable reliable production deployment
- Extensibility: The architecture supports integration with various data sources and ML models
Recommended Next Steps
- Enhance the Ontology:
- Add more detailed disease pathways and interactions
- Include temporal aspects of disease progression
- Integrate with weather and soil databases
- Improve the ML Integration:
- Train specialized models for different crop types
- Implement active learning to improve the system over time
- Add computer vision for image-based diagnosis
- Expand the API:
- Add authentication and rate limiting
- Implement WebSocket for real-time updates
- Add OpenAPI/Swagger documentation
- Deployment Improvements:
- Set up CI/CD pipelines
- Implement monitoring and logging
- Add health checks and metrics endpoints
10. Additional Resources
11. Troubleshooting Common Issues
Issue: Slow Query Performance
Solution: - Add appropriate indexes to your ontology - Use SPARQL queries with LIMIT and OFFSET for pagination - Implement caching for frequent queries
Issue: Memory Usage
Solution: - Process large ontologies in smaller chunks - Use sync_reasoner(infer_property_values=False) to reduce memory usage - Consider using a triple store for very large ontologies
Issue: Inconsistent Reasoning Results
Solution: - Check for inconsistent or conflicting axioms in your ontology - Verify that all necessary imports are properly resolved - Clear the reasonerβs cache and restart the application
12. Example Project Structure
plant-disease-diagnosis/
βββ app/ # Main application package
β βββ __init__.py
β βββ main.py # FastAPI application
β βββ models/ # Pydantic models
β β βββ schemas.py
β βββ services/
β β βββ __init__.py
β β βββ ontology.py # Ontology service
β β βββ diagnosis.py # Diagnosis logic
β β βββ ml_models.py # ML model integration
β βββ api/
β βββ __init__.py
β βββ endpoints.py # API endpoints
β βββ dependencies.py # FastAPI dependencies
βββ data/
β βββ plant-disease.owl # Ontology file
βββ tests/ # Unit and integration tests
β βββ __init__.py
β βββ test_services.py
β βββ test_api.py
βββ docker-compose.yml # For local development
βββ Dockerfile # Production Dockerfile
βββ requirements.txt # Python dependencies
βββ README.md # Project documentation
This completes our comprehensive guide to Python integration with plant disease ontologies. The provided examples and patterns should give you a solid foundation for building robust, scalable applications in this domain. results = diagnoser.diagnose(symptoms, environment)
# Convert results to JSON-serializable format
response = [{
'disease': disease.name,
'confidence': confidence,
'treatments': [t.name for t in disease.treatedWith] if disease.treatedWith else []
} for disease, confidence in results]
return jsonify(response)
if name == βmainβ: app.run(debug=True)
## 6. Next Steps
1. **Deploy to Production**
- Containerize with Docker
- Set up a GraphDB instance
- Create a REST API for ontology access
2. **Enhance the System**
- Add image recognition for symptom detection
- Integrate weather data for better predictions
- Implement a feedback loop to improve the ontology
3. **Explore Advanced Features**
- Implement a Mixture of Experts (MOE) architecture
- Add support for multilingual symptom descriptions
- Create a mobile app for field diagnosis
## Complete Project Structure
plant-disease-diagnosis/ βββ data/ β βββ plant-disease.owl β βββ training_data.csv βββ src/ β βββ init.py β βββ diagnoser.py β βββ ontology_utils.py β βββ ml_model.py βββ tests/ β βββ test_diagnoser.py βββ requirements.txt βββ README.md ```