om4r932 commited on
Commit
fe9ab1e
·
1 Parent(s): 1538533

MVP V1: Implemented workflow

Browse files
Files changed (4) hide show
  1. app.py +1 -1
  2. index.html +42 -3
  3. schemas.py +1 -0
  4. static/script.js +363 -59
app.py CHANGED
@@ -359,7 +359,7 @@ def find_requirements_from_problem_description(req: ReqSearchRequest):
359
  requirements = req.requirements
360
  query = req.query
361
 
362
- requirements_text = "\n".join([f"[Selection ID: {x} | Document: {r.document} | Context: {r.context} | Requirement: {r.requirement}]" for x, r in enumerate(requirements)])
363
 
364
  print("Called the LLM")
365
  resp_ai = llm_router.completion(
 
359
  requirements = req.requirements
360
  query = req.query
361
 
362
+ requirements_text = "\n".join([f"[Selection ID: {r.i} | Document: {r.document} | Context: {r.context} | Requirement: {r.requirement}]" for r in requirements])
363
 
364
  print("Called the LLM")
365
  resp_ai = llm_router.completion(
index.html CHANGED
@@ -90,9 +90,48 @@
90
  </table>
91
  </div>
92
 
93
- <div class="w-full max-w-[100%] mx-auto p-6 hidden" id="carousels">
94
- <h1 class="text-xl font-bold mb-8 text-center">Liste des catégories de requirements</h1>
95
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  </div>
97
 
98
  <center>
 
90
  </table>
91
  </div>
92
 
93
+ <div id="categorizeReqContainer" class="hidden">
94
+ <div class="w-full max-w-[100%] mx-auto p-6" id="carousels">
95
+ <h1 class="text-xl font-bold mb-8 text-center">Requirements categories list</h1>
96
+
97
+ </div>
98
+ <div class="w-full max-w-[100%] mx-auto px-6 pb-6" id="catReqActions">
99
+ <div class="flex items-center justify-center gap-4 flex-wrap">
100
+ <!-- Checkbox et Input Limite -->
101
+ <div class="flex items-center gap-2">
102
+ <input
103
+ type="number"
104
+ id="limitInput"
105
+ placeholder="10"
106
+ min="1"
107
+ max="9999"
108
+ class="w-20 px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
109
+ >
110
+ </div>
111
+
112
+ <!-- Boutons -->
113
+
114
+ <button
115
+ id="getSolutions"
116
+ disabled
117
+ class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors duration-200"
118
+ >
119
+ Get solutions
120
+ </button>
121
+
122
+ <button
123
+ id="getSolutionsStepByStep"
124
+ disabled
125
+ class="px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition-colors duration-200"
126
+ >
127
+ Get solutions (Step-by-step)
128
+ </button>
129
+ </div>
130
+ </div>
131
+ </div>
132
+
133
+ <div id="criticizeSoluceContainer" class="hidden">
134
+
135
  </div>
136
 
137
  <center>
schemas.py CHANGED
@@ -33,6 +33,7 @@ class RequirementsResponse(BaseModel):
33
 
34
  # --------------------------------------
35
  class SingleRequirement(BaseModel):
 
36
  document: str
37
  context: str
38
  requirement: str
 
33
 
34
  # --------------------------------------
35
  class SingleRequirement(BaseModel):
36
+ req_id: int
37
  document: str
38
  context: str
39
  requirement: str
static/script.js CHANGED
@@ -1,6 +1,15 @@
1
  let requirements;
 
 
 
2
 
3
  function downloadTDocs() {
 
 
 
 
 
 
4
  const data = tableToGenBody({
5
  "TDoc": "doc"
6
  });
@@ -23,6 +32,10 @@ function downloadTDocs() {
23
  })
24
  .then(resp => resp.blob())
25
  .then(blob => {
 
 
 
 
26
  const url = window.URL.createObjectURL(blob);
27
  const a = document.createElement("a");
28
  a.href = url;
@@ -39,7 +52,7 @@ function downloadTDocs() {
39
  "Tous") {
40
  dl_name = `${document.getElementById('docType').value}_${dl_name}`
41
  };
42
- if (document.getElementById('queryReqForm').classList.contains('hidden')) {
43
  dl_name = `requirements_${dl_name}_${url.split('/').pop()}`
44
  }
45
  dl_name = dl_name + ".zip";
@@ -52,27 +65,20 @@ function downloadTDocs() {
52
  }
53
 
54
  function getDataFrame() {
 
 
 
55
  document.getElementById("loadingBar").classList.remove("hidden");
 
 
56
  const wg = document.getElementById('workingGroupSelect').value;
57
  const meeting = document.getElementById('meetingSelect').value;
58
- document.getElementById('docType').innerHTML = `
59
- <option disabled selected value="">Type</option>
60
- <option>Tous</option>
61
- `
62
-
63
- document.getElementById('docStatus').innerHTML = `
64
- <option disabled selected value="">Status</option>
65
- <option>Tous</option>
66
- `
67
-
68
- document.getElementById('agendaItem').innerHTML = `
69
- <option disabled selected value="">Agenda Item</option>
70
- <option>Tous</option>
71
- `
72
  const dataFrame = document.getElementById("dataFrame");
73
- document.getElementById("progressText").classList.remove('hidden')
74
- document.getElementById("progressText").innerHTML = "Loading ...";
75
- document.getElementById("loadingBar").classList.remove("hidden")
76
  fetch("/get_dataframe", {
77
  method: "POST",
78
  headers: {
@@ -86,9 +92,12 @@ function getDataFrame() {
86
  .then(resp => resp.json())
87
  .then(data => {
88
  document.getElementById("filters").classList.remove("hidden");
89
- document.getElementById("loadingBar").classList.add("hidden");
90
  document.getElementById("downloadZip").classList.remove("hidden");
91
  document.getElementById("getReqs").classList.remove("hidden");
 
 
 
 
92
  const dataframeBody = dataFrame.querySelector("tbody");
93
  dataframeBody.innerHTML = "";
94
  const setType = new Set();
@@ -136,9 +145,6 @@ function getDataFrame() {
136
  document.getElementById('docStatus').appendChild(option);
137
  })
138
  })
139
-
140
- document.getElementById("progressText").classList.add('hidden')
141
- document.getElementById("loadingBar").classList.add("hidden")
142
  }
143
 
144
  function filterTable() {
@@ -157,6 +163,7 @@ function filterTable() {
157
  }
158
 
159
  function getMeetings() {
 
160
  const workingGroup = document.getElementById("workingGroupSelect").value;
161
  document.getElementById("meetingSelect").setAttribute('disabled', 'true')
162
  document.getElementById("meetingSelect").innerHTML = "<option>Loading...</option>"
@@ -173,26 +180,29 @@ function getMeetings() {
173
  .then(resp => resp.json())
174
  .then(data => {
175
  document.getElementById("meetingSelect").innerHTML = "";
176
- document.getElementById("meetingSelect").removeAttribute("disabled");
177
- document.getElementById("getTDocs").removeAttribute("disabled")
178
  for (const [key, value] of Object.entries(data.meetings)) {
179
  const option = document.createElement("option");
180
  option.textContent = key;
181
  option.value = value;
182
  document.getElementById('meetingSelect').appendChild(option);
183
  }
 
 
184
  })
185
  }
186
 
187
  function generateRequirements() {
 
 
 
 
 
 
 
188
  const bodyreq = tableToGenBody({
189
  "TDoc": "document",
190
  "URL": "url"
191
  });
192
- document.getElementById("progressText").classList.remove('hidden');
193
- document.getElementById("progressText").innerHTML =
194
- "Generating requirements, please wait, it may take a while ...";
195
- document.getElementById("loadingBar").classList.remove("hidden");
196
 
197
  fetch("/generate_requirements", {
198
  method: "POST",
@@ -205,20 +215,25 @@ function generateRequirements() {
205
  })
206
  .then(resp => resp.json())
207
  .then(data => {
 
208
  document.getElementById("loadingBar").classList.add("hidden");
209
  document.getElementById("progressText").classList.add("hidden");
210
  document.getElementById("reqStatus").classList.remove("hidden");
211
  document.getElementById("getReqs").classList.add("hidden");
212
  document.getElementById("searchReq").classList.remove("hidden");
213
  document.getElementById("categorizeReq").classList.remove("hidden");
 
 
214
  requirements = [];
215
  data.requirements.forEach(obj => {
216
  obj.requirements.forEach(req => {
217
  requirements.push({
 
218
  "document": obj.document,
219
  "context": obj.context,
220
  "requirement": req
221
  })
 
222
  })
223
  })
224
  })
@@ -246,6 +261,7 @@ function queryRequirements() {
246
  document.getElementById("getReqs").classList.add("hidden");
247
  document.getElementById("reqStatus").classList.add("hidden");
248
  document.getElementById("downloadZip").classList.remove("hidden");
 
249
 
250
  dataFrame.classList.remove("hidden");
251
 
@@ -295,7 +311,7 @@ function tableToGenBody(columnsMap) {
295
 
296
  function createCard(cardTitle, cardSub, cardText) {
297
  return `
298
- <div class="flex-none w-80 bg-white rounded-lg shadow-md p-6 mr-4 border border-gray-200 hover:shadow-lg transition-shadow duration-300">
299
  <h3 class="text-lg font-semibold text-gray-800 mb-2">${cardTitle}</h3>
300
  <p class="text-sm text-gray-600 mb-3 font-medium">${cardSub}</p>
301
  <p class="text-gray-700 text-sm leading-relaxed">${cardText}</p>
@@ -303,47 +319,330 @@ function createCard(cardTitle, cardSub, cardText) {
303
  `;
304
  }
305
 
306
- function createCarousel(carouselTitle, cards) {
 
307
  let cardsHTML = cards.join("\n");
308
  return `
309
- <div class="mb-8">
310
  <h2 class="text-xl font-bold text-gray-800 mb-4">${carouselTitle}</h2>
311
- <div class="overflow-x-auto scrollbar-thin scrollbar-thumb-gray-400 scrollbar-track-gray-200">
312
- <div class="flex pb-4">
313
- ${cardsHTML}
314
- </div>
315
  </div>
316
  </div>
317
  `;
318
  }
319
 
320
- function categorizeRequirements() {
321
- fetch("https://game4all-reqroup.hf.space/categorize_requirements", {
322
- method: "POST",
323
- headers: {
324
- "Content-Type": "application/json"
325
- },
326
- body: JSON.stringify({
327
- requirements
328
- })
 
329
  })
330
- .then(resp => resp.json())
331
- .then(data => {
332
- document.getElementById('dataFrameForm').classList.add('hidden');
333
- document.getElementById('filters').classList.add('hidden');
334
- document.getElementById('carousels').classList.remove('hidden');
335
- document.getElementById('dataFrameDiv').classList.add('hidden');
336
- document.getElementById('buttons').classList.add('hidden');
337
- data.categories.forEach(cat => {
338
- let reqCards = [];
339
- cat.requirements.forEach(reqContent => { // Correction ici
340
- reqCards.push(createCard(reqContent.document, reqContent.context, reqContent.requirement))
341
- });
342
- document.getElementById('carousels').innerHTML += createCarousel(cat.title, reqCards);
343
- })
 
 
344
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  }
346
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  // Écouteurs d'événements pour les filtres
348
 
349
  document.getElementById('docType').addEventListener('change', filterTable)
@@ -352,9 +651,14 @@ document.getElementById('agendaItem').addEventListener('change', filterTable)
352
  document.getElementById("workingGroupSelect").addEventListener('change', getMeetings)
353
  document.getElementById('getTDocs').addEventListener('click', getDataFrame)
354
  document.getElementById("getReqs").addEventListener("click", generateRequirements);
355
- document.getElementById('categorizeReq').addEventListener('click', categorizeRequirements);
356
  document.getElementById("downloadZip").addEventListener('click', downloadTDocs)
357
  document.getElementById("queryReq").addEventListener("click", queryRequirements)
 
 
 
 
 
358
  document.getElementById('searchReq').addEventListener('click', () => {
359
  document.getElementById('dataFrameForm').classList.add('hidden');
360
  document.getElementById('filters').classList.add('hidden');
 
1
  let requirements;
2
+ let categorizedRequirements;
3
+ let solutionsCriticizedVersions = [];
4
+ let isRequirements = false;
5
 
6
  function downloadTDocs() {
7
+ document.getElementById("getReqs").setAttribute('disabled', 'true')
8
+ document.getElementById("downloadZip").setAttribute('disabled', 'true')
9
+ document.getElementById("progressText").classList.remove('hidden');
10
+ document.getElementById("progressText").innerHTML = "Downloading TDocs, please wait, it may take a while ...";
11
+ document.getElementById("loadingBar").classList.remove("hidden");
12
+
13
  const data = tableToGenBody({
14
  "TDoc": "doc"
15
  });
 
32
  })
33
  .then(resp => resp.blob())
34
  .then(blob => {
35
+ document.getElementById("getReqs").removeAttribute('disabled')
36
+ document.getElementById("downloadZip").removeAttribute('disabled')
37
+ document.getElementById("loadingBar").classList.add("hidden");
38
+ document.getElementById("progressText").classList.add("hidden");
39
  const url = window.URL.createObjectURL(blob);
40
  const a = document.createElement("a");
41
  a.href = url;
 
52
  "Tous") {
53
  dl_name = `${document.getElementById('docType').value}_${dl_name}`
54
  };
55
+ if (isRequirements) {
56
  dl_name = `requirements_${dl_name}_${url.split('/').pop()}`
57
  }
58
  dl_name = dl_name + ".zip";
 
65
  }
66
 
67
  function getDataFrame() {
68
+ isRequirements = false;
69
+ document.getElementById("progressText").classList.remove('hidden');
70
+ document.getElementById("progressText").innerHTML = "Getting TDoc list of meeting ...";
71
  document.getElementById("loadingBar").classList.remove("hidden");
72
+ document.getElementById("getTDocs").setAttribute('disabled', 'true')
73
+
74
  const wg = document.getElementById('workingGroupSelect').value;
75
  const meeting = document.getElementById('meetingSelect').value;
76
+
77
+ document.getElementById('docType').innerHTML = `<option disabled selected value="">Type</option><option>Tous</option>`
78
+ document.getElementById('docStatus').innerHTML = `<option disabled selected value="">Status</option><option>Tous</option>`
79
+ document.getElementById('agendaItem').innerHTML = `<option disabled selected value="">Agenda Item</option><option>Tous</option>`
80
+
 
 
 
 
 
 
 
 
 
81
  const dataFrame = document.getElementById("dataFrame");
 
 
 
82
  fetch("/get_dataframe", {
83
  method: "POST",
84
  headers: {
 
92
  .then(resp => resp.json())
93
  .then(data => {
94
  document.getElementById("filters").classList.remove("hidden");
 
95
  document.getElementById("downloadZip").classList.remove("hidden");
96
  document.getElementById("getReqs").classList.remove("hidden");
97
+ document.getElementById("getTDocs").removeAttribute("disabled")
98
+ document.getElementById("progressText").classList.add('hidden');
99
+ document.getElementById("loadingBar").classList.add("hidden");
100
+
101
  const dataframeBody = dataFrame.querySelector("tbody");
102
  dataframeBody.innerHTML = "";
103
  const setType = new Set();
 
145
  document.getElementById('docStatus').appendChild(option);
146
  })
147
  })
 
 
 
148
  }
149
 
150
  function filterTable() {
 
163
  }
164
 
165
  function getMeetings() {
166
+ isRequirements = false;
167
  const workingGroup = document.getElementById("workingGroupSelect").value;
168
  document.getElementById("meetingSelect").setAttribute('disabled', 'true')
169
  document.getElementById("meetingSelect").innerHTML = "<option>Loading...</option>"
 
180
  .then(resp => resp.json())
181
  .then(data => {
182
  document.getElementById("meetingSelect").innerHTML = "";
 
 
183
  for (const [key, value] of Object.entries(data.meetings)) {
184
  const option = document.createElement("option");
185
  option.textContent = key;
186
  option.value = value;
187
  document.getElementById('meetingSelect').appendChild(option);
188
  }
189
+ document.getElementById("meetingSelect").removeAttribute("disabled");
190
+ document.getElementById("getTDocs").removeAttribute("disabled")
191
  })
192
  }
193
 
194
  function generateRequirements() {
195
+ isRequirements = false;
196
+ document.getElementById("getReqs").setAttribute('disabled', 'true')
197
+ document.getElementById("downloadZip").setAttribute('disabled', 'true')
198
+ document.getElementById("progressText").classList.remove('hidden');
199
+ document.getElementById("progressText").innerHTML = "Generating requirements, please wait, it may take a while ...";
200
+ document.getElementById("loadingBar").classList.remove("hidden");
201
+
202
  const bodyreq = tableToGenBody({
203
  "TDoc": "document",
204
  "URL": "url"
205
  });
 
 
 
 
206
 
207
  fetch("/generate_requirements", {
208
  method: "POST",
 
215
  })
216
  .then(resp => resp.json())
217
  .then(data => {
218
+ let req_id = 0;
219
  document.getElementById("loadingBar").classList.add("hidden");
220
  document.getElementById("progressText").classList.add("hidden");
221
  document.getElementById("reqStatus").classList.remove("hidden");
222
  document.getElementById("getReqs").classList.add("hidden");
223
  document.getElementById("searchReq").classList.remove("hidden");
224
  document.getElementById("categorizeReq").classList.remove("hidden");
225
+ document.getElementById("getReqs").removeAttribute('disabled')
226
+ document.getElementById("downloadZip").removeAttribute('disabled')
227
  requirements = [];
228
  data.requirements.forEach(obj => {
229
  obj.requirements.forEach(req => {
230
  requirements.push({
231
+ req_id,
232
  "document": obj.document,
233
  "context": obj.context,
234
  "requirement": req
235
  })
236
+ req_id++;
237
  })
238
  })
239
  })
 
261
  document.getElementById("getReqs").classList.add("hidden");
262
  document.getElementById("reqStatus").classList.add("hidden");
263
  document.getElementById("downloadZip").classList.remove("hidden");
264
+ isRequirements = true;
265
 
266
  dataFrame.classList.remove("hidden");
267
 
 
311
 
312
  function createCard(cardTitle, cardSub, cardText) {
313
  return `
314
+ <div class="bg-white rounded-lg shadow-md p-6 border border-gray-200 hover:shadow-lg transition-shadow duration-300">
315
  <h3 class="text-lg font-semibold text-gray-800 mb-2">${cardTitle}</h3>
316
  <p class="text-sm text-gray-600 mb-3 font-medium">${cardSub}</p>
317
  <p class="text-gray-700 text-sm leading-relaxed">${cardText}</p>
 
319
  `;
320
  }
321
 
322
+
323
+ function createCarousel(carouselTitle, id, cards) {
324
  let cardsHTML = cards.join("\n");
325
  return `
326
+ <div class="mb-8" id="category-${id}">
327
  <h2 class="text-xl font-bold text-gray-800 mb-4">${carouselTitle}</h2>
328
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
329
+ ${cardsHTML}
 
 
330
  </div>
331
  </div>
332
  `;
333
  }
334
 
335
+ function categorizeRequirements(max_categories) {
336
+ isRequirements = false;
337
+ fetch("https://game4all-reqroup.hf.space/reqs/categorize_requirements", {
338
+ method: "POST",
339
+ headers: {
340
+ "Content-Type": "application/json"
341
+ },
342
+ body: JSON.stringify({
343
+ requirements,
344
+ "max_n_categories": max_categories
345
  })
346
+ })
347
+ .then(resp => resp.json())
348
+ .then(data => {
349
+ categorizedRequirements = data;
350
+ document.getElementById('dataFrameForm').classList.add('hidden');
351
+ document.getElementById('filters').classList.add('hidden');
352
+ document.getElementById('categorizeReqContainer').classList.remove('hidden');
353
+ document.getElementById('dataFrameDiv').classList.add('hidden');
354
+ document.getElementById('buttons').classList.add('hidden');
355
+ document.getElementById('carousels').innerHTML = '<h1 class="text-xl font-bold mb-8 text-center">Requirements categories list</h1>'
356
+ data.categories.forEach(cat => {
357
+ let reqCards = [];
358
+ cat.requirements.forEach(reqContent => { // Correction ici
359
+ reqCards.push(createCard(reqContent.document, reqContent.context, reqContent.requirement))
360
+ });
361
+ document.getElementById('carousels').innerHTML += createCarousel(cat.title, cat.id, reqCards);
362
  })
363
+ })
364
+ }
365
+
366
+ // Variable globale pour tracker les états d'ouverture par catégorie
367
+ let accordionStates = {};
368
+
369
+ function createSolutionAccordion(solutionCriticizedHistory, containerId, versionIndex = 0, categoryIndex = null) {
370
+ const container = document.getElementById(containerId);
371
+ if (!container) {
372
+ console.error(`Container with ID "${containerId}" not found`);
373
+ return;
374
+ }
375
+
376
+ // Si categoryIndex est spécifié, ne mettre à jour que cette catégorie
377
+ if (categoryIndex !== null) {
378
+ updateSingleAccordion(solutionCriticizedHistory, containerId, versionIndex, categoryIndex);
379
+ return;
380
+ }
381
+
382
+ // Vider le container seulement si on recrée tout
383
+ container.innerHTML = '';
384
+
385
+ // Récupérer les données de la version actuelle directement
386
+ const currentVersionData = solutionCriticizedHistory[versionIndex];
387
+
388
+ // Créer l'accordéon principal
389
+ const accordion = document.createElement('div');
390
+ accordion.className = 'space-y-2';
391
+ accordion.id = 'main-accordion';
392
+
393
+ // Afficher seulement les solutions de la version actuelle
394
+ currentVersionData.critiques.forEach((item, index) => {
395
+ createSingleAccordionItem(item, index, versionIndex, solutionCriticizedHistory, accordion);
396
+ });
397
+
398
+ // Ajouter l'accordéon au container
399
+ container.appendChild(accordion);
400
+ }
401
+
402
+ function createSingleAccordionItem(item, index, versionIndex, solutionCriticizedHistory, accordion) {
403
+ const solution = item.solution;
404
+ const criticism = item.criticism;
405
+
406
+ // Récupérer le titre de la catégorie
407
+ const categoryTitle = document.querySelector(`#category-${index} h2`)?.textContent || `Catégorie ${index + 1}`;
408
+
409
+ // Container pour chaque solution
410
+ const solutionCard = document.createElement('div');
411
+ solutionCard.className = 'border border-gray-200 rounded-md shadow-sm';
412
+ solutionCard.id = `accordion-item-${index}`;
413
+
414
+ // En-tête de l'accordéon avec navigation
415
+ const header = document.createElement('div');
416
+ header.className = 'bg-gray-50 px-4 py-2 cursor-pointer hover:bg-gray-100 transition-colors duration-200';
417
+
418
+ const currentVersion = versionIndex + 1;
419
+ const totalVersions = solutionCriticizedHistory.length;
420
+
421
+ header.innerHTML = `
422
+ <div class="flex justify-between items-center">
423
+ <div class="flex items-center space-x-3">
424
+ <h3 class="text-sm font-semibold text-gray-800">${categoryTitle}</h3>
425
+ <div class="flex items-center space-x-2 bg-white px-3 py-1 rounded-full border">
426
+ <button class="version-btn-left w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-100 transition-colors ${currentVersion === 1 ? 'opacity-50 cursor-not-allowed' : ''}"
427
+ data-solution-index="${index}"
428
+ ${currentVersion === 1 ? 'disabled' : ''}>
429
+ <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
430
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
431
+ </svg>
432
+ </button>
433
+ <span class="text-xs font-medium text-gray-600 min-w-[60px] text-center version-indicator">Version ${currentVersion}</span>
434
+ <button class="version-btn-right w-6 h-6 flex items-center justify-center rounded-full hover:bg-gray-100 transition-colors ${currentVersion === totalVersions ? 'opacity-50 cursor-not-allowed' : ''}"
435
+ data-solution-index="${index}"
436
+ ${currentVersion === totalVersions ? 'disabled' : ''}>
437
+ <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
438
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
439
+ </svg>
440
+ </button>
441
+ </div>
442
+ </div>
443
+ <svg class="w-4 h-4 transform transition-transform duration-200 accordion-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
444
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
445
+ </svg>
446
+ </div>
447
+ `;
448
+
449
+ // Contenu de l'accordéon
450
+ const content = document.createElement('div');
451
+ content.className = `accordion-content px-4 py-3 space-y-3`;
452
+ content.id = `content-${index}`;
453
+
454
+ // Vérifier l'état d'ouverture précédent
455
+ const isOpen = accordionStates[index] || false;
456
+ if (!isOpen) {
457
+ content.classList.add('hidden');
458
+ } else {
459
+ header.querySelector('.accordion-icon').style.transform = 'rotate(180deg)';
460
  }
461
 
462
+ // Section Problem Description
463
+ const problemSection = document.createElement('div');
464
+ problemSection.className = 'bg-red-50 border-l-2 border-red-400 p-3 rounded-r-md';
465
+ problemSection.innerHTML = `
466
+ <h4 class="text-sm font-semibold text-red-800 mb-2 flex items-center">
467
+ <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
468
+ <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
469
+ </svg>
470
+ Problem Description
471
+ </h4>
472
+ <p class="text-xs text-gray-700 leading-relaxed">${solution['Problem Description'] || 'Aucune description du problème disponible.'}</p>
473
+ `;
474
+
475
+ // Section Solution Description
476
+ const solutionSection = document.createElement('div');
477
+ solutionSection.className = 'bg-green-50 border-l-2 border-green-400 p-3 rounded-r-md';
478
+ solutionSection.innerHTML = `
479
+ <h4 class="text-sm font-semibold text-green-800 mb-2 flex items-center">
480
+ <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
481
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
482
+ </svg>
483
+ Solution Description
484
+ </h4>
485
+ <p class="text-xs text-gray-700 leading-relaxed">${solution['Solution Description'] || 'Aucune description de solution disponible.'}</p>
486
+ `;
487
+
488
+ // Section Critique
489
+ const critiqueSection = document.createElement('div');
490
+ critiqueSection.className = 'bg-yellow-50 border-l-2 border-yellow-400 p-3 rounded-r-md';
491
+
492
+ let critiqueContent = `
493
+ <h4 class="text-sm font-semibold text-yellow-800 mb-2 flex items-center">
494
+ <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
495
+ <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
496
+ </svg>
497
+ Critique & Analysis
498
+ </h4>
499
+ `;
500
+
501
+ // Sous-sections de critique
502
+ if (criticism.technical_challenges && criticism.technical_challenges.length > 0) {
503
+ critiqueContent += `
504
+ <div class="mb-2">
505
+ <h5 class="text-xs font-semibold text-yellow-700 mb-1">Technical Challenges:</h5>
506
+ <ul class="list-disc list-inside space-y-0.5 text-xs text-gray-700 ml-3">
507
+ ${criticism.technical_challenges.map(challenge => `<li>${challenge}</li>`).join('')}
508
+ </ul>
509
+ </div>
510
+ `;
511
+ }
512
+
513
+ if (criticism.weaknesses && criticism.weaknesses.length > 0) {
514
+ critiqueContent += `
515
+ <div class="mb-2">
516
+ <h5 class="text-xs font-semibold text-yellow-700 mb-1">Weaknesses:</h5>
517
+ <ul class="list-disc list-inside space-y-0.5 text-xs text-gray-700 ml-3">
518
+ ${criticism.weaknesses.map(weakness => `<li>${weakness}</li>`).join('')}
519
+ </ul>
520
+ </div>
521
+ `;
522
+ }
523
+
524
+ if (criticism.limitations && criticism.limitations.length > 0) {
525
+ critiqueContent += `
526
+ <div class="mb-2">
527
+ <h5 class="text-xs font-semibent text-yellow-700 mb-1">Limitations:</h5>
528
+ <ul class="list-disc list-inside space-y-0.5 text-xs text-gray-700 ml-3">
529
+ ${criticism.limitations.map(limitation => `<li>${limitation}</li>`).join('')}
530
+ </ul>
531
+ </div>
532
+ `;
533
+ }
534
+
535
+ critiqueSection.innerHTML = critiqueContent;
536
+
537
+ // Ajouter les sections au contenu
538
+ content.appendChild(problemSection);
539
+ content.appendChild(solutionSection);
540
+ content.appendChild(critiqueSection);
541
+
542
+ // Événement de clic pour l'accordéon (exclure les boutons de navigation)
543
+ header.addEventListener('click', (e) => {
544
+ // Ne pas déclencher l'accordéon si on clique sur les boutons de navigation
545
+ if (e.target.closest('.version-btn-left') || e.target.closest('.version-btn-right')) {
546
+ return;
547
+ }
548
+
549
+ const icon = header.querySelector('.accordion-icon');
550
+ const isCurrentlyOpen = !content.classList.contains('hidden');
551
+
552
+ if (isCurrentlyOpen) {
553
+ content.classList.add('hidden');
554
+ icon.style.transform = 'rotate(0deg)';
555
+ accordionStates[index] = false;
556
+ } else {
557
+ content.classList.remove('hidden');
558
+ icon.style.transform = 'rotate(180deg)';
559
+ accordionStates[index] = true;
560
+ }
561
+ });
562
+
563
+ // Événements de navigation pour cette catégorie spécifique
564
+ header.querySelector('.version-btn-left')?.addEventListener('click', (e) => {
565
+ e.stopPropagation();
566
+ updateSingleAccordion(solutionCriticizedHistory, 'accordion-container', versionIndex - 1, index);
567
+ });
568
+
569
+ header.querySelector('.version-btn-right')?.addEventListener('click', (e) => {
570
+ e.stopPropagation();
571
+ updateSingleAccordion(solutionCriticizedHistory, 'accordion-container', versionIndex + 1, index);
572
+ });
573
+
574
+ // Assembler la carte de solution
575
+ solutionCard.appendChild(header);
576
+ solutionCard.appendChild(content);
577
+ accordion.appendChild(solutionCard);
578
+ }
579
+
580
+ function updateSingleAccordion(solutionCriticizedHistory, containerId, newVersionIndex, categoryIndex) {
581
+ // Vérifier les limites de version
582
+ if (newVersionIndex < 0 || newVersionIndex >= solutionCriticizedHistory.length) {
583
+ return;
584
+ }
585
+
586
+ const accordionItem = document.getElementById(`accordion-item-${categoryIndex}`);
587
+ if (!accordionItem) return;
588
+
589
+ const newData = solutionCriticizedHistory[newVersionIndex];
590
+ const newItem = newData.critiques[categoryIndex];
591
+
592
+ if (!newItem) return;
593
+
594
+ // Mettre à jour le contenu de cette catégorie spécifique
595
+ const tempContainer = document.createElement('div');
596
+ createSingleAccordionItem(newItem, categoryIndex, newVersionIndex, solutionCriticizedHistory, tempContainer);
597
+
598
+ // Remplacer l'ancien item par le nouveau
599
+ accordionItem.parentNode.replaceChild(tempContainer.firstChild, accordionItem);
600
+ }
601
+
602
+ // Fonction d'initialisation simplifiée
603
+ function initializeSolutionAccordion(solutionCriticizedHistory, containerId, startVersion = 0) {
604
+ // Réinitialiser les états d'accordéon
605
+ accordionStates = {};
606
+ createSolutionAccordion(solutionCriticizedHistory, containerId, startVersion);
607
+ document.getElementById("criticizeSoluceContainer").classList.remove('hidden')
608
+ }
609
+
610
+ async function generateSolutions(){
611
+ isRequirements = false;
612
+ let response = await fetch('https://game4all-reqroup.hf.space/solution/search_solutions_gemini', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(categorizedRequirements)})
613
+ let responseObj = await response.json()
614
+ return responseObj;
615
+ }
616
+
617
+ async function generateCriticisms(solutions){
618
+ isRequirements = false;
619
+ let response = await fetch('https://game4all-reqroup.hf.space/solution/criticize_solution', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(solutions)})
620
+ let responseObj = await response.json()
621
+ solutionsCriticizedVersions.push(responseObj)
622
+ }
623
+
624
+ async function refineSolutions(critiques){
625
+ isRequirements = false;
626
+ let response = await fetch('https://game4all-reqroup.hf.space/solution/refine_solution', {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(critiques)})
627
+ let responseObj = await response.json()
628
+ await generateCriticisms(responseObj)
629
+ }
630
+
631
+ async function workflow(steps = 1){
632
+ let soluce;
633
+ for(let step; step <= steps; step++){
634
+ if(solutionsCriticizedVersions.length == 0){
635
+ soluce = await generateSolutions();
636
+ await generateCriticisms(soluce)
637
+ } else {
638
+ let prevSoluce = solutionsCriticizedVersions[solutionsCriticizedVersions.length - 1];
639
+ await refineSolutions(prevSoluce)
640
+ }
641
+ }
642
+ initializeSolutionAccordion(solutionsCriticizedVersions, "criticizeSoluceContainer")
643
+ }
644
+
645
+
646
  // Écouteurs d'événements pour les filtres
647
 
648
  document.getElementById('docType').addEventListener('change', filterTable)
 
651
  document.getElementById("workingGroupSelect").addEventListener('change', getMeetings)
652
  document.getElementById('getTDocs').addEventListener('click', getDataFrame)
653
  document.getElementById("getReqs").addEventListener("click", generateRequirements);
654
+ document.getElementById('categorizeReq').addEventListener('click', () => categorizeRequirements(8));
655
  document.getElementById("downloadZip").addEventListener('click', downloadTDocs)
656
  document.getElementById("queryReq").addEventListener("click", queryRequirements)
657
+ document.getElementById('getSolutionsStepByStep').addEventListener('click', async () => await workflow())
658
+ document.getElementById("getSolutions").addEventListener('click', async () => {
659
+ let steps = document.getElementById('limitInput').value != '' ? document.getElementById('limitInput').value : 10;
660
+ await workflow(steps)
661
+ })
662
  document.getElementById('searchReq').addEventListener('click', () => {
663
  document.getElementById('dataFrameForm').classList.add('hidden');
664
  document.getElementById('filters').classList.add('hidden');