Skip to content

Commit

Permalink
Merge pull request #161 from kurusugawa-computer/modify-statistics-vi…
Browse files Browse the repository at this point in the history
…sualize

Modify statistics visualize
  • Loading branch information
yuji38kwmt authored Jan 22, 2020
2 parents edf665b + 9650082 commit 7509ed8
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 212 deletions.
259 changes: 128 additions & 131 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion annofabcli/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.18.1"
__version__ = "1.18.2"
Empty file added annofabcli/py.typed
Empty file.
51 changes: 32 additions & 19 deletions annofabcli/statistics/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ def write_histogram_for_worktime(self, df: pd.DataFrame):

renderer = hv.renderer("bokeh")

output_file = f"{self.outdir}/html/{self.short_project_id}-ヒストグラム-作業時間"
logger.debug(f"{output_file}.html を出力します。")

histogram_name_list = [
HistogramName(column="annotation_worktime_hour", x_axis_label="教師付時間[hour]", title="教師付時間",),
HistogramName(column="inspection_worktime_hour", x_axis_label="検査時間[hour]", title="検査時間",),
Expand Down Expand Up @@ -195,7 +198,7 @@ def write_histogram_for_worktime(self, df: pd.DataFrame):

# 軸範囲が同期しないようにする
layout = hv.Layout(histograms).options(shared_axes=False).cols(3)
renderer.save(layout, f"{self.outdir}/html/{self.short_project_id}-ヒストグラム-作業時間")
renderer.save(layout, output_file)

def write_histogram_for_other(self, df: pd.DataFrame):
"""
Expand All @@ -211,6 +214,9 @@ def write_histogram_for_other(self, df: pd.DataFrame):

renderer = hv.renderer("bokeh")

output_file = f"{self.outdir}/html/{self.short_project_id}-ヒストグラム"
logger.debug(f"{output_file}.html を出力します。")

histogram_name_list = [
HistogramName(column="annotation_count", x_axis_label="アノテーション数", title="アノテーション数"),
HistogramName(column="input_data_count", x_axis_label="画像枚数", title="画像枚数"),
Expand Down Expand Up @@ -241,7 +247,7 @@ def write_histogram_for_other(self, df: pd.DataFrame):

# 軸範囲が同期しないようにする
layout = hv.Layout(histograms).options(shared_axes=False).cols(3)
renderer.save(layout, f"{self.outdir}/html/{self.short_project_id}-ヒストグラム")
renderer.save(layout, output_file)

def write_histogram_for_annotation_count_by_label(self, df: pd.DataFrame) -> None:
"""
Expand All @@ -253,6 +259,9 @@ def write_histogram_for_annotation_count_by_label(self, df: pd.DataFrame) -> Non

renderer = hv.renderer("bokeh")

output_file = f"{self.outdir}/html/{self.short_project_id}-ヒストグラム-ラベルごとのアノテーション数"
logger.debug(f"{output_file}.html を出力します。")

histograms = []
label_columns = [e for e in df.columns if e.startswith("label_")]

Expand All @@ -267,7 +276,7 @@ def write_histogram_for_annotation_count_by_label(self, df: pd.DataFrame) -> Non

# 軸範囲が同期しないようにする
layout = hv.Layout(histograms).options(shared_axes=False).cols(3)
renderer.save(layout, f"{self.outdir}/html/{self.short_project_id}-ヒストグラム-ラベルごとのアノテーション数")
renderer.save(layout, output_file)

def create_user_id_list(
self, df: pd.DataFrame, user_id_column: str, arg_user_id_list: Optional[List[str]] = None,
Expand Down Expand Up @@ -321,6 +330,9 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
Returns:
"""
output_file = f"{self.outdir}/html/{self.short_project_id}-{html_title}.html"
logger.debug(f"{output_file} を出力します。")

figs: List[bokeh.plotting.Figure] = []
for fig_info in fig_info_list:
figs.append(
Expand Down Expand Up @@ -354,9 +366,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
self._set_legend(fig, hover_tool)

bokeh.plotting.reset_output()
bokeh.plotting.output_file(
f"{self.outdir}/html/{self.short_project_id}-{html_title}.html", title=html_title,
)
bokeh.plotting.output_file(output_file, title=html_title)
bokeh.plotting.save(bokeh.layouts.column(figs))

tooltip_item = [
Expand All @@ -380,7 +390,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
first_annotation_user_id_list = self.create_user_id_list(
df, "first_annotation_user_id", first_annotation_user_id_list
)
logger.debug(f"グラフに表示するuser_id = {first_annotation_user_id_list}")
logger.debug(f"教師付者用の折れ線グラフに表示する、教師付者のuser_id = {first_annotation_user_id_list}")

df["date_first_annotation_started_date"] = df["first_annotation_started_date"].map(
lambda e: dateutil.parser.parse(e).date()
Expand Down Expand Up @@ -530,6 +540,9 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
Returns:
"""
output_file = f"{self.outdir}/html/{self.short_project_id}-{html_title}.html"
logger.debug(f"{output_file} を出力します。")

figs: List[bokeh.plotting.Figure] = []
for fig_info in fig_info_list:
figs.append(
Expand Down Expand Up @@ -577,9 +590,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
self._set_legend(fig, hover_tool)

bokeh.plotting.reset_output()
bokeh.plotting.output_file(
f"{self.outdir}/html/{self.short_project_id}-{html_title}.html", title=html_title,
)
bokeh.plotting.output_file(output_file, title=html_title)
bokeh.plotting.save(bokeh.layouts.column(figs))

tooltip_item = [
Expand All @@ -605,7 +616,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
first_annotation_user_id_list = self.create_user_id_list(
df, "first_annotation_user_id", first_annotation_user_id_list
)
logger.debug(f"グラフに表示するuser_id = {first_annotation_user_id_list}")
logger.debug(f"教師付者用の累計折れ線グラフに表示する、教師付者のuser_id = {first_annotation_user_id_list}")

# 横軸が累計のアノテーション数
fig_info_list_annotation_count = [
Expand Down Expand Up @@ -752,6 +763,9 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
Returns:
"""
output_file = f"{self.outdir}/html/{self.short_project_id}-{html_title}.html"
logger.debug(f"{output_file} を出力します。")

figs: List[bokeh.plotting.Figure] = []
for fig_info in fig_info_list:
figs.append(
Expand Down Expand Up @@ -796,9 +810,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
self._set_legend(fig, hover_tool)

bokeh.plotting.reset_output()
bokeh.plotting.output_file(
f"{self.outdir}/html/{self.short_project_id}-{html_title}.html", title=html_title,
)
bokeh.plotting.output_file(output_file, title=html_title)
bokeh.plotting.save(bokeh.layouts.column(figs))

tooltip_item = [
Expand Down Expand Up @@ -830,7 +842,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
logger.info(f"検査フェーズを担当してユーザがいないため、検査者用のグラフは出力しません。")
return

logger.debug(f"グラフに表示するuser_id = {first_inspection_user_id_list}")
logger.debug(f"検査者用の累計折れ線グラフに表示する、検査者のuser_id = {first_inspection_user_id_list}")

# 横軸が累計のアノテーション数
fig_info_list_annotation_count = [
Expand Down Expand Up @@ -909,6 +921,9 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
Returns:
"""
output_file = f"{self.outdir}/html/{self.short_project_id}-{html_title}.html"
logger.debug(f"{output_file} を出力します。")

figs: List[bokeh.plotting.Figure] = []
for fig_info in fig_info_list:
figs.append(
Expand Down Expand Up @@ -952,9 +967,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
self._set_legend(fig, hover_tool)

bokeh.plotting.reset_output()
bokeh.plotting.output_file(
f"{self.outdir}/html/{self.short_project_id}-{html_title}.html", title=html_title,
)
bokeh.plotting.output_file(output_file, title=html_title)
bokeh.plotting.save(bokeh.layouts.column(figs))

tooltip_item = [
Expand Down Expand Up @@ -988,7 +1001,7 @@ def write_cumulative_graph(fig_info_list: List[Dict[str, str]], html_title: str)
logger.info(f"受入フェーズを担当してユーザがいないため、受入者用のグラフは出力しません。")
return

logger.debug(f"グラフに表示するuser_id = {first_acceptance_user_id_list}")
logger.debug(f"受入者用の累計折れ線グラフに表示する、受入者のuser_id = {first_acceptance_user_id_list}")

# 横軸が累計のアノテーション数
fig_info_list_annotation_count = [
Expand Down
38 changes: 24 additions & 14 deletions annofabcli/statistics/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
WorktimeStatisticsItem,
)
from annofabapi.models import InputDataId, Inspection, InspectionStatus, Task, TaskHistory, TaskPhase, TaskStatus
from annofabapi.utils import get_task_history_index_skipped_acceptance, get_task_history_index_skipped_inspection
from more_itertools import first_true

import annofabcli
Expand Down Expand Up @@ -139,6 +140,23 @@ def _create_annotation_summary(self, annotation_list: List[SimpleAnnotationDetai

return annotation_summary

@staticmethod
def operator_is_changed_by_phase(task_history_list: List[TaskHistory], phase: TaskPhase) -> bool:
"""
フェーズ内の作業者が途中で変わったかどうか
Args:
task_history_list: タスク履歴List
phase: フェーズ
Returns:
Trueならばフェーズ内の作業者が途中で変わった
"""
account_id_list = [
e["account_id"] for e in task_history_list if TaskPhase(e["phase"]) == phase and e["account_id"] is not None
]
return len(set(account_id_list)) >= 2

@staticmethod
def _inspection_condition(inspection_arg, exclude_reply: bool, only_error_corrected: bool):
"""
Expand Down Expand Up @@ -392,18 +410,6 @@ def diff_days(ended_key: str, started_key: str) -> Optional[float]:
else:
return None

def acceptance_is_skipped(arg_task_histories: List[TaskHistory]) -> bool:
skipped_histories = [
e
for e in arg_task_histories
if (
e["phase"] == TaskPhase.ACCEPTANCE.value
and e["account_id"] is None
and annofabcli.utils.isoduration_to_hour(e["accumulated_labor_time_milliseconds"]) == 0
)
]
return len(skipped_histories) > 0

annotation_histories = [e for e in task_histories if e["phase"] == TaskPhase.ANNOTATION.value]
inspection_histories = [e for e in task_histories if e["phase"] == TaskPhase.INSPECTION.value]
acceptance_histories = [e for e in task_histories if e["phase"] == TaskPhase.ACCEPTANCE.value]
Expand Down Expand Up @@ -481,8 +487,12 @@ def acceptance_is_skipped(arg_task_histories: List[TaskHistory]) -> bool:

task["diff_days_to_task_completed"] = diff_days("task_completed_datetime", "first_annotation_started_datetime")

# 自動受入されたか否か
task["acceptance_is_skipped"] = acceptance_is_skipped(task_histories)
# 抜取検査/抜取受入で、検査/受入がスキップされたか否か
task["acceptance_is_skipped"] = len(get_task_history_index_skipped_acceptance(task_histories)) > 0
task["inspection_is_skipped"] = len(get_task_history_index_skipped_inspection(task_histories)) > 0
task["annotator_is_changed"] = self.operator_is_changed_by_phase(task_histories, TaskPhase.ANNOTATION)
task["inspector_is_changed"] = self.operator_is_changed_by_phase(task_histories, TaskPhase.INSPECTION)
task["acceptor_is_changed"] = self.operator_is_changed_by_phase(task_histories, TaskPhase.ACCEPTANCE)

return task

Expand Down
32 changes: 31 additions & 1 deletion annofabcli/statistics/tsv.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def _write_csv(self, filename: str, df: pd.DataFrame) -> None:
"""
output_path = Path(f"{self.outdir}/{filename}")
output_path.parent.mkdir(exist_ok=True, parents=True)
logger.debug(f"{str(output_path)} 書き込み")
logger.debug(f"{str(output_path)} を出力します。")
df.to_csv(str(output_path), sep=",", encoding="utf_8_sig", index=False)

@staticmethod
Expand Down Expand Up @@ -146,6 +146,12 @@ def write_task_list(self, df: pd.DataFrame, dropped_columns: Optional[List[str]]
"annotation_count",
"inspection_count",
"input_data_count_of_inspection",
# タスクの状態
"annotator_is_changed",
"inspector_is_changed",
"acceptor_is_changed",
"inspection_is_skipped",
"acceptance_is_skipped",
]
required_columns = self._create_required_columns(df, prior_columns, dropped_columns)
self._write_csv(f"{self.short_project_id}-タスクlist.csv", df[required_columns])
Expand Down Expand Up @@ -182,6 +188,30 @@ def write_task_history_list(self, df: pd.DataFrame, dropped_columns: Optional[Li
required_columns = self._create_required_columns(df, prior_columns, dropped_columns)
self._write_csv(f"{self.short_project_id}-タスク履歴list.csv", df[required_columns])

def write_task_count(self, df: pd.DataFrame) -> None:
"""
タスク数の集計結果をCSVで出力する。
Args:
df: タスクListのDataFrame
"""
columns = [
"task_count",
"annotator_is_changed",
"inspector_is_changed",
"acceptor_is_changed",
"inspection_is_skipped",
"acceptance_is_skipped",
]

sum_series = df[columns].sum()
sum_df = pd.DataFrame()
sum_df["column"] = sum_series.index
sum_df["count_if_true"] = sum_series.values

self._write_csv(f"{self.short_project_id}-タスク数の集計.csv", sum_df)

def write_member_list(self, df: pd.DataFrame, dropped_columns: Optional[List[str]] = None):
"""
プロジェクトメンバ一覧をTSVで出力する
Expand Down
Loading

0 comments on commit 7509ed8

Please sign in to comment.