annofabcli 1.99.0__py3-none-any.whl → 1.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.
- annofabcli/__version__.py +1 -1
- annofabcli/annotation/change_annotation_attributes.py +2 -2
- annofabcli/annotation_specs/add_attribute_restriction.py +158 -0
- annofabcli/annotation_specs/attribute_restriction.py +240 -0
- annofabcli/annotation_specs/list_annotation_specs_attribute.py +51 -33
- annofabcli/annotation_specs/list_annotation_specs_choice.py +42 -17
- annofabcli/annotation_specs/list_annotation_specs_history.py +0 -1
- annofabcli/annotation_specs/list_annotation_specs_label.py +26 -93
- annofabcli/annotation_specs/list_annotation_specs_label_attribute.py +220 -0
- annofabcli/annotation_specs/list_attribute_restriction.py +8 -209
- annofabcli/annotation_specs/subcommand_annotation_specs.py +4 -0
- annofabcli/common/annofab/annotation_specs.py +25 -0
- annofabcli/common/cli.py +0 -29
- annofabcli/common/download.py +1 -1
- annofabcli/common/exceptions.py +2 -2
- annofabcli/common/facade.py +2 -2
- annofabcli/common/image.py +2 -2
- annofabcli/input_data/delete_input_data.py +6 -7
- annofabcli/input_data/list_input_data.py +0 -1
- annofabcli/instruction/list_instruction_history.py +0 -1
- annofabcli/job/delete_job.py +4 -4
- annofabcli/job/list_job.py +0 -1
- annofabcli/job/list_last_job.py +0 -1
- annofabcli/organization_member/list_organization_member.py +3 -5
- annofabcli/project/list_project.py +0 -3
- annofabcli/project_member/change_project_members.py +2 -3
- annofabcli/project_member/drop_project_members.py +8 -8
- annofabcli/project_member/invite_project_members.py +5 -6
- annofabcli/project_member/list_users.py +5 -7
- annofabcli/statistics/visualization/dataframe/task.py +0 -3
- annofabcli/task/complete_tasks.py +2 -2
- annofabcli/task/list_all_tasks.py +0 -1
- annofabcli/task/list_tasks.py +0 -2
- annofabcli/task/reject_tasks.py +76 -100
- annofabcli/task/update_metadata_of_task.py +1 -1
- annofabcli/task_history/list_task_history.py +1 -1
- {annofabcli-1.99.0.dist-info → annofabcli-1.100.3.dist-info}/METADATA +3 -8
- {annofabcli-1.99.0.dist-info → annofabcli-1.100.3.dist-info}/RECORD +41 -37
- {annofabcli-1.99.0.dist-info → annofabcli-1.100.3.dist-info}/LICENSE +0 -0
- {annofabcli-1.99.0.dist-info → annofabcli-1.100.3.dist-info}/WHEEL +0 -0
- {annofabcli-1.99.0.dist-info → annofabcli-1.100.3.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
|
|
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 =
|
|
65
|
+
main_obj = AttributeRestrictionMessage(
|
|
267
66
|
labels=annotation_specs["labels"],
|
|
268
67
|
additionals=annotation_specs["additionals"],
|
|
269
|
-
|
|
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
|
|
327
|
-
default=
|
|
125
|
+
choices=[e.value for e in OutputFormat],
|
|
126
|
+
default=OutputFormat.TEXT.value,
|
|
328
127
|
help=f"出力フォーマット\n"
|
|
329
128
|
"\n"
|
|
330
|
-
f"* {
|
|
331
|
-
f"* {
|
|
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)
|
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
|
|
annofabcli/common/download.py
CHANGED
|
@@ -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,
|
annofabcli/common/exceptions.py
CHANGED
|
@@ -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"
|
|
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"
|
|
60
|
+
msg = f"組織'{organization_name}'に対して、ロール'{role_values}'のいずれかが必要です。"
|
|
61
61
|
super().__init__(msg)
|
|
62
62
|
|
|
63
63
|
|
annofabcli/common/facade.py
CHANGED
|
@@ -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(
|
|
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
|
|
annofabcli/common/image.py
CHANGED
|
@@ -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
|
|
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
|
|
141
|
-
logger.warning(
|
|
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)} 件の入力データを削除しました。")
|
|
@@ -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
|
|
annofabcli/job/delete_job.py
CHANGED
|
@@ -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
|
|
39
|
-
logger.warning(
|
|
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
|
|
annofabcli/job/list_job.py
CHANGED
annofabcli/job/list_last_job.py
CHANGED
|
@@ -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
|
|
52
|
-
logger.warning(
|
|
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
|
-
|
|
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
|
|
119
|
-
logger.warning(
|
|
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})
|
|
53
|
-
except requests.HTTPError
|
|
54
|
-
logger.warning(
|
|
55
|
-
|
|
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
|
|
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
|
|
73
|
-
logger.warning(
|
|
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
|
|
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
|
|
79
|
-
logger.warning(
|
|
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):
|