om4r932 commited on
Commit
700339d
·
1 Parent(s): 8a50e8c

Update keyword search: Add 'Get section' and 'Get document' functions

Browse files
Files changed (4) hide show
  1. app.py +1 -0
  2. static/script.js +137 -5
  3. static/style.css +124 -0
  4. templates/index.html +16 -2
app.py CHANGED
@@ -495,6 +495,7 @@ def search_spec(request: KeywordRequest):
495
 
496
  if put:
497
  spec_content = spec
 
498
  spec_content["contains"] = {chap: doc[chap] for chap in contents}
499
 
500
  results.append(spec_content)
 
495
 
496
  if put:
497
  spec_content = spec
498
+ spec_content["full_doc"] = doc
499
  spec_content["contains"] = {chap: doc[chap] for chap in contents}
500
 
501
  results.append(spec_content)
static/script.js CHANGED
@@ -30,6 +30,12 @@ const resultsList = document.getElementById('results-list');
30
  const resultsStats = document.getElementById('results-stats');
31
  const errorMessage = document.getElementById('error-message');
32
 
 
 
 
 
 
 
33
  // Search mode toggle
34
  singleModeBtn.addEventListener('click', () => {
35
  dynamicTitle.textContent = "Find 3GPP Documents";
@@ -86,6 +92,14 @@ keywordSearchBtn.addEventListener("click", async ()=>{
86
  hideError();
87
 
88
  try{
 
 
 
 
 
 
 
 
89
  const response = await fetch("/search-spec", {
90
  method: "POST",
91
  headers: {
@@ -94,10 +108,7 @@ keywordSearchBtn.addEventListener("click", async ()=>{
94
  body: JSON.stringify({
95
  keywords,
96
  "case_sensitive": caseSensitiveFilter.checked,
97
- "release": releaseFilter.value != '' ? releaseFilter.value : null,
98
- "mode": modeFilter.value,
99
- "working_group": workingGroupFilter != '' ? workingGroupFilter.value : null,
100
- "spec_type": specTypeFilter.value
101
  })
102
  });
103
 
@@ -242,7 +253,8 @@ function displayKeywordResults(data) {
242
 
243
  data.results.forEach(spec => {
244
  const resultItem = document.createElement("div");
245
- resultItem.className = "result-item"
 
246
  resultItem.innerHTML = `
247
  <div class="result-header">
248
  <div class="result-id">${spec.id}</div>
@@ -257,13 +269,133 @@ function displayKeywordResults(data) {
257
  <p>URL: <a target="_blank" href="${spec.url}">${spec.url}</a></p>
258
  <p>Scope: ${spec.scope}</p>
259
  </div>
 
 
 
 
260
  `;
 
 
261
  resultsList.appendChild(resultItem);
 
 
 
 
 
 
 
 
262
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  resultsStats.textContent = `Found ${data.results.length} in ${data.search_time.toFixed(2)} seconds`
264
  resultsContainer.style.display = 'block';
265
  }
266
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  // Display batch results
268
  function displayBatchResults(data) {
269
  resultsList.innerHTML = '';
 
30
  const resultsStats = document.getElementById('results-stats');
31
  const errorMessage = document.getElementById('error-message');
32
 
33
+ const sectionPopup = document.getElementById('sectionPopup');
34
+ const popupTitle = document.getElementById('popupTitle');
35
+ const popupTextareas = document.getElementById('popupTextareas');
36
+ const copyAllBtn = document.getElementById('copyAllBtn');
37
+ const closePopupBtn = document.querySelector('.close-popup');
38
+
39
  // Search mode toggle
40
  singleModeBtn.addEventListener('click', () => {
41
  dynamicTitle.textContent = "Find 3GPP Documents";
 
92
  hideError();
93
 
94
  try{
95
+ let body = {
96
+ keywords,
97
+ "case_sensitive": caseSensitiveFilter.checked,
98
+ "mode": modeFilter.value
99
+ };
100
+ if (releaseFilter.value != ""){body.release = releaseFilter.value}
101
+ if (workingGroupFilter.value != ""){body["working_group"] = workingGroupFilter.value}
102
+ if (specTypeFilter.value != ""){body["spec_type"] = specTypeFilter.value}
103
  const response = await fetch("/search-spec", {
104
  method: "POST",
105
  headers: {
 
108
  body: JSON.stringify({
109
  keywords,
110
  "case_sensitive": caseSensitiveFilter.checked,
111
+ "mode": modeFilter.value
 
 
 
112
  })
113
  });
114
 
 
253
 
254
  data.results.forEach(spec => {
255
  const resultItem = document.createElement("div");
256
+ resultItem.className = "result-item";
257
+
258
  resultItem.innerHTML = `
259
  <div class="result-header">
260
  <div class="result-id">${spec.id}</div>
 
269
  <p>URL: <a target="_blank" href="${spec.url}">${spec.url}</a></p>
270
  <p>Scope: ${spec.scope}</p>
271
  </div>
272
+ <div class="result-actions">
273
+ <button class="get-section-btn btn" data-spec-id="${spec.id}">Get section</button>
274
+ <button class="get-all-text-btn btn" data-spec-id="${spec.id}">Get document content</button>
275
+ </div>
276
  `;
277
+
278
+ // Ajouter le bouton au DOM
279
  resultsList.appendChild(resultItem);
280
+
281
+ // Récupérer le bouton nouvellement créé
282
+ const button1 = resultItem.querySelector('.get-section-btn');
283
+ const button2 = resultItem.querySelector('.get-all-text-btn');
284
+
285
+ // Stocker l'objet directement sur l'élément DOM
286
+ button1._sections = spec.contains;
287
+ button2._sections = spec.full_doc;
288
  });
289
+
290
+ document.querySelectorAll('.get-section-btn').forEach(button => {
291
+ button.addEventListener('click', function() {
292
+ let specId = this.getAttribute("data-spec-id");
293
+ let sections = this._sections;
294
+ openSectionPopup(specId, sections);
295
+ });
296
+ });
297
+
298
+ document.querySelectorAll('.get-all-text-btn').forEach(button => {
299
+ button.addEventListener('click', function() {
300
+ let specId = this.getAttribute("data-spec-id");
301
+ let sections = this._sections;
302
+ openSectionPopup(specId, sections);
303
+ })
304
+ });
305
+
306
  resultsStats.textContent = `Found ${data.results.length} in ${data.search_time.toFixed(2)} seconds`
307
  resultsContainer.style.display = 'block';
308
  }
309
 
310
+ function openSectionPopup(specId, sections) {
311
+ popupTitle.textContent = `Sections of specification ${specId}`;
312
+
313
+ popupTextareas.innerHTML = '';
314
+ Object.entries(sections).forEach(([section, content], index) => {
315
+ const container = document.createElement("div");
316
+ container.className = "textarea-container";
317
+
318
+ const textarea = document.createElement("textarea");
319
+ textarea.id = `section-${index}`;
320
+ textarea.value = `${section}\n\n${content}`
321
+ textarea.readOnly = true;
322
+
323
+ const copyBtn = document.createElement('button');
324
+ copyBtn.className = 'copy-btn';
325
+ copyBtn.textContent = 'Copy';
326
+ copyBtn.onclick = () => copyTextarea(`section-${index}`);
327
+
328
+ container.appendChild(textarea);
329
+ container.appendChild(copyBtn);
330
+ popupTextareas.appendChild(container);
331
+ });
332
+
333
+ sectionPopup.style.display = 'block';
334
+ document.body.style.overflow = 'hidden';
335
+ }
336
+
337
+ function copyTextarea(id) {
338
+ const textarea = document.getElementById(id);
339
+ textarea.select();
340
+ document.execCommand("copy");
341
+
342
+ // Effet visuel pour confirmer la copie
343
+ const btn = textarea.nextElementSibling;
344
+ const originalText = btn.textContent;
345
+ btn.textContent = 'Copied !';
346
+ btn.style.backgroundColor = '#34a853';
347
+ btn.style.color = 'white';
348
+
349
+ setTimeout(() => {
350
+ btn.textContent = originalText;
351
+ btn.style.backgroundColor = '';
352
+ btn.style.color = '';
353
+ }, 1500);
354
+ }
355
+
356
+ // Fonction pour copier tout le contenu
357
+ copyAllBtn.addEventListener('click', () => {
358
+ const textareas = popupTextareas.querySelectorAll('textarea');
359
+ let allContent = '';
360
+
361
+ textareas.forEach((textarea, index) => {
362
+ allContent += textarea.value;
363
+ if (index < textareas.length - 1) {
364
+ allContent += '\n\n---\n\n';
365
+ }
366
+ });
367
+
368
+ // Créer un textarea temporaire pour copier le contenu
369
+ const tempTextarea = document.createElement('textarea');
370
+ tempTextarea.value = allContent;
371
+ document.body.appendChild(tempTextarea);
372
+ tempTextarea.select();
373
+ document.execCommand('copy');
374
+ document.body.removeChild(tempTextarea);
375
+
376
+ // Effet visuel pour confirmer la copie
377
+ const originalText = copyAllBtn.textContent;
378
+ copyAllBtn.textContent = 'Copied all !';
379
+
380
+ setTimeout(() => {
381
+ copyAllBtn.textContent = originalText;
382
+ }, 1500);
383
+ });
384
+
385
+ // Fermer la popup
386
+ closePopupBtn.addEventListener('click', () => {
387
+ sectionPopup.style.display = 'none';
388
+ document.body.style.overflow = ''; // Rétablir le défilement du body
389
+ });
390
+
391
+ // Fermer la popup en cliquant à l'extérieur
392
+ window.addEventListener('click', (event) => {
393
+ if (event.target === sectionPopup) {
394
+ sectionPopup.style.display = 'none';
395
+ document.body.style.overflow = '';
396
+ }
397
+ });
398
+
399
  // Display batch results
400
  function displayBatchResults(data) {
401
  resultsList.innerHTML = '';
static/style.css CHANGED
@@ -10,6 +10,130 @@
10
  --shadow-color: rgba(0, 0, 0, 0.1);
11
  }
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  .filter-tab-container {
14
  margin-top: 20px;
15
  margin-bottom: 15px;
 
10
  --shadow-color: rgba(0, 0, 0, 0.1);
11
  }
12
 
13
+ .section-popup {
14
+ display: none;
15
+ position: fixed;
16
+ top: 0;
17
+ left: 0;
18
+ width: 100%;
19
+ height: 100%;
20
+ background-color: rgba(0, 0, 0, 0.5);
21
+ z-index: 1000;
22
+ overflow: auto;
23
+ }
24
+
25
+ .section-popup-content {
26
+ position: relative;
27
+ background-color: white;
28
+ width: 80%;
29
+ max-width: 800px;
30
+ margin: 50px auto;
31
+ padding: 30px;
32
+ border-radius: 8px;
33
+ box-shadow: 0 4px 15px var(--shadow-color);
34
+ animation: popupFadeIn 0.3s;
35
+ }
36
+
37
+ @keyframes popupFadeIn {
38
+ from { opacity: 0; transform: translateY(-20px); }
39
+ to { opacity: 1; transform: translateY(0); }
40
+ }
41
+
42
+ .popup-header {
43
+ display: flex;
44
+ justify-content: space-between;
45
+ align-items: center;
46
+ margin-bottom: 20px;
47
+ padding-bottom: 15px;
48
+ border-bottom: 1px solid var(--border-color);
49
+ }
50
+
51
+ .popup-header h2 {
52
+ font-size: 22px;
53
+ font-weight: 500;
54
+ color: var(--text-color);
55
+ }
56
+
57
+ .close-popup {
58
+ font-size: 28px;
59
+ font-weight: bold;
60
+ color: var(--light-text);
61
+ cursor: pointer;
62
+ transition: color 0.3s;
63
+ }
64
+
65
+ .close-popup:hover {
66
+ color: var(--primary-color);
67
+ }
68
+
69
+ .popup-textareas {
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 20px;
73
+ max-height: 60vh;
74
+ overflow-y: auto;
75
+ padding-right: 10px;
76
+ }
77
+
78
+ .textarea-container {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 8px;
82
+ }
83
+
84
+ .textarea-container textarea {
85
+ width: 100%;
86
+ height: 100px;
87
+ padding: 12px 16px;
88
+ border: 1px solid var(--border-color);
89
+ border-radius: 4px;
90
+ font-size: 16px;
91
+ font-family: 'Roboto', sans-serif;
92
+ resize: vertical;
93
+ outline: none;
94
+ }
95
+
96
+ .textarea-container textarea:focus {
97
+ border-color: var(--primary-color);
98
+ box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
99
+ }
100
+
101
+ .copy-btn {
102
+ align-self: flex-end;
103
+ background-color: var(--secondary-color);
104
+ color: var(--primary-color);
105
+ border: 1px solid var(--border-color);
106
+ border-radius: 4px;
107
+ padding: 8px 16px;
108
+ font-size: 14px;
109
+ font-weight: 500;
110
+ cursor: pointer;
111
+ transition: background-color 0.3s;
112
+ }
113
+
114
+ .copy-btn:hover {
115
+ background-color: #e8f0fe;
116
+ }
117
+
118
+ .copy-all-btn {
119
+ display: block;
120
+ width: 100%;
121
+ background-color: var(--primary-color);
122
+ color: white;
123
+ border: none;
124
+ border-radius: 4px;
125
+ padding: 12px 24px;
126
+ font-size: 16px;
127
+ font-weight: 500;
128
+ cursor: pointer;
129
+ transition: background-color 0.3s;
130
+ margin-top: 20px;
131
+ }
132
+
133
+ .copy-all-btn:hover {
134
+ background-color: var(--accent-color);
135
+ }
136
+
137
  .filter-tab-container {
138
  margin-top: 20px;
139
  margin-bottom: 15px;
templates/index.html CHANGED
@@ -73,8 +73,9 @@
73
  </select>
74
 
75
  <select name="spec_type" class="filter-select">
76
- <option value="TR">Technical Report</option>
77
- <option value="TS">Technical Specification</option>
 
78
  </select>
79
 
80
  <select name="working_group" class="filter-select">
@@ -119,6 +120,19 @@
119
  </div>
120
  <div class="results-list" id="results-list"></div>
121
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  </div>
123
 
124
  <footer>
 
73
  </select>
74
 
75
  <select name="spec_type" class="filter-select">
76
+ <option value="">All types</option>
77
+ <option value="TR">Technical Report (TR)</option>
78
+ <option value="TS">Technical Specification (TS)</option>
79
  </select>
80
 
81
  <select name="working_group" class="filter-select">
 
120
  </div>
121
  <div class="results-list" id="results-list"></div>
122
  </div>
123
+
124
+ <div id="sectionPopup" class="section-popup">
125
+ <div class="section-popup-content">
126
+ <div class="popup-header">
127
+ <h2 id="popupTitle">Document Sections</h2>
128
+ <span class="close-popup">&times;</span>
129
+ </div>
130
+ <div id="popupTextareas" class="popup-textareas">
131
+ <!-- Les textareas seront générés ici dynamiquement -->
132
+ </div>
133
+ <button id="copyAllBtn" class="copy-all-btn">Copy all</button>
134
+ </div>
135
+ </div>
136
  </div>
137
 
138
  <footer>