package gov.nysenate.openleg.service.spotcheck.senatesite.bill;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import gov.nysenate.openleg.client.view.base.ListView;
import gov.nysenate.openleg.client.view.bill.BillActionView;
import gov.nysenate.openleg.client.view.bill.BillIdView;
import gov.nysenate.openleg.client.view.bill.BillStatusView;
import gov.nysenate.openleg.client.view.entity.MemberView;
import gov.nysenate.openleg.model.bill.BillAction;
import gov.nysenate.openleg.model.bill.BillId;
import gov.nysenate.openleg.model.spotcheck.senatesite.*;
import gov.nysenate.openleg.model.spotcheck.senatesite.bill.SenateSiteBill;
import gov.nysenate.openleg.processor.base.ParseError;
import gov.nysenate.openleg.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import gov.nysenate.openleg.service.spotcheck.senatesite.base.JsonParser;
import java.io.IOException;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class BillJsonParser extends JsonParser{
@Autowired ObjectMapper objectMapper;
public List<SenateSiteBill> parseBills(SenateSiteDump billDump) throws ParseError {
return billDump.getDumpFragments().stream()
.flatMap(fragment -> extractBillsFromFragment(fragment).stream())
.collect(Collectors.toList());
}
/** --- Internal Methods --- */
private List<SenateSiteBill> extractBillsFromFragment(SenateSiteDumpFragment fragment) throws ParseError {
try {
JsonNode billMap = objectMapper.readTree(fragment.getFragmentFile())
.path("bills");
if (billMap.isMissingNode()) {
throw new ParseError("Could not locate \"bills\" node in senate site bill dump fragment file: " +
fragment.getFragmentFile().getAbsolutePath());
}
List<SenateSiteBill> bills = new LinkedList<>();
for (JsonNode billNode : billMap) {
bills.add(extractSenSiteBill(billNode, fragment));
}
return bills;
} catch (IOException | NoSuchElementException ex) {
throw new ParseError("error while reading senate site bill dump fragment file: " +
fragment.getFragmentFile().getAbsolutePath(),
ex);
}
}
private SenateSiteBill extractSenSiteBill(JsonNode billNode, SenateSiteDumpFragment fragment) throws IOException {
SenateSiteBill bill = new SenateSiteBill(DateUtils.endOfDateTimeRange(fragment.getDumpId().getRange()));
bill.setBasePrintNo(getValue(billNode, "field_ol_base_print_no"));
bill.setActiveVersion(getValue(billNode, "field_ol_active_version"));
bill.setMilestones(getMilestones(billNode, "field_ol_all_statuses"));
bill.setChamber(getValue(billNode, "field_ol_chamber"));
bill.setCoSponsors(getMembers(billNode, "field_ol_co_sponsor_names"));
bill.setText(getValue(billNode, "field_ol_full_text"));
bill.setAmended(getBooleanValue(billNode, "field_ol_is_amended"));
bill.setLatestStatusCommittee(getValue(billNode, "field_ol_latest_status_committee"));
bill.setLawCode(getValue(billNode, "field_ol_law_code"));
bill.setLawSection(getValue(billNode, "field_ol_law_section"));
bill.setMemo(getValue(billNode, "field_ol_memo"));
bill.setMultiSponsors(getMembers(billNode, "field_ol_multi_sponsor_names"));
bill.setTitle(getValue(billNode, "field_ol_name"));
bill.setPreviousVersions(getBillIdList(billNode, "field_ol_previous_versions"));
bill.setPrintNo(getValue(billNode, "field_ol_print_no"));
bill.setPublishDate(getDateTimeValue(billNode, "field_ol_publish_date"));
bill.setSameAs(getBillIdList(billNode, "field_ol_same_as"));
bill.setSponsor(getValue(billNode, "field_ol_sponsor_name"));
bill.setSummary(getValue(billNode, "field_ol_summary"));
bill.setSessionYear(getIntValue(billNode, "field_ol_session"));
bill.setLastStatus(getValue(billNode, "field_ol_last_status"));
bill.setLastStatusDate(getDateTimeValue(billNode, "field_ol_last_status_date"));
bill.setActions(getActionList(billNode, "field_ol_all_actions"));
if (bill.getBaseBillId().getBillType().isResolution()) {
// Public Website has different models for resolution and bills. For resolutions action info is stored
// in the field_ol_all_statuses node.
bill.setActions(getActionList(billNode, "field_ol_all_statuses"));
} else {
bill.setHasSameAs(getBooleanValue(billNode, "field_ol_has_same_as"));
}
return bill;
}
private List<BillStatusView> getMilestones(JsonNode billNode, String fieldName) {
TypeReference<ListView<BillStatusView>> billStatusListType = new TypeReference<ListView<BillStatusView>>() {};
Optional<ListView<BillStatusView>> billStatusList = deserializeValue(billNode, fieldName, billStatusListType);
return billStatusList.map(ListView::getItems).orElse(ImmutableList.of());
}
private List<String> getMembers(JsonNode billNode, String memberFieldName) {
TypeReference<List<MemberView>> memberListType = new TypeReference<List<MemberView>>() {};
Optional<List<MemberView>> memberList = deserializeValue(billNode, memberFieldName, memberListType);
return memberList.orElse(Collections.emptyList()).stream()
.map(MemberView::getShortName)
.collect(Collectors.toList());
}
private List<BillId> getBillIdList(JsonNode billNode, String fieldName) {
TypeReference<List<BillIdView>> billIdListType = new TypeReference<List<BillIdView>>() {};
Optional<List<BillIdView>> billIdViews = deserializeValue(billNode, fieldName, billIdListType);
return billIdViews.orElse(Collections.emptyList()).stream()
.map(BillIdView::toBillId)
.collect(Collectors.toList());
}
private List<BillAction> getActionList(JsonNode billNode, String fieldName) {
TypeReference<ListView<BillActionView>> actionListType = new TypeReference<ListView<BillActionView>>() {};
Optional<ListView<BillActionView>> actionListView = deserializeValue(billNode, fieldName, actionListType);
return actionListView
.map(ListView::getItems)
.orElse(ImmutableList.of()).stream()
.map(BillActionView::toBillAction)
.collect(Collectors.toList());
}
}