package gov.nysenate.openleg.dao.bill.text; import com.google.common.collect.Range; import gov.nysenate.openleg.config.Environment; import gov.nysenate.openleg.dao.base.*; import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.base.Version; import gov.nysenate.openleg.model.bill.BaseBillId; import gov.nysenate.openleg.model.spotcheck.billtext.BillScrapeQueueEntry; import gov.nysenate.openleg.model.spotcheck.billtext.BillTextReference; import gov.nysenate.openleg.util.DateUtils; import gov.nysenate.openleg.util.FileIOUtils; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; import static gov.nysenate.openleg.dao.bill.text.SqlBillTextReferenceQuery.*; /** * Created by kyle on 3/19/15. */ @Repository public class SqlFsBillTextReferenceDao extends SqlBaseDao implements BillTextReferenceDao { private static final Logger logger = LoggerFactory.getLogger(SqlFsBillTextReferenceDao.class); @Autowired Environment environment; File scrapedBillIncomingDir; File scrapedBillArchiveDir; @PostConstruct public void init() { scrapedBillIncomingDir = new File(environment.getScrapedStagingDir(), "bill"); scrapedBillArchiveDir = new File(new File(environment.getArchiveDir(), "scraped"), "bill"); try { FileUtils.forceMkdir(scrapedBillIncomingDir); FileUtils.forceMkdir(scrapedBillArchiveDir); } catch (IOException ex) { logger.error("could not create bill scraped dirs " + scrapedBillIncomingDir.getPath() + " and " + scrapedBillArchiveDir.getPath()); } } @Override public Collection<File> getIncomingScrapedBills() throws IOException{ return FileIOUtils.safeListFiles(scrapedBillIncomingDir, false, new String[]{}); } @Override public void archiveScrapedBill(File scrapedBill) throws IOException { FileUtils.moveFileToDirectory(scrapedBill, scrapedBillArchiveDir, true); } @Override public List<BillTextReference> getUncheckedBillTextReferences() { return jdbcNamed.query(SELECT_UNCHECKED_BTR.getSql(schema()), billTextRefRowMapper); } @Override public void insertBillTextReference(BillTextReference ref) { MapSqlParameterSource params = getParams(ref); if (jdbcNamed.update(UPDATE_BILL_REFERENCE.getSql(schema()), params) == 0){ jdbcNamed.update(INSERT_BILL_TEXT_REFERENCE.getSql(schema()), params); } } @Override public void setChecked(BaseBillId billId) { MapSqlParameterSource params = getParams(billId); jdbcNamed.update(SET_REF_CHECKED.getSql(schema()), params); } @Override public void deleteBillTextReference(BillTextReference ref) { MapSqlParameterSource params = getParams(ref); jdbcNamed.update(DELETE_BILL_REFERENCE.getSql(schema()), params); } @Override public BillTextReference getMostRecentBillTextReference(BaseBillId id, LocalDateTime start, LocalDateTime end) { MapSqlParameterSource params = getParams(id); //not sure if need this or need to change Range<LocalDateTime> range = Range.closed(start, end); addDateTimeRangeParams(params, range); return jdbcNamed.queryForObject(SELECT_BILL_TEXT_RANGE.getSql(schema(), new OrderBy("reference_date_time", SortOrder.DESC), LimitOffset.ONE), params, billTextRefRowMapper); } @Override public BillTextReference getMostRecentBillTextReference(LocalDateTime start, LocalDateTime end) { MapSqlParameterSource params = new MapSqlParameterSource(); Range<LocalDateTime> range = Range.closed(start, end); addDateTimeRangeParams(params, range); return jdbcNamed.queryForObject(SELECT_ALL_BILL_TEXT_RANGE.getSql(schema(), new OrderBy("reference_date_time", SortOrder.DESC), LimitOffset.ONE), params, billTextRefRowMapper); } @Override public List<BillTextReference> getBillTextReference(BaseBillId id) { MapSqlParameterSource params = getParams(id); return jdbcNamed.query(SELECT_BTR_BY_PRINT_NO.getSql(schema()), params, billTextRefRowMapper); } @Override public BillTextReference getBillTextReference(BaseBillId id, LocalDateTime refDateTime) { MapSqlParameterSource params = getParams(id, refDateTime); return jdbcNamed.queryForObject(SELECT_BILL_TEXT_REFERENCE.getSql(schema()), params, billTextRefRowMapper); } @Override public void addBillToScrapeQueue(BaseBillId id, int priority) { MapSqlParameterSource params = getQueueParams(id, priority); try { jdbcNamed.update(INSERT_SCRAPE_QUEUE.getSql(schema()), params); }catch(DuplicateKeyException ex){ jdbcNamed.update(UPDATE_SCRAPE_QUEUE.getSql(schema()), params); } } @Override public void deleteBillFromScrapeQueue(BaseBillId id){ MapSqlParameterSource params = getQueueParams(id); jdbcNamed.update(DELETE_SCRAPE_QUEUE.getSql(schema()), params); } @Override public PaginatedList<BillScrapeQueueEntry> getScrapeQueue(LimitOffset limitOffset, SortOrder order) { PaginatedRowHandler<BillScrapeQueueEntry> rowHandler = new PaginatedRowHandler<>(limitOffset, "total", scrapeQueueEntryRowMapper); jdbcNamed.query(SELECT_SCRAPE_QUEUE.getSql(schema(), new OrderBy("priority", order, "added_time", SortOrder.getOpposite(order)), limitOffset), rowHandler); return rowHandler.getList(); } @Override public BaseBillId getScrapeQueueHead() throws EmptyResultDataAccessException { PaginatedList<BillScrapeQueueEntry> scrapeQueue = getScrapeQueue(LimitOffset.ONE, SortOrder.DESC); if (scrapeQueue.getResults().isEmpty()) { throw new EmptyResultDataAccessException("no bills in scrape queue", 1); } return scrapeQueue.getResults().get(0).getBaseBillId(); } /**---------- Map Parameters -------*/ public MapSqlParameterSource getParams(BillTextReference entry){ MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("bill_print_no", entry.getPrintNo()); params.addValue("bill_session_year", entry.getSessionYear()); params.addValue("reference_date_time", DateUtils.toDate(entry.getReferenceDate())); params.addValue("bill_amend_version", entry.getActiveVersion().getValue()); params.addValue("text", entry.getText()); params.addValue("memo", entry.getMemo()); params.addValue("not_found", entry.isNotFound()); return params; } public MapSqlParameterSource getParams(BaseBillId id, LocalDateTime refDateTime) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("bill_print_no", id.getPrintNo()); params.addValue("bill_session_year", id.getSession().getYear()); params.addValue("bill_amend_version", id.getVersion().getValue()); params.addValue("reference_date_time", DateUtils.toDate(refDateTime)); return params; } public MapSqlParameterSource getParams(BaseBillId id) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("bill_print_no", id.getPrintNo()); params.addValue("bill_session_year", id.getSession().getYear()); params.addValue("bill_amend_version", id.getVersion().getValue()); return params; } public MapSqlParameterSource getQueueParams(BaseBillId id) { return getQueueParams(id, 0); } public MapSqlParameterSource getQueueParams(BaseBillId id, int priority) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("printNo", id.getPrintNo()); params.addValue("sessionYear", id.getSession().getYear()); params.addValue("priority", priority); return params; } /**---------- Bill Row Mapper -------*/ private static final RowMapper<BillTextReference> billTextRefRowMapper = (rs, rowNum) -> { BillTextReference ref = new BillTextReference(); ref.setBaseBillId(new BaseBillId(rs.getString("bill_print_no"), SessionYear.of(rs.getInt("bill_session_year")))); ref.setText(rs.getString("text")); ref.setMemo(rs.getString("memo")); ref.setReferenceDate(DateUtils.getLocalDateTime(rs.getTimestamp("reference_date_time"))); ref.setActiveVersion(Version.of(rs.getString("bill_amend_version"))); ref.setNotFound(rs.getBoolean("not_found")); return ref; }; private static final RowMapper<BillScrapeQueueEntry> scrapeQueueEntryRowMapper = (rs, rowNum) -> new BillScrapeQueueEntry( new BaseBillId(rs.getString("print_no"), rs.getInt("session_year")), rs.getInt("priority"), getLocalDateTimeFromRs(rs, "added_time") ); }