IO4IT 4.0.18__tar.gz → 4.0.22__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 (114) hide show
  1. io4it-4.0.22/PKG-INFO +7 -0
  2. io4it-4.0.22/io4it.egg-info/PKG-INFO +7 -0
  3. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pst_extractor/export_pst.py +0 -1
  4. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWInboxMailMonitoring.py +17 -22
  5. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWJsonToDataTable.py +28 -8
  6. io4it-4.0.22/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +451 -0
  7. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui +0 -13
  8. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui +1 -30
  9. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +79 -34
  10. {io4it-4.0.18 → io4it-4.0.22}/setup.py +1 -1
  11. io4it-4.0.18/PKG-INFO +0 -28
  12. io4it-4.0.18/io4it.egg-info/PKG-INFO +0 -28
  13. io4it-4.0.18/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -277
  14. {io4it-4.0.18 → io4it-4.0.22}/io4it.egg-info/SOURCES.txt +0 -0
  15. {io4it-4.0.18 → io4it-4.0.22}/io4it.egg-info/dependency_links.txt +0 -0
  16. {io4it-4.0.18 → io4it-4.0.22}/io4it.egg-info/entry_points.txt +0 -0
  17. {io4it-4.0.18 → io4it-4.0.22}/io4it.egg-info/namespace_packages.txt +0 -0
  18. {io4it-4.0.18 → io4it-4.0.22}/io4it.egg-info/requires.txt +0 -0
  19. {io4it-4.0.18 → io4it-4.0.22}/io4it.egg-info/top_level.txt +0 -0
  20. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/__init__.py +0 -0
  21. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
  22. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
  23. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/toolViews/designer/key_manager_ui.ui +0 -0
  24. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/toolViews/designer/link_workflow_manager_ui.ui +0 -0
  25. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/toolViews/key_manager_ui.py +0 -0
  26. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/toolViews/linkHTLMWorkflow_manager_ui.py +0 -0
  27. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/__init__.py +0 -0
  28. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/environment_collector.py +0 -0
  29. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/keys_manager.py +0 -0
  30. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/mail.py +0 -0
  31. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pool_exec_utils.py +0 -0
  32. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pptx2jpg.py +0 -0
  33. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pst_extractor/TigerODMPST.dll +0 -0
  34. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pst_extractor/__init__.py +0 -0
  35. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pst_extractor/libpff.dll +0 -0
  36. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/pst_extractor/z.dll +0 -0
  37. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/secret_manager.py +0 -0
  38. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/utils/utils_md.py +0 -0
  39. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -0
  40. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +0 -0
  41. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py +0 -0
  42. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +0 -0
  43. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWExtractTablesDocxToXlsx.py +0 -0
  44. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWMD2HTML.py +0 -0
  45. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWMarkdownLoader.py +0 -0
  46. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -0
  47. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWOfficeNormalizer.py +0 -0
  48. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWPPTX2JPG.py +0 -0
  49. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWParserHTML.py +0 -0
  50. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWPdfType.py +0 -0
  51. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWPipAuditor.py +0 -0
  52. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWProcessPoolExecutor.py +0 -0
  53. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +0 -0
  54. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWS3downloader.py +0 -0
  55. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWS3list.py +0 -0
  56. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWWebSearch.py +0 -0
  57. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWXLSX_insert.py +0 -0
  58. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWmailLoader.py +0 -0
  59. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/OWmailSender.py +0 -0
  60. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
  61. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
  62. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
  63. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui +0 -0
  64. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
  65. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +0 -0
  66. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
  67. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui +0 -0
  68. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -0
  69. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owinsertdatatoexcel.ui +0 -0
  70. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
  71. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
  72. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +0 -0
  73. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owmarkdownloader.ui +0 -0
  74. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owmd2html.ui +0 -0
  75. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owofficenormalizer.ui +0 -0
  76. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owparserhtml.ui +0 -0
  77. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owpdftype.ui +0 -0
  78. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owpipauditor.ui +0 -0
  79. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owpptx2jpg.ui +0 -0
  80. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owprocesspoolexecutor.ui +0 -0
  81. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
  82. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
  83. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/designer/owwebsearch.ui +0 -0
  84. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
  85. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
  86. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/check_pdf.png +0 -0
  87. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
  88. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/dep_md_old.png +0 -0
  89. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
  90. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
  91. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/extract_table.png +0 -0
  92. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
  93. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/html.png +0 -0
  94. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/json-file.png +0 -0
  95. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
  96. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/load_md.png +0 -0
  97. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
  98. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
  99. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
  100. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/monitor-email.svg +0 -0
  101. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/office_normalizer.png +0 -0
  102. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/owmd2html.svg +0 -0
  103. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/pip_auditor.png +0 -0
  104. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/pptx2jpg.png +0 -0
  105. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/process_pool_executor.png +0 -0
  106. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
  107. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
  108. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
  109. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/websearch.png +0 -0
  110. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
  111. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons/xlsx_insert.png +0 -0
  112. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
  113. {io4it-4.0.18 → io4it-4.0.22}/orangecontrib/__init__.py +0 -0
  114. {io4it-4.0.18 → io4it-4.0.22}/setup.cfg +0 -0
io4it-4.0.22/PKG-INFO ADDED
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.1
2
+ Name: io4it
3
+ Version: 4.0.22
4
+ Home-page:
5
+ Author:
6
+ Author-email:
7
+ Keywords: orange3 add-on
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.1
2
+ Name: io4it
3
+ Version: 4.0.22
4
+ Home-page:
5
+ Author:
6
+ Author-email:
7
+ Keywords: orange3 add-on
@@ -3,7 +3,6 @@ import os
3
3
  from pathlib import Path
4
4
 
5
5
  BASE_DIR = Path(__file__).resolve().parent
6
- print(BASE_DIR)
7
6
 
8
7
  if hasattr(os, "add_dll_directory"):
9
8
  os.add_dll_directory(str(BASE_DIR))
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import sys
3
3
  import Orange.data
4
- from AnyQt.QtWidgets import QPushButton, QApplication, QRadioButton, QComboBox, QCheckBox, QSpinBox, QLabel, QFileDialog, QLineEdit
4
+ from AnyQt.QtWidgets import QPushButton, QApplication, QRadioButton, QComboBox, QCheckBox, QSpinBox, QLabel
5
5
  from Orange.widgets import widget
6
6
  from Orange.widgets.utils.signals import Input, Output
7
7
  from Orange.widgets.settings import Setting
@@ -59,9 +59,6 @@ class OWInboxMailMonitoring(widget.OWWidget):
59
59
  if new_text == self.type_co:
60
60
  return
61
61
  self.update_setting_from_qt_view()
62
- visible = (self.type_co == "ARCHIVE PST")
63
- self.lineEdit_pst_folder.setVisible(visible)
64
- self.pushButton_browse_pst.setVisible(visible)
65
62
 
66
63
  def on_text_changed2(self):
67
64
  self.your_email_conf = str(self.comboBox2.currentText())
@@ -76,15 +73,6 @@ class OWInboxMailMonitoring(widget.OWWidget):
76
73
  def on_email_count_changed(self, value: int):
77
74
  self.email_count = value
78
75
 
79
- def select_pst_folder(self):
80
- if self.type_co != "ARCHIVE PST":
81
- return
82
-
83
- folder = QFileDialog.getExistingDirectory(self, "Select PST folder")
84
-
85
- if folder:
86
- self.pst_folder = folder
87
- self.lineEdit_pst_folder.setText(folder)
88
76
 
89
77
 
90
78
  # ------------------------------------------------------------------
@@ -139,7 +127,7 @@ class OWInboxMailMonitoring(widget.OWWidget):
139
127
  self.checkBox_read_all.setChecked(self.read_all)
140
128
  self.spinBox_email_count.setValue(self.email_count)
141
129
  self._update_email_count_visibility()
142
- self.lineEdit_pst_folder.setText(self.pst_folder)
130
+
143
131
 
144
132
  def update_setting_from_qt_view(self):
145
133
  self.type_co = str(self.comboBox.currentText())
@@ -156,7 +144,6 @@ class OWInboxMailMonitoring(widget.OWWidget):
156
144
  self.sort_order = str(self.comboBox_sort_order.currentText())
157
145
  self.read_all = self.checkBox_read_all.isChecked()
158
146
  self.email_count = self.spinBox_email_count.value()
159
- self.pst_folder = self.lineEdit_pst_folder.text()
160
147
 
161
148
  # ------------------------------------------------------------------
162
149
  # Init
@@ -181,9 +168,6 @@ class OWInboxMailMonitoring(widget.OWWidget):
181
168
  self.checkBox_read_all = self.findChild(QCheckBox, 'checkBox_read_all')
182
169
  self.spinBox_email_count = self.findChild(QSpinBox, 'spinBox_email_count')
183
170
  self.label_email_count = self.findChild(QLabel, 'label_email_count')
184
- self.pushButton_browse_pst = self.findChild(QPushButton, "pushButton_browse_pst")
185
- self.lineEdit_pst_folder = self.findChild(QLineEdit, "lineEdit_pst_folder")
186
- self.pushButton_browse_pst.clicked.connect(self.select_pst_folder)
187
171
 
188
172
  # Populate connection type combobox
189
173
  types_co = [
@@ -303,10 +287,21 @@ class OWInboxMailMonitoring(widget.OWWidget):
303
287
  self.thread.safe_quit()
304
288
 
305
289
  if self.type_co == "ARCHIVE PST":
306
- self.pst_folder = self.lineEdit_pst_folder.text()
307
- if self.pst_folder == "":
308
- print(f"PST folder : {self.pst_folder}")
309
- self.error("You need to select a PST folder")
290
+ if self.data is None:
291
+ self.error("No input data detected")
292
+ return
293
+ meta_names = [m.name for m in self.data.domain.metas]
294
+
295
+ if "input_dir" not in meta_names:
296
+ self.error("Missing meta column 'input_dir'")
297
+ return
298
+
299
+ # Récupération de la première valeur
300
+ input_dir_idx = meta_names.index("input_dir")
301
+ self.pst_folder = str(self.data[0].metas[input_dir_idx])
302
+
303
+ if self.pst_folder == "" or self.pst_folder == "None":
304
+ self.error("The meta 'input_dir' is empty")
310
305
  return
311
306
  else:
312
307
  if self.your_email_conf == "":
@@ -105,6 +105,7 @@ class OWJsonToDataTable(base_widget.BaseListWidget):
105
105
  self.Outputs.data.send(None)
106
106
  self.data = None
107
107
  self.path = None
108
+ return
108
109
 
109
110
 
110
111
 
@@ -112,13 +113,32 @@ class OWJsonToDataTable(base_widget.BaseListWidget):
112
113
 
113
114
  try :
114
115
  if self.data:
115
- raw = self.data.get_column(self.selected_column_name)[0]
116
- if isinstance(raw, str):
117
- obj = json.loads(raw)
118
- if isinstance(obj, str):
119
- obj = json.loads(obj)
120
- else:
121
- obj = raw
116
+ obj = []
117
+ values = self.data.get_column(self.selected_column_name)
118
+
119
+ for row_index, raw in enumerate(values):
120
+ try:
121
+ if isinstance(raw, str):
122
+ if raw.strip() == "":
123
+ continue
124
+ current_obj = json.loads(raw)
125
+ if isinstance(current_obj, str):
126
+ current_obj = json.loads(current_obj)
127
+ else:
128
+ current_obj = raw
129
+
130
+ if isinstance(current_obj, list):
131
+ obj.extend(current_obj)
132
+ else:
133
+ obj.append(current_obj)
134
+ except Exception as e:
135
+ self.error(f"Error: {e}")
136
+ self.Outputs.data.send(None)
137
+ self.data = None
138
+ self.path = None
139
+ return
140
+
141
+
122
142
  if self.path:
123
143
  raw = self.path.get_column(self.selected_column_name)[0]
124
144
  with open(raw, "r", encoding="utf-8") as f:
@@ -144,4 +164,4 @@ if __name__ == "__main__":
144
164
  if hasattr(app, "exec"):
145
165
  sys.exit(app.exec())
146
166
  else:
147
- sys.exit(app.exec_())
167
+ sys.exit(app.exec_())
@@ -0,0 +1,451 @@
1
+ import os
2
+ import shutil
3
+ import sys
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ import Orange.data
8
+ from AnyQt.QtWidgets import QApplication, QCheckBox, QLabel
9
+
10
+ from Orange.widgets import widget
11
+ from Orange.widgets.utils.signals import Input, Output
12
+ from Orange.widgets.settings import Setting
13
+
14
+ if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
15
+ from Orange.widgets.orangecontrib.AAIT.utils import thread_management
16
+ from Orange.widgets.orangecontrib.AAIT.utils.import_uic import uic
17
+ from Orange.widgets.orangecontrib.IO4IT.ocr_function import word_converter
18
+ else:
19
+ from orangecontrib.AAIT.utils import thread_management
20
+ from orangecontrib.AAIT.utils.import_uic import uic
21
+ from orangecontrib.IO4IT.ocr_function import word_converter
22
+
23
+
24
+ def _import_convert_to_pdf():
25
+ """Import paresseux de `convert_to_pdf` (PageIndex)."""
26
+ if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
27
+ from Orange.widgets.orangecontrib.AAIT.llm.PageIndex_functions import convert_to_pdf
28
+ else:
29
+ from orangecontrib.AAIT.llm.PageIndex_functions import convert_to_pdf
30
+ return convert_to_pdf
31
+
32
+
33
+ def convert_single_pdf_to_docx(src: Path, dst: Path, force_basic_convertion: bool) -> None:
34
+ """Convertit UN pdf vers UN docx dont le nom/chemin est imposé.
35
+
36
+ `word_converter.convert_pdf_structure` ne sait travailler que sur des
37
+ répertoires : on passe donc par un répertoire temporaire, puis on déplace
38
+ le fichier produit vers `dst`.
39
+ """
40
+ dst.parent.mkdir(parents=True, exist_ok=True)
41
+ with tempfile.TemporaryDirectory() as tmp:
42
+ tmp_in = Path(tmp) / "in"
43
+ tmp_out = Path(tmp) / "out"
44
+ tmp_in.mkdir()
45
+ tmp_out.mkdir()
46
+ shutil.copy2(str(src), str(tmp_in / src.name))
47
+ word_converter.convert_pdf_structure(
48
+ [str(tmp_in)],
49
+ [str(tmp_out)],
50
+ ignore_exsting_out_put=False,
51
+ forceBasicConvertion=force_basic_convertion,
52
+ )
53
+ produced = sorted(tmp_out.rglob("*.docx"))
54
+ if not produced:
55
+ raise RuntimeError("aucun docx produit par la conversion")
56
+ os.replace(str(produced[0]), str(dst))
57
+
58
+
59
+ class OWwordpdf2docx(widget.OWWidget):
60
+ name = "WordPdf2Docx"
61
+ description = "Convert PDF to DOCX and/or DOCX to PDF"
62
+ icon = "icons/wordpdf2docx.png"
63
+ if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
64
+ icon = "icons_dev/wordpdf2docx.png"
65
+ gui = os.path.join(os.path.dirname(os.path.abspath(__file__)), "designer/wordpdf2docx.ui")
66
+ want_control_area = False
67
+ priority = 3000
68
+ category = "AAIT - TOOLBOX"
69
+ strDirectoryMode: str = Setting('True')
70
+ strFileMode: str = Setting('False')
71
+ strConvertPdfToDocx : str =Setting('True')
72
+ strConvertDocxToPdf : str =Setting('False')
73
+ strIgnoreExistingOuput :str =Setting('True')
74
+ strForceBasicConvertion :str =Setting('False')
75
+
76
+ class Inputs:
77
+ data = Input("Data", Orange.data.Table)
78
+
79
+ class Outputs:
80
+ data = Output("Data", Orange.data.Table)
81
+
82
+ @Inputs.data
83
+ def set_data(self, in_data):
84
+ self.data = in_data
85
+ if self.autorun:
86
+ self.run()
87
+
88
+ # ------------------------------------------------------------------ modes
89
+ def on_mode_dir_toggled(self):
90
+ if self._mode_updating:
91
+ return
92
+ self._mode_updating = True
93
+ try:
94
+ if not self.check_box_mode_dir.isChecked() and not self.check_box_mode_file.isChecked():
95
+ self.check_box_mode_dir.setChecked(True)
96
+ self.check_box_mode_file.setChecked(not self.check_box_mode_dir.isChecked())
97
+ finally:
98
+ self._mode_updating = False
99
+ self._store_mode()
100
+
101
+ def on_mode_file_toggled(self):
102
+ if self._mode_updating:
103
+ return
104
+ self._mode_updating = True
105
+ try:
106
+ if not self.check_box_mode_dir.isChecked() and not self.check_box_mode_file.isChecked():
107
+ self.check_box_mode_file.setChecked(True)
108
+ self.check_box_mode_dir.setChecked(not self.check_box_mode_file.isChecked())
109
+ finally:
110
+ self._mode_updating = False
111
+ self._store_mode()
112
+
113
+ def _store_mode(self):
114
+ self.strDirectoryMode = 'True' if self.check_box_mode_dir.isChecked() else 'False'
115
+ self.strFileMode = 'True' if self.check_box_mode_file.isChecked() else 'False'
116
+ self._update_options_enabled()
117
+
118
+ # ------------------------------------------------------------- checkboxes
119
+ def on_checkbox_toggled(self):
120
+ if self.check_box.isChecked():
121
+ self.strConvertPdfToDocx = 'True'
122
+ else:
123
+ self.strConvertPdfToDocx = 'False'
124
+ self._update_options_enabled()
125
+
126
+ def on_checkbox_toggled2(self):
127
+ if self.check_box2.isChecked():
128
+ self.strConvertDocxToPdf = 'True'
129
+ else:
130
+ self.strConvertDocxToPdf = 'False'
131
+
132
+ def on_checkbox_toggled3(self):
133
+ if self.check_box3.isChecked():
134
+ self.strIgnoreExistingOuput = 'True'
135
+ else:
136
+ self.strIgnoreExistingOuput ='False'
137
+
138
+ def on_checkbox_toggled4(self):
139
+ if self.check_box4.isChecked():
140
+ self.strForceBasicConvertion = 'True'
141
+ else:
142
+ self.strForceBasicConvertion ='False'
143
+
144
+ def _update_options_enabled(self):
145
+ file_mode = self.check_box_mode_file.isChecked()
146
+ # en mode fichier, le sens de conversion est déduit du couple path / path_out
147
+ self.check_box.setEnabled(not file_mode)
148
+ self.check_box2.setEnabled(not file_mode)
149
+ self.check_box4.setEnabled(file_mode or self.check_box.isChecked())
150
+ if self.label_description is not None:
151
+ if file_mode:
152
+ self.label_description.setText(
153
+ "Convert each file from the \"path\" column to the \"path_out\" column "
154
+ "(.pdf -> .docx or .docx -> .pdf)")
155
+ else:
156
+ self.label_description.setText(
157
+ "Convert PDF <-> DOCX for every file, from input_dir to output_dir")
158
+
159
+ def __init__(self):
160
+ super().__init__()
161
+
162
+ # Qt Management
163
+ self.setFixedWidth(470)
164
+ self.setFixedHeight(380)
165
+ uic.loadUi(self.gui, self)
166
+
167
+ self._mode_updating = True
168
+
169
+ self.check_box_mode_dir = self.findChild(QCheckBox, 'checkBox_mode_dir')
170
+ self.check_box_mode_file = self.findChild(QCheckBox, 'checkBox_mode_file')
171
+ self.check_box = self.findChild(QCheckBox, 'checkBox')
172
+ self.check_box2 = self.findChild(QCheckBox, 'checkBox_2')
173
+ self.check_box3 = self.findChild(QCheckBox, 'checkBox_3')
174
+ self.check_box4 = self.findChild(QCheckBox, 'checkBox_4')
175
+ self.label_description = self.findChild(QLabel, 'Description')
176
+
177
+ # sécurité : si les deux settings sont incohérents, on retombe sur le
178
+ # mode historique (dossier)
179
+ file_mode = (self.strFileMode == 'True' and self.strDirectoryMode != 'True')
180
+ self.check_box_mode_dir.setChecked(not file_mode)
181
+ self.check_box_mode_file.setChecked(file_mode)
182
+
183
+ self.check_box.setChecked(self.strConvertPdfToDocx == 'True')
184
+ self.check_box2.setChecked(self.strConvertDocxToPdf == 'True')
185
+ self.check_box3.setChecked(self.strIgnoreExistingOuput == 'True')
186
+ self.check_box4.setChecked(self.strForceBasicConvertion == 'True')
187
+
188
+ self.check_box_mode_dir.stateChanged.connect(self.on_mode_dir_toggled)
189
+ self.check_box_mode_file.stateChanged.connect(self.on_mode_file_toggled)
190
+ self.check_box.stateChanged.connect(self.on_checkbox_toggled)
191
+ self.check_box2.stateChanged.connect(self.on_checkbox_toggled2)
192
+ self.check_box3.stateChanged.connect(self.on_checkbox_toggled3)
193
+ self.check_box4.stateChanged.connect(self.on_checkbox_toggled4)
194
+
195
+ self._mode_updating = False
196
+ self._store_mode()
197
+
198
+ # Data Management
199
+ self.data = None
200
+ self.thread = None
201
+ self.autorun = True
202
+ self.result = None
203
+ self.post_initialized()
204
+
205
+ # -------------------------------------------------------------- utilities
206
+ def _check_string_column(self, column_name):
207
+ """Retourne True si la colonne existe et est de type Text."""
208
+ try:
209
+ self.data.domain[column_name]
210
+ except KeyError:
211
+ self.error('You need a "%s" column in input data' % column_name)
212
+ return False
213
+ if type(self.data.domain[column_name]).__name__ != 'StringVariable':
214
+ self.error('"%s" column needs to be a Text' % column_name)
215
+ return False
216
+ return True
217
+
218
+ # -------------------------------------------------------------------- run
219
+ def run(self):
220
+ self.error("")
221
+
222
+ # if thread is running quit
223
+ if self.thread is not None:
224
+ self.thread.safe_quit()
225
+
226
+ if self.data is None:
227
+ return
228
+
229
+ ignore_existing_docx = (self.strIgnoreExistingOuput == 'True')
230
+ force_basic_convertion = (self.strForceBasicConvertion == 'True')
231
+
232
+ if self.strFileMode == 'True':
233
+ worker_process = self._build_file_worker(ignore_existing_docx, force_basic_convertion)
234
+ else:
235
+ worker_process = self._build_directory_worker(ignore_existing_docx, force_basic_convertion)
236
+
237
+ if worker_process is None:
238
+ return
239
+
240
+ # Start progress bar
241
+ self.progressBarInit()
242
+
243
+ # Connexion et démarrage du thread avec la fonction interne unifiée
244
+ self.thread = thread_management.Thread(worker_process)
245
+ self.thread.progress.connect(self.handle_progress)
246
+ self.thread.result.connect(self.handle_result)
247
+ self.thread.finish.connect(self.handle_finish)
248
+ self.thread.start()
249
+
250
+ # --------------------------------------------------------- mode répertoire
251
+ def _build_directory_worker(self, ignore_existing_docx, force_basic_convertion):
252
+ convert_pdf = (self.strConvertPdfToDocx == 'True')
253
+ convert_docx = (self.strConvertDocxToPdf == 'True')
254
+
255
+ # Verification of in_data
256
+ if not self._check_string_column("input_dir"):
257
+ return None
258
+ if not self._check_string_column("output_dir"):
259
+ return None
260
+
261
+ input_dir = self.data.get_column("input_dir")
262
+ output_dir = self.data.get_column("output_dir")
263
+
264
+ def worker_process():
265
+ errors = []
266
+ pdf_tasks = []
267
+ docx_tasks = []
268
+
269
+ # 1. Scan global initial pour compter tous les fichiers
270
+ for in_dir, out_dir in zip(input_dir, output_dir):
271
+ if not in_dir or not out_dir:
272
+ continue
273
+ in_dir, out_dir = str(in_dir), str(out_dir)
274
+ if not os.path.exists(in_dir):
275
+ continue
276
+
277
+ for root, _, files in os.walk(in_dir):
278
+ for name in files:
279
+ src = Path(root) / name
280
+ rel = os.path.relpath(root, in_dir)
281
+ dst_dir = Path(out_dir) if rel == "." else Path(out_dir) / rel
282
+
283
+ if convert_pdf and name.lower().endswith(".pdf"):
284
+ dst = dst_dir / f"{src.stem}.docx"
285
+ if not (ignore_existing_docx and dst.exists()):
286
+ pdf_tasks.append((src, dst))
287
+ elif convert_docx and name.lower().endswith((".docx", ".doc")):
288
+ dst = dst_dir / f"{src.stem}.pdf"
289
+ if not (ignore_existing_docx and dst.exists()):
290
+ docx_tasks.append((src, dst))
291
+
292
+ total_files = len(pdf_tasks) + len(docx_tasks)
293
+ if total_files == 0:
294
+ return "Success"
295
+
296
+ processed_count = 0
297
+
298
+ # 2. Exécution séquentielle : PDF -> DOCX
299
+ for src, dst in pdf_tasks:
300
+ try:
301
+ dst.parent.mkdir(parents=True, exist_ok=True)
302
+ word_converter.convert_pdf_structure(
303
+ [str(src.parent)],
304
+ [str(dst.parent)],
305
+ ignore_exsting_out_put=ignore_existing_docx,
306
+ forceBasicConvertion=force_basic_convertion
307
+ )
308
+ except Exception as e:
309
+ errors.append(f"Erreur PDF->DOCX ({src.name}): {e}")
310
+
311
+ processed_count += 1
312
+ try:
313
+ self.thread.progress.emit((processed_count / total_files) * 100)
314
+ except Exception:
315
+ pass
316
+
317
+ # 3. Exécution séquentielle : DOCX -> PDF
318
+ if docx_tasks:
319
+ convert_to_pdf = _import_convert_to_pdf()
320
+ for src, dst in docx_tasks:
321
+ try:
322
+ dst.parent.mkdir(parents=True, exist_ok=True)
323
+ convert_to_pdf(Path(src), Path(dst))
324
+ except Exception as e:
325
+ errors.append(f"Erreur DOCX->PDF ({src.name}): {e}")
326
+
327
+ processed_count += 1
328
+ try:
329
+ self.thread.progress.emit((processed_count / total_files) * 100)
330
+ except Exception:
331
+ pass
332
+
333
+ if errors:
334
+ return "\n".join(errors)
335
+ return "Success"
336
+
337
+ return worker_process
338
+
339
+ # ------------------------------------------------------------ mode fichier
340
+ def _build_file_worker(self, ignore_existing_docx, force_basic_convertion):
341
+ # Verification of in_data
342
+ if not self._check_string_column("path"):
343
+ return None
344
+ if not self._check_string_column("path_out"):
345
+ return None
346
+
347
+ path_in = self.data.get_column("path")
348
+ path_out = self.data.get_column("path_out")
349
+
350
+ def worker_process():
351
+ errors = []
352
+ tasks = []
353
+
354
+ # 1. Construction et validation des couples path / path_out
355
+ for index, (p_in, p_out) in enumerate(zip(path_in, path_out), start=1):
356
+ if not p_in or not p_out:
357
+ continue
358
+ src = Path(str(p_in))
359
+ dst = Path(str(p_out))
360
+ src_ext = src.suffix.lower()
361
+ dst_ext = dst.suffix.lower()
362
+
363
+ if src_ext == ".pdf" and dst_ext == ".docx":
364
+ direction = "pdf2docx"
365
+ elif src_ext in (".docx", ".doc") and dst_ext == ".pdf":
366
+ direction = "docx2pdf"
367
+ else:
368
+ errors.append(
369
+ f"Ligne {index} : couple invalide ({src.name} -> {dst.name}), "
370
+ f"attendu .pdf -> .docx ou .docx -> .pdf")
371
+ continue
372
+
373
+ if not src.exists():
374
+ errors.append(f"Ligne {index} : fichier introuvable ({src})")
375
+ continue
376
+
377
+ if ignore_existing_docx and dst.exists():
378
+ continue
379
+
380
+ tasks.append((direction, src, dst))
381
+
382
+ total_files = len(tasks)
383
+ if total_files == 0:
384
+ if errors:
385
+ return "\n".join(errors)
386
+ return "Success"
387
+
388
+ convert_to_pdf = None
389
+ if any(direction == "docx2pdf" for direction, _, _ in tasks):
390
+ convert_to_pdf = _import_convert_to_pdf()
391
+
392
+ processed_count = 0
393
+
394
+ # 2. Exécution séquentielle, fichier par fichier
395
+ for direction, src, dst in tasks:
396
+ try:
397
+ dst.parent.mkdir(parents=True, exist_ok=True)
398
+ if direction == "pdf2docx":
399
+ convert_single_pdf_to_docx(src, dst, force_basic_convertion)
400
+ else:
401
+ convert_to_pdf(Path(src), Path(dst))
402
+ except Exception as e:
403
+ if direction == "pdf2docx":
404
+ errors.append(f"Erreur PDF->DOCX ({src.name}): {e}")
405
+ else:
406
+ errors.append(f"Erreur DOCX->PDF ({src.name}): {e}")
407
+
408
+ processed_count += 1
409
+ try:
410
+ self.thread.progress.emit((processed_count / total_files) * 100)
411
+ except Exception:
412
+ pass
413
+
414
+ if errors:
415
+ return "\n".join(errors)
416
+ return "Success"
417
+
418
+ return worker_process
419
+
420
+ def handle_progress(self, value: float) -> None:
421
+ self.progressBarSet(value)
422
+
423
+ def handle_result(self, result):
424
+ try:
425
+ self.result = result
426
+ if result == "Success":
427
+ self.error("")
428
+ else:
429
+ self.error(result)
430
+
431
+ self.Outputs.data.send(self.data)
432
+ except Exception as e:
433
+ print("An error occurred when sending out_data:", e)
434
+ self.Outputs.data.send(None)
435
+ return
436
+
437
+ def handle_finish(self):
438
+ print("conversion finished")
439
+ self.progressBarFinished()
440
+
441
+ def post_initialized(self):
442
+ pass
443
+
444
+ if __name__ == "__main__":
445
+ app = QApplication(sys.argv)
446
+ my_widget = OWwordpdf2docx()
447
+ my_widget.show()
448
+ if hasattr(app, "exec"):
449
+ sys.exit(app.exec())
450
+ else:
451
+ sys.exit(app.exec_())
@@ -38,19 +38,6 @@
38
38
  <string>Convert Json to Orange data table. You need to pass a content in input.</string>
39
39
  </property>
40
40
  </widget>
41
- <widget class="QLabel" name="label_2">
42
- <property name="geometry">
43
- <rect>
44
- <x>10</x>
45
- <y>50</y>
46
- <width>401</width>
47
- <height>21</height>
48
- </rect>
49
- </property>
50
- <property name="text">
51
- <string>You can only convert the first row from the table.</string>
52
- </property>
53
- </widget>
54
41
  <widget class="QLabel" name="label_3">
55
42
  <property name="geometry">
56
43
  <rect>
@@ -200,36 +200,7 @@
200
200
  <height>22</height>
201
201
  </rect>
202
202
  </property>
203
- <property name="text">
204
- <string>PST folder :</string>
205
- </property>
206
- </widget>
207
-
208
- <widget class="QLineEdit" name="lineEdit_pst_folder">
209
- <property name="geometry">
210
- <rect>
211
- <x>110</x>
212
- <y>280</y>
213
- <width>480</width>
214
- <height>22</height>
215
- </rect>
216
- </property>
217
- </widget>
218
-
219
- <widget class="QPushButton" name="pushButton_browse_pst">
220
- <property name="geometry">
221
- <rect>
222
- <x>600</x>
223
- <y>280</y>
224
- <width>60</width>
225
- <height>22</height>
226
- </rect>
227
- </property>
228
- <property name="text">
229
- <string>...</string>
230
- </property>
231
- </widget>
232
-
203
+ </widget>
233
204
  </widget>
234
205
 
235
206
  <widget class="QCheckBox" name="checkBox_send">