bluer-objects 6.104.1__py3-none-any.whl → 6.464.1__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.
Files changed (170) hide show
  1. bluer_objects/.abcli/abcli.sh +6 -0
  2. bluer_objects/.abcli/alias.sh +13 -6
  3. bluer_objects/.abcli/assets/cd.sh +20 -0
  4. bluer_objects/.abcli/assets/mv.sh +34 -0
  5. bluer_objects/.abcli/assets/publish.sh +40 -0
  6. bluer_objects/.abcli/assets.sh +15 -0
  7. bluer_objects/.abcli/create_test_asset.sh +10 -0
  8. bluer_objects/.abcli/download.sh +3 -1
  9. bluer_objects/.abcli/file.sh +15 -4
  10. bluer_objects/.abcli/gif.sh +18 -0
  11. bluer_objects/.abcli/host.sh +23 -7
  12. bluer_objects/.abcli/ls.sh +19 -8
  13. bluer_objects/.abcli/metadata/download.sh +9 -0
  14. bluer_objects/.abcli/metadata/edit.sh +15 -0
  15. bluer_objects/.abcli/metadata/upload.sh +9 -0
  16. bluer_objects/.abcli/mlflow/browse.sh +2 -0
  17. bluer_objects/.abcli/mlflow/deploy.sh +21 -5
  18. bluer_objects/.abcli/mlflow/lock/lock.sh +11 -0
  19. bluer_objects/.abcli/mlflow/lock/unlock.sh +12 -0
  20. bluer_objects/.abcli/mlflow/lock.sh +15 -0
  21. bluer_objects/.abcli/mlflow/tags/search.sh +1 -5
  22. bluer_objects/.abcli/mlflow.sh +0 -2
  23. bluer_objects/.abcli/pdf/convert.sh +92 -0
  24. bluer_objects/.abcli/pdf.sh +15 -0
  25. bluer_objects/.abcli/storage/clear.sh +2 -0
  26. bluer_objects/.abcli/tests/clone.sh +2 -3
  27. bluer_objects/.abcli/tests/create_test_asset.sh +16 -0
  28. bluer_objects/.abcli/tests/file.sh +64 -0
  29. bluer_objects/.abcli/tests/gif.sh +3 -3
  30. bluer_objects/.abcli/tests/help.sh +23 -7
  31. bluer_objects/.abcli/tests/ls.sh +11 -4
  32. bluer_objects/.abcli/tests/metadata.sh +35 -0
  33. bluer_objects/.abcli/tests/mlflow_lock.sh +30 -0
  34. bluer_objects/.abcli/tests/mlflow_tags.sh +1 -1
  35. bluer_objects/.abcli/tests/open.sh +11 -0
  36. bluer_objects/.abcli/tests/open_gif_open.sh +14 -0
  37. bluer_objects/.abcli/tests/pdf.sh +39 -0
  38. bluer_objects/.abcli/tests/storage_clear.sh +11 -0
  39. bluer_objects/.abcli/tests/storage_public_upload.sh +25 -0
  40. bluer_objects/.abcli/tests/storage_status.sh +12 -0
  41. bluer_objects/.abcli/tests/{storage.sh → storage_upload_download.sh} +26 -8
  42. bluer_objects/.abcli/upload.sh +26 -2
  43. bluer_objects/README/__init__.py +7 -22
  44. bluer_objects/README/alias.py +67 -0
  45. bluer_objects/README/build/__init__.py +0 -0
  46. bluer_objects/README/build/aliases.py +23 -0
  47. bluer_objects/README/build/docs.py +23 -0
  48. bluer_objects/README/build/modules.py +9 -0
  49. bluer_objects/README/consts.py +44 -0
  50. bluer_objects/README/functions.py +154 -204
  51. bluer_objects/README/items.py +78 -6
  52. bluer_objects/README/process/__init__.py +0 -0
  53. bluer_objects/README/process/assets.py +36 -0
  54. bluer_objects/README/process/details.py +20 -0
  55. bluer_objects/README/process/envs.py +23 -0
  56. bluer_objects/README/process/help.py +27 -0
  57. bluer_objects/README/process/include.py +40 -0
  58. bluer_objects/README/process/legacy.py +21 -0
  59. bluer_objects/README/process/mermaid.py +20 -0
  60. bluer_objects/README/process/national_internet.py +55 -0
  61. bluer_objects/README/process/objects.py +32 -0
  62. bluer_objects/README/process/signature.py +35 -0
  63. bluer_objects/README/process/title.py +44 -0
  64. bluer_objects/README/process/variables.py +12 -0
  65. bluer_objects/__init__.py +1 -1
  66. bluer_objects/assets/__init__.py +0 -0
  67. bluer_objects/assets/__main__.py +57 -0
  68. bluer_objects/assets/functions.py +62 -0
  69. bluer_objects/config.env +13 -1
  70. bluer_objects/env.py +27 -1
  71. bluer_objects/file/__main__.py +52 -7
  72. bluer_objects/file/functions.py +21 -4
  73. bluer_objects/file/load.py +2 -9
  74. bluer_objects/file/save.py +17 -24
  75. bluer_objects/graphics/__main__.py +7 -0
  76. bluer_objects/graphics/gif.py +11 -7
  77. bluer_objects/graphics/screen.py +9 -8
  78. bluer_objects/help/assets.py +93 -0
  79. bluer_objects/help/create_test_asset.py +22 -0
  80. bluer_objects/help/download.py +17 -3
  81. bluer_objects/help/file.py +59 -0
  82. bluer_objects/help/functions.py +9 -1
  83. bluer_objects/help/gif.py +25 -0
  84. bluer_objects/help/host.py +6 -4
  85. bluer_objects/help/ls.py +26 -3
  86. bluer_objects/help/metadata.py +51 -0
  87. bluer_objects/help/mlflow/__init__.py +23 -2
  88. bluer_objects/help/mlflow/cache.py +2 -4
  89. bluer_objects/help/mlflow/lock.py +52 -0
  90. bluer_objects/help/mlflow/tags.py +34 -23
  91. bluer_objects/help/pdf.py +67 -0
  92. bluer_objects/help/upload.py +10 -3
  93. bluer_objects/host/functions.py +4 -1
  94. bluer_objects/logger/confusion_matrix.py +76 -0
  95. bluer_objects/logger/image.py +110 -0
  96. bluer_objects/logger/stitch.py +107 -0
  97. bluer_objects/markdown.py +8 -6
  98. bluer_objects/metadata/__init__.py +1 -0
  99. bluer_objects/metadata/flatten.py +27 -0
  100. bluer_objects/mlflow/__init__.py +1 -1
  101. bluer_objects/mlflow/__main__.py +49 -31
  102. bluer_objects/mlflow/lock/__init__.py +1 -0
  103. bluer_objects/mlflow/lock/__main__.py +58 -0
  104. bluer_objects/mlflow/lock/functions.py +121 -0
  105. bluer_objects/mlflow/logging.py +53 -41
  106. bluer_objects/mlflow/models.py +7 -0
  107. bluer_objects/mlflow/objects.py +7 -0
  108. bluer_objects/mlflow/runs.py +10 -1
  109. bluer_objects/mlflow/serverless/__init__.py +3 -0
  110. bluer_objects/mlflow/serverless/api.py +88 -0
  111. bluer_objects/mlflow/serverless/read.py +19 -0
  112. bluer_objects/mlflow/serverless/search.py +35 -0
  113. bluer_objects/mlflow/serverless/write.py +42 -0
  114. bluer_objects/mlflow/tags.py +59 -9
  115. bluer_objects/objects.py +3 -1
  116. bluer_objects/pdf/__init__.py +1 -0
  117. bluer_objects/pdf/__main__.py +78 -0
  118. bluer_objects/pdf/convert/__init__.py +0 -0
  119. bluer_objects/pdf/convert/batch.py +54 -0
  120. bluer_objects/pdf/convert/combination.py +32 -0
  121. bluer_objects/pdf/convert/convert.py +110 -0
  122. bluer_objects/pdf/convert/image.py +53 -0
  123. bluer_objects/pdf/convert/md.py +97 -0
  124. bluer_objects/pdf/convert/missing.py +96 -0
  125. bluer_objects/pdf/convert/pdf.py +37 -0
  126. bluer_objects/sample.env +6 -0
  127. bluer_objects/storage/WebDAV.py +11 -7
  128. bluer_objects/storage/WebDAVrequest.py +360 -0
  129. bluer_objects/storage/WebDAVzip.py +26 -29
  130. bluer_objects/storage/__init__.py +28 -1
  131. bluer_objects/storage/__main__.py +40 -6
  132. bluer_objects/storage/base.py +84 -5
  133. bluer_objects/storage/policies.py +7 -0
  134. bluer_objects/storage/s3.py +367 -0
  135. bluer_objects/testing/__main__.py +6 -0
  136. bluer_objects/tests/test_README_consts.py +71 -0
  137. bluer_objects/tests/test_README_items.py +128 -0
  138. bluer_objects/tests/test_alias.py +33 -0
  139. bluer_objects/tests/test_env.py +42 -7
  140. bluer_objects/tests/test_file_download.py +30 -0
  141. bluer_objects/tests/test_file_load_save.py +1 -2
  142. bluer_objects/tests/test_file_load_save_text.py +46 -0
  143. bluer_objects/tests/test_graphics_gif.py +2 -0
  144. bluer_objects/tests/test_log_image_grid.py +29 -0
  145. bluer_objects/tests/test_logger_confusion_matrix.py +18 -0
  146. bluer_objects/tests/test_logger_matrix.py +2 -2
  147. bluer_objects/tests/test_logger_stitch_images.py +47 -0
  148. bluer_objects/tests/test_metadata.py +12 -6
  149. bluer_objects/tests/test_metadata_flatten.py +109 -0
  150. bluer_objects/tests/test_mlflow.py +114 -5
  151. bluer_objects/tests/test_mlflow_lock.py +26 -0
  152. bluer_objects/tests/test_objects.py +2 -0
  153. bluer_objects/tests/test_shell.py +34 -0
  154. bluer_objects/tests/test_storage.py +8 -21
  155. bluer_objects/tests/test_storage_base.py +39 -0
  156. bluer_objects/tests/test_storage_s3.py +67 -0
  157. bluer_objects/tests/test_storage_webdav_request.py +75 -0
  158. bluer_objects/tests/test_storage_webdav_zip.py +42 -0
  159. bluer_objects/tests/test_web_is_accessible.py +11 -0
  160. {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/METADATA +20 -11
  161. bluer_objects-6.464.1.dist-info/RECORD +228 -0
  162. {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/WHEEL +1 -1
  163. bluer_objects/.abcli/storage/download_file.sh +0 -9
  164. bluer_objects/.abcli/storage/exists.sh +0 -8
  165. bluer_objects/.abcli/storage/list.sh +0 -8
  166. bluer_objects/.abcli/storage/rm.sh +0 -11
  167. bluer_objects/.abcli/tests/mlflow_test.sh +0 -7
  168. bluer_objects-6.104.1.dist-info/RECORD +0 -143
  169. {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/licenses/LICENSE +0 -0
  170. {bluer_objects-6.104.1.dist-info → bluer_objects-6.464.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,42 @@
1
+ from typing import Dict, Union
2
+
3
+ from blueness import module
4
+ from bluer_options.options import Options
5
+
6
+ from bluer_objects import NAME
7
+ from bluer_objects.mlflow.serverless import api
8
+ from bluer_objects.logger import logger
9
+
10
+ NAME = module.name(__file__, NAME)
11
+
12
+
13
+ def set_tags(
14
+ object_name: str,
15
+ tags: Union[str, Dict[str, str]],
16
+ log: bool = True,
17
+ verbose: bool = False,
18
+ icon="#️⃣ ",
19
+ ) -> bool:
20
+ tags = Options(tags)
21
+
22
+ if not api.write(
23
+ object_name="_serverless_objects",
24
+ filename=f"{object_name}.yaml",
25
+ data=tags,
26
+ log=verbose,
27
+ ):
28
+ return False
29
+
30
+ for key, value in tags.items():
31
+ if not api.write(
32
+ object_name="_serverless_keys",
33
+ filename=f"{key}.yaml",
34
+ data={object_name: value},
35
+ log=verbose,
36
+ ):
37
+ return False
38
+
39
+ if log:
40
+ logger.info("{} {}.{}={}".format(icon, object_name, key, value))
41
+
42
+ return True
@@ -7,13 +7,17 @@ from bluer_options.options import Options
7
7
  from bluer_options.logger import crash_report
8
8
 
9
9
  from bluer_objects import NAME
10
+ from bluer_objects import env
11
+ from bluer_objects.mlflow import serverless
10
12
  from bluer_objects.mlflow.objects import to_experiment_name, to_object_name
11
13
  from bluer_objects.logger import logger
12
14
 
13
15
  NAME = module.name(__file__, NAME)
14
16
 
15
17
 
16
- def create_filter_string(tags: str) -> str:
18
+ def create_server_style_filter_string(
19
+ tags: Union[str, dict],
20
+ ) -> str:
17
21
  tags_options = Options(tags)
18
22
 
19
23
  # https://www.mlflow.org/docs/latest/search-experiments.html
@@ -25,7 +29,14 @@ def create_filter_string(tags: str) -> str:
25
29
  def get_tags(
26
30
  object_name: str,
27
31
  exclude_system_tags: bool = True,
32
+ verbose: bool = False,
28
33
  ) -> Tuple[bool, Dict[str, str]]:
34
+ if env.MLFLOW_IS_SERVERLESS:
35
+ return serverless.get_tags(
36
+ object_name,
37
+ verbose=verbose,
38
+ )
39
+
29
40
  experiment_name = to_experiment_name(object_name)
30
41
 
31
42
  try:
@@ -48,16 +59,45 @@ def get_tags(
48
59
 
49
60
 
50
61
  # https://www.mlflow.org/docs/latest/search-experiments.html
51
- def search(filter_string: str) -> List[str]:
52
- client = MlflowClient()
53
-
54
- return [
55
- to_object_name(experiment.name)
56
- for experiment in client.search_experiments(
62
+ def search(
63
+ filter_string: Union[str, dict],
64
+ server_style: bool = False,
65
+ verbose: bool = False,
66
+ ) -> Tuple[bool, List[str]]:
67
+ if server_style and env.MLFLOW_IS_SERVERLESS:
68
+ logger.error("server_style search is not supported when mlflow is serverless.")
69
+ return False, []
70
+
71
+ if filter_string == "-":
72
+ filter_string = ""
73
+
74
+ if env.MLFLOW_IS_SERVERLESS:
75
+ return serverless.search(
57
76
  filter_string=filter_string,
58
- view_type=ViewType.ALL,
77
+ log=verbose,
59
78
  )
60
- ]
79
+
80
+ if not server_style:
81
+ filter_string = create_server_style_filter_string(filter_string)
82
+
83
+ client = MlflowClient()
84
+
85
+ success = False
86
+ output = []
87
+
88
+ try:
89
+ output = [
90
+ to_object_name(experiment.name)
91
+ for experiment in client.search_experiments(
92
+ filter_string=filter_string,
93
+ view_type=ViewType.ALL,
94
+ )
95
+ ]
96
+ success = True
97
+ except Exception as e:
98
+ logger.error(e)
99
+
100
+ return success, output
61
101
 
62
102
 
63
103
  def set_tags(
@@ -65,7 +105,17 @@ def set_tags(
65
105
  tags: Union[str, Dict[str, str]],
66
106
  log: bool = True,
67
107
  icon="#️⃣ ",
108
+ verbose: bool = False,
68
109
  ) -> bool:
110
+ if env.MLFLOW_IS_SERVERLESS:
111
+ return serverless.set_tags(
112
+ object_name,
113
+ tags=tags,
114
+ log=log,
115
+ verbose=verbose,
116
+ icon=icon,
117
+ )
118
+
69
119
  experiment_name = to_experiment_name(object_name)
70
120
 
71
121
  try:
bluer_objects/objects.py CHANGED
@@ -69,6 +69,7 @@ def signature(info=None, object_name="."):
69
69
  def unique_object(
70
70
  prefix: str = "",
71
71
  include_time: bool = True,
72
+ log: bool = True,
72
73
  ):
73
74
  object_name = string.pretty_date(
74
75
  as_filename=True,
@@ -80,6 +81,7 @@ def unique_object(
80
81
 
81
82
  path.create(object_path(object_name))
82
83
 
83
- logger.info(f"📂 {object_name}")
84
+ if log:
85
+ logger.info(f"📂 {object_name}")
84
86
 
85
87
  return object_name
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,78 @@
1
+ import argparse
2
+
3
+ from blueness import module
4
+ from blueness.argparse.generic import sys_exit
5
+
6
+ from bluer_objects import NAME
7
+ from bluer_objects.pdf.convert.batch import batch as batch_convert
8
+ from bluer_objects.pdf.convert.convert import convert
9
+ from bluer_objects.logger import logger
10
+
11
+ NAME = module.name(__file__, NAME)
12
+
13
+ parser = argparse.ArgumentParser(NAME)
14
+ parser.add_argument(
15
+ "task",
16
+ type=str,
17
+ help="convert",
18
+ )
19
+ parser.add_argument(
20
+ "--path_prefix",
21
+ type=str,
22
+ )
23
+ parser.add_argument(
24
+ "--object_name",
25
+ type=str,
26
+ )
27
+ parser.add_argument(
28
+ "--suffixes",
29
+ type=str,
30
+ )
31
+ parser.add_argument(
32
+ "--combine",
33
+ type=int,
34
+ default=0,
35
+ help="0 | 1",
36
+ )
37
+
38
+ parser.add_argument(
39
+ "--count",
40
+ type=int,
41
+ default=-1,
42
+ help="-1: all",
43
+ )
44
+ parser.add_argument(
45
+ "--list_missing",
46
+ type=int,
47
+ default=1,
48
+ help="0 | 1",
49
+ )
50
+ parser.add_argument(
51
+ "--use_metadata",
52
+ type=int,
53
+ default=0,
54
+ help="0 | 1",
55
+ )
56
+ args = parser.parse_args()
57
+
58
+ success = False
59
+ if args.task == "convert":
60
+ if args.use_metadata == 1:
61
+ success = batch_convert(
62
+ object_name=args.object_name,
63
+ combine=args.combine == 1,
64
+ count=args.count,
65
+ list_missing=args.list_missing == 1,
66
+ )
67
+ else:
68
+ success = convert(
69
+ path_prefix=args.path_prefix,
70
+ list_of_suffixes=args.suffixes.split(","),
71
+ object_name=args.object_name,
72
+ combine=args.combine == 1,
73
+ count=args.count,
74
+ )
75
+ else:
76
+ success = None
77
+
78
+ sys_exit(logger, NAME, args.task, success)
File without changes
@@ -0,0 +1,54 @@
1
+ from typing import List
2
+
3
+ from blueness import module
4
+
5
+ from bluer_objects import NAME
6
+ from bluer_objects.metadata import get_from_object
7
+ from bluer_objects.pdf.convert.convert import convert
8
+ from bluer_objects.pdf.convert.missing import list_missing_docs
9
+ from bluer_objects.env import abcli_path_git
10
+ from bluer_objects.logger import logger
11
+
12
+
13
+ NAME = module.name(__file__, NAME)
14
+
15
+
16
+ def batch(
17
+ object_name: str,
18
+ combine: bool,
19
+ count: int = -1,
20
+ list_missing: bool = True,
21
+ ) -> bool:
22
+ logger.info(
23
+ "{}.batch: {}{}{}{}".format(
24
+ NAME,
25
+ object_name,
26
+ " + combine" if combine else "",
27
+ "" if count == -1 else f" {count} files",
28
+ " + list missing" if list_missing else "",
29
+ )
30
+ )
31
+
32
+ list_of_suffixes: List[str] = get_from_object(
33
+ object_name,
34
+ "pdf",
35
+ [],
36
+ )
37
+
38
+ if not convert(
39
+ path_prefix=abcli_path_git,
40
+ list_of_suffixes=list_of_suffixes,
41
+ object_name=object_name,
42
+ combine=combine,
43
+ count=count,
44
+ incremental=False,
45
+ ):
46
+ return False
47
+
48
+ if list_missing:
49
+ list_missing_docs(
50
+ object_name=object_name,
51
+ list_of_suffixes=list_of_suffixes,
52
+ )
53
+
54
+ return True
@@ -0,0 +1,32 @@
1
+ from typing import List
2
+ from PyPDF2 import PdfMerger
3
+
4
+ from bluer_options.logger import crash_report
5
+
6
+ from bluer_objects import objects
7
+ from bluer_objects.logger import logger
8
+
9
+
10
+ def combine_pdfs(
11
+ list_of_pdfs: List[str],
12
+ object_name: str,
13
+ ) -> bool:
14
+ logger.info(f"combining {len(list_of_pdfs)} pdf(s)...")
15
+ combined_filename = objects.path_of(
16
+ filename="release.pdf",
17
+ object_name=object_name,
18
+ )
19
+
20
+ try:
21
+ merger = PdfMerger()
22
+ for filename in list_of_pdfs:
23
+ merger.append(filename)
24
+
25
+ merger.write(combined_filename)
26
+ merger.close()
27
+ except Exception as e:
28
+ crash_report(e)
29
+ return False
30
+
31
+ logger.info(f"-> {combined_filename}")
32
+ return True
@@ -0,0 +1,110 @@
1
+ from tqdm import tqdm
2
+ from typing import List
3
+ import os
4
+ from PIL import Image
5
+
6
+ from blueness import module
7
+
8
+ from bluer_objects import NAME
9
+ from bluer_objects import file, path
10
+ from bluer_objects.metadata import get_from_object, post_to_object
11
+ from bluer_objects.pdf.convert.combination import combine_pdfs
12
+ from bluer_objects.pdf.convert.image import convert_image
13
+ from bluer_objects.pdf.convert.md import convert_md
14
+ from bluer_objects.pdf.convert.pdf import convert_pdf
15
+ from bluer_objects.logger import logger
16
+
17
+
18
+ NAME = module.name(__file__, NAME)
19
+
20
+
21
+ def convert(
22
+ path_prefix: str,
23
+ list_of_suffixes: List[str],
24
+ object_name: str,
25
+ combine: bool,
26
+ count: int = -1,
27
+ incremental: bool = True,
28
+ ) -> bool:
29
+ logger.info(f"path_prefix: {path_prefix}")
30
+
31
+ list_of_pdfs: List[str] = (
32
+ get_from_object(
33
+ object_name,
34
+ "list_of_pdfs",
35
+ [],
36
+ )
37
+ if incremental
38
+ else []
39
+ )
40
+ if incremental:
41
+ logger.info(f"found {len(list_of_pdfs)} pdf(s)...")
42
+
43
+ list_of_pdfs_len_target = -1 if count == -1 else len(list_of_pdfs) + count
44
+ for suffix in tqdm(list_of_suffixes):
45
+ if (
46
+ list_of_pdfs_len_target != -1
47
+ and len(list_of_pdfs) >= list_of_pdfs_len_target
48
+ ):
49
+ logger.info(f"max count {count}, stopping.")
50
+ break
51
+
52
+ logger.info(
53
+ "{}.convert {} -> {}".format(
54
+ NAME,
55
+ suffix,
56
+ object_name,
57
+ )
58
+ )
59
+
60
+ source_filename = os.path.join(path_prefix, suffix)
61
+ if path.exists(source_filename):
62
+ source_filename = os.path.join(source_filename, "README.md")
63
+ suffix = os.path.join(suffix, "README.md")
64
+
65
+ if source_filename.endswith(".md"):
66
+ if not convert_md(
67
+ source_filename,
68
+ suffix,
69
+ object_name,
70
+ list_of_pdfs,
71
+ ):
72
+ return False
73
+ elif file.extension(source_filename) == "pdf":
74
+ if not convert_pdf(
75
+ source_filename,
76
+ suffix,
77
+ object_name,
78
+ list_of_pdfs,
79
+ ):
80
+ return False
81
+ elif file.extension(source_filename) in [
82
+ extension.split(".", 1)[1] for extension in Image.registered_extensions()
83
+ ]:
84
+ if not convert_image(
85
+ source_filename,
86
+ suffix,
87
+ object_name,
88
+ list_of_pdfs,
89
+ ):
90
+ return False
91
+ else:
92
+ logger.error(f"{source_filename}: cannot convert to pdf.")
93
+ return False
94
+
95
+ if incremental:
96
+ logger.info(f"{len(list_of_pdfs)} pdf(s) so far ...")
97
+ if not post_to_object(
98
+ object_name,
99
+ "list_of_pdfs",
100
+ list_of_pdfs,
101
+ ):
102
+ return False
103
+
104
+ if combine:
105
+ return combine_pdfs(
106
+ list_of_pdfs,
107
+ object_name,
108
+ )
109
+
110
+ return True
@@ -0,0 +1,53 @@
1
+ from PIL import Image
2
+
3
+ from bluer_options.logger import crash_report
4
+
5
+ from bluer_objects import file, objects, path
6
+ from bluer_objects.env import abcli_path_git
7
+ from bluer_objects.logger import logger
8
+
9
+
10
+ def convert_image(
11
+ source_filename: str,
12
+ suffix: str,
13
+ object_name: str,
14
+ list_of_pdfs: list[str],
15
+ ) -> bool:
16
+ logger.info("🌠 image found!")
17
+ filename_pdf = file.add_extension(
18
+ objects.path_of(
19
+ filename="docs/{}".format(
20
+ (
21
+ suffix.split(abcli_path_git, 1)[1]
22
+ if abcli_path_git in suffix
23
+ else suffix
24
+ ),
25
+ ),
26
+ object_name=object_name,
27
+ ),
28
+ "pdf",
29
+ )
30
+
31
+ if filename_pdf not in list_of_pdfs:
32
+ list_of_pdfs.append(filename_pdf)
33
+
34
+ if file.exists(filename_pdf):
35
+ logger.info(f"✅ {filename_pdf}")
36
+ return True
37
+
38
+ if not path.create(
39
+ file.path(filename_pdf),
40
+ log=True,
41
+ ):
42
+ return False
43
+
44
+ try:
45
+ image = Image.open(source_filename)
46
+ image = image.convert("RGB")
47
+ image.save(filename_pdf)
48
+ except Exception as e:
49
+ crash_report(e)
50
+ return False
51
+
52
+ logger.info(f"-> {filename_pdf}")
53
+ return True
@@ -0,0 +1,97 @@
1
+ import pypandoc
2
+ import subprocess
3
+ import os
4
+
5
+ from bluer_options.logger import crash_report
6
+
7
+ from bluer_objects import file, objects, path
8
+ from bluer_objects.env import abcli_path_git
9
+ from bluer_objects.logger import logger
10
+
11
+ css = """
12
+ <style>
13
+ body { font-family: sans-serif; margin: 2cm; }
14
+ img { max-width: 100%; height: auto; }
15
+ table { width: 100%; border-collapse: collapse; word-break: break-word; }
16
+ th, td { border: 1px solid #ccc; padding: 4px; vertical-align: top; }
17
+ code, pre { white-space: pre-wrap; }
18
+ </style>
19
+ """
20
+
21
+
22
+ def convert_md(
23
+ source_filename: str,
24
+ suffix: str,
25
+ object_name: str,
26
+ list_of_pdfs: list[str],
27
+ ) -> bool:
28
+ filename_html = file.add_extension(
29
+ objects.path_of(
30
+ filename="docs/{}".format(
31
+ (
32
+ suffix.split(abcli_path_git, 1)[1]
33
+ if abcli_path_git in suffix
34
+ else suffix
35
+ ),
36
+ ),
37
+ object_name=object_name,
38
+ ),
39
+ "html",
40
+ )
41
+ filename_pdf = file.add_extension(
42
+ filename_html,
43
+ "pdf",
44
+ )
45
+
46
+ if filename_pdf not in list_of_pdfs:
47
+ list_of_pdfs.append(filename_pdf)
48
+
49
+ if file.exists(filename_pdf):
50
+ logger.info(f"✅ {filename_pdf}")
51
+ return True
52
+
53
+ logger.info(f"{source_filename} -> {filename_pdf}")
54
+
55
+ try:
56
+ with open(source_filename, "r", encoding="utf-8") as f:
57
+ markdown_text = f.read()
58
+
59
+ html_text = pypandoc.convert_text(
60
+ markdown_text,
61
+ "html",
62
+ format="md",
63
+ )
64
+
65
+ html_text = (
66
+ f"<!DOCTYPE html><html><head>{css}</head><body>{html_text}</body></html>"
67
+ )
68
+
69
+ if not path.create(
70
+ file.path(filename_html),
71
+ log=True,
72
+ ):
73
+ return (False,)
74
+
75
+ with open(
76
+ filename_html,
77
+ "w",
78
+ encoding="utf-8",
79
+ ) as f:
80
+ f.write(html_text)
81
+
82
+ subprocess.run(
83
+ [
84
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
85
+ "--headless",
86
+ "--disable-gpu",
87
+ "--no-margins",
88
+ f"--print-to-pdf={filename_pdf}",
89
+ os.path.abspath(filename_html),
90
+ ],
91
+ check=True,
92
+ )
93
+ except Exception as e:
94
+ crash_report(e)
95
+ return False
96
+
97
+ return True
@@ -0,0 +1,96 @@
1
+ from typing import List
2
+ import os
3
+ from functools import reduce
4
+ import re
5
+
6
+ from blueness import module
7
+ from bluer_options.logger.config import log_list
8
+
9
+ from bluer_objects import NAME
10
+ from bluer_objects import file
11
+ from bluer_objects.metadata import get_from_object
12
+ from bluer_objects.pdf.convert.convert import convert
13
+ from bluer_objects.env import abcli_path_git
14
+ from bluer_objects.logger import logger
15
+
16
+
17
+ def list_missing_docs(
18
+ object_name: str,
19
+ list_of_suffixes: List[str],
20
+ ):
21
+ logger.info(f"{NAME}.list_missing_docs: {object_name}")
22
+
23
+ list_of_suffixes_fullpath = [
24
+ os.path.join(abcli_path_git, suffix) for suffix in list_of_suffixes
25
+ ]
26
+
27
+ list_of_prefixes_to_ignore: List[str] = [
28
+ os.path.join(abcli_path_git, prefix)
29
+ for prefix in get_from_object(
30
+ object_name,
31
+ "ignore",
32
+ [],
33
+ )
34
+ ]
35
+ log_list(
36
+ logger,
37
+ "ignoring",
38
+ list_of_prefixes_to_ignore,
39
+ "prefix(s)",
40
+ max_count=-1,
41
+ )
42
+
43
+ list_of_prefixes: List[str] = sorted(
44
+ list({suffix.split(os.sep)[0] for suffix in list_of_suffixes})
45
+ )
46
+ log_list(
47
+ logger,
48
+ "found",
49
+ list_of_prefixes,
50
+ "prefix(s)",
51
+ max_count=-1,
52
+ )
53
+
54
+ list_of_missing_docs: List[str] = [
55
+ filename
56
+ for filename in reduce(
57
+ lambda x, y: x + y,
58
+ [
59
+ file.list_of(
60
+ os.path.join(
61
+ abcli_path_git,
62
+ prefix,
63
+ "*.md",
64
+ ),
65
+ recursive=True,
66
+ )
67
+ for prefix in list_of_prefixes
68
+ ],
69
+ [],
70
+ )
71
+ if not any(
72
+ thing in filename
73
+ for thing in [
74
+ "template",
75
+ "pytest_cache",
76
+ ]
77
+ )
78
+ and filename not in list_of_suffixes_fullpath
79
+ and not any(
80
+ filename.startswith(prefix) for prefix in list_of_prefixes_to_ignore
81
+ )
82
+ and re.search(r"(?:^|-)v(\d+)\.md$", file.name_and_extension(filename)) is None
83
+ and file.name_and_extension(filename) != "README.md"
84
+ ]
85
+ if list_of_missing_docs:
86
+ logger.warning("missing docs.")
87
+ log_list(
88
+ logger,
89
+ "found",
90
+ list_of_missing_docs,
91
+ "missing doc(s)",
92
+ max_count=-1,
93
+ max_length=-1,
94
+ )
95
+ else:
96
+ logger.info("✅ no missing docs.")