wip wip wip
This commit is contained in:
@@ -37,7 +37,7 @@ sys.path.insert(0, str(annotate_dir))
|
||||
|
||||
try:
|
||||
from core.annotation_manager import AnnotationManager
|
||||
from core.training_simulator import TrainingSimulator
|
||||
from core.real_training_adapter import RealTrainingAdapter
|
||||
from core.data_loader import HistoricalDataLoader, TimeRangeManager
|
||||
except ImportError:
|
||||
# Try alternative import path
|
||||
@@ -52,14 +52,14 @@ except ImportError:
|
||||
ann_spec.loader.exec_module(ann_module)
|
||||
AnnotationManager = ann_module.AnnotationManager
|
||||
|
||||
# Load training_simulator
|
||||
# Load real_training_adapter (NO SIMULATION!)
|
||||
train_spec = importlib.util.spec_from_file_location(
|
||||
"training_simulator",
|
||||
annotate_dir / "core" / "training_simulator.py"
|
||||
"real_training_adapter",
|
||||
annotate_dir / "core" / "real_training_adapter.py"
|
||||
)
|
||||
train_module = importlib.util.module_from_spec(train_spec)
|
||||
train_spec.loader.exec_module(train_module)
|
||||
TrainingSimulator = train_module.TrainingSimulator
|
||||
RealTrainingAdapter = train_module.RealTrainingAdapter
|
||||
|
||||
# Load data_loader
|
||||
data_spec = importlib.util.spec_from_file_location(
|
||||
@@ -149,7 +149,8 @@ class AnnotationDashboard:
|
||||
|
||||
# Initialize ANNOTATE components
|
||||
self.annotation_manager = AnnotationManager()
|
||||
self.training_simulator = TrainingSimulator(self.orchestrator) if self.orchestrator else None
|
||||
# Use REAL training adapter - NO SIMULATION!
|
||||
self.training_adapter = RealTrainingAdapter(self.orchestrator, self.data_provider)
|
||||
|
||||
# Initialize data loader with existing DataProvider
|
||||
self.data_loader = HistoricalDataLoader(self.data_provider) if self.data_provider else None
|
||||
@@ -199,6 +200,14 @@ class AnnotationDashboard:
|
||||
def _setup_routes(self):
|
||||
"""Setup Flask routes"""
|
||||
|
||||
@self.server.route('/favicon.ico')
|
||||
def favicon():
|
||||
"""Serve favicon to prevent 404 errors"""
|
||||
from flask import Response
|
||||
# Return a simple 1x1 transparent pixel as favicon
|
||||
favicon_data = b'\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x20\x00\x68\x04\x00\x00\x16\x00\x00\x00\x28\x00\x00\x00\x10\x00\x00\x00\x20\x00\x00\x00\x01\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
return Response(favicon_data, mimetype='image/x-icon')
|
||||
|
||||
@self.server.route('/')
|
||||
def index():
|
||||
"""Main dashboard page - loads existing annotations"""
|
||||
@@ -267,7 +276,7 @@ class AnnotationDashboard:
|
||||
<li>Manual trade annotation</li>
|
||||
<li>Test case generation</li>
|
||||
<li>Annotation export</li>
|
||||
<li>Training simulation</li>
|
||||
<li>Real model training</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@@ -446,12 +455,25 @@ class AnnotationDashboard:
|
||||
data = request.get_json()
|
||||
annotation_id = data['annotation_id']
|
||||
|
||||
self.annotation_manager.delete_annotation(annotation_id)
|
||||
# Delete annotation and check if it was found
|
||||
deleted = self.annotation_manager.delete_annotation(annotation_id)
|
||||
|
||||
return jsonify({'success': True})
|
||||
if deleted:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Annotation deleted successfully'
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'ANNOTATION_NOT_FOUND',
|
||||
'message': f'Annotation {annotation_id} not found'
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting annotation: {e}")
|
||||
logger.error(f"Error deleting annotation: {e}", exc_info=True)
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
@@ -464,12 +486,11 @@ class AnnotationDashboard:
|
||||
def clear_all_annotations():
|
||||
"""Clear all annotations"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
data = request.get_json() or {}
|
||||
symbol = data.get('symbol', None)
|
||||
|
||||
# Get current annotations count
|
||||
annotations = self.annotation_manager.get_annotations(symbol=symbol)
|
||||
deleted_count = len(annotations)
|
||||
# Use the efficient clear_all_annotations method
|
||||
deleted_count = self.annotation_manager.clear_all_annotations(symbol=symbol)
|
||||
|
||||
if deleted_count == 0:
|
||||
return jsonify({
|
||||
@@ -478,12 +499,7 @@ class AnnotationDashboard:
|
||||
'message': 'No annotations to clear'
|
||||
})
|
||||
|
||||
# Clear all annotations
|
||||
for annotation in annotations:
|
||||
annotation_id = annotation.annotation_id if hasattr(annotation, 'annotation_id') else annotation.get('annotation_id')
|
||||
self.annotation_manager.delete_annotation(annotation_id)
|
||||
|
||||
logger.info(f"Cleared {deleted_count} annotations")
|
||||
logger.info(f"Cleared {deleted_count} annotations" + (f" for symbol {symbol}" if symbol else ""))
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -493,6 +509,8 @@ class AnnotationDashboard:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing all annotations: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
@@ -633,12 +651,12 @@ class AnnotationDashboard:
|
||||
def train_model():
|
||||
"""Start model training with annotations"""
|
||||
try:
|
||||
if not self.training_simulator:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -672,10 +690,10 @@ class AnnotationDashboard:
|
||||
}
|
||||
})
|
||||
|
||||
logger.info(f"Starting training with {len(test_cases)} test cases for model {model_name}")
|
||||
logger.info(f"Starting REAL training with {len(test_cases)} test cases for model {model_name}")
|
||||
|
||||
# Start training
|
||||
training_id = self.training_simulator.start_training(
|
||||
# Start REAL training (NO SIMULATION!)
|
||||
training_id = self.training_adapter.start_training(
|
||||
model_name=model_name,
|
||||
test_cases=test_cases
|
||||
)
|
||||
@@ -700,19 +718,19 @@ class AnnotationDashboard:
|
||||
def get_training_progress():
|
||||
"""Get training progress"""
|
||||
try:
|
||||
if not self.training_simulator:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
data = request.get_json()
|
||||
training_id = data['training_id']
|
||||
|
||||
progress = self.training_simulator.get_training_progress(training_id)
|
||||
progress = self.training_adapter.get_training_progress(training_id)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -733,16 +751,16 @@ class AnnotationDashboard:
|
||||
def get_available_models():
|
||||
"""Get list of available models"""
|
||||
try:
|
||||
if not self.training_simulator:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
models = self.training_simulator.get_available_models()
|
||||
models = self.training_adapter.get_available_models()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -767,17 +785,17 @@ class AnnotationDashboard:
|
||||
model_name = data.get('model_name')
|
||||
symbol = data.get('symbol', 'ETH/USDT')
|
||||
|
||||
if not self.training_simulator:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
# Start real-time inference
|
||||
inference_id = self.training_simulator.start_realtime_inference(
|
||||
# Start real-time inference using orchestrator
|
||||
inference_id = self.training_adapter.start_realtime_inference(
|
||||
model_name=model_name,
|
||||
symbol=symbol,
|
||||
data_provider=self.data_provider
|
||||
@@ -805,16 +823,16 @@ class AnnotationDashboard:
|
||||
data = request.get_json()
|
||||
inference_id = data.get('inference_id')
|
||||
|
||||
if not self.training_simulator:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
self.training_simulator.stop_realtime_inference(inference_id)
|
||||
self.training_adapter.stop_realtime_inference(inference_id)
|
||||
|
||||
return jsonify({
|
||||
'success': True
|
||||
@@ -834,16 +852,16 @@ class AnnotationDashboard:
|
||||
def get_realtime_signals():
|
||||
"""Get latest real-time inference signals"""
|
||||
try:
|
||||
if not self.training_simulator:
|
||||
if not self.training_adapter:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {
|
||||
'code': 'TRAINING_UNAVAILABLE',
|
||||
'message': 'Training simulator not available'
|
||||
'message': 'Real training adapter not available'
|
||||
}
|
||||
})
|
||||
|
||||
signals = self.training_simulator.get_latest_signals()
|
||||
signals = self.training_adapter.get_latest_signals()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
<div class="col-md-2">
|
||||
{% include 'components/control_panel.html' %}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Main Chart Area -->
|
||||
<div class="col-md-8">
|
||||
{% include 'components/chart_panel.html' %}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Right Sidebar - Annotations & Training -->
|
||||
<div class="col-md-2">
|
||||
{% include 'components/annotation_list.html' %}
|
||||
@@ -62,43 +62,43 @@
|
||||
window.appState = {
|
||||
currentSymbol: '{{ current_symbol }}',
|
||||
currentTimeframes: {{ timeframes | tojson }},
|
||||
annotations: {{ annotations | tojson }},
|
||||
pendingAnnotation: null,
|
||||
annotations: { { annotations | tojson } },
|
||||
pendingAnnotation: null,
|
||||
chartManager: null,
|
||||
annotationManager: null,
|
||||
timeNavigator: null,
|
||||
trainingController: null
|
||||
annotationManager: null,
|
||||
timeNavigator: null,
|
||||
trainingController: null
|
||||
};
|
||||
|
||||
// Initialize components when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize chart manager
|
||||
window.appState.chartManager = new ChartManager('chart-container', window.appState.currentTimeframes);
|
||||
|
||||
// Initialize annotation manager
|
||||
window.appState.annotationManager = new AnnotationManager(window.appState.chartManager);
|
||||
|
||||
// Initialize time navigator
|
||||
window.appState.timeNavigator = new TimeNavigator(window.appState.chartManager);
|
||||
|
||||
// Initialize training controller
|
||||
window.appState.trainingController = new TrainingController();
|
||||
|
||||
// Load initial data
|
||||
loadInitialData();
|
||||
|
||||
// Setup keyboard shortcuts
|
||||
setupKeyboardShortcuts();
|
||||
|
||||
// Setup global functions
|
||||
setupGlobalFunctions();
|
||||
});
|
||||
|
||||
|
||||
// Initialize components when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Initialize chart manager
|
||||
window.appState.chartManager = new ChartManager('chart-container', window.appState.currentTimeframes);
|
||||
|
||||
// Initialize annotation manager
|
||||
window.appState.annotationManager = new AnnotationManager(window.appState.chartManager);
|
||||
|
||||
// Initialize time navigator
|
||||
window.appState.timeNavigator = new TimeNavigator(window.appState.chartManager);
|
||||
|
||||
// Initialize training controller
|
||||
window.appState.trainingController = new TrainingController();
|
||||
|
||||
// Setup global functions FIRST (before loading data)
|
||||
setupGlobalFunctions();
|
||||
|
||||
// Load initial data (may call renderAnnotationsList which needs deleteAnnotation)
|
||||
loadInitialData();
|
||||
|
||||
// Setup keyboard shortcuts
|
||||
setupKeyboardShortcuts();
|
||||
});
|
||||
|
||||
function loadInitialData() {
|
||||
// Fetch initial chart data
|
||||
fetch('/api/chart-data', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
symbol: appState.currentSymbol,
|
||||
timeframes: appState.currentTimeframes,
|
||||
@@ -106,32 +106,32 @@
|
||||
end_time: null
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.appState.chartManager.initializeCharts(data.chart_data);
|
||||
|
||||
// Load existing annotations
|
||||
console.log('Loading', window.appState.annotations.length, 'existing annotations');
|
||||
window.appState.annotations.forEach(annotation => {
|
||||
window.appState.chartManager.addAnnotation(annotation);
|
||||
});
|
||||
|
||||
// Update annotation list
|
||||
if (typeof renderAnnotationsList === 'function') {
|
||||
renderAnnotationsList(window.appState.annotations);
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.appState.chartManager.initializeCharts(data.chart_data);
|
||||
|
||||
// Load existing annotations
|
||||
console.log('Loading', window.appState.annotations.length, 'existing annotations');
|
||||
window.appState.annotations.forEach(annotation => {
|
||||
window.appState.chartManager.addAnnotation(annotation);
|
||||
});
|
||||
|
||||
// Update annotation list
|
||||
if (typeof renderAnnotationsList === 'function') {
|
||||
renderAnnotationsList(window.appState.annotations);
|
||||
}
|
||||
} else {
|
||||
showError('Failed to load chart data: ' + data.error.message);
|
||||
}
|
||||
} else {
|
||||
showError('Failed to load chart data: ' + data.error.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function setupKeyboardShortcuts() {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
document.addEventListener('keydown', function (e) {
|
||||
// Arrow left - navigate backward
|
||||
if (e.key === 'ArrowLeft') {
|
||||
e.preventDefault();
|
||||
@@ -172,7 +172,7 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function showError(message) {
|
||||
// Create toast notification
|
||||
const toast = document.createElement('div');
|
||||
@@ -187,16 +187,16 @@
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
// Add to page and show
|
||||
document.body.appendChild(toast);
|
||||
const bsToast = new bootstrap.Toast(toast);
|
||||
bsToast.show();
|
||||
|
||||
|
||||
// Remove after hidden
|
||||
toast.addEventListener('hidden.bs.toast', () => toast.remove());
|
||||
}
|
||||
|
||||
|
||||
function showSuccess(message) {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast align-items-center text-white bg-success border-0';
|
||||
@@ -210,13 +210,13 @@
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
document.body.appendChild(toast);
|
||||
const bsToast = new bootstrap.Toast(toast);
|
||||
bsToast.show();
|
||||
toast.addEventListener('hidden.bs.toast', () => toast.remove());
|
||||
}
|
||||
|
||||
|
||||
function setupGlobalFunctions() {
|
||||
// Make functions globally available
|
||||
window.showError = showError;
|
||||
@@ -224,14 +224,21 @@
|
||||
window.renderAnnotationsList = renderAnnotationsList;
|
||||
window.deleteAnnotation = deleteAnnotation;
|
||||
window.highlightAnnotation = highlightAnnotation;
|
||||
|
||||
// Verify functions are set
|
||||
console.log('Global functions setup complete:');
|
||||
console.log(' - window.deleteAnnotation:', typeof window.deleteAnnotation);
|
||||
console.log(' - window.renderAnnotationsList:', typeof window.renderAnnotationsList);
|
||||
console.log(' - window.showError:', typeof window.showError);
|
||||
console.log(' - window.showSuccess:', typeof window.showSuccess);
|
||||
}
|
||||
|
||||
|
||||
function renderAnnotationsList(annotations) {
|
||||
const listElement = document.getElementById('annotations-list');
|
||||
if (!listElement) return;
|
||||
|
||||
|
||||
listElement.innerHTML = '';
|
||||
|
||||
|
||||
annotations.forEach(annotation => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'annotation-item mb-2 p-2 border rounded';
|
||||
@@ -259,43 +266,67 @@
|
||||
listElement.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteAnnotation(annotationId) {
|
||||
if (!confirm('Delete this annotation?')) return;
|
||||
|
||||
console.log('deleteAnnotation called with ID:', annotationId);
|
||||
|
||||
if (!confirm('Delete this annotation?')) {
|
||||
console.log('Delete cancelled by user');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Sending delete request to API...');
|
||||
fetch('/api/delete-annotation', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({annotation_id: annotationId})
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ annotation_id: annotationId })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Remove from app state
|
||||
window.appState.annotations = window.appState.annotations.filter(a => a.annotation_id !== annotationId);
|
||||
|
||||
// Update UI
|
||||
renderAnnotationsList(window.appState.annotations);
|
||||
|
||||
// Remove from chart
|
||||
if (window.appState.chartManager) {
|
||||
window.appState.chartManager.removeAnnotation(annotationId);
|
||||
.then(response => {
|
||||
console.log('Delete response status:', response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Delete response data:', data);
|
||||
|
||||
if (data.success) {
|
||||
// Remove from app state
|
||||
if (window.appState && window.appState.annotations) {
|
||||
window.appState.annotations = window.appState.annotations.filter(
|
||||
a => a.annotation_id !== annotationId
|
||||
);
|
||||
console.log('Removed from appState, remaining:', window.appState.annotations.length);
|
||||
}
|
||||
|
||||
// Update UI
|
||||
if (typeof renderAnnotationsList === 'function') {
|
||||
renderAnnotationsList(window.appState.annotations);
|
||||
console.log('UI updated');
|
||||
} else {
|
||||
console.error('renderAnnotationsList function not found');
|
||||
}
|
||||
|
||||
// Remove from chart
|
||||
if (window.appState && window.appState.chartManager) {
|
||||
window.appState.chartManager.removeAnnotation(annotationId);
|
||||
console.log('Removed from chart');
|
||||
}
|
||||
|
||||
showSuccess('Annotation deleted successfully');
|
||||
} else {
|
||||
console.error('Delete failed:', data.error);
|
||||
showError('Failed to delete annotation: ' + (data.error ? data.error.message : 'Unknown error'));
|
||||
}
|
||||
|
||||
showSuccess('Annotation deleted');
|
||||
} else {
|
||||
showError('Failed to delete annotation: ' + data.error.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Delete error:', error);
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function highlightAnnotation(annotationId) {
|
||||
if (window.appState.chartManager) {
|
||||
window.appState.chartManager.highlightAnnotation(annotationId);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -5,6 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Manual Trade Annotation{% endblock %}</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23007bff'%3E%3Cpath d='M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z'/%3E%3C/svg%3E">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
@@ -165,7 +165,15 @@
|
||||
|
||||
item.querySelector('.delete-annotation-btn').addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
deleteAnnotation(annotation.annotation_id);
|
||||
console.log('Delete button clicked for:', annotation.annotation_id);
|
||||
|
||||
// Use window.deleteAnnotation to ensure we get the global function
|
||||
if (typeof window.deleteAnnotation === 'function') {
|
||||
window.deleteAnnotation(annotation.annotation_id);
|
||||
} else {
|
||||
console.error('window.deleteAnnotation is not a function:', typeof window.deleteAnnotation);
|
||||
alert('Delete function not available. Please refresh the page.');
|
||||
}
|
||||
});
|
||||
|
||||
listContainer.appendChild(item);
|
||||
@@ -204,32 +212,5 @@
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAnnotation(annotationId) {
|
||||
if (!confirm('Are you sure you want to delete this annotation?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/delete-annotation', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({annotation_id: annotationId})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Remove from UI
|
||||
appState.annotations = appState.annotations.filter(a => a.annotation_id !== annotationId);
|
||||
renderAnnotationsList(appState.annotations);
|
||||
if (appState.chartManager) {
|
||||
appState.chartManager.removeAnnotation(annotationId);
|
||||
}
|
||||
showSuccess('Annotation deleted');
|
||||
} else {
|
||||
showError('Failed to delete annotation: ' + data.error.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showError('Network error: ' + error.message);
|
||||
});
|
||||
}
|
||||
// Note: deleteAnnotation is defined in annotation_dashboard.html to avoid duplication
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user