diff --git a/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/PeriodicReport.java b/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/PeriodicReport.java index daa188d64..c7402ce15 100644 --- a/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/PeriodicReport.java +++ b/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/PeriodicReport.java @@ -50,10 +50,15 @@ public PeriodicReport(ReportLabel reportLabel, ReportPeriod reportPeriod) { */ private Integer reportingHour = 0; + /* + * The week value to be used in report lines + */ + private Integer reportingWeek = 0; + /** * Adds the time field column(s) to the given {@link ReportHeader.Builder} as * appropriate to the {@link ReportPeriod} specified during construction. DAILY - * : Day HOURLY : Day, Hour END_OF_SIMULATION has no header additions + * : day; HOURLY : day, hour; WEEKLY : day, week; END_OF_SIMULATION has no header additions */ protected final ReportHeader.Builder addTimeFieldHeaders(ReportHeader.Builder reportHeaderBuilder) { switch (reportPeriod) { @@ -67,6 +72,10 @@ protected final ReportHeader.Builder addTimeFieldHeaders(ReportHeader.Builder re reportHeaderBuilder.add("day"); reportHeaderBuilder.add("hour"); break; + case WEEKLY: + reportHeaderBuilder.add("day"); + reportHeaderBuilder.add("week"); + break; default: throw new RuntimeException("unknown report period " + reportPeriod); } @@ -82,8 +91,9 @@ protected final ReportPeriod getReportPeriod() { } /** - * Places the current reporting day and report hour on the report as appropriate - * to the {@link ReportPeriod} specified during construction. + * Places the current reporting day, reporting hour, and reporting week on the + * report as appropriate to the {@link ReportPeriod} specified during + * construction. */ protected final void fillTimeFields(final ReportItem.Builder reportItemBuilder) { @@ -98,6 +108,10 @@ protected final void fillTimeFields(final ReportItem.Builder reportItemBuilder) reportItemBuilder.addValue(reportingDay); reportItemBuilder.addValue(reportingHour); break; + case WEEKLY: + reportItemBuilder.addValue(reportingDay); + reportItemBuilder.addValue(reportingWeek); + break; default: throw new RuntimeException("unknown report period " + reportPeriod); } @@ -124,6 +138,7 @@ public final void init(ReportContext reportContext) { if (reportingHour > 23) { reportingHour = 23; } + reportingWeek = (int) (reportContext.getTime() / 7); prepare(reportContext); @@ -161,6 +176,8 @@ private double getNextPlanTime() { case HOURLY: return reportingDay + (double) (reportingHour) / 24; + case WEEKLY: + return reportingWeek * 7; default: throw new RuntimeException("unhandled report period " + reportPeriod); } @@ -178,6 +195,11 @@ private void incrementReportingTimeFields() { reportingDay++; } break; + case WEEKLY: + int extraDays = reportingDay % 7; + reportingDay += (7 - extraDays); + reportingWeek++; + break; case END_OF_SIMULATION: // do nothing break; diff --git a/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/ReportPeriod.java b/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/ReportPeriod.java index 7c7461620..0740bb459 100644 --- a/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/ReportPeriod.java +++ b/simulation/src/main/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/ReportPeriod.java @@ -5,7 +5,7 @@ * periodicity of the report. */ public enum ReportPeriod { - HOURLY, DAILY, END_OF_SIMULATION; + HOURLY, DAILY, WEEKLY, END_OF_SIMULATION; public ReportPeriod next() { int index = (ordinal()+1) % ReportPeriod.values().length; diff --git a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/groups/reports/AT_GroupPropertyReport.java b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/groups/reports/AT_GroupPropertyReport.java index 0a7499ec6..ee364b854 100644 --- a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/groups/reports/AT_GroupPropertyReport.java +++ b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/groups/reports/AT_GroupPropertyReport.java @@ -410,6 +410,12 @@ public void testInit_ReportPeriod() { assertDoesNotThrow(() -> Integer.parseInt(reportItem.getValue(1))); assertDoesNotThrow(() -> TestGroupTypeId.valueOf(reportItem.getValue(2))); break; + case WEEKLY: + assertEquals(REPORT_WEEKLY_HEADER, reportHeader); + assertDoesNotThrow(() -> Integer.parseInt(reportItem.getValue(0))); + assertDoesNotThrow(() -> Integer.parseInt(reportItem.getValue(1))); + assertDoesNotThrow(() -> TestGroupTypeId.valueOf(reportItem.getValue(2))); + break; default: throw new RuntimeException("unhandled case " + reportPeriod); } @@ -842,6 +848,9 @@ public void testInit_ReportHeader() { case HOURLY: assertEquals(REPORT_HOURLY_HEADER, reportHeader); break; + case WEEKLY: + assertEquals(REPORT_WEEKLY_HEADER, reportHeader); + break; default: throw new RuntimeException("unhandled case " + reportPeriod); } @@ -943,6 +952,7 @@ public void testInit_State() { private static final ReportHeader REPORT_DAILY_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("day").add("group_type").add("property").add("value").add("group_count").build(); private static final ReportHeader REPORT_HOURLY_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("day").add("hour").add("group_type").add("property").add("value").add("group_count").build(); + private static final ReportHeader REPORT_WEEKLY_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("day").add("week").add("group_type").add("property").add("value").add("group_count").build(); private static final ReportHeader REPORT_END_OF_SIMULATION_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("group_type").add("property").add("value").add("group_count").build(); } diff --git a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyInteractionReportPluginData.java b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyInteractionReportPluginData.java index a1796b294..9704e5d53 100644 --- a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyInteractionReportPluginData.java +++ b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyInteractionReportPluginData.java @@ -358,7 +358,7 @@ public void testToString() { String expectedValue = "PersonPropertyInteractionReportPluginData [data=Data [" + "reportLabel=SimpleReportLabel [value=report label95], " - + "reportPeriod=HOURLY, " + + "reportPeriod=WEEKLY, " + "personPropertyIds=[" + "PERSON_PROPERTY_1_BOOLEAN_MUTABLE_NO_TRACK, " + "PERSON_PROPERTY_2_INTEGER_MUTABLE_NO_TRACK, " diff --git a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyReport.java b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyReport.java index da6105852..9f942e69d 100644 --- a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyReport.java +++ b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/personproperties/reports/AT_PersonPropertyReport.java @@ -427,6 +427,9 @@ private void testInit_ReportHeader(ReportPeriod reportPeriod) { case HOURLY: expectedReportHeader = REPORT_HOURLY_HEADER; break; + case WEEKLY: + expectedReportHeader = REPORT_WEEKLY_HEADER; + break; default: throw new RuntimeException("unhandled case " + reportPeriod); @@ -1160,6 +1163,8 @@ public String toString() { private static final ReportHeader REPORT_HOURLY_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("day").add("hour").add("region") .add("property").add("value").add("person_count").build(); + private static final ReportHeader REPORT_WEEKLY_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("day").add("week").add("region") + .add("property").add("value").add("person_count").build(); private static final ReportHeader REPORT_DAILY_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("day").add("region") .add("property").add("value").add("person_count").build(); private static final ReportHeader REPORT_END_OF_SIMULATION_HEADER = ReportHeader.builder().setReportLabel(REPORT_LABEL).add("region") diff --git a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/AT_PeriodicReport.java b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/AT_PeriodicReport.java index 7e07689e8..cf2314680 100644 --- a/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/AT_PeriodicReport.java +++ b/simulation/src/test/java/gov/hhs/aspr/ms/gcm/simulation/plugins/reports/support/AT_PeriodicReport.java @@ -140,6 +140,55 @@ protected void prepare(ReportContext reportContext) { } } + /* + * Used to test the fillTimeFields() method when the period is + * ReportPeriod.WEEKLY + */ + private static class WeeklyTestReport extends PeriodicReport { + + private MutableInteger testCounter = new MutableInteger(); + + public WeeklyTestReport(ReportLabel reportLabel, ReportPeriod reportPeriod) { + super(reportLabel, reportPeriod); + if (reportPeriod != ReportPeriod.WEEKLY) { + throw new RuntimeException("must be a weekly report"); + } + } + + @Override + protected void flush(ReportContext reportContext) { + testCounter.increment(); + ReportItem.Builder reportItemBuilder = ReportItem.builder(); + + fillTimeFields(reportItemBuilder); + + ReportItem reportItem = reportItemBuilder.setReportLabel(getReportLabel()).build(); + int dayValue = (int) FastMath.ceil(reportContext.getTime()); + int extraDays = dayValue % 7; + if (extraDays != 0) { + dayValue += (7 - extraDays); + } + + String expectedDayTimeString = Integer.toString(dayValue); + String expectedWeekTimeString = Integer.toString(dayValue / 7); + String actualDayTimeString = reportItem.getValue(0); + String actualWeekTimeString = reportItem.getValue(1); + assertEquals(expectedDayTimeString, actualDayTimeString); + assertEquals(expectedWeekTimeString, actualWeekTimeString); + + reportContext.releaseOutput(reportItem); + } + + @Override + protected void prepare(ReportContext reportContext) { + ReportHeader.Builder reportHeaderBuilder = ReportHeader.builder(); + this.addTimeFieldHeaders(reportHeaderBuilder); + ReportHeader reportHeader = reportHeaderBuilder.setReportLabel(getReportLabel()).build(); + + reportContext.releaseOutput(reportHeader); + } + } + /* * Used to test the fillTimeFields() method when the period is * ReportPeriod.END_OF_SIMULATION @@ -226,6 +275,15 @@ public void testAddTimeFieldHeaders() { assertEquals("day", headerStrings.get(0)); assertEquals("hour", headerStrings.get(1)); + reportHeaderBuilder = ReportHeader.builder(); + testReport = new TestReport(reportLabel, ReportPeriod.WEEKLY); + testReport.addTimeFieldHeaders(reportHeaderBuilder); + reportHeader = reportHeaderBuilder.setReportLabel(reportLabel).build(); + headerStrings = reportHeader.getHeaderStrings(); + assertEquals(2, headerStrings.size()); + assertEquals("day", headerStrings.get(0)); + assertEquals("week", headerStrings.get(1)); + reportHeaderBuilder = ReportHeader.builder(); testReport = new TestReport(reportLabel, ReportPeriod.DAILY); testReport.addTimeFieldHeaders(reportHeaderBuilder); @@ -350,6 +408,63 @@ public void testFillTimeFields_Hourly() { } + @Test + @UnitTestMethod(target = PeriodicReport.class, name = "init", args = { ReportContext.class }) + public void testFillTimeFields_Weekly() { + double simulationEndTime = 21.5; + + List plugins = new ArrayList<>(); + + ReportLabel reportLabel = new SimpleReportLabel("report"); + WeeklyTestReport weeklyTestReport = new WeeklyTestReport(reportLabel, ReportPeriod.WEEKLY); + + plugins.add(Plugin.builder()// + .setPluginId(new SimplePluginId("anonymous plugin"))// + .setInitializer((pc) -> { + pc.addReport(weeklyTestReport::init); + })// + .build()); + + TestPluginData.Builder pluginBuilder = TestPluginData.builder(); + + // create an agent that will make the engine run for a few days + pluginBuilder.addTestActorPlan("actor", new TestActorPlan(simulationEndTime, (c) -> { + })); + + TestPluginData testPluginData = pluginBuilder.build(); + Plugin testPlugin = TestPlugin.getTestPlugin(testPluginData); + plugins.add(testPlugin); + + // build and execute the engine + TestOutputConsumer testOutputConsumer = TestSimulation.builder()// + .addPlugins(plugins)// + .build()// + .execute(); + + // weeks 0 through 4 weeks inclusive + Set expectedWeeks = new LinkedHashSet<>(); + Set expectedDays = new LinkedHashSet<>(); + for (int i = 1; i <= 4; i++) { + expectedWeeks.add(i); + expectedDays.add(i * 7); + } + + Set actualDays = new LinkedHashSet<>(); + Set actualWeeks = new LinkedHashSet<>(); + Map outputItems = testOutputConsumer.getOutputItemMap(ReportItem.class); + for (ReportItem reportItem : outputItems.keySet()) { + Integer count = outputItems.get(reportItem); + assertEquals(1, count); + int day = Integer.parseInt(reportItem.getValue(0)); + actualDays.add(day); + int week = Integer.parseInt(reportItem.getValue(1)); + actualWeeks.add(week); + } + + assertEquals(expectedDays, actualDays); + assertEquals(expectedWeeks, actualWeeks); + } + @Test @UnitTestMethod(target = PeriodicReport.class, name = "init", args = { ReportContext.class }) public void testFillTimeFields_EndOfSimulation() { @@ -459,6 +574,14 @@ public void testInit() { } expectedTimes.add(simulationEndTime); + break; + case WEEKLY: + int lastWeek = (int) (simulationEndTime / 7); + for (int i = 1; i <= lastWeek; i++) { + double time = i * 7; + expectedTimes.add(time); + } + expectedTimes.add(simulationEndTime); break; default: throw new RuntimeException("unhandled report period " + reportPeriod);