diff --git a/annofabcli/stat_visualization/mask_visualization_dir.py b/annofabcli/stat_visualization/mask_visualization_dir.py
index 87d3105c..ac4549ab 100755
--- a/annofabcli/stat_visualization/mask_visualization_dir.py
+++ b/annofabcli/stat_visualization/mask_visualization_dir.py
@@ -33,7 +33,7 @@
from annofabcli.statistics.visualization.dataframe.task_worktime_by_phase_user import TaskWorktimeByPhaseUser
from annofabcli.statistics.visualization.dataframe.user_performance import UserPerformance
from annofabcli.statistics.visualization.dataframe.worktime_per_date import WorktimePerDate
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
from annofabcli.statistics.visualization.project_dir import ProjectDir
logger = logging.getLogger(__name__)
@@ -223,12 +223,15 @@ def main(args: argparse.Namespace) -> None:
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
)
+ task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
input_project_dir = ProjectDir(
args.dir,
+ task_completion_criteria,
custom_production_volume_list=custom_production_volume_list,
)
output_project_dir = ProjectDir(
args.output_dir,
+ task_completion_criteria,
metadata=input_project_dir.read_metadata(),
)
mask_visualization_dir(
@@ -271,6 +274,16 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
"column_list": [{"value": "video_duration_minute", "name": "動画長さ"}],
}
+ parser.add_argument(
+ "--task_completion_criteria",
+ type=str,
+ choices=[e.value for e in TaskCompletionCriteria],
+ default=TaskCompletionCriteria.ACCEPTANCE_COMPLETED.value,
+ help="タスクの完了条件を指定します。\n"
+ "* ``acceptance_completed``: タスクが受入フェーズの完了状態であれば「タスクの完了」とみなす\n"
+ "* ``acceptance_reached``: タスクが受入フェーズに到達したら「タスクの完了」とみなす\n",
+ )
+
parser.add_argument(
"--custom_production_volume",
type=str,
diff --git a/annofabcli/stat_visualization/merge_visualization_dir.py b/annofabcli/stat_visualization/merge_visualization_dir.py
index cd5b4add..fd359d83 100755
--- a/annofabcli/stat_visualization/merge_visualization_dir.py
+++ b/annofabcli/stat_visualization/merge_visualization_dir.py
@@ -35,7 +35,7 @@
WholeProductivityPerFirstAnnotationStartedDate,
)
from annofabcli.statistics.visualization.dataframe.worktime_per_date import WorktimePerDate
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
from annofabcli.statistics.visualization.project_dir import MergingInfo, ProjectDir
logger = logging.getLogger(__name__)
@@ -45,11 +45,13 @@ class WritingVisualizationFile:
def __init__(
self,
output_project_dir: ProjectDir,
+ task_completion_criteria: TaskCompletionCriteria,
*,
user_id_list: Optional[List[str]] = None,
minimal_output: bool = False,
) -> None:
self.output_project_dir = output_project_dir
+ self.task_completion_criteria = task_completion_criteria
self.user_id_list = user_id_list
self.minimal_output = minimal_output
@@ -126,13 +128,13 @@ def write_task_worktime_by_phase_user(self, task_worktime_by_phase_user: TaskWor
@_catch_exception
def write_performance_per_first_annotation_started_date(self, task: Task) -> None:
- obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task)
+ obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task, self.task_completion_criteria)
self.output_project_dir.write_whole_productivity_per_first_annotation_started_date(obj)
self.output_project_dir.write_whole_productivity_line_graph_per_annotation_started_date(obj)
@_catch_exception
def write_merge_performance_per_date(self, task: Task, worktime_per_date: WorktimePerDate) -> None:
- obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date)
+ obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date, task_completion_criteria=self.task_completion_criteria)
self.output_project_dir.write_whole_productivity_per_date(obj)
self.output_project_dir.write_whole_productivity_line_graph_per_date(obj)
@@ -176,7 +178,7 @@ def merge_task_worktime_by_phase_user(self) -> TaskWorktimeByPhaseUser:
merged_obj = TaskWorktimeByPhaseUser.merge(*tmp_list, custom_production_volume_list=self.custom_production_volume_list)
return merged_obj
- def create_merging_info(self) -> MergingInfo:
+ def create_merging_info(self, task_completion_criteria: TaskCompletionCriteria) -> MergingInfo:
"""
`project_info.json`の内容から、どのようにマージしたかを示す情報を作成する。
"""
@@ -186,12 +188,15 @@ def create_merging_info(self) -> MergingInfo:
project_info = project_dir.read_project_info()
project_info_list.append(project_info)
- merge_info = MergingInfo(target_dir_list=target_dir_list, project_info_list=project_info_list)
+ merge_info = MergingInfo(
+ target_dir_list=target_dir_list, project_info_list=project_info_list, task_completion_criteria=task_completion_criteria
+ )
return merge_info
def merge_visualization_dir( # pylint: disable=too-many-statements
project_dir_list: List[ProjectDir],
+ task_completion_criteria: TaskCompletionCriteria,
output_project_dir: ProjectDir,
*,
custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None,
@@ -200,7 +205,7 @@ def merge_visualization_dir( # pylint: disable=too-many-statements
) -> None:
merging_obj = MergingVisualizationFile(project_dir_list, custom_production_volume_list=custom_production_volume_list)
- merging_info = merging_obj.create_merging_info()
+ merging_info = merging_obj.create_merging_info(task_completion_criteria)
output_project_dir.metadata = merging_info.to_dict(encode_json=True)
# 基本となるCSVファイルを読み込みマージする
@@ -210,7 +215,9 @@ def merge_visualization_dir( # pylint: disable=too-many-statements
user_performance = UserPerformance.from_df_wrapper(task_worktime_by_phase_user=task_worktime_by_phase_user, worktime_per_date=worktime_per_date)
whole_performance = WholePerformance.from_df_wrapper(task_worktime_by_phase_user=task_worktime_by_phase_user, worktime_per_date=worktime_per_date)
- writing_obj = WritingVisualizationFile(output_project_dir, user_id_list=user_id_list, minimal_output=minimal_output)
+ writing_obj = WritingVisualizationFile(
+ output_project_dir, user_id_list=user_id_list, minimal_output=minimal_output, task_completion_criteria=task_completion_criteria
+ )
writing_obj.write_task_list_and_histogram(task)
writing_obj.write_worktime_per_date(worktime_per_date)
@@ -258,13 +265,14 @@ def main(args: argparse.Namespace) -> None:
custom_production_volume_list = (
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
)
-
+ task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
merge_visualization_dir(
- project_dir_list=[ProjectDir(e) for e in args.dir],
+ project_dir_list=[ProjectDir(e, task_completion_criteria) for e in args.dir],
+ task_completion_criteria=task_completion_criteria,
user_id_list=user_id_list,
custom_production_volume_list=custom_production_volume_list,
minimal_output=args.minimal,
- output_project_dir=ProjectDir(args.output_dir),
+ output_project_dir=ProjectDir(args.output_dir, task_completion_criteria),
)
@@ -272,6 +280,16 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--dir", type=Path, nargs="+", required=True, help="マージ対象ディレクトリ。2つ以上指定してください。")
parser.add_argument("-o", "--output_dir", type=Path, required=True, help="出力先ディレクトリ。配下にプロジェクト名のディレクトリが出力される。")
+ parser.add_argument(
+ "--task_completion_criteria",
+ type=str,
+ choices=[e.value for e in TaskCompletionCriteria],
+ default=TaskCompletionCriteria.ACCEPTANCE_COMPLETED.value,
+ help="タスクの完了条件を指定します。\n"
+ "* ``acceptance_completed``: タスクが受入フェーズの完了状態であれば「タスクの完了」とみなす\n"
+ "* ``acceptance_reached``: タスクが受入フェーズに到達したら「タスクの完了」とみなす\n",
+ )
+
parser.add_argument(
"-u",
"--user_id",
diff --git a/annofabcli/stat_visualization/summarize_whole_performance_csv.py b/annofabcli/stat_visualization/summarize_whole_performance_csv.py
index a7926eef..d01aa61c 100644
--- a/annofabcli/stat_visualization/summarize_whole_performance_csv.py
+++ b/annofabcli/stat_visualization/summarize_whole_performance_csv.py
@@ -11,7 +11,7 @@
get_json_from_args,
)
from annofabcli.statistics.visualization.dataframe.project_performance import ProjectPerformance
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
from annofabcli.statistics.visualization.project_dir import ProjectDir
logger = logging.getLogger(__name__)
@@ -31,7 +31,10 @@ def create_custom_production_volume_list(cli_value: str) -> list[ProductionVolum
def main(args: argparse.Namespace) -> None:
root_dir: Path = args.dir
- project_dir_list = [ProjectDir(elm) for elm in root_dir.iterdir() if elm.is_dir()]
+ # task_completion_criteriaは何でもよいので、とりあえずACCEPTANCE_COMPLETEDを指定
+ project_dir_list = [
+ ProjectDir(elm, task_completion_criteria=TaskCompletionCriteria.ACCEPTANCE_COMPLETED) for elm in root_dir.iterdir() if elm.is_dir()
+ ]
custom_production_volume_list = (
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
diff --git a/annofabcli/stat_visualization/write_graph.py b/annofabcli/stat_visualization/write_graph.py
index 89192b7f..ac2ec245 100755
--- a/annofabcli/stat_visualization/write_graph.py
+++ b/annofabcli/stat_visualization/write_graph.py
@@ -21,7 +21,7 @@
InspectorProductivityPerDate,
)
from annofabcli.statistics.visualization.dataframe.task import Task
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
from annofabcli.statistics.visualization.project_dir import ProjectDir
logger = logging.getLogger(__name__)
@@ -129,8 +129,9 @@ def main(args: argparse.Namespace) -> None:
create_custom_production_volume_list(args.custom_production_volume) if args.custom_production_volume is not None else None
)
- input_project_dir = ProjectDir(args.dir, custom_production_volume_list=custom_production_volume_list)
- output_project_dir = ProjectDir(args.output_dir, metadata=input_project_dir.read_metadata())
+ task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
+ input_project_dir = ProjectDir(args.dir, task_completion_criteria, custom_production_volume_list=custom_production_volume_list)
+ output_project_dir = ProjectDir(args.output_dir, task_completion_criteria, metadata=input_project_dir.read_metadata())
main_obj = WritingGraph(
project_dir=input_project_dir,
output_project_dir=output_project_dir,
@@ -164,6 +165,16 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
help="必要最小限のファイルを出力します。",
)
+ parser.add_argument(
+ "--task_completion_criteria",
+ type=str,
+ choices=[e.value for e in TaskCompletionCriteria],
+ default=TaskCompletionCriteria.ACCEPTANCE_COMPLETED.value,
+ help="タスクの完了条件を指定します。\n"
+ "* ``acceptance_completed``: タスクが受入フェーズの完了状態であれば「タスクの完了」とみなす\n"
+ "* ``acceptance_reached``: タスクが受入フェーズに到達したら「タスクの完了」とみなす\n",
+ )
+
custom_production_volume_sample = {
"column_list": [{"value": "video_duration_minute", "name": "動画長さ"}],
}
diff --git a/annofabcli/stat_visualization/write_performance_rating_csv.py b/annofabcli/stat_visualization/write_performance_rating_csv.py
index 1696364c..573c62b0 100755
--- a/annofabcli/stat_visualization/write_performance_rating_csv.py
+++ b/annofabcli/stat_visualization/write_performance_rating_csv.py
@@ -21,7 +21,7 @@
ProjectPerformance,
ProjectWorktimePerMonth,
)
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn, WorktimeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria, WorktimeColumn
from annofabcli.statistics.visualization.project_dir import ProjectDir
logger = logging.getLogger(__name__)
@@ -326,7 +326,11 @@ def create_rating_df(
custom_production_volume_list_by_directory.get(p_project_dir.name) if custom_production_volume_list_by_directory is not None else None
)
project_title = p_project_dir.name
- project_dir = ProjectDir(p_project_dir, custom_production_volume_list=custom_production_volume_list)
+ project_dir = ProjectDir(
+ p_project_dir,
+ task_completion_criteria=TaskCompletionCriteria.ACCEPTANCE_COMPLETED,
+ custom_production_volume_list=custom_production_volume_list,
+ )
project_dir_list.append(project_dir)
try:
@@ -471,7 +475,8 @@ def create_user_df(target_dir: Path) -> pandas.DataFrame:
if not p_project_dir.is_dir():
continue
- project_dir = ProjectDir(p_project_dir)
+ # task_completion_criteriaは何でもよいので、とりあえずACCEPTANCE_COMPLETEDを指定
+ project_dir = ProjectDir(p_project_dir, task_completion_criteria=TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
try:
user_performance = project_dir.read_user_performance()
diff --git a/annofabcli/statistics/visualization/dataframe/task.py b/annofabcli/statistics/visualization/dataframe/task.py
index fa7939f0..e6064edd 100644
--- a/annofabcli/statistics/visualization/dataframe/task.py
+++ b/annofabcli/statistics/visualization/dataframe/task.py
@@ -110,7 +110,8 @@ def columns(self) -> list[str]:
"first_acceptance_username",
"first_acceptance_worktime_hour",
"first_acceptance_started_datetime",
- # 最後の受入
+ # 受入フェーズの日時
+ "first_acceptance_reached_datetime",
"first_acceptance_completed_datetime",
# 作業時間に関する内容
"worktime_hour",
@@ -229,6 +230,7 @@ def empty(cls, *, custom_production_volume_list: Optional[list[ProductionVolumeC
"first_acceptance_user_id",
"first_acceptance_username",
"first_acceptance_started_datetime",
+ "first_acceptance_reached_datetime",
"first_acceptance_completed_datetime",
]
diff --git a/annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py b/annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py
index b7a8e877..510f2f1e 100644
--- a/annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py
+++ b/annofabcli/statistics/visualization/dataframe/whole_productivity_per_date.py
@@ -14,7 +14,7 @@
import bokeh.layouts
import bokeh.palettes
import pandas
-from annofabapi.models import TaskStatus
+from annofabapi.models import TaskPhase, TaskStatus
from bokeh.models import DataRange1d
from bokeh.models.ui import UIElement
from bokeh.models.widgets.markups import Div
@@ -22,6 +22,7 @@
from dateutil.parser import parse
from annofabcli.common.bokeh import create_pretext_from_metadata
+from annofabcli.common.type_util import assert_noreturn
from annofabcli.common.utils import datetime_to_date, print_csv
from annofabcli.statistics.linegraph import (
LineGraph,
@@ -32,7 +33,7 @@
)
from annofabcli.statistics.visualization.dataframe.task import Task
from annofabcli.statistics.visualization.dataframe.worktime_per_date import WorktimePerDate
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
logger = logging.getLogger(__name__)
@@ -91,10 +92,17 @@ def _plot_and_moving_average(
class WholeProductivityPerCompletedDate:
- """受入完了日ごとの全体の生産量と生産性に関する情報"""
+ """完了日ごとの全体の生産量と生産性に関する情報"""
- def __init__(self, df: pandas.DataFrame, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None) -> None:
+ def __init__(
+ self,
+ df: pandas.DataFrame,
+ task_completion_criteria: TaskCompletionCriteria,
+ *,
+ custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None,
+ ) -> None:
self.df = df
+ self.task_completion_criteria = task_completion_criteria
self.custom_production_volume_list = custom_production_volume_list if custom_production_volume_list is not None else []
def _validate_df_for_output(self, output_file: Path) -> bool:
@@ -105,11 +113,15 @@ def _validate_df_for_output(self, output_file: Path) -> bool:
@classmethod
def from_csv(
- cls, csv_file: Path, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None
+ cls,
+ csv_file: Path,
+ task_completion_criteria: TaskCompletionCriteria,
+ *,
+ custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None,
) -> WholeProductivityPerCompletedDate:
"""CSVファイルからインスタンスを生成します。"""
df = pandas.read_csv(str(csv_file))
- return cls(df, custom_production_volume_list=custom_production_volume_list)
+ return cls(df, task_completion_criteria, custom_production_volume_list=custom_production_volume_list)
@staticmethod
def _create_df_date(date_index1: pandas.Index, date_index2: pandas.Index) -> pandas.DataFrame:
@@ -129,9 +141,11 @@ def _create_df_date(date_index1: pandas.Index, date_index2: pandas.Index) -> pan
return pandas.DataFrame(index=[e.strftime("%Y-%m-%d") for e in pandas.date_range(start_date, end_date)])
@classmethod
- def from_df_wrapper(cls, task: Task, worktime_per_date: WorktimePerDate) -> WholeProductivityPerCompletedDate:
+ def from_df_wrapper(
+ cls, task: Task, worktime_per_date: WorktimePerDate, task_completion_criteria: TaskCompletionCriteria
+ ) -> WholeProductivityPerCompletedDate:
"""
- 受入完了日毎の全体の生産量、生産性を算出する。
+ 完了日毎の全体の生産量、生産性を算出する。
Args:
task: タスク情報が格納されたDataFrameをラップするクラスのインスタンス
@@ -148,21 +162,25 @@ def from_df_wrapper(cls, task: Task, worktime_per_date: WorktimePerDate) -> Whol
# 生産量を表す列名
production_volume_columns = ["input_data_count", "annotation_count", *[e.value for e in task.custom_production_volume_list]]
- df_sub_task = task.df[["task_id", "first_acceptance_completed_datetime", *production_volume_columns]].copy()
- df_sub_task["first_acceptance_completed_date"] = df_sub_task["first_acceptance_completed_datetime"].map(
- lambda e: datetime_to_date(e) if not pandas.isna(e) else None
- )
+ if task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_COMPLETED:
+ datetime_column = "first_acceptance_completed_datetime"
+ elif task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_REACHED:
+ datetime_column = "first_acceptance_reached_datetime"
+ else:
+ assert_noreturn(task_completion_criteria)
+
+ df_sub_task = task.df[["task_id", datetime_column, *production_volume_columns]].copy()
+ date_column = datetime_column.replace("_datetime", "_date")
+ df_sub_task[date_column] = df_sub_task[datetime_column].map(lambda e: datetime_to_date(e) if not pandas.isna(e) else None)
- # タスク完了日を軸にして集計する
+ # 完了日を軸にして集計する
df_agg_sub_task = df_sub_task.pivot_table(
values=production_volume_columns,
- index="first_acceptance_completed_date",
+ index=date_column,
aggfunc="sum",
).fillna(0)
if len(df_agg_sub_task) > 0:
- df_agg_sub_task["task_count"] = df_sub_task.pivot_table(
- values=["task_id"], index="first_acceptance_completed_date", aggfunc="count"
- ).fillna(0)
+ df_agg_sub_task["task_count"] = df_sub_task.pivot_table(values=["task_id"], index=date_column, aggfunc="count").fillna(0)
else:
# 列だけ作る
df_agg_sub_task = df_agg_sub_task.assign(**{key: 0 for key in production_volume_columns}, task_count=0)
@@ -212,7 +230,7 @@ def from_df_wrapper(cls, task: Task, worktime_per_date: WorktimePerDate) -> Whol
df_date["date"] = df_date.index
cls._add_velocity_columns(df_date, production_volume_columns)
- return cls(df_date, custom_production_volume_list=task.custom_production_volume_list)
+ return cls(df_date, task_completion_criteria, custom_production_volume_list=task.custom_production_volume_list)
@classmethod
def _add_velocity_columns(cls, df: pandas.DataFrame, production_volume_columns: list[str]) -> None:
@@ -248,16 +266,17 @@ def add_velocity_column(df: pandas.DataFrame, numerator_column: str, denominator
for unit in production_volume_columns:
add_velocity_column(df, numerator_column=f"{category}_worktime_hour", denominator_column=unit)
- @staticmethod
- def _create_div_element() -> Div:
+ def _create_div_element(self) -> Div:
"""
HTMLページの先頭に付与するdiv要素を生成する。
"""
- return Div(
- text="""
用語
- 「X日のタスク数」とは、X日に初めて受入完了状態になったタスクの個数です。
- """
- )
+ if self.task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_COMPLETED:
+ str_task = "受入フェーズ完了状態"
+ elif self.task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_REACHED:
+ str_task = "受入フェーズ"
+ else:
+ assert_noreturn(self.task_completion_criteria)
+ return Div(text="注意
" f"「X日のタスク数」は、X日に初めて{str_task}になったタスクの個数です。
")
def plot(
self,
@@ -735,9 +754,11 @@ def create_worktime_line_graph() -> LineGraph:
write_bokeh_graph(bokeh.layouts.column(element_list), output_file)
@classmethod
- def empty(cls, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None) -> WholeProductivityPerCompletedDate:
+ def empty(
+ cls, *, task_completion_criteria: TaskCompletionCriteria, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None
+ ) -> WholeProductivityPerCompletedDate:
df = pandas.DataFrame(columns=cls.get_columns(custom_production_volume_list=custom_production_volume_list))
- return cls(df, custom_production_volume_list=custom_production_volume_list)
+ return cls(df, task_completion_criteria, custom_production_volume_list=custom_production_volume_list)
@property
def columns(self) -> list[str]:
@@ -807,17 +828,28 @@ def to_csv(self, output_file: Path) -> None:
class WholeProductivityPerFirstAnnotationStartedDate:
"""教師付開始日ごとの全体の生産量と生産性に関する情報"""
- def __init__(self, df: pandas.DataFrame, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None) -> None:
+ def __init__(
+ self,
+ df: pandas.DataFrame,
+ task_completion_criteria: TaskCompletionCriteria,
+ *,
+ custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None,
+ ) -> None:
+ self.task_completion_criteria = task_completion_criteria
self.custom_production_volume_list = custom_production_volume_list if custom_production_volume_list is not None else []
self.df = df
@classmethod
def from_csv(
- cls, csv_file: Path, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None
+ cls,
+ csv_file: Path,
+ task_completion_criteria: TaskCompletionCriteria,
+ *,
+ custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None,
) -> WholeProductivityPerFirstAnnotationStartedDate:
"""CSVファイルからインスタンスを生成します。"""
df = pandas.read_csv(str(csv_file))
- return cls(df, custom_production_volume_list=custom_production_volume_list)
+ return cls(df, task_completion_criteria, custom_production_volume_list=custom_production_volume_list)
@classmethod
def _add_velocity_columns(cls, df: pandas.DataFrame, production_volume_columns: list[str]) -> None:
@@ -840,12 +872,19 @@ def add_velocity_column(df: pandas.DataFrame, numerator_column: str, denominator
add_velocity_column(df, numerator_column="acceptance_worktime_hour", denominator_column=column)
@classmethod
- def from_task(cls, task: Task) -> WholeProductivityPerFirstAnnotationStartedDate:
+ def from_task(cls, task: Task, task_completion_criteria: TaskCompletionCriteria) -> WholeProductivityPerFirstAnnotationStartedDate:
# 生産量を表す列名
production_volume_columns = ["input_data_count", "annotation_count", *[e.value for e in task.custom_production_volume_list]]
df_task = task.df
- df_sub_task = df_task[df_task["status"] == TaskStatus.COMPLETE.value][
+ if task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_COMPLETED:
+ df_sub_task = df_task[df_task["status"] == TaskStatus.COMPLETE.value]
+ elif task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_REACHED:
+ df_sub_task = df_task[df_task["phase"] == TaskPhase.ACCEPTANCE.value]
+ else:
+ assert_noreturn(task_completion_criteria)
+
+ df_sub_task = df_sub_task[
[
"task_id",
"first_annotation_started_datetime",
@@ -893,7 +932,7 @@ def from_task(cls, task: Task) -> WholeProductivityPerFirstAnnotationStartedDate
df_date["first_annotation_started_date"] = df_date.index
# 生産性情報などの列を追加する
cls._add_velocity_columns(df_date, production_volume_columns)
- return cls(df_date, custom_production_volume_list=task.custom_production_volume_list)
+ return cls(df_date, task_completion_criteria, custom_production_volume_list=task.custom_production_volume_list)
def _validate_df_for_output(self, output_file: Path) -> bool:
if len(self.df) == 0:
@@ -902,9 +941,11 @@ def _validate_df_for_output(self, output_file: Path) -> bool:
return True
@classmethod
- def empty(cls, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None) -> WholeProductivityPerFirstAnnotationStartedDate:
+ def empty(
+ cls, task_completion_criteria: TaskCompletionCriteria, *, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None
+ ) -> WholeProductivityPerFirstAnnotationStartedDate:
df = pandas.DataFrame(columns=cls.get_columns(custom_production_volume_list=custom_production_volume_list))
- return cls(df, custom_production_volume_list=custom_production_volume_list)
+ return cls(df, task_completion_criteria, custom_production_volume_list=custom_production_volume_list)
@staticmethod
def get_columns(*, custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None) -> list[str]:
@@ -973,12 +1014,16 @@ def create_div_element() -> Div:
"""
HTMLページの先頭に付与するdiv要素を生成する。
"""
+ if self.task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_COMPLETED:
+ str_task = "受入フェーズ完了状態"
+ elif self.task_completion_criteria == TaskCompletionCriteria.ACCEPTANCE_REACHED:
+ str_task = "受入フェーズ"
+ else:
+ assert_noreturn(self.task_completion_criteria)
return Div(
- text="""注意
- 「X日の作業時間」とは、「X日に教師付開始したタスクにかけた作業時間」です。
- 「X日に作業した時間」ではありません。
-
- """
+ text="注意
"
+ f"「X日のタスク数」は、X日に教師付フェーズを開始したタスクの内、{str_task}であるタスクの個数です。
"
+ f"「X日の作業時間」は、X日のタスク数にかけた作業時間です。X日に作業した時間ではないことに注意してください。
"
)
def create_line_graph(title: str, y_axis_label: str, tooltip_columns: list[str]) -> LineGraph:
diff --git a/annofabcli/statistics/visualization/model.py b/annofabcli/statistics/visualization/model.py
index 75a60c29..fe9a01ae 100644
--- a/annofabcli/statistics/visualization/model.py
+++ b/annofabcli/statistics/visualization/model.py
@@ -21,3 +21,14 @@ class ProductionVolumeColumn:
"""CSVの列名"""
name: str
"""列の名前。グラフに表示する名前などに使用する"""
+
+
+class TaskCompletionCriteria(Enum):
+ """
+ タスクの完了の条件
+ """
+
+ ACCEPTANCE_COMPLETED = "acceptance_completed"
+ """タスクが受入フェーズの完了状態であれば「タスクの完了」とみなす"""
+ ACCEPTANCE_REACHED = "acceptance_reached"
+ """タスクが受入フェーズに到達したら「タスクの完了」とみなす"""
diff --git a/annofabcli/statistics/visualization/project_dir.py b/annofabcli/statistics/visualization/project_dir.py
index 8d5a4e0c..274a89b6 100644
--- a/annofabcli/statistics/visualization/project_dir.py
+++ b/annofabcli/statistics/visualization/project_dir.py
@@ -25,7 +25,7 @@
)
from annofabcli.statistics.visualization.dataframe.worktime_per_date import WorktimePerDate
from annofabcli.statistics.visualization.filtering_query import FilteringQuery
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
logger = logging.getLogger(__name__)
@@ -53,11 +53,13 @@ class ProjectDir(DataClassJsonMixin):
def __init__(
self,
project_dir: Path,
+ task_completion_criteria: TaskCompletionCriteria,
*,
metadata: Optional[dict[str, Any]] = None,
custom_production_volume_list: Optional[list[ProductionVolumeColumn]] = None,
) -> None:
self.project_dir = project_dir
+ self.task_completion_criteria = task_completion_criteria
self.metadata = metadata
self.custom_production_volume_list = custom_production_volume_list
@@ -223,10 +225,14 @@ def read_whole_productivity_per_date(self) -> WholeProductivityPerCompletedDate:
file = self.project_dir / self.FILENAME_WHOLE_PRODUCTIVITY_PER_DATE
if file.exists():
return WholeProductivityPerCompletedDate.from_csv(
- self.project_dir / self.FILENAME_WHOLE_PRODUCTIVITY_PER_DATE, custom_production_volume_list=self.custom_production_volume_list
+ self.project_dir / self.FILENAME_WHOLE_PRODUCTIVITY_PER_DATE,
+ task_completion_criteria=self.task_completion_criteria,
+ custom_production_volume_list=self.custom_production_volume_list,
)
else:
- return WholeProductivityPerCompletedDate.empty(custom_production_volume_list=self.custom_production_volume_list)
+ return WholeProductivityPerCompletedDate.empty(
+ task_completion_criteria=self.task_completion_criteria, custom_production_volume_list=self.custom_production_volume_list
+ )
def write_whole_productivity_per_date(self, obj: WholeProductivityPerCompletedDate) -> None:
"""
@@ -249,10 +255,13 @@ def read_whole_productivity_per_first_annotation_started_date(self) -> WholeProd
if file.exists():
return WholeProductivityPerFirstAnnotationStartedDate.from_csv(
file,
+ self.task_completion_criteria,
custom_production_volume_list=self.custom_production_volume_list,
)
else:
- return WholeProductivityPerFirstAnnotationStartedDate.empty(custom_production_volume_list=self.custom_production_volume_list)
+ return WholeProductivityPerFirstAnnotationStartedDate.empty(
+ self.task_completion_criteria, custom_production_volume_list=self.custom_production_volume_list
+ )
def write_whole_productivity_per_first_annotation_started_date(self, obj: WholeProductivityPerFirstAnnotationStartedDate) -> None:
"""
@@ -453,6 +462,8 @@ class ProjectInfo(DataClassJsonMixin):
"""入力データの種類"""
measurement_datetime: str
"""計測日時。(2004-04-01T12:00+09:00形式)"""
+ task_completion_criteria: TaskCompletionCriteria
+ """タスクの完了条件"""
query: FilteringQuery
"""集計対象を絞り込むためのクエリ"""
@@ -465,3 +476,5 @@ class MergingInfo(DataClassJsonMixin):
"""マージ対象のディレクトリ名"""
project_info_list: List[ProjectInfo]
"""マージ対象のプロジェクト情報"""
+ task_completion_criteria: TaskCompletionCriteria
+ """タスクの完了条件"""
diff --git a/annofabcli/statistics/visualize_statistics.py b/annofabcli/statistics/visualize_statistics.py
index b0d4b4e1..8e01a723 100644
--- a/annofabcli/statistics/visualize_statistics.py
+++ b/annofabcli/statistics/visualize_statistics.py
@@ -48,7 +48,7 @@
)
from annofabcli.statistics.visualization.dataframe.worktime_per_date import WorktimePerDate
from annofabcli.statistics.visualization.filtering_query import FilteringQuery, filter_tasks
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn, WorktimeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria, WorktimeColumn
from annofabcli.statistics.visualization.project_dir import ProjectDir, ProjectInfo
from annofabcli.statistics.visualization.visualization_source_files import VisualizationSourceFiles
@@ -60,10 +60,11 @@ def get_project_output_dir(project_title: str) -> str:
class WriteCsvGraph:
- def __init__(
+ def __init__( # noqa: PLR0913
self,
service: annofabapi.Resource,
project_id: str,
+ task_completion_criteria: TaskCompletionCriteria,
filtering_query: FilteringQuery,
visualization_source_files: VisualizationSourceFiles,
project_dir: ProjectDir,
@@ -76,6 +77,7 @@ def __init__(
) -> None:
self.service = service
self.project_id = project_id
+ self.task_completion_criteria = task_completion_criteria
self.project_dir = project_dir
self.filtering_query = filtering_query
self.visualize_source_files = visualization_source_files
@@ -212,11 +214,15 @@ def write_worktime_per_date(self, user_id_list: Optional[List[str]] = None) -> N
self.project_dir.write_worktime_per_date_user(worktime_per_date_obj)
task = self._get_task()
- productivity_per_completed_date_obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date_obj)
+ productivity_per_completed_date_obj = WholeProductivityPerCompletedDate.from_df_wrapper(
+ task, worktime_per_date_obj, task_completion_criteria=self.task_completion_criteria
+ )
self.project_dir.write_whole_productivity_per_date(productivity_per_completed_date_obj)
- productivity_per_started_date_obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task)
+ productivity_per_started_date_obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(
+ task, task_completion_criteria=self.task_completion_criteria
+ )
self.project_dir.write_whole_productivity_per_first_annotation_started_date(productivity_per_started_date_obj)
if not self.output_only_text:
@@ -250,6 +256,7 @@ def __init__( # noqa: PLR0913
*,
temp_dir: Path,
# タスクの絞り込み関係
+ task_completion_criteria: TaskCompletionCriteria,
task_query: Optional[TaskQuery],
start_date: Optional[str] = None,
end_date: Optional[str] = None,
@@ -268,7 +275,7 @@ def __init__( # noqa: PLR0913
self.service = service
self.facade = AnnofabApiFacade(service)
self.temp_dir = temp_dir
-
+ self.task_completion_criteria = task_completion_criteria
self.filtering_query = FilteringQuery(task_query=task_query, start_date=start_date, end_date=end_date)
self.minimal_output = minimal_output
self.output_only_text = output_only_text
@@ -289,6 +296,7 @@ def get_project_info(self, project_id: str) -> ProjectInfo:
project_title=project_title,
input_data_type=project_info["input_data_type"],
measurement_datetime=annofabapi.utils.str_now(),
+ task_completion_criteria=self.task_completion_criteria,
query=self.filtering_query,
)
return project_summary
@@ -307,7 +315,7 @@ def visualize_statistics(self, project_id: str, output_project_dir: Path) -> Non
project_info = self.get_project_info(project_id)
logger.info(f"project_title='{project_info.project_title}'")
- project_dir = ProjectDir(output_project_dir, metadata=project_info.to_dict(encode_json=True))
+ project_dir = ProjectDir(output_project_dir, self.task_completion_criteria, metadata=project_info.to_dict(encode_json=True))
project_dir.write_project_info(project_info)
if self.actual_worktime is not None:
@@ -347,6 +355,7 @@ def visualize_statistics(self, project_id: str, output_project_dir: Path) -> Non
write_obj = WriteCsvGraph(
self.service,
project_id,
+ task_completion_criteria=self.task_completion_criteria,
filtering_query=self.filtering_query,
visualization_source_files=visualization_source_files,
project_dir=project_dir,
@@ -452,6 +461,7 @@ def visualize_statistics( # noqa: PLR0913 # pylint: disable=too-many-positiona
self,
temp_dir: Path,
task_query: Optional[TaskQuery],
+ task_completion_criteria: TaskCompletionCriteria,
user_id_list: Optional[list[str]],
actual_worktime: ActualWorktime,
annotation_count: Optional[AnnotationCount],
@@ -470,6 +480,7 @@ def visualize_statistics( # noqa: PLR0913 # pylint: disable=too-many-positiona
main_obj = VisualizingStatisticsMain(
service=self.service,
temp_dir=temp_dir,
+ task_completion_criteria=task_completion_criteria,
task_query=task_query,
user_ids=user_id_list,
actual_worktime=actual_worktime,
@@ -496,7 +507,7 @@ def visualize_statistics( # noqa: PLR0913 # pylint: disable=too-many-positiona
)
if len(output_project_dir_list) > 0:
- project_dir_list = [ProjectDir(e) for e in output_project_dir_list]
+ project_dir_list = [ProjectDir(e, task_completion_criteria) for e in output_project_dir_list]
custom_production_volume_list = (
custom_production_volume.custom_production_volume_list if custom_production_volume is not None else None
)
@@ -554,11 +565,13 @@ def main(self) -> None: # pylint: disable=too-many-branches
create_custom_production_volume(args.custom_production_volume) if args.custom_production_volume is not None else None
)
+ task_completion_criteria = TaskCompletionCriteria(args.task_completion_criteria)
if args.temp_dir is None:
with tempfile.TemporaryDirectory() as str_temp_dir:
self.visualize_statistics(
temp_dir=Path(str_temp_dir),
task_query=task_query,
+ task_completion_criteria=task_completion_criteria,
user_id_list=user_id_list,
actual_worktime=actual_worktime,
annotation_count=annotation_count,
@@ -578,6 +591,7 @@ def main(self) -> None: # pylint: disable=too-many-branches
else:
self.visualize_statistics(
temp_dir=args.temp_dir,
+ task_completion_criteria=task_completion_criteria,
task_query=task_query,
user_id_list=user_id_list,
actual_worktime=actual_worktime,
@@ -614,6 +628,16 @@ def parse_args(parser: argparse.ArgumentParser) -> None:
),
)
+ parser.add_argument(
+ "--task_completion_criteria",
+ type=str,
+ choices=[e.value for e in TaskCompletionCriteria],
+ default=TaskCompletionCriteria.ACCEPTANCE_COMPLETED.value,
+ help="タスクの完了条件を指定します。\n"
+ "* ``acceptance_completed``: タスクが受入フェーズの完了状態であれば「タスクの完了」とみなす\n"
+ "* ``acceptance_reached``: タスクが受入フェーズに到達したら「タスクの完了」とみなす\n",
+ )
+
parser.add_argument("-o", "--output_dir", type=Path, required=True, help="出力先ディレクトリのパスを指定してください。")
parser.add_argument(
diff --git a/annofabcli/task/list_tasks_added_task_history.py b/annofabcli/task/list_tasks_added_task_history.py
index bfcad902..78ccd5ad 100644
--- a/annofabcli/task/list_tasks_added_task_history.py
+++ b/annofabcli/task/list_tasks_added_task_history.py
@@ -115,6 +115,25 @@ def get_first_acceptance_completed_datetime(task_histories: list[TaskHistory]) -
return None
+ @staticmethod
+ def get_first_acceptance_reached_datetime(task_histories: list[TaskHistory]) -> Optional[str]:
+ """はじめて受入フェーズに到達した日時を取得する。
+ 受入フェーズを着手した日時とは異なる。
+ 必ず`first_acceptance_started_datetime`よりも前の日時になる。
+
+ Args:
+ task_histories (List[TaskHistory]): [description]
+
+ """
+ for index, history in enumerate(task_histories):
+ if history["phase"] != TaskPhase.ACCEPTANCE.value:
+ continue
+
+ first_acceptance_reached_datetime = task_histories[index - 1]["ended_datetime"]
+ assert first_acceptance_reached_datetime is not None
+ return first_acceptance_reached_datetime
+ return None
+
@staticmethod
def is_acceptance_phase_skipped(task_histories: list[TaskHistory]) -> bool:
"""抜取受入によって、受入フェーズでスキップされたことがあるかを取得する。
@@ -269,6 +288,7 @@ def add_task_history_additional_info_to_task(self, task: dict[str, Any], task_hi
self._add_task_history_info_by_phase(task, task_histories, phase=TaskPhase.ACCEPTANCE)
# 初めて受入が完了した日時
+ task["first_acceptance_reached_datetime"] = self.get_first_acceptance_reached_datetime(task_histories)
task["first_acceptance_completed_datetime"] = self.get_first_acceptance_completed_datetime(task_histories)
# 受入完了日時を設定
@@ -339,6 +359,7 @@ def _get_output_target_columns() -> List[str]:
# 差し戻し回数
"number_of_rejections_by_inspection",
"number_of_rejections_by_acceptance",
+ "first_acceptance_reached_datetime",
"first_acceptance_completed_datetime",
"completed_datetime",
"inspection_is_skipped",
diff --git "a/docs/command_reference/statistics/visualize_output_rst/\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst" "b/docs/command_reference/statistics/visualize_output_rst/\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst"
index 58578f1b..111f12fe 100644
--- "a/docs/command_reference/statistics/visualize_output_rst/\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst"
+++ "b/docs/command_reference/statistics/visualize_output_rst/\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst"
@@ -4,7 +4,7 @@ line-graph/折れ線-横軸_日-全体.html
タスク数や作業時間、生産性などを、日毎にプロットした折れ線グラフです。
-グラフのデータは ``日毎の生産量と生産性.csv`` を参照しています。
+グラフのデータは :doc:`日毎の生産量と生産性_csv` を参照しています。
日毎のタスク数や作業時間、生産性などをの推移が分かります。
diff --git "a/docs/command_reference/statistics/visualize_output_rst/\346\225\231\345\270\253\344\273\230\351\226\213\345\247\213\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst" "b/docs/command_reference/statistics/visualize_output_rst/\346\225\231\345\270\253\344\273\230\351\226\213\345\247\213\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst"
index 1545334c..7f4c174a 100644
--- "a/docs/command_reference/statistics/visualize_output_rst/\346\225\231\345\270\253\344\273\230\351\226\213\345\247\213\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst"
+++ "b/docs/command_reference/statistics/visualize_output_rst/\346\225\231\345\270\253\344\273\230\351\226\213\345\247\213\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst"
@@ -9,13 +9,27 @@
参照頻度が高い列の詳細を、以下に記載します。
-* worktime_hour: その日に教師付を開始したタスクにかけた作業時間[hour]。
-* task_count: その日に教師付開始したタスクの個数。
-* input_data_count: その日に教師付開始したタスクに含まれている入力データの個数。
-* worktime_hour/annotation_count: アノテーションあたりの計測作業時間[hour]。生産性の指標になる。
+* ``first_annotation_started_date`` : 教師付フェーズを開始した日
+* ``worktime_hour`` : その日に教師付フェーズを開始して、現在作業が完了したタスクにかけた計測作業時間(アノテーションエディタ画面を触っていた作業の時間)。単位は時間。
+* ``task_count`` : 作業が完了したタスクの数
+* ``input_data_count`` : 作業が完了したタスクに含まれている入力データの数
+* ``annotation_count`` : 作業が完了したタスクに含まれているアノテーションの数
+* ``worktime_hour/annotation_count`` : アノテーションあたりの計測作業時間。単位は時間。
+
.. note::
+ ``worktime`` は、その日に教師付フェーズを開始したタスクにかけた作業時間です。その日に作業した時間ではないことに注意してください。
+
+
+``task_count`` の算出方法
+=================================
+``--task_completion_criteria`` で指定した値によって、 ``task_count`` の算出方法が異なります。
+
+``--task_completion_criteria acceptance_completed`` を指定すると、 ``task_count`` は、受入フェーズ完了状態のタスクの数になります。
+
+``--task_completion_criteria acceptance_reached`` を指定すると、 ``task_count`` は、受入フェーズタスクの数になります。
+
+
- ``worktime`` (作業時間)は、その日に教師付を開始したタスクにかけた作業時間です。その日に作業した時間ではないことに、注意してください。
diff --git "a/docs/command_reference/statistics/visualize_output_rst/\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst" "b/docs/command_reference/statistics/visualize_output_rst/\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst"
index 245f88ac..cb3f9f38 100644
--- "a/docs/command_reference/statistics/visualize_output_rst/\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst"
+++ "b/docs/command_reference/statistics/visualize_output_rst/\346\227\245\346\257\216\343\201\256\347\224\237\347\224\243\351\207\217\343\201\250\347\224\237\347\224\243\346\200\247_csv.rst"
@@ -7,17 +7,39 @@
`日毎の生産量と生産性.csv `_
-参照頻度が高い列の詳細を、以下に記載します。
-* monitored_worktime_hour: 計測作業時間[hour](アノテーションエディタ画面を触っていた作業時間)
-* actual_worktime_hour: 実績作業時間[hour](労務管理画面から入力した作業時間)
-* task_count: 作業したタスク数。タスクが初めて受入完了状態になったときに「作業した」とみなしている。
-* input_data_count: 作業したタスクに含まれている入力データ数
-* actual_worktime/annotation_count: アノテーションあたりの実績作業時間[hour]。生産性の指標になる。
+* ``actual_worktime_hour`` : 実績作業時間( ``--labor_csv`` で渡された実際の作業時間)。単位は時間。
+* ``monitored_worktime_hour`` : 計測作業時間(アノテーションエディタ画面を触っていた作業の時間)。単位は時間。
+* ``task_count`` : 作業が完了したタスクの数
+* ``input_data_count`` : 作業が完了したタスクに含まれている入力データの数
+* ``annotation_count`` : 作業が完了したタスクに含まれているアノテーションの数
+* ``actual_worktime_hour/annotation_count`` : アノテーションあたりの実績作業時間。単位は時間。
+* ``working_user_count`` : 作業したユーザーの数
-.. note::
- task_count は「その日に初めて受入完了状態になったタスクの個数」です。受入完了状態のタスクが差し戻されても、task_countは変わりません。
- 受入完了状態の差し戻し後の作業量が多い場合、task_countの値が実際と合わないケースがあります。
+``task_count`` の算出方法
+=================================
+``--task_completion_criteria`` で指定した値によって、 ``task_count`` の算出方法が異なります。
+
+``--task_completion_criteria acceptance_completed`` を指定すると、 ``task_count`` は、初めて受入フェーズ完了状態になったタスクの数になります。
+受入取消によって完了状態でないタスクも、「作業が完了したタスク」に含まれます。
+
+``--task_completion_criteria acceptance_reached`` を指定すると、 ``task_count`` は、初めて受入フェーズになったタスクの数になります。
+受入フェーズでの差し戻しによって受入フェーズでないタスクも、「作業が完了したタスク」に含まれます。
+
+
+
+.. note::
+
+
+ 以下のタスクが多いと、 ``task_count`` の推移は実際よりも早い日にタスクが完了したように見えます。
+
+ * 受入取消されたタスク( ``--task_completion_criteria acceptance_completed`` の場合)
+ * 受入フェーズで差し戻されたタスク( ``--task_completion_criteria acceptance_reached`` の場合)
+
+ その場合は :doc:`教師付開始日毎の生産量と生産性_csv` も参照することをお勧めします。
+ :doc:`教師付開始日毎の生産量と生産性_csv` の ``task_count`` は、教師付フェーズを開始した日ごとに集計しています。
+ 一度に大量のタスクの教師付けフェーズを開始する運用でなければ、 :doc:`教師付開始日毎の生産量と生産性_csv` の方が生産量の推移を正しく表しているかもしれません。
+
\ No newline at end of file
diff --git "a/docs/command_reference/statistics/visualize_output_rst/\347\264\257\347\251\215\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst" "b/docs/command_reference/statistics/visualize_output_rst/\347\264\257\347\251\215\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst"
index fbf7f673..e9dd14bf 100644
--- "a/docs/command_reference/statistics/visualize_output_rst/\347\264\257\347\251\215\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst"
+++ "b/docs/command_reference/statistics/visualize_output_rst/\347\264\257\347\251\215\346\212\230\343\202\214\347\267\232-\346\250\252\350\273\270_\346\227\245-\345\205\250\344\275\223_html.rst"
@@ -3,7 +3,7 @@ line-graph/累積折れ線-横軸_日-全体.html
==========================================
タスク数や作業時間の累積値をプロットした折れ線グラフです。
-グラフのデータは ``日毎の生産量と生産性.csv`` を参照しています。
+グラフのデータは :doc:`日毎の生産量と生産性_csv` を参照しています。
`累積折れ線-横軸_日-全体.html `_
diff --git a/docs/command_reference/task/list_added_task_history.rst b/docs/command_reference/task/list_added_task_history.rst
index 570db835..a85ff3f1 100644
--- a/docs/command_reference/task/list_added_task_history.rst
+++ b/docs/command_reference/task/list_added_task_history.rst
@@ -123,6 +123,7 @@ JSON出力
"first_acceptance_user_id": "alice",
"first_acceptance_username": "Alice",
"acceptance_worktime_hour": 0.16520972222222222,
+ "first_acceptance_reached_datetime": "2022-10-24T15:14:18.967+09:00",
"first_acceptance_completed_datetime": "2022-10-25T15:14:18.967+09:00",
"completed_datetime": "2022-10-25T15:14:18.967+09:00",
"inspection_is_skipped": false,
@@ -139,6 +140,7 @@ JSON出力
* annotation_worktime_hour: 教師付フェーズの作業時間[hour]
* inspection_worktime_hour: 検査フェーズの作業時間[hour]
* acceptance_worktime_hour: 受入フェーズの作業時間[hour]
+* first_acceptance_reached_datetime: はじめて受入フェーズに到達した日時。 ``first_annotation_started_datetime`` より前の日時になる
* first_acceptance_completed_datetime: はじめて受入完了状態になった日時
* completed_datetime: 受入完了状態になった日時
* inspection_is_skipped: 抜取検査により検査フェーズがスキップされたかどうか
@@ -147,9 +149,6 @@ JSON出力
* first_annotation_username: 最初の教師付フェーズを担当したユーザの名前
* first_annotation_started_datetime: 最初の教師付フェーズを開始した日時
* ...
-* last_acceptance_user_id: 最後の受入フェーズを担当したユーザのuser_id
-* last_acceptance_username: 最後の受入フェーズを担当したユーザの名前
-* last_acceptance_started_datetime: 最後の受入フェーズを開始した日時
diff --git "a/tests/data/stat_visualization/mask_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv" "b/tests/data/stat_visualization/mask_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv"
index 606db397..0a6ef3e4 100644
--- "a/tests/data/stat_visualization/mask_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv"
+++ "b/tests/data/stat_visualization/mask_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv"
@@ -1,4 +1,4 @@
-project_id,task_id,phase,phase_stage,status,number_of_rejections_by_inspection,number_of_rejections_by_acceptance,created_datetime,first_annotation_user_id,first_annotation_username,first_annotation_worktime_hour,first_annotation_started_datetime,first_inspection_user_id,first_inspection_username,first_inspection_worktime_hour,first_inspection_started_datetime,first_acceptance_user_id,first_acceptance_username,first_acceptance_worktime_hour,first_acceptance_started_datetime,first_acceptance_completed_datetime,worktime_hour,annotation_worktime_hour,inspection_worktime_hour,acceptance_worktime_hour,input_data_count,annotation_count,inspection_comment_count,inspection_comment_count_in_inspection_phase,inspection_comment_count_in_acceptance_phase,inspection_is_skipped,acceptance_is_skipped
-prj1,task1,acceptance,1,complete,0,0,2023-12-27T21:53:53.72+09:00,bob,bob,0.185208888888889,2024-01-09T11:27:00.015+09:00,,,,,alice,alice,0.547898611111111,2024-01-09T13:03:11.715+09:00,2024-01-11T13:04:01.134+09:00,0.7331075,0.185208888888889,0,0.547898611111111,10,20,0,0,0,False,False
-prj1,task2,acceptance,1,complete,0,1,2023-12-27T21:54:09.075+09:00,bob,bob,0.214761944444444,2024-01-09T11:43:19.68+09:00,,,,,alice,alice,0.267357222222222,2024-01-10T15:10:43.647+09:00,2024-01-11T14:23:30.968+09:00,0.538455555555556,0.220598611111111,0,0.317856944444444,10,20,1,0,1,False,False
-prj1,task3,acceptance,1,complete,0,1,2023-12-27T21:54:24.099+09:00,bob,bob,0.195141111111111,2024-01-09T11:56:20.118+09:00,,,,,chris,chris,0.573847777777778,2024-01-09T15:23:53.291+09:00,2024-01-16T11:14:02.961+09:00,1.3610325,0.324384722222222,0,1.03664777777778,10,20,2,0,2,False,False
+project_id,task_id,phase,phase_stage,status,number_of_rejections_by_inspection,number_of_rejections_by_acceptance,created_datetime,first_annotation_user_id,first_annotation_username,first_annotation_worktime_hour,first_annotation_started_datetime,first_inspection_user_id,first_inspection_username,first_inspection_worktime_hour,first_inspection_started_datetime,first_acceptance_user_id,first_acceptance_username,first_acceptance_worktime_hour,first_acceptance_reached_datetime,first_acceptance_started_datetime,first_acceptance_completed_datetime,worktime_hour,annotation_worktime_hour,inspection_worktime_hour,acceptance_worktime_hour,input_data_count,annotation_count,inspection_comment_count,inspection_comment_count_in_inspection_phase,inspection_comment_count_in_acceptance_phase,inspection_is_skipped,acceptance_is_skipped
+prj1,task1,acceptance,1,complete,0,0,2023-12-27T21:53:53.72+09:00,bob,bob,0.185208888888889,2024-01-09T11:27:00.015+09:00,,,,,alice,alice,0.547898611111111,2024-01-08T13:03:11.715+09:00,2024-01-09T13:03:11.715+09:00,2024-01-11T13:04:01.134+09:00,0.7331075,0.185208888888889,0,0.547898611111111,10,20,0,0,0,False,False
+prj1,task2,acceptance,1,complete,0,1,2023-12-27T21:54:09.075+09:00,bob,bob,0.214761944444444,2024-01-09T11:43:19.68+09:00,,,,,alice,alice,0.267357222222222,2024-01-09T15:10:43.647+09:00,2024-01-10T15:10:43.647+09:00,2024-01-11T14:23:30.968+09:00,0.538455555555556,0.220598611111111,0,0.317856944444444,10,20,1,0,1,False,False
+prj1,task3,acceptance,1,complete,0,1,2023-12-27T21:54:24.099+09:00,bob,bob,0.195141111111111,2024-01-09T11:56:20.118+09:00,,,,,chris,chris,0.573847777777778,2024-01-08T15:23:53.291+09:00,2024-01-09T15:23:53.291+09:00,2024-01-16T11:14:02.961+09:00,1.3610325,0.324384722222222,0,1.03664777777778,10,20,2,0,2,False,False
diff --git a/tests/data/stat_visualization/merge_visualization_dir/visualization1/project_info.json b/tests/data/stat_visualization/merge_visualization_dir/visualization1/project_info.json
index 5633a5b6..65c20f00 100644
--- a/tests/data/stat_visualization/merge_visualization_dir/visualization1/project_info.json
+++ b/tests/data/stat_visualization/merge_visualization_dir/visualization1/project_info.json
@@ -3,6 +3,7 @@
"project_title": "PRJECT1",
"input_data_type": "image",
"measurement_datetime": "2024-02-17T01:22:54.025+09:00",
+ "task_completion_criteria": "acceptance_completed",
"query": {
"task_query": null,
"task_ids": null,
diff --git "a/tests/data/stat_visualization/merge_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv" "b/tests/data/stat_visualization/merge_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv"
index 606db397..0a6ef3e4 100644
--- "a/tests/data/stat_visualization/merge_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv"
+++ "b/tests/data/stat_visualization/merge_visualization_dir/visualization1/\343\202\277\343\202\271\343\202\257list.csv"
@@ -1,4 +1,4 @@
-project_id,task_id,phase,phase_stage,status,number_of_rejections_by_inspection,number_of_rejections_by_acceptance,created_datetime,first_annotation_user_id,first_annotation_username,first_annotation_worktime_hour,first_annotation_started_datetime,first_inspection_user_id,first_inspection_username,first_inspection_worktime_hour,first_inspection_started_datetime,first_acceptance_user_id,first_acceptance_username,first_acceptance_worktime_hour,first_acceptance_started_datetime,first_acceptance_completed_datetime,worktime_hour,annotation_worktime_hour,inspection_worktime_hour,acceptance_worktime_hour,input_data_count,annotation_count,inspection_comment_count,inspection_comment_count_in_inspection_phase,inspection_comment_count_in_acceptance_phase,inspection_is_skipped,acceptance_is_skipped
-prj1,task1,acceptance,1,complete,0,0,2023-12-27T21:53:53.72+09:00,bob,bob,0.185208888888889,2024-01-09T11:27:00.015+09:00,,,,,alice,alice,0.547898611111111,2024-01-09T13:03:11.715+09:00,2024-01-11T13:04:01.134+09:00,0.7331075,0.185208888888889,0,0.547898611111111,10,20,0,0,0,False,False
-prj1,task2,acceptance,1,complete,0,1,2023-12-27T21:54:09.075+09:00,bob,bob,0.214761944444444,2024-01-09T11:43:19.68+09:00,,,,,alice,alice,0.267357222222222,2024-01-10T15:10:43.647+09:00,2024-01-11T14:23:30.968+09:00,0.538455555555556,0.220598611111111,0,0.317856944444444,10,20,1,0,1,False,False
-prj1,task3,acceptance,1,complete,0,1,2023-12-27T21:54:24.099+09:00,bob,bob,0.195141111111111,2024-01-09T11:56:20.118+09:00,,,,,chris,chris,0.573847777777778,2024-01-09T15:23:53.291+09:00,2024-01-16T11:14:02.961+09:00,1.3610325,0.324384722222222,0,1.03664777777778,10,20,2,0,2,False,False
+project_id,task_id,phase,phase_stage,status,number_of_rejections_by_inspection,number_of_rejections_by_acceptance,created_datetime,first_annotation_user_id,first_annotation_username,first_annotation_worktime_hour,first_annotation_started_datetime,first_inspection_user_id,first_inspection_username,first_inspection_worktime_hour,first_inspection_started_datetime,first_acceptance_user_id,first_acceptance_username,first_acceptance_worktime_hour,first_acceptance_reached_datetime,first_acceptance_started_datetime,first_acceptance_completed_datetime,worktime_hour,annotation_worktime_hour,inspection_worktime_hour,acceptance_worktime_hour,input_data_count,annotation_count,inspection_comment_count,inspection_comment_count_in_inspection_phase,inspection_comment_count_in_acceptance_phase,inspection_is_skipped,acceptance_is_skipped
+prj1,task1,acceptance,1,complete,0,0,2023-12-27T21:53:53.72+09:00,bob,bob,0.185208888888889,2024-01-09T11:27:00.015+09:00,,,,,alice,alice,0.547898611111111,2024-01-08T13:03:11.715+09:00,2024-01-09T13:03:11.715+09:00,2024-01-11T13:04:01.134+09:00,0.7331075,0.185208888888889,0,0.547898611111111,10,20,0,0,0,False,False
+prj1,task2,acceptance,1,complete,0,1,2023-12-27T21:54:09.075+09:00,bob,bob,0.214761944444444,2024-01-09T11:43:19.68+09:00,,,,,alice,alice,0.267357222222222,2024-01-09T15:10:43.647+09:00,2024-01-10T15:10:43.647+09:00,2024-01-11T14:23:30.968+09:00,0.538455555555556,0.220598611111111,0,0.317856944444444,10,20,1,0,1,False,False
+prj1,task3,acceptance,1,complete,0,1,2023-12-27T21:54:24.099+09:00,bob,bob,0.195141111111111,2024-01-09T11:56:20.118+09:00,,,,,chris,chris,0.573847777777778,2024-01-08T15:23:53.291+09:00,2024-01-09T15:23:53.291+09:00,2024-01-16T11:14:02.961+09:00,1.3610325,0.324384722222222,0,1.03664777777778,10,20,2,0,2,False,False
diff --git a/tests/data/stat_visualization/summarize_whole_performance_csv/visualization1/project_info.json b/tests/data/stat_visualization/summarize_whole_performance_csv/visualization1/project_info.json
index 5633a5b6..65c20f00 100644
--- a/tests/data/stat_visualization/summarize_whole_performance_csv/visualization1/project_info.json
+++ b/tests/data/stat_visualization/summarize_whole_performance_csv/visualization1/project_info.json
@@ -3,6 +3,7 @@
"project_title": "PRJECT1",
"input_data_type": "image",
"measurement_datetime": "2024-02-17T01:22:54.025+09:00",
+ "task_completion_criteria": "acceptance_completed",
"query": {
"task_query": null,
"task_ids": null,
diff --git a/tests/data/stat_visualization/write_performance_rating_csv/visualization1/project_info.json b/tests/data/stat_visualization/write_performance_rating_csv/visualization1/project_info.json
index 5633a5b6..d50fcd34 100644
--- a/tests/data/stat_visualization/write_performance_rating_csv/visualization1/project_info.json
+++ b/tests/data/stat_visualization/write_performance_rating_csv/visualization1/project_info.json
@@ -3,6 +3,7 @@
"project_title": "PRJECT1",
"input_data_type": "image",
"measurement_datetime": "2024-02-17T01:22:54.025+09:00",
+ "task_completion_criteria": "acceptance_completed",
"query": {
"task_query": null,
"task_ids": null,
diff --git "a/tests/data/stat_visualization/write_performance_rating_csv/visualization1/\343\203\241\343\203\263\343\203\220\343\201\224\343\201\250\343\201\256\347\224\237\347\224\243\346\200\247\343\201\250\345\223\201\350\263\252.csv" "b/tests/data/stat_visualization/write_performance_rating_csv/visualization1/\343\203\241\343\203\263\343\203\220\343\201\224\343\201\250\343\201\256\347\224\237\347\224\243\346\200\247\343\201\250\345\223\201\350\263\252.csv"
index a532e995..d4def0e8 100644
--- "a/tests/data/stat_visualization/write_performance_rating_csv/visualization1/\343\203\241\343\203\263\343\203\220\343\201\224\343\201\250\343\201\256\347\224\237\347\224\243\346\200\247\343\201\250\345\223\201\350\263\252.csv"
+++ "b/tests/data/stat_visualization/write_performance_rating_csv/visualization1/\343\203\241\343\203\263\343\203\220\343\201\224\343\201\250\343\201\256\347\224\237\347\224\243\346\200\247\343\201\250\345\223\201\350\263\252.csv"
@@ -1,14 +1,14 @@
-account_id,user_id,username,biography,first_working_date,last_working_date,working_days,real_monitored_worktime_hour,real_monitored_worktime_hour,real_monitored_worktime_hour,real_monitored_worktime_hour,monitored_worktime_hour,monitored_worktime_hour,monitored_worktime_hour,monitored_worktime_ratio,monitored_worktime_ratio,task_count,task_count,input_data_count,input_data_count,annotation_count,annotation_count,real_actual_worktime_hour,real_monitored_worktime_hour/real_actual_worktime_hour,actual_worktime_hour,actual_worktime_hour,actual_worktime_hour,monitored_worktime_hour/input_data_count,monitored_worktime_hour/input_data_count,actual_worktime_hour/input_data_count,actual_worktime_hour/input_data_count,monitored_worktime_hour/annotation_count,monitored_worktime_hour/annotation_count,actual_worktime_hour/annotation_count,actual_worktime_hour/annotation_count,pointed_out_inspection_comment_count,pointed_out_inspection_comment_count/input_data_count,pointed_out_inspection_comment_count/annotation_count,rejected_count,rejected_count/task_count,stdev__monitored_worktime_hour/input_data_count,stdev__monitored_worktime_hour/input_data_count,stdev__actual_worktime_hour/input_data_count,stdev__actual_worktime_hour/input_data_count,stdev__monitored_worktime_hour/annotation_count,stdev__monitored_worktime_hour/annotation_count,stdev__actual_worktime_hour/annotation_count,stdev__actual_worktime_hour/annotation_count
-,,,,,,,sum,annotation,inspection,acceptance,sum,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,sum,sum,sum,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,annotation,annotation,annotation,annotation,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance
-BW,BW,BW,category-KT,2023-11-21,2023-11-22,2,6.851844166666666,6.851844166666666,0.0,0.0,6.851844166666667,6.851844166666667,0.0,1.0,0.0,6.8896269504352485,0.0,106.31642764870466,0.0,704.6874322762493,0.0,7.083333333,0.9673191765161091,7.083333333000001,7.083333333000001,0.0,0.06444765233559979,,0.06662501261239756,,0.009723238776281494,,0.010051737846550973,,17.751501370050605,0.1669685650904852,0.025190603034753314,5.8896269504352485,0.8548542602968038,0.026679475026360432,,0.027580839576083944,,0.003011881696243145,,0.00311363794842848,
-CW,CW,CW,category-KT,2023-12-29,2023-12-29,1,0.1918455555555555,0.0,0.0,0.1918455555555555,0.1918455555555554,0.0,0.1918455555555554,0.0,1.0,0.0,0.5081261027073752,0.0,7.950989195100896,0.0,49.951637418952345,0.0,inf,0.0,0.0,0.0,,0.024128514182079824,,0.0,,0.0038406259627991003,,0.0,0.0,,,0.0,,,0.006901641498016899,,0.0,,0.0010296655042690527,,0.0
-DB,DB,DB,category-AH,2023-10-26,2023-11-22,11,40.81871361111111,19.350416666666668,0.0,21.46829694444444,40.81871361111111,19.350416666666668,21.468296944444443,0.4740574838056475,0.5259425161943526,24.37134758126235,75.50248589269926,381.31605181319634,1165.1062617307773,2933.576654251046,7997.957713291435,47.0,0.8684832683215129,47.0,22.28070173886543,24.719298261134572,0.050746399409763845,0.01842604202689038,0.05843106166897104,0.021216346588347916,0.0065961857988697786,0.002684222362012152,0.007595063761698017,0.003090701294913665,20.144676943152188,0.05282934418145319,0.006866933889032876,14.180195040232665,0.581838775757109,0.0205619883671736,0.010859332868754762,0.023675744965027397,0.012503790533284785,0.0017076237220000545,0.0011590161490904949,0.001966213724877301,0.001334529047785209
-EM,EM,EM,category-KT,2023-10-25,2024-01-11,9,14.509523055555555,11.46961361111111,0.0,3.039909444444444,14.509523055555555,11.46961361111111,3.0399094444444437,0.7904886719704758,0.20951132802952419,10.0,12.768327488075311,153.0,202.03695764154702,923.0,1640.040348797696,15.000000001,0.9673015369725502,15.000000001,11.857330080347625,3.142669920652374,0.07496479484386347,0.015046303804662492,0.07749888941403676,0.01555492597660317,0.012426450282893945,0.0018535577168409202,0.01284651146299851,0.0019162150022444552,30.0,0.19607843137254902,0.032502708559046585,12.0,1.2,0.028652605322638077,0.008824855452095989,0.029621172124169974,0.009123169058240026,0.0035644133299973977,0.0010819754887946894,0.0036849040281205997,0.001118550366601344
-ML,ML,ML,category-MK,2023-10-27,2024-01-09,9,15.325856111111111,15.325856111111111,0.0,0.0,15.325856111111111,15.325856111111111,0.0,1.0,0.0,9.0,0.0,140.0,0.0,1178.0,0.0,15.399999999,0.9951854618250842,15.399999999,15.399999999,0.0,0.1094704007936508,,0.10999999999285714,,0.013010064610450858,,0.013073005092529712,,39.0,0.2785714285714286,0.03310696095076401,12.0,1.3333333333333333,0.05252036437899548,,0.05277444897826146,,0.0016837689578328727,,0.001691914745966029,
-ND,ND,ND,category-KT,2023-10-26,2023-12-31,7,4.471973055555556,4.471973055555556,0.0,0.0,4.471973055555556,4.471973055555556,0.0,1.0,0.0,6.0881462418786745,0.0,95.3521360018727,0.0,510.08979562286163,0.0,7.75,0.5770287813620072,7.75,7.75,0.0,0.0468995582381891,,0.08127767583358375,,0.008767031008912672,,0.015193403331146063,,9.20623485744993,0.09654985450214898,0.018048263141999067,5.0881462418786745,0.8357463897431904,0.018154105632067503,,0.03146135204766894,,0.0012317972708939186,,0.0021347241431985573,
-NQ,NQ,NQ,category-MK,2023-11-16,2024-01-09,7,8.284593888888889,8.284593888888889,0.0,0.0,8.284593888888889,8.284593888888889,0.0,1.0,0.0,7.075329646405803,0.0,109.15792130391111,0.0,681.6541185465711,0.0,8.866666667,0.9343526942004629,8.866666667,8.866666667,0.0,0.07589548967155033,,0.08122788123011197,,0.012153662192423002,,0.013007574407245693,,23.0753296464058,0.21139400027745872,0.03385196248151074,8.075329646405802,1.1413361708889407,0.03639715076641981,,0.0389544023283042,,0.002675968505040457,,0.0028639811514968834,
-QE,QE,QE,category-KT,2023-10-26,2024-01-10,10,19.636556666666667,19.636556666666667,0.0,0.0,19.636556666666667,19.636556666666667,0.0,1.0,0.0,13.069062790192323,0.0,204.03594185288483,0.0,1534.3594185288484,0.0,20.383333334,0.963363368733823,20.383333334,20.383333334,0.0,0.09624067450246158,,0.09990069959682352,,0.012797885834007717,,0.013284588400769647,,11.207188370576967,0.054927520459398464,0.00730414806025206,7.069062790192322,0.5409005147253022,0.02715101838851306,,0.02818356942946507,,0.006019410129269445,,0.006248327811323088,
-RQ,RQ,RQ,category-MK,2023-11-17,2024-01-03,6,11.732706666666667,11.732706666666667,0.0,0.0,11.732706666666667,11.732706666666667,0.0,1.0,0.0,11.0,0.0,171.0,0.0,1272.0,0.0,12.1,0.9696451790633609,12.1,12.1,0.0,0.06861231968810916,,0.07076023391812865,,0.009223825995807127,,0.009512578616352201,,43.0,0.25146198830409355,0.03380503144654088,11.0,1.0,0.020828611429587012,,0.021480652798899727,,0.0018650764116650278,,0.001923462779928033,
-SK,SK,SK,category-KT,2023-10-27,2024-01-13,11,5.926304444444445,0.6270641666666666,0.0,5.299240277777778,5.926304444444443,0.6270641666666662,5.299240277777777,0.10581031949084246,0.8941896805091576,1.5064867898256038,19.88416237028506,22.821521379430365,309.4089089788781,79.63258077442372,2141.0393928747303,7.416666666999999,0.7990522846082824,7.416666666999998,0.7847598695923514,6.631906797407646,0.027476878348340773,0.017126980264616527,0.034386834100362654,0.02143411713416557,0.00787446746756782,0.0024750783640008576,0.0098547587176077,0.0030975174111594083,0.6150688123645079,0.026951262456975614,0.007723833717091521,0.6976393308552876,0.46309024119358444,0.02150022289202632,0.015643948555234398,0.026907154020048042,0.019578128811562685,0.0014024817882802457,0.0012862304348123643,0.0017551815010050074,0.00160969495937667
-TR,TR,TR,category-KT,2023-10-26,2023-11-22,9,19.23942138888889,19.23942138888889,0.0,0.0,19.23942138888889,19.23942138888889,0.0,1.0,0.0,9.0,0.0,139.0,0.0,1098.0,0.0,19.683333333,0.9774473186730587,19.683333333,19.683333333,0.0,0.13841310351718625,,0.14160671462589927,,0.0175222417020846,,0.017926533090163933,,49.0,0.35251798561151076,0.044626593806921674,11.0,1.2222222222222223,0.08909981711510603,,0.09115562078175651,,0.004185852612146469,,0.0042824329579511326,
-XT,XT,XT,category-MK,2023-10-26,2024-01-15,11,18.969883333333332,18.536526388888888,0.0,0.4333569444444443,18.969883333333332,18.536526388888888,0.43335694444444434,0.9771555292760835,0.022844470723916474,12.0,1.3368981462329987,183.0,20.496882453696607,1077.0,163.01090761718706,20.683333334,0.9171579371178997,20.683333334,20.21083353117843,0.4724998028215687,0.10129249392835457,0.021142578410322518,0.11044171328512803,0.023052276554201223,0.01721125941401011,0.0026584536628808594,0.018765862145940976,0.002898577829719112,39.0,0.21311475409836064,0.036211699164345405,10.0,0.8333333333333334,0.03824212262251798,0.006231775179058627,0.04169633285046956,0.006794658724364872,0.0024773018750205403,0.00026103198405534356,0.002701063551611706,0.00028460963318446223
+account_id,user_id,username,biography,first_working_date,last_working_date,working_days,first_working_date,first_working_date,first_working_date,last_working_date,last_working_date,last_working_date,working_days,working_days,working_days,real_monitored_worktime_hour,real_monitored_worktime_hour,real_monitored_worktime_hour,real_monitored_worktime_hour,monitored_worktime_hour,monitored_worktime_hour,monitored_worktime_hour,monitored_worktime_ratio,monitored_worktime_ratio,task_count,task_count,input_data_count,input_data_count,annotation_count,annotation_count,real_actual_worktime_hour,real_monitored_worktime_hour/real_actual_worktime_hour,actual_worktime_hour,actual_worktime_hour,actual_worktime_hour,monitored_worktime_hour/input_data_count,monitored_worktime_hour/input_data_count,actual_worktime_hour/input_data_count,actual_worktime_hour/input_data_count,monitored_worktime_hour/annotation_count,monitored_worktime_hour/annotation_count,actual_worktime_hour/annotation_count,actual_worktime_hour/annotation_count,pointed_out_inspection_comment_count,pointed_out_inspection_comment_count/input_data_count,pointed_out_inspection_comment_count/annotation_count,rejected_count,rejected_count/task_count,stdev__monitored_worktime_hour/input_data_count,stdev__monitored_worktime_hour/input_data_count,stdev__actual_worktime_hour/input_data_count,stdev__actual_worktime_hour/input_data_count,stdev__monitored_worktime_hour/annotation_count,stdev__monitored_worktime_hour/annotation_count,stdev__actual_worktime_hour/annotation_count,stdev__actual_worktime_hour/annotation_count
+,,,,,,,annotation,inspection,acceptance,annotation,inspection,acceptance,annotation,inspection,acceptance,sum,annotation,inspection,acceptance,sum,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,sum,sum,sum,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,annotation,annotation,annotation,annotation,annotation,acceptance,annotation,acceptance,annotation,acceptance,annotation,acceptance
+BW,BW,BW,category-KT,2023-11-21,2023-11-22,2,,,,,,,,,,6.85184416666667,6.85184416666667,0,0,6.85184416666667,6.85184416666667,0,1,0,6.88962695043525,0,106.316427648705,0,704.687432276249,0,7.083333333,0.967319176516109,7.083333333,7.083333333,0,0.0644476523355998,,0.0666250126123976,,0.00972323877628149,,0.010051737846551,,17.7515013700506,0.166968565090485,0.0251906030347533,5.88962695043525,0.854854260296804,0.0266794750263604,,0.0275808395760839,,0.00301188169624315,,0.00311363794842848,
+CW,CW,CW,category-KT,2023-12-29,2023-12-29,1,,,,,,,,,,0.191845555555556,0,0,0.191845555555556,0.191845555555555,0,0.191845555555555,0,1,0,0.508126102707375,0,7.9509891951009,0,49.9516374189523,0,inf,0,0,0,,0.0241285141820798,,0,,0.0038406259627991,,0,0,,,0,,,0.0069016414980169,,0,,0.00102966550426905,,0
+DB,DB,DB,category-AH,2023-10-26,2023-11-22,11,,,,,,,,,,40.8187136111111,19.3504166666667,0,21.4682969444444,40.8187136111111,19.3504166666667,21.4682969444444,0.474057483805648,0.525942516194353,24.3713475812624,75.5024858926993,381.316051813196,1165.10626173078,2933.57665425105,7997.95771329144,47,0.868483268321513,47,22.2807017388654,24.7192982611346,0.0507463994097638,0.0184260420268904,0.058431061668971,0.0212163465883479,0.00659618579886978,0.00268422236201215,0.00759506376169802,0.00309070129491367,20.1446769431522,0.0528293441814532,0.00686693388903288,14.1801950402327,0.581838775757109,0.0205619883671736,0.0108593328687548,0.0236757449650274,0.0125037905332848,0.00170762372200005,0.00115901614909049,0.0019662137248773,0.00133452904778521
+EM,EM,EM,category-KT,2023-10-25,2024-01-11,9,,,,,,,,,,14.5095230555556,11.4696136111111,0,3.03990944444444,14.5095230555556,11.4696136111111,3.03990944444444,0.790488671970476,0.209511328029524,10,12.7683274880753,153,202.036957641547,923,1640.0403487977,15.000000001,0.96730153697255,15.000000001,11.8573300803476,3.14266992065237,0.0749647948438635,0.0150463038046625,0.0774988894140368,0.0155549259766032,0.0124264502828939,0.00185355771684092,0.0128465114629985,0.00191621500224446,30,0.196078431372549,0.0325027085590466,12,1.2,0.0286526053226381,0.00882485545209599,0.02962117212417,0.00912316905824003,0.0035644133299974,0.00108197548879469,0.0036849040281206,0.00111855036660134
+ML,ML,ML,category-MK,2023-10-27,2024-01-09,9,,,,,,,,,,15.3258561111111,15.3258561111111,0,0,15.3258561111111,15.3258561111111,0,1,0,9,0,140,0,1178,0,15.399999999,0.995185461825084,15.399999999,15.399999999,0,0.109470400793651,,0.109999999992857,,0.0130100646104509,,0.0130730050925297,,39,0.278571428571429,0.033106960950764,12,1.33333333333333,0.0525203643789955,,0.0527744489782615,,0.00168376895783287,,0.00169191474596603,
+ND,ND,ND,category-KT,2023-10-26,2023-12-31,7,,,,,,,,,,4.47197305555556,4.47197305555556,0,0,4.47197305555556,4.47197305555556,0,1,0,6.08814624187867,0,95.3521360018727,0,510.089795622862,0,7.75,0.577028781362007,7.75,7.75,0,0.0468995582381891,,0.0812776758335838,,0.00876703100891267,,0.0151934033311461,,9.20623485744993,0.096549854502149,0.0180482631419991,5.08814624187867,0.83574638974319,0.0181541056320675,,0.0314613520476689,,0.00123179727089392,,0.00213472414319856,
+NQ,NQ,NQ,category-MK,2023-11-16,2024-01-09,7,,,,,,,,,,8.28459388888889,8.28459388888889,0,0,8.28459388888889,8.28459388888889,0,1,0,7.0753296464058,0,109.157921303911,0,681.654118546571,0,8.866666667,0.934352694200463,8.866666667,8.866666667,0,0.0758954896715503,,0.081227881230112,,0.012153662192423,,0.0130075744072457,,23.0753296464058,0.211394000277459,0.0338519624815107,8.0753296464058,1.14133617088894,0.0363971507664198,,0.0389544023283042,,0.00267596850504046,,0.00286398115149688,
+QE,QE,QE,category-KT,2023-10-26,2024-01-10,10,,,,,,,,,,19.6365566666667,19.6365566666667,0,0,19.6365566666667,19.6365566666667,0,1,0,13.0690627901923,0,204.035941852885,0,1534.35941852885,0,20.383333334,0.963363368733823,20.383333334,20.383333334,0,0.0962406745024616,,0.0999006995968235,,0.0127978858340077,,0.0132845884007696,,11.207188370577,0.0549275204593985,0.00730414806025206,7.06906279019232,0.540900514725302,0.0271510183885131,,0.0281835694294651,,0.00601941012926945,,0.00624832781132309,
+RQ,RQ,RQ,category-MK,2023-11-17,2024-01-03,6,,,,,,,,,,11.7327066666667,11.7327066666667,0,0,11.7327066666667,11.7327066666667,0,1,0,11,0,171,0,1272,0,12.1,0.969645179063361,12.1,12.1,0,0.0686123196881092,,0.0707602339181287,,0.00922382599580713,,0.0095125786163522,,43,0.251461988304094,0.0338050314465409,11,1,0.020828611429587,,0.0214806527988997,,0.00186507641166503,,0.00192346277992803,
+SK,SK,SK,category-KT,2023-10-27,2024-01-13,11,,,,,,,,,,5.92630444444445,0.627064166666667,0,5.29924027777778,5.92630444444444,0.627064166666666,5.29924027777778,0.105810319490842,0.894189680509158,1.5064867898256,19.8841623702851,22.8215213794304,309.408908978878,79.6325807744237,2141.03939287473,7.416666667,0.799052284608282,7.416666667,0.784759869592351,6.63190679740765,0.0274768783483408,0.0171269802646165,0.0343868341003627,0.0214341171341656,0.00787446746756782,0.00247507836400086,0.0098547587176077,0.00309751741115941,0.615068812364508,0.0269512624569756,0.00772383371709152,0.697639330855288,0.463090241193584,0.0215002228920263,0.0156439485552344,0.026907154020048,0.0195781288115627,0.00140248178828025,0.00128623043481236,0.00175518150100501,0.00160969495937667
+TR,TR,TR,category-KT,2023-10-26,2023-11-22,9,,,,,,,,,,19.2394213888889,19.2394213888889,0,0,19.2394213888889,19.2394213888889,0,1,0,9,0,139,0,1098,0,19.683333333,0.977447318673059,19.683333333,19.683333333,0,0.138413103517186,,0.141606714625899,,0.0175222417020846,,0.0179265330901639,,49,0.352517985611511,0.0446265938069217,11,1.22222222222222,0.089099817115106,,0.0911556207817565,,0.00418585261214647,,0.00428243295795113,
+XT,XT,XT,category-MK,2023-10-26,2024-01-15,11,,,,,,,,,,18.9698833333333,18.5365263888889,0,0.433356944444444,18.9698833333333,18.5365263888889,0.433356944444444,0.977155529276084,0.0228444707239165,12,1.336898146233,183,20.4968824536966,1077,163.010907617187,20.683333334,0.9171579371179,20.683333334,20.2108335311784,0.472499802821569,0.101292493928355,0.0211425784103225,0.110441713285128,0.0230522765542012,0.0172112594140101,0.00265845366288086,0.018765862145941,0.00289857782971911,39,0.213114754098361,0.0362116991643454,10,0.833333333333333,0.038242122622518,0.00623177517905863,0.0416963328504696,0.00679465872436487,0.00247730187502054,0.000261031984055344,0.00270106355161171,0.000284609633184462
diff --git a/tests/data/statistics/task.csv b/tests/data/statistics/task.csv
index c385a556..e11ba999 100644
--- a/tests/data/statistics/task.csv
+++ b/tests/data/statistics/task.csv
@@ -1,6 +1,6 @@
-project_id,task_id,phase,phase_stage,status,number_of_rejections,number_of_rejections_by_inspection,number_of_rejections_by_acceptance,created_datetime,started_datetime,updated_datetime,first_acceptance_completed_datetime,sampling,first_annotation_user_id,first_annotation_username,first_annotation_worktime_hour,first_annotation_started_datetime,first_inspection_user_id,first_inspection_username,first_inspection_worktime_hour,first_inspection_started_datetime,first_acceptance_user_id,first_acceptance_username,first_acceptance_worktime_hour,first_acceptance_started_datetime,worktime_hour,annotation_worktime_hour,inspection_worktime_hour,acceptance_worktime_hour,input_data_count,annotation_count,inspection_comment_count,inspection_comment_count_in_inspection_phase,inspection_comment_count_in_acceptance_phase,inspection_is_skipped,acceptance_is_skipped,diff_days_to_first_inspection_started,diff_days_to_first_acceptance_started,diff_days_to_first_acceptance_completed,custom_production_volume1,custom_production_volume2
-12345678-abcd-1234-abcd-1234abcd5678,task1,acceptance,1,complete,1,0,1,2019-11-01T00:00:00.000+09:00,2019-11-25T04:18:05.8+09:00,2019-11-25T04:21:32.721+09:00,2019-11-25T04:21:32.56+09:00,,user1,user1,0.458071666666667,2019-11-21T22:35:32.238+09:00,user1,user1,0,2019-11-22T20:26:36.833+09:00,user1,user1,0.209477777777778,2019-11-22T20:26:36.833+09:00,0.787161388888889,0.520250277777778,1,0.266911111111111,10,41,9,0,9,False,True,,0.910469849537037,3.24028150462963,400,50
-12345678-abcd-1234-abcd-1234abcd5678,task2,acceptance,1,complete,2,0,2,2019-11-01T00:00:00.000+09:00,2019-11-15T13:40:32.074+09:00,2019-11-15T13:43:35.02+09:00,2019-11-15T13:43:34.974+09:00,,user1,user1,0.801513055555556,2019-11-13T14:46:41.236+09:00,user2,user2,0,2019-11-15T10:12:26.528+09:00,user2,user2,0.350685555555556,2019-11-15T10:12:26.528+09:00,1.70834722222222,1.25867722222222,2,0.44967,10,65,7,0,7,True,False,,1.80955199074074,1.95617752314815,600,70
-12345678-abcd-1234-abcd-1234abcd5678,task10,acceptance,1,complete,0,0,0,2019-11-01T00:00:00.000+09:00,2019-11-14T09:15:50.754+09:00,2019-11-14T09:18:49.676+09:00,2019-11-14T09:18:49.616+09:00,,user4,user4,0.217835277777778,2019-11-11T13:26:27.456+09:00,user4,user4,0,2019-11-14T09:15:50.754+09:00,user4,user4,0.049683888888889,2019-11-14T09:15:50.754+09:00,0.267519166666667,0.217835277777778,3,0.049683888888889,10,30,0,0,0,False,False,,2.82596409722222,2.82803425925926,300,20
-12345678-abcd-1234-abcd-1234abcd5678,task4,acceptance,1,not_started,0,0,0,2019-11-01T00:00:00.000+09:00,2019-11-25T22:05:23.501+09:00,2019-11-25T22:09:02.157+09:00,,,user4,user4,0.060726388888889,2019-11-25T22:05:23.501+09:00,,,0,,,,0,,0.060726388888889,0.060726388888889,0,0,10,10,0,0,0,False,False,,,,100,5
-12345678-abcd-1234-abcd-1234abcd5678,task5,annotation,1,not_started,0,0,0,2019-11-01T00:00:00.000+09:00,,2019-11-08T22:54:26.981+09:00,,,user5,user5,0.029444166666667,2019-11-26T09:27:22.776+09:00,,,0,,,,0,,0.029444166666667,0.029444166666667,0,0,10,0,0,0,0,False,False,,,,0,0
+project_id,task_id,phase,phase_stage,status,number_of_rejections,number_of_rejections_by_inspection,number_of_rejections_by_acceptance,created_datetime,started_datetime,updated_datetime,first_acceptance_completed_datetime,sampling,first_annotation_user_id,first_annotation_username,first_annotation_worktime_hour,first_annotation_started_datetime,first_inspection_user_id,first_inspection_username,first_inspection_worktime_hour,first_inspection_started_datetime,first_acceptance_reached_datetime,first_acceptance_user_id,first_acceptance_username,first_acceptance_worktime_hour,first_acceptance_started_datetime,worktime_hour,annotation_worktime_hour,inspection_worktime_hour,acceptance_worktime_hour,input_data_count,annotation_count,inspection_comment_count,inspection_comment_count_in_inspection_phase,inspection_comment_count_in_acceptance_phase,inspection_is_skipped,acceptance_is_skipped,diff_days_to_first_inspection_started,diff_days_to_first_acceptance_started,diff_days_to_first_acceptance_completed,custom_production_volume1,custom_production_volume2
+12345678-abcd-1234-abcd-1234abcd5678,task1,acceptance,1,complete,1,0,1,2019-11-01T00:00:00.000+09:00,2019-11-25T04:18:05.8+09:00,2019-11-25T04:21:32.721+09:00,2019-11-25T04:21:32.56+09:00,,user1,user1,0.458071666666667,2019-11-21T22:35:32.238+09:00,user1,user1,0,2019-11-22T20:26:36.833+09:00,2019-11-20T20:26:36.833+09:00,user1,user1,0.209477777777778,2019-11-22T20:26:36.833+09:00,0.787161388888889,0.520250277777778,1,0.266911111111111,10,41,9,0,9,False,True,,0.910469849537037,3.24028150462963,400,50
+12345678-abcd-1234-abcd-1234abcd5678,task2,acceptance,1,complete,2,0,2,2019-11-01T00:00:00.000+09:00,2019-11-15T13:40:32.074+09:00,2019-11-15T13:43:35.02+09:00,2019-11-15T13:43:34.974+09:00,,user1,user1,0.801513055555556,2019-11-13T14:46:41.236+09:00,user2,user2,0,2019-11-15T10:12:26.528+09:00,2019-11-14T10:12:26.528+09:00,user2,user2,0.350685555555556,2019-11-15T10:12:26.528+09:00,1.70834722222222,1.25867722222222,2,0.44967,10,65,7,0,7,True,False,,1.80955199074074,1.95617752314815,600,70
+12345678-abcd-1234-abcd-1234abcd5678,task10,acceptance,1,complete,0,0,0,2019-11-01T00:00:00.000+09:00,2019-11-14T09:15:50.754+09:00,2019-11-14T09:18:49.676+09:00,2019-11-14T09:18:49.616+09:00,,user4,user4,0.217835277777778,2019-11-11T13:26:27.456+09:00,user4,user4,0,2019-11-14T09:15:50.754+09:00,2019-11-13T09:15:50.754+09:00,user4,user4,0.049683888888889,2019-11-14T09:15:50.754+09:00,0.267519166666667,0.217835277777778,3,0.049683888888889,10,30,0,0,0,False,False,,2.82596409722222,2.82803425925926,300,20
+12345678-abcd-1234-abcd-1234abcd5678,task4,acceptance,1,not_started,0,0,0,2019-11-01T00:00:00.000+09:00,2019-11-25T22:05:23.501+09:00,2019-11-25T22:09:02.157+09:00,,,user4,user4,0.060726388888889,2019-11-25T22:05:23.501+09:00,,,0,,,,,0,,0.060726388888889,0.060726388888889,0,0,10,10,0,0,0,False,False,,,,100,5
+12345678-abcd-1234-abcd-1234abcd5678,task5,annotation,1,not_started,0,0,0,2019-11-01T00:00:00.000+09:00,,2019-11-08T22:54:26.981+09:00,,,user5,user5,0.029444166666667,2019-11-26T09:27:22.776+09:00,,,0,,,,,0,,0.029444166666667,0.029444166666667,0,0,10,0,0,0,0,False,False,,,,0,0
diff --git a/tests/data/statistics/visualization-dir1/project_info.json b/tests/data/statistics/visualization-dir1/project_info.json
index c96a2dce..54cde007 100644
--- a/tests/data/statistics/visualization-dir1/project_info.json
+++ b/tests/data/statistics/visualization-dir1/project_info.json
@@ -3,6 +3,7 @@
"project_title": "test-project",
"input_data_type": "image",
"measurement_datetime": "2022-06-14T15:56:10.634+09:00",
+ "task_completion_criteria": "acceptance_reached",
"query": {
"task_query": null,
"task_ids": null,
diff --git a/tests/stat_visualization/test_mask_visualization_dir.py b/tests/stat_visualization/test_mask_visualization_dir.py
index c953e420..32d45871 100644
--- a/tests/stat_visualization/test_mask_visualization_dir.py
+++ b/tests/stat_visualization/test_mask_visualization_dir.py
@@ -1,7 +1,7 @@
from pathlib import Path
from annofabcli.stat_visualization.mask_visualization_dir import mask_visualization_dir
-from annofabcli.statistics.visualization.project_dir import ProjectDir
+from annofabcli.statistics.visualization.project_dir import ProjectDir, TaskCompletionCriteria
output_dir = Path("./tests/out/stat_visualization/mask_visualization_dir")
data_dir = Path("./tests/data/stat_visualization/mask_visualization_dir")
@@ -10,7 +10,7 @@
def test__mask_visualization_dir__minimal():
mask_visualization_dir(
- project_dir=ProjectDir(data_dir / "visualization1"),
- output_project_dir=ProjectDir(output_dir / "masked-visualization"),
+ project_dir=ProjectDir(data_dir / "visualization1", TaskCompletionCriteria.ACCEPTANCE_COMPLETED),
+ output_project_dir=ProjectDir(output_dir / "masked-visualization", TaskCompletionCriteria.ACCEPTANCE_COMPLETED),
minimal_output=True,
)
diff --git a/tests/statistics/visualization/dataframe/test_project_performance.py b/tests/statistics/visualization/dataframe/test_project_performance.py
index 8fec16b8..c093a7d2 100644
--- a/tests/statistics/visualization/dataframe/test_project_performance.py
+++ b/tests/statistics/visualization/dataframe/test_project_performance.py
@@ -6,7 +6,7 @@
ProjectPerformance,
ProjectWorktimePerMonth,
)
-from annofabcli.statistics.visualization.model import WorktimeColumn
+from annofabcli.statistics.visualization.model import TaskCompletionCriteria, WorktimeColumn
from annofabcli.statistics.visualization.project_dir import ProjectDir
output_dir = Path("./tests/out/statistics/visualization/dataframe/project_performance")
@@ -16,7 +16,7 @@
class TestProjectPerformance:
def test__from_project_dirs__and__to_csv(self):
- actual = ProjectPerformance.from_project_dirs([ProjectDir(data_dir / "visualization-dir1")])
+ actual = ProjectPerformance.from_project_dirs([ProjectDir(data_dir / "visualization-dir1", TaskCompletionCriteria.ACCEPTANCE_COMPLETED)])
df = actual.df
assert len(df) == 1
row = df.iloc[0]
@@ -30,7 +30,7 @@ def test__from_project_dirs__and__to_csv(self):
actual.to_csv(output_dir / "test__from_project_dirs__and__to_csv.csv")
def test__from_project_dirs__空ディレクトから生成する(self):
- actual = ProjectPerformance.from_project_dirs([ProjectDir(data_dir / "empty")])
+ actual = ProjectPerformance.from_project_dirs([ProjectDir(data_dir / "empty", TaskCompletionCriteria.ACCEPTANCE_COMPLETED)])
df = actual.df
assert len(df) == 1
row = df.iloc[0]
@@ -46,7 +46,8 @@ def test__from_project_dirs__空ディレクトから生成する(self):
class TestProjectWorktimePerMonth:
def test__from_project_dirs__and_to_csv(self):
actual_worktime = ProjectWorktimePerMonth.from_project_dirs(
- [ProjectDir(data_dir / "visualization-dir1")], worktime_column=WorktimeColumn.ACTUAL_WORKTIME_HOUR
+ [ProjectDir(data_dir / "visualization-dir1", TaskCompletionCriteria.ACCEPTANCE_COMPLETED)],
+ worktime_column=WorktimeColumn.ACTUAL_WORKTIME_HOUR,
)
df = actual_worktime.df
assert len(df) == 1
@@ -59,7 +60,7 @@ def test__from_project_dirs__and_to_csv(self):
def test__from_project_dirs__空ディレクトリから生成(self):
actual_worktime = ProjectWorktimePerMonth.from_project_dirs(
- [ProjectDir(data_dir / "empty")], worktime_column=WorktimeColumn.ACTUAL_WORKTIME_HOUR
+ [ProjectDir(data_dir / "empty", TaskCompletionCriteria.ACCEPTANCE_COMPLETED)], worktime_column=WorktimeColumn.ACTUAL_WORKTIME_HOUR
)
df = actual_worktime.df
assert len(df) == 1
diff --git a/tests/statistics/visualization/dataframe/test_whole_productivity_per_date.py b/tests/statistics/visualization/dataframe/test_whole_productivity_per_date.py
index 51c7de5b..fced0c49 100644
--- a/tests/statistics/visualization/dataframe/test_whole_productivity_per_date.py
+++ b/tests/statistics/visualization/dataframe/test_whole_productivity_per_date.py
@@ -6,7 +6,7 @@
WholeProductivityPerFirstAnnotationStartedDate,
)
from annofabcli.statistics.visualization.dataframe.worktime_per_date import WorktimePerDate
-from annofabcli.statistics.visualization.model import ProductionVolumeColumn
+from annofabcli.statistics.visualization.model import ProductionVolumeColumn, TaskCompletionCriteria
output_dir = Path("./tests/out/statistics/visualization/dataframe/whole_productivity_per_date")
data_dir = Path("./tests/data/statistics")
@@ -27,22 +27,40 @@ def setup_class(cls) -> None:
],
)
worktime_per_date = WorktimePerDate.from_csv(data_dir / "ユーザ_日付list-作業時間.csv")
- cls.main_obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date)
+ cls.main_obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date, TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
cls.output_dir = output_dir / "WholeProductivityPerCompletedDate"
cls.output_dir.mkdir(exist_ok=True, parents=True)
- def test__from_df_wrapper(cls):
+ def test__from_df_wrapper__task_completion_criteria_is_acceptance_completed(cls):
task = Task.from_csv(
data_dir / "task.csv",
)
worktime_per_date = WorktimePerDate.from_csv(data_dir / "ユーザ_日付list-作業時間.csv")
- WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date)
+ obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date, TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
+ df_actual = obj.df
+ assert df_actual["task_count"].sum() == 3
+ assert df_actual[df_actual["date"] == "2019-11-14"].iloc[0]["task_count"] == 1
+ assert df_actual[df_actual["date"] == "2019-11-15"].iloc[0]["task_count"] == 1
+ assert df_actual[df_actual["date"] == "2019-11-25"].iloc[0]["task_count"] == 1
+
+ def test__from_df_wrapper__task_completion_criteria_is_acceptance_reached(cls):
+ task = Task.from_csv(
+ data_dir / "task.csv",
+ )
+ worktime_per_date = WorktimePerDate.from_csv(data_dir / "ユーザ_日付list-作業時間.csv")
+ obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, worktime_per_date, TaskCompletionCriteria.ACCEPTANCE_REACHED)
+ df_actual = obj.df
+
+ assert df_actual["task_count"].sum() == 3
+ assert df_actual[df_actual["date"] == "2019-11-13"].iloc[0]["task_count"] == 1
+ assert df_actual[df_actual["date"] == "2019-11-14"].iloc[0]["task_count"] == 1
+ assert df_actual[df_actual["date"] == "2019-11-20"].iloc[0]["task_count"] == 1
def test_from_df__df_worktime引数が空でもインスタンスを生成できることを確認する(self):
# 完了タスクが1つもない状態で試す
task = Task.from_csv(data_dir / "task.csv")
- obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, WorktimePerDate.empty())
+ obj = WholeProductivityPerCompletedDate.from_df_wrapper(task, WorktimePerDate.empty(), TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
df_actual = obj.df
# 日毎の完了したタスク数が一致していることの確認
@@ -73,6 +91,22 @@ def setup_class(cls) -> None:
cls.output_dir = output_dir / "WholeProductivityPerFirstAnnotationStartedDate"
cls.output_dir.mkdir(exist_ok=True, parents=True)
+ def test__from_task__task_completion_criteria_is_acceptance_completed(self):
+ task = Task.from_csv(
+ data_dir / "task.csv",
+ )
+ obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task, TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
+ df_actual = obj.df
+ assert df_actual["task_count"].sum() == 3
+
+ def test__from_task__task_completion_criteria_is_acceptance_reached(cls):
+ task = Task.from_csv(
+ data_dir / "task.csv",
+ )
+ obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task, TaskCompletionCriteria.ACCEPTANCE_REACHED)
+ df_actual = obj.df
+ assert df_actual["task_count"].sum() == 4
+
def test__from_task__and__to_csv(self):
task = Task.from_csv(
data_dir / "task.csv",
@@ -81,7 +115,7 @@ def test__from_task__and__to_csv(self):
ProductionVolumeColumn("custom_production_volume2", "custom_生産量2"),
],
)
- obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task)
+ obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task, TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
obj.to_csv(self.output_dir / "test__from_task__and__to_csv.csv")
def test__from_task__and__plot(self):
@@ -92,5 +126,5 @@ def test__from_task__and__plot(self):
ProductionVolumeColumn("custom_production_volume2", "custom_生産量2"),
],
)
- obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task)
+ obj = WholeProductivityPerFirstAnnotationStartedDate.from_task(task, TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
obj.plot(self.output_dir / "test__from_task__and__plot.html")
diff --git a/tests/statistics/visualization/test_write_performance_rating_csv.py b/tests/statistics/visualization/test_write_performance_rating_csv.py
index d55d360d..7db11152 100644
--- a/tests/statistics/visualization/test_write_performance_rating_csv.py
+++ b/tests/statistics/visualization/test_write_performance_rating_csv.py
@@ -14,7 +14,7 @@
create_quality_indicator_by_directory,
create_threshold_infos_per_project,
)
-from annofabcli.statistics.visualization.project_dir import ProjectDir
+from annofabcli.statistics.visualization.project_dir import ProjectDir, TaskCompletionCriteria
data_dir = Path("tests/data/stat_visualization")
@@ -41,7 +41,7 @@ def test__create_quality_indicator_by_directory():
("biography", ""): ["category-KK", "category-KT", "category-KT", "category-KT"],
}
)
-project_dir = ProjectDir(data_dir / "visualization-dir")
+project_dir = ProjectDir(data_dir / "visualization-dir", TaskCompletionCriteria.ACCEPTANCE_COMPLETED)
user_performance = project_dir.read_user_performance()