annofabcli 1.99.0__py3-none-any.whl → 100.2__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.
Files changed (31) hide show
  1. annofabcli/__version__.py +1 -1
  2. annofabcli/annotation/change_annotation_attributes.py +2 -2
  3. annofabcli/annotation_specs/add_attribute_restriction.py +158 -0
  4. annofabcli/annotation_specs/attribute_restriction.py +240 -0
  5. annofabcli/annotation_specs/list_annotation_specs_attribute.py +51 -33
  6. annofabcli/annotation_specs/list_annotation_specs_choice.py +42 -17
  7. annofabcli/annotation_specs/list_annotation_specs_label.py +26 -93
  8. annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +220 -0
  9. annofabcli/annotation_specs/list_attribute_restriction.py +8 -209
  10. annofabcli/annotation_specs/subcommand_annotation_specs.py +4 -0
  11. annofabcli/common/annofab/annotation_specs.py +25 -0
  12. annofabcli/common/download.py +1 -1
  13. annofabcli/common/exceptions.py +2 -2
  14. annofabcli/common/facade.py +2 -2
  15. annofabcli/common/image.py +2 -2
  16. annofabcli/input_data/delete_input_data.py +6 -7
  17. annofabcli/job/delete_job.py +4 -4
  18. annofabcli/organization_member/list_organization_member.py +2 -3
  19. annofabcli/project_member/change_project_members.py +2 -3
  20. annofabcli/project_member/drop_project_members.py +8 -8
  21. annofabcli/project_member/invite_project_members.py +5 -6
  22. annofabcli/project_member/list_users.py +4 -4
  23. annofabcli/task/complete_tasks.py +2 -2
  24. annofabcli/task/reject_tasks.py +76 -100
  25. annofabcli/task/update_metadata_of_task.py +1 -1
  26. annofabcli/task_history/list_task_history.py +1 -1
  27. {annofabcli-1.99.0.dist-info → annofabcli-100.2.dist-info}/METADATA +1 -1
  28. {annofabcli-1.99.0.dist-info → annofabcli-100.2.dist-info}/RECORD +31 -27
  29. {annofabcli-1.99.0.dist-info → annofabcli-100.2.dist-info}/LICENSE +0 -0
  30. {annofabcli-1.99.0.dist-info → annofabcli-100.2.dist-info}/WHEEL +0 -0
  31. {annofabcli-1.99.0.dist-info → annofabcli-100.2.dist-info}/entry_points.txt +0 -0
@@ -4,15 +4,12 @@ import argparse
4
4
  import json
5
5
  import logging
6
6
  import sys
7
- from collections.abc import Collection
8
- from enum import Enum
9
7
  from pathlib import Path
10
- from typing import Any, Optional
11
-
12
- from more_itertools import first_true
8
+ from typing import Optional
13
9
 
14
10
  import annofabcli
15
11
  import annofabcli.common.cli
12
+ from annofabcli.annotation_specs.attribute_restriction import AttributeRestrictionMessage, OutputFormat
16
13
  from annofabcli.common.cli import (
17
14
  COMMAND_LINE_ERROR_STATUS_CODE,
18
15
  ArgumentParser,
@@ -25,204 +22,6 @@ from annofabcli.common.facade import AnnofabApiFacade
25
22
  logger = logging.getLogger(__name__)
26
23
 
27
24
 
28
- class FormatArgument(Enum):
29
- """
30
- 表示するフォーマット ``--format`` で指定できる値
31
-
32
- Attributes:
33
- TEXT: 属性IDや種類を隠したシンプルなテキスト
34
- DETAILED_TEXT: 属性IDや属性種類などの詳細情報を表示したテキスト
35
- """
36
-
37
- TEXT = "text"
38
- DETAILED_TEXT = "detailed_text"
39
-
40
-
41
- class ListAttributeRestrictionMain:
42
- """
43
- アノテーション仕様の制約から自然言語で書かれたメッセージを生成します。
44
-
45
- Args:
46
- labels: アノテーション仕様のラベル情報
47
- additionals: アノテーション仕様の属性情報
48
-
49
- """
50
-
51
- def __init__(
52
- self,
53
- labels: list[dict[str, Any]],
54
- additionals: list[dict[str, Any]],
55
- format: FormatArgument = FormatArgument.DETAILED_TEXT, # noqa: A002 # pylint: disable=redefined-builtin
56
- ) -> None:
57
- self.attribute_dict = {e["additional_data_definition_id"]: e for e in additionals}
58
- self.label_dict = {e["label_id"]: e for e in labels}
59
- self.format = format
60
-
61
- def get_labels_text(self, label_ids: Collection[str]) -> str:
62
- label_message_list = []
63
- for label_id in label_ids:
64
- label = self.label_dict.get(label_id)
65
- label_name = AnnofabApiFacade.get_label_name_en(label) if label is not None else ""
66
-
67
- label_message = f"'{label_name}'"
68
- if self.format == FormatArgument.DETAILED_TEXT:
69
- label_message = f"{label_message} (id='{label_id}')"
70
- label_message_list.append(label_message)
71
-
72
- return ", ".join(label_message_list)
73
-
74
- def get_object_for_equals_or_notequals(self, value: str, attribute: Optional[dict[str, Any]]) -> str:
75
- """制約条件が `Equals` or `NotEquals`のときの目的語を生成する。
76
- 属性の種類がドロップダウンかセレクトボックスのときは、選択肢の名前を返す。
77
-
78
- Args:
79
- value (str): _description_
80
- attribute (Optional[dict[str,Any]]): _description_
81
-
82
- Returns:
83
- str: _description_
84
- """
85
- if attribute is not None and attribute["type"] in ["choice", "select"]:
86
- # ラジオボタンかドロップダウンのとき
87
- choices = attribute["choices"]
88
- choice = first_true(choices, pred=lambda e: e["choice_id"] == value)
89
- if choice is not None:
90
- choice_name = AnnofabApiFacade.get_choice_name_en(choice)
91
- tmp = f"'{value}'"
92
- if self.format == FormatArgument.DETAILED_TEXT:
93
- tmp = f"{tmp} (name='{choice_name}')"
94
- return tmp
95
-
96
- else:
97
- return f"'{value}'"
98
- else:
99
- return f"'{value}'"
100
-
101
- def get_restriction_text(self, attribute_id: str, condition: dict[str, Any]) -> str: # noqa: PLR0912
102
- """制約情報のテキストを返します。
103
-
104
- Args:
105
- attribute_id (str): 属性ID
106
- condition (dict[str, Any]): 制約条件
107
-
108
- Returns:
109
- str: 制約を表す文
110
- """
111
- str_type = condition["_type"]
112
-
113
- if str_type == "Imply":
114
- # 属性間の制約
115
- premise = condition["premise"]
116
- if_condition_text = self.get_restriction_text(premise["additional_data_definition_id"], premise["condition"])
117
- then_text = self.get_restriction_text(attribute_id, condition["condition"])
118
- return f"{then_text} IF {if_condition_text}"
119
-
120
- attribute = self.attribute_dict.get(attribute_id)
121
- if attribute is not None:
122
- subject = f"'{AnnofabApiFacade.get_additional_data_definition_name_en(attribute)}'"
123
- if self.format == FormatArgument.DETAILED_TEXT:
124
- subject = f"{subject} (id='{attribute_id}', type='{attribute['type']}')"
125
- else:
126
- logger.warning(f"属性IDが'{attribute_id}'の属性は存在しません。")
127
- subject = "''"
128
- if self.format == FormatArgument.DETAILED_TEXT:
129
- subject = f"{subject} (id='{attribute_id}')"
130
-
131
- if str_type == "CanInput":
132
- verb = "CAN INPUT" if condition["enable"] else "CAN NOT INPUT"
133
- str_object = ""
134
-
135
- elif str_type == "HasLabel":
136
- verb = "HAS LABEL"
137
- str_object = self.get_labels_text(condition["labels"])
138
-
139
- elif str_type == "Equals":
140
- verb = "EQUALS"
141
- str_object = self.get_object_for_equals_or_notequals(condition["value"], attribute)
142
-
143
- elif str_type == "NotEquals":
144
- verb = "DOES NOT EQUAL"
145
- str_object = self.get_object_for_equals_or_notequals(condition["value"], attribute)
146
-
147
- elif str_type == "Matches":
148
- verb = "MATCHES"
149
- str_object = f"'{condition['value']}'"
150
-
151
- elif str_type == "NotMatches":
152
- verb = "DOES NOT MATCH"
153
- str_object = f"'{condition['value']}'"
154
- else:
155
- raise RuntimeError(f"{str_type=}はサポートしていません。")
156
-
157
- tmp = f"{subject} {verb}"
158
- if str_object != "":
159
- tmp = f"{tmp} {str_object}"
160
- return tmp
161
-
162
- def get_attribute_from_name(self, attribute_name: str) -> Optional[dict[str, Any]]:
163
- tmp = [
164
- attribute
165
- for attribute in self.attribute_dict.values()
166
- if AnnofabApiFacade.get_additional_data_definition_name_en(attribute) == attribute_name
167
- ]
168
- if len(tmp) == 1:
169
- return tmp[0]
170
- elif len(tmp) == 0:
171
- logger.warning(f"属性名(英語)が'{attribute_name}'の属性は存在しません。")
172
- return None
173
- else:
174
- logger.warning(f"属性名(英語)が'{attribute_name}'の属性は複数存在します。")
175
- return None
176
-
177
- def get_label_from_name(self, label_name: str) -> Optional[dict[str, Any]]:
178
- tmp = [label for label in self.label_dict.values() if AnnofabApiFacade.get_label_name_en(label) == label_name]
179
- if len(tmp) == 1:
180
- return tmp[0]
181
- elif len(tmp) == 0:
182
- logger.warning(f"ラベル名(英語)が'{label_name}'のラベルは存在しません。")
183
- return None
184
- else:
185
- logger.warning(f"ラベル名(英語)が'{label_name}'のラベルは複数存在します。")
186
- return None
187
-
188
- def get_target_attribute_ids(
189
- self,
190
- target_attribute_names: Optional[Collection[str]] = None,
191
- target_label_names: Optional[Collection[str]] = None,
192
- ) -> set[str]:
193
- result: set[str] = set()
194
-
195
- if target_attribute_names is not None:
196
- tmp_attribute_list = [self.get_attribute_from_name(attribute_name) for attribute_name in target_attribute_names]
197
- tmp_ids = {attribute["additional_data_definition_id"] for attribute in tmp_attribute_list if attribute is not None}
198
- result = result | tmp_ids
199
-
200
- if target_label_names is not None:
201
- tmp_label_list = [self.get_label_from_name(label_name) for label_name in target_label_names]
202
- tmp_ids_list = [label["additional_data_definitions"] for label in tmp_label_list if label is not None]
203
- for attribute_ids in tmp_ids_list:
204
- result = result | set(attribute_ids)
205
-
206
- return result
207
-
208
- def get_restriction_text_list(
209
- self,
210
- restrictions: list[dict[str, Any]],
211
- *,
212
- target_attribute_names: Optional[Collection[str]] = None,
213
- target_label_names: Optional[Collection[str]] = None,
214
- ) -> list[str]:
215
- if target_attribute_names is not None or target_label_names is not None:
216
- target_attribute_ids = self.get_target_attribute_ids(target_attribute_names=target_attribute_names, target_label_names=target_label_names)
217
- return [
218
- self.get_restriction_text(e["additional_data_definition_id"], e["condition"])
219
- for e in restrictions
220
- if e["additional_data_definition_id"] in target_attribute_ids
221
- ]
222
- else:
223
- return [self.get_restriction_text(e["additional_data_definition_id"], e["condition"]) for e in restrictions]
224
-
225
-
226
25
  class ListAttributeRestriction(CommandLine):
227
26
  COMMON_MESSAGE = "annofabcli annotation_specs list_restriction: error:"
228
27
 
@@ -263,10 +62,10 @@ class ListAttributeRestriction(CommandLine):
263
62
  else:
264
63
  raise RuntimeError("'--project_id'か'--annotation_specs_json'のどちらかを指定する必要があります。")
265
64
 
266
- main_obj = ListAttributeRestrictionMain(
65
+ main_obj = AttributeRestrictionMessage(
267
66
  labels=annotation_specs["labels"],
268
67
  additionals=annotation_specs["additionals"],
269
- format=FormatArgument(args.format),
68
+ output_format=OutputFormat(args.format),
270
69
  )
271
70
  target_attribute_names = get_list_from_args(args.attribute_name) if args.attribute_name is not None else None
272
71
  target_label_names = get_list_from_args(args.label_name) if args.label_name is not None else None
@@ -323,12 +122,12 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
323
122
  "-f",
324
123
  "--format",
325
124
  type=str,
326
- choices=[e.value for e in FormatArgument],
327
- default=FormatArgument.TEXT.value,
125
+ choices=[e.value for e in OutputFormat],
126
+ default=OutputFormat.TEXT.value,
328
127
  help=f"出力フォーマット\n"
329
128
  "\n"
330
- f"* {FormatArgument.TEXT.value}: 英語名のみ出力する形式\n"
331
- f"* {FormatArgument.DETAILED_TEXT.value}: 属性IDや属性種類などの詳細情報を出力する形式\n",
129
+ f"* {OutputFormat.TEXT.value}: 英語名のみ出力する形式\n"
130
+ f"* {OutputFormat.DETAILED_TEXT.value}: 属性IDや属性種類などの詳細情報を出力する形式\n",
332
131
  )
333
132
 
334
133
  parser.set_defaults(subcommand_func=main)
@@ -2,6 +2,7 @@ import argparse
2
2
  from typing import Optional
3
3
 
4
4
  import annofabcli
5
+ import annofabcli.annotation_specs.add_attribute_restriction
5
6
  import annofabcli.annotation_specs.export_annotation_specs
6
7
  import annofabcli.annotation_specs.get_annotation_specs_with_attribute_id_replaced
7
8
  import annofabcli.annotation_specs.get_annotation_specs_with_choice_id_replaced
@@ -10,6 +11,7 @@ import annofabcli.annotation_specs.list_annotation_specs_attribute
10
11
  import annofabcli.annotation_specs.list_annotation_specs_choice
11
12
  import annofabcli.annotation_specs.list_annotation_specs_history
12
13
  import annofabcli.annotation_specs.list_annotation_specs_label
14
+ import annofabcli.annotation_specs.list_annotation_specs_label_attribute
13
15
  import annofabcli.annotation_specs.list_attribute_restriction
14
16
  import annofabcli.annotation_specs.list_label_color
15
17
  import annofabcli.annotation_specs.put_label_color
@@ -20,6 +22,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
20
22
  subparsers = parser.add_subparsers(dest="subcommand_name")
21
23
 
22
24
  # サブコマンドの定義
25
+ annofabcli.annotation_specs.add_attribute_restriction.add_parser(subparsers)
23
26
  annofabcli.annotation_specs.export_annotation_specs.add_parser(subparsers)
24
27
  annofabcli.annotation_specs.get_annotation_specs_with_attribute_id_replaced.add_parser(subparsers)
25
28
  annofabcli.annotation_specs.get_annotation_specs_with_choice_id_replaced.add_parser(subparsers)
@@ -29,6 +32,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
29
32
  annofabcli.annotation_specs.list_annotation_specs_choice.add_parser(subparsers)
30
33
  annofabcli.annotation_specs.list_annotation_specs_history.add_parser(subparsers)
31
34
  annofabcli.annotation_specs.list_annotation_specs_label.add_parser(subparsers)
35
+ annofabcli.annotation_specs.list_annotation_specs_label_attribute.add_parser(subparsers)
32
36
  annofabcli.annotation_specs.list_label_color.add_parser(subparsers)
33
37
  annofabcli.annotation_specs.put_label_color.add_parser(subparsers)
34
38
 
@@ -0,0 +1,25 @@
1
+ from typing import Any
2
+
3
+
4
+ def keybind_to_text(keybind: list[dict[str, Any]]) -> str:
5
+ """
6
+ 以下の構造を持つkeybindを、人が読める形式に変換します。
7
+ {"alt": False, "code": "Numpad1", "ctrl": False, "shift": False}
8
+ """
9
+
10
+ def to_str(one_keybind: dict[str, Any]) -> str:
11
+ keys = []
12
+ if one_keybind.get("ctrl", False):
13
+ keys.append("Ctrl")
14
+ if one_keybind.get("alt", False):
15
+ keys.append("Alt")
16
+ if one_keybind.get("shift", False):
17
+ keys.append("Shift")
18
+ code = one_keybind.get("code", "")
19
+ assert code is not None
20
+
21
+ keys.append(f"{code}")
22
+ return "+".join(keys)
23
+
24
+ tmp_list = [to_str(elm) for elm in keybind]
25
+ return ",".join(tmp_list)
@@ -50,7 +50,7 @@ class DownloadingFile:
50
50
 
51
51
  max_wait_minutes = self.get_max_wait_minutes(wait_options)
52
52
  filetype = DOWNLOADING_FILETYPE_DICT[job_type]
53
- logger.info(f"{filetype}の更新処理が完了するまで、最大{max_wait_minutes}分間待ちます。job_id={job_id}")
53
+ logger.info(f"{filetype}の更新処理が完了するまで、最大{max_wait_minutes}分間待ちます。job_id='{job_id}'")
54
54
  result = self.service.wrapper.wait_for_completion(
55
55
  project_id,
56
56
  job_type=job_type,
@@ -46,7 +46,7 @@ class ProjectAuthorizationError(AuthorizationError):
46
46
 
47
47
  def __init__(self, project_title: str, roles: list[ProjectMemberRole]) -> None:
48
48
  role_values = [e.value for e in roles]
49
- msg = f"プロジェクト: {project_title} に、ロール: {role_values} のいずれかが付与されていません。"
49
+ msg = f"プロジェクト'{project_title}'に対して、ロール'{role_values}'のいずれかが必要です。"
50
50
  super().__init__(msg)
51
51
 
52
52
 
@@ -57,7 +57,7 @@ class OrganizationAuthorizationError(AuthorizationError):
57
57
 
58
58
  def __init__(self, organization_name: str, roles: list[OrganizationMemberRole]) -> None:
59
59
  role_values = [e.value for e in roles]
60
- msg = f"組織: {organization_name} に、ロール: {role_values} のいずれかが付与されていません。"
60
+ msg = f"組織'{organization_name}'に対して、ロール'{role_values}'のいずれかが必要です。"
61
61
  super().__init__(msg)
62
62
 
63
63
 
@@ -499,12 +499,12 @@ class AnnofabApiFacade:
499
499
  task_query.account_id = self.get_account_id_from_user_id(project_id, task_query.user_id)
500
500
  return task_query
501
501
 
502
- def validate_project( # noqa: ANN201
502
+ def validate_project(
503
503
  self,
504
504
  project_id: str,
505
505
  project_member_roles: Optional[list[ProjectMemberRole]] = None,
506
506
  organization_member_roles: Optional[list[OrganizationMemberRole]] = None,
507
- ):
507
+ ) -> None:
508
508
  """
509
509
  プロジェクト or 組織に対して、必要な権限が付与されているかを確認する。
510
510
 
@@ -85,7 +85,7 @@ def fill_annotation(
85
85
  if outer_image is not None:
86
86
  draw.bitmap([0, 0], outer_image, fill=color)
87
87
  else:
88
- logger.warning(f"アノテーション種類が`{data_type}`ですが、`outer_image`がNoneです。 annotation_id={annotation.annotation_id}")
88
+ logger.warning(f"アノテーション種類が`{data_type}`ですが、`outer_image`がNoneです。 annotation_id='{annotation.annotation_id}'")
89
89
 
90
90
  return draw
91
91
 
@@ -344,7 +344,7 @@ def write_annotation_images_from_path(
344
344
  output_image_file = output_dir_path / f"{Path(parser.json_file_path).stem}.{output_image_extension}"
345
345
  tmp_image_size = _get_image_size(parser.input_data_id)
346
346
  if tmp_image_size is None:
347
- logger.warning(f"task_id={parser.task_id}, input_data_id={parser.input_data_id}: 画像サイズを取得できなかったので、スキップします。")
347
+ logger.warning(f"task_id='{parser.task_id}', input_data_id='{parser.input_data_id}': 画像サイズを取得できなかったので、スキップします。")
348
348
  continue
349
349
 
350
350
  write_annotation_image(
@@ -42,11 +42,11 @@ class DeleteInputData(CommandLine):
42
42
  f"supplementary_data_name={supplementary_data['supplementary_data_name']}"
43
43
  )
44
44
  deleted_count += 1
45
- except requests.HTTPError as e:
46
- logger.warning(e)
45
+ except requests.HTTPError:
47
46
  logger.warning(
48
47
  f"補助情報の削除に失敗しました。input_data_id={input_data_id}, supplementary_data_id={supplementary_data_id}, "
49
- f"supplementary_data_name={supplementary_data['supplementary_data_name']}"
48
+ f"supplementary_data_name={supplementary_data['supplementary_data_name']}",
49
+ exc_info=True,
50
50
  )
51
51
  continue
52
52
 
@@ -69,7 +69,7 @@ class DeleteInputData(CommandLine):
69
69
  def delete_input_data(self, project_id: str, input_data_id: str, input_data_index: int, delete_supplementary: bool, force: bool): # noqa: ANN201, FBT001
70
70
  input_data = self.service.wrapper.get_input_data_or_none(project_id, input_data_id)
71
71
  if input_data is None:
72
- logger.info(f"input_data_id={input_data_id} は存在しません。")
72
+ logger.info(f"input_data_id='{input_data_id}'である入力データは存在しません。")
73
73
  return False
74
74
 
75
75
  task_list = self.service.wrapper.get_all_tasks(project_id, query_params={"input_data_ids": input_data_id})
@@ -137,9 +137,8 @@ class DeleteInputData(CommandLine):
137
137
  if result:
138
138
  count_delete_input_data += 1
139
139
 
140
- except requests.exceptions.HTTPError as e:
141
- logger.warning(e)
142
- logger.warning(f"input_data_id='{input_data_id}'の削除に失敗しました。")
140
+ except requests.exceptions.HTTPError:
141
+ logger.warning(f"input_data_id='{input_data_id}'である入力データの削除に失敗しました。", exc_info=True)
143
142
  continue
144
143
 
145
144
  logger.info(f"プロジェクト'{project_title}'から 、{count_delete_input_data}/{len(input_data_id_list)} 件の入力データを削除しました。")
@@ -29,14 +29,14 @@ class DeleteJobMain:
29
29
  count = 0
30
30
  for job_id in job_id_list:
31
31
  if job_id not in exists_job_id_set:
32
- logger.debug(f"job_id={job_id} のジョブは存在しなかったのでスキップします。")
32
+ logger.debug(f"job_id='{job_id}' のジョブは存在しなかったのでスキップします。")
33
33
  continue
34
34
  try:
35
35
  self.service.api.delete_project_job(project_id, job_type.value, job_id)
36
- logger.debug(f"job_type={job_type.value}, job_id={job_id} のジョブを削除しました。")
36
+ logger.debug(f"job_type={job_type.value}, job_id='{job_id}' のジョブを削除しました。")
37
37
  count += 1
38
- except Exception as e: # pylint: disable=broad-except
39
- logger.warning(e)
38
+ except Exception: # pylint: disable=broad-except
39
+ logger.warning(f"job_type={job_type.value}, job_id='{job_id}' のジョブの削除に失敗しました。", exc_info=True)
40
40
  logger.info(f"{count} / {len(job_id_list)} 件のジョブを削除しました。")
41
41
 
42
42
 
@@ -48,9 +48,8 @@ class ListOrganizationMember(CommandLine):
48
48
  for organization_name in organization_name_list:
49
49
  try:
50
50
  organization_member_list.extend(self.get_organization_member_list(organization_name))
51
- except requests.HTTPError as e:
52
- logger.warning(e)
53
- logger.warning(f"{organization_name} の組織メンバを取得できませんでした。")
51
+ except requests.HTTPError:
52
+ logger.warning(f"組織'{organization_name}'のメンバー一覧を取得できませんでした。", exc_info=True)
54
53
  continue
55
54
 
56
55
  if args.format == FormatArgument.CSV.value:
@@ -115,9 +115,8 @@ class ChangeProjectMembers(CommandLine):
115
115
  logger.debug(f"user_id = {user_id} のプロジェクトメンバ情報を変更しました。member_role={member_role}, member_info={member_info}")
116
116
  count_invite_members += 1
117
117
 
118
- except requests.exceptions.HTTPError as e:
119
- logger.warning(e)
120
- logger.warning(f"プロジェクトメンバの登録に失敗しました。user_id={user_id}")
118
+ except requests.exceptions.HTTPError:
119
+ logger.warning(f"プロジェクトメンバの登録に失敗しました。 :: user_id='{user_id}'", exc_info=True)
121
120
 
122
121
  logger.info(f"{project_title} に、{count_invite_members} / {len(user_id_list)} 件のプロジェクトメンバを変更しました。")
123
122
 
@@ -49,10 +49,11 @@ class DropProjectMembersMain:
49
49
  }
50
50
  try:
51
51
  self.service.api.put_project_member(project_id, user_id, request_body=request_body)
52
- logger.debug(f"{project_title}({project_id}) のプロジェクトメンバから、{user_id} を脱退させました。")
53
- except requests.HTTPError as e:
54
- logger.warning(e)
55
- logger.warning(f"{project_title}({project_id}) のプロジェクトメンバから、{user_id} を脱退させられませんでした。")
52
+ logger.debug(f"プロジェクト'{project_title}'(project_id='{project_id}') から、user_id='{user_id}'のユーザーを脱退させました。")
53
+ except requests.HTTPError:
54
+ logger.warning(
55
+ f"プロジェクト'{project_title}'(project_id='{project_id}') から、user_id='{user_id}'のユーザーを脱退させられませんでした。"
56
+ )
56
57
 
57
58
  def drop_role_with_organization(self, organization_name: str, user_id_list: list[str]): # noqa: ANN201
58
59
  projects = self.service.wrapper.get_all_projects_of_organization(organization_name, query_params={"account_id": self.service.api.account_id})
@@ -64,14 +65,13 @@ class DropProjectMembersMain:
64
65
  for project_id in project_id_list:
65
66
  try:
66
67
  if not self.facade.my_role_is_owner(project_id):
67
- logger.warning(f"オーナではないため、プロジェクトメンバを脱退させられません。project_id = {project_id}")
68
+ logger.warning(f"オーナではないため、プロジェクトメンバを脱退させられません。 :: project_id='{project_id}'")
68
69
  continue
69
70
 
70
71
  self.drop_project_members(project_id, user_id_list)
71
72
 
72
- except requests.HTTPError as e:
73
- logger.warning(e)
74
- logger.warning(f"project_id={project_id} のプロジェクトメンバからユーザを脱退させられませんでした。")
73
+ except requests.HTTPError:
74
+ logger.warning(f"project_id='{project_id}' のプロジェクトメンバからユーザを脱退させられませんでした。", exc_info=True)
75
75
 
76
76
 
77
77
  class DropProjectMembers(CommandLine):
@@ -55,10 +55,10 @@ class InviteProjectMemberMain:
55
55
  try:
56
56
  self.service.api.put_project_member(project_id, user_id, request_body=request_body)
57
57
  logger.debug(f"{project_title}({project_id}) のプロジェクトメンバに、{user_id} を {member_role.value} ロールで追加しました。")
58
- except requests.HTTPError as e:
59
- logger.warning(e)
58
+ except requests.HTTPError:
60
59
  logger.warning(
61
- f"{project_title}({project_id}) のプロジェクトメンバに、{user_id} を {member_role.value} ロールで追加できませんでした。"
60
+ f"{project_title}({project_id}) のプロジェクトメンバに、{user_id} を {member_role.value} ロールで追加できませんでした。",
61
+ exc_info=True,
62
62
  )
63
63
 
64
64
  def assign_role_with_organization(self, organization_name: str, user_id_list: list[str], member_role: ProjectMemberRole): # noqa: ANN201
@@ -75,9 +75,8 @@ class InviteProjectMemberMain:
75
75
 
76
76
  self.invite_project_members(project_id, user_id_list, member_role)
77
77
 
78
- except requests.HTTPError as e:
79
- logger.warning(e)
80
- logger.warning(f"project_id={project_id} のプロジェクトメンバにユーザを招待できませんでした。")
78
+ except requests.HTTPError:
79
+ logger.warning(f"project_id='{project_id}' のプロジェクトメンバにユーザを招待できませんでした。")
81
80
 
82
81
 
83
82
  class InviteUser(CommandLine):
@@ -48,16 +48,16 @@ class ListUser(CommandLine):
48
48
  for project_id in project_id_list:
49
49
  try:
50
50
  project, _ = self.service.api.get_project(project_id)
51
- except requests.exceptions.HTTPError as e:
52
- logger.warning(e)
51
+ except requests.exceptions.HTTPError:
53
52
  logger.warning(
54
- f"project_id = {project_id} のプロジェクトにアクセスできなかった(存在しないproject_id、またはプロジェクトメンバでない)"
53
+ f"project_id='{project_id}' のプロジェクトにアクセスできなかった(存在しないproject_id、またはプロジェクトメンバでない)",
54
+ exc_info=True,
55
55
  )
56
56
  continue
57
57
 
58
58
  project_title = project["title"]
59
59
  project_members = self.get_all_project_members(project_id, include_inactive=include_inactive)
60
- logger.info(f"{project_title} のプロジェクトメンバを {len(project_members)} 件取得した。project_id={project_id}")
60
+ logger.info(f"{project_title} のプロジェクトメンバを {len(project_members)} 件取得した。project_id='{project_id}'")
61
61
 
62
62
  for member in project_members:
63
63
  AddProps.add_properties_of_project(member, project_title)
@@ -76,7 +76,7 @@ class CompleteTasksMain(CommandLineWithConfirm):
76
76
  comment_status: CommentStatus,
77
77
  ):
78
78
  if comment_list is None or len(comment_list) == 0:
79
- logger.warning(f"変更対象の検査コメントはなかった。task_id = {task.task_id}, input_data_id = {input_data_id}")
79
+ logger.warning(f"変更対象の検査コメントはなかった。task_id='{task.task_id}', input_data_id='{input_data_id}'")
80
80
  return
81
81
 
82
82
  def to_req_inspection(comment: dict[str, Any]) -> dict[str, Any]:
@@ -332,7 +332,7 @@ class CompleteTasksMain(CommandLineWithConfirm):
332
332
 
333
333
  task: Task = Task.from_dict(dict_task)
334
334
  logger.info(
335
- f"{logging_prefix} : タスク情報 task_id={task_id}, phase={task.phase.value}, phase_stage={task.phase_stage}, status={task.status.value}"
335
+ f"{logging_prefix} : タスク情報 task_id='{task_id}', phase={task.phase.value}, phase_stage={task.phase_stage}, status={task.status.value}"
336
336
  )
337
337
  if not self._validate_task(task, target_phase=target_phase, target_phase_stage=target_phase_stage, task_query=task_query):
338
338
  return False