Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ The original mapping used in the repository is:

## 🧠 Model Performance

The ML pipeline was trained on the **Earthquake Alert Prediction Dataset** using `RandomizedSearchCV` for hyperparameter tuning and **SMOTE** to handle class imbalance.
The ML pipeline was trained on the **Earthquake Alert Prediction Dataset** using `XGBClassifier` for hyperparameter tuning and **SMOTE** to handle class imbalance. The model configuration was chosen by running `RandomizedSearchCV`.

**Classification Report on Test Set (260 samples):**

Expand Down Expand Up @@ -192,8 +192,6 @@ SEISMOSENSE/

- Add user authentication and log predictions for research purposes.

**Note:** Due to the absence of front-end web development expertise and the absence of other contributors for the project, I was compelled to utilize AI tools (such as LLM services like ChatGPT™, Grok™, and GitHub Copilot™) to develop a sophisticated front-end for the web app. I am looking forward to human contribution on this project in order to scale it even further.

---

## 📄 License
Expand Down
21 changes: 15 additions & 6 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# app.py
from flask import Flask, render_template, request
from flask import Flask, render_template, request, redirect, url_for, session
import joblib
import numpy as np
from pathlib import Path
from fit import main
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(32)

# paths to the pickle files
MODEL_PATH = Path("models/estimator.pkl")
Expand Down Expand Up @@ -36,8 +38,6 @@ def ensure_models():
# main routes
@app.route("/", methods=["GET", "POST"])
def index():
result = None
confidence = None
if request.method == "POST":
try:
magnitude = float(request.form["magnitude"])
Expand All @@ -49,14 +49,23 @@ def index():
X = np.array([[magnitude, depth, cdi, mmi, sig]])

pred_label = model.predict(X)[0]
confidence = None
if hasattr(model.named_steps["model"], "predict_proba"):
proba_array = model.predict_proba(X)[0]
confidence = round(100 * proba_array[pred_label], 2)
confidence = round((float(100 * proba_array[pred_label])),2)

result = label_map[pred_label]
session["result"] = label_map[pred_label]
session["confidence"] = confidence
except Exception as e:
result = f"Error: {str(e)}"
session["result"] = f"Error: {str(e)}"
session["confidence"] = None

# PRG: redirect so a page refresh won't re-submit the form
return redirect(url_for("index"))

# GET: consume the result from session (one-time display)
result = session.pop("result", None)
confidence = session.pop("confidence", None)
return render_template("index.html", result=result, confidence=confidence)

if __name__ == "__main__":
Expand Down
7 changes: 3 additions & 4 deletions conf_mat.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def load_data(path="dataset/earthquake_data.csv") -> np.ndarray:

def plotting(x, y) -> None:
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=2/10, random_state=120, shuffle=True, stratify=y
x, y, test_size=2/10, random_state=4, shuffle=True, stratify=y
)

labels = ALERT_LABELS
Expand All @@ -46,11 +46,10 @@ def plotting(x, y) -> None:
# Professional dark theme matching the frontend
plt.style.use('dark_background')

BG_COLOR = '#0f1419' # Dark background
CARD_BG = '#1a1f26' # Card background
BG_COLOR = '#151C26' # Dark background
CARD_BG = "#050506" # Card background
TEXT_PRIMARY = '#e2e8f0' # Primary text
TEXT_SECONDARY = '#94a3b8' # Muted text
ACCENT = '#00d4aa' # Teal accent

# Alert-specific colors for the heatmap
ALERT_HEATMAP_COLORS = [
Expand Down
Binary file added static/confusion_matrix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
119 changes: 119 additions & 0 deletions static/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// ========================================
// SeismoSense - JavaScript
// ========================================

// Tab Navigation
const tabs = document.querySelectorAll('.nav-tab');
const panels = document.querySelectorAll('.tab-panel');

tabs.forEach(tab => {
tab.addEventListener('click', () => {
// Remove active class from all tabs and panels
tabs.forEach(t => t.classList.remove('active'));
panels.forEach(p => p.classList.remove('active'));

// Add active class to clicked tab and corresponding panel
tab.classList.add('active');
const panelId = tab.dataset.tab + '-panel';
document.getElementById(panelId).classList.add('active');
});
});

// Toast Notification Functions
function getToastIcon(type) {
const icons = {
success: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>',
error: '<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>',
warning: '<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>',
info: '<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/>'
};
return icons[type] || icons.info;
}

function showToast(message, type = 'info') {
const container = document.getElementById('toast-container');

if (!container) return;

const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.innerHTML = `
<svg class="toast-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
${getToastIcon(type)}
</svg>
<span class="toast-message">${message}</span>
`;
container.appendChild(toast);
setTimeout(() => {
toast.classList.add('toast-out');
setTimeout(() => {
toast.remove();
}, 300);
}, 3000);
}

// Form Submission - Show loading state and toast
const form = document.getElementById('seismoForm');
const btn = document.getElementById('predictBtn');
const btnText = btn ? btn.querySelector('.btn-text') : null;
const loader = document.getElementById('loader');

if (form && btn && btnText) {
form.addEventListener('submit', () => {
// Show loading state
btn.disabled = true;
btnText.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i> Analyzing...';

if (loader) {
loader.style.display = 'inline-block';
}
});
}

// Clear button — reset all inputs
const clearBtn = document.getElementById('clearBtn');
if (clearBtn && form) {
clearBtn.addEventListener('click', () => {
form.querySelectorAll('.form-input').forEach(input => {
input.value = '';
});
form.querySelector('.form-input').focus();
});
}


// On page load, reset button state and animate confidence bar
document.addEventListener('DOMContentLoaded', () => {
// Reset button state (in case of page reload with result)
if (btn && btnText) {
btn.disabled = false;
btnText.innerHTML = '<i class="fas fa-bolt"></i> Analyze & Predict';

if (loader) {
loader.style.display = 'none';
}
}

// Show toast if there's a result
const resultCard = document.querySelector('.result-card');
if (resultCard) {
const result = resultCard.classList.contains('green') ? 'Green Alert' :
resultCard.classList.contains('orange') ? 'Orange Alert' :
resultCard.classList.contains('red') ? 'Red Alert' :
resultCard.classList.contains('yellow') ? 'Yellow Alert' : null;
if (result) {
showToast('Prediction complete!', 'success');
}
}

// Animate confidence bar if result exists
const confFill = document.getElementById('confFill');
const confValue = document.getElementById('confValue');

if (confFill && confValue) {
const width = confValue.textContent.trim();
setTimeout(() => {
confFill.style.width = width + '%';
}, 100);
}
});
Loading
Loading