# JUBE Benchmarking Environment
# Copyright (C) 2008-2024
# Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre
# http://www.fz-juelich.de/jsc/jube
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""KeyValuesResulttype definition"""
from __future__ import (print_function,
unicode_literals,
division)
from jube2.result import Result
import jube2.log
import xml.etree.ElementTree as ET
import operator
import jube2.util.util
import jube2.util.output
LOGGER = jube2.log.get_logger(__name__)
[docs]class KeyValuesResult(Result):
"""A generic key value result type"""
[docs] class KeyValuesData(Result.ResultData):
"""Key value data"""
def __init__(self, other_or_name):
if type(other_or_name) is str:
Result.ResultData.__init__(self, other_or_name)
elif type(other_or_name) is Result.ResultData:
self._name = other_or_name.name
self._data = list()
self._keys = list()
self._benchmark_ids = list()
@property
def keys(self):
"""Return keys"""
return self._keys
@property
def data(self):
"""Return table data"""
return self._data
@property
def data_dict(self):
"""Return unordered dictionary representation of data"""
result_dict = dict()
for i, key in enumerate(self._keys):
result_dict[key] = list()
for data in self._data:
result_dict[key].append(data[i])
return result_dict
@property
def benchmark_ids(self):
"""Return benchmark ids"""
return self._benchmark_ids
[docs] def add_key_value_data(self, keys, data, benchmark_ids):
"""Add a list of additional rows to current result data"""
order = list()
last_index = len(self._keys)
# Find matching rows
for key in keys:
if key in self._keys:
index = self._keys.index(key)
# Check weather key occurs multiple times
while index in order:
try:
index = self._keys.index(key, index + 1)
except ValueError:
index = len(self._keys)
self._keys.append(key)
else:
index = len(self._keys)
self._keys.append(key)
order.append(index)
# Fill up existing rows
if last_index != len(self._keys):
for row in self._data:
row += ["" for key in self._keys[last_index:]]
# Add new rows
for row in data:
new_row = ["" for key in self._keys]
for i, index in enumerate(order):
new_row[index] = row[i]
self._data.append(new_row)
if type(benchmark_ids) is int:
self._benchmark_ids.append(benchmark_ids)
if type(benchmark_ids) is list:
self._benchmark_ids += benchmark_ids
[docs] def add_result_data(self, result_data):
"""Add additional result data"""
if self.name != result_data.name:
raise RuntimeError("Cannot combine to different result sets.")
self.add_key_value_data(result_data.keys, result_data.data,
result_data.benchmark_ids)
[docs] def create_result(self, show=True, filename=None, **kwargs):
"""Create result representation"""
raise NotImplementedError("")
[docs] class DataKey(object):
"""Class represents one data key """
def __init__(self, name, title=None, format_string=None, unit=None):
self._name = name
self._title = title
self._format_string = format_string
self._unit = unit
@property
def title(self):
"""Key title"""
return self._title
@property
def name(self):
"""Key name"""
return self._name
@property
def format(self):
"""Key data format"""
return self._format_string
@property
def unit(self):
"""Key data unit"""
return self._unit
@unit.setter
def unit(self, unit):
"""Set key data unit"""
self._unit = unit
@property
def resulting_name(self):
"""Column name based on name, title and unit"""
if self._title is not None:
name = self._title
else:
name = self._name
if self._unit is not None:
name += "[{0}]".format(self._unit)
return name
[docs] def etree_repr(self):
"""Return etree object representation"""
key_etree = ET.Element("key")
key_etree.text = self._name
if self._format_string is not None:
key_etree.attrib["format"] = self._format_string
if self._title is not None:
key_etree.attrib["title"] = self._title
return key_etree
def __eq__(self, other):
return self.resulting_name == other.resulting_name
def __hash__(self):
return hash(self.resulting_name)
def __init__(self, name, sort_names=None, res_filter=None):
Result.__init__(self, name, res_filter)
self._keys = list()
if sort_names is None:
self._sort_names = list()
else:
self._sort_names = sort_names
[docs] def add_key(self, name, format_string=None, title=None, unit=None):
"""Add an additional key to the dataset"""
self._keys.append(KeyValuesResult.DataKey(name, title, format_string,
unit))
[docs] def create_result_data(self, select=None, exclude=None):
"""Create result data"""
result_data = KeyValuesResult.KeyValuesData(self._name)
if exclude is None:
exclude = []
if select is None:
select = [key.name for key in self._keys]
else:
# Check whether the same column name appears in select and exclude
if set(select) & set(exclude):
LOGGER.error("Error when checking the select and exclude names: "
"A pattern or parameter name occurs in both select "
"and exclude")
exit()
# Read pattern/parameter units if available
units = self._load_units([key.name for key in self._keys])
for key in self._keys:
if key.name in units:
key.unit = units[key.name]
sort_data = list()
for dataset in self._analyse_data():
# Add additional data if needed
for sort_name in self._sort_names:
if sort_name not in dataset:
dataset[sort_name] = None
sort_data.append(dataset)
# Sort the resultset
if len(self._sort_names) > 0:
LOGGER.debug("sort using: {0}".format(",".join(self._sort_names)))
# Use CompType for sorting to allow comparison of None values
sort_data = \
sorted(sort_data,
key=lambda x:
[jube2.util.util.CompType(x[sort_name])
for sort_name in self._sort_names])
# Check for correctness of exclude and select names
key_names = [key.name for key in self._keys]
# Help lists for multiple columns
unique_select = []
multiple_select = []
for select_name in select:
# Check if given names exist in keys
if select_name not in key_names:
LOGGER.warning("The result table does not contain a pattern "
"or parameter with the name '{0}'. This "
"name will be ignored for selection."
.format(select_name))
# Check whether the given name occurs only once
if select_name not in unique_select:
unique_select.append(select_name)
elif select_name not in multiple_select:
multiple_select.append(select_name)
LOGGER.warning("The pattern or parameter name {} occurs more "
"than once. These additional occurrences are "
"ignored for selection.".format(select_name))
# Help lists for multiple columns
unique_exclude = []
multiple_exclude = []
for exclude_name in exclude:
# Check if given names exist in keys
if exclude_name not in key_names:
LOGGER.warning("The result table does not contain a pattern "
"or parameter with the name '{0}'. This "
"name will be ignored for exclusion."
.format(exclude_name))
# Check whether the given name occurs only once
if exclude_name not in unique_exclude:
unique_exclude.append(exclude_name)
elif exclude_name not in multiple_exclude:
multiple_exclude.append(exclude_name)
LOGGER.warning("The pattern or parameter name {} occurs more "
"than once. These additional occurrences are "
"ignored for exclusion.".format(exclude_name))
# Select and exclude table columns
self._keys = [key for key in self._keys if key.name in select and \
key.name not in exclude]
# Create table data
table_data = list()
for dataset in sort_data:
row = list()
cnt = 0
for key in self._keys:
if key.name in dataset:
# Cnt number of final entries to avoid complete empty
# result entries
cnt += 1
# Set null value
if dataset[key.name] is None:
value = ""
else:
# Format data values to create string representation
if key.format is not None:
value = jube2.util.output.format_value(
key.format, dataset[key.name])
else:
value = str(dataset[key.name])
row.append(value)
else:
row.append(None)
if cnt > 0:
table_data.append(row)
# Add data to toe result set
result_data.add_key_value_data(self._keys, table_data,
self._benchmark.id)
return result_data