<commit_before>import React, { Component } from 'react';
import PageDescription from '../pageDescription/pageDescriptionComponent';
import ClusterAgentBreakdown from './agentBreakdown/clusterAgentBreakdown';
import { taskDefinitionStream$ } from '../../dataStreams/taskDefinitionStreams';
import { clusterStream$ } from '../../dataStreams/clusterStreams';
import moment from 'moment';

const taskDefColourCache = {};

function stamp() {
    return moment().format('dddd Do MMMM YYYY, h:mm:ss a');
}

function stringToColour(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
        hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    
    let colour = '#';
    for (let i = 0; i < 3; i++) {
        const value = (hash >> (i * 8)) & 0xFF;
        colour += ('00' + value.toString(16)).substr(-2);
    }
    return colour;
}

function assignColoursToTaskDefinitions(arns) {
    for (let i=0; i < arns.length; i++) {
        const arn = arns[i];
        if (taskDefColourCache[arn]) {
            continue;
        }
        taskDefColourCache[arn] = stringToColour(arn);
    }
    
    return taskDefColourCache;
}

class ClusterDashboard extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            lastUpdateStamp: stamp(),
            clusters: []
        };
        this.updateClusterState = this.updateClusterState.bind(this);
    }

    updateClusterState(newState) {
        this.setState({
            clusters: newState,
            lastUpdateStamp: stamp()
        })
    }

    componentWillMount() {
        this.taskDefinitionObserver = 
            taskDefinitionStream$.subscribe(assignColoursToTaskDefinitions);
        
        this.clusterObserver = clusterStream$.subscribe(this.updateClusterState);
    }

    componentWillUnmount() {
        this.taskDefinitionObserver.unsubscribe();
    }

    renderClusterAgentBreakdown(cluster) {
        return (
            <ClusterAgentBreakdown
                key={`breakdown::${cluster.clusterName}`}
                clusterName={cluster.clusterName} 
                agentCount={cluster.registeredContainerInstancesCount}
                runningTasksCount={cluster.runningTasksCount + cluster.pendingTasksCount}
                taskDefinitionColours={taskDefColourCache} />
        );
    }

    render() {
        const clusterBreakdownComponents = this.state.clusters.map(this.renderClusterAgentBreakdown);
        return (
            <div className="ec2-summary">
                <PageDescription header={`${this.state.clusters.length} monitored clusters`} lastUpdateStamp={this.state.lastUpdateStamp} />
                
                {clusterBreakdownComponents}
            </div>
        );
    }
}

export default ClusterDashboard;<commit_msg>Couple of things: a bug fix - A missing unsubscribe in the agent dashboard to the cluster stream was causing state changes on unmounted components. Next up is a feature - On the agents screen the clusters are ordered by running task count, the cluster with the highest running count will appear at the top. This is primarily to stop empty clusters from preceding active clusters<commit_after>import React, { Component } from 'react';
import PageDescription from '../pageDescription/pageDescriptionComponent';
import ClusterAgentBreakdown from './agentBreakdown/clusterAgentBreakdown';
import { taskDefinitionStream$ } from '../../dataStreams/taskDefinitionStreams';
import { clusterStream$ } from '../../dataStreams/clusterStreams';
import moment from 'moment';

const taskDefColourCache = {};

function stamp() {
    return moment().format('dddd Do MMMM YYYY, h:mm:ss a');
}

function stringToColour(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
        hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    
    let colour = '#';
    for (let i = 0; i < 3; i++) {
        const value = (hash >> (i * 8)) & 0xFF;
        colour += ('00' + value.toString(16)).substr(-2);
    }
    return colour;
}

function assignColoursToTaskDefinitions(arns) {
    for (let i=0; i < arns.length; i++) {
        const arn = arns[i];
        if (taskDefColourCache[arn]) {
            continue;
        }
        taskDefColourCache[arn] = stringToColour(arn);
    }
    
    return taskDefColourCache;
}

function byHighestTaskCountDesc(clusterA, clusterB) {
    return clusterA.runningTasksCount < clusterB.runningTasksCount;
}

class ClusterDashboard extends Component {
    constructor(props) {
        super(props);
        this.state = { 
            lastUpdateStamp: stamp(),
            clusters: []
        };
        this.updateClusterState = this.updateClusterState.bind(this);
    }

    updateClusterState(newState) {
        const clusters = newState.sort(byHighestTaskCountDesc);
        this.setState({
            clusters: clusters,
            lastUpdateStamp: stamp()
        })
    }

    componentWillMount() {
        this.taskDefinitionObserver = 
            taskDefinitionStream$.subscribe(assignColoursToTaskDefinitions);
        
        this.clusterObserver = clusterStream$.subscribe(this.updateClusterState);
    }

    componentWillUnmount() {
        this.taskDefinitionObserver.unsubscribe();
        this.clusterObserver.unsubscribe();
    }

    renderClusterAgentBreakdown(cluster) {
        return (
            <ClusterAgentBreakdown
                key={`breakdown::${cluster.clusterName}`}
                clusterName={cluster.clusterName} 
                agentCount={cluster.registeredContainerInstancesCount}
                runningTasksCount={cluster.runningTasksCount + cluster.pendingTasksCount}
                taskDefinitionColours={taskDefColourCache} />
        );
    }

    render() {
        const clusterBreakdownComponents = this.state.clusters.map(this.renderClusterAgentBreakdown);
        return (
            <div className="ec2-summary">
                <PageDescription header={`${this.state.clusters.length} monitored clusters`} lastUpdateStamp={this.state.lastUpdateStamp} />
                
                {clusterBreakdownComponents}
            </div>
        );
    }
}

export default ClusterDashboard;
{'fraction_non_alphanumeric': 0.11037277147487845, 'fraction_numerical': 0.00453808752025932, 'mean_word_length': 2.600350058343057, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 15, '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': '5404827', 'n_tokens_mistral': 1709, 'n_tokens_neox': 1654, 'n_words': 356}