Scientific Article: Developing a Diabetes Prediction System with Python, Spring Boot, and Angular
Abstract
This article presents the development of a comprehensive diabetes prediction system using a microservices architecture. The backend leverages Python for machine learning, Spring Boot for data processing, and a modern Angular-based user interface styled with Tailwind CSS. This system exemplifies a robust solution for diabetes prediction by integrating technologies across multiple platforms and creating an end-to-end workflow.
Introduction
Diabetes is a global health concern that demands effective predictive systems to enable early intervention. This project demonstrates the creation of a diabetes prediction system leveraging machine learning and modern web technologies. The system includes:
- A Python-based API for predictive modeling.
- A Spring Boot API for data processing.
- An Angular and Tailwind CSS frontend for user interaction.
The following sections document the development process, providing code snippets and architectural insights.
Technologies Used
Python (Machine Learning Backend)
- Flask: RESTful API for the machine learning model.
- scikit-learn: Model training and evaluation.
- joblib: Model serialization.
Spring Boot (Intermediate Layer)
- Processes prediction requests and integrates with the Python API.
Angular (Frontend)
- Provides a user-friendly interface for submitting health metrics and viewing predictions.
- Styled with Tailwind CSS.
Development Steps
Step 1: Machine Learning API with Python
The Python backend trains a machine learning model using the cdc_diabetes_health_indicators
dataset. The API exposes a /predict
endpoint for predictions.
Code for Training the Model:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import pandas as pd
import joblib
# Load dataset
cdc_data = pd.read_csv('data/diabetes.csv')
X = cdc_data.drop('Outcome', axis=1)
y = cdc_data['Outcome']
# Train model
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Save model
joblib.dump(model, 'models/diabetes_model.pkl')
Flask API Code:
from flask import Flask, request, jsonify
import joblib
import pandas as pd
app = Flask(__name__)
model = joblib.load('models/diabetes_model.pkl')
@app.route('/predict', methods=['POST'])
def predict():
data = request.get_json()
input_df = pd.DataFrame([data])
prediction = model.predict(input_df)
return jsonify({"prediction": int(prediction[0])})
if __name__ == '__main__':
app.run(debug=True)
Step 2: Spring Boot API
The Spring Boot backend integrates with the Python API, exposing a simplified endpoint for the Angular frontend.
Spring Boot Service Class:
@Service
public class PredictionService {
private final RestTemplate restTemplate;
public PredictionService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public PredictionResponse getPrediction(PredictionRequest request) {
return restTemplate.postForObject("http://localhost:5000/predict", request, PredictionResponse.class);
}
}
Spring Boot Controller:
@RestController
@RequestMapping("/api/v1/predictions")
public class PredictionController {
private final PredictionService predictionService;
@PostMapping
public ResponseEntity<PredictionResponse> predict(@RequestBody PredictionRequest request) {
return ResponseEntity.ok(predictionService.getPrediction(request));
}
}
Step 3: Angular Frontend with Tailwind CSS
The Angular frontend collects user input, sends it to the Spring Boot API, and displays the prediction results.
Service for API Calls:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface PredictionRequest {
HighBP: number;
HighChol: number;
CholCheck: number;
BMI: number;
Smoker: number;
Stroke: number;
HeartDiseaseorAttack: number;
PhysActivity: number;
Fruits: number;
Veggies: number;
HvyAlcoholConsump: number;
AnyHealthcare: number;
NoDocbcCost: number;
GenHlth: number;
MentHlth: number;
PhysHlth: number;
DiffWalk: number;
Sex: number;
Age: number;
Education: number;
Income: number;
}
export interface PredictionResponse {
classification: string;
interpretation: string;
prediction: number;
}
@Injectable({ providedIn: 'root' })
export class PredictionService {
private apiUrl = 'http://localhost:8080/api/v1/predictions';
constructor(private http: HttpClient) {}
predict(data: PredictionRequest): Observable<PredictionResponse> {
return this.http.post<PredictionResponse>(this.apiUrl, data);
}
}
Form Component:
@Component({
selector: 'app-prediction-form',
templateUrl: './prediction-form.component.html',
styleUrls: ['./prediction-form.component.css'],
animations: [
trigger('fadeInOut', [
transition(':enter', [
style({ opacity: 0 }),
animate('300ms', style({ opacity: 1 }))
]),
transition(':leave', [
animate('300ms', style({ opacity: 0 }))
])
]),
trigger('slideIn', [
transition(':enter', [
style({ transform: 'translateY(50px)', opacity: 0 }),
animate('500ms cubic-bezier(0.35, 0, 0.25, 1)',
style({ transform: 'translateY(0)', opacity: 1 }))
])
])
]
})
export class PredictionFormComponent implements OnInit {
today = new Date();
showForm = false;
showSuccess = false;
radius = 120;
circumference = 2 * Math.PI * this.radius;
request: PredictionRequest = {
HighBP: 0,
HighChol: 1,
CholCheck: 1,
BMI: 35,
Smoker: 1,
Stroke: 1,
HeartDiseaseorAttack: 1,
PhysActivity: 0,
Fruits: 0,
Veggies: 0,
HvyAlcoholConsump: 1,
AnyHealthcare: 0,
NoDocbcCost: 1,
GenHlth: 5,
MentHlth: 10,
PhysHlth: 15,
DiffWalk: 1,
Sex: 0,
Age: 60,
Education: 1,
Income: 2
};
metrics = {
eaten: {
value: 0,
total: 3000,
unit: 'cal'
},
activity: {
value: 0,
unit: 'Steps'
},
water: {
value: 0,
unit: 'GL'
},
bloodGlucose: {
value: 0,
unit: 'mg/dl'
},
heart: {
value: 0,
unit: 'bpm'
}
};
prediction: PredictionResponse = {
prediction: 0,
classification: '',
interpretation: ''
};
constructor(private predictionService: PredictionService) {}
ngOnInit() {
this.initializeMetrics();
}
initializeMetrics() {
this.metrics.eaten.value = 70;
this.metrics.activity.value = 2032;
this.metrics.water.value = 4;
this.metrics.bloodGlucose.value = 136;
this.metrics.heart.value = 72;
}
getFormKeys(): ReadonlyArray<keyof PredictionRequest> {
return this.formKeys;
}
formKeys: ReadonlyArray<keyof PredictionRequest> = [
'HighBP', 'HighChol', 'CholCheck', 'BMI', 'Smoker', 'Stroke',
'HeartDiseaseorAttack', 'PhysActivity', 'Fruits', 'Veggies',
'HvyAlcoholConsump', 'AnyHealthcare', 'NoDocbcCost', 'GenHlth',
'MentHlth', 'PhysHlth', 'DiffWalk', 'Sex', 'Age', 'Education', 'Income'
];
getRequestValue(key: keyof PredictionRequest): number {
return this.request[key];
}
setRequestValue(key: keyof PredictionRequest, value: number): void {
this.request[key] = value;
}
onSubmit() {
if (this.validateForm()) {
this.predictionService.predict(this.request).subscribe(
(response: PredictionResponse) => {
this.prediction = response;
this.updateMetrics();
this.showForm = false;
},
error => {
console.error('Prediction error:', error);
}
);
}
}
validateForm(): boolean {
const requiredNonZeroFields: (keyof PredictionRequest)[] = ['Age', 'BMI'];
for (const [key, value] of Object.entries(this.request)) {
if (value === undefined || value < 0) {
return false;
}
if (value === 0 && requiredNonZeroFields.includes(key as keyof PredictionRequest)) {
return false;
}
}
return true;
}
updateMetrics() {
if (this.prediction) {
this.metrics.bloodGlucose.value = this.request.BMI > 25 ?
Math.round(120 + this.request.BMI) :
Math.round(90 + this.request.BMI);
this.metrics.heart.value = this.request.PhysActivity === 1 ?
Math.round(65 + Math.random() * 10) :
Math.round(75 + Math.random() * 15);
this.metrics.activity.value = this.request.PhysActivity === 1 ?
Math.round(8000 + Math.random() * 2000) :
Math.round(2000 + Math.random() * 1000);
this.metrics.water.value = this.request.PhysActivity === 1 ? 8 : 4;
this.metrics.eaten.value = Math.round(70 + (this.request.PhysActivity * 10));
}
}
getPredictionScore(): number {
if (this.prediction) {
return 100 - Math.round(this.prediction.prediction * 100);
}
return 80;
}
getDashOffset(): number {
if (this.prediction) {
const progress = 1 - this.prediction.prediction;
return this.circumference - (progress * this.circumference);
}
return this.circumference * 0.2;
}
getEatenProgress(): string {
const total = this.metrics.eaten.total;
const current = (this.metrics.eaten.value / 100) * total;
const progress = (current / total) * 176;
return `${progress} 176`;
}
getFieldLabel(key: keyof PredictionRequest): string {
const labels: Record<keyof PredictionRequest, string> = {
HighBP: 'High Blood Pressure',
HighChol: 'High Cholesterol',
CholCheck: 'Cholesterol Check',
BMI: 'BMI',
Smoker: 'Smoker',
Stroke: 'Stroke History',
HeartDiseaseorAttack: 'Heart Disease/Attack',
PhysActivity: 'Physical Activity',
Fruits: 'Fruits Consumption',
Veggies: 'Vegetables Consumption',
HvyAlcoholConsump: 'Heavy Alcohol Consumption',
AnyHealthcare: 'Healthcare Coverage',
NoDocbcCost: 'Doctor Visit Cost Issues',
GenHlth: 'General Health',
MentHlth: 'Mental Health',
PhysHlth: 'Physical Health',
DiffWalk: 'Difficulty Walking',
Sex: 'Sex',
Age: 'Age',
Education: 'Education Level',
Income: 'Income Level'
};
return labels[key] || key;
}
}
Updated Form HTML with Modal and Metrics:
<div class="min-h-screen bg-gray-100">
<div class="p-4 bg-white shadow-sm">
<div class="max-w-7xl mx-auto flex justify-between items-center">
<div class="flex items-center space-x-4">
<img src="assets/doctor-profile.jpg" alt="Profile" class="w-12 h-12 rounded-full">
<div>
<h1 class="text-xl font-semibold">Will Smith</h1>
<button class="text-sm text-gray-500 hover:text-gray-700">Logout</button>
</div>
</div>
<div class="text-right">
<div class="text-lg">18°C</div>
<div class="text-sm text-gray-500">Little Bit Cloudy</div>
<div class="text-sm text-gray-500">{{today | date:'EEEE, d MMMM'}}</div>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto p-6">
<div class="grid grid-cols-3 gap-8">
<div class="space-y-6">
<div class="bg-white rounded-xl p-6 shadow-md">
<h3 class="text-gray-600 mb-4">Eaten</h3>
<div class="flex items-center">
<div class="relative w-20 h-20">
<svg class="w-full h-full transform -rotate-90">
<circle cx="40" cy="40" r="35" stroke="#e5e7eb" stroke-width="5" fill="none"/>
<circle cx="40" cy="40" r="35" stroke="#06b6d4" stroke-width="5" fill="none" [attr.stroke-dasharray]="getEatenProgress()"/>
</svg>
<div class="absolute inset-0 flex items-center justify-center">
<span class="text-lg font-semibold">70%</span>
</div>
</div>
<div class="ml-4">
<p class="text-sm text-gray-500">3000 cal</p>
</div>
</div>
</div>
<!-- Other Metric Cards... -->
</div>
<div class="flex items-center justify-center">
<div class="relative">
<svg class="w-80 h-80 transform -rotate-90">
<circle cx="160" cy="160" r="150" stroke="#e5e7eb" stroke-width="20" fill="none"/>
<circle cx="160" cy="160" r="150" stroke="#06b6d4" stroke-width="20" fill="none" [attr.stroke-dasharray]="circumference" [attr.stroke-dashoffset]="getDashOffset()"/>
</svg>
<div class="absolute inset-0 flex flex-col items-center justify-center">
<span class="text-7xl font-bold">80</span>
<span class="text-gray-500">Fitness score today</span>
</div>
</div>
</div>
<div class="space-y-6">
<div class="bg-white rounded-xl p-6 shadow-md">
<h3 class="text-gray-600 mb-4">Blood Glucose</h3>
<div class="flex items-center mb-4">
<span class="text-2xl font-bold">136</span>
<span class="ml-2 text-gray-500">mg/dl</span>
</div>
</div>
<!-- Other Metrics... -->
</div>
</div>
</div>
</div>
Conclusion
This project showcases how Python, Spring Boot, and Angular can work together seamlessly to create a predictive system for healthcare applications. The modular design ensures scalability and flexibility for future enhancements. Tailwind CSS provides a polished UI, making the application user-friendly.
References
Author: Starling Diaz
- UCI Machine Learning Repository
- Flask Documentation: https://flask.palletsprojects.com/
- Spring Boot Documentation: https://spring.io/projects/spring-boot
- LinkedIn: https://www.linkedin.com/in/starling-diaz-908225181/
- GitHub: https://github.com/NSTLRD/diabetes-prediction-ui
- Google Scholar: https://scholar.google.com/citations?view_op=view_citation&hl=es&user=_3EWA1QAAAAJ&sortby=pubdate&citation_for_view=_3EWA1QAAAAJ:n1qY4L4uFdgC