package gov.nysenate.openleg.model.spotcheck;
import com.google.common.collect.LinkedListMultimap;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A SpotCheckObservation is the result of performing a SpotCheck against some reference data. It contains
* any mismatches that were detected between the reference content and the observed content.
*
* @param <ContentKey> Class that is used as a key for identifying the specific piece of
* content that is being compared during the spot check.
*/
public class SpotCheckObservation<ContentKey>
{
/** The source used to compare our data against. */
protected SpotCheckReferenceId referenceId;
/** A key that identifies the content being checked. */
protected ContentKey key;
/** The datetime this observation was made. */
protected LocalDateTime observedDateTime;
/** The date time when the report that generated this observation was run */
protected LocalDateTime reportDateTime;
/** Mapping of mismatches that exist between the reference content and our content. */
protected Map<SpotCheckMismatchType, SpotCheckMismatch> mismatches = new HashMap<>();
/** Mapping of prior mismatches keyed by the mismatch type. This is only populated if the observation
* is made within the content of previously saved reports and the mismatch is one that has appeared before. */
protected LinkedListMultimap<SpotCheckMismatchType, SpotCheckPriorMismatch> priorMismatches = LinkedListMultimap.create();
/** --- Constructors --- */
public SpotCheckObservation() {}
public SpotCheckObservation(SpotCheckReferenceId referenceId, ContentKey key) {
this.referenceId = referenceId;
this.key = key;
this.observedDateTime = LocalDateTime.now();
}
/** --- Methods --- */
public boolean hasMismatches() {
return !mismatches.isEmpty();
}
public boolean hasMismatch(SpotCheckMismatchType type) {
return mismatches.containsKey(type);
}
public void addMismatch(SpotCheckMismatch mismatch) {
if (mismatch != null) {
mismatches.put(mismatch.getMismatchType(), mismatch);
}
}
public void addPriorMismatch(SpotCheckPriorMismatch priorMismatch) {
if (priorMismatch != null) {
priorMismatches.put(priorMismatch.getMismatchType(), priorMismatch);
}
}
/**
* Returns the number of mismatches grouped by mismatch status. So for example:
* {NEW=4, EXISTING=2} would be returned if there were four new mismatches and two
* existing mismatches.
*
* @param ignored boolean - if true, will return counts for ignored mismatches, which are left out if false
* @return Map<SpotCheckMismatchStatus, Long>
*/
public Map<SpotCheckMismatchStatus, Long> getMismatchStatusCounts(boolean ignored) {
if (mismatches != null) {
return mismatches.values().stream()
.filter(mismatch -> !mismatch.isIgnored() ^ ignored)
.collect(Collectors.groupingBy(SpotCheckMismatch::getStatus, Collectors.counting()));
}
else {
throw new IllegalStateException("Collection of mismatches is null");
}
}
/**
* Returns a mapping of mismatch type to status.
*
* @return Map<SpotCheckMismatchType, SpotCheckMismatchStatus>
*/
public Map<SpotCheckMismatchType, SpotCheckMismatchStatus> getMismatchStatusTypes(boolean ignored) {
if (mismatches != null) {
return mismatches.values().stream()
.filter(mismatch -> !mismatch.isIgnored() ^ ignored)
.collect(Collectors.toMap(SpotCheckMismatch::getMismatchType, SpotCheckMismatch::getStatus));
}
else {
throw new IllegalStateException("Collection of mismatches is null");
}
}
/**
* Returns the set of mismatch types that there are mismatches for.
*
* @return Set<SpotCheckMismatchType>
*/
public Set<SpotCheckMismatchType> getMismatchTypes(boolean ignored) {
if (mismatches != null) {
return mismatches.values().stream()
.filter(mismatch -> !mismatch.isIgnored() ^ ignored)
.map(SpotCheckMismatch::getMismatchType)
.collect(Collectors.toSet());
}
else {
throw new IllegalStateException("Collection of mismatches is null");
}
}
/** --- Basic Getters/Setters --- */
public LocalDateTime getReportDateTime() {
return reportDateTime;
}
public void setReportDateTime(LocalDateTime reportDateTime) {
this.reportDateTime = reportDateTime;
}
public SpotCheckReferenceId getReferenceId() {
return referenceId;
}
public void setReferenceId(SpotCheckReferenceId referenceId) {
this.referenceId = referenceId;
}
public ContentKey getKey() {
return key;
}
public void setKey(ContentKey key) {
this.key = key;
}
public LocalDateTime getObservedDateTime() {
return observedDateTime;
}
public void setObservedDateTime(LocalDateTime observedDateTime) {
this.observedDateTime = observedDateTime;
}
public Map<SpotCheckMismatchType, SpotCheckMismatch> getMismatches() {
return mismatches;
}
public void setMismatches(Map<SpotCheckMismatchType, SpotCheckMismatch> mismatches) {
this.mismatches = mismatches;
}
public LinkedListMultimap<SpotCheckMismatchType, SpotCheckPriorMismatch> getPriorMismatches() {
return priorMismatches;
}
}