IO4IT 4.0.22__tar.gz → 4.0.23__tar.gz

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.
Files changed (120) hide show
  1. io4it-4.0.23/PKG-INFO +28 -0
  2. io4it-4.0.23/io4it.egg-info/PKG-INFO +28 -0
  3. {io4it-4.0.22 → io4it-4.0.23}/io4it.egg-info/SOURCES.txt +7 -3
  4. io4it-4.0.23/orangecontrib/IO4IT/utils/custom_api.py +132 -0
  5. io4it-4.0.23/orangecontrib/IO4IT/utils/file2images.py +211 -0
  6. io4it-4.0.23/orangecontrib/IO4IT/widgets/OWCustomLLMApi.py +260 -0
  7. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWJsonToDataTable.py +166 -166
  8. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWPPTX2JPG.py +102 -40
  9. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWXLSX_insert.py +62 -1
  10. io4it-4.0.23/orangecontrib/IO4IT/widgets/designer/owcustomllmapi.ui +364 -0
  11. io4it-4.0.23/orangecontrib/IO4IT/widgets/designer/owfile2images.ui +229 -0
  12. io4it-4.0.23/orangecontrib/IO4IT/widgets/icons/file2images.png +0 -0
  13. io4it-4.0.23/orangecontrib/IO4IT/widgets/icons/owcustomllmapi.svg +9 -0
  14. {io4it-4.0.22 → io4it-4.0.23}/setup.py +1 -1
  15. io4it-4.0.22/PKG-INFO +0 -7
  16. io4it-4.0.22/io4it.egg-info/PKG-INFO +0 -7
  17. io4it-4.0.22/orangecontrib/IO4IT/utils/pptx2jpg.py +0 -102
  18. io4it-4.0.22/orangecontrib/IO4IT/widgets/designer/owpptx2jpg.ui +0 -132
  19. io4it-4.0.22/orangecontrib/IO4IT/widgets/icons/pptx2jpg.png +0 -0
  20. {io4it-4.0.22 → io4it-4.0.23}/io4it.egg-info/dependency_links.txt +0 -0
  21. {io4it-4.0.22 → io4it-4.0.23}/io4it.egg-info/entry_points.txt +0 -0
  22. {io4it-4.0.22 → io4it-4.0.23}/io4it.egg-info/namespace_packages.txt +0 -0
  23. {io4it-4.0.22 → io4it-4.0.23}/io4it.egg-info/requires.txt +0 -0
  24. {io4it-4.0.22 → io4it-4.0.23}/io4it.egg-info/top_level.txt +0 -0
  25. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/__init__.py +0 -0
  26. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
  27. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
  28. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/toolViews/designer/key_manager_ui.ui +0 -0
  29. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/toolViews/designer/link_workflow_manager_ui.ui +0 -0
  30. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/toolViews/key_manager_ui.py +0 -0
  31. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/toolViews/linkHTLMWorkflow_manager_ui.py +0 -0
  32. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/__init__.py +0 -0
  33. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/environment_collector.py +0 -0
  34. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/keys_manager.py +0 -0
  35. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/mail.py +0 -0
  36. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/pool_exec_utils.py +0 -0
  37. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/pst_extractor/TigerODMPST.dll +0 -0
  38. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/pst_extractor/__init__.py +0 -0
  39. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/pst_extractor/export_pst.py +0 -0
  40. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/pst_extractor/libpff.dll +0 -0
  41. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/pst_extractor/z.dll +0 -0
  42. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/secret_manager.py +0 -0
  43. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/utils/utils_md.py +0 -0
  44. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -0
  45. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +0 -0
  46. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py +0 -0
  47. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +0 -0
  48. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWExtractTablesDocxToXlsx.py +0 -0
  49. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWInboxMailMonitoring.py +0 -0
  50. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWMD2HTML.py +0 -0
  51. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWMarkdownLoader.py +0 -0
  52. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -0
  53. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWOfficeNormalizer.py +0 -0
  54. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWParserHTML.py +0 -0
  55. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWPdfType.py +0 -0
  56. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWPipAuditor.py +0 -0
  57. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWProcessPoolExecutor.py +0 -0
  58. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +0 -0
  59. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWS3downloader.py +0 -0
  60. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWS3list.py +0 -0
  61. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWWebSearch.py +0 -0
  62. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWmailLoader.py +0 -0
  63. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWmailSender.py +0 -0
  64. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -0
  65. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
  66. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
  67. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
  68. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui +0 -0
  69. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
  70. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui +0 -0
  71. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +0 -0
  72. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
  73. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui +0 -0
  74. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -0
  75. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui +0 -0
  76. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owinsertdatatoexcel.ui +0 -0
  77. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
  78. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
  79. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +0 -0
  80. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owmarkdownloader.ui +0 -0
  81. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owmd2html.ui +0 -0
  82. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owofficenormalizer.ui +0 -0
  83. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owparserhtml.ui +0 -0
  84. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owpdftype.ui +0 -0
  85. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owpipauditor.ui +0 -0
  86. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owprocesspoolexecutor.ui +0 -0
  87. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
  88. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
  89. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/owwebsearch.ui +0 -0
  90. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
  91. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
  92. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
  93. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/check_pdf.png +0 -0
  94. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
  95. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/dep_md_old.png +0 -0
  96. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
  97. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
  98. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/extract_table.png +0 -0
  99. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
  100. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/html.png +0 -0
  101. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/json-file.png +0 -0
  102. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
  103. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/load_md.png +0 -0
  104. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
  105. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
  106. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
  107. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/monitor-email.svg +0 -0
  108. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/office_normalizer.png +0 -0
  109. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/owmd2html.svg +0 -0
  110. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/pip_auditor.png +0 -0
  111. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/process_pool_executor.png +0 -0
  112. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
  113. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
  114. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
  115. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/websearch.png +0 -0
  116. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
  117. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons/xlsx_insert.png +0 -0
  118. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
  119. {io4it-4.0.22 → io4it-4.0.23}/orangecontrib/__init__.py +0 -0
  120. {io4it-4.0.22 → io4it-4.0.23}/setup.cfg +0 -0
io4it-4.0.23/PKG-INFO ADDED
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: io4it
3
+ Version: 4.0.23
4
+ Home-page:
5
+ Author:
6
+ Author-email:
7
+ Keywords: orange3 add-on
8
+ Requires-Dist: torchvision
9
+ Requires-Dist: torch
10
+ Requires-Dist: pylatexenc
11
+ Requires-Dist: docopt
12
+ Requires-Dist: boto3
13
+ Requires-Dist: opencv-python-headless
14
+ Requires-Dist: docling
15
+ Requires-Dist: docling-core
16
+ Requires-Dist: pypandoc
17
+ Requires-Dist: pypandoc-binary
18
+ Requires-Dist: scikit-learn
19
+ Requires-Dist: openai
20
+ Requires-Dist: pip-system-certs
21
+ Requires-Dist: docx2pdf
22
+ Requires-Dist: msal
23
+ Requires-Dist: exchangelib
24
+ Requires-Dist: html2text
25
+ Requires-Dist: ddgs
26
+ Requires-Dist: CATEGORIT
27
+ Dynamic: keywords
28
+ Dynamic: requires-dist
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: io4it
3
+ Version: 4.0.23
4
+ Home-page:
5
+ Author:
6
+ Author-email:
7
+ Keywords: orange3 add-on
8
+ Requires-Dist: torchvision
9
+ Requires-Dist: torch
10
+ Requires-Dist: pylatexenc
11
+ Requires-Dist: docopt
12
+ Requires-Dist: boto3
13
+ Requires-Dist: opencv-python-headless
14
+ Requires-Dist: docling
15
+ Requires-Dist: docling-core
16
+ Requires-Dist: pypandoc
17
+ Requires-Dist: pypandoc-binary
18
+ Requires-Dist: scikit-learn
19
+ Requires-Dist: openai
20
+ Requires-Dist: pip-system-certs
21
+ Requires-Dist: docx2pdf
22
+ Requires-Dist: msal
23
+ Requires-Dist: exchangelib
24
+ Requires-Dist: html2text
25
+ Requires-Dist: ddgs
26
+ Requires-Dist: CATEGORIT
27
+ Dynamic: keywords
28
+ Dynamic: requires-dist
@@ -11,11 +11,12 @@ orangecontrib/IO4IT/__init__.py
11
11
  orangecontrib/IO4IT/ocr_function/__init__.py
12
12
  orangecontrib/IO4IT/ocr_function/word_converter.py
13
13
  orangecontrib/IO4IT/utils/__init__.py
14
+ orangecontrib/IO4IT/utils/custom_api.py
14
15
  orangecontrib/IO4IT/utils/environment_collector.py
16
+ orangecontrib/IO4IT/utils/file2images.py
15
17
  orangecontrib/IO4IT/utils/keys_manager.py
16
18
  orangecontrib/IO4IT/utils/mail.py
17
19
  orangecontrib/IO4IT/utils/pool_exec_utils.py
18
- orangecontrib/IO4IT/utils/pptx2jpg.py
19
20
  orangecontrib/IO4IT/utils/secret_manager.py
20
21
  orangecontrib/IO4IT/utils/utils_md.py
21
22
  orangecontrib/IO4IT/utils/pst_extractor/TigerODMPST.dll
@@ -24,6 +25,7 @@ orangecontrib/IO4IT/utils/pst_extractor/export_pst.py
24
25
  orangecontrib/IO4IT/utils/pst_extractor/libpff.dll
25
26
  orangecontrib/IO4IT/utils/pst_extractor/z.dll
26
27
  orangecontrib/IO4IT/widgets/OWChatGpt.py
28
+ orangecontrib/IO4IT/widgets/OWCustomLLMApi.py
27
29
  orangecontrib/IO4IT/widgets/OWDeep_Search.py
28
30
  orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py
29
31
  orangecontrib/IO4IT/widgets/OWExportMarkdown.py
@@ -58,9 +60,11 @@ orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui
58
60
  orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui
59
61
  orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui
60
62
  orangecontrib/IO4IT/widgets/designer/owchatgpt.ui
63
+ orangecontrib/IO4IT/widgets/designer/owcustomllmapi.ui
61
64
  orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui
62
65
  orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui
63
66
  orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui
67
+ orangecontrib/IO4IT/widgets/designer/owfile2images.ui
64
68
  orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui
65
69
  orangecontrib/IO4IT/widgets/designer/owinsertdatatoexcel.ui
66
70
  orangecontrib/IO4IT/widgets/designer/owmailloader.ui
@@ -72,7 +76,6 @@ orangecontrib/IO4IT/widgets/designer/owofficenormalizer.ui
72
76
  orangecontrib/IO4IT/widgets/designer/owparserhtml.ui
73
77
  orangecontrib/IO4IT/widgets/designer/owpdftype.ui
74
78
  orangecontrib/IO4IT/widgets/designer/owpipauditor.ui
75
- orangecontrib/IO4IT/widgets/designer/owpptx2jpg.ui
76
79
  orangecontrib/IO4IT/widgets/designer/owprocesspoolexecutor.ui
77
80
  orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui
78
81
  orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui
@@ -86,6 +89,7 @@ orangecontrib/IO4IT/widgets/icons/dep_md_old.png
86
89
  orangecontrib/IO4IT/widgets/icons/download.png
87
90
  orangecontrib/IO4IT/widgets/icons/export_md.png
88
91
  orangecontrib/IO4IT/widgets/icons/extract_table.png
92
+ orangecontrib/IO4IT/widgets/icons/file2images.png
89
93
  orangecontrib/IO4IT/widgets/icons/file_extensor.png
90
94
  orangecontrib/IO4IT/widgets/icons/html.png
91
95
  orangecontrib/IO4IT/widgets/icons/json-file.png
@@ -96,9 +100,9 @@ orangecontrib/IO4IT/widgets/icons/mail_writer.png
96
100
  orangecontrib/IO4IT/widgets/icons/md.png
97
101
  orangecontrib/IO4IT/widgets/icons/monitor-email.svg
98
102
  orangecontrib/IO4IT/widgets/icons/office_normalizer.png
103
+ orangecontrib/IO4IT/widgets/icons/owcustomllmapi.svg
99
104
  orangecontrib/IO4IT/widgets/icons/owmd2html.svg
100
105
  orangecontrib/IO4IT/widgets/icons/pip_auditor.png
101
- orangecontrib/IO4IT/widgets/icons/pptx2jpg.png
102
106
  orangecontrib/IO4IT/widgets/icons/process_pool_executor.png
103
107
  orangecontrib/IO4IT/widgets/icons/speech_to_text.png
104
108
  orangecontrib/IO4IT/widgets/icons/upload.png
@@ -0,0 +1,132 @@
1
+ import base64
2
+ import mimetypes
3
+ import os
4
+
5
+ import requests
6
+
7
+ import Orange.data
8
+ from Orange.data import StringVariable
9
+
10
+
11
+ def build_headers(header_name, auth_scheme, api_key):
12
+ header_name = (header_name or "Authorization").strip()
13
+ auth_scheme = (auth_scheme or "").strip()
14
+ value = f"{auth_scheme} {api_key}".strip() if auth_scheme else str(api_key)
15
+ return {"Content-Type": "application/json", header_name: value}
16
+
17
+
18
+ def image_to_data_uri(file_path):
19
+ """Encode a local image file as a base64 data URI."""
20
+ mime_type, _ = mimetypes.guess_type(file_path)
21
+ mime_type = mime_type or "application/octet-stream"
22
+ with open(file_path, "rb") as f:
23
+ encoded = base64.b64encode(f.read()).decode("utf-8")
24
+ return f"data:{mime_type};base64,{encoded}"
25
+
26
+
27
+ def build_user_content(prompt, image_paths):
28
+ """Build the 'content' field of a user message, attaching images when provided."""
29
+ if not image_paths:
30
+ return str(prompt)
31
+
32
+ content = []
33
+ for image_path in image_paths:
34
+ image_path = image_path.strip().strip("'").strip('"')
35
+ if not image_path:
36
+ continue
37
+ if not os.path.exists(image_path):
38
+ content.append({"type": "text", "text": f"[ERROR] Image not found: {image_path}"})
39
+ continue
40
+ try:
41
+ content.append({"type": "image_url", "image_url": {"url": image_to_data_uri(image_path)}})
42
+ except Exception as e:
43
+ content.append({"type": "text", "text": f"[ERROR] Unable to read image {image_path}: {e}"})
44
+ content.append({"type": "text", "text": str(prompt)})
45
+ return content
46
+
47
+
48
+ def call_chat_completion(prompt, system_prompt, config, image_paths=None):
49
+ """
50
+ Call a /chat/completions endpoint using the widely adopted chat-completions
51
+ message format. Vision-capable models can be reached by passing image_paths,
52
+ which are embedded in the user message as base64 data URIs.
53
+
54
+ config keys: base_url, route, header_name, auth_scheme, api_key,
55
+ model, max_tokens, temperature, cert_path, cert_is_ca,
56
+ proxy_url
57
+ """
58
+ url = config["base_url"].rstrip("/") + config["route"]
59
+ headers = build_headers(config["header_name"], config["auth_scheme"], config["api_key"])
60
+
61
+ messages = []
62
+ if system_prompt:
63
+ messages.append({"role": "system", "content": str(system_prompt)})
64
+ messages.append({"role": "user", "content": build_user_content(prompt, image_paths)})
65
+
66
+ payload = {
67
+ "model": config["model"],
68
+ "messages": messages,
69
+ "temperature": config["temperature"],
70
+ "max_tokens": config["max_tokens"],
71
+ }
72
+
73
+ kwargs = {"timeout": 600}
74
+ cert_path = config.get("cert_path")
75
+ if cert_path:
76
+ if config.get("cert_is_ca"):
77
+ kwargs["verify"] = cert_path
78
+ else:
79
+ kwargs["cert"] = cert_path
80
+
81
+ if config.get("use_proxy"):
82
+ # Explicitly set, even when empty: an empty value forces a direct
83
+ # connection and bypasses any http_proxy/https_proxy environment
84
+ # variable (e.g. the one Orange itself sets from its preferences),
85
+ # instead of silently inheriting it.
86
+ proxy_url = config.get("proxy_url") or ""
87
+ kwargs["proxies"] = {"http": proxy_url, "https": proxy_url}
88
+
89
+ response = requests.post(url, headers=headers, json=payload, **kwargs)
90
+ response.raise_for_status()
91
+ data = response.json()
92
+ return data["choices"][0]["message"]["content"]
93
+
94
+
95
+ def generate_answers(data, config, progress_callback=None):
96
+ """
97
+ Runs call_chat_completion for every row of `data` (needs a 'prompt' column,
98
+ optionally a 'system_prompt' column and an 'image paths' column for
99
+ vision-capable models) and returns a new Table with an added 'Answer'
100
+ string column.
101
+
102
+ The 'image paths' column, when present, holds one or more image file
103
+ paths separated by ';' for that row.
104
+ """
105
+ domain = data.domain
106
+ has_system_prompt = "system_prompt" in domain
107
+ has_images = "image paths" in domain
108
+
109
+ prompts = data.get_column("prompt")
110
+ system_prompts = data.get_column("system_prompt") if has_system_prompt else None
111
+ image_paths_column = data.get_column("image paths") if has_images else None
112
+
113
+ answers = []
114
+ n_rows = len(data)
115
+ for i in range(n_rows):
116
+ prompt = prompts[i]
117
+ system_prompt = system_prompts[i] if system_prompts is not None else None
118
+ image_paths = image_paths_column[i].split(";") if image_paths_column is not None else None
119
+ try:
120
+ answer = call_chat_completion(prompt, system_prompt, config, image_paths)
121
+ except requests.HTTPError as e:
122
+ body = e.response.text if e.response is not None else str(e)
123
+ answer = f"[ERROR] HTTP {e.response.status_code if e.response is not None else '?'}: {body}"
124
+ except Exception as e:
125
+ answer = f"[ERROR] {e}"
126
+
127
+ answers.append(answer)
128
+ if progress_callback is not None:
129
+ progress_callback((int(100 * (i + 1) / max(n_rows, 1)), f"Row {i + 1}/{n_rows} done\n"))
130
+
131
+ answer_var = StringVariable("Answer")
132
+ return data.add_column(answer_var, answers)
@@ -0,0 +1,211 @@
1
+ import sys
2
+ import time
3
+ import subprocess
4
+ import shutil
5
+ from pathlib import Path
6
+
7
+
8
+ def get_conversion_method():
9
+ """Determines the best available conversion method."""
10
+ # 1. Check for MS Office
11
+ if sys.platform.startswith("win"):
12
+ try:
13
+ import win32com.client
14
+ # Try to see if the COM object is registered
15
+ win32com.client.GetActiveObject("PowerPoint.Application")
16
+ return "office"
17
+ except:
18
+ try:
19
+ # If not running, try to see if it's dispatchable
20
+ win32com.client.Dispatch("PowerPoint.Application")
21
+ return "office"
22
+ except:
23
+ pass
24
+ elif sys.platform == "darwin":
25
+ # Check if PowerPoint app exists on Mac
26
+ if Path("/Applications/Microsoft PowerPoint.app").exists():
27
+ return "office"
28
+
29
+ # 2. Check for LibreOffice (soffice)
30
+ if shutil.which("soffice"):
31
+ return "libreoffice"
32
+
33
+ # 3. Fallback to Aspose
34
+ return "aspose"
35
+
36
+
37
+ def convert_via_office(src: Path, pdf_path: Path):
38
+ if sys.platform.startswith("win"):
39
+ import win32com.client
40
+ powerpoint = win32com.client.Dispatch("PowerPoint.Application")
41
+ try:
42
+ presentation = powerpoint.Presentations.Open(str(src), WithWindow=False)
43
+ presentation.SaveAs(str(pdf_path), 32) # 32 = ppSaveAsPDF
44
+ presentation.Close()
45
+ finally:
46
+ # Note: We don't quit() powerpoint here to avoid killing other open docs
47
+ pass
48
+ elif sys.platform == "darwin":
49
+ script = f'tell application "Microsoft PowerPoint" to save (open POSIX file "{src}") in POSIX file "{pdf_path}" as PDF'
50
+ subprocess.run(["osascript", "-e", script], check=True)
51
+
52
+
53
+ def convert_via_libreoffice(src: Path, out_dir: Path):
54
+ subprocess.run([
55
+ "soffice", "--headless", "--convert-to", "pdf",
56
+ "--outdir", str(out_dir), str(src)
57
+ ], check=True, capture_output=True)
58
+ return out_dir / f"{src.stem}.pdf"
59
+
60
+
61
+ def parse_pages(spec, total):
62
+ """Convertit une spécification de pages en liste d'indices 1-based, triés et dédupliqués.
63
+
64
+ Exemples (total = nombre de pages du document) :
65
+ None / "" / "all" / "*" -> toutes les pages
66
+ "1" -> [1]
67
+ "1,3,5" -> [1, 3, 5]
68
+ "1-3" -> [1, 2, 3]
69
+ "2-4,6" -> [2, 3, 4, 6]
70
+ Les pages hors intervalle [1, total] sont ignorées silencieusement.
71
+ """
72
+ if spec is None:
73
+ return list(range(1, total + 1))
74
+
75
+ spec = str(spec).strip().lower()
76
+ if spec in ("", "all", "toutes", "tout", "*"):
77
+ return list(range(1, total + 1))
78
+
79
+ pages = set()
80
+ for part in spec.split(","):
81
+ part = part.strip()
82
+ if not part:
83
+ continue
84
+ try:
85
+ if "-" in part:
86
+ a, b = part.split("-", 1)
87
+ a, b = int(a), int(b)
88
+ for p in range(min(a, b), max(a, b) + 1):
89
+ pages.add(p)
90
+ else:
91
+ pages.add(int(part))
92
+ except ValueError:
93
+ # Fragment invalide (ex. "abc") -> on l'ignore
94
+ continue
95
+
96
+ return sorted(p for p in pages if 1 <= p <= total)
97
+
98
+
99
+ # Formats gérés nativement par Pixmap.save() de PyMuPDF.
100
+ # Les autres (tif/tiff, webp, bmp...) passent par Pillow via pix.pil_save().
101
+ _NATIVE_FORMATS = {"jpg", "jpeg", "png", "pnm", "pgm", "ppm", "pbm", "pam", "tga", "ps", "psd"}
102
+
103
+
104
+ def normalize_ext(out_ext):
105
+ """Nettoie l'extension demandée : minuscule, sans point initial, jpg par défaut."""
106
+ ext = str(out_ext or "jpg").strip().lower().lstrip(".")
107
+ if ext == "jpeg":
108
+ ext = "jpg"
109
+ return ext or "jpg"
110
+
111
+
112
+ def normalize_dpi(dpi, default=150):
113
+ """Convertit le DPI en entier valide (> 0), sinon retombe sur `default`."""
114
+ try:
115
+ dpi = int(dpi)
116
+ except (TypeError, ValueError):
117
+ return default
118
+ return dpi if dpi > 0 else default
119
+
120
+
121
+ def _save_pixmap(pix, img_path: Path, ext: str):
122
+ """Sauvegarde le pixmap au bon format selon l'extension."""
123
+ if ext in _NATIVE_FORMATS:
124
+ pix.save(str(img_path))
125
+ else:
126
+ # tif/tiff et autres formats -> nécessite Pillow
127
+ pix.pil_save(str(img_path))
128
+
129
+
130
+ def process_one_file(file_path_str: str, out_dir_str: str = None,
131
+ pages: str = "all", out_ext: str = "jpg",
132
+ dpi: int = 150, unique_folder: bool = True):
133
+ """Convertit un PPTX (ou PDF) en images.
134
+
135
+ Args:
136
+ file_path_str : chemin du fichier source (.pptx ou .pdf).
137
+ out_dir_str : dossier de sortie. Si None/vide, on retombe sur le
138
+ comportement historique (images_<nom> ou images/).
139
+ pages : pages/slides à extraire ("all", "1", "1,3", "1-3"...).
140
+ out_ext : format d'image de sortie ("jpg", "png", "tif"...).
141
+ dpi : résolution de rendu en points par pouce (défaut 150).
142
+ unique_folder : utilisé seulement quand out_dir_str n'est pas fourni.
143
+
144
+ Retour : [src, "img1|img2|...", "ok"/"nok", durée, message]
145
+ """
146
+ try:
147
+ import fitz # PyMuPDF
148
+ except ImportError:
149
+ return [file_path_str, "", "nok", "0.0", "Erreur: PyMuPDF non installé"]
150
+
151
+ t0 = time.time()
152
+ src = Path(file_path_str).resolve()
153
+ ext_out = normalize_ext(out_ext)
154
+ dpi_out = normalize_dpi(dpi)
155
+
156
+ # --- Dossier de sortie : colonne prioritaire, sinon fallback historique ---
157
+ if out_dir_str and str(out_dir_str).strip():
158
+ out_dir = Path(str(out_dir_str).strip())
159
+ else:
160
+ out_dir = src.parent / (f"images_{src.stem}" if unique_folder else "images")
161
+ out_dir.mkdir(parents=True, exist_ok=True)
162
+
163
+ img_list = []
164
+ ext = src.suffix.lower()
165
+ method = "pdf" # méthode par défaut si l'entrée est déjà un PDF
166
+ temp_pdf = False # True si on a généré un PDF intermédiaire à supprimer
167
+
168
+ try:
169
+ # --- PHASE 1 : obtenir un PDF ---
170
+ if ext == ".pdf":
171
+ pdf_path = src
172
+ else:
173
+ method = get_conversion_method()
174
+ if method == "aspose":
175
+ # import aspose.slides as slides # (fallback désactivé)
176
+ return [str(src), "", "nok", f"{time.time()-t0:.2f}",
177
+ "Conversion impossible sans LibreOffice ou MS Office"]
178
+
179
+ pdf_path = out_dir / f"{src.stem}.pdf"
180
+ temp_pdf = True
181
+ if method == "office":
182
+ convert_via_office(src, pdf_path)
183
+ else: # libreoffice
184
+ convert_via_libreoffice(src, out_dir)
185
+
186
+ # --- PHASE 2 : rasteriser uniquement les pages demandées ---
187
+ doc = fitz.open(str(pdf_path))
188
+ wanted = parse_pages(pages, doc.page_count)
189
+
190
+ for i in wanted:
191
+ page = doc.load_page(i - 1) # fitz est 0-based
192
+ pix = page.get_pixmap(dpi=dpi_out)
193
+ img_path = out_dir / f"{src.stem}_slide_{i:03d}.{ext_out}"
194
+ _save_pixmap(pix, img_path, ext_out)
195
+ img_list.append(str(img_path))
196
+ doc.close()
197
+
198
+ if temp_pdf and pdf_path.exists():
199
+ pdf_path.unlink()
200
+
201
+ duration = time.time() - t0
202
+ if not img_list:
203
+ return [str(src), "", "nok", f"{duration:.2f}",
204
+ f"Aucune page valide pour la spec '{pages}' ({method})"]
205
+
206
+ return [str(src), "|".join(img_list), "ok", f"{duration:.2f}",
207
+ f"{len(img_list)} image(s) .{ext_out} @ {dpi_out} DPI ({method})"]
208
+
209
+ except Exception as e:
210
+ return [str(src), "", "nok", f"{time.time()-t0:.2f}",
211
+ f"Method {method} failed: {str(e)}"]