def _matrix_find_majorities(self, rows, cells, for_header=False): counts = {} # dict row_id -> cell_nr -> value -> count for row in rows: if for_header: rid = None else: rid = tuple(group_value(row, [cells[0]])) for cell_nr, cell in enumerate(cells[1:]): value = group_value(row, [cell]) row_entry = counts.setdefault(rid, {}) cell_entry = row_entry.setdefault(cell_nr, {}) cell_entry.setdefault(value, 0) cell_entry[value] += 1 # Now find majorities for each row majorities = {} # row_id -> cell_nr -> majority value for rid, row_entry in counts.items(): maj_entry = majorities.setdefault(rid, {}) for cell_nr, cell_entry in row_entry.items(): maj_value = None max_non_unique = 0 # maximum count, but maybe non unique for value, count in cell_entry.items(): if count > max_non_unique and count >= 2: maj_value = value max_non_unique = count elif count == max_non_unique: maj_value = None maj_entry[cell_nr] = maj_value return counts, majorities
def create_matrices(rows, group_cells, cells, num_columns): """Create list of matrices to render for the view layout and for reports""" if len(cells) < 2: raise MKGeneralException( _("Cannot display this view in matrix layout. You need at least two columns!" )) if not group_cells: raise MKGeneralException( _("Cannot display this view in matrix layout. You need at least one group column!" )) # First find the groups - all rows that have the same values for # all group columns. Usually these should correspond with the hosts # in the matrix groups: List[Tuple[Any, Any]] = [] last_group_id = None # not a set, but a list. Need to keep sort order! unique_row_ids: List[Any] = [] # Dict from row_id -> group_id -> row matrix_cells: Dict[Any, Dict[Any, Any]] = {} col_num = 0 for row in rows: group_id = group_value(row, group_cells) if group_id != last_group_id: col_num += 1 if num_columns is not None and col_num > num_columns: yield (groups, unique_row_ids, matrix_cells) groups = [] unique_row_ids = [ ] # not a set, but a list. Need to keep sort order! matrix_cells = {} # Dict from row_id -> group_id -> row col_num = 1 last_group_id = group_id groups.append((group_id, row)) # Now the rule is that the *first* cell (usually the service # description) will define the left legend of the matrix. It defines # the set of possible rows. rid = group_value(row, [cells[0]]) if rid not in matrix_cells: unique_row_ids.append(rid) matrix_cells[rid] = {} matrix_cells[rid][group_id] = row if col_num: yield (groups, unique_row_ids, matrix_cells)
def calculate_view_grouping_of_services(rows, row_group_cells): if not config.service_view_grouping: return {}, rows # First create dictionaries for each found group containing the # group spec and the row indizes of the grouped rows groups = {} current_group = None group_id = None last_row_group = None for index, (rid, row) in enumerate(rows[:]): group_spec = try_to_match_group(row) if not group_spec: current_group = None continue # New row groups need to separate the view groups. There is no folding allowed # between row groups (e.g. services of different hosts when the host is a group cell) if row_group_cells: this_row_group = group_value(row, row_group_cells) if this_row_group != last_row_group: group_id = rid last_row_group = this_row_group if current_group is None: group_id = rid elif current_group != group_spec: group_id = rid groups.setdefault(group_id, (group_spec, [])) # When the service is not OK and should not be grouped, move it's row # in front of the group. if row.get("service_state", -1) != 0 or is_stale(row): if current_group is None or current_group != group_spec: continue # skip grouping first row elif current_group == group_spec: row = rows.pop(index) rows.insert(index - len(groups[group_id][1]), row) continue current_group = group_spec groups[group_id][1].append(rid) # Now create the final structure as described above groupings = {} for group_id, (group_spec, row_indizes) in groups.items(): if len(row_indizes) >= group_spec.get("min_items", 2): groupings[row_indizes[0]] = group_spec, len(row_indizes) return groupings, rows
def _matrix_find_majorities(self, rows, cells, for_header=False): # dict row_id -> cell_nr -> value -> count counts: Dict[Any, Dict[Any, Any]] = {} for row in rows: if for_header: rid: Optional[Tuple] = None else: # TODO: WTF??? rid = tuple(group_value(row, [cells[0]])) # type: ignore[arg-type] for cell_nr, cell in enumerate(cells[1:]): value = group_value(row, [cell]) row_entry = counts.setdefault(rid, {}) cell_entry = row_entry.setdefault(cell_nr, {}) cell_entry.setdefault(value, 0) cell_entry[value] += 1 # Now find majorities for each row # row_id -> cell_nr -> majority value majorities: Dict[Any, Dict[Any, Any]] = {} for rid, row_entry in counts.items(): maj_entry = majorities.setdefault(rid, {}) for cell_nr, cell_entry in row_entry.items(): maj_value = None max_non_unique = 0 # maximum count, but maybe non unique for value, count in cell_entry.items(): if count > max_non_unique and count >= 2: maj_value = value max_non_unique = count elif count == max_non_unique: maj_value = None maj_entry[cell_nr] = maj_value return counts, majorities
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): # N columns. Each should contain approx the same number of entries groups = [] last_group = None for row in rows: this_group = group_value(row, group_cells) if this_group != last_group: last_group = this_group current_group = [] groups.append((this_group, current_group)) current_group.append((row_id(view, row), row)) # Create empty columns columns = [] for _x in range(num_columns): columns.append([]) # First put everything into the first column for group in groups: columns[0].append(group) # Shift from left to right as long as useful did_something = True while did_something: did_something = False for i in range(0, num_columns - 1): if self._balance(columns[i], columns[i + 1]): did_something = True # render table html.open_table(class_=["boxlayout", self._css_class()]) html.open_tr() for column in columns: html.open_td(class_="boxcolumn") for header, rows_with_ids in column: self._render_group(rows_with_ids, header, view, group_cells, cells, num_columns, show_checkboxes) html.close_td() html.close_tr() html.close_table()
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): header_majorities = self._matrix_find_majorities_for_header( rows, group_cells) value_counts, row_majorities = self._matrix_find_majorities( rows, cells) painter_options = PainterOptions.get_instance() for groups, unique_row_ids, matrix_cells in \ create_matrices(rows, group_cells, cells, num_columns): # Paint the matrix. Begin with the group headers html.open_table(class_="data matrix") odd = "odd" for cell_nr, cell in enumerate(group_cells): odd = "even" if odd == "odd" else "odd" html.open_tr(class_="data %s0" % odd) html.open_td(class_="matrixhead") html.write(cell.title(use_short=False)) html.close_td() for _group, group_row in groups: tdclass, content = cell.render(group_row) if cell_nr > 0: gv = group_value(group_row, [cell]) majority_value = header_majorities.get( cell_nr - 1, None) if majority_value is not None and majority_value != gv: tdclass += " minority" html.open_td(class_=["left", tdclass]) html.write(content) html.close_td() html.close_tr() # Now for each unique service^H^H^H^H^H^H ID column paint one row for rid in unique_row_ids: # Omit rows where all cells have the same values if painter_options.get("matrix_omit_uniform"): at_least_one_different = False for counts in value_counts[rid].values(): if len(counts) > 1: at_least_one_different = True break if not at_least_one_different: continue odd = "even" if odd == "odd" else "odd" html.open_tr(class_="data %s0" % odd) tdclass, content = cells[0].render( list(matrix_cells[rid].values())[0]) html.open_td(class_=["left", tdclass]) html.write(content) html.close_td() # Now go through the groups and paint the rest of the # columns for group_id, group_row in groups: cell_row = matrix_cells[rid].get(group_id) if cell_row is None: html.td('') else: if len(cells) > 2: html.open_td(class_="cell") html.open_table() for cell_nr, cell in enumerate(cells[1:]): tdclass, content = cell.render(cell_row) gv = group_value(cell_row, [cell]) majority_value = row_majorities[rid].get( cell_nr, None) if majority_value is not None and majority_value != gv: tdclass += " minority" if len(cells) > 2: html.open_tr() html.open_td(class_=tdclass) html.write(content) html.close_td() if len(cells) > 2: html.close_tr() if len(cells) > 2: html.close_table() html.close_td() html.close_tr() html.close_table()
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): repeat_heading_every = 20 # in case column_headers is "repeat" html.open_table(class_='data table') last_group = None odd = "odd" column = 1 group_open = False num_cells = len(cells) if show_checkboxes: num_cells += 1 if not group_cells and view.get("column_headers") != "off": self._show_header_line(cells, num_columns, show_checkboxes) rows_with_ids = [(row_id(view, row), row) for row in rows] groups, rows_with_ids = calculate_view_grouping_of_services( rows_with_ids, row_group_cells=group_cells) visible_row_number = 0 group_hidden, num_grouped_rows = None, 0 for index, row in rows_with_ids: # Show group header, if a new group begins. But only if grouping # is activated if group_cells: this_group = group_value(row, group_cells) if this_group != last_group: if column != 1: # not a the beginning of a new line for _i in range(column - 1, num_columns): html.td('', class_="gap") html.td('', class_="fillup", colspan=num_cells) html.close_tr() column = 1 group_open = True visible_row_number = 0 # paint group header, but only if it is non-empty header_is_empty = True for cell in group_cells: _tdclass, content = cell.render(row) if content: header_is_empty = False break if not header_is_empty: html.open_tr(class_="groupheader") html.open_td(class_="groupheader", colspan=(num_cells * (num_columns + 2) + (num_columns - 1))) html.open_table(class_="groupheader", cellspacing=0, cellpadding=0, border=0) html.open_tr() painted = False for cell in group_cells: if painted: html.td(', ') painted = cell.paint(row) html.close_tr() html.close_table() html.close_td() html.close_tr() # Table headers if view.get("column_headers") != "off": self._show_header_line(cells, num_columns, show_checkboxes) last_group = this_group # Should we wrap over to a new line? if column >= num_columns + 1: html.close_tr() column = 1 # At the beginning of the line? Beginn new line if column == 1: if view.get("column_headers") == "repeat": if visible_row_number > 0 and visible_row_number % repeat_heading_every == 0: self._show_header_line(cells, num_columns, show_checkboxes) visible_row_number += 1 # In one-column layout we use the state of the service # or host - if available - to color the complete line if num_columns == 1: # render state, if available through whole tr if not row.get('service_description'): state = row.get("host_state", 0) if state > 0: state += 1 # 1 is critical for hosts else: state = row.get("service_state", 0) else: state = 0 if index in groups: group_spec, num_grouped_rows = groups[index] group_hidden = grouped_row_title(index, group_spec, num_grouped_rows, odd, num_cells) odd = "even" if odd == "odd" else "odd" css_classes = [] hide = "" if num_grouped_rows > 0: num_grouped_rows -= 1 if group_hidden: hide = "display:none" if group_hidden is not None and num_grouped_rows == 0: # last row in group css_classes.append("group_end") group_hidden = None odd = "even" if odd == "odd" else "odd" if num_columns > 1: css_classes.append("multicolumn") css_classes += ["%s%d" % (odd, state)] html.open_tr(class_=["data"] + css_classes, style=hide) # Not first columns: Create one empty column as separator else: html.open_td(class_="gap") html.close_td() if show_checkboxes: render_checkbox_td(view, row, num_cells) last_cell = cells[-1] for cell in cells: cell.paint(row, is_last_cell=last_cell == cell) column += 1 if group_open: for _i in range(column - 1, num_columns): html.td('', class_="gap") html.td('', class_="fillup", colspan=num_cells) html.close_tr() html.close_table() init_rowselect(view)
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): html.open_table(class_="data tiled") last_group = None group_open = False for row in rows: # Show group header if group_cells: this_group = group_value(row, group_cells) if this_group != last_group: # paint group header if group_open: html.close_td() html.close_tr() html.open_tr() html.open_td() html.open_table(class_="groupheader") html.open_tr(class_="groupheader") painted = False for cell in group_cells: if painted: html.td(', ') painted = cell.paint(row) html.close_tr() html.close_table() html.close_td() html.close_tr() html.open_tr() html.open_td(class_="tiles") group_open = True last_group = this_group # background color of tile according to item state state = row.get("service_state", -1) if state == -1: hbc = row.get("host_has_been_checked", 1) if hbc: state = row.get("host_state", 0) sclass = "hhstate%d" % state else: sclass = "hhstatep" else: hbc = row.get("service_has_been_checked", 1) if hbc: sclass = "sstate%d" % state else: sclass = "sstatep" if not group_open: html.open_tr() html.open_td(class_="tiles") group_open = True html.open_div(class_=["tile", sclass]) html.open_table() # We need at least five cells if len(cells) < 5: cells = cells + ([EmptyCell(view)] * (5 - len(cells))) rendered = [cell.render(row) for cell in cells] html.open_tr() html.open_td(class_=["tl", rendered[1][0]]) if show_checkboxes: render_checkbox(view, row, len(cells) - 1) html.write("%s" % rendered[1][1]) html.close_td() html.open_td(class_=["tr", rendered[2][0]]) html.write("%s" % rendered[2][1]) html.close_td() html.close_tr() html.open_tr() html.open_td(colspan=2, class_=["center", rendered[0][0]]) html.write("%s" % rendered[0][1]) html.close_td() html.close_tr() for css, cont in rendered[5:]: html.open_tr() html.open_td(colspan=2, class_=["cont", css]) html.write("%s" % cont) html.close_td() html.close_tr() html.open_tr() html.open_td(class_=["bl", rendered[3][0]]) html.write("%s" % rendered[3][1]) html.close_td() html.open_td(class_=["br", rendered[4][0]]) html.write("%s" % rendered[4][1]) html.close_td() html.close_tr() html.close_table() html.close_div() if group_open: html.close_td() html.close_tr() html.close_table() init_rowselect(view)