Skip to content

Commit

Permalink
Improve attribute analysis and outlier detection
Browse files Browse the repository at this point in the history
  • Loading branch information
tchrapovic committed Apr 5, 2024
1 parent 9fd83c6 commit c07128f
Show file tree
Hide file tree
Showing 27 changed files with 1,690 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected ChartConfiguration load() {

private @NotNull ChartConfiguration createChartConfiguration() {
BarChartConfiguration chart = new BarChartConfiguration();
ChartData chartData = createDataset();
ChartData chartData = generateDataset();
chart.setData(chartData);
chart.setOptions(createChartOptions());
return chart;
Expand All @@ -61,6 +61,9 @@ protected ChartConfiguration load() {
return options;
}

public ChartData generateDataset(){
return createDataset();
}
private @NotNull ChartData createDataset() {
ChartData chartData = new ChartData();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2010-2024 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.gui.impl.page.admin.role.mining.model;

import com.evolveum.wicket.chartjs.ChartDataset;

public class RoleAnalysisChartDataSet extends ChartDataset {

private String stack;

public RoleAnalysisChartDataSet() {
}

public String getStack() {
return stack;
}

public void setStack(String stack) {
this.stack = stack;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//TODO remove after chartjs upgrade to 0.4
public class RoleAnalysisChartOptions extends ChartOptions {
double barPercentage = 1;
private String scales;

public RoleAnalysisChartOptions() {
}
Expand All @@ -16,4 +17,12 @@ public double getBarPercentage() {
public void setBarPercentage(double barPercentage) {
this.barPercentage = barPercentage;
}

public String getScales() {
return scales;
}

public void setScales(String scales) {
this.scales = scales;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
* Copyright (C) 2010-2024 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.gui.impl.page.admin.role.mining.model;

import java.util.*;
import java.util.stream.Collectors;

import org.apache.wicket.model.LoadableDetachableModel;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.common.mining.objects.analysis.AttributeAnalysisStructure;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.wicket.chartjs.*;

/**
* The model for the role analysis attribute chart.
* Used for loading the data for the role analysis attribute chart.
* Used in role analysis cluster chart.
*/
// TODO - this class is just fast experiment
public class RoleAnalysisStackedAttributeChartModel extends LoadableModel<ChartConfiguration> {

LoadableDetachableModel<List<AttributeAnalysisStructure>> roleAnalysisModelsPositive;
LoadableDetachableModel<List<AttributeAnalysisStructure>> roleAnalysisModelsNegative;

public RoleAnalysisStackedAttributeChartModel(
LoadableDetachableModel<List<AttributeAnalysisStructure>> roleAnalysisModelsStack0,
LoadableDetachableModel<List<AttributeAnalysisStructure>> roleAnalysisModelsStack1) {
this.roleAnalysisModelsPositive = roleAnalysisModelsStack0;
this.roleAnalysisModelsNegative = roleAnalysisModelsStack1;
}

@Override
protected ChartConfiguration load() {
return createChartConfiguration();
}

private @NotNull ChartConfiguration createChartConfiguration() {
BarChartConfiguration chart = new BarChartConfiguration();
ChartData chartData = generateDataset();
chart.setData(chartData);
chart.setOptions(createChartOptions());
return chart;
}

private @NotNull RoleAnalysisChartOptions createChartOptions() {
RoleAnalysisChartOptions options = new RoleAnalysisChartOptions();
options.setLegend(createLegendOptions());
options.setIndexAxis(IndexAxis.AXIS_X.getValue());
ChartAnimationOption chartAnimationOption = new ChartAnimationOption();
chartAnimationOption.setDuration(0);
options.setAnimation(chartAnimationOption);
options.setScales("{\n"
+ " x: {\n"
+ " stacked: true,\n"
+ " },\n"
+ " y: {\n"
+ " stacked: true\n"
+ " }\n"
+ " }");
return options;
}

public ChartData generateDataset() {
ChartData chartData = new ChartData();
createPositiveSideData(chartData);
createNegativeSideData(chartData);
return chartData;
}

private void createPositiveSideData(ChartData chartData) {

ChartDataset datasetAttributeDensity = new ChartDataset();
datasetAttributeDensity.setLabel("Density");
datasetAttributeDensity.addBackgroudColor(getColor());
datasetAttributeDensity.setBorderWidth(1);

RoleAnalysisChartDataSet datasetUsers = new RoleAnalysisChartDataSet();
datasetUsers.setLabel("Users attribute");
datasetUsers.addBackgroudColor("Red");
datasetUsers.setStack("stack0");

RoleAnalysisChartDataSet datasetRoles = new RoleAnalysisChartDataSet();
datasetRoles.setLabel("Roles attribute");
datasetRoles.addBackgroudColor("Green");
datasetRoles.setStack("stack1");

List<AttributeAnalysisStructure> objects = roleAnalysisModelsPositive.getObject();
Map<String, List<AttributeAnalysisStructure>> itemPathMap = objects.stream()
.collect(Collectors.groupingBy(AttributeAnalysisStructure::getItemPath));

Map<String, List<AttributeAnalysisStructure>> sortedItemPathMap = itemPathMap.entrySet().stream()
.sorted((entry1, entry2) -> {
double totalDensity1 = entry1.getValue().stream()
.mapToDouble(AttributeAnalysisStructure::getDensity)
.sum();

double totalDensity2 = entry2.getValue().stream()
.mapToDouble(AttributeAnalysisStructure::getDensity)
.sum();

double averageDensity1 = totalDensity1 / 2;
double averageDensity2 = totalDensity2 / 2;

return Double.compare(averageDensity2, averageDensity1);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));

for (Map.Entry<String, List<AttributeAnalysisStructure>> entry : sortedItemPathMap.entrySet()) {
String itemPath = entry.getKey();
List<AttributeAnalysisStructure> filteredObjects = entry.getValue();

if (filteredObjects.size() == 2) {
AttributeAnalysisStructure userAttribute = null;
AttributeAnalysisStructure roleAttribute = null;

for (AttributeAnalysisStructure obj : filteredObjects) {
if (obj.getComplexType() == UserType.COMPLEX_TYPE) {
userAttribute = obj;
} else {
roleAttribute = obj;
}
}

if (userAttribute != null) {
datasetUsers.addData(userAttribute.getDensity());
}
if (roleAttribute != null) {
datasetRoles.addData(roleAttribute.getDensity());
}
} else if (filteredObjects.size() == 1) {
AttributeAnalysisStructure attributeStructure = filteredObjects.get(0);
if (attributeStructure.getComplexType() == UserType.COMPLEX_TYPE) {
datasetUsers.addData(attributeStructure.getDensity());
datasetRoles.addData(0.0);
} else {
datasetRoles.addData(attributeStructure.getDensity());
datasetUsers.addData(0.0);
}
}

chartData.addLabel(itemPath);
}

chartData.addDataset(datasetRoles);
chartData.addDataset(datasetUsers);
}

private void createNegativeSideData(ChartData chartData) {



ChartDataset datasetAttributeDensity = new ChartDataset();
datasetAttributeDensity.setLabel("Density");
datasetAttributeDensity.addBackgroudColor(getColor());
datasetAttributeDensity.setBorderWidth(1);

RoleAnalysisChartDataSet datasetUsers = new RoleAnalysisChartDataSet();
datasetUsers.setLabel("Users compare");
datasetUsers.addBackgroudColor("Red");
datasetUsers.setStack("stack0");

RoleAnalysisChartDataSet datasetRoles = new RoleAnalysisChartDataSet();
datasetRoles.setLabel("Roles compare");
datasetRoles.addBackgroudColor("Green");
datasetRoles.setStack("stack1");

List<AttributeAnalysisStructure> objects = roleAnalysisModelsNegative.getObject();
Map<String, List<AttributeAnalysisStructure>> itemPathMap = objects.stream()
.collect(Collectors.groupingBy(AttributeAnalysisStructure::getItemPath));

Collection<String> labels = chartData.getLabels();
List<String> indexedLabels = new ArrayList<>(labels);

for (String indexedLabel : indexedLabels) {
if (itemPathMap.containsKey(indexedLabel)) {
List<AttributeAnalysisStructure> filteredObjects = itemPathMap.get(indexedLabel);
String itemPath = indexedLabel;

if (filteredObjects.size() == 2) {
AttributeAnalysisStructure userAttribute = null;
AttributeAnalysisStructure roleAttribute = null;

for (AttributeAnalysisStructure obj : filteredObjects) {
if (obj.getComplexType() == UserType.COMPLEX_TYPE) {
userAttribute = obj;
} else {
roleAttribute = obj;
}
}

if (userAttribute != null) {
datasetUsers.addData(-userAttribute.getDensity());
}
if (roleAttribute != null) {
datasetRoles.addData(-roleAttribute.getDensity());
}
} else if (filteredObjects.size() == 1) {
AttributeAnalysisStructure attributeStructure = filteredObjects.get(0);
if (attributeStructure.getComplexType() == UserType.COMPLEX_TYPE) {
datasetUsers.addData(-attributeStructure.getDensity());
datasetRoles.addData(0.0);
} else {
datasetRoles.addData(-attributeStructure.getDensity());
datasetUsers.addData(0.0);
}
}

chartData.addLabel(itemPath);

}
}

chartData.addDataset(datasetRoles);
chartData.addDataset(datasetUsers);

}

private @NotNull ChartLegendOption createLegendOptions() {
ChartLegendOption legend = new ChartLegendOption();
legend.setDisplay(false);
ChartLegendLabel label = new ChartLegendLabel();
label.setBoxWidth(15);
legend.setLabels(label);
return legend;
}

public String getColor() {
return "#206F9D";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,11 @@ public PageRoleAnalysisOutlier() {
super();
}



@Override
public void savePerformed(AjaxRequestTarget target) {
super.savePerformed(target);
}



@Override
protected void onInitialize() {
super.onInitialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.List;
import java.util.Set;

import com.evolveum.midpoint.gui.impl.page.admin.role.mining.model.RoleAnalysisStackedAttributeChartModel;

import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
Expand Down Expand Up @@ -78,6 +80,11 @@ private void initChartPart() {
new ChartJsPanel<>(ID_CHART, new LoadableModel<>() {
@Override
protected ChartConfiguration load() {

if (getStackedNegativeValue() != null) {
return getRoleAnalysisStatisticsStacked().getObject();
}

return getRoleAnalysisStatistics().getObject();
}
});
Expand All @@ -92,6 +99,10 @@ protected ChartConfiguration load() {

}

public List<AttributeAnalysisStructure> getStackedNegativeValue() {
return null;
}

public RoleAnalysisAttributeChartModel getRoleAnalysisStatistics() {
return new RoleAnalysisAttributeChartModel(new LoadableDetachableModel<>() {
@Override
Expand All @@ -107,6 +118,27 @@ public String getColor() {
};
}

public RoleAnalysisStackedAttributeChartModel getRoleAnalysisStatisticsStacked() {
return new RoleAnalysisStackedAttributeChartModel(new LoadableDetachableModel<>() {
@Override
protected List<AttributeAnalysisStructure> load() {
return attributeAnalysisStructureList;
}

}, new LoadableDetachableModel<>() {
@Override
protected List<AttributeAnalysisStructure> load() {
return getStackedNegativeValue();
}

}) {
@Override
public String getColor() {
return RoleAnalysisAttributeChartPanel.this.getColor();
}
};
}

public StringResourceModel getChartTitle() {
return createStringResource("PageRoleAnalysis.chart.title");
}
Expand Down

0 comments on commit c07128f

Please sign in to comment.