Skip to content

Commit d200674

Browse files
committed
Adding feature direction and moving suppression rules to each feature (#960)
* adding feature direction and moving suppression rules to each feature Signed-off-by: Amit Galitzky <amgalitz@amazon.com> * fixing bug where two conditions didn't display Signed-off-by: Amit Galitzky <amgalitz@amazon.com> * improve error handling Signed-off-by: Amit Galitzky <amgalitz@amazon.com> * style changes and removing extra errors Signed-off-by: Amit Galitzky <amgalitz@amazon.com> * remove console log Signed-off-by: Amit Galitzky <amgalitz@amazon.com> --------- Signed-off-by: Amit Galitzky <amgalitz@amazon.com>
1 parent 2cf18c6 commit d200674

File tree

34 files changed

+8265
-888
lines changed

34 files changed

+8265
-888
lines changed

public/components/FormattedFormRow/FormattedFormRow.tsx

+15-16
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111

1212
import React, { ReactElement, ReactNode } from 'react';
13-
import { EuiCompressedFormRow, EuiText, EuiLink, EuiIcon } from '@elastic/eui';
13+
import { EuiCompressedFormRow, EuiText, EuiLink, EuiIcon, EuiToolTip } from '@elastic/eui';
1414

1515
type FormattedFormRowProps = {
1616
title?: string;
@@ -22,28 +22,27 @@ type FormattedFormRowProps = {
2222
fullWidth?: boolean;
2323
helpText?: string;
2424
hintLink?: string;
25+
linkToolTip?: boolean;
2526
};
2627

2728
export const FormattedFormRow = (props: FormattedFormRowProps) => {
28-
let hints;
29-
if (props.hint) {
30-
const hintTexts = Array.isArray(props.hint) ? props.hint : [props.hint];
31-
hints = hintTexts.map((hint, i) => {
32-
return (
29+
const hints = props.hint
30+
? (Array.isArray(props.hint) ? props.hint : [props.hint]).map((hint, i) => (
3331
<EuiText key={i} className="sublabel" style={{ maxWidth: '400px' }}>
3432
{hint}
35-
{props.hintLink ? ' ' : null}
36-
{props.hintLink ? (
37-
<EuiLink href={props.hintLink} target="_blank">
38-
Learn more
39-
</EuiLink>
40-
) : null}
33+
{props.hintLink && (
34+
<>
35+
{' '}
36+
<EuiLink href={props.hintLink} target="_blank">
37+
Learn more
38+
</EuiLink>
39+
</>
40+
)}
4141
</EuiText>
42-
);
43-
});
44-
}
42+
))
43+
: null;
4544

46-
const { formattedTitle, ...euiFormRowProps } = props;
45+
const { formattedTitle, linkToolTip, ...euiFormRowProps } = props;
4746

4847
return (
4948
<EuiCompressedFormRow

public/models/types.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ export enum ThresholdType {
9292
* (b-a)/|a| is less than or equal to ignoreNearExpectedFromBelowByRatio.
9393
*/
9494
EXPECTED_OVER_ACTUAL_RATIO = "EXPECTED_OVER_ACTUAL_RATIO",
95+
96+
/**
97+
* Specifies a threshold for ignoring anomalies based on whether the actual value
98+
* is over the expected value returned from the model.
99+
*/
100+
ACTUAL_IS_OVER_EXPECTED = "ACTUAL_IS_OVER_EXPECTED",
101+
102+
/**
103+
* Specifies a threshold for ignoring anomalies based on whether the actual value
104+
* is below the expected value returned from the model.
105+
* */
106+
ACTUAL_IS_BELOW_EXPECTED = "ACTUAL_IS_BELOW_EXPECTED",
95107
}
96108

97109
// Method to get the description of ThresholdType
@@ -113,7 +125,7 @@ export interface Rule {
113125
export interface Condition {
114126
featureName: string;
115127
thresholdType: ThresholdType;
116-
operator: Operator;
117-
value: number;
128+
operator?: Operator;
129+
value?: number;
118130
}
119131

public/pages/ConfigureModel/components/AdvancedSettings/AdvancedSettings.tsx

+2-195
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
EuiButtonIcon,
2222
EuiCompressedFieldText,
2323
EuiToolTip,
24+
EuiButtonEmpty,
2425
} from '@elastic/eui';
2526
import { Field, FieldProps, FieldArray } from 'formik';
2627
import React, { useEffect, useState } from 'react';
@@ -48,47 +49,7 @@ export function AdvancedSettings(props: AdvancedSettingsProps) {
4849
{ value: SparseDataOptionValue.SET_TO_ZERO, text: 'Set to zero' },
4950
{ value: SparseDataOptionValue.CUSTOM_VALUE, text: 'Custom value' },
5051
];
51-
52-
const aboveBelowOptions = [
53-
{ value: 'above', text: 'above' },
54-
{ value: 'below', text: 'below' },
55-
];
56-
57-
function extractArrayError(fieldName: string, form: any): string {
58-
const error = form.errors[fieldName];
59-
console.log('Error for field:', fieldName, error); // Log the error for debugging
60-
61-
// Check if the error is an array with objects inside
62-
if (Array.isArray(error) && error.length > 0) {
63-
// Iterate through the array to find the first non-empty error message
64-
for (const err of error) {
65-
if (typeof err === 'object' && err !== null) {
66-
const entry = Object.entries(err).find(
67-
([_, fieldError]) => fieldError
68-
); // Find the first entry with a non-empty error message
69-
if (entry) {
70-
const [fieldKey, fieldError] = entry;
71-
72-
// Replace fieldKey with a more user-friendly name if it matches specific fields
73-
const friendlyFieldName =
74-
fieldKey === 'absoluteThreshold'
75-
? 'absolute threshold'
76-
: fieldKey === 'relativeThreshold'
77-
? 'relative threshold'
78-
: fieldKey; // Use the original fieldKey if no match
79-
80-
return typeof fieldError === 'string'
81-
? `${friendlyFieldName} ${fieldError.toLowerCase()}` // Format the error message with the friendly field name
82-
: String(fieldError || '');
83-
}
84-
}
85-
}
86-
}
87-
88-
// Default case to handle other types of errors
89-
return typeof error === 'string' ? error : String(error || '');
90-
}
91-
52+
9253
return (
9354
<ContentPanel
9455
title={
@@ -257,160 +218,6 @@ export function AdvancedSettings(props: AdvancedSettingsProps) {
257218
);
258219
}}
259220
</Field>
260-
261-
<EuiSpacer size="m" />
262-
<FieldArray name="suppressionRules">
263-
{(arrayHelpers) => (
264-
<>
265-
<Field name="suppressionRules">
266-
{({ field, form }: FieldProps) => (
267-
<>
268-
<EuiFlexGroup>
269-
{/* Controls the width of the whole row as FormattedFormRow does not allow that. Otherwise, our row is too packed. */}
270-
<EuiFlexItem
271-
grow={false}
272-
style={{ maxWidth: '1200px' }}
273-
>
274-
<FormattedFormRow
275-
title="Suppression Rules"
276-
hint={[
277-
`Set rules to ignore anomalies by comparing actual values against expected values.
278-
Anomalies can be ignored if the difference is within a specified absolute value or a relative percentage of the expected value.`,
279-
]}
280-
hintLink={`${BASE_DOCS_LINK}/ad`}
281-
isInvalid={isInvalid(field.name, form)}
282-
error={extractArrayError(field.name, form)}
283-
fullWidth
284-
>
285-
<>
286-
{form.values.suppressionRules?.map(
287-
(rule, index) => (
288-
<EuiFlexGroup
289-
key={index}
290-
gutterSize="s"
291-
alignItems="center"
292-
>
293-
<EuiFlexItem grow={false}>
294-
<EuiText size="s">
295-
Ignore anomalies for the feature
296-
</EuiText>
297-
</EuiFlexItem>
298-
<EuiFlexItem grow={2}>
299-
<Field
300-
name={`suppressionRules.${index}.featureName`}
301-
>
302-
{({ field }: FieldProps) => (
303-
<EuiCompressedFieldText
304-
placeholder="Feature name"
305-
{...field}
306-
fullWidth
307-
/>
308-
)}
309-
</Field>
310-
</EuiFlexItem>
311-
<EuiFlexItem grow={false}>
312-
<EuiText size="s">
313-
when the actual value is no more than
314-
</EuiText>
315-
</EuiFlexItem>
316-
<EuiFlexItem grow={1}>
317-
<EuiToolTip content="Absolute threshold value">
318-
<Field
319-
name={`suppressionRules.${index}.absoluteThreshold`}
320-
validate={validatePositiveDecimal}
321-
>
322-
{({ field }: FieldProps) => (
323-
<EuiCompressedFieldNumber
324-
placeholder="Absolute"
325-
{...field}
326-
value={field.value || ''}
327-
/>
328-
)}
329-
</Field>
330-
</EuiToolTip>
331-
</EuiFlexItem>
332-
<EuiFlexItem grow={false}>
333-
<EuiText size="s">or</EuiText>
334-
</EuiFlexItem>
335-
<EuiFlexItem grow={1}>
336-
<EuiToolTip content="Relative threshold value as a percentage">
337-
<Field
338-
name={`suppressionRules.${index}.relativeThreshold`}
339-
validate={validatePositiveDecimal}
340-
>
341-
{({ field }: FieldProps) => (
342-
<div
343-
style={{
344-
display: 'flex',
345-
alignItems: 'center',
346-
}}
347-
>
348-
<EuiCompressedFieldNumber
349-
placeholder="Relative"
350-
{...field}
351-
value={field.value || ''}
352-
/>
353-
<EuiText size="s">%</EuiText>
354-
</div>
355-
)}
356-
</Field>
357-
</EuiToolTip>
358-
</EuiFlexItem>
359-
<EuiFlexItem grow={1}>
360-
<EuiToolTip content="Select above or below expected value">
361-
<Field
362-
name={`suppressionRules.${index}.aboveBelow`}
363-
>
364-
{({ field }: FieldProps) => (
365-
<EuiCompressedSelect
366-
options={aboveBelowOptions}
367-
{...field}
368-
/>
369-
)}
370-
</Field>
371-
</EuiToolTip>
372-
</EuiFlexItem>
373-
<EuiFlexItem grow={false}>
374-
<EuiText size="s">
375-
the expected value.
376-
</EuiText>
377-
</EuiFlexItem>
378-
<EuiFlexItem grow={false}>
379-
<EuiButtonIcon
380-
iconType="trash"
381-
color="danger"
382-
aria-label="Delete rule"
383-
onClick={() =>
384-
arrayHelpers.remove(index)
385-
}
386-
/>
387-
</EuiFlexItem>
388-
</EuiFlexGroup>
389-
)
390-
)}
391-
</>
392-
</FormattedFormRow>
393-
</EuiFlexItem>
394-
</EuiFlexGroup>
395-
</>
396-
)}
397-
</Field>
398-
<EuiSpacer size="s" />
399-
<EuiButtonIcon
400-
iconType="plusInCircle"
401-
onClick={() =>
402-
arrayHelpers.push({
403-
fieldName: '',
404-
absoluteThreshold: null, // Set to null to allow empty inputs
405-
relativeThreshold: null, // Set to null to allow empty inputs
406-
aboveBelow: 'above',
407-
})
408-
}
409-
aria-label="Add rule"
410-
/>
411-
</>
412-
)}
413-
</FieldArray>
414221
</>
415222
) : null}
416223
</ContentPanel>

public/pages/ConfigureModel/components/AdvancedSettings/__tests__/AdvancedSettings.test.tsx

-74
This file was deleted.

0 commit comments

Comments
 (0)