diff --git a/.gitignore b/.gitignore index 1a8be07..9049a77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,46 @@ +# Exclude OS-specific junk files. .DS_Store + +### Exclude env files - may contain confidential env-vars. .env* *.env + +### Exclude the Gradle cache, etc. .gradle/ + +### Bypass any exclude *.jar exclusions for the Gradle Wrapper JAR. !**/gradle/wrapper/gradle-wrapper.jar + +### Exclude IntelliJ workspace, project and module files - may contain confidential env-vars. .idea/ *.iws *.iml *.ipr + +### Exclude .log log files - except source files. +*.log +!**/src/main/**/*.log +!**/src/test/**/*.log + +### Exclude .run run configurations - may contain confidential env-vars. .run/ + +### Exclude Visual Studio Code workspace and project files - may contain confidential env-vars. .vscode/ +### IDE or build tool build output directories - except source files. bin/ +build/ +dist/ +out/ +target/ !**/src/main/**/bin/ !**/src/test/**/bin/ - -build/ !**/src/main/**/build/ !**/src/test/**/build/ - -dist/ !**/src/main/**/dist/ !**/src/test/**/dist/ - -out/ !**/src/main/**/out/ !**/src/test/**/out/ - -target/ !**/src/main/**/target/ !**/src/test/**/target/ diff --git a/deltaconcor/README.md b/deltaconcor/README.md index 7ade5b9..2cf7a88 100644 --- a/deltaconcor/README.md +++ b/deltaconcor/README.md @@ -1,7 +1,47 @@ # Delta Concor Contribution Records -Produce a CSV report detailing when changes to XML Concor Contribution records occurred (and which elements were -changed). +Produce a CSV report detailing when changes to CONTRIBUTIONS data occurred (and which XML elements were changed). -The columns of the produced CSV file correspond to the elements in the XML Concor Contribution schema, and the rows -correspond to days when the change occurred. +The columns of the produced CSV file correspond to the elements in the XML schema, and the rows correspond to days +when the change occurred. + +## Algorithm + +#### Preliminaries +* Keep a `Set` of repId/maatId that we are missing. +* Keep a `Map` of maatId -> previous CONTRIBUTIONS data for that maatId, starts empty. +* Keep a `List` of `flag='new'` data for each day +* Keep a `List` of `flag='update'` data for each day + - These Lists will be used to generate the CSV reports. +* Translate the start date and end date to a start ID and end ID. + +#### Pass one (find maatIds that we need previous CONTRIBUTIONS for) +* Walk forward from the start ID to the end ID over `status='SENT` rows to find maatIds we don't have: + * If it is `flag='new'`, then add the contribution to the `Map`. + * If it is `flag='update'` and the maatId is not in the `Map`, then add it to the `Set`. + +#### Pass two (back-fill the map of previous CONTRIBUTIONS data for the missing maatIds) +* Walk backward from the start ID over `status='SENT'` rows: + * Populate the `Map` of maatId -> previous CONTRIBUTIONS data for each missing maatId. + * Remove from the `Set`. + * Stop when the `Set` is empty. +* Alternate strategy for the last few maatIds that take all the time: + * Query for rows that have maatIds in the `Set`, ordered by id descending, with a limit. + * Work with the results as above. + * Next time round, update the query to include only the smaller `Set` of maatIds that remain to back-fill. + +### Pass three (generate the daily CSV data) +* Walk forward from the start ID to the end ID over `status='SENT'` rows: + - If it is `flag='new'`, then: + - Compare the current CONTRIBUTIONS data with null data. + - Update the `List` with `flag='new'` data for that day. + - Update the `Map` with the current CONTRIBUTIONS data. + - If it is `flag='update'`, then: + - Compare the current CONTRIBUTIONS data with the previous CONTRIBUTIONS data from the `Map`. + - Update the `List` with `flag='update'` data for that day. + - Update the `Map` with the current CONTRIBUTIONS data. + +## Near future changes +* Revisit how to compare "array aggregate" (e.g. `breathingSpace`) and "array leaf" (e.g. `registration`) elements in + the CONTRIBUTIONS data. Just comparing the count of elements is not sufficient for most of these (but might be OK for + `letter` and `ccOutcome` which are generally append-only). diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/Runner.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/Runner.java index 6d40b50..2142ee3 100644 --- a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/Runner.java +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/Runner.java @@ -9,11 +9,23 @@ import org.springframework.data.domain.Slice; import org.springframework.stereotype.Component; import uk.gov.justice.laadces.deltaconcor.generated.CONTRIBUTIONS; +import uk.gov.justice.laadces.deltaconcor.report.Change; import uk.gov.justice.laadces.deltaconcor.repository.ConcorContribution; import uk.gov.justice.laadces.deltaconcor.repository.ConcorContributionRepository; +import uk.gov.justice.laadces.deltaconcor.repository.ConcorTemplateRepository; +import uk.gov.justice.laadces.deltaconcor.service.ChangeService; +import uk.gov.justice.laadces.deltaconcor.service.CsvOutputService; import uk.gov.justice.laadces.deltaconcor.service.XMLTransformService; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicInteger; /** * ApplicationRunner class that examines the data in the database. @@ -23,12 +35,21 @@ @RequiredArgsConstructor @Slf4j class Runner implements ApplicationRunner { + private static final int BATCH_SIZE = 1000; + + // Constructor-wired Spring components private final ConcorContributionRepository concorRepository; + private final ConcorTemplateRepository templRepository; private final XMLTransformService xmlTransformService; + private final ChangeService changeService; + private final CsvOutputService csvoutput; - private static final long LOW_ID = 298_739_928L; // First rec of 2020 - private static final long HIGH_ID = 499_999_999L; - private static final int BATCH_SIZE = 1000; + private final Set maatIdsToBackfill = new TreeSet<>(); + private final Map maatIdToContribution = new TreeMap<>(); + private final ArrayList newChanges = new ArrayList<>(); + private final ArrayList updChanges = new ArrayList<>(); + + private int countRowsWithPredecessorsInDateRange = 0; /** * Main runner method, just iterates over the database table, calling #examineConcorContribution() for each row. @@ -38,58 +59,273 @@ class Runner implements ApplicationRunner { * @throws Exception if an error occurs. */ @Override - public void run(ApplicationArguments args) throws Exception { - final int recs = concorRepository.countByIdBetween(LOW_ID, HIGH_ID); - log.info("There are {} rows with {} <= id <= {}", recs, LOW_ID, HIGH_ID); - log.info("-----BEGIN-----"); - final long startTime = System.currentTimeMillis(); - - long nextLowId = LOW_ID; - int recsLoaded = 0; + public void run(final ApplicationArguments args) throws Exception { + final LocalDate startDate = LocalDate.of(2024, 10, 1); // inclusive + final LocalDate endDate = LocalDate.of(2024, 11, 1); // exclusive + final int nDays = (int) ChronoUnit.DAYS.between(startDate, endDate); + log.info("run: Processing {} days in period [{}, {})", nDays, startDate, endDate); + + reset(nDays); + for (int i = 0; i < nDays; ++i) { + newChanges.add(new Change(startDate.plusDays(i).toString())); + updChanges.add(new Change(startDate.plusDays(i).toString())); + } + log.info("run: Counts reset, now finding ids to process"); + + final long startId = templRepository.minIdAfterOrEquals(startDate); + final long endId = templRepository.maxIdBefore(endDate); + log.info("run: Processing concor_contributions rows with {} <= id <= {}", startId, endId); + + findMaatIdsToBackfill(startId, endId); + log.info("run: {} new maatIds, {} maatIds to back-fill, {} contributions with predecessor in date range", + maatIdToContribution.size(), maatIdsToBackfill.size(), countRowsWithPredecessorsInDateRange); + + if (!maatIdsToBackfill.isEmpty()) { + final long lastId = slowBackfillMaatIds(startId - 1); + log.info("run: {} maatIds left to back-fill after slow walk back {} >= id >= {}", maatIdsToBackfill.size(), startId - 1, lastId); + if (!maatIdsToBackfill.isEmpty()) { + final long lastId2 = fastBackfillMaatIds(lastId - 1); + log.info("run: {} maatIds left to back-fill after fast walk back {} >= id >= {}", maatIdsToBackfill.size(), lastId - 1, lastId2); + } + } + log.info("run: {} maatId predecessors found", maatIdToContribution.size()); + + generateDailyCounts(startDate, startId, endId); + log.info("run: Counts generated, now outputting CSV files"); + + csvoutput.writeChanges("/tmp/deltaconcor-new.csv", newChanges); + csvoutput.writeChanges("/tmp/deltaconcor-upd.csv", updChanges); + log.info("run: Done"); + } + + private void reset(final int nDays) { + maatIdsToBackfill.clear(); + maatIdToContribution.clear(); + newChanges.clear(); + newChanges.trimToSize(); + newChanges.ensureCapacity(nDays); + updChanges.clear(); + updChanges.trimToSize(); + updChanges.ensureCapacity(nDays); + } + + private long findMaatIdsToBackfill(final long startId, final long endId) { + return visitForwards(startId, endId, concorContribution -> { + final long maatId = concorContribution.repId(); + final CONTRIBUTIONS dto = xmlTransformService.fromXML(concorContribution.fullXml()); + if (dto == null) { + log.warn("findMaatIdsToBackfill: Failed to transform XML for concorContribution {}, maatId {}", concorContribution.id(), maatId); + } else if (dto.getFlag() == null) { + log.warn("findMaatIdsToBackfill: No flag for concorContribution {}, maatId {}", concorContribution.id(), maatId); + } else if (dto.getFlag().equalsIgnoreCase("new")) { + maatIdToContribution.put(maatId, dto); + } else if (dto.getFlag().equalsIgnoreCase("update")) { + if (!maatIdToContribution.containsKey(maatId)) { + maatIdsToBackfill.add(maatId); + } else { + ++countRowsWithPredecessorsInDateRange; + } + } else { + log.warn("findMaatIdsToBackfill: Unknown flag {} for concorContribution {}, maatId {}", dto.getFlag(), concorContribution.id(), maatId); + } + return true; + }); + } + + private long slowBackfillMaatIds(final long startId) { + final AtomicInteger countMisses = new AtomicInteger(); // needed for 'effectively final' requirement in lambda. + return slowVisitBackwards(startId, 0, concorContribution -> { + final long maatId = concorContribution.repId(); + final CONTRIBUTIONS dto = xmlTransformService.fromXML(concorContribution.fullXml()); + countMisses.incrementAndGet(); + if (dto == null) { + log.warn("slowBackfillMaatIds: Failed to transform XML for concorContribution {}, maatId {}", concorContribution.id(), maatId); + } else { + if (maatIdsToBackfill.contains(maatId)) { + maatIdToContribution.put(maatId, dto); + maatIdsToBackfill.remove(maatId); + if (maatIdsToBackfill.size() % 10 == 0) { + log.info("slowBackfillMaatIds: {} maatIds left to back-fill ({} misses since last)", maatIdsToBackfill.size(), countMisses.get()); + } + countMisses.set(0); + } + } + // switch to fast walk-back after 200 misses in a row (when there are 1000 or fewer maatIds to back-fill). + return !(maatIdsToBackfill.isEmpty() || (maatIdsToBackfill.size() <= 1000 && countMisses.get() >= 200)); + }); + } + + private long fastBackfillMaatIds(final long startId) { + return fastVisitBackwards(startId, 0, concorContribution -> { + final long maatId = concorContribution.repId(); + final CONTRIBUTIONS dto = xmlTransformService.fromXML(concorContribution.fullXml()); + if (dto == null) { + log.warn("fastBackfillMaatIds: Failed to transform XML for concorContribution {}, maatId {}", concorContribution.id(), maatId); + } else { + if (maatIdsToBackfill.contains(maatId)) { + maatIdToContribution.put(maatId, dto); + maatIdsToBackfill.remove(maatId); + if (maatIdsToBackfill.size() % 10 == 0) { + log.info("fastBackfillMaatIds: {} maatIds left to back-fill", maatIdsToBackfill.size()); + } + } + } + return !maatIdsToBackfill.isEmpty(); + }); + } + + private long generateDailyCounts(LocalDate startDate, long startId, long endId) { + return visitForwards(startId, endId, concorContribution -> { + final long maatId = concorContribution.repId(); + final CONTRIBUTIONS dto = xmlTransformService.fromXML(concorContribution.fullXml()); + if (dto == null) { + log.warn("generateDailyCounts: Failed to transform XML for concorContribution {}, maatId {}", concorContribution.id(), maatId); + } else { + final int dayIndex = (int) ChronoUnit.DAYS.between(startDate, concorContribution.dateCreated()); + if (dayIndex < 0 || dayIndex >= newChanges.size()) { + log.warn("generateDailyCounts: {} out of range for concorContribution {}, maatId {}", concorContribution.dateCreated(), concorContribution.id(), maatId); + return true; + } + if (dto.getFlag().equalsIgnoreCase("new")) { + final Change counts = newChanges.get(dayIndex); + counts.setSentRecords(counts.getSentRecords() + 1); + if (changeService.compare(null, dto, counts)) { + counts.setChangedRecords(counts.getChangedRecords() + 1); + } + } else if (dto.getFlag().equalsIgnoreCase("update")) { + final Change counts = updChanges.get(dayIndex); + counts.setSentRecords(counts.getSentRecords() + 1); + final var prev = maatIdToContribution.get(maatId); + if (changeService.compare(prev, dto, counts)) { + counts.setChangedRecords(counts.getChangedRecords() + 1); + } + } else { + log.warn("generateDailyCounts: Unknown flag {} for concorContribution {}, maatId {}", dto.getFlag(), concorContribution.id(), maatId); + } + maatIdToContribution.put(maatId, dto); + } + return true; + }); + } + + /** + * Uses an exhaustive query to visit forwards through the ConcorContribution table. + * + * @param startId start ID to walk forward from + * @param endId end ID to finish walking forward until + * @param visitor the RecordVisitor to call for each record visited + * @return the ID of the last record visited + */ + private long visitForwards(final long startId, final long endId, final RecordVisitor visitor) { + assert startId <= endId; + + long nextLowId = startId; Slice slice; List content; int contentLen; long sliceLowId, sliceHighId; do { - slice = concorRepository.findByIdBetweenOrderById(nextLowId, HIGH_ID, Pageable.ofSize(BATCH_SIZE)); + slice = concorRepository.findByIdBetweenAndStatusOrderById( + nextLowId, endId, "SENT", Pageable.ofSize(BATCH_SIZE)); content = slice.getContent(); contentLen = content.size(); - recsLoaded += contentLen; sliceLowId = contentLen > 0 ? content.get(0).id() : nextLowId; - sliceHighId = contentLen > 0 ? content.get(contentLen - 1).id() : HIGH_ID; + sliceHighId = contentLen > 0 ? content.get(contentLen - 1).id() : endId; nextLowId = sliceHighId + 1; - log.info("Loaded {} rows with {} <= id <= {}. {} / {} rows loaded, {} ms", contentLen, sliceLowId, sliceHighId, recsLoaded, recs, System.currentTimeMillis() - startTime); - content.forEach(this::examineConcorContribution); - } while (slice.hasNext() && contentLen > 0 && nextLowId <= HIGH_ID); + log.info("visitForwards: {} rows with {} <= id <= {}", contentLen, sliceLowId, sliceHighId); + for (final ConcorContribution concorContribution : content) { + if (!visitor.visit(concorContribution)) { + return concorContribution.id(); + } + } + } while (slice.hasNext() && contentLen > 0 && nextLowId <= endId); + return endId; + } + + /** + * Uses a slower query to visit backwards through the ConcorContribution table; however this method works even if + * there are more than 1000 maatIds to back-fill. + * + * @param startId start ID to walk back from + * @param endId end ID to finish walking back at + * @param visitor the RecordVisitor to call for each record visited + * @return the ID of the last record visited + */ + private long slowVisitBackwards(final long startId, final long endId, final RecordVisitor visitor) { + assert startId >= endId; + + long nextHighId = startId; + Slice slice; + List content; + int contentLen; + long sliceLowId, sliceHighId; - final long timeTaken = System.currentTimeMillis() - startTime; - log.info("-----END-----"); - log.info("Completed {} / {} rows, {} ms = {} ms / rec", recsLoaded, recs, timeTaken, 1.0 * timeTaken / recsLoaded); + do { + slice = concorRepository.findByIdBetweenAndStatusOrderByIdDesc( + endId, nextHighId, "SENT", Pageable.ofSize(BATCH_SIZE)); + content = slice.getContent(); + contentLen = content.size(); + sliceHighId = contentLen > 0 ? content.get(0).id() : nextHighId; + sliceLowId = contentLen > 0 ? content.get(contentLen - 1).id() : endId; + nextHighId = sliceLowId - 1; + log.info("slowVisitBackwards: {} rows with {} >= id >= {}", contentLen, sliceHighId, sliceLowId); + for (final ConcorContribution concorContribution : content) { + if (!visitor.visit(concorContribution)) { + return concorContribution.id(); + } + } + } while (slice.hasNext() && contentLen > 0 && nextHighId >= endId); + return endId; } /** - * Called for each row in the database table. - *

- * 1. Adds the row to the statistics repository. - * 2. Transforms the XML to a DTO. - * 3. Transforms the DTO to JSON. - * 4. Validates the JSON against the JSON schema. - * 5. Transforms the JSON back to a DTO. - * 6. Transforms the DTO to XML. - * 7. Compares the transformed XML to the original XML. - * 8. Logs any anomalies (diffs or violations) to the application log. + * Uses a faster query to visit backwards through the ConcorContribution table; however it only works if there are + * less than 1000 maatIds to back-fill. * - * @param concorContribution The database table row to process. + * @param startId start ID to walk back from + * @param endId end ID to finish walking back at + * @param visitor the RecordVisitor to call for each record visited + * @return the ID of the last record visited */ - void examineConcorContribution(ConcorContribution concorContribution) { - try { - long id = concorContribution.id(); - String xmlOriginal = concorContribution.fullXml(); - // Transform XML -> DTO - CONTRIBUTIONS dto1 = xmlTransformService.fromXML(xmlOriginal); - } catch (Exception e) { - log.error("Exception thrown", e); - } + private long fastVisitBackwards(final long startId, final long endId, final RecordVisitor visitor) { + assert startId >= endId; + + long nextHighId = startId; + Slice slice; + List content; + int contentLen; + long sliceLowId, sliceHighId; + + do { + slice = concorRepository.findByIdBetweenAndStatusAndRepIdInOrderByIdDesc( + endId, nextHighId, "SENT", maatIdsToBackfill, Pageable.ofSize(BATCH_SIZE)); + content = slice.getContent(); + contentLen = content.size(); + sliceHighId = contentLen > 0 ? content.get(0).id() : nextHighId; + sliceLowId = contentLen > 0 ? content.get(contentLen - 1).id() : endId; + nextHighId = sliceLowId - 1; + log.info("fastVisitBackwards: {} rows with {} >= id >= {}", contentLen, sliceHighId, sliceLowId); + for (final ConcorContribution concorContribution : content) { + if (!visitor.visit(concorContribution)) { + return concorContribution.id(); + } + } + } while (slice.hasNext() && contentLen > 0 && nextHighId >= endId); + return endId; + } + + /** + * Functional interface for visiting a set of ConcorContribution records (forwards or backwards). + */ + @FunctionalInterface + interface RecordVisitor { + /** + * Visits a ConcorContribution record. + * + * @param concorContribution the record to visit. + * @return true to continue visiting, false to stop. + */ + boolean visit(ConcorContribution concorContribution); } } diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorChangeCounts.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/Change.java similarity index 99% rename from deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorChangeCounts.java rename to deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/Change.java index 0b43659..8919692 100644 --- a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorChangeCounts.java +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/Change.java @@ -3,10 +3,10 @@ import lombok.Data; @Data -public class ConcorChangeCounts { +public class Change { private final String date; // predefined (YYYY-MM-DD) passed to constructor - private final int sentRecords; // predefined passed to constructor + private int sentRecords; // agg private int changedRecords; // agg private int applicant; // agg diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorChangeCountsFieldComparator.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ChangeFieldComparator.java similarity index 59% rename from deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorChangeCountsFieldComparator.java rename to deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ChangeFieldComparator.java index da0cf13..97f8fa6 100644 --- a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorChangeCountsFieldComparator.java +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ChangeFieldComparator.java @@ -2,19 +2,21 @@ import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Locale; -class ConcorChangeCountsFieldComparator implements Comparator { - static final ConcorChangeCountsFieldComparator INSTANCE = new ConcorChangeCountsFieldComparator(); +/** + * This exists to provide a consistent ordering of fields in the Change class when OpenCSV outputs them as CSV rows. + */ +public class ChangeFieldComparator implements Comparator { + public static final ChangeFieldComparator INSTANCE = new ChangeFieldComparator(); private final List fieldOrder; - private ConcorChangeCountsFieldComparator() { + private ChangeFieldComparator() { this.fieldOrder = new ArrayList<>(); - for (Field field : ConcorChangeCounts.class.getDeclaredFields()) { + for (Field field : Change.class.getDeclaredFields()) { fieldOrder.add(field.getName().toLowerCase(Locale.ROOT)); } } diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorContributionRepository.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorContributionRepository.java index fe8ff72..0fb8ff3 100644 --- a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorContributionRepository.java +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorContributionRepository.java @@ -4,11 +4,17 @@ import org.springframework.data.domain.Slice; import org.springframework.data.repository.PagingAndSortingRepository; +import java.util.Set; + /** * Spring Data JDBC repository for accessing the MAAT DB's CONCOR_CONTRIBUTIONS table. */ public interface ConcorContributionRepository extends PagingAndSortingRepository { int countByIdBetween(long lowId, long highId); - Slice findByIdBetweenOrderById(long lowId, long highId, Pageable limit); + Slice findByIdBetweenAndStatusOrderById(long lowId, long highId, String status, Pageable limit); + + Slice findByIdBetweenAndStatusOrderByIdDesc(long lowId, long highId, String status, Pageable limit); + + Slice findByIdBetweenAndStatusAndRepIdInOrderByIdDesc(long lowId, long highId, String status, Set repIds, Pageable limit); } diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorTemplateRepository.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorTemplateRepository.java new file mode 100644 index 0000000..7d5153a --- /dev/null +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/repository/ConcorTemplateRepository.java @@ -0,0 +1,25 @@ +package uk.gov.justice.laadces.deltaconcor.repository; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; + +/** + * Repository for accessing the CONCOR_CONTRIBUTIONS table but in a way that doesn't involve Spring Data JDBC. + * For example, methods that don't return a ConcorContribution entity instance or list. + */ +@Repository +@RequiredArgsConstructor +public class ConcorTemplateRepository { + private final JdbcTemplate jdbcTemplate; + + public Long maxIdBefore(LocalDate endDate) { + return jdbcTemplate.queryForObject("SELECT MAX(id) FROM concor_contributions WHERE date_created < ?", Long.class, endDate); + } + + public Long minIdAfterOrEquals(LocalDate startDate) { + return jdbcTemplate.queryForObject("SELECT MIN(id) FROM concor_contributions WHERE date_created >= ?", Long.class,startDate); + } +} diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorComparison.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/service/ChangeService.java similarity index 66% rename from deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorComparison.java rename to deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/service/ChangeService.java index af37929..4bee090 100644 --- a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/report/ConcorComparison.java +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/service/ChangeService.java @@ -1,63 +1,245 @@ -package uk.gov.justice.laadces.deltaconcor.report; +package uk.gov.justice.laadces.deltaconcor.service; import org.springframework.stereotype.Component; import uk.gov.justice.laadces.deltaconcor.generated.CONTRIBUTIONS; +import uk.gov.justice.laadces.deltaconcor.report.Change; import java.util.List; import java.util.Objects; /** - * This class has a set of methods for updating a ConcorChangeCounts instance based on the - * differences between two CONTRIBUTIONS instances. + * This class has a set of methods for updating Change instances based on the differences between two CONTRIBUTIONS + * instances, or for combining multiple Change instances into a single Change instance by addition. *

* Note this is a concrete utility class, and has no relationship to the JRE Comparator or Comparable interfaces. */ @Component -public class ConcorComparison { +public class ChangeService { + /** + * Adds the counts in `other` to the counts in `change`. + * @param change Change counts to add `other` to (i.e. will contain the result). + * @param other Change counts to be added to `change` (i.e. remains unchanged). + */ + public void addTo(Change change, Change other) { + change.setSentRecords(change.getSentRecords() + other.getSentRecords()); + change.setChangedRecords(change.getChangedRecords() + other.getChangedRecords()); + + change.setApplicant(change.getApplicant() + other.getApplicant()); + change.setApplicant_firstName(change.getApplicant_firstName() + other.getApplicant_firstName()); + change.setApplicant_lastName(change.getApplicant_lastName() + other.getApplicant_lastName()); + change.setApplicant_dob(change.getApplicant_dob() + other.getApplicant_dob()); + change.setApplicant_niNumber(change.getApplicant_niNumber() + other.getApplicant_niNumber()); + change.setApplicant_landline(change.getApplicant_landline() + other.getApplicant_landline()); + change.setApplicant_mobile(change.getApplicant_mobile() + other.getApplicant_mobile()); + change.setApplicant_email(change.getApplicant_email() + other.getApplicant_email()); + change.setApplicant_preferredPaymentDay(change.getApplicant_preferredPaymentDay() + other.getApplicant_preferredPaymentDay()); + change.setApplicant_preferredPaymentMethod(change.getApplicant_preferredPaymentMethod() + other.getApplicant_preferredPaymentMethod()); + change.setApplicant_preferredPaymentMethod_code(change.getApplicant_preferredPaymentMethod_code() + other.getApplicant_preferredPaymentMethod_code()); + change.setApplicant_preferredPaymentMethod_description(change.getApplicant_preferredPaymentMethod_description() + other.getApplicant_preferredPaymentMethod_description()); + change.setApplicant_noFixedAbode(change.getApplicant_noFixedAbode() + other.getApplicant_noFixedAbode()); + change.setApplicant_specialInvestigation(change.getApplicant_specialInvestigation() + other.getApplicant_specialInvestigation()); + change.setApplicant_homeAddress(change.getApplicant_homeAddress() + other.getApplicant_homeAddress()); + change.setApplicant_homeAddress_detail(change.getApplicant_homeAddress_detail() + other.getApplicant_homeAddress_detail()); + change.setApplicant_homeAddress_detail_line1(change.getApplicant_homeAddress_detail_line1() + other.getApplicant_homeAddress_detail_line1()); + change.setApplicant_homeAddress_detail_line2(change.getApplicant_homeAddress_detail_line2() + other.getApplicant_homeAddress_detail_line2()); + change.setApplicant_homeAddress_detail_line3(change.getApplicant_homeAddress_detail_line3() + other.getApplicant_homeAddress_detail_line3()); + change.setApplicant_homeAddress_detail_city(change.getApplicant_homeAddress_detail_city() + other.getApplicant_homeAddress_detail_city()); + change.setApplicant_homeAddress_detail_country(change.getApplicant_homeAddress_detail_country() + other.getApplicant_homeAddress_detail_country()); + change.setApplicant_homeAddress_detail_postcode(change.getApplicant_homeAddress_detail_postcode() + other.getApplicant_homeAddress_detail_postcode()); + change.setApplicant_postalAddress(change.getApplicant_postalAddress() + other.getApplicant_postalAddress()); + change.setApplicant_postalAddress_detail(change.getApplicant_postalAddress_detail() + other.getApplicant_postalAddress_detail()); + change.setApplicant_postalAddress_detail_line1(change.getApplicant_postalAddress_detail_line1() + other.getApplicant_postalAddress_detail_line1()); + change.setApplicant_postalAddress_detail_line2(change.getApplicant_postalAddress_detail_line2() + other.getApplicant_postalAddress_detail_line2()); + change.setApplicant_postalAddress_detail_line3(change.getApplicant_postalAddress_detail_line3() + other.getApplicant_postalAddress_detail_line3()); + change.setApplicant_postalAddress_detail_city(change.getApplicant_postalAddress_detail_city() + other.getApplicant_postalAddress_detail_city()); + change.setApplicant_postalAddress_detail_country(change.getApplicant_postalAddress_detail_country() + other.getApplicant_postalAddress_detail_country()); + change.setApplicant_postalAddress_detail_postcode(change.getApplicant_postalAddress_detail_postcode() + other.getApplicant_postalAddress_detail_postcode()); + change.setApplicant_employmentStatus(change.getApplicant_employmentStatus() + other.getApplicant_employmentStatus()); + change.setApplicant_employmentStatus_code(change.getApplicant_employmentStatus_code() + other.getApplicant_employmentStatus_code()); + change.setApplicant_employmentStatus_description(change.getApplicant_employmentStatus_description() + other.getApplicant_employmentStatus_description()); + change.setApplicant_bankDetails(change.getApplicant_bankDetails() + other.getApplicant_bankDetails()); + change.setApplicant_bankDetails_sortCode(change.getApplicant_bankDetails_sortCode() + other.getApplicant_bankDetails_sortCode()); + change.setApplicant_bankDetails_accountNo(change.getApplicant_bankDetails_accountNo() + other.getApplicant_bankDetails_accountNo()); + change.setApplicant_bankDetails_accountName(change.getApplicant_bankDetails_accountName() + other.getApplicant_bankDetails_accountName()); + change.setApplicant_partner(change.getApplicant_partner() + other.getApplicant_partner()); + change.setApplicant_partner_hasPartner(change.getApplicant_partner_hasPartner() + other.getApplicant_partner_hasPartner()); + change.setApplicant_partner_contraryInterest(change.getApplicant_partner_contraryInterest() + other.getApplicant_partner_contraryInterest()); + change.setApplicant_partner_ciDetails(change.getApplicant_partner_ciDetails() + other.getApplicant_partner_ciDetails()); + change.setApplicant_partner_ciDetails_code(change.getApplicant_partner_ciDetails_code() + other.getApplicant_partner_ciDetails_code()); + change.setApplicant_partner_ciDetails_description(change.getApplicant_partner_ciDetails_description() + other.getApplicant_partner_ciDetails_description()); + change.setApplicant_partnerDetails(change.getApplicant_partnerDetails() + other.getApplicant_partnerDetails()); + change.setApplicant_partnerDetails_firstName(change.getApplicant_partnerDetails_firstName() + other.getApplicant_partnerDetails_firstName()); + change.setApplicant_partnerDetails_lastName(change.getApplicant_partnerDetails_lastName() + other.getApplicant_partnerDetails_lastName()); + change.setApplicant_partnerDetails_dob(change.getApplicant_partnerDetails_dob() + other.getApplicant_partnerDetails_dob()); + change.setApplicant_partnerDetails_niNumber(change.getApplicant_partnerDetails_niNumber() + other.getApplicant_partnerDetails_niNumber()); + change.setApplicant_partnerDetails_landline(change.getApplicant_partnerDetails_landline() + other.getApplicant_partnerDetails_landline()); + change.setApplicant_partnerDetails_employmentStatus(change.getApplicant_partnerDetails_employmentStatus() + other.getApplicant_partnerDetails_employmentStatus()); + change.setApplicant_partnerDetails_employmentStatus_code(change.getApplicant_partnerDetails_employmentStatus_code() + other.getApplicant_partnerDetails_employmentStatus_code()); + change.setApplicant_partnerDetails_employmentStatus_description(change.getApplicant_partnerDetails_employmentStatus_description() + other.getApplicant_partnerDetails_employmentStatus_description()); + change.setApplicant_disabilitySummary(change.getApplicant_disabilitySummary() + other.getApplicant_disabilitySummary()); + change.setApplicant_disabilitySummary_declaration(change.getApplicant_disabilitySummary_declaration() + other.getApplicant_disabilitySummary_declaration()); + change.setApplicant_disabilitySummary_disabilities(change.getApplicant_disabilitySummary_disabilities() + other.getApplicant_disabilitySummary_disabilities()); + change.setApplicant_disabilitySummary_disabilities_disability(change.getApplicant_disabilitySummary_disabilities_disability() + other.getApplicant_disabilitySummary_disabilities_disability()); + change.setApplicant_id(change.getApplicant_id() + other.getApplicant_id()); + + change.setApplication(change.getApplication() + other.getApplication()); + change.setApplication_offenceType(change.getApplication_offenceType() + other.getApplication_offenceType()); + change.setApplication_offenceType_code(change.getApplication_offenceType_code() + other.getApplication_offenceType_code()); + change.setApplication_offenceType_description(change.getApplication_offenceType_description() + other.getApplication_offenceType_description()); + change.setApplication_caseType(change.getApplication_caseType() + other.getApplication_caseType()); + change.setApplication_caseType_code(change.getApplication_caseType_code() + other.getApplication_caseType_code()); + change.setApplication_caseType_description(change.getApplication_caseType_description() + other.getApplication_caseType_description()); + change.setApplication_repStatus(change.getApplication_repStatus() + other.getApplication_repStatus()); + change.setApplication_repStatus_status(change.getApplication_repStatus_status() + other.getApplication_repStatus_status()); + change.setApplication_repStatus_description(change.getApplication_repStatus_description() + other.getApplication_repStatus_description()); + change.setApplication_magsCourt(change.getApplication_magsCourt() + other.getApplication_magsCourt()); + change.setApplication_magsCourt_court(change.getApplication_magsCourt_court() + other.getApplication_magsCourt_court()); + change.setApplication_magsCourt_description(change.getApplication_magsCourt_description() + other.getApplication_magsCourt_description()); + change.setApplication_repStatusDate(change.getApplication_repStatusDate() + other.getApplication_repStatusDate()); + change.setApplication_arrestSummonsNumber(change.getApplication_arrestSummonsNumber() + other.getApplication_arrestSummonsNumber()); + change.setApplication_inCourtCustody(change.getApplication_inCourtCustody() + other.getApplication_inCourtCustody()); + change.setApplication_imprisoned(change.getApplication_imprisoned() + other.getApplication_imprisoned()); + change.setApplication_repOrderWithdrawalDate(change.getApplication_repOrderWithdrawalDate() + other.getApplication_repOrderWithdrawalDate()); + change.setApplication_committalDate(change.getApplication_committalDate() + other.getApplication_committalDate()); + change.setApplication_sentenceDate(change.getApplication_sentenceDate() + other.getApplication_sentenceDate()); + change.setApplication_appealType(change.getApplication_appealType() + other.getApplication_appealType()); + change.setApplication_appealType_code(change.getApplication_appealType_code() + other.getApplication_appealType_code()); + change.setApplication_appealType_description(change.getApplication_appealType_description() + other.getApplication_appealType_description()); + change.setApplication_ccHardship(change.getApplication_ccHardship() + other.getApplication_ccHardship()); + change.setApplication_ccHardship_reviewDate(change.getApplication_ccHardship_reviewDate() + other.getApplication_ccHardship_reviewDate()); + change.setApplication_ccHardship_reviewResult(change.getApplication_ccHardship_reviewResult() + other.getApplication_ccHardship_reviewResult()); + change.setApplication_solicitor(change.getApplication_solicitor() + other.getApplication_solicitor()); + change.setApplication_solicitor_accountCode(change.getApplication_solicitor_accountCode() + other.getApplication_solicitor_accountCode()); + change.setApplication_solicitor_name(change.getApplication_solicitor_name() + other.getApplication_solicitor_name()); + + change.setAssessment(change.getAssessment() + other.getAssessment()); + change.setAssessment_effectiveDate(change.getAssessment_effectiveDate() + other.getAssessment_effectiveDate()); + change.setAssessment_monthlyContribution(change.getAssessment_monthlyContribution() + other.getAssessment_monthlyContribution()); + change.setAssessment_upfrontContribution(change.getAssessment_upfrontContribution() + other.getAssessment_upfrontContribution()); + change.setAssessment_incomeContributionCap(change.getAssessment_incomeContributionCap() + other.getAssessment_incomeContributionCap()); + change.setAssessment_assessmentReason(change.getAssessment_assessmentReason() + other.getAssessment_assessmentReason()); + change.setAssessment_assessmentReason_code(change.getAssessment_assessmentReason_code() + other.getAssessment_assessmentReason_code()); + change.setAssessment_assessmentReason_description(change.getAssessment_assessmentReason_description() + other.getAssessment_assessmentReason_description()); + change.setAssessment_assessmentDate(change.getAssessment_assessmentDate() + other.getAssessment_assessmentDate()); + change.setAssessment_upliftAppliedDate(change.getAssessment_upliftAppliedDate() + other.getAssessment_upliftAppliedDate()); + change.setAssessment_upliftRemovedDate(change.getAssessment_upliftRemovedDate() + other.getAssessment_upliftRemovedDate()); + change.setAssessment_incomeEvidenceList(change.getAssessment_incomeEvidenceList() + other.getAssessment_incomeEvidenceList()); + change.setAssessment_incomeEvidenceList_incomeEvidence(change.getAssessment_incomeEvidenceList_incomeEvidence() + other.getAssessment_incomeEvidenceList_incomeEvidence()); + change.setAssessment_sufficientDeclaredEquity(change.getAssessment_sufficientDeclaredEquity() + other.getAssessment_sufficientDeclaredEquity()); + change.setAssessment_sufficientVerifiedEquity(change.getAssessment_sufficientVerifiedEquity() + other.getAssessment_sufficientVerifiedEquity()); + change.setAssessment_sufficientCapitalandEquity(change.getAssessment_sufficientCapitalandEquity() + other.getAssessment_sufficientCapitalandEquity()); + + change.setPassported(change.getPassported() + other.getPassported()); + change.setPassported_result(change.getPassported_result() + other.getPassported_result()); + change.setPassported_result_code(change.getPassported_result_code() + other.getPassported_result_code()); + change.setPassported_result_description(change.getPassported_result_description() + other.getPassported_result_description()); + change.setPassported_dateCompleted(change.getPassported_dateCompleted() + other.getPassported_dateCompleted()); + change.setPassported_reason(change.getPassported_reason() + other.getPassported_reason()); + change.setPassported_reason_code(change.getPassported_reason_code() + other.getPassported_reason_code()); + change.setPassported_reason_description(change.getPassported_reason_description() + other.getPassported_reason_description()); + + change.setEquity(change.getEquity() + other.getEquity()); + change.setEquity_undeclaredProperty(change.getEquity_undeclaredProperty() + other.getEquity_undeclaredProperty()); + change.setEquity_equityVerifiedBy(change.getEquity_equityVerifiedBy() + other.getEquity_equityVerifiedBy()); + change.setEquity_equityVerifiedDate(change.getEquity_equityVerifiedDate() + other.getEquity_equityVerifiedDate()); + change.setEquity_equityVerified(change.getEquity_equityVerified() + other.getEquity_equityVerified()); + change.setEquity_propertyDescriptor(change.getEquity_propertyDescriptor() + other.getEquity_propertyDescriptor()); + change.setEquity_propertyDescriptor_bedRoomCount(change.getEquity_propertyDescriptor_bedRoomCount() + other.getEquity_propertyDescriptor_bedRoomCount()); + change.setEquity_propertyDescriptor_residentialStatus(change.getEquity_propertyDescriptor_residentialStatus() + other.getEquity_propertyDescriptor_residentialStatus()); + change.setEquity_propertyDescriptor_residentialStatus_code(change.getEquity_propertyDescriptor_residentialStatus_code() + other.getEquity_propertyDescriptor_residentialStatus_code()); + change.setEquity_propertyDescriptor_residentialStatus_description(change.getEquity_propertyDescriptor_residentialStatus_description() + other.getEquity_propertyDescriptor_residentialStatus_description()); + change.setEquity_propertyDescriptor_propertyType(change.getEquity_propertyDescriptor_propertyType() + other.getEquity_propertyDescriptor_propertyType()); + change.setEquity_propertyDescriptor_propertyType_code(change.getEquity_propertyDescriptor_propertyType_code() + other.getEquity_propertyDescriptor_propertyType_code()); + change.setEquity_propertyDescriptor_propertyType_description(change.getEquity_propertyDescriptor_propertyType_description() + other.getEquity_propertyDescriptor_propertyType_description()); + change.setEquity_propertyDescriptor_address(change.getEquity_propertyDescriptor_address() + other.getEquity_propertyDescriptor_address()); + change.setEquity_propertyDescriptor_address_detail(change.getEquity_propertyDescriptor_address_detail() + other.getEquity_propertyDescriptor_address_detail()); + change.setEquity_propertyDescriptor_address_detail_line1(change.getEquity_propertyDescriptor_address_detail_line1() + other.getEquity_propertyDescriptor_address_detail_line1()); + change.setEquity_propertyDescriptor_address_detail_line2(change.getEquity_propertyDescriptor_address_detail_line2() + other.getEquity_propertyDescriptor_address_detail_line2()); + change.setEquity_propertyDescriptor_address_detail_line3(change.getEquity_propertyDescriptor_address_detail_line3() + other.getEquity_propertyDescriptor_address_detail_line3()); + change.setEquity_propertyDescriptor_address_detail_city(change.getEquity_propertyDescriptor_address_detail_city() + other.getEquity_propertyDescriptor_address_detail_city()); + change.setEquity_propertyDescriptor_address_detail_country(change.getEquity_propertyDescriptor_address_detail_country() + other.getEquity_propertyDescriptor_address_detail_country()); + change.setEquity_propertyDescriptor_address_detail_postcode(change.getEquity_propertyDescriptor_address_detail_postcode() + other.getEquity_propertyDescriptor_address_detail_postcode()); + change.setEquity_propertyDescriptor_percentageApplicantOwned(change.getEquity_propertyDescriptor_percentageApplicantOwned() + other.getEquity_propertyDescriptor_percentageApplicantOwned()); + change.setEquity_propertyDescriptor_percentagePartnerOwned(change.getEquity_propertyDescriptor_percentagePartnerOwned() + other.getEquity_propertyDescriptor_percentagePartnerOwned()); + change.setEquity_propertyDescriptor_applicantEquityAmount(change.getEquity_propertyDescriptor_applicantEquityAmount() + other.getEquity_propertyDescriptor_applicantEquityAmount()); + change.setEquity_propertyDescriptor_partnerEquityAmount(change.getEquity_propertyDescriptor_partnerEquityAmount() + other.getEquity_propertyDescriptor_partnerEquityAmount()); + change.setEquity_propertyDescriptor_declaredMortgage(change.getEquity_propertyDescriptor_declaredMortgage() + other.getEquity_propertyDescriptor_declaredMortgage()); + change.setEquity_propertyDescriptor_declaredValue(change.getEquity_propertyDescriptor_declaredValue() + other.getEquity_propertyDescriptor_declaredValue()); + change.setEquity_propertyDescriptor_verifiedMortgage(change.getEquity_propertyDescriptor_verifiedMortgage() + other.getEquity_propertyDescriptor_verifiedMortgage()); + change.setEquity_propertyDescriptor_verifiedValue(change.getEquity_propertyDescriptor_verifiedValue() + other.getEquity_propertyDescriptor_verifiedValue()); + change.setEquity_propertyDescriptor_tenantInPlace(change.getEquity_propertyDescriptor_tenantInPlace() + other.getEquity_propertyDescriptor_tenantInPlace()); + change.setEquity_propertyDescriptor_thirdPartyList(change.getEquity_propertyDescriptor_thirdPartyList() + other.getEquity_propertyDescriptor_thirdPartyList()); + change.setEquity_propertyDescriptor_thirdPartyList_thirdParty(change.getEquity_propertyDescriptor_thirdPartyList_thirdParty() + other.getEquity_propertyDescriptor_thirdPartyList_thirdParty()); + + change.setCapitalSummary(change.getCapitalSummary() + other.getCapitalSummary()); + change.setCapitalSummary_allEvidenceDate(change.getCapitalSummary_allEvidenceDate() + other.getCapitalSummary_allEvidenceDate()); + change.setCapitalSummary_totalCapitalAssets(change.getCapitalSummary_totalCapitalAssets() + other.getCapitalSummary_totalCapitalAssets()); + change.setCapitalSummary_noCapitalDeclared(change.getCapitalSummary_noCapitalDeclared() + other.getCapitalSummary_noCapitalDeclared()); + change.setCapitalSummary_capAllowanceWithheld(change.getCapitalSummary_capAllowanceWithheld() + other.getCapitalSummary_capAllowanceWithheld()); + change.setCapitalSummary_capAllowanceRestore(change.getCapitalSummary_capAllowanceRestore() + other.getCapitalSummary_capAllowanceRestore()); + change.setCapitalSummary_motorVehicleOwnership(change.getCapitalSummary_motorVehicleOwnership() + other.getCapitalSummary_motorVehicleOwnership()); + change.setCapitalSummary_motorVehicleOwnership_owner(change.getCapitalSummary_motorVehicleOwnership_owner() + other.getCapitalSummary_motorVehicleOwnership_owner()); + change.setCapitalSummary_motorVehicleOwnership_registrationList(change.getCapitalSummary_motorVehicleOwnership_registrationList() + other.getCapitalSummary_motorVehicleOwnership_registrationList()); + change.setCapitalSummary_motorVehicleOwnership_registrationList_registration(change.getCapitalSummary_motorVehicleOwnership_registrationList_registration() + other.getCapitalSummary_motorVehicleOwnership_registrationList_registration()); + change.setCapitalSummary_assetList(change.getCapitalSummary_assetList() + other.getCapitalSummary_assetList()); + change.setCapitalSummary_assetList_asset(change.getCapitalSummary_assetList_asset() + other.getCapitalSummary_assetList_asset()); + change.setCapitalSummary_propertyList(change.getCapitalSummary_propertyList() + other.getCapitalSummary_propertyList()); + change.setCapitalSummary_propertyList_property(change.getCapitalSummary_propertyList_property() + other.getCapitalSummary_propertyList_property()); + + change.setCcOutcomes(change.getCcOutcomes() + other.getCcOutcomes()); + change.setCcOutcomes_ccOutcome(change.getCcOutcomes_ccOutcome() + other.getCcOutcomes_ccOutcome()); + + change.setCorrespondence(change.getCorrespondence() + other.getCorrespondence()); + change.setCorrespondence_letter(change.getCorrespondence_letter() + other.getCorrespondence_letter()); + + change.setBreathingSpaceInfo(change.getBreathingSpaceInfo() + other.getBreathingSpaceInfo()); + change.setBreathingSpaceInfo_breathingSpace(change.getBreathingSpaceInfo_breathingSpace() + other.getBreathingSpaceInfo_breathingSpace()); + } + /** * Updates the change counts in `counts` based on the differences between `c1` and `c2` - * @param o1 CONTRIBUTIONS first instance - * @param o2 CONTRIBUTIONS second instance - * @param counts ConcorChangeCounts to update + * @param o1 CONTRIBUTIONS first instance. + * @param o2 CONTRIBUTIONS second instance. + * @param change Change counts to update. * @return true if there were any differences; false if there were none. */ - public boolean compare(CONTRIBUTIONS o1, CONTRIBUTIONS o2, ConcorChangeCounts counts) { + public boolean compare(CONTRIBUTIONS o1, CONTRIBUTIONS o2, Change change) { boolean diff = false; - if (compareApplicant(o1 != null ? o1.getApplicant() : null, o2 != null ? o2.getApplicant() : null, counts)) { + if (compareApplicant(o1 != null ? o1.getApplicant() : null, o2 != null ? o2.getApplicant() : null, change)) { diff = true; - counts.setApplicant(counts.getApplicant() + 1); + change.setApplicant(change.getApplicant() + 1); } - if (compareApplication(o1 != null ? o1.getApplication() : null, o2 != null ? o2.getApplication() : null, counts)) { + if (compareApplication(o1 != null ? o1.getApplication() : null, o2 != null ? o2.getApplication() : null, change)) { diff = true; - counts.setApplication(counts.getApplication() + 1); + change.setApplication(change.getApplication() + 1); } - if (compareAssessment(o1 != null ? o1.getAssessment() : null, o2 != null ? o2.getAssessment() : null, counts)) { + if (compareAssessment(o1 != null ? o1.getAssessment() : null, o2 != null ? o2.getAssessment() : null, change)) { diff = true; - counts.setAssessment(counts.getAssessment() + 1); + change.setAssessment(change.getAssessment() + 1); } - if (comparePassported(o1 != null ? o1.getPassported() : null, o2 != null ? o2.getPassported() : null, counts)) { + if (comparePassported(o1 != null ? o1.getPassported() : null, o2 != null ? o2.getPassported() : null, change)) { diff = true; - counts.setPassported(counts.getPassported() + 1); + change.setPassported(change.getPassported() + 1); } - if (compareEquity(o1 != null ? o1.getEquity() : null, o2 != null ? o2.getEquity() : null, counts)) { + if (compareEquity(o1 != null ? o1.getEquity() : null, o2 != null ? o2.getEquity() : null, change)) { diff = true; - counts.setEquity(counts.getEquity() + 1); + change.setEquity(change.getEquity() + 1); } - if (compareCapitalSummary(o1 != null ? o1.getCapitalSummary() : null, o2 != null ? o2.getCapitalSummary() : null, counts)) { + if (compareCapitalSummary(o1 != null ? o1.getCapitalSummary() : null, o2 != null ? o2.getCapitalSummary() : null, change)) { diff = true; - counts.setCapitalSummary(counts.getCapitalSummary() + 1); + change.setCapitalSummary(change.getCapitalSummary() + 1); } - if (compareCcOutcomes(o1 != null ? o1.getCcOutcomes() : null, o2 != null ? o2.getCcOutcomes() : null, counts)) { + if (compareCcOutcomes(o1 != null ? o1.getCcOutcomes() : null, o2 != null ? o2.getCcOutcomes() : null, change)) { diff = true; - counts.setCcOutcomes(counts.getCcOutcomes() + 1); + change.setCcOutcomes(change.getCcOutcomes() + 1); } - if (compareCorrespondence(o1 != null ? o1.getCorrespondence() : null, o2 != null ? o2.getCorrespondence() : null, counts)) { + if (compareCorrespondence(o1 != null ? o1.getCorrespondence() : null, o2 != null ? o2.getCorrespondence() : null, change)) { diff = true; - counts.setCorrespondence(counts.getCorrespondence() + 1); + change.setCorrespondence(change.getCorrespondence() + 1); } - if (compareBreathingSpaceInfo(o1 != null ? o1.getBreathingSpaceInfo() : null, o2 != null ? o2.getBreathingSpaceInfo() : null, counts)) { + if (compareBreathingSpaceInfo(o1 != null ? o1.getBreathingSpaceInfo() : null, o2 != null ? o2.getBreathingSpaceInfo() : null, change)) { diff = true; - counts.setBreathingSpaceInfo(counts.getBreathingSpaceInfo() + 1); + change.setBreathingSpaceInfo(change.getBreathingSpaceInfo() + 1); } if (!Objects.equals(o1 != null ? o1.getId() : null, o2 != null ? o2.getId() : null)) { diff = true; @@ -67,7 +249,7 @@ public boolean compare(CONTRIBUTIONS o1, CONTRIBUTIONS o2, ConcorChangeCounts co return diff; } - private boolean compareApplicant(CONTRIBUTIONS.Applicant o1, CONTRIBUTIONS.Applicant o2, ConcorChangeCounts counts) { + private boolean compareApplicant(CONTRIBUTIONS.Applicant o1, CONTRIBUTIONS.Applicant o2, Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getFirstName() : null, o2 != null ? o2.getFirstName() : null)) { diff = true; @@ -150,7 +332,7 @@ private boolean compareApplicant(CONTRIBUTIONS.Applicant o1, CONTRIBUTIONS.Appli private boolean compareApplicant_PreferredPaymentMethod(CONTRIBUTIONS.Applicant.PreferredPaymentMethod o1, CONTRIBUTIONS.Applicant.PreferredPaymentMethod o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -165,7 +347,7 @@ private boolean compareApplicant_PreferredPaymentMethod(CONTRIBUTIONS.Applicant. private boolean compareApplicant_HomeAddress(CONTRIBUTIONS.Applicant.HomeAddress o1, CONTRIBUTIONS.Applicant.HomeAddress o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareApplicant_HomeAddress_Detail(o1 != null ? o1.getDetail() : null, o2 != null ? o2.getDetail() : null, counts)) { diff = true; @@ -176,7 +358,7 @@ private boolean compareApplicant_HomeAddress(CONTRIBUTIONS.Applicant.HomeAddress private boolean compareApplicant_HomeAddress_Detail(CONTRIBUTIONS.Applicant.HomeAddress.Detail o1, CONTRIBUTIONS.Applicant.HomeAddress.Detail o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getLine1() : null, o2 != null ? o2.getLine1() : null)) { diff = true; @@ -207,7 +389,7 @@ private boolean compareApplicant_HomeAddress_Detail(CONTRIBUTIONS.Applicant.Home private boolean compareApplicant_PostalAddress(CONTRIBUTIONS.Applicant.PostalAddress o1, CONTRIBUTIONS.Applicant.PostalAddress o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareApplicant_PostalAddress_Detail(o1 != null ? o1.getDetail() : null, o2 != null ? o2.getDetail() : null, counts)) { diff = true; @@ -219,7 +401,7 @@ private boolean compareApplicant_PostalAddress(CONTRIBUTIONS.Applicant.PostalAdd // Can we make the different address types common? private boolean compareApplicant_PostalAddress_Detail(CONTRIBUTIONS.Applicant.PostalAddress.Detail o1, CONTRIBUTIONS.Applicant.PostalAddress.Detail o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getLine1() : null, o2 != null ? o2.getLine1() : null)) { diff = true; @@ -250,7 +432,7 @@ private boolean compareApplicant_PostalAddress_Detail(CONTRIBUTIONS.Applicant.Po private boolean compareApplicant_EmploymentStatus(CONTRIBUTIONS.Applicant.EmploymentStatus o1, CONTRIBUTIONS.Applicant.EmploymentStatus o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -265,7 +447,7 @@ private boolean compareApplicant_EmploymentStatus(CONTRIBUTIONS.Applicant.Employ private boolean compareApplicant_BankDetails(CONTRIBUTIONS.Applicant.BankDetails o1, CONTRIBUTIONS.Applicant.BankDetails o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getSortCode() : null, o2 != null ? o2.getSortCode() : null)) { diff = true; @@ -284,7 +466,7 @@ private boolean compareApplicant_BankDetails(CONTRIBUTIONS.Applicant.BankDetails private boolean compareApplicant_Partner(CONTRIBUTIONS.Applicant.Partner o1, CONTRIBUTIONS.Applicant.Partner o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getHasPartner() : null, o2 != null ? o2.getHasPartner() : null)) { diff = true; @@ -303,7 +485,7 @@ private boolean compareApplicant_Partner(CONTRIBUTIONS.Applicant.Partner o1, private boolean compareApplicant_Partner_CiDetails(CONTRIBUTIONS.Applicant.Partner.CiDetails o1, CONTRIBUTIONS.Applicant.Partner.CiDetails o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -318,7 +500,7 @@ private boolean compareApplicant_Partner_CiDetails(CONTRIBUTIONS.Applicant.Partn private boolean compareApplicant_PartnerDetails(CONTRIBUTIONS.Applicant.PartnerDetails o1, CONTRIBUTIONS.Applicant.PartnerDetails o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getFirstName() : null, o2 != null ? o2.getFirstName() : null)) { diff = true; @@ -345,7 +527,7 @@ private boolean compareApplicant_PartnerDetails(CONTRIBUTIONS.Applicant.PartnerD private boolean compareApplicant_PartnerDetails_EmploymentStatus(CONTRIBUTIONS.Applicant.PartnerDetails.EmploymentStatus o1, CONTRIBUTIONS.Applicant.PartnerDetails.EmploymentStatus o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -360,7 +542,7 @@ private boolean compareApplicant_PartnerDetails_EmploymentStatus(CONTRIBUTIONS.A private boolean compareApplicant_DisabilitySummary(CONTRIBUTIONS.Applicant.DisabilitySummary o1, CONTRIBUTIONS.Applicant.DisabilitySummary o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getDeclaration() : null, o2 != null ? o2.getDeclaration() : null)) { diff = true; @@ -375,7 +557,7 @@ private boolean compareApplicant_DisabilitySummary(CONTRIBUTIONS.Applicant.Disab private boolean compareApplicant_DisabilitySummary_Disabilities(CONTRIBUTIONS.Applicant.DisabilitySummary.Disabilities o1, CONTRIBUTIONS.Applicant.DisabilitySummary.Disabilities o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareApplicant_DisabilitySummary_Disabilities_Disability(o1 != null ? o1.getDisability() : null, o2 != null ? o2.getDisability() : null, counts)) { diff = true; @@ -386,11 +568,11 @@ private boolean compareApplicant_DisabilitySummary_Disabilities(CONTRIBUTIONS.Ap private boolean compareApplicant_DisabilitySummary_Disabilities_Disability(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } - private boolean compareApplication(CONTRIBUTIONS.Application o1, CONTRIBUTIONS.Application o2, ConcorChangeCounts counts) { + private boolean compareApplication(CONTRIBUTIONS.Application o1, CONTRIBUTIONS.Application o2, Change counts) { boolean diff = false; if (compareApplication_OffenceType(o1 != null ? o1.getOffenceType() : null, o2 != null ? o2.getOffenceType() : null, counts)) { diff = true; @@ -453,7 +635,7 @@ private boolean compareApplication(CONTRIBUTIONS.Application o1, CONTRIBUTIONS.A private boolean compareApplication_OffenceType(CONTRIBUTIONS.Application.OffenceType o1, CONTRIBUTIONS.Application.OffenceType o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -468,7 +650,7 @@ private boolean compareApplication_OffenceType(CONTRIBUTIONS.Application.Offence private boolean compareApplication_CaseType(CONTRIBUTIONS.Application.CaseType o1, CONTRIBUTIONS.Application.CaseType o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -483,7 +665,7 @@ private boolean compareApplication_CaseType(CONTRIBUTIONS.Application.CaseType o private boolean compareApplication_RepStatus(CONTRIBUTIONS.Application.RepStatus o1, CONTRIBUTIONS.Application.RepStatus o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getStatus() : null, o2 != null ? o2.getStatus() : null)) { diff = true; @@ -498,7 +680,7 @@ private boolean compareApplication_RepStatus(CONTRIBUTIONS.Application.RepStatus private boolean compareApplication_MagsCourt(CONTRIBUTIONS.Application.MagsCourt o1, CONTRIBUTIONS.Application.MagsCourt o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCourt() : null, o2 != null ? o2.getCourt() : null)) { diff = true; @@ -513,7 +695,7 @@ private boolean compareApplication_MagsCourt(CONTRIBUTIONS.Application.MagsCourt private boolean compareApplication_AppealType(CONTRIBUTIONS.Application.AppealType o1, CONTRIBUTIONS.Application.AppealType o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -528,7 +710,7 @@ private boolean compareApplication_AppealType(CONTRIBUTIONS.Application.AppealTy private boolean compareApplication_CcHardship(CONTRIBUTIONS.Application.CcHardship o1, CONTRIBUTIONS.Application.CcHardship o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getReviewDate() : null, o2 != null ? o2.getReviewDate() : null)) { diff = true; @@ -543,7 +725,7 @@ private boolean compareApplication_CcHardship(CONTRIBUTIONS.Application.CcHardsh private boolean compareApplication_Solicitor(CONTRIBUTIONS.Application.Solicitor o1, CONTRIBUTIONS.Application.Solicitor o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getAccountCode() : null, o2 != null ? o2.getAccountCode() : null)) { diff = true; @@ -556,7 +738,7 @@ private boolean compareApplication_Solicitor(CONTRIBUTIONS.Application.Solicitor return diff; } - private boolean compareAssessment(CONTRIBUTIONS.Assessment o1, CONTRIBUTIONS.Assessment o2, ConcorChangeCounts counts) { + private boolean compareAssessment(CONTRIBUTIONS.Assessment o1, CONTRIBUTIONS.Assessment o2, Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getEffectiveDate() : null, o2 != null ? o2.getEffectiveDate() : null)) { diff = true; @@ -611,7 +793,7 @@ private boolean compareAssessment(CONTRIBUTIONS.Assessment o1, CONTRIBUTIONS.Ass private boolean compareAssessment_AssessmentReason(CONTRIBUTIONS.Assessment.AssessmentReason o1, CONTRIBUTIONS.Assessment.AssessmentReason o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -626,7 +808,7 @@ private boolean compareAssessment_AssessmentReason(CONTRIBUTIONS.Assessment.Asse private boolean compareAssessment_IncomeEvidenceList(CONTRIBUTIONS.Assessment.IncomeEvidenceList o1, CONTRIBUTIONS.Assessment.IncomeEvidenceList o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareAssessment_IncomeEvidenceList_IncomeEvidence(o1 != null ? o1.getIncomeEvidence() : null, o2 != null ? o2.getIncomeEvidence() : null, counts)) { diff = true; @@ -637,11 +819,11 @@ private boolean compareAssessment_IncomeEvidenceList(CONTRIBUTIONS.Assessment.In private boolean compareAssessment_IncomeEvidenceList_IncomeEvidence(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } - private boolean comparePassported(CONTRIBUTIONS.Passported o1, CONTRIBUTIONS.Passported o2, ConcorChangeCounts counts) { + private boolean comparePassported(CONTRIBUTIONS.Passported o1, CONTRIBUTIONS.Passported o2, Change counts) { boolean diff = false; if (comparePassported_Result(o1 != null ? o1.getResult() : null, o2 != null ? o2.getResult() : null, counts)) { diff = true; @@ -660,7 +842,7 @@ private boolean comparePassported(CONTRIBUTIONS.Passported o1, CONTRIBUTIONS.Pas private boolean comparePassported_Result(CONTRIBUTIONS.Passported.Result o1, CONTRIBUTIONS.Passported.Result o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -675,7 +857,7 @@ private boolean comparePassported_Result(CONTRIBUTIONS.Passported.Result o1, private boolean comparePassported_Reason(CONTRIBUTIONS.Passported.Reason o1, CONTRIBUTIONS.Passported.Reason o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -688,7 +870,7 @@ private boolean comparePassported_Reason(CONTRIBUTIONS.Passported.Reason o1, return diff; } - private boolean compareEquity(CONTRIBUTIONS.Equity o1, CONTRIBUTIONS.Equity o2, ConcorChangeCounts counts) { + private boolean compareEquity(CONTRIBUTIONS.Equity o1, CONTRIBUTIONS.Equity o2, Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getUndeclaredProperty() : null, o2 != null ? o2.getUndeclaredProperty() : null)) { diff = true; @@ -715,7 +897,7 @@ private boolean compareEquity(CONTRIBUTIONS.Equity o1, CONTRIBUTIONS.Equity o2, private boolean compareEquity_PropertyDescriptor(CONTRIBUTIONS.Equity.PropertyDescriptor o1, CONTRIBUTIONS.Equity.PropertyDescriptor o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getBedRoomCount() : null, o2 != null ? o2.getBedRoomCount() : null)) { diff = true; @@ -778,7 +960,7 @@ private boolean compareEquity_PropertyDescriptor(CONTRIBUTIONS.Equity.PropertyDe private boolean compareEquity_PropertyDescriptor_ResidentialStatus(CONTRIBUTIONS.Equity.PropertyDescriptor.ResidentialStatus o1, CONTRIBUTIONS.Equity.PropertyDescriptor.ResidentialStatus o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -793,7 +975,7 @@ private boolean compareEquity_PropertyDescriptor_ResidentialStatus(CONTRIBUTIONS private boolean compareEquity_PropertyDescriptor_PropertyType(CONTRIBUTIONS.Equity.PropertyDescriptor.PropertyType o1, CONTRIBUTIONS.Equity.PropertyDescriptor.PropertyType o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getCode() : null, o2 != null ? o2.getCode() : null)) { diff = true; @@ -808,7 +990,7 @@ private boolean compareEquity_PropertyDescriptor_PropertyType(CONTRIBUTIONS.Equi private boolean compareEquity_PropertyDescriptor_Address(CONTRIBUTIONS.Equity.PropertyDescriptor.Address o1, CONTRIBUTIONS.Equity.PropertyDescriptor.Address o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareEquity_PropertyDescriptor_Address_Detail(o1 != null ? o1.getDetail() : null, o2 != null ? o2.getDetail() : null, counts)) { diff = true; @@ -819,7 +1001,7 @@ private boolean compareEquity_PropertyDescriptor_Address(CONTRIBUTIONS.Equity.Pr private boolean compareEquity_PropertyDescriptor_Address_Detail(CONTRIBUTIONS.Equity.PropertyDescriptor.Address.Detail o1, CONTRIBUTIONS.Equity.PropertyDescriptor.Address.Detail o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getLine1() : null, o2 != null ? o2.getLine1() : null)) { diff = true; @@ -850,7 +1032,7 @@ private boolean compareEquity_PropertyDescriptor_Address_Detail(CONTRIBUTIONS.Eq private boolean compareEquity_PropertyDescriptor_ThirdPartyList(CONTRIBUTIONS.Equity.PropertyDescriptor.ThirdPartyList o1, CONTRIBUTIONS.Equity.PropertyDescriptor.ThirdPartyList o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareEquity_PropertyDescriptor_ThirdPartyList_ThirdParty(o1 != null ? o1.getThirdParty() : null, o2 != null ? o2.getThirdParty() : null, counts)) { diff = true; @@ -861,11 +1043,11 @@ private boolean compareEquity_PropertyDescriptor_ThirdPartyList(CONTRIBUTIONS.Eq private boolean compareEquity_PropertyDescriptor_ThirdPartyList_ThirdParty(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } - private boolean compareCapitalSummary(CONTRIBUTIONS.CapitalSummary o1, CONTRIBUTIONS.CapitalSummary o2, ConcorChangeCounts counts) { + private boolean compareCapitalSummary(CONTRIBUTIONS.CapitalSummary o1, CONTRIBUTIONS.CapitalSummary o2, Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getAllEvidenceDate() : null, o2 != null ? o2.getAllEvidenceDate() : null)) { diff = true; @@ -904,7 +1086,7 @@ private boolean compareCapitalSummary(CONTRIBUTIONS.CapitalSummary o1, CONTRIBUT private boolean compareCapitalSummary_MotorVehicleOwnership(CONTRIBUTIONS.CapitalSummary.MotorVehicleOwnership o1, CONTRIBUTIONS.CapitalSummary.MotorVehicleOwnership o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (!Objects.equals(o1 != null ? o1.getOwner() : null, o2 != null ? o2.getOwner() : null)) { diff = true; @@ -919,7 +1101,7 @@ private boolean compareCapitalSummary_MotorVehicleOwnership(CONTRIBUTIONS.Capita private boolean compareCapitalSummary_MotorVehicleOwnership_RegistrationList(CONTRIBUTIONS.CapitalSummary.MotorVehicleOwnership.RegistrationList o1, CONTRIBUTIONS.CapitalSummary.MotorVehicleOwnership.RegistrationList o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareCapitalSummary_MotorVehicleOwnership_RegistrationList_Registration(o1 != null ? o1.getRegistration() : null, o2 != null ? o2.getRegistration() : null, counts)) { diff = true; @@ -930,13 +1112,13 @@ private boolean compareCapitalSummary_MotorVehicleOwnership_RegistrationList(CON private boolean compareCapitalSummary_MotorVehicleOwnership_RegistrationList_Registration(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } private boolean compareCapitalSummary_AssetList(CONTRIBUTIONS.CapitalSummary.AssetList o1, CONTRIBUTIONS.CapitalSummary.AssetList o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareCapitalSummary_AssetList_Asset(o1 != null ? o1.getAsset() : null, o2 != null ? o2.getAsset() : null, counts)) { diff = true; @@ -947,13 +1129,13 @@ private boolean compareCapitalSummary_AssetList(CONTRIBUTIONS.CapitalSummary.Ass private boolean compareCapitalSummary_AssetList_Asset(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } private boolean compareCapitalSummary_PropertyList(CONTRIBUTIONS.CapitalSummary.PropertyList o1, CONTRIBUTIONS.CapitalSummary.PropertyList o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareCapitalSummary_PropertyList_Property(o1 != null ? o1.getProperty() : null, o2 != null ? o2.getProperty() : null, counts)) { diff = true; @@ -964,11 +1146,11 @@ private boolean compareCapitalSummary_PropertyList(CONTRIBUTIONS.CapitalSummary. private boolean compareCapitalSummary_PropertyList_Property(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } - private boolean compareCcOutcomes(CONTRIBUTIONS.CcOutcomes o1, CONTRIBUTIONS.CcOutcomes o2, ConcorChangeCounts counts) { + private boolean compareCcOutcomes(CONTRIBUTIONS.CcOutcomes o1, CONTRIBUTIONS.CcOutcomes o2, Change counts) { boolean diff = false; if (compareCcOutcomes_CcOutcome(o1 != null ? o1.getCcOutcome() : null, o2 != null ? o2.getCcOutcome() : null, counts)) { diff = true; @@ -979,11 +1161,11 @@ private boolean compareCcOutcomes(CONTRIBUTIONS.CcOutcomes o1, CONTRIBUTIONS.CcO private boolean compareCcOutcomes_CcOutcome(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } - private boolean compareCorrespondence(CONTRIBUTIONS.Correspondence o1, CONTRIBUTIONS.Correspondence o2, ConcorChangeCounts counts) { + private boolean compareCorrespondence(CONTRIBUTIONS.Correspondence o1, CONTRIBUTIONS.Correspondence o2, Change counts) { boolean diff = false; if (compareCorrespondence_Letter(o1 != null ? o1.getLetter() : null, o2 != null ? o2.getLetter() : null, counts)) { diff = true; @@ -994,13 +1176,13 @@ private boolean compareCorrespondence(CONTRIBUTIONS.Correspondence o1, CONTRIBUT private boolean compareCorrespondence_Letter(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } private boolean compareBreathingSpaceInfo(CONTRIBUTIONS.BreathingSpaceInfo o1, CONTRIBUTIONS.BreathingSpaceInfo o2, - ConcorChangeCounts counts) { + Change counts) { boolean diff = false; if (compareBreathingSpaceInfo_BreathingSpace(o1 != null ? o1.getBreathingSpace() : null, o2 != null ? o2.getBreathingSpace() : null, counts)) { diff = true; @@ -1011,7 +1193,7 @@ private boolean compareBreathingSpaceInfo(CONTRIBUTIONS.BreathingSpaceInfo o1, private boolean compareBreathingSpaceInfo_BreathingSpace(List o1, List o2, - ConcorChangeCounts counts) { + Change counts) { return !Objects.equals(o1 != null ? o1.size() : 0, o2 != null ? o2.size() : 0); } } diff --git a/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/service/CsvOutputService.java b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/service/CsvOutputService.java new file mode 100644 index 0000000..4ac9e26 --- /dev/null +++ b/deltaconcor/src/main/java/uk/gov/justice/laadces/deltaconcor/service/CsvOutputService.java @@ -0,0 +1,50 @@ +package uk.gov.justice.laadces.deltaconcor.service; + +import com.opencsv.bean.HeaderColumnNameMappingStrategy; +import com.opencsv.bean.StatefulBeanToCsvBuilder; +import com.opencsv.exceptions.CsvDataTypeMismatchException; +import com.opencsv.exceptions.CsvRequiredFieldEmptyException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import uk.gov.justice.laadces.deltaconcor.report.Change; +import uk.gov.justice.laadces.deltaconcor.report.ChangeFieldComparator; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import static com.opencsv.ICSVWriter.NO_QUOTE_CHARACTER; + +@RequiredArgsConstructor +@Service +@Slf4j +public class CsvOutputService { + public void writeChanges(final String filename, final List countsList) { + final FileWriter writer; + try { + writer = new FileWriter(filename); + } catch (IOException e) { + log.error("Failed to open file for writing: {}", filename, e); + throw new IllegalArgumentException(e); + } + final var strategy = new HeaderColumnNameMappingStrategy(); + strategy.setType(Change.class); + strategy.setColumnOrderOnWrite(ChangeFieldComparator.INSTANCE); + final var beanToCsv = new StatefulBeanToCsvBuilder(writer) + .withMappingStrategy(strategy) + .withQuotechar(NO_QUOTE_CHARACTER) + .build(); + try { + beanToCsv.write(countsList); + } catch (CsvDataTypeMismatchException|CsvRequiredFieldEmptyException e) { + log.error("Failed to write CSV data", e); + throw new IllegalArgumentException(e); + } + try { + writer.close(); + } catch (IOException e) { + log.warn("Failed to close file after writing: {}", filename, e); + } + } +} diff --git a/deltaconcor/src/test/java/uk/gov/justice/laadces/deltaconcor/report/ConcorTest.java b/deltaconcor/src/test/java/uk/gov/justice/laadces/deltaconcor/report/ConcorTest.java index 122df0d..97a87e9 100644 --- a/deltaconcor/src/test/java/uk/gov/justice/laadces/deltaconcor/report/ConcorTest.java +++ b/deltaconcor/src/test/java/uk/gov/justice/laadces/deltaconcor/report/ConcorTest.java @@ -1,24 +1,23 @@ package uk.gov.justice.laadces.deltaconcor.report; -import com.opencsv.bean.HeaderColumnNameMappingStrategy; -import com.opencsv.bean.StatefulBeanToCsvBuilder; import com.opencsv.exceptions.CsvDataTypeMismatchException; import com.opencsv.exceptions.CsvRequiredFieldEmptyException; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import uk.gov.justice.laadces.deltaconcor.generated.CONTRIBUTIONS; +import uk.gov.justice.laadces.deltaconcor.service.ChangeService; +import uk.gov.justice.laadces.deltaconcor.service.CsvOutputService; -import java.io.FileWriter; import java.io.IOException; import java.util.List; -import static com.opencsv.ICSVWriter.NO_QUOTE_CHARACTER; - @SpringBootTest class ConcorTest { @Autowired - private ConcorComparison comparison; + private ChangeService changeService; + @Autowired + private CsvOutputService csvoutput; @Test void givenTwoContributions_whenCompared_thenWrittenAsCsv() throws IOException, CsvRequiredFieldEmptyException, CsvDataTypeMismatchException { @@ -35,21 +34,12 @@ void givenTwoContributions_whenCompared_thenWrittenAsCsv() throws IOException, C final var c2 = new CONTRIBUTIONS(); c2.setApplicant(a2); // when - ConcorChangeCounts counts = new ConcorChangeCounts("2024-12-31", 1); - if (comparison.compare(c1, c2, counts)) { + Change counts = new Change("2024-12-31"); + if (changeService.compare(c1, c2, counts)) { counts.setChangedRecords(counts.getChangedRecords() + 1); } final var countsList = List.of(counts); // then - final var writer = new FileWriter("/tmp/deltaconcor.csv"); - final var strategy = new HeaderColumnNameMappingStrategy(); - strategy.setType(ConcorChangeCounts.class); - strategy.setColumnOrderOnWrite(ConcorChangeCountsFieldComparator.INSTANCE); - final var beanToCsv = new StatefulBeanToCsvBuilder(writer) - .withMappingStrategy(strategy) - .withQuotechar(NO_QUOTE_CHARACTER) - .build(); - beanToCsv.write(countsList); - writer.close(); + csvoutput.writeChanges("/tmp/deltaconcor.csv", countsList); } }