diff --git a/Makefile b/Makefile index 180fa11..1bfc066 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ install: ##### TESTS ##### .PHONY: test # run tests -test: test_pylint clean clone_awesome_selfhosted test_import_awesome_selfhosted test_process_awesome_selfhosted test_export_awesome_selfhosted test_import_shaarli test_download_video +test: test_pylint clean clone_awesome_selfhosted test_import_awesome_selfhosted test_process_awesome_selfhosted test_export_awesome_selfhosted_md test_export_awesome_selfhosted_html test_import_shaarli test_download_video .PHONY: test_pylint # run linter (non blocking) test_pylint: install @@ -41,12 +41,18 @@ test_process_awesome_selfhosted: install hecat --config tests/.hecat.awesome_lint.yml cd awesome-selfhosted-data && git --no-pager diff --color=always -.PHONY: test_export_awesome_selfhosted # test export to singlepage markdown from awesome-selfhosted-data -test_export_awesome_selfhosted: install +.PHONY: test_export_awesome_selfhosted_md # test export to singlepage markdown from awesome-selfhosted-data +test_export_awesome_selfhosted_md: install source .venv/bin/activate && \ hecat --config tests/.hecat.export_markdown_singlepage.yml && \ cd awesome-selfhosted && git --no-pager diff --color=always +.PHONY: test_export_awesome_selfhosted_html # test export to singlepage HTML from awesome-selfhosted-data +test_export_awesome_selfhosted_html: install + mkdir -p awesome-selfhosted-html + source .venv/bin/activate && \ + hecat --config tests/.hecat.export_html_singlepage.yml + .PHONY: test_import_shaarli # test import from shaarli JSON test_import_shaarli: install source .venv/bin/activate && \ diff --git a/hecat/exporters/__init__.py b/hecat/exporters/__init__.py index 96b895b..74bd564 100644 --- a/hecat/exporters/__init__.py +++ b/hecat/exporters/__init__.py @@ -1,2 +1,3 @@ """exporters""" from .markdown_singlepage import render_markdown_singlepage +from .html_singlepage import render_html_singlepage diff --git a/hecat/exporters/html_singlepage.py b/hecat/exporters/html_singlepage.py new file mode 100644 index 0000000..ea80d4e --- /dev/null +++ b/hecat/exporters/html_singlepage.py @@ -0,0 +1,444 @@ +"""export data to single HTML document +$ git clone https://github.com/awesome-selfhosted/awesome-selfhosted-data +$ $EDITOR .hecat.yml +$ hecat + +# .hecat.yml +steps: + - name: export YAML data to single-page HTML + module: exporters/html_singlepage + module_options: + source_directory: awesome-selfhosted-data + output_directory: awesome-selfhosted-html + output_file: index.html # optional, default index.html + authors_file: AUTHORS.md # optional, default no authors file + exclude_licenses: # optional, default [] + - 'CC-BY-NC-4.0' + - '⊘ Proprietary' + - 'SSPL-1.0' + +Output directory structure: +└── index.html +└── TODO + +The source YAML directory structure is documented in markdown_singlepage.py. +Files containing software/platforms data must be formatted as documented in markdown_singlepage.py. +The authors_file, if set, will be generated from the `git shortlog` of your source directory. +""" + +import logging +import ruamel.yaml +from ..utils import load_yaml_data +from jinja2 import Template + +yaml = ruamel.yaml.YAML(typ='safe') +yaml.indent(sequence=4, offset=2) + +HTML_HEAD=""" + + + + + Simple Grid + + + + + + +
+ +""" + +HTML_FOOT = """ +
+ + + +""" + +CSS = """ +/* + Simple Grid + Project Page - http://thisisdallas.github.com/Simple-Grid/ + Author - Dallas Bass + Site - http://dallasbass.com +*/ + + +[class*='grid'], +[class*='col-'], +[class*='mobile-'], +.grid:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +[class*='col-'] { + float: left; + min-height: 1px; + padding-right: 20px; /* column-space */ +} + +[class*='col-'] [class*='col-']:last-child { + padding-right: 0; +} + +.grid { + width: 100%; + max-width: 1140px; + min-width: 748px; /* when using padded grid on ipad in portrait mode, width should be viewport-width - padding = (768 - 20) = 748. actually, it should be even smaller to allow for padding of grid containing element */ + margin: 0 auto; + overflow: hidden; +} + +.grid:after { + content: ""; + display: table; + clear: both; +} + +.grid-pad { + padding-top: 20px; + padding-left: 20px; /* grid-space to left */ + padding-right: 0; /* grid-space to right: (grid-space-left - column-space) e.g. 20px-20px=0 */ +} + +.push-right { + float: right; +} + +/* Content Columns */ + +.col-1-1 { + width: 100%; +} +.col-2-3, .col-8-12 { + width: 66.66%; +} + +.col-1-2, .col-6-12 { + width: 50%; +} + +.col-1-3, .col-4-12 { + width: 33.33%; +} + +.col-1-4, .col-3-12 { + width: 25%; +} + +.col-1-5 { + width: 20%; +} + +.col-1-6, .col-2-12 { + width: 16.667%; +} + +.col-1-7 { + width: 14.28%; +} + +.col-1-8 { + width: 12.5%; +} + +.col-1-9 { + width: 11.1%; +} + +.col-1-10 { + width: 10%; +} + +.col-1-11 { + width: 9.09%; +} + +.col-1-12 { + width: 8.33% +} + +/* Layout Columns */ + +.col-11-12 { + width: 91.66% +} + +.col-10-12 { + width: 83.333%; +} + +.col-9-12 { + width: 75%; +} + +.col-5-12 { + width: 41.66%; +} + +.col-7-12 { + width: 58.33% +} + +/* Pushing blocks */ + +.push-2-3, .push-8-12 { + margin-left: 66.66%; +} + +.push-1-2, .push-6-12 { + margin-left: 50%; +} + +.push-1-3, .push-4-12 { + margin-left: 33.33%; +} + +.push-1-4, .push-3-12 { + margin-left: 25%; +} + +.push-1-5 { + margin-left: 20%; +} + +.push-1-6, .push-2-12 { + margin-left: 16.667%; +} + +.push-1-7 { + margin-left: 14.28%; +} + +.push-1-8 { + margin-left: 12.5%; +} + +.push-1-9 { + margin-left: 11.1%; +} + +.push-1-10 { + margin-left: 10%; +} + +.push-1-11 { + margin-left: 9.09%; +} + +.push-1-12 { + margin-left: 8.33% +} + +@media handheld, only screen and (max-width: 767px) { + .grid { + width: 100%; + min-width: 0; + margin-left: 0; + margin-right: 0; + padding-left: 20px; /* grid-space to left */ + padding-right: 10px; /* grid-space to right: (grid-space-left - column-space) e.g. 20px-10px=10px */ + } + + [class*='col-'] { + width: auto; + float: none; + margin: 10px 0; + padding-left: 0; + padding-right: 10px; /* column-space */ + } + + [class*='col-'] [class*='col-'] { + padding-right: 0; + } + + /* Mobile Layout */ + + [class*='mobile-col-'] { + float: left; + margin: 0 0 10px; + padding-left: 0; + padding-right: 10px; /* column-space */ + padding-bottom: 0; + } + + .mobile-col-1-1 { + width: 100%; + } + .mobile-col-2-3, .mobile-col-8-12 { + width: 66.66%; + } + + .mobile-col-1-2, .mobile-col-6-12 { + width: 50%; + } + + .mobile-col-1-3, .mobile-col-4-12 { + width: 33.33%; + } + + .mobile-col-1-4, .mobile-col-3-12 { + width: 25%; + } + + .mobile-col-1-5 { + width: 20%; + } + + .mobile-col-1-6, .mobile-col-2-12 { + width: 16.667%; + } + + .mobile-col-1-7 { + width: 14.28%; + } + + .mobile-col-1-8 { + width: 12.5%; + } + + .mobile-col-1-9 { + width: 11.1%; + } + + .mobile-col-1-10 { + width: 10%; + } + + .mobile-col-1-11 { + width: 9.09%; + } + + .mobile-col-1-12 { + width: 8.33% + } + + /* Layout Columns */ + + .mobile-col-11-12 { + width: 91.66% + } + + .mobile-col-10-12 { + width: 83.333%; + } + + .mobile-col-9-12 { + width: 75%; + } + + .mobile-col-5-12 { + width: 41.66%; + } + + .mobile-col-7-12 { + width: 58.33% + } + + .hide-on-mobile { + display: none !important; + width: 0; + height: 0; + } +} +""" + +SOFTWARE_JINJA = """ +
+
+ +

{{ software['description'] }}

+ {% for tag in software['tags'] %} {{ tag }}{% endfor %} + {% if software['stargazers_count'] is defined %} {{ software['stargazers_count'] }}{% endif %} +
+
+""" + +def render_html_software(software): + """render a software project info as a HTML list item""" + tm = Template(SOFTWARE_JINJA) + html_software = tm.render(software=software) + return html_software + +def render_html_singlepage(step): + """ + Render a single-page HTML list of all software, in alphabetical order + Prepend/appends the header/footer + """ + tags = load_yaml_data(step['module_options']['source_directory'] + '/tags', sort_key='name') + software_list = load_yaml_data(step['module_options']['source_directory'] + '/software') + licenses = load_yaml_data(step['module_options']['source_directory'] + '/licenses.yml') + markdown_header = open(step['module_options']['source_directory'] + '/markdown/header.md', 'r').read() + markdown_footer = open(step['module_options']['source_directory'] + '/markdown/footer.md', 'r').read() + + + html_software_list = '' + if 'exclude_licenses' not in step['module_options']: + step['module_options']['exclude_licenses'] = [] + if 'output_file' not in step['module_options']: + step['module_options']['output_file'] = 'index.html' + + for software in software_list: + html_software = render_html_software(software) + html_software_list = html_software_list + html_software + html = '{}{}{}'.format(HTML_HEAD, html_software_list, HTML_FOOT) + with open(step['module_options']['output_directory'] + '/' + step['module_options']['output_file'], 'w+', encoding="utf-8") as outfile: + logging.info('writing output file %s', step['module_options']['output_directory'] + '/' + step['module_options']['output_file']) + outfile.write(html) + with open(step['module_options']['output_directory'] + '/simplegrid.css', 'w+', encoding="utf-8") as outfile: + outfile.write(CSS) + exit(1) diff --git a/hecat/main.py b/hecat/main.py index 6fb4992..d224749 100644 --- a/hecat/main.py +++ b/hecat/main.py @@ -33,6 +33,9 @@ def main(): elif step['module'] == 'exporters/markdown_singlepage': from .exporters import render_markdown_singlepage render_markdown_singlepage(step) + elif step['module'] == 'exporters/html_singlepage': + from .exporters import render_html_singlepage + render_html_singlepage(step) else: logging.error('step %s: unknown module %s', step['name'], step['module']) exit(1) diff --git a/setup.py b/setup.py index bc570cc..a24831e 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,8 @@ install_requires=[ 'ruamel.yaml', 'PyGithub', - 'yt_dlp' + 'yt_dlp', + 'jinja2' ], classifiers=[ 'Development Status :: 3 - Alpha', diff --git a/tests/.hecat.export_html_singlepage.yml b/tests/.hecat.export_html_singlepage.yml new file mode 100644 index 0000000..dc39bc3 --- /dev/null +++ b/tests/.hecat.export_html_singlepage.yml @@ -0,0 +1,12 @@ +steps: + - name: export YAML data to single-page HTML + module: exporters/html_singlepage + module_options: + source_directory: awesome-selfhosted-data + output_directory: awesome-selfhosted-html + output_file: index.html # optional, default index.html + authors_file: AUTHORS.md # optional, default no authors file + exclude_licenses: # optional, default [] + - 'CC-BY-NC-4.0' + - '⊘ Proprietary' + - 'SSPL-1.0'