package gov.nysenate.openleg.dao.hearing; import com.google.common.collect.MapDifference; import com.google.common.collect.Range; import gov.nysenate.openleg.dao.base.*; import gov.nysenate.openleg.model.entity.Chamber; import gov.nysenate.openleg.model.hearing.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.List; import static gov.nysenate.openleg.dao.hearing.SqlPublicHearingQuery.*; import static gov.nysenate.openleg.util.CollectionUtils.difference; import static gov.nysenate.openleg.util.DateUtils.toDate; import static gov.nysenate.openleg.util.DateUtils.toTime; @Repository public class SqlPublicHearingDao extends SqlBaseDao implements PublicHearingDao { private static final Logger logger = LoggerFactory.getLogger(SqlPublicHearingDao.class); /** {@inheritDoc} */ @Override public List<PublicHearingId> getPublicHearingIds(SortOrder order, LimitOffset limOff) { OrderBy orderBy = new OrderBy("filename", order); List<PublicHearingId> ids = jdbcNamed.query( SELECT_PUBLIC_HEARING_IDS.getSql(schema(), orderBy, limOff), publicHearingIdRowMapper); return ids; } /** {@inheritDoc} */ @Override public PublicHearing getPublicHearing(PublicHearingId publicHearingId) throws EmptyResultDataAccessException { MapSqlParameterSource params = getPublicHearingIdParams(publicHearingId); PublicHearing publicHearing = jdbcNamed.queryForObject( SELECT_PUBLIC_HEARING_BY_ID.getSql(schema()), params, publicHearingRowMapper); publicHearing.setCommittees(getPublicHearingCommittees(publicHearingId)); return publicHearing; } /** {@inheritDoc} */ @Override public void updatePublicHearing(PublicHearing publicHearing, PublicHearingFile publicHearingFile) { MapSqlParameterSource params = getPublicHearingParams(publicHearing, publicHearingFile); if (jdbcNamed.update(UPDATE_PUBLIC_HEARING.getSql(schema()), params) == 0) { jdbcNamed.update(INSERT_PUBLIC_HEARING.getSql(schema()), params); } updatePublicHearingCommittees(publicHearing); } /** {@inheritDoc} */ @Override public PaginatedList<PublicHearingUpdateToken> publicHearingsUpdatedDuring(Range<LocalDateTime> dateRange, SortOrder dateOrder, LimitOffset limOff) { MapSqlParameterSource params = new MapSqlParameterSource(); addDateTimeRangeParams(params, dateRange); OrderBy orderBy = new OrderBy("modified_date_time", dateOrder); PaginatedRowHandler<PublicHearingUpdateToken> handler = new PaginatedRowHandler<>(limOff, "total_updated", publicHearingTokenRowMapper); jdbcNamed.query(SELECT_PUBLIC_HEARING_UPDATES.getSql(schema(), orderBy, limOff), params, handler); return handler.getList(); } /** * Updates the backing store with this PublicHearings PublicHearingCommittee information, or inserts * it if it doesn't exist. * @param publicHearing */ private void updatePublicHearingCommittees(PublicHearing publicHearing) { List<PublicHearingCommittee> existingCommittees = getPublicHearingCommittees(publicHearing.getId()); if (existingCommittees != null && publicHearing.getCommittees() != null) { if (!existingCommittees.equals(publicHearing.getCommittees())) { MapDifference<PublicHearingCommittee, Integer> diff = difference(existingCommittees, publicHearing.getCommittees(), 1); diff.entriesOnlyOnLeft().forEach((member, ordinal) -> { jdbcNamed.update(DELETE_PUBLIC_HEARING_COMMITTEE.getSql(schema()), getCommitteeParams(publicHearing.getId(), member)); }); diff.entriesOnlyOnRight().forEach((member, ordinal) -> { jdbcNamed.update(INSERT_PUBLIC_HEARING_COMMITTEES.getSql(schema()), getCommitteeParams(publicHearing.getId(), member)); }); } } } /** * Get a list of {@link PublicHearingCommittee} belonging to a PublicHearing. * @param publicHearingId * @return */ private List<PublicHearingCommittee> getPublicHearingCommittees(PublicHearingId publicHearingId) { MapSqlParameterSource params = getPublicHearingIdParams(publicHearingId); return jdbcNamed.query(SELECT_PUBLIC_HEARING_COMMITTEES.getSql(schema()), params, committeeRowMapper); } /** --- Param Source Methods --- */ private MapSqlParameterSource getPublicHearingParams(PublicHearing publicHearing, PublicHearingFile publicHearingFile) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("filename", publicHearingFile.getFileName()); params.addValue("date", toDate(publicHearing.getDate())); params.addValue("title", publicHearing.getTitle()); params.addValue("address", publicHearing.getAddress()); params.addValue("text", publicHearing.getText()); params.addValue("startTime", toTime(publicHearing.getStartTime())); params.addValue("endTime", toTime(publicHearing.getEndTime())); addModPubDateParams(publicHearing.getModifiedDateTime(), publicHearing.getPublishedDateTime(), params); return params; } private MapSqlParameterSource getPublicHearingIdParams(PublicHearingId publicHearingId) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("filename", publicHearingId.getFileName()); return params; } private MapSqlParameterSource getCommitteeParams(PublicHearingId id, PublicHearingCommittee committee) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("filename", id.getFileName()); params.addValue("committeeName", committee.getName()); params.addValue("committeeChamber", committee.getChamber().name().toLowerCase()); return params; } /** --- Row Mapper Instances --- */ static RowMapper<PublicHearing> publicHearingRowMapper = (rs, rowNum) -> { PublicHearingId id = new PublicHearingId(rs.getString("filename")); PublicHearing publicHearing = new PublicHearing(id, getLocalDateFromRs(rs, "date"), rs.getString("text")); publicHearing.setTitle(rs.getString("title")); publicHearing.setAddress(rs.getString("address")); publicHearing.setStartTime(getLocalTimeFromRs(rs, "start_time")); publicHearing.setEndTime(getLocalTimeFromRs(rs, "end_time")); publicHearing.setModifiedDateTime(getLocalDateTimeFromRs(rs, "modified_date_time")); publicHearing.setPublishedDateTime(getLocalDateTimeFromRs(rs, "published_date_time")); return publicHearing; }; static RowMapper<PublicHearingId> publicHearingIdRowMapper = (rs, rowNum) -> new PublicHearingId(rs.getString("filename")); static RowMapper<PublicHearingCommittee> committeeRowMapper = (rs, rowNum) -> { PublicHearingCommittee committee = new PublicHearingCommittee(); committee.setName(rs.getString("committee_name")); committee.setChamber(Chamber.valueOf(rs.getString("committee_chamber").toUpperCase())); return committee; }; static RowMapper<PublicHearingUpdateToken> publicHearingTokenRowMapper = (rs, rowNum) -> new PublicHearingUpdateToken(new PublicHearingId(rs.getString("filename")), getLocalDateTimeFromRs(rs, "modified_date_time")); }