Skip to content

Commit

Permalink
Have StringFormatter respect nan_format (bokeh#14017)
Browse files Browse the repository at this point in the history
* Have StrinFormatter respect nan_format

* split off null_format

* update defaults
  • Loading branch information
bryevdv authored Aug 14, 2024
1 parent f6ded42 commit d55ea6a
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 16 deletions.
39 changes: 34 additions & 5 deletions bokehjs/src/lib/models/widgets/tables/cell_formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export namespace StringFormatter {
text_color: p.ColorSpec
background_color: p.ColorSpec
nan_format: p.Property<string>
null_format: p.Property<string>
}
}

Expand All @@ -64,13 +65,20 @@ export class StringFormatter extends CellFormatter {
text_align: [ p.TextAlignSpec, {value: "left"} ],
text_color: [ p.ColorSpec, null ],
background_color: [ p.ColorSpec, null ],
nan_format: [ Str, "-"],
nan_format: [ Str, "NaN"],
null_format: [ Str, "(null)"],
}))
}

override doFormat(_row: any, _cell: any, value: any, _columnDef: any, dataContext: any): string {
const {font_style, text_align, text_color, background_color} = this

if (Number.isNaN(value)) {
value = this.nan_format
} else if (value == null) {
value = this.null_format
}

const text = div(value == null ? "" : `${value}`)

// Font style
Expand Down Expand Up @@ -181,6 +189,11 @@ export class ScientificFormatter extends StringFormatter {
power_limit_high: [ Float, 5 ],
power_limit_low: [ Float, -3 ],
}))

this.override<NumberFormatter.Props>({
nan_format: "-",
null_format: "-",
})
}

get scientific_limit_low(): number {
Expand All @@ -200,8 +213,10 @@ export class ScientificFormatter extends StringFormatter {
precision = 1
}

if (value == null || isNaN(value)) {
if (Number.isNaN(value)) {
value = this.nan_format
} else if (value == null) {
value = this.null_format
} else if (value == 0) {
value = to_fixed(value, 1)
} else if (need_sci) {
Expand Down Expand Up @@ -241,19 +256,26 @@ export class NumberFormatter extends StringFormatter {
rounding: [ RoundingFunction, "round" ],

}))

this.override<NumberFormatter.Props>({
nan_format: "-",
null_format: "-",
})
}

override doFormat(row: any, cell: any, value: any, columnDef: any, dataContext: any): string {
const {format, language, nan_format} = this
const {format, language, nan_format, null_format} = this
const rounding = (() => {
switch (this.rounding) {
case "round": case "nearest": return Math.round
case "floor": case "rounddown": return Math.floor
case "ceil": case "roundup": return Math.ceil
}
})()
if (value == null || isNaN(value)) {
if (Number.isNaN(value)) {
value = nan_format
} else if (value == null) {
value = null_format
} else {
value = Numbro.format(value, format, language, rounding)
}
Expand Down Expand Up @@ -310,6 +332,11 @@ export class DateFormatter extends StringFormatter {
this.define<DateFormatter.Props>(({Str}) => ({
format: [ Str, "ISO-8601" ],
}))

this.override<NumberFormatter.Props>({
nan_format: "-",
null_format: "-",
})
}

getFormat(): string | undefined {
Expand Down Expand Up @@ -363,8 +390,10 @@ export class DateFormatter extends StringFormatter {
const NaT = -9223372036854776.0

const date = (() => {
if (epoch == null || isNaN(epoch) || epoch == NaT) {
if (Number.isNaN(epoch) || epoch == NaT) {
return this.nan_format
} else if (value == null) {
return this.null_format
} else {
return tz(epoch, this.getFormat())
}
Expand Down
76 changes: 68 additions & 8 deletions bokehjs/test/unit/models/widgets/tables/cell_formatters.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {expect} from "assertions"

import {DateFormatter, NumberFormatter, ScientificFormatter} from "@bokehjs/models/widgets/tables/cell_formatters"
import {DateFormatter, NumberFormatter, ScientificFormatter, StringFormatter} from "@bokehjs/models/widgets/tables/cell_formatters"

describe("cell_formatters module", () => {

Expand Down Expand Up @@ -75,13 +75,18 @@ describe("cell_formatters module", () => {
})

describe("doFormat method", () => {
it("should apply default nan_format to null", () => {
it("should apply default null_format to null", () => {
const df = new DateFormatter()
expect(df.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">-</div>')
})

it("should apply nan_format to null", () => {
const df = new DateFormatter({nan_format: "--"})
it("should apply default nan_format to NaN", () => {
const df = new DateFormatter()
expect(df.doFormat(0, 0, NaN, {}, {})).to.be.equal('<div style="text-align: left;">-</div>')
})

it("should apply null_format to null", () => {
const df = new DateFormatter({null_format: "--"})
expect(df.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">--</div>')
})

Expand All @@ -97,16 +102,66 @@ describe("cell_formatters module", () => {
})
})

describe("StringFormatter", () => {

describe("doFormat method", () => {
it("should apply default null_format to null", () => {
const sf = new StringFormatter()
expect(sf.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">(null)</div>')
})

it("should apply default nan_format to nan", () => {
const sf = new StringFormatter()
expect(sf.doFormat(0, 0, NaN, {}, {})).to.be.equal('<div style="text-align: left;">NaN</div>')
})

it("should apply null_format to null", () => {
const sf = new StringFormatter({null_format: "--"})
expect(sf.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">--</div>')
})

it("should apply nan_format to nan", () => {
const sf = new StringFormatter({nan_format: "--"})
expect(sf.doFormat(0, 0, NaN, {}, {})).to.be.equal('<div style="text-align: left;">--</div>')
})

it("should apply font_style to value", () => {
const sf = new StringFormatter({font_style: "bold"})
expect(sf.doFormat(0, 0, "foo", {}, {})).to.be.equal('<div style="font-weight: bold; text-align: left;">foo</div>')
})

it("should apply text_align to value", () => {
const sf = new StringFormatter({text_align: "center"})
expect(sf.doFormat(0, 0, "foo", {}, {})).to.be.equal('<div style="text-align: center;">foo</div>')
})

it("should apply text_color to value", () => {
const sf = new StringFormatter({text_color: "#ff22dd"})
expect(sf.doFormat(0, 0, "foo", {}, {})).to.be.equal('<div style="text-align: left; color: rgb(255, 34, 221);">foo</div>')
})

it("should apply background_color to value", () => {
const sf = new StringFormatter({background_color: "#ff22dd"})
expect(sf.doFormat(0, 0, "foo", {}, {})).to.be.equal('<div style="text-align: left; background-color: rgb(255, 34, 221);">foo</div>')
})
})
})

describe("NumberFormatter", () => {

describe("doFormat method", () => {
it("should apply default nan_format to null", () => {
it("should apply default null_format to null", () => {
const df = new NumberFormatter()
expect(df.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">-</div>')
})

it("should apply nan_format to null", () => {
const df = new NumberFormatter({nan_format: "--"})
it("should apply default nan_format to nan", () => {
const df = new NumberFormatter()
expect(df.doFormat(0, 0, NaN, {}, {})).to.be.equal('<div style="text-align: left;">-</div>')
})

it("should apply null_format to null", () => {
const df = new NumberFormatter({null_format: "--"})
expect(df.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">--</div>')
})

Expand All @@ -125,8 +180,13 @@ describe("cell_formatters module", () => {
expect(df.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">-</div>')
})

it("should apply default nan_format to NaN", () => {
const df = new ScientificFormatter()
expect(df.doFormat(0, 0, NaN, {}, {})).to.be.equal('<div style="text-align: left;">-</div>')
})

it("should apply nan_format to null", () => {
const df = new ScientificFormatter({nan_format: "--"})
const df = new ScientificFormatter({null_format: "--"})
expect(df.doFormat(0, 0, null, {}, {})).to.be.equal('<div style="text-align: left;">--</div>')
})

Expand Down
20 changes: 18 additions & 2 deletions src/bokeh/models/widgets/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,12 @@ def __init__(self, *args, **kwargs) -> None:
An optional background color.
""")

nan_format = String("-", help="""
Formatting to apply to NaN and None values.
nan_format = String("NaN", help="""
Formatting to apply to NaN and NaT values.
""")

null_format = String("(null)", help="""
Formatting to apply to None / null values.
""")


Expand Down Expand Up @@ -180,6 +184,10 @@ def __init__(self, *args, **kwargs) -> None:
log(x) <= power_limit_low
""")

nan_format = Override(default="-")

null_format = Override(default="-")

class NumberFormatter(StringFormatter):
''' Number cell formatter.
Expand Down Expand Up @@ -273,6 +281,10 @@ def __init__(self, *args, **kwargs) -> None:
Rounding functions (round, floor, ceil) and their synonyms (nearest, rounddown, roundup).
""")

nan_format = Override(default="-")

null_format = Override(default="-")

class BooleanFormatter(CellFormatter):
''' Boolean (check mark) cell formatter.
Expand Down Expand Up @@ -500,6 +512,10 @@ def __init__(self, *args, **kwargs) -> None:
""")

nan_format = Override(default="-")

null_format = Override(default="-")


class HTMLTemplateFormatter(CellFormatter):
''' HTML formatter using a template.
Expand Down
9 changes: 8 additions & 1 deletion tests/baselines/defaults.json5
Original file line number Diff line number Diff line change
Expand Up @@ -7178,6 +7178,8 @@
},
DateFormatter: {
__extends__: "StringFormatter",
nan_format: "-",
null_format: "-",
format: "ISO-8601",
},
GroupingInfo: {
Expand Down Expand Up @@ -7206,6 +7208,8 @@
},
NumberFormatter: {
__extends__: "StringFormatter",
nan_format: "-",
null_format: "-",
format: "0,0",
language: "en",
rounding: "round",
Expand All @@ -7219,6 +7223,8 @@
},
ScientificFormatter: {
__extends__: "StringFormatter",
nan_format: "-",
null_format: "-",
precision: 10,
power_limit_high: 5,
power_limit_low: -3,
Expand Down Expand Up @@ -7249,7 +7255,8 @@
type: "value",
value: null,
},
nan_format: "-",
nan_format: "NaN",
null_format: "(null)",
},
SumAggregator: {
__extends__: "RowAggregator",
Expand Down

0 comments on commit d55ea6a

Please sign in to comment.