package gov.nysenate.openleg.dao.entity.member.data; import com.google.common.collect.TreeMultimap; import gov.nysenate.openleg.dao.base.*; import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.Chamber; import gov.nysenate.openleg.model.entity.SessionMember; import gov.nysenate.openleg.model.entity.Person; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.stereotype.Repository; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; @Repository("sqlMember") public class SqlMemberDao extends SqlBaseDao implements MemberDao { private static final Logger logger = LoggerFactory.getLogger(SqlMemberDao.class); /** --- Implemented Methods --- */ /** {@inheritDoc} */ @Override public TreeMultimap<SessionYear, SessionMember> getMemberById(int id) { MapSqlParameterSource params = new MapSqlParameterSource("memberId", id); List<SessionMember> memberList = jdbcNamed.query(SqlMemberQuery.SELECT_MEMBER_BY_ID_SQL.getSql(schema()), params, new MemberRowMapper()); TreeMultimap<SessionYear, SessionMember> memberMap = TreeMultimap.create(); memberList.forEach(member -> memberMap.put(member.getSessionYear(), member)); return memberMap; } /** {@inheritDoc} */ @Override public SessionMember getMemberById(int id, SessionYear session) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("memberId", id); params.addValue("sessionYear", session.getYear()); return jdbcNamed.queryForObject(SqlMemberQuery.SELECT_MEMBER_BY_ID_SESSION_SQL.getSql(schema()), params, new MemberRowMapper()); } @Override public SessionMember getMemberBySessionId(int sessionMemberId) { ImmutableParams params = ImmutableParams.from( new MapSqlParameterSource().addValue("sessionMemberId", sessionMemberId)); return jdbcNamed.queryForObject(SqlMemberQuery.SELECT_MEMBER_BY_SESSION_MEMBER_ID_SQL.getSql(schema()), params, new MemberRowMapper()); } /** {@inheritDoc} */ @Override public Map<SessionYear, SessionMember> getMembersByShortName(String lbdcShortName, Chamber chamber) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("shortName", lbdcShortName); params.addValue("chamber", chamber.name().toLowerCase()); params.addValue("alternate", false); List<SessionMember> members = jdbcNamed.query(SqlMemberQuery.SELECT_MEMBER_BY_SHORTNAME_SQL.getSql(schema()), params, new MemberRowMapper()); return getMemberSessionMap(members); } /** {@inheritDoc} * * Since the short names used in the source data can be inconsistent (the short name can get modified * during the middle of a session year) we have a notion of an alternate short name. A member can only * have one primary short name mapping during a session year but can have multiple 'alternate' short names * to deal with edge cases in the data. This method will attempt to match the primary short name first * and if that fails tries to check for an alternate form. If both attempts fail the calling method will * have to handle a DataAccessException. */ @Override public SessionMember getMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("shortName", lbdcShortName.trim()); params.addValue("sessionYear", sessionYear.getYear()); params.addValue("chamber", chamber.name().toLowerCase()); params.addValue("alternate", false); logger.trace("Fetching member {} ({}) from database...", lbdcShortName, sessionYear); try { return jdbcNamed.queryForObject(SqlMemberQuery.SELECT_MEMBER_BY_SHORTNAME_SESSION_SQL.getSql(schema()), params, new MemberRowMapper()); } catch (EmptyResultDataAccessException ex) { params.addValue("alternate", true); return jdbcNamed.queryForObject(SqlMemberQuery.SELECT_MEMBER_BY_SHORTNAME_SESSION_SQL.getSql(schema(), LimitOffset.ONE), params, new MemberRowMapper()); } } /** {@inheritDoc} */ @Override public List<SessionMember> getAllMembers(SortOrder sortOrder, LimitOffset limOff) { OrderBy orderBy = new OrderBy("last_name", sortOrder); return jdbcNamed.query(SqlMemberQuery.SELECT_MEMBER_FRAGMENT.getSql(schema(), orderBy, limOff), new MapSqlParameterSource(), new MemberRowMapper()); } /** {@inheritDoc} */ @Override public List<SessionMember> getUnverifiedSessionMembers() { OrderBy order = new OrderBy("session_year", SortOrder.DESC, "lbdc_short_name", SortOrder.ASC); return jdbcNamed.query(SqlMemberQuery.SELECT_UNVERIFIED_MEMBERS_SQL.getSql(schema(), order), new MapSqlParameterSource(), new MemberRowMapper()); } /** {@inheritDoc} */ @Override public void updatePerson(Person person) { ImmutableParams params = ImmutableParams.from(getPersonParams(person)); if (jdbcNamed.update(SqlMemberQuery.UPDATE_PERSON_SQL.getSql(schema()), params) == 0) { int personId = jdbcNamed.queryForObject( SqlMemberQuery.INSERT_PERSON_SQL.getSql(schema()), params, new SingleColumnRowMapper<>()); person.setPersonId(personId); } } /** {@inheritDoc} */ @Override public void updateMember(SessionMember member) { ImmutableParams params = ImmutableParams.from(getMemberParams(member)); if (jdbcNamed.update(SqlMemberQuery.UPDATE_MEMBER_SQL.getSql(schema()), params) == 0) { int memberId = jdbcNamed.queryForObject( SqlMemberQuery.INSERT_MEMBER_SQL.getSql(schema()), params, new SingleColumnRowMapper<>()); member.setMemberId(memberId); } } /** {@inheritDoc} */ @Override public void updateSessionMember(SessionMember member) { ImmutableParams params = ImmutableParams.from(getMemberParams(member)); if (jdbcNamed.update(SqlMemberQuery.UPDATE_SESSION_MEMBER_SQL.getSql(schema()), params) == 0) { int sessionMemberId = jdbcNamed.queryForObject( SqlMemberQuery.INSERT_SESSION_MEMBER_SQL.getSql(schema()), params, new SingleColumnRowMapper<>()); member.setSessionMemberId(sessionMemberId); } } /** {@inheritDoc} */ @Override public void linkMember(int memberId, int personId) { SqlParameterSource params = new MapSqlParameterSource() .addValue("memberId", memberId) .addValue("personId", personId); jdbcNamed.update(SqlMemberQuery.LINK_MEMBER_SQL.getSql(schema()), params); } /** {@inheritDoc} */ @Override public void linkSessionMember(int sessionMemberId, int memberId) { SqlParameterSource params = new MapSqlParameterSource() .addValue("sessionMemberId", sessionMemberId) .addValue("memberId", memberId); jdbcNamed.update(SqlMemberQuery.LINK_SESSION_MEMBER_SQL.getSql(schema()), params); } /** {@inheritDoc} */ @Override public void clearOrphans() { jdbcNamed.update(SqlMemberQuery.DELETE_ORPHAN_MEMBERS_SQL.getSql(schema()), new MapSqlParameterSource()); jdbcNamed.update(SqlMemberQuery.DELETE_ORPHAN_PERSONS_SQL.getSql(schema()), new MapSqlParameterSource()); } /** --- Helper classes --- */ private static class MemberRowMapper implements RowMapper<SessionMember> { @Override public SessionMember mapRow(ResultSet rs, int rowNum) throws SQLException { SessionMember member = new SessionMember(); member.setMemberId(rs.getInt("member_id")); member.setSessionMemberId(rs.getInt("session_member_id")); member.setLbdcShortName(rs.getString("lbdc_short_name")); member.setSessionYear(getSessionYearFromRs(rs, "session_year")); member.setDistrictCode(rs.getInt("district_code")); member.setChamber(Chamber.valueOf(rs.getString("chamber").toUpperCase())); member.setIncumbent(rs.getBoolean("incumbent")); member.setPersonId(rs.getInt("person_id")); member.setFullName(rs.getString("full_name")); member.setPrefix(rs.getString("prefix")); member.setFirstName(rs.getString("first_name")); member.setMiddleName(rs.getString("middle_name")); member.setLastName(rs.getString("last_name")); member.setSuffix(rs.getString("suffix")); member.setImgName(rs.getString("img_name")); member.setAlternate(rs.getBoolean("alternate")); member.setVerified(rs.getBoolean("verified")); return member; } } /** --- Internal Methods --- */ private MapSqlParameterSource getPersonParams(Person person) { return new MapSqlParameterSource() .addValue("personId", person.getPersonId()) .addValue("fullName", person.getFullName()) .addValue("firstName", person.getFirstName()) .addValue("lastName", person.getLastName()) .addValue("middleName", person.getMiddleName()) .addValue("email", person.getEmail()) .addValue("prefix", person.getPrefix()) .addValue("suffix", person.getSuffix()) .addValue("img_name", person.getImgName()) .addValue("verified", person.isVerified()); } private MapSqlParameterSource getMemberParams(SessionMember member) { return getPersonParams(member) .addValue("memberId", member.getMemberId()) .addValue("sessionMemberId", member.getSessionMemberId()) .addValue("chamber", Optional.ofNullable(member.getChamber()).map(Chamber::asSqlEnum).orElse(null)) .addValue("incumbent", member.isIncumbent()) .addValue("fullName", member.getFullName()) .addValue("lbdcShortName", member.getLbdcShortName()) .addValue("sessionYear", Optional.ofNullable(member.getSessionYear()).map(SessionYear::getYear).orElse(null)) .addValue("districtCode", member.getDistrictCode()) .addValue("alternate", member.isAlternate()); } /** * Converts a list of member objects referring to multiple session years into a * map keyed by the session year. * * @param members List<Member> * @return Map<SessionYear, Member> */ private Map<SessionYear, SessionMember> getMemberSessionMap(List<SessionMember> members) { TreeMap<SessionYear, SessionMember> memberMap = new TreeMap<>(); members.forEach(m -> memberMap.put(m.getSessionYear(), m)); return memberMap; } }