Skip to content

Commit d060b0e

Browse files
committed
feat(hygiene-issues): add summary counts component
1 parent 4b57cd7 commit d060b0e

9 files changed

Lines changed: 160 additions & 30 deletions

File tree

testgen/ui/assets/style.css

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ div[data-testid="stVerticalBlockBorderWrapper"]:has( > div > div[data-testid="st
253253
justify-content: center;
254254
}
255255

256+
.stVerticalBlock:has(> div.stElementContainer > div.stHtml > i.flex-end) {
257+
flex-wrap: wrap;
258+
}
259+
256260
.stVerticalBlock:has(> div.stElementContainer > div.stHtml > i.no-flex-gap) {
257261
gap: unset;
258262
}
@@ -439,6 +443,45 @@ div[data-testid="stPopoverBody"]:has(i.tg-header--help-wrapper) {
439443
}
440444
/* */
441445

446+
/* Summary counts component */
447+
.tg-summary-counts--label {
448+
margin-bottom: 4px;
449+
}
450+
451+
.tg-summary-counts {
452+
height: 100%;
453+
display: flex;
454+
flex-flow: row nowrap;
455+
align-items: flex-start;
456+
justify-content: flex-start;
457+
gap: 16px;
458+
}
459+
460+
.tg-summary-counts--item {
461+
display: flex;
462+
flex-flow: row nowrap;
463+
align-items: stretch;
464+
gap: 8px;
465+
}
466+
467+
.tg-summary-counts--bar {
468+
width: 4px;
469+
}
470+
471+
.tg-summary-counts--value {
472+
line-height: 1.2;
473+
}
474+
475+
.tg-summary-counts--value > div:first-child {
476+
color: var(--caption-text-color);
477+
font-size: 12px;
478+
}
479+
480+
.tg-summary-counts--value > div:last-child {
481+
font-size: 16px;
482+
}
483+
/* */
484+
442485
/* Export Menu */
443486
.st-key-tg--export-popover [data-testid="stPopoverButton"] > div:last-child {
444487
display: none;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @typedef SummaryItem
3+
* @type {object}
4+
* @property {string} value
5+
* @property {string} color
6+
* @property {string} label
7+
*
8+
* @typedef Properties
9+
* @type {object}
10+
* @property {Array.<SummaryItem>} items
11+
*/
12+
import van from '../van.min.js';
13+
import { getValue, loadStylesheet } from '../utils.js';
14+
import { colorMap, formatNumber } from '../display_utils.js';
15+
16+
const { div } = van.tags;
17+
18+
const SummaryCounts = (/** @type Properties */ props) => {
19+
loadStylesheet('summaryCounts', stylesheet);
20+
21+
return div(
22+
{ class: 'flex-row fx-gap-5' },
23+
getValue(props.items).map(item => div(
24+
{ class: 'flex-row fx-align-stretch fx-gap-2' },
25+
div({ class: 'tg-summary-counts--bar', style: `background-color: ${colorMap[item.color] || item.color};` }),
26+
div(
27+
div({ class: 'text-caption' }, item.label),
28+
div({ class: 'tg-summary-counts--count' }, formatNumber(item.value)),
29+
)
30+
)),
31+
);
32+
};
33+
34+
const stylesheet = new CSSStyleSheet();
35+
stylesheet.replace(`
36+
.tg-summary-counts--bar {
37+
width: 4px;
38+
}
39+
40+
.tg-summary-counts--count {
41+
font-size: 16px;
42+
}
43+
`);
44+
45+
export { SummaryCounts };

testgen/ui/components/frontend/js/pages/profiling_runs.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
*/
3535
import van from '../van.min.js';
3636
import { Tooltip } from '../components/tooltip.js';
37-
import { SummaryBar } from '../components/summary_bar.js';
37+
import { SummaryCounts } from '../components/summary_counts.js';
3838
import { Link } from '../components/link.js';
3939
import { Button } from '../components/button.js';
4040
import { Streamlit } from '../streamlit.js';
@@ -54,7 +54,7 @@ const ProfilingRuns = (/** @type Properties */ props) => {
5454
Streamlit.setFrameHeight(1);
5555
window.testgen.isPage = true;
5656

57-
const columns = ['5%', '15%', '20%', '20%', '30%', '10%'];
57+
const columns = ['5%', '15%', '15%', '20%', '35%', '10%'];
5858
const userCanEdit = getValue(props.permissions)?.can_edit ?? false;
5959

6060
const pageIndex = van.state(0);
@@ -300,23 +300,21 @@ const ProfilingRunItem = (
300300
),
301301
div(
302302
{ class: 'pr-3', style: `flex: ${columns[4]}` },
303-
item.anomaly_ct ? SummaryBar({
303+
item.anomaly_ct ? SummaryCounts({
304304
items: [
305305
{ label: 'Definite', value: item.anomalies_definite_ct, color: 'red' },
306306
{ label: 'Likely', value: item.anomalies_likely_ct, color: 'orange' },
307307
{ label: 'Possible', value: item.anomalies_possible_ct, color: 'yellow' },
308308
{ label: 'Dismissed', value: item.anomalies_dismissed_ct, color: 'grey' },
309309
],
310-
height: 3,
311-
width: 350,
312310
}) : '--',
313311
item.anomaly_ct ? Link({
314312
label: `View ${item.anomaly_ct} issues`,
315313
href: 'profiling-runs:hygiene',
316314
params: { 'run_id': item.profiling_run_id },
317315
underline: true,
318316
right_icon: 'chevron_right',
319-
style: 'margin-top: 8px;',
317+
style: 'margin-top: 4px;',
320318
'data-testid': 'profiling-run-item-viewissues'
321319
}) : null,
322320
),

testgen/ui/components/frontend/js/pages/project_dashboard.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { Link } from '../components/link.js';
4545
import { SummaryBar } from '../components/summary_bar.js';
4646
import { EmptyState, EMPTY_STATE_MESSAGE } from '../components/empty_state.js';
4747
import { ScoreMetric } from '../components/score_metric.js';
48+
import { SummaryCounts } from '../components/summary_counts.js';
4849

4950
const { div, h3, hr, span } = van.tags;
5051

@@ -163,8 +164,9 @@ const TableGroupLatestProfile = (/** @type TableGroupSummary */ tableGroup) => {
163164
const daysAgo = Math.round((new Date() - new Date(tableGroup.latest_profile_start * 1000)) / (1000 * 60 * 60 * 24));
164165

165166
return div(
167+
{ class: 'flex-row tg-overview--row', style: 'width: calc(100% - 115px);' },
166168
div(
167-
{ class: 'flex-row fx-gap-1 mb-2' },
169+
{ class: 'flex-row fx-gap-2', style: 'flex: 1 1 50%;' },
168170
span('Latest profile:'),
169171
Link({
170172
label: formatTimestamp(tableGroup.latest_profile_start),
@@ -174,7 +176,9 @@ const TableGroupLatestProfile = (/** @type TableGroupSummary */ tableGroup) => {
174176
daysAgo > staleProfileDays
175177
? span({ class: 'text-error' }, `(${daysAgo} days ago)`)
176178
: null,
177-
span('|'),
179+
),
180+
div(
181+
{ class: 'flex-row fx-gap-5', style: 'flex: 1 1 50%;' },
178182
Link({
179183
label: `${tableGroup.latest_anomalies_ct} hygiene issues`,
180184
href: 'profiling-runs:hygiene',
@@ -183,19 +187,17 @@ const TableGroupLatestProfile = (/** @type TableGroupSummary */ tableGroup) => {
183187
},
184188
width: 150,
185189
}),
186-
),
187-
tableGroup.latest_anomalies_ct
188-
? SummaryBar({
190+
tableGroup.latest_anomalies_ct
191+
? SummaryCounts({
189192
items: [
190193
{ label: 'Definite', value: parseInt(tableGroup.latest_anomalies_definite_ct), color: 'red' },
191194
{ label: 'Likely', value: parseInt(tableGroup.latest_anomalies_likely_ct), color: 'orange' },
192195
{ label: 'Possible', value: parseInt(tableGroup.latest_anomalies_possible_ct), color: 'yellow' },
193196
{ label: 'Dismissed', value: parseInt(tableGroup.latest_anomalies_dismissed_ct), color: 'grey' },
194197
],
195-
height: 3,
196-
width: 350,
197198
})
198199
: '',
200+
),
199201
);
200202
};
201203

testgen/ui/components/widgets/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@
2424
from testgen.ui.components.widgets.select import select
2525
from testgen.ui.components.widgets.sidebar import sidebar
2626
from testgen.ui.components.widgets.sorting_selector import sorting_selector
27-
from testgen.ui.components.widgets.summary_bar import summary_bar
27+
from testgen.ui.components.widgets.summary import summary_bar, summary_counts
2828
from testgen.ui.components.widgets.testgen_component import testgen_component
2929
from testgen.ui.components.widgets.wizard import WizardStep, wizard

testgen/ui/components/widgets/page.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ def flex_row_start(container: DeltaGenerator | None = None):
100100
_apply_html('<i class="flex-row flex-start"></i>', container)
101101

102102

103-
def flex_row_end(container: DeltaGenerator | None = None):
104-
_apply_html('<i class="flex-row flex-end"></i>', container)
103+
def flex_row_end(container: DeltaGenerator | None = None, wrap: bool = False):
104+
_apply_html(f'<i class="flex-row flex-end {"flex-wrap" if wrap else ""}"></i>', container)
105105

106106

107107
def flex_row_center(container: DeltaGenerator | None = None):

testgen/ui/components/widgets/summary_bar.py renamed to testgen/ui/components/widgets/summary.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ def summary_bar(
2626
:param items: list of dicts with value, label, and color
2727
:param height: height of bar in pixels, default=24
2828
:param width: width of bar in pixels, default is 100% of parent
29-
:param key: unique key to give the component a persisting state
3029
"""
3130

3231
label_div = ""
@@ -52,14 +51,61 @@ def summary_bar(
5251
"""
5352

5453
st.html(f"""
55-
<div class="tg-summary-bar-wrapper">
54+
<div>
5655
{label_div}
5756
<div class="tg-summary-bar" style="height: {height}px; max-width: {f'{width}px' if width else '100%'};">
5857
{item_spans}
5958
</div>
6059
{caption_div}
6160
</div>
6261
""")
62+
63+
64+
def summary_counts(
65+
items: list["SummaryItem"],
66+
label: str | None = None,
67+
) -> None:
68+
"""
69+
Testgen component to display summary counts.
70+
71+
# Parameters
72+
:param items: list of dicts with value, label, and color
73+
"""
74+
75+
label_div = ""
76+
item_spans = ""
77+
caption_div = ""
78+
79+
if label:
80+
label_div = f"""
81+
<div class="tg-summary-counts--label">
82+
{label}
83+
</div>
84+
"""
85+
86+
item_divs = "".join(
87+
[
88+
f"""
89+
<div class="tg-summary-counts--item">
90+
<div class="tg-summary-counts--bar" style="background-color: {COLOR_MAP.get(item["color"], item["color"])};"></div>
91+
<div class="tg-summary-counts--value">
92+
<div>{item["label"]}</div>
93+
<div>{item["value"]}</div>
94+
</div>
95+
</div>
96+
"""
97+
for item in items
98+
]
99+
)
100+
101+
st.html(f"""
102+
<div>
103+
{label_div}
104+
<div class="tg-summary-counts">
105+
{item_divs}
106+
</div>
107+
</div>
108+
""")
63109

64110

65111
class SummaryItem(typing.TypedDict):

testgen/ui/views/hygiene_issues.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ def render(
7070
],
7171
)
7272

73-
others_summary_column, pii_summary_column, score_column, actions_column, export_button_column = st.columns([.2, .2, .15, .3, .15], vertical_alignment="bottom")
73+
others_summary_column, pii_summary_column, score_column, actions_column, export_button_column = st.columns([.25, .2, .1, .3, .15], vertical_alignment="bottom")
7474
(liklihood_filter_column, table_filter_column, column_filter_column, issue_type_filter_column, action_filter_column, sort_column) = (
7575
st.columns([.2, .15, .2, .2, .15, .1], vertical_alignment="bottom")
7676
)
77-
testgen.flex_row_end(actions_column)
77+
testgen.flex_row_end(actions_column, wrap=True)
7878
testgen.flex_row_end(export_button_column)
7979

8080
filters_changed = False
@@ -187,21 +187,17 @@ def render(
187187
summaries = get_profiling_anomaly_summary(run_id)
188188
others_summary = [summary for summary in summaries if summary.get("type") != "PII"]
189189
with others_summary_column:
190-
testgen.summary_bar(
190+
testgen.summary_counts(
191191
items=others_summary,
192192
label="Hygiene Issues",
193-
height=20,
194-
width=400,
195193
)
196194

197195
anomalies_pii_summary = [summary for summary in summaries if summary.get("type") == "PII"]
198196
if anomalies_pii_summary:
199197
with pii_summary_column:
200-
testgen.summary_bar(
198+
testgen.summary_counts(
201199
items=anomalies_pii_summary,
202-
label="Potential PII",
203-
height=20,
204-
width=400,
200+
label="Potential PII (Risk)",
205201
)
206202

207203
selected, selected_row = fm.render_grid_select(
@@ -525,8 +521,8 @@ def get_profiling_anomaly_summary(profile_run_id: str) -> list[dict]:
525521
{ "label": "Likely", "value": result.likely_ct, "color": "orange" },
526522
{ "label": "Possible", "value": result.possible_ct, "color": "yellow" },
527523
{ "label": "Dismissed", "value": result.dismissed_ct, "color": "grey" },
528-
{ "label": "High Risk", "value": result.pii_high_ct, "color": "red", "type": "PII" },
529-
{ "label": "Moderate Risk", "value": result.pii_moderate_ct, "color": "orange", "type": "PII" },
524+
{ "label": "High", "value": result.pii_high_ct, "color": "red", "type": "PII" },
525+
{ "label": "Moderate", "value": result.pii_moderate_ct, "color": "orange", "type": "PII" },
530526
{ "label": "Dismissed", "value": result.pii_dismissed_ct, "color": "grey", "type": "PII" },
531527
]
532528

testgen/ui/views/test_results.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def render(
9090
[.175, .2, .2, .175, .15, .1], vertical_alignment="bottom"
9191
)
9292

93-
testgen.flex_row_end(actions_column)
93+
testgen.flex_row_end(actions_column, wrap=True)
9494
testgen.flex_row_end(export_button_column)
9595

9696
filters_changed = False

0 commit comments

Comments
 (0)