Skip to content

404 ErrorのときはNoneを返すメソッドを追加 #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions annofabapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,33 @@ def to_iso8601_extension(d: datetime.datetime, tz: Optional[datetime.tzinfo] = N
tz = dateutil.tz.tzlocal()
d = d.astimezone(tz)
return d.isoformat(timespec='milliseconds')


def allow_404_error(function):
"""
Not Found Error(404)を無視(許容)して、処理する。Not Foundのとき戻りはNoneになる。
リソースの存在確認などに利用する。
try-exceptを行う。また404 Errorが発生したときのエラーログを無効化する
"""
def wrapped(*args, **kwargs):
annofabapi_logger_level = logging.getLogger("annofabapi").level
backoff_logger_level = logging.getLogger("backoff").level

try:
# 不要なログが出力されないようにする
logging.getLogger("annofabapi").setLevel(level=logging.INFO)
logging.getLogger("backoff").setLevel(level=logging.CRITICAL)

return function(*args, **kwargs)

except requests.exceptions.HTTPError as e:
if e.response.status_code == requests.codes.not_found:
return None
else:
raise e
finally:
# ロガーの設定を元に戻す
logging.getLogger("annofabapi").setLevel(level=annofabapi_logger_level)
logging.getLogger("backoff").setLevel(level=backoff_logger_level)

return wrapped
118 changes: 104 additions & 14 deletions annofabapi/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import annofabapi.utils
from annofabapi import AnnofabApi
from annofabapi.exceptions import AnnofabApiException
from annofabapi.models import (AnnotationSpecs, InputData, Inspection, Instruction, JobInfo, JobType, MyOrganization,
OrganizationMember, Project, ProjectMember, SupplementaryData, Task, InspectionStatus)
from annofabapi.models import (AnnotationSpecs, InputData, Inspection, InspectionStatus, Instruction, JobInfo, JobType,
MyOrganization, Organization, OrganizationMember, Project, ProjectMember,
SupplementaryData, Task)
from annofabapi.utils import allow_404_error

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -103,7 +105,7 @@ def _get_all_objects(func_get_list: Callable, limit: int, **kwargs_for_func_get_
return all_objects

#########################################
# Public Method : AfAnnotationApi
# Public Method : Annotation
#########################################
def download_annotation_archive(self, project_id: str, dest_path: str, v2: bool = False) -> str:
"""
Expand Down Expand Up @@ -165,7 +167,7 @@ def get_all_annotation_list(self, project_id: str,
query_params=query_params)

#########################################
# Public Method : AfAnnotationSpecsApi
# Public Method : AnnotationSpecs
#########################################
def copy_annotation_specs(self, src_project_id: str, dest_project_id: str,
comment: Optional[str] = None) -> AnnotationSpecs:
Expand Down Expand Up @@ -196,8 +198,23 @@ def copy_annotation_specs(self, src_project_id: str, dest_project_id: str,
return self.api.put_annotation_specs(dest_project_id, request_body=request_body)[0]

#########################################
# Public Method : AfInputApi
# Public Method : Input
#########################################
@allow_404_error
def get_input_data_or_none(self, project_id: str, input_data_id: str) -> Optional[InputData]:
"""
入力データを取得する。存在しない場合(HTTP 404 Error)はNoneを返す。

Args:
project_id:
input_data_id:

Returns:
入力データ
"""
input_data, _ = self.api.get_input_data(project_id, input_data_id)
return input_data

def get_all_input_data_list(self, project_id: str,
query_params: Optional[Dict[str, Any]] = None) -> List[InputData]:
"""
Expand Down Expand Up @@ -277,7 +294,7 @@ def put_input_data_from_file(self, project_id: str, input_data_id: str, file_pat
return self.api.put_input_data(project_id, input_data_id, request_body=copied_request_body)[0]

#########################################
# Public Method : AfStatisticsApi
# Public Method : Statistics
#########################################
def get_worktime_statistics(self, project_id: str) -> Dict[str, Any]:
"""
Expand All @@ -296,7 +313,7 @@ def get_worktime_statistics(self, project_id: str) -> Dict[str, Any]:
return requests.get(url).json()

#########################################
# Public Method : AfSupplementaryApi
# Public Method : Supplementary
#########################################
def put_supplementary_data_from_file(self, project_id, input_data_id: str, supplementary_data_id: str,
file_path: str, request_body: Dict[str, Any],
Expand Down Expand Up @@ -344,7 +361,7 @@ def put_supplementary_data_from_file(self, project_id, input_data_id: str, suppl
request_body=copied_request_body)[0]

#########################################
# Public Method : AfInspection
# Public Method : Inspection
#########################################
def update_status_of_inspections(self, project_id: str, task_id: str, input_data_id: str,
filter_inspection: Callable[[Inspection], bool],
Expand Down Expand Up @@ -386,7 +403,7 @@ def search_updated_inspections(arg_inspection: Inspection) -> bool:
return content

#########################################
# Public Method : AfMyApi
# Public Method : My
#########################################
def get_all_my_organizations(self) -> List[MyOrganization]:
"""
Expand All @@ -398,8 +415,22 @@ def get_all_my_organizations(self) -> List[MyOrganization]:
return self._get_all_objects(self.api.get_my_organizations, limit=200)

#########################################
# Public Method : AfOrganizationApi
# Public Method : Organization
#########################################
@allow_404_error
def get_organization_or_none(self, organization_name: str) -> Optional[Organization]:
"""
組織情報を取得する。存在しない場合(HTTP 404 Error)はNoneを返す。

Args:
organization_name: 組織名

Returns:
組織情報
"""
content, _ = self.api.get_organization(organization_name)
return content

def get_all_projects_of_organization(self, organization_name: str,
query_params: Optional[Dict[str, Any]] = None) -> List[Project]:
"""
Expand All @@ -416,8 +447,23 @@ def get_all_projects_of_organization(self, organization_name: str,
organization_name=organization_name, query_params=query_params)

#########################################
# Public Method : AfOrganizationMemberApi
# Public Method : OrganizationMember
#########################################
@allow_404_error
def get_organization_member_or_none(self, organization_name: str, user_id: str) -> Optional[OrganizationMember]:
"""
組織メンバを取得する。存在しない場合(HTTP 404 Error)はNoneを返す。

Args:
organization_name: 組織名
user_id:

Returns:
組織メンバ
"""
content, _ = self.api.get_organization_member(organization_name, user_id)
return content

def get_all_organization_members(self, organization_name: str) -> List[OrganizationMember]:
"""
すべての組織メンバ一覧を取得する
Expand All @@ -434,8 +480,22 @@ def get_all_organization_members(self, organization_name: str) -> List[Organizat
return content["list"]

#########################################
# Public Method : AfProjectApi
# Public Method : Project
#########################################
@allow_404_error
def get_project_or_none(self, project_id: str) -> Optional[Project]:
"""
プロジェクトを取得する。存在しない場合(HTTP 404 Error)はNoneを返す。

Args:
project_id:

Returns:
プロジェクト
"""
content, _ = self.api.get_project(project_id)
return content

def download_project_tasks_url(self, project_id: str, dest_path: str) -> str:
"""
プロジェクトのタスク全件ファイルをダウンロードする。
Expand Down Expand Up @@ -494,8 +554,23 @@ def download_project_task_history_events_url(self, project_id: str, dest_path: s
return url

#########################################
# Public Method : AfProjectMemberApi
# Public Method : ProjectMember
#########################################
@allow_404_error
def get_project_member_or_none(self, project_id: str, user_id: str) -> Optional[ProjectMember]:
"""
プロジェクトメンバを取得する。存在しない場合(HTTP 404 Error)はNoneを返す。

Args:
project_id:
user_id:

Returns:
プロジェクトメンバ
"""
content, _ = self.api.get_project_member(project_id, user_id)
return content

def get_all_project_members(self, project_id: str,
query_params: Optional[Dict[str, Any]] = None) -> List[ProjectMember]:
"""
Expand Down Expand Up @@ -640,7 +715,7 @@ def to_inactive(arg_member):
return self.put_project_members(dest_project_id, src_project_members)

#########################################
# Public Method : AfTaskApi
# Public Method : Task
#########################################
def initiate_tasks_generation_by_csv(self, project_id: str, csvfile_path: str,
task_id_prefix: str) -> Dict[str, Any]:
Expand Down Expand Up @@ -669,6 +744,21 @@ def initiate_tasks_generation_by_csv(self, project_id: str, csvfile_path: str,
}
return self.api.initiate_tasks_generation(project_id, request_body=request_body)[0]

@allow_404_error
def get_task_or_none(self, project_id: str, task_id: str) -> Optional[Task]:
"""
タスクを取得する。存在しない場合(HTTP 404 Error)はNoneを返す。

Args:
project_id:
task_id:

Returns:
タスク
"""
content, _ = self.api.get_task(project_id, task_id)
return content

def get_all_tasks(self, project_id: str, query_params: Optional[Dict[str, Any]] = None) -> List[Task]:
"""
すべてのタスクを取得する。
Expand Down
55 changes: 46 additions & 9 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@

annofab_user_id = service.api.login_user_id

task_id = test_wrapper.get_first_task_id(project_id)
input_data_id = test_wrapper.get_first_input_data_id_in_task(project_id, task_id)


def test_account():
pass
Expand Down Expand Up @@ -84,9 +87,6 @@ def test_annotation():
batchUpdateAnnotations, putAnnotationはテストしない.
"""

task_id = test_wrapper.get_first_task_id(project_id)
input_data_id = test_wrapper.get_first_input_data_id_in_task(project_id, task_id)

print("get_annotation_list in wrapper.get_all_annotation_list")
assert len(wrapper.get_all_annotation_list(project_id, {"query": {"task_id": task_id}})) >= 0

Expand Down Expand Up @@ -182,8 +182,6 @@ def test_input():


def test_supplementary():
input_data_id = test_wrapper.get_first_input_data(project_id)['input_data_id']

print("wrapper.put_supplementary_data_from_file(内部でput_supplementary_dataが実行される)")
supplementary_data_id = str(uuid.uuid4())
request_body = {'supplementary_data_number': 1}
Expand All @@ -206,9 +204,6 @@ def test_inspection():
batchUpdateInspectionsはテストしない.
"""

task_id = test_wrapper.get_first_task_id(project_id)
input_data_id = test_wrapper.get_first_input_data_id_in_task(project_id, task_id)

# # 作業中のタスクでなくても、検査コメントは付与できる
# req_inspection = [{
# "data": {
Expand Down Expand Up @@ -327,7 +322,6 @@ def test_statistics(self):
def test_graph_marker(self):
print("get_markers")
content, _ = api.get_markers(project_id)
print(content)
assert type(content) == dict

markers = [{
Expand Down Expand Up @@ -464,3 +458,46 @@ def test_webhook():

print("delete_webhook")
assert type(api.delete_webhook(project_id, test_webhook_id)[0]) == dict


class TestGetObjOrNone:
"""
wrapper.get_xxx_or_none メソッドの確認
"""
def test_get_input_data_or_none(self):
assert type(wrapper.get_input_data_or_none(project_id, input_data_id)) == dict

assert wrapper.get_input_data_or_none(project_id, "not-exists") is None

assert wrapper.get_input_data_or_none("not-exists", input_data_id) is None

def test_get_organization_or_none(self):
assert type(wrapper.get_organization_or_none(organization_name)) == dict

assert wrapper.get_organization_or_none("not-exists") is None

def test_get_organization_member_or_none(self):
assert type(wrapper.get_organization_member_or_none(organization_name, annofab_user_id)) == dict

assert wrapper.get_organization_member_or_none("not-exists", annofab_user_id) is None

assert wrapper.get_organization_member_or_none(organization_name, "not-exists") is None

def test_get_project_or_none(self):
assert type(wrapper.get_project_or_none(project_id)) == dict

assert wrapper.get_project_or_none("not-exists") is None

def test_get_project_member_or_none(self):
assert type(wrapper.get_project_member_or_none(project_id, annofab_user_id)) == dict

assert wrapper.get_project_member_or_none(project_id, "not-exists") is None

assert wrapper.get_project_member_or_none("not-exists", annofab_user_id) is None

def test_get_task_or_none(self):
assert type(wrapper.get_task_or_none(project_id, task_id)) == dict

assert wrapper.get_task_or_none(project_id, "not-exists") is None

assert wrapper.get_task_or_none("not-exists", task_id) is None