| <!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/daisyui@3.9.4/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> |
|
|