<commit_before>import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { Dictionary } from '../../../../models/json/dictionary';
import { AppState } from '../../../../root-store/app.reducers';
import { HeatmapComponent } from '../../heatmap/heatmap.component';
import { DEFAULT_OPTIONS, HeatBatchData, HeatCellData, HeatColor, HeatmapOptions } from '../../heatmap/heatmap.data';
import { TacticsControlService } from '../tactics-control.service';
import { TacticsTooltipService } from '../tactics-tooltip/tactics-tooltip.service';
import { TacticsView } from '../tactics-view';
import { Tactic, TacticChain } from '../tactics.model';

/**
 * @description Common data that can be found on attack patterns displayed in a heatmap cell.
 */
export interface AttackPatternCell extends HeatCellData, Tactic {
    value: string,
    values?: Array<{ name: string, color: string }>,
    text?: string, // the foreground, or text, color of an attack pattern cell
}

@Component({
    selector: 'tactics-heatmap',
    templateUrl: './tactics-heatmap.component.html',
    styleUrls: ['./tactics-heatmap.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TacticsHeatmapComponent extends TacticsView<HeatmapComponent, HeatmapOptions> {

    /**
     * @description 
     */
    public data: HeatBatchData[] = [];

    @ViewChild(HeatmapComponent) private heatmap: HeatmapComponent;

    private noColor: HeatColor = { bg: '#ccc', fg: 'black' };
    private readonly undefinedData = 'no-stix-data-defined';
    private readonly undefinedTactics = {
        title: 'No STIX Data Defined',
        value: this.undefinedData,
    };
    private readonly undefinedColor: HeatColor = { bg: 'transparent', fg: 'transparent' };

    /**
     * @description 
     */
    private baseHeats: Dictionary<HeatColor> = null;

    constructor(
        store: Store<AppState>,
        controls: TacticsControlService,
        tooltips: TacticsTooltipService,
    ) {
        super(store, controls, tooltips);
    }

    /**
     * @description
     */
    public get view() {
        return this.heatmap;
    }

    /**
     * @description
     */
    protected validateOptions() {
        if (!this.options) {
            this.options = {};
        }
        if (!this.options.color) {
            this.options.color = {};
        }
    }

    /**
     * @description ensure valid carousel option values
     */
    protected extractData(tactics: Dictionary<TacticChain>) {
        const heats = this.getBaseHeats();

        // convert the tactics in the TacticChains we were given into heat cells
        const data: Dictionary<HeatBatchData> = {};
        if (this.frameworks) {
            this.frameworks.reduce((aps, chain) => {
                if (tactics) {
                    this.convertTacticChain(tactics, chain, data, aps, heats);
                }
                return aps;
            }, {});
        }

        // now convert the phases in the TacticChains into heat batches
        Object.values(data).forEach(batch => batch.cells.sort((ap1, ap2) => ap1.title.localeCompare(ap2.title)));
        this.data = Object.values(data);
        requestAnimationFrame(() => {
            console['debug'](`(${new Date().toISOString()}) heatmap tactics`, this.data);
            if (this.heatmap && this.heatmap.options && this.heatmap.options.color) {
                this.heatmap.options.color.heatColors = heats;
                console['debug'](`(${new Date().toISOString()}) heatmap heats`, this.heatmap.options.color.heatColors);
            }
        });
    }

    /**
     * @description initialize the heat colors
     */
    private getBaseHeats() {
        if (!this.baseHeats) {
            if (this.options && this.options.color && this.options.color.heatColors) {
                this.baseHeats = this.options.color.heatColors;
            } else if (this.view && this.view.options && this.view.options.color) {
                this.baseHeats = this.view.options.color.heatColors;<commit_msg>Fixes intrustion set dashboard heatmap redraw<commit_after>import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import { Dictionary } from '../../../../models/json/dictionary';
import { AppState } from '../../../../root-store/app.reducers';
import { HeatmapComponent } from '../../heatmap/heatmap.component';
import { DEFAULT_OPTIONS, HeatBatchData, HeatCellData, HeatColor, HeatmapOptions } from '../../heatmap/heatmap.data';
import { TacticsControlService } from '../tactics-control.service';
import { TacticsTooltipService } from '../tactics-tooltip/tactics-tooltip.service';
import { TacticsView } from '../tactics-view';
import { Tactic, TacticChain } from '../tactics.model';

/**
 * @description Common data that can be found on attack patterns displayed in a heatmap cell.
 */
export interface AttackPatternCell extends HeatCellData, Tactic {
    value: string,
    values?: Array<{ name: string, color: string }>,
    text?: string, // the foreground, or text, color of an attack pattern cell
}

@Component({
    selector: 'tactics-heatmap',
    templateUrl: './tactics-heatmap.component.html',
    styleUrls: ['./tactics-heatmap.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TacticsHeatmapComponent extends TacticsView<HeatmapComponent, HeatmapOptions> {

    /**
     * @description 
     */
    public data: HeatBatchData[] = [];

    @ViewChild(HeatmapComponent) private heatmap: HeatmapComponent;

    private noColor: HeatColor = { bg: '#ccc', fg: 'black' };
    private readonly undefinedData = 'no-stix-data-defined';
    private readonly undefinedTactics = {
        title: 'No STIX Data Defined',
        value: this.undefinedData,
    };
    private readonly undefinedColor: HeatColor = { bg: 'transparent', fg: 'transparent' };

    /**
     * @description 
     */
    private baseHeats: Dictionary<HeatColor> = null;

    constructor(
        store: Store<AppState>,
        controls: TacticsControlService,
        tooltips: TacticsTooltipService,
    ) {
        super(store, controls, tooltips);
    }

    /**
     * @description
     */
    public get view() {
        return this.heatmap;
    }

    /**
     * @description
     */
    protected validateOptions() {
        if (!this.options) {
            this.options = {};
        }
        if (!this.options.color) {
            this.options.color = {};
        }
    }

    /**
     * @description ensure valid carousel option values
     */
    protected extractData(tactics: Dictionary<TacticChain>) {
        const heats = this.getBaseHeats();

        // convert the tactics in the TacticChains we were given into heat cells
        const data: Dictionary<HeatBatchData> = {};
        if (this.frameworks) {
            this.frameworks.reduce((aps, chain) => {
                if (tactics) {
                    this.convertTacticChain(tactics, chain, data, aps, heats);
                }
                return aps;
            }, {});
        }

        // now convert the phases in the TacticChains into heat batches
        Object.values(data).forEach(batch => batch.cells.sort((ap1, ap2) => ap1.title.localeCompare(ap2.title)));
        this.data = Object.values(data);
        requestAnimationFrame(() => {
            console['debug'](`(${new Date().toISOString()}) heatmap tactics`, this.data);
            if (this.heatmap && this.heatmap.options && this.heatmap.options.color) {
                this.heatmap.options.color.heatColors = heats;
                console['debug'](`(${new Date().toISOString()}) heatmap heats`, this.heatmap.options.color.heatColors);
                this.heatmap.redraw();
            }
        });
    }

    /**
     * @description initialize the heat colors
     */
    private getBaseHeats() {
        if (!this.baseHeats) {
            if (this.options && this.options.color && this.options.color.heatColors) {
                this.baseHeats = this.options.color.heatColors;
            } else if (this.view && this.view.options && this.view.options.color) {
                this.baseHeats = this.view.options.color.heatColors;
{'fraction_non_alphanumeric': 0.13534472313098267, 'fraction_numerical': 0.0009693444807948625, 'mean_word_length': 2.528858486532706, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3024559', 'n_tokens_mistral': 2410, 'n_tokens_neox': 2329, 'n_words': 535}