package gov.nysenate.openleg.service.spotcheck.senatesite.bill;
import com.google.common.collect.ImmutableList;
import gov.nysenate.openleg.client.view.base.ListView;
import gov.nysenate.openleg.client.view.base.MapView;
import gov.nysenate.openleg.client.view.bill.*;
import gov.nysenate.openleg.client.view.entity.MemberView;
import gov.nysenate.openleg.model.base.Version;
import gov.nysenate.openleg.model.bill.*;
import gov.nysenate.openleg.model.entity.Chamber;
import gov.nysenate.openleg.model.spotcheck.ReferenceDataNotFoundEx;
import gov.nysenate.openleg.model.spotcheck.SpotCheckMismatch;
import gov.nysenate.openleg.model.spotcheck.SpotCheckObservation;
import gov.nysenate.openleg.model.spotcheck.senatesite.bill.SenateSiteBill;
import gov.nysenate.openleg.service.bill.data.BillAmendNotFoundEx;
import gov.nysenate.openleg.service.spotcheck.base.BaseSpotCheckService;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors;
import static gov.nysenate.openleg.model.spotcheck.SpotCheckMismatchType.*;
@Service
public class BillCheckService extends BaseSpotCheckService<BillId, Bill, SenateSiteBill> {
/** {@inheritDoc} */
@Override
public SpotCheckObservation<BillId> check(Bill content) throws ReferenceDataNotFoundEx {
throw new NotImplementedException(":P");
}
/** {@inheritDoc} */
@Override
public SpotCheckObservation<BillId> check(Bill content, LocalDateTime start, LocalDateTime end) throws ReferenceDataNotFoundEx {
throw new NotImplementedException(":P");
}
/** {@inheritDoc} */
@Override
public SpotCheckObservation<BillId> check(Bill content, SenateSiteBill reference) {
SpotCheckObservation<BillId> observation = new SpotCheckObservation<>(reference.getReferenceId(), reference.getBillId());
BillView contentBillView = new BillView(content);
BillAmendmentView amendment;
try {
Version refVersion = reference.getBillId().getVersion();
amendment = Optional.of(contentBillView)
.map(BillView::getAmendments).map(MapView::getItems)
.map(amendments -> amendments.get(refVersion.toString()))
.orElseThrow(() -> new BillAmendNotFoundEx(reference.getBillId()));
} catch (IllegalArgumentException | BillAmendNotFoundEx ex) {
observation.addMismatch(new SpotCheckMismatch(OBSERVE_DATA_MISSING, null, reference.getBillId()));
return observation;
}
checkBasePrintNo(contentBillView, reference, observation);
checkChamber(contentBillView, reference, observation);
checkActiveVersion(contentBillView, reference, observation);
checkIsAmended(contentBillView, reference, observation);
checkPublishDate(contentBillView, reference, observation);
checkActions(contentBillView, reference, observation);
// Don't check milestones, last status, last status date, or last status committee for resolutions.
if (!reference.getBaseBillId().getBillType().isResolution()) {
checkMilestones(contentBillView, reference, observation);
checkLastStatus(contentBillView, reference, observation);
// Public website bill models last status date and last status committee are always incorrect when status = STRICKEN.
// So only check for those errors if there is a last status mismatch or the status != STRICKEN.
if (observation.hasMismatch(BILL_LAST_STATUS) || !StringUtils.equals(reference.getLastStatus(), BillStatusType.STRICKEN.name())) {
checkLastStatusDate(contentBillView, reference, observation);
checkLastStatusComm(contentBillView, reference, observation);
}
}
checkSponsor(contentBillView, reference, observation);
checkTitle(contentBillView, reference, observation);
checkSummary(contentBillView, reference, observation);
checkPrevVersions(contentBillView, reference, observation);
checkText(amendment, reference, observation);
checkMemo(amendment, reference, observation);
checkCoSponsors(amendment, reference, observation);
checkMultiSponsors(amendment, reference, observation);
checkHasSameAs(amendment, reference, observation);
checkSameAs(amendment, reference, observation);
checkLawCode(amendment, reference, observation);
checkLawSection(amendment, reference, observation);
return observation;
}
private void checkBasePrintNo(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getBasePrintNo(), reference.getBasePrintNo(), observation, BILL_BASE_PRINT_NO);
}
private void checkChamber(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
Chamber contentChamber = new BillId(content.getPrintNo(), content.getSession()).getChamber();
checkObject(contentChamber, StringUtils.upperCase(reference.getChamber()), observation, BILL_CHAMBER);
}
private void checkActiveVersion(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkObject(content.getActiveVersion(), StringUtils.upperCase(reference.getActiveVersion()),
observation, BILL_ACTIVE_AMENDMENT);
}
private void checkSameAs(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
TreeSet<BillId> olSameAs = Optional.ofNullable(content.getSameAs())
.map(ListView::getItems)
.orElse(ImmutableList.of())
.stream()
.map(BillIdView::toBillId)
.collect(Collectors.toCollection(TreeSet::new));
TreeSet<BillId> refSameAs = new TreeSet<>(reference.getSameAs());
checkCollection(olSameAs, refSameAs, observation, BILL_SAME_AS);
}
private void checkPrevVersions(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
TreeSet<BillId> olPrevVers = Optional.ofNullable(content.getPreviousVersions())
.map(ListView::getItems)
.orElse(ImmutableList.of())
.stream()
.map(BillIdView::toBillId)
.collect(Collectors.toCollection(TreeSet::new));
TreeSet<BillId> refPrevVers = new TreeSet<>(reference.getPreviousVersions());
checkCollection(olPrevVers, refPrevVers, observation, BILL_PREVIOUS_VERSIONS);
}
private void checkIsAmended(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
String amendVersion = Optional.ofNullable(reference.getBillId())
.map(billid -> billid.getVersion().toString())
.orElse("");
boolean olIsAmended = Optional.ofNullable(content.getAmendmentVersions())
.map(ListView::getItems)
.flatMap(items -> items.stream()
.filter(version -> version.compareTo(amendVersion) > 0)
.findAny())
.isPresent();
boolean refIsAmended = reference.isAmended();
checkObject(olIsAmended, refIsAmended, observation, BILL_IS_AMENDED);
}
private void checkHasSameAs(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
boolean contentHasSameAs = Optional.ofNullable(content.getSameAs())
.map(ListView::getSize)
.orElse(0) > 0;
checkObject(contentHasSameAs, reference.isHasSameAs(), observation, BILL_HAS_SAME_AS);
}
private void checkPublishDate(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
LocalDateTime olPubDateTime = content.getPublishedDateTime();
LocalDateTime refPubDateTime = reference.getPublishDate();
checkObject(olPubDateTime, refPubDateTime, observation, BILL_PUBLISH_DATE);
}
private String billActionToString(BillAction action) {
return String.valueOf(action.getSequenceNo()) + " " +
// Only check base print number, don't check the amendment version associated with the action.
// Public website models this differently causing amendment errors we are not interested in.
action.getBillId().getBasePrintNo() + " " +
action.getChamber() + " " +
action.getDate() + " " +
action.getText();
}
private void checkActions(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
List<BillAction> contentActions = Optional.ofNullable(content.getActions())
.map(ListView::getItems)
.orElse(ImmutableList.of())
.stream()
.map(BillActionView::toBillAction)
.collect(Collectors.toList());
checkCollection(contentActions, reference.getActions(), observation, BILL_ACTION,
this::billActionToString, "\n");
}
private String billStatusToString(BillStatusView status) {
return String.valueOf(status.getBillCalNo()) + " " +
status.getActionDate() + " " +
status.getCommitteeName() + " " +
status.getStatusType() + " " +
status.getStatusDesc();
}
private void checkMilestones(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
List<BillStatusView> contentMilestones = Optional.ofNullable(content.getMilestones())
.map(ListView::getItems)
.orElse(ImmutableList.of());
checkCollection(contentMilestones, reference.getMilestones(), observation, BILL_MILESTONES,
this::billStatusToString, "\n");
}
private void checkLastStatus(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
String contentStatus = Optional.ofNullable(content.getStatus())
.map(BillStatusView::getStatusType)
.orElse(null);
checkObject(contentStatus, reference.getLastStatus(), observation, BILL_LAST_STATUS);
}
private void checkLastStatusComm(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
String contentLastStatusComm = Optional.ofNullable(content.getStatus())
.map(BillStatusView::getCommitteeName)
.orElse(null);
checkStringUpper(contentLastStatusComm, reference.getLatestStatusCommittee(),
observation, BILL_LAST_STATUS_COMM);
}
private void checkLastStatusDate(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
LocalDateTime contentStatusDate = Optional.ofNullable(content.getStatus())
.map(BillStatusView::getActionDate)
.map(LocalDate::atStartOfDay)
.orElse(null);
checkObject(contentStatusDate, reference.getLastStatusDate(), observation, BILL_LAST_STATUS_DATE);
}
private void checkSponsor(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
String contentSponsor = Optional.ofNullable(content.getSponsor())
.map(SponsorView::getMember)
.map(MemberView::getShortName)
.orElse(null);
checkString(contentSponsor, reference.getSponsor(), observation, BILL_SPONSOR);
}
private void checkCoSponsors(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
List<String> contentCoSponsors = Optional.ofNullable(content.getCoSponsors())
.map(ListView::getItems)
.orElse(ImmutableList.of())
.stream()
.map(MemberView::getShortName)
.collect(Collectors.toList());
checkCollection(contentCoSponsors, reference.getCoSponsors(), observation, BILL_COSPONSOR);
}
private void checkMultiSponsors(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
List<String> contentMultiSponsors = Optional.ofNullable(content.getMultiSponsors())
.map(ListView::getItems)
.orElse(ImmutableList.of())
.stream()
.map(MemberView::getShortName)
.collect(Collectors.toList());
checkCollection(contentMultiSponsors, reference.getMultiSponsors(), observation, BILL_MULTISPONSOR);
}
private void checkTitle(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getTitle(), reference.getTitle(), observation, BILL_TITLE);
}
private void checkSummary(BillView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getSummary(), reference.getSummary(), observation, BILL_SUMMARY);
}
private void checkMemo(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getMemo(), reference.getMemo(), observation, BILL_MEMO);
}
private void checkText(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getFullText(), reference.getText(), observation, BILL_TEXT);
}
private void checkLawCode(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getLawCode(), reference.getLawCode(), observation, BILL_LAW_CODE);
}
private void checkLawSection(BillAmendmentView content, SenateSiteBill reference, SpotCheckObservation<BillId> observation) {
checkString(content.getLawSection(), reference.getLawSection(), observation, BILL_LAW_SECTION);
}
}