local-deep-research 0.4.4__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- local_deep_research/__init__.py +7 -0
- local_deep_research/__version__.py +1 -1
- local_deep_research/advanced_search_system/answer_decoding/__init__.py +5 -0
- local_deep_research/advanced_search_system/answer_decoding/browsecomp_answer_decoder.py +421 -0
- local_deep_research/advanced_search_system/candidate_exploration/README.md +219 -0
- local_deep_research/advanced_search_system/candidate_exploration/__init__.py +25 -0
- local_deep_research/advanced_search_system/candidate_exploration/adaptive_explorer.py +329 -0
- local_deep_research/advanced_search_system/candidate_exploration/base_explorer.py +341 -0
- local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py +436 -0
- local_deep_research/advanced_search_system/candidate_exploration/diversity_explorer.py +457 -0
- local_deep_research/advanced_search_system/candidate_exploration/parallel_explorer.py +250 -0
- local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +255 -0
- local_deep_research/advanced_search_system/candidates/__init__.py +5 -0
- local_deep_research/advanced_search_system/candidates/base_candidate.py +59 -0
- local_deep_research/advanced_search_system/constraint_checking/README.md +150 -0
- local_deep_research/advanced_search_system/constraint_checking/__init__.py +35 -0
- local_deep_research/advanced_search_system/constraint_checking/base_constraint_checker.py +122 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_checker.py +223 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_satisfaction_tracker.py +387 -0
- local_deep_research/advanced_search_system/constraint_checking/dual_confidence_checker.py +424 -0
- local_deep_research/advanced_search_system/constraint_checking/evidence_analyzer.py +174 -0
- local_deep_research/advanced_search_system/constraint_checking/intelligent_constraint_relaxer.py +503 -0
- local_deep_research/advanced_search_system/constraint_checking/rejection_engine.py +143 -0
- local_deep_research/advanced_search_system/constraint_checking/strict_checker.py +259 -0
- local_deep_research/advanced_search_system/constraint_checking/threshold_checker.py +213 -0
- local_deep_research/advanced_search_system/constraints/__init__.py +6 -0
- local_deep_research/advanced_search_system/constraints/base_constraint.py +58 -0
- local_deep_research/advanced_search_system/constraints/constraint_analyzer.py +143 -0
- local_deep_research/advanced_search_system/evidence/__init__.py +12 -0
- local_deep_research/advanced_search_system/evidence/base_evidence.py +57 -0
- local_deep_research/advanced_search_system/evidence/evaluator.py +159 -0
- local_deep_research/advanced_search_system/evidence/requirements.py +122 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +3 -1
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +8 -2
- local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +43 -29
- local_deep_research/advanced_search_system/findings/repository.py +54 -17
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +3 -1
- local_deep_research/advanced_search_system/query_generation/adaptive_query_generator.py +405 -0
- local_deep_research/advanced_search_system/questions/__init__.py +16 -0
- local_deep_research/advanced_search_system/questions/atomic_fact_question.py +171 -0
- local_deep_research/advanced_search_system/questions/browsecomp_question.py +287 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +13 -4
- local_deep_research/advanced_search_system/questions/entity_aware_question.py +184 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +9 -3
- local_deep_research/advanced_search_system/search_optimization/cross_constraint_manager.py +624 -0
- local_deep_research/advanced_search_system/source_management/diversity_manager.py +613 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +42 -0
- local_deep_research/advanced_search_system/strategies/adaptive_decomposition_strategy.py +564 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +4 -4
- local_deep_research/advanced_search_system/strategies/browsecomp_entity_strategy.py +1031 -0
- local_deep_research/advanced_search_system/strategies/browsecomp_optimized_strategy.py +778 -0
- local_deep_research/advanced_search_system/strategies/concurrent_dual_confidence_strategy.py +446 -0
- local_deep_research/advanced_search_system/strategies/constrained_search_strategy.py +1348 -0
- local_deep_research/advanced_search_system/strategies/constraint_parallel_strategy.py +522 -0
- local_deep_research/advanced_search_system/strategies/direct_search_strategy.py +217 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_strategy.py +320 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_with_rejection.py +219 -0
- local_deep_research/advanced_search_system/strategies/early_stop_constrained_strategy.py +369 -0
- local_deep_research/advanced_search_system/strategies/entity_aware_source_strategy.py +140 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy.py +1248 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy_v2.py +1337 -0
- local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +537 -0
- local_deep_research/advanced_search_system/strategies/improved_evidence_based_strategy.py +782 -0
- local_deep_research/advanced_search_system/strategies/iterative_reasoning_strategy.py +760 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +55 -21
- local_deep_research/advanced_search_system/strategies/llm_driven_modular_strategy.py +865 -0
- local_deep_research/advanced_search_system/strategies/modular_strategy.py +1142 -0
- local_deep_research/advanced_search_system/strategies/parallel_constrained_strategy.py +506 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +34 -16
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +29 -9
- local_deep_research/advanced_search_system/strategies/recursive_decomposition_strategy.py +492 -0
- local_deep_research/advanced_search_system/strategies/smart_decomposition_strategy.py +284 -0
- local_deep_research/advanced_search_system/strategies/smart_query_strategy.py +515 -0
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +48 -24
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +34 -14
- local_deep_research/advanced_search_system/tools/base_tool.py +7 -2
- local_deep_research/api/benchmark_functions.py +6 -2
- local_deep_research/api/research_functions.py +10 -4
- local_deep_research/benchmarks/__init__.py +9 -7
- local_deep_research/benchmarks/benchmark_functions.py +6 -2
- local_deep_research/benchmarks/cli/benchmark_commands.py +27 -10
- local_deep_research/benchmarks/cli.py +38 -13
- local_deep_research/benchmarks/comparison/__init__.py +4 -2
- local_deep_research/benchmarks/comparison/evaluator.py +316 -239
- local_deep_research/benchmarks/datasets/__init__.py +1 -1
- local_deep_research/benchmarks/datasets/base.py +91 -72
- local_deep_research/benchmarks/datasets/browsecomp.py +54 -33
- local_deep_research/benchmarks/datasets/custom_dataset_template.py +19 -19
- local_deep_research/benchmarks/datasets/simpleqa.py +14 -14
- local_deep_research/benchmarks/datasets/utils.py +48 -29
- local_deep_research/benchmarks/datasets.py +4 -11
- local_deep_research/benchmarks/efficiency/__init__.py +8 -4
- local_deep_research/benchmarks/efficiency/resource_monitor.py +223 -171
- local_deep_research/benchmarks/efficiency/speed_profiler.py +62 -48
- local_deep_research/benchmarks/evaluators/browsecomp.py +3 -1
- local_deep_research/benchmarks/evaluators/composite.py +6 -2
- local_deep_research/benchmarks/evaluators/simpleqa.py +36 -13
- local_deep_research/benchmarks/graders.py +32 -10
- local_deep_research/benchmarks/metrics/README.md +1 -1
- local_deep_research/benchmarks/metrics/calculation.py +25 -10
- local_deep_research/benchmarks/metrics/reporting.py +7 -3
- local_deep_research/benchmarks/metrics/visualization.py +42 -23
- local_deep_research/benchmarks/metrics.py +1 -1
- local_deep_research/benchmarks/optimization/__init__.py +3 -1
- local_deep_research/benchmarks/optimization/api.py +7 -1
- local_deep_research/benchmarks/optimization/optuna_optimizer.py +75 -26
- local_deep_research/benchmarks/runners.py +48 -15
- local_deep_research/citation_handler.py +65 -92
- local_deep_research/citation_handlers/__init__.py +15 -0
- local_deep_research/citation_handlers/base_citation_handler.py +70 -0
- local_deep_research/citation_handlers/forced_answer_citation_handler.py +179 -0
- local_deep_research/citation_handlers/precision_extraction_handler.py +550 -0
- local_deep_research/citation_handlers/standard_citation_handler.py +80 -0
- local_deep_research/config/llm_config.py +271 -169
- local_deep_research/config/search_config.py +14 -5
- local_deep_research/defaults/__init__.py +0 -1
- local_deep_research/metrics/__init__.py +13 -0
- local_deep_research/metrics/database.py +58 -0
- local_deep_research/metrics/db_models.py +115 -0
- local_deep_research/metrics/migrate_add_provider_to_token_usage.py +148 -0
- local_deep_research/metrics/migrate_call_stack_tracking.py +105 -0
- local_deep_research/metrics/migrate_enhanced_tracking.py +75 -0
- local_deep_research/metrics/migrate_research_ratings.py +31 -0
- local_deep_research/metrics/models.py +61 -0
- local_deep_research/metrics/pricing/__init__.py +12 -0
- local_deep_research/metrics/pricing/cost_calculator.py +237 -0
- local_deep_research/metrics/pricing/pricing_cache.py +143 -0
- local_deep_research/metrics/pricing/pricing_fetcher.py +240 -0
- local_deep_research/metrics/query_utils.py +51 -0
- local_deep_research/metrics/search_tracker.py +380 -0
- local_deep_research/metrics/token_counter.py +1078 -0
- local_deep_research/migrate_db.py +3 -1
- local_deep_research/report_generator.py +22 -8
- local_deep_research/search_system.py +390 -9
- local_deep_research/test_migration.py +15 -5
- local_deep_research/utilities/db_utils.py +7 -4
- local_deep_research/utilities/es_utils.py +115 -104
- local_deep_research/utilities/llm_utils.py +15 -5
- local_deep_research/utilities/log_utils.py +151 -0
- local_deep_research/utilities/search_cache.py +387 -0
- local_deep_research/utilities/search_utilities.py +14 -6
- local_deep_research/utilities/threading_utils.py +92 -0
- local_deep_research/utilities/url_utils.py +6 -0
- local_deep_research/web/api.py +347 -0
- local_deep_research/web/app.py +13 -17
- local_deep_research/web/app_factory.py +71 -66
- local_deep_research/web/database/migrate_to_ldr_db.py +12 -4
- local_deep_research/web/database/migrations.py +20 -3
- local_deep_research/web/database/models.py +74 -25
- local_deep_research/web/database/schema_upgrade.py +49 -29
- local_deep_research/web/models/database.py +63 -83
- local_deep_research/web/routes/api_routes.py +56 -22
- local_deep_research/web/routes/benchmark_routes.py +4 -1
- local_deep_research/web/routes/globals.py +22 -0
- local_deep_research/web/routes/history_routes.py +71 -46
- local_deep_research/web/routes/metrics_routes.py +1155 -0
- local_deep_research/web/routes/research_routes.py +192 -54
- local_deep_research/web/routes/settings_routes.py +156 -55
- local_deep_research/web/services/research_service.py +412 -251
- local_deep_research/web/services/resource_service.py +36 -11
- local_deep_research/web/services/settings_manager.py +55 -17
- local_deep_research/web/services/settings_service.py +12 -4
- local_deep_research/web/services/socket_service.py +295 -188
- local_deep_research/web/static/css/custom_dropdown.css +180 -0
- local_deep_research/web/static/css/styles.css +39 -1
- local_deep_research/web/static/js/components/detail.js +633 -267
- local_deep_research/web/static/js/components/details.js +751 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +11 -11
- local_deep_research/web/static/js/components/fallback/ui.js +23 -23
- local_deep_research/web/static/js/components/history.js +76 -76
- local_deep_research/web/static/js/components/logpanel.js +61 -13
- local_deep_research/web/static/js/components/progress.js +13 -2
- local_deep_research/web/static/js/components/research.js +99 -12
- local_deep_research/web/static/js/components/results.js +239 -106
- local_deep_research/web/static/js/main.js +40 -40
- local_deep_research/web/static/js/services/audio.js +1 -1
- local_deep_research/web/static/js/services/formatting.js +11 -11
- local_deep_research/web/static/js/services/keyboard.js +157 -0
- local_deep_research/web/static/js/services/pdf.js +80 -80
- local_deep_research/web/static/sounds/README.md +1 -1
- local_deep_research/web/templates/base.html +1 -0
- local_deep_research/web/templates/components/log_panel.html +7 -1
- local_deep_research/web/templates/components/mobile_nav.html +1 -1
- local_deep_research/web/templates/components/sidebar.html +3 -0
- local_deep_research/web/templates/pages/cost_analytics.html +1245 -0
- local_deep_research/web/templates/pages/details.html +325 -24
- local_deep_research/web/templates/pages/history.html +1 -1
- local_deep_research/web/templates/pages/metrics.html +1929 -0
- local_deep_research/web/templates/pages/progress.html +2 -2
- local_deep_research/web/templates/pages/research.html +53 -17
- local_deep_research/web/templates/pages/results.html +12 -1
- local_deep_research/web/templates/pages/star_reviews.html +803 -0
- local_deep_research/web/utils/formatters.py +9 -3
- local_deep_research/web_search_engines/default_search_engines.py +5 -3
- local_deep_research/web_search_engines/engines/full_search.py +8 -2
- local_deep_research/web_search_engines/engines/meta_search_engine.py +59 -20
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +19 -6
- local_deep_research/web_search_engines/engines/search_engine_brave.py +6 -2
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +3 -1
- local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +81 -58
- local_deep_research/web_search_engines/engines/search_engine_github.py +46 -15
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +16 -6
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +39 -15
- local_deep_research/web_search_engines/engines/search_engine_local.py +58 -25
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +15 -5
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +63 -21
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +37 -11
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +27 -9
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +12 -4
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +31 -10
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +12 -3
- local_deep_research/web_search_engines/search_engine_base.py +83 -35
- local_deep_research/web_search_engines/search_engine_factory.py +25 -8
- local_deep_research/web_search_engines/search_engines_config.py +9 -3
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/METADATA +7 -1
- local_deep_research-0.5.2.dist-info/RECORD +265 -0
- local_deep_research-0.4.4.dist-info/RECORD +0 -177
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/WHEEL +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -9,7 +9,7 @@
|
|
9
9
|
let pdfBtn = null;
|
10
10
|
let researchId = null;
|
11
11
|
let researchData = null;
|
12
|
-
|
12
|
+
|
13
13
|
/**
|
14
14
|
* Initialize the results component
|
15
15
|
*/
|
@@ -18,41 +18,52 @@
|
|
18
18
|
resultsContainer = document.getElementById('results-content');
|
19
19
|
exportBtn = document.getElementById('export-markdown-btn');
|
20
20
|
pdfBtn = document.getElementById('download-pdf-btn');
|
21
|
-
|
21
|
+
|
22
22
|
if (!resultsContainer) {
|
23
23
|
console.error('Results container not found');
|
24
24
|
return;
|
25
25
|
}
|
26
|
-
|
26
|
+
|
27
27
|
console.log('Results component initialized');
|
28
|
-
|
28
|
+
|
29
29
|
// Get research ID from URL
|
30
30
|
researchId = getResearchIdFromUrl();
|
31
|
-
|
31
|
+
|
32
32
|
if (!researchId) {
|
33
33
|
showError('Research ID not found in URL');
|
34
34
|
return;
|
35
35
|
}
|
36
|
-
|
36
|
+
|
37
37
|
// Set up event listeners
|
38
38
|
setupEventListeners();
|
39
|
-
|
39
|
+
|
40
40
|
// Load research results
|
41
41
|
loadResearchResults();
|
42
|
-
|
42
|
+
|
43
|
+
// Initialize star rating
|
44
|
+
initializeStarRating();
|
45
|
+
|
43
46
|
// Note: Log panel is now automatically initialized by logpanel.js
|
44
47
|
// No need to manually initialize it here
|
45
48
|
}
|
46
|
-
|
49
|
+
|
47
50
|
/**
|
48
51
|
* Set up event listeners
|
49
52
|
*/
|
50
53
|
function setupEventListeners() {
|
54
|
+
// View metrics button
|
55
|
+
const metricsBtn = document.getElementById('view-metrics-btn');
|
56
|
+
if (metricsBtn) {
|
57
|
+
metricsBtn.addEventListener('click', () => {
|
58
|
+
window.location.href = `/research/details/${researchId}`;
|
59
|
+
});
|
60
|
+
}
|
61
|
+
|
51
62
|
// Export button
|
52
63
|
if (exportBtn) {
|
53
64
|
exportBtn.addEventListener('click', handleExport);
|
54
65
|
}
|
55
|
-
|
66
|
+
|
56
67
|
// PDF button
|
57
68
|
if (pdfBtn) {
|
58
69
|
pdfBtn.addEventListener('click', handlePdfExport);
|
@@ -65,8 +76,9 @@
|
|
65
76
|
window.location.href = '/research/history';
|
66
77
|
});
|
67
78
|
}
|
79
|
+
|
68
80
|
}
|
69
|
-
|
81
|
+
|
70
82
|
/**
|
71
83
|
* Get research ID from URL
|
72
84
|
* @returns {string|null} Research ID
|
@@ -76,7 +88,7 @@
|
|
76
88
|
const match = path.match(/\/research\/results\/(\d+)/);
|
77
89
|
return match ? match[1] : null;
|
78
90
|
}
|
79
|
-
|
91
|
+
|
80
92
|
/**
|
81
93
|
* Load research results from API
|
82
94
|
*/
|
@@ -84,25 +96,25 @@
|
|
84
96
|
try {
|
85
97
|
// Show loading state
|
86
98
|
resultsContainer.innerHTML = '<div class="text-center my-5"><i class="fas fa-spinner fa-pulse"></i><p class="mt-3">Loading research results...</p></div>';
|
87
|
-
|
99
|
+
|
88
100
|
// Fetch result from API
|
89
101
|
const response = await fetch(`/research/api/report/${researchId}`);
|
90
|
-
|
102
|
+
|
91
103
|
if (!response.ok) {
|
92
104
|
throw new Error(`HTTP error ${response.status}`);
|
93
105
|
}
|
94
|
-
|
106
|
+
|
95
107
|
const responseData = await response.json();
|
96
108
|
console.log('Original API response:', responseData);
|
97
|
-
|
109
|
+
|
98
110
|
// Store data for export
|
99
111
|
researchData = responseData;
|
100
|
-
|
112
|
+
|
101
113
|
// Check if we have data to display
|
102
114
|
if (!responseData) {
|
103
115
|
throw new Error('No data received from server');
|
104
116
|
}
|
105
|
-
|
117
|
+
|
106
118
|
// Use the API metadata directly
|
107
119
|
if (responseData.metadata && typeof responseData.metadata === 'object') {
|
108
120
|
console.log('Using metadata directly from API response:', responseData.metadata);
|
@@ -111,7 +123,7 @@
|
|
111
123
|
// Fallback to content extraction if no metadata in response
|
112
124
|
populateMetadata(responseData);
|
113
125
|
}
|
114
|
-
|
126
|
+
|
115
127
|
// Render the content
|
116
128
|
if (responseData.content && typeof responseData.content === 'string') {
|
117
129
|
console.log('Rendering content from API response');
|
@@ -121,21 +133,21 @@
|
|
121
133
|
console.log('No direct content found, trying to find content in response');
|
122
134
|
findAndRenderContent(responseData);
|
123
135
|
}
|
124
|
-
|
136
|
+
|
125
137
|
// Enable export buttons
|
126
138
|
if (exportBtn) exportBtn.disabled = false;
|
127
139
|
if (pdfBtn) pdfBtn.disabled = false;
|
128
|
-
|
140
|
+
|
129
141
|
} catch (error) {
|
130
142
|
console.error('Error loading research results:', error);
|
131
143
|
showError(`Error loading research results: ${error.message}`);
|
132
|
-
|
144
|
+
|
133
145
|
// Disable export buttons
|
134
146
|
if (exportBtn) exportBtn.disabled = true;
|
135
147
|
if (pdfBtn) pdfBtn.disabled = true;
|
136
148
|
}
|
137
149
|
}
|
138
|
-
|
150
|
+
|
139
151
|
/**
|
140
152
|
* Populate metadata directly from API response metadata
|
141
153
|
* @param {Object} data - API response with metadata
|
@@ -143,7 +155,7 @@
|
|
143
155
|
function populateMetadataFromApiResponse(data) {
|
144
156
|
const metadata = data.metadata || {};
|
145
157
|
console.log('Using API response metadata:', metadata);
|
146
|
-
|
158
|
+
|
147
159
|
// Query field
|
148
160
|
const queryElement = document.getElementById('result-query');
|
149
161
|
if (queryElement) {
|
@@ -152,18 +164,18 @@
|
|
152
164
|
console.log('Setting query to:', query);
|
153
165
|
queryElement.textContent = query;
|
154
166
|
}
|
155
|
-
|
167
|
+
|
156
168
|
// Generated date field
|
157
169
|
const dateElement = document.getElementById('result-date');
|
158
170
|
if (dateElement) {
|
159
171
|
let dateStr = 'Unknown date';
|
160
|
-
|
172
|
+
|
161
173
|
// Try multiple sources for the timestamp - first from the API response directly, then from metadata
|
162
|
-
const timestamp = data.created_at || data.timestamp || data.date ||
|
174
|
+
const timestamp = data.created_at || data.timestamp || data.date ||
|
163
175
|
metadata.created_at || metadata.timestamp || metadata.date;
|
164
|
-
|
176
|
+
|
165
177
|
console.log('Found timestamp:', timestamp);
|
166
|
-
|
178
|
+
|
167
179
|
if (timestamp) {
|
168
180
|
if (window.formatting && typeof window.formatting.formatDate === 'function') {
|
169
181
|
dateStr = window.formatting.formatDate(timestamp);
|
@@ -177,11 +189,11 @@
|
|
177
189
|
console.error('Error parsing date:', e);
|
178
190
|
}
|
179
191
|
}
|
180
|
-
|
192
|
+
|
181
193
|
// Add duration if available - format as "Xm Ys" for values over 60 seconds
|
182
194
|
if (metadata.duration || metadata.duration_seconds || data.duration_seconds) {
|
183
195
|
const durationSeconds = parseInt(metadata.duration || metadata.duration_seconds || data.duration_seconds, 10);
|
184
|
-
|
196
|
+
|
185
197
|
if (!isNaN(durationSeconds)) {
|
186
198
|
let durationStr;
|
187
199
|
if (durationSeconds < 60) {
|
@@ -195,39 +207,39 @@
|
|
195
207
|
}
|
196
208
|
}
|
197
209
|
}
|
198
|
-
|
210
|
+
|
199
211
|
console.log('Setting date to:', dateStr);
|
200
212
|
dateElement.textContent = dateStr;
|
201
213
|
}
|
202
|
-
|
214
|
+
|
203
215
|
// Mode field
|
204
216
|
const modeElement = document.getElementById('result-mode');
|
205
217
|
if (modeElement) {
|
206
218
|
// Get mode from metadata or main response
|
207
|
-
let mode = metadata.mode || metadata.research_mode || metadata.type ||
|
219
|
+
let mode = metadata.mode || metadata.research_mode || metadata.type ||
|
208
220
|
data.mode || data.research_mode || data.type;
|
209
|
-
|
221
|
+
|
210
222
|
// Detect if this is a detailed report based on content structure
|
211
223
|
if (!mode && data.content) {
|
212
|
-
if (data.content.toLowerCase().includes('table of contents') ||
|
224
|
+
if (data.content.toLowerCase().includes('table of contents') ||
|
213
225
|
data.content.match(/^#.*\n+##.*\n+###/m)) {
|
214
226
|
mode = 'detailed';
|
215
227
|
} else {
|
216
228
|
mode = 'quick';
|
217
229
|
}
|
218
230
|
}
|
219
|
-
|
231
|
+
|
220
232
|
// Format mode using available formatter
|
221
233
|
if (window.formatting && typeof window.formatting.formatMode === 'function') {
|
222
234
|
mode = window.formatting.formatMode(mode);
|
223
235
|
console.log('Formatted mode:', mode);
|
224
236
|
}
|
225
|
-
|
237
|
+
|
226
238
|
console.log('Setting mode to:', mode || 'Quick');
|
227
239
|
modeElement.textContent = mode || 'Quick';
|
228
240
|
}
|
229
241
|
}
|
230
|
-
|
242
|
+
|
231
243
|
/**
|
232
244
|
* Find and render content from various response formats
|
233
245
|
* @param {Object} data - Research data to extract content from
|
@@ -261,7 +273,7 @@
|
|
261
273
|
// Look for any property that might contain the content
|
262
274
|
const contentProps = ['markdown', 'text', 'summary', 'output', 'research_output'];
|
263
275
|
let foundContent = false;
|
264
|
-
|
276
|
+
|
265
277
|
for (const prop of contentProps) {
|
266
278
|
if (data[prop] && typeof data[prop] === 'string') {
|
267
279
|
console.log(`Rendering from data.${prop}`);
|
@@ -270,7 +282,7 @@
|
|
270
282
|
break;
|
271
283
|
}
|
272
284
|
}
|
273
|
-
|
285
|
+
|
274
286
|
if (!foundContent) {
|
275
287
|
// Last resort: try to render the entire data object
|
276
288
|
console.log('No clear content found, rendering entire data object');
|
@@ -278,7 +290,7 @@
|
|
278
290
|
}
|
279
291
|
}
|
280
292
|
}
|
281
|
-
|
293
|
+
|
282
294
|
/**
|
283
295
|
* Populate metadata fields with information from the research data
|
284
296
|
* @param {Object} data - Research data with metadata
|
@@ -288,18 +300,18 @@
|
|
288
300
|
console.log('API response data:', data);
|
289
301
|
console.log('Data type:', typeof data);
|
290
302
|
console.log('Available top-level keys:', Object.keys(data));
|
291
|
-
|
303
|
+
|
292
304
|
// Direct extraction from content
|
293
305
|
if (data.content && typeof data.content === 'string') {
|
294
306
|
console.log('Attempting to extract metadata from content');
|
295
|
-
|
307
|
+
|
296
308
|
// Extract the query from content first line or header
|
297
309
|
// Avoid matching "Table of Contents" as query
|
298
310
|
const queryMatch = data.content.match(/^#\s*([^\n]+)/m) || // First heading
|
299
311
|
data.content.match(/Query:\s*([^\n]+)/i) || // Explicit query label
|
300
312
|
data.content.match(/Question:\s*([^\n]+)/i) || // Question label
|
301
313
|
data.content.match(/^([^\n#]+)(?=\n)/); // First line if not starting with #
|
302
|
-
|
314
|
+
|
303
315
|
if (queryMatch && queryMatch[1] && !queryMatch[1].toLowerCase().includes('table of contents')) {
|
304
316
|
const queryElement = document.getElementById('result-query');
|
305
317
|
if (queryElement) {
|
@@ -319,58 +331,58 @@
|
|
319
331
|
}
|
320
332
|
}
|
321
333
|
}
|
322
|
-
|
334
|
+
|
323
335
|
// Extract generated date/time - Try multiple formats
|
324
|
-
const dateMatch = data.content.match(/Generated at:\s*([^\n]+)/i) ||
|
336
|
+
const dateMatch = data.content.match(/Generated at:\s*([^\n]+)/i) ||
|
325
337
|
data.content.match(/Date:\s*([^\n]+)/i) ||
|
326
338
|
data.content.match(/Generated:\s*([^\n]+)/i) ||
|
327
339
|
data.content.match(/Created:\s*([^\n]+)/i);
|
328
|
-
|
340
|
+
|
329
341
|
if (dateMatch && dateMatch[1]) {
|
330
342
|
const dateElement = document.getElementById('result-date');
|
331
343
|
if (dateElement) {
|
332
344
|
const extractedDate = dateMatch[1].trim();
|
333
345
|
console.log('Extracted date from content:', extractedDate);
|
334
|
-
|
346
|
+
|
335
347
|
// Format the date using the available formatter
|
336
348
|
let formattedDate = extractedDate;
|
337
349
|
if (window.formatting && typeof window.formatting.formatDate === 'function') {
|
338
350
|
formattedDate = window.formatting.formatDate(extractedDate);
|
339
351
|
console.log('Date formatted using formatter:', formattedDate);
|
340
352
|
}
|
341
|
-
|
353
|
+
|
342
354
|
dateElement.textContent = formattedDate || new Date().toLocaleString();
|
343
355
|
}
|
344
356
|
}
|
345
|
-
|
357
|
+
|
346
358
|
// Extract mode
|
347
|
-
const modeMatch = data.content.match(/Mode:\s*([^\n]+)/i) ||
|
359
|
+
const modeMatch = data.content.match(/Mode:\s*([^\n]+)/i) ||
|
348
360
|
data.content.match(/Research type:\s*([^\n]+)/i);
|
349
|
-
|
361
|
+
|
350
362
|
if (modeMatch && modeMatch[1]) {
|
351
363
|
const modeElement = document.getElementById('result-mode');
|
352
364
|
if (modeElement) {
|
353
365
|
const extractedMode = modeMatch[1].trim();
|
354
366
|
console.log('Extracted mode from content:', extractedMode);
|
355
|
-
|
367
|
+
|
356
368
|
// Format mode using available formatter
|
357
369
|
let formattedMode = extractedMode;
|
358
370
|
if (window.formatting && typeof window.formatting.formatMode === 'function') {
|
359
371
|
formattedMode = window.formatting.formatMode(extractedMode);
|
360
372
|
console.log('Mode formatted using formatter:', formattedMode);
|
361
373
|
}
|
362
|
-
|
374
|
+
|
363
375
|
modeElement.textContent = formattedMode || 'Standard';
|
364
376
|
}
|
365
377
|
} else {
|
366
378
|
// Detect mode based on content structure and keywords
|
367
379
|
const modeElement = document.getElementById('result-mode');
|
368
380
|
if (modeElement) {
|
369
|
-
if (data.content.toLowerCase().includes('table of contents') ||
|
381
|
+
if (data.content.toLowerCase().includes('table of contents') ||
|
370
382
|
data.content.toLowerCase().includes('detailed report') ||
|
371
383
|
data.content.match(/^#.*\n+##.*\n+###/m)) { // Has H1, H2, H3 structure
|
372
384
|
modeElement.textContent = 'Detailed';
|
373
|
-
} else if (data.content.toLowerCase().includes('quick research') ||
|
385
|
+
} else if (data.content.toLowerCase().includes('quick research') ||
|
374
386
|
data.content.toLowerCase().includes('summary')) {
|
375
387
|
modeElement.textContent = 'Quick';
|
376
388
|
} else {
|
@@ -378,32 +390,32 @@
|
|
378
390
|
}
|
379
391
|
}
|
380
392
|
}
|
381
|
-
|
393
|
+
|
382
394
|
return; // Exit early since we've handled extraction from content
|
383
395
|
}
|
384
|
-
|
396
|
+
|
385
397
|
// Also check the metadata field which likely contains the actual metadata
|
386
398
|
const metadata = data.metadata || {};
|
387
399
|
console.log('Metadata object:', metadata);
|
388
400
|
if (metadata) {
|
389
401
|
console.log('Metadata keys:', Object.keys(metadata));
|
390
402
|
}
|
391
|
-
|
403
|
+
|
392
404
|
// Extract research object if nested
|
393
405
|
const researchData = data.research || data;
|
394
|
-
|
406
|
+
|
395
407
|
// Debug nested structure if exists
|
396
408
|
if (data.research) {
|
397
409
|
console.log('Nested research data:', data.research);
|
398
410
|
console.log('Research keys:', Object.keys(data.research));
|
399
411
|
}
|
400
|
-
|
412
|
+
|
401
413
|
// Query field
|
402
414
|
const queryElement = document.getElementById('result-query');
|
403
415
|
if (queryElement) {
|
404
416
|
// Try different possible locations for query data
|
405
417
|
let query = 'Unknown query';
|
406
|
-
|
418
|
+
|
407
419
|
if (metadata.query) {
|
408
420
|
query = metadata.query;
|
409
421
|
} else if (metadata.title) {
|
@@ -419,17 +431,17 @@
|
|
419
431
|
} else if (researchData.input) {
|
420
432
|
query = researchData.input;
|
421
433
|
}
|
422
|
-
|
434
|
+
|
423
435
|
console.log('Setting query to:', query);
|
424
436
|
queryElement.textContent = query;
|
425
437
|
}
|
426
|
-
|
438
|
+
|
427
439
|
// Generated date field
|
428
440
|
const dateElement = document.getElementById('result-date');
|
429
441
|
if (dateElement) {
|
430
442
|
let dateStr = 'Unknown date';
|
431
443
|
let timestampField = null;
|
432
|
-
|
444
|
+
|
433
445
|
// Try different possible date fields
|
434
446
|
if (metadata.created_at) {
|
435
447
|
timestampField = metadata.created_at;
|
@@ -446,7 +458,7 @@
|
|
446
458
|
} else if (researchData.time) {
|
447
459
|
timestampField = researchData.time;
|
448
460
|
}
|
449
|
-
|
461
|
+
|
450
462
|
// Format the date using the available formatter
|
451
463
|
if (timestampField) {
|
452
464
|
if (window.formatting && typeof window.formatting.formatDate === 'function') {
|
@@ -462,7 +474,7 @@
|
|
462
474
|
}
|
463
475
|
}
|
464
476
|
}
|
465
|
-
|
477
|
+
|
466
478
|
// Add duration if available
|
467
479
|
if (metadata.duration) {
|
468
480
|
dateStr += ` (${metadata.duration} seconds)`;
|
@@ -471,16 +483,16 @@
|
|
471
483
|
} else if (researchData.duration) {
|
472
484
|
dateStr += ` (${researchData.duration} seconds)`;
|
473
485
|
}
|
474
|
-
|
486
|
+
|
475
487
|
console.log('Setting date to:', dateStr);
|
476
488
|
dateElement.textContent = dateStr;
|
477
489
|
}
|
478
|
-
|
490
|
+
|
479
491
|
// Mode field
|
480
492
|
const modeElement = document.getElementById('result-mode');
|
481
493
|
if (modeElement) {
|
482
494
|
let mode = 'Quick'; // Default to Quick
|
483
|
-
|
495
|
+
|
484
496
|
if (metadata.mode) {
|
485
497
|
mode = metadata.mode;
|
486
498
|
} else if (metadata.research_mode) {
|
@@ -494,17 +506,17 @@
|
|
494
506
|
} else if (researchData.type) {
|
495
507
|
mode = researchData.type;
|
496
508
|
}
|
497
|
-
|
509
|
+
|
498
510
|
// Format mode using available formatter
|
499
511
|
if (window.formatting && typeof window.formatting.formatMode === 'function') {
|
500
512
|
mode = window.formatting.formatMode(mode);
|
501
513
|
}
|
502
|
-
|
514
|
+
|
503
515
|
console.log('Setting mode to:', mode);
|
504
516
|
modeElement.textContent = mode;
|
505
517
|
}
|
506
518
|
}
|
507
|
-
|
519
|
+
|
508
520
|
/**
|
509
521
|
* Render research results in the container
|
510
522
|
* @param {Object|string} data - Research data to render
|
@@ -513,10 +525,10 @@
|
|
513
525
|
try {
|
514
526
|
// Clear container
|
515
527
|
resultsContainer.innerHTML = '';
|
516
|
-
|
528
|
+
|
517
529
|
// Determine the content to render
|
518
530
|
let content = '';
|
519
|
-
|
531
|
+
|
520
532
|
if (typeof data === 'string') {
|
521
533
|
// Direct string content
|
522
534
|
content = data;
|
@@ -544,7 +556,7 @@
|
|
544
556
|
// Last resort: stringify the entire object
|
545
557
|
content = JSON.stringify(data, null, 2);
|
546
558
|
}
|
547
|
-
|
559
|
+
|
548
560
|
// Render the content as Markdown if possible
|
549
561
|
if (window.ui && window.ui.renderMarkdown) {
|
550
562
|
const renderedHtml = window.ui.renderMarkdown(content);
|
@@ -554,18 +566,18 @@
|
|
554
566
|
content = content.replace(/\n/g, '<br>');
|
555
567
|
resultsContainer.innerHTML = `<div class="markdown-content">${content}</div>`;
|
556
568
|
}
|
557
|
-
|
569
|
+
|
558
570
|
// Add syntax highlighting if Prism is available
|
559
571
|
if (window.Prism) {
|
560
572
|
window.Prism.highlightAllUnder(resultsContainer);
|
561
573
|
}
|
562
|
-
|
574
|
+
|
563
575
|
} catch (error) {
|
564
576
|
console.error('Error rendering results:', error);
|
565
577
|
showError(`Error rendering results: ${error.message}`);
|
566
578
|
}
|
567
579
|
}
|
568
|
-
|
580
|
+
|
569
581
|
/**
|
570
582
|
* Show error message in the results container
|
571
583
|
* @param {string} message - Error message
|
@@ -582,7 +594,7 @@
|
|
582
594
|
</p>
|
583
595
|
`;
|
584
596
|
}
|
585
|
-
|
597
|
+
|
586
598
|
/**
|
587
599
|
* Handle export button click
|
588
600
|
*/
|
@@ -591,21 +603,21 @@
|
|
591
603
|
if (!researchData) {
|
592
604
|
throw new Error('No research data available');
|
593
605
|
}
|
594
|
-
|
606
|
+
|
595
607
|
// Get metadata from DOM (which should be populated by now)
|
596
608
|
const query = document.getElementById('result-query')?.textContent || 'Unknown query';
|
597
609
|
const generated = document.getElementById('result-date')?.textContent || 'Unknown date';
|
598
610
|
const mode = document.getElementById('result-mode')?.textContent || 'Quick';
|
599
|
-
|
611
|
+
|
600
612
|
// Create markdown header with metadata
|
601
613
|
let markdownHeader = `# Research Results: ${query}\n\n`;
|
602
614
|
markdownHeader += `- **Generated:** ${generated}\n`;
|
603
615
|
markdownHeader += `- **Mode:** ${mode}\n\n`;
|
604
616
|
markdownHeader += `---\n\n`;
|
605
|
-
|
617
|
+
|
606
618
|
// Extract the content to export
|
607
619
|
let markdownContent = '';
|
608
|
-
|
620
|
+
|
609
621
|
// Try to extract the markdown content from various possible locations
|
610
622
|
if (typeof researchData === 'string') {
|
611
623
|
markdownContent = researchData;
|
@@ -620,9 +632,9 @@
|
|
620
632
|
'output',
|
621
633
|
'research_output'
|
622
634
|
];
|
623
|
-
|
635
|
+
|
624
636
|
let found = false;
|
625
|
-
|
637
|
+
|
626
638
|
// First check direct properties
|
627
639
|
for (const prop of contentProps) {
|
628
640
|
if (researchData[prop] && typeof researchData[prop] === 'string') {
|
@@ -632,7 +644,7 @@
|
|
632
644
|
break;
|
633
645
|
}
|
634
646
|
}
|
635
|
-
|
647
|
+
|
636
648
|
// Then check nested properties
|
637
649
|
if (!found && researchData.research) {
|
638
650
|
for (const prop of contentProps) {
|
@@ -644,7 +656,7 @@
|
|
644
656
|
}
|
645
657
|
}
|
646
658
|
}
|
647
|
-
|
659
|
+
|
648
660
|
// Check results property
|
649
661
|
if (!found && researchData.results) {
|
650
662
|
if (typeof researchData.results === 'string') {
|
@@ -661,34 +673,35 @@
|
|
661
673
|
}
|
662
674
|
}
|
663
675
|
}
|
664
|
-
|
676
|
+
|
665
677
|
// Last resort
|
666
678
|
if (!markdownContent) {
|
667
679
|
console.warn('Could not extract markdown content, using JSON');
|
668
680
|
markdownContent = "```json\n" + JSON.stringify(researchData, null, 2) + "\n```";
|
669
681
|
}
|
670
682
|
}
|
671
|
-
|
683
|
+
|
672
684
|
// Combine header and content
|
673
685
|
const fullMarkdown = markdownHeader + markdownContent;
|
674
|
-
|
686
|
+
|
675
687
|
// Create blob and trigger download
|
676
688
|
const blob = new Blob([fullMarkdown], { type: 'text/markdown' });
|
677
689
|
const link = document.createElement('a');
|
678
690
|
link.href = URL.createObjectURL(blob);
|
679
691
|
link.download = `research_${researchId}.md`;
|
680
|
-
|
692
|
+
|
681
693
|
// Trigger download
|
682
694
|
document.body.appendChild(link);
|
683
695
|
link.click();
|
684
696
|
document.body.removeChild(link);
|
685
|
-
|
697
|
+
|
686
698
|
} catch (error) {
|
687
699
|
console.error('Error exporting markdown:', error);
|
688
700
|
alert(`Error exporting markdown: ${error.message}`);
|
689
701
|
}
|
690
702
|
}
|
691
|
-
|
703
|
+
|
704
|
+
|
692
705
|
/**
|
693
706
|
* Handle PDF export button click
|
694
707
|
*/
|
@@ -697,21 +710,21 @@
|
|
697
710
|
if (!researchData) {
|
698
711
|
throw new Error('No research data available');
|
699
712
|
}
|
700
|
-
|
713
|
+
|
701
714
|
console.log('PDF export initiated for research ID:', researchId);
|
702
|
-
|
715
|
+
|
703
716
|
// Show loading indicator
|
704
717
|
pdfBtn.disabled = true;
|
705
718
|
pdfBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generating PDF...';
|
706
|
-
|
719
|
+
|
707
720
|
// Get metadata from DOM (which should be populated correctly by now)
|
708
721
|
const title = document.getElementById('result-query')?.textContent || `Research ${researchId}`;
|
709
722
|
console.log('Using title for PDF:', title);
|
710
|
-
|
723
|
+
|
711
724
|
// Check if PDF service is available
|
712
725
|
if (window.pdfService && window.pdfService.downloadPdf) {
|
713
726
|
console.log('PDF service available, calling downloadPdf');
|
714
|
-
|
727
|
+
|
715
728
|
// Add the metadata to the researchData for PDF generation
|
716
729
|
const pdfData = {
|
717
730
|
...researchData,
|
@@ -723,7 +736,7 @@
|
|
723
736
|
mode: document.getElementById('result-mode')?.textContent || 'Standard'
|
724
737
|
}
|
725
738
|
};
|
726
|
-
|
739
|
+
|
727
740
|
// Use the PDF service to generate and download the PDF
|
728
741
|
window.pdfService.downloadPdf(pdfData, researchId)
|
729
742
|
.then(() => {
|
@@ -735,7 +748,7 @@
|
|
735
748
|
.catch(error => {
|
736
749
|
console.error('Error generating PDF:', error);
|
737
750
|
alert(`Error generating PDF: ${error.message || 'Unknown error'}`);
|
738
|
-
|
751
|
+
|
739
752
|
// Reset button
|
740
753
|
pdfBtn.disabled = false;
|
741
754
|
pdfBtn.innerHTML = '<i class="fas fa-file-pdf"></i> Download PDF';
|
@@ -744,11 +757,11 @@
|
|
744
757
|
console.error('PDF service not available');
|
745
758
|
throw new Error('PDF service not available');
|
746
759
|
}
|
747
|
-
|
760
|
+
|
748
761
|
} catch (error) {
|
749
762
|
console.error('Error exporting PDF:', error);
|
750
763
|
alert(`Error exporting PDF: ${error.message || 'Unknown error'}`);
|
751
|
-
|
764
|
+
|
752
765
|
// Reset button
|
753
766
|
if (pdfBtn) {
|
754
767
|
pdfBtn.disabled = false;
|
@@ -756,11 +769,131 @@
|
|
756
769
|
}
|
757
770
|
}
|
758
771
|
}
|
759
|
-
|
772
|
+
|
773
|
+
/**
|
774
|
+
* Initialize star rating functionality
|
775
|
+
*/
|
776
|
+
function initializeStarRating() {
|
777
|
+
const starRating = document.getElementById('research-rating');
|
778
|
+
if (!starRating || !researchId) return;
|
779
|
+
|
780
|
+
const stars = starRating.querySelectorAll('.star');
|
781
|
+
let currentRating = 0;
|
782
|
+
|
783
|
+
// Load existing rating
|
784
|
+
loadExistingRating();
|
785
|
+
|
786
|
+
// Add hover effects
|
787
|
+
stars.forEach((star, index) => {
|
788
|
+
star.addEventListener('mouseenter', () => {
|
789
|
+
highlightStars(index + 1);
|
790
|
+
});
|
791
|
+
|
792
|
+
star.addEventListener('click', () => {
|
793
|
+
const rating = index + 1;
|
794
|
+
setRating(rating);
|
795
|
+
saveRating(rating);
|
796
|
+
|
797
|
+
// Visual feedback for saving
|
798
|
+
starRating.style.opacity = '0.7';
|
799
|
+
setTimeout(() => {
|
800
|
+
starRating.style.opacity = '1';
|
801
|
+
}, 500);
|
802
|
+
});
|
803
|
+
});
|
804
|
+
|
805
|
+
starRating.addEventListener('mouseleave', () => {
|
806
|
+
// Restore the permanent rating when mouse leaves
|
807
|
+
setRating(currentRating);
|
808
|
+
});
|
809
|
+
|
810
|
+
function highlightStars(rating) {
|
811
|
+
stars.forEach((star, index) => {
|
812
|
+
// Clear all classes first
|
813
|
+
star.classList.remove('hover', 'active');
|
814
|
+
// Add hover class for preview
|
815
|
+
if (index < rating) {
|
816
|
+
star.classList.add('hover');
|
817
|
+
}
|
818
|
+
});
|
819
|
+
}
|
820
|
+
|
821
|
+
function setRating(rating) {
|
822
|
+
currentRating = rating;
|
823
|
+
stars.forEach((star, index) => {
|
824
|
+
// Clear all classes first
|
825
|
+
star.classList.remove('hover', 'active');
|
826
|
+
// Set active state for permanent rating
|
827
|
+
if (index < rating) {
|
828
|
+
star.classList.add('active');
|
829
|
+
}
|
830
|
+
});
|
831
|
+
}
|
832
|
+
|
833
|
+
async function loadExistingRating() {
|
834
|
+
try {
|
835
|
+
const response = await fetch(`/metrics/api/ratings/${researchId}`);
|
836
|
+
if (response.ok) {
|
837
|
+
const data = await response.json();
|
838
|
+
if (data.rating) {
|
839
|
+
setRating(data.rating);
|
840
|
+
}
|
841
|
+
}
|
842
|
+
} catch (error) {
|
843
|
+
console.log('No existing rating found');
|
844
|
+
}
|
845
|
+
}
|
846
|
+
|
847
|
+
async function saveRating(rating) {
|
848
|
+
try {
|
849
|
+
console.log('Attempting to save rating:', rating);
|
850
|
+
|
851
|
+
// Get CSRF token from meta tag
|
852
|
+
const csrfToken = document.querySelector('meta[name=csrf-token]')?.getAttribute('content');
|
853
|
+
console.log('CSRF token:', csrfToken ? 'found' : 'missing');
|
854
|
+
|
855
|
+
const headers = {
|
856
|
+
'Content-Type': 'application/json',
|
857
|
+
};
|
858
|
+
|
859
|
+
// Add CSRF token if available
|
860
|
+
if (csrfToken) {
|
861
|
+
headers['X-CSRFToken'] = csrfToken;
|
862
|
+
}
|
863
|
+
|
864
|
+
const response = await fetch(`/metrics/api/ratings/${researchId}`, {
|
865
|
+
method: 'POST',
|
866
|
+
headers: headers,
|
867
|
+
body: JSON.stringify({ rating: rating })
|
868
|
+
});
|
869
|
+
|
870
|
+
console.log('Response status:', response.status);
|
871
|
+
const responseText = await response.text();
|
872
|
+
console.log('Response:', responseText);
|
873
|
+
|
874
|
+
if (response.ok) {
|
875
|
+
console.log('Rating saved successfully');
|
876
|
+
try {
|
877
|
+
const responseData = JSON.parse(responseText);
|
878
|
+
if (responseData.status === 'success') {
|
879
|
+
console.log('✅ Rating confirmed saved:', responseData.rating);
|
880
|
+
}
|
881
|
+
} catch (e) {
|
882
|
+
console.log('✅ Rating saved (non-JSON response)');
|
883
|
+
}
|
884
|
+
} else {
|
885
|
+
console.error('❌ Failed to save rating:', response.status, responseText);
|
886
|
+
}
|
887
|
+
} catch (error) {
|
888
|
+
console.error('❌ Error saving rating:', error);
|
889
|
+
}
|
890
|
+
}
|
891
|
+
}
|
892
|
+
|
760
893
|
// Initialize on DOM content loaded
|
761
894
|
if (document.readyState === 'loading') {
|
762
895
|
document.addEventListener('DOMContentLoaded', initializeResults);
|
763
896
|
} else {
|
764
897
|
initializeResults();
|
765
898
|
}
|
766
|
-
})();
|
899
|
+
})();
|