Skip to content

Commit

Permalink
Added dark mode (override config and example of overriding styles to …
Browse files Browse the repository at this point in the history
…change theme to dark background)
  • Loading branch information
beveradb committed Mar 7, 2024
1 parent 61b3ea3 commit 6a9d040
Show file tree
Hide file tree
Showing 11 changed files with 1,040 additions and 204 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ docker run -it -v `pwd`:/app beveradb/logo-diagram-generator -c examples/full.ex
- **Logo Download**: If the automatic logo download doesn't find a logo for a tool, you can specify the URL interactively or manually place an SVG file in the `logos` directory. The file name should match the tool's name in the configuration file, in lowercase.
- **Diagram Appearance**: The appearance of the generated diagram can be customized by modifying the `config.yml` file. See the example configs and Graphviz documentation for more info.
### Overriding Config
You can override config entries in the config file from the command-line, using the `--override` parameter.
This is particularly useful if you have a diagram which you want to keep mostly the same but generate multiple slightly different versions of, e.g. a dark themed version:
```bash
logo-diagram-generator -c examples/full.example.yml -o examples -n full.example.dark --override 'style.diagramBackgroundColor=#333333' --override 'style.groupLabelFontcolor=#ffffff' --override 'style.colorPalette=aqua,purple3,maroon3,orangered,yellow,lime,fuchsia,cornflower,peachpuff,forestgreen'
```
![Dark Theme Example Diagram](examples/full.example.dark_logos.png)
## Contributing
Contributions to improve `logo-diagram-generator` or add new features are welcome. Please submit a pull request or open an issue to discuss your ideas.
Binary file added examples/full.example.dark_logos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
486 changes: 486 additions & 0 deletions examples/full.example.dark_logos.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
272 changes: 272 additions & 0 deletions examples/full.example.dark_text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/full.example_logos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
199 changes: 99 additions & 100 deletions examples/full.example_logos.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
191 changes: 95 additions & 96 deletions examples/full.example_text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion logo_diagram_generator/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def main():
help="Directory for the output SVG diagram.",
)
parser.add_argument("-w", "--png_width", type=int, default=3000, help="Width of the resulting PNG image (default: 3000).")
parser.add_argument(
"-oc",
"--override",
type=lambda kv: dict([kv.split("=")]),
action="append",
help="Override specific configuration entries. Use format: key=value. This option can be used multiple times for multiple overrides.",
)

args = parser.parse_args()

Expand All @@ -45,7 +52,12 @@ def main():
logging.info(f"Downloaded all logos to directory: {args.logos_dir}")

output_svg_path, output_png_path = generate_diagram.generate_diagram_from_config(
config_filepath=args.config, diagram_name=args.name, output_dir=args.output_dir, logos_dir=args.logos_dir, png_width=args.png_width
config_filepath=args.config,
diagram_name=args.name,
output_dir=args.output_dir,
logos_dir=args.logos_dir,
png_width=args.png_width,
override_configs=args.override,
)

logging.info(f"Logo diagram generator completed successfully! Output filenames: {output_svg_path}, {output_png_path}")
Expand Down
26 changes: 20 additions & 6 deletions logo_diagram_generator/generate_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,32 @@ def generate_text_only_svg_diagram_from_config(config, diagram_name, output_svg_
group_label_fontsize = str(ecosystem_style.get("groupLabelFontsize", "25"))
group_label_margin = str(ecosystem_style.get("groupLabelMargin", "0.2"))

color_palette = ecosystem_style.get("colorPalette", utils.visually_distinct_colors)

dot = graphviz.Digraph(engine=diagram_engine, format="svg")
dot.attr(id=diagram_name)
dot.attr(overlap=diagram_overlap)
dot.attr(overlap_scaling=diagram_overlap_scaling)
dot.attr(overlap_shrink=diagram_overlap_shrink)
dot.attr(rankdir=diagram_rankdir)
dot.attr(pad=diagram_padding)
dot.attr(id=diagram_name)
dot.attr(bgcolor=diagram_background_color)

logging.info(
f"Diagram attributes set: engine={diagram_engine}, overlap={diagram_overlap}, "
f"Diagram level attributes set: id={diagram_name}, engine={diagram_engine}, overlap={diagram_overlap}, "
f"overlap_scaling={diagram_overlap_scaling}, overlap_shrink={diagram_overlap_shrink}, "
f"rankdir={diagram_rankdir}, padding={diagram_padding}, background_color={diagram_background_color}, "
f"group_label_shape={group_label_shape}, group_label_style={group_label_style}, "
)

logging.info(
f"Group attributes set: group_label_shape={group_label_shape}, group_label_style={group_label_style}, "
f"group_label_fontname={group_label_fontname}, group_label_fontcolor={group_label_fontcolor}, "
f"group_label_fontsize={group_label_fontsize}, group_label_margin={group_label_margin}"
)

logging.info("Color palette for groups: ")
logging.info(color_palette)

central_tool = config["ecosystem"]["centralTool"]
central_tool_name = central_tool["name"]
central_tool_label = central_tool.get("label", central_tool_name)
Expand All @@ -64,7 +72,7 @@ def generate_text_only_svg_diagram_from_config(config, diagram_name, output_svg_

for i, group in enumerate(config["ecosystem"]["groups"], start=0):
group_slug = utils.slugify(group["category"])
group_color = group.get("color", utils.visually_distinct_colors[i % len(utils.visually_distinct_colors)])
group_color = group.get("color", color_palette[i % len(color_palette)])
logging.debug(f"Processing group: {group['category']} with color {group_color}")

group_label = group["category"]
Expand Down Expand Up @@ -208,7 +216,7 @@ def embed_logos_in_diagram(diagram_name, diagram_svg_path, output_svg_path, conf
css_url_search = re.findall(r"url\(#([^)]+)\)", logo_svg_content)
for css_url_match in css_url_search:
logging.debug(f"Adding {tool_name_slug}- prefix to CSS URL reference #{css_url_match}")
logo_svg_content = re.sub(f"url\(#{css_url_match}\)", f"url(#{tool_name_slug}-{css_url_match})", logo_svg_content)
logo_svg_content = re.sub(r"url\(#([^)]+)\)", f"url(#{tool_name_slug}-{css_url_match})", logo_svg_content)

logo_svg_dom = xml.dom.minidom.parseString(logo_svg_content)
logo_node = logo_svg_dom.documentElement
Expand Down Expand Up @@ -245,10 +253,16 @@ def embed_logos_in_diagram(diagram_name, diagram_svg_path, output_svg_path, conf
logging.info("Logos embedded into diagram")


def generate_diagram_from_config(config_filepath, diagram_name, output_dir, logos_dir, png_width):
def generate_diagram_from_config(config_filepath, diagram_name, output_dir, logos_dir, png_width, override_configs):
logging.info(f"Reading configuration from file: {config_filepath}")
config = utils.read_config(config_filepath)

if override_configs:
logging.info(f"Override configuration provided, replacing config keys as specified")
config["ecosystem"] = utils.override_config(config=config["ecosystem"], override_configs=override_configs)
else:
logging.info(f"No configuration overrides set, using values from specified config file")

text_diagram_basename = utils.slugify(diagram_name)
logging.info(f"Filesystem safe diagram name: {text_diagram_basename}")

Expand Down
42 changes: 42 additions & 0 deletions logo_diagram_generator/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,48 @@
]


def override_config(config, override_configs):
"""
Overrides the original configuration with values from the list of override configurations.
Supports overriding both scalar values and lists.
:param config: The original configuration dictionary.
:param override_configs: A list of override configuration dictionaries.
:return: The updated configuration dictionary.
"""

def apply_override(config, key_path, value):
"""
Recursively apply an override value based on a list of keys indicating the path.
Supports overriding lists by detecting a delimiter in the value.
:param config: The configuration dictionary to update.
:param key_path: The list of keys indicating the path to the value to override.
:param value: The override value.
"""
key = key_path[0]
if len(key_path) == 1:
if "," in value and (key not in config or isinstance(config.get(key, ""), list)):
# If the value contains a comma and either the key isn't found in the config or the target is a list,
# assume this config entry is supposed to be a list and split the value.
value = value.split(",")
if key in config:
logging.warning(f"Overriding value of key {key} from {config[key]} to {value}")
else:
logging.warning(f"Key {key} not found in the original config. Adding it with value: {value}")
config[key] = value
else:
if key not in config or not isinstance(config[key], dict):
logging.warning(f"Creating nested config for key {key} to accommodate override.")
config[key] = {}
apply_override(config[key], key_path[1:], value)

for override_config in override_configs:
for key, value in override_config.items():
key_path = key.split(".")
apply_override(config, key_path, value)

return config


def update_config(config_filepath, tool_name, updates):
"""
Updates the configuration file with the given updates for the specified tool.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "logo-diagram-generator"
version = "0.2.3"
version = "0.3.0"
description = "Generate SVG diagrams of a (tech) ecosystem, using logos from each tool organised into groups around a central logo"
authors = ["Andrew Beveridge <[email protected]>"]
license = "MIT"
Expand Down

0 comments on commit 6a9d040

Please sign in to comment.