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

Refactor readme_builder.py #61

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
252 changes: 133 additions & 119 deletions readme_builder.py
Original file line number Diff line number Diff line change
@@ -1,120 +1,88 @@
#!/usr/bin/env python3
import shutil
from pathlib import Path
from urllib.parse import quote


def build_index():
talks, max_talk_lengths, folder_names = get_talks_metadata()

# Add extra space to save space for hyperlinks syntax: ``_
max_talk_date_length = max_talk_lengths[0] + 10
max_talk_title_length = max_talk_lengths[1] + 10
max_talk_type_length = max_talk_lengths[2] + 10
max_talk_speaker_length = max_talk_lengths[3] + 10

with open('./docs/index.rst', 'w') as indexfile:
separator = (
'='*max_talk_date_length + ' ' +
'='*max_talk_title_length + ' ' +
'='*max_talk_type_length + ' ' +
'='*max_talk_speaker_length + '\n'
)
indexfile.write("\n")
indexfile.write(separator)
indexfile.write(
'Fecha'.ljust(max_talk_date_length) + ' ' +
'Título'.ljust(max_talk_title_length) + ' ' +
'Tipo'.ljust(max_talk_type_length) + ' ' +
'Ponente'.ljust(max_talk_speaker_length) + '\n'
)
indexfile.write(separator)

hyperlinks = []
for index, talk in enumerate(talks):
date, title, talk_type, speaker = talk

# Add hyperlink to title
title_with_link = "`" + title + "`_"
indexfile.write(
date.ljust(max_talk_date_length) + ' ' +
title_with_link.ljust(max_talk_title_length) + ' ' +
talk_type.ljust(max_talk_type_length) + ' ' +
speaker.ljust(max_talk_speaker_length) + '\n'
)
hyperlinks.append(".. _`" + title + "`: " + quote(folder_names[index]))

indexfile.write(separator)
indexfile.write("\n")

for hyperlink in hyperlinks:
indexfile.write(hyperlink + "\n")


def get_talks_metadata():
talks = []
talk_date_length = []
talk_title_length = []
talk_type_length = []
talk_speaker_length = []

folder_names = get_talk_folder_names()

for folder_name in folder_names:
name_items = folder_name.split(' - ')
talk_date = name_items[0]
talk_title = name_items[1][0:name_items[1].rfind('[')].strip()
talk_type = name_items[1][name_items[1].rfind('[')+1:-1]
talk_speaker = name_items[2]
talks.append([talk_date, talk_title, talk_type, talk_speaker])
# Keep length of each field (to later get the max)
talk_date_length.append(len(talk_date))
talk_title_length.append(len(talk_title))
talk_type_length.append(len(talk_type))
talk_speaker_length.append(len(talk_speaker))

talk_fields_length = [
talk_date_length,
talk_title_length,
talk_type_length,
talk_speaker_length
]
max_field_lengths = max_talk_metadata_field_length(talk_fields_length)
return talks, max_field_lengths, folder_names


def get_talk_folder_names():
folder_names = [str(x) for x in sorted(Path('.').iterdir(), reverse=True)
if x.is_dir() and not str(x).startswith(('.', '_', 'docs'))]
return folder_names


def max_talk_metadata_field_length(talk_field_lengths):
max_talk_date_length = max(talk_field_lengths[0])
max_talk_title_length = max(talk_field_lengths[1])
max_talk_type_length = max(talk_field_lengths[2])
max_talk_speaker_length = max(talk_field_lengths[3])
max_lengths = [
max_talk_date_length,
max_talk_title_length,
max_talk_type_length,
max_talk_speaker_length
]
return max_lengths


def build_readme():
build_index()
with open('README.rst', 'wb') as readme:
# Write header
readme.write(get_readme_header())
# Concatenate index and description
for file in ['description.rst', 'index.rst']:
with open('docs/'+file, 'rb') as fd:
shutil.copyfileobj(fd, readme)


def get_readme_header():
import pathlib
import re
import typing
from dataclasses import dataclass

class TalkParseError(Exception):
"""Exception raised when parsing a talk directory name fails."""
pass

TALK_TYPE_ICONS = {
"lightning": "⚡",
"relámpago": "⚡",
"charla": "🗣️",
"talk": "🗣️",
"workshop": "🛠️",
"taller": "🛠️"
}

@dataclass
class Talk:
"""
Represents a talk with its details.
"""
date: str
title: str
talk_type: str
speaker: str

@classmethod
def from_directory_name(cls, dir_name: str) -> 'Talk':
"""
Create a Talk object from a directory name.

Args:
dir_name (str): The name of the directory to parse.

Returns:
Talk: A Talk object with extracted information.

Raises:
TalkParseError: If the directory name doesn't match the expected format.
"""
pattern = r"(\d{4}-\d{2}-\d{2}) - (.+) \[(.+)\] - (.+)"
match = re.match(pattern, dir_name)
if match:
return cls(*match.groups())
raise TalkParseError(f"❌ Failed to parse: {dir_name}")

def to_rst_row(self) -> str:
"""
Convert the Talk object to an RST table row.

Returns:
str: RST formatted table row.
"""
icon = TALK_TYPE_ICONS.get(self.talk_type.lower(), "❓")
tooltip = f':tooltip:`<span title="{self.talk_type}">{icon}</span>`'
return f" * - {self.date}\n - {tooltip}\n - {self.title}\n - {self.speaker}\n"

def read_description_file(repo_path: pathlib.Path) -> str:
"""
Read the content of docs/description.rst file.

Args:
repo_path (pathlib.Path): Path to the repository root.

Returns:
str: Content of the description file or empty string if file not found.
"""
description_path = repo_path / "docs" / "description.rst"
if description_path.exists():
return description_path.read_text(encoding="utf-8")
return ""

def generate_readme_content(talks: typing.List[Talk], repo_path: pathlib.Path) -> str:
"""
Generate the content for the README.rst file.

Args:
talks (List[Talk]): List of Talk objects.
repo_path (pathlib.Path): Path to the repository root.

Returns:
str: The content for the README.rst file.
"""
header = b""".. |travis_badge| image:: https://travis-ci.com/python-vigo/charlas.svg?branch=master
:target: https://travis-ci.com/python-vigo/charlas

Expand All @@ -124,7 +92,53 @@ def get_readme_header():


"""
return header

description = read_description_file(repo_path)

content = [
header.decode('utf-8'),
description,
".. role:: tooltip(raw)",
" :format: html",
"",
".. list-table:: Example Table",
" :header-rows: 1",
"",
" * - Fecha",
" - Tipo",
" - Título",
" - Ponente",
""
]

for talk in talks:
content.append(talk.to_rst_row())

return "\n".join(content)

def main() -> None:
"""
Main function to read directories and create README.rst file.
"""
repo_path = pathlib.Path.cwd()
talks = []

# Sort directories in reverse order (newest first)
for dir_path in sorted(repo_path.iterdir(), reverse=True):
if not dir_path.is_dir() or dir_path.name.startswith(".") or dir_path.name in ["docs"]:
continue
try:
talk = Talk.from_directory_name(dir_path.name)
talks.append(talk)
except TalkParseError as e:
print(str(e))

readme_content = generate_readme_content(talks, repo_path)

readme_path = repo_path / "README.rst"
readme_path.write_text(readme_content, encoding="utf-8")

print(f"README.rst has been created at {readme_path}")

build_readme()
if __name__ == "__main__":
main()