export function converter()

in packages/network-navigator-powerbi/src/dataConversion.ts [39:177]


export function converter(
    dataView: DataView,
    settings: INetworkNavigatorConfiguration,
    columnToFilter?: powerbi.DataViewMetadataColumn,
    createIdBuilder?: () => ISelectionIdBuilder): INetworkNavigatorData<INetworkNavigatorSelectableNode> {
    "use strict";
    let nodeList: INetworkNavigatorSelectableNode[] = [];
    const nodeMap: { [name: string]: INetworkNavigatorSelectableNode } = {};
    let linkList: INetworkNavigatorLink[] = [];
    const table = dataView.table;

    // The map of dataRoles to an index
    const colMap = {};
    const metadataColMap = {};

    // Maps all of the DATA_ROLES to an index into the data
    table.columns.forEach((c, i) => {
        Object.keys(c.roles).forEach(role => {
            colMap[role] = i;
            metadataColMap[role] = c;
        });
    });

    // group defines the bundle basically
    // name, user friendly name,
    // num, size of circle, probably meant to be the number of matches
    // source - array index into nodes
    // target - array index into node
    // value - The number of times that the link has been made, ie, I emailed bob@gmail.com 10 times, so value would be 10

    const roles = DATA_ROLES;
    const sourceIdx = colMap[roles.source.name];
    const sourceColorIdx = colMap[roles.sourceColor.name];
    const sourceLabelColorIdx = colMap[roles.sourceLabelColor.name];
    // let sourceGroup = colMap[roles.sourceGroup.name];
    // let targetGroupIdx = colMap[roles.targetGroup.name];
    const targetColorIdx = colMap[roles.targetColor.name];
    const targetLabelColorIdx = colMap[roles.targetLabelColor.name];
    const targetIdx = colMap[roles.target.name];
    const edgeValueIdx = colMap[roles.edgeValue.name];
    const edgeColorValueIdx = colMap[roles.edgeColorValue.name];
    const sourceNodeWeightIdx = colMap[roles.sourceNodeWeight.name];
    const targetNodeWeightIdx = colMap[roles.targetNodeWeight.name];

    /**
     * Creates a node with the given value if the node has not already been seen/created
     */
    function getNode(
        id: string,
        dvIdentity: powerbi.DataViewScopeIdentity,
        isSource: boolean,
        nodeWeight: number,
        color: string = "gray",
        labelColor: string,
        group: number = 0): INetworkNavigatorSelectableNode {
        const column = table.columns[isSource ? sourceIdx : targetIdx];
        let node = nodeMap[id];
        if (!nodeMap[id]) {
            const builder = createIdBuilder();
            const identityColumn = dataView.metadata.columns.filter(n => n.queryName === column.queryName)[0];
            const filterTargetColumn = columnToFilter || identityColumn;
            const target: models.IFilterColumnTarget = {
                table: filterTargetColumn.queryName.substr(0, filterTargetColumn.queryName.indexOf(".")),
                column: filterTargetColumn.displayName,
            };
            const filter = <models.IAdvancedFilter><any>new models.AdvancedFilter(target, "And", {
                operator: "Is",
                value: id,
            });
            node = nodeMap[id] = {
                name: id,
                color: color || "gray",
                labelColor,
                index: nodeList.length,
                filter,
                value: nodeWeight,
                neighbors: 1,
                selected: false,
                identity: builder ? builder
                    .withCategory({
                        // https://community.powerbi.com/t5/Developer/Creating-Selection-manager-for-Custom-Table-visuals/m-p/218391/highlight/true#M6869
                        source: identityColumn,
                        values: <any>null,
                        identity: [dvIdentity],
                    }, 0)
                    .createSelectionId() : <any>-1,
            };
            nodeList.push(node);
        }
        return node;
    }

    // The minimum necessary is a source node and a target node, otherwise we'll just have a bunch of disconnected nodes
    if (sourceIdx !== undefined && targetIdx !== undefined) {

        // Iterate through each row and create a connection between the source node and the target node
        table.rows.forEach((row, idx) => {
            const identity = table.identity[idx];
            if (row[sourceIdx] && row[targetIdx]) {
                /** These need to be strings to work properly */
                const sourceId = row[sourceIdx] + "";
                const targetId = row[targetIdx] + "";
                const edge = {
                    source:
                        getNode(sourceId,
                                identity,
                                true,
                                row[sourceNodeWeightIdx] as number,
                                row[sourceColorIdx] as string,
                                row[sourceLabelColorIdx] as string).index,
                    target:
                        getNode(targetId,
                                identity,
                                false,
                                row[targetNodeWeightIdx] as number,
                                row[targetColorIdx] as string,
                                row[targetLabelColorIdx] as string).index,
                    value: row[edgeValueIdx],
                    colorValue: row[edgeColorValueIdx],
                } as INetworkNavigatorLink;
                nodeList[edge.source].neighbors += 1;
                nodeList[edge.target].neighbors += 1;
                linkList.push(edge);
            }
        });

        // If we are limiting the number of nodes, then trim the final list
        const maxNodes = settings.maxNodeCount;
        if (typeof maxNodes === "number" && maxNodes > 0) {
            nodeList = nodeList.slice(0, maxNodes);
            linkList = linkList.filter(n => n.source < maxNodes && n.target < maxNodes);
        }
    }

    return {
        nodes: nodeList,
        links: linkList,
    };
}