Add RAN1/RAN2 working group support + full English translation
#1
by
om4r932
- opened
- app.py +2 -2
- index.html +4 -2
- 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.
|
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
|
180 |
</span>
|
181 |
<span id="selected-count" class="text-sm text-blue-700 bg-blue-50 rounded px-3 py-1 shadow">
|
182 |
-
0
|
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 = '
|
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 = '
|
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) + " (
|
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="">
|
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} (
|
197 |
} else {
|
198 |
-
labelElement.textContent = `${filterType} (${selectedCount}
|
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('
|
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('
|
317 |
-
alert('
|
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('
|
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('
|
358 |
-
alert('
|
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}
|
449 |
document.getElementById('selected-count').textContent =
|
450 |
-
`${selected}
|
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('
|
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('
|
508 |
return;
|
509 |
}
|
510 |
|
@@ -521,7 +521,7 @@ async function downloadTDocs() {
|
|
521 |
downloadBlob(blob, generateDownloadFilename());
|
522 |
} catch (error) {
|
523 |
console.error(error);
|
524 |
-
alert('
|
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('
|
591 |
return;
|
592 |
}
|
593 |
|
594 |
-
showLoadingOverlay('
|
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(`
|
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('
|
647 |
-
alert('
|
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('
|
686 |
return;
|
687 |
}
|
688 |
|
689 |
-
showLoadingOverlay('
|
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('
|
710 |
-
alert('
|
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('
|
926 |
return;
|
927 |
}
|
928 |
|
929 |
if (!formattedRequirements || formattedRequirements.length === 0) {
|
930 |
-
alert('
|
931 |
return;
|
932 |
}
|
933 |
|
934 |
-
showLoadingOverlay('
|
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('
|
953 |
-
alert('
|
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);
|