Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Maps][ML] Integration part 1: Create anomalies layer in maps #122862

Merged
merged 8 commits into from
Jan 20, 2022

Conversation

alvarezmelissa87
Copy link
Contributor

@alvarezmelissa87 alvarezmelissa87 commented Jan 12, 2022

Summary

Related draft PR: #113857

Extends and updates #90497
Incorporates latest maps changes (#112465, #112333) into initial draft PR (#111228)

  • Anomalies layer card should be displayed in maps when creating layers
  • Layer options for anomalies for the chosen job should include
    • typical
    • actual
    • difference between typical/actual
  • The card should be disabled when the user does not have access to ML
  • Tooltip for point on the map should include useful information
  • When there are no ML jobs available for selection in the wizard, we should not show the combo-box, and instead show some exaplanation/hyperlink. E.g. a link to ML job creation or job management page (for follow up)
  • Anomalies layer should incorporate time filter
  • Anomalies layer should work with query filter (for follow up)
  • getResultsForJobId needs a unit test and can be converted to service (for follow up)
  • Add some explanatory text to job selector

Nice to have:

Screenshots

Anomalies layer card in maps:

image

Job selection for jobs that contain supported geo data:

image

Layers available:

image

Tooltip:

image

Checklist

Delete any items that are not applicable to this PR.

@alvarezmelissa87 alvarezmelissa87 self-assigned this Jan 12, 2022
@alvarezmelissa87 alvarezmelissa87 changed the title wip: create anomalies layer in maps [Maps][ML] Integration part 1: Create anomalies layer in maps Jan 12, 2022
@alvarezmelissa87 alvarezmelissa87 force-pushed the ml-maps-part-1 branch 2 times, most recently from 3c5fbc9 to 2273e4d Compare January 18, 2022 15:53
Copy link
Contributor

@thomasneirynck thomasneirynck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really great from Maps-perspective.

It will be really useful to users to easily map results from spatial anomaly detection.

Below some UX suggestions mostly, but nothing blocking AFAIC.

style: {
type: 'VECTOR',
properties: {
fillColor: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider setting this same property for lineColor . That way, when users select "connected", they see the same symbology:

image

     properties: {
              fillColor: {
                type: STYLE_TYPE.DYNAMIC,
                options: {
                  customColorRamp: SEVERITY_COLOR_RAMP,
                  field: {
                    name: 'record_score',
                    origin: FIELD_ORIGIN.SOURCE,
                  },
                  useCustomColorRamp: true,
                },
              },
              lineColor: {
                type: STYLE_TYPE.DYNAMIC,
                options: {
                  customColorRamp: SEVERITY_COLOR_RAMP,
                  field: {
                    name: 'record_score',
                    origin: FIELD_ORIGIN.SOURCE,
                  },
                  useCustomColorRamp: true,
                },
              },
            } as unknown as VectorStylePropertiesDescriptor,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in e121d67

}),
icon: 'outlierDetectionJob',
getIsDisabled: () => {
// return false by default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider having this check for enterprise license. For an example, see the track layer wizard.

disabledReason: REQUIRES_GOLD_LICENSE_MSG,
icon: TracksLayerIcon,
getIsDisabled: () => {
return !getIsGoldPlus();
},

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checked in the plugin setup and passed through to the layer wizard https://github.com/elastic/kibana/pull/122862/files#diff-9658f57fcb39c79f75a675c515053ccb46c515a7e9f9eac7ecb200d21c3af09aR172

This default is just a placeholder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah makes sense, thx! (I missed that)

fwiw: you could still register the card, but in "disabled" state. That way the card is still visible (but in disabled grayed-out state) . The advantage is that this feature would still be advertized for non-enterprise users.

return (
<EuiFormRow
label={i18n.translate('xpack.ml.maps.jobIdLabel', {
defaultMessage: 'JobId',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider Job Id.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra nit - we tend to use Job ID in ML

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in e121d67

selectedOptions={
this.state.jobId ? [{ value: this.state.jobId, label: this.state.jobId }] : []
}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider adding some explanatory text.

e.g. a little help box about what this is.

e.g.: something along the lines of:
Select anomaly detection job using spatial outlier detection. These use the lon_lat_detector function. LinkToDoc, LinkToMLApp.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to follow-ups as I'll need to craft a good explanation message. 👍

import type { LayerWizard } from '../../../maps/public';

export const anomalyLayerWizard: Partial<LayerWizard> = {
categories: [LAYER_WIZARD_CATEGORY.SOLUTIONS],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also add the Elasticsearch-category, so it shows up in that facet as well

categories: [LAYER_WIZARD_CATEGORY.SOLUTIONS, LAYER_WIZARD_CATEGORY.ELASTICSEARCH],

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in e121d67

typicalActual={this.props.typicalActual}
/>
</EuiPanel>
<EuiSpacer size="s" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to add a switch here to turn on/off "time-awareness".

E.g. something along the lines of:

image

E.g. add another property applyGlobalTime to the source-descriptor.

export interface AnomalySourceDescriptor extends AbstractSourceDescriptor {
  jobId: string;
  typicalActual: MlAnomalyLayers;
  applyGlobalTime: boolean;
}

The switch controls the value of applyGlobalTime.

Then implement AnomalySource#isTimeAware as:

  async isTimeAware(): Promise<boolean> {
    return this._descriptor.applyGlobalTime;
  }

This way you can turn on/off whether users will see all the data (regardless of time-selection), or only a cut of the data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good one to discuss for a follow-up 👍

defaultMessage: 'Job Id',
}),
value: this._descriptor.jobId,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding second object with hyperlink to the anomaly-explorer page in ML based on the job-Id.

e.g. this is some sample code.

{
label: i18n.translate('xpack.maps.source.emsFile.layerLabel', {
defaultMessage: `Layer`,
}),
value: this._descriptor.id,
link: emsLink,
},

So for ML, it'd be something like:

 return [
      {
        label: i18n.translate('xpack.ml.maps.anomalySourcePropLabel', {
          defaultMessage: 'Job Id',
        }),
        value: this._descriptor.jobId,
        link: `http://localhost:5601/hen/app/ml/explorer?_g={jobIds:[%22${this._jobId}%22]` // todo: this link should be constructed with `core.http.basePath.prepend()` and utilities there are on ML-side to derive this URL
      },

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added for a follow up 👍

@alvarezmelissa87 alvarezmelissa87 marked this pull request as ready for review January 18, 2022 22:00
@alvarezmelissa87 alvarezmelissa87 requested a review from a team as a code owner January 18, 2022 22:00
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-gis (Team:Geo)

@elasticmachine
Copy link
Contributor

Pinging @elastic/ml-ui (:ml)

x-pack/plugins/ml/common/util/job_utils.ts Outdated Show resolved Hide resolved
x-pack/plugins/ml/public/maps/anomaly_job_selector.tsx Outdated Show resolved Hide resolved
private getStartServices: StartServicesAccessor<MlStartDependencies, MlPluginStart>,
private canGetJobs: boolean
) {
this.canGetJobs = canGetJobs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line seems redundant

Suggested change
this.canGetJobs = canGetJobs;

x-pack/plugins/ml/public/maps/util.ts Outdated Show resolved Hide resolved
@@ -113,6 +114,7 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
licenseManagement: pluginsSetup.licenseManagement,
home: pluginsSetup.home,
embeddable: { ...pluginsSetup.embeddable, ...pluginsStart.embeddable },
// @ts-ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove this ts-ignore?


if (pluginsSetup.maps) {
// Pass capabilites.ml.canGetJobs as minimum permission to show anomalies card in maps layers
const canGetJobs = capabilities.ml?.canGetJobs === true || false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
const canGetJobs = capabilities.ml?.canGetJobs === true || false;
const canGetJobs = !!capabilities.ml?.canGetJobs;

x-pack/plugins/ml/server/models/job_service/jobs.ts Outdated Show resolved Hide resolved
x-pack/plugins/ml/server/routes/job_service.ts Outdated Show resolved Hide resolved
onChange={this.onSelect}
options={[
{ value: 'typical', label: 'typical' },
{ value: 'actual', label: 'actual' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes more sense for actual to be the top, default option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - updated in c88762c

import { ITooltipProperty } from '../../../maps/public';
import { Filter } from '../../../../../src/plugins/data/public';

export const ANOMALY_SOURCE_FIELDS: Record<string, Record<string, string>> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these strings used for the tooltip text? If so, I think they need to be internationalized.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in c88762c

timestamp: { label: 'Timestamp', type: 'string' },
fieldName: { label: 'Field label', type: 'string' },
functionDescription: { label: 'Function description', type: 'string' },
actual: { label: 'Actual', type: 'string' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use the same labels for these fields as we use in the anomalies table:

image

using Time, Field name and Function

Can we limit the number of decimal places used for the actual and typical positions in the tooltip?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in c88762c

import { ITooltipProperty } from '../../../maps/public';
import { Filter } from '../../../../../src/plugins/data/public';

export const ANOMALY_SOURCE_FIELDS: Record<string, Record<string, string>> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get the partitioning field names and values in the tooltip too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to follow up 👍

export const anomalyLayerWizard: Partial<LayerWizard> = {
categories: [LAYER_WIZARD_CATEGORY.SOLUTIONS],
description: i18n.translate('xpack.ml.maps.anomalyLayerDescription', {
defaultMessage: 'Create anomalies layers',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message needs editing I think - something like Display anomalies from a machine learning job

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in c88762c

}),
disabledReason: i18n.translate('xpack.ml.maps.anomalyLayerUnavailableMessage', {
defaultMessage:
'Whatever reason the user cannot see ML card (likely because no enterprise license or no ML privileges)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message needs some work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in c88762c

const typicalActual: MlAnomalyLayers = selectedOptions[0].value! as
| 'typical'
| 'actual'
| 'connected';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this shows a line from the typical position to the actual? If so, I'm not sure connected is the most intuitive label. Can't think of an alternative though!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is - I also couldn't think of anything more intuitive but am super open to suggestions 🙏

@alvarezmelissa87 alvarezmelissa87 requested a review from a team as a code owner January 19, 2022 19:37
Copy link
Contributor

@nreese nreese left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gis code owners changes LGTM
code review

Copy link
Contributor

@thomasneirynck thomasneirynck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 This is a great integration. It makes the geo-anomaly-detection capabilities of ML more visible in Maps, and it allows "mashing-up" of these anomaly-layers with other contextual data. Also adds embeddability of maps with anomaly-layers, something which was not possible before It also sets the stage for better cross-linking of Maps/ML (e.g. open in Maps-button from the ML-UX).

Copy link
Contributor

@darnautov darnautov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
ml 1596 1607 +11

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
maps 213 214 +1
ml 289 290 +1
total +2

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
ml 3.5MB 3.5MB +17.8KB

Public APIs missing exports

Total count of every type that is part of your API that should be exported but is not. This will cause broken links in the API documentation system. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats exports for more detailed information.

id before after diff
maps 28 27 -1

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
ml 38.5KB 38.9KB +341.0B
Unknown metric groups

API count

id before after diff
maps 214 215 +1
ml 293 294 +1
total +2

async chunk count

id before after diff
ml 25 27 +2

ESLint disabled line counts

id before after diff
ml 95 98 +3

References to deprecated APIs

id before after diff
ml 67 73 +6

Total ESLint disabled count

id before after diff
ml 98 101 +3

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @alvarezmelissa87

@alvarezmelissa87 alvarezmelissa87 merged commit 9368aa4 into elastic:main Jan 20, 2022
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Jan 20, 2022
@alvarezmelissa87
Copy link
Contributor Author

Follow ups tracked in meta issue #123492

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting [Deprecated-Use Team:Presentation]Team:Geo Former Team Label for Geo Team. Now use Team:Presentation :ml release_note:feature Makes this part of the condensed release notes v8.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants