From 2780860beb396115611b836a4d15e7502c48255c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 13:03:07 +0300 Subject: [PATCH 1/8] Tidy D3 delegation --- src/Classes/settingsClass.ts | 5 +- src/D3 Plotting Functions/drawDots.ts | 74 ++++++++-- src/D3 Plotting Functions/drawIcons.ts | 6 +- src/D3 Plotting Functions/drawLines.ts | 5 +- src/D3 Plotting Functions/drawPlot.ts | 21 --- src/D3 Plotting Functions/drawTooltipLine.ts | 39 +++++- src/D3 Plotting Functions/drawXAxis.ts | 5 +- src/D3 Plotting Functions/drawYAxis.ts | 5 +- src/D3 Plotting Functions/highlight.ts | 6 +- src/Functions/extractConditionalFormatting.ts | 11 +- src/visual.ts | 127 +++--------------- 11 files changed, 139 insertions(+), 165 deletions(-) delete mode 100644 src/D3 Plotting Functions/drawPlot.ts diff --git a/src/Classes/settingsClass.ts b/src/Classes/settingsClass.ts index 1a9cc57..311aa34 100644 --- a/src/Classes/settingsClass.ts +++ b/src/Classes/settingsClass.ts @@ -38,9 +38,8 @@ class settingsClass implements defaultSettingsType { const allSettingGroups: string[] = Object.getOwnPropertyNames(this); allSettingGroups.forEach(settingGroup => { - const condFormatting: defaultSettingsType[defaultSettingsKey] = inputView.categorical.categories - ? extractConditionalFormatting(inputView.categorical, settingGroup, this)[0] - : null; + const categoricalView: powerbi.DataViewCategorical = inputView.categorical ? inputView.categorical : null; + const condFormatting: defaultSettingsType[defaultSettingsKey] = extractConditionalFormatting(categoricalView, settingGroup, this)[0]; // Get the names of all settings in a given class and // use those to extract and update the relevant values const settingNames: string[] = Object.getOwnPropertyNames(this[settingGroup]); diff --git a/src/D3 Plotting Functions/drawDots.ts b/src/D3 Plotting Functions/drawDots.ts index af5e51e..61f425a 100644 --- a/src/D3 Plotting Functions/drawDots.ts +++ b/src/D3 Plotting Functions/drawDots.ts @@ -1,30 +1,84 @@ -import * as d3 from "d3"; import viewModelClass from "../Classes/viewModelClass"; import { plotData } from "../Classes/viewModelClass"; import between from "../Functions/between"; +import { svgBaseType, Visual } from "../visual"; -type SelectionBase = d3.Selection; - -export default function drawDots(selection: SelectionBase, viewModel: viewModelClass) { +export default function drawDots(selection: svgBaseType, visualObj: Visual) { selection.selectAll(".dotsgroup").remove() - if (!(viewModel.plotProperties.displayPlot)) { + if (!(visualObj.viewModel.plotProperties.displayPlot)) { return; } - const lower: number = viewModel.plotProperties.yAxis.lower; - const upper: number = viewModel.plotProperties.yAxis.upper; + const lower: number = visualObj.viewModel.plotProperties.yAxis.lower; + const upper: number = visualObj.viewModel.plotProperties.yAxis.upper; selection .append('g') .classed("dotsgroup", true) .selectAll(".dotsgroup") - .data(viewModel.plotPoints) + .data(visualObj.viewModel.plotPoints) .enter() .append("circle") .filter((d: plotData) => d.value !== null) - .attr("cy", (d: plotData) => viewModel.plotProperties.yScale(d.value)) - .attr("cx", (d: plotData) => viewModel.plotProperties.xScale(d.x)) + .attr("cy", (d: plotData) => visualObj.viewModel.plotProperties.yScale(d.value)) + .attr("cx", (d: plotData) =>visualObj.viewModel.plotProperties.xScale(d.x)) .attr("r", (d: plotData) => d.aesthetics.size) .style("fill", (d: plotData) => { return between(d.value, lower, upper) ? d.aesthetics.colour : "#FFFFFF"; }) + .on("click", (event, d: plotData) => { + if (visualObj.viewModel.inputSettings.spc.split_on_click) { + // Identify whether limits are already split at datapoint, and undo if so + const xIndex: number = visualObj.viewModel.splitIndexes.indexOf(d.x) + if (xIndex > -1) { + visualObj.viewModel.splitIndexes.splice(xIndex, 1) + } else { + visualObj.viewModel.splitIndexes.push(d.x) + } + + // Store the current limit-splitting indices to make them available between full refreshes + // This also initiates a visual update() call, causing the limits to be re-calculated + visualObj.host.persistProperties({ + replace: [{ + objectName: "split_indexes_storage", + selector: undefined, + properties: { split_indexes: JSON.stringify(visualObj.viewModel.splitIndexes) } + }] + }); + } else { + // Pass identities of selected data back to PowerBI + visualObj.selectionManager + // Propagate identities of selected data back to + // PowerBI based on all selected dots + .select(d.identity, (event.ctrlKey || event.metaKey)) + // Change opacity of non-selected dots + .then(() => { visualObj.updateHighlighting(); }); + } + event.stopPropagation(); + }) + // Display tooltip content on mouseover + .on("mouseover", (event, d: plotData) => { + // Get screen coordinates of mouse pointer, tooltip will + // be displayed at these coordinates + const x = event.pageX; + const y = event.pageY; + + visualObj.host.tooltipService.show({ + dataItems: d.tooltip, + identities: [d.identity], + coordinates: [x, y], + isTouchEvent: false + }); + }) + // Hide tooltip when mouse moves out of dot + .on("mouseout", () => { + visualObj.host.tooltipService.hide({ + immediately: true, + isTouchEvent: false + }) + }); + + selection.on('click', () => { + visualObj.selectionManager.clear(); + visualObj.updateHighlighting() + }); } diff --git a/src/D3 Plotting Functions/drawIcons.ts b/src/D3 Plotting Functions/drawIcons.ts index 8c8578f..d1acbb2 100644 --- a/src/D3 Plotting Functions/drawIcons.ts +++ b/src/D3 Plotting Functions/drawIcons.ts @@ -1,12 +1,10 @@ -import * as d3 from "d3"; import * as iconSVG from "./Icons" import viewModelClass from "../Classes/viewModelClass"; import variationIconsToDraw from "../Functions/variationIconsToDraw"; import initialiseIconSVG from "./Icons/initialiseIconSVG"; +import { svgBaseType } from "../visual"; -type SelectionBase = d3.Selection; - -export default function drawIcons(selection: SelectionBase, viewModel: viewModelClass) { +export default function drawIcons(selection: svgBaseType, viewModel: viewModelClass) { selection.selectAll(".icongroup").remove() const draw_variation: boolean = viewModel.inputSettings.nhs_icons.show_variation_icons; if (!draw_variation) { diff --git a/src/D3 Plotting Functions/drawLines.ts b/src/D3 Plotting Functions/drawLines.ts index 3cdf695..e27cff0 100644 --- a/src/D3 Plotting Functions/drawLines.ts +++ b/src/D3 Plotting Functions/drawLines.ts @@ -3,10 +3,9 @@ import viewModelClass from "../Classes/viewModelClass"; import { lineData } from "../Classes/viewModelClass"; import between from "../Functions/between"; import getAesthetic from "../Functions/getAesthetic"; +import { svgBaseType } from "../visual"; -type SelectionBase = d3.Selection; - -export default function drawLines(selection: SelectionBase, viewModel: viewModelClass) { +export default function drawLines(selection: svgBaseType, viewModel: viewModelClass) { selection.selectAll(".linesgroup").remove() if (!(viewModel.groupedLines)) { return; diff --git a/src/D3 Plotting Functions/drawPlot.ts b/src/D3 Plotting Functions/drawPlot.ts deleted file mode 100644 index bd2cc61..0000000 --- a/src/D3 Plotting Functions/drawPlot.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as d3 from "d3"; -import viewModelClass from "../Classes/viewModelClass"; -import drawXAxis from "./drawXAxis"; -import drawYAxis from "./drawYAxis"; -import drawTooltipLine from "./drawTooltipLine"; -import drawLines from "./drawLines"; -import drawDots from "./drawDots"; -import drawIcons from "./drawIcons"; - -type SelectionBase = d3.Selection; - -export default function drawPlot(selection: SelectionBase, viewModel: viewModelClass) { - selection.attr("width", viewModel.plotProperties.width) - .attr("height", viewModel.plotProperties.height) - .call(drawXAxis, viewModel) - .call(drawYAxis, viewModel) - .call(drawTooltipLine, viewModel) - .call(drawLines, viewModel) - .call(drawDots, viewModel) - .call(drawIcons, viewModel) -} diff --git a/src/D3 Plotting Functions/drawTooltipLine.ts b/src/D3 Plotting Functions/drawTooltipLine.ts index 3349078..86f96da 100644 --- a/src/D3 Plotting Functions/drawTooltipLine.ts +++ b/src/D3 Plotting Functions/drawTooltipLine.ts @@ -1,15 +1,20 @@ import * as d3 from "d3"; +import powerbi from "powerbi-visuals-api"; +import ITooltipService = powerbi.extensibility.ITooltipService import viewModelClass from "../Classes/viewModelClass"; +import { svgBaseType } from "../visual"; +import plotPropertiesClass from "../Classes/plotPropertiesClass"; +import { plotData } from "../Classes/viewModelClass"; -type SelectionBase = d3.Selection; - -export default function drawTooltipLine(selection: SelectionBase, viewModel: viewModelClass) { +export default function drawTooltipLine(selection: svgBaseType, viewModel: viewModelClass, tooltipService: ITooltipService) { selection.selectAll(".ttip-line").remove() if (!(viewModel.plotProperties.displayPlot)) { + selection.on("mousemove", (event) => { return; }) + .on("mouseleave", (event) => { return; }) return; } - selection.append('g') + const xAxisLine = selection.append('g') .classed("ttip-line", true) .selectAll(".ttip-line") .data(viewModel.plotPoints) @@ -22,4 +27,30 @@ export default function drawTooltipLine(selection: SelectionBase, viewModel: vie .attr("stroke-width", "1px") .attr("stroke", "black") .style("stroke-opacity", 0); + + selection.on("mousemove", (event) => { + const plotProperties: plotPropertiesClass = viewModel.plotProperties; + const plotPoints: plotData[] = viewModel.plotPoints + + const xValue: number = plotProperties.xScale.invert(event.pageX); + const xRange: number[] = plotPoints.map(d => d.x).map(d => Math.abs(d - xValue)); + const nearestDenominator: number = d3.leastIndex(xRange,(a,b) => a-b); + const x_coord: number = plotProperties.xScale(plotPoints[nearestDenominator].x) + const y_coord: number = plotProperties.yScale(plotPoints[nearestDenominator].value) + + tooltipService.show({ + dataItems: plotPoints[nearestDenominator].tooltip, + identities: [plotPoints[nearestDenominator].identity], + coordinates: [x_coord, y_coord], + isTouchEvent: false + }); + const xAxisHeight: number = plotProperties.height - plotProperties.yAxis.start_padding; + xAxisLine.style("stroke-opacity", 1) + .attr("x1", x_coord) + .attr("x2", x_coord); + }) + .on("mouseleave", () => { + tooltipService.hide({ immediately: true, isTouchEvent: false }); + xAxisLine.style("stroke-opacity", 0); + }); } diff --git a/src/D3 Plotting Functions/drawXAxis.ts b/src/D3 Plotting Functions/drawXAxis.ts index 29b994b..ae99e5c 100644 --- a/src/D3 Plotting Functions/drawXAxis.ts +++ b/src/D3 Plotting Functions/drawXAxis.ts @@ -2,10 +2,9 @@ import * as d3 from "d3"; import viewModelClass from "../Classes/viewModelClass"; import { axisProperties } from "../Classes/plotPropertiesClass"; import {abs} from "../Functions/UnaryFunctions"; +import { svgBaseType } from "../visual"; -type SelectionBase = d3.Selection; - -export default function drawXAxis(selection: SelectionBase, viewModel: viewModelClass, refresh?: boolean) { +export default function drawXAxis(selection: svgBaseType, viewModel: viewModelClass, refresh?: boolean) { selection.selectAll(".xaxisgroup").remove() selection.selectAll(".xaxislabel").remove() if (!(viewModel.plotProperties.displayPlot)) { diff --git a/src/D3 Plotting Functions/drawYAxis.ts b/src/D3 Plotting Functions/drawYAxis.ts index 0f6459a..18dbeb2 100644 --- a/src/D3 Plotting Functions/drawYAxis.ts +++ b/src/D3 Plotting Functions/drawYAxis.ts @@ -3,10 +3,9 @@ import viewModelClass from "../Classes/viewModelClass"; import { axisProperties } from "../Classes/plotPropertiesClass"; import {abs} from "../Functions/UnaryFunctions"; import drawXAxis from "./drawXAxis"; +import { svgBaseType } from "../visual"; -type SelectionBase = d3.Selection; - -export default function drawYAxis(selection: SelectionBase, viewModel: viewModelClass, refresh?: boolean) { +export default function drawYAxis(selection: svgBaseType, viewModel: viewModelClass, refresh?: boolean) { selection.selectAll(".yaxisgroup").remove() selection.selectAll(".yaxislabel").remove() if (!(viewModel.plotProperties.displayPlot)) { diff --git a/src/D3 Plotting Functions/highlight.ts b/src/D3 Plotting Functions/highlight.ts index 58c2164..2d34a25 100644 --- a/src/D3 Plotting Functions/highlight.ts +++ b/src/D3 Plotting Functions/highlight.ts @@ -1,13 +1,11 @@ -import * as d3 from "d3"; import powerbi from "powerbi-visuals-api"; import viewModelClass from "../Classes/viewModelClass"; import { plotData } from "../Classes/viewModelClass"; import ISelectionId = powerbi.visuals.ISelectionId; import ExtensISelectionId = powerbi.extensibility.ISelectionId; +import { svgBaseType } from "../visual"; -type SelectionBase = d3.Selection; - -export default function highlight(selection: SelectionBase, viewModel: viewModelClass, allSelectionIDs: ExtensISelectionId[]) { +export default function highlight(selection: svgBaseType, viewModel: viewModelClass, allSelectionIDs: ExtensISelectionId[]) { const anyHighlights: boolean = viewModel.inputData ? viewModel.inputData.anyHighlights : false; const opacityFull: number = viewModel.inputSettings.scatter.opacity; diff --git a/src/Functions/extractConditionalFormatting.ts b/src/Functions/extractConditionalFormatting.ts index 4be758e..54a1532 100644 --- a/src/Functions/extractConditionalFormatting.ts +++ b/src/Functions/extractConditionalFormatting.ts @@ -2,12 +2,15 @@ import powerbi from "powerbi-visuals-api" import DataViewCategoryColumn = powerbi.DataViewCategoryColumn; import DataViewCategorical = powerbi.DataViewCategorical; import settingsClass from "../Classes/settingsClass"; -import { defaultSettingsType, defaultSettingsKey } from "../Classes/defaultSettings"; +import defaultSettings, { defaultSettingsType, defaultSettingsKey } from "../Classes/defaultSettings"; import extractSetting from "./extractSetting"; -function extractConditionalFormatting(inputView: DataViewCategorical, name: string, inputSettings: settingsClass): SettingsT[] { - const inputCategories: DataViewCategoryColumn = (inputView.categories as DataViewCategoryColumn[])[0]; +function extractConditionalFormatting(categoricalView: DataViewCategorical, name: string, inputSettings: settingsClass): SettingsT[] { + if ((categoricalView === null) || (categoricalView === undefined)) { + return [null]; + } + const inputCategories: DataViewCategoryColumn = (categoricalView.categories as DataViewCategoryColumn[])[0]; const settingNames = Object.getOwnPropertyNames(inputSettings[name]) const rtn: SettingsT[] = new Array(); @@ -18,7 +21,7 @@ function extractConditionalFormatting; export class Visual implements IVisual { - private host: IVisualHost; - private svg: d3.Selection; - private viewModel: viewModelClass; - private selectionManager: ISelectionManager; + host: IVisualHost; + svg: svgBaseType; + viewModel: viewModelClass; + selectionManager: ISelectionManager; // Service for notifying external clients (export to powerpoint/pdf) of rendering status - private events: IVisualEventService; + events: IVisualEventService; constructor(options: VisualConstructorOptions) { console.log("Constructor start") @@ -35,7 +41,6 @@ export class Visual implements IVisual { this.events = options.host.eventService; this.host = options.host; this.viewModel = new viewModelClass(); - this.viewModel.firstRun = true; this.selectionManager = this.host.createSelectionManager(); this.selectionManager.registerOnSelectCallback(() => this.updateHighlighting()); @@ -53,11 +58,16 @@ export class Visual implements IVisual { this.viewModel.update({ options: options, host: this.host }); console.log("Draw plot") - this.svg.call(drawPlot, this.viewModel) + this.svg.attr("width", this.viewModel.plotProperties.width) + .attr("height", this.viewModel.plotProperties.height) + .call(drawXAxis, this.viewModel) + .call(drawYAxis, this.viewModel) + .call(drawTooltipLine, this.viewModel, this.host.tooltipService) + .call(drawLines, this.viewModel) + .call(drawDots, this) + .call(drawIcons, this.viewModel) if (this.viewModel.plotProperties.displayPlot) { - this.addDotsInteractivity(); - this.addTooltipMouseover(); this.addContextMenu() this.updateHighlighting() } @@ -76,101 +86,6 @@ export class Visual implements IVisual { return this.viewModel.inputSettings.createSettingsEntry(options.objectName); } - addTooltipMouseover(): void { - const xAxisLine = this.svg.selectAll(".ttip-line").selectChildren(); - - this.svg - .on("mousemove", (event) => { - const plotProperties: plotPropertiesClass = this.viewModel.plotProperties; - const plotPoints: plotData[] = this.viewModel.plotPoints - - const xValue: number = plotProperties.xScale.invert(event.pageX); - const xRange: number[] = plotPoints.map(d => d.x).map(d => Math.abs(d - xValue)); - const nearestDenominator: number = d3.leastIndex(xRange,(a,b) => a-b); - const x_coord: number = plotProperties.xScale(plotPoints[nearestDenominator].x) - const y_coord: number = plotProperties.yScale(plotPoints[nearestDenominator].value) - - this.host.tooltipService.show({ - dataItems: plotPoints[nearestDenominator].tooltip, - identities: [plotPoints[nearestDenominator].identity], - coordinates: [x_coord, y_coord], - isTouchEvent: false - }); - const xAxisHeight: number = plotProperties.height - plotProperties.yAxis.start_padding; - xAxisLine.style("stroke-opacity", 1) - .attr("x1", x_coord) - .attr("x2", x_coord); - }) - .on("mouseleave", () => { - this.host.tooltipService.hide({ immediately: true, isTouchEvent: false }); - xAxisLine.style("stroke-opacity", 0); - }); - } - - addDotsInteractivity(): void { - // Change opacity (highlighting) with selections in other plots - // Specify actions to take when clicking on dots - this.svg - .selectAll(".dotsgroup") - .selectChildren() - .on("click", (event, d: plotData) => { - if (this.viewModel.inputSettings.spc.split_on_click) { - // Identify whether limits are already split at datapoint, and undo if so - const xIndex: number = this.viewModel.splitIndexes.indexOf(d.x) - if (xIndex > -1) { - this.viewModel.splitIndexes.splice(xIndex, 1) - } else { - this.viewModel.splitIndexes.push(d.x) - } - - // Store the current limit-splitting indices to make them available between full refreshes - // This also initiates a visual update() call, causing the limits to be re-calculated - this.host.persistProperties({ - replace: [{ - objectName: "split_indexes_storage", - selector: undefined, - properties: { split_indexes: JSON.stringify(this.viewModel.splitIndexes) } - }] - }); - } else { - // Pass identities of selected data back to PowerBI - this.selectionManager - // Propagate identities of selected data back to - // PowerBI based on all selected dots - .select(d.identity, (event.ctrlKey || event.metaKey)) - // Change opacity of non-selected dots - .then(() => { this.updateHighlighting(); }); - } - event.stopPropagation(); - }) - // Display tooltip content on mouseover - .on("mouseover", (event, d: plotData) => { - // Get screen coordinates of mouse pointer, tooltip will - // be displayed at these coordinates - const x = event.pageX; - const y = event.pageY; - - this.host.tooltipService.show({ - dataItems: d.tooltip, - identities: [d.identity], - coordinates: [x, y], - isTouchEvent: false - }); - }) - // Hide tooltip when mouse moves out of dot - .on("mouseout", () => { - this.host.tooltipService.hide({ - immediately: true, - isTouchEvent: false - }) - }); - - this.svg.on('click', () => { - this.selectionManager.clear(); - this.updateHighlighting() - }); - } - addContextMenu(): void { this.svg.on('contextmenu', (event) => { const eventTarget: EventTarget = event.target; From e459c17a00eb56278dc7c6e93ea124ec2487402c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 13:29:59 +0300 Subject: [PATCH 2/8] Fix limit trunc settings --- src/Classes/viewModelClass.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Classes/viewModelClass.ts b/src/Classes/viewModelClass.ts index e79c80a..04a4808 100644 --- a/src/Classes/viewModelClass.ts +++ b/src/Classes/viewModelClass.ts @@ -99,8 +99,8 @@ class viewModelClass { if (this.inputSettings.spc.chart_type !== "run") { const limits: Record = { - lower: this.inputSettings.y_axis.ylimit_l, - upper: this.inputSettings.y_axis.ylimit_u + lower: this.inputSettings.spc.ll_truncate, + upper: this.inputSettings.spc.ul_truncate } calcLimits.ll99 = truncate(multiply(calcLimits.ll99, multiplier), limits); calcLimits.ll95 = truncate(multiply(calcLimits.ll95, multiplier), limits); From 4011cc4ef73979602ca3a9e42e399ccf5facb943 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 13:57:46 +0300 Subject: [PATCH 3/8] Tidy limit truncation & multipliers --- src/Classes/controlLimitsClass.ts | 28 ++++++ src/Classes/viewModelClass.ts | 138 +++++++++++++----------------- src/Functions/truncate.ts | 3 +- 3 files changed, 88 insertions(+), 81 deletions(-) diff --git a/src/Classes/controlLimitsClass.ts b/src/Classes/controlLimitsClass.ts index 7b03568..481758b 100644 --- a/src/Classes/controlLimitsClass.ts +++ b/src/Classes/controlLimitsClass.ts @@ -5,6 +5,9 @@ import two_in_three from "../Outlier Flagging/two_in_three" import shift from "../Outlier Flagging/shift" import settingsClass from "./settingsClass"; import checkFlagDirection from "../Functions/checkFlagDirection" +import truncate from "../Functions/truncate"; +import { truncateInputs } from "../Functions/truncate"; +import { multiply } from "../Functions/BinaryFunctions"; type controlLimitsArgs = { keys: { x: number, id: number, label: string }[]; @@ -38,6 +41,31 @@ class controlLimitsClass { two_in_three: string[]; shift: string[]; + scaleLimits(inputSettings: settingsClass): void { + // Scale limits using provided multiplier + const multiplier: number = inputSettings.spc.multiplier; + this.alt_targets = rep(inputSettings.spc.alt_target, this.values.length); + + ["values", "targets", "ll99", "ll95", "ul95", "ul99"].forEach(limit => { + this[limit] = multiply(this[limit], multiplier) + }) + } + + truncateLimits(inputSettings: settingsClass): void { + if (inputSettings.spc.chart_type === "run") { + return; + } + + const limits: truncateInputs = { + lower: inputSettings.spc.ll_truncate, + upper: inputSettings.spc.ul_truncate + }; + + ["ll99", "ll95", "ul95", "ul99"].forEach(limit => { + this[limit] = truncate(this[limit], limits); + }); + } + flagOutliers(inputSettings: settingsClass) { const process_flag_type: string = inputSettings.outliers.process_flag_type; const improvement_direction: string = inputSettings.outliers.improvement_direction; diff --git a/src/Classes/viewModelClass.ts b/src/Classes/viewModelClass.ts index 04a4808..2c9ad9e 100644 --- a/src/Classes/viewModelClass.ts +++ b/src/Classes/viewModelClass.ts @@ -14,9 +14,6 @@ import buildTooltip from "../Functions/buildTooltip" import plotPropertiesClass from "./plotPropertiesClass" import getAesthetic from "../Functions/getAesthetic" import { defaultSettingsType } from "./defaultSettings"; -import { multiply } from "../Functions/BinaryFunctions"; -import truncate from "../Functions/truncate" -import rep from "../Functions/rep" import * as limitFunctions from "../Limit Calculations" class lineData { @@ -51,9 +48,63 @@ class viewModelClass { firstRun: boolean; limitFunction: (x: LimitArgs) => controlLimitsClass; - getLimits(): controlLimitsClass { - let calcLimits: controlLimitsClass; + update(args: { options: VisualUpdateOptions; host: IVisualHost; }) { + if (this.firstRun) { + this.inputSettings = new settingsClass(); + } + const dv: powerbi.DataView[] = args.options.dataViews; + this.inputSettings.update(dv[0]); + + let split_indexes_storage: DataViewObject = dv[0].metadata.objects ? dv[0].metadata.objects.split_indexes_storage : null; + let split_indexes: DataViewPropertyValue = split_indexes_storage ? split_indexes_storage.split_indexes : null; + this.splitIndexes = split_indexes ? JSON.parse((split_indexes)) : new Array(); + + // Make sure that the construction returns early with null members so + // that the visual does not crash when trying to process invalid data + if (checkInvalidDataView(dv)) { + this.inputData = null; + this.limitFunction = null; + this.controlLimits = null; + this.plotPoints = null; + this.groupedLines = <[string, lineData[]][]>null; + this.splitIndexes = new Array(); + } else { + // Only re-construct data and re-calculate limits if they have changed + if (args.options.type === 2 || this.firstRun) { + + // Extract input data, filter out invalid values, and identify any settings passed as data + this.inputData = new dataClass(dv[0].categorical, this.inputSettings) + + // Initialise a new chartObject class which can be used to calculate the control limits + this.limitFunction = limitFunctions[this.inputSettings.spc.chart_type as keyof typeof limitFunctions] + + // Use initialised chartObject to calculate control limits + this.calculateLimits(); + this.controlLimits.scaleLimits(this.inputSettings); + this.controlLimits.truncateLimits(this.inputSettings); + console.log("calculatedLimits: ", this.controlLimits) + + // Structure the data and calculated limits to the format needed for plotting + this.initialisePlotData(args.host); + this.initialiseGroupedLines(); + } + } + if (this.firstRun) { + this.plotProperties = new plotPropertiesClass(); + this.plotProperties.firstRun = true; + } + this.plotProperties.update({ + options: args.options, + plotPoints: this.plotPoints, + controlLimits: this.controlLimits, + inputData: this.inputData, + inputSettings: this.inputSettings + }) + this.firstRun = false; + } + + calculateLimits(): void { if (this.splitIndexes.length > 0) { const indexes: number[] = this.splitIndexes .concat([this.inputData.keys.length - 1]) @@ -74,7 +125,7 @@ class viewModelClass { }) const calcLimitsGrouped: controlLimitsClass[] = groupedData.map(d => this.limitFunction({inputData: d, inputSettings: this.inputSettings})); - calcLimits = calcLimitsGrouped.reduce((all: controlLimitsClass, curr: controlLimitsClass) => { + this.controlLimits = calcLimitsGrouped.reduce((all: controlLimitsClass, curr: controlLimitsClass) => { const allInner: controlLimitsClass = all; Object.entries(all).forEach((entry, idx) => { if (this.inputSettings.spc.chart_type !== "run" || !["ll99", "ll95", "ul95", "ul99"].includes(entry[0])) { @@ -85,29 +136,8 @@ class viewModelClass { }) } else { // Calculate control limits using user-specified type - calcLimits = this.limitFunction({inputData: this.inputData, inputSettings: this.inputSettings}); + this.controlLimits = this.limitFunction({inputData: this.inputData, inputSettings: this.inputSettings}); } - - calcLimits.flagOutliers(this.inputSettings); - - // Scale limits using provided multiplier - const multiplier: number = this.inputSettings.spc.multiplier; - - calcLimits.values = multiply(calcLimits.values, multiplier); - calcLimits.targets = multiply(calcLimits.targets, multiplier); - calcLimits.alt_targets = rep(this.inputSettings.spc.alt_target, calcLimits.values.length) - - if (this.inputSettings.spc.chart_type !== "run") { - const limits: Record = { - lower: this.inputSettings.spc.ll_truncate, - upper: this.inputSettings.spc.ul_truncate - } - calcLimits.ll99 = truncate(multiply(calcLimits.ll99, multiplier), limits); - calcLimits.ll95 = truncate(multiply(calcLimits.ll95, multiplier), limits); - calcLimits.ul95 = truncate(multiply(calcLimits.ul95, multiplier), limits); - calcLimits.ul99 = truncate(multiply(calcLimits.ul99, multiplier), limits); - } - return calcLimits; } initialisePlotData(host: IVisualHost): void { @@ -176,58 +206,6 @@ class viewModelClass { this.groupedLines = d3.groups(formattedLines, d => d.group); } - update(args: { options: VisualUpdateOptions; host: IVisualHost; }) { - if (this.firstRun) { - this.inputSettings = new settingsClass(); - } - const dv: powerbi.DataView[] = args.options.dataViews; - this.inputSettings.update(dv[0]); - let split_indexes_storage: DataViewObject = dv[0].metadata.objects ? dv[0].metadata.objects.split_indexes_storage : null; - let split_indexes: DataViewPropertyValue = split_indexes_storage ? split_indexes_storage.split_indexes : null; - this.splitIndexes = split_indexes ? JSON.parse((split_indexes)) : new Array(); - // Make sure that the construction returns early with null members so - // that the visual does not crash when trying to process invalid data - if (checkInvalidDataView(dv)) { - this.inputData = null; - this.limitFunction = null; - this.controlLimits = null; - this.plotPoints = null; - this.groupedLines = <[string, lineData[]][]>null; - this.splitIndexes = new Array(); - } else { - - // Only re-construct data and re-calculate limits if they have changed - if (args.options.type === 2 || this.firstRun) { - - // Extract input data, filter out invalid values, and identify any settings passed as data - this.inputData = new dataClass(dv[0].categorical, this.inputSettings) - - // Initialise a new chartObject class which can be used to calculate the control limits - this.limitFunction = limitFunctions[this.inputSettings.spc.chart_type as keyof typeof limitFunctions] - - // Use initialised chartObject to calculate control limits - this.controlLimits = this.getLimits(); - console.log("calculatedLimits: ", this.controlLimits) - - // Structure the data and calculated limits to the format needed for plotting - this.initialisePlotData(args.host); - this.initialiseGroupedLines(); - } - } - if (this.firstRun) { - this.plotProperties = new plotPropertiesClass(); - this.plotProperties.firstRun = true; - } - this.plotProperties.update({ - options: args.options, - plotPoints: this.plotPoints, - controlLimits: this.controlLimits, - inputData: this.inputData, - inputSettings: this.inputSettings - }) - this.firstRun = false; - } - constructor(from?: viewModelClass) { if (from) { this.inputData = from.inputData; diff --git a/src/Functions/truncate.ts b/src/Functions/truncate.ts index 846c414..a73cdac 100644 --- a/src/Functions/truncate.ts +++ b/src/Functions/truncate.ts @@ -1,6 +1,7 @@ import broadcast_binary from "./BinaryFunctions" +export type truncateInputs = { lower?: number, upper?: number }; -function truncate_impl(val: number, limits: {lower?: number, upper?: number}): number { +function truncate_impl(val: number, limits: truncateInputs): number { let rtn: number = val; if (limits.lower || limits.lower == 0) { rtn = (rtn < limits.lower ? limits.lower : rtn) From 9944e65acb3c60e9577007513db01036d60e0d7b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 14:23:25 +0300 Subject: [PATCH 4/8] Consistent function exports --- src/Classes/controlLimitsClass.ts | 4 +--- src/Classes/dataClass.ts | 6 ++--- src/Classes/plotPropertiesClass.ts | 12 +++++----- src/Classes/settingsClass.ts | 7 +++--- src/Classes/viewModelClass.ts | 14 +++++------- .../Icons/commonCause.ts | 7 ++---- .../Icons/concernHigh.ts | 7 ++---- src/D3 Plotting Functions/Icons/concernLow.ts | 7 ++---- src/D3 Plotting Functions/Icons/fail.ts | 7 ++---- .../Icons/improvementHigh.ts | 7 ++---- .../Icons/improvementLow.ts | 7 ++---- .../Icons/initialiseIconSVG.ts | 6 ++--- .../Icons/neutralHigh.ts | 7 ++---- src/D3 Plotting Functions/Icons/neutralLow.ts | 7 ++---- src/D3 Plotting Functions/Icons/pass.ts | 7 ++---- src/Functions/BinaryFunctions.ts | 22 +++++-------------- src/Functions/Constants.ts | 18 +++++---------- src/Functions/UnaryFunctions.ts | 20 +++++------------ src/Functions/between.ts | 4 +--- src/Functions/buildTooltip.ts | 6 ++--- src/Functions/checkInvalidDataView.ts | 4 +--- src/Functions/checkValidInput.ts | 3 +-- src/Functions/dateToFormattedString.ts | 2 +- src/Functions/diff.ts | 4 +--- src/Functions/extractConditionalFormatting.ts | 2 +- src/Functions/extractSetting.ts | 2 +- src/Functions/extractValues.ts | 4 +--- src/Functions/first.ts | 4 +--- src/Functions/getAesthetic.ts | 4 +--- src/Functions/rep.ts | 4 +--- src/Functions/variationIconsToDraw.ts | 2 +- src/Limit Calculations/c.ts | 4 +--- src/Limit Calculations/g.ts | 4 +--- src/Limit Calculations/i.ts | 4 +--- src/Limit Calculations/mr.ts | 4 +--- src/Limit Calculations/p.ts | 3 +-- src/Limit Calculations/pprime.ts | 4 +--- src/Limit Calculations/run.ts | 4 +--- src/Limit Calculations/s.ts | 3 +-- src/Limit Calculations/t.ts | 3 +-- src/Limit Calculations/u.ts | 4 +--- src/Limit Calculations/uprime.ts | 4 +--- src/Limit Calculations/xbar.ts | 4 +--- src/Outlier Flagging/astronomical.ts | 4 +--- src/Outlier Flagging/shift.ts | 4 +--- src/Outlier Flagging/trend.ts | 3 +-- src/Outlier Flagging/two_in_three.ts | 4 +--- src/{Classes => }/defaultSettings.ts | 0 48 files changed, 85 insertions(+), 192 deletions(-) rename src/{Classes => }/defaultSettings.ts (100%) diff --git a/src/Classes/controlLimitsClass.ts b/src/Classes/controlLimitsClass.ts index 481758b..56c2c1d 100644 --- a/src/Classes/controlLimitsClass.ts +++ b/src/Classes/controlLimitsClass.ts @@ -23,7 +23,7 @@ type controlLimitsArgs = { count?: number[]; } -class controlLimitsClass { +export default class controlLimitsClass { [key: string] : any; keys: { x: number, id: number, label: string }[]; values: number[]; @@ -112,5 +112,3 @@ class controlLimitsClass { } } } - -export default controlLimitsClass diff --git a/src/Classes/dataClass.ts b/src/Classes/dataClass.ts index 67d245b..1aa7567 100644 --- a/src/Classes/dataClass.ts +++ b/src/Classes/dataClass.ts @@ -7,9 +7,9 @@ import settingsClass from "./settingsClass" import checkValidInput from "../Functions/checkValidInput" import extractValues from "../Functions/extractValues" import extractConditionalFormatting from "../Functions/extractConditionalFormatting" -import { defaultSettingsType } from "./defaultSettings"; +import { defaultSettingsType } from "../defaultSettings"; -class dataClass { +export default class dataClass { keys: { x: number, id: number, label: string }[]; numerators: number[]; denominators: number[]; @@ -59,5 +59,3 @@ class dataClass { this.scatter_formatting = extractValues(scatter_cond, valid_ids) } } - -export default dataClass; diff --git a/src/Classes/plotPropertiesClass.ts b/src/Classes/plotPropertiesClass.ts index cdeb3e6..80aa5d6 100644 --- a/src/Classes/plotPropertiesClass.ts +++ b/src/Classes/plotPropertiesClass.ts @@ -8,7 +8,7 @@ import dataClass from "./dataClass"; import controlLimitsClass from "./controlLimitsClass"; import { pixelConverter } from "powerbi-visuals-utils-typeutils"; -type axisProperties = { +export type axisProperties = { lower: number, upper: number, start_padding: number, @@ -26,7 +26,7 @@ type axisProperties = { label_colour: string }; -class plotPropertiesClass { +export default class plotPropertiesClass { width: number; height: number; displayPlot: boolean; @@ -134,10 +134,8 @@ class plotPropertiesClass { label_font: args.inputSettings.y_axis.ylimit_label_font, label_colour: args.inputSettings.y_axis.ylimit_label_colour }; - this.initialiseScale(); - this.firstRun = false; + + this.initialiseScale(); + this.firstRun = false; } } - -export default plotPropertiesClass -export { axisProperties } diff --git a/src/Classes/settingsClass.ts b/src/Classes/settingsClass.ts index 311aa34..2d9b17f 100644 --- a/src/Classes/settingsClass.ts +++ b/src/Classes/settingsClass.ts @@ -5,8 +5,8 @@ import VisualEnumerationInstanceKinds = powerbi.VisualEnumerationInstanceKinds; import { dataViewWildcard } from "powerbi-visuals-utils-dataviewutils"; import extractSetting from "../Functions/extractSetting"; import extractConditionalFormatting from "../Functions/extractConditionalFormatting"; -import defaultSettings from "./defaultSettings" -import { defaultSettingsType, defaultSettingsKey } from "./defaultSettings"; +import defaultSettings from "../defaultSettings" +import { defaultSettingsType, defaultSettingsKey } from "../defaultSettings"; /** * This is the core class which controls the initialisation and @@ -15,7 +15,7 @@ import { defaultSettingsType, defaultSettingsKey } from "./defaultSettings"; * * These are defined in the settingsGroups.ts file */ -class settingsClass implements defaultSettingsType { +export default class settingsClass implements defaultSettingsType { canvas: defaultSettingsType["canvas"]; spc: defaultSettingsType["spc"]; outliers: defaultSettingsType["outliers"]; @@ -83,4 +83,3 @@ class settingsClass implements defaultSettingsType { }); } } -export default settingsClass; diff --git a/src/Classes/viewModelClass.ts b/src/Classes/viewModelClass.ts index 2c9ad9e..a3405ca 100644 --- a/src/Classes/viewModelClass.ts +++ b/src/Classes/viewModelClass.ts @@ -13,16 +13,16 @@ import checkInvalidDataView from "../Functions/checkInvalidDataView" import buildTooltip from "../Functions/buildTooltip" import plotPropertiesClass from "./plotPropertiesClass" import getAesthetic from "../Functions/getAesthetic" -import { defaultSettingsType } from "./defaultSettings"; +import { defaultSettingsType } from "../defaultSettings"; import * as limitFunctions from "../Limit Calculations" -class lineData { +export class lineData { x: number; line_value: number; group: string; } -class plotData { +export class plotData { x: number; value: number; aesthetics: defaultSettingsType["scatter"]; @@ -34,9 +34,9 @@ class plotData { tooltip: VisualTooltipDataItem[]; } -type LimitArgs = { inputData: dataClass; inputSettings: settingsClass; } +export type LimitArgs = { inputData: dataClass; inputSettings: settingsClass; } -class viewModelClass { +export default class viewModelClass { inputData: dataClass; inputSettings: settingsClass; controlLimits: controlLimitsClass; @@ -83,6 +83,7 @@ class viewModelClass { this.calculateLimits(); this.controlLimits.scaleLimits(this.inputSettings); this.controlLimits.truncateLimits(this.inputSettings); + this.controlLimits.flagOutliers(this.inputSettings) console.log("calculatedLimits: ", this.controlLimits) // Structure the data and calculated limits to the format needed for plotting @@ -231,6 +232,3 @@ class viewModelClass { } } } - -export { lineData, plotData, LimitArgs } -export default viewModelClass diff --git a/src/D3 Plotting Functions/Icons/commonCause.ts b/src/D3 Plotting Functions/Icons/commonCause.ts index 9ccc9de..bf6b054 100644 --- a/src/D3 Plotting Functions/Icons/commonCause.ts +++ b/src/D3 Plotting Functions/Icons/commonCause.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual"; /** * Inline function to be called by D3 for rendering the Variation - Common Cause icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function commonCause(selection: SelectionBase): void { +export default function commonCause(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -107,5 +106,3 @@ function commonCause(selection: SelectionBase): void { .attr("fill","#BFBFBF") .attr("fill-rule","evenodd") } - -export default commonCause diff --git a/src/D3 Plotting Functions/Icons/concernHigh.ts b/src/D3 Plotting Functions/Icons/concernHigh.ts index 09cc825..6c38a28 100644 --- a/src/D3 Plotting Functions/Icons/concernHigh.ts +++ b/src/D3 Plotting Functions/Icons/concernHigh.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual"; /** * Inline function to be called by D3 for rendering the Variation - Concern High icon. @@ -13,7 +12,7 @@ type SelectionBase = d3.Selection; // ESLint errors due to number of lines in function, but would reduce readability to separate further /* eslint-disable */ -function concernHigh(selection: SelectionBase): void { +export default function concernHigh(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -138,5 +137,3 @@ function concernHigh(selection: SelectionBase): void { .attr("transform","matrix(1 0 0 -1 265.5 200.5)") } /* eslint-enable */ - -export default concernHigh diff --git a/src/D3 Plotting Functions/Icons/concernLow.ts b/src/D3 Plotting Functions/Icons/concernLow.ts index 8a543f7..bdece3c 100644 --- a/src/D3 Plotting Functions/Icons/concernLow.ts +++ b/src/D3 Plotting Functions/Icons/concernLow.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Variation - Concern Low icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function concernLow(selection: SelectionBase): void { +export default function concernLow(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -115,5 +114,3 @@ function concernLow(selection: SelectionBase): void { .attr("fill","#FF6600") .attr("fill-rule","evenodd") } - -export default concernLow diff --git a/src/D3 Plotting Functions/Icons/fail.ts b/src/D3 Plotting Functions/Icons/fail.ts index bcbb4b4..09c0ea9 100644 --- a/src/D3 Plotting Functions/Icons/fail.ts +++ b/src/D3 Plotting Functions/Icons/fail.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Assurance - Fail icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function fail(selection: SelectionBase): void { +export default function fail(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -60,5 +59,3 @@ function fail(selection: SelectionBase): void { .attr("fill","none") .attr("fill-rule","evenodd") } - -export default fail diff --git a/src/D3 Plotting Functions/Icons/improvementHigh.ts b/src/D3 Plotting Functions/Icons/improvementHigh.ts index 830a9ee..2daa0c7 100644 --- a/src/D3 Plotting Functions/Icons/improvementHigh.ts +++ b/src/D3 Plotting Functions/Icons/improvementHigh.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Variation - Improvement High icon. @@ -13,7 +12,7 @@ type SelectionBase = d3.Selection; // ESLint errors due to number of lines in function, but would reduce readability to separate further /* eslint-disable */ -function improvementHigh(selection: SelectionBase): void { +export default function improvementHigh(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -138,5 +137,3 @@ function improvementHigh(selection: SelectionBase): void { .attr("transform","matrix(1 0 0 -1 265.5 200.5)") } /* eslint-disable */ - -export default improvementHigh diff --git a/src/D3 Plotting Functions/Icons/improvementLow.ts b/src/D3 Plotting Functions/Icons/improvementLow.ts index 41c5a06..87e288e 100644 --- a/src/D3 Plotting Functions/Icons/improvementLow.ts +++ b/src/D3 Plotting Functions/Icons/improvementLow.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Variation - Improvement Low icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function improvementLow(selection: SelectionBase): void { +export default function improvementLow(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -115,5 +114,3 @@ function improvementLow(selection: SelectionBase): void { .attr("fill","#0072C6") .attr("fill-rule","evenodd") } - -export default improvementLow diff --git a/src/D3 Plotting Functions/Icons/initialiseIconSVG.ts b/src/D3 Plotting Functions/Icons/initialiseIconSVG.ts index a9e9432..f418c6b 100644 --- a/src/D3 Plotting Functions/Icons/initialiseIconSVG.ts +++ b/src/D3 Plotting Functions/Icons/initialiseIconSVG.ts @@ -1,6 +1,4 @@ -import * as d3 from "d3"; - -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual"; /** * This method initialises a plotting space for rendering a given NHS SVG icon. @@ -11,7 +9,7 @@ type SelectionBase = d3.Selection; * icon rendering function from the "Icons" folder. * */ -export default function initialiseIconSVG(selection: SelectionBase, icon_name: string, svg_width: number, svg_height: number, location: string, scaling: number, count: number): void { +export default function initialiseIconSVG(selection: svgBaseType, icon_name: string, svg_width: number, svg_height: number, location: string, scaling: number, count: number): void { const scaling_factor: number = (0.08 * (svg_height / 378)) * scaling const scale: string = `scale(${scaling_factor})` const icon_x: number = location.includes("Right") diff --git a/src/D3 Plotting Functions/Icons/neutralHigh.ts b/src/D3 Plotting Functions/Icons/neutralHigh.ts index d3babe6..1ea151e 100644 --- a/src/D3 Plotting Functions/Icons/neutralHigh.ts +++ b/src/D3 Plotting Functions/Icons/neutralHigh.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Variation - Neutral High icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function neutralHigh(selection: SelectionBase): void { +export default function neutralHigh(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -40,5 +39,3 @@ function neutralHigh(selection: SelectionBase): void { .attr("fill","#8000C0") .attr("fill-rule","evenodd") } - -export default neutralHigh diff --git a/src/D3 Plotting Functions/Icons/neutralLow.ts b/src/D3 Plotting Functions/Icons/neutralLow.ts index 34f5384..dba1f5c 100644 --- a/src/D3 Plotting Functions/Icons/neutralLow.ts +++ b/src/D3 Plotting Functions/Icons/neutralLow.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Variation - Neutral Low icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function neutralLow(selection: SelectionBase): void { +export default function neutralLow(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -40,5 +39,3 @@ function neutralLow(selection: SelectionBase): void { .attr("fill","#8000C0") .attr("fill-rule","evenodd") } - -export default neutralLow diff --git a/src/D3 Plotting Functions/Icons/pass.ts b/src/D3 Plotting Functions/Icons/pass.ts index acfe6e1..6017354 100644 --- a/src/D3 Plotting Functions/Icons/pass.ts +++ b/src/D3 Plotting Functions/Icons/pass.ts @@ -1,5 +1,4 @@ -import * as d3 from "d3"; -type SelectionBase = d3.Selection; +import { svgBaseType } from "../../visual" /** * Inline function to be called by D3 for rendering the Assurance - Pass icon. @@ -10,7 +9,7 @@ type SelectionBase = d3.Selection; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -function pass(selection: SelectionBase): void { +export default function pass(selection: svgBaseType): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") @@ -60,5 +59,3 @@ function pass(selection: SelectionBase): void { .attr("fill","none") .attr("fill-rule","evenodd") } - -export default pass diff --git a/src/Functions/BinaryFunctions.ts b/src/Functions/BinaryFunctions.ts index 1cfcbe8..c4fd8c1 100644 --- a/src/Functions/BinaryFunctions.ts +++ b/src/Functions/BinaryFunctions.ts @@ -2,7 +2,7 @@ import * as math from '@stdlib/math/base/special'; type ReturnT = Input1T extends Array ? BaseT[] : Input2T extends Array ? BaseT[] : BaseT; -function broadcast_binary(fun: (x: ScalarInput1T, y: ScalarInput2T) => ScalarReturnT) { +export default function broadcast_binary(fun: (x: ScalarInput1T, y: ScalarInput2T) => ScalarReturnT) { return function(x: T1, y: T2): ReturnT { // Need to provide type hints that the scalar type of the input arguments @@ -24,18 +24,8 @@ function broadcast_binary(fun: (x: // Need to have special handling for negative x, as pow will return NaN for // negative inputs with fractional exponents -const pow = broadcast_binary((x: number, y: number): number => (x >= 0.0) ? math.pow(x, y) : -math.pow(-x, y)); -const add = broadcast_binary((x: number, y: number): number => x + y); -const subtract = broadcast_binary((x: number, y: number): number => x - y); -const divide = broadcast_binary((x: number, y: number): number => x / y); -const multiply = broadcast_binary((x: number, y: number): number => x * y); - -export { - pow, - subtract, - add, - divide, - multiply -}; - -export default broadcast_binary; +export const pow = broadcast_binary((x: number, y: number): number => (x >= 0.0) ? math.pow(x, y) : -math.pow(-x, y)); +export const add = broadcast_binary((x: number, y: number): number => x + y); +export const subtract = broadcast_binary((x: number, y: number): number => x - y); +export const divide = broadcast_binary((x: number, y: number): number => x / y); +export const multiply = broadcast_binary((x: number, y: number): number => x * y); diff --git a/src/Functions/Constants.ts b/src/Functions/Constants.ts index ce27b06..ab60e89 100644 --- a/src/Functions/Constants.ts +++ b/src/Functions/Constants.ts @@ -1,7 +1,7 @@ import { sqrt, exp, lgamma, square } from "./UnaryFunctions"; import { subtract, add, multiply, divide } from "./BinaryFunctions"; -function c4(sampleSizes: number[]): number[] { +export function c4(sampleSizes: number[]): number[] { const filt_samp: number[] = sampleSizes.map(d => d <= 1 ? null : d); const Nminus1: number[] = subtract(filt_samp, 1); const lg_n2: number[] = lgamma(divide(filt_samp, 2)); @@ -12,30 +12,24 @@ function c4(sampleSizes: number[]): number[] { return multiply(term1, term2); } -function c5(sampleSizes: number[]): number[] { +export function c5(sampleSizes: number[]): number[] { return sqrt(subtract(1, square(c4(sampleSizes)))); } -function a3(sampleSizes: number[]): number[] { +export function a3(sampleSizes: number[]): number[] { const filt_samp: number[] = sampleSizes.map(d => d <= 1 ? null : d); return divide(3, multiply(c4(filt_samp), sqrt(filt_samp))); } -function b_helper(sampleSizes: number[], use95: boolean): number[] { +export function b_helper(sampleSizes: number[], use95: boolean): number[] { const sigma: number = use95 ? 2 : 3; return divide(multiply(sigma, c5(sampleSizes)), c4(sampleSizes)); } -function b3(sampleSizes: number[], use95: boolean): number[] { +export function b3(sampleSizes: number[], use95: boolean): number[] { return subtract(1, b_helper(sampleSizes, use95)); } -function b4(sampleSizes: number[], use95: boolean): number[] { +export function b4(sampleSizes: number[], use95: boolean): number[] { return add(1, b_helper(sampleSizes, use95)); } - -export { - a3, - b3, - b4 -}; diff --git a/src/Functions/UnaryFunctions.ts b/src/Functions/UnaryFunctions.ts index df9e20e..02c9c22 100644 --- a/src/Functions/UnaryFunctions.ts +++ b/src/Functions/UnaryFunctions.ts @@ -2,7 +2,7 @@ import * as math from '@stdlib/math/base/special'; type ReturnT = InputT extends Array ? BaseT[] : BaseT; -function broadcast_unary(fun: (x: ScalarInputT) => ScalarReturnT) { +export default function broadcast_unary(fun: (x: ScalarInputT) => ScalarReturnT) { return function(y: T): ReturnT { if (Array.isArray(y)) { return (y as ScalarInputT[]).map((d: ScalarInputT) => fun(d)) as ReturnT; @@ -12,16 +12,8 @@ function broadcast_unary(fun: (x: ScalarInputT) => }; } -const sqrt = broadcast_unary(math.sqrt); -const abs = broadcast_unary((x: number): number => (x ? math.abs(x) : x)); -const exp = broadcast_unary(math.exp); -const lgamma = broadcast_unary(math.gammaln); -const square = broadcast_unary((x: number): number => math.pow(x, 2)); - -export { - sqrt, - abs, - exp, - lgamma, - square -}; +export const sqrt = broadcast_unary(math.sqrt); +export const abs = broadcast_unary((x: number): number => (x ? math.abs(x) : x)); +export const exp = broadcast_unary(math.exp); +export const lgamma = broadcast_unary(math.gammaln); +export const square = broadcast_unary((x: number): number => math.pow(x, 2)); diff --git a/src/Functions/between.ts b/src/Functions/between.ts index af0ccef..2efb4ad 100644 --- a/src/Functions/between.ts +++ b/src/Functions/between.ts @@ -1,5 +1,3 @@ -function between(x: T, lower: T, upper: T): boolean { +export default function between(x: T, lower: T, upper: T): boolean { return (lower ? (x >= lower) : true) && (upper ? (x <= upper) : true); } - -export default between; diff --git a/src/Functions/buildTooltip.ts b/src/Functions/buildTooltip.ts index 59e6edf..6656467 100644 --- a/src/Functions/buildTooltip.ts +++ b/src/Functions/buildTooltip.ts @@ -21,8 +21,8 @@ const valueNames: Record = { const integerParams: string[] = ["c", "p", "pp"]; -function buildTooltip(index: number, controlLimits: controlLimitsClass, - inputData: dataClass, inputSettings: settingsClass): VisualTooltipDataItem[] { +export default function buildTooltip(index: number, controlLimits: controlLimitsClass, + inputData: dataClass, inputSettings: settingsClass): VisualTooltipDataItem[] { const date: string = controlLimits.keys[index].label; const value: number = controlLimits.values[index]; @@ -108,5 +108,3 @@ function buildTooltip(index: number, controlLimits: controlLimitsClass, return tooltip; } - -export default buildTooltip; diff --git a/src/Functions/checkInvalidDataView.ts b/src/Functions/checkInvalidDataView.ts index adfe9b5..6469d21 100644 --- a/src/Functions/checkInvalidDataView.ts +++ b/src/Functions/checkInvalidDataView.ts @@ -1,6 +1,6 @@ import powerbi from "powerbi-visuals-api"; -function checkInvalidDataView(inputDV: powerbi.DataView[]): boolean { +export default function checkInvalidDataView(inputDV: powerbi.DataView[]): boolean { const flag1: boolean = !inputDV || !inputDV[0] || !inputDV[0].categorical @@ -30,5 +30,3 @@ function checkInvalidDataView(inputDV: powerbi.DataView[]): boolean { || inputDV[0].categorical.categories.some(d => d.values.length < 1); return flag4; } - -export default checkInvalidDataView; diff --git a/src/Functions/checkValidInput.ts b/src/Functions/checkValidInput.ts index 8a8fdb5..6499f65 100644 --- a/src/Functions/checkValidInput.ts +++ b/src/Functions/checkValidInput.ts @@ -1,4 +1,4 @@ -function checkValidInput(numerator: number, denominator: number, xbar_sd: number, data_type: string): boolean { +export default function checkValidInput(numerator: number, denominator: number, xbar_sd: number, data_type: string): boolean { const denominatorConstraintRequired: string[] = ["p", "pprime", "u", "uprime"]; const denominatorRequired: string[] = ["p", "pprime", "u", "uprime", "xbar", "s"]; const denominatorConstraintForRunIChart: string[] = ["i", "run"]; @@ -17,4 +17,3 @@ function checkValidInput(numerator: number, denominator: number, xbar_sd: number return numeratorValid && denominatorValid && proportionDenominatorValid && runIChartDenominatorValid && xbarSDValid; } -export default checkValidInput; diff --git a/src/Functions/dateToFormattedString.ts b/src/Functions/dateToFormattedString.ts index aa88e1f..a555ff0 100644 --- a/src/Functions/dateToFormattedString.ts +++ b/src/Functions/dateToFormattedString.ts @@ -1,4 +1,4 @@ -import {defaultSettingsType} from "../Classes/defaultSettings" +import { defaultSettingsType } from "../defaultSettings" import broadcast_binary from "./BinaryFunctions" type dateFormat = { diff --git a/src/Functions/diff.ts b/src/Functions/diff.ts index 854354f..73e6a07 100644 --- a/src/Functions/diff.ts +++ b/src/Functions/diff.ts @@ -1,9 +1,7 @@ -function diff(x: number[]): number[] { +export default function diff(x: number[]): number[] { const consec_diff: number[] = new Array(x.length - 1); for (let i = 1; i < x.length; i++) { consec_diff[(i-1)] = (x[i] - x[(i-1)]); } return [null].concat(consec_diff); } - -export default diff; diff --git a/src/Functions/extractConditionalFormatting.ts b/src/Functions/extractConditionalFormatting.ts index 54a1532..59adaef 100644 --- a/src/Functions/extractConditionalFormatting.ts +++ b/src/Functions/extractConditionalFormatting.ts @@ -2,7 +2,7 @@ import powerbi from "powerbi-visuals-api" import DataViewCategoryColumn = powerbi.DataViewCategoryColumn; import DataViewCategorical = powerbi.DataViewCategorical; import settingsClass from "../Classes/settingsClass"; -import defaultSettings, { defaultSettingsType, defaultSettingsKey } from "../Classes/defaultSettings"; +import defaultSettings, { defaultSettingsType, defaultSettingsKey } from "../defaultSettings"; import extractSetting from "./extractSetting"; diff --git a/src/Functions/extractSetting.ts b/src/Functions/extractSetting.ts index 10d5a73..f022981 100644 --- a/src/Functions/extractSetting.ts +++ b/src/Functions/extractSetting.ts @@ -1,7 +1,7 @@ import powerbi from "powerbi-visuals-api" import DataViewObjects = powerbi.DataViewObjects import { dataViewObjects } from "powerbi-visuals-utils-dataviewutils"; -import { settingsScalarTypes } from "../Classes/defaultSettings"; +import { settingsScalarTypes } from "../defaultSettings"; function extractSetting(inputObjects: DataViewObjects, settingsGroup: string, diff --git a/src/Functions/extractValues.ts b/src/Functions/extractValues.ts index 8bbbc82..8070628 100644 --- a/src/Functions/extractValues.ts +++ b/src/Functions/extractValues.ts @@ -1,9 +1,7 @@ -function extractValues(valuesArray: T[], indexArray: number[]): T[] { +export default function extractValues(valuesArray: T[], indexArray: number[]): T[] { if (valuesArray) { return valuesArray.filter((_,idx) => indexArray.indexOf(idx) != -1) } else { return []; } } - -export default extractValues; diff --git a/src/Functions/first.ts b/src/Functions/first.ts index 4e0eb0a..94a36e0 100644 --- a/src/Functions/first.ts +++ b/src/Functions/first.ts @@ -1,11 +1,9 @@ type ScalarT = T extends (infer U)[] ? U : T; -function first(y: T): ScalarT { +export default function first(y: T): ScalarT { if (Array.isArray(y)) { return y[0] as ScalarT; } else { return y as ScalarT; } } - -export default first; diff --git a/src/Functions/getAesthetic.ts b/src/Functions/getAesthetic.ts index efb6cb2..1334b5c 100644 --- a/src/Functions/getAesthetic.ts +++ b/src/Functions/getAesthetic.ts @@ -10,10 +10,8 @@ const lineNameMap: Record = { "alt_targets" : "alt_target" } -function getAesthetic(type: string, group: string, aesthetic: string, inputSettings: settingsClass): string | number { +export default function getAesthetic(type: string, group: string, aesthetic: string, inputSettings: settingsClass): string | number { const mapName: string = group.includes("line") ? lineNameMap[type] : type; const settingName: string = aesthetic + "_" + mapName; return inputSettings[group][settingName]; } - -export default getAesthetic; diff --git a/src/Functions/rep.ts b/src/Functions/rep.ts index b200010..3fd45a1 100644 --- a/src/Functions/rep.ts +++ b/src/Functions/rep.ts @@ -1,5 +1,3 @@ -function rep(x: T, n: number) : T[] { +export default function rep(x: T, n: number) : T[] { return Array(n).fill(x); } - -export default rep; diff --git a/src/Functions/variationIconsToDraw.ts b/src/Functions/variationIconsToDraw.ts index 22c5aba..4f5d6f0 100644 --- a/src/Functions/variationIconsToDraw.ts +++ b/src/Functions/variationIconsToDraw.ts @@ -1,7 +1,7 @@ import viewModelClass from "../Classes/viewModelClass"; import controlLimitsClass from "../Classes/controlLimitsClass"; -export default function variationIconsToDraw(viewModel: viewModelClass): string[] { +export default function variationIconsToDraw(viewModel: viewModelClass): string[] { const currLimits: controlLimitsClass = viewModel.controlLimits; const imp_direction: string = viewModel.inputSettings.outliers.improvement_direction; const suffix_map: Record = { diff --git a/src/Limit Calculations/c.ts b/src/Limit Calculations/c.ts index 7e28054..4df66b0 100644 --- a/src/Limit Calculations/c.ts +++ b/src/Limit Calculations/c.ts @@ -4,7 +4,7 @@ import dataClass from "../Classes/dataClass" import controlLimitsClass from "../Classes/controlLimitsClass" import { LimitArgs } from "../Classes/viewModelClass"; -function cLimits(args: LimitArgs): controlLimitsClass { +export default function cLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const cl: number = d3.mean(inputData.numerators); const sigma: number = Math.sqrt(cl); @@ -19,5 +19,3 @@ function cLimits(args: LimitArgs): controlLimitsClass { ul99: rep(cl + 3*sigma, inputData.keys.length), }); } - -export default cLimits; diff --git a/src/Limit Calculations/g.ts b/src/Limit Calculations/g.ts index 2340498..e0e52f7 100644 --- a/src/Limit Calculations/g.ts +++ b/src/Limit Calculations/g.ts @@ -5,7 +5,7 @@ import dataClass from "../Classes/dataClass"; import controlLimitsClass from "../Classes/controlLimitsClass"; import { LimitArgs } from "../Classes/viewModelClass"; -function gLimits(args: LimitArgs): controlLimitsClass { +export default function gLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const cl: number = d3.mean(inputData.numerators); const sigma: number = sqrt(cl * (cl + 1)); @@ -20,5 +20,3 @@ function gLimits(args: LimitArgs): controlLimitsClass { ul99: rep(cl + 3*sigma, inputData.keys.length) }); } - -export default gLimits; diff --git a/src/Limit Calculations/i.ts b/src/Limit Calculations/i.ts index fac5772..8d0e68a 100644 --- a/src/Limit Calculations/i.ts +++ b/src/Limit Calculations/i.ts @@ -7,7 +7,7 @@ import dataClass from "../Classes/dataClass"; import controlLimitsClass from "../Classes/controlLimitsClass"; import { LimitArgs } from "../Classes/viewModelClass"; -function iLimits(args: LimitArgs): controlLimitsClass { +export default function iLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const useRatio: boolean = (inputData.denominators && inputData.denominators.length > 0); const ratio: number[] = useRatio @@ -35,5 +35,3 @@ function iLimits(args: LimitArgs): controlLimitsClass { ul99: rep(cl + 3 * sigma, inputData.keys.length) }); } - -export default iLimits; diff --git a/src/Limit Calculations/mr.ts b/src/Limit Calculations/mr.ts index 834a50d..f947fb0 100644 --- a/src/Limit Calculations/mr.ts +++ b/src/Limit Calculations/mr.ts @@ -7,7 +7,7 @@ import dataClass from "../Classes/dataClass"; import controlLimitsClass from "../Classes/controlLimitsClass"; import { LimitArgs } from "../Classes/viewModelClass"; -function mrLimits(args: LimitArgs): controlLimitsClass { +export default function mrLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const useRatio: boolean = (inputData.denominators && inputData.denominators.length > 0); const ratio: number[] = useRatio @@ -29,5 +29,3 @@ function mrLimits(args: LimitArgs): controlLimitsClass { ul99: rep(3.267 * cl, inputData.keys.length) }); } - -export default mrLimits; diff --git a/src/Limit Calculations/p.ts b/src/Limit Calculations/p.ts index 8cfeb28..c2bcc6c 100644 --- a/src/Limit Calculations/p.ts +++ b/src/Limit Calculations/p.ts @@ -7,7 +7,7 @@ import dataClass from "../Classes/dataClass"; import truncate from "../Functions/truncate" import { LimitArgs } from "../Classes/viewModelClass"; -function pLimits(args: LimitArgs): controlLimitsClass { +export default function pLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const cl: number = d3.sum(inputData.numerators) / d3.sum(inputData.denominators); const sigma: number[] = sqrt(divide(cl * (1 - cl), inputData.denominators)); @@ -25,4 +25,3 @@ function pLimits(args: LimitArgs): controlLimitsClass { }); } -export default pLimits; diff --git a/src/Limit Calculations/pprime.ts b/src/Limit Calculations/pprime.ts index 016dfd1..2ad7e17 100644 --- a/src/Limit Calculations/pprime.ts +++ b/src/Limit Calculations/pprime.ts @@ -8,7 +8,7 @@ import dataClass from "../Classes/dataClass"; import truncate from "../Functions/truncate"; import { LimitArgs } from "../Classes/viewModelClass"; -function pprimeLimits(args: LimitArgs): controlLimitsClass { +export default function pprimeLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const val: number[] = divide(inputData.numerators, inputData.denominators); const cl: number = d3.sum(inputData.numerators) / d3.sum(inputData.denominators); @@ -33,5 +33,3 @@ function pprimeLimits(args: LimitArgs): controlLimitsClass { ul99: truncate(add(cl, multiply(3, sigma)), {upper: 1}) }); } - -export default pprimeLimits; diff --git a/src/Limit Calculations/run.ts b/src/Limit Calculations/run.ts index 17067e5..d665cbc 100644 --- a/src/Limit Calculations/run.ts +++ b/src/Limit Calculations/run.ts @@ -5,7 +5,7 @@ import controlLimits from "../Classes/controlLimitsClass"; import dataClass from "../Classes/dataClass"; import { LimitArgs } from "../Classes/viewModelClass"; -function runLimits(args: LimitArgs): controlLimits { +export default function runLimits(args: LimitArgs): controlLimits { const inputData: dataClass = args.inputData; const useRatio: boolean = (inputData.denominators && inputData.denominators.length > 0); const ratio: number[] = useRatio @@ -25,5 +25,3 @@ function runLimits(args: LimitArgs): controlLimits { ul99: null }); } - -export default runLimits; diff --git a/src/Limit Calculations/s.ts b/src/Limit Calculations/s.ts index 2aad1fd..690aab2 100644 --- a/src/Limit Calculations/s.ts +++ b/src/Limit Calculations/s.ts @@ -7,7 +7,7 @@ import controlLimitsClass from "../Classes/controlLimitsClass"; import dataClass from "../Classes/dataClass"; import { LimitArgs } from "../Classes/viewModelClass"; -function sLimits(args: LimitArgs): controlLimitsClass { +export default function sLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const group_sd: number[] = inputData.numerators; const count_per_group: number[] = inputData.denominators; @@ -35,4 +35,3 @@ function sLimits(args: LimitArgs): controlLimitsClass { }); } -export default sLimits; diff --git a/src/Limit Calculations/t.ts b/src/Limit Calculations/t.ts index 6235e3f..176a32f 100644 --- a/src/Limit Calculations/t.ts +++ b/src/Limit Calculations/t.ts @@ -5,7 +5,7 @@ import dataClass from "../Classes/dataClass"; import truncate from "../Functions/truncate"; import { LimitArgs } from "../Classes/viewModelClass"; -function tLimits(args: LimitArgs): controlLimitsClass { +export default function tLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const val: number[] = pow(inputData.numerators, 1 / 3.6); const argsDataCopy: LimitArgs = args; @@ -22,4 +22,3 @@ function tLimits(args: LimitArgs): controlLimitsClass { return limits; } -export default tLimits; diff --git a/src/Limit Calculations/u.ts b/src/Limit Calculations/u.ts index 8fbfeef..f513cc0 100644 --- a/src/Limit Calculations/u.ts +++ b/src/Limit Calculations/u.ts @@ -7,7 +7,7 @@ import dataClass from "../Classes/dataClass"; import truncate from "../Functions/truncate"; import { LimitArgs } from "../Classes/viewModelClass"; -function uLimits(args: LimitArgs): controlLimitsClass { +export default function uLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const cl: number = divide(d3.sum(inputData.numerators),d3.sum(inputData.denominators)); const sigma: number[] = sqrt(divide(cl,inputData.denominators)); @@ -24,5 +24,3 @@ function uLimits(args: LimitArgs): controlLimitsClass { ul99: add(cl, multiply(3, sigma)) }) } - -export default uLimits; diff --git a/src/Limit Calculations/uprime.ts b/src/Limit Calculations/uprime.ts index 51045e0..1a2cb9d 100644 --- a/src/Limit Calculations/uprime.ts +++ b/src/Limit Calculations/uprime.ts @@ -8,7 +8,7 @@ import dataClass from "../Classes/dataClass"; import truncate from "../Functions/truncate"; import { LimitArgs } from "../Classes/viewModelClass"; -function uprimeLimits(args: LimitArgs): controlLimitsClass { +export default function uprimeLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; const val: number[] = divide(inputData.numerators, inputData.denominators); const cl: number = d3.sum(inputData.numerators) / d3.sum(inputData.denominators); @@ -33,5 +33,3 @@ function uprimeLimits(args: LimitArgs): controlLimitsClass { ul99: add(cl, multiply(3,sigma)) }); } - -export default uprimeLimits; diff --git a/src/Limit Calculations/xbar.ts b/src/Limit Calculations/xbar.ts index b1bb167..c4f616b 100644 --- a/src/Limit Calculations/xbar.ts +++ b/src/Limit Calculations/xbar.ts @@ -7,7 +7,7 @@ import controlLimitsClass from "../Classes/controlLimitsClass"; import dataClass from "../Classes/dataClass"; import { LimitArgs } from "../Classes/viewModelClass"; -function xbarLimits(args: LimitArgs): controlLimitsClass { +export default function xbarLimits(args: LimitArgs): controlLimitsClass { const inputData: dataClass = args.inputData; // Calculate number of observations in each group const count_per_group: number[] = inputData.denominators; @@ -41,5 +41,3 @@ function xbarLimits(args: LimitArgs): controlLimitsClass { count: count_per_group }) } - -export default xbarLimits; diff --git a/src/Outlier Flagging/astronomical.ts b/src/Outlier Flagging/astronomical.ts index 5d3b6c6..aaf7ebb 100644 --- a/src/Outlier Flagging/astronomical.ts +++ b/src/Outlier Flagging/astronomical.ts @@ -1,6 +1,6 @@ import between from "../Functions/between" -function astronomical(val: number[], ll99: number[], ul99: number[]): string[] { +export default function astronomical(val: number[], ll99: number[], ul99: number[]): string[] { return val.map((d, i) => { if (!between(d, ll99[i], ul99[i])) { return d > ul99[i] ? "upper" : "lower"; @@ -9,5 +9,3 @@ function astronomical(val: number[], ll99: number[], ul99: number[]): string[] { } }); } - -export default astronomical diff --git a/src/Outlier Flagging/shift.ts b/src/Outlier Flagging/shift.ts index f1b80f3..a1c1ae3 100644 --- a/src/Outlier Flagging/shift.ts +++ b/src/Outlier Flagging/shift.ts @@ -1,7 +1,7 @@ import * as math from '@stdlib/math/base/special'; import * as d3 from "d3"; -function shift(val: number[], targets: number[], n: number): string[] { +export default function shift(val: number[], targets: number[], n: number): string[] { const lagged_sign: number[] = val.map((d, i) => { return Math.sign(d - targets[i]); }); @@ -25,5 +25,3 @@ function shift(val: number[], targets: number[], n: number): string[] { return shift_detected; } - -export default shift diff --git a/src/Outlier Flagging/trend.ts b/src/Outlier Flagging/trend.ts index ccf47ef..b5588f0 100644 --- a/src/Outlier Flagging/trend.ts +++ b/src/Outlier Flagging/trend.ts @@ -1,7 +1,7 @@ import * as math from '@stdlib/math/base/special'; import * as d3 from "d3"; -function trend(val: number[], n: number): string[] { +export default function trend(val: number[], n: number): string[] { const lagged_sign: number[] = val.map((d, i) => { return (i == 0) ? i : Math.sign(d - val[i - 1]); }); @@ -27,4 +27,3 @@ function trend(val: number[], n: number): string[] { return trend_detected; } -export default trend diff --git a/src/Outlier Flagging/two_in_three.ts b/src/Outlier Flagging/two_in_three.ts index a27ae89..c6ded61 100644 --- a/src/Outlier Flagging/two_in_three.ts +++ b/src/Outlier Flagging/two_in_three.ts @@ -1,7 +1,7 @@ import * as math from '@stdlib/math/base/special'; import * as d3 from "d3"; -function two_in_three(val: number[], ll95: number[], ul95: number[]): string[] { +export default function two_in_three(val: number[], ll95: number[], ul95: number[]): string[] { const outside95: number[] = val.map((d, i) => { return d > ul95[i] ? 1 : (d < ll95[i] ? -1 : 0); }); @@ -26,5 +26,3 @@ function two_in_three(val: number[], ll95: number[], ul95: number[]): string[] { return two_in_three_detected; } - -export default two_in_three diff --git a/src/Classes/defaultSettings.ts b/src/defaultSettings.ts similarity index 100% rename from src/Classes/defaultSettings.ts rename to src/defaultSettings.ts From 25c069ed4df1e7564c689dceefcc1cf3d38e4050 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 15:05:10 +0300 Subject: [PATCH 5/8] Constants to use broadcasting for efficiency --- src/Functions/Constants.ts | 66 +++++++++++-------- src/Functions/extractConditionalFormatting.ts | 2 +- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/Functions/Constants.ts b/src/Functions/Constants.ts index ab60e89..cd2653c 100644 --- a/src/Functions/Constants.ts +++ b/src/Functions/Constants.ts @@ -1,35 +1,47 @@ import { sqrt, exp, lgamma, square } from "./UnaryFunctions"; -import { subtract, add, multiply, divide } from "./BinaryFunctions"; +import broadcast_unary from "./UnaryFunctions"; +import broadcast_binary from "./BinaryFunctions"; -export function c4(sampleSizes: number[]): number[] { - const filt_samp: number[] = sampleSizes.map(d => d <= 1 ? null : d); - const Nminus1: number[] = subtract(filt_samp, 1); - const lg_n2: number[] = lgamma(divide(filt_samp, 2)); - const lg_n12: number[] = lgamma(divide(Nminus1,2)); - const term1: number[] = sqrt(divide(2, Nminus1)); - const term2: number[] = exp(subtract(lg_n2,lg_n12)); +export const c4 = broadcast_unary( + (sampleSize: number): number => { + if ((sampleSize <= 1) || (sampleSize === null)) { + return null; + } + const Nminus1: number = sampleSize - 1; - return multiply(term1, term2); -} + return sqrt(2.0 / Nminus1) + * exp(lgamma(sampleSize / 2.0) - lgamma(Nminus1 / 2.0)); + } +); -export function c5(sampleSizes: number[]): number[] { - return sqrt(subtract(1, square(c4(sampleSizes)))); -} +export const c5 = broadcast_unary( + (sampleSize: number): number => { + return sqrt(1 - square(c4(sampleSize))); + } +); -export function a3(sampleSizes: number[]): number[] { - const filt_samp: number[] = sampleSizes.map(d => d <= 1 ? null : d); - return divide(3, multiply(c4(filt_samp), sqrt(filt_samp))); -} +export const a3 = broadcast_unary( + (sampleSize: number): number => { + const filt_samp: number = sampleSize <= 1 ? null : sampleSize; + return 3.0 / (c4(filt_samp) * sqrt(filt_samp)); + } +); -export function b_helper(sampleSizes: number[], use95: boolean): number[] { - const sigma: number = use95 ? 2 : 3; - return divide(multiply(sigma, c5(sampleSizes)), c4(sampleSizes)); -} +const b_helper = broadcast_binary( + (sampleSize: number, use95: boolean): number => { + const sigma: number = use95 ? 2 : 3; + return (sigma * c5(sampleSize)) / c4(sampleSize); + } +) -export function b3(sampleSizes: number[], use95: boolean): number[] { - return subtract(1, b_helper(sampleSizes, use95)); -} +export const b3 = broadcast_binary( + (sampleSize: number, use95: boolean): number => { + return 1 - b_helper(sampleSize, use95); + } +); -export function b4(sampleSizes: number[], use95: boolean): number[] { - return add(1, b_helper(sampleSizes, use95)); -} +export const b4 = broadcast_binary( + (sampleSize: number, use95: boolean): number => { + return 1 + b_helper(sampleSize, use95); + } +); diff --git a/src/Functions/extractConditionalFormatting.ts b/src/Functions/extractConditionalFormatting.ts index 59adaef..766ff85 100644 --- a/src/Functions/extractConditionalFormatting.ts +++ b/src/Functions/extractConditionalFormatting.ts @@ -7,7 +7,7 @@ import extractSetting from "./extractSetting"; function extractConditionalFormatting(categoricalView: DataViewCategorical, name: string, inputSettings: settingsClass): SettingsT[] { - if ((categoricalView === null) || (categoricalView === undefined)) { + if ((categoricalView.categories === null) || (categoricalView.categories === undefined)) { return [null]; } const inputCategories: DataViewCategoryColumn = (categoricalView.categories as DataViewCategoryColumn[])[0]; From 2391fdb41319839a0a2fb692adc5b4d978a30ffb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 15:14:05 +0300 Subject: [PATCH 6/8] More consistent exports --- src/Functions/checkFlagDirection.ts | 60 +++++++++---------- src/Functions/dateToFormattedString.ts | 40 ++++++------- src/Functions/extractConditionalFormatting.ts | 4 +- src/Functions/extractDataColumn.ts | 4 +- src/Functions/extractSetting.ts | 3 +- src/Functions/truncate.ts | 24 ++++---- 6 files changed, 62 insertions(+), 73 deletions(-) diff --git a/src/Functions/checkFlagDirection.ts b/src/Functions/checkFlagDirection.ts index a50657f..e762366 100644 --- a/src/Functions/checkFlagDirection.ts +++ b/src/Functions/checkFlagDirection.ts @@ -1,37 +1,35 @@ import broadcast_binary from "./BinaryFunctions" -function checkFlagDirection_impl(outlierStatus: string, flagSettings: {process_flag_type: string, improvement_direction: string}): string { - if (outlierStatus === "none") { - return outlierStatus; - } +export default broadcast_binary( + (outlierStatus: string, flagSettings: {process_flag_type: string, improvement_direction: string}): string => { + if (outlierStatus === "none") { + return outlierStatus; + } - const increaseDirectionMap: Record = { - "upper" : "improvement", - "lower" : "deterioration" - } - const decreaseDirectionMap: Record = { - "lower" : "improvement", - "upper" : "deterioration" - } - const neutralDirectionMap: Record = { - "lower" : "neutral_low", - "upper" : "neutral_high" - } - const flagDirectionMap: Record = { - "increase" : increaseDirectionMap[outlierStatus], - "decrease" : decreaseDirectionMap[outlierStatus], - "neutral" : neutralDirectionMap[outlierStatus] - } + const increaseDirectionMap: Record = { + "upper" : "improvement", + "lower" : "deterioration" + } + const decreaseDirectionMap: Record = { + "lower" : "improvement", + "upper" : "deterioration" + } + const neutralDirectionMap: Record = { + "lower" : "neutral_low", + "upper" : "neutral_high" + } + const flagDirectionMap: Record = { + "increase" : increaseDirectionMap[outlierStatus], + "decrease" : decreaseDirectionMap[outlierStatus], + "neutral" : neutralDirectionMap[outlierStatus] + } - const mappedFlag: string = flagDirectionMap[flagSettings.improvement_direction]; + const mappedFlag: string = flagDirectionMap[flagSettings.improvement_direction]; - if (flagSettings.process_flag_type !== "both") { - return mappedFlag === flagSettings.process_flag_type ? mappedFlag : "none"; - } else { - return mappedFlag; + if (flagSettings.process_flag_type !== "both") { + return mappedFlag === flagSettings.process_flag_type ? mappedFlag : "none"; + } else { + return mappedFlag; + } } -} - -const checkFlagDirection = broadcast_binary(checkFlagDirection_impl); - -export default checkFlagDirection; +); diff --git a/src/Functions/dateToFormattedString.ts b/src/Functions/dateToFormattedString.ts index a555ff0..5b6c110 100644 --- a/src/Functions/dateToFormattedString.ts +++ b/src/Functions/dateToFormattedString.ts @@ -35,25 +35,23 @@ let localeDateMap: Record= { "en-US" : "\"locale\": \"en-US\"" } -function dateToFormattedString_impl(input_date: Date, date_settings: defaultSettingsType["dates"]): string { - const inpLocale: string = date_settings.date_format_locale; - const inpDay: string = date_settings.date_format_day; - const inpMonth: string = date_settings.date_format_month; - const inpYear: string = date_settings.date_format_year; - const inpDelim: string = date_settings.date_format_delim; - - const formatString: string = `{ ${localeDateMap[inpLocale]}, \"options\": { \"day\" : \"2-digit\", ${monthDateMap[inpMonth]}, ${yearDateMap[inpYear]} }, ${delimDateMap[inpDelim]} }`; - const date_format: dateFormat = JSON.parse(formatString); - const formattedString: string = input_date.toLocaleDateString(date_format.locale, date_format.options) - .replace(/(\/|(\s|,\s))/gi, date_format.delimiter); - if (inpDay !== "DD") { - const weekday: string = input_date.toLocaleDateString(date_format.locale, {weekday : weekdayDateMap[inpDay]}) - return weekday + " " + formattedString; - } else { - return formattedString; +export default broadcast_binary( + (input_date: Date, date_settings: defaultSettingsType["dates"]): string => { + const inpLocale: string = date_settings.date_format_locale; + const inpDay: string = date_settings.date_format_day; + const inpMonth: string = date_settings.date_format_month; + const inpYear: string = date_settings.date_format_year; + const inpDelim: string = date_settings.date_format_delim; + + const formatString: string = `{ ${localeDateMap[inpLocale]}, \"options\": { \"day\" : \"2-digit\", ${monthDateMap[inpMonth]}, ${yearDateMap[inpYear]} }, ${delimDateMap[inpDelim]} }`; + const date_format: dateFormat = JSON.parse(formatString); + const formattedString: string = input_date.toLocaleDateString(date_format.locale, date_format.options) + .replace(/(\/|(\s|,\s))/gi, date_format.delimiter); + if (inpDay !== "DD") { + const weekday: string = input_date.toLocaleDateString(date_format.locale, {weekday : weekdayDateMap[inpDay]}) + return weekday + " " + formattedString; + } else { + return formattedString; + } } -} - -const dateToFormattedString = broadcast_binary(dateToFormattedString_impl) - -export default dateToFormattedString; +); diff --git a/src/Functions/extractConditionalFormatting.ts b/src/Functions/extractConditionalFormatting.ts index 766ff85..4118000 100644 --- a/src/Functions/extractConditionalFormatting.ts +++ b/src/Functions/extractConditionalFormatting.ts @@ -6,7 +6,7 @@ import defaultSettings, { defaultSettingsType, defaultSettingsKey } from "../def import extractSetting from "./extractSetting"; -function extractConditionalFormatting(categoricalView: DataViewCategorical, name: string, inputSettings: settingsClass): SettingsT[] { +export default function extractConditionalFormatting(categoricalView: DataViewCategorical, name: string, inputSettings: settingsClass): SettingsT[] { if ((categoricalView.categories === null) || (categoricalView.categories === undefined)) { return [null]; } @@ -29,5 +29,3 @@ function extractConditionalFormatting(inputView: DataViewCategorical, +export default function extractDataColumn(inputView: DataViewCategorical, name: string, inputSettings?: settingsClass): T { let columnRaw: DataViewValueColumn; @@ -41,5 +41,3 @@ function extractDataColumn(inputView: DataViewCategorical, return (columnRaw ? columnRaw.values : null) as T; } } - -export default extractDataColumn; diff --git a/src/Functions/extractSetting.ts b/src/Functions/extractSetting.ts index f022981..ba72efd 100644 --- a/src/Functions/extractSetting.ts +++ b/src/Functions/extractSetting.ts @@ -3,7 +3,7 @@ import DataViewObjects = powerbi.DataViewObjects import { dataViewObjects } from "powerbi-visuals-utils-dataviewutils"; import { settingsScalarTypes } from "../defaultSettings"; -function extractSetting(inputObjects: DataViewObjects, +export default function extractSetting(inputObjects: DataViewObjects, settingsGroup: string, settingName: string, defaultValue?: settingsScalarTypes): settingsScalarTypes { @@ -26,4 +26,3 @@ function extractSetting(inputObjects: DataViewObjects, } } -export default extractSetting diff --git a/src/Functions/truncate.ts b/src/Functions/truncate.ts index a73cdac..3243b00 100644 --- a/src/Functions/truncate.ts +++ b/src/Functions/truncate.ts @@ -1,17 +1,15 @@ import broadcast_binary from "./BinaryFunctions" export type truncateInputs = { lower?: number, upper?: number }; -function truncate_impl(val: number, limits: truncateInputs): number { - let rtn: number = val; - if (limits.lower || limits.lower == 0) { - rtn = (rtn < limits.lower ? limits.lower : rtn) +export default broadcast_binary( + (val: number, limits: truncateInputs): number => { + let rtn: number = val; + if (limits.lower || limits.lower == 0) { + rtn = (rtn < limits.lower ? limits.lower : rtn) + } + if (limits.upper) { + rtn = (rtn > limits.upper ? limits.upper : rtn); + } + return rtn; } - if (limits.upper) { - rtn = (rtn > limits.upper ? limits.upper : rtn); - } - return rtn; -} - -const truncate = broadcast_binary(truncate_impl) - -export default truncate; +); From 38b36d51d6c0546f85d6008e41694bf655f667e8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 15:19:49 +0300 Subject: [PATCH 7/8] Eslint --- pbiviz.json | 2 +- src/Classes/viewModelClass.ts | 4 ++-- src/D3 Plotting Functions/drawDots.ts | 1 - src/D3 Plotting Functions/drawTooltipLine.ts | 5 ++--- src/D3 Plotting Functions/drawXAxis.ts | 4 ++-- src/Functions/buildTooltip.ts | 2 +- src/Functions/dateToFormattedString.ts | 12 ++++++------ src/defaultSettings.ts | 2 +- 8 files changed, 15 insertions(+), 17 deletions(-) diff --git a/pbiviz.json b/pbiviz.json index b9a77b9..6fd6a54 100644 --- a/pbiviz.json +++ b/pbiviz.json @@ -4,7 +4,7 @@ "displayName":"SPC Charts", "guid":"PBISPC", "visualClassName":"Visual", - "version":"1.3.3.7", + "version":"1.3.3.8", "description":"A PowerBI custom visual for SPC charts", "supportUrl":"https://github.com/AUS-DOH-Safety-and-Quality/PowerBI-SPC", "gitHubUrl":"https://github.com/AUS-DOH-Safety-and-Quality/PowerBI-SPC" diff --git a/src/Classes/viewModelClass.ts b/src/Classes/viewModelClass.ts index a3405ca..ae12c13 100644 --- a/src/Classes/viewModelClass.ts +++ b/src/Classes/viewModelClass.ts @@ -55,8 +55,8 @@ export default class viewModelClass { const dv: powerbi.DataView[] = args.options.dataViews; this.inputSettings.update(dv[0]); - let split_indexes_storage: DataViewObject = dv[0].metadata.objects ? dv[0].metadata.objects.split_indexes_storage : null; - let split_indexes: DataViewPropertyValue = split_indexes_storage ? split_indexes_storage.split_indexes : null; + const split_indexes_storage: DataViewObject = dv[0].metadata.objects ? dv[0].metadata.objects.split_indexes_storage : null; + const split_indexes: DataViewPropertyValue = split_indexes_storage ? split_indexes_storage.split_indexes : null; this.splitIndexes = split_indexes ? JSON.parse((split_indexes)) : new Array(); // Make sure that the construction returns early with null members so diff --git a/src/D3 Plotting Functions/drawDots.ts b/src/D3 Plotting Functions/drawDots.ts index 61f425a..740b499 100644 --- a/src/D3 Plotting Functions/drawDots.ts +++ b/src/D3 Plotting Functions/drawDots.ts @@ -1,4 +1,3 @@ -import viewModelClass from "../Classes/viewModelClass"; import { plotData } from "../Classes/viewModelClass"; import between from "../Functions/between"; import { svgBaseType, Visual } from "../visual"; diff --git a/src/D3 Plotting Functions/drawTooltipLine.ts b/src/D3 Plotting Functions/drawTooltipLine.ts index 86f96da..f60f2cf 100644 --- a/src/D3 Plotting Functions/drawTooltipLine.ts +++ b/src/D3 Plotting Functions/drawTooltipLine.ts @@ -9,8 +9,8 @@ import { plotData } from "../Classes/viewModelClass"; export default function drawTooltipLine(selection: svgBaseType, viewModel: viewModelClass, tooltipService: ITooltipService) { selection.selectAll(".ttip-line").remove() if (!(viewModel.plotProperties.displayPlot)) { - selection.on("mousemove", (event) => { return; }) - .on("mouseleave", (event) => { return; }) + selection.on("mousemove", () => { return; }) + .on("mouseleave", () => { return; }) return; } @@ -44,7 +44,6 @@ export default function drawTooltipLine(selection: svgBaseType, viewModel: viewM coordinates: [x_coord, y_coord], isTouchEvent: false }); - const xAxisHeight: number = plotProperties.height - plotProperties.yAxis.start_padding; xAxisLine.style("stroke-opacity", 1) .attr("x1", x_coord) .attr("x2", x_coord); diff --git a/src/D3 Plotting Functions/drawXAxis.ts b/src/D3 Plotting Functions/drawXAxis.ts index ae99e5c..6fa57ea 100644 --- a/src/D3 Plotting Functions/drawXAxis.ts +++ b/src/D3 Plotting Functions/drawXAxis.ts @@ -51,8 +51,8 @@ export default function drawXAxis(selection: svgBaseType, viewModel: viewModelCl .style("font-family", xAxisProperties.tick_font) .style("fill", xAxisProperties.tick_colour); - let axisNode: SVGGElement = selection.selectAll(".xaxisgroup").selectAll(".tick text").node() as SVGGElement; - let xAxisCoordinates: DOMRect = axisNode.getBoundingClientRect() as DOMRect; + const axisNode: SVGGElement = selection.selectAll(".xaxisgroup").selectAll(".tick text").node() as SVGGElement; + const xAxisCoordinates: DOMRect = axisNode.getBoundingClientRect() as DOMRect; // Update padding and re-draw axis if large tick values rendered outside of plot const tickBelowPadding: number = xAxisCoordinates.bottom - xAxisHeight; diff --git a/src/Functions/buildTooltip.ts b/src/Functions/buildTooltip.ts index 6656467..49b6645 100644 --- a/src/Functions/buildTooltip.ts +++ b/src/Functions/buildTooltip.ts @@ -44,7 +44,7 @@ export default function buildTooltip(index: number, controlLimits: controlLimits multiplier = 100; } const suffix: string = prop_labels ? "%" : ""; - let intNumDen: boolean = integerParams.includes(chart_type); + const intNumDen: boolean = integerParams.includes(chart_type); const sig_figs: number = inputSettings.spc.sig_figs; const tooltip: VisualTooltipDataItem[] = new Array(); diff --git a/src/Functions/dateToFormattedString.ts b/src/Functions/dateToFormattedString.ts index 5b6c110..967ed35 100644 --- a/src/Functions/dateToFormattedString.ts +++ b/src/Functions/dateToFormattedString.ts @@ -7,30 +7,30 @@ type dateFormat = { delimiter?: string } -let weekdayDateMap: Record = { +const weekdayDateMap: Record = { "DD" : "short", "Thurs DD" : "short", "Thursday DD" : "long" } -let monthDateMap: Record= { +const monthDateMap: Record= { "MM" : "\"month\" : \"2-digit\"", "Mon" : "\"month\" : \"short\"", "Month" : "\"month\" : \"long\"" } -let yearDateMap: Record= { +const yearDateMap: Record= { "YYYY" : "\"year\" : \"numeric\"", "YY" : "\"year\" : \"2-digit\"" } -let delimDateMap: Record= { +const delimDateMap: Record= { "/" : "\"delimiter\": \"/\"", "-" : "\"delimiter\": \"-\"", " " : "\"delimiter\": \" \"" } -let localeDateMap: Record= { +const localeDateMap: Record= { "en-GB" : "\"locale\": \"en-GB\"", "en-US" : "\"locale\": \"en-US\"" } @@ -43,7 +43,7 @@ export default broadcast_binary( const inpYear: string = date_settings.date_format_year; const inpDelim: string = date_settings.date_format_delim; - const formatString: string = `{ ${localeDateMap[inpLocale]}, \"options\": { \"day\" : \"2-digit\", ${monthDateMap[inpMonth]}, ${yearDateMap[inpYear]} }, ${delimDateMap[inpDelim]} }`; + const formatString: string = `{ ${localeDateMap[inpLocale]}, "options": { "day" : "2-digit", ${monthDateMap[inpMonth]}, ${yearDateMap[inpYear]} }, ${delimDateMap[inpDelim]} }`; const date_format: dateFormat = JSON.parse(formatString); const formattedString: string = input_date.toLocaleDateString(date_format.locale, date_format.options) .replace(/(\/|(\s|,\s))/gi, date_format.delimiter); diff --git a/src/defaultSettings.ts b/src/defaultSettings.ts index 62a9928..ba8c9fb 100644 --- a/src/defaultSettings.ts +++ b/src/defaultSettings.ts @@ -1,4 +1,4 @@ -let defaultSettings = { +const defaultSettings = { canvas: { lower_padding: 10, upper_padding: 10, From 9acb37da201528a4d855f4d92b7f7192552b261c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Jul 2023 17:15:45 +0300 Subject: [PATCH 8/8] Missed changes --- src/Classes/controlLimitsClass.ts | 2 +- src/D3 Plotting Functions/drawDots.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Classes/controlLimitsClass.ts b/src/Classes/controlLimitsClass.ts index 56c2c1d..4adab5d 100644 --- a/src/Classes/controlLimitsClass.ts +++ b/src/Classes/controlLimitsClass.ts @@ -46,7 +46,7 @@ export default class controlLimitsClass { const multiplier: number = inputSettings.spc.multiplier; this.alt_targets = rep(inputSettings.spc.alt_target, this.values.length); - ["values", "targets", "ll99", "ll95", "ul95", "ul99"].forEach(limit => { + ["values", "targets", "alt_targets", "ll99", "ll95", "ul95", "ul99"].forEach(limit => { this[limit] = multiply(this[limit], multiplier) }) } diff --git a/src/D3 Plotting Functions/drawDots.ts b/src/D3 Plotting Functions/drawDots.ts index 740b499..6745ed1 100644 --- a/src/D3 Plotting Functions/drawDots.ts +++ b/src/D3 Plotting Functions/drawDots.ts @@ -19,7 +19,7 @@ export default function drawDots(selection: svgBaseType, visualObj: Visual) { .append("circle") .filter((d: plotData) => d.value !== null) .attr("cy", (d: plotData) => visualObj.viewModel.plotProperties.yScale(d.value)) - .attr("cx", (d: plotData) =>visualObj.viewModel.plotProperties.xScale(d.x)) + .attr("cx", (d: plotData) => visualObj.viewModel.plotProperties.xScale(d.x)) .attr("r", (d: plotData) => d.aesthetics.size) .style("fill", (d: plotData) => { return between(d.value, lower, upper) ? d.aesthetics.colour : "#FFFFFF";