From a1aa6651311da875d3d58609a6a08eff69164c28 Mon Sep 17 00:00:00 2001 From: Danny213123 Date: Thu, 24 Oct 2024 13:16:55 -0400 Subject: [PATCH] automatic grid generation --- blogs/conf.py | 4 +- blogs/generate_grid.py | 519 ++++++++++++++++++++++++++++++++++ blogs/sphinx/requirements.in | 2 +- blogs/sphinx/requirements.txt | 2 +- 4 files changed, 524 insertions(+), 3 deletions(-) create mode 100644 blogs/generate_grid.py diff --git a/blogs/conf.py b/blogs/conf.py index a31084a..39648b6 100644 --- a/blogs/conf.py +++ b/blogs/conf.py @@ -6,7 +6,6 @@ import os import shutil - import ablog import jinja2 from rocm_docs import ROCmDocs @@ -24,6 +23,9 @@ # Jinja templates to render out. templates = [] +# blogs.generate_grid.main() +os.system("python generate_grid.py") + latex_engine = "xelatex" latex_elements = { "fontpkg": r""" diff --git a/blogs/generate_grid.py b/blogs/generate_grid.py new file mode 100644 index 0000000..27e04e8 --- /dev/null +++ b/blogs/generate_grid.py @@ -0,0 +1,519 @@ +# Index.md Generator +# Updated 2024 October 21 + +import os +import re +import yaml +import shutil +from datetime import datetime + +class Blog: + + def __init__(self, file_path, metadata): + + self.file_path = file_path + self.metadata = metadata + + # Dynamically set attributes based on metadata + for key, value in metadata.items(): + + setattr(self, key, value) + + # Ensure the 'date' field exists + if 'date' in metadata: + + self.date = self.parse_date(metadata['date']) + + else: + + self.date = None + + def normalize_date_string(self, date_str): + + # do not remove + date_str = date_str.replace("Sept", "Sep") + + return date_str + + def parse_date(self, date_str): + + # Normalize the date string + date_str = self.normalize_date_string(date_str) + + # Define possible date formats, including string-based months + date_formats = [ + "%d-%m-%Y", # e.g. 8-08-2024 + "%d/%m/%Y", # e.g. 8/08/2024 + "%d-%B-%Y", # e.g. 8-August-2024 + "%d-%b-%Y", # e.g. 8-Aug-2024 + "%d %B %Y", # e.g. 8 August 2024 + "%d %b %Y", # e.g. 8 Aug 2024 + "%d %B, %Y", # e.g. 8 August, 2024 + "%d %b, %Y", # e.g. 8 Aug, 2024 + ] + + for fmt in date_formats: + + try: + return datetime.strptime(date_str, fmt) + except ValueError: + continue + + print(f"Invalid date format in {self.file_path}: {date_str}") + + return None + + def __repr__(self): + + return f"Blog(file_path='{self.file_path}', metadata={self.__dict__})" + + +def find_readme_files(root_dir): + + readme_files = [] + + for dirpath, dirnames, filenames in os.walk(root_dir): + + for filename in filenames: + + if filename.lower() == 'readme.md': # Case-insensitive matching + + full_path = os.path.join(dirpath, filename) + + readme_files.append(full_path) + + return readme_files + + +def extract_metadata(file_path): + + with open(file_path, 'r', encoding='utf-8') as file: + content = file.read() + + # Regular expression to match YAML front matter + yaml_pattern = re.compile(r'^---\s*\n(.*?)\n---\s*\n', re.DOTALL) + + match = yaml_pattern.match(content) + + if match: + + yaml_content = match.group(1) + + try: + + metadata = yaml.safe_load(yaml_content) + + return metadata + + except yaml.YAMLError as e: + + print(f"Error parsing YAML in {file_path}: {e}") + + return None + + else: + + print(f"No metadata found in {file_path}.") + + return None + +def create_blog_objects(readme_files): + + blog_objects = [] + + for file_path in readme_files: + + metadata = extract_metadata(file_path) + + if metadata: + + blog = Blog(file_path, metadata) + + blog_objects.append(blog) + + else: + + print(f"Skipping {file_path}: No valid metadata found.") + + return blog_objects + +def sort_blogs_by_date(blogs): + + # Filter out blogs without valid dates and sort by date + blogs_with_date = [blog for blog in blogs if blog.date is not None] + + return sorted(blogs_with_date, key=lambda blog: blog.date, reverse=True) + +def generate_blog_grid(blogs, output_file='latest_blogs.md', max_blogs=18): + + index_template = """ +--- +title: ROCm Blogs +myst: + html_meta: + "description lang=en": "AMD ROCmâ„¢ software blogs" + "keywords": "AMD GPU, MI300, MI250, ROCm, blog" + "property=og:locale": "en_US" +--- + + + +

AMD ROCmâ„¢ Blogs

+ + + + + +
+

Recent Posts

+ + + +
+ +::::{grid} 1 2 2 3 +:margin 2 +{grid_items} +:::: + +

Stay informed

+ + +""" + + # remove the first new line + index_template = index_template[1:] + + grid_items = [] + author_pages_dir = './blogs/authors' # Directory where author markdown files are stored + + for index, blog in enumerate(blogs[:max_blogs]): + + title = blog.blog_title if hasattr(blog, 'blog_title') else 'No Title' + + date = blog.date.strftime('%B %d, %Y') if blog.date else 'No Date' + + # look at myst description + if hasattr(blog, 'myst'): + + print(blog.myst.get('html_meta').get('description lang=en')) + description = blog.myst.get('html_meta').get('description lang=en') if blog.myst.get('html_meta').get('description lang=en') else 'No Description' + + if len(description) > 150: + description = description[:150] + '...' + else: + # add invisible characters to ensure the card is the same size + description = description + '...' + ' ' * (150 - len(description)) + + # Get authors from the blog (assuming it's a comma-separated string) + authors_list = getattr(blog, 'author', '').split(',') + + # Create href by replacing the .md extension with .html + href = blog.file_path.replace('.md', '.html') + href = href.replace('blogs', '.') + + # Generate an image or use default + image = blog.thumbnail if hasattr(blog, 'thumbnail') else './images/generic.jpg' + + # check if image path is in the correct format + if not image.startswith('./images/'): + + image = './images/' + image + + # check if image is in images directory (blogs/images) + temp_image = image.replace('//', '/').replace('./', 'blogs/') + + if not os.path.exists(temp_image): + + print(f"Image {image} does not exist.") + + image = './images/generic.jpg' + + else: + + print(f"Image {image} exists.") + + # Create authors HTML by checking if an author page exists + author_links = [] + + for author in authors_list: + + # Clean author name and format it correctly for the file system + author_name = author.strip().replace(' ', '-').lower() + + # Path to the author's markdown file in the 'authors' directory + author_file = os.path.join(author_pages_dir, f"{author_name}.md") + + print(f"Checking for author file: {author_file}") # Debug print + + if os.path.exists(author_file): + + # If the author file exists, create a clickable link to the author's page + author_page = author_file.replace('.md', '.html') # Convert .md to .html for the link + + author_page = author_page.replace('blogs', '.') + + author_links.append(f'{author.strip()}') + + else: + + # If no author page exists, display the author's name as plain text + print(f"Author file {author_file} does not exist.") + + author_links.append(author.strip()) + + # Join author links with commas + authors_html = ', '.join(author_links) if author_links else 'Unknown Author' + + # swap href \ to / for windows + href = href.replace('\\', '/') + + # Create grid item card with authors + grid_item = f""" +:::{{grid-item-card}} +:padding: 1 +:link: {href[:href.rfind('.html')]} +:link-type: doc +:img-top: {image} +:class-img-top: small-sd-card-img-top +:class-body: small-sd-card +:class: small-sd-card ++++ + +

{title}

+
+

{description}

+
{date} by {authors_html}
+::: +""" + grid_items.append(grid_item) + + # Join all grid items into one string + grid_content = ''.join(grid_items) + + # Write the grid content to the Markdown file + with open(output_file, 'w', encoding='utf-8') as f: + + f.write(grid_content) + + print(f"Grid content successfully written to {output_file}") + + index_template = index_template.replace('{grid_items}', grid_content) + + index_template = index_template.replace('{datetime}', datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + + # dangerous + + # write new index.md + with open('blogs/index.md', 'w', encoding='utf-8') as f: + + f.write(index_template) + + return index_template + +def main(): + + root_directory = 'blogs' # Specify the root directory + + print(os.getcwd()) + + # change cwd to parent directory + os.chdir('..') + + if not os.path.exists(root_directory): + + print(f"The directory '{root_directory}' does not exist.") + + return + + print(f"Searching for 'readme.md' files in '{root_directory}' and subdirectories...") + + readme_files = find_readme_files(root_directory) + + if not readme_files: + + print("No 'readme.md' files found.") + + return + + print(f"Found {len(readme_files)} 'readme.md' file(s).") + + blogs = create_blog_objects(readme_files) + + # Sort blogs by date + sorted_blogs = sort_blogs_by_date(blogs) + + for blog in sorted_blogs: + + if hasattr(blog, 'author'): + + print(blog.author) + + # Generate the grid for the top 15 latest blogs + generate_blog_grid(sorted_blogs, max_blogs=18) + + # change back working directory + os.chdir('blogs') + +if __name__ == "__main__": + main() diff --git a/blogs/sphinx/requirements.in b/blogs/sphinx/requirements.in index ead0d4e..00eb966 100644 --- a/blogs/sphinx/requirements.in +++ b/blogs/sphinx/requirements.in @@ -1,3 +1,3 @@ ablog -rocm-docs-core==1.8.2 +rocm-docs-core==1.8.3 sphinx-hoverxref diff --git a/blogs/sphinx/requirements.txt b/blogs/sphinx/requirements.txt index 12ca759..c61cdf5 100644 --- a/blogs/sphinx/requirements.txt +++ b/blogs/sphinx/requirements.txt @@ -106,7 +106,7 @@ requests==2.32.2 # via # pygithub # sphinx -rocm-docs-core==1.8.2 +rocm-docs-core==1.8.3 # via -r requirements.in six==1.16.0 # via python-dateutil