From 349a5ea2cbca5abb3f05ba70e352db4896e8d255 Mon Sep 17 00:00:00 2001 From: travisrieglerleidos Date: Wed, 25 Jun 2025 09:33:37 -0400 Subject: [PATCH 1/2] Add weekly report period to PeriodicReport Added a new weekly value to ReportPeriod and updated PeriodicReport to accommodate it. Updated AT_PeriodicReport to test for weekly reports and updated other failing tests to now account for the weekly ReportPeriod value. --- .../reports/support/PeriodicReport.java | 26 +++- .../plugins/reports/support/ReportPeriod.java | 2 +- .../reports/AT_GroupPropertyReport.java | 10 ++ ...onPropertyInteractionReportPluginData.java | 2 +- .../reports/AT_PersonPropertyReport.java | 5 + .../reports/support/AT_PeriodicReport.java | 123 ++++++++++++++++++ 6 files changed, 163 insertions(+), 5 deletions(-) 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..7c1f5cc13 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); @@ -156,6 +171,7 @@ private void close(final ReportContext reportContext) { private double getNextPlanTime() { switch (reportPeriod) { case DAILY: + case WEEKLY: return reportingDay; case HOURLY: @@ -178,6 +194,10 @@ private void incrementReportingTimeFields() { reportingDay++; } break; + case WEEKLY: + reportingDay += 7; + 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); From 843fba517d2e281cfdf01525c7d1b97763e42a45 Mon Sep 17 00:00:00 2001 From: travisrieglerleidos Date: Thu, 26 Jun 2025 12:21:31 -0400 Subject: [PATCH 2/2] Update weekly logic in PeriodicReport Updated the weekly logic in PeriodicReport to now have weekly reports be flushed on an interval of day 7, day 14, day 21 etc regardless of what the sim start time was. With the old logic, we were just adding 7 days to whatever the sim start time was, which if the sim start time was 3.5 then the flush interval would be day 10, day 17, day 24, etc. --- .../simulation/plugins/reports/support/PeriodicReport.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 7c1f5cc13..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 @@ -171,12 +171,13 @@ private void close(final ReportContext reportContext) { private double getNextPlanTime() { switch (reportPeriod) { case DAILY: - case WEEKLY: return reportingDay; case HOURLY: return reportingDay + (double) (reportingHour) / 24; + case WEEKLY: + return reportingWeek * 7; default: throw new RuntimeException("unhandled report period " + reportPeriod); } @@ -195,7 +196,8 @@ private void incrementReportingTimeFields() { } break; case WEEKLY: - reportingDay += 7; + int extraDays = reportingDay % 7; + reportingDay += (7 - extraDays); reportingWeek++; break; case END_OF_SIMULATION: