Compare commits

..

No commits in common. "main" and "add-actions" have entirely different histories.

4 changed files with 82 additions and 155 deletions

View file

@ -6,13 +6,15 @@ SPDX-License-Identifier: MIT
This folder contains scripts that can be run to create requirement screenshots for all of the learn guide projects
To use the scripts you must set the environment `LEARN_GUIDE_REPO` to point to the location of learn guide repo. It must end with a trailing slash.
To use the scripts you must set `LEARN_GUIDE_REPO` inside of `get_imports.py` to point to the location of learn guide repo.
The default value is `"../Adafruit_Learning_System_Guides/"`, one directory above the root of this repo.
default value is `"../../Adafruit_Learning_System_Guides/"`
One directory above the root of this repo.
With that pointed at a learn guide repo you can run:
```
python3 create_requirement_images.py
python create_requirement_images.py
```
It will create images in the `generated_images` directory.

View file

@ -11,15 +11,13 @@ Create requirement screenshots for learn guide projects
from multiprocessing import Pool
import json
import os
import traceback
import click
from PIL import Image, ImageDraw, ImageFont
from get_imports import (
get_libs_for_project,
get_files_for_project,
get_libs_for_example,
get_files_for_example,
get_learn_guide_cp_projects,
)
@ -42,26 +40,20 @@ f = open("latest_bundle_data.json", "r")
bundle_data = json.load(f)
f.close()
font = ImageFont.truetype("Roboto-Regular.ttf", 24)
right_triangle = Image.open("img/right_triangle.png")
down_triangle = Image.open("img/down_triangle.png")
def asset_path(x):
"""Return the location of a file shipped with the screenshot maker"""
return os.path.join(os.path.dirname(__file__), x)
folder_icon = Image.open("img/folder.png")
folder_hidden_icon = Image.open("img/folder_hidden.png")
file_icon = Image.open("img/file.png")
file_hidden_icon = Image.open("img/file_hidden.png")
file_empty_icon = Image.open("img/file_empty.png")
file_empty_hidden_icon = Image.open("img/file_empty_hidden.png")
font = ImageFont.truetype(asset_path("Roboto-Regular.ttf"), 24)
right_triangle = Image.open(asset_path("img/right_triangle.png"))
down_triangle = Image.open(asset_path("img/down_triangle.png"))
folder_icon = Image.open(asset_path("img/folder.png"))
folder_hidden_icon = Image.open(asset_path("img/folder_hidden.png"))
file_icon = Image.open(asset_path("img/file.png"))
file_hidden_icon = Image.open(asset_path("img/file_hidden.png"))
file_empty_icon = Image.open(asset_path("img/file_empty.png"))
file_empty_hidden_icon = Image.open(asset_path("img/file_empty_hidden.png"))
file_image_icon = Image.open(asset_path("img/file_image.png"))
file_music_icon = Image.open(asset_path("img/file_music.png"))
file_font_icon = Image.open(asset_path("img/file_font.png"))
file_image_icon = Image.open("img/file_image.png")
file_music_icon = Image.open("img/file_music.png")
file_font_icon = Image.open("img/file_font.png")
FILE_TYPE_ICON_MAP = {
"py": file_icon,
@ -81,7 +73,7 @@ for file_icon in FILE_TYPE_ICON_MAP.values():
def generate_requirement_image(
project_files, libs, image_name
learn_guide_project,
): # pylint: disable=too-many-statements
"""Generate a single requirement image"""
@ -157,7 +149,7 @@ def generate_requirement_image(
font=font,
)
def make_header(position, project_files):
def make_header(position, learn_guide_project):
# Static files
make_line("CIRCUITPY", position)
make_line(
@ -189,6 +181,7 @@ def generate_requirement_image(
)
# dynamic files from project dir in learn guide repo
project_files = get_files_for_project(learn_guide_project)
rows_added = 0
project_files_to_draw = []
project_folders_to_draw = []
@ -196,10 +189,16 @@ def generate_requirement_image(
if "." in cur_file[-5:]:
cur_extension = cur_file.split(".")[-1]
if cur_extension in SHOWN_FILETYPES:
project_files_to_draw.append(cur_file)
if cur_file != "main.py":
project_files_to_draw.append(cur_file)
else:
project_folders_to_draw.append(cur_file)
try:
project_files_to_draw.remove("code.py")
except ValueError:
pass
for i, file in enumerate(sorted(project_files_to_draw)):
cur_file_extension = file.split(".")[-1]
@ -292,87 +291,48 @@ def generate_requirement_image(
triangle_icon=triangle_icon,
)
final_list_to_render = sort_libraries(libs)
try:
libs = get_libs_for_project(learn_guide_project)
final_list_to_render = sort_libraries(libs)
if "code.py" in project_files:
project_files.remove("code.py")
project_file_list = get_files_for_project(learn_guide_project)
if "main.py" in project_files:
project_files.remove("main.py")
project_files_count = len(project_file_list)
project_files_count = len(project_files)
if "code.py" in project_file_list:
project_files_count -= 1
image_height = (
PADDING * 2
+ 7 * LINE_SPACING
+ len(final_list_to_render) * LINE_SPACING
+ (project_files_count) * LINE_SPACING
)
img = Image.new("RGB", (OUT_WIDTH, image_height), "#303030")
draw = ImageDraw.Draw(img)
if "main.py" in project_file_list:
project_files_count -= 1
make_background_highlights(
7 + len(final_list_to_render) + project_files_count,
offset=(PADDING, PADDING),
)
image_height = (
PADDING * 2
+ 7 * LINE_SPACING
+ len(final_list_to_render) * LINE_SPACING
+ (project_files_count) * LINE_SPACING
)
img = Image.new("RGB", (OUT_WIDTH, image_height), "#303030")
draw = ImageDraw.Draw(img)
make_header((PADDING, PADDING), project_files)
make_libraries(
final_list_to_render,
(PADDING, PADDING + (LINE_SPACING * (7 + project_files_count))),
)
make_background_highlights(
7 + len(final_list_to_render) + project_files_count,
offset=(PADDING, PADDING),
)
img.save("generated_images/{}.png".format(image_name))
make_header((PADDING, PADDING), learn_guide_project)
make_libraries(
final_list_to_render,
(PADDING, PADDING + (LINE_SPACING * (7 + project_files_count))),
)
def generate_learn_requirement_image( # pylint: disable=invalid-name
learn_guide_project,
):
"""Generate an image for a single learn project"""
image_name = learn_guide_project.replace("/", "_")
libs = get_libs_for_project(learn_guide_project)
project_files = get_files_for_project(learn_guide_project)
generate_requirement_image(project_files, libs, image_name)
def generate_example_requirement_image(example_path): # pylint: disable=invalid-name
"""Generate an image for a library example"""
image_name = "_".join(
element
for element in example_path.split("/")
if element not in ("libraries", "drivers", "helpers", "examples")
)
libs = get_libs_for_example(example_path)
project_files = get_files_for_example(example_path)
generate_requirement_image(project_files, libs, image_name)
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
"""Main entry point; invokes the learn subcommand if nothing is specified"""
if ctx.invoked_subcommand is None:
learn()
@cli.command()
def learn():
"""Generate images for a learn-style repo"""
with Pool() as pool:
for _ in pool.imap(
generate_learn_requirement_image, get_learn_guide_cp_projects()
):
pass
@cli.command()
@click.argument("paths", nargs=-1)
def bundle(paths):
"""Generate images for a bundle-style repo"""
with Pool() as pool:
for _ in pool.imap(generate_example_requirement_image, paths):
pass
img.save("generated_images/{}.png".format(learn_guide_project))
except SyntaxError as exc:
print(exc)
traceback.print_exc()
print("SyntaxError finding imports for {}".format(learn_guide_project))
if __name__ == "__main__":
cli() # pylint: disable=no-value-for-parameter
with Pool() as p:
for _ in p.imap(generate_requirement_image, get_learn_guide_cp_projects()):
pass

View file

@ -16,11 +16,10 @@ BUNDLE_DATA = "latest_bundle_data.json"
BUNDLE_TAG = "latest_bundle_tag.json"
LEARN_GUIDE_REPO = os.environ.get(
"LEARN_GUIDE_REPO", "../Adafruit_Learning_System_Guides/"
"LEARN_GUIDE_REPO", "../../Adafruit_Learning_System_Guides/"
)
SHOWN_FILETYPES = ["py", "mpy", "bmp", "pcf", "bdf", "wav", "mp3", "json", "txt"]
SHOWN_FILETYPES_EXAMPLE = [s for s in SHOWN_FILETYPES if s != "py"]
def get_bundle(tag):
@ -141,61 +140,28 @@ def get_libs_for_project(project_name):
return found_libs
def get_files_for_example(example_path):
"""Get the set of files for a library example"""
found_files = set(("code.py",))
example_dir = os.path.dirname(example_path)
for file in os.listdir(example_dir):
if "." in file:
cur_extension = file.split(".")[-1]
if cur_extension in SHOWN_FILETYPES_EXAMPLE:
# print(file)
found_files.add(file)
else:
# add dir
found_files.add(file)
return found_files
def get_libs_for_example(example_path):
"""Get the set of libraries for a library example"""
found_libs = set()
found_imports = []
found_imports = findimports.find_imports(example_path)
for cur_import in found_imports:
cur_lib = cur_import.name.split(".")[0]
if cur_lib in bundle_data:
found_libs.add(cur_lib)
return found_libs
def get_learn_guide_projects():
"""Get the list of all folders in the learn guide"""
return os.listdir(LEARN_GUIDE_REPO)
def get_learn_guide_cp_projects():
"""Get the list of all circuitpython projects, according to some heuristics"""
for dirpath, dirnames, filenames in os.walk(LEARN_GUIDE_REPO):
# Don't consider hidden directories
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
cp_projects = []
# The top-level needs special treatment
if dirpath == LEARN_GUIDE_REPO:
continue
def has_py_file(location):
dir_files = os.listdir(location)
for file in dir_files:
if file.endswith(".py"):
return ".circuitpython.skip" not in dir_files
return False
# Skip this folder and all subfolders
if ".circuitpython.skip" in filenames:
del dirnames[:]
continue
# Skip files in this folder, but handle sub-folders
if ".circuitpython.skip-here" in filenames:
continue
# Do not reurse, but handle files in this folder
if ".circuitpython.skip-sub" in filenames:
del dirnames[:]
if any(f for f in filenames if f.endswith(".py")):
yield os.path.relpath(dirpath, LEARN_GUIDE_REPO)
if __name__ == "__main__":
for p in get_learn_guide_cp_projects():
print("PROJECT", p)
all_projects = get_learn_guide_projects()
for project in all_projects:
project_dir = "{}/{}/".format(LEARN_GUIDE_REPO, project)
try:
if has_py_file(project_dir):
cp_projects.append(project)
except NotADirectoryError:
pass
return cp_projects

View file

@ -1,4 +1,3 @@
findimports
pillow
requests
click