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.
- wordpress_to_markdown/__init__.py +3 -0
- wordpress_to_markdown/__main__.py +4 -0
- wordpress_to_markdown/assembler.py +130 -0
- wordpress_to_markdown/cli.py +141 -0
- wordpress_to_markdown/commanding.py +580 -0
- wordpress_to_markdown/config/__init__.py +1 -0
- wordpress_to_markdown/config/loader.py +24 -0
- wordpress_to_markdown/core/__init__.py +1 -0
- wordpress_to_markdown/core/models.py +49 -0
- wordpress_to_markdown/importer/__init__.py +1 -0
- wordpress_to_markdown/importer/wordpress_api.py +341 -0
- wordpress_to_markdown/processors/__init__.py +1 -0
- wordpress_to_markdown/processors/metadata.py +103 -0
- wordpress_to_markdown/processors/pipeline.py +43 -0
- wordpress_to_markdown/sources/__init__.py +1 -0
- wordpress_to_markdown/sources/imported_json.py +64 -0
- wordpress_to_markdown/writer.py +70 -0
- wordpress_to_markdown-0.1.0.dist-info/METADATA +337 -0
- wordpress_to_markdown-0.1.0.dist-info/RECORD +23 -0
- wordpress_to_markdown-0.1.0.dist-info/WHEEL +5 -0
- wordpress_to_markdown-0.1.0.dist-info/entry_points.txt +2 -0
- wordpress_to_markdown-0.1.0.dist-info/licenses/LICENSE +21 -0
- wordpress_to_markdown-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -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()
|