Add RAN1/RAN2 working group support + full English translation

#1
Files changed (3) hide show
  1. app.py +2 -2
  2. index.html +4 -2
  3. static/script.js +33 -33
app.py CHANGED
@@ -30,7 +30,7 @@ from aiolimiter import AsyncLimiter
30
  load_dotenv()
31
 
32
  logging.basicConfig(
33
- level=logging.INFO,
34
  format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]: %(message)s',
35
  datefmt='%Y-%m-%d %H:%M:%S'
36
  )
@@ -273,7 +273,7 @@ def get_change_request_dataframe(req: DataRequest):
273
  wg_folders = [item.get_text() for item in soup.select("tr td a")]
274
  selected_folder = None
275
  for folder in wg_folders:
276
- if str(wg_number) in folder:
277
  selected_folder = folder
278
  break
279
 
 
30
  load_dotenv()
31
 
32
  logging.basicConfig(
33
+ level=logging.DEBUG,
34
  format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]: %(message)s',
35
  datefmt='%Y-%m-%d %H:%M:%S'
36
  )
 
273
  wg_folders = [item.get_text() for item in soup.select("tr td a")]
274
  selected_folder = None
275
  for folder in wg_folders:
276
+ if "wg" + str(wg_number) in folder.lower():
277
  selected_folder = folder
278
  break
279
 
index.html CHANGED
@@ -44,6 +44,8 @@
44
  <option value="CT4">CT4</option>
45
  <option value="CT5">CT5</option>
46
  <option value="CT6">CT6</option>
 
 
47
  </select>
48
  </div>
49
 
@@ -176,10 +178,10 @@
176
  <!-- Right side: document counts -->
177
  <div class="flex gap-2 items-center">
178
  <span id="displayed-count" class="text-sm text-gray-700 bg-white rounded px-3 py-1 shadow">
179
- 0 document affiché
180
  </span>
181
  <span id="selected-count" class="text-sm text-blue-700 bg-blue-50 rounded px-3 py-1 shadow">
182
- 0 document sélectionné
183
  </span>
184
  </div>
185
  </div>
 
44
  <option value="CT4">CT4</option>
45
  <option value="CT5">CT5</option>
46
  <option value="CT6">CT6</option>
47
+ <option value="RAN1">RAN1</option>
48
+ <option value="RAN2">RAN2</option>
49
  </select>
50
  </div>
51
 
 
178
  <!-- Right side: document counts -->
179
  <div class="flex gap-2 items-center">
180
  <span id="displayed-count" class="text-sm text-gray-700 bg-white rounded px-3 py-1 shadow">
181
+ 0 total documents
182
  </span>
183
  <span id="selected-count" class="text-sm text-blue-700 bg-blue-50 rounded px-3 py-1 shadow">
184
+ 0 selected documents
185
  </span>
186
  </div>
187
  </div>
static/script.js CHANGED
@@ -61,7 +61,7 @@ function toggleContainersVisibility(containerIds, visible = true) {
61
  * Affiche le loading overlay avec un message personnalisé
62
  * @param {string} message - Message à afficher
63
  */
64
- function showLoadingOverlay(message = 'Chargement en cours...') {
65
  document.getElementById('progress-text').textContent = message;
66
  toggleContainersVisibility(['loading-overlay'], true);
67
  }
@@ -79,7 +79,7 @@ function hideLoadingOverlay() {
79
  * @param {Object} options - Objet avec les options {value: text}
80
  * @param {string} defaultText - Texte par défaut
81
  */
82
- function populateSelect(selectId, options, defaultText = 'Sélectionner...') {
83
  const select = document.getElementById(selectId);
84
  if (select) {
85
  select.innerHTML = `<option value="">${defaultText}</option>`;
@@ -146,7 +146,7 @@ function populateCheckboxDropdown(optionsContainerId, options, filterType, label
146
  function updateCheckboxDropdownLabel(type, labelId, set, totalCount) {
147
  const label = document.getElementById(labelId);
148
  if (!set.size) {
149
- label.textContent = type.charAt(0).toUpperCase() + type.slice(1) + " (Tous)";
150
  } else if (set.size === 1) {
151
  label.textContent = [...set][0];
152
  } else {
@@ -167,7 +167,7 @@ function populateDaisyDropdown(menuId, options, labelId, onSelect) {
167
  menu.innerHTML = '';
168
  // Option "Tous"
169
  const liAll = document.createElement('li');
170
- liAll.innerHTML = `<a data-value="">Tous</a>`;
171
  liAll.querySelector('a').onclick = e => {
172
  e.preventDefault();
173
  document.getElementById(labelId).textContent = "Type";
@@ -193,9 +193,9 @@ function updateFilterLabel(filterType) {
193
  const labelElement = document.getElementById(`${filterType}-filter-label`);
194
 
195
  if (selectedCount === 0) {
196
- labelElement.textContent = `${filterType} (Tous)`;
197
  } else {
198
- labelElement.textContent = `${filterType} (${selectedCount} sélectionné${selectedCount > 1 ? 's' : ''})`;
199
  }
200
  }
201
 
@@ -299,7 +299,7 @@ async function getMeetings() {
299
  const workingGroup = document.getElementById('working-group-select').value;
300
  if (!workingGroup) return;
301
 
302
- showLoadingOverlay('Récupération des meetings...');
303
  toggleElementsEnabled(['get-meetings-btn'], false);
304
 
305
  try {
@@ -313,8 +313,8 @@ async function getMeetings() {
313
  populateSelect('meeting-select', data.meetings, 'Select a meeting');
314
  toggleContainersVisibility(['meeting-container'], true);
315
  } catch (error) {
316
- console.error('Erreur lors de la récupération des meetings:', error);
317
- alert('Erreur lors de la récupération des meetings');
318
  } finally {
319
  hideLoadingOverlay();
320
  toggleElementsEnabled(['get-meetings-btn'], true);
@@ -330,7 +330,7 @@ async function getTDocs() {
330
 
331
  if (!workingGroup || !meeting) return;
332
 
333
- showLoadingOverlay('Récupération de la liste des TDocs...');
334
  toggleElementsEnabled(['get-tdocs-btn'], false);
335
 
336
  try {
@@ -354,8 +354,8 @@ async function getTDocs() {
354
 
355
  hasRequirementsExtracted = false;
356
  } catch (error) {
357
- console.error('Erreur lors de la récupération des TDocs:', error);
358
- alert('Erreur lors de la récupération des TDocs');
359
  } finally {
360
  hideLoadingOverlay();
361
  toggleElementsEnabled(['get-tdocs-btn'], true);
@@ -445,9 +445,9 @@ function updateSelectedAndDisplayedCount() {
445
  });
446
 
447
  document.getElementById('displayed-count').textContent =
448
- `${displayed} document${displayed !== 1 ? 's' : ''} affiché${displayed > 1 ? 's' : ''}`;
449
  document.getElementById('selected-count').textContent =
450
- `${selected} document${selected !== 1 ? 's' : ''} sélectionné${selected > 1 ? 's' : ''}`;
451
  }
452
 
453
  /**
@@ -497,14 +497,14 @@ function setupTableEvents() {
497
  * Télécharge les TDocs sélectionnés
498
  */
499
  async function downloadTDocs() {
500
- showLoadingOverlay('Téléchargement des TDocs en cours...');
501
  toggleElementsEnabled(['download-tdocs-btn', 'extract-requirements-btn'], false);
502
 
503
  try {
504
  // Extraire les données du tableau avec TDoc et URL
505
  const selectedData = extractTableData({ 'TDoc': 'document', 'URL': 'url' });
506
  if (selectedData.length === 0) {
507
- alert('Veuillez sélectionner au moins un document');
508
  return;
509
  }
510
 
@@ -521,7 +521,7 @@ async function downloadTDocs() {
521
  downloadBlob(blob, generateDownloadFilename());
522
  } catch (error) {
523
  console.error(error);
524
- alert('Erreur lors du téléchargement des TDocs');
525
  } finally {
526
  hideLoadingOverlay();
527
  toggleElementsEnabled(['download-tdocs-btn', 'extract-requirements-btn'], true);
@@ -541,14 +541,14 @@ function generateDownloadFilename() {
541
 
542
  // empty set means "Tous" is selected
543
  if (agendaItems) {
544
- for (aItem of agendaItems) {
545
  filename += `_${aItem}`;
546
  }
547
  }
548
 
549
  // empty set means "Tous" is selected
550
  if (docStatuses) {
551
- for (docStatus of docStatuses) {
552
  filename += `_${docStatus}`;
553
  }
554
  }
@@ -587,11 +587,11 @@ function downloadBlob(blob, filename) {
587
  async function extractRequirements() {
588
  const selectedData = extractTableData({ 'TDoc': 'document', 'URL': 'url' });
589
  if (selectedData.length === 0) {
590
- alert('Veuillez sélectionner au moins un document');
591
  return;
592
  }
593
 
594
- showLoadingOverlay('Extraction des requirements en cours...');
595
  toggleElementsEnabled(['extract-requirements-btn'], false);
596
 
597
  try {
@@ -600,7 +600,7 @@ async function extractRequirements() {
600
  console.log("SSE message:");
601
  console.log(msg);
602
 
603
- showLoadingOverlay(`Extraction des requirements en cours... (${msg.processed_docs}/${msg.total_docs})`);
604
  },
605
  onError: (err) => {
606
  console.error(`Error while fetching requirements: ${err}`);
@@ -643,8 +643,8 @@ async function extractRequirements() {
643
 
644
  hasRequirementsExtracted = true;
645
  } catch (error) {
646
- console.error('Erreur lors de l\'extraction des requirements:', error);
647
- alert('Erreur lors de l\'extraction des requirements');
648
  } finally {
649
  hideLoadingOverlay();
650
  toggleElementsEnabled(['extract-requirements-btn'], true);
@@ -682,11 +682,11 @@ function displayRequirements(requirementsData) {
682
  */
683
  async function categorizeRequirements(max_categories) {
684
  if (!formattedRequirements || formattedRequirements.length === 0) {
685
- alert('Aucun requirement à catégoriser');
686
  return;
687
  }
688
 
689
- showLoadingOverlay('Catégorisation des requirements en cours...');
690
  toggleElementsEnabled(['categorize-requirements-btn'], false);
691
 
692
  try {
@@ -706,8 +706,8 @@ async function categorizeRequirements(max_categories) {
706
  toggleContainersVisibility(['categorized-requirements-container', 'solutions-action-buttons-container'], true);
707
 
708
  } catch (error) {
709
- console.error('Erreur lors de la catégorisation des requirements:', error);
710
- alert('Erreur lors de la catégorisation des requirements');
711
  } finally {
712
  hideLoadingOverlay();
713
  toggleElementsEnabled(['categorize-requirements-btn'], true);
@@ -922,16 +922,16 @@ function getSelectedRequirementsByCategory() {
922
  async function searchRequirements() {
923
  const query = document.getElementById('query-input').value.trim();
924
  if (!query) {
925
- alert('Veuillez entrer une requête de recherche');
926
  return;
927
  }
928
 
929
  if (!formattedRequirements || formattedRequirements.length === 0) {
930
- alert('Aucun requirement disponible pour la recherche');
931
  return;
932
  }
933
 
934
- showLoadingOverlay('Recherche en cours...');
935
  toggleElementsEnabled(['search-requirements-btn'], false);
936
 
937
  try {
@@ -949,8 +949,8 @@ async function searchRequirements() {
949
  displaySearchResults(data.requirements);
950
 
951
  } catch (error) {
952
- console.error('Erreur lors de la recherche:', error);
953
- alert('Erreur lors de la recherche des requirements');
954
  } finally {
955
  hideLoadingOverlay();
956
  toggleElementsEnabled(['search-requirements-btn'], true);
 
61
  * Affiche le loading overlay avec un message personnalisé
62
  * @param {string} message - Message à afficher
63
  */
64
+ function showLoadingOverlay(message = 'Loading...') {
65
  document.getElementById('progress-text').textContent = message;
66
  toggleContainersVisibility(['loading-overlay'], true);
67
  }
 
79
  * @param {Object} options - Objet avec les options {value: text}
80
  * @param {string} defaultText - Texte par défaut
81
  */
82
+ function populateSelect(selectId, options, defaultText = 'Select...') {
83
  const select = document.getElementById(selectId);
84
  if (select) {
85
  select.innerHTML = `<option value="">${defaultText}</option>`;
 
146
  function updateCheckboxDropdownLabel(type, labelId, set, totalCount) {
147
  const label = document.getElementById(labelId);
148
  if (!set.size) {
149
+ label.textContent = type.charAt(0).toUpperCase() + type.slice(1) + " (All)";
150
  } else if (set.size === 1) {
151
  label.textContent = [...set][0];
152
  } else {
 
167
  menu.innerHTML = '';
168
  // Option "Tous"
169
  const liAll = document.createElement('li');
170
+ liAll.innerHTML = `<a data-value="">All</a>`;
171
  liAll.querySelector('a').onclick = e => {
172
  e.preventDefault();
173
  document.getElementById(labelId).textContent = "Type";
 
193
  const labelElement = document.getElementById(`${filterType}-filter-label`);
194
 
195
  if (selectedCount === 0) {
196
+ labelElement.textContent = `${filterType} (All)`;
197
  } else {
198
+ labelElement.textContent = `${filterType} (${selectedCount} selected)`;
199
  }
200
  }
201
 
 
299
  const workingGroup = document.getElementById('working-group-select').value;
300
  if (!workingGroup) return;
301
 
302
+ showLoadingOverlay('Getting all available meetings...');
303
  toggleElementsEnabled(['get-meetings-btn'], false);
304
 
305
  try {
 
313
  populateSelect('meeting-select', data.meetings, 'Select a meeting');
314
  toggleContainersVisibility(['meeting-container'], true);
315
  } catch (error) {
316
+ console.error('Error while getting meetings:', error);
317
+ alert('Error while getting meetings.');
318
  } finally {
319
  hideLoadingOverlay();
320
  toggleElementsEnabled(['get-meetings-btn'], true);
 
330
 
331
  if (!workingGroup || !meeting) return;
332
 
333
+ showLoadingOverlay('Getting TDocs List...');
334
  toggleElementsEnabled(['get-tdocs-btn'], false);
335
 
336
  try {
 
354
 
355
  hasRequirementsExtracted = false;
356
  } catch (error) {
357
+ console.error('Error while getting TDocs:', error);
358
+ alert('Error while getting TDocs');
359
  } finally {
360
  hideLoadingOverlay();
361
  toggleElementsEnabled(['get-tdocs-btn'], true);
 
445
  });
446
 
447
  document.getElementById('displayed-count').textContent =
448
+ `${displayed} total documents`;
449
  document.getElementById('selected-count').textContent =
450
+ `${selected} selected documents`;
451
  }
452
 
453
  /**
 
497
  * Télécharge les TDocs sélectionnés
498
  */
499
  async function downloadTDocs() {
500
+ showLoadingOverlay('Downloading TDocs...');
501
  toggleElementsEnabled(['download-tdocs-btn', 'extract-requirements-btn'], false);
502
 
503
  try {
504
  // Extraire les données du tableau avec TDoc et URL
505
  const selectedData = extractTableData({ 'TDoc': 'document', 'URL': 'url' });
506
  if (selectedData.length === 0) {
507
+ alert('Please select at least one document');
508
  return;
509
  }
510
 
 
521
  downloadBlob(blob, generateDownloadFilename());
522
  } catch (error) {
523
  console.error(error);
524
+ alert('Error while downloading TDocs');
525
  } finally {
526
  hideLoadingOverlay();
527
  toggleElementsEnabled(['download-tdocs-btn', 'extract-requirements-btn'], true);
 
541
 
542
  // empty set means "Tous" is selected
543
  if (agendaItems) {
544
+ for (const aItem of agendaItems) {
545
  filename += `_${aItem}`;
546
  }
547
  }
548
 
549
  // empty set means "Tous" is selected
550
  if (docStatuses) {
551
+ for (const docStatus of docStatuses) {
552
  filename += `_${docStatus}`;
553
  }
554
  }
 
587
  async function extractRequirements() {
588
  const selectedData = extractTableData({ 'TDoc': 'document', 'URL': 'url' });
589
  if (selectedData.length === 0) {
590
+ alert('Please select at least one document');
591
  return;
592
  }
593
 
594
+ showLoadingOverlay('Extracting requirements...');
595
  toggleElementsEnabled(['extract-requirements-btn'], false);
596
 
597
  try {
 
600
  console.log("SSE message:");
601
  console.log(msg);
602
 
603
+ showLoadingOverlay(`Extracting requirements... (${msg.processed_docs}/${msg.total_docs})`);
604
  },
605
  onError: (err) => {
606
  console.error(`Error while fetching requirements: ${err}`);
 
643
 
644
  hasRequirementsExtracted = true;
645
  } catch (error) {
646
+ console.error('Error while extracting requirements', error);
647
+ alert('Error while extracting requirements');
648
  } finally {
649
  hideLoadingOverlay();
650
  toggleElementsEnabled(['extract-requirements-btn'], true);
 
682
  */
683
  async function categorizeRequirements(max_categories) {
684
  if (!formattedRequirements || formattedRequirements.length === 0) {
685
+ alert('No requirement available to categorize');
686
  return;
687
  }
688
 
689
+ showLoadingOverlay('Categorizing requirements...');
690
  toggleElementsEnabled(['categorize-requirements-btn'], false);
691
 
692
  try {
 
706
  toggleContainersVisibility(['categorized-requirements-container', 'solutions-action-buttons-container'], true);
707
 
708
  } catch (error) {
709
+ console.error('Error while categorizing requirements:', error);
710
+ alert('Error while categorizing requirements');
711
  } finally {
712
  hideLoadingOverlay();
713
  toggleElementsEnabled(['categorize-requirements-btn'], true);
 
922
  async function searchRequirements() {
923
  const query = document.getElementById('query-input').value.trim();
924
  if (!query) {
925
+ alert('Please enter a search query');
926
  return;
927
  }
928
 
929
  if (!formattedRequirements || formattedRequirements.length === 0) {
930
+ alert('No available requirements for search');
931
  return;
932
  }
933
 
934
+ showLoadingOverlay('Searching...');
935
  toggleElementsEnabled(['search-requirements-btn'], false);
936
 
937
  try {
 
949
  displaySearchResults(data.requirements);
950
 
951
  } catch (error) {
952
+ console.error('Error while searching:', error);
953
+ alert('Error while searching requirements');
954
  } finally {
955
  hideLoadingOverlay();
956
  toggleElementsEnabled(['search-requirements-btn'], true);