annofabcli 1.100.0__py3-none-any.whl → 100.3__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 (38) hide show
  1. annofabcli/__version__.py +1 -1
  2. annofabcli/annotation/change_annotation_attributes.py +2 -2
  3. annofabcli/annotation_specs/list_annotation_specs_attribute.py +51 -33
  4. annofabcli/annotation_specs/list_annotation_specs_choice.py +42 -17
  5. annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
  6. annofabcli/annotation_specs/list_annotation_specs_label.py +26 -93
  7. annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +220 -0
  8. annofabcli/annotation_specs/subcommand_annotation_specs.py +2 -0
  9. annofabcli/common/annofab/annotation_specs.py +25 -0
  10. annofabcli/common/cli.py +0 -29
  11. annofabcli/common/download.py +1 -1
  12. annofabcli/common/exceptions.py +2 -2
  13. annofabcli/common/facade.py +2 -2
  14. annofabcli/common/image.py +2 -2
  15. annofabcli/input_data/delete_input_data.py +6 -7
  16. annofabcli/input_data/list_input_data.py +0 -1
  17. annofabcli/instruction/list_instruction_history.py +0 -1
  18. annofabcli/job/delete_job.py +4 -4
  19. annofabcli/job/list_job.py +0 -1
  20. annofabcli/job/list_last_job.py +0 -1
  21. annofabcli/organization_member/list_organization_member.py +3 -5
  22. annofabcli/project/list_project.py +0 -3
  23. annofabcli/project_member/change_project_members.py +2 -3
  24. annofabcli/project_member/drop_project_members.py +8 -8
  25. annofabcli/project_member/invite_project_members.py +5 -6
  26. annofabcli/project_member/list_users.py +5 -7
  27. annofabcli/statistics/visualization/dataframe/task.py +0 -3
  28. annofabcli/task/complete_tasks.py +2 -2
  29. annofabcli/task/list_all_tasks.py +0 -1
  30. annofabcli/task/list_tasks.py +0 -2
  31. annofabcli/task/reject_tasks.py +76 -100
  32. annofabcli/task/update_metadata_of_task.py +1 -1
  33. annofabcli/task_history/list_task_history.py +1 -1
  34. {annofabcli-1.100.0.dist-info → annofabcli-100.3.dist-info}/METADATA +3 -8
  35. {annofabcli-1.100.0.dist-info → annofabcli-100.3.dist-info}/RECORD +38 -36
  36. {annofabcli-1.100.0.dist-info → annofabcli-100.3.dist-info}/LICENSE +0 -0
  37. {annofabcli-1.100.0.dist-info → annofabcli-100.3.dist-info}/WHEEL +0 -0
  38. {annofabcli-1.100.0.dist-info → annofabcli-100.3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,220 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import logging
6
+ import sys
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Any, Optional
10
+
11
+ import pandas
12
+ from annofabapi.models import Lang
13
+ from annofabapi.util.annotation_specs import get_message_with_lang
14
+ from dataclasses_json import DataClassJsonMixin
15
+
16
+ import annofabcli
17
+ import annofabcli.common.cli
18
+ from annofabcli.common.cli import (
19
+ COMMAND_LINE_ERROR_STATUS_CODE,
20
+ ArgumentParser,
21
+ CommandLine,
22
+ build_annofabapi_resource_and_login,
23
+ )
24
+ from annofabcli.common.enums import FormatArgument
25
+ from annofabcli.common.facade import AnnofabApiFacade
26
+ from annofabcli.common.utils import print_according_to_format, print_csv
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ @dataclass
32
+ class LabelAndAttribute(DataClassJsonMixin):
33
+ label_id: str
34
+ label_name_en: Optional[str]
35
+ label_name_ja: Optional[str]
36
+ label_name_vi: Optional[str]
37
+ annotation_type: str
38
+
39
+ attribute_id: str
40
+ """属性ID
41
+
42
+ Notes:
43
+ APIレスポンスの ``additional_data_definition_id`` に相当します。
44
+ ``additional_data_definition_id`` という名前がアノテーションJSONの `attributes` と対応していることが分かりにくかったので、`attribute_id`という名前に変えました。
45
+ """ # noqa: E501
46
+ attribute_name_en: Optional[str]
47
+ attribute_name_ja: Optional[str]
48
+ attribute_name_vi: Optional[str]
49
+ attribute_type: str
50
+
51
+
52
+ def create_label_attribute_list(labels_v3: list[dict[str, Any]], additionals_v3: list[dict[str, Any]]) -> list[LabelAndAttribute]:
53
+ """
54
+ APIから取得したラベル情報(v3版)から、`LabelAndAttribute`のlistを生成します。
55
+
56
+ Args:
57
+ labels_v3: APIから取得したラベル情報(v3版)
58
+ """
59
+
60
+ def to_dataclass_list(label: dict[str, Any]) -> list[LabelAndAttribute]:
61
+ result = []
62
+ for attribute_id in label["additional_data_definitions"]:
63
+ attribute = dict_attributes[attribute_id]
64
+
65
+ result.append(
66
+ LabelAndAttribute(
67
+ label_id=label["label_id"],
68
+ label_name_en=get_message_with_lang(label["label_name"], lang=Lang.EN_US),
69
+ label_name_ja=get_message_with_lang(label["label_name"], lang=Lang.JA_JP),
70
+ label_name_vi=get_message_with_lang(label["label_name"], lang=Lang.VI_VN),
71
+ annotation_type=label["annotation_type"],
72
+ attribute_id=attribute_id,
73
+ attribute_name_en=get_message_with_lang(attribute["name"], lang=Lang.EN_US),
74
+ attribute_name_ja=get_message_with_lang(attribute["name"], lang=Lang.JA_JP),
75
+ attribute_name_vi=get_message_with_lang(attribute["name"], lang=Lang.VI_VN),
76
+ attribute_type=attribute["type"],
77
+ )
78
+ )
79
+ return result
80
+
81
+ dict_attributes = {}
82
+ for elm in additionals_v3:
83
+ dict_attributes[elm["additional_data_definition_id"]] = elm
84
+
85
+ result = []
86
+ for label in labels_v3:
87
+ result.extend(to_dataclass_list(label))
88
+ return result
89
+
90
+
91
+ class PrintAnnotationSpecsLabelAndAttribute(CommandLine):
92
+ COMMON_MESSAGE = "annofabcli annotation_specs list_label: error:"
93
+
94
+ def print_annotation_specs_label(self, annotation_specs_v3: dict[str, Any], output_format: FormatArgument, output: Optional[str] = None) -> None:
95
+ # アノテーション仕様のv2とv3はほとんど同じなので、`convert_annotation_specs_labels_v2_to_v1`にはV3のアノテーション仕様を渡す
96
+ label_attribute_list = create_label_attribute_list(annotation_specs_v3["labels"], annotation_specs_v3["additionals"])
97
+
98
+ if output_format == FormatArgument.CSV:
99
+ columns = [
100
+ "label_id",
101
+ "label_name_en",
102
+ "label_name_ja",
103
+ "label_name_vi",
104
+ "annotation_type",
105
+ "attribute_id",
106
+ "attribute_name_en",
107
+ "attribute_name_ja",
108
+ "attribute_name_vi",
109
+ "attribute_type",
110
+ ]
111
+
112
+ df = pandas.DataFrame(label_attribute_list, columns=columns)
113
+ print_csv(df, output)
114
+
115
+ elif output_format in [FormatArgument.JSON, FormatArgument.PRETTY_JSON]:
116
+ print_according_to_format([e.to_dict() for e in label_attribute_list], format=output_format, output=output)
117
+
118
+ def get_history_id_from_before_index(self, project_id: str, before: int) -> Optional[str]:
119
+ histories, _ = self.service.api.get_annotation_specs_histories(project_id)
120
+ if before + 1 > len(histories):
121
+ logger.warning(f"アノテーション仕様の履歴は{len(histories)}個のため、最新より{before}個前のアノテーション仕様は見つかりませんでした。")
122
+ return None
123
+ history = histories[-(before + 1)]
124
+ logger.info(
125
+ f"{history['updated_datetime']}のアノテーション仕様を出力します。history_id={history['history_id']}, comment={history['comment']}"
126
+ )
127
+ return history["history_id"]
128
+
129
+ def main(self) -> None:
130
+ args = self.args
131
+
132
+ if args.project_id is not None:
133
+ if args.before is not None:
134
+ history_id = self.get_history_id_from_before_index(args.project_id, args.before)
135
+ if history_id is None:
136
+ print( # noqa: T201
137
+ f"{self.COMMON_MESSAGE} argument --before: 最新より{args.before}個前のアノテーション仕様は見つかりませんでした。",
138
+ file=sys.stderr,
139
+ )
140
+ sys.exit(COMMAND_LINE_ERROR_STATUS_CODE)
141
+ else:
142
+ # args.beforeがNoneならば、必ずargs.history_idはNoneでない
143
+ history_id = args.history_id
144
+
145
+ annotation_specs, _ = self.service.api.get_annotation_specs(args.project_id, query_params={"history_id": history_id, "v": "3"})
146
+
147
+ elif args.annotation_specs_json is not None:
148
+ with args.annotation_specs_json.open() as f:
149
+ annotation_specs = json.load(f)
150
+
151
+ else:
152
+ raise RuntimeError("'--project_id'か'--annotation_specs_json'のどちらかを指定する必要があります。")
153
+
154
+ self.print_annotation_specs_label(annotation_specs, output_format=FormatArgument(args.format), output=args.output)
155
+
156
+
157
+ def parse_args(parser: argparse.ArgumentParser) -> None:
158
+ argument_parser = ArgumentParser(parser)
159
+
160
+ required_group = parser.add_mutually_exclusive_group(required=True)
161
+ required_group.add_argument(
162
+ "-p", "--project_id", help="対象のプロジェクトのproject_idを指定します。APIで取得したアノテーション仕様情報を元に出力します。"
163
+ )
164
+ required_group.add_argument(
165
+ "--annotation_specs_json",
166
+ type=Path,
167
+ help="指定したアノテーション仕様のJSONファイルを指定します。"
168
+ "JSONファイルに記載された情報を元に出力します。ただしアノテーション仕様の ``format_version`` は ``3`` である必要があります。",
169
+ )
170
+
171
+ # 過去のアノテーション仕様を参照するためのオプション
172
+ old_annotation_specs_group = parser.add_mutually_exclusive_group()
173
+ old_annotation_specs_group.add_argument(
174
+ "--history_id",
175
+ type=str,
176
+ help=(
177
+ "出力したいアノテーション仕様のhistory_idを指定してください。 "
178
+ "history_idは ``annotation_specs list_history`` コマンドで確認できます。 "
179
+ "指定しない場合は、最新のアノテーション仕様が出力されます。 "
180
+ ),
181
+ )
182
+
183
+ old_annotation_specs_group.add_argument(
184
+ "--before",
185
+ type=int,
186
+ help=(
187
+ "出力したい過去のアノテーション仕様が、最新よりいくつ前のアノテーション仕様であるかを指定してください。 "
188
+ "たとえば ``1`` を指定した場合、最新より1個前のアノテーション仕様を出力します。 "
189
+ "指定しない場合は、最新のアノテーション仕様が出力されます。 "
190
+ ),
191
+ )
192
+
193
+ parser.add_argument(
194
+ "-f",
195
+ "--format",
196
+ type=str,
197
+ choices=[FormatArgument.CSV.value, FormatArgument.JSON.value, FormatArgument.PRETTY_JSON.value],
198
+ default=FormatArgument.CSV.value,
199
+ help="出力フォーマット ",
200
+ )
201
+
202
+ argument_parser.add_output()
203
+
204
+ parser.set_defaults(subcommand_func=main)
205
+
206
+
207
+ def main(args: argparse.Namespace) -> None:
208
+ service = build_annofabapi_resource_and_login(args)
209
+ facade = AnnofabApiFacade(service)
210
+ PrintAnnotationSpecsLabelAndAttribute(service, facade, args).main()
211
+
212
+
213
+ def add_parser(subparsers: Optional[argparse._SubParsersAction] = None) -> argparse.ArgumentParser:
214
+ subcommand_name = "list_label_attribute"
215
+
216
+ subcommand_help = "アノテーション仕様のラベルとラベルに含まれている属性の一覧を出力します。"
217
+
218
+ parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help)
219
+ parse_args(parser)
220
+ return parser
@@ -11,6 +11,7 @@ import annofabcli.annotation_specs.list_annotation_specs_attribute
11
11
  import annofabcli.annotation_specs.list_annotation_specs_choice
12
12
  import annofabcli.annotation_specs.list_annotation_specs_history
13
13
  import annofabcli.annotation_specs.list_annotation_specs_label
14
+ import annofabcli.annotation_specs.list_annotation_specs_label_attribute
14
15
  import annofabcli.annotation_specs.list_attribute_restriction
15
16
  import annofabcli.annotation_specs.list_label_color
16
17
  import annofabcli.annotation_specs.put_label_color
@@ -31,6 +32,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
31
32
  annofabcli.annotation_specs.list_annotation_specs_choice.add_parser(subparsers)
32
33
  annofabcli.annotation_specs.list_annotation_specs_history.add_parser(subparsers)
33
34
  annofabcli.annotation_specs.list_annotation_specs_label.add_parser(subparsers)
35
+ annofabcli.annotation_specs.list_annotation_specs_label_attribute.add_parser(subparsers)
34
36
  annofabcli.annotation_specs.list_label_color.add_parser(subparsers)
35
37
  annofabcli.annotation_specs.put_label_color.add_parser(subparsers)
36
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)
annofabcli/common/cli.py CHANGED
@@ -12,7 +12,6 @@ from pathlib import Path
12
12
  from typing import Any, Optional
13
13
 
14
14
  import annofabapi
15
- import jmespath
16
15
  import pandas
17
16
  import requests
18
17
  import yaml
@@ -458,15 +457,6 @@ class ArgumentParser:
458
457
 
459
458
  self.parser.add_argument("-o", "--output", type=str, required=required, help=help_message)
460
459
 
461
- def add_query(self, help_message: Optional[str] = None): # noqa: ANN201
462
- """
463
- '--query` 引数を追加
464
- """
465
- if help_message is None:
466
- help_message = "JMESPath形式で指定します。出力結果の抽出や、出力内容の変更に利用できます。"
467
-
468
- self.parser.add_argument("-q", "--query", type=str, help=help_message)
469
-
470
460
  def add_task_query(self, required: bool = False, help_message: Optional[str] = None): # noqa: ANN201, FBT001, FBT002
471
461
  if help_message is None:
472
462
  help_message = (
@@ -517,9 +507,6 @@ class CommandLineWithoutWebapi:
517
507
  #: Trueならば、処理中に現れる問い合わせに対して、常に'yes'と回答したものとして処理する。
518
508
  all_yes: bool = False
519
509
 
520
- #: JMesPath
521
- query: Optional[str] = None
522
-
523
510
  #: 出力先
524
511
  output: Optional[str] = None
525
512
 
@@ -602,26 +589,10 @@ class CommandLineWithoutWebapi:
602
589
 
603
590
  return True
604
591
 
605
- def search_with_jmespath_expression(self, target: Any) -> Any: # noqa: ANN401
606
- """
607
- インスタンスで保持しているJMespath情報で、targetの中身を探す。
608
- Args:
609
- target: 検索対象
610
-
611
- Returns:
612
- JMesPathで検索した結果。``self.query`` がNoneなら引数 ``target`` を返す。
613
-
614
- """
615
- if self.query is not None:
616
- return jmespath.search(self.query, target)
617
- return target
618
-
619
592
  def print_csv(self, df: pandas.DataFrame) -> None:
620
593
  print_csv(df, output=self.output, to_csv_kwargs=self.csv_format)
621
594
 
622
595
  def print_according_to_format(self, target: Any) -> None: # noqa: ANN401
623
- target = self.search_with_jmespath_expression(target)
624
-
625
596
  print_according_to_format(target, format=FormatArgument(self.str_format), output=self.output, csv_format=self.csv_format)
626
597
 
627
598
 
@@ -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)} 件の入力データを削除しました。")
@@ -243,7 +243,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
243
243
  argument_parser.add_output()
244
244
  argument_parser.add_csv_format()
245
245
 
246
- argument_parser.add_query()
247
246
  parser.set_defaults(subcommand_func=main)
248
247
 
249
248
 
@@ -42,7 +42,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
42
42
 
43
43
  argument_parser.add_format(choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON], default=FormatArgument.CSV)
44
44
  argument_parser.add_csv_format()
45
- argument_parser.add_query()
46
45
 
47
46
  parser.set_defaults(subcommand_func=main)
48
47
 
@@ -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
 
@@ -81,7 +81,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
81
81
  argument_parser.add_output()
82
82
  argument_parser.add_csv_format()
83
83
 
84
- argument_parser.add_query()
85
84
  parser.set_defaults(subcommand_func=main)
86
85
 
87
86
 
@@ -162,7 +162,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
162
162
  argument_parser.add_output()
163
163
  argument_parser.add_csv_format()
164
164
 
165
- argument_parser.add_query()
166
165
  parser.set_defaults(subcommand_func=main)
167
166
 
168
167
 
@@ -48,13 +48,11 @@ 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:
57
- organization_member_list = self.search_with_jmespath_expression(organization_member_list)
58
56
  df = pandas.DataFrame(organization_member_list)
59
57
  columns = get_columns_with_priority(df, prior_columns=self.PRIOR_COLUMNS)
60
58
  self.print_csv(df[columns])
@@ -86,7 +84,7 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
86
84
  )
87
85
  argument_parser.add_output()
88
86
  argument_parser.add_csv_format()
89
- argument_parser.add_query()
87
+
90
88
  parser.set_defaults(subcommand_func=main)
91
89
 
92
90
 
@@ -187,11 +187,9 @@ class ListProject(CommandLine):
187
187
  logger.info(f"プロジェクト一覧の件数: {len(project_list)}")
188
188
 
189
189
  if args.format == FormatArgument.MINIMAL_CSV.value:
190
- project_list = self.search_with_jmespath_expression(project_list)
191
190
  df = create_minimal_dataframe(project_list)
192
191
  self.print_csv(df)
193
192
  elif args.format == FormatArgument.CSV.value:
194
- project_list = self.search_with_jmespath_expression(project_list)
195
193
  df = pandas.DataFrame(project_list)
196
194
  columns = get_columns_with_priority(df, prior_columns=self.PRIOR_COLUMNS)
197
195
  self.print_csv(df[columns])
@@ -254,7 +252,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
254
252
  argument_parser.add_output()
255
253
  argument_parser.add_csv_format()
256
254
 
257
- argument_parser.add_query()
258
255
  parser.set_defaults(subcommand_func=main)
259
256
 
260
257
 
@@ -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)
@@ -75,9 +75,8 @@ class ListUser(CommandLine):
75
75
  include_inactive=args.include_inactive,
76
76
  )
77
77
 
78
- logger.info(f"プロジェトメンバ一覧の件数: {len(project_members)}")
78
+ logger.info(f"プロジェクトメンバ一覧の件数: {len(project_members)}")
79
79
  if args.format == FormatArgument.CSV.value:
80
- project_members = self.search_with_jmespath_expression(project_members)
81
80
  df = pandas.DataFrame(project_members)
82
81
  columns = get_columns_with_priority(df, prior_columns=self.PRIOR_COLUMNS)
83
82
  self.print_csv(df[columns])
@@ -111,7 +110,6 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
111
110
  )
112
111
  argument_parser.add_output()
113
112
  argument_parser.add_csv_format()
114
- argument_parser.add_query()
115
113
 
116
114
  parser.set_defaults(subcommand_func=main)
117
115
 
@@ -152,9 +152,6 @@ class Task:
152
152
  "inspection_comment_count",
153
153
  "inspection_comment_count_in_inspection_phase",
154
154
  "inspection_comment_count_in_acceptance_phase",
155
- # タスクの状態
156
- "inspection_is_skipped",
157
- "acceptance_is_skipped",
158
155
  ]
159
156
 
160
157
  @property