|
<!DOCTYPE html> |
|
<html lang="fr" data-theme="light"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Requirements Extractor</title> |
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.css" rel="stylesheet"> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
</head> |
|
<body class="p-8 bg-base-100"> |
|
<div class="container mx-auto"> |
|
<h1 class="text-4xl font-bold text-center mb-8">Requirements Extractor</h1> |
|
<div id="dataFrameForm"> |
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> |
|
<select class="select select-bordered" id="workingGroupSelect"> |
|
<option disabled selected value="">Working Group</option> |
|
<option>SA1</option> |
|
<option>SA2</option> |
|
<option>SA3</option> |
|
<option>SA4</option> |
|
<option>SA5</option> |
|
<option>SA6</option> |
|
<option>CT1</option> |
|
<option>CT2</option> |
|
<option>CT3</option> |
|
<option>CT4</option> |
|
<option>CT5</option> |
|
<option>CT6</option> |
|
</select> |
|
<select class="select select-bordered" id="meetingSelect" disabled> |
|
<option disabled selected value="">Select a working group</option> |
|
</select> |
|
<button class="btn" id="getTDocs">Get TDocs</button> |
|
</div> |
|
</div> |
|
<div class="hidden" id="filters"> |
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> |
|
<select class="select select-bordered" id="docType"> |
|
<option disabled selected value="">Type</option> |
|
<option>Tous</option> |
|
</select> |
|
|
|
<select class="select select-bordered" id="docStatus"> |
|
<option disabled selected value="">Status</option> |
|
<option>Tous</option> |
|
</select> |
|
|
|
<select class="select select-bordered" id="agendaItem"> |
|
<option disabled selected value="">Agenda Item</option> |
|
<option>Tous</option> |
|
</select> |
|
</div> |
|
</div> |
|
|
|
<div class="flex justify-center mt-12 min-h-screen hidden" id="queryReqForm"> |
|
<div class="w-full max-w-md"> |
|
<div class="grid grid-cols-1 gap-4"> |
|
<textarea placeholder="Enter your problem description here ..." |
|
class="w-full mx-auto px-4 py-2 border rounded" id="problemDescription" /> |
|
<button class="w-1/2 mx-auto px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700" id="queryReq"> |
|
Find requirements |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<center> |
|
<span class="loading loading-bars loading-xl hidden" id="loadingBar"></span> |
|
<p class="hidden" id="progressText"></p> |
|
</center> |
|
|
|
|
|
<div class="max-h-[65vh] overflow-y-auto" id="dataFrameDiv"> |
|
<table class="table table-zebra w-full" id="dataFrame"> |
|
<thead class="sticky top-0 bg-base-200 z-10"> |
|
<tr class="bg-base-200"> |
|
<th>TDoc</th> |
|
<th>Title</th> |
|
<th>Type</th> |
|
<th>Status</th> |
|
<th>Agenda Item N°</th> |
|
<th>URL</th> |
|
</tr> |
|
</thead> |
|
<tbody> |
|
</tbody> |
|
</table> |
|
</div> |
|
|
|
<center> |
|
<div id="buttons"> |
|
<p id="reqStatus" class="mt-6 hidden">Requirements extracted</p> |
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> |
|
<button class="btn mt-6" id="getReqs">Get Requirements</button> |
|
<button class="btn mt-6 hidden" id="searchReq">Query requirements</button> |
|
<button class="btn mt-6 hidden" id="categorizeReq">Categorize requirements</button> |
|
</div> |
|
</div> |
|
</center> |
|
</div> |
|
|
|
<script> |
|
let requirements; |
|
|
|
function getDataFrame(){ |
|
document.getElementById("loadingBar").classList.remove("hidden"); |
|
const wg = document.getElementById('workingGroupSelect').value; |
|
const meeting = document.getElementById('meetingSelect').value; |
|
document.getElementById('docType').innerHTML = ` |
|
<option disabled selected value="">Type</option> |
|
<option>Tous</option> |
|
` |
|
|
|
document.getElementById('docStatus').innerHTML = ` |
|
<option disabled selected value="">Status</option> |
|
<option>Tous</option> |
|
` |
|
|
|
document.getElementById('agendaItem').innerHTML = ` |
|
<option disabled selected value="">Agenda Item</option> |
|
<option>Tous</option> |
|
` |
|
const dataFrame = document.getElementById("dataFrame"); |
|
document.getElementById("progressText").classList.remove('hidden') |
|
document.getElementById("progressText").innerHTML = "Loading ..."; |
|
document.getElementById("loadingBar").classList.remove("hidden") |
|
fetch("/get_dataframe", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({"working_group": wg, "meeting": meeting})}) |
|
.then(resp => resp.json()) |
|
.then(data => { |
|
document.getElementById("filters").classList.remove("hidden") |
|
document.getElementById("loadingBar").classList.add("hidden"); |
|
const dataframeBody = dataFrame.querySelector("tbody"); |
|
dataframeBody.innerHTML = ""; |
|
const setType = new Set(); |
|
const setAgenda = new Set(); |
|
const setStatus = new Set(); |
|
data.data.forEach(row => { |
|
const tr = document.createElement("tr"); |
|
tr.setAttribute("data-type", row['Type']); |
|
tr.setAttribute("data-status", row["TDoc Status"]); |
|
tr.setAttribute("data-agenda", row["Agenda item description"]); |
|
tr.innerHTML = ` |
|
<td>${row["TDoc"]}</td> |
|
<td>${row["Title"]}</td> |
|
<td>${row["Type"]}</td> |
|
<td>${row["TDoc Status"]}</td> |
|
<td>${row["Agenda item description"]}</td> |
|
<td> |
|
<a href="${row["URL"]}" class="link">${row["URL"]}</a> |
|
</td> |
|
`; |
|
dataframeBody.appendChild(tr); |
|
setType.add(row["Type"]); |
|
setAgenda.add(row["Agenda item description"]); |
|
setStatus.add(row["TDoc Status"]); |
|
}) |
|
|
|
setType.forEach(tdoctype => { |
|
const option = document.createElement("option"); |
|
option.textContent = tdoctype; |
|
option.value = tdoctype; |
|
document.getElementById('docType').appendChild(option); |
|
}) |
|
|
|
setAgenda.forEach(agenda => { |
|
const option = document.createElement("option"); |
|
option.textContent = agenda; |
|
option.value = agenda; |
|
document.getElementById('agendaItem').appendChild(option); |
|
}) |
|
|
|
setStatus.forEach(status => { |
|
const option = document.createElement("option"); |
|
option.textContent = status; |
|
option.value = status; |
|
document.getElementById('docStatus').appendChild(option); |
|
}) |
|
}) |
|
|
|
document.getElementById("progressText").classList.add('hidden') |
|
document.getElementById("loadingBar").classList.add("hidden") |
|
} |
|
|
|
function filterTable() { |
|
const type = document.getElementById('docType').value |
|
const status = document.getElementById('docStatus').value |
|
const agenda = document.getElementById('agendaItem').value |
|
|
|
document.querySelectorAll('#dataFrame tbody tr').forEach(row => { |
|
const showRow = |
|
(type === 'Tous' || row.dataset.type === type || type === "") && |
|
(status === 'Tous' || row.dataset.status === status || status === "") && |
|
(agenda === 'Tous' || row.dataset.agenda === agenda || agenda === "") |
|
|
|
row.style.display = showRow ? '' : 'none' |
|
}) |
|
} |
|
|
|
function getMeetings(){ |
|
const workingGroup = document.getElementById("workingGroupSelect").value; |
|
document.getElementById("meetingSelect").setAttribute('disabled', 'true') |
|
document.getElementById("meetingSelect").innerHTML = "<option>Loading...</option>" |
|
document.getElementById("getTDocs").setAttribute('disabled', 'true') |
|
fetch("/get_meetings", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({"working_group": workingGroup})}) |
|
.then(resp => resp.json()) |
|
.then(data => { |
|
document.getElementById("meetingSelect").innerHTML = ""; |
|
document.getElementById("meetingSelect").removeAttribute("disabled"); |
|
document.getElementById("getTDocs").removeAttribute("disabled") |
|
for(const [key, value] of Object.entries(data.meetings)){ |
|
const option = document.createElement("option"); |
|
option.textContent = key; |
|
option.value = value; |
|
document.getElementById('meetingSelect').appendChild(option); |
|
} |
|
}) |
|
} |
|
|
|
function generateRequirements(){ |
|
const bodyreq = tableToGenBody(); |
|
document.getElementById("progressText").classList.remove('hidden'); |
|
document.getElementById("progressText").innerHTML = "Generating requirements, please wait, it may take a while ..."; |
|
document.getElementById("loadingBar").classList.remove("hidden"); |
|
|
|
fetch("/generate_requirements", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({"documents": bodyreq})}) |
|
.then(resp => resp.json()) |
|
.then(data => { |
|
requirements = []; |
|
data.requirements.forEach(obj => { |
|
obj.requirements.forEach(req => { |
|
requirements.push({"document": obj.document, "context": obj.context, "requirement": req}) |
|
}) |
|
}) |
|
|
|
document.getElementById("loadingBar").classList.add("hidden"); |
|
document.getElementById("progressText").classList.add("hidden"); |
|
document.getElementById("reqStatus").classList.remove("hidden"); |
|
document.getElementById("getReqs").classList.add("hidden"); |
|
document.getElementById("searchReq").classList.remove("hidden"); |
|
document.getElementById("categorizeReq").classList.remove("hidden"); |
|
}) |
|
} |
|
|
|
function queryRequirements(){ |
|
fetch("/get_reqs_from_query", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({query: document.getElementById("problemDescription").value, requirements})}) |
|
.then(resp => resp.json()) |
|
.then(data => { |
|
const dataFrame = document.getElementById("dataFrameDiv"); |
|
const dataFrameHead = dataFrame.querySelector("thead"); |
|
const dataFrameBody = dataFrame.querySelector("tbody"); |
|
|
|
dataFrame.classList.remove("hidden"); |
|
|
|
dataFrameHead.innerHTML = ` |
|
<th>TDoc</th> |
|
<th>Context</th> |
|
<th>Requirement</th> |
|
`; |
|
|
|
dataFrameBody.innerHTML = ""; |
|
|
|
data.requirements.forEach(req => { |
|
const tr = document.createElement("tr"); |
|
tr.innerHTML = ` |
|
<td>${req["document"]}</td> |
|
<td>${req["context"]}</td> |
|
<td>${req["requirement"]}</td> |
|
`; |
|
dataFrameBody.appendChild(tr); |
|
}) |
|
}) |
|
} |
|
|
|
function tableToGenBody() { |
|
|
|
let columnsMap = {"TDoc": "document", "URL": "url"}; |
|
const headers = Array.from(dataFrame.querySelectorAll('thead th')).map(th => th.innerText.trim()); |
|
|
|
|
|
const selectedIndices = headers |
|
.map((header, idx) => columnsMap[header] ? idx : -1) |
|
.filter(idx => idx !== -1); |
|
|
|
return Array.from(dataFrame.querySelectorAll('tbody tr')) |
|
.filter(row => getComputedStyle(row).display !== 'none') |
|
.map(row => { |
|
const cells = Array.from(row.querySelectorAll('td')); |
|
const obj = {}; |
|
selectedIndices.forEach(idx => { |
|
const originalHeader = headers[idx]; |
|
const newKey = columnsMap[originalHeader]; |
|
obj[newKey] = cells[idx].innerText.trim(); |
|
}); |
|
return obj; |
|
}); |
|
} |
|
|
|
|
|
|
|
document.getElementById('docType').addEventListener('change', filterTable) |
|
document.getElementById('docStatus').addEventListener('change', filterTable) |
|
document.getElementById('agendaItem').addEventListener('change', filterTable) |
|
document.getElementById("workingGroupSelect").addEventListener('change', getMeetings) |
|
document.getElementById('getTDocs').addEventListener('click', getDataFrame) |
|
document.getElementById("getReqs").addEventListener("click", generateRequirements); |
|
document.getElementById("queryReq").addEventListener("click", queryRequirements) |
|
document.getElementById('searchReq').addEventListener('click', ()=>{ |
|
document.getElementById('dataFrameForm').classList.add('hidden'); |
|
document.getElementById('filters').classList.add('hidden'); |
|
document.getElementById('queryReqForm').classList.remove('hidden'); |
|
document.getElementById('dataFrameDiv').classList.add('hidden'); |
|
document.getElementById('buttons').classList.add('hidden'); |
|
}) |
|
</script> |
|
</body> |
|
</html> |
|
|