pywebexec 2.4.5__py3-none-any.whl → 2.4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,7 @@ function initTableFilters(table) {
26
26
  // Add row counter
27
27
  const rowCount = document.createElement('span');
28
28
  rowCount.className = 'row-count system-font';
29
+ rowCount.title = 'Export to Excel';
29
30
  rowCount.onclick = () => exportToExcel(table);
30
31
  contentSpan.appendChild(rowCount);
31
32
  }
@@ -144,49 +145,154 @@ function applyFilters(table) {
144
145
  }
145
146
  }
146
147
 
148
+ function processHtmlContent(element) {
149
+ function processLi(li, level = 0) {
150
+ const indent = ' '.repeat(level);
151
+ const items = [];
152
+
153
+ // Extraire le texte direct (avant sous-liste)
154
+ const textContent = Array.from(li.childNodes)
155
+ .filter(node => node.nodeType === Node.TEXT_NODE)
156
+ .map(node => node.textContent.trim())
157
+ .join(' ')
158
+ .replace(/\s+/g, ' ')
159
+ .trim();
160
+
161
+ if (textContent) {
162
+ items.push(indent + '• ' + textContent);
163
+ }
164
+
165
+ // Traiter récursivement les sous-listes
166
+ const subLists = li.querySelectorAll(':scope > ul > li');
167
+ if (subLists.length) {
168
+ for (const subLi of subLists) {
169
+ items.push(...processLi(subLi, level + 1));
170
+ }
171
+ }
172
+
173
+ return items;
174
+ }
175
+
176
+ const list = element.querySelector('ul');
177
+ if (list) {
178
+ const items = Array.from(list.children)
179
+ .filter(el => el.tagName === 'LI')
180
+ .map(li => processLi(li))
181
+ .flat();
182
+ return items.join('\n');
183
+ }
184
+ const text = element.textContent.replace(/\s+/g, ' ').trim();
185
+ // Return object with type info if it's a number
186
+ if (/^\d+$/.test(text)) {
187
+ return { value: parseInt(text, 10), type: 'integer' };
188
+ }
189
+ return text;
190
+ }
191
+
147
192
  function exportToExcel(table) {
148
- // Create HTML content
149
- const html = `
150
- <html>
151
- <head>
152
- <meta charset="UTF-8">
153
- </head>
154
- <body>
155
- <table border="1" bgcolor="#f0f0f0" bordercolor="#ffffff">
156
- <thead>
157
- <tr>${
158
- Array.from(table.querySelectorAll('thead th'))
159
- .filter((_, i) => i !== 4 || table !== commandsTable)
160
- .map(th => `<th bgcolor="#b0c7ff">${th.querySelector('.th-content')?.textContent.replace(/[]/, '').replace(/.*/, '').trim() || ''}</th>`)
161
- .join('')
162
- }</tr>
163
- </thead>
164
- <tbody>${
165
- Array.from(table.querySelectorAll('tbody tr'))
166
- .filter(row => row.style.display !== 'none')
167
- .map(row =>
168
- `<tr>${
169
- Array.from(row.cells)
170
- .filter((_, i) => i !== 4 || table !== commandsTable)
171
- .map(cell => `<td valign="top">${cell.innerHTML}</td>`)
172
- .join('')
173
- }</tr>`
174
- ).join('')
175
- }</tbody>
176
- </table>
177
- </body>
178
- </html>`;
179
-
180
- // Create and trigger download
181
- const blob = new Blob([html], { type: 'application/vnd.ms-excel' });
182
- const url = window.URL.createObjectURL(blob);
183
- const a = document.createElement('a');
184
- a.href = url;
185
- a.download = 'export_' + new Date().toISOString().slice(0,10) + '.xls';
186
- document.body.appendChild(a);
187
- a.click();
188
- document.body.removeChild(a);
189
- window.URL.revokeObjectURL(url);
193
+ const workbook = new ExcelJS.Workbook();
194
+ const worksheet = workbook.addWorksheet('Sheet1', {
195
+ views: [{ state: 'frozen', xSplit: 0, ySplit: 1 }]
196
+ });
197
+
198
+ // Get headers and data
199
+ const headers = Array.from(table.querySelectorAll('thead th'))
200
+ .filter((_, i) => i !== 4 || table !== commandsTable)
201
+ .map(th => th.querySelector('.th-content')?.textContent.replace(/[].*/, '').replace(/[^\w\s]/g, '').trim() || '');
202
+
203
+ // Get data rows with type information
204
+ const rows = Array.from(table.querySelectorAll('tbody tr'))
205
+ .filter(row => row.style.display !== 'none')
206
+ .map(row =>
207
+ Array.from(row.cells)
208
+ .filter((_, i) => i !== 4 || table !== commandsTable)
209
+ .map(cell => {
210
+ const content = processHtmlContent(cell);
211
+ if (content && typeof content === 'object' && content.type === 'integer') {
212
+ return content.value; // Numbers will be handled as numbers by ExcelJS
213
+ }
214
+ return (typeof content === 'string' ? content : content.toString())
215
+ })
216
+ );
217
+
218
+ // Calculate optimal column widths based on content
219
+ const columnWidths = headers.map((header, colIndex) => {
220
+ // Start with header width
221
+ let maxWidth = header.length;
222
+
223
+ // Check width needed for each row's cell in this column
224
+ rows.forEach(row => {
225
+ const cellContent = row[colIndex];
226
+ if (cellContent === null || cellContent === undefined) return;
227
+
228
+ // Convert numbers to string for width calculation
229
+ const contentStr = cellContent.toString();
230
+ // Get the longest line in multiline content
231
+ const lines = contentStr.split('\n');
232
+ const longestLine = Math.max(...lines.map(line => line.length));
233
+ maxWidth = Math.max(maxWidth, longestLine);
234
+ });
235
+
236
+ // Add some padding and ensure minimum/maximum widths
237
+ return { width: Math.min(Math.max(maxWidth + 2, 10), 100) };
238
+ });
239
+
240
+ // Define columns with calculated widths
241
+ worksheet.columns = headers.map((header, index) => ({
242
+ header: header,
243
+ key: header,
244
+ width: columnWidths[index].width
245
+ }));
246
+
247
+ // Add data rows
248
+ rows.forEach(rowData => {
249
+ const row = worksheet.addRow(rowData);
250
+ row.alignment = { vertical: 'top', wrapText: true };
251
+
252
+ // Set row height based on content, handling both strings and numbers
253
+ // const maxLines = Math.max(...rowData.map(cell => {
254
+ // if (cell === null || cell === undefined) return 1;
255
+ // const str = cell.toString();
256
+ // return (str.match(/\n/g) || []).length + 1;
257
+ // }));
258
+ // row.height = Math.max(20, maxLines * 15);
259
+ });
260
+
261
+ // Style header row
262
+ const headerRow = worksheet.getRow(1);
263
+ headerRow.font = { bold: true };
264
+ headerRow.alignment = { vertical: 'middle', horizontal: 'left' };
265
+ // headerRow.height = 20;
266
+
267
+ // Add table after all rows are defined
268
+ worksheet.addTable({
269
+ name: 'DataTable',
270
+ ref: 'A1',
271
+ headerRow: true,
272
+ totalsRow: false,
273
+ style: {
274
+ theme: 'TableStyleMedium2',
275
+ showRowStripes: true,
276
+ },
277
+ columns: headers.map(h => ({
278
+ name: h,
279
+ filterButton: true
280
+ })),
281
+ rows: rows
282
+ });
283
+
284
+ // Save file
285
+ workbook.xlsx.writeBuffer().then(buffer => {
286
+ const blob = new Blob([buffer], {
287
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
288
+ });
289
+ const url = window.URL.createObjectURL(blob);
290
+ const a = document.createElement('a');
291
+ a.href = url;
292
+ a.download = 'export_' + new Date().toISOString().slice(0,10) + '.xlsx';
293
+ a.click();
294
+ window.URL.revokeObjectURL(url);
295
+ });
190
296
  }
191
297
 
192
298
  let commandsTable = document.querySelector('#commandsTable');
@@ -68,6 +68,7 @@
68
68
  <script src="/static/js/xterm/addon-unicode-graphemes.js"></script>
69
69
  <script src="/static/js/xterm/addon-fit.js"></script>
70
70
  <script src="/static/js/marked/marked.min.js"></script>
71
+ <script src="/static/js/exceljs.min.js"></script>
71
72
  <script type="text/javascript" src="/static/jsonform/deps/jquery.min.js"></script>
72
73
  <script type="text/javascript" src="/static/jsonform/deps/underscore.js"></script>
73
74
  <script type="text/javascript" src="/static/jsonform/deps/jsv.js"></script>
@@ -76,7 +77,5 @@
76
77
  <script type="text/javascript" src="/static/js/executables.js"></script>
77
78
  <script type="text/javascript" src="/static/js/schemaform.js"></script>
78
79
  <script type="text/javascript" src="/static/js/tablefilter.js"></script>
79
-
80
-
81
80
  </body>
82
81
  </html>
pywebexec/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.4.5'
21
- __version_tuple__ = version_tuple = (2, 4, 5)
20
+ __version__ = version = '2.4.6'
21
+ __version_tuple__ = version_tuple = (2, 4, 6)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywebexec
3
- Version: 2.4.5
3
+ Version: 2.4.6
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -2,7 +2,7 @@ pywebexec/__init__.py,sha256=197fHJy0UDBwTTpGCGortZRr-w2kTaD7MxqdbVmTEi0,61
2
2
  pywebexec/host_ip.py,sha256=oiCMlo2o3AkkgXDarUSx8T3FWXKI0vk1-EPnx5FGBd8,1332
3
3
  pywebexec/pywebexec.py,sha256=NT4f7Xd4qMkAgjgwguqKKbUkOTCqT7ArRYlsW57Pfwg,48477
4
4
  pywebexec/swagger.yaml,sha256=I_oLpp7Hqel8SDEEykvpmCT-Gv3ytGlziq9bvQOrtZY,7598
5
- pywebexec/version.py,sha256=agt6014xoIR-2Pjd3clvPyl78U4pI7z7EzFGI5OfP8k,511
5
+ pywebexec/version.py,sha256=A7m3JGqv6GgWxdlAhxGqiDnKOY6QWqyUQuVpQYGKv-s,511
6
6
  pywebexec/static/css/form.css,sha256=2JUhraeL46JaiNoD_MSKA2JdouHkXaamhd77DnCqG8k,7291
7
7
  pywebexec/static/css/markdown.css,sha256=br4-iK9wigTs54N2KHtjgZ4KLH0THVSvJo-XZAdMHiE,1970
8
8
  pywebexec/static/css/style.css,sha256=TX60M-mzIPTGVDmaypRCOcyxc8A7dOjx-p1_bpJ6t9M,11772
@@ -33,12 +33,13 @@ pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65Eiuw
33
33
  pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
34
34
  pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
35
35
  pywebexec/static/images/swagger-ui.svg,sha256=FR0yeOVwe4zCYKZAjCGcT_m0Mf25NexIVaSXifIkoU0,2117
36
+ pywebexec/static/js/exceljs.min.js,sha256=fknaaFiOJQ27i7oZDSyqirN4fMAoS9odiy-AXE33Qsk,947702
36
37
  pywebexec/static/js/executables.js,sha256=cTgCFHr_F9bFCirtfG_uR32vOY3vNUr4Ih3Wglj5lFc,11988
37
38
  pywebexec/static/js/popup.js,sha256=IaKmk2U2hEn-Nv6krf_PPW6LaG8NcpCkJKb7lUX0qZo,11457
38
39
  pywebexec/static/js/schemaform.js,sha256=2AIjwdjSDTE2ide8UMmQt4tS-7-JKqidKdopq9mNzvo,12458
39
40
  pywebexec/static/js/script.js,sha256=SpNmskHKJHza0Au7QWrb17EKqiMPbMz5CDmaLt_i3M4,21548
40
41
  pywebexec/static/js/swagger-form.js,sha256=CLcSHMhk5P4-_2MIRBoJLgEnIj_9keDDSzUugXHZjio,4565
41
- pywebexec/static/js/tablefilter.js,sha256=46U4dKQAZHfF2-DGq6DEMjC6L70F-pXA8bIHp4N8Vb4,7618
42
+ pywebexec/static/js/tablefilter.js,sha256=PH_OQ2NpaXr1WryM2p4EQVIvxtSLPm4IHDVAK0N6OU8,11093
42
43
  pywebexec/static/js/js-yaml/LICENSE,sha256=oHvCRGi5ZUznalR9R6LbKC0HcztxXbTHOpi9Y5YflVA,1084
43
44
  pywebexec/static/js/js-yaml/js-yaml.min.js,sha256=Rdw90D3AegZwWiwpibjH9wkBPwS9U4bjJ51ORH8H69c,39430
44
45
  pywebexec/static/js/marked/LICENSE.md,sha256=jjo_gvWaYJWPVsoI9EVkfDKkcz3HymwsRvbriYRxq5w,2942
@@ -65,12 +66,12 @@ pywebexec/static/jsonform/deps/underscore.js,sha256=SzKOQsVYGX1bmddyfPzGC6yXY_rW
65
66
  pywebexec/static/jsonform/deps/img/glyphicons-halflings.png,sha256=hpJM0AbbMLnU8UGOBs172D7vK-dooQ8n0s_ybml3zO0,13826
66
67
  pywebexec/static/jsonform/lib/jsonform.js,sha256=U-BvOgq5gCvSUo36qSAK7Y91RPKOq7vZShkIYpzwlkk,138525
67
68
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- pywebexec/templates/index.html,sha256=29-MBjeJ5mgYdrkRB27WiY0okQLYcFFycJvdCMuwvV0,3974
69
+ pywebexec/templates/index.html,sha256=Hen-UXHenn3To-7gsHU3kO15hi2Mj--OpUVPb5DyHNk,4026
69
70
  pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
70
71
  pywebexec/templates/swagger_ui.html,sha256=MAPr-z96VERAecDvX37V8q2Nxph-O0fNDBul1x2w9SI,1147
71
- pywebexec-2.4.5.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
72
- pywebexec-2.4.5.dist-info/METADATA,sha256=je8F8Vah7DfYvAPpXVBOpNMrcPlAADKIFDnEpvq6nOw,13015
73
- pywebexec-2.4.5.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
74
- pywebexec-2.4.5.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
75
- pywebexec-2.4.5.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
76
- pywebexec-2.4.5.dist-info/RECORD,,
72
+ pywebexec-2.4.6.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
73
+ pywebexec-2.4.6.dist-info/METADATA,sha256=ZIbjKPlU2sVW1LQVZcpDnZTiltCx8_trixeXyucDXoY,13015
74
+ pywebexec-2.4.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
75
+ pywebexec-2.4.6.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
76
+ pywebexec-2.4.6.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
77
+ pywebexec-2.4.6.dist-info/RECORD,,