wordpress-to-markdown 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ """Export WordPress content to Markdown."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from wordpress_to_markdown.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+ import yaml
3
+ from markdownify import markdownify
4
+
5
+ from wordpress_to_markdown.core.models import Post, DocumentUnit
6
+
7
+
8
+ def assemble(posts: list[Post],
9
+ mode: str = "per_post",
10
+ verbose: bool = False,
11
+ include_frontmatter: bool = False,
12
+ include_excerpt: bool = False,
13
+ content_format: str = "markdown",
14
+ ) -> list[DocumentUnit]:
15
+ """
16
+ Assemble processed posts into write-ready document units.
17
+ """
18
+ if content_format not in {"markdown", "html"}:
19
+ raise ValueError(f"Unsupported content format: {content_format}")
20
+
21
+ if mode == "per_post":
22
+ return _assemble_per_post(
23
+ posts,
24
+ verbose=verbose,
25
+ include_frontmatter=include_frontmatter,
26
+ include_excerpt=include_excerpt,
27
+ content_format=content_format,
28
+ )
29
+
30
+ if mode == "book":
31
+ return _assemble_book(
32
+ posts,
33
+ verbose=verbose,
34
+ include_frontmatter=include_frontmatter,
35
+ include_excerpt=include_excerpt,
36
+ content_format=content_format,
37
+ )
38
+
39
+ raise ValueError(f"Unsupported assembly mode: {mode}")
40
+
41
+
42
+ def _process_frontmatter(data: dict) -> str:
43
+ yaml_str = yaml.dump(data, sort_keys=False, allow_unicode=True)
44
+ return f"---\n{yaml_str}---\n"
45
+
46
+
47
+ def _process_content(
48
+ post: Post,
49
+ include_excerpt: bool = False,
50
+ content_format: str = "markdown",
51
+ ) -> str:
52
+ convert = markdownify if content_format == "markdown" else lambda value: value
53
+ content = convert(post.content)
54
+ if include_excerpt and post.excerpt:
55
+ excerpt = convert(post.excerpt).strip()
56
+ if excerpt:
57
+ return f"{excerpt}\n\n{content}"
58
+ return content
59
+
60
+
61
+ def _assemble_per_post(posts: list[Post],
62
+ verbose: bool = False,
63
+ include_frontmatter: bool = False,
64
+ include_excerpt: bool = False,
65
+ content_format: str = "markdown",
66
+ ) -> list[DocumentUnit]:
67
+ """
68
+ Convert each post into its own DocumentUnit.
69
+ """
70
+ units: list[DocumentUnit] = []
71
+
72
+ for post in posts:
73
+ if include_frontmatter:
74
+ frontmatter_str = _process_frontmatter(post.frontmatter)
75
+ filename = f"{post.slug or 'untitled'}.md"
76
+
77
+ unit = DocumentUnit(
78
+ filename=filename,
79
+ title=post.title,
80
+ content=_process_content(post, include_excerpt=include_excerpt, content_format=content_format),
81
+ frontmatter=frontmatter_str if include_frontmatter else "",
82
+ meta = {
83
+ **post.meta,
84
+ "date": post.date,
85
+ "slug": post.slug,
86
+ }
87
+ )
88
+ units.append(unit)
89
+
90
+ if verbose:
91
+ print(f"Assembled: {filename}")
92
+
93
+ return units
94
+
95
+ def _assemble_book(posts: list[Post],
96
+ verbose: bool = False,
97
+ filename: str = "book.md",
98
+ include_frontmatter: bool = False,
99
+ include_excerpt: bool = False,
100
+ content_format: str = "markdown",
101
+ ) -> list[DocumentUnit]:
102
+ """
103
+ Convert all posts into a single DocumentUnit.
104
+ """
105
+
106
+ FILENAME = filename
107
+ book_content : list[str] = []
108
+
109
+ for post in posts:
110
+ frontmatter_prefix = ""
111
+ if include_frontmatter:
112
+ frontmatter_prefix = f"{_process_frontmatter(post.frontmatter)}\n"
113
+
114
+ content = _process_content(
115
+ post,
116
+ include_excerpt=include_excerpt,
117
+ content_format=content_format,
118
+ )
119
+ title = post.title
120
+ book_content.append(f"# {title}\n\n{frontmatter_prefix}{content}")
121
+
122
+ if verbose:
123
+ print(f"Assembled: {filename}")
124
+
125
+ book = DocumentUnit(
126
+ filename=FILENAME,
127
+ content="\n\n".join(book_content)
128
+ )
129
+
130
+ return list([book])
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+
4
+ from wordpress_to_markdown.config.loader import load_config
5
+ from wordpress_to_markdown.importer.wordpress_api import ensure_json_export
6
+ from wordpress_to_markdown.sources.imported_json import load_imported_json
7
+ from wordpress_to_markdown.processors.pipeline import process_posts
8
+ from wordpress_to_markdown.assembler import assemble
9
+ from wordpress_to_markdown.writer import write
10
+
11
+ import argparse
12
+
13
+
14
+ def parse_args(argv: list[str] | None = None):
15
+ parser = argparse.ArgumentParser()
16
+ parser.add_argument("--config", default="config.json", help="Path to configuration JSON file")
17
+
18
+ parser.add_argument(
19
+ "--order",
20
+ choices=["newest", "oldest"],
21
+ default="newest",
22
+ help="Order posts by date, when pulling from JSON"
23
+ )
24
+
25
+ parser.add_argument(
26
+ "--write_order",
27
+ choices=["newest", "oldest"],
28
+ default="oldest",
29
+ help="Order posts by date, when writing to document"
30
+ )
31
+
32
+ parser.add_argument(
33
+ "--limit",
34
+ type=int,
35
+ help="Limit number of posts"
36
+ )
37
+
38
+ parser.add_argument(
39
+ "--refresh",
40
+ action="store_true",
41
+ help="Fetch fresh data from WordPress"
42
+ )
43
+
44
+ parser.add_argument(
45
+ "--mode",
46
+ choices=["per_post", "book"],
47
+ default="per_post",
48
+ help="Output mode"
49
+ )
50
+
51
+ parser.add_argument(
52
+ "--frontmatter",
53
+ action="store_true",
54
+ help="Include frontmatter"
55
+ )
56
+
57
+ parser.add_argument(
58
+ "--excerpt",
59
+ action="store_true",
60
+ default=False,
61
+ help="Include the WordPress excerpt before post content"
62
+ )
63
+
64
+
65
+ parser.add_argument(
66
+ "--content-format", choices=["markdown", "html"], default="markdown",
67
+ help="Format post and excerpt content (default: markdown)"
68
+ )
69
+
70
+ return parser.parse_args(argv)
71
+
72
+
73
+ def order_and_limit_posts(posts, order: str = "newest", limit: int | None = None):
74
+ # sort posts by date order, for pulling
75
+ reverse = order == "newest"
76
+ posts = sorted(posts, key=lambda p: p.date or "", reverse=reverse)
77
+
78
+ # limit the number of posts
79
+ if limit is not None:
80
+ posts = posts[:limit]
81
+
82
+ return posts
83
+
84
+ def write_order(posts, order: str = "oldest", limit: int | None = None):
85
+ # sort posts by date order for writing
86
+ reverse = order == "newest"
87
+ posts = sorted(posts, key=lambda p: p.date or "", reverse=reverse)
88
+
89
+ return posts
90
+
91
+
92
+ def main() -> None:
93
+ args = parse_args()
94
+ config = load_config(args.config)
95
+ json_path = Path(config["paths"]["json_file"])
96
+ base_url = config["wordpress"]["base_url"]
97
+ output_dir = config["paths"].get("output_dir", str(json_path.parent.parent / "output"))
98
+
99
+ ensure_json_export(
100
+ base_url=base_url,
101
+ json_path=json_path,
102
+ refresh=args.refresh,
103
+ )
104
+
105
+ print("Starting build...")
106
+
107
+ posts = load_imported_json(input_file=json_path, verbose=True, frontmatter_fields=config["frontmatter"]["fields"])
108
+ posts = process_posts(posts, verbose=True)
109
+
110
+ posts = order_and_limit_posts(
111
+ posts,
112
+ order=args.order,
113
+ limit=args.limit
114
+ )
115
+
116
+ posts = write_order(posts, order=args.write_order)
117
+
118
+ units = assemble(posts,
119
+ mode=args.mode,
120
+ verbose=True,
121
+ include_frontmatter=args.frontmatter if args.frontmatter
122
+ else config["frontmatter"]["include_frontmatter"],
123
+ include_excerpt=args.excerpt,
124
+ content_format=args.content_format,
125
+ )
126
+
127
+ print(f"[INFO] Writing {len(units)} markdown files...")
128
+ print(f"[INFO] Output mode: {args.mode}")
129
+ write(
130
+ units,
131
+ format="md",
132
+ output_dir=output_dir,
133
+ verbose=True,
134
+ mode=args.mode,
135
+ )
136
+
137
+ print("Build completed. ✅")
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()