Skip to content

Commit

Permalink
adding feature direction and moving suppression rules to each feature
Browse files Browse the repository at this point in the history
Signed-off-by: Amit Galitzky <[email protected]>
  • Loading branch information
amitgalitz committed Dec 19, 2024
1 parent f152d98 commit 104aa8a
Show file tree
Hide file tree
Showing 33 changed files with 8,226 additions and 870 deletions.
29 changes: 14 additions & 15 deletions public/components/FormattedFormRow/FormattedFormRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/

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

type FormattedFormRowProps = {
title?: string;
Expand All @@ -22,26 +22,25 @@ type FormattedFormRowProps = {
fullWidth?: boolean;
helpText?: string;
hintLink?: string;
linkToolTip?: boolean;
};

export const FormattedFormRow = (props: FormattedFormRowProps) => {
let hints;
if (props.hint) {
const hintTexts = Array.isArray(props.hint) ? props.hint : [props.hint];
hints = hintTexts.map((hint, i) => {
return (
const hints = props.hint
? (Array.isArray(props.hint) ? props.hint : [props.hint]).map((hint, i) => (
<EuiText key={i} className="sublabel" style={{ maxWidth: '400px' }}>
{hint}
{props.hintLink ? ' ' : null}
{props.hintLink ? (
<EuiLink href={props.hintLink} target="_blank">
Learn more
</EuiLink>
) : null}
{props.hintLink && (
<>
{' '}
<EuiLink href={props.hintLink} target="_blank">
Learn more
</EuiLink>
</>
)}
</EuiText>
);
});
}
))
: null;

const { formattedTitle, ...euiFormRowProps } = props;

Expand Down
16 changes: 14 additions & 2 deletions public/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ export enum ThresholdType {
* (b-a)/|a| is less than or equal to ignoreNearExpectedFromBelowByRatio.
*/
EXPECTED_OVER_ACTUAL_RATIO = "EXPECTED_OVER_ACTUAL_RATIO",

/**
* Specifies a threshold for ignoring anomalies based on whether the actual value
* is over the expected value returned from the model.
*/
ACTUAL_IS_OVER_EXPECTED = "ACTUAL_IS_OVER_EXPECTED",

/**
* Specifies a threshold for ignoring anomalies based on whether the actual value
* is below the expected value returned from the model.
* */
ACTUAL_IS_BELOW_EXPECTED = "ACTUAL_IS_BELOW_EXPECTED",
}

// Method to get the description of ThresholdType
Expand All @@ -113,7 +125,7 @@ export interface Rule {
export interface Condition {
featureName: string;
thresholdType: ThresholdType;
operator: Operator;
value: number;
operator?: Operator;
value?: number;
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
EuiButtonIcon,
EuiCompressedFieldText,
EuiToolTip,
EuiButtonEmpty,
} from '@elastic/eui';
import { Field, FieldProps, FieldArray } from 'formik';
import React, { useEffect, useState } from 'react';
Expand Down Expand Up @@ -48,47 +49,7 @@ export function AdvancedSettings(props: AdvancedSettingsProps) {
{ value: SparseDataOptionValue.SET_TO_ZERO, text: 'Set to zero' },
{ value: SparseDataOptionValue.CUSTOM_VALUE, text: 'Custom value' },
];

const aboveBelowOptions = [
{ value: 'above', text: 'above' },
{ value: 'below', text: 'below' },
];

function extractArrayError(fieldName: string, form: any): string {
const error = form.errors[fieldName];
console.log('Error for field:', fieldName, error); // Log the error for debugging

// Check if the error is an array with objects inside
if (Array.isArray(error) && error.length > 0) {
// Iterate through the array to find the first non-empty error message
for (const err of error) {
if (typeof err === 'object' && err !== null) {
const entry = Object.entries(err).find(
([_, fieldError]) => fieldError
); // Find the first entry with a non-empty error message
if (entry) {
const [fieldKey, fieldError] = entry;

// Replace fieldKey with a more user-friendly name if it matches specific fields
const friendlyFieldName =
fieldKey === 'absoluteThreshold'
? 'absolute threshold'
: fieldKey === 'relativeThreshold'
? 'relative threshold'
: fieldKey; // Use the original fieldKey if no match

return typeof fieldError === 'string'
? `${friendlyFieldName} ${fieldError.toLowerCase()}` // Format the error message with the friendly field name
: String(fieldError || '');
}
}
}
}

// Default case to handle other types of errors
return typeof error === 'string' ? error : String(error || '');
}


return (
<ContentPanel
title={
Expand Down Expand Up @@ -257,160 +218,6 @@ export function AdvancedSettings(props: AdvancedSettingsProps) {
);
}}
</Field>

<EuiSpacer size="m" />
<FieldArray name="suppressionRules">
{(arrayHelpers) => (
<>
<Field name="suppressionRules">
{({ field, form }: FieldProps) => (
<>
<EuiFlexGroup>
{/* Controls the width of the whole row as FormattedFormRow does not allow that. Otherwise, our row is too packed. */}
<EuiFlexItem
grow={false}
style={{ maxWidth: '1200px' }}
>
<FormattedFormRow
title="Suppression Rules"
hint={[
`Set rules to ignore anomalies by comparing actual values against expected values.
Anomalies can be ignored if the difference is within a specified absolute value or a relative percentage of the expected value.`,
]}
hintLink={`${BASE_DOCS_LINK}/ad`}
isInvalid={isInvalid(field.name, form)}
error={extractArrayError(field.name, form)}
fullWidth
>
<>
{form.values.suppressionRules?.map(
(rule, index) => (
<EuiFlexGroup
key={index}
gutterSize="s"
alignItems="center"
>
<EuiFlexItem grow={false}>
<EuiText size="s">
Ignore anomalies for the feature
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={2}>
<Field
name={`suppressionRules.${index}.featureName`}
>
{({ field }: FieldProps) => (
<EuiCompressedFieldText
placeholder="Feature name"
{...field}
fullWidth
/>
)}
</Field>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s">
when the actual value is no more than
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EuiToolTip content="Absolute threshold value">
<Field
name={`suppressionRules.${index}.absoluteThreshold`}
validate={validatePositiveDecimal}
>
{({ field }: FieldProps) => (
<EuiCompressedFieldNumber
placeholder="Absolute"
{...field}
value={field.value || ''}
/>
)}
</Field>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s">or</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EuiToolTip content="Relative threshold value as a percentage">
<Field
name={`suppressionRules.${index}.relativeThreshold`}
validate={validatePositiveDecimal}
>
{({ field }: FieldProps) => (
<div
style={{
display: 'flex',
alignItems: 'center',
}}
>
<EuiCompressedFieldNumber
placeholder="Relative"
{...field}
value={field.value || ''}
/>
<EuiText size="s">%</EuiText>
</div>
)}
</Field>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<EuiToolTip content="Select above or below expected value">
<Field
name={`suppressionRules.${index}.aboveBelow`}
>
{({ field }: FieldProps) => (
<EuiCompressedSelect
options={aboveBelowOptions}
{...field}
/>
)}
</Field>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s">
the expected value.
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="trash"
color="danger"
aria-label="Delete rule"
onClick={() =>
arrayHelpers.remove(index)
}
/>
</EuiFlexItem>
</EuiFlexGroup>
)
)}
</>
</FormattedFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
</Field>
<EuiSpacer size="s" />
<EuiButtonIcon
iconType="plusInCircle"
onClick={() =>
arrayHelpers.push({
fieldName: '',
absoluteThreshold: null, // Set to null to allow empty inputs
relativeThreshold: null, // Set to null to allow empty inputs
aboveBelow: 'above',
})
}
aria-label="Add rule"
/>
</>
)}
</FieldArray>
</>
) : null}
</ContentPanel>
Expand Down

This file was deleted.

Loading

0 comments on commit 104aa8a

Please sign in to comment.