ScriptCollection 3.5.138__py3-none-any.whl → 3.5.140__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.
@@ -8,6 +8,7 @@ import keyboard
8
8
  from .TasksForCommonProjectStructure import TasksForCommonProjectStructure
9
9
  from .ScriptCollectionCore import ScriptCollectionCore
10
10
  from .GeneralUtilities import GeneralUtilities
11
+ from .ImageUpdater import ImageUpdater, VersionEcholon
11
12
 
12
13
 
13
14
  def FilenameObfuscator() -> int:
@@ -635,7 +636,7 @@ def Espoc() -> int:
635
636
  parser.add_argument('-p', '--processid', required=True)
636
637
  parser.add_argument('-f', '--file', required=True, help='Specifies the file where the process-ids of the started processes are stored (line by line). This file will be deleted when all started processes are terminated.')
637
638
  args = parser.parse_args()
638
- process_id =int(args.processid)
639
+ process_id = int(args.processid)
639
640
  process_list_file: str = args.file
640
641
  if not os.path.isabs(process_list_file):
641
642
  process_list_file = GeneralUtilities.resolve_relative_path(process_list_file, os.getcwd())
@@ -654,10 +655,74 @@ def Espoc() -> int:
654
655
  GeneralUtilities.write_message_to_stdout(f"File '{process_list_file}' does not exist. No processes to terminate.")
655
656
  return 0
656
657
 
657
- def ConvertGitRepositoryToBareRepository()->int:
658
+
659
+ def ConvertGitRepositoryToBareRepository() -> int:
658
660
  parser = argparse.ArgumentParser(description="Converts a local git-repository to a bare repository.")
659
661
  parser.add_argument('-f', '--folder', required=True, help='Git-repository-folder which should be converted.')
660
662
  args = parser.parse_args()
661
- sc=ScriptCollectionCore()
663
+ sc = ScriptCollectionCore()
662
664
  sc.convert_git_repository_to_bare_repository(args.folder)
663
665
  return 0
666
+
667
+
668
+ def OCRAnalysisOfFolder() -> int:
669
+ parser = argparse.ArgumentParser()
670
+ parser.add_argument('-s', '--serviceaddress', required=False, default=None)
671
+ parser.add_argument('-e', '--extensions', required=False, default=None)
672
+ parser.add_argument('-l', '--languages', required=False, default="en")
673
+ parser.add_argument('-f', '--folder', required=False, default=None)
674
+ args = parser.parse_args()
675
+ sc = ScriptCollectionCore()
676
+ if args.folder is None:
677
+ args.folder = os.getcwd()
678
+ extensions_value: str = None
679
+ if args.extensions is not None:
680
+ if "," in args.extensions:
681
+ extensions_value = args.extensions.split(",")
682
+ else:
683
+ extensions_value = [args.extensions]
684
+ sc.ocr_analysis_of_folder(args.folder, args.serviceaddress, extensions_value, args.languages)
685
+ return 0
686
+
687
+
688
+ def OCRAnalysisOfFile() -> int:
689
+ parser = argparse.ArgumentParser()
690
+ parser.add_argument('-s', '--serviceaddress', required=False, default=None)
691
+ parser.add_argument('-l', '--languages', required=False, default="en")
692
+ parser.add_argument('-f', '--file', required=True)
693
+ args = parser.parse_args()
694
+ sc = ScriptCollectionCore()
695
+ sc.ocr_analysis_of_file(args.file, args.serviceaddress, args.languages)
696
+ return 0
697
+
698
+
699
+ def OCRAnalysisOfRepository() -> int:
700
+ parser = argparse.ArgumentParser()
701
+ parser.add_argument('-s', '--serviceaddress', required=False, default=None)
702
+ parser.add_argument('-e', '--extensions', required=False, default=None)
703
+ parser.add_argument('-l', '--languages', required=False, default="en")
704
+ parser.add_argument('-f', '--folder', required=False, default=None)
705
+ args = parser.parse_args()
706
+ sc = ScriptCollectionCore()
707
+ if args.folder is None:
708
+ args.folder = os.getcwd()
709
+ extensions_value: str = None
710
+ if args.extensions is not None:
711
+ if "," in args.extensions:
712
+ extensions_value = args.extensions.split(",")
713
+ else:
714
+ extensions_value = [args.extensions]
715
+ sc.ocr_analysis_of_repository(args.folder, args.serviceaddress, extensions_value, args.languages)
716
+ return 0
717
+
718
+
719
+ def UpdateImagesInDockerComposeFile() -> int:
720
+ iu: ImageUpdater = ImageUpdater()
721
+ parser = argparse.ArgumentParser()
722
+ parser.add_argument('-f', '--file', required=False, default=None)
723
+ # TODO add option to specify ignored services and versionecholon
724
+ args = parser.parse_args()
725
+ if args.file is None:
726
+ args.file = os.path.join(os.getcwd(), "docker-compose.yml")
727
+ iu.update_all_services_in_docker_compose_file(args.file, VersionEcholon.LatestPatch, [])
728
+ return 0
@@ -11,7 +11,7 @@ from .GeneralUtilities import GeneralUtilities
11
11
 
12
12
 
13
13
  class VersionEcholon(Enum):
14
- Patch = 0
14
+ LatestPatch = 0
15
15
  LatestPatchOrLatestMinor = 1
16
16
  LatestPatchOrLatestMinorOrNextMajor = 2
17
17
  Newest = 3
@@ -63,8 +63,8 @@ class ImageUpdaterHelper:
63
63
 
64
64
  @staticmethod
65
65
  @GeneralUtilities.check_arguments
66
- def filter_considering_echolog(newer_versions: list[Version], current_version: Version, version_echolon: VersionEcholon) -> Version:
67
- if version_echolon == VersionEcholon.Patch:
66
+ def filter_considering_echolon(newer_versions: list[Version], current_version: Version, version_echolon: VersionEcholon) -> Version:
67
+ if version_echolon == VersionEcholon.LatestPatch:
68
68
  return ImageUpdaterHelper._internal_get_latest_patch_version(newer_versions, current_version)
69
69
  elif version_echolon == VersionEcholon.LatestPatchOrLatestMinor:
70
70
  return ImageUpdaterHelper._internal_get_latest_patch_or_latest_minor_version(newer_versions, current_version)
@@ -137,11 +137,14 @@ class ConcreteImageUpdaterForNginx(ConcreteImageUpdater):
137
137
 
138
138
  @GeneralUtilities.check_arguments
139
139
  def version_to_tag(self, version: Version) -> str:
140
- raise NotImplementedError
140
+ return f"{version.major}.{version.minor}.{version.micro}"
141
141
 
142
142
  @GeneralUtilities.check_arguments
143
143
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
144
- raise NotImplementedError
144
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
145
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
146
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
147
+ return result
145
148
 
146
149
  @GeneralUtilities.check_arguments
147
150
  def get_supported_images(self) -> list[str]:
@@ -149,14 +152,36 @@ class ConcreteImageUpdaterForNginx(ConcreteImageUpdater):
149
152
 
150
153
  @GeneralUtilities.check_arguments
151
154
  def get_version_from_tag(self, image: str, tag: str) -> Version:
152
- raise NotImplementedError
155
+ return ve.parse(tag)
156
+
157
+
158
+ class ConcreteImageUpdaterForWordpress(ConcreteImageUpdater):
159
+
160
+ @GeneralUtilities.check_arguments
161
+ def version_to_tag(self, version: Version) -> str:
162
+ return f"{version.major}.{version.minor}.{version.micro}"
163
+
164
+ @GeneralUtilities.check_arguments
165
+ def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
166
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
167
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
168
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
169
+ return result
170
+
171
+ @GeneralUtilities.check_arguments
172
+ def get_supported_images(self) -> list[str]:
173
+ return ["wordpress"]
174
+
175
+ @GeneralUtilities.check_arguments
176
+ def get_version_from_tag(self, image: str, tag: str) -> Version:
177
+ return ve.parse(tag)
153
178
 
154
179
 
155
180
  class ConcreteImageUpdaterForGitLab(ConcreteImageUpdater):
156
181
 
157
182
  @GeneralUtilities.check_arguments
158
183
  def version_to_tag(self, version: Version) -> str:
159
- raise NotImplementedError
184
+ return f"{version.major}.{version.minor}.{version.micro}-ce.0"
160
185
 
161
186
  @GeneralUtilities.check_arguments
162
187
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
@@ -175,96 +200,111 @@ class ConcreteImageUpdaterForRegistry(ConcreteImageUpdater):
175
200
 
176
201
  @GeneralUtilities.check_arguments
177
202
  def version_to_tag(self, version: Version) -> str:
178
- raise NotImplementedError
203
+ return f"{version.major}.{version.minor}.{version.micro}"
179
204
 
180
205
  @GeneralUtilities.check_arguments
181
206
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
182
- raise NotImplementedError
207
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
208
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
209
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
210
+ return result
183
211
 
184
212
  @abstractmethod
185
213
  @GeneralUtilities.check_arguments
186
214
  def get_supported_images(self) -> list[str]:
187
- return [] # TODO
215
+ return ["registry"]
188
216
 
189
217
  @GeneralUtilities.check_arguments
190
218
  def get_version_from_tag(self, image: str, tag: str) -> Version:
191
- raise NotImplementedError
219
+ return ve.parse(tag)
192
220
 
193
221
 
194
222
  class ConcreteImageUpdaterForPrometheus(ConcreteImageUpdater):
195
223
 
196
224
  @GeneralUtilities.check_arguments
197
225
  def version_to_tag(self, version: Version) -> str:
198
- raise NotImplementedError
226
+ return f"v{version.major}.{version.minor}.{version.micro}"
199
227
 
200
228
  @GeneralUtilities.check_arguments
201
229
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
202
- raise NotImplementedError
230
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^v\\d+\\.\\d+\\.\\d+$", 999)
231
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
232
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
233
+ return result
203
234
 
204
235
  @GeneralUtilities.check_arguments
205
236
  def get_supported_images(self) -> list[str]:
206
- return [] # TODO
237
+ return ["prom/prometheus"]
207
238
 
208
239
  @GeneralUtilities.check_arguments
209
240
  def get_version_from_tag(self, image: str, tag: str) -> Version:
210
- raise NotImplementedError
241
+ return ve.parse(tag[1:])
211
242
 
212
243
 
213
244
  class ConcreteImageUpdaterForPrometheusBlackboxExporter(ConcreteImageUpdater):
214
245
 
215
246
  @GeneralUtilities.check_arguments
216
247
  def version_to_tag(self, version: Version) -> str:
217
- raise NotImplementedError
248
+ return f"v{version.major}.{version.minor}.{version.micro}"
218
249
 
219
250
  @GeneralUtilities.check_arguments
220
251
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
221
- raise NotImplementedError
252
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^v\\d+\\.\\d+\\.\\d+$", 999)
253
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
254
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
255
+ return result
222
256
 
223
257
  @GeneralUtilities.check_arguments
224
258
  def get_supported_images(self) -> list[str]:
225
- return [] # TODO
259
+ return ["prom/blackbox-exporter"]
226
260
 
227
261
  @GeneralUtilities.check_arguments
228
262
  def get_version_from_tag(self, image: str, tag: str) -> Version:
229
- raise NotImplementedError
263
+ return ve.parse(tag[1:])
230
264
 
231
265
 
232
266
  class ConcreteImageUpdaterForPrometheusNginxExporter(ConcreteImageUpdater):
233
267
 
234
268
  @GeneralUtilities.check_arguments
235
269
  def version_to_tag(self, version: Version) -> str:
236
- raise NotImplementedError
270
+ return f"v{version.major}.{version.minor}.{version.micro}"
237
271
 
238
272
  @GeneralUtilities.check_arguments
239
273
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
240
- raise NotImplementedError
274
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^v\\d+\\.\\d+\\.\\d+$", 999)
275
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
276
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
277
+ return result
241
278
 
242
279
  @GeneralUtilities.check_arguments
243
280
  def get_supported_images(self) -> list[str]:
244
- return [] # TODO
281
+ return ["prom/nginx-prometheus-exporter"]
245
282
 
246
283
  @GeneralUtilities.check_arguments
247
284
  def get_version_from_tag(self, image: str, tag: str) -> Version:
248
- raise NotImplementedError
285
+ return ve.parse(tag[1:])
249
286
 
250
287
 
251
288
  class ConcreteImageUpdaterForPrometheusNodeExporter(ConcreteImageUpdater):
252
289
 
253
290
  @GeneralUtilities.check_arguments
254
291
  def version_to_tag(self, version: Version) -> str:
255
- raise NotImplementedError
292
+ return f"v{version.major}.{version.minor}.{version.micro}"
256
293
 
257
294
  @GeneralUtilities.check_arguments
258
295
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
259
- raise NotImplementedError
296
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^v\\d+\\.\\d+\\.\\d+$", 999)
297
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
298
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
299
+ return result
260
300
 
261
301
  @GeneralUtilities.check_arguments
262
302
  def get_supported_images(self) -> list[str]:
263
- return [] # TODO
303
+ return ["prom/node-exporter"]
264
304
 
265
305
  @GeneralUtilities.check_arguments
266
306
  def get_version_from_tag(self, image: str, tag: str) -> Version:
267
- raise NotImplementedError
307
+ return ve.parse(tag[1:])
268
308
 
269
309
 
270
310
  class ConcreteImageUpdaterForKeycloak(ConcreteImageUpdater):
@@ -296,7 +336,7 @@ class ConcreteImageUpdaterForMariaDB(ConcreteImageUpdater):
296
336
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
297
337
  versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
298
338
  newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
299
- result = ImageUpdaterHelper.filter_considering_echolog(newer_versions, current_version, version_echolon)
339
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
300
340
  return result
301
341
 
302
342
  @GeneralUtilities.check_arguments
@@ -312,19 +352,22 @@ class ConcreteImageUpdaterForPostgreSQL(ConcreteImageUpdater):
312
352
 
313
353
  @GeneralUtilities.check_arguments
314
354
  def version_to_tag(self, version: Version) -> str:
315
- raise NotImplementedError
355
+ return f"{version.major}.{version.minor}"
316
356
 
317
357
  @GeneralUtilities.check_arguments
318
358
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
319
- raise NotImplementedError
359
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+$", 999)
360
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
361
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
362
+ return result
320
363
 
321
364
  @GeneralUtilities.check_arguments
322
365
  def get_supported_images(self) -> list[str]:
323
- return [] # TODO
366
+ return ["postgres"]
324
367
 
325
368
  @GeneralUtilities.check_arguments
326
369
  def get_version_from_tag(self, image: str, tag: str) -> Version:
327
- raise NotImplementedError
370
+ return ve.parse(tag+".0")
328
371
 
329
372
 
330
373
  class ConcreteImageUpdaterForAdminer(ConcreteImageUpdater):
@@ -337,7 +380,7 @@ class ConcreteImageUpdaterForAdminer(ConcreteImageUpdater):
337
380
  def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
338
381
  versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
339
382
  newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
340
- result = ImageUpdaterHelper.filter_considering_echolog(newer_versions, current_version, version_echolon)
383
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
341
384
  return result
342
385
 
343
386
  @GeneralUtilities.check_arguments
@@ -358,6 +401,7 @@ class ImageUpdater:
358
401
 
359
402
  def add_default_mapper(self) -> None:
360
403
  self.updater.append(ConcreteImageUpdaterForNginx())
404
+ self.updater.append(ConcreteImageUpdaterForWordpress())
361
405
  self.updater.append(ConcreteImageUpdaterForGitLab())
362
406
  self.updater.append(ConcreteImageUpdaterForRegistry())
363
407
  self.updater.append(ConcreteImageUpdaterForPrometheus())
@@ -415,6 +459,7 @@ class ImageUpdater:
415
459
  result = self.get_latest_version_of_image(imagename, version_echolon, existing_version)
416
460
  newest_version = result[0]
417
461
  newest_tag = result[1]
462
+ # TODO write info to console if there is a newwer version available if versionecoholon==latest would have been chosen
418
463
  if existing_version < newest_version:
419
464
 
420
465
  with open(dockercompose_file, 'r', encoding="utf-8") as f:
@@ -33,7 +33,7 @@ from .ProgramRunnerPopen import ProgramRunnerPopen
33
33
  from .ProgramRunnerEpew import ProgramRunnerEpew, CustomEpewArgument
34
34
  from .SCLog import SCLog, LogLevel
35
35
 
36
- version = "3.5.138"
36
+ version = "3.5.140"
37
37
  __version__ = version
38
38
 
39
39
 
@@ -309,7 +309,7 @@ class ScriptCollectionCore:
309
309
 
310
310
  @GeneralUtilities.check_arguments
311
311
  def git_pull_with_retry(self, folder: str, remote: str, localbranchname: str, remotebranchname: str, force: bool = False, amount_of_attempts: int = 5) -> None:
312
- GeneralUtilities.retry_action(lambda: self.git_pull_with_retry(folder, remote, localbranchname, remotebranchname), amount_of_attempts)
312
+ GeneralUtilities.retry_action(lambda: self.git_pull(folder, remote, localbranchname, remotebranchname), amount_of_attempts)
313
313
 
314
314
  @GeneralUtilities.check_arguments
315
315
  def git_pull(self, folder: str, remote: str, localbranchname: str, remotebranchname: str, force: bool = False) -> None:
@@ -688,7 +688,7 @@ class ScriptCollectionCore:
688
688
  raise ValueError(f"Folder '{folder}' does not exist.")
689
689
  git_folder_path = f"{folder}/.git"
690
690
  return self.is_folder(git_folder_path) or self.is_file(git_folder_path)
691
-
691
+
692
692
  @GeneralUtilities.check_arguments
693
693
  def is_bare_git_repository(self, folder: str) -> bool:
694
694
  """This function works platform-independent also for non-local-executions if the ScriptCollection commandline-commands are available as global command on the target-system."""
@@ -697,26 +697,25 @@ class ScriptCollectionCore:
697
697
  if not self.is_folder(folder):
698
698
  raise ValueError(f"Folder '{folder}' does not exist.")
699
699
  return folder.endswith(".git")
700
-
700
+
701
701
  @GeneralUtilities.check_arguments
702
702
  def is_git_or_bare_git_repository(self, folder: str) -> bool:
703
703
  """This function works platform-independent also for non-local-executions if the ScriptCollection commandline-commands are available as global command on the target-system."""
704
- return self.is_git_repository(folder) or self.is_bare_git_repository(folder)
704
+ return self.is_git_repository(folder) or self.is_bare_git_repository(folder)
705
705
 
706
706
  @GeneralUtilities.check_arguments
707
707
  def assert_is_git_repository(self, folder: str) -> str:
708
708
  """This function works platform-independent also for non-local-executions if the ScriptCollection commandline-commands are available as global command on the target-system."""
709
709
  GeneralUtilities.assert_condition(self.is_git_repository(folder), f"'{folder}' is not a git-repository.")
710
710
 
711
-
712
711
  @GeneralUtilities.check_arguments
713
- def convert_git_repository_to_bare_repository(self,repository_folder:str):
714
- repository_folder=repository_folder.replace("\\", "/")
712
+ def convert_git_repository_to_bare_repository(self, repository_folder: str):
713
+ repository_folder = repository_folder.replace("\\", "/")
715
714
  self.assert_is_git_repository(repository_folder)
716
- git_folder=repository_folder+ "/.git"
715
+ git_folder = repository_folder + "/.git"
717
716
  if not self.is_folder(git_folder):
718
717
  raise ValueError(f"Converting '{repository_folder}' to a bare repository not possible. The folder '{git_folder}' does not exist. Converting is currently only supported when the git-folder is a direct folder in a repository and not a reference to another location.")
719
- target_folder:str = repository_folder + ".git"
718
+ target_folder: str = repository_folder + ".git"
720
719
  GeneralUtilities.ensure_directory_exists(target_folder)
721
720
  GeneralUtilities.move_content_of_folder(git_folder, target_folder)
722
721
  GeneralUtilities.ensure_directory_does_not_exist(repository_folder)
@@ -2336,3 +2335,82 @@ TXDX
2336
2335
  @GeneralUtilities.check_arguments
2337
2336
  def install_requirementstxt_file(self, requirements_txt_file: str, folder: str, verbosity: int):
2338
2337
  self.run_program_argsasarray("pip", ["install", "-r", requirements_txt_file], folder, verbosity=verbosity)
2338
+
2339
+ @GeneralUtilities.check_arguments
2340
+ def ocr_analysis_of_folder(self, folder: str, serviceaddress: str, extensions: list[str], languages: list[str]) -> list[str]: # Returns a list of changed files due to ocr-analysis.
2341
+ GeneralUtilities.write_message_to_stdout("Starting OCR analysis of folder " + folder)
2342
+ supported_extensions = ['png', 'jpg', 'jpeg', 'tiff', 'bmp', 'gif', 'pdf', 'docx', 'doc', 'xlsx', 'xls', 'pptx', 'ppt']
2343
+ changes_files: list[str] = []
2344
+ if extensions is None:
2345
+ extensions = supported_extensions
2346
+ for file in GeneralUtilities.get_direct_files_of_folder(folder):
2347
+ file_lower = file.lower()
2348
+ for extension in extensions:
2349
+ if file_lower.endswith("."+extension):
2350
+ if self.ocr_analysis_of_file(file, serviceaddress, languages):
2351
+ changes_files.append(file)
2352
+ break
2353
+ for subfolder in GeneralUtilities.get_direct_folders_of_folder(folder):
2354
+ for file in self.ocr_analysis_of_folder(subfolder, serviceaddress, extensions, languages):
2355
+ changes_files.append(file)
2356
+ return changes_files
2357
+
2358
+ @GeneralUtilities.check_arguments
2359
+ def ocr_analysis_of_file(self, file: str, serviceaddress: str, languages: list[str]) -> bool: # Returns true if the ocr-file was generated or updated. Returns false if the existing ocr-file was not changed.
2360
+ GeneralUtilities.write_message_to_stdout("Do OCR analysis of file " + file)
2361
+ supported_extensions = ['png', 'jpg', 'jpeg', 'tiff', 'bmp', 'webp', 'gif', 'pdf', 'rtf', 'docx', 'doc', 'odt', 'xlsx', 'xls', 'ods', 'pptx', 'ppt', 'odp']
2362
+ for extension in supported_extensions:
2363
+ if file.lower().endswith("."+extension):
2364
+ raise ValueError(f"Extension '{extension}' is not supported. Supported extensions are: {', '.join(supported_extensions)}")
2365
+ target_file = file+".ocr.txt"
2366
+ hash_of_current_file: str = GeneralUtilities. get_sha256_of_file(file)
2367
+ if os.path.isfile(target_file):
2368
+ lines = GeneralUtilities.read_lines_from_file(target_file)
2369
+ previous_hash_of_current_file: str = lines[1].split(":")[1].strip()
2370
+ if hash_of_current_file == previous_hash_of_current_file:
2371
+ return False
2372
+ ocr_content = self.get_ocr_content_of_file(file, serviceaddress, languages)
2373
+ GeneralUtilities.ensure_file_exists(target_file)
2374
+ GeneralUtilities.write_text_to_file(file, f"""Name of file: \"{os.path.basename(file)}\""
2375
+ Hash of file: {hash_of_current_file}
2376
+ OCR-content:
2377
+ \"{ocr_content}\"""")
2378
+ return True
2379
+
2380
+ @GeneralUtilities.check_arguments
2381
+ def get_ocr_content_of_file(self, file: str, serviceaddress: str, languages: list[str]) -> str: # serviceaddress = None means local executable
2382
+ result: str = None
2383
+ extension = Path(file).suffix
2384
+ if serviceaddress is None:
2385
+ program_result = self.run_program_argsasarray("simpleocr", ["--File", file, "--Languages", "+".join(languages)] + languages)
2386
+ result = program_result[1]
2387
+ else:
2388
+ languages_for_url = '%2B'.join(languages)
2389
+ package_url: str = f"https://{serviceaddress}/GetOCRContent?languages={languages_for_url}&fileType={extension}"
2390
+ headers = {'Cache-Control': 'no-cache'}
2391
+ r = requests.put(package_url, timeout=5, headers=headers, data=GeneralUtilities.read_binary_from_file(file))
2392
+ if r.status_code != 200:
2393
+ raise ValueError(f"Checking for latest tor package resulted in HTTP-response-code {r.status_code}.")
2394
+ result = GeneralUtilities.bytes_to_string(r.content)
2395
+ return result
2396
+
2397
+ @GeneralUtilities.check_arguments
2398
+ def ocr_analysis_of_repository(self, folder: str, serviceaddress: str, extensions: list[str], languages: list[str]) -> None:
2399
+ self.assert_is_git_repository(folder)
2400
+ changed_files = self.ocr_analysis_of_folder(folder, serviceaddress, extensions, languages)
2401
+ for changed_ocr_file in changed_files:
2402
+ GeneralUtilities.assert_condition(changed_ocr_file.endswith(".ocr.txt"), f"File '{changed_ocr_file}' is not an OCR-file. It should end with '.ocr.txt'.")
2403
+ base_file = changed_ocr_file[:-len(".ocr.txt")]
2404
+ GeneralUtilities.assert_condition(os.path.isfile(base_file), f"Base file '{base_file}' does not exist. The OCR-file '{changed_ocr_file}' is not valid.")
2405
+ base_file_relative_path = os.path.relpath(base_file, folder)
2406
+ base_file_diff_program_result = self.run_program("git", f"diff --quiet -- \"{base_file_relative_path}\"", folder, throw_exception_if_exitcode_is_not_zero=False)
2407
+ has_staged_changes: bool = None
2408
+ if base_file_diff_program_result[0] == 0:
2409
+ has_staged_changes = False
2410
+ elif base_file_diff_program_result[0] == 1:
2411
+ has_staged_changes = True
2412
+ else:
2413
+ raise RuntimeError(f"Unexpected exit code {base_file_diff_program_result[0]} when checking for staged changes of file '{base_file_relative_path}'.")
2414
+ if has_staged_changes:
2415
+ changed_ocr_file_relative_path = os.path.relpath(changed_ocr_file, folder)
2416
+ self.run_program_argsasarray("git", ["add", changed_ocr_file_relative_path], folder)
@@ -2360,6 +2360,7 @@ class TasksForCommonProjectStructure:
2360
2360
  tasks.sort(key=lambda x: x["label"].split("/")[-1], reverse=False) # sort by the label of the task
2361
2361
  for task in tasks:
2362
2362
  if task["type"] == "shell":
2363
+
2363
2364
  description: str = task["label"]
2364
2365
  name: str = GeneralUtilities.to_pascal_case(description)
2365
2366
  command = task["command"]
@@ -2381,11 +2382,18 @@ class TasksForCommonProjectStructure:
2381
2382
  if len(args) > 1:
2382
2383
  command_with_args = f"{command_with_args} {' '.join(args)}"
2383
2384
 
2385
+ if "description" in task:
2386
+ additional_description = task["description"]
2387
+ description = f"{description} ({additional_description})"
2388
+
2384
2389
  if append_cli_args_at_end:
2385
2390
  command_with_args = f"{command_with_args} {{{{.CLI_ARGS}}}}"
2386
- cwd_literal = cwd.replace("\\", "\\\\")
2391
+
2392
+ cwd_literal = cwd.replace("\\", "\\\\").replace('"', '\\"') # escape backslashes and double quotes for YAML
2393
+ description_literal = description.replace("\\", "\\\\").replace('"', '\\"') # escape backslashes and double quotes for YAML
2394
+
2387
2395
  lines.append(f" {name}:")
2388
- lines.append(f' desc: "{description}"')
2396
+ lines.append(f' desc: "{description_literal}"')
2389
2397
  lines.append(' silent: true')
2390
2398
  lines.append(f' dir: "{cwd_literal}"')
2391
2399
  lines.append(" cmds:")
@@ -2397,6 +2405,7 @@ class TasksForCommonProjectStructure:
2397
2405
  for alias in aliases:
2398
2406
  lines.append(f' - {alias}')
2399
2407
  lines.append(GeneralUtilities.empty_string)
2408
+
2400
2409
  GeneralUtilities.write_lines_to_file(task_file, lines)
2401
2410
 
2402
2411
  @GeneralUtilities.check_arguments
@@ -2560,6 +2569,14 @@ class TasksForCommonProjectStructure:
2560
2569
  from_day = datetime(now.year, now.month, now.day, 0, 0, 0)
2561
2570
  self.mark_current_version_as_supported(repository_folder, project_version, from_day, until_day)
2562
2571
 
2572
+ package_json_file = os.path.join(repository_folder, "package.json")
2573
+ if os.path.isfile(package_json_file):
2574
+ with open(package_json_file, "r", encoding="utf-8") as f1:
2575
+ package_json_data = json.load(f1)
2576
+ package_json_data["version"] = project_version
2577
+ with open(package_json_file, "w", encoding="utf-8") as f2:
2578
+ json.dump(package_json_data, f2, indent=2)
2579
+
2563
2580
  project_resources_folder = os.path.join(repository_folder, "Other", "Scripts")
2564
2581
  PrepareBuildCodeunits_script_name = "PrepareBuildCodeunits.py"
2565
2582
  prepare_build_codeunits_scripts = os.path.join(project_resources_folder, PrepareBuildCodeunits_script_name)
@@ -3115,7 +3132,7 @@ class TasksForCommonProjectStructure:
3115
3132
  # TODO validate artifactsinformation_file against xsd
3116
3133
  GeneralUtilities.write_message_to_stdout(f"Finished building codeunit {codeunit_name} without errors.")
3117
3134
 
3118
- def __add_changelog_file(self, repository_folder: str, version_of_project: str):
3135
+ def __ensure_changelog_file_is_added(self, repository_folder: str, version_of_project: str):
3119
3136
  changelog_file = os.path.join(repository_folder, "Other", "Resources", "Changelog", f"v{version_of_project}.md")
3120
3137
  if not os.path.isfile(changelog_file):
3121
3138
  GeneralUtilities.ensure_file_exists(changelog_file)
@@ -3136,16 +3153,16 @@ class TasksForCommonProjectStructure:
3136
3153
  target_environmenttype = "QualityCheck"
3137
3154
  project_name: str = os.path.basename(repository_folder)
3138
3155
  GeneralUtilities.assert_condition(not self.__sc.git_repository_has_uncommitted_changes(repository_folder), "There are uncommitted changes in the repository.")
3156
+ self.build_codeunits(repository_folder, target_environmenttype=target_environmenttype, do_git_clean_when_no_changes=True, note="Prepare dependency-update") # Required because update dependencies is not always possible for not-buildet codeunits (depends on the programming language or package manager)
3139
3157
 
3140
3158
  # update dependencies of resources
3141
3159
  global_scripts_folder = os.path.join(repository_folder, "Other", "Scripts")
3142
3160
  if os.path.isfile(os.path.join(global_scripts_folder, update_dependencies_script_filename)):
3143
- self.build_codeunits(repository_folder, target_environmenttype=target_environmenttype, do_git_clean_when_no_changes=True, note="Prepare dependency-update") # Required because update dependencies is not always possible for not-buildet codeunits (depends on the programming language or package manager)
3144
3161
  self.__sc.run_program("python", update_dependencies_script_filename, global_scripts_folder, print_live_output=True)
3145
3162
  version_of_project = self.get_version_of_project(repository_folder)
3146
- self.__add_changelog_file(repository_folder, version_of_project)
3163
+ self.__ensure_changelog_file_is_added(repository_folder, version_of_project)
3147
3164
  GeneralUtilities.write_message_to_stdout(f"Updated global dependencies of {project_name}.")
3148
- self.build_codeunits(repository_folder, verbosity, "QualityCheck", None, False, None, [], False, "Build codeunits due to updated dependencies")
3165
+ self.build_codeunits(repository_folder, verbosity, "QualityCheck", None, False, None, [], False, "Build codeunits due to updated product-wide dependencies")
3149
3166
 
3150
3167
  # update dependencies of codeunits
3151
3168
  for codeunit in codeunits:
@@ -3160,10 +3177,10 @@ class TasksForCommonProjectStructure:
3160
3177
  self.__sc.run_program("python", update_dependencies_script_filename, update_dependencies_script_folder, verbosity, print_live_output=True)
3161
3178
  if self.__sc.git_repository_has_uncommitted_changes(repository_folder):
3162
3179
  version_of_project = self.get_version_of_project(repository_folder)
3163
- self.__add_changelog_file(repository_folder, version_of_project)
3180
+ self.__ensure_changelog_file_is_added(repository_folder, version_of_project)
3164
3181
  GeneralUtilities.write_message_to_stdout(f"Updated dependencies in codeunit {codeunit}.")
3165
3182
 
3166
- self.build_codeunits(repository_folder, verbosity, "QualityCheck", None, False, None, [], False, "Build codeunits due to updated dependencies")
3183
+ self.build_codeunits(repository_folder, verbosity, "QualityCheck", None, False, None, [], False, "Build all codeunits due to updated dependencies")
3167
3184
  self.__sc.git_commit(repository_folder, "Updated dependencies")
3168
3185
 
3169
3186
  class GenericPrepareNewReleaseArguments:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ScriptCollection
3
- Version: 3.5.138
3
+ Version: 3.5.140
4
4
  Summary: The ScriptCollection is the place for reusable scripts.
5
5
  Home-page: https://github.com/anionDev/ScriptCollection
6
6
  Author: Marius Göcke
@@ -23,7 +23,7 @@ Classifier: Topic :: Utilities
23
23
  Requires-Python: >=3.10
24
24
  Description-Content-Type: text/markdown
25
25
  Requires-Dist: build>=1.2.2.post1
26
- Requires-Dist: coverage>=7.8.2
26
+ Requires-Dist: coverage>=7.9.1
27
27
  Requires-Dist: cyclonedx-bom>=6.1.1
28
28
  Requires-Dist: defusedxml>=0.7.1
29
29
  Requires-Dist: keyboard>=0.13.5
@@ -37,12 +37,12 @@ Requires-Dist: Pygments>=2.19.1
37
37
  Requires-Dist: pylint>=3.3.7
38
38
  Requires-Dist: pyOpenSSL>=25.1.0
39
39
  Requires-Dist: PyPDF>=5.6.0
40
- Requires-Dist: pytest>=8.4.0
40
+ Requires-Dist: pytest>=8.4.1
41
41
  Requires-Dist: PyYAML>=6.0.2
42
42
  Requires-Dist: qrcode>=8.2
43
43
  Requires-Dist: send2trash>=1.8.3
44
44
  Requires-Dist: twine>=6.1.0
45
- Requires-Dist: xmlschema>=4.0.1
45
+ Requires-Dist: xmlschema>=4.1.0
46
46
 
47
47
  # ScriptCollection
48
48
 
@@ -1,17 +1,17 @@
1
1
  ScriptCollection/CertificateUpdater.py,sha256=OAxrG21k_o3W3niOOGNSZzUPJlvolOWc1lRB2dMhc3g,9212
2
- ScriptCollection/Executables.py,sha256=Sbi0cuHnd8PYROA9HcHQ00KJV805LtFhyiEl5PgHoIQ,32672
2
+ ScriptCollection/Executables.py,sha256=d9QkQuaS0nxTwNUGxtqKUk9dJqT5gS670LNuCz2YMw0,35349
3
3
  ScriptCollection/GeneralUtilities.py,sha256=PKdzq382RYVSWeSG_6z6FiHu-SiTOi2BavJKvP-0slU,47308
4
- ScriptCollection/ImageUpdater.py,sha256=lel1nevTN7fgdoCDE6Xg8YY6XPsZaOQiCIyWXbmVnh0,20882
4
+ ScriptCollection/ImageUpdater.py,sha256=3B5pgAMnyne3ZogWz-suqglZGIB9FAKyWn31P9E1ST0,24491
5
5
  ScriptCollection/ProcessesRunner.py,sha256=3mu4ZxzZleQo0Op6o9EYTCFiJfb6kx5ov2YfZfT89mU,1395
6
6
  ScriptCollection/ProgramRunnerBase.py,sha256=2kMIAqdc65UjBAddOZkzy_aFx9h5roZ5a4bQNM6RV6Y,2480
7
7
  ScriptCollection/ProgramRunnerEpew.py,sha256=4pjEd0r9Fcz3TTDv0MdTSd5KkigYXcWUVI1X43regfU,6477
8
8
  ScriptCollection/ProgramRunnerPopen.py,sha256=BPY7-ZMIlqT7JOKz8qlB5c0laF2Js-ijzqk09GxZC48,3821
9
9
  ScriptCollection/SCLog.py,sha256=Dd2P8vH2PA830wAv6bchlMHHdGE_7At-F4WQY5w4XdA,4016
10
- ScriptCollection/ScriptCollectionCore.py,sha256=pzQFBJgNTZ9a_2Qqt2AoyTEbYkH61HwsPFvgBezOaMc,131120
11
- ScriptCollection/TasksForCommonProjectStructure.py,sha256=b3bb3kSHSce3HgaitDyuZdQ82KroU1HFryANBycKo0I,235854
10
+ ScriptCollection/ScriptCollectionCore.py,sha256=RRwHiyjOKelBW0a52t4goDjm0Nm5yOz-r3grzkEPZRc,136482
11
+ ScriptCollection/TasksForCommonProjectStructure.py,sha256=Aln85JyVrspQmluNyNSFT4gPycVt42W_4dM21qys13c,236756
12
12
  ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- scriptcollection-3.5.138.dist-info/METADATA,sha256=hlsJXzIOxELAE14nqRobP7vzTmVQeRqWmBOC6U2y5q4,7694
14
- scriptcollection-3.5.138.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- scriptcollection-3.5.138.dist-info/entry_points.txt,sha256=AhXNaa8RMBRFO_4TzKi9jc32C-ZNIiU12mRGu6wEpBk,3796
16
- scriptcollection-3.5.138.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
17
- scriptcollection-3.5.138.dist-info/RECORD,,
13
+ scriptcollection-3.5.140.dist-info/METADATA,sha256=h47YkJZXJr_B_LQt2qF2bEJnHxtoc6Us-cKkxiVxBts,7694
14
+ scriptcollection-3.5.140.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ scriptcollection-3.5.140.dist-info/entry_points.txt,sha256=AvmVO9iyWImExpvzL3YYQ9AOEiUIN9guPRRG_W_VNWY,4116
16
+ scriptcollection-3.5.140.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
17
+ scriptcollection-3.5.140.dist-info/RECORD,,
@@ -35,6 +35,9 @@ sclistfoldercontent = ScriptCollection.Executables:ListFolderContent
35
35
  scmergepdfs = ScriptCollection.Executables:MergePDFs
36
36
  scnpmi = ScriptCollection.Executables:NpmI
37
37
  scobfuscatefilesfolder = ScriptCollection.Executables:ObfuscateFilesFolder
38
+ scocranalysisoffile = ScriptCollection.Executables:OCRAnalysisOfFile
39
+ scocranalysisoffolder = ScriptCollection.Executables:OCRAnalysisOfFolder
40
+ scocranalysisofrepository = ScriptCollection.Executables:OCRAnalysisOfRepository
38
41
  scorganizelinesinfile = ScriptCollection.Executables:OrganizeLinesInFile
39
42
  scpdftoimage = ScriptCollection.Executables:PDFToImage
40
43
  scprintcurrentworkingdirectory = ScriptCollection.Executables:PrintCurrecntWorkingDirectory
@@ -51,5 +54,6 @@ scsetcontentoffile = ScriptCollection.Executables:SetContentOfFile
51
54
  scshow2faasqrcode = ScriptCollection.Executables:Show2FAAsQRCode
52
55
  scshowmissingfiles = ScriptCollection.Executables:ShowMissingFiles
53
56
  scsigncertificate = ScriptCollection.Executables:SignCertificate
57
+ scupdateimagesindockercomposefile = ScriptCollection.Executables:UpdateImagesInDockerComposeFile
54
58
  scupdatenugetpackagesincsharpproject = ScriptCollection.Executables:UpdateNugetpackagesInCsharpProject
55
59
  scuploadfile = ScriptCollection.Executables:UploadFile