From 71c7debb7d590c89ff2231a60f2e430e25119e06 Mon Sep 17 00:00:00 2001 From: Gabriel Augendre Date: Tue, 5 Dec 2023 22:44:32 +0100 Subject: [PATCH] add (incomplete) migration script --- .gitignore | 1 + .rtx.toml | 2 + to_ghost.py | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 .rtx.toml create mode 100644 to_ghost.py diff --git a/.gitignore b/.gitignore index 6ff3e3c..b0ce918 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ public/ resources .DS_Store +.venv diff --git a/.rtx.toml b/.rtx.toml new file mode 100644 index 0000000..dcfaa3e --- /dev/null +++ b/.rtx.toml @@ -0,0 +1,2 @@ +[tools] +python = {version="latest", virtualenv=".venv"} diff --git a/to_ghost.py b/to_ghost.py new file mode 100644 index 0000000..b16f3ea --- /dev/null +++ b/to_ghost.py @@ -0,0 +1,151 @@ +import json +import logging +import sys +import time +from pathlib import Path +import frontmatter +import dateparser + +logger = logging.getLogger("to_ghost") +logging.basicConfig(stream=sys.stderr, level=logging.INFO) + + +def main(): + meta = {"exported_on": round(time.time() * 1000), "version": "5.75.0"} + posts = [] + seen_tags = [] + tags = [] + posts_tags = [] + post_id = 0 + for folder in (Path() / "content" / "posts").iterdir(): + if not folder.is_dir(): + logger.info("%s is not dir, ignoring", folder) + continue + + post_id += 1 + index = folder / "index.md" + with index.open("r") as f: + article = frontmatter.load(f) + ghost_post = make_post(article) + if not ghost_post.get("slug"): + ghost_post["slug"] = folder.name + ghost_post["id"] = post_id + posts.append(ghost_post) + + for tag in article.get("tags", []): + if tag not in seen_tags: + seen_tags.append(tag) + tags.append({"id": seen_tags.index(tag), "name": tag}) + posts_tags.append({"tag_id": seen_tags.index(tag), "post_id": post_id}) + + print(json.dumps({"meta": meta, "data": {"posts": posts, "tags": tags, "posts_tags": posts_tags}})) + + +def make_post(article: frontmatter.Post) -> dict: + article_date = article["date"].timestamp() * 1000 + ghost_post = { + "title": article["title"], + # Lexical is used to represent your content + "lexical": "", + "feature_image": None, + "feature_image_alt": None, + "feature_image_caption": None, + "featured": 0, # boolean indicating featured status + "page": 0, # boolean indicating if this is a page or post + "status": "published", # or draft + "published_at": article_date, # epoch time in milliseconds + "published_by": 1, # the first user created has an id of 1 + "meta_title": None, + "meta_description": None, + "email_only": False, # boolean indicating email-only type of post + "author_id": 1, # the first user created has an id of 1 + "created_at": article_date, # epoch time in milliseconds + "created_by": 1, # the first user created has an id of 1 + "updated_at": article_date, # epoch time in milliseconds + "updated_by": 1 # the first user created has an id of 1 + } + if slug := article.get("canonicalURL"): + ghost_post["slug"] = slug + + return ghost_post + + +def convert_date(article_date: str) -> int: + return 0 + + +if __name__ == '__main__': + main() + + +d = { + "meta": { + "exported_on": 1388805572000, # epoch time in milliseconds + "version": "2.14.0" + }, + "data": { + "posts": [ + { + "id": 1, + "title": "my blog post title", + "slug": "my-blog-post-title", # Optional, will be generated by Ghost if missing + # Lexical is used to represent your content + "lexical": "{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Hello, beautiful world! 👋\",\"type\":\"extended-text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"paragraph\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}}", + "feature_image": None, + "feature_image_alt": None, + "feature_image_caption": None, + "featured": 0, # boolean indicating featured status + "page": 0, # boolean indicating if this is a page or post + "status": "published", # or draft + "published_at": 1283780649000, # epoch time in milliseconds + "published_by": 1, # the first user created has an id of 1 + "meta_title": None, + "meta_description": None, + "email_only": False, # boolean indicating email-only type of post + "author_id": 1, # the first user created has an id of 1 + "created_at": 1283780649000, # epoch time in milliseconds + "created_by": 1, # the first user created has an id of 1 + "updated_at": 1286958624000, # epoch time in milliseconds + "updated_by": 1 # the first user created has an id of 1 + } + ], + "tags": [ + { + "id": 5, + "name": "Colorado Ho!", + "slug": "colorado-ho", # Optional, will be generated by Ghost if missing + "description": "" + } + ], + "posts_tags": [ + {"tag_id": 5, "post_id": 1}, + ], + "users": [ + { + "id": 3, + "name": "Jo Bloggs", + "slug": "jo-blogs", # Optional, will be generated by Ghost if missing + "email": "jo@example.com", + "profile_image": None, + "cover_image": None, + "bio": None, + "website": None, + "location": None, + "accessibility": None, + "meta_title": None, + "meta_description": None, + "created_at": 1283780649000, # epoch time in millis + "created_by": 1, + "updated_at": 1286958624000, # epoch time in millis + "updated_by": 1 + } + ], + # Provide this if you want different roles to the default "Author" role + "roles_users": [ + # { + # "user_id": 2, + # "role_id": ObjectId # This must reference the id from your database + # } + ] + } +}