markdown-to-confluence 0.1.12__py3-none-any.whl → 0.1.13__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.
md2conf/application.py CHANGED
@@ -1,154 +1,154 @@
1
- import logging
2
- import os.path
3
- from pathlib import Path
4
- from typing import Dict, Optional
5
-
6
- from .api import ConfluenceSession
7
- from .converter import (
8
- ConfluenceDocument,
9
- ConfluenceDocumentOptions,
10
- ConfluencePageMetadata,
11
- attachment_name,
12
- extract_qualified_id,
13
- )
14
-
15
- LOGGER = logging.getLogger(__name__)
16
-
17
-
18
- class Application:
19
- "The entry point for Markdown to Confluence conversion."
20
-
21
- api: ConfluenceSession
22
- options: ConfluenceDocumentOptions
23
-
24
- def __init__(
25
- self, api: ConfluenceSession, options: ConfluenceDocumentOptions
26
- ) -> None:
27
- self.api = api
28
- self.options = options
29
-
30
- def synchronize(self, path: Path) -> None:
31
- "Synchronizes a single Markdown page or a directory of Markdown pages."
32
-
33
- if path.is_dir():
34
- self.synchronize_directory(path)
35
- elif path.is_file():
36
- self.synchronize_page(path)
37
- else:
38
- raise ValueError(f"expected: valid file or directory path; got: {path}")
39
-
40
- def synchronize_page(self, page_path: Path) -> None:
41
- "Synchronizes a single Markdown page with Confluence."
42
-
43
- self._synchronize_page(page_path, {})
44
-
45
- def synchronize_directory(self, local_dir: Path) -> None:
46
- "Synchronizes a directory of Markdown pages with Confluence."
47
-
48
- page_metadata: Dict[Path, ConfluencePageMetadata] = {}
49
- LOGGER.info(f"Synchronizing directory: {local_dir}")
50
-
51
- # Step 1: build index of all page metadata
52
- # NOTE: Pathlib.walk() is implemented only in Python 3.12+
53
- # so sticking for old os.walk
54
- for root, directories, files in os.walk(local_dir):
55
- for file_name in files:
56
- # Reconstitute Path object back
57
- docfile = (Path(root) / file_name).absolute()
58
-
59
- # Skip non-markdown files
60
- if docfile.suffix.lower() != ".md":
61
- continue
62
- metadata = self._get_or_create_page(docfile)
63
-
64
- LOGGER.debug(f"indexed {docfile} with metadata: {metadata}")
65
- page_metadata[docfile] = metadata
66
-
67
- LOGGER.info(f"indexed {len(page_metadata)} pages")
68
-
69
- # Step 2: Convert each page
70
- for page_path in page_metadata.keys():
71
- self._synchronize_page(page_path, page_metadata)
72
-
73
- def _synchronize_page(
74
- self,
75
- page_path: Path,
76
- page_metadata: Dict[Path, ConfluencePageMetadata],
77
- ) -> None:
78
- base_path = page_path.parent
79
-
80
- LOGGER.info(f"Synchronizing page: {page_path}")
81
- document = ConfluenceDocument(page_path, self.options, page_metadata)
82
-
83
- if document.id.space_key:
84
- with self.api.switch_space(document.id.space_key):
85
- self._update_document(document, base_path)
86
- else:
87
- self._update_document(document, base_path)
88
-
89
- def _get_or_create_page(
90
- self, absolute_path: Path, title: Optional[str] = None
91
- ) -> ConfluencePageMetadata:
92
- """
93
- Creates a new Confluence page if no page is linked in the Markdown document.
94
- """
95
-
96
- # parse file
97
- with open(absolute_path, "r") as f:
98
- document = f.read()
99
-
100
- qualified_id, document = extract_qualified_id(document)
101
- if qualified_id is not None:
102
- confluence_page = self.api.get_page(
103
- qualified_id.page_id, space_key=qualified_id.space_key
104
- )
105
- else:
106
- if self.options.root_page_id is None:
107
- raise ValueError(
108
- "expected: Confluence page ID to act as parent for Markdown files with no linked Confluence page"
109
- )
110
-
111
- # use file name without extension if no title is supplied
112
- if title is None:
113
- title = absolute_path.stem
114
-
115
- confluence_page = self.api.get_or_create_page(
116
- title, self.options.root_page_id
117
- )
118
- self._update_markdown(
119
- absolute_path,
120
- document,
121
- confluence_page.id,
122
- confluence_page.space_key,
123
- )
124
-
125
- return ConfluencePageMetadata(
126
- domain=self.api.domain,
127
- base_path=self.api.base_path,
128
- page_id=confluence_page.id,
129
- space_key=confluence_page.space_key or self.api.space_key,
130
- title=confluence_page.title or "",
131
- )
132
-
133
- def _update_document(self, document: ConfluenceDocument, base_path: Path) -> None:
134
- for image in document.images:
135
- self.api.upload_attachment(
136
- document.id.page_id, base_path / image, attachment_name(image), ""
137
- )
138
-
139
- content = document.xhtml()
140
- LOGGER.debug(f"generated Confluence Storage Format document:\n{content}")
141
- self.api.update_page(document.id.page_id, content)
142
-
143
- def _update_markdown(
144
- self,
145
- path: Path,
146
- document: str,
147
- page_id: str,
148
- space_key: Optional[str],
149
- ) -> None:
150
- with open(path, "w") as file:
151
- file.write(f"<!-- confluence-page-id: {page_id} -->\n")
152
- if space_key:
153
- file.write(f"<!-- confluence-space-key: {space_key} -->\n")
154
- file.write(document)
1
+ import logging
2
+ import os.path
3
+ from pathlib import Path
4
+ from typing import Dict, Optional
5
+
6
+ from .api import ConfluenceSession
7
+ from .converter import (
8
+ ConfluenceDocument,
9
+ ConfluenceDocumentOptions,
10
+ ConfluencePageMetadata,
11
+ attachment_name,
12
+ extract_qualified_id,
13
+ )
14
+
15
+ LOGGER = logging.getLogger(__name__)
16
+
17
+
18
+ class Application:
19
+ "The entry point for Markdown to Confluence conversion."
20
+
21
+ api: ConfluenceSession
22
+ options: ConfluenceDocumentOptions
23
+
24
+ def __init__(
25
+ self, api: ConfluenceSession, options: ConfluenceDocumentOptions
26
+ ) -> None:
27
+ self.api = api
28
+ self.options = options
29
+
30
+ def synchronize(self, path: Path) -> None:
31
+ "Synchronizes a single Markdown page or a directory of Markdown pages."
32
+
33
+ if path.is_dir():
34
+ self.synchronize_directory(path)
35
+ elif path.is_file():
36
+ self.synchronize_page(path)
37
+ else:
38
+ raise ValueError(f"expected: valid file or directory path; got: {path}")
39
+
40
+ def synchronize_page(self, page_path: Path) -> None:
41
+ "Synchronizes a single Markdown page with Confluence."
42
+
43
+ self._synchronize_page(page_path, {})
44
+
45
+ def synchronize_directory(self, local_dir: Path) -> None:
46
+ "Synchronizes a directory of Markdown pages with Confluence."
47
+
48
+ page_metadata: Dict[Path, ConfluencePageMetadata] = {}
49
+ LOGGER.info(f"Synchronizing directory: {local_dir}")
50
+
51
+ # Step 1: build index of all page metadata
52
+ # NOTE: Pathlib.walk() is implemented only in Python 3.12+
53
+ # so sticking for old os.walk
54
+ for root, directories, files in os.walk(local_dir):
55
+ for file_name in files:
56
+ # Reconstitute Path object back
57
+ docfile = (Path(root) / file_name).absolute()
58
+
59
+ # Skip non-markdown files
60
+ if docfile.suffix.lower() != ".md":
61
+ continue
62
+ metadata = self._get_or_create_page(docfile)
63
+
64
+ LOGGER.debug(f"indexed {docfile} with metadata: {metadata}")
65
+ page_metadata[docfile] = metadata
66
+
67
+ LOGGER.info(f"indexed {len(page_metadata)} pages")
68
+
69
+ # Step 2: Convert each page
70
+ for page_path in page_metadata.keys():
71
+ self._synchronize_page(page_path, page_metadata)
72
+
73
+ def _synchronize_page(
74
+ self,
75
+ page_path: Path,
76
+ page_metadata: Dict[Path, ConfluencePageMetadata],
77
+ ) -> None:
78
+ base_path = page_path.parent
79
+
80
+ LOGGER.info(f"Synchronizing page: {page_path}")
81
+ document = ConfluenceDocument(page_path, self.options, page_metadata)
82
+
83
+ if document.id.space_key:
84
+ with self.api.switch_space(document.id.space_key):
85
+ self._update_document(document, base_path)
86
+ else:
87
+ self._update_document(document, base_path)
88
+
89
+ def _get_or_create_page(
90
+ self, absolute_path: Path, title: Optional[str] = None
91
+ ) -> ConfluencePageMetadata:
92
+ """
93
+ Creates a new Confluence page if no page is linked in the Markdown document.
94
+ """
95
+
96
+ # parse file
97
+ with open(absolute_path, "r", encoding="utf-8") as f:
98
+ document = f.read()
99
+
100
+ qualified_id, document = extract_qualified_id(document)
101
+ if qualified_id is not None:
102
+ confluence_page = self.api.get_page(
103
+ qualified_id.page_id, space_key=qualified_id.space_key
104
+ )
105
+ else:
106
+ if self.options.root_page_id is None:
107
+ raise ValueError(
108
+ "expected: Confluence page ID to act as parent for Markdown files with no linked Confluence page"
109
+ )
110
+
111
+ # use file name without extension if no title is supplied
112
+ if title is None:
113
+ title = absolute_path.stem
114
+
115
+ confluence_page = self.api.get_or_create_page(
116
+ title, self.options.root_page_id
117
+ )
118
+ self._update_markdown(
119
+ absolute_path,
120
+ document,
121
+ confluence_page.id,
122
+ confluence_page.space_key,
123
+ )
124
+
125
+ return ConfluencePageMetadata(
126
+ domain=self.api.domain,
127
+ base_path=self.api.base_path,
128
+ page_id=confluence_page.id,
129
+ space_key=confluence_page.space_key or self.api.space_key,
130
+ title=confluence_page.title or "",
131
+ )
132
+
133
+ def _update_document(self, document: ConfluenceDocument, base_path: Path) -> None:
134
+ for image in document.images:
135
+ self.api.upload_attachment(
136
+ document.id.page_id, base_path / image, attachment_name(image), ""
137
+ )
138
+
139
+ content = document.xhtml()
140
+ LOGGER.debug(f"generated Confluence Storage Format document:\n{content}")
141
+ self.api.update_page(document.id.page_id, content)
142
+
143
+ def _update_markdown(
144
+ self,
145
+ path: Path,
146
+ document: str,
147
+ page_id: str,
148
+ space_key: Optional[str],
149
+ ) -> None:
150
+ with open(path, "w", encoding="utf-8") as file:
151
+ file.write(f"<!-- confluence-page-id: {page_id} -->\n")
152
+ if space_key:
153
+ file.write(f"<!-- confluence-space-key: {space_key} -->\n")
154
+ file.write(document)