195 lines
6.9 KiB
Python
Executable file
195 lines
6.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# Script to manage articles.
|
|
#
|
|
# License: MIT License
|
|
# Copyright: (c) 2024 Awiteb <a@4rs.nl>
|
|
|
|
|
|
import sys
|
|
import os
|
|
import datetime
|
|
|
|
|
|
def help(command: str | None) -> str:
|
|
if command is None:
|
|
help_message = """Script to manage articles.
|
|
Usage: ./scripts/article.py <command>
|
|
|
|
Commands:
|
|
- update: Update the header of an article or add a new header
|
|
- new: Create a new article
|
|
- card: Create a new social card for an article
|
|
- help: Folloing by a command, it will display the help of the command
|
|
"""
|
|
elif command == "update":
|
|
help_message = """Update the header of an article or add a new header
|
|
Usage: ./scripts/article.py update <article_name> <header_scope> <header_name> <header_value>
|
|
Example: ./scripts/article.py update "hello-world" global "title" "Hello World"
|
|
Header scopes: [global, taxonomies, extra]
|
|
"""
|
|
elif command == "new":
|
|
help_message = """Create a new article
|
|
Usage: ./scripts/article.py new <article_name>
|
|
Example: ./scripts/article.py new "hello-world"
|
|
"""
|
|
elif command == "card":
|
|
help_message = """Create a new social card for an article
|
|
Usage: ./scripts/article.py card <article_name>
|
|
Example: ./scripts/article.py card "hello-world"
|
|
"""
|
|
else:
|
|
help_message = f"The command `{command}` is not recognized. Available commands: [update, help]"
|
|
return help_message
|
|
|
|
def now() -> str:
|
|
"""Return the current date in the format "YYYY-MM-DD"""
|
|
return datetime.datetime.now().strftime("%Y-%m-%d")
|
|
|
|
class Article:
|
|
def __init__(self, name: str) -> None:
|
|
self.name = name
|
|
self.path = f"content/b/{name}/index.md"
|
|
self.headers = {
|
|
"global": {},
|
|
"taxonomies": {},
|
|
"extra": {},
|
|
}
|
|
self.content = ""
|
|
|
|
self.__reload_header()
|
|
self.__reload_content()
|
|
|
|
def __reload_header(self) -> None:
|
|
"""Reloads the content of the article
|
|
|
|
The header of the article is between the first and second "+++" in the file.
|
|
"""
|
|
if not os.path.exists(self.path):
|
|
return
|
|
|
|
with open(self.path, "r") as file:
|
|
current_header = "global"
|
|
|
|
for idx, line in enumerate(file):
|
|
if line == "+++\n" and idx > 0:
|
|
break
|
|
if (line == "+++\n" and idx == 0) or line.isspace():
|
|
continue
|
|
|
|
# Update the current header if the line is a header
|
|
if line.startswith("[") and line.endswith("]\n"):
|
|
current_header = line[1:-2]
|
|
continue
|
|
|
|
if len((splited_line := line.split("="))) == 2:
|
|
key, value = (splited_line[0], splited_line[1])
|
|
self.headers[current_header][key.strip()] = value.strip()
|
|
|
|
def __reload_content(self) -> None:
|
|
"""Reloads the content of the article"""
|
|
if not os.path.exists(self.path):
|
|
return
|
|
|
|
with open(self.path, "r") as file:
|
|
content = file.read()
|
|
# Remove the header from the file, and keep the rest as the content of the article
|
|
self.content = content[content.index("+++", 3) + 3 :].strip() + "\n"
|
|
|
|
def export(self) -> None:
|
|
"""Rewrite the file with the updated headers and content"""
|
|
if not os.path.exists(self.path):
|
|
os.makedirs(os.path.dirname(self.path), exist_ok=True)
|
|
with open(self.path, "w") as file:
|
|
file.write("+++\n")
|
|
for scope, headers in self.headers.items():
|
|
if scope != "global":
|
|
file.write(f"[{scope}]\n")
|
|
for key, value in headers.items():
|
|
file.write(f"{key} = {value}\n")
|
|
file.write("\n")
|
|
file.write("+++\n")
|
|
file.write(self.content)
|
|
|
|
|
|
def update_header(
|
|
article_name: str, header_scope: str, header_name: str, header_value: str
|
|
) -> None:
|
|
if header_scope not in ["global", "taxonomies", "extra"]:
|
|
print(
|
|
f"The header scope {header_scope} is not recognized. Available scopes: [global, taxonomies, extra]"
|
|
)
|
|
sys.exit(1)
|
|
article = Article(article_name)
|
|
if (
|
|
article.headers[header_scope].get(header_name) is not None
|
|
and article.headers[header_scope][header_name].startswith('"')
|
|
) or all([c.isalpha() or c.isspace() for c in header_value]):
|
|
header_value = f'"{header_value}"'
|
|
article.headers[header_scope][header_name] = header_value
|
|
article.export()
|
|
os.system("shot-scraper --silent 'http://127.0.0.1:1111/' -w 700 -h 400 --retina --quality 60 -o static/img/social_cards/index.jpg")
|
|
print("The header has been updated successfully.")
|
|
|
|
|
|
def create_new_article(article_name: str) -> None:
|
|
article = Article(article_name)
|
|
if os.path.exists(article.path):
|
|
print(f"The article {article_name} already exists.")
|
|
sys.exit(1)
|
|
article.headers["global"]["title"] = '"Title"'
|
|
article.headers["global"]["description"] = '"Description"'
|
|
article.headers["global"]["date"] = now()
|
|
article.headers["global"]["updated"] = now()
|
|
article.headers["global"]["draft"] = "true"
|
|
|
|
article.headers["taxonomies"]["tags"] = '["tag1", "tag2"]'
|
|
|
|
article.content = "Write your content here."
|
|
article.export()
|
|
print(f"The article {article_name} has been created successfully.")
|
|
print(f"Path: {article.path}")
|
|
|
|
|
|
def create_social_card(article_name: str) -> None:
|
|
article = Article(article_name)
|
|
if not os.path.exists(article.path):
|
|
print(f"The article {article_name} does not exist.")
|
|
sys.exit(1)
|
|
os.system(
|
|
f"./scripts/social-cards-zola -i b/{article_name}/index.md -b http://127.0.0.1:1111 -o static/img/social_cards/"
|
|
)
|
|
print(f"The social card for the article {article_name} has been created successfully.")
|
|
print(f"The url path is: /img/social_cards/b_{article_name.replace('-', '_')}.jpg")
|
|
|
|
|
|
def main() -> None:
|
|
# Check the current working directory, it should be the root of the project
|
|
if not (
|
|
"content" in os.listdir()
|
|
and "scripts" in os.listdir()
|
|
and "static" in os.listdir()
|
|
):
|
|
print("The script should be executed from the root of the project.")
|
|
sys.exit(1)
|
|
|
|
if len(sys.argv) < 2:
|
|
print(help(None))
|
|
sys.exit(1)
|
|
elif sys.argv[1] == "help" and len(sys.argv) == 3:
|
|
print(help(sys.argv[2]))
|
|
elif sys.argv[1] == "update" and len(sys.argv) == 6:
|
|
article_name, header_scope, header_name, header_value = sys.argv[2:]
|
|
update_header(article_name, header_scope, header_name, header_value)
|
|
elif sys.argv[1] == "new" and len(sys.argv) == 3:
|
|
create_new_article(sys.argv[2])
|
|
elif sys.argv[1] == "card" and len(sys.argv) == 3:
|
|
create_social_card(sys.argv[2])
|
|
else:
|
|
print(help(None))
|
|
sys.exit(1)
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|