Skip to content

Commit

Permalink
Clean-up and improve simulations schema
Browse files Browse the repository at this point in the history
All values of a given metric (different partitions at various levels
of detail) are now kept in a single SimulationMetricValuesType instance.

Other changes:
- Both definition and run-time schema structure and item/type names
were improved.
- On-demand value aggregation was implemented, to support GUI in
dynamic displaying of the values. See SimulationMetricValuesTypeUtil
and SimulationMetricComputer.

Work in progress.
  • Loading branch information
mederly committed Jan 19, 2023
1 parent cd02bd3 commit 1bd126d
Show file tree
Hide file tree
Showing 25 changed files with 943 additions and 522 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import java.util.List;
import java.util.stream.Collectors;

import com.evolveum.midpoint.schema.util.SimulationMetricValuesTypeUtil;

import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
Expand Down Expand Up @@ -45,11 +47,11 @@
import com.evolveum.midpoint.web.page.admin.PageAdmin;
import com.evolveum.midpoint.web.page.error.PageError404;
import com.evolveum.midpoint.web.session.UserProfileStorage;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AggregatedSimulationMetricValueType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricValuesType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationResultType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TagType;

import static com.evolveum.midpoint.schema.util.AbstractSimulationMetricReferenceTypeUtil.getDisplayableIdentifier;
import static com.evolveum.midpoint.schema.util.SimulationMetricReferenceTypeUtil.getDisplayableIdentifier;

/**
* Created by Viliam Repan (lazyman).
Expand Down Expand Up @@ -121,11 +123,11 @@ private void initLayout() {
// todo implement
IModel<List<SmallBoxData>> data = () -> {

List<AggregatedSimulationMetricValueType> metrics = model.getObject().getMetric();
List<SimulationMetricValuesType> metrics = model.getObject().getMetric();
return metrics.stream().map(m -> {
SmallBoxData sbd = new SmallBoxData();
sbd.setDescription(getDisplayableIdentifier(m.getRef()));
sbd.setTitle("" + m.getValue());
sbd.setTitle("" + SimulationMetricValuesTypeUtil.getValue(m));
sbd.setSmallBoxCssClass("bg-info");
sbd.setLinkText("More info");
sbd.setIcon("fa fa-database");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.simulation;

import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricPartitionScopeType;

import javax.xml.namespace.QName;
import java.util.List;
import java.util.Objects;

/**
* Parsed form of {@link SimulationMetricPartitionScopeType}.
*
* TODO reconcile these two; what to do with null dimensions?
*/
public class PartitionScope {

private final QName objectType;
private final String resourceOid;
private final ShadowKindType kind;
private final String intent;

public PartitionScope(QName objectType, String resourceOid, ShadowKindType kind, String intent) {
this.objectType = objectType;
this.resourceOid = resourceOid;
this.kind = kind;
this.intent = intent;
}

public static PartitionScope fromBean(SimulationMetricPartitionScopeType scope) {
if (scope == null) {
return new PartitionScope(null, null, null, null);
} else {
return new PartitionScope(
scope.getTypeName(),
scope.getResourceOid(),
scope.getKind(),
scope.getIntent());
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PartitionScope that = (PartitionScope) o;
return Objects.equals(objectType, that.objectType)
&& Objects.equals(resourceOid, that.resourceOid)
&& kind == that.kind
&& Objects.equals(intent, that.intent);
}

@Override
public int hashCode() {
return Objects.hash(objectType, resourceOid, kind, intent);
}

public SimulationMetricPartitionScopeType toBean() {
// FIXME fill nullDimensions correctly
SimulationMetricPartitionScopeType bean = new SimulationMetricPartitionScopeType()
.typeName(objectType)
.resourceOid(resourceOid)
.kind(kind)
.intent(intent);
List<QName> nullDimensions = bean.getNullDimensions();
if (objectType == null) {
nullDimensions.add(SimulationMetricPartitionScopeType.F_TYPE_NAME);
}
if (resourceOid == null) {
nullDimensions.add(SimulationMetricPartitionScopeType.F_RESOURCE_OID);
}
if (kind == null) {
nullDimensions.add(SimulationMetricPartitionScopeType.F_KIND);
}
if (intent == null) {
nullDimensions.add(SimulationMetricPartitionScopeType.F_INTENT);
}
return bean;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.simulation;

import static com.evolveum.midpoint.schema.util.SimulationMetricValuesTypeUtil.selectPartitions;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.schema.util.SimulationMetricPartitionDimensionsTypeUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricValuesType;

import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricAggregationFunctionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricPartitionType;

/**
* Works with the metric computations at one place.
*
* (Called from various x-Type-Util classes.)
*/
public class SimulationMetricComputer {

private static final int DEFAULT_SCALE = 10;

public static List<SimulationMetricPartitionType> computePartitions(
@NotNull SimulationMetricValuesType mv, @NotNull Set<QName> dimensions) {
Set<QName> sourceDimensions = SimulationMetricPartitionDimensionsTypeUtil.getDimensions(mv.getSourceDimensions());
Set<QName> missing = Sets.difference(dimensions, sourceDimensions);
if (!missing.isEmpty()) {
throw new IllegalArgumentException(
String.format(
"Cannot compute partition for %s as the following dimension(s) are missing: %s; source = %s",
dimensions, missing, sourceDimensions));
}
SimulationMetricPartitions targetPartitions = new SimulationMetricPartitions();
for (SimulationMetricPartitionType sourcePartitionBean : selectPartitions(mv, sourceDimensions)) {
targetPartitions.addPartition(sourcePartitionBean);
}
return targetPartitions.toPartitionBeans(mv.getAggregationFunction());
}

@SuppressWarnings("SameParameterValue")
static BigDecimal computeValue(
SimulationMetricPartitionType partition,
SimulationMetricAggregationFunctionType function,
ComputationParameters computationParameters) {
switch (function) {
case SELECTION_SIZE:
return asBigDecimal(partition.getSelectionSize());
case SELECTION_TOTAL_VALUE:
return partition.getSelectionTotalValue();
case DOMAIN_SIZE:
return asBigDecimal(partition.getDomainSize());
case DOMAIN_TOTAL_VALUE:
return partition.getDomainTotalValue();
case SELECTION_SIZE_TO_DOMAIN_SIZE:
return ratio(
asBigDecimal(partition.getSelectionSize()),
asBigDecimal(partition.getDomainSize()),
computationParameters);
case SELECTION_TOTAL_VALUE_TO_DOMAIN_SIZE:
return ratio(
partition.getSelectionTotalValue(),
asBigDecimal(partition.getDomainSize()),
computationParameters);
case DOMAIN_TOTAL_VALUE_TO_DOMAIN_SIZE:
return ratio(
partition.getDomainTotalValue(),
asBigDecimal(partition.getDomainSize()),
computationParameters);
case SELECTION_TOTAL_VALUE_TO_DOMAIN_TOTAL_VALUE:
return ratio(
partition.getSelectionTotalValue(),
partition.getDomainTotalValue(),
computationParameters);
case SELECTION_MIN_VALUE:
return partition.getSelectionMinValue();
case DOMAIN_MIN_VALUE:
return partition.getDomainMinValue();
case SELECTION_MAX_VALUE:
return partition.getSelectionMaxValue();
case DOMAIN_MAX_VALUE:
return partition.getDomainMaxValue();
default:
throw new AssertionError(function);
}
}

private static BigDecimal asBigDecimal(Integer value) {
return value != null ? BigDecimal.valueOf(value) : null;
}

private static BigDecimal ratio(BigDecimal part, BigDecimal total, ComputationParameters parameters) {
if (part == null || total == null || BigDecimal.ZERO.equals(total)) {
return null;
} else {
return part.divide(total, ComputationParameters.getScale(parameters), RoundingMode.HALF_UP);
}
}

public static class ComputationParameters {
private final int scale;

public ComputationParameters(int scale) {
this.scale = scale;
}

static int getScale(ComputationParameters parameters) {
return parameters != null ? parameters.scale : DEFAULT_SCALE;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.simulation;

import java.math.BigDecimal;

import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricAggregationFunctionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricPartitionType;

import static com.evolveum.midpoint.util.MiscUtil.or0;

/**
* Parsed form of {@link SimulationMetricPartitionType}.
*
* Created for fast and simple aggregation.
*/
public class SimulationMetricPartition {

private int selectionSize;
private BigDecimal selectionTotalValue = BigDecimal.ZERO;
private int domainSize;
private BigDecimal domainTotalValue = BigDecimal.ZERO;

private BigDecimal selectionMinValue;
private BigDecimal selectionMaxValue;
private BigDecimal domainMinValue;
private BigDecimal domainMaxValue;

@SuppressWarnings("DuplicatedCode")
public void addObject(BigDecimal sourceMetricValue, boolean inSelection) {
if (domainMinValue == null || sourceMetricValue.compareTo(domainMinValue) < 0) {
domainMinValue = sourceMetricValue;
}
if (domainMaxValue == null || sourceMetricValue.compareTo(domainMaxValue) > 0) {
domainMaxValue = sourceMetricValue;
}
domainSize++;
domainTotalValue = domainTotalValue.add(sourceMetricValue);

if (inSelection) {
if (selectionMinValue == null || sourceMetricValue.compareTo(selectionMinValue) < 0) {
selectionMinValue = sourceMetricValue;
}
if (selectionMaxValue == null || sourceMetricValue.compareTo(selectionMaxValue) > 0) {
selectionMaxValue = sourceMetricValue;
}
selectionSize++;
selectionTotalValue = selectionTotalValue.add(sourceMetricValue);
}
}

void addPartition(SimulationMetricPartitionType other) {
BigDecimal otherDomainMinValue = other.getDomainMinValue();
if (domainMinValue == null || otherDomainMinValue != null && otherDomainMinValue.compareTo(domainMinValue) < 0) {
domainMinValue = otherDomainMinValue;
}
BigDecimal otherDomainMaxValue = other.getDomainMaxValue();
if (domainMaxValue == null || otherDomainMaxValue != null && otherDomainMaxValue.compareTo(domainMaxValue) > 0) {
domainMaxValue = otherDomainMaxValue;
}
domainSize += or0(other.getDomainSize());
domainTotalValue = domainTotalValue.add(or0(other.getDomainTotalValue()));

BigDecimal otherSelectionMinValue = other.getSelectionMinValue();
if (selectionMinValue == null || otherSelectionMinValue != null && otherSelectionMinValue.compareTo(selectionMinValue) < 0) {
selectionMinValue = otherSelectionMinValue;
}
BigDecimal otherSelectionMaxValue = other.getSelectionMaxValue();
if (selectionMaxValue == null || otherSelectionMaxValue != null && otherSelectionMaxValue.compareTo(selectionMaxValue) > 0) {
selectionMaxValue = otherSelectionMaxValue;
}
selectionSize += or0(other.getSelectionSize());
selectionTotalValue = selectionTotalValue.add(or0(other.getSelectionTotalValue()));
}

public SimulationMetricPartitionType toBean(PartitionScope key, SimulationMetricAggregationFunctionType function) {
var bean = toBean(key);
bean.setValue(
SimulationMetricComputer.computeValue(bean, function, null));
return bean;
}

private SimulationMetricPartitionType toBean(PartitionScope scope) {
return new SimulationMetricPartitionType()
.scope(scope.toBean())
.selectionSize(selectionSize)
.selectionTotalValue(selectionTotalValue)
.domainSize(domainSize)
.domainTotalValue(domainTotalValue)
.selectionMinValue(selectionMinValue)
.selectionMaxValue(selectionMaxValue)
.domainMinValue(domainMinValue)
.domainMaxValue(domainMaxValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.simulation;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricAggregationFunctionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SimulationMetricPartitionType;

public class SimulationMetricPartitions {

@NotNull private final Map<PartitionScope, SimulationMetricPartition> partitions = new HashMap<>();

public List<SimulationMetricPartitionType> toPartitionBeans(@NotNull SimulationMetricAggregationFunctionType function) {
return partitions.entrySet().stream()
.map(e -> e.getValue().toBean(e.getKey(), function))
.collect(Collectors.toList());
}

public void addObject(PartitionScope key, BigDecimal sourceMetricValue, boolean inSelection) {
partitions
.computeIfAbsent(key, (k) -> new SimulationMetricPartition())
.addObject(sourceMetricValue, inSelection);
}

void addPartition(SimulationMetricPartitionType sourcePartitionBean) {
PartitionScope key = PartitionScope.fromBean(sourcePartitionBean.getScope());
partitions
.computeIfAbsent(key, (k) -> new SimulationMetricPartition())
.addPartition(sourcePartitionBean);
}
}

0 comments on commit 1bd126d

Please sign in to comment.