diff --git a/annofabapi/utils.py b/annofabapi/utils.py index 193c2908..e2a247d7 100644 --- a/annofabapi/utils.py +++ b/annofabapi/utils.py @@ -7,12 +7,14 @@ import datetime import logging from pathlib import Path -from typing import Optional +from typing import List, Optional import dateutil import dateutil.tz import requests +from annofabapi.models import TaskHistory, TaskPhase + def raise_for_status(response: requests.Response): """ @@ -131,3 +133,71 @@ def wrapped(*args, **kwargs): logging.getLogger("backoff").setLevel(level=backoff_logger_level) return wrapped + + +def get_task_history_index_skipped_acceptance(task_history_list: List[TaskHistory]) -> List[int]: + """ + 受入がスキップされたタスク履歴のインデックス番号(0始まり)を返す。 +​ + Args: + task_history_list: タスク履歴List +​ + Returns: + 受入フェーズがスキップされた履歴のインデックス番号(0始まり)。受入がスキップされていない場合は空リストを返す。 +​ + """ + index_list = [] + for index, history in enumerate(task_history_list): + if not (TaskPhase(history["phase"]) == TaskPhase.ACCEPTANCE and history["account_id"] is None + and history["accumulated_labor_time_milliseconds"] == "PT0S" and history["started_datetime"] is not None + and history["ended_datetime"] is not None): + continue + + if index + 1 < len(task_history_list): + # 直後の履歴あり + next_history = task_history_list[index + 1] + if TaskPhase(next_history["phase"]) in [TaskPhase.ANNOTATION, TaskPhase.INSPECTION]: + # 教師付フェーズ or 検査フェーズでの提出取消(直後が前段のフェーズ) + pass + else: + # 受入スキップ + index_list.append(index) + else: + # 直後の履歴がない + index_list.append(index) + + return index_list + + +def get_task_history_index_skipped_inspection(task_history_list: List[TaskHistory]) -> List[int]: + """ + 検査フェーズがスキップされたタスク履歴のインデックス番号(0始まり)を返す。 +​ + Args: + task_history_list: タスク履歴List +​ + Returns: + 検査フェーズがスキップされた履歴のインデックス番号(0始まり)。検査がスキップされていない場合は空リストを返す。 +​ + """ + index_list = [] + for index, history in enumerate(task_history_list): + if not (TaskPhase(history["phase"]) == TaskPhase.INSPECTION and history["account_id"] is None + and history["accumulated_labor_time_milliseconds"] == "PT0S" and history["started_datetime"] is not None + and history["ended_datetime"] is not None): + continue + + if index + 1 < len(task_history_list): + # 直後の履歴あり + next_history = task_history_list[index + 1] + if TaskPhase(next_history["phase"]) in [TaskPhase.ANNOTATION, TaskPhase.INSPECTION]: + # 教師付フェーズ or 検査フェーズでの提出取消(直後が前段のフェーズ) + pass + else: + # 検査スキップ + index_list.append(index) + else: + # 直後の履歴がない + index_list.append(index) + + return index_list diff --git a/setup.cfg b/setup.cfg index 51d3d779..25344253 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,7 +12,7 @@ split_before_named_assigns = False [flake8] max-line-length = 120 # F401: 'xxxx' imported but unused -ignore = F401 +ignore = F401, W503 [mypy] ignore_missing_imports = True diff --git a/setup.py b/setup.py index 1dc78f35..b3f2b194 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Topic :: Utilities", "Operating System :: OS Independent", ], diff --git a/tests/test_local_utils.py b/tests/test_local_utils.py index de768a81..db47cd97 100644 --- a/tests/test_local_utils.py +++ b/tests/test_local_utils.py @@ -3,3 +3,236 @@ # d = datetime.datetime(2019, 10, 8, 16, 20, 8, 241762) # tz_jst = datetime.timezone(datetime.timedelta(hours=9)) # assert to_iso8601_extension(d, tz_jst) == "2019-10-08T16:20:08.241+09:00" + +from annofabapi.utils import get_task_history_index_skipped_acceptance, get_task_history_index_skipped_inspection + + +class TestTaskHistoryUtils: + ACCOUNT_ID = "12345678-abcd-1234-abcd-1234abcd5678" + + def test_get_task_history_index_skipped_acceptance_検査0回_受入スキップ(self): + task_history_list = [{ + "started_datetime": "2020-01-22T09:32:15.284+09:00", + "ended_datetime": "2020-01-22T09:32:19.628+09:00", + "accumulated_labor_time_milliseconds": "PT4.344S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T09:32:19.63+09:00", + "ended_datetime": "2020-01-22T09:32:19.63+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "acceptance", + "phase_stage": 1, + "account_id": None + }] + + actual = get_task_history_index_skipped_acceptance(task_history_list) + expected = [1] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_acceptance_検査0回_受入スキップ後に受入取消(self): + task_history_list = [{ + "started_datetime": "2020-01-22T09:35:26.13+09:00", + "ended_datetime": "2020-01-22T09:35:29.745+09:00", + "accumulated_labor_time_milliseconds": "PT3.615S", + "phase": "annotation", + "phase_stage": 1, + "account_id": "00589ed0-dd63-40db-abb2-dfe5e13c8299" + }, { + "started_datetime": "2020-01-22T09:35:29.747+09:00", + "ended_datetime": "2020-01-22T09:35:29.747+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "acceptance", + "phase_stage": 1, + "account_id": None + }, { + "started_datetime": None, + "ended_datetime": None, + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "acceptance", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }] + + actual = get_task_history_index_skipped_acceptance(task_history_list) + expected = [1] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_acceptance_検査0回_教師付で提出取消(self): + task_history_list = [{ + "started_datetime": "2020-01-22T09:36:11.187+09:00", + "ended_datetime": "2020-01-22T09:36:14.186+09:00", + "accumulated_labor_time_milliseconds": "PT2.999S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T09:36:23.86+09:00", + "ended_datetime": "2020-01-22T09:36:23.86+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "acceptance", + "phase_stage": 1, + "account_id": None + }, { + "started_datetime": None, + "ended_datetime": None, + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }] + actual = get_task_history_index_skipped_acceptance(task_history_list) + expected = [] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_acceptance_検査1回_検査で提出取消(self): + task_history_list = [{ + "started_datetime": "2020-01-22T09:39:20.492+09:00", + "ended_datetime": "2020-01-22T09:39:24.911+09:00", + "accumulated_labor_time_milliseconds": "PT4.419S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T09:40:04.978+09:00", + "ended_datetime": "2020-01-22T09:40:08.091+09:00", + "accumulated_labor_time_milliseconds": "PT3.113S", + "phase": "inspection", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T09:40:15.136+09:00", + "ended_datetime": "2020-01-22T09:40:15.136+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "acceptance", + "phase_stage": 1, + "account_id": None + }, { + "started_datetime": None, + "ended_datetime": None, + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "inspection", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }] + + actual = get_task_history_index_skipped_acceptance(task_history_list) + expected = [] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_inspection_検査1回_検査スキップ(self): + task_history_list = [{ + "started_datetime": "2020-01-22T09:58:20.063+09:00", + "ended_datetime": "2020-01-22T09:58:23.749+09:00", + "accumulated_labor_time_milliseconds": "PT3.686S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T09:58:23.751+09:00", + "ended_datetime": "2020-01-22T09:58:23.751+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "inspection", + "phase_stage": 1, + "account_id": None + }, { + "started_datetime": "2020-01-22T09:58:23.753+09:00", + "ended_datetime": "2020-01-22T09:58:23.753+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "acceptance", + "phase_stage": 1, + "account_id": None + }] + + actual = get_task_history_index_skipped_inspection(task_history_list) + expected = [1] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_inspection_検査1回_教師付で提出取消(self): + task_history_list = [{ + "started_datetime": "2020-01-22T10:00:33.832+09:00", + "ended_datetime": "2020-01-22T10:00:37.381+09:00", + "accumulated_labor_time_milliseconds": "PT3.549S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T10:00:45.953+09:00", + "ended_datetime": "2020-01-22T10:00:45.953+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "inspection", + "phase_stage": 1, + "account_id": None + }, { + "started_datetime": None, + "ended_datetime": None, + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }] + actual = get_task_history_index_skipped_inspection(task_history_list) + expected = [] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_inspection_検査1回_教師付で提出取消(self): + task_history_list = [{ + "started_datetime": "2020-01-22T10:00:33.832+09:00", + "ended_datetime": "2020-01-22T10:00:37.381+09:00", + "accumulated_labor_time_milliseconds": "PT3.549S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T10:00:45.953+09:00", + "ended_datetime": "2020-01-22T10:00:45.953+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "inspection", + "phase_stage": 1, + "account_id": None + }, { + "started_datetime": None, + "ended_datetime": None, + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }] + actual = get_task_history_index_skipped_inspection(task_history_list) + expected = [] + assert all([a == b for a, b in zip(actual, expected)]) + + def test_get_task_history_index_skipped_inspection_検査2回_検査1回目で提出取消(self): + task_history_list = [{ + "started_datetime": "2019-09-04T16:15:51.505+09:00", + "ended_datetime": "2019-09-04T16:16:31.597+09:00", + "accumulated_labor_time_milliseconds": "PT40.092S", + "phase": "annotation", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T10:06:18.435+09:00", + "ended_datetime": "2020-01-22T10:06:21.919+09:00", + "accumulated_labor_time_milliseconds": "PT3.484S", + "phase": "inspection", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }, { + "started_datetime": "2020-01-22T10:07:38.456+09:00", + "ended_datetime": "2020-01-22T10:07:38.456+09:00", + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "inspection", + "phase_stage": 2, + "account_id": None + }, { + "started_datetime": None, + "ended_datetime": None, + "accumulated_labor_time_milliseconds": "PT0S", + "phase": "inspection", + "phase_stage": 1, + "account_id": self.ACCOUNT_ID + }] + actual = get_task_history_index_skipped_inspection(task_history_list) + expected = [] + assert all([a == b for a, b in zip(actual, expected)])