Spaces:
Restarting
Restarting
<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-[10vh] 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"></textarea> | |
<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> | |
<!-- Tableau des données --> | |
<div class="max-h-[65vh] overflow-y-auto mt-12" 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 hidden" id="getReqs">Extract Requirements</button> | |
<button class="btn mt-6 hidden" id="downloadZip">Download TDocs</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 downloadTDocs(){ | |
const data = tableToGenBody({"TDoc": "doc"}); | |
const dataSet = [...new Set(data.map(item => item.doc))]; | |
console.log(dataSet); | |
let body = {"documents": dataSet, "meeting": document.getElementById('meetingSelect').value}; | |
if (document.getElementById('agendaItem').value != "" | document.getElementById('agendaItem').value != "Tous"){ | |
body['agenda_item'] = document.getElementById('agendaItem').value; | |
} | |
fetch('/download_tdocs', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(body)}) | |
.then(resp => resp.blob()) | |
.then(blob => { | |
const url = window.URL.createObjectURL(blob); | |
const a = document.createElement("a"); | |
a.href = url; | |
let dl_name = `${document.getElementById('meetingSelect').value}`; | |
if (document.getElementById('agendaItem').value != "" | document.getElementById('agendaItem').value != "Tous"){dl_name = dl_name + `_${document.getElementById('agendaItem').value}`}; | |
if (document.getElementById('docStatus').value != "" | document.getElementById('docStatus').value != "Tous"){dl_name = dl_name + `_${document.getElementById('docStatus').value}`}; | |
if (document.getElementById('docType').value != "" | document.getElementById('docType').value != "Tous"){dl_name = `${document.getElementById('docType').value}_${dl_name}`}; | |
if (document.getElementById('queryReqForm').classList.contains('hidden')){dl_name = `requirements_${dl_name}_${url.split('/').pop()}`} | |
dl_name = dl_name + ".zip"; | |
a.download = dl_name; | |
document.body.appendChild(a); | |
a.click(); | |
a.remove(); | |
window.URL.revokeObjectURL(url); // libération mémoire | |
}) | |
} | |
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"); | |
document.getElementById("downloadZip").classList.remove("hidden"); | |
document.getElementById("getReqs").classList.remove("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({"TDoc": "document", "URL": "url"}); | |
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 => { | |
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"); | |
requirements = []; | |
data.requirements.forEach(obj => { | |
obj.requirements.forEach(req => { | |
requirements.push({"document": obj.document, "context": obj.context, "requirement": req}) | |
}) | |
}) | |
}) | |
} | |
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"); | |
document.getElementById("buttons").classList.remove("hidden"); | |
document.getElementById("searchReq").classList.add("hidden"); | |
document.getElementById("categorizeReq").classList.add("hidden"); | |
document.getElementById("getReqs").classList.add("hidden"); | |
document.getElementById("reqStatus").classList.add("hidden"); | |
document.getElementById("downloadZip").classList.remove("hidden"); | |
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(columnsMap) { | |
// columnsMap : { "NomHeaderDansTable": "nom_voulu", ... } | |
const dataFrame = document.getElementById("dataFrame"); | |
const headers = Array.from(dataFrame.querySelectorAll('thead th')).map(th => th.innerText.trim()); | |
// Indices des colonnes à extraire | |
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; | |
}); | |
} | |
// Écouteurs d'événements pour les filtres | |
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("downloadZip").addEventListener('click', downloadTDocs) | |
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> | |