Skip to content

Commit

Permalink
Property reports (#61)
Browse files Browse the repository at this point in the history
* Fixed issues with inclusion/exclusion edge cases for Group Property Report

* Fixed issues with inclusion/exclusion edge cases for Group Property Report

* Added data state recording for various report classes

---------

Co-authored-by: Shawn <[email protected]>
  • Loading branch information
shawnhatch and humbledaisy authored Mar 21, 2023
1 parent 9c554c2 commit 3565aae
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Set;

import nucleus.ReportContext;
import nucleus.SimulationStateContext;
import plugins.globalproperties.datamanagers.GlobalPropertiesDataManager;
import plugins.globalproperties.events.GlobalPropertyDefinitionEvent;
import plugins.globalproperties.events.GlobalPropertyUpdateEvent;
Expand All @@ -30,6 +31,7 @@
public final class GlobalPropertyReport {

private final Set<GlobalPropertyId> includedPropertyIds = new LinkedHashSet<>();
private final Set<GlobalPropertyId> currentProperties = new LinkedHashSet<>();
private final Set<GlobalPropertyId> excludedPropertyIds = new LinkedHashSet<>();
private final ReportLabel reportLabel;
private final boolean includeNewPropertyIds;
Expand All @@ -40,6 +42,66 @@ public final class GlobalPropertyReport {
.add("value")//
.build();//

private boolean isCurrentProperty(GlobalPropertyId globalPropertyId) {
return currentProperties.contains(globalPropertyId);
}

private boolean addToCurrentProperties(GlobalPropertyId globalPropertyId) {

// There are eight possibilities:

/*
* P -- the default inclusion policy
*
* I -- the property is explicitly included
*
* X -- the property is explicitly excluded
*
* C -- the property should be on the current properties
*
*
* P I X C Table
*
* TRUE TRUE FALSE TRUE
*
* TRUE FALSE FALSE TRUE
*
* FALSE TRUE FALSE TRUE
*
* FALSE FALSE FALSE FALSE
*
* TRUE TRUE TRUE FALSE -- not possible
*
* TRUE FALSE TRUE FALSE
*
* FALSE TRUE TRUE FALSE -- not possible
*
* FALSE FALSE TRUE FALSE
*
*
* Two of the cases above are contradictory since a property cannot be
* both explicitly included and explicitly excluded
*
*/

// if X is true then we don't add the property
if (excludedPropertyIds.contains(globalPropertyId)) {
return false;
}

// if both P and I are false we don't add the property
boolean included = includedPropertyIds.contains(globalPropertyId);

if (!included && !includeNewPropertyIds) {
return false;
}

// we have failed to reject the property
currentProperties.add(globalPropertyId);

return true;
}

/**
*
* @throws ContractException
Expand All @@ -62,16 +124,14 @@ public GlobalPropertyReport(GlobalPropertyReportPluginData globalPropertyReportP

private void handleGlobalPropertyDefinitionEvent(final ReportContext reportContext, final GlobalPropertyDefinitionEvent globalPropertyDefinitionEvent) {
final GlobalPropertyId globalPropertyId = globalPropertyDefinitionEvent.globalPropertyId();
if (!excludedPropertyIds.contains(globalPropertyId)) {
includedPropertyIds.add(globalPropertyId);
if (addToCurrentProperties(globalPropertyId)) {
writeProperty(reportContext, globalPropertyId, globalPropertyDefinitionEvent.initialPropertyValue());

}
}

private void handleGlobalPropertyUpdateEvent(final ReportContext reportContext, final GlobalPropertyUpdateEvent globalPropertyUpdateEvent) {
final GlobalPropertyId globalPropertyId = globalPropertyUpdateEvent.globalPropertyId();
if (includedPropertyIds.contains(globalPropertyId)) {
if (isCurrentProperty(globalPropertyId)) {
writeProperty(reportContext, globalPropertyId, globalPropertyUpdateEvent.currentPropertyValue());
}
}
Expand All @@ -83,36 +143,37 @@ public void init(final ReportContext reportContext) {

final GlobalPropertiesDataManager globalPropertiesDataManager = reportContext.getDataManager(GlobalPropertiesDataManager.class);

/*
* if the client has selected all extant properties, then correct the
* data's included property ids
*/
if (includeNewPropertyIds) {
includedPropertyIds.addAll(globalPropertiesDataManager.getGlobalPropertyIds());
includedPropertyIds.removeAll(excludedPropertyIds);
reportContext.subscribe(GlobalPropertyDefinitionEvent.class, this::handleGlobalPropertyDefinitionEvent);
}

/*
* We now subscribe to all update and definition events without any
* filtering
*/
reportContext.subscribe(GlobalPropertyDefinitionEvent.class, this::handleGlobalPropertyDefinitionEvent);
reportContext.subscribe(GlobalPropertyUpdateEvent.class, this::handleGlobalPropertyUpdateEvent);
reportContext.subscribeToSimulationState(this::recordSimulationState);

for (GlobalPropertyId globalPropertyId : globalPropertiesDataManager.getGlobalPropertyIds()) {
addToCurrentProperties(globalPropertyId);
}

/*
* We initialize the reporting with the current state of each global
* property
*/
for (final GlobalPropertyId globalPropertyId : includedPropertyIds) {

if (globalPropertiesDataManager.globalPropertyIdExists(globalPropertyId)) {
final Object globalPropertyValue = globalPropertiesDataManager.getGlobalPropertyValue(globalPropertyId);
writeProperty(reportContext, globalPropertyId, globalPropertyValue);
}
for (final GlobalPropertyId globalPropertyId : currentProperties) {
final Object globalPropertyValue = globalPropertiesDataManager.getGlobalPropertyValue(globalPropertyId);
writeProperty(reportContext, globalPropertyId, globalPropertyValue);
}

}

private void recordSimulationState(ReportContext reportContext, SimulationStateContext simulationStateContext) {
GlobalPropertyReportPluginData.Builder builder = simulationStateContext.get(GlobalPropertyReportPluginData.Builder.class);
builder.setReportLabel(reportLabel);
for (GlobalPropertyId globalPropertyId : includedPropertyIds) {
builder.includeGlobalPropertyId(globalPropertyId);
}
for (GlobalPropertyId globalPropertyId : excludedPropertyIds) {
builder.excludeGlobalPropertyId(globalPropertyId);
}
builder.setDefaultInclusion(includeNewPropertyIds);
}

private void writeProperty(final ReportContext reportContext, final GlobalPropertyId globalPropertyId, final Object globalPropertyValue) {
final ReportItem.Builder reportItemBuilder = ReportItem.builder();
reportItemBuilder.setReportHeader(reportHeader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Map;

import nucleus.ReportContext;
import nucleus.SimulationStateContext;
import plugins.groups.datamanagers.GroupsDataManager;
import plugins.groups.support.GroupId;
import plugins.groups.support.GroupTypeId;
Expand Down Expand Up @@ -107,6 +108,14 @@ protected void flush(ReportContext reportContext) {
@Override
protected void prepare(ReportContext reportContext) {
groupsDataManager = reportContext.getDataManager(GroupsDataManager.class);
reportContext.subscribeToSimulationState(this::recordSimulationState);
}


private void recordSimulationState(ReportContext reportContext, SimulationStateContext simulationStateContext) {
GroupPopulationReportPluginData.Builder builder = simulationStateContext.get(GroupPopulationReportPluginData.Builder.class);
builder.setReportLabel(getReportLabel());
builder.setReportPeriod(getReportPeriod());
}

}
26 changes: 24 additions & 2 deletions gcm4/src/main/java/plugins/groups/reports/GroupPropertyReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Set;

import nucleus.ReportContext;
import nucleus.SimulationStateContext;
import plugins.groups.datamanagers.GroupsDataManager;
import plugins.groups.events.GroupAdditionEvent;
import plugins.groups.events.GroupImminentRemovalEvent;
Expand Down Expand Up @@ -240,13 +241,13 @@ protected void prepare(final ReportContext reportContext) {
reportContext.subscribe(GroupImminentRemovalEvent.class, this::handleGroupImminentRemovalEvent);
reportContext.subscribe(GroupPropertyUpdateEvent.class, this::handleGroupPropertyUpdateEvent);
reportContext.subscribe(GroupPropertyDefinitionEvent.class, this::handleGroupPropertyDefinitionEvent);
reportContext.subscribeToSimulationState(this::recordSimulationState);

// update the current properties from the existing properties found in
// the data manager
for (GroupTypeId groupTypeId : groupsDataManager.getGroupTypeIds()) {
for (GroupPropertyId groupPropertyId : groupsDataManager.getGroupPropertyIds(groupTypeId)) {
addToCurrentProperties(groupTypeId, groupPropertyId);
System.out.println("Added "+groupTypeId+" "+groupPropertyId+" to current properties");
addToCurrentProperties(groupTypeId, groupPropertyId);
}
}

Expand All @@ -264,6 +265,27 @@ protected void prepare(final ReportContext reportContext) {
}
}
}


private void recordSimulationState(ReportContext reportContext, SimulationStateContext simulationStateContext) {
GroupPropertyReportPluginData.Builder builder = simulationStateContext.get(GroupPropertyReportPluginData.Builder.class);
builder.setReportLabel(getReportLabel());
builder.setReportPeriod(getReportPeriod());
builder.setDefaultInclusion(includeNewProperties);

for(GroupTypeId groupTypeId :includedProperties.keySet()) {
for(GroupPropertyId groupPropertyId : includedProperties.get(groupTypeId)) {
builder.includeGroupProperty(groupTypeId, groupPropertyId);
}
}

for(GroupTypeId groupTypeId :excludedProperties.keySet()) {
for(GroupPropertyId groupPropertyId : excludedProperties.get(groupTypeId)) {
builder.excludeGroupProperty(groupTypeId, groupPropertyId);
}
}

}

private void handleGroupPropertyDefinitionEvent(ReportContext reportContext, GroupPropertyDefinitionEvent groupPropertyDefinitionEvent) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ private final static class Counter {
* report. They are set during init()
*/
private final Set<PersonPropertyId> includedPersonPropertyIds = new LinkedHashSet<>();
private final Set<PersonPropertyId> currentProperties = new LinkedHashSet<>();
private final Set<PersonPropertyId> excludedPersonPropertyIds = new LinkedHashSet<>();

/*
Expand Down Expand Up @@ -155,15 +156,15 @@ private Counter getCounter(final RegionId regionId, final PersonPropertyId perso
private void handlePersonAdditionEvent(ReportContext reportContext, PersonAdditionEvent personAdditionEvent) {
PersonId personId = personAdditionEvent.personId();
final RegionId regionId = regionsDataManager.getPersonRegion(personId);
for (final PersonPropertyId personPropertyId : includedPersonPropertyIds) {
for (final PersonPropertyId personPropertyId : currentProperties) {
final Object personPropertyValue = personPropertiesDataManager.getPersonPropertyValue(personId, personPropertyId);
increment(regionId, personPropertyId, personPropertyValue);
}
}

private void handlePersonPropertyUpdateEvent(ReportContext reportContext, PersonPropertyUpdateEvent personPropertyUpdateEvent) {
PersonPropertyId personPropertyId = personPropertyUpdateEvent.personPropertyId();
if (includedPersonPropertyIds.contains(personPropertyId)) {
if (isCurrentProperty(personPropertyId)) {
PersonId personId = personPropertyUpdateEvent.personId();
Object previousPropertyValue = personPropertyUpdateEvent.previousPropertyValue();
final RegionId regionId = regionsDataManager.getPersonRegion(personId);
Expand All @@ -176,7 +177,7 @@ private void handlePersonPropertyUpdateEvent(ReportContext reportContext, Person
private void handlePersonImminentRemovalEvent(ReportContext reportContext, PersonImminentRemovalEvent personImminentRemovalEvent) {
PersonId personId = personImminentRemovalEvent.personId();
RegionId regionId = regionsDataManager.getPersonRegion(personId);
for (PersonPropertyId personPropertyId : includedPersonPropertyIds) {
for (PersonPropertyId personPropertyId : currentProperties) {
final Object personPropertyValue = personPropertiesDataManager.getPersonPropertyValue(personId, personPropertyId);
decrement(regionId, personPropertyId, personPropertyValue);
}
Expand All @@ -186,7 +187,7 @@ private void handlePersonRegionUpdateEvent(ReportContext reportContext, PersonRe
PersonId personId = personRegionUpdateEvent.personId();
RegionId previousRegionId = personRegionUpdateEvent.previousRegionId();
RegionId regionId = personRegionUpdateEvent.currentRegionId();
for (final PersonPropertyId personPropertyId : includedPersonPropertyIds) {
for (final PersonPropertyId personPropertyId : currentProperties) {
final Object personPropertyValue = personPropertiesDataManager.getPersonPropertyValue(personId, personPropertyId);
increment(regionId, personPropertyId, personPropertyValue);
decrement(previousRegionId, personPropertyId, personPropertyValue);
Expand All @@ -206,9 +207,68 @@ private void increment(final RegionId regionId, final PersonPropertyId personPro

private PeopleDataManager peopleDataManager;

private boolean isCurrentProperty(PersonPropertyId personPropertyId) {
return currentProperties.contains(personPropertyId);
}

private boolean addToCurrentProperties(PersonPropertyId personPropertyId) {

// There are eight possibilities:

/*
* P -- the default inclusion policy
*
* I -- the property is explicitly included
*
* X -- the property is explicitly excluded
*
* C -- the property should be on the current properties
*
*
* P I X C Table
*
* TRUE TRUE FALSE TRUE
*
* TRUE FALSE FALSE TRUE
*
* FALSE TRUE FALSE TRUE
*
* FALSE FALSE FALSE FALSE
*
* TRUE TRUE TRUE FALSE -- not possible
*
* TRUE FALSE TRUE FALSE
*
* FALSE TRUE TRUE FALSE -- not possible
*
* FALSE FALSE TRUE FALSE
*
*
* Two of the cases above are contradictory since a property cannot be
* both explicitly included and explicitly excluded
*
*/

// if X is true then we don't add the property
if (excludedPersonPropertyIds.contains(personPropertyId)) {
return false;
}

// if both P and I are false we don't add the property
boolean included = includedPersonPropertyIds.contains(personPropertyId);

if (!included && !includeNewProperties) {
return false;
}

// we have failed to reject the property
currentProperties.add(personPropertyId);

return true;
}

@Override
protected void prepare(final ReportContext reportContext) {

regionsDataManager = reportContext.getDataManager(RegionsDataManager.class);
personPropertiesDataManager = reportContext.getDataManager(PersonPropertiesDataManager.class);
peopleDataManager = reportContext.getDataManager(PeopleDataManager.class);
Expand All @@ -217,22 +277,18 @@ protected void prepare(final ReportContext reportContext) {
reportContext.subscribe(PersonImminentRemovalEvent.class, this::handlePersonImminentRemovalEvent);
reportContext.subscribe(PersonRegionUpdateEvent.class, this::handlePersonRegionUpdateEvent);
reportContext.subscribeToSimulationState(this::recordSimulationState);
reportContext.subscribe(PersonPropertyDefinitionEvent.class, this::handlePersonPropertyDefinitionEvent);
reportContext.subscribe(PersonPropertyUpdateEvent.class, this::handlePersonPropertyUpdateEvent);

if (includeNewProperties) {
includedPersonPropertyIds.addAll(personPropertiesDataManager.getPersonPropertyIds());
includedPersonPropertyIds.removeAll(excludedPersonPropertyIds);
reportContext.subscribe(PersonPropertyDefinitionEvent.class, this::handlePersonPropertyDefinitionEvent);
for (PersonPropertyId personPropertyId : personPropertiesDataManager.getPersonPropertyIds()) {
addToCurrentProperties(personPropertyId);
}

reportContext.subscribe(PersonPropertyUpdateEvent.class, this::handlePersonPropertyUpdateEvent);

for (PersonId personId : peopleDataManager.getPeople()) {
final RegionId regionId = regionsDataManager.getPersonRegion(personId);
for (final PersonPropertyId personPropertyId : includedPersonPropertyIds) {
if (personPropertiesDataManager.personPropertyIdExists(personPropertyId)) {
final Object personPropertyValue = personPropertiesDataManager.getPersonPropertyValue(personId, personPropertyId);
increment(regionId, personPropertyId, personPropertyValue);
}
for (final PersonPropertyId personPropertyId : currentProperties) {
final Object personPropertyValue = personPropertiesDataManager.getPersonPropertyValue(personId, personPropertyId);
increment(regionId, personPropertyId, personPropertyValue);
}
}
}
Expand All @@ -247,13 +303,12 @@ private void recordSimulationState(ReportContext reportContext, SimulationStateC
}
for (PersonPropertyId personPropertyId : excludedPersonPropertyIds) {
builder.excludePersonProperty(personPropertyId);
}
}
}

private void handlePersonPropertyDefinitionEvent(ReportContext actorContext, PersonPropertyDefinitionEvent personPropertyDefinitionEvent) {
PersonPropertyId personPropertyId = personPropertyDefinitionEvent.personPropertyId();
if (!excludedPersonPropertyIds.contains(personPropertyId)) {
includedPersonPropertyIds.add(personPropertyId);
if (addToCurrentProperties(personPropertyId)) {
for (PersonId personId : peopleDataManager.getPeople()) {
final RegionId regionId = regionsDataManager.getPersonRegion(personId);
final Object personPropertyValue = personPropertiesDataManager.getPersonPropertyValue(personId, personPropertyId);
Expand Down

0 comments on commit 3565aae

Please sign in to comment.