kci-confluence-cli 0.2.0__tar.gz → 0.3.0__tar.gz

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.
Files changed (28) hide show
  1. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/PKG-INFO +1 -1
  2. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/__version__.py +1 -1
  3. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/create_attachment.py +5 -1
  4. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/delete_attachment.py +1 -0
  5. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/api.py +1 -2
  6. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/cli.py +5 -5
  7. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/convert_html_to_xml.py +22 -17
  8. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/page/subcommand.py +3 -4
  9. kci_confluence_cli-0.3.0/confluence/page/update_page.py +61 -0
  10. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/pyproject.toml +1 -1
  11. kci_confluence_cli-0.2.0/confluence/page/update_page.py +0 -49
  12. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/README.md +0 -0
  13. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/__init__.py +0 -0
  14. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/__main__.py +0 -0
  15. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/__init__.py +0 -0
  16. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/get_attachment.py +0 -0
  17. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/attachment/subcommand.py +0 -0
  18. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/__init__.py +0 -0
  19. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/common/utils.py +0 -0
  20. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/__init__.py +0 -0
  21. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/get_content_by_id.py +0 -0
  22. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/content/subcommand.py +0 -0
  23. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/data/logging.yaml +0 -0
  24. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/__init__.py +0 -0
  25. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/local/subcommand.py +0 -0
  26. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/page/__init__.py +0 -0
  27. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/page/get_page_body.py +0 -0
  28. {kci_confluence_cli-0.2.0 → kci_confluence_cli-0.3.0}/confluence/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kci-confluence-cli
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: 来栖川電算が利用しているConfluence v6.15.7を操作するためのCLIです。
5
5
  Home-page: https://github.com/kurusugawa-computer/confluence-cli
6
6
  License: MIT
@@ -1 +1 @@
1
- __version__ = "0.2.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
1
+ __version__ = "0.3.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
@@ -93,7 +93,11 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser): # noqa: ANN201
93
93
 
94
94
  parser.add_argument("--mime_type", type=str, help="ファイル名からMIMEタイプが判別できないときに、この値を添付ファイルのMIMEタイプとします。")
95
95
 
96
- parser.add_argument("--allow_duplicated", action="store_true", help="指定した場合は、アップロード先にすでに同じファイルが存在している場合に上書きます。指定しない場合は、400 Errorが発生します。")
96
+ parser.add_argument(
97
+ "--allow_duplicated",
98
+ action="store_true",
99
+ help="指定した場合は、アップロード先にすでに同じファイルが存在している場合に上書きます。指定しない場合は、400 Errorが発生します。",
100
+ )
97
101
 
98
102
  parser.add_argument("--filename_pattern", help="glob形式のパターンに一致するファイル名だけアップロードします。(ex) '*.png'")
99
103
  parser.set_defaults(subcommand_func=main)
@@ -40,6 +40,7 @@ def main(args: argparse.Namespace) -> None:
40
40
  success_count = 0
41
41
 
42
42
  all_yes = False
43
+ yes = False
43
44
  for index, attachment in enumerate(results):
44
45
  attachment_id = attachment["id"]
45
46
  attachment_title = attachment["title"]
@@ -146,8 +146,7 @@ class Api:
146
146
 
147
147
  https://docs.atlassian.com/ConfluenceServer/rest/6.15.7/#api/content-update
148
148
  """
149
- headers = {"Content-Type": "application/json", "X-Atlassian-Token": "nocheck"}
150
- return self._request("put", f"content/{content_id}", headers=headers, params=query_params, data=request_body).json()
149
+ return self._request("put", f"content/{content_id}", params=query_params, json=request_body).json()
151
150
 
152
151
  def delete_content(self, content_id: str, *, query_params: Optional[QueryParams] = None) -> None:
153
152
  """
@@ -141,11 +141,11 @@ def prompt_yesno(msg: str) -> bool:
141
141
 
142
142
  """
143
143
  while True:
144
- choice = input(f"{msg} [y/N] : ")
144
+ choice = input(f"{msg} [y/n] : ")
145
145
  if choice == "y":
146
146
  return True
147
147
 
148
- elif choice == "N":
148
+ elif choice == "n":
149
149
  return False
150
150
 
151
151
 
@@ -160,14 +160,14 @@ def prompt_yesnoall(msg: str) -> tuple[bool, bool]:
160
160
 
161
161
  """
162
162
  while True:
163
- choice = input(f"{msg} [y/N/ALL] : ")
163
+ choice = input(f"{msg} [y/n/all] : ")
164
164
  if choice == "y":
165
165
  return True, False
166
166
 
167
- elif choice == "N":
167
+ elif choice == "n":
168
168
  return False, False
169
169
 
170
- elif choice == "ALL":
170
+ elif choice == "all":
171
171
  return True, True
172
172
 
173
173
 
@@ -18,16 +18,11 @@ def convert_img_elm(img_elm: HtmlElement) -> None:
18
18
  `<img src="foo.png">`を以下のXMLに変換する
19
19
 
20
20
  ```
21
- <ac:image ac:alt="aaaa" ac:border="true" ac:height="207" ac:title="aaaa" ac:width="240">
22
- <ri:attachment ri:filename="image2023-2-16_11-24-17.png"/>
21
+ <ac:image>
22
+ <ri:attachment ri:filename="foo.png"/>
23
23
  </ac:image>
24
24
  ```
25
25
 
26
-
27
- ```
28
- <ac:image><ri:url ri:value="http://confluence.atlassian.com/images/logo/confluence_48_trans.png" /></ac:image>
29
- ```
30
-
31
26
  Args:
32
27
  trim_dirname_for_src_value: src属性値からディレクトリ名を削除する。Confluenceにアップロードしたファイル名にスラッシュは含められないため。
33
28
  """
@@ -39,6 +34,10 @@ def convert_img_elm(img_elm: HtmlElement) -> None:
39
34
  url_elm.tag = "ri:url"
40
35
  url_elm.attrib["ri:value"] = src_value
41
36
  img_elm.append(url_elm)
37
+ elif src_value.startswith("data:"):
38
+ # Data URIには対応していないので、スキップする
39
+ logger.warning(f"img要素のsrc属性値はData URIが含まれていました。Confluence用のXMLはData URIに対応していません。 :: {img_elm}'")
40
+ return
42
41
  else:
43
42
  attachment_elm = fromstring("<ri:attachment/>")
44
43
  # コロン付きのタグが生成できないので、改めて置換した
@@ -49,17 +48,21 @@ def convert_img_elm(img_elm: HtmlElement) -> None:
49
48
  attachment_elm.attrib["ri:filename"] = tmp[-1]
50
49
 
51
50
  img_elm.append(attachment_elm)
52
-
51
+ del img_elm.attrib["src"]
52
+
53
+ # img要素のいくつかの属性を、`ac:image`タグの属性に変換する。
54
+ # https://ja.confluence.atlassian.com/doc/confluence-storage-format-790796544.html
55
+ # bool値を指定する以下の属性は変換しない
56
+ # ac:border, ac:thumbnail
57
+ for html_attribute_name in ("align", "class", "title", "style", "alt", "height", "width", "vspace", "hspace"):
58
+ attribute_value = img_elm.attrib.get(html_attribute_name)
59
+ if attribute_value is not None and attribute_value != "":
60
+ img_elm.attrib[f"ac:{html_attribute_name}"] = attribute_value
61
+ del img_elm.attrib[html_attribute_name]
62
+
63
+ # サムネイル画像として設定する(画像をクリックすると拡大表示される)
53
64
  img_elm.attrib["ac:thumbnail"] = "true"
54
65
 
55
- alt_value: str = img_elm.attrib.get("alt")
56
- if alt_value != "":
57
- img_elm.attrib["ac:alt"] = alt_value
58
-
59
- title_value: str = img_elm.attrib.get("title")
60
- if title_value != "":
61
- img_elm.attrib["ac:title"] = title_value
62
-
63
66
 
64
67
  def convert(input_html_file: Path, output_xml_file: Path) -> None:
65
68
  with input_html_file.open(encoding="utf-8") as f:
@@ -73,9 +76,11 @@ def convert(input_html_file: Path, output_xml_file: Path) -> None:
73
76
 
74
77
  # body要素があればその中身、なければhtmlファイルの中身をアップロードする
75
78
  if len(pq_html("body")) > 0:
79
+ # body要素以下のHTMLを取得する
76
80
  html_data = pq_html("body").html()
77
81
  else:
78
- html_data = pq_html.html()
82
+ # 要素自身のHTMLを取得する
83
+ html_data = str(pq_html)
79
84
 
80
85
  output_xml_file.parent.mkdir(exist_ok=True, parents=True)
81
86
  output_xml_file.write_text(html_data, encoding="utf-8")
@@ -8,18 +8,17 @@ import confluence.page.get_page_body
8
8
  import confluence.page.update_page
9
9
 
10
10
 
11
- def add_arguments_to_parser(parser: argparse.ArgumentParser): # noqa: ANN201
11
+ def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None:
12
12
  subparsers = parser.add_subparsers(dest="subcommand_name")
13
13
 
14
14
  # サブコマンドの定義
15
15
  confluence.page.get_page_body.add_parser(subparsers)
16
- # 動かないのでコメントアウトする
17
- # confluence.page.update_page.add_parser(subparsers)
16
+ confluence.page.update_page.add_parser(subparsers)
18
17
 
19
18
 
20
19
  def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
21
20
  subcommand_name = "page"
22
- subcommand_help = "ページに関するサブコマンド"
21
+ subcommand_help = "ページまたはブログに関するサブコマンド"
23
22
 
24
23
  parser = confluence.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description=subcommand_help, is_subcommand=False)
25
24
  add_arguments_to_parser(parser)
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ import confluence
8
+ from confluence.common.cli import create_api_instance, prompt_yesno
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def main(args: argparse.Namespace) -> None:
14
+ api = create_api_instance(args)
15
+ content_id = args.content_id
16
+ xml_file: Path = args.xml_file
17
+ comment = args.comment if args.comment is not None else "Updated via confluence-cli"
18
+ xml_text = xml_file.read_text(encoding="utf-8")
19
+
20
+ old_content = api.get_content_by_id(content_id, query_params={"expand": "version,ancestors,space,body.storage"})
21
+
22
+ content_title = args.title if args.title is not None else old_content["title"]
23
+ space_key = old_content["space"]["key"]
24
+ if not args.yes:
25
+ if not prompt_yesno(f"次のコンテンツを更新しますか? :: content_id='{content_id}', title='{content_title}', space.key='{space_key}'"):
26
+ logger.info(f"コンテンツの更新をキャンセルしました。 content_id='{content_id}', title='{content_title}', space.key='{space_key}'")
27
+ return
28
+
29
+ request_body = {
30
+ "version": {"number": old_content["version"]["number"] + 1, "message": comment},
31
+ "title": content_title,
32
+ "type": old_content["type"],
33
+ "body": {"storage": {"value": xml_text, "representation": "storage"}},
34
+ }
35
+ _ = api.update_content(content_id, request_body=request_body)
36
+ logger.info(f"次のコンテンツを'{xml_file}'の内容で更新しました。 :: content_id='{content_id}', title='{content_title}', space.key='{space_key}'")
37
+
38
+
39
+ def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None:
40
+ parser.add_argument("-c", "--content_id", required=True, help="更新対象のコンテンツのID")
41
+ parser.add_argument(
42
+ "--xml_file",
43
+ required=True,
44
+ type=Path,
45
+ help="storageフォーマットで記載されたXMLファイルのパス。このファイルの内容でページ(またはブログ)が更新されます。",
46
+ )
47
+ parser.add_argument("--title", help="ページまたはブログの新しいタイトル。指定しない場合は既存のタイトルが維持されます。")
48
+ parser.add_argument("--comment", help="ページまたはブログを更新したときに残すコメント。")
49
+ parser.add_argument("--yes", action="store_true", help="すべてのプロンプトに自動的に'yes'と答え、非対話的に実行します。")
50
+
51
+ parser.set_defaults(subcommand_func=main)
52
+
53
+
54
+ def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
55
+ subcommand_name = "update"
56
+ subcommand_help = "ページまたはブログを更新します。"
57
+
58
+ parser = confluence.common.cli.add_parser(subparsers, subcommand_name, subcommand_help)
59
+
60
+ add_arguments_to_parser(parser)
61
+ return parser
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "kci-confluence-cli"
3
- version = "0.2.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
3
+ version = "0.3.0" # `poetry-dynamic-versioning`を使ってGitHubのバージョンタグを取得している。変更不要
4
4
  description = "来栖川電算が利用しているConfluence v6.15.7を操作するためのCLIです。"
5
5
  authors = ["Kurusugawa Computer Inc."]
6
6
  license = "MIT"
@@ -1,49 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import argparse
4
- import logging
5
- from pathlib import Path
6
-
7
- import confluence
8
- from confluence.common.cli import create_api_instance
9
- from confluence.common.utils import print_json
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- def main(args: argparse.Namespace) -> None:
15
- api = create_api_instance(args)
16
- content_id = args.content_id
17
- xml_file: Path = args.xml_file
18
- xml_text = xml_file.read_text(encoding="utf-8")
19
-
20
- old_page = api.get_content_by_id(content_id, query_params={"expand": "version,ancestors,space,body.storage"})
21
- print_json(old_page, is_pretty=True, output=None)
22
- request_body = {
23
- "version": {"number": old_page["version"]["number"] + 1, "message": ""},
24
- "title": old_page["title"],
25
- "type": old_page["type"],
26
- "space": {"key": old_page["space"]["key"]},
27
- "body": {"storage": {"value": xml_text, "representation": "storage"}},
28
- }
29
- result = api.update_content(content_id, request_body=request_body)
30
- print_json(result, is_pretty=True, output=None)
31
-
32
-
33
- def add_arguments_to_parser(parser: argparse.ArgumentParser): # noqa: ANN201
34
- parser.add_argument("-c", "--content_id", required=True, help="取得対象のコンテンツのID")
35
- parser.add_argument(
36
- "--xml_file", required=True, type=Path, help="storageフォーマットで記載されたXMLファイルのパス。このファイルの内容でページが更新されます。"
37
- )
38
-
39
- parser.set_defaults(subcommand_func=main)
40
-
41
-
42
- def add_parser(subparsers: argparse._SubParsersAction | None = None) -> argparse.ArgumentParser:
43
- subcommand_name = "update"
44
- subcommand_help = "ページを更新します。"
45
-
46
- parser = confluence.common.cli.add_parser(subparsers, subcommand_name, subcommand_help)
47
-
48
- add_arguments_to_parser(parser)
49
- return parser