<commit_before>app.directive('prvisualization', function() {
    return {
        controller: 'PRVisualizationController',
        restrict: 'E',
        replace: true,
        templateUrl: 'static/ng-app/directives/pr_visualization.html',
        scope: {
            data: '='
        }
    };
});

app.controller(
    'PRVisualizationController',
    [
        '$scope',
        '$q',
        '$modal',
        'ModelService',
        function ($scope, $q, $modal, ModelService) {
            var sample_clusters = null;
            var panel_data = null;

            // Cell subset label variables for tagging clusters
            if (ModelService.current_project) {
                $scope.labels = ModelService.getCellSubsetLabels(
                    ModelService.current_project.id
                );
            }
            $scope.$on('current_project:updated', function () {
                $scope.labels = ModelService.getCellSubsetLabels(
                    ModelService.current_project.id
                );
            });

            $scope.$watch('data', function(data) {
                // first sort data.members by the sample's original_filename
                if (data != undefined && data.hasOwnProperty("members")) {
                    data.members.sort(function (a, b) {
                        if (a.sample.original_filename < b.sample.original_filename) {
                            return -1;
                        } else if (a.sample.original_filename > b.sample.original_filename) {
                            return 1;
                        } else {
                            return 0;
                        }
                    });
                }

                $scope.sample_collection = data;
                $scope.cached_plots = {};
            });

            function get_analyzed_parameters(parameters, example_cluster) {
                /*
                 Determine analyzed parameters, as not all parameters
                 may have been analyzed. We only need to copy the
                 'fcs_number' and the 'full_text' properties. This is
                 used for the parallel plot and for the initial selection
                 of scatterplot x & y parameters

                 Arguments:
                    parameters: current list of site panel parameter objects
                    example_cluster: an example cluster that will contain
                                     only the analyzed channel numbers
                 */
                var analyzed_parameters = [];
                parameters.forEach(function (param) {
                    // use the cluster params for comparison
                    for (var i = 0; i < example_cluster.parameters.length; i++) {
                        if (param.fcs_number == example_cluster.parameters[i].channel) {
                            analyzed_parameters.push(param);
                            break;
                        }
                    }
                });

                return analyzed_parameters;
            }

            $scope.initialize_visualization = function() {
                // May be "re-initializing" the vis so remember any expanded
                // clusters so we can "re-expand" them in the new sample
                var expanded_cluster_indices = [];
                if ($scope.hasOwnProperty('plot_data')) {
                    $scope.plot_data.cluster_data.forEach(function (c) {
                        if (c.display_events) {
                            expanded_cluster_indices.push(c.cluster_index);
                        }
                    });
                }

                if ($scope.chosen_member.id in $scope.cached_plots) {
                    $scope.plot_data = $scope.cached_plots[$scope.chosen_member.id];

                    $scope.analyzed_parameters = get_analyzed_parameters(
                        $scope.plot_data.panel_data.parameters,
                        $scope.plot_data.cluster_data[0]
                    );

                    // Need to init the parallel plot first so we can
                    // "bold" the expanded clusters
                    $scope.initialize_parallel_plot(
                        $scope.analyzed_parameters,
                        $scope.plot_data.cluster_data
                    );

                    // turn on display_events for originally expanded clusters
                    $scope.plot_data.cluster_data.forEach(function(c) {
                        if (expanded_cluster_indices.indexOf(c.cluster_index) != -1) {
                            c.display_events = true;
                            $scope.select_cluster_line(c);

                            if (c.events_retrieved == false) {
                                $scope.process_cluster_events(c);
                            }
                        } else {
                            c.display_events = false;
                        }
                    });

                    $scope.initialize_scatterplot(true);

                    return;
                }

                $scope.retrieving_data = true;
                $scope.has_parent_stage = $scope.$parent.process_request.parent_stage > 0;
                sample_clusters = ModelService.getSampleClusters(
                    $scope.$parent.process_request.id,
                    $scope.chosen_member.sample.id
                );

                panel_data = ModelService.getSitePanel(
                    $scope.chosen_member.sample.site_panel
                );

                $q.all([sample_clusters, panel_data.$promise]).then(function(data) {
                    $scope.cached_plots[$scope.chosen_member.id] = {
                        'cluster_data': data[0],
                        'panel_data': data[1]
                    };
                    $scope.plot_data = $scope.cached_plots[$scope.chosen_member.id];

                    $scope.analyzed_parameters = get_analyzed_parameters(
                        $scope.plot_data.panel_data.parameters,
                        $scope.plot_data.cluster_data[0]
                    );

                    $scope.initialize_scatterplot(false);
                    $scope.initialize_parallel_plot(
                        $scope.analyzed_parameters,
                        $scope.plot_data.cluster_data
                    );

                    // display events for previously expanded clusters
                    $scope.plot_data.cluster_data.forEach(function(c) {
                        // since the sample is new we can just toggle it
                        if (expanded_cluster_indices.indexOf(c.cluster_index) != -1) {
                            $scope.toggle_cluster_events(c);
                            $scope.select_cluster_line(c);
                        }
                    });
                }).catch(function(e) {
                    // show errors here
                    console.log('error!')
                }).finally(function() {
                    $scope.retrieving_data = false;
                });
            };

            $scope.previous_sample = function() {
                var current_index = $scope.sample_collection.members.indexOf(
                    $scope.chosen_member
                );

                if (current_index === 0) {
                    return;
                } else {
                    $scope.chosen_member = $scope.sample_collection.members[current_index-1];
                    $scope.initialize_visualization();
                }
            };

            $scope.next_sample = function() {
                var current_index = $scope.sample_collection.members.indexOf(
                    $scope.chosen_member
                );

                if (current_index === $scope.sample_collection.members.length - 1) {
                    return;
                } else {
                    $scope.chosen_member = $scope.sample_collection.members[current_index+1];
                    $scope.initialize_visualization();
                }
            };

            $scope.find_matching_parameter = function(param) {
                if (param == null) {
                    return null;
                }
                for (var i = 0; i < $scope.parameters.length; i++) {
                    if (param.full_name === $scope.parameters[i].full_name) {
                        return $scope.parameters[i];
                    }
                }
                return null;
            };

            $scope.parameter_changed = function() {
                $scope.parameter_changed_flag = true;

                if ($scope.auto_transition) {
                    $scope.render_plot();
                }
            };

            /*
            Note: Difference between highlighted, selected, & expanded

            There are 3 different boolean states for each cluster, all of which
            control the look or behaviour of the cluster.

            highlighted:
                Only one of the clusters can be highlighted at any single time,
                and is meant to help identify the cluster in the various
                components (parallel plot, scatter plot, and the data table).
                A cluster is highlighted when the user's mouse cursor hovers
                over either the cluster circle in the scatter-plot OR the
                table row for that cluster in the data table on the right hand
                side of the page. When highlighted the cluster circle is
                larger, the cluster circle is annotated with the cluster index
                & event percentage, the parallel plot line is bolder, and the
                table row background is grey. Highlighting when hovering over
                a cluster circle only works when brush mode is disabled.
            selected:
                Any number of clusters (including zero) can be selected at a
                single time. The only way clusters can be selected is using the
                rectangular brush when the brush mode is enabled. Brush mode
                is controlled by a check-box found above the scatter-plot.
                Cluster selection is intended to be used in conjunction with
                the "expand selected" button and then with the "label expanded"
                button to label multiple clusters at once. The selected state
                looks exactly like the highlighted state (perhaps this should
                be changed?)
            expanded:
                A cluster is expanded when the events for that cluster are
                visible. To expand a cluster the user can either click on the
                cluster circle (when brush mode is disabled) or click the
                check-box in that cluster's row of the data table.
             */

            $scope.highlight_cluster = function (cluster) {
                cluster.highlighted = true;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", $scope.cluster_radius_lg);

                $scope.select_cluster_line(cluster);
            };

            $scope.dehighlight_cluster = function (cluster) {
                cluster.highlighted = false;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", function(o) {
                        return o.selected ? $scope.cluster_radius_lg : $scope.cluster_radius;
                    }
                );

                if (!cluster.selected) {
                    $scope.deselect_cluster_line(cluster);
                }
            };

            $scope.select_cluster = function (cluster) {
                cluster.selected = true;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", $scope.cluster_radius_lg);

                $scope.select_cluster_line(cluster);
            };

            $scope.deselect_cluster = function (cluster) {
                cluster.selected = false;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", $scope.cluster_radius);

                $scope.deselect_cluster_line(cluster);
            };

            $scope.expand_cluster = function (cluster) {
                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    })
                    .style("stroke-dasharray", "4, 1")
                    .style("stroke-linecap", "butt")
                    .style("stroke-width", "5");
            };

            $scope.collapse_cluster = function (cluster) {
                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    })
                    .style("stroke-dasharray", "1, 0")
                    .style("stroke-linecap", "butt")
                    .style("stroke-width", "1");
            };

            function update_cluster_labels(sample_cluster) {<commit_msg>convert highlighted & selected functions to use the weight, uhh, weighted cluster radius method<commit_after>app.directive('prvisualization', function() {
    return {
        controller: 'PRVisualizationController',
        restrict: 'E',
        replace: true,
        templateUrl: 'static/ng-app/directives/pr_visualization.html',
        scope: {
            data: '='
        }
    };
});

app.controller(
    'PRVisualizationController',
    [
        '$scope',
        '$q',
        '$modal',
        'ModelService',
        function ($scope, $q, $modal, ModelService) {
            var sample_clusters = null;
            var panel_data = null;

            // Cell subset label variables for tagging clusters
            if (ModelService.current_project) {
                $scope.labels = ModelService.getCellSubsetLabels(
                    ModelService.current_project.id
                );
            }
            $scope.$on('current_project:updated', function () {
                $scope.labels = ModelService.getCellSubsetLabels(
                    ModelService.current_project.id
                );
            });

            $scope.$watch('data', function(data) {
                // first sort data.members by the sample's original_filename
                if (data != undefined && data.hasOwnProperty("members")) {
                    data.members.sort(function (a, b) {
                        if (a.sample.original_filename < b.sample.original_filename) {
                            return -1;
                        } else if (a.sample.original_filename > b.sample.original_filename) {
                            return 1;
                        } else {
                            return 0;
                        }
                    });
                }

                $scope.sample_collection = data;
                $scope.cached_plots = {};
            });

            function get_analyzed_parameters(parameters, example_cluster) {
                /*
                 Determine analyzed parameters, as not all parameters
                 may have been analyzed. We only need to copy the
                 'fcs_number' and the 'full_text' properties. This is
                 used for the parallel plot and for the initial selection
                 of scatterplot x & y parameters

                 Arguments:
                    parameters: current list of site panel parameter objects
                    example_cluster: an example cluster that will contain
                                     only the analyzed channel numbers
                 */
                var analyzed_parameters = [];
                parameters.forEach(function (param) {
                    // use the cluster params for comparison
                    for (var i = 0; i < example_cluster.parameters.length; i++) {
                        if (param.fcs_number == example_cluster.parameters[i].channel) {
                            analyzed_parameters.push(param);
                            break;
                        }
                    }
                });

                return analyzed_parameters;
            }

            $scope.initialize_visualization = function() {
                // May be "re-initializing" the vis so remember any expanded
                // clusters so we can "re-expand" them in the new sample
                var expanded_cluster_indices = [];
                if ($scope.hasOwnProperty('plot_data')) {
                    $scope.plot_data.cluster_data.forEach(function (c) {
                        if (c.display_events) {
                            expanded_cluster_indices.push(c.cluster_index);
                        }
                    });
                }

                if ($scope.chosen_member.id in $scope.cached_plots) {
                    $scope.plot_data = $scope.cached_plots[$scope.chosen_member.id];

                    $scope.analyzed_parameters = get_analyzed_parameters(
                        $scope.plot_data.panel_data.parameters,
                        $scope.plot_data.cluster_data[0]
                    );

                    // Need to init the parallel plot first so we can
                    // "bold" the expanded clusters
                    $scope.initialize_parallel_plot(
                        $scope.analyzed_parameters,
                        $scope.plot_data.cluster_data
                    );

                    // turn on display_events for originally expanded clusters
                    $scope.plot_data.cluster_data.forEach(function(c) {
                        if (expanded_cluster_indices.indexOf(c.cluster_index) != -1) {
                            c.display_events = true;
                            $scope.select_cluster_line(c);

                            if (c.events_retrieved == false) {
                                $scope.process_cluster_events(c);
                            }
                        } else {
                            c.display_events = false;
                        }
                    });

                    $scope.initialize_scatterplot(true);

                    return;
                }

                $scope.retrieving_data = true;
                $scope.has_parent_stage = $scope.$parent.process_request.parent_stage > 0;
                sample_clusters = ModelService.getSampleClusters(
                    $scope.$parent.process_request.id,
                    $scope.chosen_member.sample.id
                );

                panel_data = ModelService.getSitePanel(
                    $scope.chosen_member.sample.site_panel
                );

                $q.all([sample_clusters, panel_data.$promise]).then(function(data) {
                    $scope.cached_plots[$scope.chosen_member.id] = {
                        'cluster_data': data[0],
                        'panel_data': data[1]
                    };
                    $scope.plot_data = $scope.cached_plots[$scope.chosen_member.id];

                    $scope.analyzed_parameters = get_analyzed_parameters(
                        $scope.plot_data.panel_data.parameters,
                        $scope.plot_data.cluster_data[0]
                    );

                    $scope.initialize_scatterplot(false);
                    $scope.initialize_parallel_plot(
                        $scope.analyzed_parameters,
                        $scope.plot_data.cluster_data
                    );

                    // display events for previously expanded clusters
                    $scope.plot_data.cluster_data.forEach(function(c) {
                        // since the sample is new we can just toggle it
                        if (expanded_cluster_indices.indexOf(c.cluster_index) != -1) {
                            $scope.toggle_cluster_events(c);
                            $scope.select_cluster_line(c);
                        }
                    });
                }).catch(function(e) {
                    // show errors here
                    console.log('error!')
                }).finally(function() {
                    $scope.retrieving_data = false;
                });
            };

            $scope.previous_sample = function() {
                var current_index = $scope.sample_collection.members.indexOf(
                    $scope.chosen_member
                );

                if (current_index === 0) {
                    return;
                } else {
                    $scope.chosen_member = $scope.sample_collection.members[current_index-1];
                    $scope.initialize_visualization();
                }
            };

            $scope.next_sample = function() {
                var current_index = $scope.sample_collection.members.indexOf(
                    $scope.chosen_member
                );

                if (current_index === $scope.sample_collection.members.length - 1) {
                    return;
                } else {
                    $scope.chosen_member = $scope.sample_collection.members[current_index+1];
                    $scope.initialize_visualization();
                }
            };

            $scope.find_matching_parameter = function(param) {
                if (param == null) {
                    return null;
                }
                for (var i = 0; i < $scope.parameters.length; i++) {
                    if (param.full_name === $scope.parameters[i].full_name) {
                        return $scope.parameters[i];
                    }
                }
                return null;
            };

            $scope.parameter_changed = function() {
                $scope.parameter_changed_flag = true;

                if ($scope.auto_transition) {
                    $scope.render_plot();
                }
            };

            /*
            Note: Difference between highlighted, selected, & expanded

            There are 3 different boolean states for each cluster, all of which
            control the look or behaviour of the cluster.

            highlighted:
                Only one of the clusters can be highlighted at any single time,
                and is meant to help identify the cluster in the various
                components (parallel plot, scatter plot, and the data table).
                A cluster is highlighted when the user's mouse cursor hovers
                over either the cluster circle in the scatter-plot OR the
                table row for that cluster in the data table on the right hand
                side of the page. When highlighted the cluster circle is
                larger, the cluster circle is annotated with the cluster index
                & event percentage, the parallel plot line is bolder, and the
                table row background is grey. Highlighting when hovering over
                a cluster circle only works when brush mode is disabled.
            selected:
                Any number of clusters (including zero) can be selected at a
                single time. The only way clusters can be selected is using the
                rectangular brush when the brush mode is enabled. Brush mode
                is controlled by a check-box found above the scatter-plot.
                Cluster selection is intended to be used in conjunction with
                the "expand selected" button and then with the "label expanded"
                button to label multiple clusters at once. The selected state
                looks exactly like the highlighted state (perhaps this should
                be changed?)
            expanded:
                A cluster is expanded when the events for that cluster are
                visible. To expand a cluster the user can either click on the
                cluster circle (when brush mode is disabled) or click the
                check-box in that cluster's row of the data table.
             */

            $scope.highlight_cluster = function (cluster) {
                cluster.highlighted = true;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", function (d) {
                        return Math.round(d.weight) + 9;
                    });

                $scope.select_cluster_line(cluster);
            };

            $scope.dehighlight_cluster = function (cluster) {
                cluster.highlighted = false;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", function(d) {
                        return d.selected ? Math.round(d.weight) + 9 : Math.round(d.weight) + 5;
                    }
                );

                if (!cluster.selected) {
                    $scope.deselect_cluster_line(cluster);
                }
            };

            $scope.select_cluster = function (cluster) {
                cluster.selected = true;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", Math.round(d.weight) + 9);

                $scope.select_cluster_line(cluster);
            };

            $scope.deselect_cluster = function (cluster) {
                cluster.selected = false;

                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    }).attr("r", Math.round(d.weight) + 5);

                $scope.deselect_cluster_line(cluster);
            };

            $scope.expand_cluster = function (cluster) {
                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    })
                    .style("stroke-dasharray", "4, 1")
                    .style("stroke-linecap", "butt")
                    .style("stroke-width", "5");
            };

            $scope.collapse_cluster = function (cluster) {
                $scope.clusters.filter(
                    function(d) {
                        if (d.cluster_index == cluster.cluster_index) {
                            return d;
                        }
                    })
                    .style("stroke-dasharray", "1, 0")
                    .style("stroke-linecap", "butt")
                    .style("stroke-width", "1");
            };

            function update_cluster_labels(sample_cluster) {
{'fraction_non_alphanumeric': 0.08072606546136914, 'fraction_numerical': 0.001840424380210025, 'mean_word_length': 1.1579193272076, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2225732', 'n_tokens_mistral': 6536, 'n_tokens_neox': 5977, 'n_words': 1486}