<commit_before>import * as React from 'react';
import LoadingIndicator from "shared/components/loadingIndicator/LoadingIndicator";
import {observer} from "mobx-react";
import GroupComparisonStore from './GroupComparisonStore';
import {observable, computed} from 'mobx';
import Venn from './OverlapVenn';
import _ from "lodash";
import autobind from 'autobind-decorator';
import DownloadControls from 'shared/components/downloadControls/DownloadControls';
import {MakeMobxView} from "../../shared/components/MobxView";
import Loader from "../../shared/components/loadingIndicator/LoadingIndicator";
import ErrorMessage from "../../shared/components/ErrorMessage";
import {getSampleIdentifiers, OVERLAP_NOT_ENOUGH_GROUPS_MSG} from "./GroupComparisonUtils";
import {remoteData} from "../../shared/api/remoteData";
import UpSet from './UpSet';
import * as ReactDOM from 'react-dom';
import WindowStore from 'shared/components/window/WindowStore';

export interface IOverlapProps {
    store: GroupComparisonStore
}

const SVG_ID = "comparison-tab-overlap-svg";

enum PlotType {
    Upset,
    Venn
}

@observer
export default class Overlap extends React.Component<IOverlapProps, {}> {

    @observable plotExists = false;

    componentDidUpdate() {
        this.plotExists = !!this.getSvg();
    }

    readonly tabUI = MakeMobxView({
        await:()=>{
            if (this.props.store._activeGroupsNotOverlapRemoved.isComplete &&
                this.props.store._activeGroupsNotOverlapRemoved.result.length < 2) {
                // dont bother loading data for and computing overlap if not enough groups for it
                return [this.props.store._activeGroupsNotOverlapRemoved];
            } else {
                return [this.props.store._activeGroupsNotOverlapRemoved, this.overlapUI];
            }
        },
        render:()=>{
            if (this.props.store._activeGroupsNotOverlapRemoved.result!.length < 2) {
                return <span>{OVERLAP_NOT_ENOUGH_GROUPS_MSG}</span>;
            } else {
                return this.overlapUI.component;
            }
        },
        renderPending:()=><Loader isLoading={true} center={true} size={"big"}/>,
        renderError:()=><ErrorMessage/>
    });

    @autobind
    private getSvg() {
        if (this.plotType.result! === PlotType.Upset) {
            let node = ReactDOM.findDOMNode(this);

            const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGElement;
            const childSVGs = $(node!).find('svg')
            const sampleElement = childSVGs[0].cloneNode(true) as Element;
            const patientElement = childSVGs[1].cloneNode(true) as Element;
            $(node!).find('svg')

            let height = 0;
            let width = 0;

            svg.appendChild(sampleElement)
            //move patient element down by sample element size
            if (this.areUpsetPlotsSidebySide) {
                patientElement.setAttribute("x", `${$(sampleElement).width()}`);
                height = Math.max($(sampleElement).height(), $(patientElement).height());
                width = $(sampleElement).width() + $(patientElement).width();
            } else {
                patientElement.setAttribute("y", `${$(sampleElement).height()}`);
                height = $(sampleElement).height() + $(patientElement).height();
                width = Math.max($(sampleElement).width(), $(patientElement).width());
            }

            $(svg).attr("height", height);
            $(svg).attr("width", width);
            $(svg).css({ height, width });

            svg.appendChild(patientElement);
            return svg;
        }
        return document.getElementById(SVG_ID) as SVGElement | null
    }

    readonly plotType = remoteData({
        await:()=>[this.props.store._activeGroupsNotOverlapRemoved],
        invoke:async()=>(this.props.store._activeGroupsNotOverlapRemoved.result!.length > 3 ? PlotType.Upset : PlotType.Venn)
    });


    public readonly sampleGroupsWithCases = remoteData({
        await: () => [
            this.props.store._activeGroupsNotOverlapRemoved,
            this.props.store.sampleSet,
        ],
        invoke: () => {
            const sampleSet = this.props.store.sampleSet.result!;
            const groupsWithSamples = _.map(this.props.store._activeGroupsNotOverlapRemoved.result, group => {
                let samples = getSampleIdentifiers([group]).map(sampleIdentifier => sampleSet.get({studyId: sampleIdentifier.studyId, sampleId: sampleIdentifier.sampleId}));
                return {
                    uid: group.uid,
                    cases: _.map(samples, sample => sample!.uniqueSampleKey)
                }
            });
            return Promise.resolve(groupsWithSamples);
        }
    }, []);

    public readonly patientGroupsWithCases = remoteData({
        await: () => [
            this.props.store._activeGroupsNotOverlapRemoved,
            this.props.store.sampleSet,
        ],
        invoke: () => {
            const sampleSet = this.props.store.sampleSet.result!;
            const groupsWithPatients = _.map(this.props.store._activeGroupsNotOverlapRemoved.result, group => {
                let samples = getSampleIdentifiers([group]).map(sampleIdentifier => sampleSet.get({studyId: sampleIdentifier.studyId, sampleId: sampleIdentifier.sampleId}));
                return {
                    uid: group.uid,
                    cases: _.uniq(_.map(samples, sample => sample!.uniquePatientKey))
                }
            });
            return Promise.resolve(groupsWithPatients);
        }
    }, []);

    readonly uidToGroup = remoteData({
        await:()=>[this.props.store._activeGroupsNotOverlapRemoved],
        invoke:()=>Promise.resolve(_.keyBy(this.props.store._activeGroupsNotOverlapRemoved.result!, group=>group.uid))
    });

    // whether to display sample and patient sets intersection charts side by side
    @computed get areUpsetPlotsSidebySide() {
        if (this.props.store.samplesVennPartition.isComplete && this.props.store.patientsVennPartition.isComplete) {
            return this.props.store.samplesVennPartition.result!.length + this.props.store.patientsVennPartition.result!.length <= 100;
        } else {
            return true;
        }
    }

    @computed private get maxWidth() {
        return WindowStore.size.width - 80;
    }

    readonly plot = MakeMobxView({
        await:()=>[
            this.plotType,
            this.props.store.samplesVennPartition,
            this.props.store.patientsVennPartition,
            this.sampleGroupsWithCases,
            this.patientGroupsWithCases,
            this.uidToGroup
        ],
        render:()=>{
            let plotElt: any = null;
            switch (this.plotType.result!) {
                case PlotType.Upset: {
                    plotElt = (
                        <div style={{ display: `${this.areUpsetPlotsSidebySide ? "flex" : "block"}`, maxWidth: this.maxWidth, overflow: "auto hidden" }} >
                            <div>
                                <div style={{ textAlign: "center", position: this.areUpsetPlotsSidebySide ? "relative" : "absolute", width: "100%" }}>
                                    <strong style={{ fontFamily: "Verdana,Arial,sans-serif" }}>Sample Sets Intersection</strong>
                                </div>
                                <UpSet
                                    groups={this.props.store.samplesVennPartition.result!}
                                    uidToGroup={this.uidToGroup.result!}
                                    caseType="sample"
                                />
                            </div>

                            <div>
                                <div style={{ textAlign: "center", position: this.areUpsetPlotsSidebySide ? "relative" : "absolute", width: "100%" }}>
                                    <strong style={{ fontFamily: "Verdana,Arial,sans-serif" }}>Sample Sets Intersection</strong>
                                </div>
                                <UpSet
                                    groups={this.props.store.patientsVennPartition.result!}
                                    uidToGroup={this.uidToGroup.result!}
                                    caseType="patient"
                                />
                            </div>
                        </div>)
                    break;
                }
                case PlotType.Venn:
                    plotElt = (
                        <Venn
                            svgId={SVG_ID}
                            sampleGroups={this.sampleGroupsWithCases.result!}
                            patientGroups={this.patientGroupsWithCases.result!}
                            uidToGroup={this.uidToGroup.result!}
                            store={this.props.store}
                        />)
                    break;
                default:
                    return <span>Not implemented yet</span>
            }
            return plotElt;
        },<commit_msg>Fix overlap upset header and downloaded svg<commit_after>import * as React from 'react';
import LoadingIndicator from "shared/components/loadingIndicator/LoadingIndicator";
import {observer} from "mobx-react";
import GroupComparisonStore from './GroupComparisonStore';
import {observable, computed} from 'mobx';
import Venn from './OverlapVenn';
import _ from "lodash";
import autobind from 'autobind-decorator';
import DownloadControls from 'shared/components/downloadControls/DownloadControls';
import {MakeMobxView} from "../../shared/components/MobxView";
import Loader from "../../shared/components/loadingIndicator/LoadingIndicator";
import ErrorMessage from "../../shared/components/ErrorMessage";
import {getSampleIdentifiers, OVERLAP_NOT_ENOUGH_GROUPS_MSG} from "./GroupComparisonUtils";
import {remoteData} from "../../shared/api/remoteData";
import UpSet from './UpSet';
import * as ReactDOM from 'react-dom';
import WindowStore from 'shared/components/window/WindowStore';

export interface IOverlapProps {
    store: GroupComparisonStore
}

const SVG_ID = "comparison-tab-overlap-svg";

enum PlotType {
    Upset,
    Venn
}

@observer
export default class Overlap extends React.Component<IOverlapProps, {}> {

    @observable plotExists = false;

    componentDidUpdate() {
        this.plotExists = !!this.getSvg();
    }

    readonly tabUI = MakeMobxView({
        await:()=>{
            if (this.props.store._activeGroupsNotOverlapRemoved.isComplete &&
                this.props.store._activeGroupsNotOverlapRemoved.result.length < 2) {
                // dont bother loading data for and computing overlap if not enough groups for it
                return [this.props.store._activeGroupsNotOverlapRemoved];
            } else {
                return [this.props.store._activeGroupsNotOverlapRemoved, this.overlapUI];
            }
        },
        render:()=>{
            if (this.props.store._activeGroupsNotOverlapRemoved.result!.length < 2) {
                return <span>{OVERLAP_NOT_ENOUGH_GROUPS_MSG}</span>;
            } else {
                return this.overlapUI.component;
            }
        },
        renderPending:()=><Loader isLoading={true} center={true} size={"big"}/>,
        renderError:()=><ErrorMessage/>
    });

    @autobind
    private getSvg() {
        if (this.plotType.result! === PlotType.Upset) {
            let node = ReactDOM.findDOMNode(this);

            const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") as SVGElement;
            const childSVGs = $(node!).find('svg')
            const sampleElement = childSVGs[0].cloneNode(true) as Element;
            const patientElement = childSVGs[1].cloneNode(true) as Element;
            $(node!).find('svg')

            let height = 0;
            let width = 0;

            svg.appendChild(sampleElement)
            //move patient element down by sample element size
            if (this.areUpsetPlotsSidebySide) {
                patientElement.setAttribute("x", `${$(sampleElement).width()}`);
                height = Math.max($(sampleElement).height(), $(patientElement).height());
                width = $(sampleElement).width() + $(patientElement).width();
            } else {
                patientElement.setAttribute("y", `${$(sampleElement).height()}`);
                height = $(sampleElement).height() + $(patientElement).height();
                width = Math.max($(sampleElement).width(), $(patientElement).width());
            }

            $(svg).attr("height", height);
            $(svg).attr("width", width);
            $(svg).css({ height, width });

            svg.appendChild(patientElement);
            return svg;
        }
        return document.getElementById(SVG_ID) as SVGElement | null
    }

    readonly plotType = remoteData({
        await:()=>[this.props.store._activeGroupsNotOverlapRemoved],
        invoke:async()=>(this.props.store._activeGroupsNotOverlapRemoved.result!.length > 3 ? PlotType.Upset : PlotType.Venn)
    });


    public readonly sampleGroupsWithCases = remoteData({
        await: () => [
            this.props.store._activeGroupsNotOverlapRemoved,
            this.props.store.sampleSet,
        ],
        invoke: () => {
            const sampleSet = this.props.store.sampleSet.result!;
            const groupsWithSamples = _.map(this.props.store._activeGroupsNotOverlapRemoved.result, group => {
                let samples = getSampleIdentifiers([group]).map(sampleIdentifier => sampleSet.get({studyId: sampleIdentifier.studyId, sampleId: sampleIdentifier.sampleId}));
                return {
                    uid: group.uid,
                    cases: _.map(samples, sample => sample!.uniqueSampleKey)
                }
            });
            return Promise.resolve(groupsWithSamples);
        }
    }, []);

    public readonly patientGroupsWithCases = remoteData({
        await: () => [
            this.props.store._activeGroupsNotOverlapRemoved,
            this.props.store.sampleSet,
        ],
        invoke: () => {
            const sampleSet = this.props.store.sampleSet.result!;
            const groupsWithPatients = _.map(this.props.store._activeGroupsNotOverlapRemoved.result, group => {
                let samples = getSampleIdentifiers([group]).map(sampleIdentifier => sampleSet.get({studyId: sampleIdentifier.studyId, sampleId: sampleIdentifier.sampleId}));
                return {
                    uid: group.uid,
                    cases: _.uniq(_.map(samples, sample => sample!.uniquePatientKey))
                }
            });
            return Promise.resolve(groupsWithPatients);
        }
    }, []);

    readonly uidToGroup = remoteData({
        await:()=>[this.props.store._activeGroupsNotOverlapRemoved],
        invoke:()=>Promise.resolve(_.keyBy(this.props.store._activeGroupsNotOverlapRemoved.result!, group=>group.uid))
    });

    // whether to display sample and patient sets intersection charts side by side
    @computed get areUpsetPlotsSidebySide() {
        if (this.props.store.samplesVennPartition.isComplete && this.props.store.patientsVennPartition.isComplete) {
            return this.props.store.samplesVennPartition.result!.length + this.props.store.patientsVennPartition.result!.length <= 100;
        } else {
            return true;
        }
    }

    @computed private get maxWidth() {
        return WindowStore.size.width - 80;
    }

    readonly plot = MakeMobxView({
        await:()=>[
            this.plotType,
            this.props.store.samplesVennPartition,
            this.props.store.patientsVennPartition,
            this.sampleGroupsWithCases,
            this.patientGroupsWithCases,
            this.uidToGroup
        ],
        render:()=>{
            let plotElt: any = null;
            switch (this.plotType.result!) {
                case PlotType.Upset: {
                    plotElt = (
                        <div style={{ display: `${this.areUpsetPlotsSidebySide ? "flex" : "block"}`, maxWidth: this.maxWidth, overflow: "auto hidden" }} >
                            <UpSet
                                groups={this.props.store.samplesVennPartition.result!}
                                uidToGroup={this.uidToGroup.result!}
                                caseType="sample"
                                title="Samples overlap"
                            />
                            <UpSet
                                groups={this.props.store.patientsVennPartition.result!}
                                uidToGroup={this.uidToGroup.result!}
                                caseType="patient"
                                title="Patients overlap"
                            />
                        </div>)
                    break;
                }
                case PlotType.Venn:
                    plotElt = (
                        <Venn
                            svgId={SVG_ID}
                            sampleGroups={this.sampleGroupsWithCases.result!}
                            patientGroups={this.patientGroupsWithCases.result!}
                            uidToGroup={this.uidToGroup.result!}
                            store={this.props.store}
                        />)
                    break;
                default:
                    return <span>Not implemented yet</span>
            }
            return plotElt;
        },
{'fraction_non_alphanumeric': 0.11616739080722616, 'fraction_numerical': 0.00228675966155957, 'mean_word_length': 1.8933178961296726, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 15, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5699295', 'n_tokens_mistral': 4626, 'n_tokens_neox': 4443, 'n_words': 814}