bluer-sbc 8.188.1__py3-none-any.whl → 9.168.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 (53) hide show
  1. bluer_sbc/.abcli/actions.sh +3 -0
  2. bluer_sbc/.abcli/alias.sh +2 -2
  3. bluer_sbc/.abcli/{blue_sbc.sh → bluer_sbc.sh} +2 -0
  4. bluer_sbc/.abcli/rpi.sh +1 -1
  5. bluer_sbc/.abcli/sbc/parts/adjust.sh +14 -0
  6. bluer_sbc/.abcli/sbc/parts/cd.sh +5 -0
  7. bluer_sbc/.abcli/sbc/parts.sh +15 -0
  8. bluer_sbc/.abcli/seed/rpi_64_bit.sh +36 -0
  9. bluer_sbc/.abcli/tests/help.sh +7 -3
  10. bluer_sbc/.abcli/tests/parts_adjust.sh +13 -0
  11. bluer_sbc/README/aliases.py +11 -0
  12. bluer_sbc/README/build.py +29 -0
  13. bluer_sbc/README/design.py +40 -0
  14. bluer_sbc/README/designs/__init__.py +26 -0
  15. bluer_sbc/README/designs/adapter_bus.py +59 -0
  16. bluer_sbc/README/designs/battery_bus.py +52 -0
  17. bluer_sbc/README/designs/bryce.py +28 -0
  18. bluer_sbc/README/designs/cheshmak.py +29 -0
  19. bluer_sbc/README/designs/nafha.py +57 -0
  20. bluer_sbc/README/designs/shelter.py +63 -0
  21. bluer_sbc/README/designs/swallow.py +46 -0
  22. bluer_sbc/README/designs/swallow_head.py +89 -0
  23. bluer_sbc/README/designs/template.py +38 -0
  24. bluer_sbc/{designs → README/designs}/ultrasonic_sensor_tester.py +11 -10
  25. bluer_sbc/README/parts.py +29 -0
  26. bluer_sbc/README/root.py +33 -0
  27. bluer_sbc/README/shortcuts.py +18 -0
  28. bluer_sbc/__init__.py +2 -2
  29. bluer_sbc/__main__.py +3 -2
  30. bluer_sbc/config.env +1 -1
  31. bluer_sbc/env.py +4 -0
  32. bluer_sbc/help/functions.py +2 -0
  33. bluer_sbc/help/parts.py +49 -0
  34. bluer_sbc/help/rpi.py +2 -1
  35. bluer_sbc/parts/__init__.py +0 -0
  36. bluer_sbc/parts/__main__.py +48 -0
  37. bluer_sbc/parts/classes/db.py +233 -0
  38. bluer_sbc/parts/classes/part.py +96 -0
  39. bluer_sbc/parts/consts.py +3 -0
  40. bluer_sbc/parts/db.py +619 -0
  41. bluer_sbc-9.168.1.dist-info/METADATA +73 -0
  42. {bluer_sbc-8.188.1.dist-info → bluer_sbc-9.168.1.dist-info}/RECORD +48 -24
  43. bluer_sbc/README.py +0 -75
  44. bluer_sbc/designs/bluer_swallow.py +0 -26
  45. bluer_sbc/designs/bryce.py +0 -25
  46. bluer_sbc/designs/cheshmak.py +0 -25
  47. bluer_sbc-8.188.1.dist-info/METADATA +0 -63
  48. /bluer_sbc/{designs → README}/__init__.py +0 -0
  49. /bluer_sbc/{designs → README/designs}/blue_bracket.py +0 -0
  50. /bluer_sbc/{designs → README/designs}/consts.py +0 -0
  51. {bluer_sbc-8.188.1.dist-info → bluer_sbc-9.168.1.dist-info}/WHEEL +0 -0
  52. {bluer_sbc-8.188.1.dist-info → bluer_sbc-9.168.1.dist-info}/licenses/LICENSE +0 -0
  53. {bluer_sbc-8.188.1.dist-info → bluer_sbc-9.168.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,89 @@
1
+ from bluer_objects import README
2
+ from bluer_objects.README.items import ImageItems
3
+
4
+ from bluer_sbc.README.designs.consts import assets2
5
+ from bluer_sbc.README.design import design_doc
6
+
7
+
8
+ image_template = assets2 + "swallow/design/head-v1/{}?raw=true"
9
+
10
+ marquee = README.Items(
11
+ [
12
+ {
13
+ "name": "swallow head",
14
+ "marquee": image_template.format("01.jpg"),
15
+ "url": "./bluer_sbc/docs/swallow-head.md",
16
+ }
17
+ ]
18
+ )
19
+
20
+ items = ImageItems(
21
+ {image_template.format(f"{index+1:02}.jpg"): "" for index in range(6)}
22
+ )
23
+
24
+ parts = {
25
+ "sd-card-32-gb": "",
26
+ "rpi": "",
27
+ "XL4015": "",
28
+ "470-mF": "",
29
+ "Polyfuse": "optional",
30
+ "TVS-diode": "",
31
+ "resistor": "7 x 330-470 Ω + N x 2.2 kΩ + N x 3.3 kΩ",
32
+ "LED": "green + red + yellow + 4 x blue",
33
+ "rpi-camera": "",
34
+ "PCB-single-14x9_5": "2 x",
35
+ "PCB-double-9x7": "2 x",
36
+ "pushbutton": "",
37
+ "ultrasonic-sensor": "4 x",
38
+ "connector": "1 female",
39
+ "nuts-bolts-spacers": " + ".join(
40
+ [
41
+ "M2: ({})".format(
42
+ " + ".join(
43
+ [
44
+ "2 x bolt",
45
+ "2 x 5 mm spacer",
46
+ "4 x nut",
47
+ ]
48
+ )
49
+ ),
50
+ "M2.5: ({})".format(
51
+ " + ".join(
52
+ [
53
+ "4 x bolt",
54
+ "8 x 10 mm spacer",
55
+ "4 x nut",
56
+ ]
57
+ )
58
+ ),
59
+ "M3: ({})".format(
60
+ " + ".join(
61
+ [
62
+ "1 x bolt",
63
+ "3 x 35 mm spacer",
64
+ "3 x 25 mm spacer",
65
+ "7 x 15 mm spacer",
66
+ "4 x 5 mm spacer",
67
+ "5 x nut",
68
+ ]
69
+ )
70
+ ),
71
+ ]
72
+ ),
73
+ "plexiglass": "14 cm x 9.5 cm",
74
+ "white-terminal": "2 x",
75
+ "dupont-cables": "1 x 30 cm + 1 x 10 cm",
76
+ "16-awg-wire": "40 cm x (red + black/blue)",
77
+ "solid-cable-1-15": "10 cm x (red + black/blue)",
78
+ "strong-thread": "1 m",
79
+ "pin-headers": "1 x (female, 2 x 40) -> 2 x 20 + 2 x (male, 1 x 40) -> 4 x 6 + 4 x 2 + 2 x 20",
80
+ }
81
+
82
+
83
+ docs = [
84
+ design_doc(
85
+ "swallow-head",
86
+ items,
87
+ parts,
88
+ )
89
+ ]
@@ -0,0 +1,38 @@
1
+ from bluer_objects import README
2
+ from bluer_objects.README.items import ImageItems
3
+ from bluer_objects.README.consts import assets_url
4
+
5
+ from bluer_sbc.README.design import design_doc
6
+
7
+
8
+ assets2_x = assets_url(
9
+ suffix="x",
10
+ volume=2,
11
+ )
12
+
13
+ marquee = README.Items(
14
+ [
15
+ {
16
+ "name": "x",
17
+ "marquee": f"{assets2_x}/TBA.jpg",
18
+ "url": "./bluer_sbc/docs/x.md",
19
+ }
20
+ ]
21
+ )
22
+
23
+ items = ImageItems(
24
+ {
25
+ f"{assets2_x}/TBA.jpg": "",
26
+ f"{assets2_x}/TBA.jpg": "",
27
+ }
28
+ )
29
+
30
+ parts = {}
31
+
32
+ docs = [
33
+ design_doc(
34
+ "template",
35
+ items,
36
+ parts,
37
+ )
38
+ ]
@@ -1,6 +1,8 @@
1
1
  from bluer_objects import README
2
+ from bluer_objects.README.items import ImageItems
2
3
 
3
- from bluer_sbc.designs.consts import assets2
4
+ from bluer_sbc.README.designs.consts import assets2
5
+ from bluer_sbc.README.design import design_doc
4
6
 
5
7
 
6
8
  image_template = assets2 + "ultrasonic-sensor-tester/{}?raw=true"
@@ -15,12 +17,11 @@ marquee = README.Items(
15
17
  ]
16
18
  )
17
19
 
18
- items = README.Items(
19
- [
20
- {
21
- "marquee": image_template.format(f"{index:02}.jpg"),
22
- "name": "",
23
- }
24
- for index in range(6)
25
- ]
26
- )
20
+ items = ImageItems({image_template.format(f"{index:02}.jpg"): "" for index in range(6)})
21
+
22
+ docs = [
23
+ design_doc(
24
+ "ultrasonic-sensor-tester",
25
+ items,
26
+ )
27
+ ]
@@ -0,0 +1,29 @@
1
+ from bluer_objects import markdown
2
+
3
+ from bluer_sbc.parts.db import db_of_parts
4
+
5
+ docs = [
6
+ {
7
+ "path": "../docs/parts",
8
+ "macros": {
9
+ "parts_list:::": db_of_parts.README,
10
+ "parts_images:::": markdown.generate_table(
11
+ db_of_parts.as_images(
12
+ {part.name: "" for part in db_of_parts},
13
+ reference="../parts",
14
+ ),
15
+ cols=10,
16
+ log=False,
17
+ ),
18
+ },
19
+ }
20
+ ] + [
21
+ {
22
+ "path": part.filename(create=True),
23
+ "macros": {
24
+ "info:::": part.README(db_of_parts.url_prefix),
25
+ },
26
+ }
27
+ for part_name, part in db_of_parts.items()
28
+ if part_name != "template"
29
+ ]
@@ -0,0 +1,33 @@
1
+ from bluer_sbc.README.designs.cheshmak import marquee as cheshmak_marquee
2
+ from bluer_sbc.README.designs.blue_bracket import items as blue_bracket_items
3
+ from bluer_sbc.README.designs.swallow import marquee as swallow_marquee
4
+ from bluer_sbc.README.designs.swallow_head import marquee as swallow_head_marquee
5
+ from bluer_sbc.README.designs.bryce import marquee as bryce_marquee
6
+ from bluer_sbc.README.designs.nafha import marquee as nafha_marquee
7
+ from bluer_sbc.README.designs.shelter import marquee as shelter_marquee
8
+ from bluer_sbc.README.designs.adapter_bus import marquee as adapter_bus_marquee
9
+ from bluer_sbc.README.designs.battery_bus import marquee as battery_bus_marquee
10
+ from bluer_sbc.README.designs.ultrasonic_sensor_tester import (
11
+ marquee as ultrasonic_sensor_tester_marquee,
12
+ )
13
+ from bluer_sbc.README.shortcuts import items as shortcuts_items
14
+
15
+ docs = [
16
+ {
17
+ "items": []
18
+ + swallow_head_marquee
19
+ + swallow_marquee
20
+ + battery_bus_marquee
21
+ + adapter_bus_marquee
22
+ + ultrasonic_sensor_tester_marquee
23
+ + bryce_marquee
24
+ + cheshmak_marquee
25
+ + nafha_marquee
26
+ + shelter_marquee
27
+ + blue_bracket_items,
28
+ "path": "../..",
29
+ "macros": {
30
+ "shortcuts:::": shortcuts_items,
31
+ },
32
+ },
33
+ ]
@@ -0,0 +1,18 @@
1
+ from bluer_objects.README.items import Items
2
+ from bluer_objects import markdown
3
+
4
+ from bluer_sbc.parts.db import db_of_parts
5
+
6
+
7
+ items = markdown.generate_table(
8
+ Items(
9
+ [
10
+ {
11
+ "name": "parts",
12
+ "url": "./bluer_sbc/docs/parts",
13
+ "marquee": f"{db_of_parts.url_prefix}/grid.png",
14
+ },
15
+ ]
16
+ ),
17
+ log=False,
18
+ )
bluer_sbc/__init__.py CHANGED
@@ -2,9 +2,9 @@ NAME = "bluer_sbc"
2
2
 
3
3
  ICON = "🌀"
4
4
 
5
- DESCRIPTION = f"{ICON} AI for single board computers."
5
+ DESCRIPTION = f"{ICON} AI for single board computers and related designs."
6
6
 
7
- VERSION = "8.188.1"
7
+ VERSION = "9.168.1"
8
8
 
9
9
  REPO_NAME = "bluer-sbc"
10
10
 
bluer_sbc/__main__.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from blueness.argparse.generic import main
2
2
 
3
- from bluer_sbc import NAME, VERSION, DESCRIPTION, ICON, README
3
+ from bluer_sbc import NAME, VERSION, DESCRIPTION, ICON
4
+ from bluer_sbc.README.build import build
4
5
  from bluer_sbc.logger import logger
5
6
 
6
7
  main(
@@ -10,7 +11,7 @@ main(
10
11
  VERSION=VERSION,
11
12
  main_filename=__file__,
12
13
  tasks={
13
- "build_README": lambda _: README.build(),
14
+ "build_README": lambda _: build(),
14
15
  },
15
16
  logger=logger,
16
17
  )
bluer_sbc/config.env CHANGED
@@ -29,4 +29,4 @@ BLUER_SBC_SESSION_REBOOT_PERIOD=14400
29
29
  BLUER_SBC_SESSION_SCREEN_PERIOD=4
30
30
  BLUER_SBC_SESSION_TEMPERATURE_PERIOD=300
31
31
 
32
- BLUER_SBC_ENV=navigation
32
+ BLUER_SBC_ENV=navigation
bluer_sbc/env.py CHANGED
@@ -38,3 +38,7 @@ BLUER_SBC_SESSION_TEMPERATURE_PERIOD = get_env(
38
38
  BLUER_SBC_ENV = get_env("BLUER_SBC_ENV")
39
39
 
40
40
  BLUER_SBC_SWALLOW_HAS_STEERING = get_env("BLUER_SBC_SWALLOW_HAS_STEERING", 0)
41
+ BLUER_SBC_SWALLOW_HAS_FULL_KEYBOARD = get_env("BLUER_SBC_SWALLOW_HAS_FULL_KEYBOARD", 0)
42
+ BLUER_SBC_SWALLOW_HAS_BPS = get_env("BLUER_SBC_SWALLOW_HAS_BPS", 0)
43
+
44
+ BLUER_SBC_BPS_ANCHORED_AT = get_env("BLUER_SBC_BPS_ANCHORED_AT", "")
@@ -10,6 +10,7 @@ from bluer_sbc.help.camera import help_functions as help_camera
10
10
  from bluer_sbc.help.grove import help_functions as help_grove
11
11
  from bluer_sbc.help.hat import help_functions as help_hat
12
12
  from bluer_sbc.help.lepton import help_functions as help_lepton
13
+ from bluer_sbc.help.parts import help_functions as help_parts
13
14
  from bluer_sbc.help.rpi import help_functions as help_rpi
14
15
  from bluer_sbc.help.scroll_phat_hd import help_functions as help_scroll_phat_hd
15
16
  from bluer_sbc.help.sparkfun_top_phat import help_functions as help_sparkfun_top_phat
@@ -26,6 +27,7 @@ help_functions.update(
26
27
  "grove": help_grove,
27
28
  "hat": help_hat,
28
29
  "lepton": help_lepton,
30
+ "parts": help_parts,
29
31
  "rpi": help_rpi,
30
32
  "scroll_phat_hd": help_scroll_phat_hd,
31
33
  "sparkfun_top_phat": help_sparkfun_top_phat,
@@ -0,0 +1,49 @@
1
+ from typing import List
2
+
3
+ from bluer_options.terminal import show_usage, xtra
4
+
5
+ from bluer_sbc import ALIAS
6
+
7
+
8
+ def help_cd(
9
+ tokens: List[str],
10
+ mono: bool,
11
+ ) -> str:
12
+ return show_usage(
13
+ [
14
+ "@sbc",
15
+ "parts",
16
+ "cd",
17
+ ],
18
+ "cd to part images.",
19
+ mono=mono,
20
+ )
21
+
22
+
23
+ def help_adjust(
24
+ tokens: List[str],
25
+ mono: bool,
26
+ ) -> str:
27
+ options = xtra("dryrun,~grid", mono=mono)
28
+
29
+ args = [
30
+ "[--verbose 1]",
31
+ ]
32
+
33
+ return show_usage(
34
+ [
35
+ "@sbc",
36
+ "parts",
37
+ "adjust",
38
+ f"[{options}]",
39
+ ]
40
+ + args,
41
+ "adjust part images.",
42
+ mono=mono,
43
+ )
44
+
45
+
46
+ help_functions = {
47
+ "adjust": help_adjust,
48
+ "cd": help_cd,
49
+ }
bluer_sbc/help/rpi.py CHANGED
@@ -11,7 +11,8 @@ def help_fake_display(
11
11
 
12
12
  return show_usage(
13
13
  [
14
- "@rpi",
14
+ "@sbc",
15
+ "rpi",
15
16
  "fake_display",
16
17
  f"[{options}]",
17
18
  ],
File without changes
@@ -0,0 +1,48 @@
1
+ import argparse
2
+
3
+ from blueness import module
4
+ from blueness.argparse.generic import sys_exit
5
+
6
+ from bluer_sbc import NAME
7
+ from bluer_sbc.parts.db import db_of_parts
8
+ from bluer_sbc.logger import logger
9
+
10
+ NAME = module.name(__file__, NAME)
11
+
12
+ parser = argparse.ArgumentParser(NAME)
13
+ parser.add_argument(
14
+ "task",
15
+ type=str,
16
+ help="adjust",
17
+ )
18
+ parser.add_argument(
19
+ "--dryrun",
20
+ type=int,
21
+ default=1,
22
+ help="0 | 1",
23
+ )
24
+ parser.add_argument(
25
+ "--generate_grid",
26
+ type=int,
27
+ default=1,
28
+ help="0 | 1",
29
+ )
30
+ parser.add_argument(
31
+ "--verbose",
32
+ type=int,
33
+ default=0,
34
+ help="0 | 1",
35
+ )
36
+ args = parser.parse_args()
37
+
38
+ success = False
39
+ if args.task == "adjust":
40
+ success = db_of_parts.adjust(
41
+ generate_grid=args.generate_grid == 1,
42
+ dryrun=args.dryrun == 1,
43
+ verbose=args.verbose == 1,
44
+ )
45
+ else:
46
+ success = None
47
+
48
+ sys_exit(logger, NAME, args.task, success)
@@ -0,0 +1,233 @@
1
+ from typing import Dict, List, Iterator, Tuple
2
+ import copy
3
+ import os
4
+ import numpy as np
5
+ import cv2
6
+ from functools import reduce
7
+ from tqdm import tqdm
8
+
9
+ from blueness import module
10
+ from bluer_options.logger import log_list
11
+ from bluer_objects import file
12
+ from bluer_objects import README
13
+ from bluer_objects.README.consts import assets_path, assets_url
14
+ from bluer_objects.logger.image import log_image_grid
15
+
16
+ from bluer_sbc.host import signature
17
+ from bluer_sbc import NAME
18
+ from bluer_sbc.parts.classes.part import Part
19
+ from bluer_sbc.logger import logger
20
+
21
+ NAME = module.name(__file__, NAME)
22
+
23
+
24
+ class PartDB:
25
+ def __init__(self):
26
+ self._db: Dict[str, Part] = {}
27
+
28
+ suffix = "bluer-sbc/parts"
29
+ self.url_prefix = assets_url(
30
+ suffix=suffix,
31
+ volume=2,
32
+ )
33
+ self.path = assets_path(
34
+ suffix=suffix,
35
+ volume=2,
36
+ )
37
+
38
+ def __iter__(self):
39
+ return iter(self._db.values())
40
+
41
+ def __setitem__(
42
+ self,
43
+ name: str,
44
+ part: Part,
45
+ ):
46
+ assert isinstance(part, Part)
47
+
48
+ self._db[name] = copy.deepcopy(part)
49
+ self._db[name].name = name
50
+
51
+ def __getitem__(self, name: str) -> Part:
52
+ return self._db[name]
53
+
54
+ def items(self) -> Iterator[Tuple[str, Part]]:
55
+ return self._db.items()
56
+
57
+ @property
58
+ def README(self) -> List[str]:
59
+ return sorted(
60
+ [
61
+ "1. [{}](./{}.md).".format(
62
+ part.info[0],
63
+ part.name,
64
+ )
65
+ for part in self
66
+ ]
67
+ )
68
+
69
+ def adjust(
70
+ self,
71
+ generate_grid: bool = True,
72
+ dryrun: bool = True,
73
+ verbose: bool = False,
74
+ ) -> bool:
75
+ logger.info(
76
+ "{}.adjust{}".format(
77
+ NAME,
78
+ " [dryrun]" if dryrun else "",
79
+ )
80
+ )
81
+
82
+ list_of_filenames = reduce(
83
+ lambda x, y: x + y,
84
+ [
85
+ [part.images[0]]
86
+ for part_name, part in self._db.items()
87
+ if part_name != "template" and part.images
88
+ ],
89
+ [],
90
+ )
91
+ log_list(logger, "adjusting", list_of_filenames, "images")
92
+
93
+ if generate_grid:
94
+ if not log_image_grid(
95
+ items=[
96
+ {
97
+ "filename": os.path.join(self.path, part.images[0]),
98
+ "title": part_name,
99
+ }
100
+ for part_name, part in self._db.items()
101
+ if part_name != "template" and part.images
102
+ ],
103
+ filename=assets_path(
104
+ suffix="bluer-sbc/parts/grid.png",
105
+ volume=2,
106
+ ),
107
+ scale=3,
108
+ header=[
109
+ "{} part(s)".format(len(self._db) - 1),
110
+ ],
111
+ footer=signature(),
112
+ ):
113
+ return False
114
+
115
+ max_width = 0
116
+ max_height = 0
117
+ for filename in tqdm(list_of_filenames):
118
+ success, image = file.load_image(
119
+ os.path.join(self.path, filename),
120
+ log=verbose,
121
+ )
122
+ if not success:
123
+ return success
124
+
125
+ max_height = max(max_height, image.shape[0])
126
+ max_width = max(max_width, image.shape[1])
127
+
128
+ logger.info(f"size: {max_height} x {max_width}")
129
+
130
+ for filename in tqdm(list_of_filenames):
131
+ success, image = file.load_image(
132
+ os.path.join(self.path, filename),
133
+ log=verbose,
134
+ )
135
+ if not success:
136
+ return success
137
+
138
+ if image.shape[0] == max_height and image.shape[1] == max_width:
139
+ logger.info("✅")
140
+ continue
141
+
142
+ image = image[:, :, :3]
143
+
144
+ scale = min(
145
+ max_height / image.shape[0],
146
+ max_width / image.shape[1],
147
+ )
148
+ image = cv2.resize(
149
+ image,
150
+ dsize=(
151
+ int(scale * image.shape[1]),
152
+ int(scale * image.shape[0]),
153
+ ),
154
+ interpolation=cv2.INTER_LINEAR,
155
+ )
156
+
157
+ padded_image = (
158
+ np.ones(
159
+ (max_height, max_width, 3),
160
+ dtype=np.uint8,
161
+ )
162
+ * 255
163
+ )
164
+
165
+ y_offset = (max_height - image.shape[0]) // 2
166
+ x_offset = (max_width - image.shape[1]) // 2
167
+
168
+ padded_image[
169
+ y_offset : y_offset + image.shape[0],
170
+ x_offset : x_offset + image.shape[1],
171
+ ] = image
172
+
173
+ if not dryrun:
174
+ if not file.save_image(
175
+ os.path.join(self.path, filename),
176
+ padded_image,
177
+ log=verbose,
178
+ ):
179
+ return False
180
+
181
+ return True
182
+
183
+ def as_images(
184
+ self,
185
+ dict_of_parts: Dict[str, str],
186
+ reference: str = "../../parts",
187
+ ) -> List[str]:
188
+ return README.Items(
189
+ [
190
+ {
191
+ "name": self._db[part_name].info[0],
192
+ "marquee": self._db[part_name].image_url(
193
+ url_prefix=self.url_prefix
194
+ ),
195
+ "description": description,
196
+ "url": f"{reference}/{part_name}.md",
197
+ }
198
+ for part_name, description in dict_of_parts.items()
199
+ ],
200
+ sort=True,
201
+ )
202
+
203
+ def as_list(
204
+ self,
205
+ dict_of_parts: Dict[str, str],
206
+ reference: str = "../../parts",
207
+ log: bool = True,
208
+ ) -> List[str]:
209
+ if log:
210
+ logger.info(
211
+ "{}.as_list: {}".format(
212
+ self.__class__.__name__,
213
+ ", ".join(dict_of_parts.keys()),
214
+ )
215
+ )
216
+
217
+ for part_name in dict_of_parts:
218
+ if part_name not in self._db:
219
+ logger.error(f"{part_name}: part not found.")
220
+ assert False
221
+
222
+ return sorted(
223
+ [
224
+ (
225
+ "1. [{}]({}){}.".format(
226
+ self._db[part_name].info[0],
227
+ f"{reference}/{part_name}.md",
228
+ ": {}".format(description) if description else "",
229
+ )
230
+ )
231
+ for part_name, description in dict_of_parts.items()
232
+ ]
233
+ )