Advanced Mode for Configurable KPIs

Configure complex filters and calculations for KPIs using JSON.

📘

Note

Advanced configuration for KPIs is only available in workspaces created before April, 2024.

Tutorial

📘

The current KPI query format will be superseded by Facet Filters.

At the moment, Facet Filters do not support all functionalities needed to define a KPI. To make Configurable KPIs available now, KPI definitions use a custom JSON format. Facet Filters are planned to support KPIs in the future and will replace the custom JSON format.

If you want to create a KPI that cannot be defined in simple mode, you can switch to advanced mode. In advanced mode you can define a KPI using JSON format as shown in the image below.

A KPI can have one of three types:

  1. Simple KPI
    • Computes one aggregated value over a list of filtered Fact Sheets.
  2. Percentage KPI
    • Computes a value over a list of filtered Fact Sheets and a totalValue over a superset of Fact Sheets, e.g., the number of Applications with a valid Quality Seal and the number of all Applications.
  3. Group-By KPI
    • Custom Group-By KPIs are currently not supported

Simple KPI

For a simple KPI, you only need to define a filter:

{
   "filters":[
      {
         "type":"factSheetType",
         "types":[
            "Application"
         ]
      }
   ]
}

A KPI with this definition returns the number of Fact Sheets with type Application in your workspace.
You can add another filter to get the number of Applications that are currently active:

{
   "filters":[
      {
         "type":"factSheetType",
         "types":[
            "Application"
         ]
      },
      {
         "type":"equals",
         "fieldName":"lifecycle",
         "fieldValue":"active",
         "path":"$.currentPhase"
      }
   ]
}

If you would rather not count active Applications but all Applications that are not end of life, you can combine a none filter with an equals filter:

{
   "filters":[
      {
         "type":"factSheetType",
         "types":[
            "Application"
         ]
      },
      {
         "type":"none",
         "filters":[
            {
               "type":"equals",
               "fieldName":"lifecycle",
               "fieldValue":"endOfLife",
               "path":"$.currentPhase"
            }
         ]
      }
   ]
}

👍

LeanIX provides admins with predefined KPIs

Users don't have to reinvent the wheel: LeanIX provides users already with more than 50 KPI definitions that can be looked up in the workspace to understand the definition in the advanced JSON configuration, ready to be copied, modified and reused for the specific requirements of your organisation.

More Aggregations

Until now, all we did was count the number of Fact Sheets that match a specific filter, but you can also use other aggregations like sum or average. All available aggregations are listed here.

If you don't provide a specific aggregation, a KPI default to the count aggregation. You can define this explicitly like this:

{
   "filters":[
      "..."
   ],
   "aggregations":[
      {
         "name":"value",
         "operation":{
            "type":"count"
         }
      }
   ]
}

The count operation is also special as it does not need an aggregatedField. Other operations like average need to specify the aggregatedField. The aggregated field must be a numeric field on the Fact Sheet.

{
  "filters":[
      "..."
   ],
   "aggregations":[
      {
         "name":"avg",
         "operation":{
            "type":"avg"
         },
         "aggregatedField":"cost"
      }
   ]
}

This aggregation yields the average cost over all filtered Fact Sheets. A simple KPI expects exactly one aggregation.

🚧

Aggregations on Tags and Tag Groups

Because of technical limitations you can currently not aggregate on tags or tag groups.

Aggregations on complex Fields

If you want to compute for example the average completion score over all Applications, aggregations are not powerful enough. This is because the completionScore field on a Fact Sheet is not a simple numeric field, but an object, that holds the completion score for the complete Fact Sheet as well as scores for the different subsections.

To average the completion score, you need to use the fields key in the KPI definition. fields can be used to define virtual Fact Sheet fields. For the completion score, we specify the base field we want to use (completionScore) and the path key pointing to the numeric value that should be used in the virtual field.

{
   "filters":[
      "..."
   ],
   "fields":[
      {
         "name":"fsCompletion",
         "factSheetType":"Application",
         "definedAs":{
            "type":"field",
            "field":"completionScore",
            "path":"$.completion"
         }
      }
   ]
}

Now we can define an aggregation on the virtual field fsCompletion.

{
   "filters":[
      "..."
   ],
   "fields":[
      {
         "name":"fsCompletion",
         "factSheetType":"Application",
         "definedAs":{
            "type":"field",
            "field":"completionScore",
            "path":"$.completion"
         }
      }
   ],
   "aggregations":[
      {
         "name":"avg",
         "operation":{
            "type":"avg"
         },
         "aggregatedField":"fsCompletion"
      }
   ]
}

Percentage KPI

Percentage KPIs compute two values, which are used as nominator and denominator to calculate a percentage.
Next to filters the KPI definition also needs totalFilter, which defines the list of Fact Sheets used for calculating the denominator.

{
   "filters":[
      {
         "type":"factSheetType",
         "types":[
            "Application"
         ]
      },
      {
         "type":"none",
         "filters":[
            {
               "type":"equals",
               "fieldName":"lifecycle",
               "fieldValue":"endOfLife",
               "path":"$.currentPhase"
            }
         ]
      },
      {
         "type":"none",
         "filters":[
            {
               "type":"equals",
               "fieldName":"lxState",
               "fieldValue":"DRAFT"
            },
            {
               "type":"equals",
               "fieldName":"lxState",
               "fieldValue":"REJECTED"
            }
         ]
      },
      {
         "type":"none",
         "filters":[
            {
               "type":"equals",
               "fieldName":"technicalSuitability"
            }
         ]
      }
   ],
   "totalFilter":[
      {
         "type":"factSheetType",
         "types":[
            "Application"
         ]
      },
      {
         "type":"none",
         "filters":[
            {
               "type":"equals",
               "fieldName":"lifecycle",
               "fieldValue":"endOfLife",
               "path":"$.currentPhase"
            }
         ]
      }
   ]
}

If you define an aggregation, it is used for the nominator and the denominator.

Reference

Base Structure

KPI {
    filters: Filter[],
    totalFilter: Filter[],
    fields: Field[],
    aggregations: Aggregation[],
    attributes: Attribute[]
}

Filters

Filter = FactSheetTypeFilter | ForAnyRelationFilter | ForAnyConstrainingRelation| ForFactSheetFilter | MatchesFilter | EqualsFilter | ContainsFilter | AllFilter | NoneFilter | AnyFilter | FullTextFilter | PathfinderFacetFilter;

FactSheetTypeFilter {
  type: "factSheetType"
  types: string[]
}

ForAnyRelationFilter {
  type: "forAnyRelation"
  relation: string
  excludeTransitiveRelations: boolean = false
  filters: Filter[]
}

ForAnyConstrainingRelationFilter {
  type: "forAnyConstrainingRelation"
  filters: Filter[]
}

ForFactSheetFilter {
  type: "forFactSheet"
  direction: "FROM" | "TO"
  filters: Filter[]
}

LessThanFilter {
  type: "less"
  fieldValue: Object
  fieldName: string
  path?: string
}

LessOrEqualFilter {
  type: "lessEqual"
  fieldValue: Object
  fieldName: string
  path?: string
}

GreaterThanFilter {
  type: "greater"
  fieldValue: Object
  fieldName: string
  path?: string
}

GreaterOrEqualFilter {
  type: "greaterEqual"
  fieldValue: Object
  fieldName: string
  path?: string
}


FacetFilter {
  facetKey: string
  keys: string[]
}

EqualsFilter {
  type: "equals"
  fieldValue: Object
  fieldName: string
  path?: string
}

MatchesFilter {
  type: "matches"
  fieldValue: Object
  fieldName: string
  path?: string
}

ContainsFilter {
  type: "contains"
  fieldValue: Object
  fieldName: string
  path?: string
}

InFilter {
  type: "in"
  fieldName: string
  fieldValue: Object[]
  path?: string
}

AllFilter {
  type: "all"
  filters: Filter[]
}

AnyFilter {
  type: "any"
  filters: Filter[]
}

NoneFilter {
  type: "none"
  filters: Filter[]
}

Aggregations

Aggregation {
  name: "someAggregation",
  operation: Operation,
  aggregatedField: "someField",
  groupBy: "someOtherField",
}

Operations

Operation = Count | Sum | Min | Max | Avg

Count {
  type: 'count'
}

Sum {
  type: 'sum'
}

Min {
  type: 'min'
}

Max {
  type: 'max'
}

Avg {
  type: 'avg'
}

Fields

Field {
  name: "someName",
  factSheetType: "Application",
  definedAs: Path
}

Attributes

Attribute = FactSheetFieldAttribute | RelationFieldAttribute | RelatedFactSheetFieldAttribute | PathAttribute

enum AttributeType {
  Field = "field",
  RelationField = "relationField",
  TargetField = "targetField"
  Path = "path"
}

FactSheetFieldAttribute {
    name: string
    type: AttributeType.Field,
    field: string
}

RelationFieldAttribute {
    name: string
    type: AttributeType.RelationField,
    relation: string,
    field: string
}

TargetFactSheetFieldAttribute {
    name: string
    type: AttributeType.TargetField,
    relation: string,
    field: string
}

PathAttribute {
    name: string
    type: AttributeType.Path,
    path: Path
}

Paths

Path = FieldPath | RelationPath | FactSheetPath | ConstrainingRelationPath | PathsPath | AggregationPath

FieldPath {
    type: 'field',
    field: string
}

RelationPath {
    type: 'relation',
    relation: string,
    path: Path,
    filter: Filter,
    excludeTransitiveRelations: boolean = true
}

FactSheetPath {
    type: 'factsheet',
    path: Path
}

ConstrainingRelationPath {
    type: 'constrainingRelation',
    path: Path
}

PathsPath {
    type: 'paths',
    paths: {
      [name: string]: Path
    }
}

AggregationPath {
    type: 'aggregation',
    operation: Operation,
    path: Path
}

Special Paths

Lifecycle

  • $.phases.<phase>
  • $.duration.<phase> (number of days from the start of the until the start of the next phase, or null if indefinite)

The following take the current date into account:

  • $.currentPhase Current Lifecycle phase
  • $.durationOfCurrentPhase Duration of current Lifecycle phase
  • $.sinceStart.<phase> number of days from the start of the until the pointOfView date, or null if that date lies outside the phase
  • $.sinceStartOfCurrentPhase Days since the start of the current Lifecycle phase

Subscriptions

  • $.type
  • $.userId
  • $.linkedRoles.roleId

Location

  • $.rawAddress
  • $.latitude
  • $.longitude
  • $.geoCity
  • $.geoCountryCode
  • $.geoCountry
  • $.geoAddress
  • $.geoStreet
  • $.geoHouseNumber
  • $.geoPostalCode

ExternalID

  • $.externalId
  • $.comment
  • $.status
  • $.externalUrl
  • $.externalVersion