Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended build examples #118

Merged
merged 7 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 129 additions & 92 deletions src/wireviz/build_examples.py
Original file line number Diff line number Diff line change
@@ -1,113 +1,150 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import argparse
import os
import sys
from fnmatch import fnmatch

# noinspection PyUnresolvedReferences
from wv_helper import open_file_write, open_file_read
import os
from pathlib import Path

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
script_path = Path(__file__).absolute()

sys.path.insert(0, str(script_path.parent.parent)) # to find wireviz module
from wireviz import wireviz
from wv_helper import open_file_write, open_file_read, open_file_append

examples_path = os.path.join('..','..','examples')
tutorials_path = os.path.join('..','..','tutorial')
demos_path = examples_path

readme = 'readme.md'


def build_demos():
for fn in sorted(os.listdir(demos_path)):
if fnmatch(fn, "demo*.yml"):
abspath = os.path.join(demos_path, fn)

print(abspath)
wireviz.parse_file(abspath)

def build_examples():
with open_file_write(os.path.join(examples_path, readme)) as file:
file.write('# Example gallery\n')
for fn in sorted(os.listdir(examples_path)):
if fnmatch(fn, "ex*.yml"):
i = ''.join(filter(str.isdigit, fn))

abspath = os.path.join(examples_path, fn)
outfile_name = abspath.split(".yml")[0]


print(abspath)
wireviz.parse_file(abspath)

file.write(f'## Example {i}\n')
file.write(f'![]({outfile_name}.png)\n\n')
file.write(f'[Source]({fn}) - [Bill of Materials]({outfile_name}.bom.tsv)\n\n\n')

def build_tutorials():
with open_file_write(os.path.join(tutorials_path, readme)) as file:
file.write('# WireViz Tutorial\n')
for fn in sorted(os.listdir(tutorials_path)):
if fnmatch(fn, "tutorial*.yml"):
i = ''.join(filter(str.isdigit, fn))
abspath = os.path.join(tutorials_path, fn)
print(abspath)

wireviz.parse_file(abspath)

outfile_name = abspath.split(".yml")[0]

with open_file_read(outfile_name + '.md') as info:
for line in info:
file.write(line.replace('## ', '## {} - '.format(i)))
file.write(f'\n[Source]({fn}):\n\n')

with open_file_read(abspath) as src:
file.write('```yaml\n')
for line in src:
file.write(line)
file.write('```\n')
file.write('\n')

file.write('\nOutput:\n\n'.format(i))

file.write(f'![](tutorial{outfile_name}.png)\n\n')

file.write(f'[Bill of Materials](tutorial{outfile_name}.bom.tsv)\n\n\n')

def clean_examples():
generated_extensions = ['.gv', '.png', '.svg', '.html', '.bom.tsv']

for filepath in [examples_path, demos_path, tutorials_path]:
print(filepath)
for file in sorted(os.listdir(filepath)):
if os.path.exists(os.path.join(filepath, file)):
if list(filter(file.endswith, generated_extensions)) or file == 'readme.md':
print('rm ' + os.path.join(filepath, file))
os.remove(os.path.join(filepath, file))
groups = {
'examples': {
'path': Path(script_path).parent.parent.parent / 'examples',
'prefix': 'ex',
readme: [], # Include no files
'title': 'Example Gallery',
},
'tutorial' : {
'path': Path(script_path).parent.parent.parent / 'tutorial',
'prefix': 'tutorial',
readme: ['md', 'yml'], # Include .md and .yml files
'title': 'WireViz Tutorial',
},
'demos' : {
'path': Path(script_path).parent.parent.parent / 'examples',
'prefix': 'demo',
},
}

input_extensions = ['.yml']
extensions_not_containing_graphviz_output = ['.gv', '.bom.tsv']
extensions_containing_graphviz_output = ['.png', '.svg', '.html']
generated_extensions = extensions_not_containing_graphviz_output + extensions_containing_graphviz_output


def collect_filenames(description, groupkey, ext_list):
path = groups[groupkey]['path']
patterns = [f"{groups[groupkey]['prefix']}*{ext}" for ext in ext_list]
if ext_list != input_extensions and readme in groups[groupkey]:
patterns.append(readme)
print(f'{description} {groupkey} in "{path}"')
return sorted([filename for pattern in patterns for filename in path.glob(pattern)])


def build_generated(groupkeys):
for key in groupkeys:
# preparation
path = groups[key]['path']
build_readme = readme in groups[key]
if build_readme:
include_readme = 'md' in groups[key][readme]
include_source = 'yml' in groups[key][readme]
with open_file_write(path / readme) as out:
out.write(f'# {groups[key]["title"]}\n\n')
# collect and iterate input YAML files
for yaml_file in collect_filenames('Building', key, input_extensions):
print(f' "{yaml_file}"')
wireviz.parse_file(yaml_file)

if build_readme:
i = ''.join(filter(str.isdigit, yaml_file.stem))

with open_file_append(path / readme) as out:
if include_readme:
with open_file_read(yaml_file.with_suffix('.md')) as info:
for line in info:
out.write(line.replace('## ', f'## {i} - '))
out.write('\n\n')
else:
out.write(f'## Example {i}\n')

if include_source:
with open_file_read(yaml_file) as src:
out.write('```yaml\n')
for line in src:
out.write(line)
out.write('```\n')
out.write('\n')

out.write(f'![]({yaml_file.stem}.png)\n\n')
out.write(f'[Source]({yaml_file.name}) - [Bill of Materials]({yaml_file.stem}.bom.tsv)\n\n\n')


def clean_generated(groupkeys):
for key in groupkeys:
# collect and remove files
for filename in collect_filenames('Cleaning', key, generated_extensions):
if filename.is_file():
print(f' rm "{filename}"')
os.remove(filename)


def compare_generated(groupkeys, include_graphviz_output = False):
compare_extensions = generated_extensions if include_graphviz_output else extensions_not_containing_graphviz_output
for key in groupkeys:
# collect and compare files
for filename in collect_filenames('Comparing', key, compare_extensions):
cmd = f'git --no-pager diff "{filename}"'
print(f' {cmd}')
os.system(cmd)


def restore_generated(groupkeys):
for key in groupkeys:
# collect input YAML files
filename_list = collect_filenames('Restoring', key, input_extensions)
# collect files to restore
filename_list = [fn.with_suffix(ext) for fn in filename_list for ext in generated_extensions]
if readme in groups[key]:
filename_list.append(groups[key]['path'] / readme)
# restore files
for filename in filename_list:
cmd = f'git checkout -- "{filename}"'
print(f' {cmd}')
os.system(cmd)


def parse_args():
parser = argparse.ArgumentParser(
description='Wireviz Example Manager',
)
parser.add_argument('action', nargs='?', action='store', default='build')
parser.add_argument('-generate', nargs='*', choices=['examples', 'demos', 'tutorials'], default=['examples', 'demos', 'tutorials'])
parser = argparse.ArgumentParser(description='Wireviz Example Manager',)
parser.add_argument('action', nargs='?', action='store',
choices=['build','clean','compare','restore'], default='build',
help='what to do with the generated files (default: build)')
parser.add_argument('-c', '--compare-graphviz-output', action='store_true',
help='the Graphviz output is also compared (default: False)')
parser.add_argument('-g', '--groups', nargs='+',
choices=groups.keys(), default=groups.keys(),
help='the groups of generated files (default: all)')
Comment on lines +125 to +133
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is by no means mandatory, but if we're using the Click package for CLI parsing in wireviz.py, maybe it makes sense to use it here too, for consistency?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that's why I wrote in the PR description:

  • Run python build_examples.py -h or with --help to print the generated help. The auto-generated help text is not the best, but better than maintaining it manually. I wonder if click as recommended in [feature] Only output the files that user requests #60 (comment) could improve this?

I will have a look at that.

Copy link
Collaborator Author

@kvid kvid Jul 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I have the time to complete this today. Is it better to finish this PR without click?

I have looked into click, but I didn't find a way to implement the --groups option. The documentation is not very detailed. Here is what I have tried so far, but I also got a lot of errors (e.g. SyntaxError: invalid syntax) I couldn't figure out right now:

----------------------------------- setup.py -----------------------------------
index 09621bb..cc5a3da 100644
@@ -24,6 +24,7 @@ setup(
     install_requires=[
         'pyyaml',
         'graphviz',
+        'click',
         ],
     license='GPLv3',
     keywords='cable connector hardware harness wiring wiring-diagram wiring-harness',
@@ -31,7 +32,10 @@ setup(
     package_dir={'': 'src'},
     packages=find_packages('src'),
     entry_points={
-        'console_scripts': ['wireviz=wireviz.wireviz:main'],
+        'console_scripts': [
+            'wireviz=wireviz.wireviz:main',
+            'build_examples=wireviz.build_examples:cli',
+            ],
         },
     classifiers=[
         'Development Status :: 4 - Beta',

------------------------ src/wireviz/build_examples.py ------------------------
index 10a4b1b..97c2726 100755
@@ -4,6 +4,7 @@
 import argparse
 import sys
 import os
+import click
 from pathlib import Path
 
 script_path = Path(__file__).absolute()
@@ -48,6 +49,12 @@ def collect_filenames(description, groupkey, ext_list):
     return sorted([filename for pattern in patterns for filename in path.glob(pattern)])
 
 
+@click.group()
+cli()
+    pass
+
+
+@cli.command()
 def build_generated(groupkeys):
     for key in groupkeys:
         # preparation
@@ -87,6 +94,7 @@ def build_generated(groupkeys):
                     out.write(f'[Source]({yaml_file.name}) - [Bill of Materials]({yaml_file.stem}.bom.tsv)\n\n\n')
 
 
+@cli.command()
 def clean_generated(groupkeys):
     for key in groupkeys:
         # collect and remove files
@@ -96,6 +104,7 @@ def clean_generated(groupkeys):
                 os.remove(filename)
 
 
+@cli.command()
 def compare_generated(groupkeys, include_graphviz_output = False):
     compare_extensions = generated_extensions if include_graphviz_output else extensions_not_containing_graphviz_output
     for key in groupkeys:
@@ -106,6 +115,7 @@ def compare_generated(groupkeys, include_graphviz_output = False):
             os.system(cmd)
 
 
+@cli.command()
 def restore_generated(groupkeys):
     for key in groupkeys:
         # collect input YAML files
@@ -147,4 +157,4 @@ def main():
 
 
 if __name__ == '__main__':
-    main()
+    cli()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the discussion on #123 might help you. It seems click requires one -g for each group you want to include. Not sure if it's a limitation, or a feature, and what reason it might have.

return parser.parse_args()


def main():
args = parse_args()
if args.action == 'build':
generate_types = {
'examples': build_examples,
'demos': build_demos,
'tutorials': build_tutorials
}
for gentype in args.generate:
if gentype in generate_types:
generate_types.get(gentype) ()
build_generated(args.groups)
elif args.action == 'clean':
clean_examples()
clean_generated(args.groups)
elif args.action == 'compare':
compare_generated(args.groups, args.compare_graphviz_output)
elif args.action == 'restore':
restore_generated(args.groups)


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions src/wireviz/wv_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def open_file_read(filename):
def open_file_write(filename):
return open(filename, 'w', encoding='UTF-8')

def open_file_append(filename):
return open(filename, 'a', encoding='UTF-8')

def manufacturer_info_field(manufacturer, mpn):
if manufacturer or mpn:
Expand Down