diff --git a/src/caret_analyze/plot/stacked_bar/latency_stacked_bar.py b/src/caret_analyze/plot/stacked_bar/latency_stacked_bar.py index 571856d27..3554735c7 100644 --- a/src/caret_analyze/plot/stacked_bar/latency_stacked_bar.py +++ b/src/caret_analyze/plot/stacked_bar/latency_stacked_bar.py @@ -106,6 +106,10 @@ def _get_response_time_record( # include timestamp of response time (best, worst) if self._case == 'best': return response_time.to_best_case_stacked_bar() + if self._case == 'worst-in-input': + return response_time.to_worst_in_input_case_stacked_bar() + if self._case == 'all': + return response_time.to_all_stacked_bar() return response_time.to_stacked_bar() diff --git a/src/caret_analyze/record/records_service/response_time.py b/src/caret_analyze/record/records_service/response_time.py index 5d5ef6e1d..1dcb430fb 100644 --- a/src/caret_analyze/record/records_service/response_time.py +++ b/src/caret_analyze/record/records_service/response_time.py @@ -235,12 +235,15 @@ def __init__( if columns: self._start_column = columns[0] self._end_column = columns[-1] + self._columns = columns else: self._start_column = records.columns[0] self._end_column = records.columns[-1] + self._columns = records.columns self._start_timestamps: list[int] = [] self._end_timestamps: list[int] = [] + self._records: list[RecordInterface] = [] for record in reversed(records.data): if self._end_column in record.columns: @@ -257,10 +260,12 @@ def __init__( if start_ts not in self._start_timestamps: self._start_timestamps.insert(0, start_ts) self._end_timestamps.insert(0, end_ts) + self._records.insert(0, record) else: idx = self._start_timestamps.index(start_ts) if end_ts < self._end_timestamps[idx]: self._end_timestamps[idx] = end_ts + self._records[idx] = record def to_all_records(self) -> RecordsInterface: records = self._create_empty_records() @@ -296,6 +301,80 @@ def to_worst_in_input_records(self) -> RecordsInterface: return records + def to_all_stacked_bar(self) -> RecordsInterface: + end_column_record_dict: dict[int, list[RecordInterface]] = {} + + # classify records which have same end timestamp + end_ts = None + for record in reversed(self._records): + if self._end_column in record.columns: + end_ts = record.get(self._end_column) + elif end_ts is None: + continue + + if end_ts in end_column_record_dict.keys(): + end_column_record_dict[end_ts].insert(0, record) + else: + end_column_record_dict[end_ts] = [record] + + # fill empty data + filled_records_dict: dict[int, list[RecordInterface]] = {} + # for each records which have same end timestamp + for end_ts, record_list in end_column_record_dict.items(): + filled_records_dict[end_ts] = [] + + for record in reversed(record_list): + # if record doesn't have some timestamps, + # record timestamps just after + for column in self._columns: + if column in record.columns: + continue + timestamps = [record.get(column) for record in filled_records_dict[end_ts] + if column in record.columns] + record.add(column, min(timestamps)) + filled_records_dict[end_ts].append(record) + + columns = [ColumnValue(c) for c in self._columns] + stacked_bar_records: RecordsInterface =\ + RecordsFactory.create_instance(init=[_.data for _ + in sum(filled_records_dict.values(), [])], + columns=columns) + stacked_bar_records.sort_column_order() + return stacked_bar_records + + def to_worst_in_input_case_stacked_bar(self) -> RecordsInterface: + end_column_record_dict: dict[int, list[RecordInterface]] = {} + + # classify records which have same end timestamp + end_ts = None + for record in reversed(self._records): + if self._end_column in record.columns: + end_ts = record.get(self._end_column) + elif end_ts is None: + continue + + if end_ts in end_column_record_dict.keys(): + end_column_record_dict[end_ts].append(record) + else: + end_column_record_dict[end_ts] = [record] + + # generate worst-case work flow + filled_record_list: list[RecordInterface] = [] + for record_list in end_column_record_dict.values(): + worst_in_input_record = RecordFactory.create_instance() + + for column in self._columns: + timestamps = [r.get(column) for r in record_list if column in r.columns] + worst_in_input_record.add(column, min(timestamps)) + filled_record_list.append(worst_in_input_record) + + columns = [ColumnValue(c) for c in self._columns] + stacked_bar_records: RecordsInterface =\ + RecordsFactory.create_instance(init=[_.data for _ in filled_record_list], + columns=columns) + stacked_bar_records.sort_column_order() + return stacked_bar_records + def _create_empty_records( self ) -> RecordsInterface: @@ -413,6 +492,42 @@ def to_best_case_stacked_bar(self) -> RecordsInterface: """ return self._records.to_range_records('best') + def to_all_stacked_bar(self) -> RecordsInterface: + """ + Calculate records for stacked bar. + + Returns + ------- + RecordsInterface + Records of the all response time. + + Columns + - {columns[0]} + - {columns[1]} + - {...} + - {columns[n-1]} + + """ + return self._response_map_all.to_all_stacked_bar() + + def to_worst_in_input_case_stacked_bar(self) -> RecordsInterface: + """ + Calculate records for stacked bar. + + Returns + ------- + RecordsInterface + Records of the worst-in-input cases response time. + + Columns + - {columns[0]} + - {columns[1]} + - {...} + - {columns[n-1]} + + """ + return self._response_map_all.to_worst_in_input_case_stacked_bar() + def to_worst_case_stacked_bar(self) -> RecordsInterface: # NOTE: # We think this function is unnecessary. diff --git a/src/test/record/records_service/test_response_time_all.py b/src/test/record/records_service/test_response_time_all.py index 3d1f85ee8..bb4048921 100644 --- a/src/test/record/records_service/test_response_time_all.py +++ b/src/test/record/records_service/test_response_time_all.py @@ -138,3 +138,159 @@ def test_drop_case(self): ] result = to_dict(response_time.to_all_records()) assert result == expect_raw + + +class TestAllStackedBar: + + @property + def columns(self) -> list[ColumnValue]: + return [ColumnValue('start'), ColumnValue('middle0'), + ColumnValue('middle1'), ColumnValue('end')] + + @property + def column_names(self) -> list[str]: + return ['start', 'middle0', 'middle1', 'end'] + + def test_empty_case(self): + records_raw = [] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [] + result = to_dict(response_time.to_all_stacked_bar()) + assert result == expect_raw + + def test_single_case(self): + records_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 2, 'end': 3}, + {'start': 4, 'middle0': 5, 'middle1': 7, 'end': 8}, + {'start': 6, 'middle0': 7, 'middle1': 8, 'end': 9} + ] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 2, 'end': 3}, + {'start': 4, 'middle0': 5, 'middle1': 7, 'end': 8}, + {'start': 6, 'middle0': 7, 'middle1': 8, 'end': 9} + ] + result = to_dict(response_time.to_all_stacked_bar()) + assert result == expect_raw + + def test_multi_case(self): + records_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 3, 'end': 4}, + {'start': 1, 'middle0': 2, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 5, 'end': 6}, + {'start': 3, 'middle0': 4, 'middle1': 5, 'end': 6} + ] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 3, 'end': 4}, + {'start': 1, 'middle0': 2, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 5, 'end': 6}, + {'start': 3, 'middle0': 4, 'middle1': 5, 'end': 6} + ] + result = to_dict(response_time.to_all_stacked_bar()) + assert result == expect_raw + + def test_drop_case(self): + records_raw = [ + {'start': 0, 'middle0': 1}, + {'start': 1, 'middle0': 2, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 4}, + {'start': 3, 'middle0': 4, 'middle1': 5, 'end': 6} + ] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 3, 'end': 4}, + {'start': 1, 'middle0': 2, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 4, 'end': 6}, + {'start': 3, 'middle0': 4, 'middle1': 5, 'end': 6} + ] + result = to_dict(response_time.to_all_stacked_bar()) + assert result == expect_raw + + +class TestWorstInInputStackedBar: + + @property + def columns(self) -> list[ColumnValue]: + return [ColumnValue('start'), ColumnValue('middle0'), + ColumnValue('middle1'), ColumnValue('end')] + + @property + def column_names(self) -> list[str]: + return ['start', 'middle0', 'middle1', 'end'] + + def test_empty_case(self): + records_raw = [] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [] + result = to_dict(response_time.to_worst_in_input_case_stacked_bar()) + assert result == expect_raw + + def test_single_case(self): + records_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 2, 'end': 3}, + {'start': 4, 'middle0': 5, 'middle1': 7, 'end': 8}, + {'start': 6, 'middle0': 7, 'middle1': 8, 'end': 9} + ] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 2, 'end': 3}, + {'start': 4, 'middle0': 5, 'middle1': 7, 'end': 8}, + {'start': 6, 'middle0': 7, 'middle1': 8, 'end': 9} + ] + result = to_dict(response_time.to_worst_in_input_case_stacked_bar()) + assert result == expect_raw + + def test_multi_case(self): + records_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 3, 'end': 4}, + {'start': 1, 'middle0': 2, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 5, 'end': 6}, + {'start': 3, 'middle0': 4, 'middle1': 5, 'end': 6} + ] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 5, 'end': 6}, + ] + result = to_dict(response_time.to_worst_in_input_case_stacked_bar()) + assert result == expect_raw + + def test_drop_case(self): + records_raw = [ + {'start': 0, 'middle0': 1}, + {'start': 1, 'middle0': 2, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 4}, + {'start': 3, 'middle0': 4, 'middle1': 5, 'end': 6} + ] + records = create_records(records_raw, self.columns) + + response_time = ResponseTime(records, columns=self.column_names) + + expect_raw = [ + {'start': 0, 'middle0': 1, 'middle1': 3, 'end': 4}, + {'start': 2, 'middle0': 3, 'middle1': 4, 'end': 6}, + ] + result = to_dict(response_time.to_worst_in_input_case_stacked_bar()) + assert result == expect_raw