Spaces:
Sleeping
Sleeping
// Array to store problem history versions | |
const problemHistory = []; | |
let currentHistoryIndex = -1; | |
// Store all refined problem suggestions | |
let storedRefinedProblems = {}; | |
document.getElementById("tab1").style.display = "block"; | |
function openTab(evt, tabName) { | |
var i, tabcontent, tablinks; | |
tabcontent = document.getElementsByClassName("tabcontent"); | |
for (i = 0; i < tabcontent.length; i++) { | |
tabcontent[i].style.display = "none"; | |
} | |
tablinks = document.getElementsByClassName("tablinks"); | |
for (i = 0; i < tablinks.length; i++) { | |
tablinks[i].className = tablinks[i].className.replace(" active", ""); | |
} | |
document.getElementById(tabName).style.display = "block"; | |
evt.currentTarget.className += " active"; | |
} | |
function navigateProblemHistory(direction) { | |
const problemInput = document.getElementById('userProblemDescription'); | |
console.log("Current history index:", currentHistoryIndex, "Total history:", problemHistory.length); | |
if (direction === 'prev' && currentHistoryIndex > 0) { | |
currentHistoryIndex--; | |
problemInput.value = problemHistory[currentHistoryIndex]; | |
console.log("Moving to previous version:", currentHistoryIndex); | |
} else if (direction === 'next' && currentHistoryIndex < problemHistory.length - 1) { | |
currentHistoryIndex++; | |
problemInput.value = problemHistory[currentHistoryIndex]; | |
console.log("Moving to next version:", currentHistoryIndex); | |
} | |
updateHistoryNavigation(); | |
} | |
function generateQueries(event) { | |
event.preventDefault(); | |
const userInput = document.getElementById('userInput').value.trim(); | |
if (!userInput) { | |
alert('Please enter a technical problem description'); | |
return; | |
} | |
// Show loading indicator | |
document.querySelector(".progress-text").innerHTML = "Generating key issues ... Please wait." | |
document.getElementById('globalLoadingOverlay').style.display = 'flex'; | |
// Send message to Flask backend | |
fetch('/generate-key-issues', { | |
method: 'POST', | |
body: JSON.stringify({ 'query': userInput }), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
document.getElementById('globalLoadingOverlay').style.display = 'none'; | |
// Check if we have key issues in the response | |
if (data.key_issues && data.key_issues.length > 0) { | |
// Display the key issues | |
displayKeyIssues(data.key_issues); | |
} else if (data.error) { | |
alert('Error: ' + data.error); | |
} else { | |
alert('No key issues found. Please try a different query.'); | |
} | |
}) | |
.catch(error => { | |
document.getElementById('globalLoadingOverlay').style.display = 'none'; | |
console.error('Error:', error); | |
alert('There was an error communicating with the server. Please try again.'); | |
}); | |
} | |
function performSearch(query, queryItemElement) { | |
if (!query.trim()) { | |
alert('Please enter a search query'); | |
return; | |
} | |
const loadingElement = queryItemElement.querySelector('.search-loading'); | |
const tableElement = queryItemElement.querySelector('.results-table'); | |
const tableBody = tableElement.querySelector('tbody'); | |
// Show loading and hide previous results | |
loadingElement.style.display = 'block'; | |
tableElement.style.display = 'none'; | |
// Clear previous results | |
tableBody.innerHTML = ''; | |
// Get checkbox values | |
const pdfChecked = document.getElementById('pdfOption').checked; | |
const patentChecked = document.getElementById('patentOption').checked; | |
const webChecked = document.getElementById('webOption').checked; | |
// Create form data with query and checkbox values | |
const formData = new FormData(); | |
formData.append('query', query); | |
formData.append('pdfOption', pdfChecked); | |
formData.append('patentOption', patentChecked); | |
formData.append('webOption', webChecked); | |
// Send search request to backend | |
fetch('/search', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
loadingElement.style.display = 'none'; | |
if (data.results && data.results.length > 0) { | |
// Populate table with results | |
data.results.forEach(result => { | |
const row = document.createElement('tr'); | |
const typeCell = document.createElement('td'); | |
typeCell.textContent = result.type; | |
const titleCell = document.createElement('td'); | |
titleCell.textContent = result.title; | |
const bodyCell = document.createElement('td'); | |
bodyCell.textContent = result.body; | |
const urlCell = document.createElement('td'); | |
const urlLink = document.createElement('a'); | |
urlLink.href = result.url; | |
urlLink.textContent = result.url; | |
urlLink.className = 'url-link'; | |
urlLink.target = '_blank'; | |
urlCell.appendChild(urlLink); | |
// Add "Analyze" button to the URL cell | |
const analyzeButton = document.createElement('button'); | |
analyzeButton.textContent = 'Analyze'; | |
analyzeButton.className = 'action-button analyze-button'; | |
analyzeButton.onclick = function(e) { | |
e.preventDefault(); | |
analyzePaper(result.url, queryItemElement); | |
}; | |
urlCell.appendChild(document.createElement('br')); | |
urlCell.appendChild(analyzeButton); | |
row.appendChild(typeCell); | |
row.appendChild(titleCell); | |
row.appendChild(bodyCell); | |
row.appendChild(urlCell); | |
tableBody.appendChild(row); | |
}); | |
// Show the table | |
tableElement.style.display = 'table'; | |
} else { | |
// No results | |
const row = document.createElement('tr'); | |
const cell = document.createElement('td'); | |
cell.colSpan = 4; // Updated to 4 since we now have 4 columns with the type column | |
cell.textContent = 'No results found.'; | |
cell.style.textAlign = 'center'; | |
row.appendChild(cell); | |
tableBody.appendChild(row); | |
tableElement.style.display = 'table'; | |
} | |
}) | |
.catch(error => { | |
loadingElement.style.display = 'none'; | |
console.error('Error:', error); | |
// Show error in table | |
const row = document.createElement('tr'); | |
const cell = document.createElement('td'); | |
cell.colSpan = 4; // Updated to 4 since we now have 4 columns with the type column | |
cell.textContent = 'Error performing search. Please try again.'; | |
cell.style.textAlign = 'center'; | |
cell.style.color = 'red'; | |
row.appendChild(cell); | |
tableBody.appendChild(row); | |
tableElement.style.display = 'table'; | |
}); | |
} | |
function analyzePaper(paperUrl, queryItemElement) { | |
const patentBackground = document.getElementById('userProblemDescription').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
// Find the row containing this URL | |
const rows = queryItemElement.querySelectorAll('tbody tr'); | |
let targetRow; | |
let documentType = 'pdf'; // Default type | |
for (const row of rows) { | |
const urlLink = row.querySelector('.url-link'); | |
if (urlLink && urlLink.href === paperUrl) { | |
targetRow = row; | |
// Get the document type from the first cell of the row | |
documentType = row.cells[0].textContent.toLowerCase(); | |
break; | |
} | |
} | |
if (!targetRow) { | |
alert('Could not find the paper in the results table'); | |
return; | |
} | |
// Check if we already have analysis columns | |
let scoreCell, justificationCell; | |
if (targetRow.cells.length <= 4) { | |
// We need to create the cells | |
scoreCell = document.createElement('td'); | |
scoreCell.className = 'score-cell'; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell = document.createElement('td'); | |
justificationCell.className = 'justification-cell'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
targetRow.appendChild(scoreCell); | |
targetRow.appendChild(justificationCell); | |
} else { | |
// Use existing cells | |
scoreCell = targetRow.cells[4]; | |
justificationCell = targetRow.cells[5]; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
} | |
// Send analysis request to backend | |
fetch('/analyze', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': paperUrl, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = data.error; | |
} else if (data.result) { | |
// Get score and justification from the result | |
const result = data.result; | |
const score = result.score !== undefined ? result.score : 'N/A'; | |
const justification = result.justification || 'No justification provided'; | |
// Update cells with results | |
scoreCell.innerHTML = score; | |
justificationCell.innerHTML = justification; | |
// Color-code the score | |
if (score >= 0 && score <= 5) { | |
const redComponent = Math.floor((score / 5) * 255); | |
const greenComponent = Math.floor(((5 - score) / 5) * 255); | |
scoreCell.style.backgroundColor = `rgb(${redComponent}, ${greenComponent}, 0)`; | |
scoreCell.style.color = 'white'; | |
scoreCell.style.fontWeight = 'bold'; | |
scoreCell.style.textAlign = 'center'; | |
} | |
// Add "Extract Insights" button to the justify cell | |
const insightsButton = document.createElement('button'); | |
insightsButton.textContent = 'Extract Insights'; | |
insightsButton.className = 'action-button insights-button'; | |
insightsButton.style.marginTop = '5px'; | |
insightsButton.style.fontSize = '0.8em'; | |
insightsButton.onclick = function(e) { | |
e.preventDefault(); | |
extractInsights(paperUrl, targetRow); | |
}; | |
justificationCell.appendChild(document.createElement('br')); | |
justificationCell.appendChild(insightsButton); | |
} else { | |
scoreCell.innerHTML = 'No data'; | |
justificationCell.innerHTML = 'Analysis failed'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = 'Failed to analyze paper'; | |
}); | |
} | |
function extractInsights(paperUrl, row) { | |
const patentBackground = document.getElementById('userProblemDescription').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
const documentType = row.cells[0].textContent.toLowerCase(); | |
// Check if there's already an insights row for this document | |
let insightsRow = row.nextElementSibling; | |
if (insightsRow && insightsRow.className === 'insights-row') { | |
// Insights row already exists, get the container | |
insightsContainer = insightsRow.querySelector('.insights-container'); | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
} else { | |
// Create insights row that spans across all columns | |
insightsRow = document.createElement('tr'); | |
insightsRow.className = 'insights-row'; | |
const insightsTd = document.createElement('td'); | |
insightsTd.colSpan = row.cells.length; | |
insightsContainer = document.createElement('div'); | |
insightsContainer.className = 'insights-container'; | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
insightsTd.appendChild(insightsContainer); | |
insightsRow.appendChild(insightsTd); | |
// Insert after the current row | |
row.parentNode.insertBefore(insightsRow, row.nextSibling); | |
} | |
// Store the row reference as a data attribute on the container for later access | |
insightsContainer.dataset.parentRow = row.rowIndex; | |
// Send request to extract insights | |
fetch('/post_insights', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': paperUrl, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
insightsContainer.innerHTML = `<div class="error">Error: ${data.error}</div>`; | |
} else if (data.result && data.result.insights) { | |
// Create header with title and actions | |
const insightsHeader = document.createElement('div'); | |
insightsHeader.className = 'insights-header'; | |
const insightsTitle = document.createElement('div'); | |
insightsTitle.className = 'insights-title'; | |
insightsTitle.textContent = 'Document Insights'; | |
const insightsActions = document.createElement('div'); | |
insightsActions.className = 'insights-actions'; | |
// Store the insights for this specific container | |
const containerInsights = [...data.result.insights]; | |
insightsContainer.dataset.allInsights = JSON.stringify(containerInsights); | |
const selectAllBtn = document.createElement('button'); | |
selectAllBtn.className = 'insights-button select-all-btn'; | |
selectAllBtn.textContent = 'Select All'; | |
// Use a closure to ensure the button has access to the correct container and row | |
selectAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.add('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify(allInsights); | |
parentRow.dataset.unselectedInsights = JSON.stringify([]); | |
// Trigger check for enabling/disabling enhance button | |
checkSelectedInsights(); | |
}); | |
const clearAllBtn = document.createElement('button'); | |
clearAllBtn.className = 'insights-button clear-all-btn'; | |
clearAllBtn.textContent = 'Clear All'; | |
// Use a closure to ensure the button has access to the correct container and row | |
clearAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.remove('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify([]); | |
parentRow.dataset.unselectedInsights = JSON.stringify(allInsights); | |
// Trigger check for enabling/disabling enhance button | |
checkSelectedInsights(); | |
}); | |
insightsActions.appendChild(selectAllBtn); | |
insightsActions.appendChild(clearAllBtn); | |
insightsHeader.appendChild(insightsTitle); | |
insightsHeader.appendChild(insightsActions); | |
// Create insight tags | |
const insightsList = document.createElement('div'); | |
insightsList.className = 'insights-list'; | |
// Clear the container and add the new elements | |
insightsContainer.innerHTML = ''; | |
insightsContainer.appendChild(insightsHeader); | |
insightsContainer.appendChild(insightsList); | |
// Initialize selected insights | |
const selectedInsights = []; | |
const unselectedInsights = []; | |
// Add insight tags | |
data.result.insights.forEach(insight => { | |
const tagElement = document.createElement('div'); | |
tagElement.className = 'insight-tag'; | |
tagElement.textContent = insight; | |
tagElement.addEventListener('click', function() { | |
this.classList.toggle('selected'); | |
// Update selected insights | |
updateSelectedInsights(row, insightsContainer); | |
// Trigger check for enabling/disabling enhance button | |
checkSelectedInsights(); | |
}); | |
// Add to unselected by default | |
// tagElement.classList.add('selected'); | |
unselectedInsights.push(insight); | |
insightsList.appendChild(tagElement); | |
}); | |
// Store initial selections | |
row.dataset.selectedInsights = JSON.stringify(selectedInsights); | |
row.dataset.unselectedInsights = JSON.stringify(unselectedInsights); | |
// Trigger check for enabling/disabling enhance button immediately | |
checkSelectedInsights(); | |
} else { | |
insightsContainer.innerHTML = '<div class="error">No insights found</div>'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
insightsContainer.innerHTML = `<div class="error">Error extracting insights: ${error.message}</div>`; | |
}); | |
} | |
function updateSelectedInsights(row, insightsContainer) { | |
const selectedInsights = []; | |
const unselectedInsights = []; | |
const tags = insightsContainer.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => { | |
if (tag.classList.contains('selected')) { | |
selectedInsights.push(tag.textContent); | |
} else { | |
unselectedInsights.push(tag.textContent); | |
} | |
}); | |
row.dataset.selectedInsights = JSON.stringify(selectedInsights); | |
row.dataset.unselectedInsights = JSON.stringify(unselectedInsights); | |
} | |
function checkSelectedInsights() { | |
const selectedInsights = collectAllSelectedInsights(); | |
const enhanceButton = document.getElementById('enhanceProblemButton'); | |
if (selectedInsights.length > 0) { | |
enhanceButton.classList.remove('disabled'); | |
enhanceButton.disabled = false; | |
} else { | |
enhanceButton.classList.add('disabled'); | |
enhanceButton.disabled = true; | |
} | |
} | |
function collectAllSelectedInsights() { | |
const allSelectedInsights = []; | |
const queryItems = document.querySelectorAll('.query-item'); | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
if (row.dataset.selectedInsights) { | |
try { | |
const selectedInsights = JSON.parse(row.dataset.selectedInsights); | |
selectedInsights.forEach(insight => { | |
if (!allSelectedInsights.includes(insight)) { | |
allSelectedInsights.push(insight); | |
} | |
}); | |
} catch (e) { | |
console.error('Error parsing selected insights:', e); | |
} | |
} | |
}); | |
}); | |
return allSelectedInsights; | |
} | |
function exportToExcel() { | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'Generating Excel file...'; | |
} | |
try { | |
// Collect all data from all tables | |
const allData = []; | |
const queryItems = document.querySelectorAll('.query-item'); | |
queryItems.forEach(queryItem => { | |
// Get the search query text for this table | |
const queryText = queryItem.querySelector('.query-field').value; | |
// Get all rows in this table | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Skip empty rows or error rows | |
if (row.cells.length === 1 && row.cells[0].colSpan > 1) { | |
return; // Skip "No results found" rows | |
} | |
// Prepare object for this row | |
const rowData = { | |
'Topic': queryText, | |
'Type': row.cells[0].textContent, | |
'Title': row.cells[1].textContent, | |
'Description': row.cells[2].textContent, | |
'URL': row.querySelector('.url-link')?.href || '' | |
}; | |
// Add score and justification if they exist | |
if (row.cells.length > 4) { | |
rowData['Score'] = row.cells[4].textContent; | |
rowData['Justification'] = row.cells[5].textContent.split('\n')[0]; // Only get the justification text | |
} else { | |
rowData['Score'] = ''; | |
rowData['Justification'] = ''; | |
} | |
// Add selected and unselected insights if they exist | |
if (row.dataset.selectedInsights) { | |
try { | |
const selectedInsights = JSON.parse(row.dataset.selectedInsights); | |
rowData['Selected Insights'] = selectedInsights.join('; '); | |
} catch (e) { | |
rowData['Selected Insights'] = ''; | |
} | |
} else { | |
rowData['Selected Insights'] = ''; | |
} | |
if (row.dataset.unselectedInsights) { | |
try { | |
const unselectedInsights = JSON.parse(row.dataset.unselectedInsights); | |
rowData['Unselected Insights'] = unselectedInsights.join('; '); | |
} catch (e) { | |
rowData['Unselected Insights'] = ''; | |
} | |
} else { | |
rowData['Unselected Insights'] = ''; | |
} | |
allData.push(rowData); | |
}); | |
}); | |
// Check if we have any data | |
if (allData.length === 0) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert('No data to export. Please perform a search first.'); | |
return; | |
} | |
// Get all problem versions | |
const problemVersions = {}; | |
if (problemHistory.length > 0) { | |
problemHistory.forEach((problem, index) => { | |
problemVersions[`Problem Version ${index + 1}`] = problem; | |
}); | |
} else { | |
// If no history, just use the current problem | |
const currentProblem = document.getElementById('userProblemDescription').value.trim(); | |
if (currentProblem) { | |
problemVersions['Problem Version 1'] = currentProblem; | |
} | |
} | |
// Send to server for Excel generation | |
fetch('/export-excel', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'tableData': allData, | |
'userQuery': document.getElementById('userProblemDescription').value, | |
'problemVersions': problemVersions | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`Server error: ${response.status}`); | |
} | |
return response.blob(); | |
}) | |
.then(blob => { | |
// Hide loading overlay | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
// Create URL for the blob | |
const url = window.URL.createObjectURL(new Blob([blob], { | |
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | |
})); | |
// Create a link and trigger download | |
const a = document.createElement('a'); | |
a.href = url; | |
a.download = 'patent_search_results.xlsx'; | |
document.body.appendChild(a); | |
a.click(); | |
// Clean up | |
window.URL.revokeObjectURL(url); | |
document.body.removeChild(a); | |
}) | |
.catch(error => { | |
console.error('Error exporting to Excel:', error); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error exporting to Excel: ${error.message}`); | |
}); | |
} catch (error) { | |
console.error('Error preparing Excel data:', error); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error preparing data for export: ${error.message}`); | |
} | |
} | |
function hasSearchResults() { | |
const resultsContainer = document.getElementById('resultsContainer'); | |
const hasResults = resultsContainer && resultsContainer.style.display !== 'none'; | |
const hasTables = document.querySelectorAll('.results-table[style*="display: table"]').length > 0; | |
return hasResults && hasTables; | |
} | |
function analyzeAllPapers() { | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) loadingOverlay.style.display = 'flex'; | |
if (loadingOverlay) loadingOverlay.querySelector('.progress-text').textContent = 'Analyzing all papers...'; | |
// Get all query items | |
const queryItems = document.querySelectorAll('.query-item'); | |
let totalToAnalyze = 0; | |
let completedAnalyses = 0; | |
// Count total papers that need analysis | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr'); | |
rows.forEach(row => { | |
if (row.cells.length <= 4 || row.cells[4].textContent.trim() === 'Error' || | |
row.cells[4].textContent.trim() === 'N/A' || row.cells[4].textContent.trim() === '') { | |
totalToAnalyze++; | |
} | |
}); | |
}); | |
if (totalToAnalyze === 0) { | |
alert('No papers need analysis'); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
return; | |
} | |
// Function to update progress | |
function updateProgress() { | |
completedAnalyses++; | |
if (loadingOverlay) { | |
const progressElement = loadingOverlay.querySelector('.progress-text'); | |
if (progressElement) { | |
progressElement.textContent = `Analyzing papers: ${completedAnalyses}/${totalToAnalyze}`; | |
} | |
} | |
if (completedAnalyses >= totalToAnalyze) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
} | |
} | |
// Analyze each paper that needs it | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr'); | |
rows.forEach(row => { | |
// Check if this row needs analysis | |
if (row.cells.length <= 4 || row.cells[4].textContent.trim() === 'Error' || | |
row.cells[4].textContent.trim() === 'N/A' || row.cells[4].textContent.trim() === '') { | |
// Get the URL | |
const urlLink = row.querySelector('.url-link'); | |
const documentType = row.cells[0].textContent.toLowerCase(); | |
if (urlLink && urlLink.href) { | |
// Create a promise for this analysis | |
const promise = new Promise((resolve) => { | |
// Prepare for analysis | |
const patentBackground = document.getElementById('userProblemDescription').value.trim(); | |
// Create score and justification cells if needed | |
let scoreCell, justificationCell; | |
if (row.cells.length <= 4) { | |
scoreCell = document.createElement('td'); | |
scoreCell.className = 'score-cell'; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell = document.createElement('td'); | |
justificationCell.className = 'justification-cell'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
row.appendChild(scoreCell); | |
row.appendChild(justificationCell); | |
} else { | |
scoreCell = row.cells[4]; | |
justificationCell = row.cells[5]; | |
scoreCell.innerHTML = '<div class="loading-spinner"></div>'; | |
justificationCell.innerHTML = 'Analyzing...'; | |
} | |
// Send analysis request | |
fetch('/analyze', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': urlLink.href, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = data.error; | |
} else if (data.result) { | |
// Get score and justification | |
const result = data.result; | |
const score = result.score !== undefined ? result.score : 'N/A'; | |
const justification = result.justification || 'No justification provided'; | |
// Update cells | |
scoreCell.innerHTML = score; | |
justificationCell.innerHTML = justification; | |
// Color-code score | |
if (score >= 0 && score <= 5) { | |
const redComponent = Math.floor((score / 5) * 255); | |
const greenComponent = Math.floor(((5 - score) / 5) * 255); | |
scoreCell.style.backgroundColor = `rgb(${redComponent}, ${greenComponent}, 0)`; | |
scoreCell.style.color = 'white'; | |
scoreCell.style.fontWeight = 'bold'; | |
scoreCell.style.textAlign = 'center'; | |
} | |
// Add "Extract Insights" button to the justify cell - for Analyze All function | |
const insightsButton = document.createElement('button'); | |
insightsButton.textContent = 'Extract Insights'; | |
insightsButton.className = 'action-button insights-button'; | |
insightsButton.style.marginTop = '5px'; | |
insightsButton.style.fontSize = '0.8em'; | |
insightsButton.onclick = function(e) { | |
e.preventDefault(); | |
extractInsights(urlLink.href, row); | |
}; | |
justificationCell.appendChild(document.createElement('br')); | |
justificationCell.appendChild(insightsButton); | |
} else { | |
scoreCell.innerHTML = 'No data'; | |
justificationCell.innerHTML = 'Analysis failed'; | |
} | |
resolve(); | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
scoreCell.innerHTML = 'Error'; | |
justificationCell.innerHTML = 'Failed to analyze paper'; | |
resolve(); | |
}); | |
}); | |
// When this analysis is done, update the progress | |
promise.then(() => { | |
updateProgress(); | |
}); | |
} else { | |
// No URL found, count as completed | |
updateProgress(); | |
} | |
} | |
}); | |
}); | |
} | |
function removeFailedAnalyses() { | |
const queryItems = document.querySelectorAll('.query-item'); | |
let removedCount = 0; | |
queryItems.forEach(queryItem => { | |
const tbody = queryItem.querySelector('tbody'); | |
const rows = Array.from(tbody.querySelectorAll('tr')); | |
rows.forEach(row => { | |
if (row.cells.length > 4) { | |
const scoreText = row.cells[4].textContent.trim(); | |
if (scoreText === 'Error' || scoreText === 'N/A' || scoreText === 'No data') { | |
tbody.removeChild(row); | |
removedCount++; | |
} | |
} | |
}); | |
}); | |
// Create and display a simple notification instead of alert | |
const notification = document.createElement('div'); | |
notification.className = 'simple-notification'; | |
notification.textContent = `Removed ${removedCount} papers with failed analyses`; | |
notification.style.position = 'fixed'; | |
notification.style.top = '20px'; | |
notification.style.right = '20px'; | |
notification.style.background = '#4CAF50'; | |
notification.style.color = 'white'; | |
notification.style.padding = '15px'; | |
notification.style.borderRadius = '5px'; | |
notification.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'; | |
notification.style.zIndex = '10000'; | |
// Add notification to the body | |
document.body.appendChild(notification); | |
// Remove notification after 3 seconds | |
setTimeout(() => { | |
if (notification.parentNode) { | |
notification.parentNode.removeChild(notification); | |
} | |
}, 3000); | |
} | |
function extractAllInsights() { | |
const patentBackground = document.getElementById('userProblemDescription').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'Extracting insights from all documents...'; | |
} | |
// Get all query items | |
const queryItems = document.querySelectorAll('.query-item'); | |
let totalDocuments = 0; | |
let completedDocuments = 0; | |
// Count total analyzed documents that need insights extraction | |
const analyzedRows = []; | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Only include rows that have been analyzed (have score and justification cells) | |
if (row.cells.length > 4 && row.cells[4].textContent.trim() !== 'Error' && | |
row.cells[4].textContent.trim() !== 'N/A' && row.cells[4].textContent.trim() !== '') { | |
analyzedRows.push(row); | |
totalDocuments++; | |
} | |
}); | |
}); | |
if (totalDocuments === 0) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert('No analyzed documents found. Please analyze documents first.'); | |
return; | |
} | |
// Function to update progress | |
function updateProgress() { | |
completedDocuments++; | |
if (loadingOverlay) { | |
const progressElement = loadingOverlay.querySelector('.progress-text'); | |
if (progressElement) { | |
progressElement.textContent = `Extracting insights: ${completedDocuments}/${totalDocuments}`; | |
} | |
} | |
if (completedDocuments >= totalDocuments) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
checkSelectedInsights(); // Update enhance button state based on selected insights | |
} | |
} | |
// Process each analyzed document sequentially to avoid overwhelming the server | |
function processNextDocument(index) { | |
if (index >= analyzedRows.length) { | |
return; // All documents processed | |
} | |
const row = analyzedRows[index]; | |
const urlLink = row.querySelector('.url-link'); | |
const documentType = row.cells[0].textContent.toLowerCase(); | |
if (!urlLink || !urlLink.href) { | |
updateProgress(); | |
processNextDocument(index + 1); | |
return; | |
} | |
// Check if there's already an insights row for this document | |
let insightsRow = row.nextElementSibling; | |
if (insightsRow && insightsRow.className === 'insights-row') { | |
// Insights row already exists, get the container | |
insightsContainer = insightsRow.querySelector('.insights-container'); | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
} else { | |
// Create insights row that spans across all columns | |
insightsRow = document.createElement('tr'); | |
insightsRow.className = 'insights-row'; | |
const insightsTd = document.createElement('td'); | |
insightsTd.colSpan = row.cells.length; | |
insightsContainer = document.createElement('div'); | |
insightsContainer.className = 'insights-container'; | |
insightsContainer.innerHTML = '<div class="loading-spinner"></div><div>Extracting insights...</div>'; | |
insightsTd.appendChild(insightsContainer); | |
insightsRow.appendChild(insightsTd); | |
// Insert after the current row | |
row.parentNode.insertBefore(insightsRow, row.nextSibling); | |
} | |
// Store the row reference as a data attribute on the container for later access | |
insightsContainer.dataset.parentRow = row.rowIndex; | |
// Send request to extract insights | |
fetch('/post_insights', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'pdf_url': urlLink.href, | |
'data_type': documentType | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.error) { | |
insightsContainer.innerHTML = `<div class="error">Error: ${data.error}</div>`; | |
} else if (data.result && data.result.insights) { | |
// Create header with title and actions | |
const insightsHeader = document.createElement('div'); | |
insightsHeader.className = 'insights-header'; | |
const insightsTitle = document.createElement('div'); | |
insightsTitle.className = 'insights-title'; | |
insightsTitle.textContent = 'Document Insights'; | |
const insightsActions = document.createElement('div'); | |
insightsActions.className = 'insights-actions'; | |
// Store the insights for this specific container | |
const containerInsights = [...data.result.insights]; | |
insightsContainer.dataset.allInsights = JSON.stringify(containerInsights); | |
const selectAllBtn = document.createElement('button'); | |
selectAllBtn.className = 'insights-button select-all-btn'; | |
selectAllBtn.textContent = 'Select All'; | |
selectAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.add('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify(allInsights); | |
parentRow.dataset.unselectedInsights = JSON.stringify([]); | |
checkSelectedInsights(); | |
}); | |
const clearAllBtn = document.createElement('button'); | |
clearAllBtn.className = 'insights-button clear-all-btn'; | |
clearAllBtn.textContent = 'Clear All'; | |
clearAllBtn.addEventListener('click', function() { | |
const container = this.closest('.insights-container'); | |
const parentRow = row; | |
const allInsights = JSON.parse(container.dataset.allInsights || '[]'); | |
const tags = container.querySelectorAll('.insight-tag'); | |
tags.forEach(tag => tag.classList.remove('selected')); | |
parentRow.dataset.selectedInsights = JSON.stringify([]); | |
parentRow.dataset.unselectedInsights = JSON.stringify(allInsights); | |
checkSelectedInsights(); | |
}); | |
insightsActions.appendChild(selectAllBtn); | |
insightsActions.appendChild(clearAllBtn); | |
insightsHeader.appendChild(insightsTitle); | |
insightsHeader.appendChild(insightsActions); | |
// Create insight tags | |
const insightsList = document.createElement('div'); | |
insightsList.className = 'insights-list'; | |
// Clear the container and add the new elements | |
insightsContainer.innerHTML = ''; | |
insightsContainer.appendChild(insightsHeader); | |
insightsContainer.appendChild(insightsList); | |
// Initialize selected insights | |
const selectedInsights = []; | |
const unselectedInsights = []; | |
// Add insight tags | |
data.result.insights.forEach(insight => { | |
const tagElement = document.createElement('div'); | |
tagElement.className = 'insight-tag'; | |
tagElement.textContent = insight; | |
tagElement.addEventListener('click', function() { | |
this.classList.toggle('selected'); | |
updateSelectedInsights(row, insightsContainer); | |
checkSelectedInsights(); | |
}); | |
// Add to unselected by default | |
// tagElement.classList.add('selected'); | |
unselectedInsights.push(insight); | |
insightsList.appendChild(tagElement); | |
}); | |
// Store initial selections | |
row.dataset.selectedInsights = JSON.stringify(selectedInsights); | |
row.dataset.unselectedInsights = JSON.stringify(unselectedInsights); | |
} else { | |
insightsContainer.innerHTML = '<div class="error">No insights found</div>'; | |
} | |
// Process next document | |
updateProgress(); | |
processNextDocument(index + 1); | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
insightsContainer.innerHTML = `<div class="error">Error extracting insights: ${error.message}</div>`; | |
// Continue with next document even if this one failed | |
updateProgress(); | |
processNextDocument(index + 1); | |
}); | |
} | |
// Start processing documents | |
processNextDocument(0); | |
} | |
function groupInsightsByScore() { | |
// Get all query items | |
const queryItems = document.querySelectorAll('.query-item'); | |
let hasInsights = false; | |
// Create structure to hold insights grouped by score | |
const insightsByScore = { | |
5: { insights: [], sources: [] }, | |
4: { insights: [], sources: [] }, | |
3: { insights: [], sources: [] }, | |
2: { insights: [], sources: [] }, | |
1: { insights: [], sources: [] }, | |
0: { insights: [], sources: [] } | |
}; | |
// Collect all insights from all documents and group by score | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
// Skip if no score or insights | |
if (row.cells.length <= 4 || !row.dataset.selectedInsights) { | |
return; | |
} | |
// Get the score | |
const scoreText = row.cells[4].textContent.trim(); | |
if (scoreText === 'Error' || scoreText === 'N/A' || isNaN(parseFloat(scoreText))) { | |
return; | |
} | |
const score = Math.round(parseFloat(scoreText)); | |
const urlLink = row.querySelector('.url-link'); | |
if (!urlLink) return; | |
// Get insights from this document | |
try { | |
const selectedInsights = JSON.parse(row.dataset.selectedInsights || '[]'); | |
const unselectedInsights = JSON.parse(row.dataset.unselectedInsights || '[]'); | |
const allInsights = [...selectedInsights, ...unselectedInsights]; | |
// Add insights to the appropriate score group | |
if (score >= 0 && score <= 5 && allInsights.length > 0) { | |
hasInsights = true; | |
// For all insights in this document | |
allInsights.forEach(insight => { | |
// Check if this insight is already in the group | |
const existingIndex = insightsByScore[score].insights.findIndex(i => i === insight); | |
if (existingIndex === -1) { | |
// New insight | |
insightsByScore[score].insights.push(insight); | |
insightsByScore[score].sources.push({ | |
url: urlLink.href, | |
title: row.cells[1].textContent.trim().substring(0, 30) + '...', | |
selected: selectedInsights.includes(insight) | |
}); | |
} | |
}); | |
} | |
} catch (e) { | |
console.error('Error parsing insights:', e); | |
} | |
}); | |
}); | |
if (!hasInsights) { | |
alert('No insights found. Please analyze documents and extract insights first.'); | |
return; | |
} | |
// Create or get the grouped insights container | |
let container = document.getElementById('groupedInsightsContainer'); | |
if (!container) { | |
container = document.createElement('div'); | |
container.id = 'groupedInsightsContainer'; | |
container.className = 'grouped-insights-container'; | |
// Insert after the problem input card | |
const card = document.querySelector("#tab2 .card"); | |
card.parentNode.insertBefore(container, card.nextSibling); | |
} | |
// Create header | |
const header = document.createElement('div'); | |
header.className = 'grouped-insights-header'; | |
header.innerHTML = ` | |
<div>Insights Grouped by Score</div> | |
<div class="grouped-insights-actions"> | |
<button class="ai-select-btn pulse" id="aiSelectButton" onclick="aiSelectInsights()">AI Select Insights</button> | |
<div class="grouped-insights-close" onclick="closeGroupedInsights()">×</div> | |
</div> | |
`; | |
// Create content area | |
const content = document.createElement('div'); | |
content.className = 'grouped-insights-content'; | |
// Create score groups | |
for (let score = 5; score >= 0; score--) { | |
const insights = insightsByScore[score].insights; | |
const sources = insightsByScore[score].sources; | |
if (insights.length === 0) continue; | |
const scoreGroup = document.createElement('div'); | |
scoreGroup.className = 'score-group'; | |
scoreGroup.dataset.score = score; | |
// Create score header | |
const scoreHeader = document.createElement('div'); | |
scoreHeader.className = 'score-group-header'; | |
let scoreColor = ''; | |
if (score >= 0 && score <= 5) { | |
const redComponent = Math.floor((score / 5) * 255); | |
const greenComponent = Math.floor(((5 - score) / 5) * 255); | |
scoreColor = `background-color: rgb(${redComponent}, ${greenComponent}, 0); color: white;`; | |
} | |
scoreHeader.innerHTML = ` | |
<div> | |
<span style="display: inline-block; width: 25px; height: 25px; text-align: center; border-radius: 50%; ${scoreColor} font-weight: bold;">${score}</span> | |
<span style="margin-left: 8px;">Score ${score} Insights (${insights.length})</span> | |
</div> | |
<div class="score-group-actions"> | |
<button class="insights-button select-score-all-btn" onclick="selectAllInScore(${score})">Select All</button> | |
<button class="insights-button clear-score-all-btn" onclick="clearAllInScore(${score})">Clear All</button> | |
</div> | |
`; | |
// Create insights list | |
const insightsList = document.createElement('div'); | |
insightsList.className = 'score-insights-list'; | |
// Add each insight | |
insights.forEach((insight, index) => { | |
const source = sources[index]; | |
const tagElement = document.createElement('div'); | |
tagElement.className = `grouped-insight-tag${source.selected ? ' selected' : ''}`; | |
tagElement.dataset.score = score; | |
tagElement.dataset.index = index; | |
tagElement.dataset.source = source.url; | |
// Create insight text with source link | |
tagElement.innerHTML = ` | |
${insight} | |
<a href="${source.url}" target="_blank" class="insight-source" title="${source.title}">${score}</a> | |
`; | |
// Add click event to toggle selection | |
tagElement.addEventListener('click', function(e) { | |
// Prevent clicking on the source link from toggling selection | |
if (e.target.classList.contains('insight-source')) return; | |
this.classList.toggle('selected'); | |
updateGroupedInsightSelections(); | |
}); | |
insightsList.appendChild(tagElement); | |
}); | |
// Add header and list to score group | |
scoreGroup.appendChild(scoreHeader); | |
scoreGroup.appendChild(insightsList); | |
// Add score group to content | |
content.appendChild(scoreGroup); | |
} | |
// Clear the container and add new content | |
container.innerHTML = ''; | |
container.appendChild(header); | |
container.appendChild(content); | |
// Show the container | |
container.style.display = 'block'; | |
// Collapse all accordions | |
const accordions = document.querySelectorAll('.accordion-section'); | |
accordions.forEach(accordion => { | |
const header = accordion.querySelector('.accordion-header'); | |
const body = accordion.querySelector('.accordion-body'); | |
if (header && body && !header.classList.contains('collapsed')) { | |
header.classList.add('collapsed'); | |
body.classList.add('collapsed'); | |
// Update the toggle icon | |
const toggleIcon = header.querySelector('.toggle-icon'); | |
if (toggleIcon) toggleIcon.textContent = '▶'; | |
} | |
}); | |
// Scroll to the insights container | |
container.scrollIntoView({ behavior: 'smooth' }); | |
// Remove the pulse animation after a few seconds | |
setTimeout(() => { | |
const aiSelectBtn = document.getElementById('aiSelectButton'); | |
if (aiSelectBtn) { | |
aiSelectBtn.classList.remove('pulse'); | |
} | |
}, 5000); | |
} | |
function enhanceProblem() { | |
const problemInput = document.getElementById('userProblemDescription'); | |
const originalProblem = problemInput.value.trim(); | |
if (!originalProblem) { | |
alert('Please provide a technical problem description first'); | |
return; | |
} | |
const selectedInsights = collectAllSelectedInsights(); | |
if (selectedInsights.length === 0) { | |
alert('Please select at least one insight to enhance the problem'); | |
return; | |
} | |
// Get user comments if any | |
const userComments = document.getElementById('insightCommentArea')?.value || ''; | |
// If we have stored refined problems for this exact problem, just show them | |
const currentProblemKey = JSON.stringify({ | |
problem: originalProblem, | |
insights: selectedInsights.sort(), | |
comments: userComments | |
}); | |
if (storedRefinedProblems[currentProblemKey]) { | |
showRefinedProblems(storedRefinedProblems[currentProblemKey]); | |
return; | |
} | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'Enhancing problem statement...'; | |
} | |
// Send request to backend to refine the problem | |
fetch('/refine-problem', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'original_problem': originalProblem, | |
'insights': selectedInsights, | |
'user_comments': userComments | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading overlay | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
if (data.error) { | |
alert(`Error: ${data.error}`); | |
return; | |
} | |
if (data.result) { | |
// Add current problem to history if not already there | |
if (currentHistoryIndex === -1) { | |
problemHistory.push(originalProblem); | |
currentHistoryIndex = 0; | |
} | |
// Store the refined problems for future use | |
storedRefinedProblems[currentProblemKey] = data.result; | |
// Show refined problem options | |
showRefinedProblems(data.result); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error enhancing problem: ${error.message}`); | |
}); | |
} | |
function closeGroupedInsights() { | |
const container = document.getElementById('groupedInsightsContainer'); | |
if (container) { | |
container.style.display = 'none'; | |
} | |
} | |
// Select all insights for a specific score | |
function selectAllInScore(score) { | |
const container = document.getElementById('groupedInsightsContainer'); | |
const tags = container.querySelectorAll(`.grouped-insight-tag[data-score="${score}"]`); | |
tags.forEach(tag => { | |
tag.classList.add('selected'); | |
}); | |
updateGroupedInsightSelections(); | |
} | |
// Clear all insights for a specific score | |
function clearAllInScore(score) { | |
const container = document.getElementById('groupedInsightsContainer'); | |
const tags = container.querySelectorAll(`.grouped-insight-tag[data-score="${score}"]`); | |
tags.forEach(tag => { | |
tag.classList.remove('selected'); | |
}); | |
updateGroupedInsightSelections(); | |
} | |
function updateGroupedInsightSelections() { | |
// Get all selected insights from the grouped view | |
const container = document.getElementById('groupedInsightsContainer'); | |
const selectedTags = container.querySelectorAll('.grouped-insight-tag.selected'); | |
// Create a mapping of URL to selected insights | |
const selectionsBySource = {}; | |
// Process each selected tag | |
selectedTags.forEach(tag => { | |
const insight = tag.textContent.trim().replace(/\d+$/, '').trim(); // Remove the score number at the end | |
const sourceUrl = tag.dataset.source; | |
if (!selectionsBySource[sourceUrl]) { | |
selectionsBySource[sourceUrl] = []; | |
} | |
selectionsBySource[sourceUrl].push(insight); | |
}); | |
// Now update the original document rows | |
const queryItems = document.querySelectorAll('.query-item'); | |
queryItems.forEach(queryItem => { | |
const rows = queryItem.querySelectorAll('tbody tr:not(.insights-row)'); | |
rows.forEach(row => { | |
const urlLink = row.querySelector('.url-link'); | |
if (!urlLink) return; | |
const sourceUrl = urlLink.href; | |
// If we have selections for this source | |
if (selectionsBySource[sourceUrl]) { | |
// Get all insights for this row | |
try { | |
// Combine selected and unselected to get all insights | |
const currentSelected = JSON.parse(row.dataset.selectedInsights || '[]'); | |
const currentUnselected = JSON.parse(row.dataset.unselectedInsights || '[]'); | |
const allInsights = [...currentSelected, ...currentUnselected]; | |
// New selected and unselected based on grouped view | |
const newSelected = []; | |
const newUnselected = []; | |
// Process each insight | |
allInsights.forEach(insight => { | |
if (selectionsBySource[sourceUrl].includes(insight)) { | |
newSelected.push(insight); | |
} else { | |
newUnselected.push(insight); | |
} | |
}); | |
// Update the dataset | |
row.dataset.selectedInsights = JSON.stringify(newSelected); | |
row.dataset.unselectedInsights = JSON.stringify(newUnselected); | |
} catch (e) { | |
console.error('Error updating insights selections:', e); | |
} | |
} | |
}); | |
}); | |
// Update enhance button state | |
checkSelectedInsights(); | |
} | |
function showRefinedProblems(result) { | |
const refinedContainer = document.getElementById('refinedProblemContainer'); | |
const tabs = document.getElementById('refinedProblemTabs'); | |
const content = document.getElementById('refinedProblemContent'); | |
// Clear previous content | |
tabs.innerHTML = ''; | |
content.innerHTML = ''; | |
// Add tabs and content for each refined problem | |
let isFirst = true; | |
for (const key in result) { | |
if (result.hasOwnProperty(key)) { | |
const problem = result[key]; | |
// Create tab | |
const tab = document.createElement('div'); | |
tab.className = `refined-problem-tab ${isFirst ? 'active' : ''}`; | |
tab.dataset.target = key; | |
tab.textContent = `Option ${key.split('_')[1]}`; | |
tab.onclick = function() { | |
document.querySelectorAll('.refined-problem-tab').forEach(t => t.classList.remove('active')); | |
document.querySelectorAll('.refined-problem').forEach(p => p.classList.remove('active')); | |
tab.classList.add('active'); | |
document.getElementById(key).classList.add('active'); | |
}; | |
tabs.appendChild(tab); | |
// Create content | |
const problemDiv = document.createElement('div'); | |
problemDiv.className = `refined-problem ${isFirst ? 'active' : ''}`; | |
problemDiv.id = key; | |
const title = document.createElement('div'); | |
title.className = 'refined-problem-title'; | |
title.textContent = problem.title; | |
const description = document.createElement('div'); | |
description.className = 'refined-problem-description'; | |
description.textContent = problem.description; | |
const applyButton = document.createElement('button'); | |
applyButton.className = 'apply-problem-btn'; | |
applyButton.textContent = 'Apply This Problem'; | |
applyButton.onclick = function() { | |
applyRefinedProblem(problem.description); | |
}; | |
problemDiv.appendChild(title); | |
problemDiv.appendChild(description); | |
problemDiv.appendChild(applyButton); | |
content.appendChild(problemDiv); | |
isFirst = false; | |
} | |
} | |
// Show the container | |
refinedContainer.style.display = 'block'; | |
// Scroll to the container | |
refinedContainer.scrollIntoView({ behavior: 'smooth' }); | |
} | |
function applyRefinedProblem(problemText) { | |
const problemInput = document.getElementById('userProblemDescription'); | |
// Add current problem to history | |
problemHistory.push(problemInput.value); | |
currentHistoryIndex = problemHistory.length - 1; // Set to latest version | |
// Update problem text with the new version | |
problemInput.value = problemText; | |
// Now add the new version to history | |
problemHistory.push(problemText); | |
currentHistoryIndex = problemHistory.length - 1; // Update index to point to the newly added version | |
// Update history navigation display | |
updateHistoryNavigation(); | |
// Display is kept visible, removed: document.getElementById('refinedProblemContainer').style.display = 'none'; | |
// Focus on the problem input | |
problemInput.focus(); | |
console.log("Problem history after applying:", problemHistory, "Current index:", currentHistoryIndex); | |
} | |
function updateHistoryNavigation() { | |
const historyNav = document.getElementById('problemHistoryNav'); | |
const prevBtn = historyNav.querySelector('.history-prev'); | |
const nextBtn = historyNav.querySelector('.history-next'); | |
const status = historyNav.querySelector('.history-status'); | |
// Update buttons state | |
prevBtn.classList.toggle('disabled', currentHistoryIndex <= 0); | |
nextBtn.classList.toggle('disabled', currentHistoryIndex >= problemHistory.length - 1); | |
// Update status text | |
if (problemHistory.length > 0) { | |
status.textContent = `Version ${currentHistoryIndex + 1} of ${problemHistory.length}`; | |
historyNav.style.display = 'flex'; | |
} else { | |
historyNav.style.display = 'none'; | |
} | |
} | |
function aiSelectInsights() { | |
const patentBackground = document.getElementById('userProblemDescription').value.trim(); | |
if (!patentBackground) { | |
alert('Please provide a patent background in the input field'); | |
return; | |
} | |
// Change button appearance to show it's working | |
const aiSelectBtn = document.getElementById('aiSelectButton'); | |
if (aiSelectBtn) { | |
aiSelectBtn.disabled = true; | |
aiSelectBtn.textContent = 'AI Selecting...'; | |
aiSelectBtn.style.opacity = '0.7'; | |
} | |
// Show loading overlay | |
const loadingOverlay = document.getElementById('globalLoadingOverlay'); | |
if (loadingOverlay) { | |
loadingOverlay.style.display = 'flex'; | |
loadingOverlay.querySelector('.progress-text').textContent = 'AI selecting the most relevant insights...'; | |
} | |
// Get all insights from all score groups | |
const container = document.getElementById('groupedInsightsContainer'); | |
const insightTags = container.querySelectorAll('.grouped-insight-tag'); | |
const insights = Array.from(insightTags).map(tag => { | |
return tag.textContent.trim().replace(/\d+$/, '').trim(); // Remove the score number | |
}); | |
if (insights.length === 0) { | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert('No insights found'); | |
return; | |
} | |
// Send request to backend to get AI selection | |
fetch('/ai-select-insights', { | |
method: 'POST', | |
body: JSON.stringify({ | |
'patent_background': patentBackground, | |
'insights': insights | |
}), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Reset button appearance | |
if (aiSelectBtn) { | |
aiSelectBtn.disabled = false; | |
aiSelectBtn.textContent = 'AI Select Insights'; | |
aiSelectBtn.style.opacity = '1'; | |
} | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
if (data.error) { | |
alert(`Error: ${data.error}`); | |
return; | |
} | |
if (data.selected_insights && data.selected_insights.length > 0) { | |
// First clear all selections | |
insightTags.forEach(tag => { | |
tag.classList.remove('selected'); | |
}); | |
// Then select the recommended insights | |
insightTags.forEach(tag => { | |
const insightText = tag.textContent.trim().replace(/\d+$/, '').trim(); | |
if (data.selected_insights.includes(insightText)) { | |
tag.classList.add('selected'); | |
} | |
}); | |
// Update the original document rows | |
updateGroupedInsightSelections(); | |
// Remove alert notification - user can see the selected insights visually | |
// alert(`AI selected ${data.selected_insights.length} insights from ${insights.length} available insights`); | |
} else { | |
alert('AI could not identify any relevant insights to select'); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
// Reset button appearance | |
if (aiSelectBtn) { | |
aiSelectBtn.disabled = false; | |
aiSelectBtn.textContent = 'AI Select Insights'; | |
aiSelectBtn.style.opacity = '1'; | |
} | |
if (loadingOverlay) loadingOverlay.style.display = 'none'; | |
alert(`Error using AI to select insights: ${error.message}`); | |
}); | |
} | |
function generateSearchQueries() { | |
function addQueryField(queryText = '', container = null) { | |
const queriesContainer = container || document.getElementById('queriesContainer'); | |
// Count existing query items in this container for indexing | |
const queryItems = container ? | |
container.querySelectorAll('.query-item') : | |
document.getElementById('queriesContainer').querySelectorAll('.query-item'); | |
const queryIndex = queryItems.length + 1; | |
// Create main container for this query item | |
const queryItem = document.createElement('div'); | |
queryItem.className = 'query-item'; | |
// Create container for query field and buttons | |
const queryContainer = document.createElement('div'); | |
queryContainer.className = 'query-container'; | |
// Input field | |
const queryField = document.createElement('input'); | |
queryField.type = 'text'; | |
queryField.className = 'query-field'; | |
queryField.value = queryText; | |
queryField.placeholder = `Search Query ${queryIndex}`; | |
// Delete button | |
const deleteButton = document.createElement('button'); | |
deleteButton.type = 'button'; | |
deleteButton.className = 'action-button delete-button'; | |
deleteButton.textContent = 'Delete'; | |
deleteButton.onclick = function() { | |
queryItem.parentNode.removeChild(queryItem); | |
// Update the placeholder numbers for remaining queries within this container | |
const parent = container || document.getElementById('queriesContainer'); | |
const items = parent.querySelectorAll('.query-item'); | |
items.forEach((item, idx) => { | |
const field = item.querySelector('.query-field'); | |
if (field) field.placeholder = `Search Query ${idx + 1}`; | |
}); | |
}; | |
// Search button | |
const searchButton = document.createElement('button'); | |
searchButton.type = 'button'; | |
searchButton.className = 'action-button search-button'; | |
searchButton.textContent = 'Search'; | |
searchButton.onclick = function() { | |
performSearch(queryField.value, queryItem); | |
}; | |
// Add elements to container | |
queryContainer.appendChild(queryField); | |
queryContainer.appendChild(searchButton); | |
queryContainer.appendChild(deleteButton); | |
// Create loading indicator for this search | |
const searchLoading = document.createElement('div'); | |
searchLoading.className = 'search-loading'; | |
searchLoading.textContent = 'Searching... Please wait.'; | |
// Create table for results | |
const resultsTable = document.createElement('table'); | |
resultsTable.className = 'results-table'; | |
// Add table header | |
const tableHeader = document.createElement('thead'); | |
const headerRow = document.createElement('tr'); | |
const typeHeader = document.createElement('th'); | |
typeHeader.textContent = 'Type'; | |
const titleHeader = document.createElement('th'); | |
titleHeader.textContent = 'Title'; | |
const bodyHeader = document.createElement('th'); | |
bodyHeader.textContent = 'Description'; | |
const urlHeader = document.createElement('th'); | |
urlHeader.textContent = 'URL'; | |
const scoreHeader = document.createElement('th'); | |
scoreHeader.textContent = 'Score'; | |
const justificationHeader = document.createElement('th'); | |
justificationHeader.textContent = 'Justification'; | |
headerRow.appendChild(typeHeader); | |
headerRow.appendChild(titleHeader); | |
headerRow.appendChild(bodyHeader); | |
headerRow.appendChild(urlHeader); | |
headerRow.appendChild(scoreHeader); | |
headerRow.appendChild(justificationHeader); | |
tableHeader.appendChild(headerRow); | |
// Add table body | |
const tableBody = document.createElement('tbody'); | |
resultsTable.appendChild(tableHeader); | |
resultsTable.appendChild(tableBody); | |
// Add all elements to the query item | |
queryItem.appendChild(queryContainer); | |
queryItem.appendChild(searchLoading); | |
queryItem.appendChild(resultsTable); | |
// Add container to the queries list | |
queriesContainer.appendChild(queryItem); | |
} | |
function createAccordionSection(timestamp) { | |
const accordionSection = document.createElement('div'); | |
accordionSection.className = 'accordion-section'; | |
const accordionHeader = document.createElement('div'); | |
accordionHeader.className = 'accordion-header'; | |
accordionHeader.innerHTML = ` | |
<span>Search Results <span class="query-timestamp">${timestamp}</span></span> | |
<span class="toggle-icon">▼</span> | |
`; | |
accordionHeader.onclick = function() { | |
this.classList.toggle('collapsed'); | |
const body = this.nextElementSibling; | |
body.classList.toggle('collapsed'); | |
// Update the toggle icon | |
const toggleIcon = this.querySelector('.toggle-icon'); | |
if (body.classList.contains('collapsed')) { | |
toggleIcon.textContent = '▶'; | |
} else { | |
toggleIcon.textContent = '▼'; | |
} | |
}; | |
const accordionBody = document.createElement('div'); | |
accordionBody.className = 'accordion-body'; | |
const accordionContent = document.createElement('div'); | |
accordionContent.className = 'accordion-content'; | |
accordionBody.appendChild(accordionContent); | |
accordionSection.appendChild(accordionHeader); | |
accordionSection.appendChild(accordionBody); | |
return accordionSection; | |
} | |
const userInput = document.getElementById('userProblemDescription').value.trim(); | |
if (!userInput) { | |
alert('Please enter a technical problem description'); | |
return; | |
} | |
// Show loading indicator | |
document.getElementById('loadingIndicator').style.display = 'block'; | |
// Collapse all existing query sections | |
const existingAccordions = document.querySelectorAll('.accordion-section'); | |
existingAccordions.forEach(accordion => { | |
const header = accordion.querySelector('.accordion-header'); | |
const body = accordion.querySelector('.accordion-body'); | |
if (header && body && !header.classList.contains('collapsed')) { | |
header.classList.add('collapsed'); | |
body.classList.add('collapsed'); | |
} | |
}); | |
// Send message to Flask backend | |
fetch('/chat', { | |
method: 'POST', | |
body: new URLSearchParams({ 'message': userInput }), | |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
document.getElementById('loadingIndicator').style.display = 'none'; | |
try { | |
// Parse the JSON string from the LLM response | |
let jsonData; | |
// First check if data.reply is already an object | |
if (typeof data.reply === 'object') { | |
jsonData = data.reply; | |
} else { | |
// Try to extract JSON if it's a string possibly with markdown code blocks | |
const jsonString = data.reply.replace(/```json|```/g, '').trim(); | |
jsonData = JSON.parse(jsonString); | |
} | |
// Create a new accordion section for these search results | |
const timestamp = new Date().toLocaleString(); | |
const accordionSection = createAccordionSection(timestamp); | |
// Add each query from the response to this accordion | |
Object.keys(jsonData).forEach(key => { | |
addQueryField(jsonData[key], accordionSection.querySelector('.accordion-content')); | |
}); | |
// Add the accordion to the container | |
const queriesContainer = document.getElementById('queriesContainer'); | |
queriesContainer.insertBefore(accordionSection, queriesContainer.firstChild); | |
// Show results container | |
document.getElementById('resultsContainer').style.display = 'block'; | |
} catch (error) { | |
console.error('Error parsing JSON:', error); | |
alert('There was an error processing the response. Please try again.'); | |
} | |
}) | |
.catch(error => { | |
document.getElementById('loadingIndicator').style.display = 'none'; | |
console.error('Error:', error); | |
alert('There was an error communicating with the server. Please try again.'); | |
}); | |
} | |
function displayKeyIssues(keyIssues) { | |
// Get or create the key issues container | |
let keyIssuesContainer = document.getElementById('keyIssuesContainer'); | |
if (!keyIssuesContainer) { | |
keyIssuesContainer = document.createElement('div'); | |
keyIssuesContainer.id = 'keyIssuesContainer'; | |
keyIssuesContainer.className = 'key-issues-container'; | |
document.getElementById('resultsContainer').appendChild(keyIssuesContainer); | |
} | |
// Clear previous content | |
keyIssuesContainer.innerHTML = ''; | |
// Create header | |
const header = document.createElement('h3'); | |
header.textContent = 'Key Issues'; | |
keyIssuesContainer.appendChild(header); | |
// Create issues list | |
const issuesList = document.createElement('div'); | |
issuesList.className = 'key-issues-list'; | |
const KIsubmitButton = document.createElement('button'); | |
KIsubmitButton.className = "btn btn-primary"; | |
KIsubmitButton.id = "key-issues-submit-btn"; | |
KIsubmitButton.textContent = "Generate problem descriptions"; | |
KIsubmitButton.style.marginTop = "10px"; | |
KIsubmitButton.onclick = generateProblemDescriptions; | |
const centerButton = document.createElement("center"); | |
centerButton.appendChild(KIsubmitButton); | |
// Add each key issue | |
keyIssues.forEach(issue => { | |
const issueCard = document.createElement('div'); | |
issueCard.className = 'key-issue-card'; | |
issueCard.dataset.id = issue.id; | |
const issueTitle = document.createElement('div'); | |
issueTitle.className = 'key-issue-title'; | |
issueTitle.textContent = issue.title; | |
const issueDescription = document.createElement('div'); | |
issueDescription.className = 'key-issue-description'; | |
issueDescription.textContent = issue.description; | |
const challengesTitle = document.createElement('div'); | |
challengesTitle.style.fontWeight = 'bold'; | |
challengesTitle.style.marginTop = '10px'; | |
challengesTitle.textContent = 'Challenges:'; | |
const challengesList = document.createElement('ul'); | |
challengesList.className = 'key-issue-challenges'; | |
issue.challenges.forEach(challenge => { | |
const challengeItem = document.createElement('li'); | |
challengeItem.textContent = challenge; | |
challengesList.appendChild(challengeItem); | |
}); | |
const impactTitle = document.createElement('div'); | |
impactTitle.style.fontWeight = 'bold'; | |
impactTitle.style.marginTop = '10px'; | |
impactTitle.textContent = 'Potential Impact:'; | |
const issueImpact = document.createElement('div'); | |
issueImpact.className = 'key-issue-impact'; | |
issueImpact.textContent = issue.potential_impact; | |
// Add click handler to toggle selection | |
issueCard.addEventListener('click', function() { | |
this.classList.toggle('selected'); | |
}); | |
// Append all elements | |
issueCard.appendChild(issueTitle); | |
issueCard.appendChild(issueDescription); | |
issueCard.appendChild(challengesTitle); | |
issueCard.appendChild(challengesList); | |
issueCard.appendChild(impactTitle); | |
issueCard.appendChild(issueImpact); | |
issuesList.appendChild(issueCard); | |
}); | |
keyIssuesContainer.appendChild(issuesList); | |
keyIssuesContainer.appendChild(centerButton); | |
keyIssuesContainer.style.display = "block"; | |
} | |
function generateProblemDescriptions(){ | |
let result = { | |
descriptions: {}, | |
challenges: {} | |
}; | |
const selectedCards = document.querySelectorAll(".key-issue-card.selected"); | |
selectedCards.forEach(card => { | |
const userInput = document.getElementById('userInput').value.trim(); | |
const id = card.getAttribute("data-id"); | |
const description = card.querySelector('.key-issue-description').textContent.trim(); | |
const challenges = Array.from(card.querySelectorAll(".key-issue-challenges li")).map(challenge => challenge.textContent.trim()); | |
result.descriptions[id] = description; | |
result.challenges[id] = challenges; | |
result["technical_topic"] = userInput; | |
}) | |
document.querySelector('.progress-text').innerHTML = "Generating problem descriptions ... This may take a while." | |
document.getElementById('globalLoadingOverlay').style.display = 'flex'; | |
// Send message to Flask backend | |
fetch('/create-several-probdesc', { | |
method: 'POST', | |
body: JSON.stringify(result), | |
headers: { 'Content-Type': 'application/json' } | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Hide loading indicator | |
document.getElementById('globalLoadingOverlay').style.display = 'none'; | |
// Check if we have key issues in the response | |
if (data.problem_descriptions && data.problem_descriptions.length > 0) { | |
// Display the key issues | |
displayProblemDescriptions(data.problem_descriptions); | |
} else if (data.error) { | |
alert('Error: ' + data.error); | |
} else { | |
alert('No problem descriptions found/generated. Please try again with another or the same query.'); | |
} | |
}) | |
.catch(error => { | |
document.getElementById('globalLoadingOverlay').style.display = 'none'; | |
console.error('Error:', error); | |
alert('There was an error communicating with the server. Please try again.'); | |
}); | |
} | |
function displayProblemDescriptions(probDescs) { | |
// Get or create the key issues container | |
let probDescCards = document.getElementById("probDescCards"); | |
let probDescContainer = document.getElementById('probDescContainer'); | |
// Clear previous content | |
probDescContainer.innerHTML = ''; | |
// Create header | |
const header = document.createElement('h3'); | |
header.textContent = 'Problem Descriptions'; | |
probDescContainer.appendChild(header); | |
// Create issues list | |
const descriptionList = document.createElement('div'); | |
descriptionList.className = 'prob-desc-list'; | |
// Add each key issue | |
probDescs.forEach((desc, index) => { | |
const descCard = document.createElement('div'); | |
descCard.className = 'prob-desc-card'; | |
descCard.dataset.id = index+1; | |
const descTitle = document.createElement('div'); | |
descTitle.className = 'prob-desc-title'; | |
descTitle.textContent = `Problem Description n°${index+1}`; | |
const descDescription = document.createElement('div'); | |
descDescription.className = 'prob-desc-description'; | |
descDescription.textContent = desc; | |
// Add click handler to toggle selection | |
descCard.addEventListener('click', function() { | |
document.getElementById("userProblemDescription").textContent = desc; | |
document.getElementsByClassName("tablinks")[1].click(); | |
}); | |
// Append all elements | |
descCard.appendChild(descTitle); | |
descCard.appendChild(descDescription); | |
descriptionList.appendChild(descCard); | |
}); | |
probDescContainer.appendChild(descriptionList); | |
probDescCards.style.display = "block"; | |
} | |
function initRibbonAccordion() { | |
const ribbon = document.querySelector('.ribbon-accordion'); | |
const ribbonHeader = ribbon.querySelector('.ribbon-header'); | |
ribbonHeader.addEventListener('click', function() { | |
ribbon.classList.toggle('collapsed'); | |
}); | |
} | |
function setupRibbonButtons() { | |
// Generate Queries | |
document.getElementById('ribbonGenerateQueriesButton').addEventListener('click', function() { | |
generateSearchQueries(); | |
}); | |
// Analyze All | |
document.getElementById('ribbonAnalyzeAllButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
analyzeAllPapers(); | |
} | |
}); | |
// Remove Failed | |
document.getElementById('ribbonRemoveFailedButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
removeFailedAnalyses(); | |
} | |
}); | |
// Extract All Insights | |
document.getElementById('ribbonExtractAllInsightsButton').addEventListener('click', function() { | |
extractAllInsights(); | |
}); | |
// Group Insights | |
document.getElementById('ribbonGroupInsightsButton').addEventListener('click', function() { | |
groupInsightsByScore(); | |
}); | |
// Enhance Problem | |
document.getElementById('ribbonEnhanceProblemButton').addEventListener('click', function() { | |
enhanceProblem(); | |
}); | |
// Export to Excel | |
document.getElementById('ribbonExportExcelButton').addEventListener('click', function() { | |
if(hasSearchResults()) { | |
exportToExcel(); | |
} | |
}); | |
} | |
initRibbonAccordion(); | |
setupRibbonButtons(); |