From cba182af2503e3ff3c9c71feb181d6d11ff3f60e Mon Sep 17 00:00:00 2001 From: Jacob Keegan Date: Mon, 27 Jan 2020 10:45:51 -0500 Subject: [PATCH 01/29] Changed the transcript table to use a datetime as the primary key, so that updates are not repeated on the transcripts API call. Changed file naming to pull date from text to correct naming errors, and to have a consistent way of showing new versions. --- docs/backend/index.md | 67 ++++++++++--------- .../transcript/SqlFsTranscriptFileDao.java | 11 ++- .../transcript/SqlTranscriptFileQuery.java | 4 ++ .../model/transcript/TranscriptFile.java | 10 +++ .../openleg/processor/DataProcessor.java | 2 +- .../ManagedTranscriptProcessService.java | 1 + .../transcript/TranscriptParser.java | 10 +-- .../V20200124.0512__transcript_update_fix.sql | 4 ++ 8 files changed, 70 insertions(+), 39 deletions(-) create mode 100644 src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql diff --git a/docs/backend/index.md b/docs/backend/index.md index d27e63898..22804ab34 100644 --- a/docs/backend/index.md +++ b/docs/backend/index.md @@ -6,44 +6,44 @@ General installation instructions for Ubuntu. ### Java 8 1. `sudo apt-get install openjdk-8-jdk` -1. Set `$JAVA_HOME` environment variable +2. Set `$JAVA_HOME` environment variable * https://askubuntu.com/questions/175514/how-to-set-java-home-for-java ### Git 1. `sudo apt-get install git` -1. Configuration +2. Configuration 1. `git config --global user.name "<>"` - 1. `git config --global user.email <>` + 2. `git config --global user.email <>` ### IntelliJ 1. Download the Ultimate Edition from https://www.jetbrains.com/idea/download/#section=linux -1. Extract: `sudo tar -xzvf <> -C /usr/share/` -1. Run: `bin/idea.sh` which will be located in the directory extracted in the previous step. +2. Extract: `sudo tar -xzvf <> -C /usr/share/` +3. Run: `bin/idea.sh` which will be located in the directory extracted in the previous step. ### Tomcat 1. Download the latest version of Tomcat from https://tomcat.apache.org/download-90.cgi * You want the Core tar.gz distribution. -1. `mkdir ~/tomcat8` -1. `tar -xzvf ~/Downloads/<> -C ~/tomcat8` -1. If you need to run tomcat as a non-root user, e.g. in IntelliJ. +2. `mkdir ~/tomcat8` +3. `tar -xzvf ~/Downloads/<> -C ~/tomcat8` +4. If you need to run tomcat as a non-root user, e.g. in IntelliJ. Make sure the contents of the tomcat directory are readable an executable for all users. e.g. `chmod -R +rx ~/tomcat8` ### Elasticsearch -1. `wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.5.4.deb` -1. `sudo dpkg -i elasticsearch-6.5.4.deb` -1. `sudo systemctl enable elasticsearch.service` +1. `wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.5.1-amd64.deb` +2. `sudo dpkg -i elasticsearch-7.5.1-amd64.deb` +3. `sudo systemctl enable elasticsearch.service` ### Postgresql, Maven, Nodejs -1. `sudo apt-get install postgresql postgresql-contrib maven nodejs` +`sudo apt-get install postgresql postgresql-contrib maven nodejs` ### Bower and Grunt -1. `sudo npm install -g bower grunt` +`sudo npm install -g bower grunt` ## Source Code Setup @@ -52,21 +52,21 @@ e.g. `chmod -R +rx ~/tomcat8` Clone the Open Legislation codebase to your computer. 1. `cd <>` Move into the directory where you want to keep the source code. -1. `git clone https://github.com/nysenate/OpenLegislation ` +2. `git clone https://github.com/nysenate/OpenLegislation ` ### Open Code in Intellij 1. From the start screen, click Import project * If you are already in an open project, go to File->New->Project from Existing Sources -1. Select the Open Legislation directory that was cloned from GitHub -1. In the Import Project screen, select "Import project from external model" and choose Maven from the list of models -1. The next screen will be a bunch of checkbox options. Ensure the following are checked: +2. Select the Open Legislation directory that was cloned from GitHub +3. In the Import Project screen, select "Import project from external model" and choose Maven from the list of models +4. The next screen will be a bunch of checkbox options. Ensure the following are checked: * Search for projects recursively * Import Maven projects automatically * Automatically download * Sources * Documentation -1. Be sure to use Java 8 on this project +5. Be sure to use Java 8 on this project ## Database Setup @@ -74,19 +74,20 @@ Clone the Open Legislation codebase to your computer. Log into psql as the postgres user. -1.`sudo su - postgres` -1.`psql -U postgres` +`sudo su - postgres` + +`psql -U postgres` In psql, create a user with the same name as you linux user. 1. Create a new role in postgres. - `CREATE USER <> WITH LOGIN SUPERUSER PASSWORD '<>';` + `CREATE USER <> WITH LOGIN SUPERUSER PASSWORD '<>';` ### Create Open Legislation Database -1. Enter psql in a terminal with the database user you created in the previous step: `psql -U openleg postgres` -1. Create a database for Open Legislation: `CREATE DATABSE openleg;` -1. Exit psql with `\q` +1. Enter psql in a terminal with the database user you created in the previous step: `psql -U openleg <>` +2. Create a database for Open Legislation: `CREATE DATABSE openleg;` +3. Exit psql with `\q` ## Elasticsearch setup @@ -181,11 +182,11 @@ Run `mvn compile flyway:migrate` to generate a build that is deployable by tomca We typically run Open Legislation in Tomcat through IntelliJ. 1. Open Intellij and go to menu: Run -> Edit Configurations -1. Click the plus sign in the top left corner -1. Scroll down until you find Tomcat Server. Select local server -1. In the Server tab -> application server link your download of tomcat from before -1. In the Deployment tab -> hit the plus sign again and select legislation:war exploded. Set the Application Context to `/`. -1. Apply these changes +2. Click the plus sign in the top left corner +3. Scroll down until you find Tomcat Server. Select local server +4. In the Server tab -> application server link your download of tomcat from before +5. In the Deployment tab -> hit the plus sign again and select legislation:war exploded. Set the Application Context to `/`. +6. Apply these changes Now you can run Tomcat by selecting it and pressing the green play button towards the top right of the Intellij UI. Once running you should be able to view the application at: http://localhost:8080 @@ -195,18 +196,18 @@ Once running you should be able to view the application at: http://localhost:808 ### Setup Open Legislation Environment Directories 1. `sudo mkdir /data` -1. `sudo chown -R $USER:$USER /data` -1. `mkdir /data/openleg /data/openleg/archive /data/openleg/staging /data/openleg/staging/xmls` +2. `sudo chown -R $USER:$USER /data` +3. `mkdir /data/openleg /data/openleg/archive /data/openleg/staging /data/openleg/staging/xmls` ### Rsync Legislative Data -1. `rsync -a --info=progress2 '<>@tyrol.nysenate.gov:/data/2017-18_xml_files' /data/openleg/staging/xmls` +`rsync -a --info=progress2 '<>@tyrol.nysenate.gov:/data/2017-18_xml_files' /data/openleg/staging/xmls` ### Process Data Now we can process the xml data we downloaded in our local Open Legislation environment. This process can take a long time so be prepared to leave it running for up to a day or two. -1. `curl -XPOST -v -u '<>:<>' localhost:8080/api/3/admin/process/run` +`curl -XPOST -v -u '<>:<>' localhost:8080/api/3/admin/process/run` **NOTE** * Do not attempt this on a system with 4GB of ram without changing the cache limits in app.properties diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index 8a74f7a46..e242e37a2 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -62,12 +62,21 @@ public void updateTranscriptFile(TranscriptFile transcriptFile) { } } + private int pastVersions(TranscriptFile transcriptFile) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("datetime", transcriptFile.getTranscript().getDateTime()); + Integer ret = jdbcNamed.queryForObject(GET_OLD_FILES.getSql(schema()), params, Integer.class); + return ret == null ? 0 : ret; + } + /** {@inheritDoc} */ @Override public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws IOException { File stagedFile = transcriptFile.getFile(); if (stagedFile.getParentFile().compareTo(incomingTranscriptDir) == 0) { - File archiveFile = new File(archiveTranscriptDir, transcriptFile.getFileName()); + String currVersion = Integer.toString(pastVersions(transcriptFile)+1); + String trueName = transcriptFile.getTranscript().getDateTime().toString() + ".v" + currVersion; + File archiveFile = new File(archiveTranscriptDir, trueName); FileIOUtils.moveFile(stagedFile, archiveFile); transcriptFile.setFile(archiveFile); transcriptFile.setArchived(true); diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java index b0832fd7a..672c17d4f 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java @@ -23,6 +23,10 @@ public enum SqlTranscriptFileQuery implements BasicSqlQuery " pending_processing = :pendingProcessing," + " archived = :archived " + "WHERE file_name = :fileName" + ), + GET_OLD_FILES( + "SELECT COUNT(file_name) FROM ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + + "WHERE file_name LIKE ':datetime%'" ); private String sql; diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java index b1bdcf641..1c4e6c616 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java @@ -16,6 +16,8 @@ public class TranscriptFile extends BaseSourceData /** Indicates if the underlying 'file' reference has been moved into an archive directory. */ private boolean archived; + private Transcript transcript; + /** --- Constructors --- */ public TranscriptFile(File file) throws FileNotFoundException { @@ -43,6 +45,14 @@ public void setFile(File file) { this.file = file; } + public Transcript getTranscript() { + return transcript; + } + + public void setTranscript(Transcript transcript) { + this.transcript = transcript; + } + public boolean isArchived() { return archived; } diff --git a/src/main/java/gov/nysenate/openleg/processor/DataProcessor.java b/src/main/java/gov/nysenate/openleg/processor/DataProcessor.java index 3cab25e29..461b3b749 100644 --- a/src/main/java/gov/nysenate/openleg/processor/DataProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/DataProcessor.java @@ -181,7 +181,7 @@ public synchronized void ingest() throws IOException { ingestedCounts.put(processor.getIngestType(), ingestedCount); } } else { - logger.info("Not injesting data, processing is disabled."); + logger.info("Not ingesting data, processing is disabled."); } } if (ingestedCounts.size() > 0) { diff --git a/src/main/java/gov/nysenate/openleg/processor/transcript/ManagedTranscriptProcessService.java b/src/main/java/gov/nysenate/openleg/processor/transcript/ManagedTranscriptProcessService.java index 31ce83c9f..9c5165eda 100644 --- a/src/main/java/gov/nysenate/openleg/processor/transcript/ManagedTranscriptProcessService.java +++ b/src/main/java/gov/nysenate/openleg/processor/transcript/ManagedTranscriptProcessService.java @@ -55,6 +55,7 @@ public int collateTranscriptFiles() { transcriptFiles = transcriptFileDao.getIncomingTranscriptFiles(LimitOffset.FIFTY); for (TranscriptFile file : transcriptFiles) { file.setPendingProcessing(true); + file.setTranscript(transcriptParser.getTranscriptFromFile(file)); transcriptFileDao.archiveAndUpdateTranscriptFile(file); numCollated++; } diff --git a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java index e0bdbc9ae..6049f9c1e 100644 --- a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java @@ -27,6 +27,10 @@ public class TranscriptParser private TranscriptDataService transcriptDataService; public void process(TranscriptFile transcriptFile) throws IOException { + transcriptDataService.saveTranscript(getTranscriptFromFile(transcriptFile), transcriptFile, true); + } + + public Transcript getTranscriptFromFile(TranscriptFile transcriptFile) throws IOException { String sessionType = null; String location = null; String date = null; @@ -41,7 +45,7 @@ public void process(TranscriptFile transcriptFile) throws IOException { String lineText; BufferedReader reader = new BufferedReader(new InputStreamReader( - new FileInputStream(transcriptFile.getFile()), TRANSCRIPT_ENCODING)); + new FileInputStream(transcriptFile.getFile()), TRANSCRIPT_ENCODING)); while ((lineText = reader.readLine()) != null) { TranscriptLine line = new TranscriptLine(lineText); @@ -86,9 +90,7 @@ public void process(TranscriptFile transcriptFile) throws IOException { LocalDateTime dateTime = LocalDateTime.parse(date + " " + time, dtf); TranscriptId transcriptId = new TranscriptId(transcriptFile.getFileName()); - Transcript transcript = new Transcript(transcriptId, sessionType, dateTime, location, transcriptText.toString()); - - transcriptDataService.saveTranscript(transcript, transcriptFile, true); + return new Transcript(transcriptId, sessionType, dateTime, location, transcriptText.toString()); } private boolean areWeDoneWithFirstPage(String sessionType, String location, String date, String time) { diff --git a/src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql b/src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql new file mode 100644 index 000000000..1f0ee8dc4 --- /dev/null +++ b/src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql @@ -0,0 +1,4 @@ +ALTER TABLE master.transcript + DROP CONSTRAINT transcript_pkey; +ALTER TABLE master.transcript + ADD PRIMARY KEY (date_time); \ No newline at end of file From b1badc038ebc92fad7b36e7e9e87536dcbd5ddc6 Mon Sep 17 00:00:00 2001 From: Kevin Caseiras Date: Mon, 27 Jan 2020 12:03:38 -0500 Subject: [PATCH 02/29] Update grunt-sass and some gruntfile syntax to support nodejs v12 Tested on nodejs v12.14.1 and v10.13.0 These changes should not break current functionality with v10. After upgrading to v12, make sure to delete the `src/main/webapp/node_modules` directory before building. --- src/main/webapp/Gruntfile.js | 3 ++ src/main/webapp/package-lock.json | 70 ++----------------------------- src/main/webapp/package.json | 2 +- 3 files changed, 7 insertions(+), 68 deletions(-) diff --git a/src/main/webapp/Gruntfile.js b/src/main/webapp/Gruntfile.js index 35561518f..79db4a189 100644 --- a/src/main/webapp/Gruntfile.js +++ b/src/main/webapp/Gruntfile.js @@ -8,6 +8,8 @@ var pomDoc = libxmljs.parseXml(pomXml); var artifactId = pomDoc.find("/*/*[name()='artifactId']")[0].text(); var version = pomDoc.find("/*/*[name()='version']")[0].text(); +var sass = require('node-sass'); + module.exports = function (grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), @@ -33,6 +35,7 @@ module.exports = function (grunt) { // Compile sass into css sass: { options: { + implementation: sass, sourceMap: true }, openleg: { diff --git a/src/main/webapp/package-lock.json b/src/main/webapp/package-lock.json index 4e19fd0b2..e8795eefe 100644 --- a/src/main/webapp/package-lock.json +++ b/src/main/webapp/package-lock.json @@ -1139,15 +1139,6 @@ "is-obj": "^1.0.0" } }, - "each-async": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", - "integrity": "sha1-3uUim98KtrogEqOV4bhpq/iBNHM=", - "requires": { - "onetime": "^1.0.0", - "set-immediate-shim": "^1.0.0" - } - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1759,39 +1750,9 @@ } }, "grunt-sass": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/grunt-sass/-/grunt-sass-1.2.1.tgz", - "integrity": "sha1-+4e2yqxG+zLUUXf9Lktv90aMGRk=", - "requires": { - "each-async": "^1.0.0", - "node-sass": "^3.7.0", - "object-assign": "^4.0.1" - }, - "dependencies": { - "node-sass": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-3.13.1.tgz", - "integrity": "sha1-ckD7v/I5YwS0IjUn7TAgWJwAT8I=", - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.3.2", - "node-gyp": "^3.3.1", - "npmlog": "^4.0.0", - "request": "^2.61.0", - "sass-graph": "^2.1.1" - } - } - } + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/grunt-sass/-/grunt-sass-3.1.0.tgz", + "integrity": "sha512-90s27H7FoCDcA8C8+R0GwC+ntYD3lG6S/jqcavWm3bn9RiJTmSfOvfbFa1PXx4NbBWuiGQMLfQTj/JvvqT5w6A==" }, "grunt-shell": { "version": "1.3.1", @@ -2247,16 +2208,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "lodash.indexof": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/lodash.indexof/-/lodash.indexof-4.0.5.tgz", @@ -2411,11 +2362,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" - }, "needle": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", @@ -2729,11 +2675,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -5117,11 +5058,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, "shelljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index 3bc0d2337..32c7405c1 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -19,7 +19,7 @@ "grunt-contrib-watch": "^1.1.0", "grunt-file-exists": "^0.1.4", "grunt-postcss": "^0.9.0", - "grunt-sass": "^1.2.1", + "grunt-sass": "^3.1.0", "grunt-shell": "^1.1.1", "libxmljs": "^0.19.7", "node-gyp": "^3.7.0", From 735e15ce682cd196a3986762d57845d2741136ad Mon Sep 17 00:00:00 2001 From: Jacob Keegan Date: Mon, 27 Jan 2020 15:58:11 -0500 Subject: [PATCH 03/29] Some more changes to add the dateTime and original filename to transcript_file. --- .../openleg/config/StartupHousekeeper.java | 2 +- .../transcript/SqlFsTranscriptFileDao.java | 8 +++-- .../transcript/SqlTranscriptFileQuery.java | 14 +++++---- .../dao/transcript/SqlTranscriptQuery.java | 4 +-- .../model/transcript/TranscriptFile.java | 30 +++++++++++++++---- .../transcript/TranscriptParser.java | 2 +- ...0200127.0316__transcript_table_changes.sql | 7 +++++ ...20200127.0338__transcript_file_changes.sql | 2 ++ 8 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql create mode 100644 src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql diff --git a/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java b/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java index a83652431..e7d8cba62 100644 --- a/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java +++ b/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java @@ -24,6 +24,6 @@ public class StartupHousekeeper { */ @EventListener(ContextRefreshedEvent.class) public void onContextRefreshedEvent() { - eventBus.post(new CacheWarmEvent(Sets.newHashSet(ContentCache.values()))); + //eventBus.post(new CacheWarmEvent(Sets.newHashSet(ContentCache.values()))); } } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index e242e37a2..8095e9df1 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -64,8 +64,8 @@ public void updateTranscriptFile(TranscriptFile transcriptFile) { private int pastVersions(TranscriptFile transcriptFile) { MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("datetime", transcriptFile.getTranscript().getDateTime()); - Integer ret = jdbcNamed.queryForObject(GET_OLD_FILES.getSql(schema()), params, Integer.class); + params.addValue("dateTime", transcriptFile.getDateTime()); + Integer ret = jdbcNamed.queryForObject(OLD_FILE_COUNT.getSql(schema()), params, Integer.class); return ret == null ? 0 : ret; } @@ -75,7 +75,7 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws File stagedFile = transcriptFile.getFile(); if (stagedFile.getParentFile().compareTo(incomingTranscriptDir) == 0) { String currVersion = Integer.toString(pastVersions(transcriptFile)+1); - String trueName = transcriptFile.getTranscript().getDateTime().toString() + ".v" + currVersion; + String trueName = transcriptFile.getDateTime().toString() + ".v" + currVersion; File archiveFile = new File(archiveTranscriptDir, trueName); FileIOUtils.moveFile(stagedFile, archiveFile); transcriptFile.setFile(archiveFile); @@ -139,6 +139,8 @@ private MapSqlParameterSource getTranscriptFileParams(TranscriptFile transcriptF params.addValue("processedCount", transcriptFile.getProcessedCount()); params.addValue("pendingProcessing", transcriptFile.isPendingProcessing()); params.addValue("archived", transcriptFile.isArchived()); + params.addValue("dateTime", transcriptFile.getDateTime()); + params.addValue("originalFilename", transcriptFile.getOriginalFilename()); return params; } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java index 672c17d4f..aeb456a5d 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java @@ -11,22 +11,24 @@ public enum SqlTranscriptFileQuery implements BasicSqlQuery ), INSERT_TRANSCRIPT_FILE( "INSERT INTO ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + - "(file_name, processed_date_time, processed_count," + - "pending_processing, archived)" + "\n" + + "(file_name, processed_date_time, processed_count, " + + "pending_processing, archived, date_time, original_filename)" + "\n" + "VALUES (:fileName, :processedDateTime, :processedCount, " + - ":pendingProcessing, :archived)" + ":pendingProcessing, :archived, :dateTime, :originalFilename)" ), UPDATE_TRANSCRIPT_FILE( "UPDATE ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + "SET processed_date_time = :processedDateTime," + " processed_count = :processedCount," + " pending_processing = :pendingProcessing," + - " archived = :archived " + + " archived = :archived," + + " date_time = :dateTime," + + " original_filename = :originalFilename " + "WHERE file_name = :fileName" ), - GET_OLD_FILES( + OLD_FILE_COUNT( "SELECT COUNT(file_name) FROM ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + - "WHERE file_name LIKE ':datetime%'" + "WHERE date_time = :dateTime" ); private String sql; diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java index 7e3dfd71b..f02b2f19d 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java @@ -14,8 +14,8 @@ public enum SqlTranscriptQuery implements BasicSqlQuery ), UPDATE_TRANSCRIPT( "UPDATE ${schema}." + SqlTable.TRANSCRIPT + "\n" + - "SET session_type = :sessionType, date_time = :dateTime, location = :location, text = :text, modified_date_time = :modified_date_time\n" + - "WHERE transcript_filename = :transcriptFilename" + "SET session_type = :sessionType, transcript_filename = :transcriptFilename, location = :location, text = :text, modified_date_time = :modified_date_time\n" + + "WHERE date_time = :dateTime" ), INSERT_TRANSCRIPT( "INSERT INTO ${schema}." + SqlTable.TRANSCRIPT + "\n" + diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java index 1c4e6c616..3a4d664a7 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.FileNotFoundException; +import java.time.LocalDateTime; /** * File containing the raw transcript text @@ -16,13 +17,21 @@ public class TranscriptFile extends BaseSourceData /** Indicates if the underlying 'file' reference has been moved into an archive directory. */ private boolean archived; + /** Saves original filename for use in database. */ + private String originalFilename; + + /** Used to extract the dateTime, then reused later to not repeat processing. */ private Transcript transcript; + /** Used to rename file, and identify unique files. */ + private LocalDateTime dateTime; + /** --- Constructors --- */ public TranscriptFile(File file) throws FileNotFoundException { if (file.exists()) { this.file = file; + this.originalFilename = file.getName(); } else { throw new FileNotFoundException(file.getAbsolutePath()); @@ -45,19 +54,28 @@ public void setFile(File file) { this.file = file; } + public boolean isArchived() { + return archived; + } + + public void setArchived(boolean archived) { + this.archived = archived; + } + + public String getOriginalFilename() { + return originalFilename; + } + public Transcript getTranscript() { return transcript; } public void setTranscript(Transcript transcript) { this.transcript = transcript; + this.dateTime = transcript.getDateTime(); } - public boolean isArchived() { - return archived; - } - - public void setArchived(boolean archived) { - this.archived = archived; + public LocalDateTime getDateTime() { + return dateTime; } } diff --git a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java index 6049f9c1e..350f9da9a 100644 --- a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java @@ -27,7 +27,7 @@ public class TranscriptParser private TranscriptDataService transcriptDataService; public void process(TranscriptFile transcriptFile) throws IOException { - transcriptDataService.saveTranscript(getTranscriptFromFile(transcriptFile), transcriptFile, true); + transcriptDataService.saveTranscript(transcriptFile.getTranscript(), transcriptFile, true); } public Transcript getTranscriptFromFile(TranscriptFile transcriptFile) throws IOException { diff --git a/src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql b/src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql new file mode 100644 index 000000000..3eaf13b8d --- /dev/null +++ b/src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql @@ -0,0 +1,7 @@ +ALTER TABLE master.transcript + DROP CONSTRAINT transcript_transcript_file_fkey; +ALTER TABLE master.transcript_file + ADD COLUMN date_time timestamp; +ALTER TABLE master.transcript_file + ADD CONSTRAINT transcript_transcript_file_fkey FOREIGN KEY (date_time) + REFERENCES master.transcript(date_time); \ No newline at end of file diff --git a/src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql b/src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql new file mode 100644 index 000000000..52a717a47 --- /dev/null +++ b/src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql @@ -0,0 +1,2 @@ +ALTER TABLE master.transcript_file + ADD COLUMN original_filename text; \ No newline at end of file From 9f1f49402aef161acfc091d19749961111fb9380 Mon Sep 17 00:00:00 2001 From: Jacob Keegan Date: Tue, 28 Jan 2020 11:51:00 -0500 Subject: [PATCH 04/29] Fixed bug where old versions weren't being cleared out of Elasticsearch. Enhancement seems to be finished, but need to discuss changes to the public website. --- .../openleg/dao/transcript/SqlFsTranscriptFileDao.java | 1 + .../transcript/search/ElasticTranscriptSearchDao.java | 10 ++++++++++ .../openleg/model/transcript/TranscriptFile.java | 4 ++++ .../openleg/model/transcript/TranscriptId.java | 5 ++--- .../openleg/processor/transcript/TranscriptParser.java | 1 + .../V20200127.0400__another_transcript_update.sql | 2 ++ 6 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index 8095e9df1..63d17852b 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -118,6 +118,7 @@ public TranscriptFile mapRow(ResultSet rs, int i) throws SQLException { try { transcriptFile = new TranscriptFile(file); transcriptFile.setProcessedDateTime(getLocalDateTimeFromRs(rs, "processed_date_time")); + transcriptFile.setOriginalFilename(rs.getString("original_filename")); transcriptFile.setProcessedCount(rs.getInt("processed_count")); transcriptFile.setStagedDateTime(getLocalDateTimeFromRs(rs, "staged_date_time")); transcriptFile.setPendingProcessing(rs.getBoolean("pending_processing")); diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java index 859d59d7d..84855a55f 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java @@ -57,6 +57,16 @@ public void updateTranscriptIndex(Collection transcripts) { .map(t -> getJsonIndexRequest(transcriptIndexName, t.getFilename(), t)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); + // If a transcript was updated, old versions of transcripts need to be removed. + for (Transcript t : transcripts) { + String filename = t.getTranscriptId().getFilename(); + int version = Integer.parseInt(filename.substring(filename.length()-1)); + if (version != 1) { + String oldVersion = String.valueOf(version-1); + String oldFilename = filename.substring(0, filename.length()-1) + oldVersion; + deleteTranscriptFromIndex(new TranscriptId(oldFilename)); + } + } } /** {@inheritDoc} */ diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java index 3a4d664a7..f252df0c2 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptFile.java @@ -66,6 +66,10 @@ public String getOriginalFilename() { return originalFilename; } + public void setOriginalFilename(String originalFilename) { + this.originalFilename = originalFilename; + } + public Transcript getTranscript() { return transcript; } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java index 1a785c7b3..8d8c82ff8 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.time.LocalDateTime; +import java.util.Objects; /** * Used to uniquely identify transcripts. @@ -28,9 +29,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TranscriptId that = (TranscriptId) o; - if (filename != null ? !filename.equals(that.filename) : that.filename != null) return false; - - return true; + return Objects.equals(filename, that.filename); } @Override diff --git a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java index 350f9da9a..2983440e3 100644 --- a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java @@ -27,6 +27,7 @@ public class TranscriptParser private TranscriptDataService transcriptDataService; public void process(TranscriptFile transcriptFile) throws IOException { + transcriptFile.setTranscript(getTranscriptFromFile(transcriptFile)); transcriptDataService.saveTranscript(transcriptFile.getTranscript(), transcriptFile, true); } diff --git a/src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql b/src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql new file mode 100644 index 000000000..e277d9a9c --- /dev/null +++ b/src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql @@ -0,0 +1,2 @@ +ALTER TABLE master.transcript_file + DROP CONSTRAINT transcript_transcript_file_fkey; \ No newline at end of file From d14681344ad5edba1ac183e9d9503d7f88b042e7 Mon Sep 17 00:00:00 2001 From: Jacob Keegan Date: Wed, 29 Jan 2020 10:28:39 -0500 Subject: [PATCH 05/29] Parser can now pull endTimes from the end of the document as well. --- .../openleg/config/StartupHousekeeper.java | 2 +- .../model/hearing/PublicHearingCommittee.java | 8 ++-- .../hearing/PublicHearingDateParser.java | 42 ++++++++----------- .../hearing/PublicHearingParser.java | 7 +++- .../hearing/PublicHearingDateParserTest.java | 32 +++++++------- 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java b/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java index e7d8cba62..a83652431 100644 --- a/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java +++ b/src/main/java/gov/nysenate/openleg/config/StartupHousekeeper.java @@ -24,6 +24,6 @@ public class StartupHousekeeper { */ @EventListener(ContextRefreshedEvent.class) public void onContextRefreshedEvent() { - //eventBus.post(new CacheWarmEvent(Sets.newHashSet(ContentCache.values()))); + eventBus.post(new CacheWarmEvent(Sets.newHashSet(ContentCache.values()))); } } diff --git a/src/main/java/gov/nysenate/openleg/model/hearing/PublicHearingCommittee.java b/src/main/java/gov/nysenate/openleg/model/hearing/PublicHearingCommittee.java index 10a39504d..f99cc660e 100644 --- a/src/main/java/gov/nysenate/openleg/model/hearing/PublicHearingCommittee.java +++ b/src/main/java/gov/nysenate/openleg/model/hearing/PublicHearingCommittee.java @@ -2,6 +2,8 @@ import gov.nysenate.openleg.model.entity.Chamber; +import java.util.Objects; + /** * A Committee, Task Force, or other group * that can hold Public Hearings. Not necessarily a valid @@ -40,10 +42,8 @@ public boolean equals(Object o) { PublicHearingCommittee that = (PublicHearingCommittee) o; - if (chamber != null ? !chamber.equals(that.chamber) : that.chamber != null) return false; - if (name != null ? !name.equals(that.name) : that.name != null) return false; - - return true; + if (!Objects.equals(chamber, that.chamber)) return false; + return Objects.equals(name, that.name); } @Override diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java index 1248581ca..2924211b1 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java @@ -15,11 +15,13 @@ @Service public class PublicHearingDateParser { - private static Pattern START_TIME = Pattern.compile("\\d+:\\d{2} [ap].m."); + private static Pattern TIME = Pattern.compile("\\d+:\\d{2} [ap].m."); private static Pattern DATE_TIME = Pattern.compile("(?\\w+ \\d{1,2}, \\d{4})(( at)? " + - "(?\\d{1,2}:\\d{2} [ap].m.)" + - "( to (?\\d{1,2}:\\d{2} [ap].m.))?)?"); + "(?" + TIME + ")" + + "( to (?" + TIME + "))?)?"); + + private static Pattern END_TIME = Pattern.compile("Whereupon.*(" + TIME +")"); private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MMMM d, yyyy"); @@ -38,27 +40,22 @@ public LocalDate parseDate(List firstPage) { return LocalDate.parse(matcher.group("date"), dateFormatter); } - public LocalTime parseStartTime(List firstPage) { + public LocalTime parseTime(boolean isStartTime, List firstPage) { Matcher matcher = getDateTimeMatcher(firstPage); - matcher.find(); - - String startTime = matcher.group("startTime"); - if (startTime == null) { + if(!matcher.find()) return null; - } - startTime = formatAmPm(startTime); - return LocalTime.parse(startTime, timeFormatter); + String time = matcher.group(isStartTime ? "startTime" : "endTime"); + if (time == null) + return null; + return LocalTime.parse(formatAmPm(time), timeFormatter); } - public LocalTime parseEndTime(List firstPage) { - Matcher matcher = getDateTimeMatcher(firstPage); - matcher.find(); - - String endTime = matcher.group("endTime"); - if (endTime == null) { + public LocalTime alternateParseEndTime(List lastPage) { + String wholePage = String.join("", lastPage); + Matcher matcher = END_TIME.matcher(wholePage); + if (!matcher.find()) return null; - } - endTime = formatAmPm(endTime); + String endTime = formatAmPm(matcher.group(1)); return LocalTime.parse(endTime, timeFormatter); } @@ -97,7 +94,7 @@ private String getDateTimeString(List firstPage) { /** Returns the String containing time information. * If no time exists return null.*/ private String getTimeString(String line) { - if (START_TIME.matcher(line).find()) { + if (TIME.matcher(line).find()) { return line; } return null; @@ -141,10 +138,7 @@ private boolean containsDayOfWeekAndDate(String line) { */ private boolean containsDateAndTime(String line) { String singleLineDate = "(\\w+ \\d+, \\d+)(, at \\d+:\\d+ [apm.]{4})"; - if (line.matches(singleLineDate)) { - return true; - } - return false; + return line.matches(singleLineDate); } /** Removes Line numbers, excess whitespace, new line, and non text characters */ diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java index f35a8d53d..ed0d7739f 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java @@ -52,8 +52,11 @@ public void process(PublicHearingFile publicHearingFile) throws IOException { String title = titleParser.parse(firstPage); String address = addressParser.parse(firstPage); LocalDate date = dateTimeParser.parseDate(firstPage); - LocalTime startTime = dateTimeParser.parseStartTime(firstPage); - LocalTime endTime = dateTimeParser.parseEndTime(firstPage); + LocalTime startTime = dateTimeParser.parseTime(true, firstPage); + LocalTime endTime = dateTimeParser.parseTime(false, firstPage); + if (endTime == null) + endTime = dateTimeParser.alternateParseEndTime(pages.get(pages.size()-1)); + List committees = committeeParser.parse(firstPage); String text = textParser.parse(pages); diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java index 3db30965b..ebfab5637 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java @@ -36,8 +36,8 @@ public void singleDigitHoursParse() throws IOException, URISyntaxException, Pars LocalTime expectedEndTime = LocalTime.of(15, 00); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -55,8 +55,8 @@ public void doubleDigitHoursParse() throws IOException, URISyntaxException, Pars LocalTime expectedEndTime = LocalTime.of(12, 00); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -74,8 +74,8 @@ public void onlyStartTimeGivenParses() throws IOException, URISyntaxException, P LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -93,8 +93,8 @@ public void dateTimeOnSingleLineParses() throws IOException, URISyntaxException, LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -113,8 +113,8 @@ public void noTimeParses() throws IOException, URISyntaxException, ParseExceptio LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -133,8 +133,8 @@ public void invalidCharactersParse() throws IOException, URISyntaxException, Par LocalTime expectedEndTime = LocalTime.of(16, 00); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -152,8 +152,8 @@ public void erroneousCharactersParse() throws IOException, URISyntaxException, P LocalTime expectedEndTime = LocalTime.of(17, 30); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -170,8 +170,8 @@ public void dateTimeLabelsTest() throws IOException, URISyntaxException { LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0)); + LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); + LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); From f81e9b979d9e45759465e60ec3ba8af94cf578a6 Mon Sep 17 00:00:00 2001 From: Jacob Keegan Date: Wed, 29 Jan 2020 16:22:15 -0500 Subject: [PATCH 06/29] All titles are now parsed correctly. --- .../hearing/PublicHearingTitleParser.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java index f985336c3..17699f039 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java @@ -12,14 +12,17 @@ public class PublicHearingTitleParser { private static final Pattern TITLE = Pattern.compile( - "(?" + - "((NEW YORK STATE )?FORUM/TOWN HALL" + - "|PUBLIC (HEARING|FORUM)" + - "|ROUNDTABLE DISCUSSION" + - "|A NEW YORK STATE SENATE HEARING" + - "|NEW YORK STATE \\d{4})" + - ".+?) " + // Title body - "*(?=-{10,})"); // Marks the end of title. + ".*(?<prefix>" + + "(NEW YORK STATE |PUBLIC )?FORUM/TOWN HALL" + + "|PUBLIC (HEARINGS?|FORUM)" + + "|(A )?ROUNDTABLE DISCUSSION" + + "|(A NEW YORK STATE SENATE|JUDICIAL) HEARING" + + "|TO EXAMINE THE ISSUES FACING COMMUNITIES IN THE WAKE" + + "|.*NOMINATION(S:)?" + + "|NEW YORK STATE \\d{4}" + + "|-{10,})[ :]*" + + "(?<title>.+?) " + // Title body + "(-{10,})"); // Marks the end of title. /** * Extracts the PublicHearing title from the first page of the PublicHearingFile. @@ -29,10 +32,13 @@ public class PublicHearingTitleParser public String parse(List<String> firstPage) { String pageText = turnPageIntoString(firstPage); Matcher matchTitle = TITLE.matcher(pageText); - if (!matchTitle.find()) { + if (!matchTitle.find()) return null; - } - return matchTitle.group("title"); + String title = matchTitle.group("title").trim(); + String prefix = matchTitle.group("prefix"); + if (title.matches("^(TO|ON|FOR|OF).*") && prefix != null) + title = prefix + " " + title; + return title; } /** From dbe830422f321e9462509b362c1933b32ac322fa Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Thu, 30 Jan 2020 12:35:11 -0500 Subject: [PATCH 07/29] All addresses are now parsed correctly, and all tests pass. Added prefix back into PublicHearingTitleParser. --- .../processor/hearing/PublicHearingAddressParser.java | 3 +-- .../processor/hearing/PublicHearingTitleParser.java | 9 +++++---- .../processor/hearing/PublicHearingTitleParserTest.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java index 259e12f59..f5e808f29 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java @@ -12,8 +12,7 @@ public class PublicHearingAddressParser { private static final Pattern LAST_ADDRESS_LINE = Pattern.compile( - "^(\\w+( \\w+)?, (New York|NY)( +)?(\\d+)?)|(Cattaraugus County Reservation)$"); - + "^([\\w ,]+ *(New York|NY) *(\\d+)?)|(Cattaraugus County Reservation)$"); private static final int INVALID_INDEX = -1; /** diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java index 17699f039..be8ce601a 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java @@ -13,14 +13,14 @@ public class PublicHearingTitleParser private static final Pattern TITLE = Pattern.compile( ".*(?<prefix>" + - "(NEW YORK STATE |PUBLIC )?FORUM/TOWN HALL" + + "((NEW YORK STATE |PUBLIC )?FORUM/TOWN HALL" + "|PUBLIC (HEARINGS?|FORUM)" + "|(A )?ROUNDTABLE DISCUSSION" + "|(A NEW YORK STATE SENATE|JUDICIAL) HEARING" + "|TO EXAMINE THE ISSUES FACING COMMUNITIES IN THE WAKE" + "|.*NOMINATION(S:)?" + "|NEW YORK STATE \\d{4}" + - "|-{10,})[ :]*" + + "|-{10,})[ :]*)" + "(?<title>.+?) " + // Title body "(-{10,})"); // Marks the end of title. @@ -35,8 +35,9 @@ public String parse(List<String> firstPage) { if (!matchTitle.find()) return null; String title = matchTitle.group("title").trim(); - String prefix = matchTitle.group("prefix"); - if (title.matches("^(TO|ON|FOR|OF).*") && prefix != null) + String prefix = matchTitle.group("prefix").trim(); + // Uncomment this if you want prefixes to be excluded. + //if (title.matches("^(TO|ON|FOR|OF).*") && prefix != null) title = prefix + " " + title; return title; } diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java index 5dbccf852..62b5b0192 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java @@ -50,7 +50,7 @@ public void newYorkStateForumTownHallTitleParses() throws IOException, URISyntax List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( "05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt"); - String expected = "NEW YORK STATE FORUM/TOWN HALL ROUNDTABLE ON THE SAGE COMMISSION'S " + + String expected = "FORUM/TOWN HALL ROUNDTABLE ON THE SAGE COMMISSION'S " + "PROPOSAL TO MERGE THE NYS OFFICE FOR THE AGING WITH THE DEPARTMENT OF HEALTH"; String actual = titleParser.parse(pages.get(0)); assertThat(actual, is(expected)); From efa8794f564566d4c9e97ce9e98954399e1ba0c0 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Fri, 31 Jan 2020 14:28:55 -0500 Subject: [PATCH 08/29] The transcripts API now uses dateTime's as identifiers. Appears to be working on the API calls and UI, testing more now. --- .../view/transcript/TranscriptIdView.java | 8 ++-- .../api/transcript/TranscriptGetCtrl.java | 18 ++++----- .../controller/pdf/TranscriptPdfCtrl.java | 8 ++-- .../transcript/SqlFsTranscriptFileDao.java | 11 ++++- .../dao/transcript/SqlTranscriptDao.java | 15 ++++--- .../transcript/SqlTranscriptFileQuery.java | 4 ++ .../dao/transcript/SqlTranscriptQuery.java | 4 +- .../search/ElasticTranscriptSearchDao.java | 14 +------ .../openleg/model/transcript/Transcript.java | 12 +++++- .../model/transcript/TranscriptId.java | 40 ++++++++++++++----- .../transcript/TranscriptParser.java | 6 +-- .../content/transcript/transcript-list.jsp | 2 +- 12 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java index 0c064bd6c..42183f37c 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java @@ -5,14 +5,14 @@ public class TranscriptIdView implements ViewObject { - protected String filename; + protected String timestamp; public TranscriptIdView(TranscriptId transcriptId) { - this.filename = transcriptId.getFilename(); + this.timestamp = transcriptId.getTimestamp().toString(); } - public String getFilename() { - return filename; + public String getTimestamp() { + return timestamp; } @Override diff --git a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java index 0cfef7408..3216547bb 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java @@ -100,33 +100,33 @@ public BaseResponse getTranscriptsByYear(@PathVariable int year, * Single Transcript Retrieval API * ------------------------------- * - * Retrieve a single transcripts by its filename (GET) /api/3/transcripts/{filename} + * Retrieve a single transcripts by its filename (GET) /api/3/transcripts/{dateTime} * * <p>Request Parameters: None.</p> * * Expected Output: TranscriptView */ - @RequestMapping("/{filename:.*}") - public BaseResponse getTranscript(@PathVariable String filename) { + @RequestMapping("/{dateTime:.*}") + public BaseResponse getTranscript(@PathVariable String dateTime) { return new ViewObjectResponse<>( - new TranscriptView(transcriptData.getTranscript(new TranscriptId(filename))), - "Data for transcript " + filename); + new TranscriptView(transcriptData.getTranscript(new TranscriptId(dateTime))), + "Data for transcript " + dateTime); } /** * Single Transcript PDF retrieval API * ----------------------------------- * - * Retrieve a single transcript text pdf: (GET) /api/3/transcripts/{filename}.pdf + * Retrieve a single transcript text pdf: (GET) /api/3/transcripts/{dateTime}.pdf * * Request Parameters: None. * * Expected Output: PDF response. */ - @RequestMapping("/{filename}.pdf") - public ResponseEntity<byte[]> getTranscriptPdf(@PathVariable String filename) + @RequestMapping("/{dateTime}.pdf") + public ResponseEntity<byte[]> getTranscriptPdf(@PathVariable String dateTime) throws IOException, COSVisitorException { - TranscriptId transcriptId = new TranscriptId(filename); + TranscriptId transcriptId = new TranscriptId(dateTime); Transcript transcript = transcriptData.getTranscript(transcriptId); ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); TranscriptPdfView.writeTranscriptPdf(transcript, pdfBytes); diff --git a/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java b/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java index 7e476dc98..4d81a7247 100644 --- a/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java @@ -34,16 +34,16 @@ public class TranscriptPdfCtrl * Single Transcript PDF retrieval * ------------------------------- * - * Retrieve a single transcript text pdf: (GET) /pdf/transcripts/{filename}/ + * Retrieve a single transcript text pdf: (GET) /pdf/transcripts/{dateTime}/ * * Request Parameters: None. * * Expected Output: PDF response. */ - @RequestMapping("/{filename}") - public ResponseEntity<byte[]> getTranscriptPdf(@PathVariable String filename, HttpServletResponse response) + @RequestMapping("/{dateTime}") + public ResponseEntity<byte[]> getTranscriptPdf(@PathVariable String dateTime, HttpServletResponse response) throws IOException { - TranscriptId transcriptId = new TranscriptId(filename); + TranscriptId transcriptId = new TranscriptId(dateTime); try { Transcript transcript = transcriptData.getTranscript(transcriptId); ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index 63d17852b..f629cd0fb 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -3,6 +3,7 @@ import gov.nysenate.openleg.dao.base.LimitOffset; import gov.nysenate.openleg.dao.base.SqlBaseDao; import gov.nysenate.openleg.model.transcript.TranscriptFile; +import gov.nysenate.openleg.util.DateUtils; import gov.nysenate.openleg.util.FileIOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,6 +17,7 @@ import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -75,7 +77,8 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws File stagedFile = transcriptFile.getFile(); if (stagedFile.getParentFile().compareTo(incomingTranscriptDir) == 0) { String currVersion = Integer.toString(pastVersions(transcriptFile)+1); - String trueName = transcriptFile.getDateTime().toString() + ".v" + currVersion; + LocalDateTime dateTime = transcriptFile.getDateTime(); + String trueName = DateUtils.toDate(dateTime).toString() + ".v" + currVersion; File archiveFile = new File(archiveTranscriptDir, trueName); FileIOUtils.moveFile(stagedFile, archiveFile); transcriptFile.setFile(archiveFile); @@ -88,6 +91,12 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws } } + protected String getFilenameFromOriginalFilename(String originalFilename) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("originalFilename", originalFilename); + return jdbcNamed.queryForObject(GET_RENAMED_FILE.getSql(schema()), params, String.class); + } + /** {@inheritDoc} */ @Override public List<TranscriptFile> getPendingTranscriptFiles(LimitOffset limOff) { diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java index f8c3b7f13..e1d1b565f 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java @@ -6,11 +6,13 @@ import gov.nysenate.openleg.model.transcript.TranscriptFile; import gov.nysenate.openleg.model.transcript.TranscriptId; import gov.nysenate.openleg.model.transcript.TranscriptUpdateToken; +import gov.nysenate.openleg.util.DateUtils; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Repository; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; @@ -23,7 +25,7 @@ public class SqlTranscriptDao extends SqlBaseDao implements TranscriptDao /** {@inheritDoc} */ @Override public List<TranscriptId> getTranscriptIds(SortOrder sortOrder, LimitOffset limOff) { - OrderBy orderBy = new OrderBy("transcript_filename", sortOrder); + OrderBy orderBy = new OrderBy("date_time", sortOrder); return jdbcNamed.query(SELECT_TRANSCRIPT_IDS_BY_YEAR.getSql(schema(), orderBy, limOff), transcriptIdRowMapper); } @@ -69,15 +71,16 @@ private MapSqlParameterSource getTranscriptParams(Transcript transcript, Transcr private MapSqlParameterSource getTranscriptIdParams(TranscriptId transcriptId) { MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("transcriptFilename", transcriptId.getFilename()); + params.addValue("dateTime", transcriptId.getTimestamp()); return params; } /** --- Row Mapper Instances --- */ static RowMapper<Transcript> transcriptRowMapper = (rs, rowNum) -> { - TranscriptId id = new TranscriptId(rs.getString("transcript_filename")); - Transcript transcript = new Transcript(id, rs.getString("session_type"), getLocalDateTimeFromRs(rs, "date_time"), + LocalDateTime dateTime = getLocalDateTimeFromRs(rs, "date_time"); + TranscriptId id = new TranscriptId(dateTime); + Transcript transcript = new Transcript(id, rs.getString("transcript_filename"), rs.getString("session_type"), rs.getString("location"), rs.getString("text")); transcript.setModifiedDateTime(getLocalDateTimeFromRs(rs, "modified_date_time")); transcript.setPublishedDateTime(getLocalDateTimeFromRs(rs, "published_date_time")); @@ -85,10 +88,10 @@ private MapSqlParameterSource getTranscriptIdParams(TranscriptId transcriptId) { }; static RowMapper<TranscriptId> transcriptIdRowMapper = (rs, rowNum) -> - new TranscriptId(rs.getString("transcript_filename")); + new TranscriptId(rs.getString("date_time")); private static RowMapper<TranscriptUpdateToken> transcriptUpdateRowMapper = (rs, rowNum) -> - new TranscriptUpdateToken(new TranscriptId(rs.getString("transcript_filename")), + new TranscriptUpdateToken(new TranscriptId(rs.getString("date_time")), getLocalDateTimeFromRs(rs, "modified_date_time")); } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java index aeb456a5d..ca6a938f8 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java @@ -29,6 +29,10 @@ public enum SqlTranscriptFileQuery implements BasicSqlQuery OLD_FILE_COUNT( "SELECT COUNT(file_name) FROM ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + "WHERE date_time = :dateTime" + ), + GET_RENAMED_FILE( + "SELECT MAX(file_name) FROM ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + + "WHERE original_filename = :originalFilename" ); private String sql; diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java index f02b2f19d..7c1d88428 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java @@ -6,11 +6,11 @@ public enum SqlTranscriptQuery implements BasicSqlQuery { SELECT_TRANSCRIPT_IDS_BY_YEAR( - "SELECT transcript_filename FROM ${schema}." + SqlTable.TRANSCRIPT + "SELECT date_time FROM ${schema}." + SqlTable.TRANSCRIPT ), SELECT_TRANSCRIPT_BY_ID( "SELECT * FROM ${schema}." + SqlTable.TRANSCRIPT + "\n" + - "WHERE transcript_filename = :transcriptFilename" + "WHERE date_time = :dateTime" ), UPDATE_TRANSCRIPT( "UPDATE ${schema}." + SqlTable.TRANSCRIPT + "\n" + diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java index 84855a55f..58a6662ec 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java @@ -54,26 +54,16 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { BulkRequest bulkRequest = new BulkRequest(); transcripts.stream() .map(TranscriptView::new) - .map(t -> getJsonIndexRequest(transcriptIndexName, t.getFilename(), t)) + .map(t -> getJsonIndexRequest(transcriptIndexName, t.getTimestamp(), t)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); - // If a transcript was updated, old versions of transcripts need to be removed. - for (Transcript t : transcripts) { - String filename = t.getTranscriptId().getFilename(); - int version = Integer.parseInt(filename.substring(filename.length()-1)); - if (version != 1) { - String oldVersion = String.valueOf(version-1); - String oldFilename = filename.substring(0, filename.length()-1) + oldVersion; - deleteTranscriptFromIndex(new TranscriptId(oldFilename)); - } - } } /** {@inheritDoc} */ @Override public void deleteTranscriptFromIndex(TranscriptId transcriptId) { if (transcriptId != null) { - deleteEntry(transcriptIndexName, transcriptId.getFilename()); + deleteEntry(transcriptIndexName, transcriptId.getTimestamp().toString()); } } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/Transcript.java b/src/main/java/gov/nysenate/openleg/model/transcript/Transcript.java index b2b3f1a99..93c2b5fe8 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/Transcript.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/Transcript.java @@ -25,12 +25,16 @@ public class Transcript extends BaseLegislativeContent /** The raw text of the transcript. */ private String text; + /** The filename of this transcript. */ + private String filename; + /** --- Constructors --- */ - public Transcript(TranscriptId transcriptId, String sessionType, LocalDateTime dateTime, String location, String text) { + public Transcript(TranscriptId transcriptId, String filename, String sessionType, String location, String text) { this.transcriptId = transcriptId; + this.filename = filename; this.sessionType = sessionType; - this.dateTime = dateTime; + this.dateTime = transcriptId.getDateTime(); this.location = location; this.text = text; this.year = this.dateTime.getYear(); @@ -56,4 +60,8 @@ public String getLocation() { public String getText() { return text; } + + public String getFilename() { + return filename; + } } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java index 8d8c82ff8..a5a91b65e 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java @@ -1,8 +1,11 @@ package gov.nysenate.openleg.model.transcript; import com.google.common.collect.ComparisonChain; +import gov.nysenate.openleg.util.DateUtils; import java.io.Serializable; +import java.sql.Time; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.Objects; @@ -13,13 +16,26 @@ public class TranscriptId implements Serializable, Comparable<TranscriptId> { private static final long serialVersionUID = -6509878885942142022L; - /** The filename which contains the transcript. */ - private String filename; + /** The timestamp which corresponds to the transcript. */ + private Timestamp timestamp; /** --- Constructors --- */ - public TranscriptId(String filename) { - this.filename = filename; + public TranscriptId(Timestamp timestamp) { + this.timestamp = timestamp; + } + + public TranscriptId(LocalDateTime localDateTime) { + this.timestamp = DateUtils.toDate(localDateTime); + } + + public TranscriptId(String time) { + try { + this.timestamp = Timestamp.valueOf(time); + } + catch (IllegalArgumentException e) { + this.timestamp = DateUtils.toDate(LocalDateTime.parse(time)); + } } /** --- Overrides --- */ @@ -29,29 +45,33 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TranscriptId that = (TranscriptId) o; - return Objects.equals(filename, that.filename); + return Objects.equals(timestamp, that.timestamp); } @Override public int hashCode() { - return filename != null ? filename.hashCode() : 0; + return timestamp != null ? timestamp.hashCode() : 0; } @Override public int compareTo(TranscriptId o) { return ComparisonChain.start() - .compare(this.filename, o.filename) + .compare(this.timestamp, o.timestamp) .result(); } @Override public String toString() { - return "Transcript: " + filename; + return "Transcript: " + timestamp; } /** --- Basic Getters/Setters --- */ - public String getFilename() { - return filename; + public Timestamp getTimestamp() { + return timestamp; + } + + public LocalDateTime getDateTime() { + return timestamp.toLocalDateTime(); } } diff --git a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java index 2983440e3..8267963ff 100644 --- a/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/transcript/TranscriptParser.java @@ -90,11 +90,11 @@ public Transcript getTranscriptFromFile(TranscriptFile transcriptFile) throws IO DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMMM d yyyy hmma"); LocalDateTime dateTime = LocalDateTime.parse(date + " " + time, dtf); - TranscriptId transcriptId = new TranscriptId(transcriptFile.getFileName()); - return new Transcript(transcriptId, sessionType, dateTime, location, transcriptText.toString()); + TranscriptId transcriptId = new TranscriptId(dateTime); + return new Transcript(transcriptId, transcriptFile.getFileName(), sessionType, location, transcriptText.toString()); } private boolean areWeDoneWithFirstPage(String sessionType, String location, String date, String time) { - return sessionType != null && location != null && date != null && time !=null; + return sessionType != null && location != null && date != null && time != null; } } diff --git a/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp b/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp index 1b0168cb6..cee15739a 100644 --- a/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp +++ b/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp @@ -56,7 +56,7 @@ total-items="transcriptSearch.paginate.totalItems" current-page="transcriptSearch.paginate.currPage" pagination-id="tx-paginate" - ng-href="${ctxPath}/transcripts/{{transcriptSearch.type}}/{{tx.result.filename}}/" + ng-href="${ctxPath}/transcripts/{{transcriptSearch.type}}/{{tx.result.dateTime}}/" class="result-link transcript-result-link"> <div ng-if="transcriptSearch.type == 'session'"> <h4> From ee0c47ff84f100e06e96c0b1064219855c2b9592 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Fri, 31 Jan 2020 16:31:55 -0500 Subject: [PATCH 09/29] All API and UI calls to get transcripts work correctly, and error out gracefully on bad input. --- .../api/transcript/TranscriptGetCtrl.java | 1 + .../dao/transcript/SqlTranscriptQuery.java | 2 +- .../model/transcript/TranscriptId.java | 23 +++++++++++++++---- .../transcript/TranscriptNotFoundEx.java | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java index 3216547bb..a7ddcbaae 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java @@ -148,6 +148,7 @@ private BaseResponse getTranscriptResponse(boolean summary, boolean full, LimitO @ExceptionHandler(TranscriptNotFoundEx.class) @ResponseStatus(value = HttpStatus.NOT_FOUND) public ErrorResponse handleTranscriptNotFoundEx(TranscriptNotFoundEx ex) { + // TODO: Doesn't display anything on the webpage, even though the response is received. return new ViewObjectErrorResponse(ErrorCode.TRANSCRIPT_NOT_FOUND, new TranscriptIdView(ex.getTranscriptId())); } } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java index 7c1d88428..53ef2fecb 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptQuery.java @@ -23,7 +23,7 @@ public enum SqlTranscriptQuery implements BasicSqlQuery "VALUES (:transcriptFilename, :sessionType, :dateTime, :location, :text)" ), SELECT_TRANSCRIPTS_UPDATED_DURING( - "SELECT transcript_filename, modified_date_time, COUNT(*) OVER() as total_updated " + + "SELECT date_time, modified_date_time, COUNT(*) OVER() as total_updated " + "FROM ${schema}." + SqlTable.TRANSCRIPT + "\n" + "WHERE modified_date_time BETWEEN :startDateTime AND :endDateTime" ); diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java index a5a91b65e..b10109f11 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java @@ -7,6 +7,7 @@ import java.sql.Time; import java.sql.Timestamp; import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; import java.util.Objects; /** @@ -17,25 +18,37 @@ public class TranscriptId implements Serializable, Comparable<TranscriptId> private static final long serialVersionUID = -6509878885942142022L; /** The timestamp which corresponds to the transcript. */ - private Timestamp timestamp; + private Timestamp timestamp = null; + /** The original string passed in to the constructor. Needed to properly + * display error when a time cannot be parsed out.*/ + private String passedIn = ""; /** --- Constructors --- */ public TranscriptId(Timestamp timestamp) { - this.timestamp = timestamp; + this(timestamp.toString()); } public TranscriptId(LocalDateTime localDateTime) { - this.timestamp = DateUtils.toDate(localDateTime); + this(localDateTime.toString()); } public TranscriptId(String time) { + this.passedIn = time; try { this.timestamp = Timestamp.valueOf(time); } catch (IllegalArgumentException e) { - this.timestamp = DateUtils.toDate(LocalDateTime.parse(time)); + try { + this.timestamp = DateUtils.toDate(LocalDateTime.parse(time)); + } + catch (DateTimeParseException e1) { + // The String was not a time at all. + } } + // now() will never match a transcript. + if (this.timestamp == null) + this.timestamp = Timestamp.valueOf(LocalDateTime.now()); } /** --- Overrides --- */ @@ -62,7 +75,7 @@ public int compareTo(TranscriptId o) { @Override public String toString() { - return "Transcript: " + timestamp; + return "Transcript: " + passedIn; } /** --- Basic Getters/Setters --- */ diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java index 2ca9e9548..49b495a31 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java @@ -9,6 +9,7 @@ public class TranscriptNotFoundEx extends RuntimeException public TranscriptNotFoundEx(TranscriptId transcriptId, Exception ex) { super((transcriptId != null) ?"Transcript " + transcriptId.toString() + " could not be retrieved." : "Transcript could not be retrieved since the given TranscriptId was null", ex); + this.transcriptId = transcriptId; } public TranscriptId getTranscriptId() { From 895d92810fcdf3aa99e6d9b1eccf3f9c89a2c428 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Mon, 3 Feb 2020 12:00:51 -0500 Subject: [PATCH 10/29] Cleaned up migration. --- .../client/view/transcript/TranscriptView.java | 3 --- .../dao/transcript/SqlFsTranscriptFileDao.java | 10 +++------- .../dao/transcript/SqlTranscriptFileQuery.java | 4 ---- .../V20200124.0512__transcript_update_fix.sql | 4 ---- .../V20200127.0316__transcript_table_changes.sql | 7 ------- .../V20200127.0338__transcript_file_changes.sql | 2 -- .../V20200127.0400__another_transcript_update.sql | 2 -- .../V20200203.1102__transcript_changes.sql | 15 +++++++++++++++ 8 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql delete mode 100644 src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql delete mode 100644 src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql delete mode 100644 src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql create mode 100644 src/main/resources/sql/migrations/V20200203.1102__transcript_changes.sql diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptView.java index f9f262b4f..3fa4ed23e 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptView.java @@ -1,9 +1,6 @@ package gov.nysenate.openleg.client.view.transcript; import gov.nysenate.openleg.model.transcript.Transcript; -import gov.nysenate.openleg.model.transcript.TranscriptId; - -import java.time.LocalDateTime; public class TranscriptView extends TranscriptInfoView { diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index f629cd0fb..f5c6483c6 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -19,6 +19,7 @@ import java.sql.SQLException; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import static gov.nysenate.openleg.dao.transcript.SqlTranscriptFileQuery.*; @@ -53,6 +54,7 @@ public List<TranscriptFile> getIncomingTranscriptFiles(LimitOffset limOff) throw for (File file : files) { transcriptFiles.add(new TranscriptFile(file)); } + transcriptFiles.sort(Comparator.comparing(TranscriptFile::getFileName)); return transcriptFiles; } @@ -78,7 +80,7 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws if (stagedFile.getParentFile().compareTo(incomingTranscriptDir) == 0) { String currVersion = Integer.toString(pastVersions(transcriptFile)+1); LocalDateTime dateTime = transcriptFile.getDateTime(); - String trueName = DateUtils.toDate(dateTime).toString() + ".v" + currVersion; + String trueName = DateUtils.toDate(dateTime).toString().replaceAll(":", ";") + ".v" + currVersion; File archiveFile = new File(archiveTranscriptDir, trueName); FileIOUtils.moveFile(stagedFile, archiveFile); transcriptFile.setFile(archiveFile); @@ -91,12 +93,6 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws } } - protected String getFilenameFromOriginalFilename(String originalFilename) { - MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("originalFilename", originalFilename); - return jdbcNamed.queryForObject(GET_RENAMED_FILE.getSql(schema()), params, String.class); - } - /** {@inheritDoc} */ @Override public List<TranscriptFile> getPendingTranscriptFiles(LimitOffset limOff) { diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java index ca6a938f8..aeb456a5d 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptFileQuery.java @@ -29,10 +29,6 @@ public enum SqlTranscriptFileQuery implements BasicSqlQuery OLD_FILE_COUNT( "SELECT COUNT(file_name) FROM ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + "WHERE date_time = :dateTime" - ), - GET_RENAMED_FILE( - "SELECT MAX(file_name) FROM ${schema}." + SqlTable.TRANSCRIPT_FILE + "\n" + - "WHERE original_filename = :originalFilename" ); private String sql; diff --git a/src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql b/src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql deleted file mode 100644 index 1f0ee8dc4..000000000 --- a/src/main/resources/sql/migrations/V20200124.0512__transcript_update_fix.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE master.transcript - DROP CONSTRAINT transcript_pkey; -ALTER TABLE master.transcript - ADD PRIMARY KEY (date_time); \ No newline at end of file diff --git a/src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql b/src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql deleted file mode 100644 index 3eaf13b8d..000000000 --- a/src/main/resources/sql/migrations/V20200127.0316__transcript_table_changes.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE master.transcript - DROP CONSTRAINT transcript_transcript_file_fkey; -ALTER TABLE master.transcript_file - ADD COLUMN date_time timestamp; -ALTER TABLE master.transcript_file - ADD CONSTRAINT transcript_transcript_file_fkey FOREIGN KEY (date_time) - REFERENCES master.transcript(date_time); \ No newline at end of file diff --git a/src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql b/src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql deleted file mode 100644 index 52a717a47..000000000 --- a/src/main/resources/sql/migrations/V20200127.0338__transcript_file_changes.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE master.transcript_file - ADD COLUMN original_filename text; \ No newline at end of file diff --git a/src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql b/src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql deleted file mode 100644 index e277d9a9c..000000000 --- a/src/main/resources/sql/migrations/V20200127.0400__another_transcript_update.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE master.transcript_file - DROP CONSTRAINT transcript_transcript_file_fkey; \ No newline at end of file diff --git a/src/main/resources/sql/migrations/V20200203.1102__transcript_changes.sql b/src/main/resources/sql/migrations/V20200203.1102__transcript_changes.sql new file mode 100644 index 000000000..c8fc118a9 --- /dev/null +++ b/src/main/resources/sql/migrations/V20200203.1102__transcript_changes.sql @@ -0,0 +1,15 @@ +ALTER TABLE master.transcript + DROP CONSTRAINT transcript_pkey; +ALTER TABLE master.transcript + ADD PRIMARY KEY (date_time); + +ALTER TABLE master.transcript + DROP CONSTRAINT IF EXISTS transcript_transcript_file_fkey; +ALTER TABLE master.transcript + ADD CONSTRAINT transcript_transcript_file_fkey FOREIGN KEY (transcript_filename) + REFERENCES master.transcript_file(file_name); + +ALTER TABLE master.transcript_file + ADD COLUMN IF NOT EXISTS date_time timestamp; +ALTER TABLE master.transcript_file + ADD COLUMN IF NOT EXISTS original_filename text; \ No newline at end of file From 726513cebfeecd6e5f23a8ac0938c823b5544644 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Fri, 7 Feb 2020 15:52:21 -0500 Subject: [PATCH 11/29] Made some changes based on Kevin's suggestions. Most importantly, TranscriptId now uses a LocalDateTime instead of a Timestamp. --- .../view/transcript/TranscriptIdView.java | 8 ++-- .../transcript/SqlFsTranscriptFileDao.java | 3 +- .../dao/transcript/SqlTranscriptDao.java | 3 +- .../search/ElasticTranscriptSearchDao.java | 4 +- .../model/transcript/TranscriptId.java | 41 ++++++------------- .../transcript/TranscriptNotFoundEx.java | 2 +- .../hearing/PublicHearingDateParser.java | 13 +++++- .../hearing/PublicHearingParser.java | 7 ++-- .../hearing/PublicHearingDateParserTest.java | 40 +++++++++--------- 9 files changed, 56 insertions(+), 65 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java index 42183f37c..3ea8eaf0d 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java @@ -5,14 +5,14 @@ public class TranscriptIdView implements ViewObject { - protected String timestamp; + protected String localDateTime; public TranscriptIdView(TranscriptId transcriptId) { - this.timestamp = transcriptId.getTimestamp().toString(); + this.localDateTime = transcriptId.getLocalDateTime().toString(); } - public String getTimestamp() { - return timestamp; + public String getLocalDateTime() { + return localDateTime; } @Override diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index f5c6483c6..83008adf3 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -80,7 +80,8 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws if (stagedFile.getParentFile().compareTo(incomingTranscriptDir) == 0) { String currVersion = Integer.toString(pastVersions(transcriptFile)+1); LocalDateTime dateTime = transcriptFile.getDateTime(); - String trueName = DateUtils.toDate(dateTime).toString().replaceAll(":", ";") + ".v" + currVersion; + // Since Windows has issues dealing with filenames with : in it, we'll replace it with ; to archive it. + String trueName = dateTime.toString().replaceAll(":", ";") + ".v" + currVersion; File archiveFile = new File(archiveTranscriptDir, trueName); FileIOUtils.moveFile(stagedFile, archiveFile); transcriptFile.setFile(archiveFile); diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java index e1d1b565f..2b22aa099 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java @@ -12,7 +12,6 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Repository; -import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.List; @@ -71,7 +70,7 @@ private MapSqlParameterSource getTranscriptParams(Transcript transcript, Transcr private MapSqlParameterSource getTranscriptIdParams(TranscriptId transcriptId) { MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("dateTime", transcriptId.getTimestamp()); + params.addValue("dateTime", DateUtils.toDate(transcriptId.getLocalDateTime())); return params; } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java index 58a6662ec..94b3eb06f 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java @@ -54,7 +54,7 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { BulkRequest bulkRequest = new BulkRequest(); transcripts.stream() .map(TranscriptView::new) - .map(t -> getJsonIndexRequest(transcriptIndexName, t.getTimestamp(), t)) + .map(t -> getJsonIndexRequest(transcriptIndexName, t.getLocalDateTime(), t)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); } @@ -63,7 +63,7 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { @Override public void deleteTranscriptFromIndex(TranscriptId transcriptId) { if (transcriptId != null) { - deleteEntry(transcriptIndexName, transcriptId.getTimestamp().toString()); + deleteEntry(transcriptIndexName, transcriptId.getLocalDateTime().toString()); } } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java index b10109f11..f6f4884fc 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java @@ -1,10 +1,8 @@ package gov.nysenate.openleg.model.transcript; import com.google.common.collect.ComparisonChain; -import gov.nysenate.openleg.util.DateUtils; import java.io.Serializable; -import java.sql.Time; import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.format.DateTimeParseException; @@ -18,37 +16,22 @@ public class TranscriptId implements Serializable, Comparable<TranscriptId> private static final long serialVersionUID = -6509878885942142022L; /** The timestamp which corresponds to the transcript. */ - private Timestamp timestamp = null; - /** The original string passed in to the constructor. Needed to properly - * display error when a time cannot be parsed out.*/ - private String passedIn = ""; + private LocalDateTime localDateTime; /** --- Constructors --- */ - public TranscriptId(Timestamp timestamp) { - this(timestamp.toString()); - } - public TranscriptId(LocalDateTime localDateTime) { this(localDateTime.toString()); } public TranscriptId(String time) { - this.passedIn = time; try { - this.timestamp = Timestamp.valueOf(time); + this.localDateTime = LocalDateTime.parse(time); } - catch (IllegalArgumentException e) { - try { - this.timestamp = DateUtils.toDate(LocalDateTime.parse(time)); - } - catch (DateTimeParseException e1) { - // The String was not a time at all. - } + catch (DateTimeParseException e) { + // The time may be the String format of a Timestamp. + this.localDateTime = Timestamp.valueOf(time).toLocalDateTime(); } - // now() will never match a transcript. - if (this.timestamp == null) - this.timestamp = Timestamp.valueOf(LocalDateTime.now()); } /** --- Overrides --- */ @@ -58,33 +41,33 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TranscriptId that = (TranscriptId) o; - return Objects.equals(timestamp, that.timestamp); + return Objects.equals(localDateTime, that.localDateTime); } @Override public int hashCode() { - return timestamp != null ? timestamp.hashCode() : 0; + return localDateTime != null ? localDateTime.hashCode() : 0; } @Override public int compareTo(TranscriptId o) { return ComparisonChain.start() - .compare(this.timestamp, o.timestamp) + .compare(this.localDateTime, o.localDateTime) .result(); } @Override public String toString() { - return "Transcript: " + passedIn; + return "Transcript " + localDateTime; } /** --- Basic Getters/Setters --- */ - public Timestamp getTimestamp() { - return timestamp; + public LocalDateTime getLocalDateTime() { + return localDateTime; } public LocalDateTime getDateTime() { - return timestamp.toLocalDateTime(); + return localDateTime; } } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java index 49b495a31..39f931e22 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptNotFoundEx.java @@ -7,7 +7,7 @@ public class TranscriptNotFoundEx extends RuntimeException protected TranscriptId transcriptId; public TranscriptNotFoundEx(TranscriptId transcriptId, Exception ex) { - super((transcriptId != null) ?"Transcript " + transcriptId.toString() + " could not be retrieved." + super((transcriptId != null) ? "Transcript " + transcriptId.toString() + " could not be retrieved." : "Transcript could not be retrieved since the given TranscriptId was null", ex); this.transcriptId = transcriptId; } diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java index 2924211b1..77ed2f371 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java @@ -40,7 +40,16 @@ public LocalDate parseDate(List<String> firstPage) { return LocalDate.parse(matcher.group("date"), dateFormatter); } - public LocalTime parseTime(boolean isStartTime, List<String> firstPage) { + public LocalTime parseStartTime(List<String> firstPage) { + return parseTime(true, firstPage); + } + + public LocalTime parseEndTime(List<String> firstPage, List<String> lastPage) { + LocalTime t = parseTime(false, firstPage); + return t == null ? alternateParseEndTime(lastPage):t; + } + + private LocalTime parseTime(boolean isStartTime, List<String> firstPage) { Matcher matcher = getDateTimeMatcher(firstPage); if(!matcher.find()) return null; @@ -50,7 +59,7 @@ public LocalTime parseTime(boolean isStartTime, List<String> firstPage) { return LocalTime.parse(formatAmPm(time), timeFormatter); } - public LocalTime alternateParseEndTime(List<String> lastPage) { + private LocalTime alternateParseEndTime(List<String> lastPage) { String wholePage = String.join("", lastPage); Matcher matcher = END_TIME.matcher(wholePage); if (!matcher.find()) diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java index ed0d7739f..9047ccd30 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingParser.java @@ -48,14 +48,13 @@ public void process(PublicHearingFile publicHearingFile) throws IOException { final List<List<String>> pages = PublicHearingTextUtils.getPages( FileUtils.readFileToString(publicHearingFile.getFile(), Charset.defaultCharset())); final List<String> firstPage = pages.get(0); + final List<String> lastPage = pages.get(pages.size()-1); String title = titleParser.parse(firstPage); String address = addressParser.parse(firstPage); LocalDate date = dateTimeParser.parseDate(firstPage); - LocalTime startTime = dateTimeParser.parseTime(true, firstPage); - LocalTime endTime = dateTimeParser.parseTime(false, firstPage); - if (endTime == null) - endTime = dateTimeParser.alternateParseEndTime(pages.get(pages.size()-1)); + LocalTime startTime = dateTimeParser.parseStartTime(firstPage); + LocalTime endTime = dateTimeParser.parseEndTime(firstPage, lastPage); List<PublicHearingCommittee> committees = committeeParser.parse(firstPage); String text = textParser.parse(pages); diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java index ebfab5637..2f808d842 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java @@ -32,12 +32,12 @@ public void singleDigitHoursParse() throws IOException, URISyntaxException, Pars "06-04-14 NYsenate Heroin-Opioid Addiction Special Task Force_Seneca Nation_FINAL.txt"); LocalDate expectedDate = LocalDate.of(2014, 6, 4); - LocalTime expectedStartTime = LocalTime.of(13, 00); - LocalTime expectedEndTime = LocalTime.of(15, 00); + LocalTime expectedStartTime = LocalTime.of(13, 0); + LocalTime expectedEndTime = LocalTime.of(15, 0); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -51,12 +51,12 @@ public void doubleDigitHoursParse() throws IOException, URISyntaxException, Pars "06-02-14 NYsenate_Labor_Savino_FINAL.txt"); LocalDate expectedDate = LocalDate.of(2014, 6, 2); - LocalTime expectedStartTime = LocalTime.of(10, 00); - LocalTime expectedEndTime = LocalTime.of(12, 00); + LocalTime expectedStartTime = LocalTime.of(10, 0); + LocalTime expectedEndTime = LocalTime.of(12, 0); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -74,8 +74,8 @@ public void onlyStartTimeGivenParses() throws IOException, URISyntaxException, P LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -93,8 +93,8 @@ public void dateTimeOnSingleLineParses() throws IOException, URISyntaxException, LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -113,8 +113,8 @@ public void noTimeParses() throws IOException, URISyntaxException, ParseExceptio LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -133,8 +133,8 @@ public void invalidCharactersParse() throws IOException, URISyntaxException, Par LocalTime expectedEndTime = LocalTime.of(16, 00); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -152,8 +152,8 @@ public void erroneousCharactersParse() throws IOException, URISyntaxException, P LocalTime expectedEndTime = LocalTime.of(17, 30); LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); @@ -170,8 +170,8 @@ public void dateTimeLabelsTest() throws IOException, URISyntaxException { LocalTime expectedEndTime = null; LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseTime(true, pages.get(0)); - LocalTime actualEndTime = dateParser.parseTime(false, pages.get(0)); + LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); + LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); assertThat(actualDate, is(expectedDate)); assertThat(actualStartTime, is(expectedStartTime)); From e866a5f9f75561e4a37185a5a993a9192ff27dbb Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Mon, 10 Feb 2020 16:46:15 -0500 Subject: [PATCH 12/29] Added Public Hearing tests based on my changes. Also improved some parsers. Fixed a bug that made it impossible to access hearings through the UI. --- .../hearing/PublicHearingAddressParser.java | 14 +- .../hearing/PublicHearingDateParser.java | 57 +- .../hearing/PublicHearingTitleParser.java | 26 +- .../content/transcript/transcript-list.jsp | 2 +- .../openleg/model/bill/BillVersionTest.java | 20 +- .../processor/bill/BillActionParserTest.java | 19 +- .../PublicHearingAddressParserTest.java | 77 +- .../hearing/PublicHearingDateParserTest.java | 145 +- .../hearing/PublicHearingTestHelper.java | 4 +- .../hearing/PublicHearingTitleParserTest.java | 74 +- .../processor/law/LawTitleParserTest.java | 2 +- ...6 NYS Task Force Heroin_Penn Yan Final.txt | 10717 ++++++++++++ ...YS Senate Flooding_IJC Plan 2014 FINAL.txt | 14080 ++++++++++++++++ ...enate Hudson River Barge Hearing FINAL.txt | 8152 +++++++++ 14 files changed, 33115 insertions(+), 274 deletions(-) create mode 100644 src/test/resources/hearing/02-23-2016 NYS Task Force Heroin_Penn Yan Final.txt create mode 100644 src/test/resources/hearing/10-10-17 NYS Senate Flooding_IJC Plan 2014 FINAL.txt create mode 100644 src/test/resources/hearing/10-19-16 NYS Senate Hudson River Barge Hearing FINAL.txt diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java index f5e808f29..32fbd1c90 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParser.java @@ -12,13 +12,13 @@ public class PublicHearingAddressParser { private static final Pattern LAST_ADDRESS_LINE = Pattern.compile( - "^([\\w ,]+ *(New York|NY) *(\\d+)?)|(Cattaraugus County Reservation)$"); + "^([\\w ,-]+ +(New York(, )?|NY){1,2}[\\d -]*)$|(Cattaraugus County Reservation)$"); private static final int INVALID_INDEX = -1; /** * Extract the Address information from the first page of a PublicHearing. - * @param firstPage - * @return + * @param firstPage of the hearing. + * @return the address where the hearing took place. */ public String parse(List<String> firstPage) { String address = null; @@ -37,15 +37,15 @@ private String extractAddress(List<String> firstPage, int lastLineOfAddress) { return null; } - String address = ""; + StringBuilder address = new StringBuilder(); for (int i = firstLineOfAddress; i <= lastLineOfAddress; i++) { - address += PublicHearingTextUtils.stripLineNumber(firstPage.get(i)); + address.append(PublicHearingTextUtils.stripLineNumber(firstPage.get(i))); // Keep address multiline format. if (i != lastLineOfAddress) { - address += "\n"; + address.append("\n"); } } - return address; + return address.toString(); } /** Always a blank line before address info */ diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java index 77ed2f371..903754c84 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParser.java @@ -15,19 +15,24 @@ @Service public class PublicHearingDateParser { - private static Pattern TIME = Pattern.compile("\\d+:\\d{2} [ap].m."); + private final static String AM_PM = "(a.m.|p.m.)"; - private static Pattern DATE_TIME = Pattern.compile("(?<date>\\w+ \\d{1,2}, \\d{4})(( at)? " + + private final static Pattern TIME = Pattern.compile("\\d+:\\d{2} " + AM_PM); + + private final static Pattern DATE_TIME = Pattern.compile("(?<date>\\w+ \\d{1,2}, \\d{4})(( at)? " + "(?<startTime>" + TIME + ")" + "( to (?<endTime>" + TIME + "))?)?"); - private static Pattern END_TIME = Pattern.compile("Whereupon.*(" + TIME +")"); + private final static Pattern END_TIME = Pattern.compile("Whereupon[^\\d]+(" + TIME +")"); + + private final static String DATE_FORMATTER = "MMMM d, yyyy"; + + private final static DateTimeFormatter DAY_OF_WEEK_DATE_FORMATTER = DateTimeFormatter.ofPattern("EEEE, " + DATE_FORMATTER); - private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MMMM d, yyyy"); + private final static DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("h:mm a"); - private DateTimeFormatter dayOfWeekDateFormatter = DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy"); - private DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("h:mm a"); + private final static String SINGLE_LINE_DATE = "(\\w+ \\d+, \\d+)(, at \\d+:\\d+ " + AM_PM + ")"; /** * Extract a LocalDate from the first page of a PublicHearing. @@ -36,8 +41,9 @@ public class PublicHearingDateParser */ public LocalDate parseDate(List<String> firstPage) { Matcher matcher = getDateTimeMatcher(firstPage); - matcher.find(); - return LocalDate.parse(matcher.group("date"), dateFormatter); + if (!matcher.find()) + return null; + return LocalDate.parse(matcher.group("date"), DateTimeFormatter.ofPattern(DATE_FORMATTER)); } public LocalTime parseStartTime(List<String> firstPage) { @@ -53,10 +59,10 @@ private LocalTime parseTime(boolean isStartTime, List<String> firstPage) { Matcher matcher = getDateTimeMatcher(firstPage); if(!matcher.find()) return null; - String time = matcher.group(isStartTime ? "startTime" : "endTime"); + String time = formatAmPm(matcher.group(isStartTime ? "startTime" : "endTime")); if (time == null) return null; - return LocalTime.parse(formatAmPm(time), timeFormatter); + return LocalTime.parse(time, TIME_FORMATTER); } private LocalTime alternateParseEndTime(List<String> lastPage) { @@ -65,7 +71,9 @@ private LocalTime alternateParseEndTime(List<String> lastPage) { if (!matcher.find()) return null; String endTime = formatAmPm(matcher.group(1)); - return LocalTime.parse(endTime, timeFormatter); + if (endTime == null) + return null; + return LocalTime.parse(endTime, TIME_FORMATTER); } private Matcher getDateTimeMatcher(List<String> firstPage) { @@ -77,7 +85,7 @@ private Matcher getDateTimeMatcher(List<String> firstPage) { * Finds the Strings containing date and time information. * Concatenates these Strings into * a "<code>MMMM d, yyyy h:mm a to h:mm a</code>" formatted single String. - * @param firstPage + * @param firstPage of the hearing. * @return A String containing date time information. */ private String getDateTimeString(List<String> firstPage) { @@ -112,18 +120,17 @@ private String getTimeString(String line) { /** * Determines if the given String contains date time information. * Matches date Strings like: April 5, 2014. - * @param line - * @return + * @param line to check. + * @return if it contians a date. */ private boolean containsDate(String line) { try { - dateFormatter.parse(line); + DateTimeFormatter.ofPattern(DATE_FORMATTER).parse(line); return true; } catch (DateTimeParseException ex) { - // Ignore + return false; } - return false; } /** @@ -132,13 +139,12 @@ private boolean containsDate(String line) { */ private boolean containsDayOfWeekAndDate(String line) { try { - dayOfWeekDateFormatter.parse(line); + DAY_OF_WEEK_DATE_FORMATTER.parse(line); return true; } catch (DateTimeParseException ex) { - // Ignore + return false; } - return false; } /** @@ -146,8 +152,7 @@ private boolean containsDayOfWeekAndDate(String line) { * Matches date Strings like: March 12, 2014, at 10:00 a.m. */ private boolean containsDateAndTime(String line) { - String singleLineDate = "(\\w+ \\d+, \\d+)(, at \\d+:\\d+ [apm.]{4})"; - return line.matches(singleLineDate); + return line.matches(SINGLE_LINE_DATE); } /** Removes Line numbers, excess whitespace, new line, and non text characters */ @@ -186,9 +191,11 @@ private String removeDateTimeLabels(String line) { /** Capitalize a.m./p.m and remove the all '.' characters. */ private String formatAmPm(String dateTime) { - final Pattern AM_PM = Pattern.compile("(a.m.|p.m.)"); - Matcher matcher = AM_PM.matcher(dateTime); - matcher.find(); + if (dateTime == null) + return null; + Matcher matcher = Pattern.compile(AM_PM).matcher(dateTime); + if (!matcher.find()) + return null; String capitalized = matcher.group(1).toUpperCase(); return matcher.replaceFirst(capitalized).replaceAll("\\.", ""); } diff --git a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java index be8ce601a..a3512eace 100644 --- a/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParser.java @@ -24,16 +24,28 @@ public class PublicHearingTitleParser "(?<title>.+?) " + // Title body "(-{10,})"); // Marks the end of title. + + +static final Pattern oold_title = Pattern.compile("(?<title>" + + "((NEW YORK STATE )?FORUM/TOWN HALL" + + "|PUBLIC (HEARING|FORUM)" + + "|ROUNDTABLE DISCUSSION" + + "|A NEW YORK STATE SENATE HEARING" + + "|NEW YORK STATE \\d{4})" + + ".+?) " + // Title body + "*(?=-{10,})"); // Marks the end of title.) + /** * Extracts the PublicHearing title from the first page of the PublicHearingFile. - * @param firstPage - * @return + * @param firstPage of the hearing. + * @return the title. */ public String parse(List<String> firstPage) { String pageText = turnPageIntoString(firstPage); Matcher matchTitle = TITLE.matcher(pageText); if (!matchTitle.find()) return null; + String title = matchTitle.group("title").trim(); String prefix = matchTitle.group("prefix").trim(); // Uncomment this if you want prefixes to be excluded. @@ -45,16 +57,16 @@ public String parse(List<String> firstPage) { /** * Turns a list of String's into a single String with * whitespace and line numbers removed. - * @param firstPage - * @return + * @param firstPage of the hearing. + * @return a single string of the first page. */ private String turnPageIntoString(List<String> firstPage) { - String pageText = ""; + StringBuilder pageText = new StringBuilder(); for (String line : firstPage) { if (PublicHearingTextUtils.hasContent(line)) { - pageText += " " + PublicHearingTextUtils.stripLineNumber(line); + pageText.append(" ").append(PublicHearingTextUtils.stripLineNumber(line)); } } - return pageText; + return pageText.toString(); } } diff --git a/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp b/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp index cee15739a..024f9e4df 100644 --- a/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp +++ b/src/main/webapp/WEB-INF/view/partial/content/transcript/transcript-list.jsp @@ -56,7 +56,7 @@ total-items="transcriptSearch.paginate.totalItems" current-page="transcriptSearch.paginate.currPage" pagination-id="tx-paginate" - ng-href="${ctxPath}/transcripts/{{transcriptSearch.type}}/{{tx.result.dateTime}}/" + ng-href="${ctxPath}/transcripts/{{transcriptSearch.type}}/{{(transcriptSearch.type == 'session') ? tx.result.dateTime : tx.result.filename}}/" class="result-link transcript-result-link"> <div ng-if="transcriptSearch.type == 'session'"> <h4> diff --git a/src/test/java/gov/nysenate/openleg/model/bill/BillVersionTest.java b/src/test/java/gov/nysenate/openleg/model/bill/BillVersionTest.java index 89a7af424..f7aa458d8 100644 --- a/src/test/java/gov/nysenate/openleg/model/bill/BillVersionTest.java +++ b/src/test/java/gov/nysenate/openleg/model/bill/BillVersionTest.java @@ -4,8 +4,6 @@ import gov.nysenate.openleg.model.base.Version; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Collections; @@ -16,22 +14,20 @@ @Category(UnitTest.class) public class BillVersionTest { - private static final Logger logger = LoggerFactory.getLogger(BillVersionTest.class); - @Test - public void testToString() throws Exception { + public void testToString() { assertEquals("", Version.of(" ").toString()); assertEquals("A", Version.of("A ").toString()); } @Test - public void testName() throws Exception { + public void testName() { assertEquals("ORIGINAL", Version.of(" ").name()); assertEquals("A", Version.of("A").name()); } @Test - public void testOf_WhitespaceReturnsDefault() throws Exception { + public void testOf_WhitespaceReturnsDefault() { String s1 = ""; String s2 = " "; String s3 = " "; @@ -43,7 +39,7 @@ public void testOf_WhitespaceReturnsDefault() throws Exception { } @Test - public void testGetValue_Succeeds() throws Exception { + public void testGetValue_Succeeds() { String s1 = "a"; String s2 = "A "; String s3 = " z"; @@ -55,13 +51,13 @@ public void testGetValue_Succeeds() throws Exception { } @Test(expected = IllegalArgumentException.class) - public void testGetValue_Fails() throws Exception { + public void testGetValue_Fails() { String invalid = "z1"; Version.of(invalid); } @Test - public void testComparable() throws Exception { + public void testComparable() { List<Version> versions = Arrays.asList(Version.Z, Version.B, Version.ORIGINAL, Version.D); Collections.sort(versions); List<Version> sorted = Arrays.asList(Version.ORIGINAL, Version.B, Version.D, Version.Z); @@ -69,7 +65,7 @@ public void testComparable() throws Exception { } @Test - public void testGetVersionsBefore() throws Exception { - logger.info("{}", Version.before(Version.A).get(0).name()); + public void testGetVersionsBefore() { + assertEquals(Version.before(Version.A).get(0), Version.ORIGINAL); } } diff --git a/src/test/java/gov/nysenate/openleg/processor/bill/BillActionParserTest.java b/src/test/java/gov/nysenate/openleg/processor/bill/BillActionParserTest.java index 343f81c1d..4a47eb5b5 100644 --- a/src/test/java/gov/nysenate/openleg/processor/bill/BillActionParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/bill/BillActionParserTest.java @@ -1,17 +1,12 @@ package gov.nysenate.openleg.processor.bill; -import com.mchange.v2.cfg.PropertiesConfigSource; import gov.nysenate.openleg.annotation.UnitTest; import gov.nysenate.openleg.model.bill.BillAction; import gov.nysenate.openleg.model.bill.BillId; import gov.nysenate.openleg.processor.base.ParseError; -import gov.nysenate.openleg.service.bill.data.BillDataService; import gov.nysenate.openleg.util.XmlHelper; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.xml.sax.SAXException; import org.w3c.dom.Node; @@ -22,9 +17,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; @Category(UnitTest.class) public class BillActionParserTest @@ -40,12 +32,9 @@ public class BillActionParserTest } } - private static final Logger logger = LoggerFactory.getLogger(BillActionParserTest.class); - @Test public void billActionParser() { List<BillAction> actions = BillActionParser.parseActionsList(new BillId("S3664B", 2013), actionsList1); - logger.info("{}", actions); // counts items assertEquals(actions.size(), actionsList1.split("\n").length); @@ -58,14 +47,12 @@ public void billActionParser() { public void testBillActionParserXML() throws IOException, SAXException, XPathExpressionException { Node actionsNode = xmlHelper.getNode("actions", xmlHelper.parse(actionsXml1)); List<BillAction> actions = BillActionParser.parseActionsListXML(new BillId("S3664B", 2013), actionsNode); - logger.info("{}", actions); // first two items are different assertNotEquals(actions.get(0), actions.get(1)); // compare to the old method of parsing List<BillAction> actionsOldMethod = BillActionParser.parseActionsList(new BillId("S3664B", 2013), actionsList2); - logger.info("{}", actions); // counts items assertEquals(actionsOldMethod.size(), actions.size()); @@ -78,14 +65,12 @@ public void testBillActionParserXML() throws IOException, SAXException, XPathExp public void billActionParserXMLMissingData() throws IOException, SAXException, XPathExpressionException { Node actionsNode = xmlHelper.getNode("actions", xmlHelper.parse(actionsXml2)); List<BillAction> actions = BillActionParser.parseActionsListXML(new BillId("S3664B", 2013), actionsNode);// throws here - logger.info("{}", actions); } @Test (expected = Exception.class) public void billActionParserXMLCorrupted() throws IOException, SAXException, XPathExpressionException { Node actionsNode = xmlHelper.getNode("actions", xmlHelper.parse(actionsXml3));// throws here List<BillAction> actions = BillActionParser.parseActionsListXML(new BillId("S3664B", 2013), actionsNode); - logger.info("{}", actions); } @Test @@ -336,7 +321,7 @@ public void printAction() { "<action no=\"2\">" + "<actionbillamd></actionbillamd>" + "<actionhouse>S</actionhouse>" + - "<actioncode>9<actioncode>" + + "<actioncode>9</actioncode>" + "<actiontype>S</actiontype>" + "<actiondata>27</actiondata>" + "<actiondataamd></actiondataamd>" + @@ -352,7 +337,7 @@ public void printAction() { "<actionhouse>S</actionhouse>" + "<actioncode>17</actioncode>" + "<actiontype>F</actiontype>" + - "<actiondata>2008<actiondata>" + + "<actiondata>2008</actiondata>" + "<actiondataamd></actiondataamd>" + "<actiondate>2018-06-19</actiondate>" + "<actiondesc>" + diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParserTest.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParserTest.java index 259d80c05..da1479bd6 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingAddressParserTest.java @@ -25,78 +25,63 @@ public void setup() { @Test public void basicAddressParses() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "01-17-13 NYSenateHearing_Marcellino_Final.txt"); - - String expected = "Legislative & Executive Chamber\nNassau County Office Building\n1550 Franklin Avenue\nMineola, New York 11501"; - String actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); - + testHearingAddress("01-17-13 NYSenateHearing_Marcellino_Final.txt", + "Legislative & Executive Chamber\nNassau County Office Building\n1550 Franklin Avenue\nMineola, New York 11501"); // Line numbers aligned differently - pages = PublicHearingTestHelper.getPagesFromFileName( - "01-03-13 HurricaneSandy_NYS TaskForce Roundtable_Final.txt"); - - expected = "Freeport Recreation Center\n130 East Merrick Road\nFreeport, New York 11520"; - actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + testHearingAddress("01-03-13 HurricaneSandy_NYS TaskForce Roundtable_Final.txt", + "Freeport Recreation Center\n130 East Merrick Road\nFreeport, New York 11520"); } @Test public void multiWordCityParses() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "01-25-12 Young_Roundtable_III_Final.txt"); - - String expected = "250 Broadway - 19th Floor\nNew York, New York 10007"; - String actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + testHearingAddress("01-25-12 Young_Roundtable_III_Final.txt", + "250 Broadway - 19th Floor\nNew York, New York 10007"); } @Test public void extraSpacesParses() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "02-04-13 NYSJudiciaryHearing_Bonacic FINAL.txt"); - - String expected = "New York State Capitol Building\n172 State Street, Room 124 CAP\nAlbany, New York 12247"; - String actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + testHearingAddress("02-04-13 NYSJudiciaryHearing_Bonacic FINAL.txt", + "New York State Capitol Building\n172 State Street, Room 124 CAP\nAlbany, New York 12247"); } @Test public void zipPlusFiveParses() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "09-17-13 Carlucci_Mental Health_Ogdensburg_FINAL.txt"); - - String expected = "Ogdensburg City Hall\nCity Council Chambers\n330 Ford Street\nOgdensburg, New York 13669-1626"; - String actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + testHearingAddress("09-17-13 Carlucci_Mental Health_Ogdensburg_FINAL.txt", + "Ogdensburg City Hall\nCity Council Chambers\n330 Ford Street\nOgdensburg, New York 13669-1626"); } @Test public void noZipCodeParses() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "09-12-13 NYSsenate_DeFrancisco_Buffalo_FINAL.txt"); - - String expected = "Buffalo City Hall\nCommon Council Chambers, 13th Floor\n65 Niagara Square\nBuffalo, New York"; - String actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + testHearingAddress("09-12-13 NYSsenate_DeFrancisco_Buffalo_FINAL.txt", + "Buffalo City Hall\nCommon Council Chambers, 13th Floor\n65 Niagara Square\nBuffalo, New York"); } @Test public void parsesWithoutStateZipInfo() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "06-04-14 NYsenate Heroin-Opioid Addiction Special Task Force_Seneca Nation_FINAL.txt"); - - String expected = "Seneca Nation of Indians'\nCattaraugus County Reservation"; - String actual = addressParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + testHearingAddress("06-04-14 NYsenate Heroin-Opioid Addiction Special Task Force_Seneca Nation_FINAL.txt", + "Seneca Nation of Indians'\nCattaraugus County Reservation"); } @Test public void stateAbbreviationParses() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "10-10-13 NYsenate_Fuschillo_MTA_FINAL.txt"); + testHearingAddress("10-10-13 NYsenate_Fuschillo_MTA_FINAL.txt", + "Senate Majority Office\n250 Broadway, Suite 2034\nNew York, NY 10007-2375"); + } + + @Test + public void noZipParses() throws IOException, URISyntaxException { + testHearingAddress("10-10-17 NYS Senate Flooding_IJC Plan 2014 FINAL.txt", + "Mexico High School Auditorium\n3338 Main Street, Mexico, New York"); + } + + @Test + public void withDashesParses() throws IOException, URISyntaxException { + testHearingAddress("10-19-16 NYS Senate Hudson River Barge Hearing FINAL.txt", + "Croton-on-Hudson Town Hall\n1 Van Wyck Street\nCroton-on-Hudson, New York 10520"); + } - String expected = "Senate Majority Office\n250 Broadway, Suite 2034\nNew York, NY 10007-2375"; + private void testHearingAddress(String filename, String expected) throws IOException, URISyntaxException { + List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName(filename); String actual = addressParser.parse(pages.get(0)); assertThat(actual, is(expected)); } diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java index 2f808d842..f88785fb9 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingDateParserTest.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.text.ParseException; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; @@ -27,147 +26,69 @@ public void setup() { /** Parses the date time string: June 4, 2014 1:00 p.m. to 3:00 p.m. */ @Test - public void singleDigitHoursParse() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "06-04-14 NYsenate Heroin-Opioid Addiction Special Task Force_Seneca Nation_FINAL.txt"); - - LocalDate expectedDate = LocalDate.of(2014, 6, 4); - LocalTime expectedStartTime = LocalTime.of(13, 0); - LocalTime expectedEndTime = LocalTime.of(15, 0); - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void singleDigitHoursParse() throws IOException, URISyntaxException { + testHearingDate("06-04-14 NYsenate Heroin-Opioid Addiction Special Task Force_Seneca Nation_FINAL.txt", + LocalDate.of(2014, 6, 4), LocalTime.of(13, 0), LocalTime.of(15, 0)); } /** Parses the date time string: June 2, 2014 10:00 a.m. to 12:00 p.m. */ @Test - public void doubleDigitHoursParse() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "06-02-14 NYsenate_Labor_Savino_FINAL.txt"); - - LocalDate expectedDate = LocalDate.of(2014, 6, 2); - LocalTime expectedStartTime = LocalTime.of(10, 0); - LocalTime expectedEndTime = LocalTime.of(12, 0); - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void doubleDigitHoursParse() throws IOException, URISyntaxException { + testHearingDate("06-02-14 NYsenate_Labor_Savino_FINAL.txt", + LocalDate.of(2014, 6, 2), LocalTime.of(10, 0), LocalTime.of(12, 0)); } /** Parses the date time string: Tuesday, August 23, 2011 9:30 a.m. */ @Test - public void onlyStartTimeGivenParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "08-23-11 NYS Senator Ball SCOPED Final.txt"); - - LocalDate expectedDate = LocalDate.of(2011, 8, 23); - LocalTime expectedStartTime = LocalTime.of(9, 30); - LocalTime expectedEndTime = null; - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void onlyStartTimeGivenParses() throws IOException, URISyntaxException { + testHearingDate("08-23-11 NYS Senator Ball SCOPED Final.txt", + LocalDate.of(2011, 8, 23), LocalTime.of(9, 30),null); } /** Parses the date time string: Albany, New York March 12, 2014, at 10:00 a.m. */ @Test - public void dateTimeOnSingleLineParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "03-12-14 Roundtable on the Compassionate Care Act_Savino_FINAL.txt"); - - LocalDate expectedDate = LocalDate.of(2014, 3, 12); - LocalTime expectedStartTime = LocalTime.of(10, 00); - LocalTime expectedEndTime = null; - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void dateTimeOnSingleLineParses() throws IOException, URISyntaxException { + testHearingDate("03-12-14 Roundtable on the Compassionate Care Act_Savino_FINAL.txt", + LocalDate.of(2014, 3, 12), LocalTime.of(10, 0),null); } /** Parses the date time string: January 25, 2012 Afternoon Session */ @Test - public void noTimeParses() throws IOException, URISyntaxException, ParseException { - // 01-03-13 HurricaneSandy_NYS TaskForce Roundtable_Final.txt - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "01-25-12 Young_Roundtable_III_Final.txt"); - - LocalDate expectedDate = LocalDate.of(2012, 1, 25); - LocalTime expectedStartTime = null; - LocalTime expectedEndTime = null; - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void noTimeParses() throws IOException, URISyntaxException { + testHearingDate("01-25-12 Young_Roundtable_III_Final.txt", + LocalDate.of(2012, 1, 25), null,null); } /** Parses the date time string: August 22, 2013 11:00 a.m. <96> 4:00 p.m. */ @Test - public void invalidCharactersParse() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "08-22-13 NYSSenateHearing_Buffalo_Martins_FINAL.txt"); - - LocalDate expectedDate = LocalDate.of(2013, 8, 22); - LocalTime expectedStartTime = LocalTime.of(11, 00); - LocalTime expectedEndTime = LocalTime.of(16, 00); - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void invalidCharactersParse() throws IOException, URISyntaxException { + testHearingDate("08-22-13 NYSSenateHearing_Buffalo_Martins_FINAL.txt", + LocalDate.of(2013, 8, 22), LocalTime.of(11, 0), LocalTime.of(16, 0)); } /** Parses the date time string: May 9, 2012 2:30 p.m. to - 5:30 p.m. */ @Test - public void erroneousCharactersParse() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "05-09-12 NYS SenateAgriculture_Ritchie_Final.txt"); - - LocalDate expectedDate = LocalDate.of(2012, 5, 9); - LocalTime expectedStartTime = LocalTime.of(14, 30); - LocalTime expectedEndTime = LocalTime.of(17, 30); - - LocalDate actualDate = dateParser.parseDate(pages.get(0)); - LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); - LocalTime actualEndTime = dateParser.parseEndTime(pages.get(0), pages.get(pages.size()-1)); - - assertThat(actualDate, is(expectedDate)); - assertThat(actualStartTime, is(expectedStartTime)); - assertThat(actualEndTime, is(expectedEndTime)); + public void erroneousCharactersParse() throws IOException, URISyntaxException { + testHearingDate("05-09-12 NYS SenateAgriculture_Ritchie_Final.txt", + LocalDate.of(2012, 5, 9), LocalTime.of(14, 30), LocalTime.of(17, 30)); } @Test public void dateTimeLabelsTest() throws IOException, URISyntaxException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "04-26-19 NYS Joint Farmworkers Hearing Long Island FINAL.txt"); + testHearingDate("04-26-19 NYS Joint Farmworkers Hearing Long Island FINAL.txt", + LocalDate.of(2019, 4, 26), LocalTime.of(14, 30),null); + } + + @Test + public void alternateEndTimeTest() throws IOException, URISyntaxException { + testHearingDate("05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt", + LocalDate.of(2011, 5, 18), LocalTime.of(9, 0), LocalTime.of(10, 29)); + } - LocalDate expectedDate = LocalDate.of(2019, 4, 26); - LocalTime expectedStartTime = LocalTime.of(14, 30); - LocalTime expectedEndTime = null; + private void testHearingDate(String filename, LocalDate expectedDate, LocalTime expectedStartTime, + LocalTime expectedEndTime) throws IOException, URISyntaxException { + List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName(filename); LocalDate actualDate = dateParser.parseDate(pages.get(0)); LocalTime actualStartTime = dateParser.parseStartTime(pages.get(0)); diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTestHelper.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTestHelper.java index be4b417ac..a42df12b8 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTestHelper.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTestHelper.java @@ -12,8 +12,8 @@ public class PublicHearingTestHelper { - public static List<List<String>> getPagesFromFileName(String fileName) throws URISyntaxException, IOException { - File file = TestUtils.openTestResource("hearing/" + fileName); + public static List<List<String>> getPagesFromFileName(String filename) throws URISyntaxException, IOException { + File file = TestUtils.openTestResource("hearing/" + filename); return PublicHearingTextUtils.getPages(FileUtils.readFileToString(file, Charset.defaultCharset())); } } diff --git a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java index 62b5b0192..8b462a02d 100644 --- a/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/hearing/PublicHearingTitleParserTest.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.text.ParseException; import java.util.List; import static org.hamcrest.Matchers.is; @@ -25,68 +24,55 @@ public void setup() { } @Test - public void basicTitleParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "03-12-14 Roundtable on the Compassionate Care Act_Savino_FINAL.txt"); - - String expected = "ROUNDTABLE DISCUSSION ON THE COMPASSIONATE CARE ACT"; - String actual = titleParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + public void basicTitleParses() throws URISyntaxException, IOException { + testTitle("03-12-14 Roundtable on the Compassionate Care Act_Savino_FINAL.txt", + "ROUNDTABLE DISCUSSION ON THE COMPASSIONATE CARE ACT"); } @Test - public void forumTownHallTitleParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "02-09-12 ChildCareHearing_Final.txt"); - - String expected = "FORUM/TOWN HALL: HUMAN SERVICES FORUM ON THE CURRENT AND FUTURE " + - "ISSUES AND CONCERNS OF HUMAN SERVICES ADMINISTRATORS, ADVOCATES AND CLIENTS"; - String actual = titleParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + public void forumTownHallTitleParses() throws URISyntaxException, IOException { + testTitle("02-09-12 ChildCareHearing_Final.txt", + "FORUM/TOWN HALL: HUMAN SERVICES FORUM ON THE CURRENT AND FUTURE " + + "ISSUES AND CONCERNS OF HUMAN SERVICES ADMINISTRATORS, ADVOCATES AND CLIENTS"); } @Test - public void newYorkStateForumTownHallTitleParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt"); - - String expected = "FORUM/TOWN HALL ROUNDTABLE ON THE SAGE COMMISSION'S " + - "PROPOSAL TO MERGE THE NYS OFFICE FOR THE AGING WITH THE DEPARTMENT OF HEALTH"; - String actual = titleParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + public void newYorkStateForumTownHallTitleParses() throws URISyntaxException, IOException { + testTitle("05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt", + "FORUM/TOWN HALL ROUNDTABLE ON THE SAGE COMMISSION'S " + + "PROPOSAL TO MERGE THE NYS OFFICE FOR THE AGING WITH THE DEPARTMENT OF HEALTH"); } @Test - public void senateHearingTitleParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "05-23-11 NY Senate Flanagan Education Hearing FINAL_AMENDED COVER SHEET ONLY.txt"); - - String expected = "A NEW YORK STATE SENATE HEARING " + + public void senateHearingTitleParses() throws URISyntaxException, IOException { + testTitle("05-23-11 NY Senate Flanagan Education Hearing FINAL_AMENDED COVER SHEET ONLY.txt", + "A NEW YORK STATE SENATE HEARING " + "DUE PROCESS TEACHER DISCIPLINE WITHOUT DELAY: " + "REFORMING SECTION 3020-A OF THE EDUCATION LAW " + - "TO MEET THE NEEDS OF THE 21st CENTURY"; - String actual = titleParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + "TO MEET THE NEEDS OF THE 21st CENTURY"); } @Test - public void titleFoundIfCommitteeMissing() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "01-03-13 HurricaneSandy_NYS TaskForce Roundtable_Final.txt"); + public void titleFoundIfCommitteeMissing() throws URISyntaxException, IOException { + testTitle("01-03-13 HurricaneSandy_NYS TaskForce Roundtable_Final.txt", + "ROUNDTABLE DISCUSSION HELD BY THE NEW YORK STATE SENATE " + + "BIPARTISAN TASK FORCE FOR \"HURRICANE SANDY\" RECOVERY"); + } - String expected = "ROUNDTABLE DISCUSSION HELD BY THE NEW YORK STATE SENATE " + - "BIPARTISAN TASK FORCE FOR \"HURRICANE SANDY\" RECOVERY"; - String actual = titleParser.parse(pages.get(0)); - assertThat(actual, is(expected)); + @Test + public void conferenceTitleParses() throws URISyntaxException, IOException { + testTitle("02-28-13 2013 RevenueConsensusConference_Final.txt", + "NEW YORK STATE 2013 ECONOMIC AND REVENUE CONSENSUS FORECASTING CONFERENCE"); } @Test - public void conferenceTitleParses() throws IOException, URISyntaxException, ParseException { - List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName( - "02-28-13 2013 RevenueConsensusConference_Final.txt"); + public void onHeroinEpidemicParses() throws URISyntaxException, IOException { + testTitle("02-23-2016 NYS Task Force Heroin_Penn Yan Final.txt", + "TO EXAMINE THE ISSUES FACING COMMUNITIES IN THE WAKE OF INCREASED HEROIN AND OPIOID ABUSE"); + } - String expected = "NEW YORK STATE 2013 ECONOMIC AND REVENUE " + - "CONSENSUS FORECASTING CONFERENCE"; + private void testTitle(String filename, String expected) throws URISyntaxException, IOException{ + List<List<String>> pages = PublicHearingTestHelper.getPagesFromFileName(filename); String actual = titleParser.parse(pages.get(0)); assertThat(actual, is(expected)); } diff --git a/src/test/java/gov/nysenate/openleg/processor/law/LawTitleParserTest.java b/src/test/java/gov/nysenate/openleg/processor/law/LawTitleParserTest.java index 2d032bda2..2af68f1c0 100644 --- a/src/test/java/gov/nysenate/openleg/processor/law/LawTitleParserTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/law/LawTitleParserTest.java @@ -171,7 +171,7 @@ public void noTitleStarStarting() { " * Notwithstanding that Chapter 851 of the Laws of 1983 adds\\n" + "subdivision 10 to Section 672 to the Executive Law, it is the"; // TODO Cant find this on the LRS website to confirm the title. - String expectedTitle = ""; + String expectedTitle = "ATTENTION"; assertTitle(expectedTitle, text, "", "ACAATTN"); } diff --git a/src/test/resources/hearing/02-23-2016 NYS Task Force Heroin_Penn Yan Final.txt b/src/test/resources/hearing/02-23-2016 NYS Task Force Heroin_Penn Yan Final.txt new file mode 100644 index 000000000..5f4898c80 --- /dev/null +++ b/src/test/resources/hearing/02-23-2016 NYS Task Force Heroin_Penn Yan Final.txt @@ -0,0 +1,10717 @@ + + + + 1 NEW YORK STATE JOINT SENATE TASK FORCE + ON HEROIN AND OPIOID ADDICTION + 2 ------------------------------------------------------ + + 3 + TO EXAMINE THE ISSUES FACING COMMUNITIES + 4 + IN THE WAKE OF INCREASED HEROIN AND OPIOID ABUSE + 5 + + 6 ------------------------------------------------------ + + 7 + Penn Yan Middle School Auditorium + 8 515 Liberty Street + Penn Yan, New York 14527 + 9 + February 23, 2016 + 10 6:30 p.m. to 9:30 p.m. + + 11 + + 12 PRESIDING: + + 13 Senator Thomas F. O'Mara, Sponsor + + 14 Senator Terrence Murphy, Chair + + 15 Senator George Amedore, Jr., Co-Chair + + 16 Senator Robert Ortt, Co-Chair + + 17 + + 18 ALSO PRESENT: + + 19 Senator Rich Funke + + 20 Assemblyman Phil Palmesano + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 2 + 1 + SPEAKERS: PAGE + 2 + PANEL I 8 + 3 + Joseph G. Fazzary + 4 District Attorney + Schuyler County District Attorney's Office + 5 + Ronald G. Spike + 6 Sheriff + Yates County Sheriff's Department + 7 + William E. Yessman, Jr. + 8 Sheriff + Schuyler County Sheriff's Department + 9 + Thomas Dunham + 10 Investigator + Penn Yan Village Police Department + 11 + Valerie G. Gardner + 12 District Attorney + Yates County District Attorney's Office + 13 + Jason Cook + 14 Assistant District Attorney + Chemung County District Attorney's Office + 15 + + 16 PANEL II 46 + + 17 W. Patrick Falvey + County Court Judge + 18 Yates County + + 19 Matthew Conlon + Village Justice + 20 Penn Yan Village + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 3 + 1 + SPEAKERS (Continued): PAGE + 2 + PANEL VI 59 + 3 + Janet Heaven + 4 Personal Story + + 5 Arianna Chadwick + Personal Story + 6 + Donna McKay + 7 Arianna Chadwick's Aunt + + 8 Alexis Pleus + Truth Pharm + 9 + Devon Pierce + 10 Personal Story + + 11 Gail Owen + Personal Story + 12 + PANEL V 97 + 13 + Howard Dennis + 14 Superintendent + Penn Yan Central School District + 15 + Kelly Houck + 16 Superintendent + Dundee Central School District + 17 + Tom Phillips + 18 Superintendent + Watkins Glen Central School District + 19 + PANEL III 119 + 20 + Danielle Tilden, CASAC + 21 Finger Lakes Addictions Counseling & + Referral Agency + 22 + Annmarie F. Flanagan, FNP, MS + 23 Yates Substance Abuse Coalition + + 24 Mike Ballard, MS + Council on Alcoholism and Addictions + 25 of the Finger Lakes + + + + + + + + 4 + 1 + SPEAKERS (Continued): PAGE + 2 + PANEL IV 130 + 3 + Deb Minor + 4 Director + Yates County Public Health Department + 5 + George Roets + 6 Director, Community Services + Yates County Public Health Department + 7 + + 8 Comments from Task Force Co-Chairs and 139 + Other Presiding Members + 9 + + 10 Audience Participation 153 + + 11 + + 12 + + 13 + + 14 + + 15 + + 16 + + 17 + + 18 + + 19 + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 5 + 1 SENATOR O'MARA: Okay. I think we're good to + + 2 go. We're just about on time. + + 3 I thank you all for coming out. + + 4 We have a great panel here. + + 5 I'm not going to belabor the length of time + + 6 that we have here this evening by taking it up by + + 7 going through introductions. + + 8 We'll get to that with each panel that we get + + 9 through. + + 10 We've got it separated into different + + 11 subject-matter areas, to hear from each group + + 12 individually. And if the groups can kind coordinate + + 13 amongst yourselves as we're talking here, and then + + 14 we want to make sure we have some time to have some + + 15 questions and answers with the audience, and some + + 16 discussion with the audience, of issues that come + + 17 up. + + 18 Obviously, the issue of heroin and opioid + + 19 addiction is something that's significant, it's very + + 20 real, and it's hitting us here locally in our + + 21 communities here, but across the state. + + 22 We have from the Legislature here with us who + + 23 I will introduce is: + + 24 Senator Funke, Rich Funke, from the Rochester + + 25 area, second to my right; + + + + + + + + 6 + 1 Senator George Amedore from the Schenectady + + 2 region of New York; + + 3 Senator Terrence Murphy from Westchester; + + 4 Senator Rob Ortt from Niagara; + + 5 And we have Assemblyman Phil Palmesano, who + + 6 represents us here in Yates, and Steuben, Seneca, + + 7 and Schuyler counties. + + 8 So, you know, in the interest of time, we're + + 9 going to keep it short on introductions. + + 10 As I said, these are critically important + + 11 issues that we're dealing with. We want to make + + 12 sure we get input from a variety of factors. + + 13 We know other communities across the state + + 14 and across the country are grappling with this + + 15 crisis of heroin. + + 16 We've seen all too many tragedies in our + + 17 hometowns, and read about it in the news all the + + 18 time. It seems to be getting worse and worse. + + 19 And there are issues that we need to address, + + 20 and do a better job of, from prevention, to + + 21 treatment, to law enforcement and the crime aspects + + 22 of this as well. + + 23 But it's got to be a community-wide-based + + 24 approach from our local leaders, local community + + 25 organizations, our local governments, our school + + + + + + + + 7 + 1 districts, law enforcement and district attorneys + + 2 that are here, the court system, right up to our + + 3 state government and our federal government, because + + 4 this is a nationwide crisis that we are dealing + + 5 with. + + 6 So, I thank you all for showing your interest + + 7 and coming out here to, hopefully, be able to share + + 8 some of your experiences with you -- with us, and to + + 9 learn some things about what we're doing, because + + 10 we're here to learn ideas on how and what we should + + 11 be doing in Albany, moving forward. + + 12 We have -- we're in the budget process right + + 13 now in Albany. + + 14 And in the budget proposal that's out there + + 15 now, I think it's about $140 million that's proposed + + 16 in the budget for "heroin and opioid addiction" + + 17 categories. But that's only about a $6 million + + 18 increase from last year's budget in that, and we're + + 19 looking at ways how we can further bolster our + + 20 community services to provide for treatment and + + 21 prevention of these -- these -- this real + + 22 public-health issue that we have and the individuals + + 23 that are addicted. + + 24 And we recognize it as a public-health issue, + + 25 as well as a crime issue, and it needs to be + + + + + + + + 8 + 1 approached on all of those levels for us to be + + 2 effective and everyone to be involved. + + 3 So, I thank you again for coming out. + + 4 We're going to start out with the -- + + 5 Do you guys want to do any opening remarks? + + 6 SENATOR MURPHY: No, just go right to it. + + 7 SENATOR O'MARA: Okay. We are going to start + + 8 with our first panel, which is a group of district + + 9 attorneys and sheriffs, law enforcement, from + + 10 Yates County here, from Schuyler County, and + + 11 Chemung County. + + 12 We have Joe Fazzary, the Schuyler County + + 13 District Attorney; + + 14 Ron Spike, the Yates County Sheriff; + + 15 Bill Yessman, the Schuyler County Sheriff; + + 16 Jason Cook, Assistant DA. Former DA here in + + 17 Yates County. He's an assistant in Chemung County + + 18 now, representing the Chemung County DA's Office; + + 19 Investigator Thomas Dunham from the Penn Yan + + 20 Village Police Department; + + 21 And our Yates County District Attorney here + + 22 with us, Valerie Gardner. + + 23 I thank you all for being with us here today. + + 24 I don't know if, Valerie, you would like to + + 25 start it out on being the hometown DA here? + + + + + + + + 9 + 1 DA VALERIE G. GARDNER: Sure, I'd love to. + + 2 Thank you very much. + + 3 Senators, Assemblyman, all of our guests + + 4 here, but, particularly our community members here + + 5 in Yates County who are facing a terrible situation + + 6 with the heroin epidemic that is sweeping across our + + 7 county, I want to welcome you especially, and say, + + 8 thank you for taking time out to come out tonight. + + 9 So, thank you very, very much. + + 10 [Applause.] + + 11 So in terms of -- we all, I think, are + + 12 familiar with the national statistics and the + + 13 statewide statistics that are shocking. + + 14 This epidemic has taken us, literally, by + + 15 storm. + + 16 And we're doing the best we can here in + + 17 Yates County to try and combat that, and address the + + 18 significant issues with opiate addiction, and, + + 19 specifically, the influx of heroin that we've had + + 20 here in Yates County. + + 21 And my request tonight to our State + + 22 Legislators here is that, please, we are a small + + 23 county. We need your help in committing the + + 24 financial resources for us to address this problem. + + 25 It is widespread. + + + + + + + + 10 + 1 And one thing about heroin addiction is + + 2 that -- or any addiction, but, specifically, heroin + + 3 addiction, as it plays out here, it does not know + + 4 our county boundary, and so as a result, we have + + 5 individuals here who are using, who are addicted, + + 6 they're traveling outside of Yates County for their + + 7 supply of heroin. And our law enforcement agencies + + 8 have jurisdictions that they have to abide by. + + 9 So I would ask the State to please commit + + 10 significant resources to the New York State Police, + + 11 to interagency cooperation, so that we can address + + 12 this problem beyond the boundaries that impact us so + + 13 greatly here locally, please. + + 14 In addition, I think we all can appreciate + + 15 that by the time an addict gets to us, we're at the + + 16 end of the pipeline here, and there's already been + + 17 significant harm, not only to the addict and their + + 18 families, but also victims involved in crime. + + 19 And, so, when we look at this, I think that + + 20 we really have to appreciate that every strategy to + + 21 attack this problem must be employed, and the most + + 22 important is prevention, education, to keep people + + 23 out of the criminal justice system, and that + + 24 benefits all of us. + + 25 But in order for that to happen, we must have + + + + + + + + 11 + 1 treatment available, so that the option, when an + + 2 addict decides that they want to get help, that they + + 3 have the ability to seek treatment. + + 4 Currently, you have some choices before you + + 5 in terms of dealing with the insurance industry and + + 6 the historic regulations that the insurance + + 7 companies are allowed to force addicts to fail, two, + + 8 three times, before they have access to inpatient + + 9 treatment. + + 10 So I would ask that the Legislature insist + + 11 that the insurance companies take notice that heroin + + 12 is unlike other addictions; that outpatient + + 13 treatment is just not as effective as significant + + 14 inpatient treatment, and make those resources + + 15 available. + + 16 [Applause.] + + 17 DA VALERIE G. GARDNER: I know that the + + 18 proposed budget includes approximately 30 beds to be + + 19 located in the upstate area. + + 20 And I would just say that, while it is a + + 21 start, it is woefully insufficient. + + 22 And I hope that you will all advocate + + 23 strongly for more beds to be available for our + + 24 citizens who are battling this addiction. + + 25 And, I just would note to you that, in the + + + + + + + + 12 + 1 absence of doing that, the very serious consequence + + 2 is that the beds that will end up being available + + 3 for these folks are the beds in our county jail, and + + 4 that is not something that we want to see. + + 5 I think the Sheriff can tell you that we have + + 6 had just a tremendous increase in population because + + 7 of this. + + 8 And so we need to remember that the + + 9 consequences are widespread for all of us here. + + 10 In addition, I would also ask that the + + 11 alternatives to heroin that some turn to, if they + + 12 happen to be on probation supervision, or in a + + 13 program where they are trying to address getting + + 14 well and fighting their addiction, that synthetic + + 15 drugs absolutely must be addressed within our + + 16 Legislature so that our penal law is not defining + + 17 drugs and controlled substances that are illegal by + + 18 the polymers that can so easily be changed. + + 19 And so, please, if you could support that, + + 20 that would be a tremendous help to law enforcement. + + 21 SENATOR O'MARA: And, Valerie, as we spoke + + 22 about this yesterday in front of the Yates + + 23 Legislature, we have passed that in prior years in + + 24 the State Senate, and we have passed it this year + + 25 already. + + + + + + + + 13 + 1 We'll put the pressure on our Assemblyman + + 2 here, Phil Palmesano, to get that through the + + 3 Assembly, which he's very supportive of, don't get + + 4 me wrong. I'm just teasing him. + + 5 But our battle there, with any kind of these + + 6 changes in criminal penalties, our struggle is to + + 7 get it through the State Assembly. + + 8 But we're there with you on that. + + 9 DA VALERIE G. GARDNER: Okay. So go, Phil! + + 10 [Laughter.] + + 11 DA VALERIE G. GARDNER: So, lastly, I just + + 12 want to give you an idea of how this has impacted us + + 13 locally in Yates County for our criminal justice + + 14 system. + + 15 Our county court, Judge Falvey, who serves us + + 16 in county court, hears felonies. And, surprisingly, + + 17 Yates County has been pretty consistent over the + + 18 last 17 years, as I look back at the statistics, in + + 19 terms of how many felonies get filed. + + 20 We are over 20 percent higher as of last + + 21 year, directly related to the heroin epidemic. + + 22 And with regard -- if you can imagine, so, we + + 23 had 91, either superior court informations or + + 24 indictments, filed last year alone. + + 25 That is a tremendous amount for Yates County, + + + + + + + + 14 + 1 and there are victims attached to every single one + + 2 of those cases. + + 3 And that is felonies only. + + 4 If you add in the misdemeanors that are + + 5 handled in local courts, the possession cases, there + + 6 are -- it's just a tremendous amount of resources + + 7 that we're putting into the criminal justice system + + 8 instead of prevention. + + 9 And, so, I'm hoping that everybody can + + 10 support that effort through the Yates Substance + + 11 Abuse Coalition, and however you can, in talking to + + 12 your families and neighbors. + + 13 With regard to our numbers, a total of + + 14 98 cases over the last 2 years, the highest charge + + 15 has been a drug charge. So we're not talking -- + + 16 that's not the only cases that involved drug + + 17 involvement. + + 18 There are lots of larcenies, and other + + 19 things, that the impetus for the crime is drugs. + + 20 But I'm talking about the charge is a drug + + 21 charge. And out of those 98 cases, 32 are felony + + 22 cases. + + 23 We do have 100 percent conviction rate here + + 24 in Yates County on all of those cases. + + 25 And, so, we're doing what we can through the + + + + + + + + 15 + 1 programs that we have in the criminal justice + + 2 system, and I hope that you'll continue your support + + 3 for that. + + 4 And, also, continue to ask our + + 5 representatives to help with the budgetary + + 6 constraints as well. + + 7 So thank you very much. + + 8 [Applause.] + + 9 SENATOR O'MARA: Thank you. + + 10 Thank you, District Attorney Gardner. + + 11 Now I'll turn to our sheriffs. + + 12 Sheriff Spike, Sheriff Yessman, if you could + + 13 give us an overview of your perspective on this, and + + 14 where you think we at the State, at the legislative + + 15 level, may be able to offer assistance in your + + 16 efforts. + + 17 SHERIFF RONALD G. SPIKE: Sure. + + 18 SENATOR O'MARA: And kind of the trends, + + 19 obviously, that you're seeing here locally. + + 20 SHERIFF RONALD G. SPIKE: Good evening, + + 21 everyone. + + 22 Thank you, Senator, for having this hearing + + 23 and thank you, Committee Members, for being here, + + 24 and everyone else. + + 25 When this community was first forming a + + + + + + + + 16 + 1 coalition, I put together some slides at the + + 2 Penn Yan Academy, to speak, and I said at the time + + 3 that heroin and opiate abuse was an epidemic. + + 4 And I stand by that label today, that's what + + 5 it is. + + 6 And I never thought at the time, a few years + + 7 ago, I'd be standing in a school in this community + + 8 and talking about heroin. It just wasn't something + + 9 we talked about. + + 10 So it's been quite a challenge for public + + 11 safety, public health, for the criminal justice + + 12 system, public education, and all the treatment + + 13 entities that are around. + + 14 We've had a dozen deaths over the last few + + 15 years, ten from heroin, one from pills, and one from + + 16 an addict who was so needing, they chewed on a + + 17 fentanyl patch, and killed themselves. + + 18 The oldest was a 45-year-old woman who + + 19 started out taking Oxycontin, and then went to + + 20 heroin because it was available and affordable. + + 21 Sadly, we also had the life of a 17-year-old + + 22 girl who once tried it for fun and was soon + + 23 addicted. + + 24 For this county, perhaps to some, a dozen + + 25 deaths may not seem high. But in this close + + + + + + + + 17 + 1 community, it is huge to us. It is huge. + + 2 And it's just a terrible problem because, in + + 3 some areas of this country, the deaths by heroin are + + 4 exceeding the deaths by car crashes, which normally + + 5 is the number-one killer of our young people. + + 6 I and members of my office have interacted + + 7 with families and those addicted and those who have + + 8 overdosed. + + 9 On the majority of those cases of overdoses, + + 10 the Good Samaritan laws have been around; and, thus, + + 11 we don't get to get into the criminal justice + + 12 systems or make an arrest because of that. They + + 13 seem to apply more often than not. + + 14 But, we have dealt with many moms and dads + + 15 who don't know what to do. They're kind of behind + + 16 closed doors in some way, and perplexed, on seeing + + 17 their son or their daughter transform into an + + 18 addict. + + 19 And it is -- it's just terrible, because even + + 20 those that find treatment have to travel 50 miles to + + 21 get that treatment. And so that, in itself, is yet + + 22 another problem. + + 23 Those that are abusing it just cannot help + + 24 themselves. It is such a powerful drug, such a + + 25 powerful craving, it cares less whether you're young + + + + + + + + 18 + 1 or old, rich or poor, it matters not to that. + + 2 Many in their 20s are stealing from their + + 3 parents, they're stealing from their grandparents, + + 4 or they go to their neighbor's house and steal + + 5 property, shoplift, forge checks, that type of + + 6 thing. + + 7 Our investigators have spent considerable + + 8 time in the city of Rochester at pawn shops where + + 9 individuals take this property to get the cash to + + 10 buy the drugs. + + 11 All drug-possession cases in the last 4 years + + 12 have risen 120 percent for our agency, and felony + + 13 drug cases for our agency in the last 4 years have + + 14 risen 150 percent. + + 15 We have no dedicated drug unit in this + + 16 county, and the majority of the County's + + 17 investigators' time involves drug investigations. + + 18 I recently deputized the local Penn Yan + + 19 police investigator, who is here at this table, who + + 20 works very hard in this area. + + 21 As we all work together very much, I wanted + + 22 him covered, because he was outside of his + + 23 jurisdiction and spending a lot of time in the + + 24 county with our people, working on cases related to + + 25 the village of Penn Yan. + + + + + + + + 19 + 1 I said to him, I said, "Tom, how much time + + 2 are you spending on drugs?" + + 3 80 percent of his time. + + 4 One year ago, after four months of + + 5 investigation, we were able to take down -- with the + + 6 Penn Yan police and our agency, took down a guy who + + 7 was selling drugs in Dundee, and then moved to + + 8 Penn Yan. He was selling heroin, cocaine, + + 9 oxycodone, and Suboxone. + + 10 He ended up getting 12 years in prison. + + 11 Once again, the time involved with these + + 12 cases is huge. To make these drug arrests, to be + + 13 involved with informants and doing surveillance is + + 14 huge. + + 15 Another statistic that I think is very + + 16 relevant for everyone to understand, I looked at our + + 17 statistics in the last five years for making arrests + + 18 for driving while impaired by use of drugs. + + 19 Driving when under the influence of drugs, in + + 20 the last five years they've increased by 300 percent + + 21 in this county. + + 22 And we have put up billboards. + + 23 We have trained a deputy as a + + 24 drug-recognition expert. They call him a "DRE." + + 25 There's only about 220 of them in the whole + + + + + + + + 20 + 1 state of New York, and they're very important. + + 2 And he's a big asset to local police deputies + + 3 and the troopers. + + 4 In 2011 I found myself on the national news + + 5 when we had a tragic fatal traffic accident on a + + 6 county road, where we had several Amish were killed + + 7 in a van, because of a driver who was under the + + 8 influence of cocaine, who struck them, and took all + + 9 their lives. Convicted of homicide. + + 10 The men and women who perform public safety + + 11 in Yates County, whether they be county or the local + + 12 police, don't get enough recognition for all the + + 13 good work that they are doing. + + 14 And we have really been touched by this + + 15 epidemic. + + 16 Our 911 communications center deals with + + 17 overdose cases, it ties up a dispatcher doing + + 18 pre-arrival instructions when they're sending EMS, + + 19 and what have you. + + 20 Our patrol deputies have to respond to these + + 21 increased calls of stolen property. And, they've + + 22 all been trained in the use of Narcan, in every + + 23 patrol car, and have saved some lives. + + 24 We have put receptacles in our public safety + + 25 building and other satellite offices, where people + + + + + + + + 21 + 1 can bring in drugs and drop them off, no longer + + 2 being used. + + 3 And, we have put up a crime-tip hotline. + + 4 And I'm trying to get an app on a smartphone, + + 5 and I'm very close to having that done. + + 6 Our K-9 units have increased calls for + + 7 searches of property to detect drugs. + + 8 We had to get a special license from + + 9 New York State, and now I'm working on a license + + 10 from the DEA, to actually hold the drugs in our + + 11 possession so the drug dogs can be trained on + + 12 finding those drugs. + + 13 So I'm working -- we're working on that right + + 14 now. + + 15 Our youth officer is assigned to the Dundee + + 16 school, and is involved with D.A.R.E. (drug-abuse + + 17 resistant education) for fifth graders. + + 18 And don't I wish I had the resources to + + 19 continue to do that in the eighth grade, and, again, + + 20 in eleventh grade, because it's through repetition, + + 21 I feel, that we can optimize our education efforts + + 22 of our kids. + + 23 So I thank the Yates County schools for all + + 24 they are doing and have done for public-health + + 25 education. + + + + + + + + 22 + 1 Our criminal investigators are handling + + 2 informants, doing surveillance, handling evidence. + + 3 And with handling evidence, we are very aware of the + + 4 fentanyl issue. + + 5 These dealers are cutting heroin with + + 6 fentanyl. And in some cases, recently, up in the + + 7 Erie County area, they had 23 deaths in 11 days + + 8 where it was pure fentanyl. + + 9 They're seeing this down in the Binghamton + + 10 area, and also other areas of the state. + + 11 And so we're constantly doing training on + + 12 safety, so that when we're handling this drug + + 13 evidence, we don't get hurt ourselves. + + 14 The other thing we're seeing more of, which + + 15 I think is important for you to know, is we're + + 16 seeing more synthetic marijuana starting to appear + + 17 on the scene. + + 18 So I think that's important. + + 19 Last, but not least, is our county jail. + + 20 We have several addicts in our jail on a + + 21 regular basis. + + 22 We've seen withdrawal. + + 23 We have seen infections due to needle use. + + 24 In the last year we had more inmates + + 25 hospitalized than ever before, and a lot of that was + + + + + + + + 23 + 1 due to drugs, and we often have to guard them at our + + 2 local hospital. + + 3 Right now, I've got officers guarding an + + 4 individual, who was in our jail for drugs, up at the + + 5 local hospital. And we have to do that 24/7. + + 6 We've had to do guard duty at + + 7 Strong Memorial Hospital, travel up there and back + + 8 with guards, to guard these individuals at the + + 9 hospital. + + 10 It puts a strain on jail resources and + + 11 overtime. + + 12 We have FLACRA coming into our jail on a + + 13 regular basis for substance-abuse and alcohol + + 14 counseling. + + 15 And we are seeing a lot of attempts to + + 16 smuggle contraband into the jail through visitation + + 17 and other methods, especially Suboxone which comes + + 18 in the strips. We're seeing more and more of that. + + 19 We even had a drug-court participant, who + + 20 knew she was going to get sanctioned in drug court + + 21 by the judge and put in jail, hide heroin inside her + + 22 body. And then we caught her snorting heroin in her + + 23 jail cell. + + 24 And that type of thing. + + 25 So we continue to see increases in the number + + + + + + + + 24 + 1 of females that are involved in drugs, kind of + + 2 disproportionate to those of the males, especially + + 3 in our jail. + + 4 And so I think it's important to know that we + + 5 had as many as 21 females in our jail last August. + + 6 I only have four cells in my jail for + + 7 females. + + 8 Okay? + + 9 We had 21 in jail. + + 10 And a few weeks ago we had an inmate give + + 11 birth, and she now has her baby in her jail cell in + + 12 my jail. And we've had to -- had to provide a + + 13 nursery, and providing that whole setting, keeping + + 14 her separated and that child separated, because, + + 15 under the law, she has a right to have that baby + + 16 with her in the jail, and she wants it, keeping that + + 17 separated from everybody else that's in that jail + + 18 and the people that she shouldn't be around, has + + 19 been very challenging for us. + + 20 So, finally, our jail medical staff and our + + 21 jail physician, we are commencing a plan for a + + 22 program to introduce a drug in our jail called + + 23 "Vivitrol" for addicts who want to recover and who + + 24 will go to counseling. + + 25 We need to rethink alcohol and opiate + + + + + + + + 25 + 1 dependence for those in the criminal justice system. + + 2 Vivitrol is FDA-approved. It's + + 3 non-addictive. And contrary to other drug + + 4 treatments, it is an opioid-blocker and an + + 5 antagonist, and a single shot will last 28 days. + + 6 It appears to me that Vivitrol, together with + + 7 counseling, for six months, may be the best chance + + 8 for detoxification and reduced relapses. + + 9 Relapses have taken many of our local addicts + + 10 to their death, and rehospitalization. + + 11 Let me conclude and say this: + + 12 Law enforcement will continue to do our part + + 13 as best we can with the manpower and resources we + + 14 have. + + 15 I've been asked, Could we do more? + + 16 The answer is, yes, I could do more with + + 17 added resources, but those appear to be limited. + + 18 County budgets are already strained to stay + + 19 under the tax cap. + + 20 I have looked for grant opportunities, and + + 21 those available at this time do not address drug + + 22 investigation or interdiction. + + 23 So grants, similar to the federal Byrne + + 24 grants of yesteryear, would be something that would + + 25 be very accepted so that we could have our own + + + + + + + + 26 + 1 dedicated drug unit. + + 2 Working with the Village police, and + + 3 together, we will make an impact. + + 4 Law enforcement has a very important role to + + 5 play in this multi-faceted community crime and + + 6 health problem. + + 7 The sellers of heroin belong in jail. + + 8 Those addicted do not belong in jail. They + + 9 need affordable treatment. + + 10 I want to thank our local county + + 11 substance-abuse coalition for all their volunteers + + 12 and all their advocacy and the partnership of + + 13 education and awareness efforts. + + 14 I thank this Committee for allowing me to + + 15 make a few remarks, and thank you for coming to + + 16 Penn Yan. + + 17 [Applause.] + + 18 SENATOR O'MARA: Thank you, Sheriff Spike. + + 19 Sheriff Yessman, from Schuyler County, do you + + 20 have any comments to add? + + 21 SHERIFF WILLIAM E. YESSMAN, JR.: Yes, thank + + 22 you. + + 23 And thank you for coming out tonight. + + 24 I'd like to say that Schuyler County is + + 25 different from Yates County. + + + + + + + + 27 + 1 We're very similar in size. We're also very + + 2 similar in the problems we're seeing. + + 3 We've had this conversation with + + 4 Sheriff Spike in the past. + + 5 We're trying to share information now, but, + + 6 we just don't have the resources, being small + + 7 counties, small agencies, to dedicate full-time drug + + 8 investigators to this issue. + + 9 In over 30 years with the Sheriff's Office, + + 10 I've never seen a drug come in and take hold of the + + 11 community like heroin has. + + 12 We've seen cocaine come in. + + 13 We've seen the marijuana. + + 14 We're still dealing with methamphetamine in + + 15 Schuyler County. + + 16 But heroin is taking hold in the younger + + 17 population in our counties, it's destroying them. + + 18 We have made a couple of saves with Narcan, + + 19 only to arrest the people the next day for + + 20 possession again, because they need this drug. + + 21 The jail situation, I do not have any female + + 22 cells in Schuyler County. We're the second-smallest + + 23 jail in the state. + + 24 I currently have six females boarded out to + + 25 Chemung County, all on drug-related charges, every + + + + + + + + 28 + 1 one of them. Some of them for larcenies, because + + 2 they had to steal to get money to get the drugs, but + + 3 they're all in on drug-related offenses. + + 4 And that's an expense that we're not prepared + + 5 for, and it just keeps getting worse and worse. + + 6 When Sheriff Spike told me he had 21 females + + 7 in jail last summer, I thought, what's going on up + + 8 there? + + 9 You know, we weren't seeing the heroin down + + 10 in Schuyler County. We were kind of isolated from + + 11 it for a while. + + 12 And we said, Geez, we're lucky. You know, + + 13 it's up in Yates County. It's over in + + 14 Tompkins County. It's down in Chemung County. + + 15 We have it. + + 16 We saw a lot of our people overdosing in + + 17 other counties. + + 18 Now, they're overdosing at home in our + + 19 county. + + 20 And we have to take steps to be able to fight + + 21 this. + + 22 Again, counseling, prevention, is a great + + 23 thing, but, right now, people come to us as the + + 24 local law enforcement, as their elected + + 25 law-enforcement officials in the counties, and want + + + + + + + + 29 + 1 our help. And we're trying to do what we can. + + 2 Again, my investigators work on this, + + 3 basically, in their -- it used to be their spare + + 4 time. Now it's taking over their job. They spend a + + 5 majority of their time. + + 6 Every morning we sit in our criminal + + 7 investigations division and talk about drugs. + + 8 We have known locations in our county where + + 9 we're making our presence very well known in the + + 10 area. These people know we're out there. And, + + 11 we're trying to put enough pressure on them to maybe + + 12 move on, but that's not solving the problem. + + 13 Even if we move our dealers out, others are + + 14 going to move in and take over, or, they're going to + + 15 go into the surrounding area and buy their drugs + + 16 there. + + 17 It's not going to be any easy fix. + + 18 Like the Sheriff said, this is an epidemic, + + 19 and I totally agree. + + 20 A year ago, I would have said, maybe not. + + 21 But the way this has moved in and taken hold, + + 22 it's just a terrible thing that we have to do + + 23 something about. + + 24 And as a law-enforcement leader, this is one + + 25 of those things that our hands are tied, because, + + + + + + + + 30 + 1 again, budget-wise, there's no money there, they + + 2 reduce our budgets all the time. + + 3 We need some type of prolonged funding, at + + 4 least have task forces, maybe between counties. You + + 5 know, we're seeing similar movement between + + 6 Yates County and Schuyler-County at this time. + + 7 And we're working with, the sheriff + + 8 investigator from Penn Yan here, was just down in + + 9 our office a couple weeks ago, working a case with + + 10 us. + + 11 And this is what we're going to have to do, + + 12 is team together. + + 13 But, agencies aren't going to -- I know the + + 14 police department in Penn Yan, he's working most of + + 15 the time out of the village, and they're not going + + 16 to be able to financially keep that going. + + 17 So we need to find a way to fund these + + 18 investigations, and the investigators that are + + 19 handling these investigations. + + 20 Thank you. + + 21 SENATOR O'MARA: Thank you, Sheriff. + + 22 [Applause.] + + 23 SENATOR O'MARA: Investigator Dunham, from + + 24 your perspective as the drug investigator in + + 25 Yates County, I take it, wearing many hats, and + + + + + + + + 31 + 1 I thank the Penn Yan Village Police Department for + + 2 allowing you to do that. + + 3 If you could give us your perspective on + + 4 this. + + 5 INV. THOMAS DUNHAM: Yes. + + 6 Thank you all for coming, and thank you for + + 7 putting this on. + + 8 And I do wear many hats, and I do spend the + + 9 vast majority of my days dealing specifically with + + 10 heroin. + + 11 And I look out in the crowd and I see many + + 12 faces that I know. And the community plays a huge + + 13 role in this, and helping combat this. + + 14 But heroin has been a growing problem in the + + 15 Penn Yan community over the last several years, and + + 16 it's become one of the most prevalent drugs being + + 17 used in our area. + + 18 We've seen a dramatic increase in heroin use + + 19 and overdoses, and we're dealing not only with the + + 20 increased crime that comes along with that, but we + + 21 see the effects it has on families, and the + + 22 community as a whole. + + 23 And we need to work together with other + + 24 agencies, we need to work with treatment counseling + + 25 and education, to help combat this. + + + + + + + + 32 + 1 And while there are several agencies and + + 2 specialty services involved in treating and + + 3 combating heroin on a law-enforcement level, like + + 4 the other Sheriff said, and the DA said, funding is + + 5 a big obstacle for us. + + 6 Our officers received a grant in 2011 under a + + 7 Byrne grant that the Sheriff had mentioned, to + + 8 purchase surveillance equipment. + + 9 This equipment has been invaluable to us. We + + 10 have used it effectively to investigate the sale of + + 11 heroin and other controlled substances. + + 12 However, this equipment can be -- it quickly + + 13 gets outdated. And there's new technologies + + 14 available now that have reoccurring costs associated + + 15 with them to help combat this. + + 16 We have limited manpower. + + 17 We do not have anyone currently assigned + + 18 specifically to investigate the drug problem. + + 19 In Penn Yan there's -- I am the investigator, + + 20 so I deal with drug cases, sexual assaults, any type + + 21 of crime that happens, that's me. + + 22 So with the way heroin has taken off in the + + 23 last few years, it's been tough, it's been busy. + + 24 We're dealing -- on top of that, we're + + 25 dealing with the increased availability of needles, + + + + + + + + 33 + 1 when in years past, in our community, in order to + + 2 get needles, you would have to become a member a + + 3 needle-exchange program, and you'd have to travel to + + 4 that needle-exchange program to get needles. + + 5 Now you can purchase needles at any pharmacy + + 6 here in the village. And people purchasing those + + 7 needles, they're not required to carry any paperwork + + 8 as to where they were purchased or sign a log + + 9 indicating they purchased those needles. + + 10 However, individuals purchasing + + 11 pseudoephedrine are required to produce + + 12 identification and they sign a log. That's used as + + 13 a tool with law enforcement to combat the + + 14 methamphetamine manufacturing. + + 15 On top of the greater availability of + + 16 needles, law enforcement can no longer effect an + + 17 arrest on individuals carrying used needles with + + 18 residual amounts of heroin in the needle. + + 19 And while we understand the public-health + + 20 concerns, and the fear that individuals may share or + + 21 reuse needles, the availability and the ease at + + 22 which a person can obtain them is a concern for law + + 23 enforcement. + + 24 We continue to see increases of heroin, and + + 25 increased crime related to its use. + + + + + + + + 34 + 1 There have been some effective steps and + + 2 tools that have been taken and used, such as + + 3 naloxone being issued to every officer. Public + + 4 health and the Sheriff's Office, both were great in + + 5 implementing that. + + 6 Naloxone is a medication that's used to + + 7 reverse the effects of opiates, and it's especially + + 8 used after someone overdoses. + + 9 We were trained in that, our office was + + 10 trained in that, in 2014, and every officer in our + + 11 department was issued a naloxone kit. + + 12 Since we've received that training, we have + + 13 successfully used naloxone 11 times on individuals + + 14 who have overdosed. We've also been present on + + 15 several other instances where EMS has used it. + + 16 And while there's been some progress, and + + 17 there are several agencies and groups working + + 18 towards stopping this problem, we continue to see an + + 19 issue in this community. + + 20 And, law enforcement plays a key role in + + 21 combating heroin. + + 22 We're right on the front lines, dealing with + + 23 this stuff every day, and we're often dealing with + + 24 people when they're at their lowest point. + + 25 We don't only work towards arresting people + + + + + + + + 35 + 1 and dealers and stemming the flow of heroin into the + + 2 community, but we're also working on educating the + + 3 public and working with the public to help tackle + + 4 the issue. + + 5 And, you know, I can't stress enough how + + 6 important it is for the public to contact law + + 7 enforcement if they see something going wrong. + + 8 We're always available, and we love to have + + 9 the public's input. + + 10 And often law enforcement, I know it's not + + 11 the only tool here, but often that's -- + + 12 law-enforcement intervention is the catalyst to push + + 13 someone into treatment that they might not seek on + + 14 their own. + + 15 We continue to see major issues with it, and, + + 16 on our level, I think that we need funding, tools, + + 17 and appropriate legislation to combat it. + + 18 And I just want to thank everyone for coming. + + 19 SENATOR O'MARA: Thank you, Investigator. + + 20 [Applause.] + + 21 SENATOR O'MARA: Assistant DA Cook, can you + + 22 give us a perspective from Chemung County, a little + + 23 bit further south from where we've heard from. + + 24 ADA JASON COOK: Thank you, Senator O'Mara. + + 25 And thank you all for coming out tonight. + + + + + + + + 36 + 1 I don't think it is an exaggeration to use + + 2 the term that heroin is "a cancer" that is really + + 3 plaguing all of our counties, the number of people + + 4 that are dying every day in one of our -- in Yates + + 5 County or Chemung County or any of these counties. + + 6 And another analogy, certainly, we need all + + 7 hands on deck. + + 8 It's a law-enforcement problem. + + 9 It's a public health problem. + + 10 It's an education problem. + + 11 We all have to contribute. + + 12 I guess my perspective, and I'll share a + + 13 couple of insights that I've had, but, certainly, + + 14 prosecution is where, often, sometimes the dam + + 15 breaks. + + 16 A parent discovers they've been stolen from. + + 17 A child gets arrested for possession of a + + 18 forge instrument; forged checks. + + 19 But when you hear the term "heroin," I guess + + 20 the distinction that I immediately make, is there's + + 21 a big distinction between: + + 22 People who are addicted and who are + + 23 committing crimes to feed that addiction. Every day + + 24 they get up, Where am I going to get my drugs? + + 25 Where am I going to get my drugs? Where am I going + + + + + + + + 37 + 1 to get my drugs? + + 2 And those who are feeding -- those who are + + 3 dealing drugs, those who are selling to those who + + 4 are then becoming addicted. + + 5 I think that's a major distinction that we + + 6 see. + + 7 And, certainly, separating those class of + + 8 defendants, those who are committing larcenies and + + 9 other thefts to feed their own habit, and those who + + 10 are willingly selling to everybody they can sell, to + + 11 make money, to profit from the suffering of + + 12 everybody they're selling to, is a key distinction + + 13 in the analysis. + + 14 And every case is different on its own facts. + + 15 But there is a reason why prison is + + 16 authorized for the sale of drugs. + + 17 And a practical thing that I raised with + + 18 Senator O'Mara, and I'm sure he'll bring it up, is + + 19 in dealing with these crimes, the lab reports that + + 20 we get for heroin are -- and the statutes that + + 21 govern heroin, the lab reports are typically, for a + + 22 single heroin dose, are in the thousandths of + + 23 percent. + + 24 Like, a single heroin dose, for example, + + 25 it would be in a small envelope, glassine envelope, + + + + + + + + 38 + 1 it would get sent to the lab. It would + + 2 register .025 (25/1000) of a gram. + + 3 So that would land on a prosecutor's desk. + + 4 Now, that is a minuscule amount, + + 5 quantity-wise. + + 6 Certainly can be lethal. A single dose can + + 7 be lethal. + + 8 And as Sheriff Spike referenced, drug dealers + + 9 often water it down, they cut it down, because they + + 10 want to make more money, they want to sell to more + + 11 people. So they'll cut it down and mix it with + + 12 fentanyl or some other drug component, but -- and + + 13 it's typically sold in bundles. Ten envelopes to a + + 14 bundle. + + 15 But the practical thing that I have raised + + 16 with Senator O'Mara, is it's different from, let's + + 17 say, another controlled substance like cocaine. + + 18 To get to a significant felony charge, you + + 19 need to get an eighth of an ounce. + + 20 So doing a little bit of math, if you have a + + 21 single dose that's .025 (25/1000) of a gram, and you + + 22 need to get to an eighth of an ounce, which is + + 23 3.5 grams, to have a significant felony charge over + + 24 someone, you would have -- with a significant prison + + 25 sentence -- + + + + + + + + 39 + 1 In other words, you're looking at a + + 2 possession of a large amount of drugs for, + + 3 presumably, a dealer. Not someone who is just using + + 4 every day for their own use, but a dealer, someone + + 5 who is spreading this poison here in our community. + + 6 -- you would have to have so many of those, + + 7 it doesn't happen very often. + + 8 So what I've suggested to Senator O'Mara is + + 9 really a revamping, a changing of the penal-law + + 10 sections, because that's what governs what we do, + + 11 the statutes, the law, pertaining to heroin, because + + 12 it is so much lighter, it is so much smaller, and + + 13 cut so differently than cocaine and other + + 14 controlled-substance drugs, that would allow + + 15 prosecutors to seek higher-grade felony charges for + + 16 possession of the dealers, of the dealers that are + + 17 peddling this poison and creating the addicts that + + 18 is the cancer among us. + + 19 So, I proposed that to Senator O'Mara, and + + 20 I think he'll certainly discuss that with his + + 21 colleagues. + + 22 But that would be a practical -- that's a + + 23 practical problem that we face in identifying the + + 24 dealers. + + 25 And, again, making a distinction between + + + + + + + + 40 + 1 those who are addicted, committing crimes for their + + 2 own addiction, and those who are pedaling, those who + + 3 are selling to those and profiting from those that + + 4 are becoming addicted to that. + + 5 And that's certainly a big distinction that + + 6 I have seen in Chemung County and throughout all of + + 7 the counties. + + 8 And that's not to take away from any of the + + 9 treatment component or any of the aftercare or any + + 10 of the prevention. + + 11 But that's simply from the prosecution + + 12 perspective. + + 13 And thank you again for all coming tonight. + + 14 [Applause.] + + 15 SENATOR O'MARA: Thank you, Jason. + + 16 District Attorney Fazzary. + + 17 Thank you for being here, Joe. + + 18 DA JOSEPH G. FAZZARY: Thank you, Senator. + + 19 I'll keep this brief, primarily because + + 20 Senator O'Mara, my good friend, told me to keep it + + 21 brief. + + 22 [Laughter.] + + 23 DA JOSEPH G. FAZZARY: Two years ago, I sat + + 24 at a hearing just look this in Elmira, at + + 25 Elmira College. Senator O'Mara was present for + + + + + + + + 41 + 1 that. + + 2 And, I heard somebody from Tioga County + + 3 treatment say that they had had 10 overdose deaths + + 4 in the last 6 months. + + 5 And I looked at the sheriff and I said, "Not + + 6 in Schuyler County. We hadn't had any." + + 7 I listened, and I thought, boy, it sounds + + 8 like it may be coming our way. + + 9 And as the Sheriff said, our, probably, + + 10 number-one drug in Schuyler is methamphetamine. + + 11 But, certainly, in the last five to six + + 12 months, I have seen just how wrong I was that it was + + 13 going to be a problem. + + 14 Schuyler County, second-smallest -- you guys + + 15 are small, we're smaller -- second-smallest county + + 16 in the state of New York, we had 18 reported opioid + + 17 overdoses in the last 4 months. We had six of those + + 18 people die. + + 19 Okay? + + 20 So, all of a sudden, this is a real problem, + + 21 not just for the cities, but for places just like + + 22 us. + + 23 I have heard more times in the community, in + + 24 the last 6 or 8 months, than I've heard in my entire + + 25 24-year career in Schuyler County, "What are you + + + + + + + + 42 + 1 doing about the drugs, Mr. Fazzary?" + + 2 And I said, You all think that we're doing + + 3 nothing, you all may think that this table is doing + + 4 nothing, because you don't necessarily see us out + + 5 there. + + 6 And then I educate them on how, in order to + + 7 get somebody for selling drugs, you have to get + + 8 somebody in. + + 9 Okay? + + 10 That's not easy to do, because this isn't + + 11 really like the city of Elmira, where you can pull + + 12 up on a street corner and hand 20 bucks out a car + + 13 window to somebody that walks up to your car and + + 14 gives you a bag of heroin. + + 15 Not in Schuyler County, not in Yates County, + + 16 we don't have them standing on the street corner. + + 17 We have them in houses, in apartments. + + 18 So, the neighbor comes up -- I had one in my + + 19 office yesterday, from the County, County employee, + + 20 she said, "There's a drug dealer right next to my + + 21 house. There's cars pulling in there all night + + 22 long, all day long. What are you people doing about + + 23 it?" + + 24 I said, "Well, it's the first that I've heard + + 25 about it, but you live out in the country, and we + + + + + + + + 43 + 1 can't exactly put a police car in your yard. We + + 2 can't exactly put a patrol car, or even an unmarked + + 3 car, on the side of the street." + + 4 So what we need is for these people that get + + 5 in trouble, and they want to help themselves out, we + + 6 need for those people to come to us, or for our + + 7 police officers to go to them and say, Would you be + + 8 willing to help us out? + + 9 So I want you to understand, it's not for a + + 10 lack of our attention to this, because we all know + + 11 it's a serious, serious problem, but, we don't + + 12 always have an in. + + 13 And sometimes when we do have an in, we have + + 14 to wait six or eight months to make it, so that when + + 15 the person does get arrested, and I know a lot of + + 16 people -- a lot of bad things can happen to a lot of + + 17 people in that time frame, but when we do get in, we + + 18 can't immediately let them know that this guy was + + 19 the informant, all right, because maybe we're using + + 20 the informant for an ongoing investigation. + + 21 So, I want you to understand it's just not + + 22 that easy for somebody in the community to come up + + 23 and tell either one of the sheriffs or the officer + + 24 from Penn Yan that "There's a drug dealer next to my + + 25 house." + + + + + + + + 44 + 1 We probably already know that, but we can't + + 2 just get in. + + 3 So, in the interest of keeping this short, + + 4 I would like to tell you that, several years ago -- + + 5 I don't know if it was seven, eight years ago -- the + + 6 State of New York, not necessarily these fine + + 7 Senators and our Assemblyman, said: The Rockefeller + + 8 drug laws from the 1970s are Draconian in nature. + + 9 They're just bad laws, and our jails are + + 10 overpopulated with drug users and drug dealers. + + 11 And they said, We need to change that. + + 12 And they changed it. + + 13 And when they said "We need to change that," + + 14 the District Attorneys Association of the State of + + 15 New York looked and said, That's going to create a + + 16 problem. + + 17 Well, here we are, seven, eight years later, + + 18 and we've got that problem. + + 19 So my suggestion to you guys is: + + 20 Help the people that are addicted, for sure. + + 21 Give the judges an out so they can help the + + 22 people that are addicted. + + 23 But as far as the people that are dealing + + 24 these drugs, they got to go to prison, and I say put + + 25 a mandatory minimum prison sentence, and you can + + + + + + + + 45 + 1 give them a maximum sentence too, it doesn't matter. + + 2 But they've got to do some sort of prison, so + + 3 that the judges -- and, I'm sorry, I don't want to + + 4 offend anybody here, but sometimes we have some + + 5 pretty liberal judges that think nobody should go to + + 6 jail, nobody should go to prison. + + 7 Well, I'm telling you, you want to stop some + + 8 of this from going on, you have to put these people + + 9 in prison, the people that are selling it, that are + + 10 making the money. + + 11 Like Assistant DA Cook said, there are people + + 12 out there that are making money, and they're killing + + 13 our own citizens. + + 14 And so I say, you create a stronger + + 15 punishment for the dealers. + + 16 And you create a special crime for a dealer + + 17 that has provided heroin to somebody, or another + + 18 opiate, and they die, and that crime should be + + 19 elevated, and it should be a separate crime. + + 20 And I know there's other things that can be + + 21 in there, but that should be a separate crime. + + 22 [Applause.] + + 23 DA JOSEPH G. FAZZARY: Thank you. + + 24 SENATOR O'MARA: Thank you, Joe. + + 25 That's very helpful, and it's a great segue + + + + + + + + 46 + 1 into our second panel. + + 2 We've got our -- initially, our largest panel + + 3 out of the way. + + 4 Thank you for the law-enforcement respective. + + 5 A number of people I'm talking about on the + + 6 panel, the next panel is our judges. + + 7 We have the Honorable Patrick Falvey, + + 8 Yates County Court Judge, and the + + 9 Honorable Matthew Conlon, Penn Yan Village Court + + 10 Justice. + + 11 I want to thank you, Your Honors, for both + + 12 being with us this evening, and, hopefully, being + + 13 able to share some of your perspectives from the + + 14 bench; and, particularly, as it regards drug courts + + 15 and alternatives, or, particularly, lack of + + 16 alternatives, that you may see in the struggles you + + 17 deal with, and how to handle some of these cases, + + 18 particularly with regards to the addicts in that. + + 19 And, Judge Falvey, you've been our county + + 20 court judge here in Yates County for a long time + + 21 now. + + 22 I thank you for your many years of service, + + 23 and look forward to your perspective here. + + 24 Thank you for being here. + + 25 HON. W. PATRICK FALVEY: Thank you, + + + + + + + + 47 + 1 Senator O'Mara, and Senators, and Mr. Assemblyman, + + 2 and it's great to see everyone in the community here + + 3 tonight, and showing their interest in this very + + 4 serious issue that we have before us. + + 5 Among my duties as county judge and family + + 6 court judge, et cetera, I preside over the treatment + + 7 court, the Yates County Drug Treatment Courts, and + + 8 I've done that since its inception in 2002. + + 9 And I just wanted to give a little overview + + 10 of what our treatment courts are. + + 11 Our treatment courts are a collaborative + + 12 approach of the bench; the bar, which is prosecution + + 13 and defense; treatment providers; probation and law + + 14 enforcement; and it's there to stem, or attempt to + + 15 stem, the overwhelming impact of drug-related + + 16 offenses facing the criminal justice system. + + 17 Participants are those convicted of certain + + 18 crimes who face the possibility of jail or a state + + 19 prison sentence. + + 20 Some have co-dependent disorders, such as + + 21 mental-health issues as well. + + 22 The objective is to rehabilitate alcohol or + + 23 substance-abuse offenders while protecting the + + 24 community, by reducing drug-related crimes and + + 25 reducing recidivism through a program of intensive + + + + + + + + 48 + 1 court supervision and treatment. + + 2 The ultimate goal that we are trying to + + 3 obtain is to break the cycle of addiction and create + + 4 an environment which encourages law-abiding conduct, + + 5 education, and gainful employment. + + 6 Participants must be approved for + + 7 participation after being evaluated, to determine + + 8 that they have a drug or alcohol abuse or + + 9 dependency, and have agreed to participate by + + 10 signing a contract to complete the program + + 11 successfully. + + 12 Participants are on probation. + + 13 Some are even required to serve time in jail + + 14 first, depending on their crime, and all agree to + + 15 follow through with treatment and attend + + 16 drug-treatment court. + + 17 They are subject to random drug and alcohol + + 18 testing, and being drug- and alcohol-free for at + + 19 least one year makes a participant eligible for + + 20 graduation, followed, usually, by a continuation on + + 21 probation as a form of aftercare. + + 22 Now, relapse or dishonesty will subject the + + 23 participant to graduated sanctions, including + + 24 community service, time in jail, or discharge from + + 25 the program. + + + + + + + + 49 + 1 In addition, appropriate conduct by a + + 2 participant is as important as maintaining sobriety. + + 3 It's an absolute requirement, in our opinion. + + 4 Therefore, failure to conduct oneself + + 5 appropriately by not following the rules, or + + 6 committing a new crime, may result in immediate + + 7 expulsion and a potential sentence to prison. + + 8 So, drug court is not easy. It requires the + + 9 participant to take responsibility to achieve + + 10 success. + + 11 And since the inception of our treatment + + 12 court, we have dealt with abuse of or addictions to + + 13 alcohol, cocaine, marijuana, methamphetamines, + + 14 synthetic drugs, prescription medicines, opioids, + + 15 and now, of course, heroin. + + 16 That's why we're here. + + 17 I believe, as many of my colleagues, that + + 18 drug courts were created to differentiate between + + 19 those with substance-abuse disorders and drug + + 20 dealers selling for profit. + + 21 This gave us great latitude in determining + + 22 the circumstances of a particular case in order to + + 23 measure if a person truly was an addicted or + + 24 dependent, or, was selling for profit. + + 25 However, which has already been alluded to, + + + + + + + + 50 + 1 in 2009, with the reform of the Rockefeller drug + + 2 laws, legislation entitled judicial-diversion + + 3 program for certain felony offenders was passed. + + 4 This expanded the type of drug and marijuana + + 5 crimes that could be considered for drug court, + + 6 including serious felonies involving persons who + + 7 were selling drugs for profit. + + 8 Now, this has caused us to struggle, for, + + 9 before, sellers were usually not qualified for the + + 10 program, but now they can be by statute. + + 11 For instance, we now need to consider and + + 12 determine "what is a sale under the law?" for a sale + + 13 does not always involve the exchange of money, or + + 14 whether the person is selling to support a habit, or + + 15 doing so for profit. + + 16 Now, I also believe that use and sale of + + 17 heroin is only the tip of the iceberg, for I see the + + 18 influx of heroin, other drugs, and alcohol in our + + 19 family court as well, with devastating effects, + + 20 resulting in child neglect, abuse, domestic + + 21 violence, juvenile delinquency, educational issues. + + 22 This leads to social and monetary costs to + + 23 the community not only for incarceration and foster + + 24 care, but expenses for children born of addicted + + 25 mothers, and the general breakdown of the family + + + + + + + + 51 + 1 unit. + + 2 Currently, we are seeing more treatment-court + + 3 participants resulting from possession, sale, and + + 4 use of heroin. + + 5 In the past, approximately 69 percent of our + + 6 participants were addicted in some form to alcohol. + + 7 And now almost 90 percent are addicted to + + 8 heroin; yet, heroin has been around for a long time. + + 9 I recall its scourge when in Vietnam 46 years + + 10 ago. + + 11 It is still here today, however, because it's + + 12 inexpensive and so dangerously addictive. + + 13 It attacks the very social fabric of our + + 14 community, our sense of order, children, families, + + 15 and society. + + 16 Therefore, I would respectfully recommend to + + 17 this honorable panel the following: + + 18 First, treatment options. + + 19 In the long run, successful treatment does + + 20 work, and the community and public health clearly, + + 21 I believe, benefits, especially in the comparison to + + 22 costly -- the costly temporary solution of housing a + + 23 person in state prison, or a child in foster care or + + 24 detention, for an extensive period of time, and + + 25 recovery is still not achieved. + + + + + + + + 52 + 1 Therefore, the State should encourage more + + 2 Office of Alcoholism and Substance Abuse Services, + + 3 called "OASAS," certified treatment facilities with + + 4 emphasis in the rural areas, and increase staffing + + 5 for existing treatment facilities for all types of + + 6 treatment, especially outpatient and halfway houses, + + 7 including addressing specific treatment needs unique + + 8 to women, children, and, yes, men. + + 9 The Department of Health or OASAS or some + + 10 other agency of the state should collect data to + + 11 determine which treatment elements offer the most + + 12 success for recovery of addiction, so that those + + 13 elements can be incorporated into patient care + + 14 statewide and avoid duplication of expense and time. + + 15 For example, data on Vivitrol as a treatment + + 16 element should be evaluated, and treatment protocols + + 17 examined, to see what is most effective. + + 18 The State could encourage health-insurance + + 19 companies to provide coverage for those treatment + + 20 elements found to be most successful. + + 21 Early detection: + + 22 Prevention in the form of early detection of + + 23 drug and alcohol use by our young people is key. + + 24 More intensive monitoring by family and + + 25 friends who are not afraid to speak up is needed to + + + + + + + + 53 + 1 recognize there is an issue before they grow into + + 2 the extent of heroin use. + + 3 This would help address the demand, + + 4 I believe, for the drug as well. + + 5 Crime prevention: + + 6 I strongly urge the Legislature to consider + + 7 modifying the judicial-diversion statute by + + 8 excepting out drug sellers, in order to allow the + + 9 treatment courts, in collaboration with law + + 10 enforcement who are on our on staffing teams, more + + 11 flexibility to determine the eligibility, on a + + 12 case-by-case basis, in order to determine if a + + 13 person is truly addicted, or is selling for profit. + + 14 I believe the rise of heroin in our community + + 15 shows the drug-court concept is even more important + + 16 and relevant now to help identify those who are + + 17 substance-abuse-dependent versus one who is a + + 18 seller. + + 19 Although incarceration may be the necessary + + 20 and proper temporary solution, I believe successful + + 21 treatment provides the best chance for the better + + 22 long-term result of recovery and a successful return + + 23 to society. + + 24 One of the ten key components to the + + 25 successful drug court is partnership with the + + + + + + + + 54 + 1 support of the community, people such as yourselves. + + 2 Together we have achieved some successes. + + 3 We have 71 graduates to date. + + 4 While at the same time, this current heroin + + 5 epidemic has challenged us all -- law enforcement, + + 6 courts, treatment -- to rethink and retool our + + 7 assets in order to continue to assure community + + 8 acceptance, community safety, and reduction in + + 9 recidivism through judicially-supervised treatment. + + 10 I thank this honorable Task Force for the + + 11 opportunity to address you this evening. + + 12 Thank you. + + 13 [Applause.] + + 14 SENATOR O'MARA: Thank you, Judge Falvey. + + 15 And just briefly, before we move on to + + 16 Justice Conlon, the -- I'm going to go out of order + + 17 on the next panel, primarily because my neck is + + 18 getting stiff looking left for this long. + + 19 So I'm going to go over to the right for + + 20 another panel, all the way down to the end, next. + + 21 So, get ready down there. I don't want you + + 22 to sweat any more than you're already sweating, + + 23 waiting for this to happen. + + 24 But -- and to go, briefly, perhaps, + + 25 politically, back to 2009, when the Rockefeller drug + + + + + + + + 55 + 1 laws were voted out, two of us here were in office, + + 2 Senator Amedore and myself. We were in the Assembly + + 3 at that time in the Republican Minority, and we both + + 4 voted "no" on that action at that time. + + 5 There was a period in New York State's modern + + 6 history, the only two years of which the + + 7 New York State Senate had a majority of Democrats. + + 8 That was the year in which the Rockefeller + + 9 drug laws were taken away. + + 10 The next year was the year that the + + 11 gap-elimination adjustment was put in in our local + + 12 school districts, to cut into deficits that we had + + 13 from overspending in New York State, which cut + + 14 drastically into our schools and our programs and a + + 15 lot of extra activities, that I think would be + + 16 helpful and useful for the prevention aspects of + + 17 what we talk about as part of our processes here. + + 18 That's just a side note, but I wanted to make + + 19 it clear where I stood on the Rockefeller drug-law + + 20 reforms, and I know Senator Amedore did as well. + + 21 So with that, Justice Conlon, thank you for + + 22 being here with us. + + 23 HON. MATTHEW CONLON: I'd like to thank + + 24 everyone for coming out tonight, and thank the + + 25 Senators for having us. + + + + + + + + 56 + 1 As Penn Yan Village Justice, I hear over + + 2 200 cases per month, making Penn Yan Village Court, + + 3 far and away, the busiest justice court in + + 4 Yates County. + + 5 Of those 200-plus cases, well over 80 percent + + 6 are criminal cases, and of those criminal cases, + + 7 over half are drug-related. + + 8 Approximately one-third of the drug-related + + 9 criminal cases I hear involve heroin directly. + + 10 That's about one a day. + + 11 Additionally, not included in that statistic, + + 12 are cases that involve heroin indirectly, such as + + 13 personal crimes, including assaults over drug deals + + 14 gone bad, and, property crimes, such as burglary and + + 15 larceny to support a drug habit. + + 16 As a local attorney for over 25 years, I both + + 17 prosecuted and defended individuals caught up in the + + 18 vicious cycle of drug abuse. + + 19 I participated in the federally-sponsored + + 20 drug treatment-court training, as Judge Falvey + + 21 alluded to, mandated at the inception of the local + + 22 treatment courts a decade ago, and I have + + 23 participated in the treatment courts in Yates, + + 24 Seneca, and Ontario counties. + + 25 As a sitting justice and a practicing lawyer, + + + + + + + + 57 + 1 I urge a greater commitment to the education of town + + 2 and village justices. + + 3 I know of no town or village justice that + + 4 I have talked to that wouldn't appreciate more + + 5 training, and especially more training in the area + + 6 of drug crimes. + + 7 Specifically, I wish to note the dubious + + 8 distinction that New York leads the nation in the + + 9 percentage of town and village justices that are not + + 10 legally trained. + + 11 While this may have made sense 100 years ago, + + 12 we are now dealing with heroin. + + 13 We are now confronted with often + + 14 sophisticated drug dealers who use cell phones and + + 15 the Internet in their drug trade. + + 16 We are using an antiquated nineteenth-century + + 17 justice court system to confront a modern + + 18 twenty-first-century problem. + + 19 I think it's shocking to know that New York + + 20 requires only 12 hours a year of training for town + + 21 and village justices, less than that required to be + + 22 a licensed beautician or manicurist in the state of + + 23 New York. + + 24 We local judges are often called on to review + + 25 search-warrant applications, rule on complex + + + + + + + + 58 + 1 evidentiary issues, and conduct hearings at the very + + 2 outset of the beginning of serious drug cases. + + 3 In drug cases we have to be familiar with + + 4 such terms as "aggregate weight," "possession with + + 5 intent to sell," and the "reagent test." + + 6 An amateur judge, however well-meaning, is + + 7 much more likely to make an error on a legal ruling + + 8 or in the application of criminal-procedure law than + + 9 a lawyer justice. + + 10 This sometimes results in big-time drug + + 11 dealers going free because of some technical error + + 12 on the part of an untrained local justice. + + 13 I do see hope, however, that no time in my + + 14 25-year legal career have I seen more lawyer + + 15 justices than I do now. + + 16 And I note that we have lawyer justices in + + 17 Seneca County, in Ontario County, in Wayne County, + + 18 and I think this is the growing trend among + + 19 New York's justice courts. + + 20 I think it's time to level the playing field + + 21 so that the judges have the same training and + + 22 expertise as those who argue cases before them. + + 23 We owe that to the crime victims as much as + + 24 to those standing accused of crimes. + + 25 Until lawyer justices are the rule and not + + + + + + + + 59 + 1 the exception in New York, I urge the State to + + 2 address this lack of training and education for town + + 3 and village justices, particularly as it relates to + + 4 drug crimes, to ensure that all offenders, including + + 5 those caught up in this scourge of heroin, are dealt + + 6 with fairly according to the law. + + 7 Thank you. + + 8 SENATOR O'MARA: Thank you, Judge. + + 9 [Applause.] + + 10 SENATOR O'MARA: And now we're going to shift + + 11 over to the right with our panel of individuals who + + 12 have been impacted personally by the heroin epidemic + + 13 that we struggle with in our communities, and that + + 14 panel is made up of: + + 15 Janet Heaven; + + 16 Arianna Chadwick and Donna McKay; + + 17 Alexis Pleus, Truth Pharm; + + 18 Devon Pierce; + + 19 And, Gail Owen. + + 20 Janet, are you comfortable with leading us + + 21 off? + + 22 Thank you. + + 23 Thank you for being here. + + 24 JANET HEAVEN: Hi. + + 25 Hi, my name is Janet Heaven. + + + + + + + + 60 + 1 My husband and I lost our son on January 5, + + 2 2016, because of heroin. + + 3 Thank you for allowing me the opportunity to + + 4 tell our story. Our hearts are broken. + + 5 Chad James Heavens spent two weeks in the + + 6 ICU, Rochester General Hospital, starting on + + 7 December 23, 2015, and ending on January 5, 2016. + + 8 His body was septic. He had no known + + 9 infection. He had a 105 fever. He had hepatitis C. + + 10 His blood pressure was off the charts. He had a + + 11 collapsed lung. + + 12 He was never conscious during those two weeks + + 13 in the hospital. + + 14 The doctors were not sure what exactly + + 15 happened. + + 16 We had to make the horrible decision to let + + 17 him go. + + 18 It could have been a bad needle. + + 19 It could have been a bad batch of heroin. + + 20 It could have been an overdose. + + 21 It could have been any number of things, but + + 22 the end result was death. + + 23 The reason I want to speak to all of you is + + 24 to try and save other addicts, parents, family + + 25 members, from this incredible pain and having to go + + + + + + + + 61 + 1 through the horrific ordeal. + + 2 We need to raise awareness and provide + + 3 education, treatment, and most of all, prevention. + + 4 Our son was a great fun-loving child full of + + 5 life, fun, and energy. + + 6 Here are a few pictures of him. + + 7 You wont be able to see him, but there they + + 8 are. + + 9 No child hopes to become an addict when he + + 10 grows up, and no parent hopes to raise an addict. + + 11 No human is immune to this addiction. + + 12 The addict is not the only person affected by + + 13 this horrible drug. + + 14 When people try to help an active addict, + + 15 they are setting themselves up to be hurt. + + 16 A heroin user will lie, cheat, steal, pawn, + + 17 and do, basically, anything to get high. + + 18 Nice people will offer them a place to stay, + + 19 and the next thing you know, their money, jewelry, + + 20 electronics are missing, and the addict has gone on + + 21 to the next kind soul trying to help this person who + + 22 can tell a great story and get what they need. + + 23 My good friend says, "If a heroin addict's + + 24 lips are moving, they're lying." + + 25 This is so sad but true. + + + + + + + + 62 + 1 Our son started out -- started his + + 2 experimentation with drugs in high school, drinking, + + 3 pot, pills. + + 4 The Stark Program which is a boot camp for + + 5 kids, maybe it was helpful, but not enough. + + 6 He had some minor brushes with law + + 7 enforcement, and the use of marijuana and pills + + 8 escalated to more powerful addictive drugs, and most + + 9 recently, heroin. + + 10 Parents and family members need to know what + + 11 to do when their kids are using drugs, what their + + 12 options are. + + 13 Based on our experience, we feel long-term + + 14 rehab, 90 days, is critical. + + 15 After graduating from high school, our son + + 16 joined the Army and he loved being in the Army. + + 17 He spent time in Iraq and excelled at being a + + 18 soldier. + + 19 He was stationed in Hawaii, and later + + 20 transferred to Colorado. + + 21 He had two wonderful daughters who now have + + 22 no father, and their mother is an active addict + + 23 still in denial. They both lost custody of their + + 24 daughters because they were unable to get clean. + + 25 Fortunately, the girls have been adopted by a + + + + + + + + 63 + 1 loving family. + + 2 We helped our son numerous times to get help. + + 3 He had been in four different 30-day rehabs, + + 4 was fine for a while after he got out, but could not + + 5 stay clean. + + 6 We need to help our -- we tried to help our + + 7 son, time and time again, by giving him clothing, + + 8 money, a car to drive, and a place to stay. + + 9 When our son was clean and sober, he was + + 10 awesome. + + 11 When he was using, he was not a pleasant + + 12 person and we lived in fear. + + 13 He had been arrested a couple of times for + + 14 stealing and was in jail for a few days or weeks at + + 15 a time. + + 16 I can remember, as awful as this may seem, + + 17 that I wished they would keep him in jail, because + + 18 I knew he had three meals a day and a place to lay + + 19 his head at night. + + 20 If I may, I'd like to talk about some myths + + 21 and clarify them with facts on my own experience. + + 22 Myth: Heroin is cheap. + + 23 Fact: Heroin is not cheap. + + 24 It cost my son numerous Xboxes, + + 25 PlayStations, TVs, furniture, jewelry, watches, + + + + + + + + 64 + 1 phones, cars, and more. + + 2 It cost him his dignity, his self-esteem, his + + 3 self-respect. + + 4 It cost him a decent apartment and all its + + 5 furnishings, and, sadly, his daughters. + + 6 It cost him his life at the age of 28. + + 7 Myth: My friend gets it for me. + + 8 The fact is, your heroin dealer is not your + + 9 friend. He or she is a heroin dealer. + + 10 If he were your friend, you would be alive to + + 11 talk about it. + + 12 If he were your friend, you wouldn't have + + 13 gotten started in the first place because friends + + 14 don't want their friends dead. + + 15 Hold on. + + 16 Myth: Heroin dealers look like thugs. + + 17 And the fact is, they can look like a + + 18 choirboy, be well-spoken, well-mannered, very + + 19 charming, and come from a decent home just like you + + 20 did. + + 21 Myth: Heroin is the ultimate high. + + 22 The fact is, while the rush lasts minutes, + + 23 withdrawal symptoms are always waiting for you. + + 24 They include muscle and bone pain, diarrhea + + 25 and vomiting, abdominal cramps, insomnia, + + + + + + + + 65 + 1 restlessness, runny nose, cold flashes and + + 2 goosebumps, sweating, involuntary kicking motions, + + 3 racing pulse, high blood pressure, increased + + 4 respiratory rate, and severe anxiety. + + 5 Myth: I can handle it. + + 6 The fact is: + + 7 Chad James Heaven, December 18, 1987, to + + 8 January 5, 2016. + + 9 James and Janet Heaven, parents who lost + + 10 their son, Chad, 28, on January 5, 2016, to heroin. + + 11 Please feel free to ask me any questions, and + + 12 I am open and honest and willing to share my + + 13 thoughts and feelings. + + 14 Thank you for your time. + + 15 [Applause.] + + 16 SENATOR O'MARA: Wow. + + 17 Janet, thank you very much for sharing that + + 18 with us, and we're so sorry for what you've been + + 19 through, and for your loss of your son. + + 20 JANET HEAVEN: Thank you. + + 21 SENATOR O'MARA: Truly, it's an unbelievable + + 22 account of the tragedy of what we're dealing with + + 23 here. + + 24 So thank you so much for being here this + + 25 evening. + + + + + + + + 66 + 1 JANET HEAVEN: You're welcome. + + 2 SENATOR O'MARA: Arianna and Donna, you two + + 3 are together? Or are you -- okay. + + 4 ARIANNA CHADWICK: My name is + + 5 Arianna Chadwick. + + 6 I overdosed on January 7, 2016, and the whole + + 7 experience was completely shocking in every way that + + 8 you can imagine. + + 9 There was the obvious, Oh, my God, what + + 10 happened? Who are all these people in my house? + + 11 Then it hit me, this overwhelming feeling of + + 12 guilt and embarrassment, and the thought of, Oh, + + 13 great, now I'm just a junky to all these people. + + 14 And it's that guilt and embarrassment that + + 15 prevents so many from getting help, especially in + + 16 these small towns. + + 17 Our community has done a lot to get rid of + + 18 that stigma. + + 19 Now we actually need to put the resources + + 20 together and offer ways for people to get help and + + 21 make it easy to access. + + 22 Finding the resources to get help shouldn't + + 23 be such a difficult thing. + + 24 When the problem is bad, information on any + + 25 resources to help with the substance abuse should be + + + + + + + + 67 + 1 readily available. + + 2 I think there should be a website that is a + + 3 database of resources that you can search by county + + 4 for the entire state. + + 5 Inpatient facilities, outpatient, sober + + 6 living, NA meetings, substance-abuse counselors, all + + 7 that information should be in one place because it + + 8 is so overwhelming to figure out what is actually + + 9 available in your area. + + 10 I think we should offer a protocol to all + + 11 hospitals in the state on how to deal with an + + 12 overdose, because it's quite clear they don't know + + 13 exactly how to handle it in these small communities. + + 14 I understand being firm with a patient in my + + 15 situation, but the doctors shouldn't be talking + + 16 about your condition so loudly that the entire ER + + 17 knows what is going on with you. + + 18 The hospital just didn't know what to do or + + 19 say to me. + + 20 A social worker didn't come talk to me. + + 21 They didn't talk to me about detox or rehab + + 22 facilities. + + 23 All they did was hand me a packet of + + 24 information that YSAC had put together, and sent me + + 25 on my way. + + + + + + + + 68 + 1 Thankfully, I have a family to help me + + 2 navigate everything, but not everyone does. + + 3 I think offering hospitals a basic protocol + + 4 to handling overdose patients will make it easier to + + 5 make sure everyone gets offered the same information + + 6 and care. + + 7 I think an amnesty program would be + + 8 beneficial to the addicts in our area. + + 9 There were so many nights where all I wanted + + 10 to do was go get help, but there is nowhere to go in + + 11 our area at two in the morning. And if there is, + + 12 the addicts don't know about it. + + 13 The window to actually help someone isn't + + 14 that big. + + 15 They need to be able to get the help when + + 16 they want it, because it is so hard to talk yourself + + 17 into taking that step and to find the motivation to + + 18 try. + + 19 I also want to ask you to take into + + 20 consideration that what works in bigger cities might + + 21 not work in rural communities like ours. That there + + 22 probably won't be examples of programs that were + + 23 successful for communities like ours because heroin + + 24 being such a big problem in rural areas is a + + 25 relatively new thing. + + + + + + + + 69 + 1 So we might just have to take a chance and + + 2 try different programs until we find one that fits. + + 3 I understand that it's frustrating, from a + + 4 legislative point of view, because it is hard to + + 5 justify spending our money on something that isn't a + + 6 guarantee. + + 7 But if it helps just a few people, then it + + 8 was successful, especially in the eyes of the family + + 9 affected by this addiction. + + 10 Thank you so much for allowing me to speak. + + 11 [Applause.] + + 12 SENATOR O'MARA: Thank you. + + 13 Thank you, Arianna, for sharing that with us. + + 14 I know that it's not easy to talk about that. + + 15 It's not easy to talk in front of a crowd of + + 16 people like this either. + + 17 Donna is with you. + + 18 Did you have anything you wanted to add, + + 19 Donna? + + 20 DONNA McKAY: Hi. + + 21 Thank you for allowing me to do this. + + 22 I'm here as a community member, as well as an + + 23 aunt to a recovering addict. + + 24 I just wanted to talk a little bit about the + + 25 insurance. I know we already touched on that. + + + + + + + + 70 + 1 It's a big issue. + + 2 And Ari's right, that when somebody goes + + 3 forward and they say, "I need help," they need it + + 4 then. + + 5 They can't wait 24 hours. They can't wait + + 6 until the next week. + + 7 They need it right then and there. + + 8 I was doing some research online, and + + 9 I firmly believe that we cannot arrest our way out + + 10 of this. + + 11 I'm not saying that some people don't need to + + 12 be arrested, but I -- I want to mention that, also, + + 13 that the most -- most of the dealers around here are + + 14 addicts themselves. + + 15 You get in the bigger cities, then you have + + 16 the ones who aren't addicts. + + 17 But I'm pretty sure, what from I've heard, + + 18 and the people I've talked to, that, around here, + + 19 they're addicts as well. + + 20 So, to the amnesty program, I found one that + + 21 a chief of police in Gloucester, Massachusetts, + + 22 created, and it's called" Police-Assisted Addiction + + 23 and Recovery Initiative." + + 24 From what I read, the program allows for + + 25 people to bring in their drugs and drug + + + + + + + + 71 + 1 paraphernalia, and not be arrested or serve any jail + + 2 time for possession. + + 3 They are also immediately taken to a hospital + + 4 or facility for detox and hooked up with an ANGEL. + + 5 The ANGEL will be with them for the entire + + 6 process to offer support and encouragement. + + 7 And from there, other resources are available + + 8 to help them continue with their recovery. + + 9 Although I am told that they wouldn't be + + 10 arrested or jailed if they walked in the sheriff's + + 11 department here, and asked for help, there is + + 12 nothing that is set in stone. + + 13 I believe that if it was put on paper and + + 14 advertised and gotten around our community, that + + 15 people would start to feel more comfortable and come + + 16 forward for help. + + 17 This program can help conquer two things: + + 18 getting drugs and paraphernalia off the streets, and + + 19 getting help for those who want help. + + 20 In my opinion, this would be a huge step + + 21 forward. + + 22 I also have done some research on + + 23 sober-living homes. Studies have shown that + + 24 aftercare programs such as this can better one's + + 25 chances at avoiding relapse and maintaining + + + + + + + + 72 + 1 sobriety. + + 2 According to a study that was highlighted in + + 3 the "Journal of Psychoactive Drugs," abstinence + + 4 rates went from 11 percent, to 68 percent, over + + 5 6- and 12-month follow-ups, and another facility + + 6 with had an increase, from 20, to 40 percent, after + + 7 just 6 months in sober-living homes. + + 8 These are great results, considering the + + 9 success rate for heroin users trying to quit on + + 10 their own is a mere 4 percent. + + 11 These homes are designed to offer a clean, + + 12 healthy, well-structured environment, peer support, + + 13 healthy activities, freedom to make their own + + 14 healthy choices, allows them to be responsible for + + 15 themselves. + + 16 It teaches the living skills, like cooking + + 17 and money management, which is so important, and + + 18 offers a place for them to grow in success and + + 19 self-worth. + + 20 This, without a doubt, is worth it. + + 21 Going to elementary level, middle-school + + 22 level, as part of a prevention method, I believe + + 23 that our community center needs to have some + + 24 activities for us to do, for where to take our + + 25 children. + + + + + + + + 73 + 1 We have nothing to take our children to. + + 2 I know they started a weight, exercise, + + 3 place, but I'm talking like, pool, like a public + + 4 pool, laser tag, something fun, roller skating, + + 5 something to get -- and not just thrown out there + + 6 for anybody to walk into and just use, but to have + + 7 it managed by responsible people and people who are + + 8 overlooking and overseeing this. + + 9 So, it's also creating jobs. + + 10 Just wanted to put that out there. + + 11 And I understand that this is a lot to ask, + + 12 but is it worth it? + + 13 Is it worth the price of our community + + 14 members' lives to ignore that these are real + + 15 possibilities? + + 16 And, thank you. + + 17 [Applause.] + + 18 SENATOR AMEDORE: Hi, Donna. + + 19 It's Senator Amedore. + + 20 And, you know, you mentioned the initiative + + 21 from Massachusetts, and I think it's "P.A.A.R.I.," + + 22 that it -- what it spells out. + + 23 And the Task Force, both Senator Murphy and + + 24 myself, today we were in Oneonta, and talking to + + 25 sheriffs that implement this initiative. + + + + + + + + 74 + 1 So it is here. + + 2 It's something that we're going to be -- the + + 3 Task Force is going to be looking at more into, so + + 4 that we can implement, possibly, some of the + + 5 initiatives of that particular program, and see how + + 6 we can adapt it. + + 7 You also mentioned a community center. + + 8 And one of things that OASAS has just + + 9 announced was their "clubhouse" programs that they + + 10 are starting to put in in specific regions. + + 11 So, with some time, hopefully, we'll see in + + 12 this region, and I'm sure Senator O'Mara will be + + 13 working on a clubhouse to be brought into an area + + 14 here in the 58th Senate District, and that will + + 15 help with the community activities. + + 16 Thank you. + + 17 SENATOR O'MARA: Thank you very much, Donna, + + 18 for sharing that with us, and for your involvement + + 19 with Arianna in helping through this very difficult + + 20 situation. + + 21 It's extremely important to have that kind of + + 22 support, which is often lacking in these -- in many + + 23 of these situations, but, certainly not -- certainly + + 24 not all. + + 25 Next up, Alexis. + + + + + + + + 75 + 1 Ready to go? + + 2 ALEXIS PLEUS: Hi. + + 3 Thank you. + + 4 I'm going to read for a change. + + 5 If I talk, I'll just keep talking all night. + + 6 I'm the mother of three sons, but today I'm + + 7 here to tell you about my firstborn son Jeff. + + 8 Jeff was a good student, popular, and a great + + 9 athlete. + + 10 He was charismatic, kind, and always stuck up + + 11 for the underdog. + + 12 He was absolutely passionate about everything + + 13 that he did. + + 14 He graduated high school in 2004, and went on + + 15 to college, and even played football and wrestled + + 16 there. + + 17 Upon completion, he was a chef at excellent + + 18 restaurants. He was very successful, and he lived + + 19 independently. + + 20 In 2011 I got a phone call that my son had + + 21 been arrested for house burglaries; my son, my son + + 22 who was raised well, and as far as I knew, had never + + 23 gotten in trouble beyond speeding tickets. + + 24 I was devastated, but even more so, when + + 25 I met with the public defender, and when I said to + + + + + + + + 76 + 1 him, "My son couldn't have done this," he said to + + 2 me, "A lot of things that heroin addicts do don't + + 3 make sense." + + 4 You could have knocked me over with a + + 5 feather. + + 6 When Jeff got out of jail, he hooked up with + + 7 a gal who had a toddler boy already. + + 8 I had custody of this child more than once + + 9 due to their active addiction and heroin use. + + 10 Jeff and the mother wanted and attempted to + + 11 get treatment on more than one occasion. + + 12 We had times he was shaking and sweating on + + 13 my couch, going into withdrawals, while we called + + 14 our local detox center every hour on the hour, as + + 15 they instructed us to do to meet the bed lottery. + + 16 He did, at one point, get inpatient treatment + + 17 in Syracuse, but he had to fake being suicidal to + + 18 get them to keep him for an entire 21 days, and even + + 19 he knew that that wasn't enough treatment. + + 20 He begged me not to take him home, but they + + 21 made me. + + 22 He relapsed within a month. + + 23 The gal became pregnant with my grandchild. + + 24 And shortly after my son was arrested and + + 25 sent to jail again, I was given custody of her son, + + + + + + + + 77 + 1 and she entered treatment at an inpatient methadone + + 2 clinic, where she met a new fellow. + + 3 He signed on as the father of my grandson + + 4 when he was born in June of 2013. + + 5 At that time, the child I had custody of was + + 6 given back to her through County Services. + + 7 Jeff spent eight months in jail, and was + + 8 doing very well upon release. + + 9 I truly felt like I had my son back. + + 10 He had filed to establish paternity of his + + 11 son, and was anxious to have his life fully in + + 12 order, so that when he could see his son, he would + + 13 be able to provide him with a wonderful home. + + 14 My grandson was nearly eight months old when + + 15 the judge finally ordered the DNA test. + + 16 Two days later, and just one day prior to + + 17 that scheduled DNA test, the baby died. He was + + 18 accidentally smothered during an afternoon nap by + + 19 the boyfriend. + + 20 Needless to say, Jeff was devastated. + + 21 Remarkably, he soldiered on, maintaining + + 22 sobriety for another six months, and I'll be forever + + 23 grateful for that time with him. + + 24 On Saturday, August 2, 2014, Jeff had his + + 25 10th class-year reunion. So many people who saw my + + + + + + + + 78 + 1 son said it was the greatest they had seen him look + + 2 in years. + + 3 Sunday, Jeff came over and we had a nice big + + 4 family breakfast. + + 5 Monday, Jeff went golfing with his dad and + + 6 his little brother John and his girlfriend. + + 7 Monday night, Jeff used heroin, and it killed + + 8 him. + + 9 On Tuesday morning, just 18 months ago, they + + 10 found my son's body. + + 11 Every day, from the day that I learned I was + + 12 carrying my son, to the day of his death, he was + + 13 loved, but love was not enough to conquer this + + 14 addiction. + + 15 I'm not sure what would have been enough for + + 16 Jeff, but I know that he was not offered the types + + 17 or lengths of treatment that we know to be + + 18 effective, and there are types of treatment and + + 19 lengths of treatment that we already know are proven + + 20 to be effective. + + 21 We just don't offer it here. + + 22 Though to us the cost of our loss is beyond + + 23 measure, I have taken to providing the economics of + + 24 the situation in case there is any confusion at all + + 25 about the cost to society and the taxpayer, and how + + + + + + + + 79 + 1 truly logical and what economic sense it makes to + + 2 provide effective and long-term treatment for anyone + + 3 who seeks it. + + 4 The cost of Jeff and his fiancee's addiction + + 5 are as follows: + + 6 At least four separate criminal proceedings. + + 7 A combined eleven months of jail stay between + + 8 the two of them in two different jails. + + 9 At least two criminal proceedings for the + + 10 mother. + + 11 At least four criminal proceedings for my + + 12 son. + + 13 Eighteen months of combined probation. + + 14 Two hepatitis C treatments at $100,000 each. + + 15 My son was hospitalized for sepsis and he nearly + + 16 lost his arm. Those were unpaid medical bills. + + 17 Methadone program for the pregnant mother. + + 18 My son and the mother were both on social + + 19 services and Medicaid at the time, and he had a + + 20 college degree. + + 21 My son and the mother had many unpaid medical + + 22 bills. + + 23 There was a negligence proceeding against my + + 24 son and the mother for having her child in their + + 25 care while they were in the act of addiction. + + + + + + + + 80 + 1 These proceedings were run separately in + + 2 family court, and included 4 Court-appointed + + 3 attorneys, a County attorney, 2 County caseworkers, + + 4 in addition to the court staff, and required over + + 5 20 appearances over the course of a full year. + + 6 There was foster care for the boy for + + 7 eight months, and not to mention all the emotional + + 8 damage that that child has suffered. + + 9 There's all the victims of the crimes that + + 10 the two committed. Family members: father, mother, + + 11 brothers, sister, grandmother was even stolen from; + + 12 not to mention retailers. + + 13 Family court custody proceedings for the + + 14 little boy that I had custody of, which included + + 15 four Court-appointed attorneys, a County attorney, a + + 16 caseworker, in addition to the court staff. + + 17 Between the custody hearings, visitation + + 18 hearings, and violation proceedings, we were in + + 19 family court over 40 times in the course of 3 years. + + 20 The family had Family Services through the + + 21 County for 18 months. + + 22 And then, finally, the County coroner and the + + 23 child-fatality review team to review the death of my + + 24 grandchild. + + 25 And then the County coroner for the autopsy + + + + + + + + 81 + 1 of my son. + + 2 These are a great cost to our society, and + + 3 I'm quite certain that if my son and his fiancee had + + 4 been provided treatment, society would not have had + + 5 these costs. + + 6 I founded an advocacy organization called + + 7 "Truth Pharm." + + 8 We work to reduce the stigma and raise + + 9 awareness. + + 10 We create, implement, and advocate for + + 11 programs and policy change to have a profound impact + + 12 on the opioid epidemic. + + 13 We're responsible for implementing four + + 14 P.A.A.R.I. programs in our area. + + 15 One of the problems that we'll have in + + 16 New York State with implementing the P.A.A.R.I. + + 17 programs is that our hospitals are not willing to + + 18 participate as they were in Gloucester, + + 19 Massachusetts, where they are willing to house + + 20 people who were addicted until they could find + + 21 treatment. + + 22 In New York State, they're just not willing + + 23 to do that. + + 24 We also provide community-response action + + 25 plans, and we've developed a best-practices + + + + + + + + 82 + 1 procedures for hospitals to handle overdoses. + + 2 Unfortunately, hospitals aren't willing to do + + 3 that, and they need to be required to do so. + + 4 It isn't time for a Band-Aid. It's time for + + 5 a tourniquet. + + 6 This epidemic is insane. + + 7 Mothers are losing sons. Fathers are losing + + 8 daughters. Siblings, like my boys, are left with + + 9 the loss of their brothers and sisters for a + + 10 lifetime. + + 11 We're creating orphans at an alarming rate, + + 12 which is anyone's guess what the consequence of that + + 13 will be 10 to 20 years down the road. + + 14 Everything that we do, aside from the things + + 15 that I'm going to mention, in my opinion, are a + + 16 waste of time right now. + + 17 We have people right now who are dying and + + 18 who need help, and who, literary, ask for help. + + 19 I met with three other mothers before coming + + 20 here this evening, so there's four mothers at the + + 21 table. + + 22 Three of us, our loved ones asked for help + + 23 and sought help before they ever entered the + + 24 criminal justice system. + + 25 Our law-enforcement folks are doing an + + + + + + + + 83 + 1 incredible job trying to clean up this mess, but the + + 2 problem is, we need to help people before they get + + 3 to the criminal justice system, and we need to give + + 4 them the help that they actually need, not some + + 5 sub-partial treatment. + + 6 [Applause.] + + 7 ALEXIS PLEUS: Everyone says that what + + 8 started this epidemic was this whole idea that we + + 9 needed to treat people's pain. + + 10 The thing that confuses me is that we're not + + 11 doing the same thing for people who are addicted. + + 12 They go to the hospital after an overdose. + + 13 They've been revived with Narcan, and they're in + + 14 severe pain, and we do nothing for them. + + 15 We need absolutely, in New York State, to + + 16 offer humane, medically-assisted detox, and that + + 17 needs to happen immediately. + + 18 [Applause.] + + 19 ALEXIS PLEUS: There are other states that + + 20 are offering same-day evaluations. + + 21 We need to do that in New York State. + + 22 We need immediate access to treatment. + + 23 This isn't a situation where a person can + + 24 wait days or weeks for treatment. It's a matter of + + 25 life or death. + + + + + + + + 84 + 1 And I'm going to allow the Senator to remain + + 2 nameless who told me, that insurers are not going to + + 3 allow this, because he's sitting at this table. + + 4 But I'm going to tell you right now, we + + 5 absolutely have to require insurers to pay for the + + 6 type and length of treatment known to be effective. + + 7 [Applause.] + + 8 ALEXIS PLEUS: I'm not sure why he's allowing + + 9 insurers to tell him what we're going to do with our + + 10 laws. + + 11 [Applause.] + + 12 ALEXIS PLEUS: And, finally, we need to + + 13 increase insurance reimbursement rates so that + + 14 treatment centers can afford to open and operate in + + 15 New York State. + + 16 Thank you. + + 17 [Applause.] + + 18 SENATOR O'MARA: Thank you very much, Alexis, + + 19 for sharing this with us, and your difficult + + 20 situations that you've been through, and your + + 21 excellent ideas for us moving forward. And your + + 22 experiences in other states is very helpful as well. + + 23 So thank you for being here with us tonight. + + 24 Next we have Devon. + + 25 DEVON PIERCE: Thank you, sir. + + + + + + + + 85 + 1 I'm Devon Pierce. I'm a recovering addict. + + 2 I've been doing heroin since I was 16 years + + 3 old, and it has destroyed my life. + + 4 It has destroyed my family's life. + + 5 It's destroyed the members of my church. + + 6 It's destroyed their life. + + 7 They have all been there to support me, and + + 8 love me, and that is one huge thing, as an addict, + + 9 is you need. + + 10 So do not give up on your family members. + + 11 Please, don't give up on them. + + 12 They are not the same people when that drug + + 13 is inside them as they are without that drug. + + 14 We are loving, we do care, and we appreciate + + 15 everything that you guys do. + + 16 And as Sheriff Ron Spike said, he said, we + + 17 hit them in high school -- we hit them -- well, we + + 18 hit them at fifth grade with the D.A.R.E. committee. + + 19 We don't hit them again in middle school. + + 20 We don't hit them in high school. + + 21 And I feel, that's what we need to do. We + + 22 need to push for this. + + 23 [Applause.] + + 24 DEVON PIERCE: What has been done now, it can + + 25 be stopped, it can be prevented. + + + + + + + + 86 + 1 But, we can prevent the younger kids from + + 2 going in the same steps that I took, and I don't + + 3 want to see anybody fall down the same path that + + 4 I went through. + + 5 And whatever I can do to help, from families, + + 6 family members that have addicts in their family, + + 7 please ask. I will be there to help. + + 8 We have a Nar-Anon group in this community, + + 9 and, I sit in that group and I hear the struggles, + + 10 just like I hear the struggle coming from these + + 11 women about their family and their losses. + + 12 And I'm sorry for that, that this drug has + + 13 taken your children. + + 14 That has to hurt, I know it hurts, and it's + + 15 horrible. + + 16 That could have been me. + + 17 And the Nar-Anon group is just a great group. + + 18 The families get together, they talk about + + 19 it, they try to help themselves. They need to + + 20 figure out something for themselves. + + 21 And as addicts, we have nothing around here. + + 22 We don't have any groups. + + 23 I've heard of one Nar-Anon group since I've + + 24 been home. + + 25 Have I been able to participate in it? + + + + + + + + 87 + 1 No, because I don't know where it is. + + 2 Do we have rehab in this community? + + 3 No, we do not have a rehab in the community. + + 4 It's something that we need. + + 5 Right before I went to jail last time, + + 6 September 2nd, I got arrested, I was going to turn + + 7 myself into a rehab. But on my way to a rehab, + + 8 I can get drugs. + + 9 Why not get high before I get to the rehab? + + 10 You know, we need that, where we can go in, + + 11 right in this community, and go in and say, Hey, + + 12 I need help. + + 13 You know, that's what we need here, as a + + 14 community. + + 15 I know this community can come together. + + 16 We've done it. + + 17 Look what we did for the flood. + + 18 Look what we do for the school students. + + 19 Penn Yan Mustangs, this is the home of + + 20 Penn Yan Mustangs. + + 21 We can do this, guys. + + 22 And I'll ask for your help as addicts. + + 23 I'm not speaking for all of them, but I'm + + 24 speaking for myself. + + 25 I need your help, so I can help other people, + + + + + + + + 88 + 1 please. + + 2 And that is what I want to do, is help the + + 3 other people that are in need. + + 4 And I thank everybody for coming out here, + + 5 guys. + + 6 I really do. + + 7 This means a lot, just so that you can hear + + 8 my voice as an addict. + + 9 This is awesome. + + 10 Thank you. + + 11 [Applause.] + + 12 DEVON PIERCE: We also have groups that have + + 13 participated in the jail ministry, the "Inside and + + 14 Out" program, and that's for a transaction that, + + 15 when you're in jail, these guys come in and talk to + + 16 you, that help you, they support you, they love you. + + 17 They're not giving you money so you can get + + 18 food or anything like that, but they're giving you + + 19 love, they're caring for you. And that's what their + + 20 biggest thing is, is love. + + 21 It is a Christian-based thing, but, that's + + 22 not who they come off as. They just come off as + + 23 people. They come off as people who care. + + 24 And it is a great program. + + 25 The Nar-Anon program, as I said, for those + + + + + + + + 89 + 1 families that hurt, they need help. They really + + 2 need help. + + 3 The AA meetings that we got going on around + + 4 here, that's awesome, but do we have anything for + + 5 narcotics? + + 6 We do not have anything for the opioids. We + + 7 don't have anything for that. + + 8 Yes, we have FLACRA. + + 9 You got $30,000 a year to put yourself in + + 10 FLACRA? + + 11 Because I know I don't. + + 12 We need help. + + 13 We need to be able to walk to a program and + + 14 get the help that we need, guys. + + 15 And I ask for the help. + + 16 Please, as an addict myself, I need the help. + + 17 Please. + + 18 Thank you so much, guys. + + 19 [Applause.] + + 20 SENATOR O'MARA: Thank you. + + 21 Thank you so much, Devon, for being here and + + 22 sharing that with us. + + 23 I missed, or maybe you didn't say, how old + + 24 are you? + + 25 DEVON PIERCE: I'm 23 now. + + + + + + + + 90 + 1 SENATOR O'MARA: 23? + + 2 DEVON PIERCE: Yes. + + 3 SENATOR O'MARA: And how long has it been + + 4 since -- + + 5 DEVON PIERCE: Since my last use? + + 6 SENATOR O'MARA: Yes. + + 7 DEVON PIERCE: September 2nd was my last + + 8 use. + + 9 SENATOR O'MARA: Well, thank you, and good + + 10 luck to you. + + 11 DEVON PIERCE: Thank you. + + 12 [Applause.] + + 13 SENATOR O'MARA: Next we have Gail Owen. + + 14 Gail, thank you for being with us. + + 15 GAIL OWEN: Yes. + + 16 Thank you very much for allowing me to bend + + 17 your ear for a moment here. + + 18 First of all, those of you who know me, and + + 19 I do see some familiar faces out here, this is way + + 20 out of my comfort zone to speak to people like this. + + 21 So, if I'm able to step out of my comfort + + 22 zone for five minutes to help another family to not + + 23 go through the bull -- baloney that I've been + + 24 through, I've done my job. + + 25 Not many people have heard this story that + + + + + + + + 91 + 1 I'm about to tell. + + 2 And, my mother is sitting in the front row, + + 3 and she has not heard this story, and no grandmother + + 4 should have to hear this about their -- her + + 5 grandson. + + 6 So I apologize in advance, mom. + + 7 The story I'm about to tell, I went to my son + + 8 and told him what I was going to say. + + 9 I got my son's permission to tell this story. + + 10 And I told him what I was going to say, and + + 11 he said, Mom, I don't remember any of that story. + + 12 So you have my permission, but I don't remember any + + 13 of that happening. + + 14 So, now, speaking as a mother whose been + + 15 through more than I have ever wanted to go through, + + 16 here are some of my thoughts. + + 17 A little over two years ago, in November of + + 18 2013, my son came to me, asking me for help, as he + + 19 was going through heroin withdrawals and he didn't + + 20 know what to do, and neither did I. + + 21 I first called my doctor as to what I should + + 22 do with him. + + 23 She said to take him to the emergency room. + + 24 The emergency room doctor gave him a drug to + + 25 treat the symptoms of withdrawals, and I was told to + + + + + + + + 92 + 1 take him to the Elmira detox center the next day. + + 2 I remember crying to emergency room doctor + + 3 before he was released to me at 1:00 in the morning, + + 4 saying, "I don't want to take him home. I don't + + 5 know what he will do when the withdrawal drugs wear + + 6 off." + + 7 I was scared to death. + + 8 What kind of mother does not want to take her + + 9 child home? + + 10 The fact was, he wasn't my child. + + 11 It was the drugs that had taken him, and + + 12 I had no control over it, and I just wanted my son + + 13 back. + + 14 I took him to the Elmira detox center the + + 15 next day. + + 16 Once we got there, we had to wait until a + + 17 doctor could see him. + + 18 During that five-hour wait, the drug that the + + 19 emergency room doctor had given him the night before + + 20 had worn off and he was out of control. + + 21 He was so out of control that a security + + 22 guard had to come and watch him. + + 23 He tore his gown off and wrapped it around + + 24 his neck. He tried to take a plastic knife to his + + 25 wrist. + + + + + + + + 93 + 1 He was a raging lunatic, he was a madman, he + + 2 was totally out of control. + + 3 He felt so horrible that he wanted to end his + + 4 life. + + 5 I stood outside his room and watched this + + 6 happen. + + 7 I had so many emotions as a mother: fear, + + 8 helplessness, anger, frustration. + + 9 I wanted to make everything right, and + + 10 I couldn't. + + 11 He stayed there for four days. + + 12 Once he started using, he was always chasing + + 13 that first high. + + 14 On a positive note, he has been clean since + + 15 September 12, 2014, which is the day he turned 24. + + 16 [Applause.] + + 17 GAIL OWEN: Prior to my son using heroin, he + + 18 was picked up for five, and I said, yes, five UPM + + 19 charges, which stands for "unlawful possession of + + 20 marijuana," within a year and a half. + + 21 Those are only tickets, which is just a + + 22 monetary fine. + + 23 I feel that these should be more than + + 24 tickets. The first one could be a ticket, but after + + 25 the first ticket, it should be a tougher penalty. + + + + + + + + 94 + 1 This was definitely a pattern of drug use in + + 2 which there was no consequence, other than hitting + + 3 his wallet, and that didn't have any effect on him. + + 4 Finding treatment for my son was a nightmare. + + 5 There was no inpatient facilities in + + 6 Penn Yan. + + 7 When you do finally find an inpatient + + 8 facility with an open bed, sometimes the wait is + + 9 over a week, and by then, the addict has changed + + 10 their mind about getting help. + + 11 You have to fight with insurance companies to + + 12 cover their stay. + + 13 You need to strike when the iron is hot and + + 14 get the addict into rehab immediately. + + 15 Insurance wants you to fail at outpatient + + 16 before they would consider paying for inpatient + + 17 treatment. + + 18 Inpatient treatment can be very expensive, + + 19 costing thousands of dollars a month. + + 20 My son's first rehab was 4 days in Elmira, + + 21 and the second rehab was 14 days in Tully Hill. + + 22 The third rehab was 30 days at G&G Holistic + + 23 in Florida, and when I put hum on that plane in + + 24 Syracuse, I didn't know whether I would ever see my + + 25 son again. + + + + + + + + 95 + 1 There is FLACRA in Penn Yan, which is an + + 2 outpatient, and is -- in my opinion, is not working, + + 3 as they just find more connections to get drugs. + + 4 The employees at FLACRA are good at their + + 5 jobs, but we need more of them. + + 6 Let's not forget the mental-health part of + + 7 this. + + 8 People have mental-health issues and they try + + 9 to treat that problem with drugs and alcohol because + + 10 seeking help for the problem is sometimes a stigma + + 11 that shows weakness. + + 12 Both my sons learned about the Just Say No + + 13 program in fifth grade. + + 14 The Just Say No program stopped at fifth + + 15 grade. + + 16 I am not sure how the middle school handles + + 17 the topic of drugs, but maybe the Just Say No + + 18 program could follow through to the middle school + + 19 and beyond. + + 20 The drugs and smoking issues seemed to start + + 21 at a much younger age. + + 22 Middle-school students seem to have little + + 23 fear and peer pressure rears its ugly head. + + 24 We need to start the education of dangers of + + 25 drugs and alcohol at the elementary school in grades + + + + + + + + 96 + 1 third -- in third and fourth grade using appropriate + + 2 language for them to understand. + + 3 When children get to the middle school and + + 4 academy, it may be too late. Some may have already + + 5 experimented with drugs and alcohol. + + 6 If a student is willing to put an unnatural + + 7 or unhealthy substance in their body, it shows to me + + 8 that there is a willingness to do more dangerous + + 9 actions. + + 10 This is not always the case, but it could + + 11 happen, and it did happen with my oldest son who + + 12 started smoking pot in seventh grade. + + 13 By the time my son got to the academy, he was + + 14 smoking marijuana frequently, and I used to think + + 15 that marijuana was the least of my worries. + + 16 Boy, was I wrong. + + 17 This is just a short synopsis of what I have + + 18 done to help my son in his recovery. + + 19 No parent should have to go through an ordeal + + 20 like this. + + 21 So in closing, I would like to see the + + 22 following take place in Yates County: + + 23 A program for the elementary students to + + 24 learn about the dangers of drugs. + + 25 Stiffer penalties for unlawful possession of + + + + + + + + 97 + 1 marijuana charges. + + 2 A rehab center in Penn Yan. + + 3 More outpatient facilities for addicts to go + + 4 to for counseling. + + 5 Insurance companies to recognize that this is + + 6 a disease just like cancer and needs to be covered. + + 7 Thank you for listening. + + 8 [Applause.] + + 9 SENATOR O'MARA: Gail, thank you very much. + + 10 Thank you so much for sharing that with us. + + 11 And I think with that bit of a segue, and + + 12 thank you all, all of the individuals that are here + + 13 to talk about your personal circumstances. + + 14 Every circumstance has terrible consequences. + + 15 They're all very different, but they all + + 16 involve the same drug. + + 17 And, I thank you for sharing, how difficult + + 18 it is to share that with all of us here in a room + + 19 full of this many people. + + 20 And with that, I think we'll move into our + + 21 educators that are here, and talk about what is + + 22 going on as far as prevention in schools, what you'd + + 23 like to see going on as far as prevention in your + + 24 schools, and what it's going to take for you to be + + 25 able to do that in your schools, because there's + + + + + + + + 98 + 1 none of us sitting up here that want to put another + + 2 unfunded mandate on our school districts. + + 3 So our panel here is: + + 4 Superintendent Howard Dennis from Penn Yan; + + 5 Superintendent Kelly Houck from Dundee; + + 6 Superintendent Tommy Phillips from + + 7 Watkins Glen. + + 8 Howard, we'll start with you. + + 9 Now, let me just point out, in the interests + + 10 of time, I know you guys -- there's no need to read. + + 11 We have your testimony, and it's fine to read + + 12 if you want to, but don't feel you have to read the + + 13 whole thing, because we have it in the record and + + 14 it's part of our package. + + 15 So, just hit the high notes, and I appreciate + + 16 you being here. + + 17 SUPT. HOWARD DENNIS: Sure. + + 18 I've been adjusting my speech throughout the + + 19 evening, so, hopefully, it will make sense. + + 20 But, thank you for the opportunity to speak + + 21 tonight, because this is the premier challenge + + 22 facing Yates County and Penn Yan. + + 23 You know, as educators, every school + + 24 district's mission includes educating and informing + + 25 our students on a daily basis, and we teach health + + + + + + + + 99 + 1 curriculum in Penn Yan at the mandated levels, + + 2 according to the State Education Department. + + 3 But in the past, we were able to offer more. + + 4 And I've heard tonight the reference to -- + + 5 many different times, to additional health + + 6 education. + + 7 I would agree with that. + + 8 Unfortunately, the reality comes about that, + + 9 over time, due to budget cuts, due to restructuring, + + 10 we've had to cut back on additional programming that + + 11 we've had. + + 12 So any assistance that the state government, + + 13 the State Legislature, could offer, as far as + + 14 funding goes, that would be beneficial to our + + 15 students. + + 16 And I agree with you, Senator, the financial + + 17 support without the unfunded -- without the mandate + + 18 would be wonderful. + + 19 The second thing that would be really helpful + + 20 to us as a school district in Yates County and in + + 21 Penn Yan is after-school programming. + + 22 We want to have those positive role models. + + 23 We want to have supportive and creative + + 24 places for our students to go after school, so that + + 25 we can offer possibilities for them. And that's + + + + + + + + 100 + 1 become a board goal for us here in Penn Yan, is to + + 2 push for more and more of those possibilities for + + 3 our students. + + 4 We want to be able to offer them education in + + 5 those smaller settings, positive role modeling, and + + 6 we want -- and, unfortunately, we're not able to + + 7 offer those at this time due to the financial + + 8 support that would be needed to establish and to + + 9 continue those. + + 10 I believe the Penn Yan community is very + + 11 supportive and generous in the support that we can + + 12 offer to our students. + + 13 We're able to offer dedicated psychologists, + + 14 social workers, youth counselors, guidance + + 15 counselors, and other support professionals, but, + + 16 obviously, it's not enough. + + 17 You're hearing from some Penn Yan students + + 18 tonight who have gone through the program. + + 19 And, so, we need more of those. + + 20 Right now, those are falling totally on the + + 21 local taxpayers. And, we need additional support in + + 22 order to offer the services, because, as you've + + 23 heard, those services aren't offered in our + + 24 community, and so the school district wants to help + + 25 step up and do as many of those things as we can. + + + + + + + + 101 + 1 But, again, the resources are tough. + + 2 Once addicted, the individual then has to go + + 3 on and look for counseling, and you've heard that + + 4 many times this evening. And the scarcity of those + + 5 treatment facilities here in rural areas is + + 6 difficult. + + 7 You know, I heard a story recently about a + + 8 community member on our staff who had convinced + + 9 their family member that they needed help, and went, + + 10 and finally worked through that entire process, and + + 11 reach out to their insurance carrier. + + 12 And I think you've leader this referred to + + 13 tonight, the response from the insurance carrier was + + 14 unbelievable. + + 15 They didn't qualify for inpatient treatment + + 16 because they hadn't failed enough times at + + 17 outpatient treatment. + + 18 And as we talk about the heroin epidemic and + + 19 the outcome of that, that's an unbelievable + + 20 statement for anyone to make, is that you have to + + 21 fail enough times with that drug in order to qualify + + 22 for higher level of services. + + 23 You know, if you compare that scenario to the + + 24 whole idea of being educated about anything, the + + 25 treatment model that we have at this point is not + + + + + + + + 102 + 1 adequate. + + 2 You know, learning, in general, doesn't + + 3 happen in three or four days with no follow-up. + + 4 And, so, anything that we can do in our + + 5 community to help with that kind of follow-up and + + 6 support for people in our community would be + + 7 helpful. + + 8 We're very fortunate in Penn Yan to have a + + 9 school resource officer. Our SRO is a village + + 10 policeman. + + 11 But, again, we have one person who is trying + + 12 to maintain the level of education around drugs, + + 13 trying to maintain relationships with kids, in a + + 14 1500-student school district in 3 different + + 15 buildings. + + 16 That's a very tough -- that's a very tough + + 17 situation for anyone to be in. + + 18 And he does an excellent job for us, and + + 19 we're very lucky to have him again. But we could + + 20 probably use three of him, and that would be tough + + 21 for our community to step up and be able to support. + + 22 Our County Legislature, as well, has talked + + 23 about additional drug-enforcement staff. And, + + 24 again, the funding is a tough piece of that. + + 25 And the district would be willing to help + + + + + + + + 103 + 1 step up and help support that in any way, but we do + + 2 not have the funds to do that. + + 3 Legislating stronger and harsher penalties + + 4 for dealers is overwhelmingly encouraged. + + 5 And many of the ideas that I received + + 6 tonight, I actually went out and garnered from our + + 7 staff. + + 8 You know, I looked for support, including + + 9 from some people on this panel, that stepped up and + + 10 said, Howard, this is what needs to happen. This is + + 11 what you need to advocate for. You have this + + 12 opportunity, make sure you send this information + + 13 along. + + 14 So I am touching on some things that have + + 15 been duplicated, but it's because they were + + 16 important to our staff. + + 17 So, you know, in conclusion -- I've cut quite + + 18 a bit out of my speech. + + 19 But, in conclusion, as to what others have + + 20 said: + + 21 We need your help with funding. We need your + + 22 help with legislative reform. + + 23 And those are things that you have heard + + 24 throughout the evening, so I'm grateful for the + + 25 opportunity to reiterate some of those. + + + + + + + + 104 + 1 And thank you for the opportunity to speak. + + 2 [Applause.] + + 3 SENATOR O'MARA: Thank you very much. + + 4 Kelly. + + 5 SUPT. KELLY HOUCK: Thank you. + + 6 Since Howard so graciously cut some of his, + + 7 I'm going to keep all of mine. + + 8 Thank you, Howard. + + 9 [Laughter.] + + 10 SUPT. KELLY HOUCK: And, also, before + + 11 I begin, I want to thank you again for this + + 12 opportunity. + + 13 But, I feel like I would be remissed right + + 14 now if I didn't thank the panel that went before me. + + 15 You truly touched every one of us, and I know + + 16 how difficult that was to share those stories. + + 17 My heart aches for each and every one of you. + + 18 Thank you for being so courageous and being + + 19 here with us this evening. + + 20 I appreciate that. + + 21 [Applause.] + + 22 SUPT. KELLY HOUCK: So my testimony is really + + 23 a story of sorts. + + 24 So I'm going to share a story from the + + 25 Dundee Central School community, and with that, + + + + + + + + 105 + 1 I have some suggestions for help and assistance, and + + 2 some need that we desperately have in our school, + + 3 but also in our community. + + 4 Heroin and other drug use has become an + + 5 epidemic in our community and in our county. + + 6 Just like any other epidemic, we must work + + 7 together to eradicate it. + + 8 This is something we cannot do alone; + + 9 however, we can accomplish together. + + 10 Everyone is part of the solution, and we + + 11 cannot allow this growing and pervasive problem to + + 12 touch or take one more life. + + 13 In my school district of just over + + 14 700 students, heroin has taken the life of a + + 15 17-year-old girl, a high school senior with her + + 16 entire life ahead of her; and a former + + 17 Dundee Central School student, just a mere 23 years + + 18 old, again, an individual with a whole life ahead of + + 19 him. This has happened in just a mere three months. + + 20 And I cannot reiterate enough that, + + 21 fortunately, I had the opportunity to get to know + + 22 the 17-year-old student in my year and a half at + + 23 Dundee, and she was not that kid. + + 24 I cannot say that enough to you. + + 25 She was not that kid. + + + + + + + + 106 + 1 And even though I didn't get to know the + + 2 23-year-old that passed, from all the stories of his + + 3 high school athleticism, he was not that kid either. + + 4 This does not discriminate. It touches + + 5 everyone and anyone. + + 6 Additionally, it has touched the lives of + + 7 many faculty members and students, by not only their + + 8 relationship with these two individuals; however, + + 9 through their own family members who are currently + + 10 struggling with addiction, or, even worse, still + + 11 actively using despite their pleas and exhausting + + 12 efforts to quit. + + 13 It leads me to ask the question: How many + + 14 more people must die, or who must die, in order for + + 15 this to finally be enough? + + 16 I can't begin to explain or paint a picture + + 17 that is vivid for each of you that accurately + + 18 displays how heart-wrenching it is for me to send an + + 19 e-mail to my entire faculty, that I need to have an + + 20 emergency faculty meeting with them before class + + 21 begins, to where I can share with them that we have + + 22 lost another child, or how incredibly devastating it + + 23 is to a student's classmates when faced with the + + 24 harsh reality of the tragic loss of an untimely + + 25 death of a fellow classmate. + + + + + + + + 107 + 1 Watching these students gather in the school + + 2 foyer, carrying purple helium balloons with memories + + 3 and messages of love and "I'll never forget yous," + + 4 as they walk as an inconsolable group to the funeral + + 5 home to say their goodbyes to their classmate, their + + 6 childhood friend, their fellow cheerleader, the + + 7 person who they sat next to in English class since + + 8 seventh grade. + + 9 As I try to comfort them and help them + + 10 through one of the most difficult times in their + + 11 lives that they will ever experience, I find myself + + 12 not being able to control my own thoughts and fears + + 13 that this will not be the last time that we walk + + 14 this walk. + + 15 The cruel, harsh reality, it will only be a + + 16 matter of time, unless significant, actionable, and + + 17 tangible steps are taken to combat this epidemic. + + 18 According to the American Society of + + 19 Addiction and Medicine, drug overdose is the leading + + 20 cause of accidental death in the United States. + + 21 Opiate and heroin addiction is driving this + + 22 epidemic. + + 23 Four and five new heroin users started out + + 24 misusing prescription painkillers. + + 25 As a consequence, the rate of heroin-overdose + + + + + + + + 108 + 1 deaths nearly quadrupled from 2000 to 2013. + + 2 In just 3 years, from 2010 to 2013, there was + + 3 a 37 percent increase in heroin-overdose deaths. + + 4 The current largest group of heroin users are + + 5 between the ages of 18 and 25. This group is also + + 6 the cohort that continues to grow exponentially over + + 7 all other age group and users. + + 8 Adolescents, my students, your children, are + + 9 falling victim to this addiction faster than we can + + 10 intervene. + + 11 Research has indicated that over + + 12 30,000 adolescents have used heroin in the past + + 13 year, and more than 50 percent have moved past into + + 14 the world of addiction. + + 15 These are national statistics, and, yes, they + + 16 matter; however, honestly, the statistics that + + 17 matter to me most are the ones that are occurring in + + 18 our own county and in our own schools. + + 19 I firmly believe that the solution to this + + 20 problem is multi-faceted and involves many layers, + + 21 with education being the center and the pillar that + + 22 every layer is anchored to. + + 23 We must be proactive in our approach to + + 24 addressing heroin- and opiate-addiction epidemic. + + 25 We now, more than ever, have the chance to + + + + + + + + 109 + 1 fight this epidemic, but we cannot wait. We must + + 2 combat this before it even begins. + + 3 Effective, meaningful, and result-driven + + 4 educational practices surrounding this issue + + 5 requires resources. + + 6 It's not just an option. It is a necessity. + + 7 Resources, such as financial assistance, and, + + 8 people, such as full-time public-health educators in + + 9 our schools to coordinate these efforts, is + + 10 critical. + + 11 We need the monetary support to procure + + 12 people who are well-versed in this work, identifying + + 13 the risk factors, creating intervention plans, and + + 14 assisting preventing the addiction before it becomes + + 15 a reality. + + 16 With this comes the need for financial + + 17 assistance with programs and outreach opportunities, + + 18 as well as certain real-life experiences for our + + 19 students to participate in, that clearly and vividly + + 20 illustrate the dangers of involving themselves in + + 21 this activity. + + 22 Lectures and pamphlets and videos are not + + 23 enough. + + 24 We need to provide and create meaningful + + 25 engagement for our students and their families, and + + + + + + + + 110 + 1 these need to be ongoing. + + 2 This education cannot be a one-and-done + + 3 platform or a six-week course. + + 4 Our education strategy and implementation + + 5 needs to be as pervasive as the heroin is in our + + 6 community. + + 7 This education needs to reach beyond the + + 8 walls of our schools, and needs to act as seepage, + + 9 seeping into our community and into our toughest + + 10 neighborhoods. + + 11 This work cannot be accomplished without + + 12 proper financial resources and people needed to + + 13 carry this work forward. + + 14 We cannot continue trying to solve the + + 15 problem by being active and expecting different + + 16 results. Our own local history clearly illustrates + + 17 that the current methods are insufficient. + + 18 Ultimately, even with the best education + + 19 practice in place, we will not stop all heroin and + + 20 opiate addiction. + + 21 Therefore, another layer to the solution that + + 22 I referenced in the beginning of this testimony + + 23 involves treatment plans and facilities, which, at + + 24 this point, in the great state of New York, are + + 25 dismal at best, a clear picture of inadequate. + + + + + + + + 111 + 1 When the hardest decision of an addict's life + + 2 has been made to help -- to seek help and assistance + + 3 to begin the very difficult and challenging path of + + 4 recovery, it is completely unacceptable that there + + 5 is not a treatment facility or an option immediately + + 6 available to them. + + 7 When told that it will take two or + + 8 three months, or even longer, to start the process, + + 9 that we have, in sense, told the addict to keep + + 10 using. + + 11 These individuals are barely holding on + + 12 minute to minute, and can't fathom how they will + + 13 make it through another day, let alone a month, or + + 14 longer, without using. + + 15 And that is exactly what we are forcing them + + 16 to do by not having adequate treatment facilities + + 17 and recovery essentials available and accessible. + + 18 School districts are having the hard and + + 19 difficult conversations with our students and + + 20 faculty. + + 21 We impose accountability to the greatest + + 22 extent or authority allows us to do so; however, we + + 23 need all stakeholders to be part of this work. + + 24 Parents, families, friends, community + + 25 members, and our elected officials all must be part + + + + + + + + 112 + 1 of the solution. + + 2 We need to invest in what matters, we need to + + 3 invest in our future, we need to invest in our + + 4 students' emotional, social, and mental health, in + + 5 order to eradicate this epidemic. + + 6 The treatment options and resources for + + 7 addicts need to go beyond just addiction. + + 8 We need to promote support, and provide + + 9 better and stronger mental-health services for our + + 10 students and communities. + + 11 In attempting to make an appointment for a + + 12 student in crisis to have a complete psychiatric + + 13 evaluation, due to the clear and evident lack of + + 14 resources and availability of mental-health + + 15 services, it took two months for a student of mine + + 16 to be even given an appointment. + + 17 It is very transparent that there are + + 18 significant mental-health issues and needs that are + + 19 severely underdiagnosed; and, therefore, not + + 20 treating, which is leading to an increased drug use + + 21 and activity. + + 22 We absolutely do not have any local control + + 23 over the lack of mental-health services in our + + 24 schools and in our communities. A significant + + 25 amount of preventive measures could take place if + + + + + + + + 113 + 1 the services were available. + + 2 Additionally, support is needed by providing + + 3 adequate funding and resources to our local law + + 4 enforcement. This must entail the addition of a + + 5 drug investigator. + + 6 It is evident that our county law enforcement + + 7 does not have the resource or capacity to address + + 8 the heroin and opiate epidemic that is eradicating + + 9 and taking hold of our communities. + + 10 To expect that any changes will result in + + 11 using the current structure and system is nothing + + 12 more than a fallacy. + + 13 As an educator for several years, and as an + + 14 educator who has worked in several capacities, + + 15 I have never witnessed the devastating effects that + + 16 heroin has had on a school and a school community as + + 17 I have over the past few years. + + 18 I shudder and fear at the thought of losing + + 19 one more child. + + 20 I ask all of us to stop and think, if that + + 21 child was your son, daughter, granddaughter, + + 22 grandson, niece, or nephew, what would you expect + + 23 these changes, and what would be warranted, to stop + + 24 this epidemic? + + 25 This epidemic does not discriminate. + + + + + + + + 114 + 1 Everyone is vulnerable to it. + + 2 All of these children are mine, and I take + + 3 that to heart, and I am at the place of the cold, + + 4 hard truth that I do not have, I can't provide, the + + 5 resources needed to save them alone. + + 6 Our system and resources are at capacity. + + 7 My children, my students, our collective + + 8 futures, depend on significant changes being made + + 9 regarding allocations of resources and funding to + + 10 truly eradicate this epidemic. + + 11 Thank you. + + 12 [Applause.] + + 13 SENATOR O'MARA: Thank you, Kelly, and for + + 14 the work that you've been doing. + + 15 You've been, certainly, at the front lines in + + 16 recent months on this, and I do thank you for being + + 17 here participating in this forum this evening. + + 18 Mr. Phillips. + + 19 SUPT. TOM PHILLIPS: Thank you, + + 20 Senator O'Mara, Assemblyman Palmesano, and members + + 21 of the Joint Senate Task Force on Heroin and Opioid + + 22 Addiction, for hosting the public hearing and + + 23 soliciting input relating to stemming this crisis. + + 24 I'd like to start by recounting my attendance + + 25 at the New York State Physical Education, Health, + + + + + + + + 115 + 1 Recreation, and Dance Conference this past November. + + 2 I attended several workshops; however, + + 3 I never believed in my most vivid imagination that + + 4 I would leave that conference as a trained responder + + 5 in opioid-overdose prevention, including the use of + + 6 intranasal Narcan. + + 7 While I applaud the association for taking + + 8 the initiative to train attendees, the reality that, + + 9 as an educator, I would need this training was + + 10 sobering. + + 11 This clearly speaks to the scope of the + + 12 epidemic we are facing. + + 13 As to the specific questions, my answers to + + 14 both of these questions focus on the need for a + + 15 community approach to addressing this epidemic. + + 16 Remember this, rural schools in + + 17 Upstate New York often do not have a YMCA, a YWCA, + + 18 or a Boys & Girls Club. + + 19 We don't know what they are. + + 20 As I've learned through the process of + + 21 developing community-based school programming, + + 22 there's not one agency or institution in any + + 23 community that can address this epidemic alone. + + 24 This will require collaboration, sharing of + + 25 information and ideas, as well as shared + + + + + + + + 116 + 1 responsibility for quality implementation of + + 2 community programming. + + 3 First, one cannot focus only on the results + + 4 of heroin and opioid abuse. + + 5 The real question is, how do we prevent this + + 6 from happening? + + 7 My experience tells me we need to create a + + 8 sense of community, and develop what I refer to as a + + 9 "surround and support climate" that is inclusive. + + 10 The community-school approach, or a similar + + 11 model, provides support for academic assistance and + + 12 enrichment, wellness activity, addresses child-care + + 13 issues, family-support centers, summer programming, + + 14 as well as quality before- and after-school + + 15 activities for our children. + + 16 Additionally, let me say, some of the best + + 17 money ever spent by the State of New York was the + + 18 School Resource Officer program. + + 19 [Applause.] + + 20 SUPT. TOM PHILLIPS: We must break down the + + 21 silos of mental health, children and family + + 22 services, our court system, health-care systems, and + + 23 any other governmental agency or community-service + + 24 provider, in addition to the regulations that + + 25 prohibit information sharing. + + + + + + + + 117 + 1 I understand the right to privacy, per HIPPA, + + 2 for individuals, but when the right prohibits or + + 3 delays meaningful, collaborative intervention for + + 4 individuals or a family, the consequences can be + + 5 disastrous. + + 6 Funding: + + 7 This does not necessarily mean additional + + 8 monies. + + 9 The reality is, the state and federal + + 10 government spend an enormous amount of money and + + 11 resources on the establishment of bureaucracies, + + 12 departments, and institutions, all focused on + + 13 providing assistance to family and persons in need. + + 14 There is enormous duplication of services + + 15 within our communities. + + 16 This is a direct outcome of the barriers in + + 17 place that prevent meaningful collaboration between + + 18 school, community agencies, and the courts. + + 19 I believe all communities would benefit from + + 20 an effort to coordinate service, including + + 21 resources, as a means of streamlining intervention + + 22 and providing education and assistance to those in + + 23 need. + + 24 Additional funding: + + 25 The concept of grant funding or competitive + + + + + + + + 118 + 1 funding for communities simply does not work. + + 2 If the State believes the epidemic is a + + 3 priority, then fund it. + + 4 On a personal note, at Watkins Glen, we are + + 5 already discussing what options we have for our + + 6 community-based school program once the grant funds + + 7 expire. + + 8 This is not an effective strategy for quality + + 9 program development. + + 10 In closing, I would like to again thank + + 11 Senator O'Mara and members of the Task Force for + + 12 their commitment to addressing the heroin and opioid + + 13 addiction epidemic. + + 14 I believe there are two primary issues that + + 15 need to be addressed: + + 16 The establishment of community schools, or a + + 17 similar model, in an effort to create a + + 18 surround-and-support climate that includes + + 19 educational support and enrichment, wellness + + 20 programs, family-support centers, and the like. + + 21 When children and families feel a sense of + + 22 belonging, they are more likely to be productive and + + 23 willing to seek intervention when needed. + + 24 Finally, appropriate levels of funding for + + 25 such community centers, whether through a more + + + + + + + + 119 + 1 coordinated effort of existing resources or the + + 2 elimination of non-competitive -- competitive and + + 3 non-competitive grant funding. + + 4 If we are serious about addressing this + + 5 issue, we need to provide the funding, inclusive of + + 6 sustainability options for programs once they are + + 7 established. + + 8 Again, thank you for asking us for our input. + + 9 [Applause.] + + 10 SENATOR O'MARA: Thank you, Tommy, for that. + + 11 Now we're going to stretch our neck back over + + 12 to the left here for us up here, so we'll go back to + + 13 Panel III, which consists of: + + 14 Danielle Tilden from the Finger Lakes + + 15 Addictions Counseling Referral Agency; + + 16 And, Marie Flanagan of the Yates Substance + + 17 Abuse Coalition; + + 18 Mike Ballard, the Council on Alcoholism and + + 19 Addictions of the Finger Lakes. + + 20 Danielle, would you like to start? + + 21 DANIELLE TILDEN, CASAC: Certainly. + + 22 Thank you for having me tonight. + + 23 On behalf of FLACRA, we have identified the + + 24 following needs for funding and regulation waiver, + + 25 and I'll just keep it short and sweet, because many + + + + + + + + 120 + 1 people have already said many of these ideas. + + 2 The first is an increase in or additional + + 3 funding for supportive living and housing + + 4 opportunities within Schuyler and Yates counties. + + 5 We are working towards supportive-living + + 6 beds; however, you know, there's always a struggle + + 7 to get the funding and the availability of + + 8 appropriate housing and beds in our communities. + + 9 An increase in and an availability of + + 10 Suboxone or buprenorphine therapy for opioid-use + + 11 disorder through the extension of waiver to nurse + + 12 practitioners and physicians' assistants. + + 13 M.D.s are currently the only ones who are + + 14 available to get the waiver and prescribe Suboxone, + + 15 and there are caps on the number of individuals who + + 16 can be on their caseload: 30 individuals per doctor + + 17 in the first year, 100 individuals on -- at a time + + 18 on their caseload in subsequent years. + + 19 This means that they can prescribe as many + + 20 opiates as are deemed necessary, but, if somebody is + + 21 struggling with dependence or substance-use disorder + + 22 related to the opiates, the treatment -- one of the + + 23 treatment options for that, Suboxone therapy, is not + + 24 available to them if there's not a doctor available. + + 25 Vivitrol is an excellent alternative to this + + + + + + + + 121 + 1 as well; however, there are some insurance issues + + 2 with that, as well as some contraindications, + + 3 especially for persons with active hepatitis, which + + 4 often goes hand-in-hand with IV drug use. + + 5 Increase in or additional funding for + + 6 medically-supervised outpatient detoxification + + 7 programs, programs with easement of regulations + + 8 around this treatment type in order to ease + + 9 implementation in our communities. + + 10 There's little to no access to these services + + 11 in our community. And when we look to get people + + 12 into services, there is typically a significant wait + + 13 time for those. + + 14 And as people have said, if you want + + 15 treatment, if you need treatment, it needs to be at + + 16 the point when you're ready to accept it. You can't + + 17 be waiting six, eight days for detox. + + 18 It just doesn't -- it doesn't, typically, + + 19 have positive outcomes. + + 20 Increased funding for and early intervention + + 21 services to allow for the identification and + + 22 treatment of childhood disorders, such as ADHD, + + 23 things like functional language delays, and other + + 24 issues that affect our, you know, very young + + 25 children. + + + + + + + + 122 + 1 Research continues to demonstrate a + + 2 connection between the childhood disorders and + + 3 subsequent risk for the development of substance-use + + 4 disorders in adolescents and adulthood. + + 5 And an increased support and funding for + + 6 Narcan training kits and refills on the medication. + + 7 Currently, there are some grants and + + 8 easements available; however, we'd like to ensure + + 9 continued, reasonable, and affordable access to + + 10 Narcan for all who need it and their families. + + 11 We appreciate the opportunity to share our + + 12 needs and our concerns for the community, and we + + 13 look forward to working together for the future in + + 14 this, and getting treatment to all those who need it + + 15 in a timely manner. + + 16 Thank you. + + 17 [Applause.] + + 18 SENATOR O'MARA: Thank you, Danielle. + + 19 Next, Annmarie. + + 20 ANNMARIE F. FLANAGAN, FNP, MS: Thank you. + + 21 I am also a nurse practitioner and I work in + + 22 primary care, and I did talk with some of my + + 23 patients that have substance-abuse disorders and + + 24 heroin issues, and they want -- there are a couple + + 25 of things that they wanted you to hear. + + + + + + + + 123 + 1 The main thing is that, they don't choose to + + 2 be an addict. The addiction chooses them. And they + + 3 don't want to be in the position that they are. + + 4 They do support legislation that would allow + + 5 drug dealers to be charged with homicide if they + + 6 sell drugs to an individual who dies as a result of + + 7 an overdose. + + 8 So that's I think important for you all to + + 9 hear. + + 10 Health-care providers are also frustrated + + 11 with trying to treat patients with substance-abuse + + 12 disorders. And we are looking for more education, + + 13 at any point in time, to be able to help us be able + + 14 to care for these patients better, and look for + + 15 evidenced-based practice treatment plans that would + + 16 benefit them. + + 17 But, a few things, and I've cut this + + 18 significantly shorter. + + 19 But, providing parity of access to services + + 20 for all residents with mental-health and + + 21 substance-abuse disorders in New York State, + + 22 especially in the rural areas. + + 23 As you all know, that there is a significant + + 24 lack in the rural areas for people to be able to + + 25 access services, especially for mental-health + + + + + + + + 124 + 1 disorders. And substance abuse is a mental-health + + 2 disorder, and we do need to destigmatize that out + + 3 there. + + 4 Increased transparency of services, + + 5 decreasing barriers, and, as Danielle did say, + + 6 working with our DEA to allow nurse practitioners + + 7 and physicians' assistants to be able to prescribe + + 8 Suboxone. + + 9 This will work -- this will allow a broader + + 10 network of rural health-care professionals to + + 11 provide this important and effective treatment + + 12 option in rural areas. + + 13 And at this point in time my patients are + + 14 buying that off the street, which, if they could get + + 15 it from me, we would be much safer than buying + + 16 something off the street that could potentially be + + 17 laced with something else. + + 18 Instituting a reporting system at the local + + 19 and state level to capture statistical data for + + 20 substance abuse to support systematic planning and + + 21 treatment. + + 22 Thank you. + + 23 [Applause.] + + 24 SENATOR AMEDORE: Thank you. + + 25 Annmarie, actually, what you were referring + + + + + + + + 125 + 1 to, with going after the dealers, we have in the + + 2 State Senate, I sponsored a bill, it's called + + 3 "Laurie's Law," that does exactly that. + + 4 It goes after the drug dealers, mid- and + + 5 upper-level drug dealers, who are connected to the + + 6 death of an overdose of heroin, and that the + + 7 law-enforcement agency can then go and charge that + + 8 dealer with homicide. + + 9 We got to get it through the Senate -- + + 10 through the Assembly, but we're working on it. + + 11 And, also, you mentioned something, I think + + 12 your last point that you mentioned was...? + + 13 ANNMARIE F. FLANAGAN, FNP, MS: The reporting + + 14 system? + + 15 SENATOR AMEDORE: ...the reporting system. + + 16 With the sale of over-the-counter of Narcan, + + 17 naloxone, one of our fears, and that the Task Force + + 18 has, that we have a piece of legislation that we're + + 19 actually trying to work it within the budget + + 20 language, is to have DOH have a database, implement + + 21 a database, so that we are tracking how many kits + + 22 are going over-the-counter, as well as every time + + 23 it's being administered, it will be mandatory + + 24 reporting. + + 25 Keep in mind, we are not looking for the name + + + + + + + + 126 + 1 and the address of individuals. We're very + + 2 sensitive to that, because we do not -- we already + + 3 know about the stigma. + + 4 But we want to make sure, we want to gather + + 5 the statistical information, so that we can pinpoint + + 6 exactly where resources need to be, where the + + 7 problematic areas are throughout the state. And + + 8 that will help get us down that road of more + + 9 database statistical data so that we can use it. + + 10 ANNMARIE F. FLANAGAN, FNP, MS: Thank you. + + 11 You're on the right track there. + + 12 SENATOR O'MARA: Thank you, Annmarie. + + 13 Mike. + + 14 MIKE BALLARD, MS: Thank you. + + 15 Thank you for having me tonight. + + 16 The primary objective of the council is to + + 17 provide education and prevention strategies to our + + 18 counties, the communities, and the coalitions that + + 19 we serve. + + 20 I work in 5 different counties in about + + 21 15 different coalitions on many of the same issues + + 22 here, so I see a lot of these in every one of the + + 23 counties. + + 24 One of the beliefs that we have is through + + 25 the efforts -- you know, these efforts that we can + + + + + + + + 127 + 1 reach families, youth, and the community members, + + 2 and we can provide them with the capacity to make + + 3 informed decisions about substance use and abuse. + + 4 One of the programs that we have recently + + 5 started to implement in Ontario County, actually, is + + 6 a community-based -- or, school-based education + + 7 program. + + 8 We have a contract with eight of the school + + 9 districts in the county, and we have educators that + + 10 are in each one of those school districts + + 11 full-time -- or, close to full-time, anyway, to + + 12 provide education directly in the school, so they + + 13 integrate themselves into those programs. + + 14 That's one of the programs that we would + + 15 really like to see, you know, some increased + + 16 funding, to be able to provide to not only + + 17 Yates County, but a larger percentage of the + + 18 county -- or, the schools within our catch-basin, if + + 19 you will. + + 20 The program is seeing some really increased + + 21 needs. + + 22 We've had a lot of requests from schools, you + + 23 know, for more programming, for more of the forums + + 24 that we've done, all kinds of different programming, + + 25 not just for heroin, but all kinds of substance + + + + + + + + 128 + 1 uses. + + 2 So, that's one of the programs that we would + + 3 really like to see, you know, increased. + + 4 Family education is also very important at + + 5 the council. + + 6 Not only reaching these kids at a young age, + + 7 but also being able to provide families with the + + 8 resources, the education that they need, to be able + + 9 to make informed decisions as family members, so + + 10 that they understand the importance of, you know, + + 11 seeking help as a family. + + 12 And the last piece that the council would + + 13 like to talk about is the lack of opiate-specific + + 14 treatments within our region. + + 15 There are some options, but finding treatment + + 16 that is dedicated to opiate use has been very + + 17 difficult. + + 18 Helping provide access and access to + + 19 appropriate treatments is a vital part of the + + 20 recovery process. + + 21 So, the Yates County community, and the + + 22 region as a whole, has seen detrimental effects that + + 23 substance abuse can have on their users, their + + 24 families, their children, and the community as a + + 25 whole. + + + + + + + + 129 + 1 Reaching our youth at an early age and + + 2 educating the family members is something that we + + 3 see is a tremendously important piece to helping + + 4 people protect themselves and make better and more + + 5 informed decisions. + + 6 So I'd like to thank the Task Force, as well + + 7 as everyone who came out tonight, to help us push + + 8 some of this forward. + + 9 So, thank you. + + 10 [Applause.] + + 11 SENATOR O'MARA: Thank you. + + 12 And thank you to the panel for participating + + 13 tonight. + + 14 We'll get to our final panel here, but that's + + 15 not going to be the end of the night. + + 16 I know we're running on here. + + 17 But we have our Yates County Public Health + + 18 Department representatives here. + + 19 And then when we finish with Deb and George, + + 20 we're going to give an opportunity for my colleagues + + 21 to speak or ask questions of anybody, from what + + 22 we've heard. + + 23 I don't let them speak at the beginning, so + + 24 when it gets later they tend to speak shorter, and + + 25 they're politicians, so that helps in that regard. + + + + + + + + 130 + 1 And then after that, we do want to open it up + + 2 for questions from the audience. + + 3 We have a microphone up here. + + 4 We're going to ask anybody that wants to + + 5 speak on whatever aspect of this, whatsoever, to + + 6 come up to the microphone and state who you are and + + 7 where you're from, and give a brief outline of the + + 8 points you would like to make. + + 9 And depending on how long that line gets will + + 10 depend on how long I'll let you speak. + + 11 But, with that, we'll move on to our final + + 12 panel here, and thank you for being here, and your + + 13 patience here to go last, to kind of round this out, + + 14 with the Yates County Public Health Department + + 15 Director Deb Minor, and the director of community + + 16 services, George Roets. + + 17 Deb, if you'd like to start out, and give us + + 18 your perspectives here on putting this all together + + 19 for us here in Yates County. + + 20 DIR. DEB MINOR: Thank you. + + 21 Thank you, Senator O'Mara and Task Force + + 22 members, and audience. + + 23 And, being the next-to-the-last speaker, + + 24 I will be very brief, and I'm going to just + + 25 highlight, so I hope it doesn't sound too chopped + + + + + + + + 131 + 1 up, and I hope it makes sense. + + 2 First, I want to note that we do appreciate + + 3 the Governor and the State Legislature's efforts in + + 4 making heroin and opiate a top public-safety, + + 5 public-health, and mental-health priority in 2016. + + 6 And we know that the Governor has put mention + + 7 of this in the executive budget. + + 8 Across New York State, many of the + + 9 law-enforcement offices and public-health + + 10 departments have been working to implement the + + 11 opioid-overdose prevention programs, the Narcan kits + + 12 and training. + + 13 Since we started that program in the fall of + + 14 2014, we have trained over 200 law enforcement and + + 15 community members, and we have you distributed over + + 16 700 kits in Yates County. + + 17 This is not a measure that's going to prevent + + 18 our problem, but it helps to give that addicted + + 19 individual and his or her family an additional + + 20 opportunity to seek treatment and work towards + + 21 recovery. + + 22 So even though we now can access the naloxone + + 23 at the pharmacies, I would ask that you not cut the + + 24 funding to that program. + + 25 I think it's crucial that we at Public Health + + + + + + + + 132 + 1 and the other opiate-prevention programs have the + + 2 opportunity to train folks on how to properly use + + 3 this. And it also gives us another opportunity to + + 4 give them some information about recovery and + + 5 treatment resources. + + 6 We have heard from many of the folks on the + + 7 panel tonight, and we've also heard from our forums, + + 8 about the barriers that the families and the + + 9 individuals suffering from addictions have in + + 10 dealing with the insurance companies. + + 11 So, again, we applaud the efforts of the + + 12 Legislature in what they are doing to no longer + + 13 permit insurance companies to require a fail-first + + 14 policy. + + 15 We need to stick to that, we need to hold + + 16 them to that. + + 17 And, so, we do ask for your commitment that + + 18 that will take place. + + 19 And we also are appreciative of the new + + 20 realtime online tool for families and providers, + + 21 that they can access at the OASAS website, in + + 22 locating inpatient- and residential-treatment beds. + + 23 However, anybody who looks at that will see + + 24 that the capacity is not sufficient. + + 25 I have seen mentioned that the executive + + + + + + + + 133 + 1 budget proposes $7 million in new funding, and + + 2 proposes 300 new treatment beds to be developed over + + 3 the next two years. + + 4 This is good news for the state, but please + + 5 make sure that some of these beds are coming to the + + 6 Finger Lakes area. + + 7 They cannot all go downstate and to the + + 8 metropolitan areas. It needs to be fairly + + 9 distributed. + + 10 I just don't want to be too choppy here, but + + 11 I'm trying to be brief. + + 12 SENATOR O'MARA: Take your time. + + 13 DIR. DEB MINOR: At Public Health, we are + + 14 also asking for continued movement towards the + + 15 integration of outpatient drug treatment into the + + 16 more traditional medically-focused health-care + + 17 settings. + + 18 We need to have comprehensive treatment plans + + 19 for our patients which address the + + 20 medically-assisted treatment, behavioral counseling, + + 21 identification and treatment of infectious disease. + + 22 We know that the use of heroin and other + + 23 injectable drugs brings risk for HIV and + + 24 hepatitis C. + + 25 We need screening for comorbid psychiatric + + + + + + + + 134 + 1 diagnosis, as well as care of their medical needs. + + 2 So we do need to have this as an integrated + + 3 approach, and not be sending patients to multiple + + 4 different locations to receive their treatment. + + 5 Those in recovery have spoken to us of the + + 6 increased social acceptability of using heroin by + + 7 their -- by the youth and young adults, and of the + + 8 increased availability. + + 9 Parents have asked us for assistance in + + 10 understanding how they can best prevent their + + 11 children from making choices that lead to addiction. + + 12 We know that the issue of addiction cannot be + + 13 effectively addressed through treatment alone or + + 14 through the arrest of those who are selling or using + + 15 illegal products. + + 16 We must focus a portion of our resources on + + 17 prevention. + + 18 And I would bring your attention to programs + + 19 that are evidenced-based and proven strategies, such + + 20 as Healthy Families New York and the Nurse-Family + + 21 Partnership, Parents as Teachers, and the + + 22 Parent-Child Home Program. + + 23 I would ask that funding not be diverted from + + 24 these programs. We need to have these programs to + + 25 continue to help our families. + + + + + + + + 135 + 1 But we also need to look for expansion of + + 2 programs that move beyond the pre-school years. + + 3 We have to find ways to help our families to + + 4 parent and to have those difficult conversations + + 5 with their children, and help to prevent some of + + 6 these folks from turning to use of substances. + + 7 There are some new tools online through + + 8 OASAS, tools such as Talk to Parent and the + + 9 Kitchen Table Toolkit. + + 10 We need to locate additional resources such + + 11 as these. + + 12 And I think I've hit all of the highlights, + + 13 and you do have my written testimony. + + 14 Again, I thank you for this opportunity, and + + 15 I look forward to continuing to work and to address + + 16 this issue. + + 17 Thank you. + + 18 [Applause.] + + 19 SENATOR O'MARA: Thank you very much, Deb. + + 20 George. + + 21 GEORGE ROETS: Well, I have the auspicious + + 22 position here to be the last speaker from the + + 23 panels. + + 24 First of all, thank you, for everyone, for + + 25 hanging in there. + + + + + + + + 136 + 1 The support in this community is tremendous. + + 2 I could repeat and say, ditto, about almost + + 3 everything that's been said tonight. + + 4 We've heard about the agony. + + 5 I want to just counterbalance that with + + 6 saying that the involvement of the community, with + + 7 the coalition, and in efforts to really corral this + + 8 epidemic, is just tremendous. + + 9 This is a great community that has come out + + 10 and has stood up for change, and we certainly do + + 11 need change. + + 12 We have a whole-world problem. + + 13 From a public-health point of view, it's a + + 14 community problem which requires community + + 15 solutions. + + 16 One of the things I want to suggest that + + 17 I haven't heard -- I've heard allusions to it -- and + + 18 that is we need to be able to offer you, and + + 19 community members, in general, ways in which they + + 20 can participate more fully, both in helping protect + + 21 their children from the scourge of either mental + + 22 illness or substance abuse, to help your children + + 23 grow healthier, and to be stronger and more + + 24 resilient. + + 25 The schools play a part in that, but every + + + + + + + + 137 + 1 family and every community plays a big part in that. + + 2 There are programs that are being sponsored + + 3 across New York State, across the United States of + + 4 America. + + 5 There's two great programs that, basically, + + 6 begin to do that. + + 7 One is called "Mental Health First-Aid," + + 8 which is really directed at adults, to learn about + + 9 substance abuse, and mental illness, and what you + + 10 can do as a family member, as a partner, as a + + 11 sibling, as an employer, to really help somebody you + + 12 see may be having a mental-health problem, or a + + 13 substance-abuse problem. + + 14 It's training that can be offered to schools, + + 15 offered to the community, offered to child-care + + 16 organizations, and other organizations that work + + 17 with adults, at all levels. It doesn't require a + + 18 college degree. + + 19 But it does give skills to people, and it + + 20 orients people to their role in having a healthy + + 21 community. + + 22 The Youth Mental Health First-Aid program is + + 23 specifically focused on youth. + + 24 We know that when we ask people who, in fact, + + 25 are addicted, 90 percent of them say they started + + + + + + + + 138 + 1 while they were in school. + + 2 Did you hear that? + + 3 They started while they were in school. + + 4 They're the target for us. + + 5 You heard from the educators. You've heard + + 6 from other people here. + + 7 Early identification, early prevention, is + + 8 really a core. + + 9 They can't all be in school. They're in + + 10 school a few hours. + + 11 They're home and in their neighborhoods the + + 12 rest of their lives. + + 13 So we all need to be part of the solution. + + 14 We all need to be willing to step up and + + 15 learn. + + 16 We all need to be willing to step up and find + + 17 new ways for us to be involved, and to make a + + 18 difference. + + 19 And I have a lot of other things I could + + 20 mention. + + 21 We don't have enough treatment. + + 22 We don't have the right staff. + + 23 We need more prevention. + + 24 We have to look at early prevention, early + + 25 identification. + + + + + + + + 139 + 1 We have to look at support for those who, in + + 2 fact, are addicted, to protect them, and to support + + 3 them in treatment, and give them the treatment they + + 4 need. To help them change the environments, because + + 5 they can't stay in the same environment as an + + 6 addicted person. They need to be able to move + + 7 beyond that. + + 8 So, that's where I'm going to stop. + + 9 But I'm going to say that there is a role for + + 10 this community, and we really have to be invested + + 11 with working towards giving people the tools so that + + 12 we can move this army of people who now want to see + + 13 a change forward and really do something in this + + 14 community. + + 15 Thank you. + + 16 [Applause.] + + 17 SENATOR O'MARA: Thank you. + + 18 Now I'll turn it over to the Chair of our + + 19 Task Force in the Senate, Senator Terrence Murphy. + + 20 SENATOR MURPHY: First of all, + + 21 Senator O'Mara, it's an honor and a privilege for + + 22 the invitation to come up here. + + 23 I come from Westchester County. It's just + + 24 north of New York City. + + 25 This afternoon; I got in the car early, and + + + + + + + + 140 + 1 I met Senator Amedore in Albany, and him and I had + + 2 traveled up to Oneonta to do a 12:00 Task Force + + 3 meeting in Oneonta, and then out to here. + + 4 So, it's been a long day, but well worth it, + + 5 I will tell you that. + + 6 To the panel of experts up here, thank you. + + 7 Thank you for all you do for your community. + + 8 Thank you for all you do for New York State. + + 9 You folks, right down there, Arianna, Devon, + + 10 to sit on this stage and give your story, you are so + + 11 far ahead of it. + + 12 You're doing a great, great job. + + 13 [Applause.] + + 14 SENATOR MURPHY: You keep it up. + + 15 You keep it up. + + 16 You should be damn proud of yourself, to be + + 17 able to sit up on this stage and share with + + 18 everybody. + + 19 That takes courage, and it takes guts. + + 20 And, Alexis, I don't even know where to + + 21 start. My heart goes out to you. + + 22 You are so, so strong to be able to share + + 23 that story with all of us up here. + + 24 It's, just, I got the chills when you were + + 25 saying. + + + + + + + + 141 + 1 I leaned over to Senator Ortt and I said, + + 2 This is unbelievable. + + 3 And I've been around New York State, and I've + + 4 heard stories. + + 5 And, this, it's pretty deep. + + 6 And to be able to be here and share, I thank + + 7 you. + + 8 And for everybody who is sitting in the + + 9 audience, it's now 9:00 at night, and, you know + + 10 what? + + 11 To still be here says something, to you, and + + 12 how much you care about your community, and to + + 13 listen to all the experts up here, because this is a + + 14 team effort. + + 15 None of us have all the answers right here. + + 16 This is why we go around the state, and this + + 17 is why we listen to everybody, to gather the + + 18 information and to make educated decisions on what's + + 19 the best for your community. + + 20 We don't know it unless you tell us. + + 21 Yes, we have the experts. + + 22 Who was it, Sheriff Spike over here. + + 23 You know what? + + 24 You're hiding behind closed doors? + + 25 No more. The stigma has got to go. + + + + + + + + 142 + 1 This has -- + + 2 [Applause.] + + 3 SENATOR MURPHY: This has no boundaries. + + 4 It has no ethnicity. + + 5 It has no gender. + + 6 It has no religion. + + 7 It will shake down really, really, really + + 8 good families. + + 9 And this is what communities are all about: + + 10 It's to rally around and make sure we do the right + + 11 thing, to care for our community, and make sure we + + 12 can do the right thing. + + 13 I'll leave it off with, it has been an honor + + 14 and a privilege to be able to be here tonight. + + 15 Whatever we could do as a panel up here, and, + + 16 Senator O'Mara as your State Senator, we are behind + + 17 him 1,000 percent. + + 18 And you're doing a great job. + + 19 And thank you for allowing me to be here + + 20 tonight. + + 21 SENATOR O'MARA: Thank you, Terrence. + + 22 [Applause.] + + 23 SENATOR AMEDORE: Well, thank you, + + 24 Senator O'Mara, and it is an honor to be in the + + 25 58th Senate District. + + + + + + + + 143 + 1 And for you to have a -- I think a + + 2 most-outspoken State Senator that really is + + 3 advocating for the needs of all of the district + + 4 here. + + 5 I have served with Senator O'Mara in the + + 6 Assembly, and now in the Senate, and, he's got your + + 7 back, and he's trying very hard. + + 8 To Janet and to Alexis and to Gail, you know, + + 9 losing a loved one or going through the trauma and + + 10 the grief that you have gone through, and, to Gail, + + 11 being able to have somewhat of a victory, you have + + 12 all given so many much hope. + + 13 And to anyone who is going to try to limit + + 14 and say that we can't is unfortunate, because the + + 15 more we go around and do these Task Force meetings + + 16 and have public-address meetings, the -- there's a + + 17 lot of moving parts, and there's not one solution. + + 18 But I got to tell you, this -- it's evolving. + + 19 And where the State was, to where we are + + 20 today, it has been the State Majority -- the State + + 21 Senate Majority Conference that has really + + 22 catapulted and thrusted so many of the ideas that + + 23 the panelists have already brought up. + + 24 We know that there's gaps in the system. + + 25 We know that there is inefficiencies still + + + + + + + + 144 + 1 that needs to be addressed. + + 2 We know that we need to go and tackle big + + 3 insurance companies and get them to pay more for + + 4 longer treatment. + + 5 We know that we can do more on the + + 6 law-enforcement side. + + 7 And we struggle every day in the State Senate + + 8 to, how do we work through the -- that wall + + 9 sometimes that's in the State Assembly, that a lot + + 10 of times blocks a lot of these great ideas and + + 11 initiatives that's already been mentioned here + + 12 today: reforms to laws, to policies, to funding. + + 13 And we need to do -- everyone, together, + + 14 collectively, needs to work together, as you are + + 15 doing here, we need to work together on driving the + + 16 message home to the New York State Assembly, because + + 17 they have blocked a lot of great initiatives and + + 18 advancements so that we could -- it could curtail or + + 19 eradicate this crisis that we have. + + 20 Substance abuse is a huge problem, and I'm + + 21 not just narrowing in on opiate or heroin addiction, + + 22 even though that was the topic tonight. + + 23 But, substance abuse, and a wide spectrum of + + 24 it, it grabs ahold of an individual, ahold of a + + 25 family, ahold of a community, that is just + + + + + + + + 145 + 1 deteriorating the quality of life throughout the + + 2 state of New York. + + 3 And we need to do everything possible, + + 4 collectively, to fix this. + + 5 So, thank you so much. + + 6 God bless you all, and I appreciate the time + + 7 and your stories. + + 8 [Applause.] + + 9 SENATOR O'MARA: Thank you, George. + + 10 Senator Rob Ortt. + + 11 SENATOR ORTT: Thank you, Senator O'Mara. + + 12 I want to thank you for hosting this event. + + 13 I want to thank all of you for being here + + 14 tonight. + + 15 And I certainly want to thank the panel, and + + 16 echo the comments that have been made by my + + 17 colleagues already. + + 18 And, you know, I think, when you look at this + + 19 panel up here, and this is my fourth or fifth + + 20 hearing across the state, you will see a whole range + + 21 of spectrums. + + 22 You have law enforcement. + + 23 You have family members. + + 24 You have legislators. + + 25 You have superintendents. + + + + + + + + 146 + 1 You have public-health officials. + + 2 You have service agencies. + + 3 And that shows you how complex, how dynamic, + + 4 this issue is, but it also shows you that there is + + 5 no one thing. + + 6 I wish there was one thing we could do that + + 7 would fix this. + + 8 I wish there was one thing we could do that + + 9 would save lives. + + 10 But, unfortunately, there's not just one + + 11 thing. + + 12 We're going to have to do a lot of things, + + 13 and after that, we're going to have to recalibrate + + 14 and probably do a lot more things. + + 15 It's not just funding. + + 16 It's not just this or that. + + 17 It's going to be a lot of things. + + 18 It's going to have to be a comprehensive + + 19 approach. + + 20 And that's one of reasons why we've done + + 21 these hearings across the state, is to try and get a + + 22 sense from the people who live this every day, from + + 23 the survivors, to the families, to law-enforcement + + 24 officials, what is going to work? + + 25 This has to be thought out. + + + + + + + + 147 + 1 This cannot be just a reaction. + + 2 This cannot be just, I need to get in the + + 3 headline tomorrow. + + 4 Or this cannot be, something bad's happening + + 5 so we need to do something. + + 6 We need to do something, but we need to do + + 7 something that works. + + 8 And, you know, for these families over here, + + 9 it does no good to them, and, for these guys sitting + + 10 over here, who go out every day and put their lives + + 11 on the line, it does no good to them, if what we're + + 12 doing isn't effective, if what we're doing doesn't + + 13 work, and if we don't take into account what they + + 14 have to say. + + 15 I wanted to specifically mention Janet. + + 16 All of you gave very heartfelt and emotional + + 17 stories. + + 18 But Janet talked about her son, and one of + + 19 the things that resonated with me was, she said he + + 20 was a veteran of Iraq. + + 21 And, I was a veteran of Afghanistan, and + + 22 I know a lot of veterans who come out and who suffer + + 23 from mental health-related issues. + + 24 And as Chair of the Mental Health Committee + + 25 in the Senate, I see this all the time with + + + + + + + + 148 + 1 self-medicating. And, a lot of veterans are being + + 2 struck by this very epidemic, this opioid and heroin + + 3 epidemic. + + 4 These are men and women who served their + + 5 country in combat, who wore a uniform, who wore the + + 6 flag on their shoulder, who did amazing things; and, + + 7 yet, they come back, and, it's not Iraq that gets + + 8 them, it's not Afghanistan, it's not ISIS or the + + 9 Taliban or some insurgent. + + 10 It's heroin, which I think is really tragic + + 11 and ironic. + + 12 And on the good side, we've started the + + 13 Joseph P. Dwyer program in Niagara County. It's a + + 14 program that exists elsewhere in the state. It's a + + 15 peer-to-peer support service. + + 16 And, basically, one of the things that + + 17 they're going to start doing is going to the VA, + + 18 working with the VA, when the veterans coming out, + + 19 who have been there for heroin or drug treatment. + + 20 They're going to be there to get these guys or these + + 21 girls right when they come out, because that's one + + 22 of the most vulnerable times, which we heard about, + + 23 when they are discharged. + + 24 That 48-to-72-hour time frame is when you see + + 25 a lot of relapses, and, actually, a lot of deaths. + + + + + + + + 149 + 1 But I just -- I know you'll probably remember + + 2 your son in a lot of different ways. + + 3 I think you should remember him, certainly, + + 4 as a soldier, and as a hero for the country. + + 5 And, last, but not least, I'll just say that, + + 6 you know, we heard a lot tonight, too, about + + 7 marijuana. + + 8 We heard -- I forget who it was who talked + + 9 about five previous unlawful marijuana possessions. + + 10 And I met a family in Niagara County whose + + 11 daughter passed away late last year, and they came + + 12 up to me and they told me that their daughter, who + + 13 was 22 at the time, told them that this all started + + 14 when she started with marijuana. + + 15 And I know there's a lot of different + + 16 opinions on this across the country, but all I can + + 17 say is, most of these folks here whose loved ones + + 18 are either recovering or are not here, I guarantee + + 19 you, they didn't start with heroin. + + 20 They started somewhere with something else, + + 21 and that led to heroin. + + 22 So, I think we have to be very realistic that + + 23 it's not always a victimless crime. That there are + + 24 such things as gateway drugs, and there are such + + 25 things as entry-level drugs, that lead to something + + + + + + + + 150 + 1 a lot more -- a lot more significant. + + 2 And while substance abuse is very serious, + + 3 this heroin epidemic is very lethal. + + 4 You don't come back from this one, you know, + + 5 for very long. + + 6 And I think that everyone up here gets it. + + 7 We're all trying -- we're trying to find a + + 8 solution. + + 9 But there's an ownership that's a part of + + 10 this as well, and I want to thank all of you, + + 11 because you're owning it. + + 12 By being here tonight, you're owning it, + + 13 because if you think that it doesn't happen in your + + 14 community, you're wrong. + + 15 If you think it doesn't happen in your + + 16 family, you're wrong. + + 17 And if you think that it's just going to go + + 18 away by itself, you're wrong. + + 19 So I want to thank you all for being here, + + 20 and I want to thank my colleagues for being here as + + 21 well. + + 22 [Applause.] + + 23 SENATOR O'MARA: Thank you, Rob. + + 24 Next we'll go to our colleague in the + + 25 Assembly, who is, fortunate for me, to serve in my + + + + + + + + 151 + 1 Senate District, which includes Yates County, + + 2 Schuyler County, Steuben County, part of + + 3 Chemung County, and Seneca County, for -- Phil. + + 4 But before we get to Phil, we do want to get + + 5 to whoever in the audience would like to speak. + + 6 So if you could move over by the microphone + + 7 and line up against the wall, we'll get to you as + + 8 soon as Phil is done. + + 9 But, Phil. + + 10 ASSEMBLYMAN PALMESANO: Thank you for + + 11 listening. + + 12 Certainly, the members of panel who are up + + 13 here, offering your expertise. + + 14 But most importantly, the six of you at the + + 15 end of the table, for having the courage to share + + 16 your personal stories. + + 17 You put a human face and human side to this + + 18 growing epidemic that's affecting every community + + 19 throughout the state. + + 20 And as you heard, and as we all know, this + + 21 doesn't matter what color you are, whether you're + + 22 rich or poor, upstate or downstate. + + 23 This issue knows no boundaries. + + 24 And to have you come out and take the time to + + 25 share that with all of us, should give us all + + + + + + + + 152 + 1 motivation to try to solve this problem. + + 2 So, thank you again. + + 3 I did just want to say, just to my colleagues + + 4 here in the Senate, that -- and the members of the + + 5 community, our Assembly Minority did actually have + + 6 hearings around the state last fall, and we just + + 7 wanted to make sure we presented that with you. + + 8 You know, we think there are good ideas, a + + 9 lot of that we heard about today, that, as you guys + + 10 go forward, and as we look for legislation, we have + + 11 some good ideas and blueprints in there to move + + 12 forward, and we certainly have to act. + + 13 You know, we do these forums and we get this + + 14 input, but it's imperative for us to act, and it has + + 15 to be a comprehensive approach, from prevention, to + + 16 rehab, to detox, to recovery, to education, to help + + 17 with law enforcement. + + 18 And, certainly, again, we need to take the + + 19 stigma off this, and treat this disease, and not be + + 20 afraid to tell the insurance companies they have to + + 21 be a part of the solution. + + 22 But we have to do it together, it has to be a + + 23 community effort. + + 24 And I think the people at this table signify + + 25 their commitment to make this a community effort. + + + + + + + + 153 + 1 You being here signifies that you want to be + + 2 a part of this community effort to solve this + + 3 problem. + + 4 And we just want you to know that we want to + + 5 be a part of the solution and help as much as we can + + 6 as well. + + 7 So, I thank you for coming out. + + 8 We appreciated your testimony, and we look + + 9 forward to hearing from the audience as well. + + 10 So thank you very, very much for being here + + 11 tonight. + + 12 Appreciate it. + + 13 SENATOR O'MARA: Thank you, Phil. + + 14 [Applause.] + + 15 SENATOR O'MARA: We'll begin with you at the + + 16 microphone. + + 17 If you could please state your name, and if + + 18 you're with an organization, state that, or, + + 19 otherwise, just state what your hometown is so we + + 20 have an idea where you're from. + + 21 And out of respect for those that are in line + + 22 behind you, so that we get to everybody, please keep + + 23 it to a couple minutes. + + 24 And, we look forward to -- and thank you for + + 25 standing up and addressing us here tonight. + + + + + + + + 154 + 1 REV. RYAN SMITH: Hello. My name is + + 2 Reverend Ryan Smith. I'm from Penn Yan. + + 3 First of all, I'd like to thank our State + + 4 Legislature for coming down to address this issue. + + 5 As a clergy member, and a freshman in + + 6 high school, a lot of students approach me with + + 7 their problems. + + 8 And, out of all the things that I've heard, + + 9 drugs is one of the number-one things that I heard. + + 10 And I want to stress that we need to be + + 11 teaching children, our students, that we are on the + + 12 front line. We're the ones who are interacting with + + 13 the kids who are doing these drugs and who are being + + 14 exposed to these things. + + 15 So, you know, things like Natural Helpers, + + 16 and other groups, that teach students to help other + + 17 students, their peers, who are struggling with this + + 18 issue. + + 19 I hear it all the time. + + 20 And it's -- I think our students really need + + 21 to step up, because we're the ones who are on the + + 22 front line. + + 23 We're the ones who are being -- when it comes + + 24 to the schools, the number-one people who are being + + 25 affected by this, our families, and are being + + + + + + + + 155 + 1 exposed to this. + + 2 And so I really think students need to learn + + 3 how to stand up and talk with their peers about this + + 4 issue. + + 5 SENATOR O'MARA: Thank you very much. + + 6 [Applause.] + + 7 CHRISTINA CLEVELAND: Hi. + + 8 I'm Christina Cleveland. + + 9 I'm the mother of Miranda Cleveland. + + 10 She was a 17-year-old who died unexpectedly + + 11 November 9, 2015, from a heroin overdose. + + 12 She was a senior at Dundee school. + + 13 Her death is being treated as if she was + + 14 another junky off the street. + + 15 The New York State trooper that is + + 16 investigating the case has made some very + + 17 inappropriate comments two days after her death. + + 18 He said, "There was one way off heroin, and + + 19 she just took that exit." + + 20 He has also made accusations, assumptions, + + 21 took her character from a family that didn't know + + 22 her, where she was doing the drug activity, where + + 23 the OD took place, instead of her loving family of + + 24 almost 18 years. + + 25 He's not asked anyone. + + + + + + + + 156 + 1 When I do call him, I've never met the man + + 2 face-to-face, so I have to call him, when he decides + + 3 to call me back, days, he acts as if he has no time + + 4 for me. + + 5 "I have a case overload. I'm too busy." + + 6 I do believe we need more, and we need more + + 7 officers. + + 8 I'm being treated -- I couldn't tell you the + + 9 last time I even talked to him. I can't tell you + + 10 where her case is at. + + 11 All I hear is, "We're waiting. We're + + 12 waiting." + + 13 Thank you. + + 14 [Applause.] + + 15 SENATOR O'MARA: Thank you. + + 16 Thank you very much for sharing that with us, + + 17 and our heartfelt condolences for your loss. + + 18 CARRIE AHEARN (ph.): Good evening, and I + + 19 invite anybody who's a note-taker to get their pen + + 20 ready. + + 21 I know a lot of people have tucked them away. + + 22 My name is Carrie Ahern, and I am a village + + 23 resident here in Penn Yan, and I have been inspired + + 24 by all of the members of YSAC, whether I'm an + + 25 acquaintance of theirs or a personal friend of + + + + + + + + 157 + 1 theirs. + + 2 And, I just wanted to share with everybody + + 3 here in our community that I am working with the + + 4 Penn Yan United Methodist Church. + + 5 We are going bring a program to Penn Yan + + 6 called "Celebrate Recovery." + + 7 This program is a crisis-center recovery + + 8 group, but it is for anybody and everybody, to + + 9 include addicts, but also to include people who are + + 10 codependent, or if they have an anxiety behavior + + 11 that they are covering and masking with an + + 12 addiction, whether it's substance abuse or + + 13 behavioral-based. + + 14 And for more information, we're going to have + + 15 a public-information meeting at the Methodist church + + 16 on Sunday, March 20th, at 6:00 p.m., in their + + 17 basement fellowship hall. + + 18 Thank you. + + 19 [Applause.] + + 20 SENATOR O'MARA: Thank you. + + 21 PAMELA FINGER (ph.): Good evening. + + 22 My name is Pamela Finger, and I'm a member of + + 23 YSAC, and I've also been an educator in the + + 24 New York State education system since 1993. + + 25 In my 23 years in education, I've come in + + + + + + + + 158 + 1 contact with a countless amount of youth in the + + 2 Steuben and Yates county areas. + + 3 Lately, I've talked to several former + + 4 students who are, and have been, affected by this + + 5 heroin problem, and they've all told me the same + + 6 thing: We need more education past fifth grade. + + 7 I'm asking that we listen to these young + + 8 people, and figure out how to get more education out + + 9 to all students earlier, rather than later, before + + 10 it's too late, because I don't need to go to -- + + 11 I don't like to go to another forum and have a + + 12 former student come up to me and say: Mrs. Finger, + + 13 we need help, and we need help now. + + 14 Thank you. + + 15 [Applause.] + + 16 SENATOR O'MARA: Thank you. + + 17 PATTY O'KELLEY (ph.): Okay. This is going + + 18 to be the strangest of all. + + 19 I've been here about two months. + + 20 I grew up in Elmira as a small child. + + 21 I moved, Boston, New York, Ohio, Kentucky, + + 22 Florida, Georgia, Illinois, and Colorado. + + 23 My mother just passed away, so I decided to + + 24 come home, and this is what I call "home." + + 25 I will tell you that the cities that you're + + + + + + + + 159 + 1 referring to, less than 1 percent are enjoying, if + + 2 they have, a community center. + + 3 So the rural isn't necessarily it. + + 4 And forgive me for not thanking you all, but + + 5 we all know we're out of time. + + 6 You guys are doing your jobs. + + 7 You guys are doing your jobs. + + 8 But here's where I'm coming from, and this + + 9 has been ignored for the last 2 1/2 years. + + 10 There is no presidential candidate right now + + 11 that's going to address any of this. + + 12 We don't know whether it's going to get + + 13 better or worse. + + 14 The state has no money. + + 15 The government has no money. + + 16 The schools have no money. + + 17 There's a side of this that doesn't need + + 18 money. + + 19 I'm thrilled that, first, two church members + + 20 came up here. + + 21 One of the reasons I came back to a small + + 22 town is that I need a family. + + 23 I am on the streets and I'm already running + + 24 into people I know. + + 25 Dennis, one of our superintendents, has + + + + + + + + 160 + 1 already heard my story a bit. + + 2 But, a legislation or an idea or an + + 3 understanding needs to pass that is free, and that + + 4 is, to let the people know, who know, these -- if + + 5 you can't find an NA meeting, I have news for you, + + 6 you're the ones starting it. + + 7 Where am I coming from? + + 8 I'm from a generation back in the day. + + 9 My father died of alcoholism, many of us had + + 10 the same situation, but I suffered from that. And + + 11 I went to Adult Children, Women Who Love Too Much, + + 12 and those -- I mean, everything out there, and + + 13 I finally barged into an AA meeting and lied for + + 14 17 years about not being an alcoholic. + + 15 Rare, but, that group of men had the secret, + + 16 and it was free. It was a dollar in the basket for + + 17 the coffee. + + 18 What I'm saying is, is that if this is a + + 19 small town and we have churches coming together, + + 20 I have been to two of them. I'm trying to make my + + 21 way to all of them. I heard the best words I ever + + 22 heard, which one of them said, "all the churches are + + 23 coming together." + + 24 We need the churches. + + 25 But as far as the schools and getting better + + + + + + + + 161 + 1 education, why is this gentleman not telling his + + 2 story? + + 3 I have a story to tell, but because I'm not + + 4 all these certifications, the certified people can + + 5 assist. But if you don't use the people who have + + 6 been there, done that, this mother said, "I knew + + 7 nothing." + + 8 Guess what? + + 9 She knows what this mother may need to know + + 10 tomorrow. + + 11 This entire community can answer the phone + + 12 when she calls and says "I need help." + + 13 And we can sit there with coffee in a lobby + + 14 somewhere until the help gets there, because if we + + 15 just put 92 things on your desk that said, Give us + + 16 money, this is a community. + + 17 I have a picture in my phone, when we were + + 18 this big, on this lake. + + 19 We don't need $600 condos. + + 20 We need some canoes. + + 21 There was ten of our kids, all together, + + 22 meaning our friends, and I said, No wonder we were + + 23 happy. We had ten kids, and we were all in bathing + + 24 suits. + + 25 I'm going to say something that's going to + + + + + + + + 162 + 1 really upset people, but please understand I am + + 2 aspiring to go to University of Pennsylvania for my + + 3 master's in positive psychology, which is, get these + + 4 kids off of drugs, and I'm talking about the little + + 5 ones. + + 6 I went a middle-school band tour, and there + + 7 was this many drugs for our children. + + 8 It isn't the pot that got them stuck on it. + + 9 It's that we did not teach them how to get + + 10 over anxiety, and we didn't teach them how to get + + 11 over depression, and we didn't teach them how to + + 12 deal with some of these issues. + + 13 I was a college recruiter for 10 years, and + + 14 I had a gentleman come to tell me he was the only + + 15 one in his school who had not been on Adderall. + + 16 The drug laws for our children in school + + 17 needs to be change now, that it's not every kid has + + 18 ADD. + + 19 I know the educators. I know people -- + + 20 [Applause.] + + 21 PATTY O'KELLEY (ph.): -- they come out and + + 22 they are used to being given a tool to deal with + + 23 what they're going through. + + 24 I will give my card to all of you guys. + + 25 I will give my card to you guys. + + + + + + + + 163 + 1 You guys are all doing great jobs. + + 2 But the educators, let us in. + + 3 Let us in who can help your kids. + + 4 Why can't we -- when I was in psychology, you + + 5 all heard of arachnophobia? + + 6 Okay. We studied it in a book. We looked at + + 7 it in a piece of paper. + + 8 But it wasn't until we saw the video of the + + 9 true guy in the corner, sweating, shaking, and + + 10 vomiting, that we realized what it really was, and + + 11 it isn't just, he's afraid of spiders. + + 12 And it's the same with heroin. + + 13 He can't come in like this and just tell + + 14 kids, Don't do it, and posters and all that, + + 15 nothing. + + 16 But he can come in with some serious video + + 17 and go back to the world of scared straight. + + 18 [Applause.] + + 19 PATTY O'KELLEY (ph.): You know, "Breaking + + 20 Bad" romanced this, and I mean romanced it. + + 21 That was a great show as far as dialogue. + + 22 But as far as when they were laying sick and + + 23 vomiting, man, you just kind of looked past that. + + 24 It romanced it. + + 25 But until that's the picture these kids see + + + + + + + + 164 + 1 in fifth grade, sixth grade, seventh grade, + + 2 eighth grade, ninth grade, tenth grade. It can't be + + 3 an auditorium like this, because the kids are going + + 4 to giggle over here. + + 5 You need to get small groups and get these + + 6 people in to help. + + 7 But as a community, now that I am back, I'm + + 8 back because it is a small town, and we need to + + 9 start having some phone people that answer phones + + 10 when people need. + + 11 I have gone through a lot in my life, + + 12 depression, all of that. But when I pick the phone + + 13 up and say "Help," I get, "Well, what did you want? + + 14 Well, she can't call you back for two years." + + 15 I have offered to volunteer at several + + 16 places, in schools, in committees. + + 17 I have not had one phone call. + + 18 I have been through this, family-wise, + + 19 kid-wise. + + 20 I was recruiting juniors and seniors for + + 21 10 years. + + 22 That's why I do this, and all I did was get + + 23 their heads turned around, changed their grades, and + + 24 get them out of college. + + 25 It doesn't take all your money that you don't + + + + + + + + 165 + 1 have, and all these things. + + 2 We need the church basements. + + 3 We need the auditoriums. + + 4 We need some canoes, and we need some fishing + + 5 poles. + + 6 And we need some things to get these kids out + + 7 of that depression, and we need to understand. + + 8 And if you have funding to send me to U Penn + + 9 for positive psychology, I'll come back and do it + + 10 all. + + 11 So, that's all I've got. + + 12 [Applause.] + + 13 SENATOR O'MARA: Thank you. + + 14 Could you give us your name for the record, + + 15 please. + + 16 I'm sorry. + + 17 PATTY O'KELLEY (ph.): They all know it. + + 18 Somehow they all know me. + + 19 Patty O'Kelley. + + 20 SENATOR O'MARA: Thank you, Patty. + + 21 STEPHANIE CAMPBELL: Good evening. + + 22 My name is Stephanie Campbell, and I'm the + + 23 director of policy for Friends of Recovery New York. + + 24 I just want to express my gratitude to my + + 25 Senators who I've been speaking with, and having + + + + + + + + 166 + 1 conversations with, and a new Assembly friend. + + 2 Thank you. That's awesome to meet you. + + 3 I also worked with the Assembly Minority on + + 4 that task force, and it was fantastic, and there's + + 5 some great things in it. + + 6 I also, though, want to say that, my name is + + 7 Stephanie Campbell and I'm a person in long-term + + 8 recovery. And what that means is -- + + 9 [Applause.] + + 10 STEPHANIE CAMPBELL: Thank you. + + 11 -- what that means is, I haven't used drugs + + 12 or alcohol for 15 years. + + 13 And what that's allowed me to do, is it's + + 14 allowed me to be a mother of two beautiful children, + + 15 one who turned 21 today. It's allowed me to be a + + 16 wife. It's allowed me to be an employed person. + + 17 Prior to my coming into recovery, I must have + + 18 cost New York State taxpayers a couple millions of + + 19 dollars, maybe. + + 20 So I've saved you guys a lot of money. + + 21 [Laughter.] + + 22 STEPHANIE CAMPBELL: And, I hope that Devon + + 23 and Adrianna -- + + 24 Is it Adrianna? + + 25 -- Arianna, continue on this journey. And, + + + + + + + + 167 + 1 let's stay connected, let's get connected. + + 2 Alexis, you are amazing. Just blew my socks + + 3 off. I'm sitting over there, going, Who is this + + 4 woman? + + 5 Just really powerful. + + 6 And I loved what the last speaker said, "We + + 7 know recovery." + + 8 And, you know, we know the problem of + + 9 addiction. + + 10 It's horrible. + + 11 It's horrific. + + 12 It's not just heroin. + + 13 That's the drug du jour right now. + + 14 But it was crack in the '80s. And the + + 15 Rockefeller drug laws did not make that situation + + 16 better, I'm sorry to say. + + 17 What I can say to you is that, as a person in + + 18 long-term recovery, I know that, you know, I'm an + + 19 expert in this field, because I'm doing it, because + + 20 I have battled addiction, and because, you know, I'm + + 21 here as living proof that recovery works. + + 22 And, so, in the conversation that we've been + + 23 having, you know, which is incredible, that we're + + 24 now talking about we can't incarcerate our way out + + 25 of this. Right? + + + + + + + + 168 + 1 We need treatment. + + 2 We need education. + + 3 We need prevention. + + 4 But we need recovery. + + 5 Someone comes out of crisis and they leave + + 6 treatment, and they go back into a drug-infested + + 7 housing project, you know, a system which offers no + + 8 supports, there's no recovery community + + 9 organization, there's no recovery community centers, + + 10 there's no recovery coaches, there's no family + + 11 navigators. + + 12 We're setting them up to fail, and we have to + + 13 stop doing that, and we do have to invest in the + + 14 solution of recovery. + + 15 So, I just want to, you know, reiterate, last + + 16 month in Buffalo there were 23 overdoses, in one + + 17 month. + + 18 It's not getting any better, and we know + + 19 that. + + 20 But there is a solution, and we're calling on + + 21 our representatives in the Legislature to put more + + 22 money. + + 23 We're asking for $50 million. + + 24 That's a drop in the bucket. + + 25 We need $50 million for recovery supports in + + + + + + + + 169 + 1 the community, so that we can do recovery, so that + + 2 don't have to go and visit our friends over here, so + + 3 they don't have to wreak havoc and tornado through + + 4 life, and possibly die, and leave their children, + + 5 you know, parentless. + + 6 So we do ask that this disease of addiction + + 7 is treated as a chronic illness, not as a moral + + 8 deficiency. + + 9 And we need that funding now. + + 10 Thank you. + + 11 [Applause.] + + 12 YANA KHASHPER: Hi, good evening. + + 13 I'm a little short. + + 14 My name is Yana Khashper, and I'm the + + 15 co-founder of ROCovery Fitness. We're a brand new + + 16 nonprofit here in Rochester. + + 17 And I just want to say, thank you, to all the + + 18 panelists, all the people who spoke, the room full + + 19 of people that are here to talk about recovery, talk + + 20 about addiction and recovery. + + 21 I'm also a licensed clinical social worker, + + 22 and I've been working in the field for about a + + 23 decade. + + 24 I received a graduate degree from NYU, and + + 25 I had a 4.0 in the height of my addiction. + + + + + + + + 170 + 1 I was not dumb. I was not lacking willpower. + + 2 I had a chronic disease, and that chronic + + 3 disease took everything from me. + + 4 But I'm here to tell that you recovery is + + 5 possible. + + 6 As Stephanie mentioned, we know what the + + 7 problem is. + + 8 You don't need me to tell you what the + + 9 problem is. + + 10 We don't need all you folks to tell us what + + 11 the problem is. + + 12 We can look in the news, we can see all the + + 13 deaths. + + 14 You know, and -- I'm a little nervous. + + 15 And like Stephanie mentioned, we have a + + 16 solution, a solution that we've seen work. + + 17 The Rockefeller drug laws that targeted + + 18 low-level criminals with mandatory minimums probably + + 19 didn't take away the money from the schools, and + + 20 they're probably not the solution that we have + + 21 today. + + 22 The problem that we have today is a chronic + + 23 illness, which is addiction. + + 24 And a lot of folks talked about cancer being + + 25 a chronic illness. + + + + + + + + 171 + 1 I don't know about you guys, but I haven't + + 2 seen anyone get sick from cancer, and people look at + + 3 them and say, Oh, your vomit is repulsive. + + 4 But I've seen people go through withdrawal + + 5 and have that same reaction. + + 6 And that is the problem that we face. + + 7 As Stephanie talked about, we need more + + 8 money. + + 9 We need more money for services. + + 10 Absolutely, we need more supports. + + 11 The Rochester-area detox generally has + + 12 100-person wait list. + + 13 Think about that. + + 14 100-person wait list in Rochester, New York. + + 15 That is unacceptable. + + 16 We could increase incarceration stays. + + 17 We could increase substance abuse -- + + 18 substance-program stays. They would have a benefit. + + 19 But you leave these people and drop them + + 20 right back into those communities, and then what? + + 21 You set them up to fail. + + 22 What we're asking for is recovery community + + 23 organization. Peer-support programs. + + 24 What ROCovery Fitness is, is a nonprofit in + + 25 Rochester, based around sober peers committed to + + + + + + + + 172 + 1 leading physically-active lifestyles. + + 2 We have a live network of supports. + + 3 The Gloucester department, the ANGELS they + + 4 talk about, the recovery coaches, that's what we + + 5 need money for. + + 6 The treatment services, absolutely, all that + + 7 stuff is important, but we have a low-cost + + 8 alternative to death: peer support. + + 9 Our programs cost nothing. + + 10 Our average hikes yield about 40 people in + + 11 recovery. + + 12 We have touched over 500 individuals since + + 13 inception in September. + + 14 Those are some big numbers. + + 15 And we need your help. + + 16 We need your help to reach more people. + + 17 There needs to be an atmosphere of recovery, + + 18 and there needs to be less stigma. We need to + + 19 shatter that stigma. + + 20 And that's what ROCovery Fitness aims to do. + + 21 We have three initiatives under ROCovery. + + 22 One is the community of sober peers that help + + 23 and guide one another. + + 24 Another is the outreach. We talk to + + 25 treatment centers. We talk to anyone that will + + + + + + + + 173 + 1 listen, parole, probation. + + 2 And the last one is education. + + 3 To get into the schools, give them + + 4 presentations, talk about recovery, talk about what + + 5 it looks like; not just say, Stop using drugs and + + 6 alcohol, and here you go, live freely. + + 7 It talks about what you do when you stop + + 8 using drugs and alcohol. + + 9 And that's really the miracle of this. + + 10 I lost my entire life to addiction, and + + 11 I gained it back in recovery, and I live a life + + 12 beyond my wildest dreams. + + 13 And that's possible. + + 14 And that's the atmosphere we need to put out + + 15 there: Recovery is possible. + + 16 Thank you so much for having me up here. + + 17 Sorry I took your time. + + 18 SENATOR O'MARA: Thank you. + + 19 [Applause.] + + 20 JIM OHBROSKI (ph.): Hi. My name is + + 21 Jim Ohbroski (ph.). + + 22 I'm a New York State taxpayer, and a common + + 23 citizen. + + 24 I just look at things pragmatically. + + 25 First, what really irks me are dealers + + + + + + + + 174 + 1 themselves and the sociopathic nature they have. + + 2 They don't care about anybody. + + 3 And what I find interesting, there are + + 4 avenues, is to take the profit out of drug + + 5 trafficking. + + 6 And I also question, just to bring up the + + 7 aspect of Afghanistan, which supplies 60 percent of + + 8 America's heroin now, three times what it did back + + 9 before 9/11, and, where is the federal role in this? + + 10 You know, if we don't have the substance, we + + 11 can't start the addiction. + + 12 And I know it's a long process, and I know + + 13 there's many factors, and I won't address that. + + 14 And the other thing is, Tom Brokaw wrote a + + 15 book called "The Greatest Generation." + + 16 I think we've lost that. + + 17 And I think that one of the root causes of + + 18 addiction and the crisis problems we have is, we've + + 19 kind of lost hope. Is the American Dream there + + 20 anymore? + + 21 If I were 18 years old today, I'd be scared. + + 22 I started out as an electrician, and 30 years + + 23 ago, I would make as much as I do today; and, yet, + + 24 the price of a car has escalated six to eight times + + 25 that. The cost of a house is out of reach. + + + + + + + + 175 + 1 All the things that I was brought up on, I'm + + 2 a second-generation American. My grandparents came + + 3 from Europe, they were poor. + + 4 I actually went back to the areas they were, + + 5 and people don't realize they're still driving + + 6 oxcarts in Russia. + + 7 That's part of family. + + 8 They're still doing that today. + + 9 And they came, my grandparents came, and he-- + + 10 and he was in this country, what, eight years, and + + 11 he owned a house, he owned property. + + 12 That was unheard of in Europe. + + 13 Unheard of. + + 14 But where is that today for an 18-year-old? + + 15 What does an 18-year-old -- I think Devon, + + 16 what he talked about, and I'm saying, Would I be + + 17 disillusioned? + + 18 Would I seek another thing that would make me + + 19 feel good? + + 20 You know, cost of education is outrageous. + + 21 I could go -- in my time, I could go to + + 22 Rutgers, which was an Ivy League school, for $400 a + + 23 semester. + + 24 That's out of the question for a community + + 25 college today. + + + + + + + + 176 + 1 So I look at root causes, and ask you people, + + 2 and the people who have gone through the experience + + 3 of seeking some other source: Is there a future for + + 4 the American youth today? + + 5 And with the universality of labor, China, + + 6 other jobs, I'm not blaming other countries. It's + + 7 just an evolution process. We're becoming more + + 8 global. Political borders are going to change, + + 9 geographic borders are going to change. + + 10 They're going to become economic borders in + + 11 the near future, and I say within 20 years. + + 12 Our whole system is going through a massive + + 13 change, and we need to factor that in. + + 14 And where drugs fits in, is fulfilling some + + 15 kind of need that we can't get, that we would rather + + 16 have, like having a home, having security, things + + 17 that today just are not present. + + 18 And I'm not blaming anybody. + + 19 I think it's an evolution process, as well as + + 20 other things. + + 21 And we need to factor that in. + + 22 What is the hope for the future in this + + 23 country? + + 24 Thank you. + + 25 [Applause.] + + + + + + + + 177 + 1 SENATOR O'MARA: Thank you. + + 2 MICHAEL CHRISTIANSEN (ph.): Good evening. + + 3 SENATOR O'MARA: Good evening. + + 4 MICHAEL CHRISTIANSEN (ph.): I'm + + 5 Michael Christiansen (ph.), lifelong resident here + + 6 in the village of Penn Yan. + + 7 Before I get into a lot of my background, + + 8 I have a question. + + 9 I've seen -- or, I've heard tonight a lot of + + 10 reference to funding. + + 11 And one issue that came to light weeks ago to + + 12 me, in the media, I saw it once, and I haven't seen + + 13 it since, and I don't know if it's true or not, + + 14 especially seeing as how I got it from the media. + + 15 But, I was with the Sheriff's Office, worked + + 16 with Sheriff Spike for 31 years. We were in the + + 17 back of a beat-up old green vans down on Route 14, + + 18 stumbling around back in the day. + + 19 But, in the mid-'80s, a program was + + 20 initiated, where the federal government would + + 21 sponsor a seizure if you had a defendant, whether + + 22 they had a vehicle, whether they had a residence, + + 23 that the federal government, as long as local law + + 24 enforcement did the paperwork, proved their case, + + 25 and took it to the U.S. Attorney up in Rochester, + + + + + + + + 178 + 1 they would pursue a seizure for the funds that these + + 2 drug dealers. + + 3 And we've talked about funding today, we've + + 4 heard increased funding enough times, so I'm trying + + 5 to get to the crux of things here. + + 6 But it was a great program, wherein the + + 7 U.S. Attorney's Office would handle the seizure + + 8 of -- it could be a home, it could be a car, it + + 9 could be the cash money that was taken during the + + 10 arrest. And for 20 percent of the net, they would + + 11 handle it, and local law enforcement got the other + + 12 80 percent, and it had to be used for increased drug + + 13 enforcement, drug programs, anything involved in the + + 14 drug issue. + + 15 And it's my understanding now, and I'm not + + 16 pointing the finger at you gentlemen, because it's + + 17 my understanding that it's at the national level, + + 18 the U.S. Department of Justice, and I heard the + + 19 reference earlier today that this is a nationwide + + 20 crisis, which, if anybody reads the paper, you + + 21 recognized that this heroin issue is not just in + + 22 Yates County. + + 23 So, anyways, my question is: Is that true? + + 24 Has that program been nixed? + + 25 And who's is responsible for that? + + + + + + + + 179 + 1 SENATOR O'MARA: Yeah, there has been a + + 2 recent change in that. + + 3 I would defer to the table over here on the + + 4 specifics of that. + + 5 SHERIFF RONALD G. SPIKE: Yeah, the + + 6 U.S. Department of Justice has put an end to that + + 7 program. + + 8 If you remember the DAG 71 form? + + 9 MICHAEL CHRISTIANSEN (ph.): Yes, I do. + + 10 SHERIFF RONALD G. SPIKE: No longer available + + 11 to us, and they've stopped it. + + 12 So any asset forfeiture through the feds, + + 13 through the DEA, is stopped. + + 14 MICHAEL CHRISTIANSEN (ph.): Now, who made + + 15 that decision? + + 16 SENATOR O'MARA: Not us. + + 17 MICHAEL CHRISTIANSEN (ph.): I know you + + 18 didn't. + + 19 [Laughter.] + + 20 MICHAEL CHRISTIANSEN (ph.): I prefaced my + + 21 question with that. I know you guys didn't. + + 22 But is that the Obama Administration? + + 23 SENATOR ORTT: Former-U.S. Attorney + + 24 Eric Holder. + + 25 MICHAEL CHRISTIANSEN (ph.): Eric Holder. + + + + + + + + 180 + 1 And who does he get his marching orders from? + + 2 I'm not trying to -- + + 3 SENATOR ORTT: Well, today, probably just + + 4 Mrs. Holder. + + 5 But back in the day, it was President Obama. + + 6 MICHAEL CHRISTIANSEN (ph.): Okay. And I'm + + 7 not politicizing things, but if anybody, and I defer + + 8 to the sheriffs here and the District Attorney's + + 9 Office, and Judge Falvey, who was a former district + + 10 attorney, I believe, back in the '80s when we were + + 11 dealing with this, that that program initiated a lot + + 12 of money. + + 13 And all I've heard today from everybody here + + 14 is increased funding. + + 15 And there is -- it's gone out the window. + + 16 This week, up in Rochester, 18 pounds of + + 17 cocaine. + + 18 Now, those of you that aren't involved in + + 19 drug enforcement might not recognize, that's a lot + + 20 of coke. 300-and-some-odd-thousand dollars, a BMW. + + 21 And now that's not getting seized? + + 22 And all I heard about tonight is how much + + 23 more money we need. + + 24 I implore on our political leaders to get to + + 25 the bottom of that issue and put a hammer on. + + + + + + + + 181 + 1 With this epidemic, and that's what it's been + + 2 referred to here today, a national crisis, an + + 3 epidemic, that we need more funding. + + 4 There it is. + + 5 There it is, and it's just gone up in smoke + + 6 in the last week. + + 7 And how many here tonight had heard about + + 8 that? + + 9 Not a lot. + + 10 Never. + + 11 DA VALERIE G. GARDNER: If I could just + + 12 address that over here. + + 13 Valerie. + + 14 Woo-hoo. + + 15 Hi. + + 16 MICHAEL CHRISTIANSEN (ph.): Hi. + + 17 DA VALERIE G. GARDNER: While there are some + + 18 serious restrictions for the federal forfeiture + + 19 procedures, they are still available, in a very + + 20 limited way, if the federal prosecution started as a + + 21 federal investigation. + + 22 What we do have, though, available through + + 23 our state law and our state seizure asset-forfeiture + + 24 provisions, is the ability to -- and we've done this + + 25 over the last two years, it's one of the things that + + + + + + + + 182 + 1 I've instituted -- is done some asset forfeiture. + + 2 So you're looking at vehicles, some cash, and that + + 3 gets disbursed to, a percentage to the + + 4 law-enforcement agency, a percentage to OASAS, which + + 5 is the state substance-abuse agencies, and a smaller + + 6 percent to the DA's Office. And that gets used + + 7 specifically to fund law enforcement-related + + 8 activities, such as training, et cetera. + + 9 MICHAEL CHRISTIANSEN (ph.): Right, and I -- + + 10 DA VALERIE G. GARDNER: So those funds, by + + 11 statute, it's very strictly regulated as to what we + + 12 can spend that kind of money on. + + 13 But make no mistake, I mean, we're really not + + 14 seeing -- the primary dealing that you're talking + + 15 about, where people have bundles of cash, isn't + + 16 really happening here. + + 17 MICHAEL CHRISTIANSEN (ph.): Right, but + + 18 there's still vehicles and there's still homes. + + 19 And I'm familiar with the program that you're + + 20 talking about at the state level. + + 21 Unfortunately, the State, they got such a + + 22 huge hunk of the pie, that there's none for local + + 23 law enforcement. + + 24 DA VALERIE G. GARDNER: It's over 40 percent + + 25 that goes to OASAS. + + + + + + + + 183 + 1 MICHAEL CHRISTIANSEN (ph.): Exactly. + + 2 DA VALERIE G. GARDNER: It's almost + + 3 30 percent that goes to the local law enforcement + + 4 agency -- + + 5 MICHAEL CHRISTIANSEN (ph.): 30 percent. + + 6 DA VALERIE G. GARDNER: -- and the rest into + + 7 the DA's Office to go on law enforcement. + + 8 MICHAEL CHRISTIANSEN (ph.): Right, and + + 9 that's my concern. + + 10 With this federal program, 80 percent went + + 11 back to local law enforcement, and the feds kept + + 12 20 percent for their problem. + + 13 And now that's gone. + + 14 So that question being answered, I also have + + 15 to reflect on my experiences, as I mentioned + + 16 earlier, with Sheriff Spike, I go back 30-plus + + 17 years, and was actively involved in drug + + 18 enforcement. + + 19 And, I don't have to tell a lot of people, + + 20 here, but you would think that this is something new + + 21 after what you've heard tonight. + + 22 This has been going on for a while. + + 23 And, I keep hearing, once again, increased + + 24 funding. + + 25 I think we need to take another look at what + + + + + + + + 184 + 1 we've been doing for the last 30 years, that hasn't + + 2 been working, and whether it's the Rockefeller laws + + 3 that shouldn't have been changed, whether it's this + + 4 Good Samaritan law, that really concerns me. + + 5 I mean, we need to hold people accountable. + + 6 Gail spoke about just being concerned with + + 7 the marijuana laws. + + 8 But we have somebody that goes into an + + 9 overdose, and because they call for help, the gloves + + 10 are off. + + 11 Now, how would that work as far as + + 12 enforcement of our DWI laws, if somebody goes off + + 13 the road and hits a telephone pole, and they get on + + 14 the phone and call for help? + + 15 Do they get a free ride for their DWI because + + 16 they called for help? No. + + 17 We wouldn't have good enforcement if that was + + 18 the case. + + 19 So I think we need to take a look at the laws + + 20 that we're already using, that aren't being + + 21 effective. + + 22 We need to look at the treatment programs. + + 23 I served on both the village board and the + + 24 school board with Dr. Dennis, and I've heard all + + 25 this talk about, we have to get into the schools. + + + + + + + + 185 + 1 We've been getting into the schools. + + 2 Okay? + + 3 I think maybe what we need to do is to get + + 4 into the families. + + 5 We need two parents back in the household. + + 6 We need some parents taking responsibility. + + 7 We need to get into the families to get this + + 8 dealt with. + + 9 [Applause.] + + 10 MICHAEL CHRISTIANSEN (ph.): Asking the + + 11 government for more funding, for more programs, + + 12 I think it's time to take a step back and say, Wait + + 13 a minute, what have we been doing with the funding + + 14 we were already spending? + + 15 I apologize for my delivery. + + 16 My wife often tells me, Mike, it's not your + + 17 message, it's your delivery. + + 18 So, I apologize for that, and thanks for your + + 19 time. + + 20 [Applause.] + + 21 SENATOR O'MARA: Michael, thank you very + + 22 much. + + 23 Michael was last, and certainly not least. + + 24 Thank you for wrapping things up for us in + + 25 good fashion. + + + + + + + + 186 + 1 Alexis has something she would like to say + + 2 down here regarding Truth Pharm. + + 3 ALEXIS PLEUS: Thank you so much for letting + + 4 me do that. + + 5 A lot of people spoke tonight about the + + 6 community getting involved. + + 7 And I agree with the Senators, they've put a + + 8 lot of good legislation out last year, and the + + 9 Assembly failed to pass, I believe, every single + + 10 measure that you put in place, aside from maybe one. + + 11 Truth Pharm is taking trips to Albany, + + 12 February 25th, March 1st, March 15th, and + + 13 March 16th. + + 14 Any community member is welcome to join us, + + 15 and we're going to visit Assemblymen and Senators in + + 16 Albany, and we really -- we really, really, really + + 17 need community support. + + 18 We need more people to join us, more people + + 19 to go to Albany and talk to our political leaders, + + 20 and tell them the things that you're all talking + + 21 about tonight. + + 22 We need to go to Albany and talk to them + + 23 face-to-face. They need to hear from us. + + 24 So you can find our information our on + + 25 website, www.truthpharm, spelled with a "ph," .org, + + + + + + + + 187 + 1 or you can find us on our Facebook. + + 2 And we'd love to have anybody that would like + + 3 to go with us, join us. + + 4 SENATOR O'MARA: Thank you, Alexis. + + 5 [Applause.] + + 6 SENATOR O'MARA: Okay. I want to -- it's + + 7 almost 10:00. We're going to wrap it up. + + 8 I want to thank Penn Yan school system for + + 9 the use of this beautiful auditorium here this + + 10 evening. + + 11 I want to thank all of our panelists for your + + 12 time, your input on this; my colleagues for coming + + 13 from all across the state to be here; for all of you + + 14 for coming and attending and being so attentive for + + 15 this. + + 16 It shows the extent of the concern that this + + 17 community has for this problem. + + 18 And I agree with what all of our speakers + + 19 said here this evening with regards to, this needs + + 20 to be a whole community approach to this, and it's + + 21 not just about money. + + 22 We do look at refocusing our programs, but we + + 23 have a $145 billion budget we're considering in + + 24 Albany right now. + + 25 We ought to be able to find the appropriate + + + + + + + + 188 + 1 resources, or reshift resources, to focus more + + 2 intensely on this. + + 3 But it is going to take the community at + + 4 large to really fight this, through our churches, + + 5 through our community organizations, all of these + + 6 groups to do that. + + 7 So thank you, all, and please be active in + + 8 your community. + + 9 (Whereupon, at approximately 9:51 p.m., + + 10 the public hearing held before the New York State + + 11 Joint Senate Task Force on Heroin and Opioid + + 12 Addiction, concluded.) + + 13 + + 14 ---oOo--- + + 15 + + 16 + + 17 + + 18 + + 19 + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + \ No newline at end of file diff --git a/src/test/resources/hearing/10-10-17 NYS Senate Flooding_IJC Plan 2014 FINAL.txt b/src/test/resources/hearing/10-10-17 NYS Senate Flooding_IJC Plan 2014 FINAL.txt new file mode 100644 index 000000000..2b3697c6d --- /dev/null +++ b/src/test/resources/hearing/10-10-17 NYS Senate Flooding_IJC Plan 2014 FINAL.txt @@ -0,0 +1,14080 @@ + + + + 1 JOINT HEARING BEFORE THE NEW YORK STATE SENATE + STANDING COMMITTEE ON ENVIRONMENTAL CONSERVATION + 2 AND + STANDING COMMITTEE ON AGRICULTURE + 3 ------------------------------------------------------ + + 4 PUBLIC HEARING + + 5 TO HEAR FROM STAKEHOLDERS ON THE IMPLEMENTATION OF + PLAN 2014, TO DISCUSS THE IMPACTS OF RISING WATER + 6 LEVELS ON LOCAL BUSINESSES AND HOMEOWNERS, THE + ABILITY OF INTERNATIONAL LAKE LEVEL REGULATORS TO + 7 PREEMPTIVELY MANAGE LAKE LEVELS, THE ADEQUACY AND + ACCESSIBILITY OF STATE AND FEDERAL PROGRAMS DESIGNED + 8 TO ASSIST LOCALS IN PROTECTING AGAINST RISING WATER + LEVELS, AND TO EXPLORE POSSIBLE STATE OPTIONS FOR + 9 IMPROVED ASSISTANCE + + 10 ------------------------------------------------------ + + 11 Mexico High School Auditorium + 3338 Main Street, Mexico, New York + 12 + October 10, 2017, at 4:00 p.m. + 13 + + 14 PRESIDING: + + 15 Senator Thomsa F. O'Mara, Chair + Standing Committee on Environmental Conservation + 16 + Senator Patty Ritchie, Chair + 17 Standing Committee on Agriculture + + 18 + + 19 PRESENT: + + 20 Assemblyman William A. Barclay + + 21 Assemblyman Bob Oaks + + 22 + + 23 + + 24 + + 25 + + + + + + + + 2 + 1 + SPEAKERS: PAGE QUESTIONS + 2 + Stephen Durrett 11 21 + 3 Alternate Co-Chair + Arun Heer + 4 U.S. Secretary + The International Lake Ontario- + 5 St. Lawrence River Board + + 6 Bill Werick 47 58 + Technical Advisor + 7 International Joint Commission + + 8 Lieutenant Colonel Adam Czekanski 94 108/153 + Commander, Buffalo District + 9 Craig Forgette 94 108 + Management Team & Continuing + 10 Authorities Program Manager, + Buffalo District + 11 Bridget Brown 94 108 + Regulator, Auburn Field Office + 12 U.S. Army Corps of Engineers + + 13 Kenneth Lynch 111 119 + Executive Deputy Commissioner + 14 New York State Department of + Environmental Conservation + 15 + Chris Leo 131 136 + 16 Acting President + New York State Homes and + 17 Community Renewal + + 18 Frank Sciremammano, Jr., Ph.D., P.E. 153 163 + F-E-S Associates + 19 Professor (retired) of Engineering, + Rochester Institute of Technology + 20 International Lake Ontario-St. Lawrence + River Board + 21 + Mary Austerman 177 186 + 22 Coastal Community Development + Specialist, New York Sea Grant + 23 Wayne County Cooperative Extension + + 24 + + 25 + + + + + + + + 3 + 1 + SPEAKERS (continued): PAGE QUESTIONS + 2 + Dale Currier 190 198 + 3 Director + Oswego County Emergency Management + 4 + Gary DeYoung 201 209 + 5 Director of Tourism + Thousand Islands International + 6 Tourism Council + + 7 Scott Aubertine 211 221 + Supervisor + 8 Town of Lyme + + 9 Dr. Dan Barletta 223 231 + Riparian, Town of Greece + 10 Member of Grand View Beach Association + + 11 Cathleen Goodnough 233 242 + Co-Owner + 12 Green Point Marina & Mobile Home Park + + 13 Mark LaLonde 243 246 + Owner + 14 Hutchinson's Boat Works + + 15 + + 16 + + 17 + + 18 + + 19 + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 4 + 1 SENATOR O'MARA: I want to thank everyone for + + 2 coming out this evening on this very important issue + + 3 that we have throughout the shore of Lake Ontario + + 4 and the St. Lawrence River. + + 5 So, I appreciate everyone coming out tonight. + + 6 I'm Senator Tom O'Mara, senator from the + + 7 Southern Tier area of New York; the Elmira, Corning, + + 8 Hornell, Ithaca, area and the Southern Finger Lakes. + + 9 I'm pleased to be here as Chair of the + + 10 Environmental Conservation Committee in the Senate + + 11 to host this hearing. + + 12 I thank Patty Ritchie for her involvement in + + 13 this issue throughout the entire past year. + + 14 With that, I will turn it over to Patty for + + 15 some opening remarks. + + 16 SENATOR RITCHIE: Thank you, Senator. + + 17 I would like to thank everyone for coming out + + 18 tonight for this important hearing on the + + 19 unprecedented flooding of the Lake Ontario and + + 20 St. Lawrence River. + + 21 I particularly want to thank my colleague + + 22 Tom O'Mara, the Chair of the Senate's Environmental + + 23 Conservation Committee, in holding this hearing so + + 24 we can get some answers -- + + 25 + + + + + + + + 5 + 1 (Member of the audience asks, "Is the + + 2 mic on?") + + 3 SENATOR RITCHIE: -- so we can get some + + 4 answers to some very important questions. + + 5 I'd also like to thank Assemblyman Will + + 6 Barclay and Assemblyman Bob Oaks who have been + + 7 partnering with the Senate to address all of the + + 8 concerns from the people that we represent. + + 9 The record high quart of Lake Ontario and the + + 10 St. Lawrence River have had a profound impact on the + + 11 entire region; in particular, more than 150 miles of + + 12 shoreline that I represent. + + 13 From docks and breakwalls, to primary and + + 14 seasonal homes, to the impact on local businesses, + + 15 this devastation has been heartbreaking. It will be + + 16 years before people completely recover. + + 17 But please know that we are all working hard + + 18 to get people back on their feet and on the path to + + 19 recovery. + + 20 Visiting your homes and businesses, and + + 21 hearing your stories, is why I, Senator O'Mara, + + 22 Assemblyman Barclay, and Assemblyman Oaks enlisted + + 23 the help of our colleagues to work with the Governor + + 24 to deliver 45 million in emergency assistance to + + 25 primary and seasonal property owners, businesses, + + + + + + + + 6 + 1 and local governments at the end of session. + + 2 Then, to help make sure those who were + + 3 affected were able to access those resources, + + 4 I co-sponsored, along with my colleague + + 5 Assemblyman Barclay, workshops in six communities, + + 6 where we were able to answer questions, and where + + 7 residents could learn about the assistance that was + + 8 available to them. + + 9 Which brings us here today. + + 10 I've been working closely with the Governor's + + 11 Office, as I shared concerns and stories about the + + 12 shoreline damage. + + 13 And as you have heard, as of last Friday, + + 14 everyone who has applied for help will receive + + 15 funding. + + 16 I just want to take a moment to thank + + 17 Governor Cuomo for listening to us, understanding + + 18 the situation, and working with us to make sure the + + 19 funding was available for all applications. + + 20 I know many in this room, as we watched what + + 21 was going on around the country and the other + + 22 devastating storms, we were really worried that + + 23 there wouldn't be any help, potentially, with the + + 24 FEMA application, and where that was going to leave + + 25 many of you. + + + + + + + + 7 + 1 So that was great news, to find out that the + + 2 Governor was willing to the work with the Senate and + + 3 the Assembly, to make sure that all the applications + + 4 that had been turned in will receive funding, and + + 5 that is something that's been important to all of + + 6 us. + + 7 All of us here were determined to hold this + + 8 hearing because many of you suffered from this + + 9 flooding. + + 10 We know that you're still trying to recover, + + 11 and that goes beyond money. + + 12 The peace of mind that comes with hearing + + 13 directly from all those involved is extremely + + 14 important, since the question everyone is waiting to + + 15 be answered, is whether this will happen again next + + 16 year. + + 17 There are a number of experts here today who, + + 18 hopefully, can shed light on this issue. + + 19 We also have families and business owners who + + 20 have been affected. + + 21 State agencies and the IJC have agreed to + + 22 testify, to help us better understand Plan 2014. + + 23 I personally want to thank them for coming + + 24 here this evening. + + 25 If we all understand the size of this + + + + + + + + 8 + 1 flooding issue and we work together, we will be + + 2 better prepared for the future, and better prepared + + 3 to take care of everyone who lives, works, and plays + + 4 on the lake and the river. + + 5 And tonight is an opportunity to hear + + 6 directly from those of you who have been affected, + + 7 whether you're a homeowner, a secondary resident, + + 8 has been affected, or you're a business owner. + + 9 So I want to thank you for coming out + + 10 tonight, and taking time out of your busy schedule + + 11 to be here. + + 12 And I also want to thank, once again, + + 13 Senator O'Mara for taking time out of his busy + + 14 schedule to come and hold this hearing in my + + 15 district. + + 16 Thank you, Senator. + + 17 SENATOR O'MARA: Well, thank you, Patty. + + 18 It's quite all right. + + 19 I got some fishing in while I was here this + + 20 weekend, so it's worth the trip. + + 21 Assemblyman Barclay. + + 22 ASSEMBLYMAN BARCLAY: Thank you, Senator. + + 23 I will be very brief. + + 24 I want to thank Patty Ritchie for the great + + 25 job she's done in the Senate to help secure the + + + + + + + + 9 + 1 financial relief for those who are affected by this + + 2 high water levels. + + 3 And I also want to thank her for the great + + 4 job she's doing to try to get additional funding, so + + 5 we make sure everybody, as she mentioned, can get + + 6 some relief from the high waters. + + 7 I want to thank Tom, my friend + + 8 Tom O'Mara/Senator O'Mara, who served in the + + 9 Assembly for six years with Bob and myself. + + 10 And what Tom didn't tell you, although he's + + 11 Chair of the Environmental Committee in the Senate, + + 12 he also -- we think of him has a resident here of + + 13 Mexico, because his parents have a camp down on + + 14 Ramona Beach. + + 15 And, obviously, I want to thank my colleague + + 16 Bob Oaks, on my far right, from Wayne County. + + 17 He's intimately involved also with this + + 18 problem because, obviously, Wayne County is being + + 19 affected quite heavily like we are here. + + 20 So, thank you all for being here. + + 21 SENATOR O'MARA: Assemblyman Oaks. + + 22 ASSEMBLYMAN OAKS: Thank you, Senator. + + 23 And I would just like to add my greetings. + + 24 Thanks to my colleagues Senator O'Mara, + + 25 Senator Ritchie, and Assemblyman Barclay. + + + + + + + + 10 + 1 Just focusing on this issue, for me, my + + 2 district runs west, from the city of Oswego line + + 3 to the Monroe County line. So, through part of + + 4 Oswego County, Cayuga County, Wayne County. + + 5 And so the issue of, concerned about possible + + 6 flooding, came up in recent years, as considerations + + 7 were being given on what types of policies should + + 8 oversee the lake. + + 9 And then, clearly, this year we had the + + 10 problem that affected all of us on the south and + + 11 eastern shores and the St. Lawrence River area, that + + 12 the impact of the high water. + + 13 And, so, my major concern tonight, and + + 14 interest, is listening, and hearing responses to + + 15 questions about how we can move forward in a way + + 16 that prevents future lake problems, and also puts us + + 17 all on a path of protecting the property that people + + 18 own, the businesses that operate, along that shore. + + 19 So, I appreciate the opportunity, and the + + 20 Senate and Senator O'Mara for coming here, and + + 21 Senator Ritchie for providing that invitation, so + + 22 that we can cover that issue tonight. + + 23 Thank you. + + 24 SENATOR O'MARA: Thank you, Bob. + + 25 And just to let you all know, briefly, a + + + + + + + + 11 + 1 little bit of my background, as Will alluded to a + + 2 little bit: + + 3 My parents are both graduates of Mexico + + 4 Academy. + + 5 My mother grew up two blocks down the street, + + 6 across the street from here, so I've been coming + + 7 here my entire life. + + 8 My family has had a place on the beach, at + + 9 Ramona Beach, for the past 36 years that I've been + + 10 coming here. + + 11 So I've been an avid boater and fisherman in + + 12 the area, and take the concerns of Lake Ontario, its + + 13 beauty, its natural wonder that we have, and the + + 14 economic activity that is generated from that for so + + 15 many. + + 16 So we hope to learn tonight about how the + + 17 programs of relief are going. + + 18 And probably more importantly, for myself and + + 19 others here, what do we expect next year, and the + + 20 year after that, and the year after that? + + 21 Is this the new normal because of Plan 2014, + + 22 and where are we going? + + 23 So I thank you all for being here. + + 24 We're going to start out with + + 25 Mr. Stephen Durrett, the alternate co-chair of the + + + + + + + + 12 + 1 International Lake Ontario-St. Lawrence River Board. + + 2 STEPHEN DURRETT: Thank you all very much. + + 3 Good afternoon, members of the Senate + + 4 Standing Committee on the Environment Conservation, + + 5 and, ladies and gentlemen in the audience. + + 6 My name is Steve Durrett. + + 7 I am the alternate U.S. chair for the + + 8 International Lake Ontario-St. Lawrence River Board. + + 9 I am also a member of the senior executive + + 10 service, civilian, for the U.S. Army Corps of + + 11 Engineers, in the Cincinnati, Great Lakes, and + + 12 Ohio River division. + + 13 I am here today on behalf of Major General -- + + 14 Brigadier General, excuse me, Mark Toy, to represent + + 15 the International Lake Ontario-St. Lawrence River + + 16 Board, which I will refer to as "the board." + + 17 General Toy regrets he could not make it. + + 18 He is in the process of getting his training, + + 19 and stuff. He will be deploying to Afghanistan here + + 20 next month. + + 21 The board was established by the + + 22 International Joint Commission and has ten members; + + 23 five from the United States and five from Canada. + + 24 Our primary duty is to ensure that outflows + + 25 from Lake Ontario meet the requirements of the + + + + + + + + 13 + 1 IJC orders of approval. + + 2 In my comments today, I would like to explain + + 3 the hydrologic conditions which led to the record + + 4 high water levels on Lake Ontario this year, and + + 5 explain the water-level regulation activities the + + 6 board carried out in accordance with the IJC + + 7 December 8, 2016, supplemental orders of approval, + + 8 referred to as "Plan 2014." + + 9 I hope this forum provides the Committee and + + 10 the public with an understanding of three facts: + + 11 The extreme high water levels on Lake Ontario + + 12 this year were primarily driven by extreme weather + + 13 conditions. + + 14 The extreme weather conditions -- + + 15 [Indiscernible audience comments.] + + 16 SENATOR O'MARA: Can we please -- hold the + + 17 comments, please, during this hearing. + + 18 STEPHEN DURRETT: The extreme weather + + 19 conditions cannot be reliably forecast months, or + + 20 even weeks, in advance. + + 21 And the potential impacts to all interests in + + 22 the Lake Ontario-St. Lawrence River system must be + + 23 balanced when making water-management decisions. + + 24 The high water levels on Lake Ontario this + + 25 year primarily stem from two factors: Heavy + + + + + + + + 14 + 1 rainfalls across the entire Lake Ontario and + + 2 St. Lawrence River system that began in April and + + 3 continued through early August, and, a related + + 4 record-setting spring runoff event in the Ottawa + + 5 River Basin. + + 6 Two additional factors also play a role in + + 7 increased water levels: An unusual mild and wet + + 8 winter we had this last year, and, the above-average + + 9 inflows from the upper Great Lakes. + + 10 Water levels on Lake Ontario began in + + 11 January 2017, very similar to 2015 and '16, slightly + + 12 above long-term average, with Lake Erie water levels + + 13 above average. + + 14 Unlike the years of '15 and '16, + + 15 Lake Ontario-St. Lawrence River system and the + + 16 Ottawa River Basin received widespread, + + 17 record-setting precipitation during the months of + + 18 April and May, leading to high water levels and + + 19 flood-related impacts simultaneously occurring on + + 20 Lake Ontario and downstream in the Montreal region. + + 21 During April, the total amount of water + + 22 entering Lake Ontario, known as the "net total + + 23 supply," was the second highest recorded since 1900. + + 24 Net total supply includes precipitation, + + 25 runoff from tributaries, evaporation from the lake + + + + + + + + 15 + 1 itself, and inflow from Lake Erie. + + 2 The Ottawa River Basin also experienced heavy + + 3 rainfall in April, leading to record-setting flows + + 4 and one of the highest recorded since 1960. + + 5 To balance flooding impacts between + + 6 Lake Ontario and Montreal region, Plan 2014 includes + + 7 the F limit, which was based upon previous board + + 8 decisions making strategies -- board-making + + 9 strategies. + + 10 During high-water events in the 1990s, under + + 11 the previous regulation plan, 1958D, the F limit + + 12 determines the maximum outflow from Lake Ontario to + + 13 limit flooding on Lake St. Louis near Montreal. + + 14 In consideration of Lake Ontario's levels, it + + 15 is a multi-tiered rule that attempts to balance + + 16 upstream and downstream flooding damages by keeping + + 17 the level of St. Louis -- Lake St. Louis below the + + 18 given stage for a corresponding Lake Ontario level. + + 19 Outflows were set in accordance with the + + 20 F limit for the entire month of April. + + 21 By April 28th, Lake Ontario water levels had + + 22 reached Plan 2014 Criteria H14 high threshold. + + 23 The Criteria H14 authorizes the board to make + + 24 major deviations from the regulation plan to provide + + 25 all possible relief to riparian owners both upstream + + + + + + + + 16 + 1 and downstream. + + 2 After an extensive review of the high water + + 3 levels occurring throughout the system, along with a + + 4 number of potential outflow strategies, the board + + 5 decide to continue following the F limit, + + 6 recognizing that any greater increases in outflow + + 7 would further increase flooding in the lower + + 8 St. Lawrence River system, while only providing a + + 9 minimal decrease to the Lake Ontario water levels. + + 10 Moving into May, extreme precipitation + + 11 continued across the Lake Ontario-St. Lawrence River + + 12 Basin, and, on Lake Erie itself, resulting in the + + 13 highest net total supply to Lake Ontario in May + + 14 since 1900. + + 15 When combined with April, the total -- the + + 16 net total supply was the highest two months ever + + 17 recorded on Lake Ontario. + + 18 At the same time, more record-breaking rain + + 19 on the Ottawa River Basin caused its outflows to + + 20 peak at historic record rates, and induced more + + 21 significant flooding in the Montreal area and + + 22 further downstream. + + 23 In accordance with the F limit, outflow from + + 24 Lake Ontario was significantly reduced during the + + 25 first week of May during the peak of Ottawa River + + + + + + + + 17 + 1 outflow. + + 2 As Ottawa River flows subsided, Lake Ontario + + 3 outflows was quickly increased, from 6200 cubic + + 4 meters per second during the first week of May, to + + 5 10,200 cubic meters per second by the last week of + + 6 May. + + 7 This flow rate exceeds the flow specified by + + 8 Plan 2014, and was equivalent to the highest weekly + + 9 average outflow released in 1993 and 1998. + + 10 This was also considered to be the maximum + + 11 outflow that could be released while maintaining + + 12 safe velocities for navigation in the international + + 13 section of the St. Lawrence River when Lake Ontario + + 14 levels are high. + + 15 Despite these record high releases, water + + 16 supplies into Lake Ontario remained above average, + + 17 and Lake Ontario continued to rise, peaking at a + + 18 record high level of 75.88 meters by the end of May. + + 19 At this time, the board concluded that any + + 20 further increase in outflows would have further + + 21 adverse impacts to water levels downstream, while + + 22 only providing minimal lowering of the water levels + + 23 on Lake Ontario. + + 24 June and July brought more rain, which + + 25 contributed to the second highest net total supplies + + + + + + + + 18 + 1 on record since 1990 for each of these two months. + + 2 By June -- by mid-June, water levels + + 3 downstream in Montreal began to decline, and + + 4 the board increased the Lake Ontario outflow to a + + 5 new record maximum weekly average outflow of + + 6 10,400 cubic meters per second. + + 7 This required the St. Lawrence Seaway to put + + 8 in mitigation measures in place to ensure safe + + 9 navigations could continue under the increased and + + 10 current velocities that were caused by the increased + + 11 outflow. + + 12 As wet weather continued, the board + + 13 maintained this high record flow of 10,400 cubic + + 14 meters per second into early August, nearly six + + 15 weeks, or eight weeks, of high record flows we were + + 16 discharging through Moses-Saunders Dam. + + 17 By the middle of August, hydraulic conditions + + 18 improved, and since then, the Lake Ontario levels + + 19 have been on a steady decline. + + 20 In fact, Lake Ontario fell 13.8 inches during + + 21 the month of August, which is the largest drop ever + + 22 recorded during any month since records were + + 23 maintained in 1918. + + 24 It continued to fall in September, and + + 25 dropped an additional 11 inches, which is the + + + + + + + + 19 + 1 largest decline on record for the month of + + 2 September. + + 3 Water levels are expected to continue to + + 4 decline over the next several weeks, and with hope + + 5 of average weather conditions, Lake Ontario water + + 6 levels is expected to continue to decline and + + 7 approach average water levels by the end of the + + 8 calendar year. + + 9 That concludes my accounts of the hydraulic + + 10 condition and the board's role in level regulation. + + 11 In summary: + + 12 The magnitude of the record precipitation + + 13 events that occurred this spring and summer were + + 14 caused -- were the cause of high water. + + 15 The coincidental high water levels and + + 16 persistent rainfall, both upstream and downstream in + + 17 the Lake Ontario and the St. Lawrence River system, + + 18 presented a challenge in providing relief to + + 19 riparian property and business owners system-wide. + + 20 With the water levels of Lake Ontario + + 21 starting in 2017, similar to '15 and '16, and no + + 22 forecast that indicated the record-breaking weather + + 23 arrived in the spring and summer, this year events + + 24 demonstrates the uncertainty associated with + + 25 long-term forecasts and the inability to schedule + + + + + + + + 20 + 1 proactive water releases in anticipation of extreme + + 2 weather. + + 3 I hope the Committee and the public have + + 4 gained an understanding of the need to balance the + + 5 impacts to all interests within the system, and the + + 6 complexity of managing water levels during extreme + + 7 weather conditions. + + 8 Despite record outflows being released from + + 9 Lake Ontario, they played a minor role in affecting + + 10 water levels when compared to the effect of the + + 11 weather conditions. + + 12 Due to the large size of Lake Ontario, when + + 13 compared to the capacity of the St. Lawrence River, + + 14 relatively large changes in Lake Ontario outflow + + 15 have a small effect in changing the levels of + + 16 Lake Ontario, but may severely affect interests on + + 17 Lake St. Lawrence and downstream in the lower + + 18 St. Lawrence River near Montreal. + + 19 Before closing, I would like to mention that + + 20 the board, in conjunction with the Great Lakes and + + 21 St. Lawrence River Adaptive Management Committee, + + 22 will be producing a post high-water assessment. + + 23 This assessment would document the hydraulic + + 24 conditions, the actions taken by the board, compiled + + 25 information on known impact, and identify any other + + + + + + + + 21 + 1 strategies that could have occurred under the + + 2 current regulation plan. + + 3 We hope to have this report available next + + 4 year after data collection efforts are completed. + + 5 Thank you for this opportunity, and I now ask + + 6 the Senate Committee if they have any questions? + + 7 SENATOR O'MARA: Thank you, and thank you for + + 8 being here this evening with us as well. + + 9 Patty? + + 10 SENATOR RITCHIE: (No audio.) + + 11 ASSEMBLYMAN BARCLAY: I have a few, I think, + + 12 relatively simple questions. + + 13 Thank you for your testimony. + + 14 First, can we get a copy -- do we have a copy + + 15 of that testimony? + + 16 STEPHEN DURRETT: Yes, you do. + + 17 ASSEMBLYMAN BARCLAY: Oh, it is? + + 18 Thank you. + + 19 How is the board appointed? + + 20 STEPHEN DURRETT: The board is appointed by + + 21 the IJC. + + 22 ASSEMBLYMAN BARCLAY: And you, essentially, + + 23 if I understand the board, your job is, basically, + + 24 the mechanics of keeping the water level; correct? + + 25 STEPHEN DURRETT: Our job is the execution of + + + + + + + + 22 + 1 Plan 2014, yes. + + 2 ASSEMBLYMAN OAKS: Which is, essentially, + + 3 keeping the lake levels. + + 4 Do you feel -- and this is more of an + + 5 opinion, but do you feel, because of Plan 2014, your + + 6 hands are now tied? + + 7 Where, my understanding, in the past, the + + 8 board had some leeway in regulating the lake. + + 9 They thought, maybe it was going to be a + + 10 heavy rainfall in the spring, they might level more + + 11 water out than they would, you know, in a normal + + 12 year. + + 13 Have you felt, under the Plan 2014, that the + + 14 board's, you know, hands are tied because of the + + 15 plan? + + 16 STEPHEN DURRETT: I do not believe so. + + 17 I think this is a record -- my opinion is, + + 18 this is all based upon a record event. + + 19 It's not a record event in one area. It was + + 20 a base -- really, it was a region-wide event that + + 21 occurred over Lake Ontario and the lower + + 22 St. Lawrence River. + + 23 Whatever plan we would have in place, we + + 24 would have had the same conditions. + + 25 I believe we had the same conditions we had + + + + + + + + 23 + 1 this year, no matter what plan was in place. + + 2 ASSEMBLYMAN BARCLAY: In the spring -- + + 3 [Indiscernible audience comments.] + + 4 ASSEMBLYMAN BARCLAY: -- if I understand the + + 5 Plan 2014 correctly, you couldn't release water + + 6 until you hit the 248 level? Is that correct? + + 7 STEPHEN DURRETT: No. We couldn't release + + 8 till we hit the H14 criteria, which varies, from + + 9 week to week, across the given year. + + 10 That's when we have deviation authorities to + + 11 change. + + 12 But then it's a matter of balancing the + + 13 interests upstream and downstream of Lake Ontario. + + 14 ASSEMBLYMAN BARCLAY: And how -- just, how do + + 15 you get on the board? + + 16 It's, the IJC, what, do you put a resume in? + + 17 Or, how do people get on this control board? + + 18 I know it's appointed by the IJC, but -- + + 19 STEPHEN DURRETT: That would have to be a + + 20 question for the IJC. + + 21 ASSEMBLYMAN BARCLAY: Thank you. + + 22 SENATOR O'MARA: Senator Ritchie. + + 23 SENATOR RITCHIE: Just a couple questions. + + 24 I guess I am a little confused on, and I need + + 25 some clarification on, the deviation authority. + + + + + + + + 24 + 1 You're saying that, with what's in place now, + + 2 you're not able to make changes, and there's a + + 3 certain deviation authority. + + 4 Has that changed in the last year under + + 5 Plan 2014? + + 6 STEPHEN DURRETT: No, any major deviations, + + 7 even under the 1958 plan, we would have to ask for + + 8 deviation and do a payback. + + 9 So there was rules in place even under the + + 10 1958D plan as well. + + 11 The 214 -- Plan 2014 tried to establish, + + 12 based upon historical guidelines that we had had in + + 13 1990s, in '93 and '98 events, how we regulated + + 14 Lake Ontario, that was incorporated into the 2'14 + + 15 plan. + + 16 SENATOR RITCHIE: So there is no -- there's + + 17 no change between what has been historically done + + 18 and what was done under Plan 2014? + + 19 STEPHEN DURRETT: That would be hard to say, + + 20 since it's hard to compare historical events, unless + + 21 the floods, the rainstorms, and the events were + + 22 exactly the same. + + 23 And they are not exactly -- no two rain + + 24 events or flood events are exactly the same. + + 25 So it would be impossible to say there's no + + + + + + + + 25 + 1 difference between the two plans. + + 2 SENATOR RITCHIE: And in order to get + + 3 authority to start a deviation plan earlier, where + + 4 would that come from? + + 5 STEPHEN DURRETT: We would have to -- the + + 6 board would have to request authority from the IJC. + + 7 SENATOR RITCHIE: And is that something that, + + 8 potentially, you're going to look at doing in case + + 9 this is an unprecedented wet spring next year? + + 10 Is there anything that you're going to put in + + 11 place that would, potentially, allow you to act + + 12 sooner, or to ask for that authority to deviate from + + 13 the plan? + + 14 STEPHEN DURRETT: Some of that data will come + + 15 out in the post-flood assessment, to look at ways, + + 16 is there anything we could have done differently + + 17 last year, that we could apply to the change or make + + 18 modifications to the plan? + + 19 So, hopefully, some of those things may be -- + + 20 may come out in the post-flood assessment that we'll + + 21 be looking at, that our adaptive management + + 22 committee is looking into. + + 23 SENATOR RITCHIE: And I know it was taken + + 24 under consideration, the flooding in Montreal. + + 25 And just for my own knowledge, what was done + + + + + + + + 26 + 1 to control the outflow from the Ottawa River, which + + 2 was then restricting what could be let out of the + + 3 Robert Moses Dam? + + 4 STEPHEN DURRETT: The Ottawa River + + 5 restrictions, I'm not familiar with because that's + + 6 in Canada. + + 7 I know, by talking to my Canadian + + 8 counterparts, many of their dams along the + + 9 Ottawa River were overflowing as they were, so they + + 10 were already past the ability to do any kind of + + 11 control whatsoever. + + 12 They had reached capacity, and it was just + + 13 the natural flow of river at that point. + + 14 There was no control mechanism available to + + 15 them. + + 16 SENATOR RITCHIE: So is there a way to ask + + 17 that that be looked at? + + 18 Because, if, in fact, there wasn't the issue + + 19 with the flooding in Montreal, and the outflow from + + 20 the Ottawa River could have been slowed down, then, + + 21 it looks like we would have been able to have a + + 22 greater release, which would have kept the damage + + 23 down on Lake Ontario and the St. Lawrence River. + + 24 So, I understand it's a bi-national + + 25 organization, but, how does that work, that the + + + + + + + + 27 + 1 Canadian board is asked to look at those issues + + 2 also? + + 3 STEPHEN DURRETT: Well, they're part of the + + 4 post-flood assessment. + + 5 So, the Canadian and the U.S. government will + + 6 both be looking at the post-flood assessment. + + 7 What could be done on the U.S. side, what + + 8 could be done on the Canadian side, that will all be + + 9 part of the post-flood assessment. + + 10 SENATOR RITCHIE: (Technical difficulties/no + + 11 audio.) + + 12 (Inaudible) what consideration was given to + + 13 the shipping? + + 14 I know a lot of people were hearing on the + + 15 news that, potentially, the water release was slowed + + 16 down because of shipping. + + 17 And what kind of consideration is given to + + 18 shipping, versus personal property, businesses, that + + 19 are affected by the high water? + + 20 [Member of the audience says "They're + + 21 not."] + + 22 STEPHEN DURRETT: Consideration is given to + + 23 all interests (inaudible) upstream and downstream on + + 24 Lake Ontario and the St. Lawrence Seaway. + + 25 So, there's no plan or a formula that says + + + + + + + + 28 + 1 one interest gets more -- more -- more benefit than + + 2 another. + + 3 We try to look at all things: trying to + + 4 balance the environment, trying to balance the + + 5 riparian owners, trying to balance the navigation + + 6 interests. + + 7 The navigation industry put in some extreme + + 8 measures because of the discharges we were + + 9 discharging out of Moses-Saunders, at 10,200. + + 10 They had to put some measures in place to + + 11 help some of their boats. + + 12 They limited the power on boats, so some + + 13 boats could not travel through the St. Lawrence + + 14 Seaway. They were not powered appropriately. + + 15 They also put some tugs to assist at some + + 16 very key points along the seaway, to make sure that + + 17 it was safe passage. + + 18 We did not want an environmental spill, and a + + 19 ship hitting a rock. + + 20 So all those things were put in place, not by + + 21 us, but by the Seaway itself. + + 22 So other interests were considered, and it's + + 23 a matter of balancing what is -- what's -- how much + + 24 water is too much water upstream, or how much water + + 25 is too much water downstream? + + + + + + + + 29 + 1 Discharging more than 10,400 will run the + + 2 St. Lawrence River out of its banks. + + 3 SENATOR RITCHIE: I guess I would just have + + 4 one comment for you to take -- one comment for you + + 5 to take back. + + 6 That if there is a mechanism in place that + + 7 ties your hands under this deviation authority, that + + 8 when it looks like there's a situation, going + + 9 forward, that you need to adjust for, if there's a + + 10 certain time frame for that to happen, or a level, + + 11 that maybe somebody who's making the decisions take + + 12 a look at that again, to untie the hands of the + + 13 people who have to make adjustments. + + 14 STEPHEN DURRETT: And we're hoping to get + + 15 that out of this post-flood assessment. + + 16 Is there anything we could have done + + 17 differently? we will document that in the post-flood + + 18 assessment. + + 19 SENATOR RITCHIE: Thank you. + + 20 SENATOR O'MARA: Assemblyman Oaks. + + 21 ASSEMBLYMAN OAKS: Yes, just to follow up on + + 22 that, so, at this point, there's nothing that's + + 23 happened -- you're going to do the assessment. + + 24 So there's nothing that's happened this year + + 25 that is changing your few tractions at this moment? + + + + + + + + 30 + 1 There could be something, depending on what + + 2 you find through your assessment? + + 3 SENATOR O'MARA: That is correct. + + 4 There is nothing that we think we could have + + 5 done differently this year, but the assessment may + + 6 tell us something different. + + 7 We, literally, had a region-wide, a regional + + 8 flood event, that spanned, not only one area, but + + 9 the entire region of Lake Ontario and the + + 10 St. Lawrence Seaway. + + 11 We had a lot of water that fell over several + + 12 months a period of time that doesn't normally fall + + 13 at that time of year. + + 14 June and July were extremely wet months as + + 15 well. That's not normally wet. + + 16 So we -- it was -- literally, was a + + 17 basin-wide event. + + 18 ASSEMBLYMAN OAKS: In the acceptance of + + 19 Plan 2014, when discussions were about that, people + + 20 raised concerns as we went through that process, one + + 21 of the things that was adopted, was to accept, + + 22 generally, that the plan allowed for higher heights + + 23 in certain years, and, also, lower lows, than what + + 24 the earlier maintenance plans did. + + 25 At what point this year -- and if you said it + + + + + + + + 31 + 1 in your remarks, just to, you know, reiterate that, + + 2 at what point this year did we meet what was the + + 3 acceptable or encouraged higher height as a part of + + 4 Plan 2014? + + 5 STEPHEN DURRETT: I don't know if there was + + 6 actually -- I did not say in it my remarks, but + + 7 there was not a point in time, I think, where we + + 8 could actually say that you've met -- you know, you + + 9 hit a point and you say, Oh, we're there. We need + + 10 to do something differently. + + 11 The lake rose extremely fast in the + + 12 March-April time frame. And it -- by the time-- and + + 13 there was, literally, nothing we could do. + + 14 The lake jumped 80 centimeters in a matter of + + 15 a couple of months, and it was all because of, like + + 16 I said, the basin-wide flooding. + + 17 We knew there was going to be problems, but + + 18 there was nothing we could do because there was no + + 19 place to send the water. + + 20 ASSEMBLYMAN OAKS: But if we look at, + + 21 I guess, and my concern, and your description of + + 22 this event, we know it rained a lot this year, and + + 23 we know there were, you know, various + + 24 considerations, and I know there's different + + 25 perspectives of why we ended up with what we have. + + + + + + + + 32 + 1 I'm very concerned about what happens next + + 2 year and the year after, and beyond, is the sense + + 3 that we have accepted more of higher water, and + + 4 where, other years, lower water than what's been the + + 5 norm, the impacts on those years that we have the + + 6 higher waters, the defense, I remember saying, well, + + 7 the increase is only a few inches. It's about like + + 8 a tennis-ball size. + + 9 But in -- and a number of people's concern + + 10 raised at that point was, in my understanding, every + + 11 other high-water event, '73, '92, that the incident + + 12 of the high water came quite quickly, and then it + + 13 went down. + + 14 And a concern of many of us was, 3 inches + + 15 sustained for months is going to, ultimately, have a + + 16 devastating effect on shorelines because we're going + + 17 to get weather events in that time. + + 18 And so the encouraged or acceptable levels + + 19 also become a great deal more damaging than what + + 20 high-water incidents were before. + + 21 So do you think future high-water incidents, + + 22 because of the plan, look for more sustained water, + + 23 that we can -- if we continue with the plan as is, + + 24 we're going to, on those high-water years, + + 25 anticipate property damage, either approaching or at + + + + + + + + 33 + 1 levels that we've seen this year? + + 2 STEPHEN DURRETT: I can't predict the + + 3 weather. And that's part our problem with doing + + 4 water management, is predicting the weather. + + 5 [Audience member says "Can't hear you."] + + 6 STEPHEN DURRETT: Sorry. + + 7 Part of our problem is predicting the + + 8 weather, and we're not able to predict the weather + + 9 very well. + + 10 There is no one forecasted -- no weather + + 11 forecast said we were going to have the amount of + + 12 precipitation in 2017 that we had, for the whole + + 13 basin. Not just falling on just the Ottawa River or + + 14 falling on Lake Ontario, but it fell up and down the + + 15 entire basin, from the lower St. Lawrence, all the + + 16 way up through the entire Lake Ontario region. + + 17 So if we know that, then you can plan better. + + 18 We don't know what the weather forecast is + + 19 going to be. + + 20 We have a lot of evaporation that occurs on + + 21 Lake Ontario in the summer. + + 22 If you don't have -- if you try to hold the + + 23 lake too low, and you have a lot of evaporation, + + 24 we're going to have a lot of dry docks instead of + + 25 wet docks, because we just don't know the weather. + + + + + + + + 34 + 1 The thing is, we -- and by March -- the + + 2 middle of March of this year, 2017, we were the same + + 3 as we were in 2016. There was no difference between + + 4 '16 and '17. The lake rose just look it did in '16. + + 5 But the events then started in the April time + + 6 frame, April and May, June and July, were all + + 7 extremely wet months for the normal time of year. + + 8 That's not predicted. + + 9 Now, the lake will have a little bit higher + + 10 highs and a little bit lower lows under lake -- + + 11 under Plan 2014, under normal river -- normal + + 12 conditions. + + 13 But that's a normal condition. That's not + + 14 extreme conditions. + + 15 That's the key point I'd like to make, is we + + 16 had extreme wet weather, not in an area, but across + + 17 the entire region. And it's the weather and the + + 18 rainfall precipitation is what was the contributing + + 19 factor for high lake levels on Lake Ontario. + + 20 ASSEMBLYMAN OAKS: So you really can't say, + + 21 under the acceptability level under Plan 2014, at + + 22 what point it's kind of okay, and what point it's, + + 23 you know, not okay? + + 24 I understand we're going to be susceptible in + + 25 the future to whatever the weather is, but there's + + + + + + + + 35 + 1 some anticipating that can be done. + + 2 And, again -- or, outflow, you know, in the + + 3 future, if it reaches to the higher level, some + + 4 would say that there's property damage going to be + + 5 caused even at that upper level. And, certainly, + + 6 with a storm or two, that people are going to suffer + + 7 continually, irrespective of extreme high water, + + 8 because we're accepting a level that's above what + + 9 had been the norm. + + 10 STEPHEN DURRETT: And that's the purpose + + 11 of -- built into Plan 24 (sic) is this H14 criteria. + + 12 There's an upper and a lower band on this H14 + + 13 criteria which allows that, as what you referred to + + 14 as, "a little higher high and little lower lows," + + 15 that we try to stay within. + + 16 The plan operates, as long as you stay within + + 17 that band, the plan runs as the plan is set up to + + 18 run. + + 19 Once we exceed those levels, whether you + + 20 exceed them on the upper end or during a drought + + 21 when you exceed them on the lower end, only does + + 22 then does the board really come into play about + + 23 asking and looking for deviations. + + 24 That's how Plan 2014 is established and we're + + 25 supposed to operate. + + + + + + + + 36 + 1 We will be looking at those levels: Are + + 2 those triggers too high or too low? + + 3 Well, too low, we won't be able to look at it + + 4 very well. + + 5 But the "too highs" we can look at as part of + + 6 our assessment. Are the triggers too high? + + 7 I can't tell you the answer to that question. + + 8 I would say, at this point in time, the + + 9 answer would be, I don't think the triggers are too + + 10 high. But, our assessment may tell us that, yeah, + + 11 maybe the triggers are too high. + + 12 [Indiscernible audience comments.] + + 13 STEPHEN DURRETT: But -- so that's part of + + 14 the post-flood assessment. We will look at those + + 15 triggers, that H14 trigger elevation. + + 16 ASSEMBLYMAN BARCLAY: Well, isn't that the + + 17 biggest different -- sorry, Bob. + + 18 ASSEMBLYMAN OAKS: Go ahead. + + 19 ASSEMBLYMAN BARCLAY: Isn't that the biggest + + 20 difference between the old plan and the current + + 21 plan, that your hands are tied, because you can't -- + + 22 isn't it 248, is that the number, before you go -- + + 23 when you can deviate? + + 24 STEPHEN DURRETT: There's no set number. It + + 25 varies from the time of year, depending -- + + + + + + + + 37 + 1 ASSEMBLYMAN BARCLAY: Well, you just + + 2 mentioned the -- + + 3 [Indiscernible audience comments.] + + 4 ASSEMBLYMAN BARCLAY: Excuse me. + + 5 Excuse me. + + 6 [Indiscernible audience comments.] + + 7 ASSEMBLYMAN BARCLAY: I know. + + 8 Hold on. + + 9 Excuse me. + + 10 -- I just want to -- you mentioned "the + + 11 bands." + + 12 Could you just tell us what the "bands" are? + + 13 STEPHEN DURRETT: It depends on what month of + + 14 the year you want to go. + + 15 The H14 bands that I was referring to, + + 16 there's a high H14 limit and there's a low band. + + 17 ASSEMBLYMAN BARCLAY: What are the numbers, + + 18 though? + + 19 STEPHEN DURRETT: Well, it changes from year + + 20 to year. I mean, I can't tell a number. + + 21 Pick a month, and I'll be able to tell you a + + 22 number. + + 23 ASSEMBLYMAN BARCLAY: All right. + + 24 Take -- take April or May, the spring, when + + 25 the water is high. + + + + + + + + 38 + 1 STEPHEN DURRETT: It's somewhere around + + 2 75.5 meters, approximately. + + 3 [Indiscernible audience comments.] + + 4 ASSEMBLYMAN BARCLAY: And then -- + + 5 Excuse me, excuse me. + + 6 Sorry. I can't have everybody yelling + + 7 questions. I can't hear. + + 8 And then how was -- what was the band under + + 9 the prior plan? + + 10 STEPHEN DURRETT: I can't tell you that. + + 11 ASSEMBLYMAN BARCLAY: Okay. + + 12 All right. + + 13 Okay. + + 14 [Indiscernible audience comments.] + + 15 SENATOR O'MARA: You don't know what the + + 16 criteria was under Plan 1958? + + 17 STEPHEN DURRETT: No, I do not. + + 18 SENATOR O'MARA: Was the International Board + + 19 involved in the formulating of Plan 2014? + + 20 STEPHEN DURRETT: I have only been on the + + 21 board for 2 1/2 years, so, the Plan 2'14 was done + + 22 prior to me arriving at the board. + + 23 The IJC would be a better person to ask the + + 24 question to. + + 25 SENATOR O'MARA: All right. + + + + + + + + 39 + 1 Now, on these so-called "trigger points," at + + 2 the high or the low, what action can you at the + + 3 board take, anticipating that trigger point being + + 4 exceeded? + + 5 STEPHEN DURRETT: Prior to the trigger point + + 6 being exceeded, we could ask permission to do a + + 7 deviation from the IJC. + + 8 Once the trigger point is achieved, we can + + 9 make deviations, and then we notify the IJC. + + 10 So, lower than the trigger, we have to ask + + 11 permission. + + 12 Higher than the trigger, we can -- actually, + + 13 we just do notification. + + 14 SENATOR O'MARA: It's my understanding that, + + 15 for the past several years, Lake Erie's water level + + 16 has been high. + + 17 ASSEMBLYMAN BARCLAY: That's correct. + + 18 SENATOR O'MARA: Is that your understanding? + + 19 STEPHEN DURRETT: Yes. + + 20 SENATOR O'MARA: And the flow of water from + + 21 Lake Erie to Lake Ontario is not controlled; + + 22 correct? + + 23 STEPHEN DURRETT: It is somewhat controlled, + + 24 yes. + + 25 SENATOR O'MARA: How is it controlled? + + + + + + + + 40 + 1 STEPHEN DURRETT: We control -- it's + + 2 minimally controlled, I'll say. + + 3 But there's -- the control is mostly over the + + 4 distribution of water between the United States and + + 5 Canada more than it is the flow over the falls + + 6 itself. + + 7 SENATOR O'MARA: And how high has Lake Erie + + 8 been over the past four or five years? + + 9 STEPHEN DURRETT: I don't know that number + + 10 off the top of my head. + + 11 SENATOR O'MARA: No? + + 12 It's my understanding that, in February of + + 13 this year, Lake Ontario levels rose as a result of + + 14 the elevated Lake Erie levels in February, and no + + 15 action was taken by the board at that point to deal + + 16 with that increase. + + 17 STEPHEN DURRETT: Lake Erie's flows have been + + 18 high for the last several years. + + 19 Yes. + + 20 ARUN HEER: I just want to say -- I just want + + 21 to mention, Bill Werick from the IJC is going to be + + 22 speaking next. He'll be able to answer your + + 23 questions concerning the plan formulation a lot + + 24 better than a board member here. + + 25 So I just wanted to prep you up for some of + + + + + + + + 41 + 1 these questions you might have. You're going to get + + 2 a better answer from the next speaker. + + 3 SENATOR O'MARA: Okay. + + 4 What's your name, and who are you from? + + 5 ARUN HEER: My name is Arun Heer. I'm the + + 6 U.S. secretary for the board. + + 7 SENATOR O'MARA: Thank you. + + 8 ARUN HEER: You're welcome. + + 9 Thank you. + + 10 STEPHEN DURRETT: So, I'm sorry. + + 11 Can you repeat your question, or, do you want + + 12 to save it for Bill? + + 13 SENATOR O'MARA: There was no anticipatory + + 14 actions taken by the board because of excess water + + 15 coming from Lake Erie because of its elevated state + + 16 in the month of February this year? + + 17 STEPHEN DURRETT: That is correct, because + + 18 there was no real need to. It did follow in a very + + 19 similar path to what it did in 2016. + + 20 In February -- the 2016 levels and 2017 were + + 21 about the same in February. + + 22 SENATOR O'MARA: How much are the riparian + + 23 landowners' rights considered in your determinations + + 24 of what to do with the outflows of Lake Ontario? + + 25 STEPHEN DURRETT: They're considered with + + + + + + + + 42 + 1 every other interest. + + 2 SENATOR O'MARA: Is it weighted at any + + 3 extent? + + 4 STEPHEN DURRETT: No. + + 5 SENATOR O'MARA: And what are those factors, + + 6 other than riparian rights? + + 7 STEPHEN DURRETT: There's environmental + + 8 interests. There's riparian interests. There's + + 9 navigation interests. And there's -- I'm missing + + 10 one. + + 11 [Member of the audience says "power."] + + 12 STEPHEN DURRETT: -- power. + + 13 Thank you. + + 14 SENATOR O'MARA: Power? + + 15 What are those environmental interests? + + 16 STEPHEN DURRETT: A lot of those have to do + + 17 with the sudden rise and lowering, of whether it be + + 18 upstream or downstream. + + 19 It's mainly about fish habitat. + + 20 When you get into a flood event, the + + 21 environmental interests are very minimal. + + 22 SENATOR RITCHIE: I just have one follow-up + + 23 question. + + 24 Earlier you stated that there was -- you + + 25 noticed -- or, the board noticed that there was a + + + + + + + + 43 + 1 high water level and there was nothing that could be + + 2 done. + + 3 Can you just clarify what you mean by + + 4 "nothing could be done"? + + 5 STEPHEN DURRETT: Well, balancing all the + + 6 interests associated with it, you try to discharge + + 7 water out of Lake Ontario, it's gonna go impact + + 8 someone else. + + 9 The water downstream was already at record + + 10 levels as well. + + 11 There was no place to put the water. + + 12 The water, do I send it downstream? do I hold + + 13 it where I'm at? + + 14 It's a balancing act, when you're in a flood + + 15 event, as to how you manage water and where you + + 16 store it. + + 17 Discharging, and increasing discharges, will + + 18 have a very minimal impact on the level of + + 19 Lake Ontario because the lake is so large. + + 20 Increasing by 1,000 cubic meters per second + + 21 is a very small amount of impact on Lake Ontario, + + 22 but it could be devastating to what's downstream + + 23 because of the St. Lawrence Channel is so narrow. + + 24 So, we already were in a flood event + + 25 downstream. Changing something by a millimeter on + + + + + + + + 44 + 1 Lake Ontario could be several inches in -- on the + + 2 St. Lawrence River downstream. + + 3 SENATOR RITCHIE: And the flood event + + 4 downstream, was that mostly caused by the + + 5 Ottawa River? + + 6 STEPHEN DURRETT: And other tributaries, yes. + + 7 But the Ottawa River had record flooding. + + 8 SENATOR RITCHIE: Okay. + + 9 Then I guess, one last question: Once again, + + 10 do you believe that, if you had the ability to + + 11 release water before the situation with the + + 12 Ottawa River was at that level, it would have helped + + 13 the situation, or you don't believe it would have + + 14 made any difference? + + 15 STEPHEN DURRETT: I don't believe it would + + 16 have made any difference because, before the + + 17 Ottawa River and the flooding event going on, + + 18 I wouldn't have been wanting to release water. + + 19 I would have stayed within the normal guides + + 20 that tells our historical data from 1900. We have + + 21 115, 118 years of data, and we were within the + + 22 normal range, up till March. And then, all of a + + 23 sudden, in April and May, it shot through the roof, + + 24 but it shot through the roof everywhere across the + + 25 entire basin; not just on Lake Ontario, but the + + + + + + + + 45 + 1 entire basin, including the St. Lawrence River + + 2 Basin. + + 3 There was no place for the water to go. + + 4 SENATOR RITCHIE: I know I have grave + + 5 concerns after Senator O'Mara brought up the + + 6 controls on Lake Erie, and the fact that there's + + 7 minimum controls. + + 8 But the people that I represent in the + + 9 middle, their property was affected because of the + + 10 controls that were used at Robert Moses to keep from + + 11 flooding Montreal any worse than it already was. + + 12 So, I guess that is a question, that I would + + 13 hope it would be addressed when you go back, is + + 14 that: If the water levels are controlled and the + + 15 outflow is controlled, at some portions along the + + 16 system, then maybe Lake Erie ought to be controlled + + 17 a little more than minimally. + + 18 STEPHEN DURRETT: Okay. + + 19 SENATOR RITCHIE: Thank you. + + 20 ASSEMBLYMAN BARCLAY: Just quickly following + + 21 up again, when did the board -- do you have a date + + 22 when the board realized that you might have to + + 23 deviate from the Plan 2014? + + 24 STEPHEN DURRETT: I don't have a date off the + + 25 top of my head, but I could give you a range, that + + + + + + + + 46 + 1 we probably were thinking -- + + 2 ASSEMBLYMAN BARCLAY: Even a month. + + 3 STEPHEN DURRETT: -- have to deviate in the + + 4 April time frame. + + 5 ASSEMBLYMAN BARCLAY: And then what was the + + 6 band that they were in when you understood that this + + 7 was a flood condition where have to deviate from the + + 8 plan? + + 9 STEPHEN DURRETT: We were still below the H14 + + 10 criteria. But, we saw the rise, and we were + + 11 starting to meet weekly, or every other week, and + + 12 starting to talk about water levels and predictions, + + 13 weather forecasts, and what we were gonna do, and + + 14 what could we do? + + 15 So we were meeting about every -- I'll say, + + 16 on average, every two weeks, starting in, probably, + + 17 the April time frame, all the way through August. + + 18 ASSEMBLYMAN BARCLAY: I can't -- the H -- + + 19 what was the band you called it? What was it -- + + 20 STEPHEN DURRETT: H14. + + 21 ASSEMBLYMAN BARCLAY: H14. + + 22 What is that in meters or inches? + + 23 I prefer inches, but I'll take meters. + + 24 STEPHEN DURRETT: In the April time frame, it + + 25 would be somewhere around, and it's kind of hard to + + + + + + + + 47 + 1 read off this chart, but about 246.8. + + 2 ASSEMBLYMAN BARCLAY: Okay. Thank you. + + 3 STEPHEN DURRETT: That would be feet. + + 4 ASSEMBLYMAN BARCLAY: Feet. Right. + + 5 I meant feet. Sorry. + + 6 SENATOR O'MARA: Thank you very much. + + 7 STEPHEN DURRETT: Thank you for your time. + + 8 SENATOR O'MARA: Next up, from the + + 9 International Joint Commission, Bill Werick, + + 10 technical advisor. + + 11 BILL WERICK: My name is Bill Werick. I'm + + 12 here representing the International Joint + + 13 Commission. + + 14 They asked that I attend because of a broad + + 15 technical understanding of the regulation of + + 16 Lake Ontario levels, and water resources more + + 17 globally. + + 18 I was the lead U.S. planner during the + + 19 Lake Ontario-St. Lawrence River study in the 2000 to + + 20 2006 range. And I'm now a member of the IJC's + + 21 Great Lakes Adaptive Management Committee. + + 22 [Slide show begins.] + + 23 BILL WERICK: In my testimony today I will + + 24 make two points. + + 25 The first, is that IJC's regulation of + + + + + + + + 48 + 1 Lake Ontario levels substantially reduces flood + + 2 levels for people along the shore, but it does not + + 3 prevent them. These high levels are bound to happen + + 4 again. + + 5 The second point, is that any significant + + 6 reduction in high-water damages will require a + + 7 difficult, perhaps unprecedented level, of + + 8 collaboration and innovation, with roles for the + + 9 IJC, the state, local, and federal governments, and + + 10 landowners. + + 11 There is a history to this, and it is really + + 12 informative. + + 13 The IJC held its first meetings on this issue + + 14 back in 1920. + + 15 I'm from Buffalo, and I remember the + + 16 opposition to the seaway. + + 17 There were attempts for 30 years to get the + + 18 seaway built, and they were rebuffed. + + 19 On June 30, 1952, the two countries applied + + 20 to the IJC for approval of a power project, + + 21 including a hydropower dam. + + 22 Now, we think of dams as raising water + + 23 levels, but the regulation plan included in the + + 24 application was designed so that the project would + + 25 not raise the natural levels of Lake Ontario. + + + + + + + + 49 + 1 1952, coincidentally, the lake reached record + + 2 high levels in 1952. + + 3 These are pictures from the Rochester + + 4 Democrat & Chronicle, on Edgemere Drive near Dewey. + + 5 And the governments asked the IJC to study + + 6 whether, having regard for all other interests, + + 7 measures could be taken to regulate the level of + + 8 Lake Ontario for the benefit of property owners on + + 9 the shores of Lake Ontario. + + 10 So this is the difference. + + 11 In other words, could the project not just + + 12 protect against higher-than-natural water levels, + + 13 but actually lower them? + + 14 So the IJC held hearings on this in '52, '53, + + 15 and '55. + + 16 Higher lake levels are generally better for + + 17 producing power, and for providing water during + + 18 droughts for navigation on the river. + + 19 Hydropower interests argued that reducing + + 20 high levels would increase construction costs and + + 21 reduce power production. + + 22 And, of course, this was the 1950s, so there + + 23 was no consideration of the environmental impacts of + + 24 damming the St. Lawrence River. + + 25 Despite the costs of lost hydropower, the IJC + + + + + + + + 50 + 1 issued an order in 1956, with the criteria that + + 2 provided flood relief for Lake Ontario coastal + + 3 property owners. + + 4 The order prescribed a range of mean monthly + + 5 elevations for Lake Ontario, from 243.3, to + + 6 247.3 feet, as nearly as may be. + + 7 So what does that "as nearly may be" part? + + 8 It reflects the fact that this regulation + + 9 plan was designed by the engineers of the day, with + + 10 water levels from -- water supplies from 1860 to + + 11 1954. + + 12 So water supplies, since regulation began, + + 13 have included much wetter periods -- + + 14 You see the supplies on the right there in + + 15 red. + + 16 -- than were done historically. + + 17 And the IJC said, if you run into this, if + + 18 you have these higher supplies, you have to deviate + + 19 to protect riparian owners above and below the dam. + + 20 Looking at the graph of Lake Ontario levels, + + 21 it's pretty obvious that, in most years, the dam and + + 22 regulation did compress Lake Ontario levels, but you + + 23 can see there's still levels with very high levels. + + 24 So, in 1972, the Rochester Democrat & + + 25 Chronicle reported that Lake Ontario was the only + + + + + + + + 51 + 1 lake spared flooding on the Great Lakes because of + + 2 that new dam. + + 3 But a year later, the paper reported that + + 4 Lake Ontario had risen to damaging heights, not as + + 5 bad as it would have been without the dam -- with + + 6 the -- without the dam, but still damaging. + + 7 I know many of you remember that. + + 8 Another story from 1973 quoted Rochester + + 9 realtors questioning how homes along the shore could + + 10 be bought or sold. + + 11 There had been $3 million damage in '52. + + 12 The corps was saying, in 1973, the damages in + + 13 Monroe County alone would be 7 to 10 million. + + 14 That same year, the paper investigated + + 15 charges that Lake Ontario was being kept needlessly + + 16 high. + + 17 They concluded property owners would have + + 18 been worse off without the dam. + + 19 They reported, the IJC proposal, that year, + + 20 in 1973, the future lakeshore development should be + + 21 subject to strict zoning and setback regulations. + + 22 In 1993, just as the IJC was commissioning + + 23 another big study for all the Great Lakes, + + 24 Lake Ontario flooded again. + + 25 In its 1993 report, the IJC again advised + + + + + + + + 52 + 1 that the project was not capable of full control of + + 2 the water levels. + + 3 Commissioners recommended land-use and + + 4 management measures as the principal response to the + + 5 adverse consequences of fluctuating water levels. + + 6 Allowing the structural shore protection + + 7 might be the only alternative for + + 8 intensely-developed shorelines. + + 9 The study reported that most private + + 10 protection structures failed after 10 years, and + + 11 encouraged the use of better structures, with + + 12 provisions for inspection and approval of those + + 13 plans. + + 14 During the LOSLR study in the early 2000s, + + 15 we considered -- we had much better analytic tools, + + 16 and we considered a much broader range of water + + 17 supplies than had occurred historically. + + 18 We formed four teams that competed to produce + + 19 the best regulation plan. + + 20 We did statistical studies that showed the + + 21 lake could go much higher than it even had in + + 22 history. + + 23 Now, the board concluded that changes to the + + 24 criteria in the existing plan were not possible + + 25 without harming some interests. + + + + + + + + 53 + 1 The Plan '58D, with deviations, came close to + + 2 minimizing damages from Lake Ontario for the + + 3 property owners. + + 4 Because it reduced the range of rain -- the + + 5 range and the variability of Lake Ontario levels, + + 6 though, it also reduced the diversity of plant types + + 7 along the shore and populations of animal species + + 8 who feed on and live in the environments affected by + + 9 the reduced water-level ranges. + + 10 The board said that '58DD had caused + + 11 dewatering drawdowns in the fall, through the early + + 12 spring, to the detriment of some habitat and + + 13 species. + + 14 It took another 10 years of debate before + + 15 Plan 2014 was ratified by the governments of Canada + + 16 and the United States. + + 17 And in 2017, the first year of its + + 18 implementation, unprecedented weather condition + + 19 caused Lake Ontario to rise even higher than it had + + 20 in 1952. + + 21 As this graph shows, IJC -- you can see on + + 22 the top there, IJC regulations still lowered levels + + 23 below what would be occurring naturally. + + 24 And you can see on the bottom, as Mr. Durrett + + 25 had talked about, it's because the dam allows much + + + + + + + + 54 + 1 bigger releases than would have occurred naturally. + + 2 So, what should we do to prepare for future + + 3 high-water conditions? + + 4 My remarks reflect finding some other + + 5 studies, as well as those done by the IJC, and my + + 6 own experience in flood-damage reduction here, and + + 7 all around the world. + + 8 I'm a member of the Great Lakes Adaptive + + 9 Management Committee, and we will review how + + 10 Plan 2014 performed this year, but we already know + + 11 that the regulation plan can only do so much. + + 12 So the question then is: What else can we do + + 13 if we want real changes? + + 14 First, even though this is a very emotional + + 15 issue, it's really important that people stay + + 16 committed. + + 17 We know from experience, it's ironic, but + + 18 it's time of crises that makes people do long-term + + 19 strategic planning. + + 20 We're all here together, this issue is on the + + 21 table, but, in my experience, that window will + + 22 close. + + 23 In a few months, this will become an issue + + 24 that has -- people remember, but they haven't acted + + 25 on. + + + + + + + + 55 + 1 So it's really important that we stay engaged + + 2 and that we act soon. + + 3 Second, it's really important that we + + 4 recognize how difficult this will be. + + 5 So I see this happen all over the world, and + + 6 the sad news is, is that, as you saw in this history + + 7 here, that floods happen, and people get upset, and + + 8 they look at what could be done, and the interest + + 9 dies, and floods happen, and the cycle repeats + + 10 itself. + + 11 Part of that is because there are no easy + + 12 answers. + + 13 The kind of solutions that we might imagine + + 14 are not solutions that everybody says, oh, that + + 15 would be great, everybody is in favor of that. + + 16 Consider shore-protection damages, for + + 17 example. + + 18 As the commission had said, no structure will + + 19 last forever, but structures that are designed with + + 20 heavier stone and are -- which are taller and + + 21 designed for a 6 1/2-foot range will be more robust. + + 22 But they're more expensive, quite a bit more + + 23 expensive. + + 24 People often talk about floodplain management + + 25 as a solution. + + + + + + + + 56 + 1 I'm not really sure, we haven't gotten all + + 2 the data yet, but I think that floodplain management + + 3 actually has helped on Lake Ontario. + + 4 When I look at the new homes that have been + + 5 built, I think they are up higher, and that has + + 6 reduced flood damages. + + 7 Of course, there are a lot of older homes, + + 8 there's homes with basements. + + 9 And even when your home isn't flooded, + + 10 outbuildings may be flooded. You may have water on + + 11 the street that makes it very difficult to live your + + 12 life. + + 13 And, finally, there's the erosion to property + + 14 which has no shore protection, and this can be + + 15 really difficult. + + 16 This is going to happen no matter the + + 17 regulation plan, although, the regulation plan can + + 18 change the rate at which it happens. + + 19 Now, some people who have no shore protection + + 20 can build it, but it's very expensive, and there's + + 21 environmental concerns about shore protection too. + + 22 People are worried about hardening the shore. + + 23 I've talked to landowners who have said, + + 24 well, you know, when I lose the bank, I just drag my + + 25 cottage back closer to the road. + + + + + + + + 57 + 1 But, of course, there are limits to that. + + 2 It's not a satisfying solution at all. + + 3 And then the big question: What role should + + 4 government play in helping private-property owners? + + 5 And this is an issue that's been debated, not + + 6 just here, but everywhere in the United States and + + 7 in every country around the world. + + 8 And the answer is different in different + + 9 places, at different times. And there's a trade-off + + 10 between providing relief after a disaster and + + 11 providing protection before the disaster. + + 12 The IJC is involved in some efforts that may + + 13 help. + + 14 We're doing a study of flooding in the + + 15 Lake Champlain Basin. This just started. + + 16 The interesting thing there, is there is no + + 17 dam, so we're not talking about a regulation plan. + + 18 We're only talking about land-management + + 19 measures and flood-response plans; so, plans that + + 20 better forecast floods, and help towns put out + + 21 sandbags or other flood-control measures. + + 22 And, we recognized the interest in this is + + 23 bigger than Lake Champlain, and we want to make sure + + 24 that we share those results with you. + + 25 The IJC is also working with NOAA and Cornell + + + + + + + + 58 + 1 on flood forecasting and high-water response plans. + + 2 In sum, my two points: + + 3 Water-level regulation has helped, but it can + + 4 only do so much; + + 5 And the only hope for doing better in the + + 6 future, this is very difficult, it almost never + + 7 works, but there is hope if we work together. + + 8 Thanks so much for organizing this meeting. + + 9 This is the first time we've really had a + + 10 hearing that have brought -- that's brought all the + + 11 players together. + + 12 This is the most important thing, that we + + 13 start talking, and I really appreciate you doing + + 14 this. + + 15 And, I may be able to answer some questions + + 16 on Plan 2014. + + 17 SENATOR O'MARA: Okay. + + 18 Well, we really appreciate you being here as + + 19 well, and thank you for your testimony. + + 20 Would you like to start? + + 21 ASSEMBLYMAN BARCLAY: Sure. + + 22 Could you explain -- I just -- I don't know + + 23 what the Great Lakes - St. Lawrence River Adaptive + + 24 Management Committee is. + + 25 What is that? + + + + + + + + 59 + 1 BILL WERICK: That is -- that's a -- it's an + + 2 idea that's been talked about for a long time, but + + 3 hardly anybody in the world does it. + + 4 And the idea is, is that after you make a + + 5 decision, you continue to monitor the evidence to + + 6 make sure that your decision was right. + + 7 So, in other words, you think, if I do this + + 8 Plan 2014, we'll strike this kind of a balance + + 9 between hydropower and navigation and + + 10 shore-protection damages and the environment. + + 11 And, typically speaking, when those studies + + 12 are done, nobody then monitors every one of those + + 13 things and says, hey, we were right about that, but + + 14 wrong about that, and now let's think about + + 15 adjusting. + + 16 So in this case, the IJC has actually started + + 17 monitoring programs, and our intent is to continue + + 18 to watch, and in 15 years, or less, we will report + + 19 on that and say, we could do better or we could do + + 20 worse. + + 21 ASSEMBLYMAN BARCLAY: So it's a committee + + 22 that's put together by the IJC to give them advice + + 23 on all the state -- you know, what's happening with + + 24 all the different stakeholders -- + + 25 BILL WERICK: Exactly. + + + + + + + + 60 + 1 ASSEMBLYMAN BARCLAY: -- along (inaudible) -- + + 2 BILL WERICK: The actual results of the plan, + + 3 and what could be done better. + + 4 ASSEMBLYMAN BARCLAY: Okay. + + 5 Were you involved in the Plan 2014, the + + 6 implementation of Plan 2014? + + 7 BILL WERICK: I was involved as a planner, so + + 8 I would run evaluation models, and -- yes. + + 9 ASSEMBLYMAN BARCLAY: Could you just go + + 10 through that process? + + 11 Because I think a lot of people are + + 12 discouraged, the way Plan 2014 came in, and I think + + 13 people are suspicious, because it was implemented + + 14 in, what, December of -- what was it, last year; + + 15 right? + + 16 BILL WERICK: Exactly. + + 17 ASSEMBLYMAN BARCLAY: And, you know, I've + + 18 heard a lot of complaints, that there wasn't + + 19 public -- appropriate public input on 2014. + + 20 You know, you went through a lot of different + + 21 iterations with different plans. Some faded off. + + 22 And then, all of a sudden, this one came out in, + + 23 seemed like, the dead of night. + + 24 BILL WERICK: Sure. + + 25 ASSEMBLYMAN BARCLAY: So, maybe, if you could + + + + + + + + 61 + 1 explain the process that it went through for us. + + 2 BILL WERICK: So this is a process that + + 3 probably, you could say, it started in the + + 4 late '80s, with the Great Lakes Levels Reference + + 5 Study. + + 6 The first -- during the study that + + 7 I participated in, and I know many of you did too, + + 8 from 2000 to 2006, we had teams competing to produce + + 9 better plans. + + 10 And that study board made recommendations to + + 11 the IJC for three different plans, and each one had + + 12 a different nature, they stressed different balance + + 13 of results. + + 14 And there was a plan called "B+," which was + + 15 more natural levels, and that's the predecessor to + + 16 Plan 2014. + + 17 The next several years, people -- the IJC had + + 18 hearings on a plan that they called "Plan 2007." + + 19 It was rejected by almost everybody. + + 20 They came back with other studies. + + 21 That B+ plan became Bv7, and there were many + + 22 public hearings on that, with the people opposed and + + 23 people supporting it, just as 2014 was. + + 24 And they held a series of public hearings all + + 25 along the south shore, before they wrote their + + + + + + + + 62 + 1 report to governments recommending Plan 2014. + + 2 So, full public involvement up to that point. + + 3 Now, once the IJC sends it to the + + 4 governments, then the state department and + + 5 department of foreign affairs each do consultations + + 6 within their countries. + + 7 And at that point, I don't -- I'm an outsider + + 8 to that process. + + 9 So, basically, at that point, each country is + + 10 looking at it in their own interests. And then they + + 11 have negotiations, and they come back together. + + 12 And, for me, that period of time was more + + 13 opaque. + + 14 But they, basically, accepted the + + 15 recommendations of the IJC, with some minor edits. + + 16 ASSEMBLYMAN BARCLAY: Is the board fully -- + + 17 all the appointees still on the IJC? + + 18 Has Trump made his appointees? + + 19 BILL WERICK: There are no new appointees by + + 20 either the Prime Minister or the President. + + 21 ASSEMBLYMAN BARCLAY: No new. + + 22 Are there vacancies? + + 23 BILL WERICK: They're all people that were on + + 24 the commission from years ago. + + 25 ASSEMBLYMAN BARCLAY: There are no vacancies + + + + + + + + 63 + 1 currently? + + 2 BILL WERICK: There's one vacancy on the U.S. + + 3 side. + + 4 Is there a vacancy on the Canadian side? + + 5 No. + + 6 ASSEMBLYMAN BARCLAY: And then the Adaptive + + 7 Management Committee, how many members are on that + + 8 committee? + + 9 BILL WERICK: I think there are -- I would + + 10 say ten. + + 11 I can get you the list of people. + + 12 ASSEMBLYMAN BARCLAY: Anyone from the + + 13 southern or eastern end of Lake Ontario? + + 14 BILL WERICK: I'd have to look. + + 15 Nobody comes to mind. + + 16 These are mostly agency people, so the Corps + + 17 of Engineers has representatives. + + 18 ASSEMBLYMAN BARCLAY: I mean, I think -- + + 19 again, my concern is, and I think a concern of a lot + + 20 of my constituents, is our voice isn't being heard + + 21 by the IJC. + + 22 I think we feel like, with the implementation + + 23 of the Plan 2014, that the southern and eastern end + + 24 of Lake Ontario are the losers in that process. + + 25 So it would be nice to know at least there's + + + + + + + + 64 + 1 a voice there, and someone hearing the concerns of, + + 2 particularly, the property owners along the lake. + + 3 BILL WERICK: Well, let's solve that problem, + + 4 because the committee certainly knows that it has to + + 5 have that connection. + + 6 And we're in the process now of trying to + + 7 establish those connections. + + 8 And what we're looking for, really, is + + 9 somebody from the community who speaks for the + + 10 community. + + 11 They can be skeptical. They can be against + + 12 Plan 2014. As long as they're willing to work with + + 13 us, and express the views of the community, we see + + 14 that as an essential thing. + + 15 So, that's something we'd like to fix. + + 16 ASSEMBLYMAN BARCLAY: All right. Let's do + + 17 that. + + 18 Thank you. + + 19 SENATOR O'MARA: Patty? + + 20 SENATOR RITCHIE: And can I just ask for you + + 21 to follow up on a previous speaker? + + 22 As far as the deviation authority, do you + + 23 believe that has changed with Plan 2014? + + 24 BILL WERICK: There are changes, and some of + + 25 them are kind of technical. But when you look at + + + + + + + + 65 + 1 the actual instruction to the board, it's almost + + 2 identical. + + 3 But the condition -- in other words, the old + + 4 Criterion K under the old plan said, when supplies + + 5 are greater than the supplies of the past, do + + 6 everything you can for riparians above and below the + + 7 dam. + + 8 The question was: When are supplies greater + + 9 than the past? + + 10 It was a very fuzzy notion. + + 11 Now we have the trigger levels, so that the + + 12 triggering mechanism is very precise and clear, and + + 13 the instruction is about the same. + + 14 It's when the water -- when the -- you exceed + + 15 the trigger levels, do everything possible for + + 16 riparians above and below the dam. + + 17 SENATOR RITCHIE: Do you believe the trigger + + 18 level is set too high, given what's happened this + + 19 past year? + + 20 BILL WERICK: No, and I'll say that for two + + 21 reasons. + + 22 The first is, is that, when you look back at + + 23 this with the benefit of hindsight, you say, boy, if + + 24 we had started releasing 10,000 cubic meters per + + 25 second way back when, this would have been better + + + + + + + + 66 + 1 off. + + 2 But the fact is, is that, as Mr. Durrett + + 3 said, our forecasts for one month out are really not + + 4 very skillful. + + 5 You can look back at the forecasts that + + 6 people made at that time, and they missed this + + 7 completely. + + 8 So, for instance, 2016 looked very much like + + 9 2017. And had you done that, you would have made + + 10 the lower levels of 2016. + + 11 Lake Ontario ended up below average, much + + 12 lower. + + 13 So you have to kind of play the odds. + + 14 The second thing is, is that Plan 2014 builds + + 15 in a lot of the decision-making that the board used + + 16 to do anyway, so that, as the water levels rose + + 17 higher, and as Mr. Durrett talked, they were + + 18 operating under this F limit. + + 19 They were releasing as much water as they + + 20 could, balancing the flooding that was occurring + + 21 downstream. + + 22 And, in fact, the best test of that is that, + + 23 when they finally got to the end of April and hit + + 24 the trigger, they continued to use that same F-limit + + 25 strategy for some time. + + + + + + + + 67 + 1 So you could argue that, from a mechanical + + 2 viewpoint, once they hit the trigger, they were + + 3 already doing everything they could to lower + + 4 Lake Ontario level. + + 5 SENATOR RITCHIE: And the previous speaker + + 6 spoke about meeting every two weeks, which that + + 7 probably does not sound great to people who were + + 8 dealing with the water levels on an -- any given + + 9 hour, how much it was fluctuating. + + 10 So is there -- will that be taken into + + 11 consideration, that maybe there should be more, + + 12 either on-the-ground people seeing what's actually + + 13 happening here, or meeting more regularly, so things + + 14 can be changed at a quicker pace? + + 15 BILL WERICK: I'll let -- Mr. Durrett, + + 16 correct me if I'm wrong -- but I know, from + + 17 monitoring the situation, that there is a -- what + + 18 they call "the coordinating committee" that advises + + 19 the board. These are technical guys. + + 20 And these guys are looking at electronic + + 21 readouts of all of these measurements, and they are + + 22 constantly talking to one another. + + 23 So this is not something that they check in + + 24 every two weeks. This is something that they watch + + 25 all day long. + + + + + + + + 68 + 1 SENATOR RITCHIE: And I know there was great + + 2 concern by the people that I represent, that + + 3 Montreal's interests were being represented at maybe + + 4 a higher level than our own, given the fact that the + + 5 Ottawa River was a pretty significant contributing + + 6 factor. + + 7 What will be done in working with the + + 8 Canadians to try to address the outflow from the + + 9 Ottawa River, if that's what significantly decreased + + 10 the ability of the Robert Moses Dam to release + + 11 water? + + 12 BILL WERICK: You know, we are in the process + + 13 now of outlining our report on this year. + + 14 And I would say, one of the things that we + + 15 can do with you, and with the representatives we + + 16 work with, is try to articulate those questions that + + 17 are on everybody's mind. + + 18 My gut feeling now, is that there isn't much + + 19 that could have been done about the Ottawa River + + 20 discharge. + + 21 But let's make that, and other questions, + + 22 part of that report, and we'll try to have a really + + 23 good answer for that. + + 24 Because, this is the hard part, it's really + + 25 important that we establish a factual basis for any + + + + + + + + 69 + 1 modifications we make in the future, whether it's to + + 2 the regulation plan or to other decisions that are + + 3 made. + + 4 So, we'll work with you to develop a list of + + 5 questions that that report should answer. + + 6 SENATOR RITCHIE: Because I personally am + + 7 concerned that the people that I represent are + + 8 getting the squeeze on both ends. + + 9 That, now it comes out that Lake Erie is + + 10 minimally controlled, and what was possibly been + + 11 able to happen to help in the middle, couldn't + + 12 happen because of the situation in Montreal. + + 13 So, if this is going to be a solution to the + + 14 problem as a whole, how do we make sure that the + + 15 Lake Erie part of the conversation and the + + 16 Ottawa River part of the conversation is held + + 17 accountable to the same level? + + 18 BILL WERICK: You know, we can -- we can + + 19 answer those questions. + + 20 I grew up on Lake Erie, and I can say with + + 21 even more confidence, that Lake Erie will not be + + 22 regulated. + + 23 I mean, they have looked at that in the past, + + 24 and the downsides to it are so great, that it almost + + 25 isn't even worth asking anymore. + + + + + + + + 70 + 1 So I -- that -- I can answer you now, that + + 2 Lake Erie is not going to be regulated, unless the + + 3 world changes a lot. + + 4 SENATOR RITCHIE: And if there are + + 5 recommendations put forward, how does the plan + + 6 actually get changed by, whose authority? + + 7 BILL WERICK: Yeah, that's a good question. + + 8 I mean, if you look at the names of these + + 9 plans, 1958, 2014, you realize these plans don't + + 10 change very often, and it's because they are so + + 11 important to so many people, and they require a + + 12 buy-off -- an international buy-off between the two + + 13 countries. + + 14 That said, because of the Great Lakes + + 15 Adaptive Management Committee, we want to be open to + + 16 great ideas, and there is a mechanism for us to + + 17 report back to the IJC. + + 18 I don't see us making any enormous + + 19 breakthroughs. But, if we can find ways to tweak + + 20 the plan to make it better, then we have complete + + 21 freedom to recommend those things to the IJC. + + 22 SENATOR RITCHIE: And my last question: Do + + 23 you believe the people who are dealing with the + + 24 damage this year, are they going to be dealing with + + 25 the same situation next year, or the year after? + + + + + + + + 71 + 1 BILL WERICK: Boy, I have no idea, and I'm + + 2 saying that not casually, because I've tried very + + 3 hard to make those projections. + + 4 If you look at 2015, 2016, and 2017, and you + + 5 look at Lake Ontario and Lake Erie levels, they're + + 6 all pretty similar, and yet each one of those years + + 7 turned out so differently. + + 8 It's -- I will say that, over the next + + 9 20 years -- as you saw, I showed you pictures of + + 10 floods in '43, '47, '52, '73, and '93. + + 11 Over the next 20 years this will probably + + 12 happen again. + + 13 Climate change, the very warm winters, this + + 14 may make it more likely. + + 15 But, next year, I have no idea. + + 16 SENATOR RITCHIE: Thank you. + + 17 ASSEMBLYMAN OAKS: Your giving the historical + + 18 perspective was helpful, I think, in seeing how + + 19 things have developed over those years, and your + + 20 contention that, without the dam, without the + + 21 seaway, this year probably would have been worse + + 22 than if there was nothing there. + + 23 I think I can, you know, understand. + + 24 However, under the new terms, and looking + + 25 forward, we're saying we're going to manage it less. + + + + + + + + 72 + 1 And so we have the opportunity -- we + + 2 controlled, and we tried to stay here, and + + 3 conditions have taken us outside of it in different + + 4 years. But now we're saying we're opening it up. + + 5 And so I guess one of my questions, in + + 6 looking at it: + + 7 '43, '47, '52, that's good history. + + 8 Do we have any sense -- and I didn't see -- + + 9 you know, we have the chart showing this. + + 10 Do we have any sense of days of flooding that + + 11 occurred those years, as well as '73 and '92, + + 12 compared to 2017, which that flooding sustained? + + 13 And, does the plan -- I guess as a follow-up, + + 14 does the plan encourage more days that we then have + + 15 to deal with higher waters and potential for + + 16 flooding? + + 17 BILL WERICK: Okay. + + 18 So, this is a good question to ask, and + + 19 answer more formally in the report, but let me make + + 20 some general comments off the top of my head. + + 21 The first is the obvious, that it's -- + + 22 I understand that it's cold comfort to say it would + + 23 have been worse with the dam, because this is the + + 24 worst flood that's ever happened in -- since the + + 25 Civil War on Lake Ontario. + + + + + + + + 73 + 1 And, you know, I've worked on flooding since + + 2 1969. And I know that if you go through a flood, + + 3 people just don't understand how terrible that is + + 4 until you've gone through it. + + 5 So I know it offers cold comfort. + + 6 The next thing I would say is that, + + 7 Plan 2014, as the IJC has reported, does produce + + 8 higher highs and lower lows. + + 9 That generally happens in the middle highs, + + 10 in the middle lows. That's where you'll see a + + 11 difference. + + 12 And there are impacts to things along the + + 13 shore that are in that elevation range. + + 14 So Mayor Turtenac (ph.) is in the audience + + 15 tonight, and we've talked about parts of Sodus that + + 16 are vulnerable in that medium-high range. + + 17 Shore-protection structures that are not + + 18 built as high are going to be more susceptible to + + 19 overtopping, and that's why the damage figures for + + 20 Plan 2014 are a little higher. + + 21 Now, as the floods get worse, the new plan + + 22 gets to be more like the old plan, for two reasons: + + 23 One is, is that it doesn't do us much good + + 24 for the environment to have floods, so there's no + + 25 impetus in the plan rules to say, oh, let's have a + + + + + + + + 74 + 1 flood, it will be good for the environment. + + 2 The plan does try to control flooding. + + 3 And -- so, especially, once you get to the + + 4 deviations, there's really no difference between the + + 5 plans, and this is where you get the tennis-ball + + 6 comparison. + + 7 In real life, most of the time, with moderate + + 8 floods, Plan 2014 will have slightly higher water + + 9 levels, and that's because it's keeping the levels + + 10 higher in the fall and the spring for the benefit of + + 11 wetlands and everything that lives on there. + + 12 As the water levels get higher, the two plans + + 13 will be more alike. + + 14 Every once in a while Plan 2014 is better for + + 15 shoreline residents than the old plan because it + + 16 also does lower lows. + + 17 So if you have a year where Plan 2014 draws + + 18 the lake down, and have a flood the next year, and + + 19 this is not the majority, maybe one in five, then + + 20 the new plan is actually better. + + 21 ASSEMBLYMAN OAKS: Your -- the plan does + + 22 call, my recollection is, that over a 20-year + + 23 period, when we've typically seen a flooding event + + 24 in a 20-year period that we might see because of + + 25 higher highs, that happened, maybe, three times + + + + + + + + 75 + 1 during that 20-year period, is that the estimate of + + 2 the plan, and similar on lower water as well? + + 3 Or is -- + + 4 BILL WERICK: The -- if you look at the + + 5 frequency of stages, Plan 2014 tends to have more + + 6 frequent middle-high levels, and then they start to + + 7 go back together on the higher levels. + + 8 And as far as the sequence, there are people + + 9 that argue that there are some sorts of cycles at + + 10 work here. + + 11 If you go out to North Dakota, you can see it + + 12 on a lake called Devil's Lake, that it almost + + 13 disappeared in the '40s, and then it came back, + + 14 and had enormous floods in the '90s and 2000s. + + 15 I think people who live along the lake would + + 16 remember this, the '60s as a low-water period. We + + 17 had lots of years where Lake Ontario was low. It + + 18 seemed to be a persistent multi-year thing. + + 19 And the '70s, '80s, and '90s were + + 20 considered wet. + + 21 And then the 2000s, and going into 2015, was + + 22 considered a low-water condition. + + 23 So we had, not on Lake Ontario which is + + 24 regulated, but up on Lake Superior, and + + 25 Lake Michigan and Huron, people were very upset + + + + + + + + 76 + 1 because the lake was so low. + + 2 ASSEMBLYMAN OAKS: On the business interests, + + 3 I guess just in this thinking, I mean, this was + + 4 because of the days of flooding and the damage done. + + 5 BILL WERICK: Yes. + + 6 ASSEMBLYMAN OAKS: For many, this was a lost + + 7 season of recreational boating, of marinas, of + + 8 businesses that serve people, et cetera. + + 9 Is that -- again, the concern would be, a lot + + 10 of our economy is driven by those. + + 11 With this plan, are we setting those + + 12 businesses up to have other lost summers, or is + + 13 there a way to minimize the days even in those + + 14 high-water times -- minimize the lost days, you + + 15 know, and trying to narrow those events? + + 16 BILL WERICK: So with the very high events, + + 17 I would say there won't be much difference. + + 18 I think this year was a particular year where + + 19 we said, we really can't see any difference between + + 20 2014 and the old plan, and in part, that is because + + 21 the fall levels were set by the old plan. + + 22 As you get into moderate flood years, there + + 23 will be marinas that start to suffer business losses + + 24 before other marinas do, and Plan 2014 will tend to + + 25 be a little worse for them. + + + + + + + + 77 + 1 SENATOR O'MARA: Again, thank you. + + 2 One thing that I'm having trouble + + 3 comprehending here, is where you say that a dam + + 4 along the St. Lawrence River actually helps lower + + 5 Lake Ontario levels. + + 6 BILL WERICK: Yeah. + + 7 SENATOR O'MARA: Can you expound on that a + + 8 little bit? + + 9 BILL WERICK: Sure. + + 10 I mean, it's -- you think of a dam as, like a + + 11 beaver, builds a dam and the level goes up. + + 12 So the difference is -- and I just worked on + + 13 the study on Rainy Lake in northern Minnesota, and + + 14 the dam cannot bring the lake down. + + 15 So if you look at an undammed situation, the + + 16 water that flows out of a lake will be a function of + + 17 how high the lake is. + + 18 You can't have a big discharge unless the + + 19 lake is really high. + + 20 But what was different about the St. Lawrence + + 21 project, and this is what the hydropower companies + + 22 complained about, is that they did excavations. + + 23 So now the board has the ability to create + + 24 much greater discharges than they would have without + + 25 the dam, because of that excavation, and because of + + + + + + + + 78 + 1 that they can actually lower water levels. + + 2 SENATOR O'MARA: Can you talk a little bit + + 3 about, you know, we're here as New York State + + 4 Legislators, representing the south shore and the + + 5 eastern shore and the river. + + 6 We haven't heard much, at least I haven't, + + 7 about damage around the rest of the lake, the north + + 8 shore, the west shore. + + 9 What were the impacts of those shorelines + + 10 throughout this year? + + 11 BILL WERICK: The Great Lakes Adaptive + + 12 Management Committee is trying to collect damages + + 13 for the north shore, south shore. + + 14 We're working with the New York Sea Grant and + + 15 Cornell to do that. + + 16 And then we're also trying to get the damages + + 17 downstream, and separate the St. Lawrence-related + + 18 damages from all the other flooding that Quebec had. + + 19 In general, I think that the north shore had + + 20 less damage. + + 21 SENATOR O'MARA: Was that expected in the + + 22 Plan 2014 considerations? + + 23 Because I kind of recall that being an + + 24 expectation. + + 25 BILL WERICK: I would expect it, yes. + + + + + + + + 79 + 1 I think, and this is something we were trying + + 2 to document, that Canada has taken more aggressive + + 3 measures to reduce its vulnerability to flooding. + + 4 Also, northwest and northeast winds are + + 5 really harmful to the south shore, whereas they + + 6 aren't to the north shore. + + 7 SENATOR O'MARA: You mentioned in your + + 8 presentation that Plan 1958DD came close to minimal + + 9 damage from the regulation. + + 10 BILL WERICK: Yes. + + 11 SENATOR O'MARA: So, 1958DD was particularly + + 12 designed to minimize shoreline damage? + + 13 BILL WERICK: Almost. + + 14 I mean, it does -- '58D also balanced all the + + 15 different interests. But, during that study, we -- + + 16 we -- basically, we created these things called + + 17 "fence-post plans." + + 18 What if we disregarded everybody else, except + + 19 hydropower, how much good could we do for + + 20 hydropower? + + 21 What if we disregarded everybody else, except + + 22 riparians, how much good we could do? + + 23 And you could do a little bit better than + + 24 '58DD if you didn't care about anybody else in the + + 25 system, which, of course, I have to say is forbidden + + + + + + + + 80 + 1 by the treaty. + + 2 But we said '58DD is pretty close to as much + + 3 you can do with that dam and channel. + + 4 SENATOR O'MARA: So changing it to Plan 2014 + + 5 by -- basically, raising it by about 2-1/2 inches? + + 6 BILL WERICK: The highest level by about + + 7 2-1/2 inches, yeah. + + 8 SENATOR O'MARA: The highest levels, yeah, + + 9 and the lowest levels. + + 10 It was known that there would be an increase + + 11 to property damage -- + + 12 BILL WERICK: Yes. + + 13 SENATOR O'MARA: -- as a result of that, when + + 14 you exceeded those highs? + + 15 BILL WERICK: We -- we -- our models expected + + 16 that. + + 17 Now, we're going back to check that too. + + 18 SENATOR O'MARA: Yeah. + + 19 Yet, in Plan 2014, in the IJC, and, frankly, + + 20 the countries of Canada and the United States, did + + 21 not provide any remuneration for the shoreline + + 22 property owners for that expected damage? + + 23 BILL WERICK: That's correct. + + 24 SENATOR O'MARA: Do you know why that was? + + 25 BILL WERICK: I would say -- I would say + + + + + + + + 81 + 1 that, as I showed in the historical slides, the IJC + + 2 had to approve the dam. + + 3 They were required by the treaty to protect + + 4 riparian interests. + + 5 So they had to assure that the water levels, + + 6 because of that dam, would be no higher than they + + 7 would be without the dam. + + 8 That's when the treaty would have required + + 9 mitigation. + + 10 In either case, Plan 2014 or '58DD, water + + 11 levels are substantially below natural levels, so + + 12 that's why it wouldn't be required. + + 13 Now, my argument is, set aside the treaty + + 14 requirements, look at the problem, that there are + + 15 damages, and ask with an open mind and heart, what + + 16 could be done? + + 17 Understanding, that there are many people who + + 18 believe that government shouldn't help private + + 19 property owners in these circumstances, but also + + 20 arguing that, in almost every case, and we've seen + + 21 so many of these stories on the news, government + + 22 does help to pay for damage after it has occurred. + + 23 It just doesn't like to pay for damage before + + 24 it occurs. + + 25 So this is why I'm calling for an open-minded + + + + + + + + 82 + 1 collaborative approach. + + 2 It's going to be very difficult. + + 3 There are not many communities who solved + + 4 this problem, but, it's worth a try. + + 5 And this interest is only going to be + + 6 sustained for a few more months. + + 7 SENATOR O'MARA: Now, the trigger points, the + + 8 highs and the lows under the new plan, those are + + 9 based off of the average high for that time of year, + + 10 and the average -- or, the -- I'm sorry -- the + + 11 average of the extreme high for that time of year + + 12 and the extreme low of that time of year? + + 13 BILL WERICK: The high levels represent, + + 14 I think, the 2 percent exceedance frequency. + + 15 So they're a fairly high level, and they were + + 16 based on experimentation that was done with + + 17 different levels, watching the trade-off between + + 18 economic and environmental benefits. + + 19 So they varied throughout the year, just like + + 20 Lake Ontario does. + + 21 SENATOR O'MARA: Yeah, but I guess what I'm + + 22 getting at is, averaging off of the extreme highs + + 23 and the extreme lows, why didn't the commission take + + 24 a median approach, as opposed to an average of the + + 25 extremes? + + + + + + + + 83 + 1 And do you know what the difference is over + + 2 the years between the median and the extreme highs + + 3 and lows in that average? + + 4 BILL WERICK: So let me say this: + + 5 That the H14 high triggers represent the + + 6 levels that would be reached about 2 percent of the + + 7 time, so they don't relate to the median. + + 8 These are all about the frequency of those + + 9 high levels. + + 10 And we actually simulated these things, and + + 11 we would try 1 percent, we would try 2 percent, we'd + + 12 try different levels, and we would watch all our + + 13 models to see what results came out. + + 14 And it is finally the governments that + + 15 decided on the "295" that became part of the new + + 16 orders. + + 17 SENATOR O'MARA: Just in my mind, it seems + + 18 like using a median, or what happens more often, + + 19 rather than using the extremes, we base these + + 20 numbers on the extreme. + + 21 So now we're basing it at 2-1/2 inches higher + + 22 than what our extremes are in history has been, + + 23 rather than what a normal year is. + + 24 BILL WERICK: It is based on higher levels. + + 25 The lower you make those trigger points, the + + + + + + + + 84 + 1 less damage you would have, and the more the plan + + 2 becomes like '58DD, bad for the environment, but + + 3 causing less damage. + + 4 That was the -- that's the balance that you + + 5 strike as you adjust those triggers. + + 6 SENATOR O'MARA: Well, if the dam was + + 7 designed not to change the water levels of + + 8 Lake Ontario, which you said it was -- + + 9 BILL WERICK: Yes, it was designed to reduce + + 10 the water levels. + + 11 SENATOR O'MARA: Yeah. + + 12 BILL WERICK: The high levels. + + 13 SENATOR O'MARA: -- so, I don't follow how we + + 14 then had to alter it later on, to have a more + + 15 environmentally-friendly ebb and flow of the level + + 16 of the lake. + + 17 BILL WERICK: This was a decision that the + + 18 two governments made, to recognize that the + + 19 environment is an interest that both countries have, + + 20 and is protected by the treaty. + + 21 The 1950s is -- I mean, I'll say, when + + 22 I started out with the Corps of Engineers in the + + 23 '60s as a summer school -- summer student at + + 24 Conesus, I worked in the Cuyahoga River, and I had + + 25 to wear -- in the summertime, I had to wear rubber + + + + + + + + 85 + 1 gear from head to foot, because we knew that you + + 2 couldn't let river water touch your skin. + + 3 So that was the climate under which '58DD was + + 4 designed. There was absolutely no interest in the + + 5 environment. + + 6 On the LOSLR study, we tried to do something + + 7 for the environment, while doing as little damage as + + 8 we could to the shoreline protection damages. + + 9 SENATOR O'MARA: What in Plan 2014 will allow + + 10 the international board to make anticipatory changes + + 11 before that trigger point is hit? + + 12 BILL WERICK: The board can't deviate until + + 13 it hits the trigger. + + 14 But as Mr. Durrett said, even before they + + 15 hit the trigger, they were operating under the + + 16 F limit. + + 17 So Plan 2014 really incorporates the + + 18 knowledge of board representatives, and guys like + + 19 David Fay (ph.) who's in the audience, because it's + + 20 the intent of Plan 2014 to avoid those floods. + + 21 And, those rules are based on tests with + + 22 50,000 years of different possible inflow, so + + 23 there's many different ways that Lake Ontario can + + 24 flood. + + 25 So even before you hit the trigger, Plan 2014 + + + + + + + + 86 + 1 is trying to reduce those water levels. + + 2 When it comes to the trigger, then the board + + 3 is free to do much more, it can go off the plan. + + 4 Now, in this year, they still stayed on the + + 5 F limit because the plan was good. + + 6 SENATOR O'MARA: So the board then can + + 7 increase outflows from the river before the lake + + 8 level hits that trigger level? + + 9 BILL WERICK: Plan 2014 will increase + + 10 releases naturally, generically, as part of the + + 11 code. + + 12 SENATOR O'MARA: Okay. + + 13 You suggested in your remarks that the + + 14 outflow from Lake Champlain might be looked at, as + + 15 far as damming that lake. + + 16 You didn't mention anything in your remarks + + 17 about controlling the outflow from the Ottawa River + + 18 which was a factor in the flooding this year. + + 19 BILL WERICK: No. + + 20 SENATOR O'MARA: What -- what's the + + 21 distinction there? + + 22 BILL WERICK: First of all, we're not going + + 23 to look at damming Lake Champlain. + + 24 So the last dam study that I got involved in, + + 25 it lasted 30 years, and at the end of the 30-year + + + + + + + + 87 + 1 study the dam was rejected. + + 2 So we're not building many dams anymore. + + 3 So we're not gonna even think about building + + 4 a dam on Lake Champlain. + + 5 That's why the focus is all on the land-side + + 6 measures, and then, also, flood response plans. So + + 7 helping communities respond better to floods so that + + 8 they can minimize the damage. + + 9 On the Ottawa River, this is one of the + + 10 questions we'll answer better and more formally than + + 11 I can do right now. + + 12 My belief is, that the Ottawa River has some + + 13 regulation in the upper part of the basin, but it + + 14 has very little impact, and this year had almost no + + 15 impact. + + 16 So, we'll answer it more carefully, but + + 17 I would say there's very little hope that + + 18 controlling the Ottawa River can make things better. + + 19 SENATOR O'MARA: Okay. + + 20 ASSEMBLYMAN BARCLAY: Just one quick + + 21 question. + + 22 I'm a little confused on your flooding + + 23 comment you made about flooding not being good for + + 24 the environment, so Plan 2014 does not, I guess -- + + 25 BILL WERICK: Yes. + + + + + + + + 88 + 1 ASSEMBLYMAN BARCLAY: -- regulates against, + + 2 I assume you're saying, extreme flooding. + + 3 But the whole idea is to have water flow into + + 4 the wetlands and have an ebb and flow. + + 5 But, I guess, if you're a property owner, and + + 6 that ebb and flow is ruining your property, you + + 7 would consider it a flood. + + 8 I mean, am I understanding your -- you're + + 9 talking about extreme flooding? Is that what -- + + 10 BILL WERICK: That's really the difference. + + 11 So, Plan 2014 -- so, typically, a dam + + 12 operator who's only worried about flooding, because + + 13 there's always uncertainty about what's gonna happen + + 14 in the spring, they will lower the dam levels, just + + 15 in case. + + 16 The thing is, is that if you do that, then + + 17 every year the wetlands are flooded less, and that + + 18 has an impact on wetlands. + + 19 So to produce a higher level of water on the + + 20 wetlands, and we're not talking a flooding level, + + 21 just a higher level, then you slightly increase the + + 22 risk that you will have floods later on. + + 23 ASSEMBLYMAN BARCLAY: Why did the Plan 2014 + + 24 take away the discretion of the control board? + + 25 I don't -- I mean, you can say this is our + + + + + + + + 89 + 1 policy. We want to have a more ebb and flow. You + + 2 guys gotta follow through with our policy. + + 3 But why tie their hands and have certain + + 4 higher, you know, catch levels? + + 5 BILL WERICK: I think it's a good thing, + + 6 although people will argue about it. + + 7 I think Frank Sciremammano would say that I'm + + 8 wrong. + + 9 Here's the difference, is that when the board + + 10 confronts a decision, and its hands aren't tied, + + 11 then it makes a reactionary decision, that has a + + 12 little amount of time to study what the + + 13 possibilities are. + + 14 Sometimes it does good, and sometimes it does + + 15 bad. + + 16 With more regulation, you have the benefit of + + 17 studying these things with many more tests. + + 18 So the rules of Plan 2014 have been tested to + + 19 a much greater degree than the board decisions could + + 20 have been in the old days. + + 21 And, in fact, when the IJC first had hearings + + 22 on Plan 2007, this was a discussion because 2007 was + + 23 not an environmental plan. It was at least as good + + 24 at '58DD for shoreline owners. And it was all + + 25 automated the same way, with very little deviation. + + + + + + + + 90 + 1 ASSEMBLYMAN BARCLAY: And then how are they + + 2 going to judge the success of Plan 2014 with the + + 3 environment? + + 4 Are you going to say we got more + + 5 Northern Pike, and that's a success? + + 6 And the costs of that, I guess, that's a hard + + 7 thing for us, at least get my arms around, is, + + 8 obviously, because, potential, it would be very + + 9 costly to the property owners along the lake, and + + 10 what the return benefit. + + 11 And it's great to have, I think we're all + + 12 environmentalists. We would love to have more fish + + 13 and wildlife, and everything, but, is it worth the + + 14 cost? + + 15 So what -- what's going into that plan? + + 16 BILL WERICK: It's always going to be + + 17 controversial because you can't monetize all + + 18 environmental benefits. + + 19 I mean, some you can. Some you just can't. + + 20 I mean, there will be some environmental + + 21 benefits that will probably translate to real + + 22 economic benefits. + + 23 But we will try to at least quantify. + + 24 So what we've been doing for 10 years, is + + 25 sending biologists out into the field to actually + + + + + + + + 91 + 1 measure how much of the wetlands are covered by + + 2 different types of plants. + + 3 So continuing to gather evidence to see + + 4 whether or not wetlands change as we expected them + + 5 to. + + 6 Both the Corps of Engineers and New York + + 7 State DEC have gone out, and they've measured the + + 8 top elevations of shore-protection structures along + + 9 the south shore, because we want to make sure that + + 10 our models actually predicted the failure of + + 11 shore-protection structures correctly. + + 12 And now with the damage reporting that we're + + 13 doing with Cornell and New York Sea Grant, we'll + + 14 actually try to see whether our damage estimates + + 15 were reasonable. + + 16 Beyond that, we're trying to encourage even + + 17 broader monitoring. The Northern Pike is one + + 18 example. + + 19 I think that there are efforts underway + + 20 by the State University of New York to count + + 21 Northern Pike. + + 22 And we'll try to compare those to what they + + 23 would be in other years, to see whether we're really + + 24 getting a benefit or not. + + 25 ASSEMBLYMAN BARCLAY: Thank you. + + + + + + + + 92 + 1 BILL WERICK: We'll try to produce the + + 2 evidence. + + 3 ASSEMBLYMAN BARCLAY: Good. + + 4 SENATOR RITCHIE: Did you say earlier that + + 5 response was part of Plan 2014, a response to + + 6 flooding? + + 7 BILL WERICK: Yes. + + 8 SENATOR RITCHIE: I guess I would ask, did + + 9 anybody come to the local areas to prepare them for + + 10 what they might see, or what the extremes would be, + + 11 so they weren't taken by surprise? + + 12 Because I know most of the people here + + 13 thought there was, maybe, a two-week period they + + 14 were gonna have to live with the high water, and + + 15 possibly they could get through it. But it lasted + + 16 all summer until Labor Day. + + 17 So I'm just wondering, did anybody prepare + + 18 them for what the plan was going to mean to the + + 19 people who live here? + + 20 BILL WERICK: You know, we could always do + + 21 better. + + 22 And not knowing what the future would bring + + 23 limited the ability for us to forewarn people. + + 24 But I -- and I'm not on the board, but I did + + 25 watch modern tools, like Facebook. + + + + + + + + 93 + 1 There was a lot of advertising of this, and + + 2 so I think a lot of people in the audience were + + 3 watching Facebook and saw this as it rolled out. + + 4 Personally, I think that if we could make the + + 5 connections with the community to work with the + + 6 Great Lakes Adaptive Management Committee, that we + + 7 could establish better networks. + + 8 In our experience, communication works better + + 9 if you talk to community networks that already + + 10 exist. And we need to establish those. + + 11 So any help we can get with that, we would + + 12 appreciate. + + 13 SENATOR RITCHIE: Okay. + + 14 Thank you. + + 15 SENATOR O'MARA: What were the estimates of + + 16 damage that the IJC considered in coming up with + + 17 Plan 2014? + + 18 BILL WERICK: For Plan 2014, we had a model + + 19 that looked at damages to shore-protection + + 20 structures, to first-floor flooding in homes, and to + + 21 erosion of unprotected parcels that had buildings on + + 22 them. + + 23 And this had never been done before. + + 24 The difference in technology between 2000 and + + 25 1980 was, of course, enormous. + + + + + + + + 94 + 1 So we were able to make these whole-lake + + 2 estimates of how individual storm events would + + 3 damage shore-protection structures, and so we had + + 4 the dollar estimates. + + 5 SENATOR O'MARA: But what were the dollar + + 6 estimates? + + 7 BILL WERICK: They -- I don't have them in + + 8 mind, but I can get you a copy of the Plan 2014 + + 9 report that has actual dollar damage limits. + + 10 What you would see there is that the vast + + 11 majority, something like 87 percent of the damage + + 12 that occurs, is to shore-protection structures. + + 13 And, of course, that's one of the things + + 14 we're trying to measure now, to see whether we can + + 15 do better at estimating those. + + 16 SENATOR O'MARA: Anyone else? + + 17 Thank you very much. + + 18 BILL WERICK: Thank you. + + 19 SENATOR O'MARA: We're going to take a brief + + 20 five-minute break. + + 21 We don't want to be any longer than that. + + 22 [A recess was taken.] + + 23 [The hearing recommences.] + + 24 SENATOR O'MARA: If we could -- take your + + 25 seats, please. + + + + + + + + 95 + 1 We're ready to go on with our next + + 2 witness from the U.S. Army Corps of Engineers, + + 3 Lieutenant Colonel Adam Czekanski, commander of the + + 4 Buffalo District. + + 5 LT. COL. ADAM CZEKANSKI: Senator O'Mara -- + + 6 SENATOR O'MARA: Are you being joined at this + + 7 point by Mr. Forgette, or is he coming after you? + + 8 LT. COL. ADAM CZEKANSKI: Yes, sir. + + 9 SENATOR O'MARA: And we also have + + 10 Craig Forgette, chief of planning management team + + 11 and continuing authorities program manager for the + + 12 Buffalo District of the Army Corps as well. + + 13 Thank you, gentlemen, for being here. + + 14 LT. COL. ADAM CZEKANSKI: Thank you, sir. + + 15 Senator O'Mara, Senator Ritchie, + + 16 Assemblyman Barclay, Assemblyman Oaks, ladies and + + 17 gentlemen. + + 18 I want to thank you for the invitation of + + 19 inviting our team to come speak with you today. + + 20 As Senator O'Mara said, my name is Lieutenant + + 21 Colonel Adam Czekanski, commander of the Buffalo + + 22 District, U.S. Army Corps of Engineers. + + 23 With me today is Craig Forgette, who's + + 24 our planning management team chief. And, also, + + 25 Miss Bridget Brown. She is one of our regulators + + + + + + + + 96 + 1 from our -- leading regulators out of the Auburn + + 2 field office, southwest of Syracuse. + + 3 And I appreciate the invitation to speak + + 4 tonight. + + 5 I want to cover two different areas, really, + + 6 two primary areas of focus. + + 7 First of all, I just want to talk briefly + + 8 about the actions that the corps has been part of, + + 9 with respect to response to the high water levels on + + 10 Lake Ontario during these past months. + + 11 But then more importantly, I want to talk + + 12 about what the corps can do, moving forward. + + 13 And we talk about, and you hear in the news, + + 14 a lot about coastal resiliency. + + 15 We like to think about, you talk about, the + + 16 East Coast and the West Coast. + + 17 We think about the Great Lakes, really, as + + 18 the "North Coast." + + 19 So it's just as important on the north coast + + 20 to look at shoreline or coastal resiliency, and, + + 21 really, it's important, as we move forward, to look + + 22 at what can be done in the future to mitigate + + 23 hazards, as we move forward. + + 24 So just a very brief background, and I don't + + 25 have a slide for this, but just to give everyone an + + + + + + + + 97 + 1 idea, when we talk about the Buffalo District for + + 2 the Army Corps of Engineers, the Corps of Engineer + + 3 districts, in general, are delineated not by + + 4 political boundaries, but by watershed. + + 5 So our watershed is the watershed that drains + + 6 into Lake Erie, Lake Ontario, and the St. Lawrence + + 7 Seaway. + + 8 So our boundaries go from Messina, New York, + + 9 in the east, all the way out to the Ohio-Indiana + + 10 state line in the west. 38,000 square miles. + + 11 I bring that up because a big part of what we + + 12 do is, obviously, what goes on on great -- on the + + 13 lower Great Lakes. + + 14 On Lake Ontario, that includes navigation. + + 15 We have authority to dredge and maintain + + 16 federal navigation channels, a number of + + 17 recreational and commercial harbors on Lake Ontario, + + 18 but also to maintain the brick-wall structures and + + 19 a lot of those harbors, as you saw probably in + + 20 Oswego just a couple years ago. + + 21 In addition to the navigation mission, a + + 22 number of other missions we have, a number of other + + 23 projects we have, to include ecosystem restoration. + + 24 Some of you may be familiar with the + + 25 Braddock Bay project we're working on, just to the + + + + + + + + 98 + 1 west of Rochester, as one example of the work we do. + + 2 Our district has been around for 160 years. + + 3 And, really, for 160 years, we've had a close tie to + + 4 the communities on Lake Ontario. + + 5 So when the water level started to rise in + + 6 April, we were very much in tune with what was going + + 7 on on the ground. And our emergency-management + + 8 folks were in close coordination with the state's + + 9 regional emergency-management leadership, and, also, + + 10 with a number of the county emergency-management + + 11 chiefs. + + 12 On the 2nd of May, when the Governor declared + + 13 a state of emergency in the eight counties that + + 14 border Lake Ontario, we also activated our emergency + + 15 operations center, and requested funding from our + + 16 headquarters to support that activation, so that we + + 17 could be more responsive to the collective response + + 18 efforts to the high water levels on Lake Ontario. + + 19 One week after doing that, the Governor + + 20 formally requested assistance from the corps. + + 21 Within 48 hours we were able to provide + + 22 almost 200,000 sandbags for use in the communities + + 23 around Lake Ontario. + + 24 [Slide show begins.] + + 25 LT. COL. ADAM CZEKANSKI: Within 72 hours -- + + + + + + + + 99 + 1 if you look on the map here, what's highlighted is, + + 2 within 72 hours, we were able to launch teams -- + + 3 technical teams to a number of the communities who + + 4 were experiencing some of the most extreme high + + 5 water and flooding conditions on Lake Ontario. + + 6 Those teams, during the course of the + + 7 following weeks, were able to visit 17 different + + 8 communities on 20 different visits, and they were + + 9 able to provide assistance with respect to the flood + + 10 plight. + + 11 Technical assistance, as far as materials + + 12 being used and techniques being used, and provided + + 13 to the leadership of those communities, formal input + + 14 and feedback on what was being done, and what could + + 15 be done, to improve the situation. + + 16 So what the corps did, again, that's in the + + 17 May and June time frame, that technical assistance + + 18 to these communities you see highlighted, the direct + + 19 assistance with respect to sandbags. And, also, + + 20 regulatory, our regulatory branch, was very well + + 21 nested with the New York State DEC. + + 22 When the State DEC issued their regional + + 23 general permit on the 2nd of May, we were nested + + 24 with them to ensure that the process was very -- as + + 25 streamlined as possible for applicants so they could + + + + + + + + 100 + 1 protect their property. + + 2 And I want -- I'm going to turn it over to + + 3 Bridget right now so she can speak a little more + + 4 clearly, and a little more in detail, about those + + 5 efforts on the regulatory front, both, initially, + + 6 but also in the subsequent months, how we were + + 7 nested with the DEC, and how we were very much + + 8 committed to the communities in this area, to ensure + + 9 that residents could protect their property as + + 10 effectively as possible. + + 11 Bridget. + + 12 BRIDGET BROWN: Good evening. + + 13 I just wanted to mention that Lake Ontario + + 14 and the St. Lawrence River are regulated under + + 15 Section 10 of the Rivers and Harbors Act, as well as + + 16 Section 404 of the Clean Water Act. + + 17 And, therefore, all work in, over, or under + + 18 those waterways, including the discharge of dredge + + 19 or fill material, requires a Department of the Army + + 20 permit. + + 21 Once the regulatory branch was made aware of + + 22 the damages from the high water, we immediately + + 23 activated the regulatory emergency team. + + 24 In that case, we set up teams of project + + 25 managers to field all the phone calls, to do site + + + + + + + + 101 + 1 visits, and to expedite permitting. + + 2 The -- our nationwide permit program includes + + 3 two nationwide permits. One is Nationwide Permit 3 + + 4 for maintenance, and Nationwide Permit 13 for bank + + 5 stabilization, which cover most of the anticipated + + 6 work reasonable to confirm the high-water damage. + + 7 The vast majority of those permits do not + + 8 require the corps to review those before work can + + 9 commence over there. + + 10 So they're out and available for the public + + 11 to use. + + 12 We confirmed that those activities would + + 13 qualify for the emergency permit that the DEC + + 14 authorized for storm recovery, would also qualify + + 15 for Nationwide Permit 3 and 13. + + 16 So at that point, we worked in conjunction + + 17 with the DEC to prepare joint application process + + 18 and permit materials so that we could provide a + + 19 united front. + + 20 At that same time, we developed a plan to + + 21 funnel all the applicants through DEC, so to ensure + + 22 that only the ones that really needed to be reviewed + + 23 by the corps came our way, to reduce the amount of + + 24 delay that would happen during the permitting + + 25 process. + + + + + + + + 102 + 1 In addition, we worked with the + + 2 New York State Department of State to modify their + + 3 nationwide permit coastal consistency requirements + + 4 that are set on the nationwide permits, which, + + 5 normally, would require individual review by the + + 6 Department of State for any activities that included + + 7 lakeward encroachment. + + 8 Further, we also consulted with the U.S. Fish + + 9 and Wildlife Service, to develop a streamlined + + 10 process for any projects we have -- that may have a + + 11 potential effect on endangered species. + + 12 With that, we were able to allow for the + + 13 endangered-species consultation process to be + + 14 completed in one to two days versus the normal + + 15 30-day process. + + 16 So, overall, in general, it was a cooperative + + 17 effort between our state and federal agencies, to + + 18 try to make a very streamlined permit process, to + + 19 ensure that there was very minimal red tape as + + 20 possible, and to make sure that people were able to + + 21 implement their protection measures as soon as + + 22 possible. + + 23 LT. COL. ADAM CZEKANSKI: Thanks, Bridget. + + 24 So we're very proud of the fact that we were + + 25 able to assist very quickly, and, really, along + + + + + + + + 103 + 1 with -- I want to clarify one point, that throughout + + 2 this response effort and floodplain effort, we the + + 3 corps was, at all times, supplementing the State-led + + 4 effort. + + 5 The entire time, it was led by the State of + + 6 New York. And, again, as resources were being + + 7 utilized at the local level, county level, state + + 8 level, the corps was supplementing that effort. + + 9 I was able to visit a number of communities + + 10 along the shoreline, from Ogdensburg, all the way + + 11 down to Old Fort Niagara. And so, again, I can + + 12 sympathize with what was going on with respect to + + 13 the high water and with respect to damage to + + 14 property. + + 15 And, again, we were committed to assisting as + + 16 best we could throughout that effort. + + 17 But now the more important part I would say + + 18 is: + + 19 What can we do, moving forward? + + 20 What can we do, moving forward, to be more + + 21 resilient? + + 22 Because, as was mentioned earlier, in the + + 23 earlier briefings, you know, this is bound to happen + + 24 again. + + 25 We don't know if it's going to be to the same + + + + + + + + 104 + 1 extent, but there will be high water again at some + + 2 point. + + 3 So, it's most important to look at how can we + + 4 be more resilient along the Lake Ontario shoreline? + + 5 And Craig's gonna talk a little bit more + + 6 about that, as far as where the corps can assist. + + 7 Craig. + + 8 CRAIG FORGETTE: So the corps has a number of + + 9 authorities where we can help out, and do good + + 10 things on Lake Ontario. + + 11 And the first of these is the Streambank & + + 12 Shoreline Protection Authority, and this is for + + 13 public infrastructure, for significant public + + 14 infrastructure. + + 15 An example is this site at Western Reserve in + + 16 Cleveland, Ohio. It was completed, 775 feet of + + 17 shoreline protection, which was done back in May of + + 18 2005. + + 19 The general scope, is for us to provide for + + 20 developing and constructing streambank and shoreline + + 21 protection projects for highways, bridge approaches; + + 22 public-work facilities, such as water, wastewater + + 23 treatment plants, sewer lines; churches, public and + + 24 private and non-profit facilities. And that's + + 25 limited to a cost of $5 million, federal. + + + + + + + + 105 + 1 The -- we begin at the bottom of the slide. + + 2 The first step is a letter of intent. + + 3 And in the case of this flooding here on + + 4 Lake Ontario, we've received three letters of intent + + 5 back in May and June of 2017: + + 6 One from the Town of Greece. On + + 7 Edgemere Drive, which was discussed earlier, has had + + 8 a history of flooding; + + 9 Also at the Golden Hills State Park + + 10 Lighthouse; + + 11 And the Old Fort Niagara at the French + + 12 Castle. + + 13 So those are just a couple of the spots that + + 14 we've already received requests to help on using + + 15 this authority. + + 16 The first part of our process was to do a + + 17 feasibility study, environmental assessment, and + + 18 public involvement. + + 19 Then we move on to -- and that can take about + + 20 18 months, or, once from the time we get funded, + + 21 maybe a little longer. + + 22 And then we move into design and construction + + 23 phase. + + 24 That phase is cost-share. It's 65 percent + + 25 federal, 35 percent non-federal. So there is a + + + + + + + + 106 + 1 cost-sharing requirement to this. + + 2 But this is just one way that we can work to + + 3 help solve some of the problems along the coastline, + + 4 to help prevent further damage from future flooding. + + 5 Another authority we have is the planning + + 6 assistance to states and tribes. + + 7 These are non-structural solutions; meaning, + + 8 that it's not something that we're ever going to + + 9 design and construct. But it's -- some examples + + 10 are: Water-quality studies, wetland-evaluation + + 11 studies, floodplain-management studies, coastal-zone + + 12 management, harbor and port studies, or any other + + 13 water-resource planning investigation that may be + + 14 valid. + + 15 We -- those are cost-shared, 50 percent + + 16 federal, 50 percent non-federal, and has a maximum + + 17 of $2 million per state or federal funds for these + + 18 studies. + + 19 Again, these are not things that we can + + 20 design and construct, but we can certainly provide + + 21 support and the factors I just discussed. + + 22 And then one other way that we can help -- so + + 23 those are kind of mid-term solutions, things that we + + 24 can do to help on specific locations. + + 25 But in taking a broader approach and looking + + + + + + + + 107 + 1 at things on a larger scale across the Great Lakes + + 2 or across Lake Ontario, there's a proposal out for a + + 3 Great Lakes Coastal Resiliency Study. And, it can + + 4 certainly look at the things we just talked about, + + 5 that would be done on a specific site-by-site + + 6 location. But this is trying looking at things + + 7 across Lake Ontario or a Great Lakes-wide approach. + + 8 You know, all the things that we've been + + 9 talking about today, lake-level fluctuations, + + 10 changes in rain patterns, coastal storms, the + + 11 agricultural and stormwater runoffs, and invasive + + 12 species, and even aging infrastructure, some of the + + 13 flood-control structure -- or, the storm damage -- + + 14 the breakwalls, all the other things that we've + + 15 built, are getting older and may need repairs. + + 16 So this is a way to look at coastal + + 17 resiliency. + + 18 How do -- what's the ability of coastal areas + + 19 to withstand, recover from, and adapt to changes, + + 20 while making sure that we take a look at the + + 21 economic, environmental, social, and cultural + + 22 values? + + 23 So, balancing all of those things out -- + + 24 looking at those problems, and trying to look at a + + 25 holistic approach across all of those factors on + + + + + + + + 108 + 1 Lake Ontario -- is a goal of the study. + + 2 We look at an array of measures, a spectrum + + 3 of things, everything from: + + 4 Those structural-type components, whether + + 5 it's armoring, or putting more stone in; + + 6 Non-structural, meaning, we're not going to + + 7 build a real structure, but we may do dredging, or + + 8 add sand, or look at how the sand moves through the + + 9 system; + + 10 To just doing wetland or plantings, and those + + 11 types of activities; + + 12 To just land-use planning. + + 13 So all of those will be looked at as part of + + 14 this study. + + 15 We've received letters of intent from all of + + 16 the Great Lakes states to work towards this, and + + 17 this is something we would be working through, + + 18 through our investigations authority. + + 19 And we've requested federal funds to move out + + 20 on this effort. + + 21 Are there any other questions for us? + + 22 SENATOR O'MARA: Thank you. + + 23 SENATOR RITCHIE: Can you tell me how many + + 24 permit applications you received? + + 25 BRIDGET BROWN: I don't have the -- a number + + + + + + + + 109 + 1 on that at this point, because, again, a lot of it + + 2 was funneled through the DEC, to kind of get that + + 3 first tier out of there. And then we are still + + 4 receiving applications. + + 5 One issue for a regulatory permitting is + + 6 that, because the water was high, not a lot of + + 7 assessment could be done to what damage was there, + + 8 and to figure out exactly what needed to be done. + + 9 So our bigger lift of permit applications + + 10 will be coming. So we're expecting that to be + + 11 coming, you know, after the waters go down and + + 12 people starting assessing, and figuring out what + + 13 they can do. + + 14 And we'll be seeing the applications rise at + + 15 that point. + + 16 SENATOR RITCHIE: Do you have a large request + + 17 for people who have no shoreline stabilization in + + 18 place, now looking to do that? + + 19 BRIDGET BROWN: Yes. + + 20 Yes, we're getting lots of requests in. + + 21 We do lots of site visits to take a look at + + 22 those things. + + 23 And, yeah, so there are people who are now + + 24 looking at those measures. + + 25 SENATOR RITCHIE: So the corps will go out + + + + + + + + 110 + 1 and make recommendations of what should be done? + + 2 BRIDGET BROWN: We don't necessarily make + + 3 recommendations, what should be done. But we're + + 4 assessing what they're proposing to do, to see what + + 5 types of permits that was to require. + + 6 SENATOR RITCHIE: Okay. + + 7 Thank you. + + 8 ASSEMBLYMAN BARCLAY: I don't have any + + 9 questions. + + 10 I just want to say, thank you. + + 11 I've heard from many of my constituents, and + + 12 from municipalities, how great the corps has been + + 13 through this. + + 14 So thank you for what you've done so far. + + 15 SENATOR O'MARA: Bob. + + 16 ASSEMBLYMAN OAKS: Nothing for them. + + 17 Go ahead. + + 18 SENATOR O'MARA: Did the corps have any + + 19 involvement in the formulation of Plan 2014? + + 20 LT. COL. ADAM CZEKANSKI: No, sir. + + 21 I mean, we have members who are part of the + + 22 board of the -- International Lake Ontario and + + 23 St. Lawrence River Board. + + 24 But as far as actual formulation, we were not + + 25 directly involved with that. + + + + + + + + 111 + 1 SENATOR O'MARA: Do you have any estimate of + + 2 what the Army Corps has expended as a result of the + + 3 extensive flooding this year along Lake Ontario? + + 4 LT. COL. ADAM CZEKANSKI: I don't have a + + 5 figure with me, sir. + + 6 But, again, we did receive supplemental funds + + 7 from our headquarters, specifically for our + + 8 involvement with response to the flood event. + + 9 SENATOR O'MARA: Thank you. + + 10 Thank you very much for being here today. + + 11 LT. COL. ADAM CZEKANSKI: Thank you, sir. + + 12 SENATOR O'MARA: Next up, we have + + 13 Mr. Ken Lynch, executive deputy commissioner from + + 14 the New York State Department of Environmental + + 15 Conservation. + + 16 KENNETH LYNCH: Good evening, Senator O'Mara, + + 17 Senator Ritchie, Assemblyman Barclay, and + + 18 Assemblyman Oaks. + + 19 Thank you for the opportunity to discuss + + 20 the serious issue of this year's flooding along + + 21 Lake Ontario and the St. Lawrence River. + + 22 And I also want to personally thank you for + + 23 bringing me back to my home region for tonight. + + 24 My name is Ken Lynch. I'm the executive + + 25 deputy commissioner for New York State Department of + + + + + + + + 112 + 1 Environmental Conservation. + + 2 Commissioner Seggos sends his regrets that he + + 3 could not be here today. + + 4 I appreciate the opportunity to discuss the + + 5 State's approach to addressing these impacts, and + + 6 how to make our shorelines and neighborhoods more + + 7 resilient. + + 8 This spring, New Yorkers living along + + 9 Lake Ontario and the St. Lawrence River faced the + + 10 devastating impacts of a slow-moving disaster, + + 11 ultimately resulting in record-level water levels + + 12 according to NOAA data. + + 13 Heavy rainfall and snow melt throughout the + + 14 Great Lakes system caused flooding that inundated + + 15 houses and docks; eroded dunes, bluffs, and + + 16 backyards; threatened wastewater infrastructure in + + 17 low-lying areas; and flooded streets. + + 18 Based on a close scrutiny of the facts and + + 19 science, it appears that the International Joint + + 20 Commission, which manages Plan 2014 and determines + + 21 the water levels of Lake Ontario and the + + 22 St. Lawrence River, exacerbated the impacts of high + + 23 water levels by failing to increase outflows from + + 24 the lake, and by not properly using the flexibility + + 25 they have within their authority. + + + + + + + + 113 + 1 The IJC's actions over the course of the + + 2 spring and summer clearly illustrated that they + + 3 favored the interest of commercial waterborne + + 4 shipping interests over the homeowners, businesses, + + 5 and municipalities along both sides of the boundary + + 6 waters. + + 7 Such an action exacerbated the suffering of + + 8 the people who live and work along the lake and + + 9 river, particularly New Yorkers on the south side of + + 10 the system who faced more erosion and flooding due + + 11 to the system's hydraulics. + + 12 As flooding levels reached the peak, + + 13 Governor Cuomo took swift and decisive actions to + + 14 help communities cope with the -- this emergency, + + 15 and recover from the damage caused by this + + 16 historically high water levels. + + 17 Beginning in early May, before water levels + + 18 had reached their peak, Governor Cuomo, + + 19 Commissioner Seggos, Department of Homeland Security + + 20 and Emergency Services Commissioner Parrino, and + + 21 other state officials, including myself, were on the + + 22 scene of flooded properties along Lake Ontario and + + 23 the St. Lawrence River to review firsthand the + + 24 devastation that these high water levels were + + 25 causing. + + + + + + + + 114 + 1 The Governor stood up for our waterfront + + 2 communities and demonstrated his commitment through + + 3 several bold actions since day one. + + 4 And both the Governor and commissioners have + + 5 upheld our responsibility to assist these + + 6 communities, repeatedly visiting this area, to + + 7 ensure people receive the help they need. + + 8 Recognizing the gravity and the extent of + + 9 this emergency, Governor Cuomo issued Executive + + 10 Order Number 165 on May 2, 2017, which declared a + + 11 disaster for counties bordering Lake Ontario and the + + 12 St. Lawrence River, and directed state agencies, + + 13 including DEC, to assist the people and local + + 14 governments affected by it. + + 15 Since that time, the State has deployed + + 16 New York State emergency response mobile command + + 17 centers to provide swift aid to people in need of + + 18 assistance in dealing with floodwaters, including + + 19 one here in Mexico, implementing the Lake Ontario + + 20 Flood Assistance Hotline to help people with + + 21 insurance-related issues; assist homeowners with + + 22 flood-mitigation measures, such as sand bags, and + + 23 help them obtain technical guidance regarding + + 24 on-site repairs to property. + + 25 We expedited and streamlines permits to + + + + + + + + 115 + 1 enable property owners to quickly undertake + + 2 projects. + + 3 To date, we have issued 93 emergency + + 4 authorizations, nearly 1,300 general permits, and + + 5 more than 400 individual permits, for projects to + + 6 protect and restore the shoreline. + + 7 DEC also completed more than + + 8 300 coastal-erosion hazard area inspections. + + 9 The State deployed nearly 1.6 million + + 10 sandbags to protect the properties of residents and + + 11 businesses, as well as temporary removable dam + + 12 systems in the town of Greece and the village of + + 13 Sodus Point. + + 14 We provided 1 million for emergency repairs + + 15 and upgrades to wastewater treatment systems in + + 16 Sodus Point and Greece. + + 17 We requested, and received, assistance from + + 18 federal agencies, including the U.S. Army Corps of + + 19 Engineers and U.S. Geological Survey. + + 20 We imposed a 5-mile-per-hour speed limit to + + 21 control damaging wakes within 600 feet of Lake + + 22 Ontario and the St. Lawrence shoreline. + + 23 With your support, we made $45 million + + 24 available to municipalities, homeowners, and small + + 25 businesses to repair the damage caused by sustained + + + + + + + + 116 + 1 flooding. + + 2 And, we called upon President Trump to + + 3 provide federal disaster relief to this beleaguered + + 4 region, and demanded that he replace the current + + 5 U.S. IJC members. + + 6 New York's swift, comprehensive, and + + 7 effective efforts to address the high water levels + + 8 demonstrate our ability to quickly respond to + + 9 emergency situations. + + 10 The water levels experienced this year + + 11 throughout the Lake Ontario-St. Lawrence River + + 12 system are among the highest ever recorded. + + 13 Waves created by strong winds intensified the + + 14 impacts of the water levels to the detriment of + + 15 properties along Lake Ontario's southern and eastern + + 16 shores. + + 17 The impact on these communities was + + 18 staggering, as Governor Cuomo and other + + 19 administration officials saw during their tours of + + 20 the region. + + 21 I visited lake communities on several + + 22 occasions myself, to help address area needs with + + 23 our regional staff. + + 24 And I just want to take a moment to recognize + + 25 some of many regional staff that worked on this + + + + + + + + 117 + 1 project in this area, led by Regional Director + + 2 Matt Marko; + + 3 His regional permit administrator, + + 4 Dave Bimber, who was in all of our communities, + + 5 helping people with permits, together with his + + 6 staff; + + 7 Our information officer, Stephanie Webb, who + + 8 got a lot of the information out to the public, and + + 9 made sure they were educated on what programs we had + + 10 to offer; + + 11 And, Tim Walsh, who was our lead technical + + 12 assistant, helping property owners with protective + + 13 measures along the shoreline. + + 14 During his tenure, Governor Cuomo has + + 15 witnessed flooding in many parts of the state, and + + 16 he recognized that these natural disasters are the + + 17 reality of living in a water-rich state, exacerbated + + 18 by climate change. + + 19 Statewide, the Governor has directed state + + 20 agencies to take actions that are making flood-prone + + 21 regions more resilient to future storms. + + 22 Our challenge now is to address the impacts + + 23 of climate change and more frequent flooding on the + + 24 Lake Ontario-St. Lawrence River shoreline. + + 25 We all acknowledge that high water levels + + + + + + + + 118 + 1 have occurred before on the lake and river. + + 2 They devastated the region this year, and we + + 3 must plan for their reality that water levels will + + 4 rise and fall again. + + 5 It is essential that we work closely with all + + 6 levels of government to prepare for future flooding + + 7 by making the region as resilient as possible. + + 8 Already, DEC and our sister agencies + + 9 have opened up a dialogue with the Army Corps + + 10 of Engineers, FEMA, USGS, and EPA on new + + 11 short- and long-term activities to make the + + 12 Lake Ontario-St. Lawrence River shoreline more + + 13 resilient to future storms and flood events. + + 14 While this discussion follows the pattern we + + 15 developed in response to the state's recovery from + + 16 other natural disasters, the solutions developed + + 17 must be uniquely suited to the needs of this river + + 18 region. + + 19 To do so, we are exploring measures to help + + 20 reduce and mitigate the impacts of flooding if water + + 21 levels, again, are again high in the near future, + + 22 and longer-term measures to make the shoreline more + + 23 resilient to flooding. + + 24 Based on our experience with coastal storms, + + 25 such as "Superstorm Sandy," we recognize that + + + + + + + + 119 + 1 natural features can improve shoreline resiliency; + + 2 for example, wetlands act as natural sponges to + + 3 absorb floodwaters and protect property, and are + + 4 particularly helpful in urban areas. + + 5 Where natural features are less likely to be + + 6 effective, we will work with our federal partners to + + 7 develop structural measures to protect both people + + 8 and property. + + 9 Should the federal government issue the + + 10 disaster declaration the Governor requested in July, + + 11 we will work with local governments to help rebuild + + 12 smarter and more resilient. + + 13 We are also working closely with the Army + + 14 Corps to evaluate additional permanent steps to + + 15 protect and prepare for future high water levels and + + 16 flooding. + + 17 Thank you for the opportunity to discuss the + + 18 State's response to the Lake Ontario-St. Lawrence + + 19 River corridor flooding this year, and we look + + 20 forward to working with you to advance needed + + 21 resiliency measures in the near future. + + 22 I'll take any questions. + + 23 SENATOR O'MARA: Thank you, Mr. Lynch. + + 24 Assemblyman Oaks. + + 25 ASSEMBLYMAN OAKS: Yes. + + + + + + + + 120 + 1 Deputy Commissioner, I was just, both with + + 2 the Army Corps' presentation and then yours, you + + 3 talked about resiliency issues and, in essence, + + 4 trying to provide more protection to current + + 5 shoreline. + + 6 Some of the areas along the lake are publicly + + 7 owned. + + 8 Port Bay, for instance, has a barrier bar. + + 9 It's had a breach in it. It was fixed. But then we + + 10 had a new breach this year. And, so, efforts trying + + 11 to make that more secure, to protect some of the + + 12 bays or the properties off Lake Ontario. + + 13 There's other ones that may be publicly + + 14 owned, and also that may be privately owned; for + + 15 instance, Crescent Beach barrier bar for Sodus Bay + + 16 is all privately owned. + + 17 The question I guess I would ask both, you + + 18 know, of yourself and the department and the corps + + 19 is: + + 20 Is there -- I can see using public dollars, + + 21 and the legitimacy of that of protecting those + + 22 publicly-owned barrier areas. + + 23 But, also, I think there's been a strong case + + 24 made, even for some of the privately owned, not + + 25 necessarily for the protection of the individual + + + + + + + + 121 + 1 property that's privately owned, but the property on + + 2 the other side in the bay. + + 3 Is there any definitive type of policy on + + 4 that which could show strengthening, or using public + + 5 dollars for barrier bars that protect our bays along + + 6 Lake Ontario? + + 7 KENNETH LYNCH: Yeah, and I think we've heard + + 8 tonight that it is much more difficult to find + + 9 specific funding for private-property owners and + + 10 private protection. + + 11 Although, I will say, you and the Governor + + 12 have already found an exception to that with the + + 13 45 million ways to help those individual property + + 14 owners. + + 15 There is no definitive prohibition or + + 16 allowance of that type of activity. + + 17 I think in this case we need to be creative. + + 18 Obviously, it's easier to construct on public + + 19 property, with public entities joining the state and + + 20 federal government to help fund and construct those + + 21 projects. + + 22 But I think if we can find a connection + + 23 between private work and public protection, there + + 24 may be ways to fund those projects also with public + + 25 money. + + + + + + + + 122 + 1 ASSEMBLYMAN BARCLAY: Most of your remarks + + 2 tonight were on resiliency, the issues of things + + 3 that you did, obviously, in relation to this year's + + 4 flooding challenges. + + 5 They didn't go directly to challenging or + + 6 changing, you know, policy that may, you know, + + 7 lessen that in the future. + + 8 In other words, some -- we -- earlier + + 9 speakers we've talked about, who were talking about, + + 10 specifically, Plan 2014, or answering questions + + 11 along that. + + 12 I guess I would focus on, one, was -- did + + 13 DEC, as an agency, take a stand or provide public + + 14 direction on Plan 2014 before it was adopted? + + 15 KENNETH LYNCH: We did not take a formal + + 16 position on 2014. + + 17 We were out in the community, listening to + + 18 concerns. + + 19 We heard a lot about the environmental + + 20 benefits that 2014 provided. + + 21 We also heard a lot about the concerns that + + 22 property owners had, especially in this area, about + + 23 the potential impacts. + + 24 We passed those messages on to the federal + + 25 government as part of the process, but the plan + + + + + + + + 123 + 1 itself was adopted by the federal government. + + 2 ASSEMBLYMAN BARCLAY: Is there a role now, do + + 3 you think, for state government to take a reaction? + + 4 I know the Governor made some comments about, + + 5 you know, how it was this specific crisis was + + 6 handled, or, how it might have been created, + + 7 et cetera. + + 8 Is there -- is it appropriate now for the + + 9 State to push, perhaps, more strongly of future -- + + 10 as we look at what any future issues might be, that + + 11 encourage changes and/or any alternatives to the + + 12 existing plan? + + 13 KENNETH LYNCH: Well, again, it's a federal + + 14 plan, so we don't have a direct input into it. + + 15 But, certainly, I think there's always an + + 16 opportunity for us to participate with both our + + 17 federal, state, and local partners, to lend our + + 18 position and our perspective, representing the + + 19 people of the state of New York, towards changing or + + 20 adapting plans. + + 21 ASSEMBLYMAN OAKS: Thank you. + + 22 SENATOR RITCHIE: First, I would like to + + 23 thank you and the Commissioner and your department + + 24 for being on the ground with the Assemblymen and + + 25 I at the workshops, and trying to help to turn + + + + + + + + 124 + 1 permits around as quickly as possible, along with + + 2 the Army Corps. + + 3 The Army Corps was very helpful also. + + 4 When you opened up, you did say that either + + 5 the Governor or the department had beliefs that the + + 6 IJC did not act soon enough. + + 7 Given what we heard during the first two + + 8 testimonies, saying that they don't believe that + + 9 would have changed, I think the consensus in this + + 10 room is that we all believe that, maybe, if water + + 11 would have been increased during the outflow, some + + 12 of the damage may not have happened. + + 13 Can you give us some information that would + + 14 help refute what was told during the first two + + 15 testimonies? + + 16 KENNETH LYNCH: Well, I -- you know, the + + 17 issue of when releases should have begun, and + + 18 whether or not the plan allowed that flexibility, is + + 19 debatable. + + 20 But I think what's very clear is that, once + + 21 releases started, and we reached certain thresholds, + + 22 that there was a clear requirement to balance the + + 23 various perspectives. + + 24 The Governor was very clear and the + + 25 Commissioner was very clear that the most important + + + + + + + + 125 + 1 aspect that should have been balanced were the + + 2 property owners. + + 3 And I think that is part of the plan. + + 4 We heard a lot about shipping interests, and + + 5 we couldn't increase the flows because of shipping + + 6 interests. + + 7 The Governor and Commissioner were clear that + + 8 the interests of the property owners should come + + 9 first. + + 10 SENATOR RITCHIE: And do you believe that the + + 11 interests of Montreal was taken, potentially, at a + + 12 higher level than the people in this district? + + 13 KENNETH LYNCH: Well, certainly, there were + + 14 flooding impacts downstream too. + + 15 But I think the Commissioner's and Governor's + + 16 point was that, IJC, just don't look at downstream, + + 17 just don't just look at shipping interests. + + 18 We have a lot going on here on the shores of + + 19 Lake Ontario and New York State that you have to + + 20 also be very aware of, and give that as strong, if + + 21 not more, consideration, certainly, than some of the + + 22 other interests. + + 23 SENATOR RITCHIE: Will you be making a -- + + 24 some kind of presentation to the federal government, + + 25 or at least sending on a letter, asking them to take + + + + + + + + 126 + 1 a look at this? + + 2 KENNETH LYNCH: Well, both the Commissioner + + 3 and Governor have already sent letters in respect to + + 4 our position that more should have been done. + + 5 And I would anticipate that, going forward, + + 6 we will work with the IJC to have them better + + 7 address the situation in advance of it occurring + + 8 again. + + 9 SENATOR RITCHIE: And can you just give me an + + 10 update on the dunes in the Sandy Pond area, where + + 11 we're at with the dunes there? + + 12 KENNETH LYNCH: As you know, Senator, we flew + + 13 the dune area, at least on two days, with our new + + 14 drone technology. We got some great footage. + + 15 We helped state parks do improvements and + + 16 stabilization to their property, which, in turn, + + 17 protected some of the residents behind that. + + 18 We've worked closely with several property + + 19 owners in the Sandy Pond area to get their permits + + 20 so they can protect their shoreline. + + 21 We also put out a fact sheet, with some + + 22 guidance to property owners on how they can help + + 23 protect their shorelines, and talking about both + + 24 hardening the shoreline and resiliency projects to + + 25 protect it over the long term. + + + + + + + + 127 + 1 The dunes are very dynamic. They change + + 2 every year whether there's flooding or not. + + 3 But we're here to offer our technical + + 4 guidance, and work specifically with, not just + + 5 private-property owners, but the communities in the + + 6 Sandy Pond area, to see if we can get better + + 7 protection of those dunes. + + 8 SENATOR RITCHIE: Thank you. + + 9 ASSEMBLYMAN BARCLAY: Thanks for being here. + + 10 I just want to echo the Senator's comments + + 11 about how well Dave Bimber and region -- Matt did + + 12 with the permitting. + + 13 I know Dave worked a lot of weekends and late + + 14 at night, and they really were very responsive. + + 15 So we appreciate that. + + 16 I don't have any real questions, Ken. + + 17 The only thing I would just mention, if the + + 18 department or the Governor would come out publicly + + 19 against 2014, I think that would help our cause + + 20 immensely. + + 21 I know, during the whole process, we didn't + + 22 hear much from state government. And I know he does + + 23 have the property owners in the southern and eastern + + 24 part of Lake Ontario, their interests, at heart. + + 25 So it would be nice to hear from him publicly + + + + + + + + 128 + 1 about Plan 2014. + + 2 KENNETH LYNCH: Okay. I'll take that back, + + 3 Assemblyman. + + 4 ASSEMBLYMAN BARCLAY: Thank you. + + 5 SENATOR O'MARA: Ken, can you touch on the + + 6 fact that we have approved, between -- negotiations + + 7 between the State Legislature and the Governor for + + 8 this $45 million to be expended for relief and + + 9 damages, why there has been no FEMA declaration for + + 10 the damages in this flooding incident? + + 11 KENNETH LYNCH: Yeah, I mean, the Governor + + 12 made the request, I believe in June, or July, for + + 13 that declaration. + + 14 We have not seen that yet. + + 15 I know (indiscernible) is working closely + + 16 with the Governor's Office on putting together all + + 17 of our damages. + + 18 But, I'm not intimately familiar with the + + 19 status of that, but we have not heard back + + 20 definitively from FEMA at this point. + + 21 SENATOR O'MARA: Do you -- can you -- are you + + 22 in a position here tonight to outline what damages + + 23 we've seen at our state parks along the shoreline? + + 24 I know Senator Ritchie just touched on + + 25 Sandy Island State Park, but, there's many others as + + + + + + + + 129 + 1 well. + + 2 And, can you touch on what damages resulted + + 3 at other parks? + + 4 And, also, what the impact of this flooding + + 5 was on the activity at those parks this summer? + + 6 The campgrounds, was usage down, were state + + 7 revenue down, because of the flooding issues that we + + 8 had? + + 9 KENNETH LYNCH: Yeah, I don't have any of + + 10 those specific numbers for our sister agency. + + 11 But I do know, and I mentioned Sandy Pond, + + 12 and the impact that the nearby park had, and the + + 13 erosion of the shoreline and the considerable work + + 14 they had to do there. + + 15 I was in Fair Haven with the Senator, + + 16 I believe, one day, and watching the bank erode from + + 17 that small state park in Fair Haven. + + 18 I know the damage was significant. + + 19 I do know that, certainly, usage was down + + 20 because of the flooding, not only state parks, but + + 21 DEC had to close some of our boat launches, for + + 22 safety reasons, because the water was so high. Just + + 23 didn't know what was under the water, so we couldn't + + 24 have people launching in certain areas. We tried to + + 25 keep as many open as possible. + + + + + + + + 130 + 1 So, it was considerable across the state. + + 2 And I'm sure state parks could provide you + + 3 with some more detail. + + 4 SENATOR O'MARA: Which is one of the reasons + + 5 I wanted state parks to be here tonight, but + + 6 somebody else had another idea on that. + + 7 So, on the wetlands, and the higher levels of + + 8 wetlands, good environmental purposes, but, you + + 9 know, we've seen a lot of damage around the lake + + 10 this year, not just from the direct lake waves + + 11 hitting, but coming around from behind, in a lot of + + 12 cases, from these elevated wetlands. + + 13 KENNETH LYNCH: Uh-huh. + + 14 SENATOR O'MARA: What is the department + + 15 doing, or looking at, in regards to how to mitigate + + 16 those types of damages, coming in from behind, so to + + 17 speak? + + 18 KENNETH LYNCH: Yeah, you know, it's really + + 19 the same approach that we use from the lakeshore, + + 20 although, you don't have as many of the hardening + + 21 interests that you have on the backside. + + 22 But, certainly, you try to find those + + 23 resilient measures, that you can build natural + + 24 barriers to protect the individual property owners + + 25 while, at the same time, preserving the integrity of + + + + + + + + 131 + 1 the wetland, and making the connections between the + + 2 lake and the wetland as fluid as possible, if you + + 3 will, so that, you know, you don't have restricted + + 4 activity that will cause water to go in other + + 5 directions; for example, to neighboring properties. + + 6 So, it's looking holistically at the design + + 7 and improvements to those wetlands, to make sure you + + 8 can get the benefits of those wetland areas while, + + 9 at the same time, protect private property. + + 10 SENATOR O'MARA: Thank you. + + 11 KENNETH LYNCH: All right. + + 12 Thanks for having me. + + 13 SENATOR O'MARA: Next up, we have Chris Leo, + + 14 acting president of New York State Homes and + + 15 Community Renewal. + + 16 CHRIS LEO: Good evening, Senator O'Mara, + + 17 Senator Ritchie, Assemblyman Barclay, and + + 18 Assemblyman Oaks. + + 19 On behalf of Governor Cuomo and + + 20 Commissioner Visnauskas, I am pleased to offer this + + 21 testimony regarding New York State Homes and + + 22 Community Renewal's response to the Lake Ontario + + 23 2017 flooding. + + 24 In the spring of 2017, the southern coast of + + 25 Lake Ontario experienced historic flooding, with the + + + + + + + + 132 + 1 lake rising 30 inches above normal levels. + + 2 The flooding impacted the counties of Cayuga, + + 3 Jefferson, Monroe, Niagara, Orleans, Oswego, + + 4 St. Lawrence, and Wayne. + + 5 The Governor immediately recognized the + + 6 impact that this flooding would have on the region's + + 7 residents and businesses. + + 8 On May 2, 2017, the Governor declared a state + + 9 of emergency and deployed the National Guard and + + 10 other state resources to help deal with the impacts + + 11 of the flooding. + + 12 As the impact on homeowners came into + + 13 specific focus, Governor Cuomo announced the + + 14 availability of $7 million for homeowners impacted + + 15 by flooding on Lake Ontario and the St. Lawrence + + 16 River, and directed HCR to develop programs to help + + 17 residents rebuild their homes. + + 18 At the same time, Governor Cuomo also + + 19 announced $10 million in support for public + + 20 infrastructure repairs and $5 million in support for + + 21 small businesses. + + 22 HCR subsequently tasked its Affordable + + 23 Housing Corporation to assist with its response. + + 24 Through AHC, HCR deployed state resources to + + 25 help qualified homeowners to purchase and repair + + + + + + + + 133 + 1 their homes. + + 2 AHC's operating model is to contract with + + 3 local not-for-profit corporations to conduct + + 4 community outreach, help with applications, + + 5 determine eligibility, suggest contractors, verify + + 6 construction, among the many services that they + + 7 offer. + + 8 For this initiative, AHC issued a request for + + 9 proposals. We evaluated submissions, and selected + + 10 the following not-for-profit organizations to + + 11 administer the program: + + 12 Sheen Housing, covering Cayuga, Monroe, and + + 13 Wayne counties; + + 14 Niagara Falls Neighborhood Housings Services + + 15 in Niagara County; + + 16 Pathstone in Orleans County; + + 17 And Neighbors of Watertown in Jefferson, + + 18 Huron, Oswego, and St. Lawrence counties. + + 19 Each of these not-for-profit organizations is + + 20 a pillar of their local communities, with the + + 21 expertise and a wide range of state and federal + + 22 housing and community-development programs. + + 23 Thanks to the Governor's and the + + 24 Legislature's bipartisan efforts, the Lake Ontario + + 25 Relief Bill was passed, and signed by the Governor + + + + + + + + 134 + 1 on July 6, 2017, making a total of 15 million + + 2 available to local homeowners impacted by the flood. + + 3 Homeowners that sustained flood-related + + 4 damage are eligible to receive up to $50,000 in + + 5 reimbursements for eligible losses, including for + + 6 repairs to and restoration of structures, equipment, + + 7 and other physical damage. + + 8 Throughout the application period, which + + 9 concluded on September 29th, the not-for-profit + + 10 organizations conducted extensive outreach to ensure + + 11 that communities were aware of the resources made + + 12 available. + + 13 The not-for-profits participated in a total + + 14 of nearly 50 public meetings and town halls to share + + 15 information, answer questions, and provide + + 16 application assistance. + + 17 We recognize that this is a difficult time as + + 18 communities work to rebuild. + + 19 The not-for-profits continue to review and + + 20 process applications as expeditiously as possible. + + 21 We encourage homeowners to reach out to their + + 22 local not-for-profit organizations with questions + + 23 about their applications. + + 24 The Governor is committed to ensuring that + + 25 communities have the resources they need to complete + + + + + + + + 135 + 1 their repairs. + + 2 Last week, the Governor and the Legislature + + 3 announced that all necessary funding will be + + 4 available to support eligible homeowners, + + 5 businesses, and communities, and we will continue to + + 6 work hand in hand with our colleagues across state + + 7 government, including Empire State Development, the + + 8 Division of Homeland Security and Emergency + + 9 Services, and the Department of Environmental + + 10 Conservation, financial services, and tax and + + 11 finance, to ensure a comprehensive whole of + + 12 government response. + + 13 We are also grateful to our colleagues in + + 14 local government at the front lines of this + + 15 response, and continue to work closely with them to + + 16 ensure that they have the resources they need to + + 17 respond effectively. + + 18 Thank you again for the opportunity to + + 19 testify in this important matter. + + 20 We share your commitment to helping these + + 21 communities rebuild, and we look forward to working + + 22 with you in the months ahead. + + 23 I'd be happy to take any questions that you + + 24 might have. + + 25 + + + + + + + + 136 + 1 SENATOR O'MARA: Thank you. + + 2 Go ahead. + + 3 ASSEMBLYMAN BARCLAY: Thank you. + + 4 How many -- do you know the number of + + 5 applications that were submitted? + + 6 CHRIS LEO: Yeah. + + 7 The application deadline was September 29th, + + 8 which was recently. + + 9 We received, roughly, 3400 applications. And + + 10 there was a big uptick as the deadline was amplified + + 11 towards the end of the program. + + 12 So these applications are now being reviewed + + 13 by the not-for-profits. + + 14 We do expect that number to drop maybe a + + 15 little bit, as we suspect that some of the final + + 16 applications that were submitted may or may not be + + 17 eligible. + + 18 So, thirty-four is a rough number. + + 19 ASSEMBLYMAN BARCLAY: You know, I understand, + + 20 when we did the legislation, with all legislation, + + 21 there gets to be holes, that you don't see it when + + 22 you're writing it. And we rely on, obviously, the + + 23 agencies to kind of fill in some of the, you know, + + 24 spaces that inevitably occur with legislation. + + 25 I -- I've had a lot of people reach out to my + + + + + + + + 137 + 1 office that have manufactured homes on leased + + 2 property. + + 3 CHRIS LEO: Uh-huh. + + 4 ASSEMBLYMAN BARCLAY: They're not eligible, + + 5 is your understanding, for this type of relief that + + 6 we're providing? + + 7 CHRIS LEO: Yeah, that's my understanding, is + + 8 that we need -- we are looking for ownership of the + + 9 unit and the land to qualify for -- + + 10 ASSEMBLYMAN BARCLAY: Some of these have very + + 11 long-term leases. You know, it's not -- it's almost + + 12 like they do own it. + + 13 CHRIS LEO: Oh, like a 99-year lease? + + 14 ASSEMBLYMAN BARCLAY: Yeah. Leases -- + + 15 CHRIS LEO: Yeah, yeah. + + 16 ASSEMBLYMAN BARCLAY: -- right. + + 17 So is there a way to relook at that, maybe? + + 18 CHRIS LEO: Yes, we can certainly -- + + 19 ASSEMBLYMAN BARCLAY: I can do it more + + 20 formally in a letter, telling you, specifically, the + + 21 issues and where they are? + + 22 CHRIS LEO: Yes. + + 23 ASSEMBLYMAN BARCLAY: But -- if you would be + + 24 willing to look at that, because I think, if you + + 25 read the legislation, they may or may not have been + + + + + + + + 138 + 1 included in there. + + 2 But I think, when the regulations came out, + + 3 or -- you know, I don't know if they're official + + 4 regulations, whatever your decision-making on it, + + 5 they got left out. + + 6 So I would appreciate that. + + 7 CHRIS LEO: Absolutely. + + 8 ASSEMBLYMAN BARCLAY: The only other thing, + + 9 and it's not maybe a question, but it's more of a + + 10 comment: + + 11 I hope -- you know, when we talk about + + 12 Plan 2014, and some of the winners and losers in + + 13 this whole thing, I hope you're coordinating with, + + 14 and you mentioned ESDC, and just the total cost out + + 15 there. + + 16 And -- is there any hope that there's going + + 17 to be a report at the end of this about -- so we can + + 18 really track how much damage was done -- + + 19 CHRIS LEO: Sure. + + 20 We're still processing, and we won't know the + + 21 tally on the homeowners' side. That will become + + 22 clearer first. + + 23 The application deadlines for the municipal + + 24 infrastructure program and the ESD small-business + + 25 program are at the end of December. + + + + + + + + 139 + 1 So I would expect that it would be at that + + 2 time that, you know, the cost would be -- come into + + 3 greater focus. + + 4 ASSEMBLYMAN BARCLAY: And you mentioned the + + 5 fact that the Governor is trying to reach agreement + + 6 to add more money to this program. + + 7 CHRIS LEO: Yes. + + 8 ASSEMBLYMAN BARCLAY: Do -- can you opine on + + 9 how much money is needed? + + 10 CHRIS LEO: Well, based on our estimates for + + 11 the homeowner portion, I believe that the total of + + 12 $50 million, so that would be what we have already, + + 13 plus the additional funding that I believe was + + 14 agreed to by the Legislature. + + 15 ASSEMBLYMAN BARCLAY: So you're comfortable + + 16 with that? + + 17 CHRIS LEO: I am comfortable with that + + 18 number. + + 19 We've been having weekly meetings with the + + 20 not-for-profits, and we're almost getting live-time + + 21 reporting on kind of what the damage estimates are. + + 22 So we're fairly confident with that number. + + 23 ASSEMBLYMAN BARCLAY: Okay. + + 24 Thank you. + + 25 SENATOR RITCHIE: Can you tell me how many + + + + + + + + 140 + 1 applications have been funded up to this point out + + 2 of the 3400? Do you have that number with you? + + 3 CHRIS LEO: Out of the, roughly, + + 4 3400 applications that we've received, right now + + 5 I would say just over 60 percent have been reviewed + + 6 and processed by the not-for-profits. + + 7 They've conducted, roughly, 1,000 site + + 8 visits. + + 9 And we are looking, so far, at disbursements, + + 10 roughly, $3 million to date. + + 11 You know, as the application deadline + + 12 approached, the not-for-profits had to focus their + + 13 resources on actually processing the applications. + + 14 So we fully expect them now to pivot towards + + 15 recovery, to see the rate of expenditures increase. + + 16 SENATOR RITCHIE: I know in the beginning you + + 17 were focusing on primary homes -- + + 18 CHRIS LEO: Uh-huh. + + 19 SENATOR RITCHIE: -- which, of course, is + + 20 something that's critical. + + 21 CHRIS LEO: Absolutely. + + 22 SENATOR RITCHIE: Because we were all worried + + 23 that there was not going to be enough funds in + + 24 place. + + 25 Now, with last Friday's announcement, will + + + + + + + + 141 + 1 you start looking at secondary homes, and also + + 2 shoreline stabilization, to be funded at the same + + 3 time, instead of putting them further down the list? + + 4 CHRIS LEO: Sure, absolutely. + + 5 In regards to prioritization, obviously, + + 6 primary homes, where it was an issue of habitability + + 7 of the home, would be a top priority. + + 8 But as far as lakeshore hardening, if that + + 9 proved to be an imminent threat to the home, then + + 10 that would have received a higher priority. + + 11 So we are trying to work both in tandem. + + 12 What we're finding here is, you know, there + + 13 is a bit of a contractor shortage that we are trying + + 14 to deal with here. + + 15 And that was one of the side benefits of + + 16 having these not-for-profits, is some of them have + + 17 wide coverage areas where they are able to bring + + 18 more contractors into the picture. + + 19 So we are working on the issue. + + 20 SENATOR RITCHIE: So at this point, given, + + 21 now, all the applications are gonna -- going to be + + 22 in at the same time -- + + 23 CHRIS LEO: Uh-huh. + + 24 SENATOR RITCHIE: -- will you be hiring or + + 25 putting on extra temporary staff to deal with the + + + + + + + + 142 + 1 applications, to get the money out as quickly as + + 2 possible? + + 3 CHRIS LEO: We won't hire necessarily at HCR. + + 4 And the not-for-profits know that if they + + 5 need resources to fully meet the, you know, + + 6 customer-service expectations, quite honestly, of + + 7 the Legislature and Governor, that they are free to + + 8 do so. + + 9 SENATOR RITCHIE: And are you keeping track + + 10 of how quickly the actual check disbursements are + + 11 going out? + + 12 CHRIS LEO: Yes, absolutely, we are. + + 13 And, you know, it's worth noting that the + + 14 legislation was signed on July 6th. + + 15 We started -- the not-for-profits started + + 16 issuing checks in the last week in -- in, basically, + + 17 the first week of August. + + 18 So, you know, we hope now, as -- again, as + + 19 I mentioned earlier, that as the initial intake of + + 20 applications and review of those applications + + 21 subsides, that you will see an uptick in the + + 22 delivery of funds. + + 23 SENATOR RITCHIE: Okay. + + 24 Thank you. + + 25 SENATOR O'MARA: You said you had a big + + + + + + + + 143 + 1 uptick in the applications towards deadline. + + 2 CHRIS LEO: Uh-huh. + + 3 SENATOR O'MARA: Do you see any need to open + + 4 up that deadline a little bit longer, to allow those + + 5 that might have not gotten to it? + + 6 CHRIS LEO: I think that the message was + + 7 fairly well amplified and advertised before. + + 8 You know, if there are homeowners who feel + + 9 that, if they were denied for some reason, or if + + 10 there is some special case that they would seek to + + 11 make, there is an appeals process that homeowners + + 12 can avail themselves of. + + 13 But I don't see a need right now to open that + + 14 up. + + 15 SENATOR O'MARA: With second homes, I've + + 16 heard diverging opinions on the multiple-owner + + 17 situation -- + + 18 CHRIS LEO: Yes. + + 19 SENATOR O'MARA: -- and the income threshold, + + 20 (indiscernible). + + 21 Can you tell us how that is working? + + 22 CHRIS LEO: We -- we've heard the same + + 23 confusion, where the -- just to settle on the policy + + 24 now: When there is more than one person on the deed + + 25 of the home, an owner will be eligible for + + + + + + + + 144 + 1 assistance so long as no individual, or married + + 2 couple if they file their taxes jointly, on the deed + + 3 has an income of more than $275,000 in the + + 4 taxable-year 2016. The applicant need not be the + + 5 individual with the highest income among those on + + 6 the deed. + + 7 So, essentially, you know, we will look, to + + 8 check the income of the owner submitting the + + 9 application. + + 10 They would need to provide their tax returns + + 11 so that we can make sure that they're within the + + 12 income threshold set forth in the legislation. And + + 13 then there would just be a certification that no + + 14 other owner of the property makes more than the + + 15 threshold limit of $275,000. + + 16 [Inaudible comments from the audience.] + + 17 SENATOR O'MARA: So -- yeah, can you speak + + 18 closer to the microphone? + + 19 CHRIS LEO: Oh, I'm sorry. + + 20 Sure. + + 21 SENATOR O'MARA: So now that the + + 22 interpretation is, you're not just checking the + + 23 income of the -- one of multiple owners, the one + + 24 applying, you're not just checking theirs -- their + + 25 income? + + + + + + + + 145 + 1 CHRIS LEO: We would just check -- I'm sorry. + + 2 We would just check the income of the person + + 3 submitting the application. + + 4 SENATOR O'MARA: Okay. But I thought you + + 5 just said that you would then certify that no of the + + 6 other owners made more than $257,000 -- + + 7 CHRIS LEO: Then they would be asked to + + 8 certify that no other owner makes more than + + 9 $275,000. + + 10 SENATOR O'MARA: So if one of the owners + + 11 makes more than $275,000, their property is not + + 12 eligible? + + 13 CHRIS LEO: That's our interpretation of the + + 14 legislation, yes. + + 15 SENATOR O'MARA: Do you think that that + + 16 income threshold is necessary, to have it in there + + 17 at all? + + 18 CHRIS LEO: I believe that there should be + + 19 some threshold on second homes. + + 20 I mean, it was lifted for primary homes, + + 21 where we are certainly dealing with issues of people + + 22 becoming homeless. + + 23 And we understand that a lot of these + + 24 secondary homes are legacy cottages. + + 25 You yourself mentioned earlier, you know, how + + + + + + + + 146 + 1 special that residence was to you. + + 2 But we do feel that there should be certain + + 3 cost controls in place as we're giving out this + + 4 assistance. + + 5 SENATOR O'MARA: Well, I happen to disagree + + 6 with that. And if somebody has damage to their + + 7 property from something that, in this case, was at + + 8 least partially government-aggravated, anyways, + + 9 I don't agree with that threshold on that. + + 10 But, I want to move on to the businesses. + + 11 CHRIS LEO: Yes. + + 12 SENATOR O'MARA: I've heard multiple + + 13 complaints from businesses in the area here about + + 14 the process being onerous, voluminous documentation + + 15 being required to be provided, that many businesses + + 16 are just opting to not pursue it, because of that. + + 17 And I understand that the business + + 18 applications for relief were, pretty much, below + + 19 what was anticipated. + + 20 Is that correct? + + 21 CHRIS LEO: It's my understanding. + + 22 But, again, the deadline for that + + 23 applications has not expired yet, so there is still + + 24 time for applications to be submitted. + + 25 SENATOR O'MARA: What is the deadline for + + + + + + + + 147 + 1 that? + + 2 CHRIS LEO: I believe it's the -- the end of + + 3 December, December 29th. + + 4 SENATOR O'MARA: Can you outline for us, + + 5 generally, what is required of a business to + + 6 provide? + + 7 CHRIS LEO: You know, I'm sorry, it's not my + + 8 area of expertise, so I cannot. + + 9 But I can -- you now, we have conferred -- + + 10 and we know that you have concerns, Senator. + + 11 I know that we have brought them back to ESD, + + 12 and my colleagues at ESD are working on streamlining + + 13 that. + + 14 SENATOR O'MARA: I've heard concerns that a + + 15 1099 would be issued to the grant recipient. + + 16 Can you discuss that? + + 17 CHRIS LEO: Yes. + + 18 We've certainly looked into that issue in + + 19 regards to the 1099s. + + 20 Again, I would defer to my colleagues at ESD. + + 21 But it's my understanding that the small + + 22 business recovery program will be sending 1099s to + + 23 grant recipients, and that that's consistent with + + 24 federal law. + + 25 So that there has been -- we've understood + + + + + + + + 148 + 1 those concerns, but we think now it's a matter of + + 2 federal law. + + 3 SENATOR O'MARA: Is that just to businesses, + + 4 or is that to property -- + + 5 CHRIS LEO: It's just to businesses, + + 6 actually. So it does specifically state for + + 7 businesses. But that -- + + 8 SENATOR O'MARA: So they'll get a 1099 for + + 9 the grant and be taxed on that? + + 10 CHRIS LEO: It's my understanding that that's + + 11 consistent with the federal law, sir. + + 12 SENATOR O'MARA: I've also heard, in regard + + 13 to the business process, that a business owner is + + 14 not allowed to get reimbursement for having paid + + 15 their employees to do some of the recovery work. + + 16 CHRIS LEO: Uh-huh. + + 17 SENATOR O'MARA: Is that being reviewed? + + 18 CHRIS LEO: Well, again, you know, it's not + + 19 with the ESD program, but we have consulted with our + + 20 colleagues there. And I believe that -- well, + + 21 actually, I'm not sure. + + 22 I would have to get back to you. + + 23 I don't know the answer to that, about the + + 24 labor issue. + + 25 SENATOR O'MARA: It was my understanding + + + + + + + + 149 + 1 that -- and you're talking about ESD (Empire State + + 2 Development). Correct? + + 3 CHRIS LEO: Yes. Uh-huh. + + 4 SENATOR O'MARA: And it was my understanding + + 5 somebody was coming this evening, but, nobody is on + + 6 this witness list today. + + 7 So, do you know what happened there? + + 8 CHRIS LEO: No, I don't. + + 9 I was never aware that ESD was invited to + + 10 attend. + + 11 SENATOR O'MARA: I've also heard, with + + 12 regards to the business program, that a business + + 13 that paid cash for supplies, materials, or wages, is + + 14 not eligible to be reimbursed, even if they have + + 15 receipts? + + 16 CHRIS LEO: Right. + + 17 Well, there, I do have news for you, sir. + + 18 That the ESD has clarified that cash payments + + 19 will, in fact, be eligible for assistance as long as + + 20 those payments can be verified. + + 21 SENATOR O'MARA: Okay. + + 22 Thank you. + + 23 CHRIS LEO: Thank you, sir. + + 24 SENATOR RITCHIE: I have one follow-up + + 25 question. + + + + + + + + 150 + 1 Just one with follow-up question. + + 2 CHRIS LEO: Yes. + + 3 SENATOR RITCHIE: I know that we've had many + + 4 conversations over the multiple dwelling. + + 5 And I appreciate the Governor and his staff + + 6 making sure that what was agreed to was how this is + + 7 unfolding now. + + 8 My only concern is that, there probably are + + 9 some individuals who came to a workshop, who were + + 10 told they were eligible, and then went to apply, and + + 11 found out that, you know, 10 members had to turn in + + 12 their tax returns, and they decided not to do it. + + 13 So, for those individuals, how do we get the + + 14 information out to them, since that changed? + + 15 And, is the deadline now extended so that + + 16 they can make sure their application is included? + + 17 CHRIS LEO: Sure. + + 18 I'd like to assure you that any homeowner who + + 19 was either given incorrect information or had an + + 20 incorrect understanding of the regulations, and did + + 21 not submit an application because of that, will be + + 22 eligible to apply. + + 23 We'd like to direct them to, kind of, our + + 24 appeals process, and that can start as simply as + + 25 sending an e-mail to LakeOntario@nyshcr.org, at + + + + + + + + 151 + 1 which point we will consider, kind of, their + + 2 circumstances, and allow them access to the program, + + 3 should they be eligible. + + 4 SENATOR RITCHIE: And do you have any way to + + 5 actually disseminate this information, so that if + + 6 they don't come to the flood hearing tonight, that + + 7 they would have access to the change in the + + 8 regulations? + + 9 CHRIS LEO: Absolutely. + + 10 One of the benefits of working with the + + 11 not-for-profits is they have lists of all the + + 12 people. + + 13 SENATOR RITCHIE: They have lists. + + 14 Okay. + + 15 CHRIS LEO: HCR, also my office, received + + 16 1,000 calls the first three weeks, so we also have a + + 17 robust contact list. + + 18 And we would be more than happy to send + + 19 emails to every person that we have on our list and + + 20 the not-for-profit's lists. + + 21 SENATOR RITCHIE: And I would be interested + + 22 in that appeals process, how that is actually going + + 23 to work, and how many people apply for an appeal. + + 24 CHRIS LEO: Oh, absolutely, we will keep + + 25 track. + + + + + + + + 152 + 1 We've received, I think, four or five appeals + + 2 so far. + + 3 We expect that to increase as the processing + + 4 of the applications continues, and ends. + + 5 So we will -- + + 6 SENATOR O'MARA: Up to the microphone, + + 7 please. + + 8 CHRIS LEO: Oh, sorry. Yes. I apologize. + + 9 Yes, we will actually keep track, and let you + + 10 know. + + 11 SENATOR RITCHIE: Thank you. + + 12 CHRIS LEO: Yes. + + 13 SENATOR O'MARA: Thank you very much for + + 14 coming this evening. + + 15 CHRIS LEO: Thank you, sir. + + 16 Next up is Frank Sciremammano. + + 17 LT. COL. ADAM CZEKANSKI: Senator O'Mara, + + 18 sorry to jump in again here, but I want to correct + + 19 the record real quick before Frank takes the stand. + + 20 SENATOR O'MARA: Sure. + + 21 LT. COL. ADAM CZEKANSKI: So you had asked me + + 22 earlier if the corps was involved in the development + + 23 of Plan 2014. + + 24 So, my colleagues just corrected me. + + 25 So the Buffalo District did provide some + + + + + + + + 153 + 1 technical assistance during the study phase that was + + 2 occurring. And then our headquarters, the Great + + 3 Lakes and Ohio River Division, did have a little + + 4 more direct involvement in the development of that + + 5 plan. + + 6 So I apologize for the confusion. + + 7 I just wanted to set the record straight. + + 8 SENATOR O'MARA: Okay. + + 9 Thank you. + + 10 Frank Sciremammano is a Ph.D., professional + + 11 engineer -- + + 12 FRANK SCIREMAMMANO, JR.: I'm sorry? + + 13 SENATOR O'MARA: "PE," is that professional + + 14 engineer? + + 15 FRANK SCIREMAMMANO, JR.: Yes. + + 16 SENATOR O'MARA: Yeah. + + 17 -- with F-E-S Associates, a retired professor + + 18 of engineering at Rochester Institute of Technology, + + 19 and International Lake Ontario-St. Lawrence River + + 20 Board. + + 21 Thank you for being here this evening. + + 22 FRANK SCIREMAMMANO, JR.: Good afternoon, or + + 23 I guess this is good evening at this point. + + 24 I apologize. + + 25 I probably should have talked earlier when we + + + + + + + + 154 + 1 were talking about Plan 2014, rather than the grant + + 2 process, because I won't be dealing with that, + + 3 obviously. + + 4 SENATOR O'MARA: I agree. + + 5 FRANK SCIREMAMMANO, JR.: So, again, my name + + 6 is Frank Sciremammano, Jr. I'm a retired full + + 7 professor of engineering at RIT, and a principal at + + 8 F-E-S Associates in Rochester, New York. + + 9 Of more relevance to this hearing, I'm + + 10 currently, and have been since 1995, a member of the + + 11 International Lake Ontario-St. Lawrence River Board, + + 12 which was formerly called "The International + + 13 St. Lawrence River Board of Control." + + 14 I am now the senior member on that board, + + 15 over 20 years, dealing with this issue directly. + + 16 I also served, from 2000 to 2006, on the IJC + + 17 Lake Ontario-St. Lawrence River Study Board, which + + 18 examined and reported on alternative methods for + + 19 regulating outflows from the lake. + + 20 And Bill gave you -- Bill Werick gave you + + 21 some history on that. + + 22 I will fill in some of the holes that he left + + 23 out. + + 24 And for the record, I do not live on the + + 25 lakeshore. I live 10 miles in. + + + + + + + + 155 + 1 I do not have any financial interest in + + 2 property or businesses on the shoreline. + + 3 So you can think of it as a hobby, if will + + 4 want. More of an academic interest. + + 5 Today I speak as an individual. + + 6 I want to be very clear, I'm not speaking on + + 7 behalf of the Lake Ontario-St. Lawrence River Board, + + 8 I'm not speaking on behalf of the IJC, or any other + + 9 organization. + + 10 Mr. Stephen Durrett did speak on behalf of + + 11 the board, and I generally concur with his formal + + 12 statement, although, I do have some different + + 13 answers to some of your questions than he had. + + 14 Let me start by stating very clearly, that + + 15 Plan 2014 did not cause the flooding experienced + + 16 this year on Ontario. + + 17 However, it's also just as clear that + + 18 Plan 2014 did not protect against extreme water + + 19 levels on the lake, as was stated by the IJC, while + + 20 promoting Plan 2014. + + 21 Further, I would argue that Plan 2014 is not + + 22 capable of protecting against extreme levels on + + 23 Lake Ontario, because the entire plan is purposely + + 24 biased to protecting the downstream areas of the + + 25 St. Lawrence River at the expense of the shoreline + + + + + + + + 156 + 1 communities of Lake Ontario, and especially those + + 2 along the south shore of the lake in Western + + 3 New York. + + 4 Further, Plan 2014 does tie the hands of the + + 5 board in dealing with taking preventive measures and + + 6 dealing with lake levels, until it's too late to + + 7 avoid them. + + 8 I want to be very clear on that. + + 9 As opposed to Plan '58D, the board is + + 10 prohibited from taking -- making deviations that + + 11 will benefit the lake, although we're allowed to + + 12 take deviations that will benefit others. + + 13 The only time we're allowed to do it is after + + 14 we hit the trigger levels, and at that point, + + 15 I would argue it's much too late. + + 16 To illustrate how this bias was created in + + 17 Plan 2014, I want to give you a short summary of how + + 18 Plan 2014 came about. + + 19 In 1999, the IJC appointed a study board, of + + 20 which I was a member, and Dr. Dan Barletta who's in + + 21 the audience, to examine the whole issue of outflow + + 22 control on the St. Lawrence. + + 23 The study acted in a transparent, open, and + + 24 public way to develop guidelines, and to come up + + 25 with recommendations to the IJC, which were + + + + + + + + 157 + 1 contained in a report delivered in March of 2006. + + 2 The study recommended consideration of three + + 3 plans: + + 4 Plan A+, the economic plan, maximized + + 5 economic benefits. + + 6 Plan B+, the environmental plan, maximized + + 7 the environmental benefits. + + 8 Plan D+, the balanced plan, which as the name + + 9 implies, balanced these things. + + 10 Plan 2014, and I want to be very clear about + + 11 this, Plan 2014 is not one of the recommended plans + + 12 from the IJC study, and, in fact, it violates three + + 13 of the principal guidelines of that study. + + 14 Those guidelines stated that, if damages + + 15 result from any plan, they should not fall + + 16 disproportionately on any one geographic area or + + 17 interest group. + + 18 Well, almost all the damages from Plan 2014 + + 19 fall to the Lake Ontario shoreline, with a smaller + + 20 damage to boating in the Thousand Islands area. + + 21 But it's, basically, all to Lake Ontario and + + 22 the Thousand Islands. + + 23 All other geographic areas and interests are + + 24 held harmless or benefit. + + 25 The guidelines also state, that if damages + + + + + + + + 158 + 1 are anticipated, mitigation and compensation + + 2 measures should be in place prior to implementation. + + 3 Plan 2014 has none. + + 4 Finally, the guidelines state, that any plan + + 5 should be developed in an open process with wide + + 6 public participation. + + 7 Plan 2014 was developed in secret by a group + + 8 that only consulted with environmental advocates. + + 9 So how did this all happen? + + 10 Well, after the study was completed, the IJC + + 11 announced the proposed new order and plan that + + 12 consisted of a revised Plan D+ from the study. + + 13 That was the balanced plan. + + 14 They renamed it Plan 2007, and then 2008, and + + 15 they stated at that time, quote, Plan 2008 is an + + 16 improvement with respect to the environmental and + + 17 overall economic benefits, and takes a more balanced + + 18 approach to all interests. + + 19 They further stated that: The environmental + + 20 benefits of Plan B+, the environmental plan, are + + 21 desirable, but implementation of Plan B+ is not + + 22 possible, quote, without unduly reducing the + + 23 benefits and protections currently accorded to other + + 24 interests, so that the environmental plan would + + 25 cause too much damage. + + + + + + + + 159 + 1 After holding public hearings, and facing + + 2 demands from environmental groups and the + + 3 New York State DEC -- + + 4 I see the assistant, or, whatever, + + 5 commissioner, has left. + + 6 -- but, they demanded that the environmental + + 7 plan would be the only thing acceptable to them. + + 8 So the IJC withdrew its proposal, and formed + + 9 a new secret working group of representatives only. + + 10 They worked in secret. Nobody knew who was + + 11 on the committee. Nobody knew when they met. No + + 12 minutes. No freedom of information. + + 13 After a while they came out with a new + + 14 version of Plan B+, which they recommended, which + + 15 was termed "Bv7" for Plan B, Version 7. + + 16 After some further secret negotiations, the + + 17 working group came up with Plan 2014, which is just + + 18 Plan Bv7, but with a slight modification to add + + 19 trigger levels. + + 20 When I examined Plan Bv7 and Plan 2014, + + 21 I found that the environmental benefits were almost + + 22 the same as the original Plan B+. + + 23 And I'll direct your attention to the graphs + + 24 I have -- + + 25 I would have put them up here. I wasn't sure + + + + + + + + 160 + 1 you had a projector available. + + 2 -- on the back of my -- the last page of my + + 3 testimony. + + 4 I have the environmental benefits of the + + 5 plans: B+ from the study, Bv7, and 2014 from the + + 6 secret negotiations. + + 7 They're almost identical, the environmental + + 8 benefits. + + 9 However, if you look at the damages to the + + 10 coastal areas, both downstream on Lake Ontario -- + + 11 or, upstream on Lake Ontario, and downstream on the + + 12 lower river, Plan B+, because of the guidelines from + + 13 the study, distributed the damages between the two, + + 14 and they were almost equal. + + 15 However, Bv7 and Plan 2014, all the damages + + 16 are to Lake Ontario, and the downstream areas have + + 17 none. + + 18 So why did that happen? + + 19 I believe this was the result of the fact + + 20 that the Providence of Quebec stood up for its -- by + + 21 its commitments -- stood by its commitment that its + + 22 citizens in downstream areas of the St. Lawrence + + 23 should receive no less protection under any new plan + + 24 than they did under the previous plan of operation, + + 25 1958D. + + + + + + + + 161 + 1 As a result, all the damages were shifted to + + 2 the lake. + + 3 And, apparently, the New York State + + 4 government representatives, and in particular, the + + 5 New York State DEC, that were on the secret working + + 6 group were fine with this shift. + + 7 The shift is clearly illustrated in the + + 8 graphs that I have attached. + + 9 The environmental benefits again remain the + + 10 same, yet the damages to Lake Ontario were increased + + 11 dramatically, and those to the lower St. Lawrence + + 12 River were eliminated. + + 13 I'll give you just one example in which the + + 14 way Plan 2014 achieved this shift. + + 15 The F limit that Mr. Durrett talked about, + + 16 that we worked under during April and May, seeks to + + 17 balance flooding between downstream and upstream. + + 18 True. + + 19 However, the downstream level has a + + 20 not-to-exceed maximum. We can't go over that. + + 21 Lake Ontario has no maximum. + + 22 So this spring, while we held their levels + + 23 steady, by either maintaining low flows or reducing + + 24 flows dramatically, Lake Ontario jumped + + 25 28 centimeters, almost a foot. + + + + + + + + 162 + 1 And this was done to hold the downstream + + 2 level to its maximum; thus, downstream is protected + + 3 while Lake Ontario is not. + + 4 There are other ways, both subtle and not-so + + 5 subtle, in Plan 2014, that show its imbalance, with + + 6 a bias against protection for the shoreline + + 7 communities of the lake. + + 8 And I would be happy to explain these to you + + 9 in an appropriate forum, with sufficient time, + + 10 because it takes a while. + + 11 Let me just summarize by stating that: + + 12 Contrary to many of the IJC's statements, + + 13 Plan 2014 will not protect from extreme levels on + + 14 Lake Ontario. + + 15 Further, in my opinion, the primary + + 16 responsibility for this lies with the New York State + + 17 government, in the form of the New York State DEC + + 18 and the current administration which negotiated + + 19 Plan 2014. And if they didn't approve it, they at + + 20 least acquiesced in it. + + 21 They didn't stop it. + + 22 Whenever I point out the inequities in the + + 23 plan to my colleagues, I'm told, simply, that my + + 24 New York State government negotiated and approved + + 25 the plan, so what's the problem? + + + + + + + + 163 + 1 I thank you for your time today. + + 2 I invite you to look further into this + + 3 matter. + + 4 If New York State does not act forcefully to + + 5 modify the terms of Plan 2014, you should be + + 6 prepared to budget continuing sums of State funding + + 7 to address the direct damages and the economic + + 8 degradation of our shoreline communities that will + + 9 result from future operations under 2014. + + 10 Again, I'm happy to answer any questions you + + 11 might have. + + 12 You may get slightly different answers than + + 13 you got earlier. + + 14 [Applause.] + + 15 SENATOR O'MARA: Do you want to start? + + 16 Go ahead. + + 17 ASSEMBLYMAN BARCLAY: Thank you for your + + 18 testimony. It's been very enlightening. + + 19 And as I told you earlier, I was able to see + + 20 you on that Facebook video that you did with the + + 21 Democratic & Chronicle, so that helped -- it gave me + + 22 a great perspective of your opinion too. + + 23 Just for this year's flooding, could anything + + 24 have been done to prevent the flooding, or make it + + 25 less than what it turned out to be, by the control + + + + + + + + 164 + 1 board? + + 2 FRANK SCIREMAMMANO, JR.: Not, in my opinion, + + 3 to prevent it completely. + + 4 We would have had flooding under the old + + 5 plan, under this plan. + + 6 We may have been able to make a difference of + + 7 a few inches, especially on the rising part of it. + + 8 And as you heard from the DEC commissioner, + + 9 who was speaking about shipping, we may have been + + 10 able to get it down a little quicker, but only at + + 11 the expense of stopping the St. Lawrence Seaway from + + 12 operating. + + 13 So I think, within a few inches, we probably + + 14 would have had the same flood. + + 15 ASSEMBLYMAN BARCLAY: Right. + + 16 You know, I find your testimony interesting. + + 17 Potentially, it could get you in trouble, I + + 18 guess, with various parties that may have a + + 19 different opinion, and this goes back to my original + + 20 question. + + 21 I'm a little confused about the whole + + 22 hierarchy of how this works. + + 23 I understand the control board answers to the + + 24 IJC. + + 25 But how are -- how -- who's on the control -- + + + + + + + + 165 + 1 how many people are on the control board? + + 2 How are you appointed? + + 3 Can you get fired from the control -- + + 4 I suppose you could get fired. + + 5 How does this work? + + 6 FRANK SCIREMAMMANO, JR.: So here's how it + + 7 works: + + 8 The IJC, six members: three U.S., three + + 9 Canadian. + + 10 On the U.S. side is presidential appointees, + + 11 so they turn over with the administration. + + 12 One is full-time, the chair; and the others + + 13 are part-time. + + 14 They appoint the control board. + + 15 They have 18 boards, because they deal with + + 16 the boundary orders from coast to coast. + + 17 We're one of the more controversial ones, so + + 18 we have a large membership, 10 members. We're + + 19 appointed on the base -- by the IJC, and we serve at + + 20 their pleasure, on the basis of our personal and + + 21 professional capacities, not to represent any one + + 22 group. + + 23 We can be fired, and we can be terminated, at + + 24 any time. + + 25 And I fully expected to be terminated when + + + + + + + + 166 + 1 I came out in opposition of Plan 2014 strongly, but, + + 2 they reappointed me. And, so, I'm trying to work + + 3 with it. + + 4 The IJC sets the policies, adopts the plan, + + 5 and we implement. So we're the guys turning the + + 6 crank, if you will. + + 7 ASSEMBLYMAN BARCLAY: Right. + + 8 FRANK SCIREMAMMANO, JR.: And Plan 2014 does + + 9 not allow us to deviate the way Plan '58D did, + + 10 I think I mentioned that, until we hit the trigger + + 11 levels. + + 12 ASSEMBLYMAN BARCLAY: Right. + + 13 FRANK SCIREMAMMANO, JR.: And to me, that's a + + 14 real problem. + + 15 ASSEMBLYMAN BARCLAY: I think from your + + 16 testimony, the thing that I was most surprised, and + + 17 maybe hadn't heard, is the fact that the + + 18 environmental benefits of Plan 2014 aren't that more + + 19 superior than the other potential plans out there. + + 20 FRANK SCIREMAMMANO, JR.: Well, it was not + + 21 different than the environmental plan that was + + 22 recommended, which distributed the damages. + + 23 ASSEMBLYMAN BARCLAY: Okay. + + 24 FRANK SCIREMAMMANO, JR.: So the + + 25 environmental benefit was the same, but the damages + + + + + + + + 167 + 1 were all shifted to the lake. + + 2 ASSEMBLYMAN BARCLAY: Okay. All right. + + 3 FRANK SCIREMAMMANO, JR.: But the + + 4 environmental benefit was greater than Plan D, which + + 5 was the balanced plan, which had environmental + + 6 benefits, but not to the same extent. + + 7 So Plan D -- + + 8 ASSEMBLYMAN BARCLAY: So was the -- who + + 9 they -- what I'm getting at, because I've heard, you + + 10 know, the issue is, Plan 2014 was implemented at the + + 11 behest of the environmentalists. + + 12 So -- but they -- they probably want to + + 13 (indiscernible) -- they like the environmentalists' + + 14 plan anyways. + + 15 So, I guess I'm not misled by the fact that + + 16 they were pushing -- they didn't care about the lake + + 17 interests, or maybe the shipping interests of the + + 18 St. Lawrence. They didn't have Quebec. + + 19 They really just cared about the + + 20 environmental issues, so they would have gone with + + 21 Plan 2014 -- + + 22 FRANK SCIREMAMMANO, JR.: Well, they + + 23 represented -- + + 24 ASSEMBLYMAN BARCLAY: -- over the + + 25 environmentalists' plan? + + + + + + + + 168 + 1 FRANK SCIREMAMMANO, JR.: -- but they + + 2 represented the entire state in the secret + + 3 negotiations. + + 4 ASSEMBLYMAN BARCLAY: Right. + + 5 FRANK SCIREMAMMANO, JR.: They didn't + + 6 represent just the DEC and just the environmental + + 7 groups. + + 8 They should have represented the homeowners, + + 9 the businesses, along the lake and along the river + + 10 as well. And that was absent. + + 11 My understanding, again, this was a secret + + 12 process. + + 13 I got some secondhand accounts of what went + + 14 on. And, basically, you know, this area was sold + + 15 down the river, in my opinion. + + 16 ASSEMBLYMAN BARCLAY: Well, certainly, you + + 17 can say, from the Governor's Office, there was + + 18 certainly a lot of -- I don't know what happened + + 19 behind the scenes, but there was, certainly, nothing + + 20 done publicly. + + 21 I never heard them come out against any of + + 22 the plans, or, obviously, endorse any of the plans. + + 23 But, you know, I've read and seen that they + + 24 had to have some sort of tacit type of approval on + + 25 it. + + + + + + + + 169 + 1 Did they go -- what other -- was it, + + 2 I guess -- New York is the only one affected, + + 3 I guess, by this plan. Right? There's is no other + + 4 governmental -- + + 5 FRANK SCIREMAMMANO, JR.: That's right. + + 6 It's New York, province of Quebec, and + + 7 province of Ontario, and then the two federal + + 8 governments, that's who was negotiating. + + 9 ASSEMBLYMAN BARCLAY: Right. + + 10 FRANK SCIREMAMMANO, JR.: That's who were + + 11 negotiating. + + 12 And, again, I repeat, Quebec stood up for + + 13 their citizens. They said, We will not accept any + + 14 more damage. + + 15 New York State, from what I heard, said, + + 16 Fine. + + 17 ASSEMBLYMAN BARCLAY: Hmm. + + 18 All right. + + 19 Thank you very much. + + 20 SENATOR RITCHIE: Could you just explain to + + 21 me, when you're talking about being able to deviate, + + 22 that's the question we asked numerous times, and got + + 23 a different answer? + + 24 So could you explain that? + + 25 FRANK SCIREMAMMANO, JR.: So I'll try and be + + + + + + + + 170 + 1 clear. + + 2 Under '58D, the board could deviate from -- + + 3 the plan is just a calculation. It tells you how + + 4 much water to let out every week. + + 5 And so, under '58D, we were allowed to + + 6 deviate from the plan, higher or lower, in response + + 7 to our providing a benefit for one interest group if + + 8 we didn't significantly harm another. + + 9 That was the old way. + + 10 Under Plan 2014, we are not allowed to + + 11 deviate from the calculated value -- so the computer + + 12 program runs the show -- unless we go above the + + 13 trigger level or below the lower trigger level; + + 14 except, we can deviate for a request from the River. + + 15 If a ship is coming into Montreal, then we're + + 16 allowed to deviate. + + 17 This weekend we did a deviation to allow boat + + 18 haul-outs on Lake St. Lawrence. + + 19 Now, generally, those are fairly minor. + + 20 But I think the whole idea that we can + + 21 deviate for everybody else, except the lake, just + + 22 grates on me a bit. + + 23 And, again, we cannot deviate until we + + 24 actually hit the trigger. + + 25 We could have a perfect forecast, a month in + + + + + + + + 171 + 1 advance, that we're going to exceed the trigger. + + 2 We're not allowed to do anything, until we + + 3 hit it. + + 4 And that's in the order, and it says that + + 5 can't be changed unless they go back to the + + 6 governments and get approval. + + 7 So, that's a hard-and-fast. + + 8 SENATOR RITCHIE: So do you believe, if that + + 9 authority still existed, that it would have helped + + 10 the situation this year? + + 11 FRANK SCIREMAMMANO, JR.: Again, I think this + + 12 it might have helped. + + 13 It's hard to judge what actions the board + + 14 would have taken and when. + + 15 But the most difference it would have made + + 16 was a few inches, I believe, because things did + + 17 happen very quickly in the month of April. + + 18 SENATOR RITCHIE: So do you believe, in the + + 19 future, without that ability to deviate, that you + + 20 could end up with flooding in a year that, maybe, + + 21 flooding is not something that would happen? + + 22 FRANK SCIREMAMMANO, JR.: I do. + + 23 I believe it's true, and especially with the + + 24 bias in the plan with the F limit. + + 25 I believe that, even without the flooding + + + + + + + + 172 + 1 downstream on the Ottawa, we could have a problem in + + 2 future, and the board could see it coming, and + + 3 wouldn't be able to do anything. + + 4 SENATOR RITCHIE: And so I assume you do + + 5 believe that Montreal's interests were taken over + + 6 this area's interest? + + 7 FRANK SCIREMAMMANO, JR.: I think they + + 8 were -- they were taken over that in the development + + 9 of Plan 2014, and it has to do with the + + 10 representation and the secret negotiations that went + + 11 on, in my opinion. + + 12 SENATOR RITCHIE: Thank you. + + 13 FRANK SCIREMAMMANO, JR.: And that nobody was + + 14 there standing up for New York State beyond the + + 15 wetlands, if you will. + + 16 SENATOR RITCHIE: Thank you. + + 17 ASSEMBLYMAN OAKS: In some of the discussions + + 18 today, or comments back to us, I was given some hope + + 19 that, you know, there's analysis. + + 20 We had the event. We've been through that. + + 21 Analysis is going to take place. We're gonna get a + + 22 report. And, that we might have some changes in the + + 23 way things -- you know, what we learned from that, + + 24 we can move forward with that information, make some + + 25 changes, and go forward. + + + + + + + + 173 + 1 But, in some of your remarks you were just + + 2 making, of the -- how you're constrained on the + + 3 control board, you're saying it's written into the + + 4 plan. It's not something that simply, + + 5 administratively, we can do this better if we do + + 6 something -- you're not going of be able to make + + 7 that change without Plan 2014 itself being amended? + + 8 FRANK SCIREMAMMANO, JR.: That's my + + 9 understanding, the way it's written. + + 10 Not only that, the IJC can't make a change in + + 11 our deviation authority until they go back to + + 12 governments and ask. + + 13 But I am hopeful. + + 14 You know, I do not believe 2014 will be + + 15 thrown out, and it does have some good features. It + + 16 does bring it up to date, it's more modern than + + 17 '58D. + + 18 But, I've already suggested some changes. + + 19 One: The F limit needs to be extended so + + 20 that the lake and the river are balanced better in a + + 21 high-water event. + + 22 Number two: I think the board should have + + 23 discretion to deviate if we have a good forecast. + + 24 And we do probabilistic forecasts that go out + + 25 six months. + + + + + + + + 174 + 1 But, you know, in a month forecast, on six + + 2 weeks, if we have a good forecast, where we're going + + 3 to see the trigger level, then maybe we should be + + 4 able to deviate at that point, instead of waiting + + 5 till we actually get above it. + + 6 And, in addition, I think the trigger levels + + 7 are set too high, and I had pointed that out in the + + 8 hearings. + + 9 [Applause.] + + 10 FRANK SCIREMAMMANO, JR.: On the upper end, + + 11 they're set at the 2 percent level. At the bottom, + + 12 95 percent. + + 13 You see a bias right there against high + + 14 water. + + 15 And, when we pointed that out at the + + 16 hearings, the answer I got is: Well, we tried + + 17 5 percent, we tried 1 percent, we tried... + + 18 But, the group, the secret working group, + + 19 didn't feel the environmental benefits were adequate + + 20 then. + + 21 So my question was: Who was at the table + + 22 saying that the damages were too much with the + + 23 2 percent? + + 24 Nobody. + + 25 ASSEMBLYMAN OAKS: Thank you. + + + + + + + + 175 + 1 [Technical difficulties.] + + 2 [No audio or video.] + + 3 FRANK SCIREMAMMANO, JR.: [No audio or + + 4 video]... A year earlier, the level was exactly the + + 5 same at the end of the winter. + + 6 The board took no action at that point. + + 7 We looked at the snowpack. + + 8 We looked at what had been happening, in + + 9 terms of the precipitation figures, and whatever + + 10 outlooks we could find. + + 11 We decided in 2016 not to take any action. + + 12 In 2017, there was really no discussion + + 13 because we didn't have that authority. + + 14 So it's hard to say what the board would have + + 15 done at that point. + + 16 But, no, that was not an unusual -- it was + + 17 not unusual to be about 11 inches above average, + + 18 given the warm, wet winter. We had very little + + 19 snowpack. + + 20 So, the anticipation was, that all that + + 21 runoff that we would normally get in April, say, was + + 22 already in the lake. + + 23 SENATOR O'MARA: What -- can you talk a + + 24 little bit about Lake Erie levels, and that's been + + 25 high for several years, and that's impact on what + + + + + + + + 176 + 1 we're dealing with here on Lake Ontario? + + 2 FRANK SCIREMAMMANO, JR.: Yeah, I can't give + + 3 you the exact figures. + + 4 It was high, but it wasn't unusually high. + + 5 And the supply from Lake Erie, while above average, + + 6 was nothing that we were super-concerned about, + + 7 because it wasn't that much above average. + + 8 And, again, that's just the kind of action, + + 9 that we're prohibited from taking any action at that + + 10 point. Even if we saw Lake Erie very, very high, + + 11 we're not allowed to do anything until we hit the + + 12 trigger level. + + 13 SENATOR O'MARA: Yes. + + 14 Again, back to these negotiations, just to + + 15 clarify for me: + + 16 So they started around 2008. + + 17 What was the end time frame of that -- + + 18 FRANK SCIREMAMMANO, JR.: You know, I don't + + 19 know for sure. It was all secret. + + 20 We had no way of even finding out they were + + 21 meeting, and who was on the group. + + 22 So I would ask the IJC these questions. Or, + + 23 the State of New York, because they participated + + 24 fully on it. They had representatives on this + + 25 negotiating team. + + + + + + + + 177 + 1 I can't give you precise dates. + + 2 It's roughly that time frame, roughly, 2008, + + 3 2009, ending up around 2012, '13. + + 4 SENATOR O'MARA: Thank you. + + 5 We definitely should have had you testify + + 6 earlier. + + 7 However, we hope you will be at our next + + 8 forum. + + 9 FRANK SCIREMAMMANO, JR.: Anytime. + + 10 I appreciate the opportunity, and I thank you + + 11 for taking up this issue, and I hope you will follow + + 12 up on it. + + 13 [Applause.] + + 14 SENATOR O'MARA: Next we have Mary Austerman + + 15 from the New York Sea Grant, Wayne County + + 16 Cooperative Extension. + + 17 MARY AUSTERMAN: Good evening. + + 18 My name is Mary Austerman, and I'm the + + 19 coastal community development specialist for + + 20 New York Sea Grant. + + 21 I would like the thank the Senate Standing + + 22 Committee on Environmental Conservation, + + 23 particularly Senators O'Mara and Ritchie, for + + 24 the invitation to testify on the impacts of the + + 25 2017 Lake Ontario record high water level event. + + + + + + + + 178 + 1 New York Sea Grant is one of 33 state + + 2 sea-grant programs in the United States, and is a + + 3 cooperative program of the State University of + + 4 New York, Cornell University, and NOAA. + + 5 As an extension professional with New York + + 6 Sea Grant, it is my job to provide science to my + + 7 stakeholders that will allow them to make better + + 8 informed decisions. + + 9 My comments today are intended to provide + + 10 preliminary results from the 2017 Lake Ontario + + 11 High Water Level Impact Survey coordinated by + + 12 Cornell University and New York Sea Grant. + + 13 This survey was developed in response to + + 14 stakeholders' requests for standardized impact + + 15 reporting. + + 16 New York Sea Grant awarded funding to + + 17 Drs. Scott Steinschneider and Richard Stedman at + + 18 Cornell University to develop and implement a high + + 19 water level impact survey. + + 20 The survey was live from May 26th until + + 21 August 31st of this year. + + 22 Various outreach methods were used to + + 23 advertise the survey. These included newspaper, + + 24 social media, TV, radio, flyers, municipal e-lists, + + 25 and agenda time at related meetings. + + + + + + + + 179 + 1 The goals of the survey were documenting the + + 2 parcel-level impacts of the event on waterfront + + 3 properties, providing municipalities with + + 4 information that can assist them in community-based + + 5 planning to reduce flood risks, and verifying + + 6 existing flood-risk modeling. + + 7 The survey targeted all waterfront + + 8 properties. + + 9 We collected qualitative data about parcel + + 10 location, severity of inundation, severity of the + + 11 erosion, damage to shoreline protection, flood + + 12 insurance, business impacts, and severity of overall + + 13 impacts. + + 14 In addition, pictures of the waterline, and + + 15 of property damage, were provide by respondents. + + 16 This survey did not collect economic data + + 17 because many impacts would not be known until the + + 18 water levels recede, and inundation reports during + + 19 peak or near peak water levels are necessary to + + 20 verify existing flood-risk modeling. + + 21 In a moment I'm going review some of the + + 22 preliminary results from the survey. + + 23 And as I go through those, I'd just like to + + 24 you please keep in mind that we did conduct a + + 25 separate St. Lawrence River survey. Those data have + + + + + + + + 180 + 1 not been analyzed at this point. + + 2 Although this survey was available for all + + 3 waterfront properties, it is possible that those who + + 4 experienced impacts were more likely to respond than + + 5 those who did not. + + 6 These results have not been published. + + 7 This report out is on preliminary analyses. + + 8 This report out is in percentages, and + + 9 removed all instances of "does not apply" or + + 10 "I don't know" before calculating those percentages. + + 11 I also would like to note that these results + + 12 were provided by my survey partner, + + 13 Dr. Scott Steinschneider. + + 14 So what I'll do is, you all have these + + 15 graphics in the testimony that was provided. But + + 16 I do want to also put some slides up so everyone in + + 17 the audience can see what we're talking about. + + 18 [Slide show begins.] + + 19 MARY AUSTERMAN: This first graphic is a + + 20 distribution map showing our response. + + 21 I will say that the map does show all + + 22 507 "true" responses. + + 23 We did receive almost 900 responses in the + + 24 Qualtrics online database, but many of those were + + 25 screened out. + + + + + + + + 181 + 1 Some of those reasons were: Because they + + 2 were not on the lake itself. Some of those were + + 3 St. Lawrence and Canada. They were incomplete, or + + 4 duplicates. + + 5 Some of these preliminary results that I can + + 6 share with you, again, these are in percentages, and + + 7 first we'll look at the inundation of different + + 8 areas on the property. + + 9 And so if you'll look here, you'll see that + + 10 this is this near-shore area. Almost 100 percent of + + 11 survey respondents indicated that they were + + 12 experiencing inundation in those near-shore areas. + + 13 I would like to note that fewer respondents + + 14 reported experiencing inundation on their first + + 15 floor; however, many, nearly 50 percent, were still + + 16 experiencing inundation impacts to their foundation. + + 17 The next graph shows erosion damage to + + 18 different aspects of the property again. + + 19 And I know this is a little confusing to look + + 20 at. + + 21 So some of the take-homes on this: + + 22 If you look over here to the left, this is + + 23 "no impact." + + 24 So here you can see that 71 percent of people + + 25 were responding that they did not have + + + + + + + + 182 + 1 erosional-damage impact to their main structure. + + 2 Over here, in the most severe impacted areas, + + 3 as you can imagine, most of the erosion damage that + + 4 was experienced is in the shore areas. + + 5 But I would also mention that, here, we still + + 6 do have impacts as well in the small- and + + 7 moderate-impact categories. + + 8 The next thing I'd like to show you is land + + 9 loss from erosion. Again, this is percentages. + + 10 And the take-home here is that over + + 11 80 percent of people were reporting at least some + + 12 degree of land loss from erosion. + + 13 The next slide is showing the damage to + + 14 shoreline protective features, and this is + + 15 specifically talking about either sloping or + + 16 vertical walls. + + 17 And, again, you can see that, of the + + 18 respondents that responded that they did have these + + 19 types of structures, nearly 90 percent were + + 20 reporting some type of impact to those structures. + + 21 And one of the questions I always ask in + + 22 these types of survey are: Taking the full event + + 23 into account, what is your perception of the overall + + 24 impact? + + 25 And as you can see, right here: + + + + + + + + 183 + 1 From 7 to 10 being the most extreme. + + 2 So, 1 is no impact, 10 is substantial. + + 3 Over 50 percent of respondents indicated that + + 4 the severity level was 7 or greater. + + 5 I also mentioned that we collected pictures + + 6 from survey respondents, and I will share some of + + 7 those with you. + + 8 And, again, we were looking at the impacts + + 9 of -- well, we were looking at the location of the + + 10 waterline on the property, the overall impact of + + 11 water on the property. + + 12 And these images are being used for + + 13 validation of an existing flood-risk model, and that + + 14 work will begin in 2018 with Sea Grant funding to, + + 15 again, Drs. Steinschneider and Stedman. + + 16 We'll also be archiving these photos and + + 17 making them available. And this is only 6 of over + + 18 500 responses. + + 19 The lighting in here is not great, so I'll + + 20 try to explain what's happening here. + + 21 So this red area here is where the + + 22 property -- the property line. This is the lake. + + 23 And these pictures are showing where each of + + 24 these features on the property are located. + + 25 This is in Orleans County, the end of May. + + + + + + + + 184 + 1 And you can see that they are experiencing + + 2 inundation impacts to a lot of those near-shore + + 3 areas that was described earlier in the + + 4 presentation. + + 5 Again, in Orleans County, this here is also + + 6 water. That this is lakefront. + + 7 You can see on the left, they're experiencing + + 8 wave action on a wall. + + 9 And, here, this is pretty inland, and you can + + 10 see inundation that far back, from here to here. + + 11 In Monroe County, again, here is where the + + 12 property is located. + + 13 On this particular day in May, there was + + 14 inundation back as far as this location, and here as + + 15 well. + + 16 Moving east to Wayne County, again, property + + 17 location. Water from the bay or lake. + + 18 This is from the lake. + + 19 And you can see again, these are the + + 20 locations of these images, and we have inundation + + 21 impacts, at least foundational impacts as well. + + 22 Again, in Wayne County, Sodus Point, we have + + 23 foundation inundation. Potentially, structural + + 24 inundation. And, again, location of images on those + + 25 property -- on that piece of property. + + + + + + + + 185 + 1 Oswego County, beginning of May, the images + + 2 were taken here on the shoreline, and here inland. + + 3 And you can see here, this is inundated. + + 4 This is a structure, and the water is at + + 5 least at the foundation, if not in the structure, + + 6 and threatening this wall. + + 7 So just to -- I'd like to just mention a few + + 8 of the uses of this data. + + 9 Some immediate uses include: + + 10 Documenting this record high water level + + 11 event, providing standardized results that will + + 12 allow for lake-wide reporting; + + 13 Identifying areas that are most vulnerable to + + 14 high water levels for future community-based + + 15 planning; + + 16 Validating an existing flood-risk model that + + 17 could inform community-level flood-resiliency + + 18 planning, which, again, initial steps are underway + + 19 with that, with the 2018 Sea Grant-funded project; + + 20 And assisting in the leveraging of + + 21 competitive funds for making communities, + + 22 businesses, and private landowners more adaptable to + + 23 high water levels. + + 24 As an extension associate representing + + 25 New York Sea Grant and Cornell University, it is my + + + + + + + + 186 + 1 job to provide science-based information so + + 2 stakeholders can make better informed decisions; in + + 3 this case, about reducing risks to future coastal + + 4 flood events. + + 5 I'm grateful for this opportunity to share + + 6 these results with you today, and am hopeful that + + 7 they will empower communities to begin improving + + 8 their coastal-flood resiliency. + + 9 Thank you. + + 10 SENATOR O'MARA: Go ahead, Bob. + + 11 ASSEMBLYMAN OAKS: You mentioned that this + + 12 does not include economic impact to this point. + + 13 Is that within the plans, or not? + + 14 Not really? + + 15 MARY AUSTERMAN: It's not within our current + + 16 scope. + + 17 One, this was -- this happened quick, and so + + 18 we had to get money out, to get the survey out + + 19 quickly. + + 20 We had to go through institutional review + + 21 board approval to conduct the survey, which also + + 22 takes time. + + 23 And to include an economic component to that, + + 24 we'd have to also come up with a way to validate + + 25 those figures. + + + + + + + + 187 + 1 And in the interests of time, and also the + + 2 expertise that we had on our limited team, we just + + 3 couldn't do it at that point. + + 4 ASSEMBLYMAN OAKS: I was asking that as + + 5 much -- I know we -- the Governor put in for FEMA + + 6 disaster declaration, and that is economic-based. + + 7 You have to meet thresholds, and -- and -- you know. + + 8 So I was wondering if those might be + + 9 connected at all. + + 10 The other thing you showed, 506 properties. + + 11 Do you have any sense how many properties + + 12 there are along there, or what percentage you are, + + 13 you know, dealing with, in showing this survey? + + 14 MARY AUSTERMAN: That is a good question. + + 15 That's one of the ones that + + 16 Dr. Scott Steinschneider and I have been talking + + 17 about. And he's working on analysis at this point, + + 18 to see what percentage of folks were actually + + 19 reached and responded. + + 20 ASSEMBLYMAN OAKS: The number of people that + + 21 had in -- in your analysis, do you feel like -- you + + 22 said you did reject some -- + + 23 MARY AUSTERMAN: Uh-huh. + + 24 ASSEMBLYMAN OAKS: -- because they weren't + + 25 appropriate, or something. + + + + + + + + 188 + 1 So were these on -- if you were on a bay, + + 2 would have you -- off Lake Ontario, would you have + + 3 been included, or no? + + 4 MARY AUSTERMAN: Yes, waterfront. + + 5 So, bay communities were included. + + 6 Some of the examples of screen-out responses + + 7 were either: + + 8 Not complete; + + 9 Some were clicked-through; + + 10 Some people stopped at the initial upload of + + 11 photos, so none of the other information was + + 12 collected; + + 13 We did have a couple of Canadian responses, + + 14 and we had some St. Lawrence responses as well. + + 15 But we did collect bay responses. + + 16 ASSEMBLYMAN OAKS: Your sense -- would it be + + 17 your sense that it's enough of a representative + + 18 group that, perhaps, those doing the study might be + + 19 able to project that out, showing, you know, + + 20 complete impact, or, you know, of the whole south + + 21 and east shore? + + 22 MARY AUSTERMAN: Well, again, when we started + + 23 this, we weren't sure what the response rate would + + 24 be. + + 25 One reason, knowing that it was an extremely + + + + + + + + 189 + 1 challenging time for those waterfront property + + 2 owners, so we didn't know if people would even take + + 3 the time to respond to the survey. + + 4 We're happy with the results that we got, the + + 5 response number that we got. + + 6 I can't really -- I can't really provide an + + 7 answer, if someone could make the jump from what + + 8 we've collected to putting economic dollars to that. + + 9 But the data is available. + + 10 ASSEMBLYMAN OAKS: And did you do any + + 11 connection, or will there be any, with -- we've been + + 12 told there were 3400 people that made application to + + 13 the State. + + 14 Do you know what -- you know, will there be + + 15 any connection between the properties that you + + 16 researched and those 3400 who might have made + + 17 application? + + 18 MARY AUSTERMAN: Well, our survey rolled out + + 19 before that funding opportunity was available. + + 20 It would have been good to put in a question + + 21 in there, to ask if people were applying for those + + 22 funds. + + 23 But our survey was rolled out first, so + + 24 I don't know if there's a way to make that + + 25 connection. + + + + + + + + 190 + 1 ASSEMBLYMAN OAKS: Thank you. + + 2 SENATOR O'MARA: All set. + + 3 Thank you very much for your work. + + 4 MARY AUSTERMAN: Thank you. + + 5 [Applause.] + + 6 SENATOR O'MARA: Next up is Dale Currier, + + 7 director of Oswego County Emergency Management. + + 8 DALE CURRIER: Can you hear me? + + 9 SENATOR O'MARA: Maybe a little closer. + + 10 DALE CURRIER: A little closer. + + 11 How's that? + + 12 SENATOR O'MARA: That's better. + + 13 DALE CURRIER: Can you guys hear me? + + 14 All right. Just don't throw anything sharp + + 15 or pointed. + + 16 All right. + + 17 Good evening. + + 18 My name is Dale Currier, and I'm the director + + 19 of emergency management for Oswego County. + + 20 First, I'd like to thank Senator O'Mara and + + 21 Senator Ritchie for the invitation to present + + 22 tonight, as well as thank Assemblymen Oaks and + + 23 Barclay for your time and interest in this panel, + + 24 and this event. + + 25 This is a topic of utmost importance to + + + + + + + + 191 + 1 thousands of Oswego County residents whose daily way + + 2 of life, livelihoods, standard of living, and + + 3 properties, both personal and business and + + 4 municipal, have been severely impacted. + + 5 This community-wide impact from high water + + 6 levels has continued for the past six-plus months, + + 7 and despite the best efforts to reduce the lake + + 8 level to normal levels, will likely continue for + + 9 some time to come. + + 10 There are many who are still experiencing + + 11 damage, increasing damage, particularly when the + + 12 high winds on Lake Ontario blow from the north and + + 13 the northwest. + + 14 I am not here today to discuss the merits or + + 15 deficiencies of the Plan 2014. This is an issue to + + 16 be dealt with by policymakers. + + 17 Instead, I do speak as an + + 18 emergency-management professional with + + 19 responsibility for over 120,000 residents of + + 20 Oswego County, of which 25 percent of our area is + + 21 water, most of which is Lake Ontario and its + + 22 tributaries. + + 23 As such, much of our county has been built + + 24 and developed around water-based businesses and + + 25 industries that rely on having a consistent source + + + + + + + + 192 + 1 and a consistent level of water. + + 2 I believe it's important to note that the + + 3 impacts and damage, whether directly through erosion + + 4 and structural damage, or indirectly through the + + 5 degradation and outright destruction of businesses + + 6 that rely on people coming from around the world for + + 7 recreation, vacations, and summer residents. + + 8 Since mid-May, I and many other first + + 9 responders have experienced this flood, at times, + + 10 7 days a week, 16 hours a day, which pales compared + + 11 to some of the people who have been directly + + 12 impacted in their homes. + + 13 But I could spend hours sharing the + + 14 experiences we've had standing in knee-deep water in + + 15 someone's front yard, as they wonder, will local + + 16 emergency services be able to get to their home, as + + 17 they're giving care to an elderly resident with a + + 18 terminal illness? + + 19 And they don't want to leave their home, the + + 20 home they've worked their entire life for, and they + + 21 have no local family. + + 22 I recently, during the last storm, stood on + + 23 the shore and watched 10-foot waves throw + + 24 football-sized rocks 20 to 30 feet past the + + 25 breakwall into the side of a home. + + + + + + + + 193 + 1 And I've sat and listened patiently while + + 2 people shared their most personal, financial, and + + 3 family situations regarding their business, that + + 4 cannot function, or could not function, because of + + 5 the high water. They felt totally helpless, and at + + 6 the mercy of what they believed initially to be + + 7 solely the result of Plan 2014. + + 8 Regardless of the underlying initiating cause + + 9 or causes of the incredibly high water, the outcome + + 10 has been the same: Severe economic damage which + + 11 will impact many people's lives for decades to come, + + 12 which, for some, is the rest of their natural life. + + 13 And always they ask the question, "Will it + + 14 come again?" + + 15 You know, in the interest of time, and + + 16 previous presenters, I'm cutting out some of my + + 17 presentation. So, if a few of my notes get a little + + 18 cryptic here, please bear with me. + + 19 From what we've heard, it's readily apparent + + 20 that we can't expect to effectively and safely drain + + 21 this water system when it's overloaded. + + 22 It's analogous to draining a full bathtub + + 23 through that a straw when the faucet is still on. + + 24 Furthermore, artificially introducing more + + 25 water into the system will only serve to cause more + + + + + + + + 194 + 1 damage elsewhere in the system. + + 2 I believe, by definition, what we've + + 3 experienced is the truest form of a dilemma related + + 4 to a natural disaster. + + 5 There's no one answer or solution to the + + 6 myriad problems, and everyone involved is going to + + 7 lose something, hence I believe only time will tell + + 8 if Plan 2014 was, in fact, a significant + + 9 contributing factor for this event. + + 10 To me, however, the most important part of + + 11 these hearings, at least from the perspective of the + + 12 persons impacted, is to identify, how do they get + + 13 help? + + 14 We have heard some information tonight. + + 15 But in terms of grants and low-interest loans + + 16 to repair or replace what has been lost, time is of + + 17 the essence. + + 18 Oswego County's industrial tax base, compared + + 19 to the amount of property that is agricultural or + + 20 tax-free, is not like some other areas that have + + 21 been impacted by this same flooding. + + 22 Living on or near the water is not generally + + 23 a sign of wealth in Oswego County. + + 24 Instead, it's often the result of a + + 25 residence, business, or piece of waterfront property + + + + + + + + 195 + 1 being passed through many generations of a family. + + 2 Numerous people have shared with me that they + + 3 retain the property as best they can, paying the + + 4 cost of the taxes on waterfront property, out of a + + 5 sense of family obligation to continue on with it. + + 6 Many have said this may well be the + + 7 proverbial straw that broke the camel's back, as + + 8 they cannot afford to pay ten, twenty, and, in some + + 9 cases, fifty to sixty thousand dollars, to restore + + 10 their frontage to a level that would protect them + + 11 from this type of event in the future. + + 12 So let's look at the early response. + + 13 New York State did very well. + + 14 We heard from DEC. + + 15 We know Homeland Security, Office of + + 16 Emergency Management, worked well with sandbagging. + + 17 The national guard and the DOT stepped up to + + 18 the plate, to almost 60,000 sandbags here in Oswego + + 19 County. + + 20 Local first responders, coast guardsmen, + + 21 stepped up to help people who couldn't place + + 22 sandbags, to take care of that. + + 23 But we still have a lot of damage. + + 24 I believe in Oswego County the figure now is + + 25 in the tens of millions of dollars, with significant + + + + + + + + 196 + 1 damage to the Port of Oswego, not to mention the + + 2 business losses incurred throughout the county west + + 3 of Route 81, and the damage to residential + + 4 properties. + + 5 Looking ahead to recovery and mitigation, + + 6 I believe the picture is more dire. + + 7 This is a seven-month flood. + + 8 Yes, New York State did appropriate + + 9 $45 million for relief; however, accessing this + + 10 funding has been a struggle for many people needing + + 11 to make repairs before winter sets in. + + 12 And many people -- in many cases, people have + + 13 had to borrow money to hire engineers and pay for + + 14 stopgap property remediation to prevent further + + 15 damage caused by the winter storms that will soon be + + 16 upon us. + + 17 In many cases, people tell me they cannot + + 18 start remediation until the water recedes and they + + 19 can see the true extent of their damage. + + 20 New York State, as noted, has requested a + + 21 Presidential disaster declaration back in August. + + 22 Shortly thereafter, we know the southern -- + + 23 or, the southern U.S., most notably, Florida and + + 24 Texas, took a major pounding with hurricanes, and + + 25 other hurricanes have taken other resources and time + + + + + + + + 197 + 1 from FEMA. + + 2 Despite reassurances from federal officials, + + 3 however, that financial support for our area is + + 4 being worked on, no tangible relief has been + + 5 received yet from them. + + 6 I believe personally, and professionally, the + + 7 Lake Ontario flood will go down in the history of + + 8 modern emergency management as, and I quote, the + + 9 huge seven-month flood that only the locals knew + + 10 about, end quote. + + 11 Therefore, I respectfully ask this panel and + + 12 all who are witness to these proceedings to move + + 13 forward on getting financial funding now -- this + + 14 week, if possible -- so more people can begin + + 15 rebuilding. + + 16 With immediate funding, it's likely much + + 17 rebuilding will be completed a year from now, yet + + 18 some large projects will take longer. + + 19 I'd like to end with an old adage from my + + 20 days as a corporate trainer. + + 21 When often asked, quote, "What if we spend + + 22 money to train people, and then they leave us?" my + + 23 general response was, "What if we don't train them, + + 24 and they stay? What will that cost us?" + + 25 Along the same vein I ask, "How much are the + + + + + + + + 198 + 1 people of Oswego County, and all eight impacted + + 2 counties along Lake Ontario, going to continue to + + 3 lose if they cannot afford to fix the current damage + + 4 before more happens?" + + 5 The answer, I believe, is that providing the + + 6 needed money now will go a long way to reducing the + + 7 future costs of reduced property taxes collected, + + 8 lost business, and the trickle-down effect that will + + 9 follow, such as lost sales tax, et cetera. + + 10 With continued inaction, the true costs will + + 11 only be staggering, but will last for a decade or + + 12 more. + + 13 Thank you for your invitation. + + 14 I trust this has been helpful. + + 15 Is there anything I can add? + + 16 SENATOR O'MARA: I'm sure. + + 17 ASSEMBLYMAN BARCLAY: I just want to say + + 18 thank you, Dale, for testifying today. + + 19 And, also, thanks for your office's courtesy + + 20 shown to my office through, really, the tough -- the + + 21 really tough parts of the flooding. And I know you + + 22 and I toured around, looking at some of the damage + + 23 that was done. + + 24 Maybe just a quick question, then. + + 25 If you could change, and now have gone + + + + + + + + 199 + 1 through this whole process, is there something that + + 2 you would say that we can improve upon, at a state + + 3 level or a local level, to provide relief to people, + + 4 especially in regards to emergency management? + + 5 DALE CURRIER: Well, right now, timing is + + 6 fortunate, in that we're at the five-year cycle of + + 7 redoing the county's hazard mitigation plan. + + 8 And, certainly, we have a very different + + 9 picture of lake flooding, coastal erosion, + + 10 et cetera, than we had when we did it seven years + + 11 ago. + + 12 So the good news is, we'll be working with + + 13 all of the towns and municipalities to update that + + 14 plan. + + 15 That said, to increase their resilience that + + 16 we've heard a lot about, to mitigate future damage, + + 17 it's great to come up with a plan. But as we know, + + 18 municipalities and individuals are strapped for + + 19 cash, and the plan is not going to accomplish much + + 20 unless funding is available, up front, to mitigate + + 21 this for the future. + + 22 ASSEMBLYMAN BARCLAY: Thanks. + + 23 SENATOR RITCHIE: Just more of a comment, + + 24 Dale. + + 25 I certainly understand your comment about + + + + + + + + 200 + 1 getting the funding out the door, and frustration + + 2 some of the people are dealing with. + + 3 And that is something that, pretty much, on a + + 4 daily basis, I've been calling to make sure that the + + 5 money is flowing. + + 6 With Friday's announcement that they don't + + 7 have to prioritize what applications are funding, my + + 8 hope is that many of those applications that are + + 9 further down the pile will begin to see some kind of + + 10 disbursements, going forward. + + 11 So, I would just ask, if you or anybody here + + 12 has an application in, it doesn't look like it's + + 13 moving, just please let our office know. + + 14 DALE CURRIER: Okay. + + 15 And to Will's question, looking forward, if + + 16 this were to happen again, it's taken seven + + 17 months -- and I realize bureaucracy can take time -- + + 18 but it's taken seven months to get to this point. + + 19 And I can safely say that, probably, four + + 20 weeks in, a lot of people had a lot of ideas as to + + 21 how much this was, potentially, going to cost. + + 22 So if I were going to suggest something + + 23 different if this happened in the future, is get + + 24 those pots of money, get that process going, much, + + 25 much sooner, because you know it's going to be + + + + + + + + 201 + 1 needed. + + 2 SENATOR O'MARA: This may be outside your + + 3 area in the county, but, do you have any sense of + + 4 what the impact to sales-tax collections in the + + 5 county has been impacted as a result of that? + + 6 DALE CURRIER: Yeah, I have no idea, and I've + + 7 not heard any numbers on that. + + 8 If you'd like, I'll see if I can find out. + + 9 I don't know if it may be a little too early, + + 10 at the summer season, to know. + + 11 SENATOR O'MARA: Thank you. + + 12 DALE CURRIER: Thank you. + + 13 [Applause.] + + 14 SENATOR O'MARA: Next up, Gary DeYoung, + + 15 director of tourism, Thousand Islands International + + 16 Tourism Council. + + 17 GARY DeYOUNG: Thank you. + + 18 The Thousand Islands International Tourism + + 19 Council is the destination marketing organization. + + 20 We play an official role in Jefferson County. + + 21 We also actively collaborate with the tourism + + 22 agencies in Oswego and St. Lawrence county. + + 23 And by the word "International" in our title, + + 24 we represent and market the Ontario side of the + + 25 St. Lawrence as well in the Thousand Islands region. + + + + + + + + 202 + 1 Today's topic is particularly important for + + 2 tourism. + + 3 Lake Ontario and the St. Lawrence River are + + 4 at the very core of the tourist industry in the + + 5 Thousand Islands. + + 6 Much of the region's tourism is based on + + 7 water activity, such as boating, paddling, fishing, + + 8 and scuba diving. + + 9 Waterfront attractions, including scenic boat + + 10 tours, lighthouses, museums, and island castles are + + 11 important parts of drawing visitors to the Thousand + + 12 Islands, and the waterfront accommodations from + + 13 campsites to four-star hotels differentiate our + + 14 destination from others. + + 15 This year's wet, dreary spring and early + + 16 summer really resulted in a bad impact on this + + 17 region's tourism business. + + 18 In early September, the Thousand Islands + + 19 International Tourism Council conducted a survey + + 20 regarding the high-water impacts on business. + + 21 With 109 stakeholders responding, 47 percent + + 22 indicated a very negative impact, 35 percent + + 23 indicated a somewhat negative impact. + + 24 So on the negative side, 80 percent of our + + 25 responses came in. + + + + + + + + 203 + 1 The average decline in business from normal, + + 2 of 29 percent, on average, with many being well over + + 3 50 percent. + + 4 The wet weather and high water forced some + + 5 attractions to open late or to curtail operations as + + 6 they scrambled to make adaptations. + + 7 It created huge problems for marine + + 8 businesses. + + 9 It impacted the region's tourism in many + + 10 ways, and at the bottom line, it simply kept + + 11 visitors away during the crucial summer season. + + 12 I want to touch on a few points that are + + 13 sometimes lost in the conversation about tourism and + + 14 water levels. + + 15 First, it's important to understand that the + + 16 impacts of the weather and water go well beyond + + 17 those that fell directly on the shore. + + 18 With waterfront activity curtailed, lodging + + 19 operators, attractions, restaurants, and retailers + + 20 in tourism-sensitive communities all lost business. + + 21 With those -- with waterfront had to deal + + 22 with damages, but businesses a few blocks away, or + + 23 even a few miles away, also suffered substantial + + 24 losses. + + 25 Secondly, for tourism, perception is often + + + + + + + + 204 + 1 reality. + + 2 If a customer believes an area is flooded and + + 3 cancels a trip, the businesses lose money even if + + 4 they were, in reality, not flooded and fully + + 5 operational. + + 6 While waterfront businesses in the region + + 7 took hard hits from the water inundation, many were + + 8 able to make adaptations and continue operations, + + 9 and others near the waterfront had little or no + + 10 physical impact on their operations; however, + + 11 anecdotal stories of customer misperceptions about + + 12 the conditions abound. + + 13 The tourism council saw this trend early in + + 14 the summer season, and allocated some of its + + 15 rainy-day funds -- pun intended -- to bolster a + + 16 social-media program to defeat some of the things + + 17 that were out there. + + 18 And the council has also partnered with the + + 19 tourism agencies in Oswego and St. Lawrence counties + + 20 to submit a Market New York grant application, which + + 21 we hope will be successful, and allow to us market + + 22 Thousand Islands region waterfront activity more + + 23 aggressively next year. + + 24 Third, one aspect of the Thousand Islands is + + 25 quite different from many other areas of the state. + + + + + + + + 205 + 1 It's a vacation-home destination. + + 2 According to the U.S. census, Jefferson + + 3 County has 10,800 vacation homes, representing about + + 4 19 percent of its housing stock. + + 5 In total, Jefferson, St. Lawrence, and Oswego + + 6 county have 22,000 vacation homes, while the + + 7 Lake Ontario south-shore counties have about 5700 in + + 8 total. + + 9 But across the area, we're talking about, + + 10 that's 27,000 vacation homes. + + 11 The majority of these homes are on the + + 12 waterfront. + + 13 They boasted the property-tax base in + + 14 shoreline communities, some of which actually have + + 15 more seasonal homes than permanent homes. + + 16 They usually support a wide range of + + 17 businesses, and contribute significantly to both + + 18 employment and local sales income. + + 19 This year, much of that activity was + + 20 curtailed due to a variety of impacts from the high + + 21 water. + + 22 Despite this year's weather challenge, + + 23 tourism trends in the region are strong. + + 24 Our 2016 survey of businesses showed the + + 25 highest level of business confidence in 20 years. + + + + + + + + 206 + 1 Response to our marketing has indicated + + 2 strong visitor interest, even in 2017. + + 3 And the results from the late summer, after + + 4 the water went down, seem to be bearing out the fact + + 5 that we can have a good solid season absent the + + 6 water. + + 7 Attraction attendance and hotel occupancy + + 8 have been growing steadily. + + 9 New tourism-based businesses are opening, and + + 10 established operators are making fresh investments. + + 11 The region's craft beverage and egg tourism + + 12 businesses have expanded, helping to extend the + + 13 traditional season well into the fall. + + 14 For example, between 2010 and 2016, + + 15 annualized employment in Jefferson County's + + 16 hospitality businesses grew, from 3880 jobs, to + + 17 4,346 jobs. + + 18 That's 466 new North Country jobs, with an + + 19 additional $17 million in annual wages. + + 20 More than a decade ago, I served on the + + 21 technical workgroup, looking at management plan's + + 22 impacts on recreational boating and tourism. + + 23 At the time, the existing plan did not + + 24 formally take into account recreational boating, nor + + 25 any other impacts of water levels on tourism. + + + + + + + + 207 + 1 Essentially, people who depend upon + + 2 water-based tourism in the region were not part of + + 3 the formula at that -- in the 1958 formula. + + 4 A couple of things came out of that technical + + 5 workgroup that I want to point out. + + 6 They did a study of recreational boating on + + 7 Lake Ontario and the St. Lawrence, and, in 2002, + + 8 estimated that $429.7 million was spent on + + 9 recreational boating trips in the basin. + + 10 They also found that the greatest incremental + + 11 gains to recreational boating, when you talked about + + 12 all those plans, were if higher water levels were + + 13 achieved in the fall. + + 14 So there's a couple takeaways from that + + 15 workgroup that I think are still valid today. + + 16 First, there's real economic impact from + + 17 tourism and recreation to consider in this + + 18 conversation. + + 19 The plan limits tourism considerations to + + 20 recreational boating. The vacation-home values and + + 21 trade waterfront businesses are impacted by the + + 22 management plan as well. + + 23 Second, much of the spending tracked in 2002 + + 24 was concentrated in smaller communities. And water + + 25 levels have critical impacts on those small resort + + + + + + + + 208 + 1 communities which have water-based tourism as their + + 2 core economic driver; that is to say, the + + 3 concentration of dollar losses in small communities + + 4 creates much more stress on that community than in + + 5 communities with larger populations and more diverse + + 6 economies. + + 7 Third, although we're dealing with high water + + 8 this year, the most negative impacts to boating and + + 9 tourism over time have been due to low water in the + + 10 fall. + + 11 In several recent years, every low water -- + + 12 early low water has curtailed boating activity, and + + 13 led to closing of seasonal homes, and loss of + + 14 businesses at marinas. + + 15 What can the State do? + + 16 Please recognize that tourism is an important + + 17 and growing part of a fragile North Country economy. + + 18 It depends heavily on the lake and the river. + + 19 Tourism's overall use of the water system + + 20 should be a significant consideration formulating + + 21 policies about the system's management. + + 22 Please recognize that our reputation as a + + 23 desirable waterfront destination is one of our most + + 24 precious assets. + + 25 Simply put, please don't scare off the + + + + + + + + 209 + 1 tourists when a flood is happening. + + 2 And please keep in mind that different areas + + 3 have different needs. Craft policies with the + + 4 flexibility to accommodate those needs. + + 5 For instance, the 5-mile-per-hour, no-wake, + + 6 boating rule stayed into place long after waters had + + 7 dropped on the St. Lawrence River. + + 8 And within 600 feet on the St. Lawrence + + 9 River, it's very difficult to navigate at 5 miles an + + 10 hour. + + 11 So, although that may -- it made sense + + 12 with -- as an abundance of caution in some areas, we + + 13 ended up finding frustrated boaters who were being + + 14 ticketed for normal operations of their boat under + + 15 normal conditions on the river. + + 16 Thank you for your time. + + 17 SENATOR RITCHIE: Just one question, Gary. + + 18 Do you think something has to be done as far + + 19 as marketing early on this spring so that + + 20 individuals know that it's okay to come back? + + 21 GARY DeYOUNG: Right. + + 22 We applied for that grant, and the idea is, + + 23 we'd get out on social media and really tell + + 24 individual stories about how individual businesses + + 25 have recovered, or how experiences can still be + + + + + + + + 210 + 1 enjoyed even though you may have heard differently. + + 2 But to be candid, if the State can help. + + 3 After "Superstorm Sandy," there were special + + 4 federal funds that were heavily spent by the State + + 5 to promote Long Island, the Adirondacks, and the + + 6 Catskill regions. + + 7 I haven't heard any kind of plans to make a + + 8 special focus on the Lake Ontario counties in the + + 9 wake of this flood. + + 10 SENATOR RITCHIE: Thank you. + + 11 SENATOR O'MARA: A couple of questions. + + 12 Have you heard from your member businesses, + + 13 any specific or generalized complaints about the + + 14 process of the business going after grants? + + 15 You know, I've heard a lot of anecdotal + + 16 issues on that, but, what have you been hearing from + + 17 your members about that process? + + 18 GARY DeYOUNG: I haven't heard that. + + 19 And, you know, we did the survey, and the + + 20 survey has, literally, dozens of comments about what + + 21 they experienced specifically as a business, and how + + 22 they were coping with it. But we didn't ask about + + 23 if they were having any success with getting the + + 24 grants. + + 25 SENATOR O'MARA: Where were your -- where was + + + + + + + + 211 + 1 the consensus of your membership on Plan 2014 before + + 2 it was implemented? + + 3 GARY DeYOUNG: Well, you know, there's a + + 4 difference, I think, from what I heard, and this is + + 5 just my anecdotal listening on the street, we depend + + 6 heavily on fishing and wildlife biology, you know. + + 7 So, when we were told that this new plan + + 8 would really help the fishery, I think it won a lot + + 9 of supporters. + + 10 Now, maybe some of the testimony today calls + + 11 that into question. + + 12 So I think, especially on the river, there + + 13 was a feeling that, if we got a little bit of extra + + 14 water in the fall, and then have those low-water + + 15 years, where people are hauling out at the end of + + 16 August and leaving town, and, if the fishery + + 17 improved, and we could get in the spring and really + + 18 attract more fishermen, that was a pretty good deal. + + 19 So I think there was a lot of hope for + + 20 Plan 2014. + + 21 SENATOR O'MARA: Thank you very much. + + 22 [Applause.] + + 23 SENATOR O'MARA: Next, Scott Aubertine, + + 24 supervisor of the town of Lyme. + + 25 SCOTT AUBERTINE: Senator O'Mara and Senator + + + + + + + + 212 + 1 Richie, Assemblymen Oaks and Barclay, thank you for + + 2 the invitation to speak here tonight. + + 3 Thank you for this opportunity to share the + + 4 thoughts and opinions of myself, and I believe those + + 5 of the majority of the residents of the town of + + 6 Lyme. + + 7 The town of Lyme is located in Jefferson + + 8 County. + + 9 The western side of our town is defined by + + 10 beautiful Lake Ontario. + + 11 The year-round population, according to the + + 12 2010 census, was a whopping 2,185, and more than + + 13 doubles in the summer due to a large seasonal + + 14 population. + + 15 We are comprised of the hamlet of Three Mile + + 16 Bay and the village of Chaumont, both located on + + 17 Chaumont Bay, which, at one time, we promoted as the + + 18 largest freshwater bay in the world. + + 19 We have since been argued at by residents of + + 20 Georgian Bay in Huron who says they are the largest. + + 21 So -- but we will gladly accept recognition + + 22 as the second or third largest fresh-water bay in + + 23 the world. + + 24 We are confident, however, that with over + + 25 55 miles of shoreline, the town of Lyme has the most + + + + + + + + 213 + 1 shoreline of any town in New York State. + + 2 Allow me to tell you a little about myself, + + 3 and, thereby, hopefully, expressing the thinking of + + 4 our town residents. + + 5 I have lived in the town of Lyme all my life. + + 6 In high school, and after, I hunted ducks and + + 7 geese regularly. + + 8 As I grew older and began raising a family, + + 9 I dedicated myself to serving the community and town + + 10 that I love and have lived in all my life. + + 11 I worked for five years for the Town of Lyme + + 12 Highway Department, and for the past 30 years, at + + 13 Township Telephone Company, the local telephone + + 14 company serving the towns of Lyme, Brownville, and + + 15 Cape Vincent, and the three islands of Carrollton, + + 16 Fox, and Grenadier. + + 17 I mention this to emphasize my experience in + + 18 having seen the extensive shorelines in those towns, + + 19 and my regular interaction with shoreline residents, + + 20 and our residents in general. + + 21 I have served on the Lyme Central School + + 22 Board, the Chaumont Village Board, my church + + 23 session, and now town supervisor for the last + + 24 10 years. + + 25 I feel I have a pretty good understanding of + + + + + + + + 214 + 1 the pulse of our town residents. + + 2 Having been a young boy in the '50s and + + 3 '60s, I was unaware of Plan 1958D. + + 4 I did not understand the purpose of the + + 5 Moses-Saunders Dam until a middle school field trip + + 6 there in the late '60s. + + 7 However, as a senior in 1973, which I think + + 8 was the year of the previous high-water record for + + 9 Lake Ontario, I worked several weekends helping + + 10 shoreline residents fill sandbags. + + 11 From those days, on, I paid attention to some + + 12 of the comments of long-time residents and the many + + 13 commercial fishermen we had living and working in + + 14 our town. + + 15 I'm not sure how many people were familiar + + 16 with Plan 1958D, but I can assure you that when + + 17 water levels changed noticeably, everyone knew that + + 18 they were playing with the water levels again. + + 19 Whenever the water levels went up or down, + + 20 people said, Must be the people in Rochester or + + 21 Montreal are unhappy about the water levels. + + 22 There is no denying that the creeks and + + 23 marshes that used to have water in them have dried + + 24 up. + + 25 This spring we saw water running in creeks + + + + + + + + 215 + 1 that we haven't seen since the 1970s. + + 2 With water levels rising nearly 3 feet this + + 3 spring, I heard many long-time residents say, that + + 4 if the water went down about a foot and a half, the + + 5 water levels would be at what they were in the '70s. + + 6 It is strange, though, that in the last + + 7 10 years, we have noticed a distinct increase in our + + 8 wildlife population. We are seeing animals never + + 9 seen years ago. + + 10 In addition to increased deer and turkey + + 11 populations, we now have eagles, beaver, raccoon, + + 12 porcupine, geese, heron, osprey, minks, and fishers. + + 13 I've been told the DEC denies the existence + + 14 of panthers and bobcats, but we have seen them and + + 15 they have been photographed. + + 16 I will admit, however, that the duck + + 17 population is lower than in the past. + + 18 Since the intent of the plan was to help + + 19 restore plant diversity and habitat for fish, it is + + 20 hard to determine if Plan 2014 factors into our + + 21 increased wildlife population, considering that the + + 22 creeks, marshes, and swamps only returned or saw + + 23 increased water levels this spring. + + 24 Restored wildlife was also an intent of the + + 25 plan, although we have noticed the increase in + + + + + + + + 216 + 1 wildlife long before 2014. + + 2 In reading the website of the + + 3 Lake Ontario-St. Lawrence River Plan 2014, + + 4 it states that: + + 5 "Plan 2014 is designed to provide for more + + 6 natural variations of water levels of Lake Ontario + + 7 and the St. Lawrence River that are needed to + + 8 restore echo-health system. + + 9 "It will continue to moderate extreme high + + 10 and low levels, better maintain systemwide levels + + 11 for navigation, frequently extend the recreational + + 12 boating season, and slightly increase hydropower + + 13 production." + + 14 According to the website homepage, one of the + + 15 purposes of the plan is to protect against high + + 16 water levels. + + 17 That certainly was not the case this spring. + + 18 Also, our recreational boating and tourism + + 19 levels were painfully low this summer. + + 20 I received many calls from seasonal residents + + 21 in the spring, wondering how the water levels were. + + 22 I had several mention that they weren't going + + 23 to bother coming to their summer cottage. + + 24 We understand the diverse interest of coastal + + 25 development, hydropower production, improving + + + + + + + + 217 + 1 conditions for commercial navigation, increasing + + 2 commercial business opportunities and recreational + + 3 boating. + + 4 We cannot argue with 16 years worth of + + 5 extensive studies performed by professional water + + 6 managers, environmentalists, and engineers. + + 7 I am not sure when the process of raising + + 8 water levels, according to Plan 2014, began; + + 9 however, last summer, we had a drought and the lake + + 10 levels were low. + + 11 Usually, Mother Nature takes care of herself. + + 12 Trying to keep the lake levels up this + + 13 spring appears to have gone against the plans of + + 14 Mother Nature. + + 15 With heavy wet snowfalls last winter, and + + 16 more than average rainfall this spring, it appears + + 17 Mother Nature intended to correct last summer's + + 18 drought. + + 19 Trying to set higher water levels this + + 20 spring, in an effort to follow through with + + 21 Plan 2014 initiatives, may not have been a good + + 22 decision. + + 23 The phrase "It's not nice to fool with + + 24 Mother Nature" has been quoted quite often in our + + 25 little lakeside town. + + + + + + + + 218 + 1 The high water levels not only caused + + 2 property damage, soil erosion, and stress among the + + 3 residents, but the impact on our volunteer fire + + 4 department members and budgets was substantial. + + 5 The Chaumont Volunteer Fire Department + + 6 incurred unplanned and unbudgeted expenses during + + 7 the 2017 flooding, which included 2,620 total + + 8 volunteer manhours, and $2,460 for meal expenses, + + 9 and $7,342 for equipment and supplies for sandbag + + 10 details. + + 11 Additionally, the Three Mile Bay Fire + + 12 Department responded to 56 flood-related incidents + + 13 and sandbag details, totaling 781 volunteer manhours + + 14 in May and June. + + 15 Sandbag totals exceeded 8,000 sandbags placed + + 16 by fire department personnel. And that is about + + 17 equal for both of them. + + 18 An additional, unbudgeted, 138 gallons of + + 19 fuel was used for fire department vehicles, and + + 20 80 gallons were used for fire chief and deputy chief + + 21 vehicles, totaling 218 unplanned and unbudgeted + + 22 gallons of fuel. + + 23 The Three Mile Bay Fire Department Auxiliary + + 24 provided over 100 meals to fire, EMS, and New York + + 25 State employees, exceeding $700. + + + + + + + + 219 + 1 The contracting for tractor-trailer delivery + + 2 of sandbags to residents totaled 460 miles and $920. + + 3 In addition to these figures, both fire + + 4 departments were the ones who began the operation of + + 5 filling sandbags by hand. + + 6 This work was performed each evening, for + + 7 about two weeks or more, before we could get + + 8 Senator Richie, and Graham and her staff, National + + 9 Guard, Cape Vincent Correctional Facility, and + + 10 New York State DOT to assist in the operation of the + + 11 sandbag center, which I reiterate and emphasize, was + + 12 initiated by us at the Town of Lyme, the Lyme + + 13 Highway Department, and the two fire departments -- + + 14 yeah, initiated by us at the Town of Lyme, the + + 15 highway department, and the two fire departments. + + 16 Enough cannot be said to commend everyone + + 17 involved for the services they performed. + + 18 Approximately 80,000 sandbags came out of our + + 19 facility. + + 20 The Town of Lyme Highway Department costs + + 21 were also substantial. + + 22 Charges for labor were $96,981, materials + + 23 totaled 19,186, and diesel fuel and gas amounted to + + 24 2,856. + + 25 We rented an excavator for $3,300, because + + + + + + + + 220 + 1 ours broke down while we were placing the big rocks + + 2 on the Point Peninsula isthmus. + + 3 If it wasn't for the little road going from + + 4 the mainland to Point Peninsula, Point Peninsula + + 5 would be an island. + + 6 This was done to follow the recommendations + + 7 of the Army Corps of Engineers. + + 8 So while we were doing that, our excavator + + 9 broke down, and we are gonna have to pay about + + 10 $12,000 to have that repaired. + + 11 Although some of those expenditures would + + 12 have occurred through normal operations, by no means + + 13 would they have been as high as they were. + + 14 If you talked to the average town of Lyme + + 15 resident on the street, most likely, you will hear + + 16 them say that the intent of Plan 2014 allowing for + + 17 slightly higher water levels in Lake Ontario, in an + + 18 effort to replenish marshes, streams, and wildlife, + + 19 may be a good one. + + 20 Obviously, this past spring, it did not work + + 21 out to the benefit of anyone, and only caused severe + + 22 property, shoreline, economic, and environmental + + 23 disaster. + + 24 Let's hope it doesn't happen again. + + 25 Thank you. + + + + + + + + 221 + 1 [Applause.] + + 2 SENATOR O'MARA: Patty. + + 3 SENATOR RITCHIE: I'd just like to start off + + 4 by commending you and your local government. + + 5 I know you were one of the first + + 6 municipalities who had things in place when + + 7 residents started calling, you know, far above many + + 8 other municipalities in making sure the sandbags + + 9 were available. + + 10 You know, when we stopped to look at your + + 11 operation, and you actually had cones upside down + + 12 and two boards, and filling sandbags that way, you + + 13 know, I think that we should acknowledge the fact + + 14 that you really were prepared, and tried to get to + + 15 the residents as soon as possible. + + 16 With that, I know that you said it was two + + 17 weeks before anyone really kicked in from the state + + 18 level. + + 19 So I guess my one question is: What else + + 20 could we do at the state level to help get the + + 21 resources to you faster? + + 22 What were you lacking in getting for help + + 23 from the State? + + 24 SCOTT AUBERTINE: I really think that + + 25 everyone worked as well as they could, as quickly as + + + + + + + + 222 + 1 they could. I know things don't happen overnight. + + 2 I declared a state of emergency on May 2nd, + + 3 I think it was, at noon. And Governor Cuomo + + 4 declared his at 3:00 or so. So, we beat him by a + + 5 few hours. + + 6 But, I think everybody did the best they + + 7 could. + + 8 You know, thank God for our fire departments, + + 9 who were the ones that came one the idea of putting + + 10 the cones upside down. + + 11 And, you know, we have to thank the Cape + + 12 Correctional Facility, and -- because we were taking + + 13 sand down to them, and their inmates were filling + + 14 sandbags also. + + 15 I think everything was done fairly well. + + 16 I don't know how long it takes to get the + + 17 National Guard there. + + 18 I was actually please they showed up as + + 19 quickly as they did. + + 20 So I think things went as well as they could + + 21 have. + + 22 SENATOR RITCHIE: Thank you. + + 23 SENATOR O'MARA: Thank you very much. + + 24 And thank you to our great fire departments + + 25 that we have throughout all of our communities here. + + + + + + + + 223 + 1 So, we rely on them so much. + + 2 SCOTT AUBERTINE: Thank you. + + 3 [Technical difficulties.] + + 4 [No audio or video.] + + 5 DR. DAN BARLETTA: (No audio or video)... + + 6 Lake Ontario, and, more specifically, the south + + 7 shore. + + 8 A lot of my thunder was taken by Dr. Frank. + + 9 A lot of his comments were my comments. + + 10 I promise, if we do this again, I will call + + 11 him, so that we can coordinate what we're gonna talk + + 12 about, and so we don't repeat the same stuff. + + 13 But let me start by giving you a little bit + + 14 of my background. + + 15 I've been a lakeshore riparian since 1985. + + 16 My wife's family's been down there for four + + 17 generations since the 1940s. + + 18 Our houses were not recently built. They may + + 19 have been remodeled, but my house, I date it back to + + 20 at least 1940. + + 21 My wife's family has a house that was back + + 22 probably into the '20s. + + 23 But, I became a -- very involved with + + 24 lake-level issues in the spring of 1993. + + 25 During that high water period, my breakwall + + + + + + + + 224 + 1 was damaged, and I went through, you know, all kind + + 2 of contortions to get the permits. + + 3 But -- so after that, I attended many + + 4 Lake Ontario - St. Lawrence River Board of Control + + 5 meetings on behalf of my neighborhood association. + + 6 In 1999, I was asked to serve as a member of + + 7 the public-interest advisory group as part of the + + 8 Lake Ontario-St. Lawrence River Study. + + 9 Within a year, I was chair of the public + + 10 interest advisory group, and, as well, becoming a + + 11 member of the study board. I oversaw the whole + + 12 entire study. + + 13 I was also co-author of the minority report + + 14 on the study. Myself and Dr. Frank are the + + 15 authors of it. + + 16 Presently, I represent my neighborhood + + 17 association on lake-level issues, and I'm a director + + 18 of Lake Ontario Riparian Alliance, which is + + 19 committed to getting rid of Plan 2014. + + 20 As a member of the Lake Ontario-St. Lawrence + + 21 River Study, let me state, as Dr. Frank said, Plan + + 22 2014 was not a plan proposed by the study, as the + + 23 IJC would let you -- lead you to believe. The + + 24 environmental plan proposed, as Dr. Frank said, + + 25 was Plan B+. It was entirely different. + + + + + + + + 225 + 1 And they stated that they would not implement + + 2 it -- they would like to implement it, but because + + 3 of the excessive damages it caused, they couldn't. + + 4 But, in hindsight, it might have been better, + + 5 because at least the damages would have been spread. + + 6 With Plan 2014, the damages are concentrated + + 7 on the riparians and businesses, primarily along the + + 8 south shore, whose population constitutes the + + 9 weakest of all the interests involved with the lake + + 10 and the river. + + 11 Unlike the Lake Ontario-St. Lawrence River + + 12 Study, which was relatively transparent, 2014 was + + 13 developed by closed-door committee, populated by + + 14 members that came from government agencies only, + + 15 mostly environmental agencies from both New York and + + 16 Canadian provinces. + + 17 In the references in the back, I actually + + 18 have the list of the first members of that group. + + 19 The entities -- the agency stayed the same, + + 20 but the people might have changed over the course of + + 21 the workings of that group. + + 22 We do know, however, that they did consult + + 23 with environmental groups that wanted Plan B that + + 24 was put out by the Lake Ontario study, but there was + + 25 no representation for recreational boaters or + + + + + + + + 226 + 1 homeowners on this committee. + + 2 They first proposed, as you've heard already, + + 3 Plan Bv7, which is Plan 2014 without trigger levels. + + 4 Plan Bv7 caused substantial damages on the + + 5 lake and on the lower river. + + 6 Indeed, we learned from a person involved + + 7 with this committee that representatives from the + + 8 province of Quebec stated they would not accept any + + 9 new regulation plan that caused more damage on the + + 10 lower river than occurred with the old plan. + + 11 Thus, the committee moved the damages + + 12 primarily to the south shore of Lake Ontario without + + 13 regard to the remediation of these damages. + + 14 Plan 2014 took away the board's ability to + + 15 deviate from the regulation plan until the trigger + + 16 levels were surpassed. + + 17 These trigger levels are at extremes, and + + 18 do not represent any of the ideal water levels + + 19 proposed by the technical worker groups of the + + 20 Lake Ontario-St. Lawrence River Study. + + 21 And in the references in the back, there's + + 22 actually a chart that shows what the levels were. + + 23 And the highest level that was asked for was 247.3. + + 24 The highest level with Plan 2014, the highest + + 25 trigger, is 248.1. + + + + + + + + 227 + 1 A recent newsletter article that was in a + + 2 Rochester paper, that stated that there was + + 3 1.3 trillion gallons of water at its high point on + + 4 the lake in late May or early June. + + 5 This amount only reinforces the fact that the + + 6 board is not allowed to deviate when it comes -- + + 7 when it sees excess water coming into the system + + 8 from either Lake Erie or from the local basin. + + 9 Many of us on the south shore believe that + + 10 with -- if it was under the old plan, the board + + 11 might have been able to anticipate and take some + + 12 action. + + 13 While it may not have done much to change + + 14 what happened this year, at least it would have been + + 15 better doing nothing than being stuck due to issues + + 16 with Ottawa River flows. + + 17 Let me give you this analogy, think of this: + + 18 If you're going down the expressway at + + 19 60 miles an hour, and the car in front is stopping, + + 20 but under the new rules of the road, you can't stop + + 21 and apply the breaks until you were 20 feet away. + + 22 It's a little too late, and you ignore the + + 23 facts, telling you what is coming. + + 24 Plan 2014 triggers are set way too high + + 25 during wet seasons and too low during dry seasons. + + + + + + + + 228 + 1 Attached as a reference also on this paper, + + 2 is a spreadsheet created by one of my neighbors's + + 3 college kids. + + 4 It's important -- the important message that + + 5 we learned from this is that, if the board was able + + 6 to deviate when the lake was a foot above average at + + 7 the end of the February, beginning of March, the + + 8 lake may not have reached the levels experienced. + + 9 By increasing outflows by a minimum of + + 10 300 cubic meters per second, the effect would have + + 11 lowered the lake between 3 to 9 inches by + + 12 April 19th, the day the flooding began in Montreal + + 13 due to the high water flows in the Ottawa River. + + 14 I want to put a caveat on that. + + 15 We're still working on the calculations. We + + 16 still have to factor in the ice-formation periods + + 17 that took place in February and March. + + 18 As I stated already, Plan 2014 does not allow + + 19 the board to deviate for the lake until triggers are + + 20 reached, so this oversupply will occur more often on + + 21 the lake. + + 22 Based on IJC data itself, levels above 247, + + 23 which is the level that causes flooding on the south + + 24 shore, will occur 300 percent more often with + + 25 Plan 2014 than with the old Plan '58DD. + + + + + + + + 229 + 1 Going forward, if there is no change in the + + 2 trigger levels in the spring conditions, the spring + + 3 conditions will be ripe for recurrence of this + + 4 year's crisis. + + 5 The Ottawa River's two-part freshets are a + + 6 known factor occurring every mid-April to mid-May. + + 7 The Plan 2014 triggers present us with the + + 8 danger, regardless of how the lake got this high, + + 9 whether it was the excess precipitation or + + 10 Plan 2014. But Plan 2014 will continue to cause + + 11 damage to this lakeshore line. + + 12 A lot of this damage was not included in the + + 13 Lake Ontario-St. Lawrence River Study. + + 14 The costs included were damages to shoreline + + 15 protection. + + 16 Public lands, public infrastructure, private + + 17 properties on embankments like Sodus Bay were not + + 18 included. + + 19 Facts on property-tax assessments were shoved + + 20 under the rug. + + 21 In addition, where the money to retrofit the + + 22 shore to the higher levels was not investigated. + + 23 Who's going to pay for retrofitting the south + + 24 shore? + + 25 Moneys that the State has already allocated + + + + + + + + 230 + 1 may not be sufficient to help the 10,000 properties + + 2 along the south shore. + + 3 The IJC went as far to say that we're not + + 4 being damaged, it's a reduction of benefits; thus, + + 5 getting around the requirement of the boundary + + 6 (indiscernible) and make whole any interests damaged + + 7 by any IJC decision. + + 8 So let me conclude by circling back to where + + 9 I began and became involved with the lake issues. + + 10 Back in 1993, as I previously stated, my + + 11 breakwall was damaged. + + 12 In the process of redesigning, I took into + + 13 consideration the operating range used by the board + + 14 of control at that time; that being, between 243.3 + + 15 to 247.3. + + 16 With this -- with information obtained from + + 17 the Army Corps of Engineers, I learned that + + 18 80 percent of the waves hitting my house -- or, my + + 19 shore were 6 feet or less. So I built my protection + + 20 at 253.5 feet. + + 21 The cost to do this was over $75,000. + + 22 I'm presently looking to modify my breakwall + + 23 to increase its height. + + 24 The problem is, Plan 2014 does not have no + + 25 upper limit. + + + + + + + + 231 + 1 Additionally, with Plan 2000 -- with -- + + 2 additionally, with Plan '58DD, my property boundary + + 3 was set at the upper limit of the range, at 247.3. + + 4 With Plan 2014, again, there is no upper + + 5 limit. + + 6 So where is my property line, and who's going + + 7 to pay for the property being taken by the + + 8 government? + + 9 And I thank you for letting me put my input + + 10 in. + + 11 SENATOR O'MARA: Thank you. + + 12 DR. DAN BARLETTA: And, a little dry mouth. + + 13 So, sorry for that. + + 14 ASSEMBLYMAN BARCLAY: Thanks for your + + 15 testimony. + + 16 And, actually, you started hitting on it at + + 17 the very end. + + 18 Has your association investigated any kind of + + 19 legal action, as far as the taking by the government + + 20 on this? + + 21 DR. DAN BARLETTA: Well, the problem that + + 22 occurs there is, who do you sue? + + 23 ASSEMBLYMAN BARCLAY: What's that? + + 24 DR. DAN BARLETTA: Who do you sue? + + 25 ASSEMBLYMAN BARCLAY: Well, the federal + + + + + + + + 232 + 1 government, I suppose. + + 2 DR. DAN BARLETTA: I mean, we've looked at + + 3 that. + + 4 There's been other members of our groups that + + 5 were involved with the lawsuit back in the '80s. + + 6 They spent $80,000, and they got the right to + + 7 go to next stage, which is the discovery stage. + + 8 We can't afford it. + + 9 ASSEMBLYMAN BARCLAY: Yeah. + + 10 I wonder if it's possible to get other + + 11 associations along the lake to -- work together + + 12 to -- + + 13 DR. DAN BARLETTA: I know that there are + + 14 other groups looking it. + + 15 But, you can't sue the IJC because they're a + + 16 treaty organization. + + 17 ASSEMBLYMAN BARCLAY: Right. + + 18 DR. DAN BARLETTA: The federal government, + + 19 I doubt will -- you know, who you gonna go after in + + 20 the federal government? + + 21 ASSEMBLYMAN BARCLAY: Interesting. + + 22 Thanks. + + 23 DR. DAN BARLETTA: Thank you. + + 24 SENATOR O'MARA: Can you explain to me what + + 25 you mean, you said towards the end: The problem is + + + + + + + + 233 + 1 Plan 2014 has no set upper range limit? + + 2 What do you mean by that? + + 3 DR. DAN BARLETTA: '58D had a range. + + 4 The range on 2014, just, it can go to 249 + + 5 again. + + 6 SENATOR O'MARA: Within the plan itself? + + 7 DR. DAN BARLETTA: There's no set limits. + + 8 [Indiscernible audience comments.] + + 9 SENATOR O'MARA: Do you have any questions? + + 10 Who ultimately approved Plan 2014 on the + + 11 United States side? + + 12 DR. DAN BARLETTA: The Department of State, + + 13 I believe. The Federal Department of State. + + 14 SENATOR O'MARA: Just the Secretary of State? + + 15 DR. DAN BARLETTA: Yes. + + 16 SENATOR O'MARA: Okay. + + 17 Thank you. + + 18 Next up is Cathleen Goodnough from Green + + 19 Point Marina. + + 20 CATHLEEN GOODNOUGH: Assemblymen, Senators. + + 21 SENATOR O'MARA: Good evening. + + 22 CATHLEEN GOODNOUGH: Thank you for the + + 23 invitation to speak. + + 24 My name is Cathleen Goodnough, and I'm one of + + 25 the business owners of Green Point Marina, Mobile + + + + + + + + 234 + 1 Home Park. + + 2 SENATOR O'MARA: If you could get a little + + 3 closer to the mic, please. + + 4 CATHLEEN GOODNOUGH: I'm sorry. + + 5 It's been in my family and operating for over + + 6 112 years. + + 7 We own a unique peninsula consisting of + + 8 212 acres located in North Sandy Pond in Upstate + + 9 New York. + + 10 Our business is seasonal and run by myself, + + 11 my sister Cheryl Yerson, and our mother + + 12 Linda Goodnough. + + 13 This year the high water levels have left our + + 14 138-site mobile home park and 95-slip marina + + 15 devastated. + + 16 We were warned April 18th that the water + + 17 levels would be up high, and the high winds would be + + 18 expected until mid-May. + + 19 Nothing could have prepared us for the height + + 20 of the water levels and the length of the time that + + 21 we were flooded. + + 22 We spent hours and days picking up shoreline + + 23 debris; parts, tools, in the shop; walking along the + + 24 shoreline to secure trees; picking up our people's + + 25 furniture and other items that could float away and + + + + + + + + 235 + 1 become a hazard in the water. + + 2 Our main marina building had standing water + + 3 for over 60 days straight. + + 4 We worked in hip boots daily. + + 5 Our people in the park could not use their + + 6 seasonal residence as the water surrounded most, and + + 7 covered their septics. + + 8 Roads were flooded with over 9 inches of + + 9 water. + + 10 We have 33 private landowners that have a + + 11 right-of-way over our road to reach their + + 12 properties. Many evacuated. Some stayed to protect + + 13 their homes. + + 14 Access to their home and our business was + + 15 priority. + + 16 Along with providing access for fire and + + 17 ambulance services for anyone that stayed, we had to + + 18 close our road for over a week, and this led to us + + 19 building up the road with over 4500 sandbags. And + + 20 that was hauling in 26 trucks of 20-ton stone to + + 21 bring the road up out of the water. + + 22 The main building of over 3200 square feet + + 23 has been completely emptied of a vast inventory of + + 24 parts and moved to rental containers. + + 25 Our concrete floors have heaved and cracked + + + + + + + + 236 + 1 and caused major destruction. + + 2 We monitor the water levels in the spring to + + 3 prepare for our docks to be repaired. + + 4 This year we have photos. + + 5 March 30th, the water level was a foot below + + 6 our docks. + + 7 April 1st, the water was a foot below the + + 8 docks. + + 9 And April 5th, it was at the bottom of the + + 10 docks. + + 11 That's a foot in four days. + + 12 April 14th, we raised our docks 13 inches out + + 13 of the water, and April 24th, the docks were + + 14 underwater. + + 15 In essence, that's over 24 inches/2 feet in + + 16 24 days. + + 17 While we understand we had a very wet spring, + + 18 when reviewing the outflows from the IJC on + + 19 April 13th, they were 7700 cubic meters, and on + + 20 April 24th, 7800 cubic meters, respectively. + + 21 With the difference of 2-foot already being + + 22 seen on our shorelines, we did wonder why the + + 23 outflows were not at the maximum of 10,200 cubic + + 24 meters. + + 25 There is no escaping water. It goes + + + + + + + + 237 + 1 everywhere, and left in an enclosed building for + + 2 over 60 days, we had black mold, wet insulation, + + 3 soggy, wet wood shelves, and wet walls. + + 4 Our days would start out by, what can we get + + 5 done today? + + 6 And the main business that we count on to + + 7 survive stopped altogether. + + 8 There were no customers, there was no + + 9 business, there was no foot-track of any kind, and + + 10 we felt very displaced. + + 11 We should have been preparing boats to be + + 12 ready, our seasonal snack bar for people to enjoy. + + 13 Instead, we had to focus daily on picking up + + 14 wet, soggy wood, insulation, save what we could in + + 15 inventory, and move everything into three storage + + 16 containers and a workshop to save it. + + 17 We flooded the worst on June 1st with the + + 18 water levels of 248.9. + + 19 If the IJC trigger levels are to remain at + + 20 248.13 for June 1st of next year, we cannot imagine + + 21 how we will remain in business if this new Plan 2014 + + 22 is not modified. + + 23 June, historically, the month of high water. + + 24 And we also have Lake Ontario right to the west of + + 25 us. And when the wind blows, we can see a + + + + + + + + 238 + 1 difference in the water levels of almost 2 feet if + + 2 the west wind blows heavy for more than a few days. + + 3 We were very fortunate to be backed by + + 4 Pathfinder Bank, and they have waived our principal + + 5 payments on our mortgage, totaling over 36,000. + + 6 They've also extended a line of credit, for + + 7 up to $100,000, to make repairs needed on our + + 8 building. + + 9 Thus far, we have received little or no help + + 10 from funding from anyone. + + 11 We have put in for the grants available to + + 12 us, but you have to expense the money first. + + 13 We have made many calls to Governor Cuomo's + + 14 office to find out if this area has been declared a + + 15 federal disaster so that FEMA might come in. + + 16 And we have received very little insurance + + 17 for our flood damage to our building. + + 18 And to date, we have expensed over $50,000 + + 19 already, and our marina receipts are down over + + 20 $120,000. + + 21 Our entire summer was non-existent, and our + + 22 residents have major concerns that this is going to + + 23 be the new normal. + + 24 On October 2, 19 -- or, 2017, the media + + 25 release from the IJC states "The board will assist + + + + + + + + 239 + 1 with boat haul-outs from Lake St. Lawrence by + + 2 decreasing water flows." + + 3 With the consideration to assist them, one + + 4 wonders where the consideration or assistance was + + 5 for the entire southern shore of Lake Ontario. + + 6 And also stated in this release "The board + + 7 urges everyone to be prepared to live within the + + 8 full range that have occurred in the past, and those + + 9 that may occur in the future. And based on that" -- + + 10 "historical observations, and projected for future + + 11 conditions, at a minimum, Lake Ontario water levels + + 12 are expected to range from a high of 248.95, to a + + 13 low of 241.3, at infrequent intervals. + + 14 If these levels remain the same and the + + 15 trigger levels are not changed within the IJC, our + + 16 business, and many other businesses and homeowners, + + 17 will be under water again. + + 18 Our property taxes are $58,000 a year, and + + 19 33 private landowners have -- pay hefty taxes. + + 20 On an average basis of 7,000 per private + + 21 landowner, plus our taxes, it's $289,000. + + 22 If the water levels remain high, and flooding + + 23 occurs more frequently, the towns along the lake + + 24 will no longer be able to count on the + + 25 waterfront-property taxes that are a large + + + + + + + + 240 + 1 source of their revenue. + + 2 Our property suffered massive erosion in and + + 3 around our seven canals. + + 4 We had several residents flood in their + + 5 cabanas. + + 6 Our campers were in eminent danger of being + + 7 knocked off their cinder blocks with the high winds + + 8 and the water levels. + + 9 We have major concerns for the reconstruction + + 10 of our property. + + 11 We have miles of shoreline to be rebuilt, and + + 12 breakwalls to build up, and fill to bring in to + + 13 level the ground which is uneven from water sitting + + 14 on it for over two months. + + 15 If our business is to remain open, we need + + 16 to: + + 17 Raise our park road; + + 18 Convert our 95 docks into floating docks; + + 19 And raise the level of concrete in our shop, + + 20 over 3200 square feet, up 7 inches; + + 21 Raise the breakwalls; + + 22 Repair existing rock breakwalls; + + 23 Install a septic removal system throughout + + 24 the park so that our tenants may use their places. + + 25 This year we rented (indiscernible) for a few + + + + + + + + 241 + 1 tenants that came up to check on their places. + + 2 Our tenants pay us lot rent to us each year, + + 3 on a yearly basis, and most could not use their + + 4 place this year. + + 5 With little or no income coming in, and + + 6 having to refund rental customers' dockage, trailer + + 7 rentals, boat rentals, because we could not operate + + 8 within the 248.9 water-level range, was extremely + + 9 stressful. + + 10 And we have so much devastation to our land + + 11 and business, and no funding in sight to help + + 12 prepare to live within these ranges. + + 13 The funding we did apply for, through the + + 14 loss of revenue, will be $50,000, if granted. + + 15 And our business is not the only one that has + + 16 suffered this year. + + 17 Our neighborhood restaurants, bars, ice + + 18 distributors, beer distributors, gas-sales vendors, + + 19 local grocery stores, liquor stores, + + 20 laundrymats (ph.)... the list goes on and on. + + 21 They have all suffered a financial loss from + + 22 the lack of people in our area. + + 23 The State will see a decrease in their + + 24 sales-tax revenue, as well as the County and the + + 25 Town. + + + + + + + + 242 + 1 This year's water levels not only flooded + + 2 businesses and homes, it hurt everyone in our + + 3 community in some way. + + 4 And from the joy and the pride we have of + + 5 running a family business, to daily stress, + + 6 uncertainty of finances, and actual horror of + + 7 watching the water levels come up a foot, and then + + 8 another foot, and then another foot, has left us + + 9 with the determination to rebuild our business, but, + + 10 also, to help to make change to the Plan 2014 so + + 11 that our once beautiful land and barrier beaches + + 12 that so many people enjoy, can be seen again. + + 13 Thank you for the opportunity to express our + + 14 opinion of the water levels of Lake Ontario. + + 15 Our hope is that the IJC board and other + + 16 governing agencies involved see the magnitude of + + 17 desperation for the need to change the trigger + + 18 levels of the Plan 2014. + + 19 [Applause.] + + 20 SENATOR O'MARA: Thank you. + + 21 SENATOR RITCHIE: I would just like to say + + 22 that, when we talk about heartbreaking stories, + + 23 I don't think anyone could convey how horrible the + + 24 situation was at your marina. + + 25 The fact that, where the parts were, you were + + + + + + + + 243 + 1 out there, over your knees in water, trying to go + + 2 and get parts. And every time we stopped, the + + 3 situation got worse and worse and worse. + + 4 So, the fact that you're here, and you're + + 5 saying that you're looking forward to keeping the + + 6 family business going, in itself, is a pretty + + 7 amazing statement. + + 8 I am concerned, when you said you haven't + + 9 received any resources yet from the State. + + 10 So -- + + 11 CATHLEEN GOODNOUGH: We did receive a phone + + 12 call. They are reviewing the application for the + + 13 income loss of revenue. + + 14 They're reviewing it. + + 15 SENATOR RITCHIE: Okay. Will you keep me + + 16 posted on that? + + 17 CATHLEEN GOODNOUGH: I will. + + 18 SENATOR RITCHIE: Thank you. + + 19 CATHLEEN GOODNOUGH: Thank you. + + 20 SENATOR O'MARA: Thank you. + + 21 Next we have Wendy and Mark LaLonde from + + 22 Hutchinson's Boat Works. + + 23 MARK LaLONDE: (Inaudible) and thank you for + + 24 inviting me. + + 25 I thought that our situation was dire, and, + + + + + + + + 244 + 1 yet, compared to what I've just heard, you know, it + + 2 was really not -- it was really nothing. + + 3 But, I run -- own and run a full-service + + 4 marina in Alexandria Bay, on the St. Lawrence + + 5 River -- + + 6 SENATOR O'MARA: Get a little closer to the + + 7 mic, please. + + 8 MARK LaLONDE: I own and run a full-service + + 9 marina in Alexandria Bay, on the St. Lawrence River, + + 10 with my wife, Wendy. + + 11 When we opened up at the beginning of January + + 12 this year, all the indicators were, that we were + + 13 going to have a banner year, possibly the best we've + + 14 had since 2007, which was our last really good year + + 15 in the marine business. + + 16 I say that because, our pre-season sales were + + 17 good. We were talking to lots of prospects, and + + 18 they were talking positively. + + 19 We had a lot of hope for, you know, moving + + 20 into the season. + + 21 So we started launching boats toward the end + + 22 of April, at kind of a slow pace, because the water + + 23 was coming up and we didn't know how high it was + + 24 gonna get. + + 25 That pace continued to slow until the river + + + + + + + + 245 + 1 peaked out in the first part of June, and it came to + + 2 a complete halt. + + 3 We ended up with about 15 percent of our + + 4 customers not going in the water at all. + + 5 Our fuel dock was underwater. + + 6 We lost Victoria Day weekend, Memorial Day + + 7 weekend. Partially opened the dock on June 18th. + + 8 And, with the 5-mile-an-hour speed limit, a + + 9 lot of our customers that were in, were keeping + + 10 their boat at the dock, or, you know, running much + + 11 slower. + + 12 Our gas sales were off 46 percent; + + 13 Retail labor was down 15 percent; + + 14 Parts were down 8 percent; + + 15 And dockage was down 8 percent. + + 16 At the same time we were dealing with reduced + + 17 revenues, we were struggling to keep our -- the + + 18 vertical surfaces of our docks high enough so our + + 19 customers' fenders had something to ride against. + + 20 So, we built brackets to accomplish that. + + 21 We spent, all together, about $90,000 on + + 22 doing that, shoring up a seawall, filling one of our + + 23 gravel parking lots with 14 loads of gravel, to turn + + 24 it from a swimming pool back into a parking lot. + + 25 So, that's what we're dealing with. + + + + + + + + 246 + 1 You know, nothing like some other people here + + 2 are dealing with. + + 3 We were able to borrow $100,000 from our + + 4 local bank, to keep going. And they're working with + + 5 us on the repayment of that. + + 6 All those boats that didn't go in the water, + + 7 we're not going be winterizing. They didn't pay for + + 8 any dockage. So there's about $40,000 more in lost + + 9 sales there. + + 10 That's all I have. + + 11 SENATOR O'MARA: Thank you. + + 12 SENATOR RITCHIE: Could I just ask, did you + + 13 submit a grant application to the State? + + 14 MARK LaLONDE: Yes, we did. + + 15 SENATOR RITCHIE: And how is that going? + + 16 MARK LaLONDE: It's not doing anything yet. + + 17 SENATOR RITCHIE: Not doing anything yet. + + 18 Okay. + + 19 Thank you. + + 20 MARK LaLONDE: Thank you. + + 21 SENATOR O'MARA: How did you find that + + 22 process of application? + + 23 MARK LaLONDE: My wife handled that, and she + + 24 was talking about the detailed information that was + + 25 required, and, you know, proof of payment for + + + + + + + + 247 + 1 everything that we were submitting. And it was + + 2 pretty grueling. + + 3 SENATOR O'MARA: Thank you. + + 4 SENATOR RITCHIE: Thank you. + + 5 MARK LaLONDE: Thank you. + + 6 [Applause.] + + 7 SENATOR O'MARA: Well, that concludes our + + 8 hearing this evening. + + 9 We thank you all for coming out, sticking + + 10 through it. + + 11 And we hope to have another one of these + + 12 hearings in early November, further west down the + + 13 lake. + + 14 So, that will remain to be announced. + + 15 But thank you all for your attention, and + + 16 your coming out to help us try to get to the bottom + + 17 of this. + + 18 Thank you. + + 19 (Whereupon, at approximately 8:33 p.m., + + 20 the joint public hearing held before the New York + + 21 State Senate Standing Committee on Environmental + + 22 Conservation and the Standing Committee on + + 23 Agriculture concluded, and adjourned.) + + 24 + + 25 + + + + + + \ No newline at end of file diff --git a/src/test/resources/hearing/10-19-16 NYS Senate Hudson River Barge Hearing FINAL.txt b/src/test/resources/hearing/10-19-16 NYS Senate Hudson River Barge Hearing FINAL.txt new file mode 100644 index 000000000..9b798ee62 --- /dev/null +++ b/src/test/resources/hearing/10-19-16 NYS Senate Hudson River Barge Hearing FINAL.txt @@ -0,0 +1,8152 @@ + + + + 1 BEFORE THE NEW YORK STATE SENATE + + 2 ------------------------------------------------------ + + 3 PUBLIC HEARING + + 4 TO HEAR TESTIMONY ABOUT COAST GUARD'S CONTROVERSIAL + + 5 PLAN TO CREATE TEN COMMERCIAL BARGE ANCHORAGES FROM + + 6 YONKERS TO KINGSTON + + 7 + ------------------------------------------------------ + 8 + Croton-on-Hudson Town Hall + 9 1 Van Wyck Street + Croton-on-Hudson, New York 10520 + 10 + October 19, 2016 + 11 7:00 p.m. + + 12 + PRESIDING: + 13 + Senator Senator Terrence Murphy + 14 Chair + + 15 Matt Slatter + Chief of Staff to Senator Murphy + 16 + + 17 PRESENT: + + 18 Senator David Carlucci + + 19 Senator Sue Serino + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 2 + 1 + OPENING BY: PAGE + 2 + Dr. Greg Schmidt 4 + 3 Mayor + Croton-on-Hudson + 4 + + 5 SPEAKERS: PAGE + + 6 Liam McLaughlin 10 + President + 7 Yonkers City Council + + 8 Jason Baker 20 + Sr. Assistant to the Mayor + 9 Office of Yonkers Mayor Michael Spano + + 10 Linda Puglisi 26 + Town Supervisor + 11 Town of Cortlandt + + 12 Eoin Wrafter 31 + Planning and Development Commissioner + 13 Office of Dutchess County Executive Marc Molinaro + + 14 Jim Monaghan 35 + Supervisor + 15 Stony Point, NY + + 16 Deborah Milone 38 + Executive Director + 17 Hudson Valley Gateway Chamber of Commerce + + 18 Robert Astorino 42 + County Executive + 19 Westchester County, NY + + 20 Ned Sullivan 47 + President + 21 Scenic Hudson + + 22 Edward Kelly 55 + Executive Director + 23 Maritime Association, Port of NY and NJ + + 24 + + 25 + + + + + + + + 3 + 1 + SPEAKERS (Continued): PAGE + 2 + Barbara Scuccimarra 83 + 3 Legislator + Office of Putnam County Executive MaryEllen Odell + 4 + John Testa 86 + 5 County Executive + Putnam County, NY + 6 + John Cronin 90 + 7 Senior Fellow for Environmental Affairs + Pace University Environmental Law School + 8 + Betsy Garthwaite 105 + 9 Chairman of the Board + Clearwater + 10 + John Parker 110 + 11 Director of Legal Programs + Riverkeeper + 12 + Frank Bergman 121 + 13 President + Hudson River Boat and Yacht Club Association + 14 + Emily Majer 127 + 15 Deputy Mayor + Village of Tivoli, NY + 16 + Jerry Faiella 133 + 17 Executive Director + Historic Hudson River Towns + 18 + + 19 + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 4 + 1 MAYOR GREG SCHMIDT: I pledge allegiance to + + 2 the United States of America, and to the republic + + 3 for which it stands, one nation under God, + + 4 indivisible with liberty and justice for all. + + 5 Thank you. The door is open. How exciting. + + 6 So I am Dr. Greg Schmidt, the Mayor of the + + 7 Village of Croton-on-Hudson, and I just want to + + 8 thank everybody for coming here today, and I want to + + 9 thank Senator Terrence Murphy for sponsoring this + + 10 public hearing tonight. And I'm glad he came to + + 11 Croton because Croton has been at the forefront, as + + 12 many other river communities, in terms of helping to + + 13 keep the Hudson River clean and viable and the + + 14 economic resource that it really is. And the + + 15 biggest thing that we've worked on for many, many + + 16 years is to keep it clean. + + 17 Here in Croton, we've had the dump that was + + 18 here from the early 1920s that finally shut down + + 19 several years ago. So we have a long history here + + 20 in Croton of the environmental damage that has been + + 21 done to this river. So I'm very proud to see how + + 22 many people have come out to really voice their + + 23 concerns about this barge project. + + 24 We are going to be hearing from many eloquent + + 25 people tonight to tell us their concerns, but for + + + + + + + + 5 + 1 now, I'm going to turn it over to my dear friend and + + 2 I'm very happy that he has led the charge on this, + + 3 Senator Terrence Murphy. Thank you very much. + + 4 SENATOR MURPHY: Thank you, Mayor. + + 5 [ Applause ] + + 6 Thank you so much, Mayor, and thank you + + 7 everybody for coming out here to tonight's hearing + + 8 on the U.S. Coast Guard's proposed federal rule that + + 9 establishing 10 -- or excuse me -- 16 new anchorage + + 10 sites from Yonkers up to Kingston. The Hudson River + + 11 as we all know is one of the most cherished natural + + 12 resources in the Hudson Valley. It is crystal clear + + 13 the public needs abundantly answers that we must + + 14 have, and that's what tonight is all about, is about + + 15 transparency. + + 16 It was October -- I'm sorry, August 8 -- when + + 17 we had our first press conference about finding out + + 18 about these proposed anchorage sites, and we had + + 19 immediately called the Coast Guard to find out what + + 20 this was all about. And unfortunately, I'm not sure + + 21 if we have a representative here tonight from the + + 22 Coast Guard, but I do know they were invited. + + 23 We invited as many people as we could + + 24 possibly do to get the answers that we are all + + 25 concerned about. + + + + + + + + 6 + 1 I had the press conference on August 8, and I + + 2 immediately put up a petition on my website, and + + 3 within three weeks we had 1900 people sign up to + + 4 figure out what this was all about. + + 5 And this is the reason why we are here + + 6 tonight. Not only are we all looking for answers, + + 7 and as your elected officials between the three of + + 8 us represent close to 900,000 people within our + + 9 districts combined. + + 10 These are extremely important issues. + + 11 Senator Sue Serino here, Senator David Carlucci and + + 12 myself, we all have part of this with regards to the + + 13 Hudson Valley, and there are a tremendous amount of + + 14 answers we are all concerned about. + + 15 I see a lot of elected officials here. County + + 16 Legislator Testa, Mayor Catalina, Barbara + + 17 Scuccimarra, Liam McLaughlin. + + 18 I'm looking for all your input tonight and + + 19 actually the public's input, so I look forward to a + + 20 robust conversation tonight. I do know we have a + + 21 bunch of speakers. + + 22 First of all, and I would just like to -- + + 23 I'm not sure where he went, but thank Mayor Schmidt + + 24 for hosting us tonight. It is awful kind of him and + + 25 we've had multiple conversations about this, and we + + + + + + + + 7 + 1 are here tonight to find out some answers. + + 2 So with all due respect, we are going to be + + 3 on a tight timeframe, and I would just like to turn + + 4 it over to my colleague Senator Sue Serino who came + + 5 down here to be with us tonight. + + 6 Thank you, Sue. + + 7 SENATOR SERINO: Thank you Senator Murphy and + + 8 Senator Carlucci. + + 9 I think it's so important for the public to + + 10 have their voice heard. + + 11 Public comment gets lost under the radar so + + 12 often, and you really need to have your questions + + 13 answered. + + 14 I know myself, I had the opportunity to meet + + 15 with the Coast Guard, and I can tell you, I have a + + 16 lot more questions than I did before. So I just + + 17 think that it's fair for the public to be able to + + 18 have their time with the Coast Guard and ask those + + 19 questions directly. + + 20 You know, as a mom, I worry about my children + + 21 and my grandchildren having our beautiful majestic + + 22 river to enjoy like I have for most of my life, + + 23 boating on the river, and so many people rely on + + 24 that resource. + + 25 So I just want to say thank you to everyone + + + + + + + + 8 + 1 that is here tonight, our other elected officials, + + 2 and I look forward to hearing your thoughts. + + 3 Thank you. + + 4 SENATOR MURPHY: Thank you, Senator Serino. + + 5 Senator Carlucci, thank you for being here. + + 6 SENATOR CARLUCCI: Thank you, Senator Murphy, + + 7 thank you, Senator Serino. + + 8 I want to thank everyone that's here, the + + 9 elected officials. I know Assemblywoman Sandy Galef + + 10 is here, Supervisor Monaghan from the Town of Stony + + 11 Point. Thank you for being here. + + 12 And I thank each and every person for being + + 13 here for this very important issue. + + 14 For decades now, many of the people in this + + 15 room, environmental advocates, local government + + 16 leaders, have been working to clean up from the + + 17 mistakes of prior generations. + + 18 And that's why this hearing and this issue is + + 19 so important because we've learned from the mistakes + + 20 of the previous government, from the business + + 21 interests that went up and down this river that we + + 22 can't tread lightly on this issue. + + 23 We've got to make sure that every I is + + 24 dotted. Every T is crossed. No stone is left + + 25 unturned. Every question is answered. + + + + + + + + 9 + 1 That's why it really boils my blood that the + + 2 Coast Guard is not here tonight at a Senate hearing + + 3 to answer these important questions. + + 4 That's the big problem here. There is no + + 5 transparency on this issue. When I'm walking down + + 6 the street in my district, people are asking + + 7 questions and rightfully so. + + 8 So I want to thank everyone that is here. I + + 9 think together if we continue our vigilance that + + 10 we've done over the past few decades that we can + + 11 once again enjoy the majesty of the Hudson River. + + 12 And I have been working with the students at + + 13 Ossining High School in collaboration with + + 14 Riverkeeper, and we have been out there every week + + 15 monitoring the river, testing the water quality of + + 16 the river. And our goal, our mission is to reopen + + 17 the beach in Ossining so that the residents of the + + 18 community can enjoy the beauty of our river. + + 19 This river belongs to all of us. So we've + + 20 got to protect it vigorously so that generations to + + 21 come, they don't look back and say, "What did you + + 22 do? What did you do?" + + 23 And that's why we deserve answers. We + + 24 deserve every question to be answered, and I'm so + + 25 grateful to everyone that's here tonight to make + + + + + + + + 10 + 1 sure we push, and we don't tread lightly on this + + 2 very important decision. Thank you. + + 3 SENATOR MURPHY: Thank you Senator Carlucci. + + 4 Now I'd like to turn it over to my Chief of + + 5 Staff Matt Slater who is going to introduce our + + 6 guest speakers here tonight, and let us know that + + 7 how we are doing. + + 8 MATT SLATER: I'm Matt Slater. I'm Senator + + 9 Murphy's Chief of Staff. Thank you all for coming. + + 10 Just some ground rules for this evening. We + + 11 do a very in-depth agenda with some fantastic + + 12 speakers tonight. + + 13 We are asking for five minutes for testimony + + 14 and questions and answers for five minutes. I do + + 15 believe there is a timer here to keep us as best we + + 16 can on time for everybody's sake. + + 17 So I'm going to begin with our first speaker. + + 18 Our first speaker is not here yet, but we'll go with + + 19 the next speaker who Liam McLauglin, President of + + 20 the City of Yonkers. + + 21 LIAM McLAUGHLIN: Thank you, Matt. + + 22 Good evening, Senators. And first I would + + 23 like to start off by saying thank you for holding + + 24 this very important public hearing and listening to + + 25 constituencies about this terribly crucial issue. + + + + + + + + 11 + 1 I would also like to take a second to thank + + 2 everyone for being here. There's a very large + + 3 crowd, a lot of people here showing how much the + + 4 public cares about this issue. + + 5 As was stated, I'm the Yonkers City Council + + 6 president, Liam McLaughlin. + + 7 Back in July, I was one of the first + + 8 individuals to submit public comment against this + + 9 proposal. + + 10 And the proposed rule the United States Coast + + 11 Guard is considering establishing new long-term + + 12 anchorages along the Hudson River estuary from + + 13 Yonkers to Kingston. The Coast Guard is also + + 14 contemplating a Yonkers anchorage extension that + + 15 would cover approximately 715 acres for up to 16 + + 16 vessels with a draft of less than 35 feet for + + 17 long-term usage which commercial tankers would + + 18 basically use for rest stops. + + 19 The rule would extend significantly the + + 20 Hudson River anchorage grounds adjacent to the City + + 21 of Yonkers and other locations in order to allow for + + 22 increased shipping and on-river storage activities. + + 23 UNIDENTIFIED AUDIENCE MEMBER: No way. + + 24 LIAM HUDSON: Yes. + + 25 The proposal would effectively result in + + + + + + + + 12 + 1 continuous end-to-end barge traffic and parking + + 2 along the entire-- nearly the entirety of our + + 3 waterfront. + + 4 Yonkers is garnering national acclaim for the + + 5 work we've accomplished in rehabilitating our + + 6 waterfront. + + 7 In speaking to Yonkers residents, downtown + + 8 business groups, environmental groups, residents, + + 9 local marine pilots, marina users including + + 10 paddlers, kayakers and rowers and members of the + + 11 public who access our riverfront. + + 12 It is clear the proposed rule would severely + + 13 diminish the progress we have made in recent years. + + 14 I'd like to get into the specifics of the + + 15 proposed rule if I could. And I apologize, I'm + + 16 going read this quickly because there are a lot of + + 17 people here. But I think it's important stuff. + + 18 SENATOR MURPHY: Take your time. + + 19 LIAM McLAUGHLIN: Congress designated the + + 20 Hudson River National Heritage Area Title IX of the + + 21 Public Law 104-333 of 1996. The state's Hudson + + 22 River Greenway administers the heritage area on + + 23 behalf of the National Park Services. + + 24 As specified in the legislation, we recognize + + 25 not only the history and importance of the river, + + + + + + + + 13 + 1 but the federal government assists the state and the + + 2 communities of the Hudson River in protecting and + + 3 preserving it for the benefit of the entire nation, + + 4 especially through increased recreation and public + + 5 access and regional intermunicipal and + + 6 intergovernmental planning to increase economic + + 7 development and vitality through tourism, not + + 8 through industry. + + 9 The proposed rule is simply irreconcilable + + 10 with the adopted Hudson River Valley National + + 11 Heritage Area Management Plan approved by the + + 12 United States Secretary of the Interior which + + 13 provides a pertinent part for the recognition, + + 14 interpretation and most importantly the preservation + + 15 of sites along the Hudson River. + + 16 In fact, the rule flies in the face of the + + 17 management plan's most important objective, which is + + 18 to increase access to the river and provide + + 19 long-term sustainable heritage tourism which has + + 20 been a major economic engine for the City of Yonkers + + 21 and the Hudson Valley and will be adversely impacted + + 22 by these unsightly barges. + + 23 The Hudson River Greenway strategy to + + 24 implement the plan centers on six areas and at least + + 25 three of the strategies, resource preservation, + + + + + + + + 14 + 1 recreational uses and community impact have + + 2 apparently failed to be considered by the Coast + + 3 Guard. + + 4 One of the most important objectives of the + + 5 heritage area is to increase public access, yet + + 6 instead, the Coast Guard is seeking to increase + + 7 private usage. + + 8 Further, Congress has also designated the + + 9 Hudson River as a National Heritage river. It is + + 10 one of only 14 National Heritage rivers in the + + 11 entire country. The proposed rule again would seem + + 12 incompatible with the allowed usage and regulations + + 13 surrounding that area of federal law. + + 14 Locally there was no direct notification of + + 15 the proposed rule made to the City of Yonkers, nor + + 16 any of the affected communities along the length of + + 17 the Hudson River as required by the federal Coastal + + 18 Zone Management requirements. The Coast Guard knew + + 19 or should have known about federal Coastal + + 20 Management Zone consistency and consistency with + + 21 National Heritage Area and Natural Heritage River + + 22 laws and rules. + + 23 The proposed rules in direct conflict with 50 + + 24 years of significant effort to clean up the Hudson + + 25 River estuary and to restore its natural habitats by + + + + + + + + 15 + 1 all levels of local government and numerous regional + + 2 and community-based organizations. + + 3 The said proposal would create navigational, + + 4 health, environmental, homeland security, economic + + 5 and quality of life problems for the City of Yonkers + + 6 and should be rejected. This section of the river + + 7 is an urban river not an industrial river. + + 8 Now, relative to navigation, these anchorage + + 9 sites pose a navigational hazard to recreational and + + 10 commercial boaters who will be forced to navigate + + 11 around anchorages creating the risk of collision. + + 12 I'm not a scientist, and I'm sure we'll hear from + + 13 scientific experts tonight, but throughout the + + 14 public comment period, I've learned that our fishery + + 15 and wildlife habitat scientific research has + + 16 demonstrated in other cases that habitats of some + + 17 fish have been adversely affected by previous + + 18 anchorage sites. + + 19 The pile moorings used to create long-term + + 20 anchorages also pose an environmental risk by + + 21 disturbing sediment along the riverbed and natural + + 22 habitat of two Hudson River endangered species, the + + 23 short nose and stake sturgeon. + + 24 Regarding environmental risks, there is a + + 25 question of what the barges are transporting. + + + + + + + + 16 + 1 Vessels containing volatile crude oil and petroleum + + 2 products pose a serious health risk whereby an + + 3 anchor boat containing these hazardous materials + + 4 could catch fire or spill toxic oil in the river. + + 5 Regarding Homeland Security, owing to the + + 6 location in the largest metropolitan area in the + + 7 United States, these anchorages would present an + + 8 opportune target for terrorists, and the proposal + + 9 provides no additional mechanism for funding or + + 10 policing our waterfront. + + 11 Finally, in regard to light and noise + + 12 pollution, the proposed anchorages would take a toll + + 13 on the scenic beauty of our city and our waterfront + + 14 revitalization and tourism. + + 15 Many residents in Yonkers are concerned about + + 16 the impact, constant noise, as well as the light and + + 17 smoke from anchored barges, and many of the proposed + + 18 sites of nearby homes and local businesses. + + 19 The new expanded Yonkers anchorage ground + + 20 would accommodate up to 16 vessels for long-term use + + 21 stretching all the way up to the Hudson River from + + 22 the downtown Yonkers train station and up into + + 23 Hastings. + + 24 In Yonkers, we have begun an advocacy + + 25 campaign opposing this proposal. The Yonkers City + + + + + + + + 17 + 1 Council unanimously passed a resolution opposing the + + 2 rule in September, and we have joined all of our + + 3 neighboring communities in what we are calling the + + 4 Hudson River Waterfront Alliance. It is a group of + + 5 elected leaders from Westchester riverfront towns + + 6 and villages, and Yonkers is galvanizing their + + 7 efforts collectively and locally to prevent + + 8 additional anchorages from lining the shores of the + + 9 Hudson River. + + 10 We have launched our own petition where the + + 11 public can register their opposition to the proposal + + 12 which will be delivered to the Coast Guard and which + + 13 can be found at www.yonkersny.gov/ban/thebarges. + + 14 Yonkers is experiencing a revival, a true + + 15 Renaissance. We have already over one billion + + 16 dollars of economic development going on in our + + 17 city, and that's in addition to the vast sums of + + 18 money that have already been spent cleaning up and + + 19 restoring our Hudson River. + + 20 The shores of the Hudson should be a place + + 21 where the our residents and visitors can gather to + + 22 live, work and play. Industry says that this + + 23 dramatic expansion is necessary for safety, but it's + + 24 really about their desire to expand their industrial + + 25 use of the river, especially for crude oil + + + + + + + + 18 + 1 transport. Thank you. + + 2 In other major spills in the United States, + + 3 it has been proven crude oil cannot be cleaned up or + + 4 recovered. The environmental consequences are + + 5 simply too dire to be ignored. This river belongs + + 6 to all of us. It is not a parking lot and is not + + 7 something the City of Yonkers will support. + + 8 I'm happy to answer any questions that you + + 9 may have, and I truly appreciate your time tonight. + + 10 SENATOR MURPHY: First of all, + + 11 Mr. President, thank you for being here, coming up + + 12 from Yonkers. I have been down in Yonkers, and the + + 13 revitalization of the waterfront is tremendous what + + 14 you are doing down there. + + 15 Have you heard from the residents down there + + 16 of -- are they afraid of this coming? And the + + 17 second question I have, were you ever officially + + 18 notified about any of this? + + 19 LIAM McLAUGHLIN: No. I will answer your + + 20 second question first. We have never been + + 21 officially notified by the Coast Guard, and that was + + 22 something we really took offense to. + + 23 SENATOR MURPHY: Correct. + + 24 LIAM McLAUGHLIN: With waterfront efforts in + + 25 the works for going on 20 years now, it is really + + + + + + + + 19 + 1 unbelievable that they wouldn't take the time to + + 2 notify all the communities that would be affected. + + 3 As to the residents, particularly of that + + 4 section of town, they're completely beside + + 5 themselves. They've been drawn to that community + + 6 because of the view, because of the vistas, our + + 7 beautiful palisades. It is truly something that is + + 8 unmatched, and the thought of having barges parked + + 9 along long the entirety of our waterfront is + + 10 something that they just simply cannot believe. + + 11 SENATOR MURPHY: Senator Carlucci, anything? + + 12 Thank you for coming up here out of your way + + 13 to come up here tonight on this incredibly important + + 14 issue. + + 15 LIAM McLAUGHLIN: Thank you. + + 16 [ Applause ] + + 17 MATT SLATER: Before we bring up our next + + 18 speaker, I just want to acknowledge some of the + + 19 elected officials that are in the room today. You + + 20 are going to be hearing from quite a few of them, + + 21 but just real quickly, we have County Legislator + + 22 John Testa. John, thank you very much for being + + 23 here today. Peekskill Mayor Pete Catalina. I know + + 24 we already mentioned Assemblywoman Sandy Galef is + + 25 here joining us. And we have Croton trustees + + + + + + + + 20 + 1 Ann Gallelli and Bob Anderson. I'm not sure where + + 2 Bob is. Bob is in the back. + + 3 Next up, we are going to invite Jason Baker, + + 4 Senior Assistant to the Mayor's Office of Yonkers, + + 5 Mayor Michael Spano. Jason? + + 6 JASON BAKER: Thank you, Senator Murphy, + + 7 Senator Serino, Senator Carlucci, and thanks to + + 8 everybody who has come out tonight for being here to + + 9 talk about this important issue. + + 10 Thank you also Council President Liam + + 11 McLaughlin for your outstanding partnership in + + 12 this important issue as well. + + 13 Thanks for providing me the opportunity to + + 14 testify on behalf of Mayor Spano on the important + + 15 issue impacting the entire Hudson region. + + 16 The anchorage expansion proposal put forth on + + 17 behalf of the barge industry was done so without any + + 18 prior notification nor any discussion with the City + + 19 of Yonkers, the fourth largest city State of + + 20 New York, nor other municipalities likely to be most + + 21 impacted by this plan that seems to move our + + 22 region's most prized natural resource on a pathway + + 23 toward reindustrialization. That's why it's + + 24 critical we make sure our concerns are heard through + + 25 hearings like this, and that we also take the + + + + + + + + 21 + 1 necessary actions to organize and educate ourselves + + 2 on what this proposal could mean for our + + 3 communities, what it could mean for our environment, + + 4 the justification for choosing the areas proposed + + 5 for additional anchorage sites and what is driving + + 6 the need to line our fragile river with barges. + + 7 Since learning of this proposal which + + 8 includes the most amount of anchorages along the + + 9 shores of the City of Yonkers, Mayor Mike Spano has + + 10 organized the Hudson River Waterfront Alliance known + + 11 as HRWA as Council President McLaughlin spoke to. + + 12 It's a bipartisan coalition of local government + + 13 leaders and advocacy organizations throughout + + 14 Westchester County and beyond to unite in opposition + + 15 to proposed anchorage expansion. + + 16 In an effort to better assist and better + + 17 understand the process for rule making, facts and + + 18 impacts with the anchorages and appeal the rights + + 19 and actions our community may take, mayor Spano has + + 20 obtained special council and experts in policy and + + 21 procedure in relation to the issues presented by + + 22 this proposal. + + 23 So tonight I would like to share with you + + 24 what we know at this point, what we may not know + + 25 yet, and the actions that HRWA is taking to protect + + + + + + + + 22 + 1 our communities in the Hudson River. + + 2 As mentioned already, the proposed anchorage + + 3 expansion includes up to 16, possibly more new + + 4 anchorage and barge sites on the Hudson from Yonkers + + 5 to Kingston with the Yonkers extension encompassing + + 6 approximately 715 acres for up to 16 vessels with a + + 7 swing radius of 1200 feet per vessel. + + 8 This extension alone could result in + + 9 continuous barge parking and traffic from the + + 10 Yonkers' southern border to Dobbs Ferry. + + 11 The Montrose Point site would cover + + 12 approximately 127 for up to three vessels with a + + 13 swing radius approximately 1400 feet per vessel. So + + 14 why here? Why are these anchorages sites necessary + + 15 now. Barge industry has cited water safety but they + + 16 have safe harbor options under maritime regulations. + + 17 If safety is a primary issue, why is it only + + 18 an issue now? What is not being acknowledged by the + + 19 barge industry advocates is the link to the recently + + 20 lifted export ban on crude oil, nor is there + + 21 acknowledgment of increased crude oil transport in + + 22 the Hudson from terminals at the port of Albany. + + 23 Today it is estimated there is some 1500 + + 24 annual one-way trips of vessels carrying crude oil + + 25 on the Hudson, up 400 at the time the DEC permitted + + + + + + + + 23 + 1 the handling of crude oil at the global partners + + 2 terminal. + + 3 The Hudson is becoming a major transportation + + 4 route for crude oil, and it's crude transport that + + 5 we feel is likely fueling the need for additional + + 6 anchorages. What is the impact? Billions of + + 7 dollars in economic development have been invested + + 8 in waterfront communities like Yonkers along the + + 9 river. There is little question that continuous + + 10 barge traffic from Yonkers to Dobbs Ferry would + + 11 alter the picturesque Hudson River and Palisades + + 12 views from the waterfront. + + 13 While our experts are continuing to explore + + 14 the levels of noise and light pollution from the + + 15 barges that would result from the approval of this + + 16 proposal, you only have to visit Yonkers waterfront + + 17 in the evening when the barge is anchored along its + + 18 shores to see firsthand the amount of light emitted + + 19 from just one barge. In fact, I've seen it + + 20 personally firsthand. I've taken pictures, and we + + 21 are going to document what it looks like because + + 22 there are still some questions as to exactly what it + + 23 might look like with a line of barges along the + + 24 river. + + 25 There is great concern about the threat to + + + + + + + + 24 + 1 marinas and recreational boating as well as the + + 2 impact of future development and property owners. + + 3 Even more critical is the impact this proposal could + + 4 have on the health and efforts to protect the Hudson + + 5 River. A 1.7 billion-dollar cleanup recently + + 6 completed by G.E. and millions of additional dollars + + 7 have been invested in restoring the health of the + + 8 Hudson. + + 9 Some of the vessels traveling the river carry + + 10 as much as 12 million gallons of crude oil. That's + + 11 as much as the Exxon Valdez. In fact only a few + + 12 years ago a vessel of this size ran aground near the + + 13 port of Albany. Fortunately there was no leak but + + 14 the possibility of a spill of this magnitude on the + + 15 shallow Hudson waters is frightening and could + + 16 potentially devastate the river. Anchoring aside, + + 17 this issue alone should warrant extensive review and + + 18 consideration. + + 19 Finally, there needs to be consideration of + + 20 the placement of these anchorage sites and any + + 21 impacts on human safety on shore. The sites + + 22 selected under this proposal include the largest + + 23 city in Westchester and the fourth largest city in + + 24 the state where 200,000 people reside, as well as a + + 25 site just outside of the nuclear power plant. + + + + + + + + 25 + 1 Now common sense would suggest placing + + 2 millions of gallons of crude oil just outside of + + 3 these locations could pose unthinkable safety + + 4 concerns. And while advocates of this proposal + + 5 might suggest the unlikeliness of any threat to + + 6 civilian safety, history in New York alone would + + 7 suggest the unthinkable tragedies do happen, and we + + 8 should not be exposing ourselves to unnecessary + + 9 risks. + + 10 The question of how these barges and how our + + 11 communities would be protected must be answered. + + 12 So what have we done and what can we do? + + 13 Since organizing in August, Mayor Spano and the + + 14 Hudson River Waterfront Alliance have partnered with + + 15 organizations included Riverkeeper, historic Hudson + + 16 river towns, Hudson Valley Gateway Chamber of + + 17 Commerce and recreation boating advocates. Launched + + 18 a petition that has about 1500 signatures and + + 19 launched advocacy campaigns requesting the Coast + + 20 Guard extend the public comment period that was + + 21 recently approved and extended 90 days to December + + 22 6. They've provided web and social media resources + + 23 to each community to help in their own local + + 24 advocacy efforts. HRWA will convene and we invite + + 25 everybody to join the effort, sign the petition that + + + + + + + + 26 + 1 can be found and please submit your comments, your + + 2 public comments to the Coast Guard before the end of + + 3 the public comment period. Again that's December 6. + + 4 If you look around tonight, you will see the + + 5 entire region united in opposition to this proposal, + + 6 Democrats and Republicans, from Yonkers to Kingston, + + 7 we stand united to protect our river and our + + 8 communities. Thank you. + + 9 [ Applause ] + + 10 SENATOR MURPHY: Jason, thank you very much + + 11 for coming up here and testifying and tell the mayor + + 12 thank you very much for his support on this. I know + + 13 this has been pushed out in one and one of the big + + 14 reasons we are having this, when we reached out to + + 15 the Coast Guard, our answer was we'll have our + + 16 public hearing in the spring. Meanwhile public + + 17 hearing comment is going to close in December. So + + 18 this was important to get this done tonight, so I + + 19 thank you guys for being part of, like you said, a + + 20 bipartisan issue to do the right thing for our + + 21 communities. Tell the mayor thank you very much. + + 22 JASON BAKER: Will do. Thank you very much + + 23 for having us. + + 24 MATT SLATER: Next I'd like to invite + + 25 Cortlandt Town Supervisor Linda Puglisi. + + + + + + + + 27 + 1 SENATOR MURPHY: Madam supervisor, welcome. + + 2 LINDA PUGLISI: Senator, thank you. + + 3 Senators, thank you very much for hosting + + 4 this hearing and this forum and to the Village of + + 5 Croton-on-Hudson for allowing us to use their + + 6 village hall as well. + + 7 I am so delighted to see so many people here + + 8 today as I know you are, also. We've been joining + + 9 you at some of the press conferences, and we were + + 10 down in Yonkers about a month ago with the mayor and + + 11 members of his administration to form a coalition to + + 12 fight this. + + 13 It seems like we are always fighting + + 14 something, right? The gas line, you know, this. We + + 15 are always fighting something. And I just want to + + 16 echo what has been said before is that we in + + 17 Cortlandt have not received any official + + 18 notification from the Coast Guard. + + 19 Now, if any of us elected officials were + + 20 considering a proposal or we were going to pass a + + 21 local law or state law, we would send out + + 22 notification, correct? They have not. They didn't + + 23 ask any of us for our input, and that, in itself, is + + 24 outrageous in my opinion. + + 25 As has been said, I want to reiterate it, the + + + + + + + + 28 + 1 Hudson River was designated an American Heritage + + 2 River over a decade ago, and decades and decades of + + 3 work by environmental groups and communities have + + 4 gone into cleaning up the Hudson River. + + 5 We can't go backwards. It cannot become a + + 6 parking lot. We don't even know is how long these + + 7 barges would be allowed to anchor at these + + 8 anchorages. That's a question to be asked of the + + 9 Coast Guard. + + 10 Can you imagine, they could be there for a + + 11 long period of time. The pollution would be + + 12 amazing. + + 13 So for environmental reasons, economic, + + 14 security reasons, my colleagues on the town board + + 15 and I passed a resolution adamantly opposing this + + 16 United States Coast Guard anchor project, and we + + 17 will continue to fight it with all of you. + + 18 It's just a wonderful, bipartisan + + 19 non-partisan coalition, and I know we will prevail. + + 20 The Town of Cortlandt adopted a targeted + + 21 local waterfront revitalization Verplank waterfront + + 22 area of Cortlandt in our master plan. The proposed + + 23 anchors would not be consistent with this town's + + 24 vision or its adopted goals and policies for the + + 25 waterfront since the proposed anchors would be + + + + + + + + 29 + 1 located directly within the viewshed of the + + 2 Cortlandt waterfront park and the segments of the + + 3 historic Washington Rochambeau National Park Trail + + 4 that was designated by the National Parks Department + + 5 as you probably know. + + 6 The proposed anchorage off Montrose Point in + + 7 Cortlandt would affect views down the Hudson River + + 8 from the Westchester County from the Oscawana Island + + 9 Park and George's Park where you held a press + + 10 conference, Senator. + + 11 These waterfront parks are regional resources + + 12 that offer picturesque views of the historic Hudson + + 13 River and contain tidal wetlands, wooded trails and + + 14 boat access to the Hudson River as well as nature + + 15 study and family gatherings. + + 16 Since the proposed anchors are located + + 17 directly offshore of Cortlandt's waterfront parks, + + 18 the recreational, environmental and cultural values + + 19 of these parks would be a negative impact. + + 20 In addition, the long-term anchoring of + + 21 vessels carrying crude oil and other hazardous + + 22 materials directly offshore of our community poses a + + 23 significant safety and environmental risk to the + + 24 community since the spill of crude oil or other + + 25 hazardous materials into the river could devastate + + + + + + + + 30 + 1 the ecosystem, put people's health at risk and harm + + 2 the regional economy. + + 3 In summary, the town is requesting that a + + 4 full environmental review be undertaken of the + + 5 proposed anchors and compliance with NEPA. The + + 6 review must consider and respond to all the issues + + 7 and questions raised by all of us as part of the + + 8 public process, and that the U.S. Coast Guard hold + + 9 public meetings in our community. + + 10 We oppose this ill-conceived proposal. We + + 11 submitted our request to have the Coast Guard hold + + 12 meetings in our area immediately once we found out + + 13 about this proposal in the local newspapers. + + 14 Thank you all so much. + + 15 SENATOR MURPHY: Madam supervisor, thank you. + + 16 I don't have any questions for you because we have + + 17 talked numerous times about this already, but thank + + 18 you for coming here tonight. + + 19 Senator Carlucci? + + 20 SENATOR CARLUCCI: Thank you for testifying, + + 21 and something that you raised, the question of how + + 22 long a length of stay would a barge stay, and that's + + 23 a question that we have been trying to find the + + 24 answer to. And as far as I know, really the only + + 25 thing dictating that is the free market, right? + + + + + + + + 31 + 1 That if someone decides to park their cargo there, + + 2 they can do that, and that's the uncomfortableness + + 3 that we have that. + + 4 Okay, maybe commerce will dictate that nobody + + 5 wants to park their cargo there, but what happens + + 6 when they do? + + 7 LINDA PUGLISI: We are all for commerce. We + + 8 just don't want a parking lot in our beautiful + + 9 majestic Hudson River. + + 10 SENATOR CARLUCCI: Right. And what are the + + 11 tools we have when a barge is parked out there + + 12 staying there an absurd amount of time. + + 13 LINDA PUGLISI: We need help. + + 14 Thank you all so very much. + + 15 SENATOR MURPHY: Thank you, Madam Supervisor. + + 16 [ Applause ] + + 17 MATT SLATER: Next I'd like to invite + + 18 Eoin Wrafter. Eoin, if would you like to come up. + + 19 Eoin is representing Dutchess County + + 20 Executive Marcus Molinaro, and Eoin is the + + 21 Commissioner of Planning and Development of Dutchess + + 22 County. + + 23 EOIN WRAFTER: Good evening, Senators Murphy, + + 24 Serino, Carlucci. Thank you for the opportunity to + + 25 comment on the proposed rule establishing 10 new + + + + + + + + 32 + 1 anchors along the Hudson River between Yonkers and + + 2 Kingston. + + 3 My name is Eoin Wrafter. I'm the + + 4 Commissioner of Planning and Development for + + 5 Dutchess County. I'm offering this testimony on + + 6 behalf of County Executive Marcus Molinaro who + + 7 unfortunately could not be here this evening due to + + 8 a prior conflict. + + 9 The Hudson River with its beautiful + + 10 waterfronts, irreplaceable ecosystems and rich + + 11 history is a tremendous asset to Dutchess County, + + 12 and part of what makes us distinctly Dutchess. + + 13 Seven of the proposed anchorages are in close + + 14 proximity to Dutchess County, borders or within our + + 15 borders. Hudson River is an irreplaceable part of + + 16 our community. + + 17 The current proposal lacks sufficient detail + + 18 necessary to make a fully informed decision. The + + 19 proposal formalizes existing anchorage locations; + + 20 however, it does not detail the types of ships that + + 21 will use them, the cargo they will hold, the + + 22 duration they will stay, or the frequency with which + + 23 they will be used. All of these could and should be + + 24 studied further to identify their impacts on the + + 25 surrounding adjacent communities. + + + + + + + + 33 + 1 We join Scenic Hudson and others in + + 2 expressing concerns regarding the potential + + 3 environmental and community impacts of this proposed + + 4 project, including the potential use of the proposed + + 5 anchorage areas by vessels carrying crude oil or + + 6 refined petroleum products. + + 7 These are concerns that must be mitigated, + + 8 particularly the concern about the potential for an + + 9 oil spill or exposure to pollutants. + + 10 The Hudson River is home to many precious + + 11 species, and it must be demonstrated their aquatic + + 12 habitats will be protected. + + 13 The Hudson River is also utilized for tourism + + 14 and recreation throughout the Hudson Valley region. + + 15 The increased number of commercial vessels could + + 16 have a negative effect on the natural beauty of + + 17 these areas and potentially change the viewshed of + + 18 these iconic settings. Communities like Beacon, + + 19 Poughkeepsie and Rhinebeck have made significant + + 20 investments in their waterfronts to bring people and + + 21 development back to the river, so it is critical + + 22 that these communities and the public in general + + 23 understand how these anchorages may or may not + + 24 impact them. + + 25 We recognize that the Hudson River is a + + + + + + + + 34 + 1 significant economic engine, and the vessels + + 2 traveling along it must be able to do so safely. We + + 3 encourage efforts to improve transportation safety; + + 4 however, the process to approve these anchorages has + + 5 been disappointing at best. + + 6 The Coast Guard must take the time to better + + 7 explain this proposal, provide education answer the + + 8 questions and address concerns. + + 9 Clearly little has been done to engage in the + + 10 necessary process of including stakeholders and the + + 11 public at large, and this must be corrected. + + 12 The extended comment period and public + + 13 opportunities for comments is an important step as + + 14 the Coast Guard considers its final decision. It is + + 15 vital that these concerns are addressed and + + 16 responded to as part of an open and transparent + + 17 process. Respectfully Marcus J. Molinaro, Dutchess + + 18 County Executive. + + 19 Thank you. + + 20 SENATOR SERINO: Thank you, and actually, + + 21 Eoin lives in my district, Dutchess County, and we + + 22 have the beautiful walkway over the Hudson. Can you + + 23 imagine if we had increased traffic? + + 24 We just hosted some officials from China + + 25 today, and they loved the walk way over the Hudson. + + + + + + + + 35 + 1 And we have hundreds of thousands of people that + + 2 come to see that as well as our many historic sites, + + 3 Vanderbilt Mansion, FDR home, Mills Mansion along + + 4 the river. + + 5 Thank you very much for coming tonight to + + 6 represent our county executive. + + 7 EOIN WRAFTER: My pleasure. + + 8 SENATOR MURPHY: Please send regards to the + + 9 county executive. He was at a few of our press + + 10 conferences, and tell him thank you for the support. + + 11 EOIN WRAFTER: He regretted he couldn't come + + 12 in person. + + 13 SENATOR MURPHY: I get it. Thank you for + + 14 coming here. + + 15 MATT SLATER: Next I would like to invite Jim + + 16 Monaghan who is the Supervisor of Stony Point. + + 17 SUPERVISOR MONAGHAN: Good evening. Senator + + 18 Murphy, Senator Serino and Senator Carlucci. I + + 19 commend you and thank you for holding these very + + 20 important hearings. + + 21 And just for the people to know, Stony Point + + 22 is across the river. It's an historic river town, + + 23 it's home to the oldest lighthouse on the river. + + 24 It's home to the historic Stony Point Battlefield. + + 25 The Town of Stony Point unanimously passed a + + + + + + + + 36 + 1 resolution opposing the establishments of the + + 2 anchorage grounds in the Hudson River. + + 3 Haverstraw Bay and the vicinity stretching + + 4 from the Tappan Zee Bridge to the Bear Mountain + + 5 Bridge is the most active and congested recreational + + 6 boating area on the Hudson with more than 35 yacht + + 7 clubs, boat clubs, marinas, public boat ramps, + + 8 serving at least 4,000 boats; the north end holding + + 9 almost 200 of those slips within a mile of the + + 10 proposed anchorage site. + + 11 Stony Point Bay Channel runs through very + + 12 shallow waters serving several marinas and boat + + 13 clubs. In Stony Point, there is a very busy marine + + 14 fuel station that serves the high-speed ferries, + + 15 police and fire boats. There is an active junior + + 16 sailing camp at the Minisceongo Yacht Club in Stony + + 17 Point and an adult sailing school out of the + + 18 Haverstraw Marina. + + 19 The Hudson River Yacht Racing Association + + 20 sponsors regular races and regattas. The immediate + + 21 area surrounding the proposed Montrose site has + + 22 recreational traffic perpendicular to the barge + + 23 channel branching in all directions. + + 24 The underway barges would now be constricted + + 25 in their maneuverability due to the proposed + + + + + + + + 37 + 1 anchorage. + + 2 The river narrows between Stony Point and + + 3 Verplank Point to the north and Croton Point to the + + 4 south and is used more like a tidal lake. + + 5 If there is an oil spill in this particular + + 6 location, it will definitely spread into the tidal + + 7 wetlands running from Stony Point Bay behind the + + 8 Grassy Point along the Minisceongo Creek, and Cedar + + 9 Pond Brook to Haverstraw Cove and Bow Line Point on + + 10 the west. It would also threaten wetlands in + + 11 George's Park to the east, Lent Cove to Annesville, + + 12 Peekskill Bay and Iona Island area to the north and + + 13 Croton River wetlands to the south. + + 14 In fact, these areas are essential to many + + 15 fish, bird and other wildlife species. + + 16 Our river towns up and down the Hudson River + + 17 are finally able to develop their waterfront with + + 18 recreational parks and activities that draw tourists + + 19 to the beautiful Hudson Valley. We must inspire to + + 20 keep the rivers clean and beautiful for all our + + 21 members of the community. + + 22 The Town, we also submitted questions that we + + 23 are looking to be answered to the Coast Guard. + + 24 And once again I just want to thank you and + + 25 commend you. + + + + + + + + 38 + 1 SENATOR MURPHY: I can't thank you enough for + + 2 coming across the new Tappan Zee and being with us + + 3 here tonight and shedding some light on it. + + 4 Senator Carlucci? + + 5 SENATOR CARLUCCI: Thank you for being here + + 6 tonight, and for bringing up the point about the + + 7 recoverability that's something that has been a + + 8 pressing issue. We saw I believe it was a few years + + 9 ago in the Mississippi River a bad spill, and a + + 10 small percentage of the oil that spilled out was + + 11 recoverable. + + 12 We don't know what will be in the barges but + + 13 if it's bakken crude, we know the recoverability + + 14 rate is so minimal, so to bring that up and the + + 15 important wetlands we have along both sides of the + + 16 Hudson is an important point to raise, and I + + 17 appreciate you for doing that. + + 18 Thank you for being here. + + 19 MATT SLATER: Next I would like to invite + + 20 Deborah Milone, Executive Director of the Hudson + + 21 Valley Gateway Chamber of Commerce. + + 22 DEBORAH MILONE: Thank you, Senators, for + + 23 asking me to be here tonight, and thank you very + + 24 much for including the business community into this + + 25 public hearing. + + + + + + + + 39 + 1 I just want to say that our chamber covers a + + 2 small region. Where we serve Croton-on-Hudson, the + + 3 Town of Cortlandt, towns, hamlets and villages, the + + 4 City of Peekskill and the Town of Putnam Valley. + + 5 I'm going to read a statement that I provided + + 6 to your office Senator Murphy. + + 7 The Hudson Valley Chamber of Commerce + + 8 adamantly opposes the proposed Hudson River + + 9 commercial anchorages from Yonkers to Kingston. + + 10 Beside the obvious environmental and safety + + 11 concerns, the barges would be a visual pollutant to + + 12 our historic river at a time when we are positioning + + 13 ourselves as a tourist destination. + + 14 The river is an important economic generator + + 15 for local businesses in the Hudson Valley. + + 16 Communities with river access and vistas promote the + + 17 waterfront as a place to take in the breathtaking + + 18 panoramic views of the majestic waterway. The river + + 19 also serves as recreational resource for residents + + 20 and visitors alike which fuels the local boating and + + 21 watersports industries. + + 22 The Hudson is the centerpiece of our tourism + + 23 efforts and attracts people from all over the world. + + 24 Now we are also a tourism information center, + + 25 and we weekly receive visitors every week coming in + + + + + + + + 40 + 1 using -- looking for things to do along the river. + + 2 The City of Peekskill, I guess, Frank, how + + 3 many months ago opened up the Riverwalk? + + 4 UNIDENTIFIED VOICE: One year ago. + + 5 DEBORAH MILONE: And people coming in, it's + + 6 drawing more and more tourists and visitors from + + 7 outside of our local communities to come and walk + + 8 this beautiful river. + + 9 The tourist dollars benefit our hospitality, + + 10 food and beverage industries, as well as other + + 11 businesses that rely on visitors to survive. Here + + 12 tonight is Lou Lanzer (ph) along with Diamond + + 13 Brothers. They're redeveloping the Old Cove + + 14 property previously known as Crystal Bay and will be + + 15 reopening and renovating the Charles Point Marina. + + 16 SENATOR MURPHY: Awesome. + + 17 DEBORAH MILONE: In 2012, visitors spent + + 18 $4.75 billion in the Hudson River Valley creating + + 19 over 81,000 direct jobs and generating 318 million + + 20 in local taxes benefiting small towns as well as + + 21 larger cities. + + 22 The Hudson Valley is ranked number two by + + 23 Lonely Planet in its top 10 travel destinations + + 24 worldwide. I can attest to that. I get calls from + + 25 all over the country, and because we are regional + + + + + + + + 41 + 1 chamber, I get calls from Europe and Europeans + + 2 coming in because they want to visit the Hudson + + 3 Valley region. + + 4 Mariners have been navigating the Hudson + + 5 River for 400 years without the need for anchor + + 6 barges. This ill-conceived concept by people who + + 7 don't even live here is unfathomable at a time when + + 8 the Hudson and its shoreline communities are making + + 9 a comeback. + + 10 On behalf of our nearly 500 member businesses + + 11 and organizations, please consider the economic + + 12 environmental safety and esthetic impact this will + + 13 have on our business and residential communities. + + 14 SENATOR MURPHY: Deb, thank you so much for + + 15 coming here tonight, and thank you for all the work + + 16 that you do. + + 17 This is part of the reason why we are having + + 18 these public hearings is to figure out, there is a + + 19 security issue, there is an environmental issue + + 20 here. There is also an economic issue here, and + + 21 this is the stuff that people are investing hundreds + + 22 of millions of dollars on this waterfront, and they + + 23 could be gone tomorrow. Thank you. + + 24 DEBORAH MILONE: You are welcome. I just + + 25 want to add our chamber and the business council of + + + + + + + + 42 + 1 Westchester have come out to oppose this, and I hope + + 2 other Chambers of Commerce along the Hudson River + + 3 will come out and do the same. + + 4 SENATOR MURPHY: Thank you for being here + + 5 tonight, Deb. Thank you. + + 6 [ Applause ] + + 7 MATT SLATER: I would like to invite + + 8 Westchester County Executive Robert Astorino to come + + 9 testify. + + 10 SENATOR MURPHY: Thank you, County Executive. + + 11 You are under oath. + + 12 COUNTY EXECUTIVE ROBERT ASTORINO: Senator, + + 13 thank you very much for hosting this. Senator + + 14 Serino, good to have you here. Senator Carlucci and + + 15 Senator Murphy, thank you very much. We appreciate + + 16 this, you taking the lead on what is a very + + 17 important issue and one that wouldn't have got the + + 18 light of day if the three of you and others didn't + + 19 start talking about this because, like a lot of + + 20 things the federal government does in all of their + + 21 overreach, they tend to do things quietly and in the + + 22 dark so nobody understands what is really going on. + + 23 But this is a really important issue for -- + + 24 and you just touched upon it: Environmental issues, + + 25 for Westchester tourism issues, and public safety. + + + + + + + + 43 + 1 You know, that is a very big issue post-9/11. + + 2 I am dumbfounded. There are 2,800,000 + + 3 federal employees, and they couldn't accepted one to + + 4 Croton tonight to represent their point of view. + + 5 [ Applause ] + + 6 So a couple things I just wanted to touch + + 7 upon. You know, the beauty of itself of George's + + 8 Island or Croton Point Park or in Yonkers where + + 9 they're redeveloping the waterfront, these proposals + + 10 are one that would create, in my estimation -- you + + 11 know, I'll start with national security issues. + + 12 You know, we have soft and hard targets in + + 13 Westchester. One is pretty close to here, and + + 14 that's Indian Point, but we also have bridges and + + 15 tunnels and malls and many other areas that are + + 16 considered to be targets. And we drill for that, + + 17 and we understand what to do if we had to do + + 18 something. But the thought of having barges moored + + 19 along the Hudson River and the responsibility of now + + 20 having new targets, soft targets, would create an + + 21 additional layer of National Security and issues + + 22 that we would have to deal with locally, and the + + 23 response to that would be very difficult at best. + + 24 You know, the proposal that is for Montrose and I + + 25 know the supervisor is here, and I'm sure she will + + + + + + + + 44 + 1 talk and she has talked about this as well, + + 2 supervisor and I both understand as do you that if + + 3 you had these barges and that would be about 127 + + 4 acres would be the area that they could moor as well + + 5 as halfway across the Hudson River, you think about + + 6 that just visually how big that would be. + + 7 Forget the visual blight, but as I said, the + + 8 security issues are very, very grave or potentially + + 9 as well as the environmental issues that come into + + 10 effect here, too. So I know Yonkers and I think + + 11 representative was here, Mr. McLaughlin. + + 12 So the one problem is when you look at the + + 13 definition under the Federal Register of what + + 14 they're trying to do, it's pretty disconcerting + + 15 because they list this as long-term. There is no + + 16 definition other than long-term being longer than 30 + + 17 days. So it could be anywhere from 30 days to in + + 18 perpetuity, and that's not just stopping by. That's + + 19 moving in. + + 20 And so we have a big problem with the size + + 21 and scope of this, and that is federal government + + 22 vagueness at its worst. You cannot pin them down + + 23 for what this actually would be. So I wanted to + + 24 come here tonight. We've outlined this in letters + + 25 to the Coast Guard. We in our press conferences + + + + + + + + 45 + 1 have talked about this, and I'm glad and really + + 2 happy there is a full house here tonight because + + 3 this is an important issue that stretches from + + 4 Yonkers all the way up past our borders up through, + + 5 you know, through Orange County and above, but that + + 6 really will affect two parts of our county and + + 7 everybody in between. + + 8 So I want to thank the three of you for + + 9 taking the lead on this. To the Coast Guard and to + + 10 the federal government, shame on you for not being + + 11 here and defending your position. And though this + + 12 might not technically be a public hearing to the + + 13 federal government, the public is very much a part + + 14 of this process whether they like it or not. And so + + 15 thank you for putting the lights on in here, and + + 16 they will hear from us not just from tonight but as + + 17 we continue to go forward. So thank you. + + 18 SENATOR MURPHY: Listen, thank you so much + + 19 for coming out of your busy schedule, being here + + 20 tonight, and I know we've had a few press + + 21 conferences together just to kind of pound the drum + + 22 and make sure that people are aware of this. + + 23 The reality is that this was going underneath + + 24 the table. This was flying low. I don't think they + + 25 wanted anything to do with us knowing anything about + + + + + + + + 46 + 1 it. And I'm going to ask, were you ever officially + + 2 notified by the Coast Guard that they were + + 3 interested in doing this along our majestic Hudson + + 4 River? + + 5 Madam supervisor back there, the lady who + + 6 runs the town here, found out in the newspaper, + + 7 disgraceful. Absolutely disgraceful. . + + 8 COUNTY EXECUTIVE ROBERT ASTORINO: Like many + + 9 things the federal government does, and we have been + + 10 dealing with Housing and Urban Development, a + + 11 different agency, but it's very similar in the + + 12 treatment that the federal government gives to state + + 13 and local governments. + + 14 And in this country, the system of government + + 15 we have is a balance, supposed to be a balance + + 16 between the federal government and state and local. + + 17 But the federal government runs roughshod over that + + 18 all the time unless and until the citizens actually + + 19 speak up and fight back. And so this is a perfect + + 20 example of the big bad federal government actually + + 21 being barked at and going to be bitten by the people + + 22 who are affected by this. So thank you. + + 23 SENATOR MURPHY: The nice part about it is + + 24 that the we did pound the drum and they extended the + + 25 public comment. + + + + + + + + 47 + 1 COUNTY EXECUTIVE ROBERT ASTORINO: Yes, and + + 2 that's important. I think people need to get on the + + 3 record on something like this because if we don't, + + 4 they're going say that this may not be a public + + 5 hearing official, et cetera. It is really important + + 6 that this go to them as part of the public record + + 7 and that people, not just the elected officials, but + + 8 average citizens affected by this write a letter and + + 9 get in the public record. + + 10 SENATOR MURPHY: This is officially being + + 11 videotaped by the New York State Senate, and they + + 12 will be delivered a copy, I promise you. + + 13 COUNTY EXECUTIVE ROBERT ASTORINO: Great, let + + 14 me point right at the camera and tell you to get the + + 15 barges out of here. + + 16 SENATOR MURPHY: Thanks for coming down. + + 17 Appreciate it. + + 18 [ Applause ] + + 19 MATT SLATER: Our next speaker for tonight is + + 20 Ned Sullivan, president of Scenic Hudson. + + 21 Ned. + + 22 NED SULLIVAN: Good evening. + + 23 SENATOR MURPHY: Thank you for being here + + 24 tonight. + + 25 NED SULLIVAN: Senator Murphy, Senator + + + + + + + + 48 + 1 Carlucci, Mayor Schmidt, thank you for convening + + 2 this and being out late at night on an important + + 3 night in this nation's history. + + 4 As everybody here has said, the Hudson River + + 5 is our region's most important natural asset. It's + + 6 vital to the environment, to the public health and a + + 7 powerful engine for the economy and job creations. + + 8 You've heard from elected officials from + + 9 Westchester, and they and the state government, + + 10 federal government agencies, private organizations + + 11 like Scenic Hudson and others, partners have + + 12 invested hundreds of millions of dollars in + + 13 revitalizing our riverfronts and creating beautiful + + 14 parks and restaurants and residential developments + + 15 that bring people down to the Hudson, that make it + + 16 an asset that we are all very proud of. + + 17 Scenic Hudson has created over 60 parks and + + 18 preserves along the Hudson, here in Westchester. We + + 19 have parks in Yonkers and Irvington and Peekskill. + + 20 On the other side of the river in Haverstraw and all + + 21 the way up the river through the Kingston-Rhinecliff + + 22 area where the northern-most anchorages are + + 23 proposed. All of these would be put at risk by this + + 24 proposed rule making to park and warehouse huge + + 25 barges carrying crude oil and other chemicals on the + + + + + + + + 49 + 1 river. + + 2 As everybody knows, everybody is familiar + + 3 with the proposal, 43 berths, 10 locations. It's + + 4 really crazy and something that we are adamantly + + 5 opposed to and that we are hearing a unified chorus + + 6 of opposition to. So I commend you all for your + + 7 early action on this, for creating this opportunity, + + 8 and we will all be working together to defeat this. + + 9 The industry sponsors of this have stated + + 10 that trade will increase on the Hudson River + + 11 significantly over the next few years with the + + 12 lifting of the ban on American crude exports for + + 13 foreign trade and federally-designated anchorages + + 14 are key to supporting this trade. + + 15 So this is something that we are very, very + + 16 concerned about. That they are anticipating this + + 17 great increase in exports so we are going to be + + 18 exploiting America's crude oil for export, and the + + 19 Hudson would become the super highway for fossil + + 20 fuels, endangering all the tremendous resources that + + 21 we have, both natural and constructed along it. + + 22 So as you've heard, the proposal would + + 23 encompass 2400 acres of the Hudson, exposing it to + + 24 these industrial impacts to long-term storage of + + 25 barges carrying crude and other products. We are + + + + + + + + 50 + 1 equally concerned, as the county executive just + + 2 stated, about the length of time that is clearly an + + 3 uncertainty, but the notion that they would be + + 4 parked for 30 days or longer is unthinkable. + + 5 Each of these barges and tankers could + + 6 measure up to 600 feet. They would be visible from + + 7 homes along the river, bridges, local waterfronts, + + 8 the national historic landmark district, as well as + + 9 many popular destinations listed on the National + + 10 Register of Historic Places. + + 11 The capacity of these is comparable to the + + 12 Exxon Valdez, the tremendous devastating spill that + + 13 ravaged the waterfronts and natural resources of + + 14 Alaska. + + 15 One of these sites, of course, has been noted + + 16 would be in close proximity to Indian Point. What + + 17 are they thinking? What are they thinking? + + 18 [ Applause ] + + 19 So the proposal would jeopardize the valley's + + 20 tremendous world-renowned scenery, the basis for a + + 21 $4.7 billion tourism economy as we heard from the + + 22 Gateway Chamber representative. + + 23 The Department of State has designated scenic + + 24 areas of statewide significance. They're one of the + + 25 only places in New York State that has these + + + + + + + + 51 + 1 designations because of the incredible beauty that + + 2 we have. And these serve as the foundation, a + + 3 regulatory foundation for protection of the natural + + 4 historic and economic resources along the coast. + + 5 And these are -- New York State has delegated + + 6 responsibility for protecting these coastal areas, + + 7 so the New York Department of State has a very + + 8 important role to play here. + + 9 As we've heard, the vessels parked along the + + 10 river would bring unwanted light and noise pollution + + 11 that we associate with industrial facilities, + + 12 especially at night when deck and navigational + + 13 lights would be needed for safety. + + 14 Many of these are powered by diesel + + 15 generators that would be creating noise and air + + 16 pollution. This would, you know, truly threaten the + + 17 incredible tourism economy that is coming up. Just + + 18 imagine sitting at one of the beautiful restaurants + + 19 along the Hudson and Westchester as many of the + + 20 finest and looking out on these barges and smell the + + 21 exhaust. + + 22 How is that going to be for Friday night or + + 23 Saturday night? I think it's going to be + + 24 devastating for our tourism recreational + + 25 destinations and will really just damage the quality + + + + + + + + 52 + 1 of life that we are coming to enjoy and relish, and + + 2 that is such a magnet for tourists from all over the + + 3 world. + + 4 Our natural resources are being put at risk. + + 5 Hudson is home to over 200 species of fish including + + 6 the endangered Atlantic and short-nosed sturgeon. + + 7 The anchorages have been proposed in the + + 8 Kingston-Poughkeepsie region, known spawning grounds + + 9 for these majestic and iconic species. + + 10 They've also been, the anchorages have also + + 11 been proposed for Haverstraw Bay, the most + + 12 highly-rated significant coastal life wildlife + + 13 habitat in the Hudson River and vital over wintering + + 14 habitat for sturgeon. + + 15 So allowing the vast storage of container + + 16 ships containing millions of gallons of volatile and + + 17 harmful chemicals that are causing damage and + + 18 explosions and spills all over the country, add to + + 19 the dangers our communities face every day from the + + 20 crude oil transported by trains in poorly-designed + + 21 rail cars. + + 22 This is something that wasn't happening at + + 23 all a few years ago, and there has been a massive + + 24 increase. And the entire region absolutely lacks + + 25 the safeguards to prevent and respond to spills. + + + + + + + + 53 + 1 The legislature and the governor have taken + + 2 some action over the last several years. There have + + 3 been budget allocations of a couple million dollars + + 4 to plan for spill response and to coordinate with + + 5 the federal agencies on the national contingency + + 6 plan. But we know they're inadequate. + + 7 We know that the response, the Coast Guard + + 8 response vehicles are based down in New York harbor + + 9 and that it would take hours, hours for them to get + + 10 to a spill upriver. + + 11 We know that the Hudson is tidal, so a spill + + 12 would -- the product, the contaminants would go up + + 13 and down with the tide, ebbing and flowing and + + 14 contaminating one waterfront after another. We know + + 15 that drinking water supplies, and there are at least + + 16 half a dozen in the upper reaches of the Hudson, + + 17 that are in the direct line, directly adjacent to + + 18 the anchorage areas. + + 19 We are talking about beach, we are talking + + 20 about water supplies, wetlands, aquatic life, you + + 21 know, all the way from Albany to New York Harbor + + 22 because of the flow of the river and of the tides. + + 23 So the proposed anchorage importantly is + + 24 inconsistent with New York State's coastal + + 25 management program, and there are specific policies + + + + + + + + 54 + 1 relating to aquatic habitat, to coastal development, + + 2 to public access, to recreation and historic and + + 3 scenic resources, to water and air resources, + + 4 wetlands and others. + + 5 These are highly detailed rules that have + + 6 been very important in stopping other ill-conceived + + 7 proposals over the years. + + 8 So the Department of State plays an important + + 9 role here. I personally briefed the Secretary of + + 10 State on this matter, and I would encourage you to + + 11 work with her and other D.E.C. and other state + + 12 agencies on this. But the Coastal Zone Management + + 13 rules are delegated to New York State, so these are + + 14 federal and state rules that are brought into play + + 15 here, and this is the area that we are going to be + + 16 focusing on. + + 17 So in summary, Scenic Hudson opposes this + + 18 proposal. We urge you to work with our U.S. + + 19 Congressional and Senate representatives, the + + 20 D.E.C., the Department of State and others to block + + 21 this regulatory misstep. + + 22 We are heartened by instruction of + + 23 legislation by Congressman Sean Patrick Maloney that + + 24 would prevent the permitting of these anchorages + + 25 along the Hudson and urge you to support this + + + + + + + + 55 + 1 initiative to try to get a Senate Bill that would + + 2 enable us to move through the Congress as rapidly as + + 3 possible. + + 4 And finally, it's important for everyone in + + 5 the room to tell your friends about the December 6 + + 6 deadline, and to file your comments and to take all + + 7 possible actions to protect our river and valley + + 8 from this dangerous proposal. + + 9 I've included with the testimony I gave you + + 10 photo simulation that we've made of just four of, + + 11 let's see, five of the barges parked on the + + 12 Westchester waterfront. This would be kind of the + + 13 northern area of the Yonkers extension. They're + + 14 parked right in the middle of the river so I have + + 15 provided them to you for your review. + + 16 You can just get a sense of what a bad idea + + 17 this is and why we should all fight this with + + 18 everything we've got. Thank you very much. + + 19 SENATOR MURPHY: Thank you, Ned. + + 20 [ Applause ] + + 21 MATT SLATER: Our next speaker is Edward + + 22 Kelly. Ed is the Executive Director of the Maritime + + 23 Association Port of New York and New Jersey. + + 24 SENATOR MURPHY: Thank you, Ed. Appreciate + + 25 you coming tonight. + + + + + + + + 56 + 1 EDWARD KELLY: My pleasure. Good evening, + + 2 Mr. Chairman, Senator Murphy, esteemed panelists, + + 3 ladies and gentlemen. + + 4 My name is Edward J. Kelly, and I'm the + + 5 Executive Director of the Maritime Association of + + 6 the Port of New York and New Jersey. In + + 7 coordination with the Hudson River Pilots + + 8 Association and the American Waterways Operators, + + 9 our Tug and Barge Committee is the one who made the + + 10 proposal to the U.S. Coast Guard to establish new + + 11 designated anchorages on the Hudson River that + + 12 proposal has been published as U.S. Coast Guard + + 13 Docket 2016-0132 as an advance notice of proposed + + 14 rule making pertaining to the establishment of these + + 15 designated anchorages. + + 16 We have submitted written testimony that will + + 17 give some additional information pertaining to + + 18 economic impacts, the thousands of jobs that are + + 19 created literally billions of dollars of taxes paid + + 20 by this industry to federal, state and municipal + + 21 organizations. But we also would like to address, + + 22 we are available to anyone who would like to talk to + + 23 us about these facts about what these proposals mean + + 24 and to certainly clarify and hopefully dispel the + + 25 many misconceptions and misstatements that have been + + + + + + + + 57 + 1 made in regards to this proposal. + + 2 We have seen several published websites, + + 3 et cetera, that contain erroneous information. We + + 4 stand ready to meet with anybody who would like to + + 5 meet with us. I have already met with Ulster county + + 6 legislative groups, met with several people. Our + + 7 name, address, telephone number and emailing listed + + 8 on the proposal, and anyone who would like to meet + + 9 with us further, I have a business card and would be + + 10 more than happy to arrange a mutual time, date and + + 11 flies have a discussion. + + 12 I started my day in Philadelphia this morning + + 13 with another meeting. I think it's important enough + + 14 I made it up here tonight to attend this hearing. + + 15 SENATOR MURPHY: Thank you. + + 16 EDWARD KELLY: We have very firm beliefs this + + 17 is good for the economy, for the region, for + + 18 security. + + 19 So if I could proceed just quickly. Our + + 20 operators believe that we have a tremendous + + 21 dedication and responsibility for the protection of + + 22 human life, of property, and the ecology of this + + 23 river and the ecosystem that it contains. We feel + + 24 that these anchorages are all about safety. These + + 25 anchorages, since this guy named Hudson stumbled + + + + + + + + 58 + 1 across this river 400 years ago, this river has been + + 2 used for commercial navigation, and ships have + + 3 regularly anchored in many of the areas that are + + 4 currently proposed to be designated formally. + + 5 We are seeking formal designation because the + + 6 river has become busier, and we would like to have + + 7 designated, safe, supervised locations for anchorage + + 8 so that people are not forced to operate in unsafe + + 9 conditions. + + 10 If I could just run through a couple what + + 11 have we believe are some of the facts involved in + + 12 this, and then specifically a few things I've heard + + 13 addressed tonight regarding sturgeon, long-term + + 14 anchorages, et cetera. We would like to talk about + + 15 how these anchorages will help to enhance the + + 16 safety, security and environmental stewardship of + + 17 the vessels and the waterways in which they operate. + + 18 Anchorages are good for safety. That's what + + 19 this is all about. A safe place to anchor is + + 20 essential to the safety of the crew, the vessels, + + 21 other operators, property and cargoes as well as the + + 22 health of the river environment itself. Vessels are + + 23 forced to anchor for many safety reasons, including + + 24 fog, weather conditions, equipment issues, ice and + + 25 many other reasons. + + + + + + + + 59 + 1 Anchorages also allow vessels to wait to + + 2 navigate at first light and at high tide when it is + + 3 the safest potential to operate in the river. A + + 4 safe place to anchor is essential to safety. + + 5 The proposed anchorage sites have been + + 6 selected due to a variety of physical + + 7 characteristics that facilitate safety including the + + 8 depth of the water, shelter from currents, width of + + 9 the river, the interval of spacing and the location + + 10 of what have been usual and customary anchorage + + 11 locations. + + 12 These proposed anchorages are intended to + + 13 simply formalize decades, if not centuries, of safe + + 14 industry practice and give the U.S. Coast Guard + + 15 oversight of these anchorage areas. + + 16 We have heard about long-term parking lots. + + 17 Anchorages are not parking lots. Vessels are + + 18 typically anchored for very limited period of time, + + 19 usually between four to six hours. They're always + + 20 manned during this time and lit in accordance with + + 21 U.S. Coast Guard regulations. + + 22 Anchorages do not require construction or + + 23 placement of infrastructure in or around the river. + + 24 The long-term expression is simply a Coast Guard + + 25 expression that says it's not a temporary. In other + + + + + + + + 60 + 1 words, it's there. It will be designated on federal + + 2 charts, navigational charts, and they will be there + + 3 until change. It does not mean the vessels will + + 4 stay there for long periods of time. + + 5 If anyone wants to find the truth, you follow + + 6 the money. It makes absolutely no economic sense to + + 7 store any type of product, especially barges, on a + + 8 river location. The cost to do so is magnitude + + 9 differences compared to storing it in shoreside + + 10 facilities. + + 11 If you store any type of product, but a lot + + 12 of people are talking about oil, and in particular + + 13 oil. Barges have to be manned. Tugs have to be + + 14 anchored to accompany them. Those crews have to be + + 15 paid, the cost of the tug has to be paid. Barge + + 16 hire has to be paid. + + 17 Transportation companies only make money when + + 18 their vessels and cargoes are moving. It makes no + + 19 sense to spend that kind of money to hold a product + + 20 that could very easily have been held in storage + + 21 facilities either downriver in the port or upriver + + 22 in the various terminals and storage locations that + + 23 are around the river. It makes no economic sense. + + 24 People will go out of business in a short + + 25 period of time by trying to do ridiculous economic + + + + + + + + 61 + 1 things. There is no economic advantage. + + 2 Anchorages are good for the environment. The + + 3 reason we say that is because environmentally, our + + 4 industry is subject to numerous environmental + + 5 regulations including Oil Pollution Act which as of + + 6 last year and since last year has required all + + 7 barges to be double hulled. The anchorage areas + + 8 would further serve to protect the environment by + + 9 allowing operators to avoid navigating during unsafe + + 10 conditions that could lead to accidents and/or + + 11 spills. + + 12 The environmental benefit of maritime + + 13 transportation is supported by the industry's track + + 14 record. The latest stat available from the U.S. + + 15 Army Corps of Engineers shows there has been no oil + + 16 leaked from tank barges into the Hudson since data + + 17 tracking began with the Army Corps of Engineers. + + 18 Water-borne transport reduces roadway congestion and + + 19 emissions by reducing the need for truck transport. + + 20 The New York City Economic Development Corp + + 21 did a study that found that in one single year, + + 22 water-borne barge transportation eliminated 3.1 + + 23 million truck trips within New York City alone. + + 24 This is because the efficiency of maritime + + 25 transportation, a typical inland barge, has the + + + + + + + + 62 + 1 capacity 15 times greater than one rail car and 60 + + 2 times greater than one semitrailer truck. + + 3 By the way, I don't believe a 600-foot barge + + 4 exists on this planet, but people keep talking about + + 5 it. + + 6 Anchorages are good for security. From a + + 7 security standpoint, vessels are required to comply + + 8 with U.S. Coast Guard-approved security plans, and + + 9 all professional Mariners are required to be U.S. + + 10 citizens who are required to hold transportation + + 11 worker identification credentials issued by the TSA + + 12 which provide digital photo I.D. and require + + 13 extensive criminal and drug background checks. + + 14 Additionally U.S. Coast Guard regulations + + 15 require all water-borne crew to hold proper Coast + + 16 Guard licenses, ratings and training certifications. + + 17 These measures are intended to strictly control + + 18 access to vessels at all times including while + + 19 they're at anchor. + + 20 Anchorages are good for the economy. + + 21 Economically commercial vessel are engaged in moving + + 22 vital cargoes to communities along the Hudson. Most + + 23 of the vessels that would use these anchorage areas + + 24 are vessels already engaged in moving products + + 25 directly to consumers. Refined oil products like + + + + + + + + 63 + 1 home heating oil, the gasoline that powers your car, + + 2 the fuel that powers the power companies that make + + 3 the electricity go on, and it also is in the best + + 4 economic interest of maritime companies that vessels + + 5 are engaged in transporting cargo, not waiting at + + 6 anchor. + + 7 Other majority of the cargoes include + + 8 aggregate which comes downriver, an export commodity + + 9 moves from upstate New York to the lower place for + + 10 road bed construction. + + 11 We heard there is a little bit of ice and + + 12 snow upstate New York. We move sand and salt + + 13 upriver. Construction materials move upriver, + + 14 recyclables are moved up and down the river for more + + 15 economic disposal. + + 16 Designation of anchorages by the Coast Guard + + 17 will neither directly increase nor decrease cargo + + 18 shipments on the river. Only the actual consumption + + 19 of gasoline, heating oil, sand, construction + + 20 materials, et cetera, by the region's residents will + + 21 have an impact on the amount of cargo moving. + + 22 Based on that, it's clear that the creation + + 23 of federally-designated anchorages under U.S. Coast + + 24 Guard supervision is in the interest of all the + + 25 parties. We would like to have support on this. + + + + + + + + 64 + 1 Our people have operated on this river for + + 2 centuries. We have operated safely, economically, + + 3 and we have moved the goods that provide the + + 4 American way of life. + + 5 As long as people want to have home heating + + 6 oil, as long as they want to have electricity, they + + 7 need salt, sand and the other commodities these + + 8 barges move, it makes sense to let them operate + + 9 safely. + + 10 If you were driving down the road and you + + 11 were faced by a very severe fog or extremely heavy + + 12 thunderstorm, what do you do? Do you keep driving + + 13 and just say I hope we don't hit anything and kill + + 14 people, or do you look for a safe way to slow down + + 15 and operate safely, pull off the road. + + 16 What if you had ice coming down the river + + 17 facing you? You would want a safe place to pull + + 18 out. That's what this is all about. We would be + + 19 more than happy to address sturgeon. If anybody is + + 20 interested, I have a copy and I have actually read + + 21 the study that was commissioned by the + + 22 New York State, the Hudson River Foundation and + + 23 several other people. It's here. I've spoken to + + 24 the authors. I'm not just listening to the buzz and + + 25 misinformation. + + + + + + + + 65 + 1 This report does conclude that anchors make + + 2 marks on the bottom, and it says the anchors silt + + 3 over rather quickly because the river moves and the + + 4 bottom shifts. + + 5 They have not proven this is detrimental. In + + 6 fact, a quote from the author, and I will provide it + + 7 to whoever would like it says at this point, we + + 8 cannot say if there is any impact to the bottom + + 9 habitat with regard to sturgeon, and we have been + + 10 very careful to stay away from making these + + 11 linkages. I hear people laughing. Here is the + + 12 report with the quote from the authors. + + 13 This is what they allege they're talking + + 14 about. I would like people to read this report, not + + 15 just make distorted comments about it. + + 16 The Coast Guard procedures, we agree, are + + 17 awkward at best. This is typical federal rule + + 18 making procedures. All legislators do have staff + + 19 that monitor the federal register. That's how you + + 20 find out what these crazy feds are doing in your + + 21 areas. They do not reach out to individual people. + + 22 They expect that you should reach out to them. It's + + 23 published in the Federal Register. + + 24 The typical pattern on this on any federal + + 25 regulation whether you are dealing with the corps or + + + + + + + + 66 + 1 any agency, there is a public comment period. + + 2 Because of the pushback and information that people + + 3 are seeking, the public written comment period was + + 4 extended. + + 5 On the conclusion of that, they will gather + + 6 all of these. At last count I think it was 3600 + + 7 public comments. They will sort them, evaluate + + 8 them. Based on that they will determine, which in + + 9 this case I'm sure they will, that they need to have + + 10 public hearings. + + 11 They will publish the public hearing + + 12 information in the Federal Register. They will not + + 13 reach out to people. They don't reach out to me + + 14 anymore than they reach out to you. There will be + + 15 public hearings. There will be a series of these + + 16 probably in the spring because the Federal Register + + 17 has posting periods. + + 18 People will come out. We expect to have a + + 19 full discussion. We would like to talk about facts, + + 20 not misconceptions or distortions. We don't want to + + 21 see websites showing 4500 TEU container ships that + + 22 physically could not fit past the George Washington + + 23 bridge and never would economically. + + 24 The Exxon Valdez. I knew the captain of the + + 25 Exxon Valdez. I'm a maritime captain. I sail these + + + + + + + + 67 + 1 ships. Anything the size of the Exxon Valdez could + + 2 never make it up the Hudson River. There is not + + 3 enough depth. It would run aground long before it + + 4 hit Yonkers. These things require deep draft. + + 5 Misconceptions, distortion of fact. + + 6 I have a pocket full of business cards if + + 7 anybody would like to have further discussions. I + + 8 don't want to take up too much time. We are trying + + 9 to keep it on schedule. I've made my notes, and + + 10 I've seen quite a few things. There are no parking + + 11 lots. It makes no economic sense. Long-term is + + 12 just a definition the Coast Guard uses as opposed to + + 13 like when we have the fourth of July fireworks, they + + 14 establish temporary anchorages so that people can + + 15 park their recreational boats and watch the + + 16 fireworks. A long-term designation means it gets + + 17 put on a chart. + + 18 So anyway, if anybody would like to further + + 19 discuss this, we've got facts on this. We would be + + 20 more than happy to meet with anybody that would ask. + + 21 I have to say that no one has asked to meet with us. + + 22 Anybody that has, we meet with them. + + 23 We are here tonight. We will be any place + + 24 else that anybody wants us to be to further pursue + + 25 this. + + + + + + + + 68 + 1 SENATOR MURPHY: Ed, thank you for coming up + + 2 from Philadelphia and to this incredibly important + + 3 meeting for our community. + + 4 EDWARD KELLY: Certainly. + + 5 SENATOR MURPHY: This is the first + + 6 information that has ever been told to me. We have + + 7 been trying to reach out. You just explained a lot + + 8 of stuff that we've all had questions about, and + + 9 there is going to be a bunch more trust me. There + + 10 is going to be a bunch more. This isn't the end of + + 11 it. You are the first representative to come up to + + 12 answer some of the questions we've had. We can't + + 13 get the Coast Guard here. + + 14 EDWARD KELLY: Likely you won't get the Coast + + 15 Guard here. + + 16 SENATOR MURPHY: That's disturbing. Yet + + 17 they're making the rules and regulations on our + + 18 Hudson River for us to live by, for us business + + 19 owners, for us constituents, for our people to live + + 20 there yet they're not even coming to a meeting for + + 21 us. That's unacceptable. + + 22 EDWARD KELLY: Senator, I agree federal + + 23 procedures are awkward and annoying to everybody. + + 24 Ourselves included. We deal with the Coast Guard, + + 25 the Corps of Engineers and NOAA. We will hold + + + + + + + + 69 + 1 ourselves open as an industry to anybody that would + + 2 like to meet with us. Any further hearing, if + + 3 people, legislators or community groups wish us to + + 4 sit down with them, that's what we do. That's what + + 5 I will do. We will be there. + + 6 SENATOR MURPHY: I can't thank you enough for + + 7 coming here and explaining some of this because + + 8 we've had a heck of a lot of questions, and + + 9 obviously, there are a lot more. But being here + + 10 tonight, coming out of your way, this is a very, + + 11 very, very important meeting to all three of us who + + 12 represent close to a million people, and that's what + + 13 we do. We represent the people. And when the Coast + + 14 Guard can't get here to answer some of the questions + + 15 for a million people that we represent, that's + + 16 disturbing. + + 17 EDWARD KELLY: Yep. I agree, but on behalf + + 18 of the industry that would use and need these + + 19 anchorages, as I say, we are available if anybody + + 20 would like to stop by. I have a pocket full of + + 21 business cards and would be more than happy to + + 22 arrange a drill down, talk about any of the facts. + + 23 We clearly understand communities have a very + + 24 valid concerns. We need to discuss back and forth + + 25 how to make this proposal work because the reality + + + + + + + + 70 + 1 is the business is moving on the river right now + + 2 today, and we want to make sure it operates safely. + + 3 This is not a question of if they designate + + 4 this, and again, there is nothing to build. It's + + 5 just a designation on the chart. There is no + + 6 infrastructure. It doesn't touch the bottom. + + 7 There's nothing, just a designated to drop an anchor + + 8 and some of the large areas don't mean the whole + + 9 area will be used. + + 10 It means as we mentioned, the Hudson is + + 11 tidal. The reason we want to establish anchorages + + 12 so vessel owners/operators will know where it is + + 13 safe so they can swing without hitting shallow + + 14 ground causing spills, etc, and very frankly, we + + 15 don't want to use these anchorages if we don't have + + 16 to. We don't make money when the barges are not + + 17 moving, so I don't want to take up too much time. + + 18 SENATOR CARLUCCI: I have a couple of + + 19 questions. Thank you for being here, and thank you + + 20 for your passion and your profession. I guess what + + 21 I'm still unclear about, and I really appreciate you + + 22 shedding some light is, what has changed? + + 23 We talk about centuries of maritime passing + + 24 through the Hudson River, and we talk about the + + 25 extremes. Of course, no one wants captains to be + + + + + + + + 71 + 1 sailing down the river when there is ice in the + + 2 river, or there are storms. And I guess what I'm + + 3 still uncomfortable about is what has changed? What + + 4 have we done up until this point? And why not have + + 5 any standards in terms of hey, when do you dock, or + + 6 when do you lower the anchor, and how long you stay + + 7 there. + + 8 I know you talk about economic sense, but + + 9 being in the senate for six years, I've seen some + + 10 things that might not make economic sense, but if we + + 11 leave it solely up to the free market and to + + 12 commerce to dictate what is done, it leaves us + + 13 holding the bag in some circumstances. + + 14 So I can point to a few examples of that, and + + 15 that's just where have I some problems, so maybe you + + 16 could shed a little more light on what has changed + + 17 and why now. + + 18 EDWARD KELLY: What has changed is we have + + 19 had situations where there is a Champlain Hudson + + 20 power exchange they're looking to lay a cable down + + 21 the river. Unless there is a designated anchorage, + + 22 we have no voice in saying don't put cables there. + + 23 Cables could foul anchors. We could have problems + + 24 with that. There is increased usage. + + 25 SENATOR CARLUCCI: Just to expand on that. + + + + + + + + 72 + 1 The Champlain Power Express, if we have this federal + + 2 designation, they cannot build... + + 3 EDWARD KELLY: They can't build through an + + 4 anchorage. + + 5 That anchorage would not be used, drop an + + 6 anchor and pull a cable up and disrupt the power + + 7 supplies. That's one of the considerations. + + 8 Another consideration is by designating this on + + 9 charts, all Mariners, recreational, commercial, know + + 10 that that is a safe place to anchor, and there may + + 11 be vessels there. They will be able to calculate + + 12 the swing radius so they know the movement of + + 13 vessels at anchor could be. + + 14 And we've worked with Sandy Galef to promote + + 15 legislation in New York State to increase + + 16 recreational boater education and awareness. We + + 17 want these people responsible. We want them well + + 18 educated. They need to understand charts and how + + 19 this stuff works. So there is a lot of things. + + 20 What has changed? We need these because the + + 21 river has had more usage. There are more + + 22 recreational people out there. Some businesses + + 23 increased, and we need to have a spot -- you can see + + 24 with the push back we are getting right now, for + + 25 basically what are established anchorages. + + + + + + + + 73 + 1 SENATOR CARLUCCI: That's the point I'm + + 2 trying to understand. + + 3 SENATOR MURPHY: Excuse me. We are going + + 4 respect everybody here tonight, okay? We'll let + + 5 Mr. Kelly finish. + + 6 SENATOR CARLUCCI: That's the hard part I + + 7 have communicating to residents is understanding and + + 8 knowing tug boat captains that sail up and down the + + 9 Hudson River, that what has changed? + + 10 Because in an emergency situation or when + + 11 there is heavy fog or ice on the river, when it's + + 12 unsafe, we expect them to anchor. + + 13 And we know, like you talked about the + + 14 licensing and credentials that our captains need, + + 15 that they would understand and know the Hudson + + 16 River. So it is not all adding up to me in terms of + + 17 if you can anchor now, what has changed, or can now + + 18 anchor now? Are they doing it illegally maybe shed + + 19 light on that. + + 20 EDWARD KELLY: In any emergency situation, + + 21 you can anchor wherever you feel you have to anchor. + + 22 The question is if you know there is a designated + + 23 anchorage, say you have gear adjustments or things + + 24 to make, these are designated spots that have proven + + 25 to be safe. + + + + + + + + 74 + 1 SENATOR CARLUCCI: So we could expect, I + + 2 guess that now with these designations, that this + + 3 would be a destination for cargo traveling up the + + 4 Hudson. So we could expect that they will be + + 5 filled. + + 6 EDWARD KELLY: Not filled but they will be + + 7 used. And these as I say, because of the + + 8 geophysical characteristics of these particular + + 9 locations that are being proposed, these are the + + 10 places that are already currently in use because it + + 11 is deep enough so that they don't run aground as + + 12 they swing, where there is lesser impact to the + + 13 current, where there is not a very narrow section of + + 14 the river so it precludes other people getting + + 15 around them. + + 16 There are a lot of factors that go into + + 17 finding a good place to anchor without hurting the + + 18 bottom, et cetera. + + 19 SENATOR CARLUCCI: As the regulations as we + + 20 read them say three barges could be anchored at any + + 21 one time. + + 22 EDWARD KELLY: Could be. In some of them + + 23 there is space for one, some two, some four. + + 24 SENATOR CARLUCCI: The ones in this region in + + 25 the Westchester-Rockland area, we are talking about + + + + + + + + 75 + 1 three each. + + 2 And so you don't see that now. We would only + + 3 see one maybe sporadically anchored at a time? + + 4 EDWARD KELLY: And the provision that they + + 5 exist does not mean that they will all be used. And + + 6 to go back to the parking lot concept, it doesn't + + 7 make economic sense. + + 8 SENATOR CARLUCCI: So why allow that in the + + 9 regulations or put that in the regulations? Are + + 10 they expecting some explosion in terms of travel + + 11 along the Hudson? + + 12 Are they expecting, you know, we talk about + + 13 the 600 foot barge, and I guess I might have read it + + 14 wrong, but I thought the requirements or the + + 15 regulations were allowing for a barge up to 600 feet + + 16 to be parked. + + 17 EDWARD KELLY: Those are different types of + + 18 vessels than are barges. There are barges that move + + 19 and carry cement. There is a lot of cement that + + 20 moves up here, again construction materials that + + 21 move in what are called coastal vessels. + + 22 SENATOR CARLUCCI: So a coastal vessel up to + + 23 600 feet could be parked at one of these spots. + + 24 EDWARD KELLY: Yes. Depending on the amount + + 25 of anchor swing and provision, how much space would + + + + + + + + 76 + 1 be needed. + + 2 SENATOR CARLUCCI: Another thing I have + + 3 trouble with, caring deeply about the Hudson and + + 4 wanting to preserve its integrity. You mention + + 5 about oil spills or oil leakage in the Hudson, that + + 6 none has been documented. + + 7 How far back are we talking about that + + 8 documentation? + + 9 EDWARD KELLY: That goes back to the corps + + 10 records which are not that far back. It goes back + + 11 to about 2010. + + 12 SENATOR CARLUCCI: 2010. + + 13 EDWARD KELLY: Six years of impeccable safety + + 14 record. It doesn't go back that long. + + 15 These are the same type of anchorages and the + + 16 same type of commercial operations that are taking + + 17 place in other federal navigable channels, the + + 18 Mississippi, Columbia, so this is not unique to the + + 19 Hudson Valley. + + 20 SENATOR CARLUCCI: I appreciate your + + 21 answering these questions, and it is a delight for + + 22 me to have someone who can answer these with + + 23 integrity and authority. The other question I have + + 24 is just that many of our elected officials and + + 25 residents have brought up is the safety. And the + + + + + + + + 77 + 1 fact that we have to deal with the reality of soft + + 2 targets in our community and just the advent of what + + 3 type of materials are going to be transported along + + 4 the river. + + 5 Is there any type of mechanism where local + + 6 law enforcement, the community will be notified + + 7 about what type of materials are being parked in + + 8 their community? In terms of toxins or crude... + + 9 EDWARD KELLY: We can and do work with state + + 10 and municipal entities, their abilities to deal with + + 11 different issues, whether they be security based or + + 12 environmentally based. + + 13 Our industry and Coast Guard and Corps of + + 14 Engineers, we work extensively with security-related + + 15 things. Obviously if we want to talk about + + 16 security, I'm from New York City. We know about + + 17 9/11. We don't just talk about it, and we have + + 18 existing security protocols that integrate local, + + 19 municipal and state first responders, OEMs, + + 20 et cetera. + + 21 If there are any failures on the part of + + 22 communities or state issues to take a look and + + 23 consider they ought to engage with people running + + 24 and how can we best work together, this industry is + + 25 working together. We have operators that work this + + + + + + + + 78 + 1 river work in New York City and the harbor. We are + + 2 the people that lifted the people off Manhattan + + 3 Island when the towers came down and it burned. + + 4 Bee don't want to talk about security. We'll + + 5 show you security. We take it seriously in this + + 6 business because we lived with it. We had to take + + 7 those people off that burning island that day, and + + 8 we have built that. + + 9 Captain Sully, those people didn't get off + + 10 that plane by accident. It's because we have + + 11 trained and worked and coordinated our commercial + + 12 navigators with OEM, New York Department of Fire, + + 13 police, all those people. That's how those people + + 14 got off that plane because we drill and practice + + 15 that, and your local communities should do it as + + 16 well. + + 17 And frankly being very blunt, New York State + + 18 in this area doesn't spend enough money on doing + + 19 what they ought to do to do those things. We stand + + 20 ready to work with people. We hope the communities + + 21 will reach out to us and other organizations like us + + 22 to enhance the security and ecology of this river. + + 23 It's important to us. We hope it's also important + + 24 to you. We don't talk about it. We live security. + + 25 SENATOR CARLUCCI: Well, I appreciate your + + + + + + + + 79 + 1 commitment to security and that's great to hear. + + 2 EDWARD KELLY: Every one of our professional + + 3 Mariners as I've said is a U.S. citizen, they have + + 4 extensive background checks. We have more + + 5 background checks than you need to be a member of + + 6 NYPD. + + 7 We are federal. It's done by TSA. We have + + 8 transport worker identification credentials with + + 9 embedded credentials, picture I.D.s. + + 10 We take this seriously. Every one of our + + 11 Mariners has to be certified by Coast Guard. Every + + 12 vessel has to have a vessel security plan. Every + + 13 company has to have a company security plan. Every + + 14 facility needs a facility security plan that is + + 15 inspected physically in person by Coast Guard at + + 16 least once a year. Our vessels are certified. We + + 17 are willing to talk about any of this. + + 18 SENATOR CARLUCCI: I appreciate your + + 19 commitment to safety and security. I think what + + 20 would be, what is refreshing to hear is the + + 21 commitment to work with local law enforcement to + + 22 share that information with what is being stored out + + 23 in the river. + + 24 EDWARD KELLY: Absolutely. + + 25 SENATOR CARLUCCI: Thank you. + + + + + + + + 80 + 1 Senator Serino. + + 2 SENATOR SERINO: Ed, I think you can + + 3 understand our concern especially with the proximity + + 4 to Indian Point nuclear power plant. I think you'll + + 5 hear that from the crowd as we've spoken about + + 6 tonight the crazy world we live in now, you never + + 7 know what to expect so that is a verified fear that + + 8 we all have. + + 9 Actually you've given us a lot more + + 10 information tonight than we've heard and thank you + + 11 for that. And I think that's what everybody feels + + 12 the same way because we didn't have any answers. We + + 13 didn't even have somebody to ask a question to. I + + 14 actually did meet with the Coast Guard, and I asked + + 15 them the question about having five barges like tied + + 16 up or anchored together, and he couldn't answer yes + + 17 or no because I don't think that they have a plan, + + 18 and that's a huge part of the problem, and the + + 19 unknown is really scaring the daylights out of + + 20 everybody. + + 21 EDWARD KELLY: We can help you get to the + + 22 right people in Coast Guard. Again, the people that + + 23 just are the administrators and rule making are not + + 24 the people that are waterways management security. + + 25 So you might have gotten into the wrong people in + + + + + + + + 81 + 1 the Coast Guard. So we know there are people in + + 2 Coast Guard that can and will respond to these types + + 3 of questions. + + 4 SENATOR SERINO: As you mentioned that they + + 5 don't notify us but, you know, they should have + + 6 notified us so we could talk to the public, and let + + 7 them know what we found out, or at least that there + + 8 was a notification that this was even something that + + 9 was on the radar. + + 10 EDWARD KELLY: Sure. + + 11 SENATOR SERINO: We didn't know about that. + + 12 EDWARD KELLY: They use the Federal Register. + + 13 SENATOR MURPHY: Three weeks, 2,000 people + + 14 signed a petition. We got questions. We had to + + 15 really kind of start saying -- they wanted to hold a + + 16 public hearing in the spring, the Coast Guard. + + 17 EDWARD KELLY: Probably what will happen. + + 18 SENATOR MURPHY: In the spring. You know + + 19 public comments ending December. + + 20 EDWARD KELLY: Public written comments and + + 21 the Coast Guard procedures which are standard + + 22 federal procedures, once the written comment period + + 23 concludes, they evaluate the written comments. They + + 24 put them in the proper piles and however they sort + + 25 them out, and based on this, certainly on this + + + + + + + + 82 + 1 issue, they would say there is sufficient concern + + 2 that they would schedule public hearings. They put + + 3 that into the Federal Register to notify people. + + 4 They pick out locations scattered throughout the + + 5 region. They would establish, get places and they + + 6 will be open for public hearings, but because of the + + 7 public notice period through the Federal Register, I + + 8 would expect the real public hearings would start in + + 9 the spring. + + 10 SENATOR MURPHY: Ed, you know as well as I + + 11 do, we are dealing with the federal government, and + + 12 you know how that works. You know how that works. + + 13 EDWARD KELLY: Slowly but surely. + + 14 SENATOR MURPHY: I appreciate your coming out + + 15 of your way and getting here. This is the most + + 16 information that I've heard. We have been asking + + 17 questions, and like Senator Serino said, we have had + + 18 no one to ask the question to. You are the first + + 19 person, you are the first person. We can't get in + + 20 touch with the Coast Guard. + + 21 It's disgraceful that they're not here + + 22 tonight. I have the ultimate respect for our + + 23 military and Coast Guard. I respect them, but this + + 24 doesn't hold water, so to speak. It doesn't hold + + 25 water. + + + + + + + + 83 + 1 Thank you for being here. + + 2 EDWARD KELLY: Thank you. If anybody would + + 3 like a followup, please. + + 4 MATT SLATER: Next we are going to be + + 5 inviting county legislators John G. Testa and + + 6 Barbara Scuccimarra. Barbara is representing + + 7 MaryEllen Odell, County Executive of Putnam County. + + 8 SENATOR MURPHY: Excuse me, Ed, I know you + + 9 are being bombarded. I don't mean to be + + 10 disrespectful. I would like to keep this moving if + + 11 we can go outside. + + 12 Thank you County Legislator Testa, County + + 13 Legislator Scuccimarra. Thank you for being here. + + 14 LEGISLATOR BARBARA SCUCCIMARRA: Thank you + + 15 for having this tonight. + + 16 SENATOR MURPHY: Excuse me, can we respect + + 17 everybody's time here, please. + + 18 LEGISLATOR BARBARA SCUCCIMARRA: I appreciate + + 19 you having this public hearing. I attended your + + 20 August public hearing, and that was the first time I + + 21 heard about this and my community. And on behalf + + 22 Putnam County Executive MaryEllen Odell and the + + 23 Putnam County Legislature, I stand with my community + + 24 in opposition to the Coast Guard proposed rule. + + 25 The west side of Putnam county borders the + + + + + + + + 84 + 1 Hudson from Bear Mountain Bridge to Beacon, and + + 2 although we are only a little bit in there, we value + + 3 our river, and we value the river communities, and + + 4 we have to stand together. + + 5 I've lived on the river for over 40 years. + + 6 Or near the river. I overlook it. And I see barges + + 7 all the time going up and down the river, and + + 8 actually it's a lovely sight to see these barges. + + 9 But what this gentleman is talking about, this + + 10 increased amount of these barges, I think, is going + + 11 to be problematic, no matter how you look at it. + + 12 And the fact that they're so increased and + + 13 they're going to be parking in the middle of the + + 14 river at times. And when you had your public + + 15 hearing, we were in Verplank, and just looking out + + 16 at the water, and I think one barge is too many + + 17 barges. + + 18 You have to respect the people that live + + 19 along the river. You have to. Parking acres of + + 20 barges -- now first we heard 10 locations and now + + 21 it's 16, would risk undoing environmental efforts + + 22 which have transformed a sick, polluted river into a + + 23 vibrant and beautiful ecological environment that + + 24 continues to draw people. And that's what we have + + 25 to focus on. We can't focus on the commerce. I'm + + + + + + + + 85 + 1 sorry. We have to focus on the people that live + + 2 along the river and enjoy the river. The fishermen, + + 3 the boaters. I can't even wrap my head around more + + 4 barges on that river. I just can't do that. + + 5 Millions of gallons of bakken crude oil -- + + 6 and Scenic Hudson touched on this -- are being + + 7 transported by rail along the river. These trains + + 8 lack any of the basic safety mechanisms and are a + + 9 constant threat to all our communities. Bakken + + 10 crude oil is a very heavy consistency that sinks, + + 11 and it is very flammable. + + 12 Our communities along the river are not + + 13 capable of fighting a fire of bakken crude. They're + + 14 not. And by the time the Coast Guard gets there, + + 15 it's going to be devastating. + + 16 SENATOR MURPHY: As he said. + + 17 LEGISLATOR BARBARA SCUCCIMARRA: A resolution + + 18 has been drafted and sent in opposition to this + + 19 proposed rule, and we are in communication with the + + 20 governor and the Secretary of State to have this not + + 21 move forward. + + 22 You know, again, I thank you all for having + + 23 this, and I thank people like you for coming to this + + 24 because these are the people that are going to stop + + 25 this. These are the people. + + + + + + + + 86 + 1 I don't mean to date myself, but back in the + + 2 70s, they proposed a hydro electric plant on Storm + + 3 King Mountain, and if it wasn't for Scenic Hudson + + 4 and grassroots efforts, that mountain would have + + 5 been destroyed. But it was stopped. So I'm hoping + + 6 the same thing will happen here. Thank you so much. + + 7 SENATOR MURPHY: Thank you. + + 8 [ Applause ] + + 9 LEGISLATOR JOHN TESTA: Thank you for fitting + + 10 me in with Barbara. This is a pleasure. And I + + 11 really want to thank you, Senator Murphy, for + + 12 spearheading this and the other senators for being a + + 13 part of it and to really bring this to the public + + 14 eye. + + 15 As you mentioned before, we would not have + + 16 known about it, and we have known with the federal + + 17 government, if you don't find out early enough in + + 18 the process, you are not going to do anything about + + 19 it. So by having the early warning signs and to + + 20 have our voices heard early and now tonight to put + + 21 them on notice again, I think is very, very + + 22 important. + + 23 I was pleased to be able to bring a + + 24 resolution to the Board of Legislators in + + 25 Westchester, unanimously approved by all my + + + + + + + + 87 + 1 colleagues on both sides of the aisle. That + + 2 resolution has been posted to the Coast Guard site + + 3 so they know the County of Westchester as we know + + 4 with the County Executive are against these, against + + 5 this proposal. + + 6 And it seems to me after tonight, this is + + 7 really a matter of volume. As Barbara said, we've + + 8 always seen boats going up and barges and tug boats + + 9 going up and down the Hudson. I grew up on + + 10 Peekskill on the river. This is a volume problem + + 11 and we have to think of it as the worst case + + 12 scenario, as we do in government with all the things + + 13 we plan. When you see a full contingent of barges + + 14 parked in these areas, it's taking up the whole + + 15 center of the river just about, and our area + + 16 especially. + + 17 What I'm concerned about is the safety on all + + 18 aspects that was mentioned tonight -- I'm not going + + 19 to reiterate everything that has been said -- but + + 20 just look what happened when the Tappan Zee Bridge + + 21 started. There was one barge put there for the + + 22 staging for the beginning of the construction, and + + 23 there as was a fatal accident there with just one + + 24 barge. Think of all the barges up and down the + + 25 center of the Hudson what could happen for + + + + + + + + 88 + 1 recreational boaters. Who is this going to fall on? + + 2 Is it going to fall on the counties and individual + + 3 municipalities? Are they going to have to have + + 4 maritime response teams? Are they going to have to + + 5 expand their police force? Expand their fire + + 6 departments? Millions and millions of dollars per + + 7 community is going to be needed. Where that money + + 8 going to come from? Is the federal government going + + 9 to subsidize? I don't think so. + + 10 SENATOR MURPHY: Can you say unfunded + + 11 mandate? + + 12 LEGISLATOR JOHN TESTA: Perfect. I won't get + + 13 into my schoolteacher mode of historic lecture, but + + 14 this area, the Verplank Montrose area, Stony Point + + 15 and Heritage River, this is the cradle of our nation + + 16 was formed there. People might not realize it, but + + 17 it's the most historic area in our country as far as + + 18 I'm concerned. Protecting West Point, the whole + + 19 issue of the spies and Benedict Arnold, that has + + 20 become a very popular narrative over the past few + + 21 years. Books have been written about it movies made + + 22 about it. TV shows have been made about it. This + + 23 is where it happened. People come here just to see + + 24 that area because that's where history began for our + + 25 United States of America. + + + + + + + + 89 + 1 So to have barges all up and down the Hudson, + + 2 people don't come to see barges. That's not what + + 3 they are coming for. They're coming to enjoy the + + 4 Hudson. Just in my lifetime so much has been + + 5 tremendously done to improve the Hudson River. We + + 6 used to go swimming even though we were told not to + + 7 back in the day. Now it's not even a problem to go + + 8 out there and jump in the river, and people do it + + 9 all the time. It's going to change, and we can't + + 10 let that change after all this hard work we've done. + + 11 Thank you for letting us stay. + + 12 SENATOR MURPHY: Very briefly. You know, as + + 13 a resident of Peekskill, you have been there a + + 14 number of years, the former mayor of Peekskill you + + 15 have been there a number of years, and now as the + + 16 county legislator, you have seen drastic, drastic + + 17 changes that have gone on there. That walkway is + + 18 just absolutely beautiful what has gone on down + + 19 there, and to turn around and start walking down + + 20 that walk way and see niece anchorage sites and + + 21 barges to be able to park there with the investment + + 22 that some of your business owners put down there and + + 23 it's just not right. + + 24 Barb, thank you for being there from day one. + + 25 LEGISLATOR BARBARA SCUCCIMARRA: Sure. + + + + + + + + 90 + 1 SENATOR MURPHY: You came to the first public + + 2 comment period that we had, our press conference + + 3 just to pound the drum on this and get it notified, + + 4 you know, so I appreciate all that you do and thank + + 5 you for you being you and representing the county + + 6 executive up in Putnam County. So keep up the great + + 7 work and thank you for being here tonight, and I'm + + 8 sorry you had to wait for so long. + + 9 MATT SLATER: Next up we have John Cronin of + + 10 the Pace University Environmental Law School. + + 11 John? + + 12 JOHN CRONIN: Good evening. + + 13 SENATOR MURPHY: Good evening, John. Thank + + 14 you for being here. + + 15 JOHN CRONIN: Thank you. I appreciate each + + 16 one of you holding this meeting tonight. + + 17 My name is John Cronin. I'm senior fellow + + 18 for environmental affairs at the Pace University + + 19 Academy for Applied Environmental Studies. I'm a + + 20 resident of Cold Spring, New York. + + 21 And Senator Serino, when I was a commercial + + 22 fisherman I lived on the docks in Nyack, New York. + + 23 Senator Murphy, I'm here tonight with my + + 24 co-faculty Michelle Land from the Land Environmental + + 25 Policy Clinic and students from the clinic where you + + + + + + + + 91 + 1 are our environmental hero for reasons that don't + + 2 relate to even to the Coast Guard. The Elephant + + 3 Protection Act which our students worked with you + + 4 on, and you delivered for us in New York State + + 5 Senate, and we are greatly appreciative of that. + + 6 I want to start out my comments by saying + + 7 something about the Coast Guard. We throw the term + + 8 Coast Guard around very loosely. Let's be clear + + 9 about something. The rank and file of the Coast + + 10 Guard is our front line of maritime defense for + + 11 Homeland Security. They do drug interdiction, they + + 12 do spilled response. The Coast Guard swimmers are + + 13 amongst the bravest of those in the American + + 14 service, and I have nothing but the greatest respect + + 15 for the Coast Guard rank and file. + + 16 But as those in the maritime industry like to + + 17 say, and they do, there are two Coast Guards. There + + 18 is the rank and file Coast Guard who does everything + + 19 that I told you about, and there is what they call + + 20 the other Coast Guard. And I want to talk a little + + 21 bit tonight about the other Coast Guard. + + 22 If you asked anyone five years ago to + + 23 describe the maritime nexus for South Dakota, they + + 24 probably would have said zero. The bakken oil + + 25 fields have changed that dynamic dramatically as + + + + + + + + 92 + 1 tank barges move product to market. It is no + + 2 coincidence that Senator Thune of South Dakota is + + 3 chairman of the Senate Committee with oversight for + + 4 the Coast Guard. South Dakota most certainly has a + + 5 maritime nexus today. + + 6 Those are not my words. Those are the words + + 7 of Admiral Paul, the head of the Coast Guard at the + + 8 annual meeting of the American Waterways Operators + + 9 last April. + + 10 He concluded his remarks by saying the Coast + + 11 Guard will facilitate economic prosperity. Let's be + + 12 clear that is not the Coast Guard's job. The Coast + + 13 Guard's job is navigation. The Coast Guard's job is + + 14 safety. The Coast Guard's job is the health of the + + 15 environment. It is not to facilitate the commercial + + 16 success of the oil industry. But that was the... + + 17 [ Applause ] + + 18 But that was the thrust of his remarks at the + + 19 American Waterway operators last April at their + + 20 annual meeting. + + 21 And why is this important? It's important + + 22 because the special anchorage designation that the + + 23 Coast Guard is asking for is exempt from the + + 24 National Environmental Policy Act. A federal + + 25 regulation that exempts the Coast Guard from having + + + + + + + + 93 + 1 to do an environmental impact statement under the + + 2 National Environmental Policy Act because special + + 3 anchorages are an exempt activity. + + 4 Now the problem with exempt activities is + + 5 that they're pretty broad. Once you've got that + + 6 power, you've got that power. You can declare a + + 7 special anchorage, and you are exempt. You don't + + 8 have to do an environmental impact statement. + + 9 I would argue that even if this is not -- + + 10 does not fly in the face of the letter of the law, + + 11 it does in the spirit. It is clear to me, and a lot + + 12 of people have looked at this issue, that in fact + + 13 this is about facilitating economic growth outside + + 14 of New York State. It is not about navigation and + + 15 safety. And I will get to some of those issues. + + 16 And let me just tell you for those of you who + + 17 don't know. I've spent a lot of time on the Hudson + + 18 River. I've worked on it for 43 years. I'm a + + 19 former licensed Coast Guard captain. I was the + + 20 Hudson Riverkeeper for 17 years. I was a commercial + + 21 fisherman for three years on the Hudson River. I + + 22 know my way around the river. + + 23 SENATOR MURPHY: I'm going to consider you an + + 24 expert. + + 25 [ Applause ] + + + + + + + + 94 + 1 JOHN CRONIN: Better others say it than I, + + 2 but -- and part of that experience is some pretty + + 3 dramatic stuff. In 1977, I was one of the + + 4 organizers of the bird cleaning station in Rockland + + 5 county when the Ethel H. barge went up on Con Hook + + 6 Reef and spilled over 640,000 gallons of fuel oil in + + 7 the Hudson River, fuel oil that took over a year to + + 8 dissipate. + + 9 I cleaned the birds. I watched the birds + + 10 die. My brother helped us at the bird cleaning + + 11 station. He euthanized the birds that couldn't make + + 12 it anymore. We saw their dissolved internal organs + + 13 from trying to preen themselves of the oil. + + 14 This is serious business when you are talking + + 15 about the transport of oil. If we were doing an + + 16 environmental impact statement on this proposal, I + + 17 want you to consider what actually would be in + + 18 writing right now because you said very rightfully, + + 19 Senator Murphy, that we don't have enough + + 20 information. + + 21 If there were an environmental impact + + 22 statement, it would spell out the environmental + + 23 impacts, the social impacts, the cultural impacts, + + 24 the visual impacts, the economic impacts and any + + 25 contributory impacts to climate change. It would + + + + + + + + 95 + 1 have to spell out alternatives to special anchorages + + 2 including the no action alternative, and it would + + 3 have to give all the reasons to defend every one of + + 4 those alternatives. + + 5 Now by way of example, in Kingston, New York, + + 6 the Hudson River pilots call Kingston, New York, the + + 7 point of no return. They call it the point of no + + 8 return because there is not room for more than one + + 9 vessel. And once the vessel is committed up there, + + 10 that vessel cannot turn around until it gets up to + + 11 the point of Albany. They call it the point of no + + 12 return. + + 13 SENATOR MURPHY: Didn't know that. + + 14 JOHN CRONIN: Here is the question that would + + 15 have to be answered in an environmental impact + + 16 statement. If above Kingston is such a narrow + + 17 channel that it is the point of no return, why do we + + 18 need 17 anchorages in Yonkers, New York? + + 19 It doesn't compute why we would need 17 + + 20 anchorages in Yonkers, New York, when 70 miles + + 21 upriver you can't squeeze more than one tug and + + 22 barge on its way to the port of Albany. + + 23 Explaining that is important. We are not + + 24 seeing those explanations, and I don't think we are + + 25 going to. We are going see contested testimony in a + + + + + + + + 96 + 1 spring hearing. Were there an environmental impact + + 2 statement, however, we would see all of this and + + 3 more. + + 4 So what is the Coast Guard asking us to do? + + 5 The Coast Guard is asking us to trust the Coast + + 6 Guard. I trust the rank and file of the Coast + + 7 Guard. My problem with is the decision makers. And + + 8 let me give you a firsthand experience that I had. + + 9 In 1983, in my first year as Hudson + + 10 Riverkeeper, I spent the summer training oil tankers + + 11 up and down the Hudson River. They would anchor at + + 12 the anchorage Port Ewen and anchor at the anchorage + + 13 at Kingston. They would rinse themselves out, fill + + 14 up with Hudson River water, they would bring the + + 15 water to the island of Aruba. When they anchored at + + 16 the village of Port Ewen, they discharged benzene, + + 17 ethyl benzine, toluene and xylene 1500 feet from the + + 18 drinking water intake of the village of Port Ewen. I + + 19 personally did the sampling from the tanker, and we + + 20 brought it to a laboratory. + + 21 We brought the information and the evidence + + 22 to the United States Coast Guard, and this is what + + 23 the United States Coast Guard said. We decline to + + 24 investigate because this is part of the normal + + 25 operations of the vessel. + + + + + + + + 97 + 1 Now were there an environmental impact + + 2 statement, the issue of monitoring enforcement would + + 3 also have to be addressed. + + 4 In 1990, there were what was called the high + + 5 grade spill. It was A barge that went up on Diamond + + 6 Reef, a kerosene spill that dumped 164,000 gallons + + 7 of kerosene into the Hudson River. These reefs I'm + + 8 talking about are well known navigation hazards on + + 9 the Hudson. + + 10 The immediate Coast Guard response, and its + + 11 public statement to the newspapers was, it was due + + 12 to high winds and choppy water. The winds that + + 13 night were 8 to 10 miles per hour with occasional + + 14 gusts to 20. It was a clear night, and months after + + 15 those public statements were made to the newspapers, + + 16 "The New York Times," "The Times Herald Record," + + 17 months after that an investigation was done, and the + + 18 captain was suspended. + + 19 But the first inclination, the first response + + 20 of the Coast Guard was it was the fault of the + + 21 river. It was not the fault of the captain behind + + 22 the wheel. + + 23 You can see this repeated in incident after + + 24 incident. In the 1980s to 1990, there were over a + + 25 period of one decade, there were 10 significant + + + + + + + + 98 + 1 spills by barges going up on reefs in the Hudson + + 2 River. In June 5, 1991, there was a Congressional + + 3 hearing held. I testified at this Congressional + + 4 hearing. I had also testified at a Congressional + + 5 hearing about navigation two years before the + + 6 Merchant Marine Fisheries Committee. That doesn't + + 7 exist anymore, but when they had the subcommittee at + + 8 navigation and the Coast Guard. I proposed, and + + 9 Congresswoman Nita Lowey proposed that any tug and + + 10 barge that brought hazardous cargo up the Hudson + + 11 River should have an independent Hudson River pilot + + 12 aboard in addition to the captain to assure the + + 13 safety of that tug and barge. + + 14 The reason for this is Hudson River pilots + + 15 are required on board tankers, and you have to look + + 16 long and hard to find a tanker accident on the + + 17 Hudson River because the Hudson River pilots are on + + 18 board. But guess what? The Coast Guard showed up + + 19 at the hearing and testified against the idea. They + + 20 said there was no evidence that there were safety + + 21 problems. There were no evidence that there were + + 22 accidents, and the commercial industry testified + + 23 against it because they said it would increase their + + 24 costs for navigating on the Hudson River. + + 25 Now mind you, every single one of these + + + + + + + + 99 + 1 spills had a licensed captain behind the wheel, but + + 2 almost none of them had significant Hudson River + + 3 experience. I'm replaying this history to you to + + 4 explain to you in part the real world problems that + + 5 we have had on the Hudson River when navigation + + 6 and-- I take that back. The Coast Guard calls it + + 7 incidents. They don't call them accidents and for + + 8 good reason. When I trained for my Coast Guard + + 9 license, what was drilled into me there is no such + + 10 thing as an accident. There is somebody's fault and + + 11 an act of God. That's all there is, and this is + + 12 true. On the water, there is somebody's fault, and + + 13 there is an act of God. There is no in between. + + 14 That's the way it works, and not one of those barge + + 15 incidents was due to an act of God. + + 16 Now when I look at this proposal, I look at + + 17 other things as well. We have a rich history of + + 18 having had a commercial fishery on the Hudson River. + + 19 I was a commercial fisherman for three years, part + + 20 of the time living in the back of a pickup truck on + + 21 the Bird Street Dock working with the Gabrielson + + 22 family. + + 23 We used to have hundreds of commercial + + 24 fishermen on the Hudson River. And there is a sad + + 25 story there. Striped bass and American eel were + + + + + + + + 100 + 1 banned from fishing in 1976. Sturgeon were banned + + 2 from fishing in 1996. Shad was banned from fishing + + 3 in 2010. The net commercial net fisheries no longer + + 4 occupies the Hudson River. We wanted to some day, + + 5 but a lot of these anchorages will be located in + + 6 former commercial fishing locations. In other + + 7 words, the federal government is giving up on the + + 8 idea that we are going to have commercial fishermen + + 9 again on the Hudson River. + + 10 To me, if nothing else, this is totally + + 11 unacceptable. This is part of our working life on + + 12 the Hudson, part of our culture and history, an + + 13 economic safety net in times of economic duress for + + 14 people who needed another income. This is important + + 15 for us. Commercial fishermen are not the cause of + + 16 any of them losing their jobs. The federal + + 17 government has a lot to do with them losing their + + 18 jobs. + + 19 And so let me turn my attention to that just + + 20 for a moment before I conclude. The federal + + 21 government has a lot to answer for on the Hudson + + 22 River, and the Coast Guard proposal is the latest. + + 23 Since the 1960s, citizens groups on the Hudson + + 24 River, and this is no exaggeration, have spent tens + + 25 of millions of dollars -- I suppose it has probably + + + + + + + + 101 + 1 reached close to $60 million to $70 million at this + + 2 point fighting federal proposals. + + 3 Make no mistake. The Storm King Mountain + + 4 proposal that Barbara Scuccimarra referred to, yes, + + 5 it was a Con Ed proposal, a Federal Power Commission + + 6 proposal in the exact same way the proposal in front + + 7 of us tonight is not a proposal from American + + 8 Waterway Operators. It's a proposal from the Coast + + 9 Guard, because when we testified, when Congressman + + 10 Lowey and I testified and went to the Coast Guard + + 11 and said we want better safety measures in the pilot + + 12 house, in those tugs, the Coast Guard didn't take + + 13 our idea and publish it for everybody to comment on. + + 14 The Coast Guard filtered it out. The Coast Guard + + 15 publishes things it wants. + + 16 So make no mistake about it. This is in the + + 17 Federal Register because the Coast Guard wants it. + + 18 If the Coast Guard didn't want it, it would never + + 19 see print, in the same way our request for + + 20 requirement for pilots and better training for + + 21 captains never saw print in the proposed regulation + + 22 from the Coast Guard on the Hudson River. + + 23 The Hudson River Expressway that would fill + + 24 in the shallows of the Hudson River in Westchester + + 25 County, Westway, that would have filled in the + + + + + + + + 102 + 1 striped bass spawning wintering grounds off the + + 2 coast of Manhattan, the PCB discharge permit given + + 3 by the E.P.A. to General Electric in 1973 when the + + 4 E.P.A. had in its hand a report that said it would + + 5 result in the contamination of Hudson River striped + + 6 bass. + + 7 I can go through a list of these, at least a + + 8 dozen incidents and proposals by the federal + + 9 government that we have had to fight over the last + + 10 four to five decades to keep this Hudson River safe, + + 11 this Coast Guard proposal is the latest. + + 12 Let me give you something to contrast this + + 13 with. Does anybody here want to guess how much + + 14 money Congress is going to spend on protection, + + 15 restoration and rebuilding of Chesapeake Bay next + + 16 year -- this year -- on the Chesapeake Bay estuary? + + 17 They're going to spend $65 million restoring and + + 18 protecting the Chesapeake Bay estuary in a special + + 19 program for the Chesapeake Bay that has a permanent + + 20 line item in the Congressional budget every single + + 21 year. + + 22 There is no such line item for the Hudson + + 23 River. We are here talking about a backroom fight + + 24 to protect this river when we should be having a + + 25 hearing about is how much money should the federal + + + + + + + + 103 + 1 government be investing in the restoration of the + + 2 Hudson River and estuary? + + 3 [ Applause ] + + 4 Not fighting off yet another agency proposal. + + 5 When is the commercial fishery coming back? When + + 6 are the shores of our cities going to be rebuilt? + + 7 Where are the docks for the tour boats that used to + + 8 be the boast of the Hudson River that brought people + + 9 up here by water? + + 10 Let's not bring oil up here by water. Let's + + 11 bring people up here by water for God's sakes. + + 12 That's what we should be doing on the Hudson River. + + 13 But most of all, I want to stress to you, and + + 14 I implore you to make this request, and I invite the + + 15 Maritime Association of the Port of New York and + + 16 New Jersey to join us in a request that the Coast + + 17 Guard do a full environmental impact statement with + + 18 all the alternatives, all the impacts from social to + + 19 economic to environmental so that we can examine + + 20 this proposal and the kind of detail that it should + + 21 be examined. And then let's start talking about the + + 22 money we should be spending from the federal + + 23 government on restoring this river, not creating + + 24 anchorages for bakken oil from South Dakota because + + 25 the senator from South Dakota is the chair of the + + + + + + + + 104 + 1 Coast Guard subcommittee. + + 2 JOHN CRONIN: Thank you very much. + + 3 [ Applause ] + + 4 SENATOR MURPHY: John, one second if you + + 5 don't mind. Like I said, like I said, thank you. + + 6 Thank you for being here tonight. You are a + + 7 plethora of information. The stuff that you can + + 8 dial in on, I would love to make sure that if you + + 9 are available when we have more of these things + + 10 because you know what we are up against. I love the + + 11 analogy of the rank and file versus the really -- + + 12 the kind of nonsense that we get put into. And when + + 13 they do that E.I.S., it's going to be this big. + + 14 It's going to be that big. It is going to be + + 15 unbelievable, and I did not know that it was -- they + + 16 did not -- it was the anchorage were exempt. I did + + 17 not know that. + + 18 JOHN CRONIN: Special anchorages are exempt. + + 19 It's a Coast Guard regulation. + + 20 SENATOR MURPHY: So I thank you for being + + 21 here tonight. I know you have been at the press + + 22 conferences, and you are an information source for + + 23 myself that I can only just learn more from you and + + 24 dial this in and we can fight it together. + + 25 So thank you for going out of your way and + + + + + + + + 105 + 1 I'm sorry we couldn't get you on earlier. + + 2 JOHN CRONIN: Let me ask one final thought + + 3 before I walk away. If the Coast Guard does not -- + + 4 refuses to do an E.I.S., our environmental policy + + 5 clinic is in the midst of preparing a massive + + 6 freedom of information request that goes back 30 + + 7 years, trying to get the documentation that would + + 8 show up in an E.I.S. + + 9 The December 6 deadline should be extended + + 10 because we have to engage in our own process to + + 11 collect the information. And they have 20 days to + + 12 respond to the freedom of information request. It's + + 13 going to take them probably two months to comply + + 14 with it, and the hearing and notice, the comment + + 15 period will be over. + + 16 This comment period should be extended and + + 17 there should be -- the record should be open and + + 18 included in the hearing that takes place in the + + 19 spring. There is no reason to close the record now + + 20 or any time between now and when the hearing takes + + 21 place. + + 22 SENATOR MURPHY: Thank you very much. + + 23 [ Applause ] + + 24 MATT SLATER: Thank you, John. + + 25 Our next speaker is Betsy Garthwaite. She is + + + + + + + + 106 + 1 the Chairman of the Board for Clearwater. + + 2 Betsy? + + 3 BETSY GARTHWAITE: Good evening. Whoever's + + 4 idea it was that I had to follow John Cronin -- + + 5 [ Laughter ] + + 6 SENATOR MURPHY: A tough act to follow, huh. + + 7 BETSY GARTHWAITE: I want to thank the + + 8 senators tonight and especially Senator Murphy for + + 9 reaching out to Clearwater to ask us to participate + + 10 this evening. + + 11 My name is Betsy Garthwaite, and I am the + + 12 president of the Board of Directors of Hudson River + + 13 Clearwater and also former captain of the Sloop + + 14 Clearwater, and for the record as a United States + + 15 Coast Guard captain, I have a deep respect for other + + 16 maritime professionals and their concerns about + + 17 safety. + + 18 As most everyone knows, the Sloop Clearwater + + 19 was the brainchild of the late folk singer Pete + + 20 Seeger. Pete had an idea to build a replica of the + + 21 old Hudson River sailing vessels of 18th and 19th + + 22 centuries not just as a nod to the valley's maritime + + 23 history, but as a way to focus people's attention on + + 24 what was then a very polluted Hudson River. And + + 25 Pete's scheme worked. + + + + + + + + 107 + 1 That first summer in 1969, the sloop + + 2 attracted many thousands of people in waterfront + + 3 communities up and down the river. Today, nearly + + 4 50 years after Clearwater first sailed up the + + 5 Hudson, the river is noticeably cleaner. + + 6 No one environmental organization, no one + + 7 piece of clean water legislation, no one lawsuit can + + 8 take credit for cleaning up the Hudson. It took + + 9 many organizations and government leaders and + + 10 concerned citizens coming together and working + + 11 toward a common goal. And because of the efforts of + + 12 people decades ago, and the ongoing efforts of + + 13 people today, we live in one of the most beautiful + + 14 regions in these United States. + + 15 The Hudson is no longer a place that people + + 16 avoid. Rather, people flock to it. They swim in + + 17 the river, fish in the river, canoe, kayak and wind + + 18 surf, dine along the river's edge. You name it. + + 19 Just about any recreational activity that can be + + 20 done on the water or next to the water is happening + + 21 up and down the Hudson. + + 22 Today, tourism is a multi-billion-dollar + + 23 industry here in the valley, and it is growing with + + 24 every year. But now the maritime ministry has + + 25 requested 10 additional federally-designated + + + + + + + + 108 + 1 anchorages between Yonkers and Kingston with a total + + 2 capacity up to 43 vessels. By its own admission, + + 3 this request is being made due to the anticipated + + 4 increase in traffic of bakken crude oil on the + + 5 Hudson after the lifting of the crude oil export ban + + 6 last December. + + 7 Our communities will be assuming a huge + + 8 amount of risk with the promise of little to no + + 9 reward from the increase in shipments of crude oil + + 10 which, unlike the home heating oil that is + + 11 transported upriver each winter, is not intended for + + 12 our consumption. This oil will be headed overseas. + + 13 I believe that the Hudson River is + + 14 New York State's greatest natural resource, and the + + 15 Hudson has always had value to the people of + + 16 New York and New Jersey as a shipping route. But + + 17 those interests have got to be balanced with the + + 18 many other interests that we, as residents of the + + 19 valley, also hold dear and depend on. + + 20 I believe that additional anchorages are not + + 21 in the best interest of Hudson River residents, and + + 22 on behalf of the Clearwater organization, exempt or + + 23 not, I am asking the Coast Guard for a full + + 24 environmental impact statement. + + 25 Funny that John mentioned the ethyl h today. + + + + + + + + 109 + 1 I was going to bring it up myself. To this day, it + + 2 is my understanding that it was the largest single + + 3 crude oil spill to occur in the river north of + + 4 New York Harbor, and the bird cleaning center that + + 5 was set up operated around the clock for 10 days by + + 6 volunteers and professionals caring for the captured + + 7 birds. + + 8 Early attempts at using dish detergent to + + 9 clean those birds proved ineffective, so a solvent + + 10 made by the Shell Oil company was used which + + 11 required the cleaners to don suits, gloves and + + 12 respirators. The Coast Guard responded to the spill + + 13 with a 300-person team. By their own estimate, they + + 14 recovered less than 25% of the oil spilled. Of the + + 15 hundreds or even thousands of birds that may have + + 16 been contaminated, just 50 were captured, and one + + 17 third of them did not survive. + + 18 This is a stark reminder that nearly 40 years + + 19 later, our ability to clean up spills and save + + 20 wildlife has not improved greatly. We can put a + + 21 price on the value of a tanker full of crude oil. + + 22 We can also put a price on the cost of an oil spill + + 23 cleanup and resulting economic damage. These spills + + 24 may be uncommon in part thanks to the + + 25 professionalism of the Hudson River pilots and the + + + + + + + + 110 + 1 tug and barge captains and crew, but they do happen. + + 2 And make no mistake, oil companies consider such + + 3 spills part of the cost of doing business. + + 4 There is one thing, however, that we can't + + 5 put a price tag on, and that's the quality of life + + 6 we enjoy here in the Hudson Valley today, and I + + 7 believe that quality of life is worth protecting. + + 8 [ Applause ] + + 9 SENATOR MURPHY: Betsy, I'm sorry had you to + + 10 go after John, but thank you very much for being + + 11 here tonight and testifying because this is all + + 12 being recorded by the New York State Senate, and + + 13 it's going to be distributed to the Coast Guard. + + 14 And the E.I.S. is a great start. + + 15 [ Applause ] + + 16 BETSY GARTHWAITE: Thank you very much. + + 17 SENATOR SERINO: Thank you, Betsy. Thank + + 18 you. + + 19 MATT SLATER: John Parker is with us. He is + + 20 the Director of Legal Programs for Riverkeeper. + + 21 John, thank you for being here. + + 22 JOHN PARKER: Thank you, sir. + + 23 SENATOR MURPHY: Thanks, John. + + 24 JOHN PARKER: Thank you, Senators. + + 25 On behalf of Riverkeeper, its members and + + + + + + + + 111 + 1 constituents, we thank you for calling attention to + + 2 the request for 10 new anchorage grounds with 43 + + 3 berths in the Hudson River. + + 4 In the many concerns that have been expressed + + 5 by numerous people today, I'm going to go through a + + 6 couple of things again because I think it bears + + 7 important merit to talk about some of the aspects of + + 8 the claims with respect to the justification for + + 9 these proposals and a little bit of the detail about + + 10 the environmental impact statement process. So here + + 11 we go. + + 12 SENATOR MURPHY: You're on. + + 13 JOHN PARKER: Thank you. Ready. + + 14 Riverkeeper opposes the proposed request for + + 15 additional anchorages because it is clearly driven + + 16 by a desire to vastly increase industrialization, + + 17 reindustrialization of this river corridor, and + + 18 because the industry has tied the need to increase + + 19 safety, instead of the vast increase in crude oil + + 20 transport to and from Albany. + + 21 The river has had its share of industry and + + 22 industry impacts, and the public now with its + + 23 overwhelming outpouring of concern, condemnation and + + 24 critique, is pushing back to say no more. + + 25 The advanced proposed rule making requested + + + + + + + + 112 + 1 by the maritime industry to establish new anchorage + + 2 grounds is particularly discouraging given the time + + 3 that has been spent in the recovery of the river and + + 4 the communities along it. They are finally + + 5 rebounding from a legacy of its industrial past. + + 6 Our residents and communities appreciate the + + 7 Hudson as a living river in ways that are much + + 8 stronger today and much stronger than they've ever + + 9 been before, and this understanding is increasing. + + 10 Riverkeeper's concerns about the proposed + + 11 dramatic increase in anchorage grounds focuses on + + 12 two core areas: First, the industry's claims + + 13 regarding the need for additional anchorage grounds + + 14 will go through now are unsubstantiated, and second, + + 15 as we've heard discussed tonight, should this + + 16 advanced proposal become an actual proposal, it must + + 17 be subject to a full and comprehensive environmental + + 18 impact statement, and that includes things like we + + 19 are doing here tonight: Robust public participation + + 20 and opportunity for those impacted to have their say + + 21 before any decisions are made. + + 22 So are the increased number of anchorage + + 23 grounds necessary? We say no. We say the facts say + + 24 no. + + 25 The Coast Guard has the power to define and + + + + + + + + 113 + 1 establish anchorage grounds, and they have that + + 2 power for over a century under existing law. The + + 3 maritime industry on January 21 of this year claimed + + 4 that the anchorages are, quote, critical to + + 5 America's economy, navigational safety, + + 6 environmental protection and energy independence. + + 7 There are many factual points that refute + + 8 these claims. First, commercial vessels already + + 9 have the ability to anchor in the event of any + + 10 unexpected events or conditions which place them at + + 11 risk, including mechanical failure or + + 12 weather-related hazards. + + 13 Hurricane Sandy is an excellent example of + + 14 the Coast Guard giving emergency and temporary + + 15 authorization in times of trouble to multiple + + 16 vessels in the harbor of New York. + + 17 In fact, these types of requests for + + 18 authorization are always granted for safety. The + + 19 industry lobbyists we've heard tonight have admitted + + 20 such. Second, Riverkeeper's patrol boats and staff, + + 21 some of which have spoken tonight, have patrolled + + 22 the Hudson estuary and the river for thousands and + + 23 thousands of miles and many, many years. + + 24 JOHN CRONIN: The industry's request for + + 25 43 berths in 10 areas does not appear to be related + + + + + + + + 114 + 1 to any demonstrated need. We have never witnessed + + 2 within a half dozen commercial vessels anchored + + 3 between the George Washington Bridge in Albany in + + 4 the existing two authorized anchorage grounds. + + 5 In fact, only on the rarest of occasions are + + 6 the existing Hyde Park and Yonkers anchorages full. + + 7 Third, on closer examination, the fact that industry + + 8 has requested eight berths at the Kingston hub which + + 9 we have heard discussed tonight, and 16 additional + + 10 berths at the existing Yonkers anchorage grounds, + + 11 clearly show the true reason for the request. The + + 12 Kingston hub is the Northern-most area of the river + + 13 with swinging room for large vessels. + + 14 The Yonkers extension is the closest + + 15 anchorage to the Port of New York and New Jersey. + + 16 There is no possible safety-related scenario where + + 17 eight additional vessels would need emergency + + 18 anchorages in Kingston and where 16 additional + + 19 vessels would be needed for emergency anchors off + + 20 Yonkers. No possible scenario. + + 21 The industry representatives have made clear + + 22 their security and security credentials that + + 23 demonstrate there are few, in fact, few emergencies, + + 24 and again, when and where they can anchor in those + + 25 emergencies. Simply the proposal is an effort to + + + + + + + + 115 + 1 expand the ports of Albany and New York Harbor to + + 2 relieve congestion and support an expansion of + + 3 commercial transport of oil as we'll discuss. + + 4 It is not about recreational use increasing, + + 5 and it's not about using traditionally-used + + 6 anchorages. + + 7 Fourth, both the oil and maritime industries + + 8 expect a significant increase in crude oil + + 9 transport. This is tied to the 2015 federal + + 10 government's action that lifted the ban on the + + 11 export of oil to foreign nations. + + 12 The many millions gallons of crude that + + 13 already travel this corridor will be dramatically + + 14 increased, but for foreign markets. + + 15 Fifth, transport of crude oil on the Hudson + + 16 Valley presents unacceptable threats to public + + 17 health as we've heard tonight. For example, again, + + 18 public drinking water supplies are along the Hudson + + 19 River and use the Hudson River as that supply. + + 20 The shipment of crude oil on the Hudson River + + 21 itself is a hazardous condition because spills are + + 22 essentially unrecoverable. And it only takes one + + 23 such spill to wreak havoc here. That needs to be + + 24 stressed. It doesn't need to be multiple for us to + + 25 get the point. + + + + + + + + 116 + 1 Bakken crude which has been discussed tonight + + 2 from the midwest cannot be recovered in moving water + + 3 bodies like the Hudson River. Burning bakken is + + 4 left to burn itself out. + + 5 Again and again, we've seen examples where + + 6 the fires are too hot to extinguish. It is the + + 7 risk. + + 8 Further, the tar sand crude oil that might be + + 9 coming as well in the future, if spilled, sinks to + + 10 the bottom, not recoverable. + + 11 Despite industry claims that there is no need + + 12 for the additional anchorages to support delivery of + + 13 refined petroleum products -- sorry, excuse me. + + 14 There is no need for additional barges to bring + + 15 refined oil products to and from Albany. + + 16 For many, many decades, as we all know, the + + 17 barges have gone to Albany, and they've come back + + 18 and there has never been a concern. This is just a + + 19 scare tactic. It's not real. It's not been + + 20 witnessed ever. + + 21 Moving to the environmental impact statement, + + 22 because I think it's important we discuss this, it's + + 23 our position, and I think it's the position of many + + 24 others, that there is a need for a comprehensive + + 25 environmental impact statement. + + + + + + + + 117 + 1 We are not talking a comprehensive + + 2 environmental assessment, and we are not talking + + 3 about a comprehensive environmental review. We are + + 4 talking about a comprehensive environmental impact + + 5 statement. + + 6 You've heard others before me talk about the + + 7 reasons, the comprehensive nature. The national + + 8 environment policy act which would authorize such a + + 9 comprehensive statement requires a comprehensive + + 10 look at potentially-significant environmental + + 11 impacts before decisions are made. And that is very + + 12 important and it's very meaningful, particularly in + + 13 this in case. And as we've also heard, there is a + + 14 problem and it is not a small one. + + 15 This review of this expansion of anchorages + + 16 is categorically excluded, but we have done work and + + 17 we are convinced that there is and are bases that + + 18 the substantial impacts we've identified and will + + 19 discuss now provide the legal grounds for the Coast + + 20 Guard to, in fact, go beyond the exclusion and + + 21 prepare what we will ask you tonight and what you've + + 22 discussed the need for the full impact statement. + + 23 So we ask you and urge you to join us in that + + 24 request. + + 25 The impacts are many. We've heard them + + + + + + + + 118 + 1 discussed tonight, and I'll summarize them as air, + + 2 noise, light pollution, scarring and scouring of the + + 3 Hudson River and its related impacts to the fish + + 4 that call this place, this river, their home, and + + 5 the associated impacts from the reindustrialization + + 6 of the river corridor. + + 7 This proposal also creates a worst case + + 8 scenario. As I mentioned earlier, it's the oil + + 9 spill. There isn't going to be a response to the + + 10 oil spill that's going to recover anything. It's + + 11 just going to happen, and it's going to be over. + + 12 Thousands of public comments to date and + + 13 dozens of town and elected officials and community + + 14 leaders have demonstrated the clear opposition and + + 15 concern. + + 16 The historical properties' impacts alone are + + 17 significant. As we've heard and we agree, the + + 18 Hudson River is American history. It's clear from + + 19 the Battery to the Hudson Highlands, from West Point + + 20 to the Erie Canal, it's all right here. + + 21 Finally, it's inconsistent with several + + 22 environmental laws that the state implements and + + 23 that the state has passed. These include the + + 24 state's authority under the endangered species act. + + 25 We've heard discussions about the Coastal Zone + + + + + + + + 119 + 1 Management Act that empowers local communities up + + 2 and down the Hudson River to create a vision for + + 3 their communities as the river is restored, for + + 4 economic and ecological benefit and future economic + + 5 growth, and very importantly, as we've just + + 6 mentioned, the National Historic Preservation Act. + + 7 Any of these factors alone would be + + 8 sufficient to prompt a full environmental review + + 9 under the regulations of the Coast Guard. + + 10 That this proposal implicates all of them + + 11 leads to the clear and undeniable conclusion that it + + 12 must undergo full environmental review, but public + + 13 support, public pressure, public officials, + + 14 concerned community groups and everybody else is + + 15 going to be necessary to make that happen. + + 16 The public deserves an opportunity to be + + 17 heard on all of this. There needs to be extensive + + 18 public hearings up and down the river in all of the + + 19 communities impacted. + + 20 In conclusion, the proposal to establish new + + 21 anchor grounds is being made to the federal + + 22 government. It's a federal action. We've discussed + + 23 this tonight. But it has tremendous and significant + + 24 implications for our state, for our state's future, + + 25 and for our community and its residents. + + + + + + + + 120 + 1 Hundreds of thousands of New Yorkers are + + 2 relying on their state representatives, to you, to + + 3 challenge this proposal, to raise these substantive + + 4 and significant issues we've raised and to ensure + + 5 that their voices are heard throughout the process. + + 6 Riverkeeper at our website Riverkeeper.org + + 7 has taken extensive efforts to both identify and + + 8 document from both our experience and others the + + 9 multitude of issues we found community by community, + + 10 resolution by resolution, along this entire valley + + 11 and its corridor, and we encourage folks to use + + 12 those resources, investigate them and help us + + 13 understand better what is necessary to be done with + + 14 respect to each individual community. + + 15 And in the end, we thank you, and we ask you + + 16 and urge you to be part of our voice, all of our + + 17 voices, as this proposal seems like it's going to + + 18 advance. + + 19 So thank you very much. + + 20 SENATOR MURPHY: John, thanks for coming + + 21 tonight. You know, this is the reason why we are + + 22 here. We call this a federal issue. We are your + + 23 state representatives. I don't see any one federal + + 24 representative here tonight. + + 25 [ Applause ] + + + + + + + + 121 + 1 I don't know where they are, but they should + + 2 be here. So we are pounding the drum on this, and + + 3 this is what we are trying do is bring light to it + + 4 and get the questions answered. + + 5 There is a tremendous amount of questions + + 6 that we've had. We've said it over and over and + + 7 over tonight. Thank you for being here. + + 8 This is something that Senator Serino and + + 9 Senator Carlucci, who is coming back in right now, + + 10 and myself, have been trying to make sure that we + + 11 represent the people that put us in these leadership + + 12 rolls and these elected position. That's what we + + 13 are supposed to do, and that's why we are doing this + + 14 tonight. And we have been here for what, three + + 15 hours now and it's not going to be enough. + + 16 SENATOR CARLUCCI: Agreed, and as we + + 17 mentioned, this is the beginning. Advanced notice. + + 18 We thank you for your continued support. + + 19 SENATOR SERINO: I believe Congressman + + 20 Maloney has the bill against the barges. He made a + + 21 statement. + + 22 But thank you very much. + + 23 [ Applause ] + + 24 MATT SLATER: Our next speaker is Frank + + 25 Bergman. Is Frank here? There we go. Frank is the + + + + + + + + 122 + 1 president of the Hudson River Boat and Yacht Club + + 2 Association. + + 3 Frank, thank you for joining us today. + + 4 [ Applause ] + + 5 FRANK BERGMAN: I represent Hudson River Boat + + 6 and Yacht Club Association. And first of all, we + + 7 want to thank you for holding this hearing. + + 8 Hudson River Boat and Yacht Club Association + + 9 represents 31 boat clubs from Poughkeepsie, from + + 10 Pirate Canoe Club down to Yonkers Yacht Club, and on + + 11 the east shore, Marlboro Yacht Club, and down to + + 12 north Jersey on the west shore. + + 13 We've got about 3,000 members and in 31 boat + + 14 clubs. We are very concerned, and we're strongly + + 15 opposed to the U.S. Coast Guard proposal to put + + 16 barge anchorage sites along the Hudson River. + + 17 We consider the proposal for these anchorages + + 18 seriously flawed. While the oil companies may + + 19 benefit greatly from using the Hudson River as a + + 20 parking lot, most others will surely see + + 21 overwhelming disadvantages. + + 22 The Hudson River and the public are losers. + + 23 Let's examine some reasons. All of the boat clubs + + 24 in Hudson River Boat and Yacht Club Association are + + 25 opposed to this proposal. Letters opposing it with + + + + + + + + 123 + 1 specific objections have been sent to the Coast + + 2 Guard from New Hamburg Yacht Club, Chelsea Yacht + + 3 Club, and Minisceongo Yacht Club. + + 4 Individual Hudson River Boat and Yacht Club + + 5 Association members have also expressed their + + 6 concerns. + + 7 New Hamburg Yacht Club is especially worried + + 8 about water contamination, boater safety, noise, air + + 9 and light pollution. + + 10 Chelsea Yacht Club is one of the worst + + 11 affected. They're concerned about the dangers posed + + 12 by the anchorage in Rosedon interfering with their + + 13 mooring fields which New York State authorizes + + 14 through a submerged land license. + + 15 The proposed anchorage would also obstruct + + 16 the racing course and impede the safety of their + + 17 boats, essentially fencing them off from the river + + 18 at certain times if there are three barges across + + 19 there swinging at anchorage. + + 20 Minisceongo has many environmental and safety + + 21 boater interference concerns. + + 22 In addition, our association has received + + 23 letters from Peekskill, Mountain and Yonkers Yacht + + 24 Clubs expressing similar apprehensions. Many others + + 25 have been vocal at our meetings about how this + + + + + + + + 124 + 1 proposal would, without a careful consideration of + + 2 the damage that could be done, reverse all the + + 3 progress made over the years, over recent years + + 4 especially, to have the Hudson River keep its + + 5 designation as a National Heritage River. + + 6 In addition, from an environmental + + 7 standpoint, from the public record, the Hudson River + + 8 Estuary Action Agenda published by the + + 9 New York State D.E.C. and formulated by the Hudson + + 10 River Estuary Management Advisory Committee cites + + 11 six major benefits of a strong and vibrant Hudson + + 12 River ecosystem. + + 13 It strives to achieve those benefits through + + 14 action plans. Those benefits are: clean water, + + 15 resilient communities, vital estuary ecosystems, + + 16 estuary fish, wildlife and habitats, natural + + 17 scenery, education, river access, recreation and + + 18 inspiration. + + 19 Hudson River Estuary Management Advisory + + 20 Committee has worked hard to achieve these goals + + 21 over time. None of them is advanced by this U.S. + + 22 Coast Guard proposal, and most of them are negated + + 23 or harmed by it. + + 24 The consequences could be catastrophic. + + 25 For clean water, this proposal would + + + + + + + + 125 + 1 seriously open up the risks of spills and pollution. + + 2 For resilient communities, it would bring in noise, + + 3 light and air pollution. For vital estuary + + 4 ecosystem protection, it would disturb the submerged + + 5 aquatic vegetation beds so essential for a balanced + + 6 and clean river. + + 7 For estuary fish, wildlife and habitats, the + + 8 anchorages are in the spawning grounds of Atlantic + + 9 sturgeon. They would scar the river bottom and + + 10 destroy underwater vegetation. + + 11 For natural scenery, it would turn the river + + 12 into a barge parking lot. For education, river + + 13 access, recreation and inspiration, the anchorages + + 14 would negate all of the progress that Hudson River + + 15 Estuary Management Advisory Committee has achieved + + 16 over the years. + + 17 These public and public concerns are very + + 18 important and deserve thorough consideration. + + 19 Again, we urge the U.S. Coast Guard to hold + + 20 accessible public hearings, not in New York City and + + 21 Albany, but up and down the river communities that + + 22 we represent, that they hold them up and down the + + 23 river so that they may hear for themselves what this + + 24 proposal will do to our magnificent National + + 25 Heritage Hudson River. + + + + + + + + 126 + 1 We also respectfully request that a thorough + + 2 environmental review be conducted to ensure that we + + 3 do not undo all of the progress made in recent years + + 4 to make the Hudson the public asset and jewel it is + + 5 today. + + 6 Thank you. + + 7 [ Applause ] + + 8 SENATOR CARLUCCI: Thank you for being here, + + 9 and I really appreciate your advocacy and + + 10 particularly when you talk about boater safety. + + 11 And I know we've worked together and it was + + 12 mentioned working with Assemblywoman Galef to + + 13 actually pass a boater safety requirement in + + 14 New York State, and we know we've got a long way to + + 15 go. But that's something that I think really hasn't + + 16 been talked about too much tonight. And something + + 17 that should be highlighted is that now we did pass + + 18 the legislation that we are phasing in, depending + + 19 how old you are, who is required to take a basic + + 20 boater safety education. + + 21 It was mentioned about some of the horrific + + 22 accidents we've seen, the tragedies, the life that + + 23 has been lost, and we know that that is a direct + + 24 correlation to people that just did not have that + + 25 basic safety education. + + + + + + + + 127 + 1 And so I appreciate you highlighting that, + + 2 and I think that's something that as we have more + + 3 barges on the river, even though it has been + + 4 highlighted in the positive of doing this, that it + + 5 could be more safe. We know that as a recreational + + 6 user that it could be a hazard as well. + + 7 So I think that's another thing that we have + + 8 to highlight and talk about in terms of how this + + 9 could be a more dangerous circumstance given the + + 10 lack of requirement in terms of who needs that basic + + 11 boater safety education on the river, and the people + + 12 that have testified that know how unique the Hudson + + 13 River is and how education and experience is + + 14 something that is extremely important. + + 15 FRANK BERGMAN: Right. + + 16 And we really appreciate the efforts that you + + 17 and Sandy Galef have worked on for safety and boater + + 18 education, and I've spoken at some of your news + + 19 conferences about that as well. + + 20 SENATOR MURPHY: Frank, thank you for being + + 21 here tonight. Thank you for coming out of your way. + + 22 FRANK BERGMAN: Thank you very much. + + 23 [ Applause ] + + 24 MATT SLATER: I would like to invite + + 25 Emily Majer, who is the Deputy Mayor for the Village + + + + + + + + 128 + 1 of Tivoli, all the way up in northern Dutchess + + 2 County. + + 3 Emily. + + 4 [ Applause ] + + 5 SENATOR SERINO: Emily, I want to say thank + + 6 you because you have traveled even further than I + + 7 have. + + 8 EMILY MAJER: It's an honor to be here. + + 9 SENATOR SERINO: So thank you. You are the + + 10 last one to speak. Sorry you had to wait so long. + + 11 EMILY MAJER: That's okay. I'll make it + + 12 brief. Thank you for doing this, and thank you for + + 13 the invitation. + + 14 The Village of Tivoli adds our collective + + 15 voice to the chorus expressing concern over the + + 16 request from the Maritime Association of the Port of + + 17 New York and New Jersey, the American Waterways + + 18 Operators and other industry organizations to + + 19 increase the number of federally-designated + + 20 anchorages on the Hudson River between Yonkers and + + 21 Kingston. + + 22 The Maritime Association of the Port of + + 23 New York and New Jersey stated in their letter to + + 24 the Coast Guard dated January 21, 2016, "Trade will + + 25 increase on the Hudson River significantly over the + + + + + + + + 129 + 1 next few years with the lifting of the ban on + + 2 American crude exports for foreign trade, and + + 3 federally-designated anchorages are key to + + 4 supporting trade." + + 5 The Kingston hub of unofficial anchorages + + 6 below -- just south of the Kingston-Rhinecliff + + 7 Bridge at Kingston Flats, Port Ewen, and Big Rock + + 8 Point, in addition to the existing designated berths + + 9 at Hyde Park anticipate this increased trade. + + 10 There are certainly conversations to be had + + 11 about larger issues, about the environmental costs + + 12 of energy independence and how to plan for our + + 13 future energy needs. + + 14 I'm not qualified to speak to those issues, + + 15 but as a resident and representative of Tivoli, + + 16 which is just north of a cluster of three proposed + + 17 anchorage sites, I can talk about the impact that + + 18 increased industrial traffic will have on one tiny + + 19 village and the section of river that we call home. + + 20 Tivoli was active in maritime trade by the + + 21 mid-18th century. Wharfs and warehouses lined the + + 22 shore, but the construction of the railroad + + 23 gradually choked out that business, and by the early + + 24 20th century, the riverfront was comparatively + + 25 quiet, most businesses having moved a mile inland. + + + + + + + + 130 + 1 The waterfront, although currently + + 2 undeveloped, is well used by fishermen, kayakers, + + 3 and people, residents and visitors who come to + + 4 admire the Hudson and the views of the Catskills. + + 5 Although there are no proposed anchorages + + 6 within the Tivoli reach of the Hudson, the traffic + + 7 that will be encouraged and enabled by this change + + 8 will have immediate and dramatic physical impacts + + 9 upon our waterfront. + + 10 Within yards of the channel, our shoreline is + + 11 eroded by the wake of each tanker ship and barge + + 12 that passes. Consistent with our local waterfront + + 13 revitalization plan, the village has engaged the + + 14 Department of State, D.E.C., Dutchess County + + 15 Planning and Scenic Hudson to stabilize our + + 16 shoreline with the goal of creating a public park to + + 17 secure access to the water. + + 18 Along with this obvious immeasurable impact, + + 19 we are concerned about the environmental effects. + + 20 Legitimatizing the anchorages south of the + + 21 Kingstone-Rhinecliff Bridge, in conjunction with + + 22 future increased capacity at the Port of Albany, + + 23 could potentially allow an endless, noisy diesel + + 24 parade just off our shore. This would essentially + + 25 cut off river access to those recreational boaters + + + + + + + + 131 + 1 and fishermen, diminish the quality of life of + + 2 residents and visitors, and severely compromise the + + 3 public's experience of this scenic area of statewide + + 4 significance which is also within the Mid-Hudson + + 5 Historic Shoreline Scenic District and the Hudson + + 6 Valley Historic Landmark District. + + 7 Most worrisome and potentially disastrous is + + 8 the effect that this would have on the coves and + + 9 tidal marshes of the Tivoli Bay's Wildlife + + 10 Management area and the furred, feathered, and + + 11 finned inhabitants therein. + + 12 The Tivoli Bay's area is 1700 acres of tidal + + 13 marsh and upland forest with hiking trails, boat + + 14 launches and a bike path connecting the Village of + + 15 Tivoli to Bard College. + + 16 The bays are a designated New York bird + + 17 conservation area in recognition of its unique marsh + + 18 bird community. Its importance as a staging area + + 19 for migrating water fowl and migratory stopover + + 20 habitat for warblers. + + 21 Furthermore, the area is a New York State + + 22 Natural Heritage area recognized by the Department + + 23 of Environmental Conversation to call attention to + + 24 and protect the rare animals, rare plants and + + 25 significant natural communities on the state-owned + + + + + + + + 132 + 1 land. + + 2 The bays are also part of the Hudson River + + 3 Estuary and Research Preserve which provide field + + 4 laboratories for estuary, research, stewardship and + + 5 education by the D.E.C. + + 6 This unique and sensitive site is imperilled + + 7 by every petroleum product-bearing tanker that + + 8 passes by, and increasing the traffic increases the + + 9 risk. + + 10 We are well aware that accidents or incidents + + 11 do occur as evidenced by the running aground of a + + 12 dirt-bearing barge on the rocks of Magdalene Island + + 13 April 2013. + + 14 The impacts and dangers to Tivoli and other + + 15 communities, especially the natural resources along + + 16 the river that would be courted by allowing more + + 17 federally-designated anchorages are undeniable, and + + 18 in our estimation, the proposal is unsupportable. + + 19 SENATOR SERINO: Thank you very much, Emily. + + 20 [ Applause ] + + 21 Thank you, Emily, and you know what has been + + 22 the greatest thing about tonight is this concern is + + 23 not an R or a D situation. It is a quality of life. + + 24 It would only take one accident because they are + + 25 accidents that. You know, we have people that take + + + + + + + + 133 + 1 their drinking water out of the river. What do we + + 2 do? This is about our children and our + + 3 grandchildren having sustainability with our clean + + 4 water because our river is getting cleaner. I water + + 5 skied in that river for many years. + + 6 I thank you very much for traveling and for + + 7 everyone that has come tonight and stayed tonight + + 8 through the whole session. So thank you very much. + + 9 SENATOR MURPHY: Emily, thank you. + + 10 [ Applause ] + + 11 MATT SLATER: We have one last speaker not on + + 12 the agenda tonight, Jerry -- I'm going to botch your + + 13 name -- Faiella, from Hudson River Historic River + + 14 Towns. + + 15 JERRY FAIELLA: Thank you very much. + + 16 I appreciate the ability to put a comment on the + + 17 record. + + 18 Senators Murphy, Carlucci, and Serino, thank + + 19 you for putting organizing this event, and I + + 20 apologize for not pre-registering. I appreciate the + + 21 opportunity to speak. + + 22 I'm Jerry Faiella, the Executive Director of + + 23 Historic Hudson River Towns. HHRT, we are a + + 24 not-for-profit organization formed in 1984, and we + + 25 are one of the intermunicipal organizations to help + + + + + + + + 134 + 1 the Hudson Valley riverfront communities build + + 2 tourism, marketing, revitalization and downtown + + 3 renewal. + + 4 We are very active with Hudson Valley + + 5 Greenway, and we also have a seat on the Hudson + + 6 River Estuary Management Advisory Committee, the + + 7 D.E.C., something that Frank Bergman mentioned. + + 8 We are very committed to the region both for + + 9 tourism and to the protection of the Hudson River. + + 10 Our membership consists of 20 local jurisdictions on + + 11 the lower Hudson Valley, both sides of the river + + 12 from Yonkers to Beacon and from Nyack to Newburgh, + + 13 and we have been working closely with the City of + + 14 Yonkers and Riverkeeper on this endeavor. + + 15 I'm talking fast because the hour is late. I + + 16 appreciate the time. + + 17 The direct impacts of the designated + + 18 anchorage areas vary from community to community, + + 19 and it is not HHRT's position to simply move the + + 20 issue from one riverfront community to another. We + + 21 think a reasonable solution needs to be developed + + 22 that protects the community's waterfront which many + + 23 communities consider now as their front door, + + 24 something that is environmental sound and + + 25 esthetically acceptable. + + + + + + + + 135 + 1 As such, the Coast Guard should recognize the + + 2 need for an E.I.S. which you've heard tonight + + 3 already, because this proposal should not be + + 4 included in an categorical exemption normally + + 5 bestowed upon the placement of anchorages. + + 6 You heard from Deb Malone tonight that + + 7 tourism is a $4.7 billion industry in the region + + 8 that employs 81,000 people. We should not lose + + 9 sight of that as an economic driver when people talk + + 10 about this as an economic driver. + + 11 Our fears rest with the underlying intention + + 12 that has not been stated up front, but is clearly + + 13 evident when you start to read between the lines of + + 14 the letters that have been submitted in support of + + 15 this proposal. And it comes from the Pilot's + + 16 Association letter where they talk about this as + + 17 supply chain management. + + 18 So when you think about supply chain + + 19 management, you have to conclude that we are talking + + 20 about the movement of 2.8 billion gallons of bakken + + 21 crude oil through the Port of Albany a year. And + + 22 most of this is not for domestic refineries since + + 23 the Linden plant is at 85% capacity, but is + + 24 predominantly targeted for foreign consumption now + + 25 that the federal ban has been lifted. + + + + + + + + 136 + 1 We believe these anchorage locations will + + 2 grow into floating storage facilities that will + + 3 impede the growth of the tourism industry throughout + + 4 the valley. + + 5 So HHRT, we have engaged the firm of + + 6 Blanchard and Wilson to see what our legal rights + + 7 are or for what the legal rights are for the + + 8 jurisdictions in this proposal so we can have an + + 9 impact on the outcome. + + 10 Now you've heard about the federal Coastal + + 11 Zone Management Program. What that does is it + + 12 provides local jurisdictions empowered through the + + 13 New York State Department of State to adopt local + + 14 waterfront revitalization plans, and these plans, + + 15 once adopted, and approved by the New York + + 16 Department of State and the federal government, + + 17 requires all parties, governmental and private, that + + 18 are putting forth an action, to be consistent, and + + 19 we stress the word consistent -- you've heard that + + 20 here tonight -- with the plan. + + 21 In addition, New York State law provides + + 22 local jurisdictions with the authority to legislate + + 23 land use issues and develop local zoning which you + + 24 are all familiar with. The adoption of a + + 25 comprehensive master plan gives the local + + + + + + + + 137 + 1 jurisdictions considerable authority in determining + + 2 their overall development strategy. + + 3 We think understanding these two principles, + + 4 which I think Ned Sullivan talked about, HRRT is + + 5 developing for its member community a draft + + 6 resolution that will initiative a systematic review + + 7 of their LWRP and comprehensive land use plans, to + + 8 incorporate the importance of maintaining river + + 9 access to the navigable channel for commercial + + 10 tourism use, address coastal uses within and + + 11 immediately beyond their boundaries so as to not + + 12 negatively impact passive recreational use of the + + 13 waterfront, and to preserve the esthetic water + + 14 quality for mixed-use development and protect the + + 15 environmental features of the Hudson River and its + + 16 estuaries to maintain a balanced ecosystem for + + 17 water-related recreational use. + + 18 We think it's going to be imperative for the + + 19 communities to move in that direction because that's + + 20 going to give them standing here in this case. + + 21 We think it's imperative for the Coast Guard + + 22 anchorage program to prove that it is consistent + + 23 with the goals stated in these LWRPs and local land + + 24 use comprehensive plans and use that as the + + 25 objective to force the need for review that we are + + + + + + + + 138 + 1 talking about tonight. + + 2 Some argue that the rule making is exempt, + + 3 but we think that, we believe that in this instance + + 4 because consistency with the CZM, federal + + 5 regulations will justify the preparation of an E.I.S + + 6 and really address the potential impacts. + + 7 Without further knowledge of the actual terms + + 8 and conditions of the rule making, it is difficult + + 9 to further comment, but at this particular point in + + 10 time, we are taking the position that we really want + + 11 to see the E.I.S. developed. + + 12 Thank you very much for your time. + + 13 Appreciate it. + + 14 [ Applause ] + + 15 SENATOR MURPHY: I believe that was our last + + 16 speaker for the evening. + + 17 Senator Serino, do you want to say any + + 18 closing words, then I'll go to Senator Carlucci. + + 19 SENATOR SERINO: I just want to say thank + + 20 you. And I also believe -- I don't know if anyone + + 21 is here from the Women's Council of Realtors or the + + 22 Dutchess County Association of Realtors because they + + 23 both came out in opposition. + + 24 I am a realtor myself in opposition of the + + 25 barges, and thank you once again everyone for being + + + + + + + + 139 + 1 here this evening. Thank you. + + 2 SENATOR MURPHY: Senator Carlucci. + + 3 SENATOR CARLUCCI: I want to thank Senator + + 4 Murphy and Senator Serino for being here and + + 5 everyone that has testified and everyone that is + + 6 here. + + 7 We've heard the reasons why this is so + + 8 important. I also wanted to mention Councilwoman + + 9 Elizabeth Feldman is here from Ossining who I forgot + + 10 to announce earlier. Thank you. You have been here + + 11 since the beginning and sat all the way through. We + + 12 know you think this is very important. + + 13 And Jerry who spoke. We talked about + + 14 tourism. One of the things we are working on and we + + 15 believe is a reality is building a museum at + + 16 Sing-Sing. And we talk about how, Jerry talked about + + 17 that the river is the front door to many of the + + 18 communities along the Hudson River, and it's + + 19 something that we have to protect dearly. And we've + + 20 heard so many great and enlightening words being + + 21 spoken tonight. And the fact that we've got to make + + 22 sure we don't tread lightly, that we leave no stone + + 23 unturned, that we dot our Is and cross our Ts + + 24 because if we learn anything from history, it's the + + 25 mistake of past generations. And we can't allow + + + + + + + + 140 + 1 that mistake to happen again. + + 2 So I appreciate everyone, no matter what side + + 3 of the issue you are on. All of us have to work + + 4 together to make sure that we protect the integrity + + 5 of the river and protect the quality of life of the + + 6 residents living among the river. + + 7 So I look forward to working with all of you, + + 8 everyone in this room and beyond, to make sure that + + 9 the best decisions are being made for us currently + + 10 and for future generations. + + 11 So thank you so much for being here. I look + + 12 forward to working with everyone in the future. + + 13 Thank you. + + 14 [ Applause ] + + 15 SENATOR MURPHY: I would just like to thank + + 16 Mayor Schmidt. I'm not sure if he is still here, + + 17 but he is the one who opened up his doors to allow + + 18 us all to be here tonight. + + 19 And to the residents hanging in there with us + + 20 for close to four hours, for being here tonight on + + 21 this incredible, incredibly important topic. + + 22 To the 14 people who testified tonight. + + 23 And to Ed Cook for actually coming up and + + 24 giving us a few answers. Ed, I know you traveled... + + 25 you're a trooper. Thank you for coming up and + + + + + + + + 141 + 1 answering some of our questions. We truly + + 2 appreciate it. + + 3 There is going to be much, much more, but + + 4 this is a start in the right direction to figure out + + 5 what we have to do. + + 6 The Hudson River is one of the most majestic + + 7 places, I believe, in not only New York State but in + + 8 the United States. Look at the foliage that is + + 9 going on the foliage. It is absolutely beautiful. + + 10 I'll put it up against anybody and any place in the + + 11 United States. + + 12 Some of the stuff that we've talked about + + 13 tonight, these LWRPs, these local waterfront + + 14 revitalization plans, and these are important + + 15 designations to our community and to the waterfront + + 16 areas. + + 17 I personally over the past two years have + + 18 passed 14 bills to designate some of our lakes and + + 19 some of our estuaries to make sure that we can keep + + 20 these places the beautiful little lakes and + + 21 estuaries that they are. + + 22 This are stuff that Senator Serino said, + + 23 there is no D on this. There is no R on this. + + 24 There is no I on this. There is no C on this. This + + 25 is the right thing to do for our environment, for + + + + + + + + 142 + 1 our kids, and for our future. And these are the + + 2 reasons why we are having -- and we are going to + + 3 continue beating the drum of having these public + + 4 hearings. I'm not going to wait for the Coast Guard + + 5 to show up, because they didn't. + + 6 [ Applause ] + + 7 They were invited. They were invited here + + 8 and no one had the courtesy to show up on an + + 9 important event, knowing that these anchorage sites + + 10 that they're proposing, they won't even come and + + 11 give us answers about. To me that's is graceful, + + 12 and I -- don't get me wrong -- I hold them in high + + 13 regard. They do wonderful work. And like + + 14 Mr. Cronin said, there is the rank and file and + + 15 then there is the administration, and the + + 16 administration is again, like I said before, put the + + 17 federal government in it and they can screw up + + 18 anything. Okay. + + 19 So I thank you all for being patient here + + 20 tonight. I thank you for participating, for the + + 21 people who testified, for Senator Carlucci coming + + 22 across from Rockland County. Thank you. It's + + 23 always great working with you. For Senator Serino + + 24 coming down from Dutchess County, and my colleagues, + + 25 I appreciate the support here. This is something + + + + + + + + 143 + 1 that is vital to all of us. + + 2 Between the three of us, we represent close + + 3 to a million people, and your voices are going to be + + 4 heard through us. I can promise you that. + + 5 Thank you, and good night. + + 6 + + 7 (Whereupon, the Hudson River Barge + + 8 Public Hearing, concluded and adjourned.) + + 9 + + 10 ---oOo--- + + 11 + + 12 + + 13 + + 14 + + 15 + + 16 + + 17 + + 18 + + 19 + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + \ No newline at end of file From ff199d11a71c0b41ef4801a7c0bb2d9c8c5e6d0f Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Mon, 10 Feb 2020 17:21:03 -0500 Subject: [PATCH 13/29] Added handling on error when a LocalDateTime cannot be parsed. --- .../controller/api/transcript/TranscriptGetCtrl.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java index a7ddcbaae..65343564e 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java @@ -151,4 +151,11 @@ public ErrorResponse handleTranscriptNotFoundEx(TranscriptNotFoundEx ex) { // TODO: Doesn't display anything on the webpage, even though the response is received. return new ViewObjectErrorResponse(ErrorCode.TRANSCRIPT_NOT_FOUND, new TranscriptIdView(ex.getTranscriptId())); } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public ErrorResponse handleIllegalArgumentEx(IllegalArgumentException ex) { + // TODO: Doesn't display anything on the webpage, even though the response is received. + return new ViewObjectErrorResponse(ErrorCode.INVALID_ARGUMENTS, ex.getMessage()); + } } From 1aa9eceab1f4a95e00938d03663dee6bc6b12c1c Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Thu, 20 Feb 2020 11:18:56 -0500 Subject: [PATCH 14/29] Fixed a file that only had the first page of a hearing, rather than the whole text. --- ...1 ValeskyAgingCommitteeRoundtableFINAL.txt | 4448 ++++++++++++++++- 1 file changed, 4219 insertions(+), 229 deletions(-) diff --git a/src/test/resources/hearing/05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt b/src/test/resources/hearing/05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt index a17ea2c66..08aae17ea 100644 --- a/src/test/resources/hearing/05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt +++ b/src/test/resources/hearing/05-18-11 ValeskyAgingCommitteeRoundtableFINAL.txt @@ -1,229 +1,4219 @@ - - - - 1 BEFORE THE NEW YORK STATE SENATE - COMMITTEE FOR THE AGING - 2 ----------------------------------------------- - - 3 NEW YORK STATE FORUM/TOWN HALL - - 4 ROUNDTABLE ON THE - SAGE COMMISSION'S PROPOSAL TO MERGE THE - 5 NYS OFFICE FOR THE AGING WITH - THE DEPARTMENT OF HEALTH - 6 - ----------------------------------------------- - 7 - Senate Capitol Building - 8 172 State Street - Room 124, CAP - 9 Albany, New York - - 10 May 18, 2011 - 9:00 a.m. - 11 - - 12 PRESIDING: - - 13 Senator David J. Valesky - Chair - 14 - - 15 SENATE MEMBERS PRESENT: - - 16 Senator Martin Golden - - 17 - - 18 ROUNDTABLE PARTICIPANTS: - - 19 Maria Alvarez - Executive Director of the - 20 NY Statewide Senior Action Council - - 21 Allison Auldridge - Policy Associate - 22 Services and Advocacy for GLBT Elders - - 23 Michael Burgess - Public Policy - 24 NY Statewide Senior Action Council - - 25 - - - - - - - - 2 - 1 ROUNDTABLE PARTICIPANTS, Continued: - - 2 Laura A. Cameron - Executive Director - 3 NYS Association of Area Agencies on Aging - - 4 AnneMarie Cook - President & CEO - 5 Lifespan of Greater Rochester - - 6 Ann Disarro - New York State Coalition for the Aging - 7 - Richard Haldeman - 8 Long-Term Care Ombudsman Program, - representing the Alliance - 9 - Igal Jellinek - 10 Executive Director - Council of Senior Centers and - 11 Services of New York City - - 12 Molly Krakowski - Director of Legislative Affairs - 13 JPAC at the Jewish Association of - Services for the Aged in New York City - 14 - JoAnne Martel - 15 Public Policy - NYS Association of Area Agencies on Aging - 16 - David McNally - 17 AARP - - 18 Edie Mesick - UJA Federation of New York - 19 - Nancy Miller, LSMW - 20 Executive Director/CEO - VISIONS/Services for the Blind - 21 and Visually Impaired; - and president of the NY Vision - 22 Rehab Association. - - 23 Christopher Nadeau - NYS Adult Day Services Association - 24 - - 25 - - - - - - - - 3 - 1 - - 2 ROUNDTABLE PARTICIPANTS, Continued: - - 3 Deb Ritano - Jewish Family Services for - 4 the NORC project - - 5 Mike Romano - Director - 6 Oneida County Office for - Aging and Continuing Care; - 7 also, board of directors of the - NYS Association of Area Agencies - 8 on Aging - - 9 Carin A. Tinney - Policy Analyst - 10 United Neighborhood Houses - - 11 - - 12 ALSO IN ATTENDANCE: - - 13 Brendon Bachelor - - 14 Jean Cerenos - - 15 Brenda Marino - - 16 Ed Wardenstein - - 17 Frank Stevio - - 18 - - 19 - - 20 - - 21 - - 22 - - 23 - - 24 - - 25 - - - - - - - - 4 - 1 SENATOR VALESKY: I want to welcome everyone - - 2 to -- this is called "a meeting of public interest." - - 3 It's intended to be more of a roundtable discussion - - 4 than what a public hearing would be. And I thought - - 5 that might serve our purposes better for the topic - - 6 that we're discussing. - - 7 For those of you who don't know, my name is - - 8 Senator David Valesky. I'm the chair of the - - 9 Senate Aging Committee. - - 10 And, I have an interest in gathering all of - - 11 you together, and many of you have actually reached - - 12 out to me in my office, to have a discussion about - - 13 the SAGE Commission's work, and the work that - - 14 they're doing right now, and what may or may not be - - 15 included as part of their report as it relates to - - 16 the state Office for the Aging. - - 17 Just before we start, the reason for the - - 18 microphones, we in the senate have -- as, hopefully, - - 19 many of you know, are placing a high priority on - - 20 transparency and openness in government. So, this - - 21 meeting is being webcast. And that's why the - - 22 technology, here. - - 23 And, in fact, anyone who has any comments, - - 24 who might be watching this, we certainly ask you to - - 25 submit them to my office. - - - - - - + + + + 1 BEFORE THE NEW YORK STATE SENATE + COMMITTEE FOR THE AGING + 2 ----------------------------------------------- + + 3 NEW YORK STATE FORUM/TOWN HALL + + 4 ROUNDTABLE ON THE + SAGE COMMISSION'S PROPOSAL TO MERGE THE + 5 NYS OFFICE FOR THE AGING WITH + THE DEPARTMENT OF HEALTH + 6 + ----------------------------------------------- + 7 + Senate Capitol Building + 8 172 State Street + Room 124, CAP + 9 Albany, New York + + 10 May 18, 2011 + 9:00 a.m. + 11 + + 12 PRESIDING: + + 13 Senator David J. Valesky + Chair + 14 + + 15 SENATE MEMBERS PRESENT: + + 16 Senator Martin Golden + + 17 + + 18 ROUNDTABLE PARTICIPANTS: + + 19 Maria Alvarez + Executive Director of the + 20 NY Statewide Senior Action Council + + 21 Allison Auldridge + Policy Associate + 22 Services and Advocacy for GLBT Elders + + 23 Michael Burgess + Public Policy + 24 NY Statewide Senior Action Council + + 25 + + + + + + + + 2 + 1 ROUNDTABLE PARTICIPANTS, Continued: + + 2 Laura A. Cameron + Executive Director + 3 NYS Association of Area Agencies on Aging + + 4 AnneMarie Cook + President & CEO + 5 Lifespan of Greater Rochester + + 6 Ann Disarro + New York State Coalition for the Aging + 7 + Richard Haldeman + 8 Long-Term Care Ombudsman Program, + representing the Alliance + 9 + Igal Jellinek + 10 Executive Director + Council of Senior Centers and + 11 Services of New York City + + 12 Molly Krakowski + Director of Legislative Affairs + 13 JPAC at the Jewish Association of + Services for the Aged in New York City + 14 + JoAnne Martel + 15 Public Policy + NYS Association of Area Agencies on Aging + 16 + David McNally + 17 AARP + + 18 Edie Mesick + UJA Federation of New York + 19 + Nancy Miller, LSMW + 20 Executive Director/CEO + VISIONS/Services for the Blind + 21 and Visually Impaired; + and president of the NY Vision + 22 Rehab Association. + + 23 Christopher Nadeau + NYS Adult Day Services Association + 24 + + 25 + + + + + + + + 3 + 1 + + 2 ROUNDTABLE PARTICIPANTS, Continued: + + 3 Deb Ritano + Jewish Family Services for + 4 the NORC project + + 5 Mike Romano + Director + 6 Oneida County Office for + Aging and Continuing Care; + 7 also, board of directors of the + NYS Association of Area Agencies + 8 on Aging + + 9 Carin A. Tinney + Policy Analyst + 10 United Neighborhood Houses + + 11 + + 12 ALSO IN ATTENDANCE: + + 13 Brendon Bachelor + + 14 Jean Cerenos + + 15 Brenda Marino + + 16 Ed Wardenstein + + 17 Frank Stevio + + 18 + + 19 + + 20 + + 21 + + 22 + + 23 + + 24 + + 25 + + + + + + + + 4 + 1 SENATOR VALESKY: I want to welcome everyone + + 2 to -- this is called "a meeting of public interest." + + 3 It's intended to be more of a roundtable discussion + + 4 than what a public hearing would be. And I thought + + 5 that might serve our purposes better for the topic + + 6 that we're discussing. + + 7 For those of you who don't know, my name is + + 8 Senator David Valesky. I'm the chair of the + + 9 Senate Aging Committee. + + 10 And, I have an interest in gathering all of + + 11 you together, and many of you have actually reached + + 12 out to me in my office, to have a discussion about + + 13 the SAGE Commission's work, and the work that + + 14 they're doing right now, and what may or may not be + + 15 included as part of their report as it relates to + + 16 the state Office for the Aging. + + 17 Just before we start, the reason for the + + 18 microphones, we in the senate have -- as, hopefully, + + 19 many of you know, are placing a high priority on + + 20 transparency and openness in government. So, this + + 21 meeting is being webcast. And that's why the + + 22 technology, here. + + 23 And, in fact, anyone who has any comments, + + 24 who might be watching this, we certainly ask you to + + 25 submit them to my office. + + + + + + + + 5 + 1 So, again, this intended -- we may have other + + 2 members of the Aging Committee join us. There are a + + 3 number of things going on here in the Capitol this + + 4 morning. So, hopefully, we'll have some of my + + 5 colleagues join us as well. + + 6 I thought what we could do: We have about an + + 7 hour, give or take, and this is intended, again, to + + 8 be a rather informal conversation. + + 9 My goal in conducting this roundtable + + 10 discussion, is to listen to all of you, to take your + + 11 comments, to take your suggestions. And my intent + + 12 is to catalog the comments that I receive here + + 13 today, and get them to the SAGE Commission, and + + 14 Paul Francis, who is heading up that effort, just as + + 15 soon as possible. + + 16 The intent is, therefore, that all of you + + 17 will have a voice in the commission's deliberations, + + 18 which may be taking a while. I think that, you + + 19 know, from a process perspective, I think it may be + + 20 taking a little longer than they had originally + + 21 anticipated; but, that's fine with us. It gives us + + 22 more time to make our case. + + 23 So, I think with that, I would just like to + + 24 go around the room, and have everyone, at this + + 25 point, introduce themselves; tell us where you are + + + + + + + + 6 + 1 from, and then we'll open up the conversation. + + 2 So, if we could, we'll start on my left. + + 3 MS. MILLER: Hi. I'm Nancy Miller. I'm the + + 4 executive director/CEO of VISIONS/Services for the + + 5 Blind and Visually Impaired; and president of the + + 6 New York Vision Rehab Association. + + 7 And we serve people who are blind and + + 8 visually impaired in the New York City metropolitan + + 9 area. + + 10 And, NYVRA, the association, represents all + + 11 of the vision rehab agencies, consumer groups, and + + 12 people who are blind, around New York State. + + 13 MS. KRAKOWSKI: Hi. My name is + + 14 Molly Krakowski. I'm the director of legislative + + 15 affairs in JPAC at the Jewish Association of + + 16 Services for the Aged in New York City. + + 17 MS. ALVAREZ: Hi. My name is Maria Alvarez. + + 18 I'm the executive director of the New York Statewide + + 19 Senior Action Council. + + 20 MR. BURGESS: Mike Burgess. I'm doing public + + 21 policy work for the Statewide Senior Action Council, + + 22 now. + + 23 MS. MESICK: Edie Mesick. I'm with + + 24 UJA Federation of New York. + + 25 MR. JELLINEK: Igal Jellinek. I'm the + + + + + + + + 7 + 1 executive director of the Council of Senior Centers + + 2 and Services of New York City. + + 3 MS. COOK: AnnMarie Cook, president & CEO of + + 4 Lifespan of Greater Rochester. + + 5 MR. McNALLY: David McNally, with AARP. + + 6 MR. HALDEMAN: Richard Haldeman. I'm with + + 7 the Long-Term Care Ombudsman Program, representing + + 8 the Alliance. + + 9 MS. TINNEY: I'm Carin Tinney, policy analyst + + 10 for United Neighborhood Houses. + + 11 We have a federation of 37 settlement houses + + 12 and community centers around New York City. + + 13 MS. RITANO: I am Deb Ritano, for + + 14 Jewish Family Services for the NORC project, and our + + 15 mission is to help seniors age in place. + + 16 MR. ROMANO: I'm Mike Romano, director of + + 17 Oneida County Office for Aging and Continuing Care. + + 18 And, I also serve on the board of directors + + 19 of the New York State Association of Area Agencies + + 20 on Aging. + + 21 MS. CAMERON: I'm Laura Cameron, executive + + 22 director of the New York State Association of Area + + 23 Agencies on Aging. + + 24 MR. NADEAU: Christopher Nadeau, New York + + 25 State Adult Day Services Association. + + + + + + + + 8 + 1 MS. MARTEL: JoAnne Martel, public policy of + + 2 the New York State Association of Area Agencies on + + 3 Aging. + + 4 MS. AULDRIDGE: Allison Auldridge. I'm the + + 5 policy associate for Services and Advocacy for + + 6 GLBT Elders. + + 7 MS. DISARRO: I'm Ann Disarro, and I'm with + + 8 NYSCA (the New York State Coalition for the Aging). + + 9 SENATOR VALESKY: And in the back? + + 10 MR. BACHELOR: Brendon Bachelor (ph.) from + + 11 our town hall. + + 12 SENATOR VALESKY: Okay? + + 13 MS. CERENOS: Hi. Jean Cerenos (ph.), from + + 14 [inaudible]. + + 15 SENATOR VALESKY: Back there? + + 16 Go ahead. + + 17 MS. MARINO: Brenda Marino. + + 18 MR. WARDENSTEIN: Ed Wardenstein (ph.). + + 19 MR. STEVIO: Frank Stevio (ph.), [inaudible]. + + 20 SENATOR VALESKY: Very good. + + 21 Thank you all for being here. + + 22 I'm going to ask -- even though he doesn't + + 23 know this -- I'm going to actually ask Mike Burgess, + + 24 who, as all of you know, is a former director of the + + 25 state Office for the Aging, actually, speak first, + + + + + + + + 9 + 1 and offer us his thoughts, from his perspective. + + 2 And then, again, really, the intent is to + + 3 have a give-and-take. I would like everybody to + + 4 have an opportunity to, certainly, weigh-in. But, + + 5 certainly, feel free to have a back-and-forth, + + 6 because that's the intent of doing this in this + + 7 format. + + 8 So, Mike, if you wouldn't mind kicking us + + 9 off, that would be great. + + 10 MR. BURGESS: Thank you for having this + + 11 meeting, Senator, because we requested that you do + + 12 this, and appreciate it. + + 13 I would just say, to start, that, you know, + + 14 the state Office for the Aging, the history of it, + + 15 50 years, actually, this year, Governor Rockefeller + + 16 started it before there was an Older Americans Act, + + 17 and before there were state Offices for the Aging. + + 18 But, anyway, you know, for 45 years, there + + 19 has been a state Office for the Aging in New York + + 20 State. And it has been, the original charge of it, + + 21 was to be an agency to, advocate, and serve older + + 22 people, across the board, on a wide variety of + + 23 issues and concerns. + + 24 And, then, you know, here in New York State, + + 25 we have an elder law that was put into place also. + + + + + + + + 10 + 1 And I think, in the context of the discussion + + 2 today, I would like to keep pointing out, that the + + 3 mission has always been very broad. In fact, the + + 4 idea was, at the time, when older people were not + + 5 being fully represented in state government, and had + + 6 a lot of very serious problems, which many of us + + 7 have helped to address over the years, that older + + 8 people needed an advocate in all of state + + 9 government. Not just providing services, but that + + 10 there would be an agency that would sort of do an + + 11 aging-impact statement about everything going on in + + 12 government, whether it be in, health or housing, + + 13 discrimination, or whatever. + + 14 So, that's been the nature of the agency. + + 15 And, then, the network of county agencies, + + 16 over the years, of course, has been fully developed, + + 17 along with all of the program and services in + + 18 different areas that are represented at the table + + 19 here today. + + 20 And I think that many of us feel that, as we + + 21 go forward, looking at, certainly, the fact that + + 22 there can be some efficiencies, and some + + 23 administrative changes, that will be saving some + + 24 money, and maybe make services more coordinated, we + + 25 don't want to lose the things that have made this + + + + + + + + 11 + 1 network so unique and special. And that older + + 2 people look upon this as their gateway into state + + 3 government. + + 4 It's really kind of a constitu- -- one of the + + 5 few agencies that really, as opposed to being + + 6 regulatory, or just a bureaucracy, is the + + 7 constituent service agency, that provides some + + 8 direct services, like the ombuds program, is + + 9 actually run by the state Office for the Aging. The + + 10 only program that the state actually runs. + + 11 And, then, having a senior citizens' help + + 12 line, having a governor's advisory council, I think + + 13 that all of these issues, as we have heard some of + + 14 the debate about the SAGE Commission, are these + + 15 kinds of things, this kind of entry, this kind of + + 16 outreach to the public, is this going to be + + 17 maintained? + + 18 Or, you know, if there are other + + 19 alternatives, let's separate out some of the + + 20 functions that we've seen, and make sure that, that + + 21 just for the sake of efficiencies, that we don't + + 22 lose the advocacy part of it. + + 23 So, that would be kind of my overview of + + 24 this, but everybody else has a lot more they can + + 25 give. + + + + + + + + 12 + 1 SENATOR VALESKY: Who would like to -- + + 2 MS. MESICK: Laura, do you want to speak + + 3 first, and then I will? + + 4 MS. CAMERON: Sure. + + 5 MS. MESICK: Okay. + + 6 MS. CAMERON: Well, Senator, thank you again + + 7 for convening this meeting. It's very important, as + + 8 we're looking at the future of aging. + + 9 In preparation for this meeting, the group + + 10 that is seated today has been working together in an + + 11 informal coalition. We've been having a lot of + + 12 conference calls and in-person meetings. + + 13 And our goal was, really, after we saw the + + 14 SAGE Commission's goal of efficiency in government, + + 15 et cetera, and there were a few recommendations + + 16 that, you know, have bubbled to the surface from, + + 17 apparently, their steering committees, and, we, as + + 18 various groups representing senior programming, got + + 19 together and said: + + 20 Let's collectively put our minds together, + + 21 and what can we dream that the state Office for + + 22 Aging could be? And, what would the possibilities + + 23 be? + + 24 And, how could we best serve the seniors of + + 25 this state? -- which, pretty soon, will be us, + + + + + + + + 13 + 1 sitting in the room. I think we're all realizing, + + 2 that's kind around the corner for us as well. + + 3 So, how can we put our minds together, and + + 4 make a recommendation to the SAGE Commission, and to + + 5 you, as chair of the Aging Committee? + + 6 And, what would it look like? What would we + + 7 call it? + + 8 All of those kinds of questions we've been + + 9 talking about. + + 10 So, we do -- we do -- we did put together, + + 11 actually, a written proposal. And we'd be happy to + + 12 share it with you. + + 13 To be honest, we finalized it in the late + + 14 hours last night, so -- + + 15 [Laughter.] + + 16 MS. CAMERON: -- sort of "hot off the press," + + 17 if you will. + + 18 But, what we would like to do is, I'll + + 19 introduce the concept. And, then, there are some + + 20 others that will really talk about the underpinnings + + 21 and the guiding principles that got us to the + + 22 proposal we're going to give to you today. + + 23 SENATOR VALESKY: Great. + + 24 MS. CAMERON: Essentially, what we have + + 25 proposed, is something that we call "The Office for + + + + + + + + 14 + 1 Aging and Community Supports," with added + + 2 responsibilities; because, we believe that NYSOFA + + 3 has always been the picture of efficiency, and has + + 4 delivered services exceptionally well. + + 5 Mike Burgess is a previous NYSOFA director; + + 6 has highlighted a lot of the really good things that + + 7 NYSOFA has done. + + 8 What we envision is, an enhanced-and-improved + + 9 NYSOFA; because, you know, as our state has fiscal + + 10 issues, we believe that NYSOFA is well positioned to + + 11 provide the cost-effective services to keep seniors + + 12 in their homes, which is where they want to be. + + 13 And, they only can do that, however, if they + + 14 do have additional resources and additional programs + + 15 that helps seniors. + + 16 You're well aware of all of the demographics + + 17 of seniors. And we really believe that this would + + 18 position the state office very well, for the future, + + 19 and for our seniors. + + 20 And as we examine this, what really led us to + + 21 this, was a long list of guiding principles, that + + 22 Edie will talk about, which will help you to + + 23 understand how we came to that conclusion. + + 24 MS. MESICK: Right. + + 25 So, we wanted to approach the question, + + + + + + + + 15 + 1 because we both understood that there was a + + 2 proposal, perhaps, that the SAGE Commission was + + 3 considering. + + 4 But, we also understood that the real + + 5 question is: What is best for the aging of the + + 6 population in New York State? + + 7 And, also: What is best, from a public + + 8 policy perspective? + + 9 And, so, we looked at, really: What are our + + 10 guiding principles? + + 11 What do we think are the issues that need to + + 12 be considered in any configuration of service + + 13 delivery in New York State? + + 14 And, so, we started to understand, to make + + 15 clear to ourselves -- and we thank you again, + + 16 Senator, for this opportunity to make it clear + + 17 through your efforts as well -- that the aging + + 18 process is multi-faceted. That it is not -- most + + 19 seniors, as they age, do not need medical services. + + 20 What they need is social supports. + + 21 And, as Mike said, as Laura said, it's so + + 22 important that this ---therefore, that the state's + + 23 Office for Aging services be, really, an independent + + 24 office; an office that reports directly to the + + 25 Governor, so that it really reflects the voice of + + + + + + + + 16 + 1 the people. + + 2 We looked at the fact that, obviously, aging + + 3 in place, in homes, and with community-based + + 4 services, is the least expensive, and the most + + 5 appropriate, way of delivering services. + + 6 So, that has to be a part of whatever this + + 7 new entity looks like. + + 8 We recognize that the ombudsman program is a + + 9 very successful program. And, it needs to be + + 10 protected from conflict of interest with an agency + + 11 that otherwise regulates services. + + 12 So, we put that down as one of our guiding + + 13 principles. + + 14 We also recognized that there is an important + + 15 opportunity in this question for many other services + + 16 that are provided in different agencies in + + 17 New York State. + + 18 And, therefore, our recommendation is, that + + 19 we have, truly, a strengthened Office for the Aging, + + 20 and an Office for Aging and Community Supports, that + + 21 takes in some other of the services. + + 22 So, those are our guiding principles. That's + + 23 how we came to this recommendation. + + 24 And I'm sure there are others who want to + + 25 jump in, from their particular perspectives. + + + + + + + + 17 + 1 MS. COOK: Senator, AnnMarie Cook again, from + + 2 Rochester. + + 3 We also talked about strengthening, really, + + 4 the Office for Aging, for two reasons, as others + + 5 have said: + + 6 One: We recognize the difficult financial + + 7 times that there are. There is no question about + + 8 that, and that services have to be delivered in a + + 9 cost-effective manner. + + 10 Second thing: With this proposal, that we're + + 11 saying, preserves the desire of older adults to + + 12 remain independent. + + 13 And I think the philosophy of NYSOFA has + + 14 always been to support and empower older adults; + + 15 and, therefore, has always led to that + + 16 cost-effective way. + + 17 We're also suggesting, in addition to + + 18 state Office for Aging programs, because they are + + 19 disbursed throughout the state, I'm just going to + + 20 name a few: + + 21 First of all: The Kinship Care Program + + 22 currently resides in the Office of Children and + + 23 Family Services, in which, of course, is + + 24 Grandparents Raising Grandchildren. + + 25 And those individuals can benefit greatly, I + + + + + + + + 18 + 1 think, from other support services that Office for + + 2 Aging could bring, and melding that into the office. + + 3 Second one: HEAP, the heating assistance + + 4 program for older adults over the age of 60, + + 5 currently, an OTDA, and, as we all know here, that + + 6 provide direct services. There are so many older + + 7 adults who need that assistance. And, need many + + 8 other programs, that they need HEAP services. + + 9 Third one we'd like to talk about is: + + 10 False Prevention Program. + + 11 Currently, in the department of health, falls + + 12 are the leading cause of accidental death in older + + 13 adults. + + 14 Also, is a great way, very cost-effective + + 15 way, in which we can provide evidence-based services + + 16 to older adults, to prevent falls, in our senior + + 17 centers, and other areas where older adults gather. + + 18 And, then, the residential emergency + + 19 services, home repair, to the elderly; once, again, + + 20 helping them remain independent in their own + + 21 community. + + 22 There are other services in other areas of + + 23 state government. We have highlighted these, but we + + 24 would like to bring these services that help older + + 25 adults under the umbrella of the Office for Aging + + + + + + + + 19 + 1 and Community Support Services. + + 2 MS. ALVAREZ: Maria Alvarez, from New York + + 3 Statewide Senior Action Council. + + 4 The other thing is, that we also recognize + + 5 that the population of New York State is + + 6 increasingly aging, so the infrastructure has to be + + 7 adapted to what the population realistically is + + 8 becoming in New York State. And, so, all of these + + 9 services would be more efficient. + + 10 In order to provide them a more efficient + + 11 service to the -- this population, it would be + + 12 better to do, you know, to consider it, this + + 13 proposal. + + 14 The other thing is, that, all of these -- all + + 15 of these programs represent economic stability for + + 16 seniors. People who are living on fixed incomes + + 17 need these supports in order to stay in the + + 18 community. + + 19 We have -- through our study, we have found + + 20 that, on the average, in New York State, it takes + + 21 about $26,000 for a healthy senior to live in the + + 22 community, independently. You know, that's without + + 23 any type of home care or home attendants or + + 24 long-term care in the community. + + 25 We also know that only a quarter of the + + + + + + + + 20 + 1 seniors live socially on social security, which is + + 2 about, you know, $14,000, this year. + + 3 So, in order to bridge that gap, we need + + 4 these services in order to keep seniors in the + + 5 community, which are becoming the largest part of + + 6 the population. + + 7 MS. MILLER: Nancy Miller, from VISIONS. + + 8 I wanted to add to what Maria was saying, but + + 9 from a slightly different perspective. + + 10 People think of NYSOFA as an umbrella, but I + + 11 think of it as an upside-down funnel. + + 12 NYSOFA has a brand. It is well known. It is + + 13 where people focus when they have a question, of all + + 14 economic backgrounds. + + 15 And, although NYSOFA has been a strong voice + + 16 for people of low income and limited income, what + + 17 they have been able to do is, mobilize people of all + + 18 economic backgrounds to build the supports that + + 19 older people need. + + 20 They may be well-off today, but 30 years from + + 21 now, when they outlive their savings, they may not + + 22 be so well-off. + + 23 So, think of NYSOFA as the funnel. + + 24 It's like a laser. Everybody knows where + + 25 that point is. + + + + + + + + 21 + 1 That entry point is NYSOFA. + + 2 And, then, the funnel opens to all of the + + 3 other services and supports that exist, both, in the + + 4 Aging Network, but also in the other agencies around + + 5 New York State. + + 6 The role that NYSOFA has played, as an + + 7 advocate, to amplify the voice of older people, is, + + 8 actually, to work collaboratively with other state + + 9 agencies. + + 10 So, for example: The New York State + + 11 Commission for the Blind and Visually Handicapped, + + 12 which is housed in the Office of Children and + + 13 Family Services, for the 200,000 older severely + + 14 visually impaired and blind people, CBVH, the + + 15 commission, works collaboratively with NYSOFA so + + 16 that they can get the information out to the + + 17 population that is most in need. + + 18 I understand the purposes behind SAGE. And + + 19 there are some state agencies that, if they were to + + 20 disappear, nobody would notice. + + 21 NYSOFA is not one of them. + + 22 NYSOFA is so well known, and is such a focal + + 23 point, that its loss would be like creating a black + + 24 hole. + + 25 I think it also represents the extreme + + + + + + + + 22 + 1 diversity of New York State. And some of the recent + + 2 census analysis, is that, as New York State ages, + + 3 it's not just the 60-and-over population, but we + + 4 have fewer younger people. And the average age + + 5 upstate now, has gone up several years; which sounds + + 6 like not much, going from, maybe, 30 to 33, average. + + 7 But, when you look at what makes that happen, it's + + 8 that dramatic increase of the 85-plus population. + + 9 It's people who are the most frail, the most + + 10 vulnerable, that's where you find most severe vision + + 11 loss and blindness. You're talking about other + + 12 disabilities that people have to manage with. + + 13 But, if you talk to older blind people, and + + 14 if you ask them, "Are you sick?" they say: + + 15 Absolutely not. With rehabilitation, with support + + 16 services, with transportation, with respite, I'm not + + 17 sick. I just need access. And with that access, I + + 18 can be as independent as any person, with or without + + 19 sight. + + 20 That's what NYSOFA is able to do; is to take + + 21 that resource, that is the strength of New York, and + + 22 provide it to individuals, so that, not only can + + 23 they age in place, but they can age in place as + + 24 healthy as possible, which is less stress on all of + + 25 the other resources of New York State. + + + + + + + + 23 + 1 So, it's nursing-home diversion, it's + + 2 Medicaid delay. + + 3 We're not going to say that our services + + 4 prevent people from needing long-term care. But, if + + 5 we can delay it six months, eight months, + + 6 nine months, that's billions of dollars of savings + + 7 for New York State. + + 8 So, why fix something that isn't broken? + + 9 NYSOFA works. + + 10 And it works as a collaborator, and it works + + 11 as a focal point. Everybody knows it's there, uses + + 12 it. And, NYSOFA has been agile enough to really + + 13 meet the needs of seniors who ask for it, and need + + 14 very different things. + + 15 SENATOR VALESKY: Uh-huh. + + 16 I want to follow up on one of the points that + + 17 you made. But, before I do that, I want to + + 18 introduce Marty Golden, who, as all of you know, is + + 19 former chair of the Aging Committee, and a + + 20 tremendous advocate for seniors around the state. + + 21 We just began about, 10, 15 minutes ago. + + 22 SENATOR GOLDEN: Thank you, Senator. + + 23 SENATOR VALESKY: And the participants here + + 24 have, actually, done a lot of homework already, + + 25 before this meeting. + + + + + + + + 24 + 1 And, Laura began -- actually, Mike began. + + 2 They have actually put a proposal together, + + 3 that would actually strengthen the role of NYSOFA, + + 4 as opposed to potential of diluting that role. + + 5 SENATOR GOLDEN: I happen to agree with them. + + 6 SENATOR VALESKY: I do too. + + 7 [Laughter.] + + 8 SENATOR VALESKY: Very wise. + + 9 Let me, just, to your point, you had said, + + 10 many seniors, who view NYSOFA, and use NYSOFA, and + + 11 your point about them not being sick. + + 12 You know, we have all heard the potential of + + 13 NYSOFA being, in some way, shape, or form, folded + + 14 into, or swallowed up, by one of the largest + + 15 agencies in the state. + + 16 When you think of state bureaucracy, you + + 17 know, you think of the department of health. + + 18 Can -- I would just like, maybe, some folks + + 19 to speak to that particular issue, and what that -- + + 20 the message that that could send to seniors in the + + 21 state, if, in fact, that's what -- + + 22 MR. JELLINEK: Igal Jellinek, Council of + + 23 Senior Centers and Services. + + 24 I think we're at a critical time in services; + + 25 in aging and health services. + + + + + + + + 25 + 1 And I think we -- there's an opportunity here + + 2 to really do some culture change. + + 3 With the longevity revolution, people are + + 4 living longer. The issues out that there are no + + 5 longer acute care. + + 6 The system has to shift into chronic-based + + 7 care. And that's the home and community-based + + 8 services that folks around this room provide. + + 9 And that is also, less expensive. It makes + + 10 sense. It gives us social supports in the + + 11 community. + + 12 One of the other points that we thought of, + + 13 we came to the conclusion, is that, the state Office + + 14 for the Aging needs, as representing all the seniors + + 15 in New York State, not just the people who are on + + 16 Medicaid, is, needs to have an identity of its own, + + 17 and needs to report directly to the Governor, not to + + 18 the health commissioner. + + 19 I think it medicalizes the process. + + 20 And I think, if we're going, and with the + + 21 baby-boomers coming, I think it's critical that the + + 22 state Office for the Aging be enhanced with the + + 23 social supports. + + 24 So, instead of things going over to health, + + 25 is that, health -- some of the health programs that + + + + + + + + 26 + 1 have been stated here, and I'm sure there's many + + 2 others, be brought over to SOFA, and really make it + + 3 a robust state Office for the Aging, because that's + + 4 where the needs are going to be, and that's where + + 5 the services, that are in the community. + + 6 Because, if you want to age in place today, + + 7 you don't need high-tech supports. You need your + + 8 Meals-on-Wheels. You need to have a case worker. + + 9 You need to have -- when you're discharged from the + + 10 hospital, you need to have a set of services that + + 11 don't bring you back into the hospitals, which the + + 12 hospitals are no longer going to get paid for, + + 13 including falls prevention. + + 14 So, all I'm saying is, there's an opportunity + + 15 to really shift this whole system, and rebalance it. + + 16 And I don't think this comes along a lot. + + 17 It's really a critical time for us, right + + 18 now. And I think we all feel very strongly about + + 19 this; that there needs to be, instead of the state + + 20 Office for the Aging going over to health and + + 21 becoming medicalized, it's sort of the reverse, and + + 22 having an identity, and making it much more robust, + + 23 and representative of the aging population in + + 24 New York State. + + 25 MS. DISARRO: Ann DiSarro, from NYSCA. + + + + + + + + 27 + 1 I think there's also a culture difference -- + + 2 MS. MESICK: Absolutely. + + 3 MS. DISARRO: -- between the community-based + + 4 service providers and the health system. + + 5 I sit on the board of Northeast Health, which + + 6 is a local large health-provider organization. And + + 7 having worked in the aging field, I can now see both + + 8 sides of that street. + + 9 The health system is very regulated, and + + 10 everything is very prescribed. And there is no -- + + 11 or, little opportunity for responding to, kind of + + 12 on-the-ground operational efficiencies and needs of + + 13 the consumer. + + 14 The aging system has been developed across + + 15 the country, in a very different mode; looking to + + 16 having local development of service provisions. + + 17 One area agency is one area agency, across + + 18 the state. They are: Take their money, and respond + + 19 to the needs that are in the community, according to + + 20 what other community supports are there, trying to + + 21 maximize the resources among the community. + + 22 It's a very cost-effective way of organizing + + 23 service-delivery system. + + 24 And that's why we think, if we look at + + 25 companion services in the department of health, and + + + + + + + + 28 + 1 bring them to Aging, we actually think we can help + + 2 save money. + + 3 This is an opportunity to kind of take the + + 4 Governor's challenge to all of us, to look at how we + + 5 can be more efficient and effective, and kind of + + 6 turn the idea on its head, and say: We agree. We + + 7 think there's an opportunity to save money here. + + 8 But, it's to look at this more, + + 9 community-based. Use the power and strength of the + + 10 community. And, also, of older people. + + 11 We're very committed to using the + + 12 independence and power and strength of older people, + + 13 to advocate for themselves. But, also, they can be + + 14 a force of doing good things in the community, not + + 15 just receiving services. + + 16 Aging is not a health issue. It's actually a + + 17 resource that we can look at in the community. + + 18 So, there's a very acute culture difference + + 19 between the two organizations. And we think that + + 20 there is a danger that the strength and the + + 21 cost-effectiveness that the Aging Network now + + 22 provides, will get lost, if we made this move into + + 23 health. + + 24 SENATOR VALESKY: Let me just come back to + + 25 you. I know that David wanted to jump in, and then + + + + + + + + 29 + 1 we'll come back with you. + + 2 MR. McNALLY: Well, thank you, + + 3 Senator Golden, and Valesky, for this opportunity. + + 4 We very much appreciate it. + + 5 Well, this is upside-down conversation for + + 6 sure. + + 7 I think she referred to it as "a funnel," and + + 8 said to think of everything in reverse. + + 9 I mean, to take a well-run, fairly small + + 10 agency, that has a very targeted purpose, and has a + + 11 very clear mission, and to dump it into the abyss of + + 12 what is now called "department of health," makes no + + 13 sense, politically, economically, and even from a + + 14 public-policy perspective. + + 15 And I don't want to repeat all of the words + + 16 that have been said here, but, you know, I'm -- + + 17 I am David McNally, with AARP, by the way. + + 18 Hello. + + 19 [Laughter.] + + 20 MR. McNALLY: You know, we have 2.4 million + + 21 members. + + 22 50-plus, and aging, is not about a disease. + + 23 Aging is not an illness. Aging is not a + + 24 disease. + + 25 And when we put it in the department of + + + + + + + + 30 + 1 health, that's immediately what it becomes, in + + 2 everybody's mind. Okay? + + 3 So, rather than saying, maybe we've -- maybe + + 4 the staffs for Aging should, somehow, be put into + + 5 that abyss of DOH, DOH ought to be taking some + + 6 lessons from what SOFA does. + + 7 And I think the whole idea that this is just + + 8 about the medical model is a terrible way for this + + 9 state to go, again, both from a public-policy + + 10 perspective and from cost perspective, as Ann and + + 11 Michael and Igal have suggested. + + 12 The -- this is an opportunity, for sure, + + 13 though. + + 14 Aging is not just health. It's + + 15 transportation and housing, and older workers, and + + 16 discrimination. And those issues are -- do not + + 17 belong anywhere in a place called "the department of + + 18 health." + + 19 But, I do -- as Ann said, this as challenge + + 20 by the Governor. We should take that up and run + + 21 with it. That this is the time to create a robust, + + 22 fully integrated opportunity for us to -- for all + + 23 aging in New York. + + 24 And aging is not just 65-plus. We're all + + 25 aging. And it's an office that is called upon + + + + + + + + 31 + 1 frequently by those who are not, what we consider, + + 2 older, but those who are grappling with trying to + + 3 provide those services to the aging, of caregivers, + + 4 of family members. + + 5 So, it is an office critical to people of all + + 6 ages. + + 7 And I might say that, I think we learned, I + + 8 think, a terrible snapshot of what the future might + + 9 hold. We kind of saw this most recent budget with + + 10 New York Connects. + + 11 We almost lost New York Connects because, the + + 12 health department, or DOB, or somebody, said, + + 13 "Oh, here, we need this one," without thinking about + + 14 the long-term consequences as it relates to state + + 15 and federal health-care reform. + + 16 And we needed an ABRC. And, here, we + + 17 almost -- we've got one on the works here, and it + + 18 was almost thrown out because we needed to save + + 19 money. + + 20 Again, I think that we're going to see more + + 21 and more and more of that, if SOFA does not remain, + + 22 and become even more, a powerful voice for the + + 23 senior. + + 24 And the last piece I want to add is what Ann + + 25 alluded to about, about, this isn't just about -- + + + + + + + + 32 + 1 this is a two-way street. + + 2 This is: How do we provide the supports, and + + 3 services, so people can remain in their homes and + + 4 their communities, which we know costs less money, + + 5 which we know have better outcomes? + + 6 But, it's also the other way around; how can + + 7 all of us create opportunities to help to provide + + 8 those services? + + 9 We have a vast array of volunteer + + 10 opportunities before us, if we could seize just a + + 11 moment to create the communities, or "villages," as + + 12 some are calling them. + + 13 A volunteer; you know, we see what high-cap + + 14 counselors can do. But we also know, there's + + 15 bill-paying programs, and delivering meals, and + + 16 escorts to medical appointments, in the community. + + 17 These are all, yes, sometimes they need to be paid + + 18 for; but, many, many times, they can be developed + + 19 into volunteer opportunities. + + 20 Again, we're saving money. We're providing a + + 21 method to deliver them in the community, and the + + 22 locality. + + 23 And, even pet walking, frankly. We all know + + 24 the importance of pets in some of the older people's + + 25 lives; in particular, when it comes to isolation. + + + + + + + + 33 + 1 That's another type of service. + + 2 But, we should be seizing this moment, for + + 3 sure. And, not running from it, but running to the + + 4 SAGE Commission and saying: This is our moment. We + + 5 need to plan for the future, and for the burgeoning + + 6 elderly population in the state. + + 7 SENATOR VALESKY: I couldn't agree more. + + 8 That's a great point. + + 9 Michael, before you do it, I just wanted to + + 10 ask a question, and correct me if I'm wrong. + + 11 But, this notion of an aging -- an + + 12 "Office for the Aging" director, reporting to + + 13 someone other than the governor, and, therefore, not + + 14 a cabinet-level position; does not the federal + + 15 Older Americans Act require, for receipt of certain + + 16 federal dollars, require cabinet-level position + + 17 for -- and I'm looking to you, Michael? + + 18 No? + + 19 MR. BURGESS: There has to be a director. + + 20 But, you know, a lot of these states have a + + 21 health -- have, like in Washington, health and human + + 22 services, and then there's an Aging division within + + 23 that. + + 24 But, a lot of them are independent. + + 25 In our elder law, which was enacted, it says + + + + + + + + 34 + 1 that there has to be a director, I believe, + + 2 reporting to the governor. + + 3 And I think the point here is, that, if there + + 4 were a proposal to change that, you would be + + 5 changing the elder law. And we'd like to know if + + 6 that was the intent, if that's what the + + 7 SAGE Commission comes up with. + + 8 But I think that's a good point, because the + + 9 director should have the governor's ear, and should + + 10 be confirmed by the senate. Otherwise, it's just a + + 11 division within another department. + + 12 SENATOR VALESKY: Mike? + + 13 MR. ROMANO: Mike Romano, from Oneida County. + + 14 And I just thought it was important to speak + + 15 to the fact that there are a number of counties in + + 16 New York State who have effectively blended + + 17 Older American Act programs with Medicaid long-term + + 18 care community-based programs. And it has proven to + + 19 be cost-effective and consumer-oriented. + + 20 For example: Those counties, at the + + 21 programs, can, you know, seamlessly transition + + 22 consumers from non-medical programs into more + + 23 skilled-type community-based services, fairly + + 24 seamlessly. And it has proven to be a good model of + + 25 Aging services. + + + + + + + + 35 + 1 The key to that, though, is that, the + + 2 Aging Network really needs to be the focal point for + + 3 those types of programs and services. + + 4 The Aging Network, especially in the last few + + 5 years, has really taken some federal dollars and + + 6 federal programs, and created consumer-directed care + + 7 under the Older Americans Act. + + 8 So, it's proven to be able to administer, you + + 9 know, flexible consumer-directed services. + + 10 I feel that any model that the Aging Network + + 11 is not the lead of, you know, would be a detriment + + 12 to the aging population, and their consumers. + + 13 And just one final point, is that, just + + 14 within the last two years, other networks, such as + + 15 the Veterans Administration, has looked upon the + + 16 Aging Network as a leader for community-based care, + + 17 and has established partnerships with several + + 18 counties here in New York State, to provide services + + 19 for veterans; long-term care services for veterans. + + 20 SENATOR VALESKY: Richard? + + 21 MR. HALDEMAN: Yes, Senator, thank you for + + 22 inviting us here today. + + 23 My name is Richard Haldeman. I represent the + + 24 Ombudsman Alliance. + + 25 By the way, the "Haldeman" name has no + + + + + + + + 36 + 1 reference to any past political parties down in + + 2 Washington. + + 3 [Laughter.] + + 4 MR. HALDEMAN: As the ombudsman program is + + 5 listed, we came here to Albany, February 8th, to + + 6 make our legislators aware of the ombudsman program. + + 7 Many people in this state, when you mention + + 8 "ombudsman," they look at you, like, funny, "What is + + 9 that?" + + 10 We're the largest volunteer organization in + + 11 the state of New York. + + 12 We are under the Department of Aging. We're + + 13 funded by Title 7, the Older Americans Act. So, we + + 14 have the money from the money stream that does come + + 15 from Washington, and it's disseminated through the + + 16 Office of Aging. + + 17 Last year, our volunteers provided over + + 18 206,000 volunteer hours, to nursing homes, assisted + + 19 livings, family-type homes, adult homes. + + 20 If the department of health had to take over + + 21 that, that equates to about $5.8 million that the + + 22 state would have had to spend to send people from + + 23 DOH out, to do the same job that our volunteers do. + + 24 So, we're probably one of the few programs + + 25 that don't normally come in and ask for money. Our + + + + + + + + 37 + 1 funding does come from Washington. + + 2 And some of our concerns would be: + + 3 If we were to go under a department of + + 4 health-type environment, how would that money stream + + 5 be disseminated? + + 6 Would it be, continually with, the Aging, you + + 7 know, budgeting program? Where would it go? + + 8 Also, another concern that we have is, our + + 9 program is very unique, in that, as we certify our + + 10 volunteers -- which we are doing around the state + + 11 now, again -- we are exempt from all HIPAA laws when + + 12 we advocate for residents in nursing homes, assisted + + 13 living. + + 14 Our advocacy is that of, you know, a + + 15 doctor-patient, lawyer-client. + + 16 We also spend a lot of time in these + + 17 facilities that, normally, a person in a working + + 18 environment would not be able to do. Our volunteers + + 19 are in nursing homes, two, three, four hours a week, + + 20 trying to maintain, and indicate, that the care is + + 21 being provided, that's supposed to be provided, in + + 22 those facilities. + + 23 So, we advocate for that. + + 24 Protections under the Older Americans Act, + + 25 are unique to us alone. And our concern, that if + + + + + + + + 38 + 1 Aging did move into another entity of DOH, DOH is + + 2 part of what we work with. But, also, when we find + + 3 problems in facilities, we have to move that, + + 4 sometimes, up the ladder, to DOH, and -- to look + + 5 into it. + + 6 And if they decide that it's not something + + 7 that they feel is a complaint in a nursing home, + + 8 they could kind of overrule, or cause a conflict, if + + 9 you will. + + 10 So, conflict is another issue that, you know, + + 11 we really can't be a part of, being the unique + + 12 program that we are. + + 13 In our autonomy, is what I was talking + + 14 before, DOH being a licensing agency, also, could + + 15 overrule some of the concerns that we would have in + + 16 our advocacy. It would put us in a very difficult + + 17 position. + + 18 And, perception, more than anything else, if + + 19 we were to tie into an entity, such as DOH, the + + 20 people that we advocate for, and the staff and + + 21 people that we become involved with in facilities, + + 22 know that DOH comes in and does surveys every 12 to + + 23 15 months. + + 24 They come in, looking for critical issues + + 25 that, you know, they want to cite you for one thing + + + + + + + + 39 + 1 or another. + + 2 By being autonomous, we, many times, hear + + 3 from staff, administrators, about issues that are + + 4 very high concern, through the department of health. + + 5 So, when you get down to where the rubber + + 6 meets the road, our autonomy, and + + 7 conflict-of-interest issue, is extremely important, + + 8 to ensure that this program is viable, and + + 9 continues. + + 10 And I heard around the table, saying, that + + 11 the population is aging. + + 12 Well, it is. We're all aging. And I'm doing + + 13 it pretty fast myself. + + 14 [Laughter.] + + 15 But, when you look down the road, with the + + 16 baby-boomers coming through the doors, nursing homes + + 17 are going to be expanding. I hate to say that, but + + 18 it's going to happen, even though you're going to do + + 19 a lot of care in home, you know, in-home care. + + 20 So, you know, the ombudsman program is very + + 21 vital. They're probably the most vulnerable part of + + 22 our society. + + 23 We don't want to see that interrupted, and be + + 24 in conflict with, or have another interest, that, + + 25 you know, may be detrimental to what we do. + + + + + + + + 40 + 1 And I got to tell you: We're teaching an + + 2 ombudsman certification course right now, up in + + 3 Saratoga. We have 24 people. + + 4 People come from all walks of life to become + + 5 volunteers in this program. And they are the + + 6 most -- I just can't -- their advocacy is just + + 7 something that they bring to the table, they bring + + 8 it to these people, in nursing homes, and assisted + + 9 livings. You know, it's almost like it's their + + 10 family. People create that relationship. + + 11 We would not want to lose that; that ability + + 12 to do that. + + 13 And we, also, and I think, the gal that + + 14 mentioned before, I think that the SOFA, director, + + 15 or whatever may be the outcome of this, should have + + 16 the ear of the governor's office. + + 17 And I don't really have anything else. + + 18 MS. MESICK: Senator, one other comment from + + 19 one of the other meetings that we had: We heard + + 20 from a North Country local Office for the Aging, + + 21 that talked about their blend, also, of the medical, + + 22 with social supports, again, directed by the local + + 23 Office for the Aging. + + 24 And they talked about difficulty in getting + + 25 answers and direction from the health side for some + + + + + + + + 41 + 1 of the work, from the state Department of Health. + + 2 And only recognizing, kind of the size of the + + 3 bureaucracy, and the way in which questions, by + + 4 culture, need to be answered; as opposed to the way + + 5 that the Office for the Aging -- the state Office + + 6 for Aging is able to provide support. + + 7 So, again, it's an example to your question + + 8 of: What are some of the issues we see, if we were + + 9 to make the move in that direction, as opposed to + + 10 really strengthening the state Office for the Aging. + + 11 MS. AULDRIDGE: If I could build off the + + 12 point you're saying, I also think -- + + 13 My name is Allison Auldridge, from + + 14 Services and Advocacy for GLBT Elders. + + 15 -- and I wanted to raise the issue of the + + 16 population of older adults who are in the greatest + + 17 social and economic need, which is something the OAA + + 18 spells out as a mandate for state units on aging, + + 19 which is -- which are groups that, I think, that + + 20 most of us are concerned about, kind of losing a + + 21 focus if NYSOFA were to be absorbed into DOH. + + 22 These include: People of color. GLBT + + 23 elders. Older adults with HIV, disabilities. + + 24 A number of these groups experience extreme + + 25 health disparities. They have lower rates of + + + + + + + + 42 + 1 health-insurance coverage, higher rates of diseases. + + 2 They experience more stress than other counterparts + + 3 of their age. + + 4 So, I think that that's another concern that + + 5 we also outline in our principles, as a concern for + + 6 what happens when the DOH is overseeing all of + + 7 these. + + 8 MS. MILLER: I think, also -- + + 9 Nancy Miller, from VISIONS. + + 10 -- that my experience with the department of + + 11 health -- and we are not funded in any way by DOH, + + 12 so I don't speak as a contractor -- it's a very + + 13 rigid system by -- by its funding streams. + + 14 NYSOFA is, soft, and cushy, and it molds to + + 15 the need. + + 16 And I think that -- + + 17 SENATOR VALESKY: Is that your legacy, Mike? + + 18 MR. BURGESS: I hope not. + + 19 [Laughter.] + + 20 MS. MILLER: But the point is, that -- and I + + 21 think it's been mentioned by most of the speakers, + + 22 that there's a creativity and flexibility that is + + 23 allowed by an agency that has to look at, new ways, + + 24 pilots, demonstrations, new ways of communicating. + + 25 I think the strength of NYSOFA is actually in + + + + + + + + 43 + 1 this room, if you see the diversity of the various + + 2 Aging cohorts that are being represented, that's + + 3 natural for NYSOFA. We all sit together, and figure + + 4 out how to blend what is there. + + 5 That's not the culture of DOH. And it's much + + 6 harder to get to the level of a decision-maker; + + 7 whereas, the director of the state Office for the + + 8 Aging has always been extremely accessible. You + + 9 pick up a phone, and you can actually get to that + + 10 person. + + 11 And that's what state government should be. + + 12 Why is state government in business? It's + + 13 for the people. + + 14 NYSOFA is state government, that is + + 15 accessible to the people; the little people, as well + + 16 as the advocates. + + 17 And that's something you would lose through + + 18 the culture of DOH. + + 19 SENATOR VALESKY: Well said. + + 20 MR. BURGESS: I just want to make one point + + 21 about what Rich said, because I want to illustrate, + + 22 there was a real conflict of interest that we faced + + 23 last year. + + 24 The ombuds program was appointed by the + + 25 bankruptcy court to represent the patients -- or, + + + + + + + + 44 + 1 the residents of four nursing homes, Northwoods, + + 2 that was go through a bankruptcy proceeding. And + + 3 that court put the ombuds program -- the judge said, + + 4 "I will decide this case on the best interests of + + 5 the patient" -- "of the residents." + + 6 And what the ombuds director says -- and the + + 7 local ombuds' director, not the state office -- + + 8 "Will be what I determine." + + 9 That situation was not exactly -- I mean, the + + 10 health department did go in and inspect these + + 11 facilities, and try to make sure that they were not + + 12 short-staffing during this bankruptcy proceeding. + + 13 But if the ombuds program had said, they + + 14 are being mistreated, and whatever, and those + + 15 facilities had closed, that wasn't necessarily a + + 16 gift to the health department, because they would + + 17 have had to look at finding a place for all of them. + + 18 So, that's an example of where there was a + + 19 direct difference between what the ombuds program + + 20 would be doing, and I'm not saying the + + 21 health department didn't care, but it would have had + + 22 a different role, and a different purpose. + + 23 And I wanted you to be aware of that because + + 24 there are other responsibilities that the ombuds + + 25 program has. And recognizing -- the bankruptcy + + + + + + + + 45 + 1 court recognizing them is something totally outside + + 2 of state government. + + 3 SENATOR VALESKY: Deb? + + 4 MS. RITANO: Hi. Deb Ritano, from + + 5 Jewish Family Services, the neighborhood NORC + + 6 program; and, so, I have the opportunity to really + + 7 give direct service. + + 8 And what I wanted to say, something that + + 9 Nancy had said, when she was describing the + + 10 "cushy" NYSOFA, was, for us to be able to pick up + + 11 that phone and speak to them directly is very + + 12 important, because they are the agency that combines + + 13 their head and their heart when they're dealing with + + 14 an individual. And you can call, with an + + 15 individual, through the NORC program. + + 16 And for us it would be disastrous, if, in + + 17 fact, as you said, we were swallowed up by the + + 18 department of health. + + 19 Our seniors are -- we do not have a medical + + 20 model. We have social model. + + 21 We do, now, have a part-time nurse who can + + 22 follow up, so that we can prevent re-admission to + + 23 the hospital. + + 24 We have a handy man that the seniors can + + 25 call, to come and change that light bulb, or to be + + + + + + + + 46 + 1 able to clean out those gutters so that they won't + + 2 fall. + + 3 We're able to do those kinds of things. + + 4 But, the bigger part of it is, is that, on + + 5 that grant, we're a bigger -- has -- is a bigger + + 6 partnership, with all of these senior services and + + 7 Catholic clarities, and all of the other people + + 8 around the community, who are able to support that + + 9 NORC initiative, to be able to respond to the needs + + 10 of the seniors in the community. + + 11 MS. KRAKOWSKI: Molly Krakowski at JSA. + + 12 Actually, I wanted to go back to what Nancy + + 13 said, and what we talked about, in terms of that + + 14 point of entry, because I think that that speaks a + + 15 lot to the difference between the two directions + + 16 that are sort of in front of us. + + 17 A lot of energy went into bringing us all to + + 18 the table, creating these points of entry, looking + + 19 at how we can all work together, being community + + 20 partners with the state Office for the Aging. + + 21 We were reached out to when it came do the + + 22 Medicare Part D enrollment, education. A lot of us + + 23 had been involved with the advocacy when it came to + + 24 you know, Part D, for example. But, then, we were + + 25 the same go-to people when it came to trying to + + + + + + + + 47 + 1 educate people, and help them to figure out how to + + 2 navigate the system. + + 3 I think that it's, both, tangible, and it's + + 4 also the perception of, if you fold in aging into + + 5 this vast DOH, or other, you know, medical model, it + + 6 loses that exact piece and component which all of + + 7 us, I think, are connected to, which is, that we are + + 8 a partner with this state. + + 9 We are very effective at getting out the + + 10 information, communicating with people, and saving a + + 11 lot of money in the process by funneling people to + + 12 the right direction -- you know, getting people to + + 13 the right service, in the right way, and so that + + 14 they're not inundating a health-care model or a + + 15 health system. + + 16 And I think, also -- and I'm not a dollars' + + 17 person -- but, I think it's clear, that once an + + 18 Aging department would be folded into a health + + 19 model, we're no longer really on the -- we're no + + 20 longer a point of interest in terms of looking at a + + 21 budget. + + 22 I think it then becomes this enormous budget + + 23 that we always look at. And, how can we cut the + + 24 budget? And, how can we slice-and-dice these huge + + 25 departments? + + + + + + + + 48 + 1 And, Aging is just inside there, somewhere, + + 2 but it's no longer on the radar as a distinct budget + + 3 cut, or as something very clear, and how it's going + + 4 to affect all of us in the community. + + 5 So, you know, we are a lot of big and small + + 6 agencies out in the community. And we do represent + + 7 very different but conjoined -- you know, conjoining + + 8 organizations. + + 9 And, that network, I think, would be + + 10 completely decimated once folded into a different + + 11 model. + + 12 I think that things we do now with VISIONS, + + 13 for example, or other groups which are now under + + 14 state Office for the Aging, would then fall under + + 15 some other category within health. + + 16 It wouldn't necessarily be that same flow + + 17 that we currently have, and where we are able to + + 18 pull together and say: + + 19 Okay, what do we all need? + + 20 And, how can we word this so that it's + + 21 inclusive? + + 22 Or, that, you know, we're really recognizing + + 23 where the state is coming from, but also not losing + + 24 where we all are. + + 25 And the other thing is, this idea of block + + + + + + + + 49 + 1 grants being the answer, when a lot of us have been + + 2 doing services in the community, and have a lot of + + 3 experience doing really good work in the community. + + 4 And trying to sort of not lose sight of what's a lot + + 5 of work has gone into over the years, in developing + + 6 that other model. + + 7 MS. TINNEY: Carin Tinney from + + 8 United Neighborhood Houses. + + 9 I just wanted to build on a lot of what's + + 10 been said here, and to speak a little bit about what + + 11 happens when you medicalize a service. + + 12 When you think about somebody who has, say, + + 13 dementia, and they go in to a doctor; and, what does + + 14 the doctor do? + + 15 They prescribe, like, an anti-anxiety + + 16 medication and anti-psychotic, Aricept. They treat + + 17 the symptoms. Right? + + 18 The programs offered by SOFA will look at the + + 19 whole person, and look at the whole family and the + + 20 whole support system, and help that person and that + + 21 family get along in society, that's the community, + + 22 which is often the sick part. Right? + + 23 They'll look at, you know, if somebody is in + + 24 the third stage of dementia, or they're speaking in + + 25 tongues, and they don't know, you know, what + + + + + + + + 50 + 1 their -- their spouse doesn't know what they're + + 2 saying anymore, they'll help their spouse, as well + + 3 as the person who has dementia. + + 4 So, SOFA programs often look at the broader + + 5 picture. They're not just looking at blood-pressure + + 6 outcome. They're looking at the whole person, the + + 7 whole community, the whole support network. + + 8 So, that's -- you know, that's one of the + + 9 downsides of a medical model, is that, you're losing + + 10 sight of the whole self -- to which SOFA does, + + 11 amazingly so, with NORC programs, with + + 12 SAD programs (social adult day), with + + 13 case-management programs, caregiver-support + + 14 programs -- you know, in addition to the financial + + 15 cost of medicalizing something, that that is there, + + 16 and that's very much a reality. + + 17 I also just wanted to, you know, point out, + + 18 like, something that I've often thought of, is that, + + 19 we've never really prepared our own communities and + + 20 our neighborhoods to deal with an older-adult + + 21 population. Because of longevity, we are living + + 22 longer. + + 23 So, things like -- you know, and AARP has + + 24 done amazing work on this, and level of communities + + 25 has done amazing work on this -- is that, you're + + + + + + + + 51 + 1 looking at things, that, we don't have the + + 2 transportation systems to support somebody once they + + 3 have not -- where they've stopped driving. Right? + + 4 You're looking -- you can go outside and + + 5 probably find five broken sidewalks, you know. + + 6 You can -- you know, just this -- the pure + + 7 computerization of every service out there, you + + 8 know. And computers are like a third and fourth + + 9 language to a lot of older adults, so it's a + + 10 preventive factor from going out and doing the + + 11 business that they used to do. + + 12 And, you know, and on fixed incomes, and + + 13 somebody mentioned before, you make something like, + + 14 $1,400 a month. + + 15 You know, the cost of a meal; you know, for + + 16 somebody who cannot -- can no longer really pick up + + 17 a pot because it's too heavy, and can't buy a pot + + 18 because they're on a fixed income, you know, that's + + 19 lighter, a cost of a meal is preventive to living + + 20 wholly in the community. + + 21 So, I often, really feel that our communities + + 22 are the ones that are sick. You know, not + + 23 necessarily the people who are receiving services + + 24 from SOFA. + + 25 So, I think that the SOFA services that we + + + + + + + + 52 + 1 have are, often, fill the gaps to make the community + + 2 whole, so that somebody can live very independently, + + 3 and with dignity. + + 4 And that's a huge word: with "dignity." + + 5 The other point that I just wanted to make; + + 6 you know, I've worked in nursing homes that are -- + + 7 you know, have not subscribed to culture change, and + + 8 I've worked in nursing homes that have subscribed to + + 9 culture change. + + 10 And culture change is trying to undo that + + 11 medical model. Right? + + 12 So, you know, an advocacy point for not + + 13 putting SOFA in DOH, is that we don't want to do + + 14 what they've done in the nursing homes; is, go to a + + 15 place where the language is foreign, and, like, + + 16 everything there is medicalized. Right? Because + + 17 they're trying to get out of that now. + + 18 Why would we want to go into that? + + 19 So... + + 20 And the last point is, that, you know, when + + 21 this population was school-aged, they had to expand + + 22 the schools. Right? Had to add more classrooms to + + 23 on to deal with this. + + 24 You know, when they started to get jobs, you + + 25 know, the workforce was increased. You know, and + + + + + + + + 53 + 1 the numbers of houses that were built, and + + 2 apartments that were built, were increased. + + 3 Now this population, that we're facing right + + 4 now, is coming into communities. We need to expand + + 5 the services within the communities, to help to + + 6 support them, to live. + + 7 So, with that... + + 8 MR. McNALLY: I want to build off that a + + 9 little bit. + + 10 We're not ready, and the future is not, as + + 11 far as the size of the aging population that Nancy + + 12 mentioned. + + 13 If you go to Miami-Dade County, or if you + + 14 have ever been to Senior Day at the State Fair, + + 15 that's what our population is going to look like + + 16 very, very soon. And we're not prepared at all. + + 17 And we couldn't -- even if everyone wanted to + + 18 go into a nursing home, and could afford to -- which + + 19 are two ridiculous "ifs" -- we couldn't build + + 20 enough. + + 21 [Laughter.] + + 22 MR. McNALLY: We couldn't build enough. + + 23 So, we really need to be focusing on these + + 24 pieces of the non-medical. And, SOFA is the only + + 25 place to do it. Or, something like SOFA, or a + + + + + + + + 54 + 1 more-empowered SOFA. + + 2 But I don't want to miss the opportunity to + + 3 raise the issue of efficiencies as well. + + 4 And I'm not -- I don't know anything about + + 5 SOFA's internal bureaucracy. Maybe Mike can speak + + 6 to that a little more, or some of the providers that + + 7 deal with them. + + 8 But, if there are, HR issues, web-design + + 9 issues, contracts; all that kind of stuff that goes + + 10 on in a bureaucracy, if that, somehow, can help save + + 11 money, that's great, by doing it with other parts of + + 12 the government. + + 13 But more important, from my -- our + + 14 perspective, is -- + + 15 And I'm David McNally, with AARP. + + 16 -- is: Does that free the agency up even + + 17 more, to actually focus on their mission, and + + 18 delivery of services and programs? + + 19 MR. NADEAU: Christopher Nadeau, with + + 20 NYSADSA. + + 21 I think most big states are moving in the + + 22 direction of -- of, they're realizing that they've + + 23 over-medicalized their long-term-care system. And, + + 24 so, they're sort of moving in the other direction, + + 25 looking how you can blend in services. + + + + + + + + 55 + 1 And I think that's a lot of what we're + + 2 talking about here. + + 3 And the division is, that NYSOFA could be a + + 4 tremendous gateway to coordinated care. And I think + + 5 that's the direction that we need to go. + + 6 And, I think to be on the right side of + + 7 history here, in terms of home and innovative + + 8 services, that's what families want. And we know + + 9 it's cheaper. + + 10 So, I think that there's a tremendous amount + + 11 of synergy here, to move in that direction. I just + + 12 hope that we have the will to do it. + + 13 SENATOR VALESKY: Just one second. + + 14 Yes, Senator Golden? + + 15 SENATOR GOLDEN: I want to jump in. I have a + + 16 load of committee meetings this morning. + + 17 I think it's important. I want to thank + + 18 Senator Valesky, the chairman, for putting this + + 19 together, and bringing all of the advocates together + + 20 in a room, to discuss this issue. + + 21 There are several things, obviously, and it's + + 22 been said: From the design of --, they can absorb + + 23 some of the -- HR, some of the other areas that + + 24 might be duplicated. Maybe they can do it better, + + 25 in health, in certain instances. + + + + + + + + 56 + 1 The problem that I've seen with aging over + + 2 the years, at SOFA, I've seen the, I think, + + 3 neighborhood NORCs? Tremendous. + + 4 Open NORCs? Tremendous. + + 5 Ombudsman; we worked to make sure that we got + + 6 that done. + + 7 Mike, we worked with you as the advocate, and + + 8 as the director. + + 9 And we've always had one problem, and that + + 10 was getting enough money for the director. + + 11 We always seem to be able to fight for, + + 12 keeping senior centers open in New York City, + + 13 getting transportation for Upstate New York, trying + + 14 to get these open NORCs to include areas -- vast + + 15 areas of upstate and downstate. + + 16 And, coming up with, you know, the tens of + + 17 millions of hours that are donated by the + + 18 caregivers, coordinating that, and making sure we're + + 19 keeping our aging population out of nursing homes, + + 20 and out of the assisted-living facilities, and out + + 21 hospitals; as a last resort, after we have exhausted + + 22 all of our volunteers, and all of our volunteers -- + + 23 and we've trained those volunteers, and they can't + + 24 do it anymore, and it's the only step left, is to go + + 25 into the medical -- into an assisted-living + + + + + + + + 57 + 1 facility, which saves this state, and this nation, + + 2 tremendous, tremendous money, with what this SOFA + + 3 has done, in the past, and can do in the future. + + 4 There are two areas that we have to -- I + + 5 think, to be able to expand on, and that's, you + + 6 know, recommendation that you brought here today. + + 7 And, getting our message to the Governor. + + 8 And I applaud the Governor for trying to do + + 9 the work that he's trying to do here in this state. + + 10 I think that, the new Medicaid design, I + + 11 think is going to have some issues. + + 12 God willing, we'll be able to work through + + 13 them over the course of next six or eight months. + + 14 But, I already see a low number of areas, + + 15 where it's already impacted, and they need help. + + 16 And that's before we get to the 2014 + + 17 President Obama's health care for our nation. + + 18 So, when you finish with the state and the + + 19 nation, what is left here for the consumer, the + + 20 client, us? + + 21 What are we to have, as we grow older; what + + 22 are we to expect? + + 23 And we cannot allow our dignity, and we + + 24 cannot allow our quality of life, to be changed to + + 25 such a degree that it would impact on our health, + + + + + + + + 58 + 1 impact on our families, and impact on our + + 2 volunteers. + + 3 So, the second part of this is: How do we + + 4 get our message to the Governor, and have the + + 5 Governor understand the importance of, maintaining a + + 6 SOFA agency, funding more money into SOFA, and + + 7 allowing them to continue to do, and to enhance the + + 8 mission, that they have been, and should be, given? + + 9 I think we all agree here, department of + + 10 health, I love them, but it's a black hole. + + 11 And, the commissioner is going to do a great + + 12 job in trying to coordinate and organize, and do + + 13 what his responsibility will be. But, his + + 14 responsibilities in the department of health are too + + 15 many. + + 16 There are too many tentacles in the + + 17 department of health; and, therefore, only one + + 18 reason to move SOFA into the department of health, + + 19 is for savings, so that means we're covered. Right? + + 20 So, the programs we see here today, that we + + 21 talked about, are going to be cut. So, we can't + + 22 allow some of those programs -- many of those + + 23 programs, we have to enhance those programs, bring + + 24 new programs in, do best practices across this + + 25 nation, and around the world, to bring the best + + + + + + + + 59 + 1 possible outcome to the seniors here in our + + 2 communities. + + 3 So, that's the two, I think, missions that I + + 4 think Senator Valesky, the chairman, is focusing on; + + 5 and that's: The recommendation expansion. And, + + 6 getting our message, and the understanding, that + + 7 this is something that this state must have. + + 8 So, that challenge is to you: + + 9 How do we get this governor, this good + + 10 governor, to understand the necessity of SOFA? + + 11 How does that message get delivered to your + + 12 client base? + + 13 How does that get delivered to our governor? + + 14 And, how do we manage this, and create and + + 15 expand on the recommendations, that the good + + 16 governor would want to put his name to, and + + 17 participate in? + + 18 I think that's the focus. + + 19 And I might have left a couple of things out, + + 20 but that's what -- I have had the privilege of + + 21 working with a good director, and a couple of good + + 22 directors here. And Mike did a good job. + + 23 Thank you. + + 24 MR. McNALLY: Could you tell us a little bit + + 25 about what your perspec- -- what is going to happen? + + + + + + + + 60 + 1 I mean to say, we keep hearing different + + 2 rumors of: + + 3 Whether they've met. + + 4 Or, are they meeting? + + 5 Or, are they going to make this May 30th + + 6 deadline? + + 7 Is it an up-or-down vote this year? + + 8 Do they have several years to do this? + + 9 What is the legislature's -- + + 10 SENATOR VALESKY: We're the same -- + + 11 MR. McNALLY: Okay. + + 12 SENATOR VALESKY: We're in same boat as you; + + 13 we're hearing the rumors that no official + + 14 communication, certainly, with me and/or with you. + + 15 I don't think -- whatever happens, I think + + 16 the process is, will take a much longer period of + + 17 time than they may have originally indicated. I + + 18 don't think we're going to be doing anything, + + 19 legislatively before the end of this legislative + + 20 session, on any this stuff. + + 21 And I think -- I think they're acknowledging + + 22 that now. + + 23 So -- but, you know, I think Senator Golden + + 24 was right. I think the -- one of the great + + 25 challenges that we have -- and I want to commend + + + + + + + + 61 + 1 everyone for being proactive. + + 2 I mean, you know, so often in government, we + + 3 can sit back and wait for something to happen, and + + 4 then we're forced into reactive mode. + + 5 You know, from a budget perspective, I guess + + 6 that's the way the process works. But, we ought to + + 7 see this, not as a threat, but as an opportunity. + + 8 And I don't think we're up against a clock, + + 9 necessarily, that has anything to do with this + + 10 legislative session. + + 11 So, you know, rolling out a communication + + 12 strategy with the commission, and, as Senator Golden + + 13 says, directly with the Governor, is, I think, all + + 14 of our collective challenge. And, I think this is + + 15 the beginning of that process. + + 16 SENATOR GOLDEN: And if I may, I do believe + + 17 that there are people within state government that + + 18 believe this is a done deal. + + 19 So, having said that; so, if people already + + 20 believe it's a done deal, it's just a matter of time + + 21 before the legislation is sent down: Is the + + 22 legislation going to be required, or is it just + + 23 going to be absorbed? + + 24 Because of the way the SOFA is listed in + + 25 the -- on this state, I don't know if they need + + + + + + + + 62 + 1 legislation to move you over. + + 2 MR. BURGESS: This is the way -- + + 3 SENATOR GOLDEN: Pardon me? + + 4 MR. BURGESS: This is the way that it's + + 5 listed, as part of the executive -- + + 6 SENATOR GOLDEN: Correct. + + 7 MR. BURGESS: -- department? + + 8 SENATOR GOLDEN: Correct. + + 9 Do they need legislation to move you over? + + 10 MR. BURGESS: Well, the elder law, as I + + 11 looked at it, I think they'd have to change it. + + 12 You wrote it, actually -- + + 13 SENATOR GOLDEN: I know. + + 14 MR. BURGESS: -- when you were there. Right? + + 15 [Laughter.] + + 16 SENATOR GOLDEN: Which they didn't want to + + 17 take; which, they took three-quarters of it. I wish + + 18 they had taken all of it. But, we did the + + 19 neighborhood NORCs, we did the ombudsman; we did all + + 20 of these programs over the last five years. + + 21 MR. BURGESS: That's right. + + 22 SENATOR GOLDEN: And I'm proud of each and + + 23 every one of them. And, of course, the elder law. + + 24 MR. BURGESS: Well, I think they would have + + 25 to say that, you know, there's no longer a separate + + + + + + + + 63 + 1 agency, and no longer a director. + + 2 That language had to be stricken from the + + 3 elder law, and repealed. + + 4 And -- so, I don't know that they could do it + + 5 without you, the legislature. + + 6 SENATOR GOLDEN: Okay. So, then, there would + + 7 be -- I think we would have to do a little bit of + + 8 homework on that. And then that would be, we would + + 9 increase, now, the goal of, not just talking with + + 10 our governor, but also talking with the legislative + + 11 bodies here, on the assembly and the senate side, to + + 12 see the importance of delivering for the clients and + + 13 base across the state of New York. + + 14 And I think the legislature would understand + + 15 that, and I think the Governor would understand, + + 16 when, in numbers, and, in fact, what the savings can + + 17 be, and how we can expand on those savings for the + + 18 state of New York. + + 19 I think that message has to be delivered. + + 20 And I think the message has to go out, this can't be + + 21 a done deal. This has to be -- there are, all of + + 22 the advocates -- and this is, again, just a small + + 23 portion of the advocates across this great state -- + + 24 they have to be heard. And we cannot allow it to go + + 25 forward. + + + + + + + + 64 + 1 So, I agree with my good colleague, + + 2 Senator Valesky, here. I don't see it as one of the + + 3 pressing issues. There are a number of pressing + + 4 issues coming down in the next four weeks. But, I'm + + 5 not so sure this can't just be absorbed. So -- and + + 6 come in for the legislative fixes in January or + + 7 February. + + 8 So, I think it's important, and imperative, + + 9 and that we get our message out as soon as possible. + + 10 MR. BURGESS: Could I just add one thing; + + 11 that, regardless of whether there is legislation, + + 12 having been in that job over there, I think it would + + 13 be unfair to the agency if the Governor did not -- + + 14 if there's no legislation this year, did not give + + 15 clarity, and put a director in the agency, whether + + 16 it's the acting director and make him official; but + + 17 to keep the agency going as -- in limbo for another + + 18 year, I think would be unfair to all of us, as well + + 19 as to the staff there. + + 20 So, I would urge you, that, if nothing else, + + 21 that you urge the Governor to make a decision on the + + 22 status of the director of that agency, so that we + + 23 have something for the next year. + + 24 SENATOR GOLDEN: I think it's important that + + 25 he makes an educated decision. And that educated + + + + + + + + 65 + 1 decision is what you're putting together here today. + + 2 MS. DISARRO: To the pressure points that we + + 3 have to make, would you tell us, I mean, is it, to + + 4 the Governor? is it to the SAGE Commission? + + 5 Where is -- + + 6 SENATOR GOLDEN: Both. + + 7 MS. CERENOS: Okay. We need to make sure + + 8 that we take your charge, and do it to the right + + 9 places here. + + 10 SENATOR GOLDEN: You have to show what some + + 11 best practices around the nation. + + 12 You pointed out Dade County. + + 13 I was down in Hollandale and -- what's the + + 14 county next-- Fort Lauderdale. And I couldn't + + 15 believe how that had changed. I was there 40 years + + 16 ago, with kids. + + 17 I went down there, recently. It was nothing + + 18 but people, like me. + + 19 [Laughter.] + + 20 SENATOR GOLDEN: The transformation, it was + + 21 amazing. + + 22 Then you went to Miami, and this whole part + + 23 of Miami, there was thousands and thousand of young + + 24 people. + + 25 But, the majority of that part of Florida is + + + + + + + + 66 + 1 seniors. + + 2 I think you have to expand on the + + 3 recommendations. You have to show savings. And, + + 4 you have to show what your mission -- what the + + 5 mission of SOFA, can, and will be able to do, going + + 6 forward. + + 7 MS. DISARRO: I understand. + + 8 SENATOR GOLDEN: And it comes down to the + + 9 tens of millions of volunteers' hours that are going + + 10 out there: the transportation, the Meals-on-Wheels. + + 11 I dare to say, you know, I would ask the + + 12 SAGE. + + 13 And maybe I'm pushing this just a little bit + + 14 too far, this envelope, but I don't want to do that, + + 15 and I don't want to be dramatic on it, but, if any + + 16 of the individuals that sit, and think that this is + + 17 something that we should move, brought into health, + + 18 they should go on -- in a van and deliver a meal + + 19 at -- to a family, or to a woman, or a senior. Go + + 20 inside to some of the homes of our richest + + 21 communities, our best communities, and you will see, + + 22 and walk away from that in tears. + + 23 So, there are people living substandard, our + + 24 seniors. And if it wasn't for the refrigerators, + + 25 you would cry again. + + + + + + + + 67 + 1 So, I'm not trying to be dramatic here. I'm + + 2 trying to get a point across: there is a need. + + 3 And I believe we cannot allow that need to be + + 4 absorbed into what can be a bureaucracy that will + + 5 slow down the process, and put more people into + + 6 nursing homes, assisted-living facilities, and deny + + 7 a quality of life for many of our seniors here in + + 8 this great state. + + 9 So, I think that has to be, without being + + 10 with the drama, but I think that it has to be that + + 11 dramatic; that they have to understand the + + 12 necessities: + + 13 The number of meals. + + 14 The transportation Upstate New York, and how + + 15 you have to be able to get around. + + 16 The hospitals; how you keep people out of the + + 17 hospitals, and out of nursing homes. + + 18 And, real quickly, I have to close: In + + 19 Brooklyn, New York, where I represent, you actually + + 20 wait 40 to 50 hours in the emergency room to get a + + 21 bed. + + 22 That's today. That was before any of this + + 23 went into effect. + + 24 There are some points of the year, that you + + 25 can wait over 100 hours for a bed in the hospitals + + + + + + + + 68 + 1 in my community. + + 2 Now, if you know somebody, like a + + 3 Senator Golden, you pick up the phone to get + + 4 somebody a bed? Well, great for that individual; + + 5 but, what about the other 99 people that are + + 6 sitting, waiting for a bed. + + 7 Now, in self-injury; you get the bed. Now + + 8 you need rehab. You've got to go to a nursing home. + + 9 There's a line of a nursing home. You cannot + + 10 get into that rehab. You have to actually wait, and + + 11 make appointments for that. That could be weeks + + 12 before you get into a nursing home. + + 13 I have no nursing beds in my community. I + + 14 don't know where all these nursing beds are. + + 15 So, I think we have some serious crises going + + 16 on. I think Brooklyn is definitely going to be a + + 17 "ground zero" for health-care issues in the future. + + 18 But, there are a number of Brooklyns around + + 19 the state of New York. And we have to try to + + 20 concentrate on that. + + 21 Just think about that. + + 22 There are lines, today. And that's before + + 23 the baby-boomers kick in. That's before we see some + + 24 real changes that will change that system to a way + + 25 that we no longer know. And we can't allow that to + + + + + + + + 69 + 1 happen. + + 2 I have to run to my committee meetings. I + + 3 know I threw a lot out on the table there. + + 4 But you have a very qualified and good + + 5 chairman here. He'll help. And I will work with + + 6 you, to coordinate, to get our message across. And + + 7 I think we have to get it across in a timely + + 8 fashion, and it has to be a concise and decisive + + 9 piece of literature, and information, that goes to + + 10 the Governor, and to SAGE. + + 11 Thank you. + + 12 MS. MESICK: Thank you. + + 13 MR. JELLINEK: Senator Golden, thank you for + + 14 all the good work you have done on behalf of seniors + + 15 in New York State. + + 16 MS. MESICK: Yes, thank you. + + 17 SENATOR GOLDEN: You're quite welcome. + + 18 You keep up the good work that you're doing + + 19 for seniors. + + 20 Tell that "good little Bobbie," I said hello. + + 21 [Laughter.] + + 22 SENATOR GOLDEN: Where are we hiding that + + 23 woman today? + + 24 MR. JELLINEK: City hall. + + 25 [Laughter.] + + + + + + + + 70 + 1 SENATOR VALESKY: Thank you, Senator. + + 2 SENATOR GOLDEN: You're quite welcome. + + 3 Mr. Chair, thank you. + + 4 SENATOR VALESKY: Okay, who else? + + 5 I want to make sure we have -- I don't know + + 6 if we've heard from everyone? Is there anyone who + + 7 hasn't had a chance to weigh in? + + 8 No? + + 9 To wrap up, the last five minutes or so, + + 10 anybody have any final thoughts that they want to + + 11 share? + + 12 MR. JELLINEK: I think what's exciting about + + 13 this, for us here, is that we're all embracing this + + 14 opportunity. + + 15 SENATOR VALESKY: Uh-huh. + + 16 MR. JELLINEK: We're not being defensive. + + 17 We're trying to take advantage of a seed change + + 18 that's occurring. + + 19 SENATOR VALESKY: Yep. + + 20 MR. JELLINEK: And we believe it's the right + + 21 thing to do. + + 22 And I think, in the last three months, or + + 23 longer, we have come together, to unite on this + + 24 position. And I think that could become a very + + 25 powerful force. + + + + + + + + 71 + 1 And working with you, and all of groups + + 2 around this room, and the groups that aren't in this + + 3 room, I think we can force some of this change. + + 4 The strategic work that needs to be done, we + + 5 would love, all of us, to work closely with your + + 6 office, and see how we can make this happen. + + 7 And, with the Affordable Care health-care + + 8 act, hospital is not getting a lot to-- getting + + 9 reimbursed for discharge planning. + + 10 Anybody who's discharged, and if someone is + + 11 telling you, discharge planning is an oxymoron, + + 12 there's no such thing. + + 13 So, if they're not getting reimbursed for + + 14 this, it's the community-based services, that are + + 15 there, that need to be invested in. + + 16 To me, it's a no-brainer. + + 17 I mean, it's -- the services out there, that + + 18 need to be invested in. And there needs to be a + + 19 flip, a rebalancing, of how to look at this. + + 20 And from what I understand, hearing what the + + 21 health commissioner said yesterday, he certainly + + 22 gets this, in terms of product-based services. + + 23 And the last piece is, you need a voice for + + 24 aging in New York State that's independent, of + + 25 somebody reporting to the health department. + + + + + + + + 72 + 1 SENATOR VALESKY: Right. + + 2 MR. JELLINEK: So, I don't want to speak for + + 3 everybody, but I'm just saying, I think we're pretty + + 4 united on all of this. + + 5 And I think the question is: Do we get the + + 6 seniors -- it's time to go back to the seniors, and + + 7 start talking once, and putting some numbers to + + 8 this. + + 9 And if your staff can be helpful, in helping + + 10 us to come up with some of the numbers -- I know + + 11 some people have the expertise -- that would be + + 12 really helpful. + + 13 SENATOR VALESKY: Absolutely. + + 14 MS. MESICK: I just also think, Senator, + + 15 again, thanking you for your statement at the + + 16 outset, which is, that you would like to communicate + + 17 to the SAGE Commission, your own perspective. + + 18 I think that's very powerful, and important. + + 19 And we thank you for your willingness to do that. + + 20 SENATOR VALESKY: Well, that's my immediate + + 21 next step after this. And, again, communication is + + 22 key. + + 23 And I don't -- you know, the SAGE Commission + + 24 is charged with lots and lots of, or will develop + + 25 lots of, recommendations across the whole scope of a + + + + + + + + 73 + 1 $132 1/2 billion state budget that funds all our + + 2 state agencies. + + 3 But I think that, first, and foremost, all of + + 4 you coming together, and being proactive -- and I + + 5 don't have to repeat what I have already said -- + + 6 but, putting a proactive plan in place, as opposed + + 7 to reacting to whatever they suggest, number one, + + 8 critical. + + 9 Now, it's that communication, from me, and + + 10 from the perspective of the committee; from all of + + 11 you, from the perspective of your leadership + + 12 positions in your own communities. And, then, as + + 13 you just indicated, going back to your seniors, and + + 14 others. + + 15 Because, I don't know that -- you know, I + + 16 haven't heard, in other areas, this kind of a united + + 17 front, and communicating to the commission, and + + 18 then, by extension, to the Governor. + + 19 So, I think that's, clearly, the next step. + + 20 I think the one thing that I would just, to + + 21 follow up to Mike, on the issue of whether or not + + 22 legislation would be needed? + + 23 I think in addition to the elder law, you + + 24 know, I mean, the budget is legislation. + + 25 I can't imagine that there could be any + + + + + + + + 74 + 1 defunding of any state agency, without that being + + 2 part of a legislative process. + + 3 So, I can't -- you know, I could be wrong, + + 4 but I can't imagine that there is any way that + + 5 something like this could happen without the + + 6 legislature's ultimate approval, so... + + 7 And I don't see any chance of that happening + + 8 in the next four weeks before this session ends, + + 9 so... + + 10 So, this is, again, the beginning of a + + 11 process, and hopefully the end will be successful. + + 12 And, you know, everyone, as I anticipated, raised + + 13 incredibly salient points about why this is so + + 14 important. + + 15 So -- + + 16 MS. MESICK: Thank you, Senator. + + 17 (Roundtable participants say "Thank you.") + + 18 SENATOR VALESKY: Okay. + + 19 All right, thank you all very much. + + 20 (Roundtable participants say "Thank you.") + + 21 + + 22 * * * * * + + 23 + + 24 (Whereupon, at 10:29 a.m., the senate + + 25 roundtable session concluded.) + + + + + + \ No newline at end of file From d8a9d394176a3acccd5ce3caf573fc0181838d8e Mon Sep 17 00:00:00 2001 From: Kevin Caseiras <kcaseiras@gmail.com> Date: Thu, 20 Feb 2020 12:49:02 -0500 Subject: [PATCH 15/29] Adjustments to the new transcript processing - Removed support for timestamp date time formats. This simplifies the API by only accepting one date time format. Consistant with the rest of our APIs. - Validated API params in the controllers so we could return an informative InvalidRequestParamEx if the param was invalid. - Clarified the field names of the TranscriptUpdateTokenView - Clarified the meaning of the TranscriptId date time field. refs #13232 --- .../view/transcript/TranscriptIdView.java | 8 ++--- .../transcript/TranscriptUpdateTokenView.java | 8 ++--- .../api/transcript/TranscriptGetCtrl.java | 18 +++++----- .../controller/pdf/TranscriptPdfCtrl.java | 7 ++-- .../transcript/SqlFsTranscriptFileDao.java | 4 +-- .../dao/transcript/SqlTranscriptDao.java | 6 ++-- .../search/ElasticTranscriptSearchDao.java | 4 +-- .../model/transcript/TranscriptId.java | 36 +++++++++---------- 8 files changed, 45 insertions(+), 46 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java index 3ea8eaf0d..5c19055e9 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java @@ -5,14 +5,14 @@ public class TranscriptIdView implements ViewObject { - protected String localDateTime; + protected String sessionDateTime; public TranscriptIdView(TranscriptId transcriptId) { - this.localDateTime = transcriptId.getLocalDateTime().toString(); + this.sessionDateTime = transcriptId.getSessionDateTime().toString(); } - public String getLocalDateTime() { - return localDateTime; + public String getSessionDateTime() { + return sessionDateTime; } @Override diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptUpdateTokenView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptUpdateTokenView.java index d44762b9a..38e4aa79e 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptUpdateTokenView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptUpdateTokenView.java @@ -8,19 +8,19 @@ public class TranscriptUpdateTokenView implements ViewObject { private TranscriptIdView transcriptId; - private LocalDateTime dateTime; + private LocalDateTime updateDateTime; public TranscriptUpdateTokenView(TranscriptUpdateToken token) { this.transcriptId = new TranscriptIdView(token.getTranscriptId()); - this.dateTime = token.getUpdateDateTime(); + this.updateDateTime = token.getUpdateDateTime(); } public TranscriptIdView getTranscriptId() { return transcriptId; } - public LocalDateTime getDateTime() { - return dateTime; + public LocalDateTime getUpdateDateTime() { + return updateDateTime; } @Override diff --git a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java index 65343564e..86be2a884 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/transcript/TranscriptGetCtrl.java @@ -11,6 +11,7 @@ import gov.nysenate.openleg.client.view.transcript.TranscriptPdfView; import gov.nysenate.openleg.client.view.transcript.TranscriptView; import gov.nysenate.openleg.controller.api.base.BaseCtrl; +import gov.nysenate.openleg.controller.api.base.InvalidRequestParamEx; import gov.nysenate.openleg.dao.base.LimitOffset; import gov.nysenate.openleg.model.search.SearchException; import gov.nysenate.openleg.model.search.SearchResults; @@ -31,6 +32,8 @@ import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; import java.util.stream.Collectors; import static gov.nysenate.openleg.controller.api.base.BaseCtrl.BASE_API_PATH; @@ -108,8 +111,9 @@ public BaseResponse getTranscriptsByYear(@PathVariable int year, */ @RequestMapping("/{dateTime:.*}") public BaseResponse getTranscript(@PathVariable String dateTime) { + LocalDateTime localDateTime = parseISODateTime(dateTime, "dateTime"); return new ViewObjectResponse<>( - new TranscriptView(transcriptData.getTranscript(new TranscriptId(dateTime))), + new TranscriptView(transcriptData.getTranscript(new TranscriptId(localDateTime))), "Data for transcript " + dateTime); } @@ -126,7 +130,8 @@ public BaseResponse getTranscript(@PathVariable String dateTime) { @RequestMapping("/{dateTime}.pdf") public ResponseEntity<byte[]> getTranscriptPdf(@PathVariable String dateTime) throws IOException, COSVisitorException { - TranscriptId transcriptId = new TranscriptId(dateTime); + LocalDateTime localDateTime = parseISODateTime(dateTime, "dateTime"); + TranscriptId transcriptId = new TranscriptId(localDateTime); Transcript transcript = transcriptData.getTranscript(transcriptId); ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); TranscriptPdfView.writeTranscriptPdf(transcript, pdfBytes); @@ -148,14 +153,7 @@ private BaseResponse getTranscriptResponse(boolean summary, boolean full, LimitO @ExceptionHandler(TranscriptNotFoundEx.class) @ResponseStatus(value = HttpStatus.NOT_FOUND) public ErrorResponse handleTranscriptNotFoundEx(TranscriptNotFoundEx ex) { - // TODO: Doesn't display anything on the webpage, even though the response is received. + // TODO: Openleg UI doesn't display anything on the webpage, even though the response is received. return new ViewObjectErrorResponse(ErrorCode.TRANSCRIPT_NOT_FOUND, new TranscriptIdView(ex.getTranscriptId())); } - - @ExceptionHandler(IllegalArgumentException.class) - @ResponseStatus(value = HttpStatus.BAD_REQUEST) - public ErrorResponse handleIllegalArgumentEx(IllegalArgumentException ex) { - // TODO: Doesn't display anything on the webpage, even though the response is received. - return new ViewObjectErrorResponse(ErrorCode.INVALID_ARGUMENTS, ex.getMessage()); - } } diff --git a/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java b/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java index 4d81a7247..71c7de94b 100644 --- a/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/pdf/TranscriptPdfCtrl.java @@ -1,6 +1,7 @@ package gov.nysenate.openleg.controller.pdf; import gov.nysenate.openleg.client.view.transcript.TranscriptPdfView; +import gov.nysenate.openleg.controller.api.base.BaseCtrl; import gov.nysenate.openleg.model.transcript.Transcript; import gov.nysenate.openleg.model.transcript.TranscriptId; import gov.nysenate.openleg.model.transcript.TranscriptNotFoundEx; @@ -20,10 +21,11 @@ import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.time.LocalDateTime; @RestController @RequestMapping(value = "/pdf/transcripts") -public class TranscriptPdfCtrl +public class TranscriptPdfCtrl extends BaseCtrl { private static final Logger logger = LoggerFactory.getLogger(TranscriptPdfCtrl.class); @@ -43,7 +45,8 @@ public class TranscriptPdfCtrl @RequestMapping("/{dateTime}") public ResponseEntity<byte[]> getTranscriptPdf(@PathVariable String dateTime, HttpServletResponse response) throws IOException { - TranscriptId transcriptId = new TranscriptId(dateTime); + LocalDateTime localDateTime = parseISODateTime(dateTime, "dateTime"); + TranscriptId transcriptId = new TranscriptId(localDateTime); try { Transcript transcript = transcriptData.getTranscript(transcriptId); ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java index 83008adf3..71424dd2e 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlFsTranscriptFileDao.java @@ -80,8 +80,8 @@ public void archiveAndUpdateTranscriptFile(TranscriptFile transcriptFile) throws if (stagedFile.getParentFile().compareTo(incomingTranscriptDir) == 0) { String currVersion = Integer.toString(pastVersions(transcriptFile)+1); LocalDateTime dateTime = transcriptFile.getDateTime(); - // Since Windows has issues dealing with filenames with : in it, we'll replace it with ; to archive it. - String trueName = dateTime.toString().replaceAll(":", ";") + ".v" + currVersion; + // Remove ':' chars from file name since they are not supported in windows. + String trueName = dateTime.toString().replaceAll(":", "") + ".v" + currVersion; File archiveFile = new File(archiveTranscriptDir, trueName); FileIOUtils.moveFile(stagedFile, archiveFile); transcriptFile.setFile(archiveFile); diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java index 2b22aa099..5e099b3d8 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java @@ -70,7 +70,7 @@ private MapSqlParameterSource getTranscriptParams(Transcript transcript, Transcr private MapSqlParameterSource getTranscriptIdParams(TranscriptId transcriptId) { MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("dateTime", DateUtils.toDate(transcriptId.getLocalDateTime())); + params.addValue("dateTime", DateUtils.toDate(transcriptId.getSessionDateTime())); return params; } @@ -87,10 +87,10 @@ private MapSqlParameterSource getTranscriptIdParams(TranscriptId transcriptId) { }; static RowMapper<TranscriptId> transcriptIdRowMapper = (rs, rowNum) -> - new TranscriptId(rs.getString("date_time")); + new TranscriptId(getLocalDateTimeFromRs(rs, "date_time")); private static RowMapper<TranscriptUpdateToken> transcriptUpdateRowMapper = (rs, rowNum) -> - new TranscriptUpdateToken(new TranscriptId(rs.getString("date_time")), + new TranscriptUpdateToken(new TranscriptId(getLocalDateTimeFromRs(rs, "date_time")), getLocalDateTimeFromRs(rs, "modified_date_time")); } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java index 94b3eb06f..b3b023cfd 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java @@ -54,7 +54,7 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { BulkRequest bulkRequest = new BulkRequest(); transcripts.stream() .map(TranscriptView::new) - .map(t -> getJsonIndexRequest(transcriptIndexName, t.getLocalDateTime(), t)) + .map(t -> getJsonIndexRequest(transcriptIndexName, t.getSessionDateTime(), t)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); } @@ -63,7 +63,7 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { @Override public void deleteTranscriptFromIndex(TranscriptId transcriptId) { if (transcriptId != null) { - deleteEntry(transcriptIndexName, transcriptId.getLocalDateTime().toString()); + deleteEntry(transcriptIndexName, transcriptId.getSessionDateTime().toString()); } } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java index f6f4884fc..28b1bc1c8 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java @@ -3,7 +3,6 @@ import com.google.common.collect.ComparisonChain; import java.io.Serializable; -import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.format.DateTimeParseException; import java.util.Objects; @@ -16,22 +15,21 @@ public class TranscriptId implements Serializable, Comparable<TranscriptId> private static final long serialVersionUID = -6509878885942142022L; /** The timestamp which corresponds to the transcript. */ - private LocalDateTime localDateTime; + private LocalDateTime sessionDateTime; /** --- Constructors --- */ - public TranscriptId(LocalDateTime localDateTime) { - this(localDateTime.toString()); + public TranscriptId(LocalDateTime sessionDateTime) { + this.sessionDateTime = sessionDateTime; } - public TranscriptId(String time) { - try { - this.localDateTime = LocalDateTime.parse(time); - } - catch (DateTimeParseException e) { - // The time may be the String format of a Timestamp. - this.localDateTime = Timestamp.valueOf(time).toLocalDateTime(); - } + /** + * Creates a TranscriptId from a string representation of an ISO date time. + * @param dateTime String in the format of 'yyyy-mm-ddThh:mm:ss' + * @throws DateTimeParseException if <code>datetime</code> is in an invalid format. + */ + public TranscriptId(String dateTime) { + this(LocalDateTime.parse(dateTime)); } /** --- Overrides --- */ @@ -41,33 +39,33 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TranscriptId that = (TranscriptId) o; - return Objects.equals(localDateTime, that.localDateTime); + return Objects.equals(sessionDateTime, that.sessionDateTime); } @Override public int hashCode() { - return localDateTime != null ? localDateTime.hashCode() : 0; + return sessionDateTime != null ? sessionDateTime.hashCode() : 0; } @Override public int compareTo(TranscriptId o) { return ComparisonChain.start() - .compare(this.localDateTime, o.localDateTime) + .compare(this.sessionDateTime, o.sessionDateTime) .result(); } @Override public String toString() { - return "Transcript " + localDateTime; + return "Transcript " + sessionDateTime; } /** --- Basic Getters/Setters --- */ - public LocalDateTime getLocalDateTime() { - return localDateTime; + public LocalDateTime getSessionDateTime() { + return sessionDateTime; } public LocalDateTime getDateTime() { - return localDateTime; + return sessionDateTime; } } From a70d8a71feff821c9d5e2939b929c4da8bd56f80 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Thu, 5 Mar 2020 17:46:35 -0500 Subject: [PATCH 16/29] Removed redundant field and updated documentation. --- docs/api/transcripts_floor.rst | 32 +++++++++---------- docs/backend/index.md | 2 +- .../view/transcript/TranscriptInfoView.java | 6 ---- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/docs/api/transcripts_floor.rst b/docs/api/transcripts_floor.rst index 811b7272f..1a177fcbe 100644 --- a/docs/api/transcripts_floor.rst +++ b/docs/api/transcripts_floor.rst @@ -8,15 +8,15 @@ Get a single Transcript **Usage** -Retrieve transcript by filename +Retrieve transcript by dateTime :: - (GET) /api/3/transcripts/{filename} + (GET) /api/3/transcripts/{dateTime} **Examples** -Request transcript 090314.txt +Request transcript 2014-09-03T09:00 :: - /api/3/transcripts/090314.txt + /api/3/transcripts/2014-09-03T09:00 **Response** @@ -25,15 +25,15 @@ Full Transcript Response .. code-block:: javascript { - "success" : true, // Indicates if a transcript was found. - "message" : "Data for transcript 090314.txt", // Response description. - "responseType" : "transcript", // Response data type. + "success" : true, // Indicates if a transcript was found. + "message" : "Data for transcript 2014-09-03T09:00:00", // Response description. + "responseType" : "transcript", // Response data type. "result" : { - "filename" : "090314.txt", // Filename of transcript. - "sessionType" : "REGULAR SESSION", // Session type - "dateTime" : "2014-09-03T09:00", // Date Time of senate session. - "location" : "ALBANY, NEW YORK", // Location of senate session. - "text" : "5100\n\n 1 NEW YORK STATE SE.." // The text of the transcript. + "sessionDateTime" : "2014-09-03T09:00", // Filename of transcript. + "sessionType" : "REGULAR SESSION", // Session type + "dateTime" : "2014-09-03T09:00:00 // Date Time of senate session. + "location" : "ALBANY, NEW YORK", // Location of senate session. + "text" : "5100\n\n 1 NEW YORK STATE SE.." // The text of the transcript. } } @@ -42,15 +42,15 @@ Get a transcript pdf **Usage** -Retrieve transcript pdf by filename +Retrieve transcript pdf by dateTime :: - (GET) /api/3/transcripts/{filename}.pdf + (GET) /api/3/transcripts/{dateTime}.pdf **Examples** -Request transcript 090314.txt +Request transcript 2014-09-03T09:00 :: - /api/3/transcripts/090314.txt.pdf + /api/3/transcripts/2014-09-03T09:00 Get a list of transcripts ------------------------- diff --git a/docs/backend/index.md b/docs/backend/index.md index 22804ab34..479dcdd96 100644 --- a/docs/backend/index.md +++ b/docs/backend/index.md @@ -207,7 +207,7 @@ Once running you should be able to view the application at: http://localhost:808 Now we can process the xml data we downloaded in our local Open Legislation environment. This process can take a long time so be prepared to leave it running for up to a day or two. -`curl -XPOST -v -u '<<admin.user>>:<<admin.password>>' localhost:8080/api/3/admin/process/run` +`curl -XPOST -v -u '<<default.admin.user>>:<<default.admin.password>>' localhost:8080/api/3/admin/process/run` **NOTE** * Do not attempt this on a system with 4GB of ram without changing the cache limits in app.properties diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptInfoView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptInfoView.java index 744e22d8c..8952dad08 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptInfoView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptInfoView.java @@ -7,14 +7,12 @@ public class TranscriptInfoView extends TranscriptIdView { protected String sessionType; - protected LocalDateTime dateTime; protected String location; public TranscriptInfoView(Transcript transcript) { super((transcript != null) ? transcript.getTranscriptId() : null); if (transcript != null) { this.sessionType = transcript.getSessionType(); - this.dateTime = transcript.getDateTime(); this.location = transcript.getLocation(); } } @@ -23,10 +21,6 @@ public String getSessionType() { return sessionType; } - public LocalDateTime getDateTime() { - return dateTime; - } - public String getLocation() { return location; } From f84a59f6ebcca29ddf8dd5f2cf579ca31b4c254a Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Tue, 10 Mar 2020 11:19:55 -0400 Subject: [PATCH 17/29] Aligned SessionMember and Member classes with their respective SQL tables. --- .../client/view/bill/BaseBillIdView.java | 2 +- .../client/view/entity/FullMemberView.java | 1 - .../controller/api/entity/MemberGetCtrl.java | 7 +++--- .../dao/bill/data/SqlBillUpdatesDao.java | 2 +- .../entity/member/data/SqlMemberQuery.java | 2 +- .../openleg/model/entity/Chamber.java | 2 +- .../nysenate/openleg/model/entity/Member.java | 22 +++++++++++++++---- .../nysenate/openleg/model/entity/Person.java | 18 +++++++-------- .../openleg/model/entity/SessionMember.java | 21 ++++-------------- 9 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/client/view/bill/BaseBillIdView.java b/src/main/java/gov/nysenate/openleg/client/view/bill/BaseBillIdView.java index 8e99a8d3a..636ab3c09 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/bill/BaseBillIdView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/bill/BaseBillIdView.java @@ -20,7 +20,7 @@ public BaseBillIdView(BillId billId) { if (billId != null) { this.basePrintNo = billId.getBasePrintNo(); this.session = Optional.ofNullable(billId.getSession()) - .map(SessionYear::getYear).orElse(null); + .map(SessionYear::getYear).orElse(0); this.basePrintNoStr = BaseBillId.of(billId).toString(); } } diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java index 7d74903b5..5cc3c988b 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java @@ -1,6 +1,5 @@ package gov.nysenate.openleg.client.view.entity; -import com.google.common.collect.TreeMultimap; import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.FullMember; import gov.nysenate.openleg.model.entity.SessionMember; diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java index 718929912..e022d27f1 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java @@ -71,7 +71,7 @@ public BaseResponse getMembersByYear(@RequestParam(defaultValue = "shortName:asc * limit - Limit the number of results * offset - Start results from an offset. */ - @RequestMapping(value = "/{sessionYear:\\d+}") + @RequestMapping(value = "/{sessionYear:\\d{4}}}") public BaseResponse getMembersByYear(@PathVariable int sessionYear, @RequestParam(defaultValue = "shortName:asc") String sort, @RequestParam(defaultValue = "false") boolean full, @@ -88,11 +88,11 @@ public BaseResponse getMembersByYear(@PathVariable int sessionYear, * Retrieve information for a member from a session year: (GET) /api/3/members/{sessionYear}/{id} * Request Parameters : full - If true, the full member view will be returned. */ - @RequestMapping(value = "/{sessionYear:[\\d]{4}}/{id:\\d+}") + @RequestMapping(value = "/{sessionYear:\\d{4}}/{id:\\d+}") public BaseResponse getMembersByYear(@PathVariable int id, @PathVariable int sessionYear, @RequestParam(defaultValue = "true") boolean full, - WebRequest request) throws SearchException, MemberNotFoundEx { + WebRequest request) throws MemberNotFoundEx { return new ViewObjectResponse<>( (full) ? new ExtendedMemberView(memberData.getMemberById(id, SessionYear.of(sessionYear))) : new SimpleMemberView(memberData.getMemberById(id, SessionYear.of(sessionYear))) @@ -127,7 +127,6 @@ private BaseResponse getMemberResponse(boolean full, LimitOffset limOff, SearchR .map(memberData::getMemberById) .map(member -> full ? new FullMemberView(member) : new SimpleMemberView(member.getLatestSessionMember().get())) .collect(Collectors.toList()); - return ListViewResponse.of(memberList, results.getTotalResults(), limOff); } diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java index a96705574..aa1c2f014 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java @@ -180,7 +180,7 @@ public UpdateDigest<BaseBillId> mapRow(ResultSet rs, int rowNum) throws SQLExcep if (updateTable != null && !updateTable.columns.isEmpty()) { Set<String> columnSet = new HashSet<>(updateTable.columns); data.keySet().retainAll( - data.keySet().stream().filter(col -> columnSet.contains(col)).collect(Collectors.toSet())); + data.keySet().stream().filter(columnSet::contains).collect(Collectors.toSet())); } } digest.setAction(rs.getString("action")); diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java index 6e08db927..a399f7470 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java @@ -21,7 +21,7 @@ public enum SqlMemberQuery implements BasicSqlQuery SELECT_MEMBER_SELECT_FRAGMENT.sql + "\n" + SELECT_MEMBER_TABLE_FRAGMENT.sql ), SELECT_MEMBER_BY_ID_SQL( - SELECT_MEMBER_FRAGMENT.sql + " WHERE sm.member_id = :memberId" + SELECT_MEMBER_FRAGMENT.sql + " WHERE sm.member_id = :memberId AND sm.alternate = FALSE" ), SELECT_MEMBER_BY_ID_SESSION_SQL( SELECT_MEMBER_BY_ID_SQL.sql + " AND sm.session_year = :sessionYear AND sm.alternate = FALSE" diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Chamber.java b/src/main/java/gov/nysenate/openleg/model/entity/Chamber.java index 9cb3db0b4..db108275e 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Chamber.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Chamber.java @@ -10,7 +10,7 @@ public enum Chamber private char abbreviation; - private Chamber(char abbreviation){ + Chamber(char abbreviation){ this.abbreviation = abbreviation; } diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Member.java b/src/main/java/gov/nysenate/openleg/model/entity/Member.java index b701bfa6a..14d87208f 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Member.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Member.java @@ -13,7 +13,10 @@ public class Member extends Person { /** The legislative chamber this member is associated with. */ protected Chamber chamber; - public Member() { } + /** Indicates if the member is currently an incumbent. */ + protected boolean incumbent; + + public Member() {} public Member(int memberId) { this.memberId = memberId; @@ -23,16 +26,18 @@ public Member(Member member) { super(member); this.memberId = member.memberId; this.chamber = member.chamber; + this.incumbent = member.incumbent; } /** * Updates a Members fields to be equal to other. - * @param other + * @param other to copy from. */ public void updateFromOther(Member other) { super.updateFromOther(other); this.memberId = other.getMemberId(); this.chamber = other.getChamber(); + this.incumbent = other.incumbent; } /** --- Overrides --- */ @@ -44,12 +49,13 @@ public boolean equals(Object o) { if (!super.equals(o)) return false; Member member = (Member) o; return memberId == member.memberId && - chamber == member.chamber; + chamber == member.chamber && + incumbent == member.incumbent; } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), memberId, chamber); + return Objects.hashCode(super.hashCode(), memberId, chamber, incumbent); } /** --- Getters / Setters --- */ @@ -69,4 +75,12 @@ public Chamber getChamber() { public void setChamber(Chamber chamber) { this.chamber = chamber; } + + public boolean isIncumbent() { + return this.incumbent; + } + + public void setIncumbent(boolean incumbent) { + this.incumbent = incumbent; + } } diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Person.java b/src/main/java/gov/nysenate/openleg/model/entity/Person.java index 433c1ef81..1bb078ba3 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Person.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Person.java @@ -11,9 +11,6 @@ public class Person implements Comparable<Person> * This value should only be set after retrieval from the persistence layer. */ private Integer personId; - /** The prefix (Mr, Mrs, Senator, etc) */ - private String prefix = ""; - /** The full name of the person. */ private String fullName = ""; @@ -26,18 +23,21 @@ public class Person implements Comparable<Person> /** The last name of the person. */ private String lastName = ""; + /** The email address of the person. */ + private String email = ""; + + /** The prefix (Mr, Mrs, Senator, etc) */ + private String prefix = ""; + /** The suffix of the person (Jr, Sr, etc) */ private String suffix = ""; - /** The email address of the person. */ - private String email = ""; + /** True if this person has been manually verified */ + private boolean verified; /** The name of the image for this person. */ private String imgName = ""; - /** True if this person has been manually verified */ - protected boolean verified; - /** --- Constructors --- */ public Person () {} @@ -65,7 +65,7 @@ public Person(Person other) { /** * Updates a Person's fields to be equal to other. - * @param other + * @param other to copy from. */ public void updateFromOther(Person other) { this.personId = other.getPersonId(); diff --git a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java index 125f8efaa..3ee9d9585 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java @@ -23,18 +23,15 @@ public class SessionMember extends Member implements Serializable * This shortName is only unique to the scope of a (2 year) session */ protected String lbdcShortName; - /** True if the shortname on this member is an alternate shortname */ - protected boolean alternate; - /** The session year the member is active in. */ protected SessionYear sessionYear; - /** Indicates if the member is currently an incumbent. */ - protected boolean incumbent; - /** The district number the member is serving in during the given session year. */ protected Integer districtCode; + /** True if the shortname on this member is an alternate shortname */ + protected boolean alternate; + /** --- Constructors --- */ public SessionMember() {} @@ -49,7 +46,6 @@ public SessionMember(SessionMember other) { this.sessionMemberId = other.sessionMemberId; this.lbdcShortName = other.lbdcShortName; this.sessionYear = other.sessionYear; - this.incumbent = other.incumbent; this.districtCode = other.districtCode; this.alternate = other.alternate; } @@ -101,7 +97,6 @@ public void updateFromOther(SessionMember other) { this.lbdcShortName = other.getLbdcShortName(); this.alternate = other.isAlternate(); this.sessionYear = other.getSessionYear(); - this.incumbent = other.isIncumbent(); this.districtCode = other.getDistrictCode(); } @@ -123,7 +118,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return 31 * super.hashCode() + Objects.hash(memberId, sessionYear, chamber, incumbent, districtCode); + return 31 * super.hashCode() + Objects.hash(memberId, sessionYear, districtCode); } @Override @@ -155,14 +150,6 @@ public void setSessionMemberId(int sessionMemberId) { this.sessionMemberId = sessionMemberId; } - public boolean isIncumbent() { - return incumbent; - } - - public void setIncumbent(boolean incumbent) { - this.incumbent = incumbent; - } - public String getLbdcShortName() { return lbdcShortName; } From ff1691c9ab91faad4b69e1ce979b6623f0e9aade Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Fri, 13 Mar 2020 11:19:02 -0400 Subject: [PATCH 18/29] Aligned member views with their related Java classes, for the most part. --- .../view/committee/CommitteeMemberView.java | 2 +- .../view/entity/ExtendedMemberView.java | 103 ------------------ .../client/view/entity/FullMemberView.java | 29 +++-- .../client/view/entity/MemberView.java | 38 +++++-- .../client/view/entity/PersonView.java | 36 +++++- .../client/view/entity/SessionMemberView.java | 71 ++++++++++++ .../client/view/entity/SimpleMemberView.java | 62 ----------- .../api/admin/MemberManageCtrl.java | 12 +- .../controller/api/entity/MemberGetCtrl.java | 18 +-- .../api/entity/MemberSearchCtrl.java | 4 +- .../controller/ui/BaseContentPageCtrl.java | 2 +- .../committee/data/SqlCommitteeDao.java | 11 +- .../dao/entity/member/data/MemberDao.java | 28 ++--- .../dao/entity/member/data/SqlMemberDao.java | 52 +++++---- .../member/search/ElasticMemberSearchDao.java | 2 +- .../spotcheck/SqlSpotCheckReportQuery.java | 1 - .../openleg/model/entity/CommitteeMember.java | 18 +-- .../openleg/model/entity/FullMember.java | 5 +- .../nysenate/openleg/model/entity/Person.java | 2 +- .../openleg/model/entity/SessionMember.java | 66 ++++++----- .../processor/entity/XmlSenCommProcessor.java | 2 +- .../openleg/script/ImportCommittees.java | 10 +- .../openleg/script/MemberScraperCLI.java | 2 +- .../member/data/CachedMemberService.java | 10 +- .../entity/member/data/FullMemberIdCache.java | 2 +- .../data/SessionChamberShortNameCache.java | 2 +- .../search/ElasticMemberSearchService.java | 2 +- .../DataProcessNotificationService.java | 4 +- .../spotcheck/base/SpotCheckUtils.java | 2 +- .../scrape/BillScrapeCheckService.java | 2 +- .../openleg/util/MemberScraperUtils.java | 34 ++++-- .../dao/entity/committee/TestCommittees.java | 6 +- .../entry/XmlSenCommProcessorIT.java | 19 ++-- 33 files changed, 317 insertions(+), 342 deletions(-) delete mode 100644 src/main/java/gov/nysenate/openleg/client/view/entity/ExtendedMemberView.java create mode 100644 src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java delete mode 100644 src/main/java/gov/nysenate/openleg/client/view/entity/SimpleMemberView.java diff --git a/src/main/java/gov/nysenate/openleg/client/view/committee/CommitteeMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/committee/CommitteeMemberView.java index c6338c12e..be140d49b 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/committee/CommitteeMemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/committee/CommitteeMemberView.java @@ -9,7 +9,7 @@ public class CommitteeMemberView extends MemberView { protected String title; public CommitteeMemberView(CommitteeMember committeeMember) { - super(committeeMember != null ? committeeMember.getMember() : null); + super(committeeMember != null ? committeeMember.getSessionMember() : null); if (committeeMember != null) { this.sequenceNo = committeeMember.getSequenceNo(); this.title = committeeMember.getTitle() != null ? committeeMember.getTitle().name() : null; diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/ExtendedMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/ExtendedMemberView.java deleted file mode 100644 index 6915c838e..000000000 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/ExtendedMemberView.java +++ /dev/null @@ -1,103 +0,0 @@ -package gov.nysenate.openleg.client.view.entity; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import gov.nysenate.openleg.model.base.SessionYear; -import gov.nysenate.openleg.model.entity.Chamber; -import gov.nysenate.openleg.model.entity.SessionMember; - - -public class ExtendedMemberView extends MemberView { - - protected boolean incumbent; - - protected int personId; - protected String prefix; - protected String firstName; - protected String middleName; - protected String lastName; - protected String suffix; - protected String email; - protected boolean verified; - - protected ExtendedMemberView(){} - - public ExtendedMemberView(SessionMember member) { - super(member); - if (member != null) { - this.incumbent = member.isIncumbent(); - this.personId = member.getPersonId(); - this.prefix = member.getPrefix(); - this.firstName = member.getFirstName(); - this.middleName = member.getMiddleName(); - this.lastName = member.getLastName(); - this.suffix = member.getSuffix(); - this.email = member.getEmail(); - this.verified = member.isVerified(); - } - } - - @JsonIgnore - public SessionMember toMember() { - SessionMember member = new SessionMember(); - member.setMemberId(this.memberId); - member.setSessionMemberId(this.sessionMemberId); - member.setLbdcShortName(this.shortName); - member.setSessionYear(SessionYear.of(this.sessionYear)); - member.setChamber(Chamber.getValue(this.chamber)); - member.setAlternate(this.alternate); - member.setFullName(fullName); - member.setDistrictCode(districtCode); - member.setImgName(imgName); - member.setIncumbent(this.incumbent); - member.setPersonId(this.personId); - member.setPrefix(this.prefix); - member.setFirstName(this.firstName); - member.setMiddleName(this.middleName); - member.setLastName(this.lastName); - member.setSuffix(this.suffix); - member.setEmail(this.email); - member.setVerified(this.verified); - return member; - } - - public boolean isIncumbent() { - return incumbent; - } - - public int getPersonId() { - return personId; - } - - public String getPrefix() { - return prefix; - } - - public String getFirstName() { - return firstName; - } - - public String getMiddleName() { - return middleName; - } - - public String getLastName() { - return lastName; - } - - public String getSuffix() { - return suffix; - } - - public String getEmail() { - return email; - } - - public boolean isVerified() { - return verified; - } - - @Override - public String getViewType() { - return "member-extended"; - } -} diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java index 5cc3c988b..160ecd995 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java @@ -7,16 +7,18 @@ import java.util.*; import java.util.stream.Collectors; -public class FullMemberView extends ExtendedMemberView { +public class FullMemberView extends MemberView { - protected Map<Integer, List<SimpleMemberView>> sessionShortNameMap; + protected PersonView personView; + protected Map<Integer, List<SessionMemberView>> sessionShortNameMap; public FullMemberView(FullMember member) { super(member.getLatestSessionMember().orElse(null)); + this.personView = new PersonView(member); this.sessionShortNameMap = member.getSessionMemberMap().keySet().stream() .collect(Collectors.toMap(SessionYear::getYear, session -> member.getSessionMemberMap().get(session).stream() - .map(SimpleMemberView::new) + .map(SessionMemberView::new) .collect(Collectors.toList()))); } @@ -25,23 +27,18 @@ public FullMemberView(FullMember member) { * @param member Member */ public FullMemberView(SessionMember member) { - super(member); - this.sessionShortNameMap = new HashMap<>(); - if (member != null && member.getSessionYear() != null) { - this.sessionShortNameMap.put(member.getSessionYear().getYear(), - Collections.singletonList(new SimpleMemberView(member))); - } + this(new FullMember(Collections.singletonList(member))); } - public FullMemberView(Collection<SessionMember> members) { - super(members.stream().max(SessionMember::compareTo).orElse(null)); - this.sessionShortNameMap = members.stream() - .sorted() - .map(SimpleMemberView::new) - .collect(Collectors.groupingBy(SimpleMemberView::getSessionYear)); + public FullMemberView(Collection<SessionMember> sessionMembers) { + this(new FullMember(sessionMembers)); } - public Map<Integer, List<SimpleMemberView>> getSessionShortNameMap() { + public PersonView getPerson() { + return personView; + } + + public Map<Integer, List<SessionMemberView>> getSessionShortNameMap() { return sessionShortNameMap; } diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java index b0f790443..9a44b73c7 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java @@ -1,32 +1,52 @@ package gov.nysenate.openleg.client.view.entity; +import gov.nysenate.openleg.client.view.base.ViewObject; +import gov.nysenate.openleg.model.entity.Member; import gov.nysenate.openleg.model.entity.SessionMember; -public class MemberView extends SimpleMemberView +public class MemberView implements ViewObject { + protected int memberId; + protected String chamber; + protected boolean incumbent; protected String fullName; - protected String imgName; + protected String shortName; public MemberView(){} - public MemberView(SessionMember member) { - super(member); - if (member != null) { + public MemberView(SessionMember sessionMember) { + if (sessionMember != null && sessionMember.getMember() != null) { + Member member = sessionMember.getMember(); + this.memberId = member.getMemberId(); + this.chamber = member.getChamber() == null ? "" : member.getChamber().name(); + this.incumbent = member.isIncumbent(); this.fullName = member.getFullName(); - this.imgName = member.getImgName(); + this.shortName = sessionMember.getLbdcShortName(); } } + public int getMemberId() { + return memberId; + } + + public String getChamber() { + return chamber; + } + + public boolean isIncumbent() { + return incumbent; + } + public String getFullName() { return fullName; } - public String getImgName() { - return imgName; + public String getShortName() { + return shortName; } @Override public String getViewType() { return "member"; } -} \ No newline at end of file +} diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/PersonView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/PersonView.java index c35b0649c..5f5cdb97b 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/PersonView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/PersonView.java @@ -5,30 +5,38 @@ public class PersonView implements ViewObject { + protected int personId; protected String fullName; - protected String prefix; protected String firstName; protected String middleName; protected String lastName; + protected String email; + protected String prefix; protected String suffix; + protected boolean verified; + protected String imgName; public PersonView(Person person) { if (person != null) { + this.personId = person.getPersonId(); this.fullName = person.getFullName(); - this.prefix = person.getPrefix(); this.firstName = person.getFirstName(); this.middleName = person.getMiddleName(); this.lastName = person.getLastName(); + this.prefix = person.getPrefix(); this.suffix = person.getSuffix(); + this.email = person.getEmail(); + this.verified = person.isVerified(); + this.imgName = person.getImgName(); } } - public String getFullName() { - return fullName; + public int getPersonId() { + return personId; } - public String getPrefix() { - return prefix; + public String getFullName() { + return fullName; } public String getFirstName() { @@ -43,10 +51,26 @@ public String getLastName() { return lastName; } + public String getPrefix() { + return prefix; + } + public String getSuffix() { return suffix; } + public String getEmail() { + return email; + } + + public boolean isVerified() { + return verified; + } + + public String getImgName() { + return imgName; + } + @Override public String getViewType() { return "person"; diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java new file mode 100644 index 000000000..70f86ecb6 --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java @@ -0,0 +1,71 @@ +package gov.nysenate.openleg.client.view.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import gov.nysenate.openleg.client.view.base.ViewObject; +import gov.nysenate.openleg.model.base.SessionYear; +import gov.nysenate.openleg.model.entity.Member; +import gov.nysenate.openleg.model.entity.SessionMember; + +public class SessionMemberView implements ViewObject +{ + protected int sessionMemberId; + private Member member; + protected String shortName; + protected int sessionYear; + protected Integer districtCode; + protected boolean alternate; + + protected SessionMemberView(){} + + public SessionMemberView(SessionMember sessionMember) { + if (sessionMember != null) { + this.sessionMemberId = sessionMember.getSessionMemberId(); + this.member = sessionMember.getMember(); + this.shortName = sessionMember.getLbdcShortName(); + this.sessionYear = sessionMember.getSessionYear().getYear(); + this.districtCode = sessionMember.getDistrictCode(); + this.alternate = sessionMember.isAlternate(); + } + } + + @JsonIgnore + public SessionMember toSessionMember() { + SessionMember ret = new SessionMember(); + ret.setSessionMemberId(this.sessionMemberId); + ret.setMember(this.member); + ret.setLbdcShortName(this.shortName); + ret.setSessionYear(SessionYear.of(this.sessionYear)); + ret.setDistrictCode(this.districtCode); + ret.setAlternate(this.alternate); + return ret; + } + + public int getSessionMemberId() { + return sessionMemberId; + } + + public int getMemberId() { + return member.getMemberId(); + } + + public String getShortName() { + return shortName; + } + + public int getSessionYear() { + return sessionYear; + } + + public Integer getDistrictCode() { + return districtCode; + } + + public boolean isAlternate() { + return alternate; + } + + @Override + public String getViewType() { + return "session-member"; + } +} \ No newline at end of file diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/SimpleMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/SimpleMemberView.java deleted file mode 100644 index 48fea7640..000000000 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/SimpleMemberView.java +++ /dev/null @@ -1,62 +0,0 @@ -package gov.nysenate.openleg.client.view.entity; - -import gov.nysenate.openleg.client.view.base.ViewObject; -import gov.nysenate.openleg.model.entity.SessionMember; - -public class SimpleMemberView implements ViewObject -{ - protected int memberId; - protected int sessionMemberId; - protected String shortName; - protected int sessionYear; - protected String chamber; - protected Integer districtCode; - protected boolean alternate; - - protected SimpleMemberView(){} - - public SimpleMemberView(SessionMember member) { - if (member != null) { - this.memberId = member.getMemberId(); - this.sessionMemberId = member.getSessionMemberId(); - this.shortName = member.getLbdcShortName(); - this.sessionYear = member.getSessionYear().getYear(); - this.chamber = (member.getChamber() != null) ? member.getChamber().name() : null; - this.districtCode = member.getDistrictCode(); - this.alternate = member.isAlternate(); - } - } - - public int getSessionMemberId() { - return sessionMemberId; - } - - public int getMemberId() { - return memberId; - } - - public String getShortName() { - return shortName; - } - - public int getSessionYear() { - return sessionYear; - } - - public String getChamber() { - return chamber; - } - - public Integer getDistrictCode() { - return districtCode; - } - - public boolean isAlternate() { - return alternate; - } - - @Override - public String getViewType() { - return "member-simple"; - } -} \ No newline at end of file diff --git a/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java index b770ebcac..b2cba3d24 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java @@ -3,8 +3,8 @@ import gov.nysenate.openleg.client.response.base.BaseResponse; import gov.nysenate.openleg.client.response.base.ListViewResponse; import gov.nysenate.openleg.client.response.base.SimpleResponse; -import gov.nysenate.openleg.client.view.entity.ExtendedMemberView; import gov.nysenate.openleg.client.view.entity.FullMemberView; +import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.base.BaseCtrl; import gov.nysenate.openleg.dao.base.LimitOffset; import gov.nysenate.openleg.model.entity.FullMember; @@ -31,15 +31,15 @@ public class MemberManageCtrl extends BaseCtrl { @Autowired MemberService memberService; - private static class MemberViewList extends ArrayList<ExtendedMemberView>{} + private static class MemberViewList extends ArrayList<SessionMemberView>{} @RequiresPermissions("admin:member:get") @RequestMapping(value = "", method = RequestMethod.GET) public BaseResponse getExtendedMembers(@RequestParam(defaultValue = "false") boolean unverifiedOnly) { - Map<Boolean, List<FullMember>> poopTition = memberService.getAllFullMembers().stream() + Map<Boolean, List<FullMember>> partition = memberService.getAllFullMembers().stream() .collect(Collectors.partitioningBy(FullMember::isVerified)); - logger.info("true: {}, false: {}", Optional.ofNullable(poopTition.get(true)).map(List::size).orElse(0), - Optional.ofNullable(poopTition.get(false)).map(List::size).orElse(0)); + logger.info("true: {}, false: {}", Optional.ofNullable(partition.get(true)).map(List::size).orElse(0), + Optional.ofNullable(partition.get(false)).map(List::size).orElse(0)); List<FullMemberView> fullMembers = memberService.getAllFullMembers().stream() .filter(member -> !unverifiedOnly || !member.isVerified()) .map(FullMemberView::new) @@ -51,7 +51,7 @@ public BaseResponse getExtendedMembers(@RequestParam(defaultValue = "false") boo @RequestMapping(value = "", method = RequestMethod.POST) public BaseResponse updateMembers(@RequestBody MemberViewList memberViewList) { memberService.updateMembers(memberViewList.stream() - .map(ExtendedMemberView::toMember) + .map(SessionMemberView::toSessionMember) .collect(Collectors.toList())); return new SimpleResponse(true, "members updated", "member-update-success"); } diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java index e022d27f1..238974b82 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java @@ -6,9 +6,9 @@ import gov.nysenate.openleg.client.response.error.ErrorCode; import gov.nysenate.openleg.client.response.error.ErrorResponse; import gov.nysenate.openleg.client.view.base.ViewObject; -import gov.nysenate.openleg.client.view.entity.ExtendedMemberView; import gov.nysenate.openleg.client.view.entity.FullMemberView; -import gov.nysenate.openleg.client.view.entity.SimpleMemberView; +import gov.nysenate.openleg.client.view.entity.MemberView; +import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.base.BaseCtrl; import gov.nysenate.openleg.dao.base.LimitOffset; import gov.nysenate.openleg.model.base.SessionYear; @@ -71,7 +71,7 @@ public BaseResponse getMembersByYear(@RequestParam(defaultValue = "shortName:asc * limit - Limit the number of results * offset - Start results from an offset. */ - @RequestMapping(value = "/{sessionYear:\\d{4}}}") + @RequestMapping(value = "/{sessionYear:\\d{4}}") public BaseResponse getMembersByYear(@PathVariable int sessionYear, @RequestParam(defaultValue = "shortName:asc") String sort, @RequestParam(defaultValue = "false") boolean full, @@ -88,14 +88,14 @@ public BaseResponse getMembersByYear(@PathVariable int sessionYear, * Retrieve information for a member from a session year: (GET) /api/3/members/{sessionYear}/{id} * Request Parameters : full - If true, the full member view will be returned. */ - @RequestMapping(value = "/{sessionYear:\\d{4}}/{id:\\d+}") - public BaseResponse getMembersByYear(@PathVariable int id, + @RequestMapping(value = "/{sessionYear:\\d{4}}/{memberId:\\d+}") + public BaseResponse getMembersByYear(@PathVariable int memberId, @PathVariable int sessionYear, @RequestParam(defaultValue = "true") boolean full, WebRequest request) throws MemberNotFoundEx { return new ViewObjectResponse<>( - (full) ? new ExtendedMemberView(memberData.getMemberById(id, SessionYear.of(sessionYear))) - : new SimpleMemberView(memberData.getMemberById(id, SessionYear.of(sessionYear))) + (full) ? new FullMemberView(memberData.getMemberById(memberId, SessionYear.of(sessionYear))) + : new SessionMemberView(memberData.getMemberById(memberId, SessionYear.of(sessionYear))) ); } @@ -109,7 +109,7 @@ public BaseResponse getMembersByYear(@PathVariable int id, * limit - Limit the number of results * offset - Start results from an offset. */ - @RequestMapping(value = "/{sessionYear}/{chamber:\\D*}") + @RequestMapping(value = "/{sessionYear}/{chamber:\\D+}") public BaseResponse getMembersByYear(@PathVariable int sessionYear, @PathVariable String chamber, @RequestParam(defaultValue = "shortName:asc") String sort, @@ -125,7 +125,7 @@ public BaseResponse getMembersByYear(@PathVariable int sessionYear, private BaseResponse getMemberResponse(boolean full, LimitOffset limOff, SearchResults<Integer> results) throws MemberNotFoundEx { List<ViewObject> memberList = results.getRawResults().stream() .map(memberData::getMemberById) - .map(member -> full ? new FullMemberView(member) : new SimpleMemberView(member.getLatestSessionMember().get())) + .map(member -> full ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().get())) .collect(Collectors.toList()); return ListViewResponse.of(memberList, results.getTotalResults(), limOff); } diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java index 8cc8ab036..d3c801c37 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java @@ -4,7 +4,7 @@ import gov.nysenate.openleg.client.response.base.ListViewResponse; import gov.nysenate.openleg.client.view.base.ViewObject; import gov.nysenate.openleg.client.view.entity.FullMemberView; -import gov.nysenate.openleg.client.view.entity.SimpleMemberView; +import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.base.BaseCtrl; import gov.nysenate.openleg.dao.base.LimitOffset; import gov.nysenate.openleg.model.base.SessionYear; @@ -84,7 +84,7 @@ private BaseResponse getSearchResponse(SearchResults<Integer> results, boolean f } catch (MemberNotFoundEx ex) { throw new SearchException("No Member found.", ex); } - viewtypes.add((full) ? new FullMemberView(member) : new SimpleMemberView(member.getLatestSessionMember().get())); + viewtypes.add((full) ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().get())); } return ListViewResponse.of(viewtypes, results.getTotalResults(), limOff); } diff --git a/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java b/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java index 2f599185b..d26350b84 100644 --- a/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java @@ -54,7 +54,7 @@ protected void addContentAttributesToRequest(HttpServletRequest request) { */ private void initializeMembers() { List<Member> allMembers = memberData.getAllMembers(SortOrder.ASC, LimitOffset.ALL).stream() - .map(m -> new Member(m)) + .map(m -> new Member(m.getMember())) .distinct() .collect(Collectors.toList()); senatorsList = allMembers.stream() diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/committee/data/SqlCommitteeDao.java b/src/main/java/gov/nysenate/openleg/dao/entity/committee/data/SqlCommitteeDao.java index 45466531e..4bb96e948 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/committee/data/SqlCommitteeDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/committee/data/SqlCommitteeDao.java @@ -6,7 +6,6 @@ import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.*; import gov.nysenate.openleg.model.sourcefiles.LegDataFragment; -import gov.nysenate.openleg.service.entity.member.data.MemberService; import gov.nysenate.openleg.util.DateUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -273,7 +272,7 @@ public CommitteeVersionId mapRow(ResultSet rs, int rowNum) throws SQLException { } } - protected class CommitteeRowMapper implements RowMapper<Committee> { + protected static class CommitteeRowMapper implements RowMapper<Committee> { @Override public Committee mapRow(ResultSet rs, int i) throws SQLException { Committee committee = new Committee(); @@ -292,15 +291,15 @@ public Committee mapRow(ResultSet rs, int i) throws SQLException { } } - protected class CommitteeMemberRowMapper implements RowMapper<CommitteeMember> { + protected static class CommitteeMemberRowMapper implements RowMapper<CommitteeMember> { @Override public CommitteeMember mapRow(ResultSet rs, int i) throws SQLException { CommitteeMember committeeMember = new CommitteeMember(); committeeMember.setSequenceNo(rs.getInt("sequence_no")); SqlMemberDao.MemberRowMapper memberRowMapper = new SqlMemberDao.MemberRowMapper(); int sessionMemberId = rs.getInt("session_member_id"); - committeeMember.setMember(memberRowMapper.mapRow(rs, i)); - if (committeeMember.getMember().getMemberId() == 0) { + committeeMember.setSessionMember(memberRowMapper.mapRow(rs, i)); + if (committeeMember.getSessionMember().getMember().getMemberId() == 0) { logger.error("Could not retrieve session member " + sessionMemberId); } committeeMember.setTitle(CommitteeMemberTitle.valueOfSqlEnum(rs.getString("title"))); @@ -379,7 +378,7 @@ private MapSqlParameterSource getCommitteeVersionParams(Committee committee) { private MapSqlParameterSource getCommitteeMemberParams(CommitteeMember committeeMember, CommitteeVersionId cvid) { MapSqlParameterSource params = getCommitteeVersionIdParams(cvid); - params.addValue("session_member_id", committeeMember.getMember().getSessionMemberId()); + params.addValue("session_member_id", committeeMember.getSessionMember().getSessionMemberId()); params.addValue("sequence_no", committeeMember.getSequenceNo()); params.addValue("title", committeeMember.getTitle().asSqlEnum()); params.addValue("majority", committeeMember.isMajority()); diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/MemberDao.java b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/MemberDao.java index d59d27d8a..c4340d6d8 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/MemberDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/MemberDao.java @@ -17,7 +17,7 @@ public interface MemberDao * @param session SessionYear * @return Member */ - public SessionMember getMemberById(int id, SessionYear session); + SessionMember getMemberById(int id, SessionYear session); /** * Retrieve a member by session member id @@ -27,7 +27,7 @@ public interface MemberDao * @param sessionMemberId * @return Member */ - public SessionMember getMemberBySessionId(int sessionMemberId); + SessionMember getMemberBySessionId(int sessionMemberId); /** * Retrieves map of session year -> Member for a given member id. @@ -35,7 +35,7 @@ public interface MemberDao * @param id int * @return Map<Integer, Member> */ - public FullMember getMemberById(int id) throws MemberNotFoundEx; + FullMember getMemberById(int id) throws MemberNotFoundEx; /** * Retrieve a map of session year -> Member given the LBDC short name. @@ -44,7 +44,7 @@ public interface MemberDao * @param chamber Chamber * @return Map<Integer,Member> */ - public Map<SessionYear, SessionMember> getMembersByShortName(String lbdcShortName, Chamber chamber); + Map<SessionYear, SessionMember> getMembersByShortName(String lbdcShortName, Chamber chamber); /** * Retrieve the Member instance via the LBDC shortName and the session year. @@ -54,18 +54,18 @@ public interface MemberDao * @param chamber Chamber * @return Member */ - public SessionMember getMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber); + SessionMember getMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber); /** * Retrieve members from all years and both chambers. * @return */ - public List<SessionMember> getAllMembers(SortOrder sortOrder, LimitOffset limOff); + List<SessionMember> getAllMembers(SortOrder sortOrder, LimitOffset limOff); /** * @return List<Member> - a list of members that were created on the fly during processing and have not yet been verified */ - public List<SessionMember> getUnverifiedSessionMembers(); + List<SessionMember> getUnverifiedSessionMembers(); /** * Updates or inserts a person into the data store @@ -73,21 +73,21 @@ public interface MemberDao * @param person Person * */ - public void updatePerson(Person person); + void updatePerson(Person person); /** * Updates or inserts a member into the data store * Sets the memberId field of the given member using the newly generated id * @param member Member */ - public void updateMember(SessionMember member); + void updateMember(Member member); /** * Updates or inserts a session member into the data store * Sets the sessionMemberId field of the given member using the newly generated id - * @param member Member + * @param sessionMember sessionMember */ - public void updateSessionMember(SessionMember member); + void updateSessionMember(SessionMember sessionMember); /** @@ -95,19 +95,19 @@ public interface MemberDao * @param memberId int - member id * @param personId int - person id */ - public void linkMember(int memberId, int personId); + void linkMember(int memberId, int personId); /** * Links a session member to a member in the data store * @param sessionMemberId int - session member id * @param memberId int - member id */ - public void linkSessionMember(int sessionMemberId, int memberId); + void linkSessionMember(int sessionMemberId, int memberId); /** * Removes all persons and members that do not have an associated session member * These are typically persons/members/session members that were created on the fly during processing * where the session */ - public void clearOrphans(); + void clearOrphans(); } diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberDao.java b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberDao.java index 83a56653e..0759e49bb 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberDao.java @@ -115,7 +115,7 @@ public List<SessionMember> getUnverifiedSessionMembers() { 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( + Integer personId = jdbcNamed.queryForObject( SqlMemberQuery.INSERT_PERSON_SQL.getSql(schema()), params, new SingleColumnRowMapper<>()); person.setPersonId(personId); } @@ -123,10 +123,10 @@ public void updatePerson(Person person) { /** {@inheritDoc} */ @Override - public void updateMember(SessionMember member) { + public void updateMember(Member member) { ImmutableParams params = ImmutableParams.from(getMemberParams(member)); if (jdbcNamed.update(SqlMemberQuery.UPDATE_MEMBER_SQL.getSql(schema()), params) == 0) { - int memberId = jdbcNamed.queryForObject( + Integer memberId = jdbcNamed.queryForObject( SqlMemberQuery.INSERT_MEMBER_SQL.getSql(schema()), params, new SingleColumnRowMapper<>()); member.setMemberId(memberId); } @@ -134,12 +134,12 @@ public void updateMember(SessionMember member) { /** {@inheritDoc} */ @Override - public void updateSessionMember(SessionMember member) { - ImmutableParams params = ImmutableParams.from(getMemberParams(member)); + public void updateSessionMember(SessionMember sessionMember) { + ImmutableParams params = ImmutableParams.from(getSessionMemberParams(sessionMember)); if (jdbcNamed.update(SqlMemberQuery.UPDATE_SESSION_MEMBER_SQL.getSql(schema()), params) == 0) { - int sessionMemberId = jdbcNamed.queryForObject( + Integer sessionMemberId = jdbcNamed.queryForObject( SqlMemberQuery.INSERT_SESSION_MEMBER_SQL.getSql(schema()), params, new SingleColumnRowMapper<>()); - member.setSessionMemberId(sessionMemberId); + sessionMember.setSessionMemberId(sessionMemberId); } } @@ -174,12 +174,15 @@ public 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")); + SessionMember sessionMember = new SessionMember(); + + sessionMember.setSessionMemberId(rs.getInt("session_member_id")); + sessionMember.setLbdcShortName(rs.getString("lbdc_short_name")); + sessionMember.setSessionYear(getSessionYearFromRs(rs, "session_year")); + sessionMember.setDistrictCode(rs.getInt("district_code")); + sessionMember.setAlternate(rs.getBoolean("alternate")); + + Member member = new Member(rs.getInt("member_id")); member.setChamber(Chamber.valueOf(rs.getString("chamber").toUpperCase())); member.setIncumbent(rs.getBoolean("incumbent")); member.setPersonId(rs.getInt("person_id")); @@ -190,10 +193,11 @@ public SessionMember mapRow(ResultSet rs, int rowNum) throws SQLException { 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")); member.setEmail(rs.getString("email")); - return member; + + sessionMember.setMember(member); + return sessionMember; } } @@ -213,17 +217,21 @@ private MapSqlParameterSource getPersonParams(Person person) { .addValue("verified", person.isVerified()); } - private MapSqlParameterSource getMemberParams(SessionMember member) { + private MapSqlParameterSource getMemberParams(Member 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()); + .addValue("fullName", member.getFullName()); + } + + private MapSqlParameterSource getSessionMemberParams(SessionMember sessionMember) { + return getMemberParams(sessionMember.getMember()) + .addValue("sessionMemberId", sessionMember.getSessionMemberId()) + .addValue("lbdcShortName", sessionMember.getLbdcShortName()) + .addValue("sessionYear", Optional.ofNullable(sessionMember.getSessionYear()).map(SessionYear::getYear).orElse(null)) + .addValue("districtCode", sessionMember.getDistrictCode()) + .addValue("alternate", sessionMember.isAlternate()); } /** diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/member/search/ElasticMemberSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/entity/member/search/ElasticMemberSearchDao.java index 0d31aea95..25655d5c1 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/member/search/ElasticMemberSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/member/search/ElasticMemberSearchDao.java @@ -46,7 +46,7 @@ public void updateMemberIndex(Collection<FullMember> members) { BulkRequest bulkRequest = new BulkRequest(); members.stream() .map(FullMemberView::new) - .map(mv -> getJsonIndexRequest(memberIndexName, String.valueOf(mv.getMemberId()), mv)) + .map(fmv -> getJsonIndexRequest(memberIndexName, String.valueOf(fmv.getMemberId()), fmv)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); } diff --git a/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java b/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java index 560c4625d..cf64d4426 100644 --- a/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java @@ -145,7 +145,6 @@ public enum SqlSpotCheckReportQuery implements BasicSqlQuery "WHERE mismatch_id = :mismatchId\n" ) ; - ; private String sql; diff --git a/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java b/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java index 46bb5dcef..b2a04ec23 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java @@ -14,7 +14,7 @@ public class CommitteeMember implements Serializable, Comparable<CommitteeMember protected int sequenceNo; /** The member. */ - protected SessionMember member; + protected SessionMember sessionMember; /** The title of the member, e.g Chairperson, Vice-Chair. */ protected CommitteeMemberTitle title; @@ -29,7 +29,7 @@ public class CommitteeMember implements Serializable, Comparable<CommitteeMember public int compareTo(CommitteeMember o) { return ComparisonChain.start() .compare(this.sequenceNo, o.sequenceNo) - .compare(this.member, o.member) + .compare(this.sessionMember, o.sessionMember) .compare(this.title, o.title) .compareTrueFirst(this.majority, o.majority) .result(); @@ -42,13 +42,13 @@ public boolean equals(Object o) { CommitteeMember that = (CommitteeMember) o; return sequenceNo == that.sequenceNo && majority == that.majority && - Objects.equal(member, that.member) && + Objects.equal(sessionMember, that.sessionMember) && title == that.title; } @Override public int hashCode() { - return Objects.hashCode(sequenceNo, member, title, majority); + return Objects.hashCode(sequenceNo, sessionMember, title, majority); } /** --- Constructors --- */ @@ -57,7 +57,7 @@ public CommitteeMember() {} public CommitteeMember(CommitteeMember other) { this.sequenceNo = other.sequenceNo; - this.member = new SessionMember(other.member); + this.sessionMember = new SessionMember(other.sessionMember); this.title = other.title; this.majority = other.majority; } @@ -78,12 +78,12 @@ public void setSequenceNo(int sequenceNo) { this.sequenceNo = sequenceNo; } - public SessionMember getMember() { - return member; + public SessionMember getSessionMember() { + return sessionMember; } - public void setMember(SessionMember member) { - this.member = member; + public void setSessionMember(SessionMember sessionMember) { + this.sessionMember = sessionMember; } public CommitteeMemberTitle getTitle() { diff --git a/src/main/java/gov/nysenate/openleg/model/entity/FullMember.java b/src/main/java/gov/nysenate/openleg/model/entity/FullMember.java index 0ce7cd821..d347acac5 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/FullMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/FullMember.java @@ -18,12 +18,11 @@ public class FullMember extends Member { protected final TreeMultimap<SessionYear, SessionMember> sessionMemberMap = TreeMultimap.create(); public FullMember(Collection<SessionMember> sessionMembers) { - super(sessionMembers.stream().max(SessionMember::compareTo).orElse(null)); + super(sessionMembers.stream().max(SessionMember::compareTo).orElse(new SessionMember()).member); sessionMembers.stream() .peek(sm -> { - if (sm.memberId != this.memberId) { + if (sm.member.memberId != this.memberId) throw new IllegalArgumentException("All supplied session members must have the same member id"); - } }) .forEach(sm -> sessionMemberMap.put(sm.getSessionYear(), sm)); } diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Person.java b/src/main/java/gov/nysenate/openleg/model/entity/Person.java index 1bb078ba3..d06b4a25b 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Person.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Person.java @@ -47,7 +47,7 @@ public Person(Integer personId) { } public Person (String fullName) { - this.fullName = fullName; + this.fullName = fullName.trim(); } public Person(Person other) { diff --git a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java index 3ee9d9585..d998e2471 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java @@ -9,16 +9,19 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class SessionMember extends Member implements Serializable +public class SessionMember implements Comparable<SessionMember>, Serializable { private static final long serialVersionUID = -8348372884270872363L; - public static final Pattern shortNamePattern = Pattern.compile("([A-Z-_']+)( ([A-Z]+))?"); + private static final Pattern shortNamePattern = Pattern.compile("([A-Z-_']+)( ([A-Z]+))?"); /** Unique session member id generated by the persistence layer. Maps to a lbdcShortName. * A member may have multiple sessionMemberIds in a single session for different representations of their shortname */ protected int sessionMemberId; + /** Member that this SessionMember matches up to. */ + protected Member member; + /** Current mapping to LBDC's representation of the member id. * This shortName is only unique to the scope of a (2 year) session */ protected String lbdcShortName; @@ -37,12 +40,12 @@ public class SessionMember extends Member implements Serializable public SessionMember() {} public SessionMember(int memberId, SessionYear sessionYear) { - super(memberId); + this.member = new Member(memberId); this.sessionYear = sessionYear; } public SessionMember(SessionMember other) { - super(other); + this.member = other.member; this.sessionMemberId = other.sessionMemberId; this.lbdcShortName = other.lbdcShortName; this.sessionYear = other.sessionYear; @@ -60,14 +63,15 @@ public SessionMember(SessionMember other) { * @return Member */ public static SessionMember newMakeshiftMember(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws ParseError { - if (lbdcShortName == null) { + if (lbdcShortName == null) throw new ParseError("Attempted to create makeshift member, but lbdcShortName was null!"); - } // Assembly members are not already uppercase lbdcShortName = lbdcShortName.toUpperCase().trim(); - SessionMember member = new SessionMember(); - member.setLbdcShortName(lbdcShortName); - member.setSessionYear(sessionYear); + SessionMember sessionMember = new SessionMember(); + sessionMember.setLbdcShortName(lbdcShortName); + sessionMember.setSessionYear(sessionYear); + + Member member = new Member(); member.setChamber(chamber); member.setIncumbent(sessionYear.equals(SessionYear.current())); @@ -78,21 +82,21 @@ public static SessionMember newMakeshiftMember(String lbdcShortName, SessionYear member.setFirstName(shortNameMatcher.group(3)); member.setFullName((member.getFirstName() != null ? member.getFirstName() + " " : "") + member.getLastName()); } - else { + else member.setFullName(member.getLastName()); - } } - else { + else throw new ParseError("Can not create makeshift member: LBDC shortname '" + lbdcShortName + "' does not match specification"); - } - return member; + sessionMember.member = member; + return sessionMember; } /** * Updates a session member with the fields of other. */ public void updateFromOther(SessionMember other) { - super.updateFromOther(other); + this.member = new Member(); + this.member.updateFromOther(other.member); this.sessionMemberId = other.getSessionMemberId(); this.lbdcShortName = other.getLbdcShortName(); this.alternate = other.isAlternate(); @@ -112,32 +116,28 @@ public boolean equals(Object obj) { if (!super.equals(obj)) return false; final SessionMember other = (SessionMember) obj; return Objects.equals(this.sessionYear, other.sessionYear) && - Objects.equals(this.incumbent, other.incumbent) && + Objects.equals(this.member.incumbent, other.member.incumbent) && Objects.equals(this.districtCode, other.districtCode); } @Override public int hashCode() { - return 31 * super.hashCode() + Objects.hash(memberId, sessionYear, districtCode); + return 31 * super.hashCode() + Objects.hash(member.memberId, sessionYear, districtCode); } @Override public String toString() { - return lbdcShortName + " (year: " + sessionYear + ", id: " + memberId + ")"; + return lbdcShortName + " (year: " + sessionYear + ", id: " + member.memberId + ")"; } @Override - public int compareTo(Person o) { - int personComparison = super.compareTo(o); - if (personComparison == 0 && (o instanceof SessionMember)) { - SessionMember that = (SessionMember) o; - return ComparisonChain.start() - .compare(this.sessionYear, that.sessionYear) - .compareFalseFirst(this.alternate, that.alternate) - .compare(this.lbdcShortName, that.lbdcShortName) - .result(); - } - return personComparison; + public int compareTo(SessionMember o) { + return ComparisonChain.start() + .compare(this.member, o.member) + .compare(this.sessionYear, o.sessionYear) + .compareFalseFirst(this.alternate, o.alternate) + .compare(this.lbdcShortName, o.lbdcShortName) + .result(); } /** --- Basic Getters/Setters --- */ @@ -150,6 +150,14 @@ public void setSessionMemberId(int sessionMemberId) { this.sessionMemberId = sessionMemberId; } + public Member getMember() { + return member; + } + + public void setMember(Member member) { + this.member = member; + } + public String getLbdcShortName() { return lbdcShortName; } diff --git a/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java b/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java index 8669c5486..2ccba6ea3 100644 --- a/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java @@ -144,7 +144,7 @@ private List<CommitteeMember> processCommitteeMembers(Node committeeMembership, CommitteeMember committeeMember = new CommitteeMember(); committeeMember.setSequenceNo(Integer.parseInt(xml.getString("@seqno", memberNode))); - committeeMember.setMember(sessionMember); + committeeMember.setSessionMember(sessionMember); committeeMember.setMajority( xml.getString("memberlist/text()", memberNode) .trim() diff --git a/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java b/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java index e6d266ab1..fcadbc12e 100644 --- a/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java +++ b/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java @@ -106,10 +106,10 @@ private Committee getCommitteeFromJson(File jsonFile, int year, Chamber chamber) try { CommitteeMember committeeMember = getCommitteeMemberFromJson(chairs.next(), year, chamber); committeeMember.setTitle(sequenceNum == 1 ? CommitteeMemberTitle.CHAIR_PERSON : CommitteeMemberTitle.VICE_CHAIR); - if (!addedMembers.contains(committeeMember.getMember())) { + if (!addedMembers.contains(committeeMember.getSessionMember())) { committeeMember.setSequenceNo(sequenceNum++); committeeMembers.add(committeeMember); - addedMembers.add(committeeMember.getMember()); + addedMembers.add(committeeMember.getSessionMember()); } } catch (MemberNotFoundEx ex) { @@ -122,10 +122,10 @@ private Committee getCommitteeFromJson(File jsonFile, int year, Chamber chamber) try { CommitteeMember committeeMember = getCommitteeMemberFromJson(members.next(), year, chamber); committeeMember.setTitle(CommitteeMemberTitle.MEMBER); - if (!addedMembers.contains(committeeMember.getMember())) { + if (!addedMembers.contains(committeeMember.getSessionMember())) { committeeMember.setSequenceNo(sequenceNum++); committeeMembers.add(committeeMember); - addedMembers.add(committeeMember.getMember()); + addedMembers.add(committeeMember.getSessionMember()); } } catch (MemberNotFoundEx ex) { @@ -141,7 +141,7 @@ private Committee getCommitteeFromJson(File jsonFile, int year, Chamber chamber) private CommitteeMember getCommitteeMemberFromJson(JsonNode memberNode, int year, Chamber chamber) throws MemberNotFoundEx{ CommitteeMember committeeMember = new CommitteeMember(); SessionMember member = memberService.getMemberByShortName(memberNode.get("shortName").textValue(), SessionYear.of(year), chamber); - committeeMember.setMember(member); + committeeMember.setSessionMember(member); return committeeMember; } diff --git a/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java b/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java index d34975097..0c5617754 100644 --- a/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java +++ b/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java @@ -25,7 +25,7 @@ protected void execute(CommandLine opts) throws Exception { List<SessionMember> assemblyMembers = MemberScraperUtils.getAssemblyMembers(); assemblyMembers.stream().forEach(m -> { try { - InputStream in = new UrlResource(m.getImgName()).getInputStream(); + InputStream in = new UrlResource(m.getMember().getImgName()).getInputStream(); FileIOUtils.writeToFile(in, "/tmp/assembly/" + RandomUtils.getRandomString(10) + ".jpg"); } catch (IOException e) { logger.error("Failed to ", e); diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java index 13e0c478f..63b3dd56b 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java @@ -97,8 +97,8 @@ public SessionMember getMemberByShortNameEnsured(String lbdcShortName, SessionYe } catch (MemberNotFoundEx ex) { SessionMember member = SessionMember.newMakeshiftMember(lbdcShortName, sessionYear, chamber); - memberDao.updatePerson(member); - memberDao.updateMember(member); + memberDao.updatePerson(member.getMember()); + memberDao.updateMember(member.getMember()); memberDao.updateSessionMember(member); eventBus.post(new UnverifiedMemberEvent(member, LocalDateTime.now())); return member; @@ -121,11 +121,11 @@ public List<FullMember> getAllFullMembers() { @Override public void updateMembers(List<SessionMember> sessionMembers) { Collection<? extends Person> persons = sessionMembers.stream() - .collect(Collectors.toMap(Person::getPersonId, Function.identity(), (a,b) -> b)) + .collect(Collectors.toMap((a -> a.getMember().getPersonId()), SessionMember::getMember, (a, b) -> b)) .values(); - Collection<SessionMember> members = sessionMembers.stream() - .collect(Collectors.toMap(SessionMember::getMemberId, Function.identity(), (a,b) -> b)) + Collection<Member> members = sessionMembers.stream() + .collect(Collectors.toMap(a -> a.getMember().getMemberId(), SessionMember::getMember, (a,b) -> b)) .values(); persons.forEach(memberDao::updatePerson); diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/FullMemberIdCache.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/FullMemberIdCache.java index e32681a90..1c2d8d5d8 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/FullMemberIdCache.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/FullMemberIdCache.java @@ -130,7 +130,7 @@ public List<SessionMember> getAllMembers(SortOrder sortOrder, LimitOffset limOff */ public List<FullMember> getAllFullMembers() { return getAllMembers(SortOrder.ASC, LimitOffset.ALL).stream() - .collect(Collectors.groupingBy(SessionMember::getMemberId, LinkedHashMap::new, Collectors.toList())) + .collect(Collectors.groupingBy(sm -> sm.getMember().getMemberId(), LinkedHashMap::new, Collectors.toList())) .values().stream() .map(FullMember::new) .collect(Collectors.toList()); diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java index f34001df6..24d2c3610 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java @@ -153,7 +153,7 @@ private SimpleKey genCacheKey(SessionMember sessionMember) { return genCacheKey( sessionMember.getLbdcShortName(), sessionMember.getSessionYear(), - sessionMember.getChamber() + sessionMember.getMember().getChamber() ); } diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java index 577cd3db3..48dc0c4c4 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java @@ -174,7 +174,7 @@ public void handleBulkMemberUpdate(BulkMemberUpdateEvent bulkMemberUpdateEvent) /* --- Internal Methods --- */ private void updateSessionMember(SessionMember sessionMember) { - FullMember member = memberDataService.getMemberById(sessionMember.getMemberId()); + FullMember member = memberDataService.getMemberById(sessionMember.getMember().getMemberId()); updateIndex(member); } diff --git a/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java b/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java index 9d47263b7..d77efa65d 100644 --- a/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java +++ b/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java @@ -71,9 +71,9 @@ public void unverifiedMemberNotification(SessionMember member) { LocalDateTime occurred = LocalDateTime.now(); String summary = "New unverified session member: " + member.getLbdcShortName(); String message = "A new unverified session member has been created" + (run.isPresent() ? " during data processing" : "") + "\n" + - (run.isPresent() ? getDataProcessRunUrl(run.get().getProcessId()) + "\n" : "") + + (run.map(dataProcessRun -> getDataProcessRunUrl(dataProcessRun.getProcessId()) + "\n").orElse("")) + "shortname: " + member.getLbdcShortName() + "\n" + - "chamber: " + member.getChamber()+ "\n" + + "chamber: " + member.getMember().getChamber()+ "\n" + "session: " + member.getSessionYear(); Notification notification = new Notification(UNVERIFIED_MEMBER, occurred, summary, message); eventBus.post(notification); diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java index 9089d9a3d..5fd04db14 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java @@ -179,7 +179,7 @@ public String getPrimaryShortname(SessionYear sessionYear, Chamber chamber, Stri } try { SessionMember member = memberService.getMemberByShortName(shortname, sessionYear, chamber); - return getPrimaryShortname(sessionYear, member.getMemberId()); + return getPrimaryShortname(sessionYear, member.getMember().getMemberId()); } catch (MemberNotFoundEx ex) { return "<unknown shortname: " + sessionYear + " " + chamber + " " + shortname + ">"; } diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java index 133d6cb8f..741278f37 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java @@ -161,7 +161,7 @@ private Set<BillScrapeVote> createOpenlegVotes(Bill bill) { LocalDate voteDate = vote.getVoteDate(); for (BillVoteCode code : vote.getMemberVotes().keySet()) { for (SessionMember member : vote.getMembersByVote(code)) { - voteMultiList.put(code, member.getLastName()); + voteMultiList.put(code, member.getMember().getLastName()); } } BillScrapeVote v = new BillScrapeVote(voteDate, voteMultiList); diff --git a/src/main/java/gov/nysenate/openleg/util/MemberScraperUtils.java b/src/main/java/gov/nysenate/openleg/util/MemberScraperUtils.java index 0ba54f5f7..59bf8318b 100644 --- a/src/main/java/gov/nysenate/openleg/util/MemberScraperUtils.java +++ b/src/main/java/gov/nysenate/openleg/util/MemberScraperUtils.java @@ -2,9 +2,11 @@ import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.Chamber; +import gov.nysenate.openleg.model.entity.Member; import gov.nysenate.openleg.model.entity.SessionMember; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +37,7 @@ public static List<SessionMember> getAssemblyMembers() throws IOException { Elements districtElements = listingPage.select(".email2"); Elements picElements = directoryPage.select(".mem-pic a img"); Elements fullNameElements = directoryPage.select(".leader-info strong a"); - List<String> fullNames = fullNameElements.stream().map(e -> e.text()).collect(toList()); + List<String> fullNames = fullNameElements.stream().map(Element::text).collect(toList()); Pattern districtPattern = Pattern.compile("(\\d+)\\w+"); List<Integer> districts = districtElements.stream().map(e -> { Matcher m = districtPattern.matcher(e.text()); @@ -44,19 +46,23 @@ public static List<SessionMember> getAssemblyMembers() throws IOException { }).collect(toList()); List<String> lastNames = csvNameElements.stream().map(e -> e.text().split(",")[0]).collect(toList()); List<String> imageNames = picElements.stream().map(i -> i.attr("src")).collect(toList()); - List<SessionMember> members = new ArrayList<>(); + List<SessionMember> sessionMembers = new ArrayList<>(); for (int i = 0; i < lastNames.size(); i++) { - SessionMember m = new SessionMember(); + Member m = new Member(); m.setLastName(lastNames.get(i)); m.setFullName(fullNames.get(i)); m.setImgName(imageNames.get(i)); - m.setDistrictCode(districts.get(i)); - m.setSessionYear(SessionYear.current()); m.setChamber(Chamber.ASSEMBLY); - members.add(m); + + SessionMember sm = new SessionMember(); + sm.setDistrictCode(districts.get(i)); + sm.setSessionYear(SessionYear.current()); + sm.setMember(m); + + sessionMembers.add(sm); } - return members; + return sessionMembers; } public static List<SessionMember> getSenateMembers() throws IOException { @@ -67,7 +73,7 @@ public static List<SessionMember> getSenateMembers() throws IOException { Elements districtElems = nodes.select(".views-field-field-senators-district-nid span"); List<String> imageUrls = imageElems.stream() .map(i -> i.attr("src")).collect(toList()); - List<String> names = nameElems.stream().map(n -> n.text()).collect(toList()); + List<String> names = nameElems.stream().map(Element::text).collect(toList()); Pattern districtPattern = Pattern.compile("District (\\d+)"); List<Integer> districts = districtElems.stream().map(d -> { Matcher m = districtPattern.matcher(d.text()); @@ -76,15 +82,19 @@ public static List<SessionMember> getSenateMembers() throws IOException { }).collect(toList()); List<SessionMember> senators = new ArrayList<>(); for (int i = 0; i < names.size(); i++) { - SessionMember m = new SessionMember(); String[] splitName = names.get(i).split(","); + Member m = new Member(); m.setLastName(splitName[0]); m.setFullName(splitName[1] + " " + splitName[0]); m.setImgName(imageUrls.get(i)); - m.setDistrictCode(districts.get(i)); - m.setSessionYear(SessionYear.current()); m.setChamber(Chamber.SENATE); - senators.add(m); + + SessionMember sm = new SessionMember(); + sm.setDistrictCode(districts.get(i)); + sm.setSessionYear(SessionYear.current()); + sm.setMember(m); + + senators.add(sm); } return senators; } diff --git a/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java b/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java index 32dd16c63..d566a339c 100644 --- a/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java +++ b/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java @@ -33,9 +33,9 @@ public class TestCommittees { private CommitteeMember committeeMemberFromTriple(Object[] triple){ try { CommitteeMember cm = new CommitteeMember(); - SessionMember m = memberService.getMemberById((int)triple[1], new SessionYear((int)triple[2])); - cm.setMember(m); - cm.setMajority(m.getMemberId()%2==0); + SessionMember sm = memberService.getMemberById((int)triple[1], new SessionYear((int)triple[2])); + cm.setSessionMember(sm); + cm.setMajority(sm.getMember().getMemberId()%2==0); cm.setTitle(CommitteeMemberTitle.MEMBER); return cm; } catch (MemberNotFoundEx memberNotFoundEx) { diff --git a/src/test/java/gov/nysenate/openleg/processor/entry/XmlSenCommProcessorIT.java b/src/test/java/gov/nysenate/openleg/processor/entry/XmlSenCommProcessorIT.java index c26c8e98a..a30abc473 100644 --- a/src/test/java/gov/nysenate/openleg/processor/entry/XmlSenCommProcessorIT.java +++ b/src/test/java/gov/nysenate/openleg/processor/entry/XmlSenCommProcessorIT.java @@ -46,19 +46,24 @@ public void processCommittee() throws ParseError { expected.setMeetTime(LocalTime.of(9, 0)); expected.setLocation("Room 412 LOB"); expected.setSession(SessionYear.of(2017)); - CommitteeMember committeeMember = new CommitteeMember(); + + Member member = new Member(); + member.setIncumbent(true); + member.setPersonId(1237); + member.setFullName("RITCHIE"); + member.setMemberId(1415); + member.setVerified(false); + SessionMember sessionMember = new SessionMember(); + sessionMember.setMember(member); sessionMember.setAlternate(false); - sessionMember.setIncumbent(true); sessionMember.setDistrictCode(0); sessionMember.setLbdcShortName("RITCHIE"); sessionMember.setSessionMemberId(1413); sessionMember.setSessionYear(SessionYear.of(2017)); - sessionMember.setPersonId(1237); - sessionMember.setFullName("RITCHIE"); - sessionMember.setMemberId(1415); - sessionMember.setVerified(false); - committeeMember.setMember(sessionMember); + + CommitteeMember committeeMember = new CommitteeMember(); + committeeMember.setSessionMember(sessionMember); committeeMember.setMajority(true); committeeMember.setTitle(CommitteeMemberTitle.CHAIR_PERSON); expected.addMember(committeeMember); From 683a83cf5f7f7e02458d47ace73d8c82cf937bc9 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Mon, 16 Mar 2020 11:51:59 -0400 Subject: [PATCH 19/29] Some cleanup. --- .../api/admin/MemberManageCtrl.java | 2 - .../controller/api/entity/MemberGetCtrl.java | 1 - .../controller/ui/BaseContentPageCtrl.java | 5 +- .../dao/bill/data/SqlBillUpdatesDao.java | 35 ++++++----- .../entity/member/data/SqlMemberQuery.java | 2 +- .../spotcheck/SqlSpotCheckReportQuery.java | 1 - .../openleg/model/entity/CommitteeMember.java | 2 +- .../openleg/model/entity/MemberId.java | 63 ------------------- .../nysenate/openleg/model/entity/Person.java | 2 +- .../openleg/model/entity/SessionMember.java | 4 +- .../openleg/script/ImportCommittees.java | 4 +- .../openleg/script/MemberScraperCLI.java | 4 +- .../member/data/CachedMemberService.java | 12 ++-- .../data/SessionChamberShortNameCache.java | 7 +-- .../search/ElasticMemberSearchService.java | 1 - .../DataProcessNotificationService.java | 10 +-- .../spotcheck/base/SpotCheckUtils.java | 4 +- .../scrape/BillScrapeCheckService.java | 6 +- .../dao/entity/committee/TestCommittees.java | 2 +- 19 files changed, 47 insertions(+), 120 deletions(-) delete mode 100644 src/main/java/gov/nysenate/openleg/model/entity/MemberId.java diff --git a/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java index b2cba3d24..cc8bf330f 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/admin/MemberManageCtrl.java @@ -55,6 +55,4 @@ public BaseResponse updateMembers(@RequestBody MemberViewList memberViewList) { .collect(Collectors.toList())); return new SimpleResponse(true, "members updated", "member-update-success"); } - - } diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java index 238974b82..e2144da8d 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java @@ -7,7 +7,6 @@ import gov.nysenate.openleg.client.response.error.ErrorResponse; import gov.nysenate.openleg.client.view.base.ViewObject; import gov.nysenate.openleg.client.view.entity.FullMemberView; -import gov.nysenate.openleg.client.view.entity.MemberView; import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.base.BaseCtrl; import gov.nysenate.openleg.dao.base.LimitOffset; diff --git a/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java b/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java index d26350b84..c972d611c 100644 --- a/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java @@ -6,7 +6,6 @@ import gov.nysenate.openleg.dao.base.SortOrder; import gov.nysenate.openleg.model.entity.Chamber; import gov.nysenate.openleg.model.entity.Member; -import gov.nysenate.openleg.model.entity.SessionMember; import gov.nysenate.openleg.service.entity.member.data.MemberService; import gov.nysenate.openleg.service.entity.member.event.BulkMemberUpdateEvent; import gov.nysenate.openleg.service.entity.member.event.MemberUpdateEvent; @@ -54,7 +53,7 @@ protected void addContentAttributesToRequest(HttpServletRequest request) { */ private void initializeMembers() { List<Member> allMembers = memberData.getAllMembers(SortOrder.ASC, LimitOffset.ALL).stream() - .map(m -> new Member(m.getMember())) + .map(sm -> new Member(sm.getMember())) .distinct() .collect(Collectors.toList()); senatorsList = allMembers.stream() @@ -74,4 +73,4 @@ protected void handleMemberUpdate(MemberUpdateEvent event) { protected void handleMemberUpdate(BulkMemberUpdateEvent event) { initializeMembers(); } -} \ No newline at end of file +} diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java index aa1c2f014..98eb089fe 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillUpdatesDao.java @@ -24,6 +24,7 @@ import static gov.nysenate.openleg.dao.bill.data.SqlBillUpdatesQuery.SELECT_BILL_UPDATE_TOKENS; import static gov.nysenate.openleg.dao.bill.data.SqlBillUpdatesQuery.SELECT_UPDATE_DIGESTS_FOR_SPECIFIC_BILL; import static gov.nysenate.openleg.model.bill.BillUpdateField.*; +import static gov.nysenate.openleg.dao.base.SqlTable.*; @Repository public class SqlBillUpdatesDao extends SqlBaseDao implements BillUpdatesDao @@ -46,24 +47,24 @@ public BillUpdateTable(SqlTable table, String... columns) { private final static Map<BillUpdateField, BillUpdateTable> updateMappings = new HashMap<>(); static { - updateMappings.put(PUBLISHED_BILL, new BillUpdateTable(SqlTable.BILL, "published_date_time")); - updateMappings.put(ACT_CLAUSE, new BillUpdateTable(SqlTable.BILL_AMENDMENT, "act_clause")); - updateMappings.put(ACTION, new BillUpdateTable(SqlTable.BILL_AMENDMENT_ACTION)); - updateMappings.put(ACTIVE_VERSION, new BillUpdateTable(SqlTable.BILL, "active_version")); - updateMappings.put(APPROVAL, new BillUpdateTable(SqlTable.BILL_APPROVAL)); - updateMappings.put(COSPONSOR, new BillUpdateTable(SqlTable.BILL_AMENDMENT_COSPONSOR)); - updateMappings.put(FULLTEXT, new BillUpdateTable(SqlTable.BILL_AMENDMENT, "full_text")); - updateMappings.put(LAW, new BillUpdateTable(SqlTable.BILL_AMENDMENT, "law_code", "law_section")); - updateMappings.put(MEMO, new BillUpdateTable(SqlTable.BILL_AMENDMENT, "sponsor_memo")); - updateMappings.put(MULTISPONSOR, new BillUpdateTable(SqlTable.BILL_AMENDMENT_MULTISPONSOR)); - updateMappings.put(SPONSOR, new BillUpdateTable(SqlTable.BILL_SPONSOR)); - updateMappings.put(STATUS, new BillUpdateTable(SqlTable.BILL, "status", "status_date", "bill_cal_no", + updateMappings.put(PUBLISHED_BILL, new BillUpdateTable(BILL, "published_date_time")); + updateMappings.put(ACT_CLAUSE, new BillUpdateTable(BILL_AMENDMENT, "act_clause")); + updateMappings.put(ACTION, new BillUpdateTable(BILL_AMENDMENT_ACTION)); + updateMappings.put(ACTIVE_VERSION, new BillUpdateTable(BILL, "active_version")); + updateMappings.put(APPROVAL, new BillUpdateTable(BILL_APPROVAL)); + updateMappings.put(COSPONSOR, new BillUpdateTable(BILL_AMENDMENT_COSPONSOR)); + updateMappings.put(FULLTEXT, new BillUpdateTable(BILL_AMENDMENT, "full_text")); + updateMappings.put(LAW, new BillUpdateTable(BILL_AMENDMENT, "law_code", "law_section")); + updateMappings.put(MEMO, new BillUpdateTable(BILL_AMENDMENT, "sponsor_memo")); + updateMappings.put(MULTISPONSOR, new BillUpdateTable(BILL_AMENDMENT_MULTISPONSOR)); + updateMappings.put(SPONSOR, new BillUpdateTable(BILL_SPONSOR)); + updateMappings.put(STATUS, new BillUpdateTable(BILL, "status", "status_date", "bill_cal_no", "committee_name", "committee_chamber")); - updateMappings.put(STATUS_CODE, new BillUpdateTable(SqlTable.BILL, "status")); - updateMappings.put(SUMMARY, new BillUpdateTable(SqlTable.BILL, "summary")); - updateMappings.put(TITLE, new BillUpdateTable(SqlTable.BILL, "title")); - updateMappings.put(VETO, new BillUpdateTable(SqlTable.BILL_VETO)); - updateMappings.put(VOTE, new BillUpdateTable(SqlTable.BILL_AMENDMENT_VOTE_INFO)); + updateMappings.put(STATUS_CODE, new BillUpdateTable(BILL, "status")); + updateMappings.put(SUMMARY, new BillUpdateTable(BILL, "summary")); + updateMappings.put(TITLE, new BillUpdateTable(BILL, "title")); + updateMappings.put(VETO, new BillUpdateTable(BILL_VETO)); + updateMappings.put(VOTE, new BillUpdateTable(BILL_AMENDMENT_VOTE_INFO)); } /** {@inheritDoc} */ diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java index a399f7470..4499dc597 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java @@ -136,4 +136,4 @@ public enum SqlMemberQuery implements BasicSqlQuery public String getSql() { return this.sql; } -} \ No newline at end of file +} diff --git a/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java b/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java index cf64d4426..83b082f72 100644 --- a/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/spotcheck/SqlSpotCheckReportQuery.java @@ -156,5 +156,4 @@ public enum SqlSpotCheckReportQuery implements BasicSqlQuery public String getSql() { return this.sql; } - } diff --git a/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java b/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java index b2a04ec23..5ed73eae8 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/CommitteeMember.java @@ -65,7 +65,7 @@ public CommitteeMember(CommitteeMember other) { /** --- Functional Getters/Setters --- */ public static Comparator<CommitteeMember> getComparator() { - return (l,r) -> l.compareTo(r); + return CommitteeMember::compareTo; } /** --- Basic Getters/Setters --- */ diff --git a/src/main/java/gov/nysenate/openleg/model/entity/MemberId.java b/src/main/java/gov/nysenate/openleg/model/entity/MemberId.java deleted file mode 100644 index 30022a418..000000000 --- a/src/main/java/gov/nysenate/openleg/model/entity/MemberId.java +++ /dev/null @@ -1,63 +0,0 @@ -package gov.nysenate.openleg.model.entity; - -import java.io.Serializable; -import java.util.Objects; - -public class MemberId implements Serializable -{ - private static final long serialVersionUID = -3479872987089824973L; - - /** Id generated by the persistence layer which can uniquely identify a person within a - * specific legislative chamber. */ - private int id; - - /** Session year that this member is being referenced. */ - private int sessionYear; - - /** Short name that the source data uses. There may be multiple short names for a given member. */ - private String lbdcShortName; - - /** --- Constructors --- */ - - public MemberId (int id, int sessionYear, String lbdcShortName) { - this.id = id; - this.sessionYear = sessionYear; - this.lbdcShortName = lbdcShortName; - } - - /** --- Overrides --- */ - - @Override - public String toString () { - return "member #" + id + " lbdc (" + lbdcShortName + ") session (" + sessionYear + ")"; - } - - @Override - public boolean equals (Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - final MemberId other = (MemberId) obj; - return Objects.equals(this.id, other.id) && - Objects.equals(this.sessionYear, other.sessionYear) && - Objects.equals(this.lbdcShortName, other.lbdcShortName); - } - - @Override - public int hashCode () { - return Objects.hash(id, sessionYear, lbdcShortName); - } - - /** --- Basic Getters/Setters --- */ - - public int getId () { - return id; - } - - public int getSessionYear () { - return sessionYear; - } - - public String getLbdcShortName () { - return lbdcShortName; - } -} \ No newline at end of file diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Person.java b/src/main/java/gov/nysenate/openleg/model/entity/Person.java index d06b4a25b..1880b1e12 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Person.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Person.java @@ -208,4 +208,4 @@ public boolean isVerified() { public void setVerified(boolean verified) { this.verified = verified; } -} \ No newline at end of file +} diff --git a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java index d998e2471..bfcd695db 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java @@ -122,7 +122,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return 31 * super.hashCode() + Objects.hash(member.memberId, sessionYear, districtCode); + return Objects.hash(member, sessionYear, districtCode); } @Override @@ -189,4 +189,4 @@ public boolean isAlternate() { public void setAlternate(boolean alternate) { this.alternate = alternate; } -} \ No newline at end of file +} diff --git a/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java b/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java index fcadbc12e..9e6790a79 100644 --- a/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java +++ b/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java @@ -59,7 +59,7 @@ protected void execute(CommandLine opts) throws Exception { File committeeDir = new File(opts.getOptionValue("committeeDir")); if (committeeDir.isDirectory()) { List<File> yearDirs = Arrays.asList(committeeDir.listFiles(intFileNameFilter)); - yearDirs.sort((File f1, File f2 ) -> ((Integer) Integer.parseInt(f1.getName())).compareTo(Integer.parseInt(f2.getName()))); + yearDirs.sort(Comparator.comparingInt((File f) -> Integer.parseInt(f.getName()))); for (File yearDir : committeeDir.listFiles(intFileNameFilter)) { int year = Integer.parseInt(yearDir.getName()); if (yearDir.isDirectory()) { @@ -144,6 +144,4 @@ private CommitteeMember getCommitteeMemberFromJson(JsonNode memberNode, int year committeeMember.setSessionMember(member); return committeeMember; } - - } diff --git a/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java b/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java index 0c5617754..4eee94a9e 100644 --- a/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java +++ b/src/main/java/gov/nysenate/openleg/script/MemberScraperCLI.java @@ -23,9 +23,9 @@ public class MemberScraperCLI extends BaseScript @Override protected void execute(CommandLine opts) throws Exception { List<SessionMember> assemblyMembers = MemberScraperUtils.getAssemblyMembers(); - assemblyMembers.stream().forEach(m -> { + assemblyMembers.stream().forEach(sm -> { try { - InputStream in = new UrlResource(m.getMember().getImgName()).getInputStream(); + InputStream in = new UrlResource(sm.getMember().getImgName()).getInputStream(); FileIOUtils.writeToFile(in, "/tmp/assembly/" + RandomUtils.getRandomString(10) + ".jpg"); } catch (IOException e) { logger.error("Failed to ", e); diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java index 63b3dd56b..31325b191 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java @@ -96,12 +96,12 @@ public SessionMember getMemberByShortNameEnsured(String lbdcShortName, SessionYe return getMemberByShortName(lbdcShortName, sessionYear, chamber); } catch (MemberNotFoundEx ex) { - SessionMember member = SessionMember.newMakeshiftMember(lbdcShortName, sessionYear, chamber); - memberDao.updatePerson(member.getMember()); - memberDao.updateMember(member.getMember()); - memberDao.updateSessionMember(member); - eventBus.post(new UnverifiedMemberEvent(member, LocalDateTime.now())); - return member; + SessionMember sessionMember = SessionMember.newMakeshiftMember(lbdcShortName, sessionYear, chamber); + memberDao.updatePerson(sessionMember.getMember()); + memberDao.updateMember(sessionMember.getMember()); + memberDao.updateSessionMember(sessionMember); + eventBus.post(new UnverifiedMemberEvent(sessionMember, LocalDateTime.now())); + return sessionMember; } } diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java index 24d2c3610..4b5b21f3b 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/SessionChamberShortNameCache.java @@ -30,10 +30,7 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Optional; +import java.util.*; @Component public class SessionChamberShortNameCache implements CachingService<String> { @@ -79,7 +76,7 @@ public void setupCaches() { @Override public List<Ehcache> getCaches() { - return Arrays.asList(memberCache); + return Collections.singletonList(memberCache); } @Override diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java index 48dc0c4c4..5fb74082c 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java @@ -188,4 +188,3 @@ private QueryBuilder requireSessionYear(SessionYear sessionYear) { return QueryBuilders.existsQuery("sessionShortNameMap." + sessionYear.getYear()); } } - diff --git a/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java b/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java index d77efa65d..64f3b7067 100644 --- a/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java +++ b/src/main/java/gov/nysenate/openleg/service/process/DataProcessNotificationService.java @@ -66,15 +66,15 @@ public void warningNotification(String warning, int dataProcessId, DataProcessUn eventBus.post(notification); } - public void unverifiedMemberNotification(SessionMember member) { + public void unverifiedMemberNotification(SessionMember sessionMember) { Optional<DataProcessRun> run = dataProcessor.getCurrentRun(); LocalDateTime occurred = LocalDateTime.now(); - String summary = "New unverified session member: " + member.getLbdcShortName(); + String summary = "New unverified session member: " + sessionMember.getLbdcShortName(); String message = "A new unverified session member has been created" + (run.isPresent() ? " during data processing" : "") + "\n" + (run.map(dataProcessRun -> getDataProcessRunUrl(dataProcessRun.getProcessId()) + "\n").orElse("")) + - "shortname: " + member.getLbdcShortName() + "\n" + - "chamber: " + member.getMember().getChamber()+ "\n" + - "session: " + member.getSessionYear(); + "shortname: " + sessionMember.getLbdcShortName() + "\n" + + "chamber: " + sessionMember.getMember().getChamber()+ "\n" + + "session: " + sessionMember.getSessionYear(); Notification notification = new Notification(UNVERIFIED_MEMBER, occurred, summary, message); eventBus.post(notification); } diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java index 5fd04db14..805da47a4 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java @@ -178,8 +178,8 @@ public String getPrimaryShortname(SessionYear sessionYear, Chamber chamber, Stri return null; } try { - SessionMember member = memberService.getMemberByShortName(shortname, sessionYear, chamber); - return getPrimaryShortname(sessionYear, member.getMember().getMemberId()); + SessionMember sessionMember = memberService.getMemberByShortName(shortname, sessionYear, chamber); + return getPrimaryShortname(sessionYear, sessionMember.getMember().getMemberId()); } catch (MemberNotFoundEx ex) { return "<unknown shortname: " + sessionYear + " " + chamber + " " + shortname + ">"; } diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java index 741278f37..2bac43c3d 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java @@ -44,7 +44,7 @@ public SpotCheckObservation<BaseBillId> check(Bill bill, BillScrapeReference ref final SpotCheckObservation<BaseBillId> observation = new SpotCheckObservation<>(reference.getReferenceId(), bill.getBaseBillId()); - //Add mismatches to observation + // Add mismatches to observation // If not found on LRS and not published in openleg, don't create mismatch. LRS removes bills when unpublished. Optional<PublishStatus> publishStatus = bill.getPublishStatus(bill.getActiveVersion()); @@ -160,8 +160,8 @@ private Set<BillScrapeVote> createOpenlegVotes(Bill bill) { SortedSetMultimap<BillVoteCode, String> voteMultiList = TreeMultimap.create(); LocalDate voteDate = vote.getVoteDate(); for (BillVoteCode code : vote.getMemberVotes().keySet()) { - for (SessionMember member : vote.getMembersByVote(code)) { - voteMultiList.put(code, member.getMember().getLastName()); + for (SessionMember sessionMember : vote.getMembersByVote(code)) { + voteMultiList.put(code, sessionMember.getMember().getLastName()); } } BillScrapeVote v = new BillScrapeVote(voteDate, voteMultiList); diff --git a/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java b/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java index d566a339c..28f5a9057 100644 --- a/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java +++ b/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java @@ -110,7 +110,7 @@ public Committee getCommittee(String name){ return createdCommittees.get(name); } public List<Committee> getCommittees(){ - return new ArrayList<Committee>(createdCommittees.values()); + return new ArrayList<>(createdCommittees.values()); } public void putCommittee(String name, Committee committee){ createdCommittees.put(name, committee); From a8d805568f2c878f67c624f54071bc14140e7195 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Tue, 17 Mar 2020 19:24:44 -0400 Subject: [PATCH 20/29] Added testing to make sure changes didn't break anything. --- docs/api/members.rst | 46 +++++++++++---- .../controller/api/entity/MemberGetCtrl.java | 12 ++-- .../api/entity/MemberSearchCtrl.java | 2 +- .../controller/api/law/LawGetCtrl.java | 2 +- .../entity/member/data/SqlMemberQuery.java | 2 +- .../member/data/CachedMemberService.java | 4 +- .../entity/member/data/MemberService.java | 4 +- .../search/ElasticMemberSearchService.java | 2 +- .../spotcheck/base/SpotCheckUtils.java | 2 +- .../bill/SenateSiteBillCheckService.java | 2 +- .../openleg/controller/api/ApiTest.java | 21 +++++++ .../api/entity/MemberGetCtrlTest.java | 57 +++++++++++++++++++ .../dao/entity/committee/TestCommittees.java | 2 +- .../entity/member/SqlMemberServiceTest.java | 12 ++-- .../nysenate/openleg/stupid/BillTextTest.java | 7 +-- 15 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java create mode 100644 src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java diff --git a/docs/api/members.rst b/docs/api/members.rst index dadf75f46..499e19876 100644 --- a/docs/api/members.rst +++ b/docs/api/members.rst @@ -29,18 +29,39 @@ Get member with id 371 during 2013 session year. .. code-block:: javascript { - "success" : true, - "message" : "", - "responseType" : "member", - "result" : { - "memberId" : 371, // The member id. - "shortName" : "SEWARD", // The members short name, uniquely identifies each member during a session. - "sessionYear" : 2013, // The session year. - "chamber" : "SENATE", // The chamber - "fullName" : "James L. Seward", // Members full name. - "districtCode" : 51 // Members district. - } + "success" : true, + "message" : "", + "responseType" : "member-sessions", + "result" : { + "memberId" : 371, + "chamber" : "SENATE", + "incumbent" : true, + "fullName" : "James L. Seward", + "shortName" : "SEWARD", + "sessionShortNameMap" : { + "2013" : [ { + "sessionMemberId" : 127, + "shortName" : "SEWARD", + "sessionYear" : 2013, + "districtCode" : 51, + "alternate" : false, + "memberId" : 371 + } ] + }, + "person" : { + "personId" : 190, + "fullName" : "James L. Seward", + "firstName" : "James", + "middleName" : "L.", + "lastName" : "Seward", + "email" : "seward@senate.state.ny.us", + "prefix" : "Senator", + "suffix" : null, + "verified" : true, + "imgName" : "371_james_l._seward.jpg" } + } +} Get a list of members @@ -95,6 +116,9 @@ Search within a session year :: (GET) /api/3/members/{sessionYear}/search?term=YOUR_TERM +Note: given a sessionMemberId = #### in a session year yyyy, you can get the member that sessionMemberId is used by with: +:: + (GET) /api/3/members/search?term=sessionShortNameMap.yyyy.sessionMemberId=#### **Required Params** diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java index e2144da8d..09175a59a 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java @@ -52,7 +52,7 @@ public MemberGetCtrl(MemberService memberData, MemberSearchService memberSearch) * offset - Start results from an offset. */ @RequestMapping(value = "") - public BaseResponse getMembersByYear(@RequestParam(defaultValue = "shortName:asc") String sort, + public BaseResponse getAllMembers(@RequestParam(defaultValue = "shortName:asc") String sort, @RequestParam(defaultValue = "false") boolean full, WebRequest request) throws SearchException, MemberNotFoundEx { LimitOffset limOff = getLimitOffset(request, 50); @@ -88,13 +88,13 @@ public BaseResponse getMembersByYear(@PathVariable int sessionYear, * Request Parameters : full - If true, the full member view will be returned. */ @RequestMapping(value = "/{sessionYear:\\d{4}}/{memberId:\\d+}") - public BaseResponse getMembersByYear(@PathVariable int memberId, + public BaseResponse getMembersByYearAndId(@PathVariable int memberId, @PathVariable int sessionYear, @RequestParam(defaultValue = "true") boolean full, WebRequest request) throws MemberNotFoundEx { return new ViewObjectResponse<>( - (full) ? new FullMemberView(memberData.getMemberById(memberId, SessionYear.of(sessionYear))) - : new SessionMemberView(memberData.getMemberById(memberId, SessionYear.of(sessionYear))) + (full) ? new FullMemberView(memberData.getFullMemberById(memberId)) + : new SessionMemberView(memberData.getSessionMemberById(memberId, SessionYear.of(sessionYear))) ); } @@ -109,7 +109,7 @@ public BaseResponse getMembersByYear(@PathVariable int memberId, * offset - Start results from an offset. */ @RequestMapping(value = "/{sessionYear}/{chamber:\\D+}") - public BaseResponse getMembersByYear(@PathVariable int sessionYear, + public BaseResponse getMembersByYearAndChamber(@PathVariable int sessionYear, @PathVariable String chamber, @RequestParam(defaultValue = "shortName:asc") String sort, @RequestParam(defaultValue = "false") boolean full, @@ -123,7 +123,7 @@ public BaseResponse getMembersByYear(@PathVariable int sessionYear, private BaseResponse getMemberResponse(boolean full, LimitOffset limOff, SearchResults<Integer> results) throws MemberNotFoundEx { List<ViewObject> memberList = results.getRawResults().stream() - .map(memberData::getMemberById) + .map(memberData::getFullMemberById) .map(member -> full ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().get())) .collect(Collectors.toList()); return ListViewResponse.of(memberList, results.getTotalResults(), limOff); diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java index d3c801c37..c6e6af68a 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java @@ -80,7 +80,7 @@ private BaseResponse getSearchResponse(SearchResults<Integer> results, boolean f for (SearchResult<Integer> result : results.getResults()) { FullMember member; try { - member = memberData.getMemberById(result.getResult()); + member = memberData.getFullMemberById(result.getResult()); } catch (MemberNotFoundEx ex) { throw new SearchException("No Member found.", ex); } diff --git a/src/main/java/gov/nysenate/openleg/controller/api/law/LawGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/law/LawGetCtrl.java index 0bc1d9c68..f190b205c 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/law/LawGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/law/LawGetCtrl.java @@ -57,7 +57,7 @@ public BaseResponse getLaws(WebRequest webRequest) { LimitOffset limOff = getLimitOffset(webRequest, 0); List<LawInfo> lawInfoList = lawDataService.getLawInfos(); ListViewResponse<LawInfoView> response = ListViewResponse.of( - LimitOffset.limitList(lawInfoList.stream().map(li -> new LawInfoView(li)).collect(toList()), limOff), + LimitOffset.limitList(lawInfoList.stream().map(LawInfoView::new).collect(toList()), limOff), lawInfoList.size(), limOff); response.setMessage("Listing of consolidated and unconsolidated NYS Laws"); return response; diff --git a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java index 4499dc597..1290e333c 100644 --- a/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/entity/member/data/SqlMemberQuery.java @@ -21,7 +21,7 @@ public enum SqlMemberQuery implements BasicSqlQuery SELECT_MEMBER_SELECT_FRAGMENT.sql + "\n" + SELECT_MEMBER_TABLE_FRAGMENT.sql ), SELECT_MEMBER_BY_ID_SQL( - SELECT_MEMBER_FRAGMENT.sql + " WHERE sm.member_id = :memberId AND sm.alternate = FALSE" + SELECT_MEMBER_FRAGMENT.sql + " WHERE sm.member_id = :memberId" ), SELECT_MEMBER_BY_ID_SESSION_SQL( SELECT_MEMBER_BY_ID_SQL.sql + " AND sm.session_year = :sessionYear AND sm.alternate = FALSE" diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java index 31325b191..1ff74cd5c 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java @@ -61,7 +61,7 @@ private void init() { /** {@inheritDoc} */ @Override - public SessionMember getMemberById(int memberId, SessionYear sessionYear) throws MemberNotFoundEx { + public SessionMember getSessionMemberById(int memberId, SessionYear sessionYear) throws MemberNotFoundEx { try { FullMember member = fullMemberIdCache.getMemberById(memberId); Optional<SessionMember> sessionMembOpt = member.getSessionMemberForYear(sessionYear); @@ -73,7 +73,7 @@ public SessionMember getMemberById(int memberId, SessionYear sessionYear) throws } @Override - public FullMember getMemberById(int memberId) throws MemberNotFoundEx { + public FullMember getFullMemberById(int memberId) throws MemberNotFoundEx { return fullMemberIdCache.getMemberById(memberId); } diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java index 61ebba8be..177f6d0e1 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java @@ -21,7 +21,7 @@ public interface MemberService * @return Member * @throws MemberNotFoundEx If no matching member was found. */ - public SessionMember getMemberById(int memberId, SessionYear sessionYear) throws MemberNotFoundEx; + public SessionMember getSessionMemberById(int memberId, SessionYear sessionYear) throws MemberNotFoundEx; /** * Retrieves map of session year -> Member for a given member id. @@ -29,7 +29,7 @@ public interface MemberService * @param id int * @return Map<Integer, Member> */ - public FullMember getMemberById(int id) throws MemberNotFoundEx; + public FullMember getFullMemberById(int id) throws MemberNotFoundEx; /** * Retrieve a member by session member id diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java index 5fb74082c..040157ddb 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/search/ElasticMemberSearchService.java @@ -174,7 +174,7 @@ public void handleBulkMemberUpdate(BulkMemberUpdateEvent bulkMemberUpdateEvent) /* --- Internal Methods --- */ private void updateSessionMember(SessionMember sessionMember) { - FullMember member = memberDataService.getMemberById(sessionMember.getMember().getMemberId()); + FullMember member = memberDataService.getFullMemberById(sessionMember.getMember().getMemberId()); updateIndex(member); } diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java index 805da47a4..65cd7fdc9 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java @@ -190,7 +190,7 @@ public String getPrimaryShortname(SessionYear sessionYear, Chamber chamber, Stri */ public String getPrimaryShortname(SessionYear sessionYear, int memberId) { try { - SessionMember sessionMember = memberService.getMemberById(memberId, sessionYear); + SessionMember sessionMember = memberService.getSessionMemberById(memberId, sessionYear); return sessionMember.getLbdcShortName(); } catch (MemberNotFoundEx ex) { return "<invalid session/memberId: " + sessionYear + "/" + memberId + ">"; diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java index 026c8ffcf..7cf2a5c9a 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java @@ -370,7 +370,7 @@ private String getVoteString(SenateSiteBillVote vote) { for (int memberId : vote.getVoteRoll().get(code)) { String shortName; try { - FullMember member = memberService.getMemberById(memberId); + FullMember member = memberService.getFullMemberById(memberId); shortName = member.getLatestSessionMember() .orElseThrow(MemberNotFoundEx::new) .getLbdcShortName(); diff --git a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java new file mode 100644 index 000000000..28ee89f38 --- /dev/null +++ b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java @@ -0,0 +1,21 @@ +package gov.nysenate.openleg.controller.api; + +import gov.nysenate.openleg.BaseTests; +import org.junit.Before; +import org.mockito.Mockito; +import org.springframework.web.context.request.WebRequest; + +public abstract class ApiTest extends BaseTests { + protected WebRequest testRequest = Mockito.mock(WebRequest.class); + + @Before + public void clearParams() { + testRequest = Mockito.mock(WebRequest.class); + Mockito.when(testRequest.getParameter(Mockito.anyString())).thenReturn(null); + } + + protected void addParam(String key, String value) { + Mockito.when(testRequest.getParameter(key)).thenReturn(value); + } +} + diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java new file mode 100644 index 000000000..1c3b74462 --- /dev/null +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -0,0 +1,57 @@ +package gov.nysenate.openleg.controller.api.entity; + +import gov.nysenate.openleg.annotation.UnitTest; +import gov.nysenate.openleg.client.response.base.BaseResponse; +import gov.nysenate.openleg.client.response.base.ListViewResponse; +import gov.nysenate.openleg.client.response.base.PaginationResponse; +import gov.nysenate.openleg.client.view.entity.FullMemberView; +import gov.nysenate.openleg.client.view.entity.SessionMemberView; +import gov.nysenate.openleg.controller.api.ApiTest; +import gov.nysenate.openleg.model.base.SessionYear; +import gov.nysenate.openleg.model.entity.FullMember; +import gov.nysenate.openleg.model.entity.MemberNotFoundEx; +import gov.nysenate.openleg.model.search.SearchException; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +@Category(UnitTest.class) +public class MemberGetCtrlTest extends ApiTest { + @Autowired + private MemberGetCtrl testCtrl; + + @Test + public void getAllMembersTest() throws SearchException, MemberNotFoundEx { + testCtrl.getAllMembers("shortName:asc", true, testRequest); + } + + @Test + public void getMembersByYearTest() throws SearchException, MemberNotFoundEx { + BaseResponse baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", false, testRequest); + ListViewResponse<?> paginationResponse = (ListViewResponse<?>) baseResponse; + assertEquals(paginationResponse.getTotal(), 224); + + long numAlternates = paginationResponse.getResult().getItems().stream().filter(sm -> + sm instanceof SessionMemberView && ((SessionMemberView) sm).isAlternate()).count(); + assertEquals(numAlternates, 0); + + addParam("limit", "all"); + baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", true, testRequest); + paginationResponse = (ListViewResponse<?>) baseResponse; + FullMemberView testFmv = (FullMemberView) paginationResponse.getResult().getItems().stream().filter(fm -> + fm instanceof FullMemberView && ((FullMemberView) fm).getMemberId() == 591).collect(Collectors.toList()).get(0); + assertEquals(2, testFmv.getSessionShortNameMap().get(2015).size()); + } + + @Test + public void getMembersByYearAndChamberTest() throws SearchException, MemberNotFoundEx { + BaseResponse baseResponse = testCtrl.getMembersByYearAndChamber(2017, "senate", "shortName:asc", false, testRequest); + assertEquals(((PaginationResponse) baseResponse).getTotal(), 67); + baseResponse = testCtrl.getMembersByYearAndChamber(2017, "assembly", "shortName:asc", false, testRequest); + assertEquals(((PaginationResponse) baseResponse).getTotal(), 163); + } +} diff --git a/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java b/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java index 28f5a9057..9ab2ceba8 100644 --- a/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java +++ b/src/test/java/gov/nysenate/openleg/dao/entity/committee/TestCommittees.java @@ -33,7 +33,7 @@ public class TestCommittees { private CommitteeMember committeeMemberFromTriple(Object[] triple){ try { CommitteeMember cm = new CommitteeMember(); - SessionMember sm = memberService.getMemberById((int)triple[1], new SessionYear((int)triple[2])); + SessionMember sm = memberService.getSessionMemberById((int)triple[1], new SessionYear((int)triple[2])); cm.setSessionMember(sm); cm.setMajority(sm.getMember().getMemberId()%2==0); cm.setTitle(CommitteeMemberTitle.MEMBER); diff --git a/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java b/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java index ffeaa5b99..082984e75 100644 --- a/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java +++ b/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java @@ -18,20 +18,20 @@ public class SqlMemberServiceTest extends BaseTests private static final Logger logger = LoggerFactory.getLogger(SqlMemberServiceTest.class); @Autowired - private MemberService sqlMemberService; + public MemberService sqlMemberService; @Test - public void testGetMemberByShortName_UsesCache() throws Exception { + public void testGetMemberByShortName_UsesCache() { logger.info(OutputUtils.toJson(sqlMemberService.getMemberBySessionId(667))); } @Test(expected = MemberNotFoundEx.class) - public void testGetMemberBySessionNegativeId() throws Exception { - logger.info(OutputUtils.toJson(sqlMemberService.getMemberById(-1, SessionYear.current()))); + public void testGetMemberBySessionNegativeId() { + logger.info(OutputUtils.toJson(sqlMemberService.getSessionMemberById(-1, SessionYear.current()))); } @Test(expected = MemberNotFoundEx.class) - public void testGetMemberByNegativeId() throws Exception { - logger.info(OutputUtils.toJson(sqlMemberService.getMemberById(-1))); + public void testGetMemberByNegativeId() { + logger.info(OutputUtils.toJson(sqlMemberService.getFullMemberById(-1))); } } diff --git a/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java b/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java index 2accebbcc..c2d87dcc6 100644 --- a/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java +++ b/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java @@ -33,10 +33,7 @@ import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -85,7 +82,7 @@ public void getReportTest() { @Test public void queueTest() { - List<BaseBillId> billIds = Arrays.asList( + List<BaseBillId> billIds = Collections.singletonList( new BaseBillId("S5513", 2015)); billIds.forEach(billId -> dao.addBillToScrapeQueue(billId, ScrapeQueuePriority.MANUAL_ENTRY.getPriority())); logger.info("queue is now {}", dao.getScrapeQueue(LimitOffset.ALL, SortOrder.DESC).getResults().stream() From 8568cd5a1fff2a9b9c78572eb4d01488a006089d Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Wed, 18 Mar 2020 13:31:03 -0400 Subject: [PATCH 21/29] Finished test for MemberGetCtrl. Renamed some methods to be more clear. --- .../client/view/entity/FullMemberView.java | 19 +++++ .../controller/api/entity/MemberGetCtrl.java | 2 +- .../controller/ui/BaseContentPageCtrl.java | 2 +- .../agenda/data/SqlAgendaVoteAddendumDao.java | 4 +- .../openleg/dao/bill/data/SqlBillDao.java | 10 +-- .../nysenate/openleg/model/entity/Member.java | 7 ++ .../nysenate/openleg/model/entity/Person.java | 14 ++++ .../openleg/model/entity/SessionMember.java | 23 ++++-- .../processor/base/AbstractDataProcessor.java | 2 +- .../processor/entity/XmlSenCommProcessor.java | 2 +- .../CalendarAlertSupplementalParser.java | 2 +- .../openleg/script/ImportCommittees.java | 2 +- .../member/data/CachedMemberService.java | 11 ++- .../entity/member/data/MemberService.java | 26 +++---- .../spotcheck/base/SpotCheckUtils.java | 2 +- .../api/entity/MemberGetCtrlTest.java | 70 +++++++++++++++++-- .../openleg/model/bill/BillVoteIT.java | 8 +-- .../bill/XmlSenAgenVoteProcessorIT.java | 4 +- .../entity/member/SqlMemberServiceTest.java | 2 +- 19 files changed, 161 insertions(+), 51 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java index 160ecd995..34c26f98d 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java @@ -42,6 +42,25 @@ public Map<Integer, List<SessionMemberView>> getSessionShortNameMap() { return sessionShortNameMap; } + public boolean exactEquals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FullMemberView that = (FullMemberView) o; + if (!Objects.equals(sessionShortNameMap.keySet(), that.sessionShortNameMap.keySet())) + return false; + for (Integer key : sessionShortNameMap.keySet()) { + List<SessionMemberView> thisSms = sessionShortNameMap.get(key); + List<SessionMemberView> thatSms = that.sessionShortNameMap.get(key); + if (thisSms.size() != thatSms.size()) + return false; + for (int i = 0; i < thisSms.size(); i++) { + if (!thisSms.get(i).toSessionMember().exactEquals(thatSms.get(i).toSessionMember())) + return false; + } + } + return true; + } + @Override public String getViewType() { return "member-sessions"; diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java index 09175a59a..5244adf36 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java @@ -108,7 +108,7 @@ public BaseResponse getMembersByYearAndId(@PathVariable int memberId, * limit - Limit the number of results * offset - Start results from an offset. */ - @RequestMapping(value = "/{sessionYear}/{chamber:\\D+}") + @RequestMapping(value = "/{sessionYear:\\d{4}}/{chamber:\\D+}") public BaseResponse getMembersByYearAndChamber(@PathVariable int sessionYear, @PathVariable String chamber, @RequestParam(defaultValue = "shortName:asc") String sort, diff --git a/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java b/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java index c972d611c..695e166db 100644 --- a/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/ui/BaseContentPageCtrl.java @@ -52,7 +52,7 @@ protected void addContentAttributesToRequest(HttpServletRequest request) { * static.. */ private void initializeMembers() { - List<Member> allMembers = memberData.getAllMembers(SortOrder.ASC, LimitOffset.ALL).stream() + List<Member> allMembers = memberData.getAllSessionMembers(SortOrder.ASC, LimitOffset.ALL).stream() .map(sm -> new Member(sm.getMember())) .distinct() .collect(Collectors.toList()); diff --git a/src/main/java/gov/nysenate/openleg/dao/agenda/data/SqlAgendaVoteAddendumDao.java b/src/main/java/gov/nysenate/openleg/dao/agenda/data/SqlAgendaVoteAddendumDao.java index 40cee1ee0..8fbbbc9b3 100644 --- a/src/main/java/gov/nysenate/openleg/dao/agenda/data/SqlAgendaVoteAddendumDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/agenda/data/SqlAgendaVoteAddendumDao.java @@ -90,7 +90,7 @@ voteCommParams, new AgendaVoteAttendanceRowMapper(memberService) // Set full session member information for (AgendaVoteAttendance va : voteAttendances) { try { - va.setMember(memberService.getMemberBySessionId(va.getMember().getSessionMemberId())); + va.setMember(memberService.getSessionMemberBySessionId(va.getMember().getSessionMemberId())); } catch (MemberNotFoundEx memberNotFoundEx) { logger.info("Failed to map member for attendance listing."); } @@ -106,7 +106,7 @@ private Map<BillId, AgendaVoteBill> queryAgendaVoteBills(ImmutableParams voteCom for (AgendaVoteBill agendaVoteBill : billVotes.values()) { for (SessionMember member: agendaVoteBill.getBillVote().getMemberVotes().values()) { try { - SessionMember fullMember = memberService.getMemberBySessionId(member.getSessionMemberId()); + SessionMember fullMember = memberService.getSessionMemberBySessionId(member.getSessionMemberId()); member.updateFromOther(fullMember); } catch (MemberNotFoundEx memberNotFoundEx) { logger.error("Failed to add member vote since member could not be found!", memberNotFoundEx); diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java index 302845ce0..b49bfff3d 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java @@ -304,7 +304,7 @@ public BillSponsor getBillSponsor(ImmutableParams baseParams) { int sessionMemberId = pair.getRight(); if (sessionMemberId > 0) { try { - sponsor.setMember(memberService.getMemberBySessionId(sessionMemberId)); + sponsor.setMember(memberService.getSessionMemberBySessionId(sessionMemberId)); } catch (MemberNotFoundEx memberNotFoundEx) { logger.warn("Bill referenced a sponsor that does not exist. {}", memberNotFoundEx.getMessage()); } @@ -326,7 +326,7 @@ public List<SessionMember> getAdditionalSponsors(ImmutableParams baseParams) { baseParams, Integer.class); for (int id : sessionMemberIds) { try { - sessionMembers.add(memberService.getMemberBySessionId(id)); + sessionMembers.add(memberService.getSessionMemberBySessionId(id)); } catch (MemberNotFoundEx memberNotFoundEx) { logger.warn("Bill referenced a member that does not exist: {}", memberNotFoundEx.getMessage()); } @@ -378,7 +378,7 @@ public List<SessionMember> getCoSponsors(ImmutableParams amendParams) { List<SessionMember> sessionMembers = new ArrayList<>(); for (int id : sessionMemberIds) { try { - sessionMembers.add(memberService.getMemberBySessionId(id)); + sessionMembers.add(memberService.getSessionMemberBySessionId(id)); } catch (MemberNotFoundEx memberNotFoundEx) { logger.warn("Bill referenced a member that does not exist: {}", memberNotFoundEx.getMessage()); } @@ -394,7 +394,7 @@ public List<SessionMember> getMultiSponsors(ImmutableParams amendParams) { List<SessionMember> sessionMembers = new ArrayList<>(); for (int id : sessionMemberIds) { try { - sessionMembers.add(memberService.getMemberBySessionId(id)); + sessionMembers.add(memberService.getSessionMemberBySessionId(id)); } catch (MemberNotFoundEx memberNotFoundEx) { logger.warn("Bill referenced a member that does not exist: {}", memberNotFoundEx.getMessage()); } @@ -412,7 +412,7 @@ public List<BillVote> getBillVotes(ImmutableParams baseParams) { for (BillVote billVote : billVotes) { for (SessionMember member : billVote.getMemberVotes().values()) { try { - SessionMember fullMember = memberService.getMemberBySessionId(member.getSessionMemberId()); + SessionMember fullMember = memberService.getSessionMemberBySessionId(member.getSessionMemberId()); member.updateFromOther(fullMember); } catch (MemberNotFoundEx memberNotFoundEx) { logger.error("Failed to add member vote since member could not be found!", memberNotFoundEx); diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Member.java b/src/main/java/gov/nysenate/openleg/model/entity/Member.java index 14d87208f..49cca68f1 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Member.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Member.java @@ -22,6 +22,13 @@ public Member(int memberId) { this.memberId = memberId; } + public Member(Person person, int memberId, Chamber chamber, boolean incumbent) { + super(person); + this.memberId = memberId; + this.chamber = chamber; + this.incumbent = incumbent; + } + public Member(Member member) { super(member); this.memberId = member.memberId; diff --git a/src/main/java/gov/nysenate/openleg/model/entity/Person.java b/src/main/java/gov/nysenate/openleg/model/entity/Person.java index 1880b1e12..f341bf569 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/Person.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/Person.java @@ -50,6 +50,20 @@ public Person (String fullName) { this.fullName = fullName.trim(); } + public Person(Integer personId, String fullName, String firstName, String middleName, String + lastName, String email, String pref, String suffix, boolean verified, String imgName) { + this.personId = personId; + this.fullName = fullName; + this.firstName = firstName; + this.middleName = middleName; + this.lastName = lastName; + this.email = email; + this.prefix = pref; + this.suffix = suffix; + this.verified = verified; + this.imgName = imgName; + } + public Person(Person other) { this.personId = other.personId; this.prefix = other.prefix; diff --git a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java index bfcd695db..04a526eca 100644 --- a/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java +++ b/src/main/java/gov/nysenate/openleg/model/entity/SessionMember.java @@ -44,6 +44,15 @@ public SessionMember(int memberId, SessionYear sessionYear) { this.sessionYear = sessionYear; } + public SessionMember(int sessionMemberId, Member member, String lbdcShortName, SessionYear sessionYear, Integer districtCode, boolean alternate) { + this.sessionMemberId = sessionMemberId; + this.member = member; + this.lbdcShortName = lbdcShortName; + this.sessionYear = sessionYear; + this.districtCode = districtCode; + this.alternate = alternate; + } + public SessionMember(SessionMember other) { this.member = other.member; this.sessionMemberId = other.sessionMemberId; @@ -104,6 +113,13 @@ public void updateFromOther(SessionMember other) { this.districtCode = other.getDistrictCode(); } + public boolean exactEquals(SessionMember sessionMember) { + return this.equals(sessionMember) && + Objects.equals(this.sessionMemberId, sessionMember.sessionMemberId) && + Objects.equals(this.lbdcShortName, sessionMember.lbdcShortName) && + Objects.equals(this.alternate, sessionMember.alternate); + } + /** --- Overrides --- */ /** @@ -113,11 +129,10 @@ public void updateFromOther(SessionMember other) { public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; - if (!super.equals(obj)) return false; final SessionMember other = (SessionMember) obj; return Objects.equals(this.sessionYear, other.sessionYear) && - Objects.equals(this.member.incumbent, other.member.incumbent) && - Objects.equals(this.districtCode, other.districtCode); + Objects.equals(this.districtCode, other.districtCode) && + Objects.equals(this.member, other.member); } @Override @@ -135,7 +150,7 @@ public int compareTo(SessionMember o) { return ComparisonChain.start() .compare(this.member, o.member) .compare(this.sessionYear, o.sessionYear) - .compareFalseFirst(this.alternate, o.alternate) + .compareTrueFirst(this.alternate, o.alternate) .compare(this.lbdcShortName, o.lbdcShortName) .result(); } diff --git a/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java b/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java index 3137a6ef2..c19439df8 100644 --- a/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java @@ -202,7 +202,7 @@ protected void flushBillUpdates() { * Retrieves a member from the LBDC short name. Creates a new unverified session member entry if no member can be retrieved. */ protected SessionMember getMemberFromShortName(String shortName, SessionYear sessionYear, Chamber chamber) throws ParseError { - return memberService.getMemberByShortNameEnsured(shortName, sessionYear, chamber); + return memberService.getSessionMemberByShortNameEnsured(shortName, sessionYear, chamber); } /** diff --git a/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java b/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java index 2ccba6ea3..5329a4e08 100644 --- a/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/entity/XmlSenCommProcessor.java @@ -139,7 +139,7 @@ private List<CommitteeMember> processCommitteeMembers(Node committeeMembership, Node memberNode = committeeMembersNodes.item(i); if (memberNode.getNodeName().equals("member")) { String shortName = xml.getString("name/text()", memberNode); - SessionMember sessionMember = memberService.getMemberByShortNameEnsured( + SessionMember sessionMember = memberService.getSessionMemberByShortNameEnsured( shortName, committee.getSession(), committee.getChamber()); CommitteeMember committeeMember = new CommitteeMember(); diff --git a/src/main/java/gov/nysenate/openleg/processor/spotcheck/calendar/CalendarAlertSupplementalParser.java b/src/main/java/gov/nysenate/openleg/processor/spotcheck/calendar/CalendarAlertSupplementalParser.java index 9d1a49aba..0d3f2fb5b 100644 --- a/src/main/java/gov/nysenate/openleg/processor/spotcheck/calendar/CalendarAlertSupplementalParser.java +++ b/src/main/java/gov/nysenate/openleg/processor/spotcheck/calendar/CalendarAlertSupplementalParser.java @@ -98,7 +98,7 @@ private CalendarSupplementalEntry createSupplementalEntry(CalendarSupplemental s */ private Chamber getChamber(SessionYear sessionYear, String sponsor) { try { - memberService.getMemberByShortName(sponsor, sessionYear, Chamber.ASSEMBLY); + memberService.getSessionMemberByShortName(sponsor, sessionYear, Chamber.ASSEMBLY); return Chamber.ASSEMBLY; } catch (Exception ex) { return Chamber.SENATE; diff --git a/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java b/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java index 9e6790a79..46c36a01c 100644 --- a/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java +++ b/src/main/java/gov/nysenate/openleg/script/ImportCommittees.java @@ -140,7 +140,7 @@ private Committee getCommitteeFromJson(File jsonFile, int year, Chamber chamber) private CommitteeMember getCommitteeMemberFromJson(JsonNode memberNode, int year, Chamber chamber) throws MemberNotFoundEx{ CommitteeMember committeeMember = new CommitteeMember(); - SessionMember member = memberService.getMemberByShortName(memberNode.get("shortName").textValue(), SessionYear.of(year), chamber); + SessionMember member = memberService.getSessionMemberByShortName(memberNode.get("shortName").textValue(), SessionYear.of(year), chamber); committeeMember.setSessionMember(member); return committeeMember; } diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java index 1ff74cd5c..23364d520 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/CachedMemberService.java @@ -24,7 +24,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Collectors; @Service @@ -79,21 +78,21 @@ public FullMember getFullMemberById(int memberId) throws MemberNotFoundEx { /** {@inheritDoc} */ @Override - public SessionMember getMemberBySessionId(int sessionMemberId) throws MemberNotFoundEx { + public SessionMember getSessionMemberBySessionId(int sessionMemberId) throws MemberNotFoundEx { return sessionMemberIdCache.getMemberBySessionId(sessionMemberId); } /** {@inheritDoc} */ @Override - public SessionMember getMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws MemberNotFoundEx { + public SessionMember getSessionMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws MemberNotFoundEx { return sessionChamberShortNameCache.getMemberByShortName(lbdcShortName, sessionYear, chamber); } /** {@inheritDoc} */ @Override - public SessionMember getMemberByShortNameEnsured(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws ParseError { + public SessionMember getSessionMemberByShortNameEnsured(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws ParseError { try { - return getMemberByShortName(lbdcShortName, sessionYear, chamber); + return getSessionMemberByShortName(lbdcShortName, sessionYear, chamber); } catch (MemberNotFoundEx ex) { SessionMember sessionMember = SessionMember.newMakeshiftMember(lbdcShortName, sessionYear, chamber); @@ -107,7 +106,7 @@ public SessionMember getMemberByShortNameEnsured(String lbdcShortName, SessionYe /** {@inheritDoc} */ @Override - public List<SessionMember> getAllMembers(SortOrder sortOrder, LimitOffset limOff) { + public List<SessionMember> getAllSessionMembers(SortOrder sortOrder, LimitOffset limOff) { return fullMemberIdCache.getAllMembers(sortOrder, limOff); } diff --git a/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java b/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java index 177f6d0e1..a57281056 100644 --- a/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java +++ b/src/main/java/gov/nysenate/openleg/service/entity/member/data/MemberService.java @@ -21,7 +21,7 @@ public interface MemberService * @return Member * @throws MemberNotFoundEx If no matching member was found. */ - public SessionMember getSessionMemberById(int memberId, SessionYear sessionYear) throws MemberNotFoundEx; + SessionMember getSessionMemberById(int memberId, SessionYear sessionYear) throws MemberNotFoundEx; /** * Retrieves map of session year -> Member for a given member id. @@ -29,7 +29,7 @@ public interface MemberService * @param id int * @return Map<Integer, Member> */ - public FullMember getFullMemberById(int id) throws MemberNotFoundEx; + FullMember getFullMemberById(int id) throws MemberNotFoundEx; /** * Retrieve a member by session member id @@ -37,10 +37,10 @@ public interface MemberService * then the primary session member will be returned instead * * @param sessionMemberId - * @return Member + * @return SessionMember * @throws MemberNotFoundEx if no session member exists with sessionMemberId */ - public SessionMember getMemberBySessionId(int sessionMemberId) throws MemberNotFoundEx; + SessionMember getSessionMemberBySessionId(int sessionMemberId) throws MemberNotFoundEx; /** * Retrieve Member (which can represent either a senator or assemblymember) using the LBDC shortname, @@ -49,13 +49,13 @@ public interface MemberService * @param lbdcShortName String - The short name of the member as represented in the source data. * @param sessionYear SessionYear - The session year in which this member was active. * @param chamber Chamber - * @return Member + * @return SessionMember * @throws MemberNotFoundEx If no matching member was found. */ - public SessionMember getMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws MemberNotFoundEx; + SessionMember getSessionMemberByShortName(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws MemberNotFoundEx; /** - * This functions in the same way as {@link #getMemberByShortName(String, gov.nysenate.openleg.model.base.SessionYear, gov.nysenate.openleg.model.entity.Chamber)} + * This functions in the same way as {@link #getSessionMemberByShortName(String, gov.nysenate.openleg.model.base.SessionYear, gov.nysenate.openleg.model.entity.Chamber)} * with the exception that, instead of throwing an exception when a member is not found, * this method creates a new Member in storage and returns that * This should only be used in the processor layer @@ -63,22 +63,22 @@ public interface MemberService * @param lbdcShortName String - The short name of the member as represented in the source data. * @param sessionYear SessionYear - The session year in which this member was active. * @param chamber Chamber - * @return Member + * @return SessionMember * @throws ParseError - if the provided short name does not match specification */ - public SessionMember getMemberByShortNameEnsured(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws ParseError; + SessionMember getSessionMemberByShortNameEnsured(String lbdcShortName, SessionYear sessionYear, Chamber chamber) throws ParseError; /** - * Retrieves all members from all years and both chambers. + * Retrieves all session members from all years and both chambers. * Useful for rebuilding the search index. * @return */ - public List<SessionMember> getAllMembers(SortOrder sortOrder, LimitOffset limOff); + List<SessionMember> getAllSessionMembers(SortOrder sortOrder, LimitOffset limOff); /** * @return List<FullMember> - a list of all members containing all linked session members */ - public List<FullMember> getAllFullMembers(); + List<FullMember> getAllFullMembers(); /** * Adds the given members to the data store @@ -86,5 +86,5 @@ public interface MemberService * because it will trigger cache and search index rebuilds * @param members List<Member> */ - public void updateMembers(List<SessionMember> members); + void updateMembers(List<SessionMember> members); } \ No newline at end of file diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java index 65cd7fdc9..1d1e4c81f 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotCheckUtils.java @@ -178,7 +178,7 @@ public String getPrimaryShortname(SessionYear sessionYear, Chamber chamber, Stri return null; } try { - SessionMember sessionMember = memberService.getMemberByShortName(shortname, sessionYear, chamber); + SessionMember sessionMember = memberService.getSessionMemberByShortName(shortname, sessionYear, chamber); return getPrimaryShortname(sessionYear, sessionMember.getMember().getMemberId()); } catch (MemberNotFoundEx ex) { return "<unknown shortname: " + sessionYear + " " + chamber + " " + shortname + ">"; diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java index 1c3b74462..661ce6f63 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -4,17 +4,20 @@ import gov.nysenate.openleg.client.response.base.BaseResponse; import gov.nysenate.openleg.client.response.base.ListViewResponse; import gov.nysenate.openleg.client.response.base.PaginationResponse; +import gov.nysenate.openleg.client.response.base.ViewObjectResponse; +import gov.nysenate.openleg.client.response.error.ErrorCode; +import gov.nysenate.openleg.client.response.error.ErrorResponse; import gov.nysenate.openleg.client.view.entity.FullMemberView; import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.ApiTest; import gov.nysenate.openleg.model.base.SessionYear; -import gov.nysenate.openleg.model.entity.FullMember; -import gov.nysenate.openleg.model.entity.MemberNotFoundEx; +import gov.nysenate.openleg.model.entity.*; import gov.nysenate.openleg.model.search.SearchException; import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.beans.factory.annotation.Autowired; +import java.util.Arrays; import java.util.stream.Collectors; import static org.junit.Assert.*; @@ -32,21 +35,68 @@ public void getAllMembersTest() throws SearchException, MemberNotFoundEx { @Test public void getMembersByYearTest() throws SearchException, MemberNotFoundEx { BaseResponse baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", false, testRequest); - ListViewResponse<?> paginationResponse = (ListViewResponse<?>) baseResponse; - assertEquals(paginationResponse.getTotal(), 224); + ListViewResponse<?> listResponse = (ListViewResponse<?>) baseResponse; + assertEquals(listResponse.getTotal(), 224); - long numAlternates = paginationResponse.getResult().getItems().stream().filter(sm -> + long numAlternates = listResponse.getResult().getItems().stream().filter(sm -> sm instanceof SessionMemberView && ((SessionMemberView) sm).isAlternate()).count(); assertEquals(numAlternates, 0); addParam("limit", "all"); baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", true, testRequest); - paginationResponse = (ListViewResponse<?>) baseResponse; - FullMemberView testFmv = (FullMemberView) paginationResponse.getResult().getItems().stream().filter(fm -> + listResponse = (ListViewResponse<?>) baseResponse; + FullMemberView testFmv = (FullMemberView) listResponse.getResult().getItems().stream().filter(fm -> fm instanceof FullMemberView && ((FullMemberView) fm).getMemberId() == 591).collect(Collectors.toList()).get(0); assertEquals(2, testFmv.getSessionShortNameMap().get(2015).size()); } + @Test + public void getMembersByYearAndIdTest() { + String name = "HASSELL-THOMPSO"; + Person testP = new Person(199, "Ruth Hassell-Thompson", "Ruth", null, "Hassell-Thompson", + "hassellt@senate.state.ny.us", "Senator", null, true, "380_ruth_hassell-thompson.jpg"); + Member testM = new Member(testP, 380, Chamber.SENATE, false); + SessionMember nonAlt2011 = new SessionMember(74, testM, name + "N", new + SessionYear(2011), 36, false); + + BaseResponse resp = testCtrl.getMembersByYearAndId(testM.getMemberId(), 2011, false, testRequest); + SessionMember actualSm = ((SessionMemberView)(((ViewObjectResponse<?>) resp).getResult())).toSessionMember(); + assertTrue(nonAlt2011.exactEquals(actualSm)); + + SessionMember nonAlt2009 = new SessionMember(nonAlt2011); + nonAlt2009.setSessionMemberId(12); + nonAlt2009.setSessionYear(new SessionYear(2009)); + + SessionMember alt2009 = new SessionMember(nonAlt2009); + nonAlt2009.setSessionMemberId(668); + nonAlt2009.setAlternate(true); + nonAlt2009.setLbdcShortName(name.replaceFirst("-", "_").replaceFirst("L", "")); + + SessionMember alt2011 = new SessionMember(nonAlt2011); + alt2011.setSessionMemberId(669); + alt2011.setAlternate(true); + alt2011.setLbdcShortName(name); + + SessionMember nonAlt2013 = new SessionMember(nonAlt2011); + nonAlt2013.setSessionMemberId(136); + nonAlt2013.setSessionYear(new SessionYear(2013)); + + SessionMember alt2013 = new SessionMember(nonAlt2013); + alt2013.setSessionMemberId(670); + alt2013.setAlternate(true); + alt2013.setLbdcShortName(name); + + SessionMember only2015 = new SessionMember(nonAlt2011); + only2015.setSessionMemberId(693); + only2015.setSessionYear(new SessionYear(2015)); + + FullMemberView testFmv = new FullMemberView(new FullMember(Arrays.asList(alt2009, nonAlt2009, + alt2011, nonAlt2011, alt2013, nonAlt2013, only2015))); + resp = testCtrl.getMembersByYearAndId(testM.getMemberId(), 2015, true, testRequest); + FullMemberView actualFmv = (FullMemberView)(((ViewObjectResponse<?>) resp).getResult()); + assertTrue(testFmv.exactEquals(actualFmv)); + } + @Test public void getMembersByYearAndChamberTest() throws SearchException, MemberNotFoundEx { BaseResponse baseResponse = testCtrl.getMembersByYearAndChamber(2017, "senate", "shortName:asc", false, testRequest); @@ -54,4 +104,10 @@ public void getMembersByYearAndChamberTest() throws SearchException, MemberNotFo baseResponse = testCtrl.getMembersByYearAndChamber(2017, "assembly", "shortName:asc", false, testRequest); assertEquals(((PaginationResponse) baseResponse).getTotal(), 163); } + + @Test + public void handleMemberNotFoundExTest() { + ErrorResponse resp = testCtrl.handleMemberNotFoundEx(null); + assertEquals(resp.getErrorCode(), ErrorCode.MEMBER_NOT_FOUND.getCode()); + } } diff --git a/src/test/java/gov/nysenate/openleg/model/bill/BillVoteIT.java b/src/test/java/gov/nysenate/openleg/model/bill/BillVoteIT.java index c19357bee..b256e0012 100644 --- a/src/test/java/gov/nysenate/openleg/model/bill/BillVoteIT.java +++ b/src/test/java/gov/nysenate/openleg/model/bill/BillVoteIT.java @@ -27,10 +27,10 @@ public void testBillVoteEquality() throws Exception { BillId billId = new BillId("S1234", 2013); SessionYear sessionYear = SessionYear.of(2013); - SessionMember ball = memberService.getMemberByShortName("BALL", sessionYear, Chamber.SENATE); - SessionMember lavalle = memberService.getMemberByShortName("LAVALLE", sessionYear, Chamber.SENATE); - SessionMember lanza = memberService.getMemberByShortName("LANZA", sessionYear, Chamber.SENATE); - SessionMember breslin = memberService.getMemberByShortName("BRESLIN", sessionYear, Chamber.SENATE); + SessionMember ball = memberService.getSessionMemberByShortName("BALL", sessionYear, Chamber.SENATE); + SessionMember lavalle = memberService.getSessionMemberByShortName("LAVALLE", sessionYear, Chamber.SENATE); + SessionMember lanza = memberService.getSessionMemberByShortName("LANZA", sessionYear, Chamber.SENATE); + SessionMember breslin = memberService.getSessionMemberByShortName("BRESLIN", sessionYear, Chamber.SENATE); BillVote vote1 = new BillVote(billId, date, BillVoteType.FLOOR, 1); vote1.addMemberVote(BillVoteCode.AYE, ball); diff --git a/src/test/java/gov/nysenate/openleg/processor/bill/XmlSenAgenVoteProcessorIT.java b/src/test/java/gov/nysenate/openleg/processor/bill/XmlSenAgenVoteProcessorIT.java index 8b8357995..f9c66cc08 100644 --- a/src/test/java/gov/nysenate/openleg/processor/bill/XmlSenAgenVoteProcessorIT.java +++ b/src/test/java/gov/nysenate/openleg/processor/bill/XmlSenAgenVoteProcessorIT.java @@ -62,9 +62,9 @@ public void processSenAgendaVote() throws JsonProcessingException { LocalDateTime meetDataTime = DateUtils.getLrsDateTime("2017-02-06T00.00.00Z"); AgendaVoteCommittee voteCommittee = new AgendaVoteCommittee(committeeId, chair, meetDataTime); SessionYear sessionYear = new SessionYear(2017); - SessionMember member = memberService.getMemberByShortNameEnsured("Flanagan",sessionYear,Chamber.SENATE); + SessionMember member = memberService.getSessionMemberByShortNameEnsured("Flanagan",sessionYear,Chamber.SENATE); AgendaVoteAttendance memberAttendance = new AgendaVoteAttendance(member,1,"R","Present"); - SessionMember member1 = memberService.getMemberByShortNameEnsured("DeFrancisco",sessionYear,Chamber.SENATE); + SessionMember member1 = memberService.getSessionMemberByShortNameEnsured("DeFrancisco",sessionYear,Chamber.SENATE); AgendaVoteAttendance memberAttendance1 = new AgendaVoteAttendance(member1,2,"R","Present"); voteCommittee.addAttendance(memberAttendance); voteCommittee.addAttendance(memberAttendance1); diff --git a/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java b/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java index 082984e75..e2302ece5 100644 --- a/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java +++ b/src/test/java/gov/nysenate/openleg/service/entity/member/SqlMemberServiceTest.java @@ -22,7 +22,7 @@ public class SqlMemberServiceTest extends BaseTests @Test public void testGetMemberByShortName_UsesCache() { - logger.info(OutputUtils.toJson(sqlMemberService.getMemberBySessionId(667))); + logger.info(OutputUtils.toJson(sqlMemberService.getSessionMemberBySessionId(667))); } @Test(expected = MemberNotFoundEx.class) From ea4638c1412119dc683bd43d0df0bc122b5460ea Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Wed, 18 Mar 2020 16:04:50 -0400 Subject: [PATCH 22/29] Created new class to test MemberSearchCtrl. --- .../openleg/controller/api/base/BaseCtrl.java | 24 +++++++++---------- .../api/entity/MemberSearchCtrl.java | 8 +++---- .../api/entity/MemberGetCtrlTest.java | 8 ++++++- .../api/entity/MemberSearchCtrlTest.java | 17 +++++++++++++ 4 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java diff --git a/src/main/java/gov/nysenate/openleg/controller/api/base/BaseCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/base/BaseCtrl.java index 7b509a17a..ee8e3c4b3 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/base/BaseCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/base/BaseCtrl.java @@ -103,8 +103,8 @@ protected SortOrder getSortOrder(String sortOrder, SortOrder defaultSortOrder) { protected LimitOffset getLimitOffset(WebRequest webRequest, int defaultLimit) { int limit = defaultLimit; int offset = 0; - if (webRequest.getParameter("limit") != null) { - String limitStr = webRequest.getParameter("limit"); + String limitStr = webRequest.getParameter("limit"); + if (limitStr != null) { if (limitStr.equalsIgnoreCase("all")) { limit = 0; } @@ -128,7 +128,7 @@ protected LimitOffset getLimitOffset(WebRequest webRequest, int defaultLimit) { * @param dateString The parameter value to be parsed * @param parameter The name of the parameter. Used to generate the exception * @return LocalDate - * @throws InvalidRequestParamEx + * @throws InvalidRequestParamEx if the date could not be parsed. */ protected LocalDate parseISODate(String dateString, String parameter) { try { @@ -147,7 +147,7 @@ protected LocalDate parseISODate(String dateString, String parameter) { * @param dateTimeString The parameter value to be parsed * @param parameterName The name of the parameter. Used to generate the exception * @return LocalDateTime - * @throws InvalidRequestParamEx + * @throws InvalidRequestParamEx if the datetime could not be parsed. */ protected LocalDateTime parseISODateTime(String dateTimeString, String parameterName) { try { @@ -174,7 +174,7 @@ protected LocalDateTime parseISODateTime(String dateTimeString, String parameter */ protected LocalDateTime parseISODateTime(String dateTimeString, LocalDateTime defaultValue) { try { - return parseISODateTime(dateTimeString, "dont matter"); + return parseISODateTime(dateTimeString, "don't matter"); } catch (InvalidRequestParamEx ex) { return defaultValue; } @@ -300,7 +300,7 @@ protected LinkedHashSet<BillTextFormat> getFullTextFormats(WebRequest request) { * @param <T> T * @return Range<T> */ - protected <T extends Comparable> Range<T> getRange(T lower, T upper, String fromName, String upperName, + protected <T extends Comparable<?>> Range<T> getRange(T lower, T upper, String fromName, String upperName, BoundType lowerType, BoundType upperType) { try { return Range.range(lower, lowerType, upper, upperType); @@ -312,19 +312,19 @@ protected <T extends Comparable> Range<T> getRange(T lower, T upper, String from } } - protected <T extends Comparable> Range<T> getOpenRange(T lower, T upper, String fromName, String upperName) { + protected <T extends Comparable<?>> Range<T> getOpenRange(T lower, T upper, String fromName, String upperName) { return getRange(lower, upper, fromName, upperName, BoundType.OPEN, BoundType.OPEN); } - protected <T extends Comparable> Range<T> getOpenClosedRange(T lower, T upper, String fromName, String upperName) { + protected <T extends Comparable<?>> Range<T> getOpenClosedRange(T lower, T upper, String fromName, String upperName) { return getRange(lower, upper, fromName, upperName, BoundType.OPEN, BoundType.CLOSED); } - protected <T extends Comparable> Range<T> getClosedOpenRange(T lower, T upper, String fromName, String upperName) { + protected <T extends Comparable<?>> Range<T> getClosedOpenRange(T lower, T upper, String fromName, String upperName) { return getRange(lower, upper, fromName, upperName, BoundType.CLOSED, BoundType.OPEN); } - protected <T extends Comparable> Range<T> getClosedRange(T lower, T upper, String fromName, String upperName) { + protected <T extends Comparable<?>> Range<T> getClosedRange(T lower, T upper, String fromName, String upperName) { return getRange(lower, upper, fromName, upperName, BoundType.CLOSED, BoundType.CLOSED); } @@ -335,7 +335,7 @@ protected int getIntegerParam(WebRequest request, String paramName) { String intString = request.getParameter(paramName); try { return Integer.parseInt(intString); - } catch (NumberFormatException ex) { + } catch (NullPointerException | NumberFormatException ex) { throw new InvalidRequestParamEx(intString, paramName, "integer", "integer"); } } @@ -377,7 +377,7 @@ protected UpdateType getUpdateTypeFromParam(WebRequest request) { private <T extends Enum<T>> InvalidRequestParamEx getEnumParamEx(Class<T> enumType, Function<T, String> valueFunction, String paramName, String paramValue) { throw new InvalidRequestParamEx(paramValue, paramName, "string", - Arrays.asList(enumType.getEnumConstants()).stream() + Arrays.stream(enumType.getEnumConstants()) .map(valueFunction) .reduce("", (a, b) -> (StringUtils.isNotBlank(a) ? a + "|" : "") + b)); } diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java index c6e6af68a..2a83245e4 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java @@ -44,7 +44,7 @@ public class MemberSearchCtrl extends BaseCtrl * offset - Start results from offset */ @RequestMapping(value = "/search") - public BaseResponse globalSearch(@RequestParam(required = true) String term, + public BaseResponse globalSearch(@RequestParam String term, @RequestParam(defaultValue = "") String sort, @RequestParam(defaultValue = "false") boolean full, WebRequest webRequest) throws SearchException { @@ -64,9 +64,9 @@ public BaseResponse globalSearch(@RequestParam(required = true) String term, * limit - Limit the number of results (default 50) * offset - Start results from offset */ - @RequestMapping(value = "/{sessionYear}/search") + @RequestMapping(value = "/{sessionYear:\\d{4}}/search") public BaseResponse globalSearch(@PathVariable int sessionYear, - @RequestParam(required = true) String term, + @RequestParam String term, @RequestParam(defaultValue = "") String sort, @RequestParam(defaultValue = "false") boolean full, WebRequest webRequest) throws SearchException { @@ -84,7 +84,7 @@ private BaseResponse getSearchResponse(SearchResults<Integer> results, boolean f } catch (MemberNotFoundEx ex) { throw new SearchException("No Member found.", ex); } - viewtypes.add((full) ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().get())); + viewtypes.add((full) ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().orElse(null))); } return ListViewResponse.of(viewtypes, results.getTotalResults(), limOff); } diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java index 661ce6f63..5b218e90e 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -27,17 +27,23 @@ public class MemberGetCtrlTest extends ApiTest { @Autowired private MemberGetCtrl testCtrl; + /** + * Basically just ensures the MemberGetCtrl has been properly injected. + */ @Test public void getAllMembersTest() throws SearchException, MemberNotFoundEx { testCtrl.getAllMembers("shortName:asc", true, testRequest); } + /** + * Tests that all members of a certain year are correctly retrieved. + */ @Test public void getMembersByYearTest() throws SearchException, MemberNotFoundEx { BaseResponse baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", false, testRequest); ListViewResponse<?> listResponse = (ListViewResponse<?>) baseResponse; assertEquals(listResponse.getTotal(), 224); - + // If just SessionMemberViews are returned, there should be no alternates. long numAlternates = listResponse.getResult().getItems().stream().filter(sm -> sm instanceof SessionMemberView && ((SessionMemberView) sm).isAlternate()).count(); assertEquals(numAlternates, 0); diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java new file mode 100644 index 000000000..3cb2c51b1 --- /dev/null +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java @@ -0,0 +1,17 @@ +package gov.nysenate.openleg.controller.api.entity; + +import gov.nysenate.openleg.annotation.UnitTest; +import gov.nysenate.openleg.controller.api.ApiTest; +import gov.nysenate.openleg.model.entity.SessionMember; +import org.junit.experimental.categories.Category; +import org.springframework.beans.factory.annotation.Autowired; + +@Category(UnitTest.class) +public class MemberSearchCtrlTest extends ApiTest { + @Autowired + private MemberSearchCtrl testCtrl; + + private SessionMember searchBySessionMemberId() { + return null; + } +} From 5bd5640a00732010ae28488c6d619c4a107592a0 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Fri, 20 Mar 2020 22:06:35 -0400 Subject: [PATCH 23/29] Tests now run based on data from initial SQL files only. --- docs/api/members.rst | 2 +- .../controller/api/entity/MemberGetCtrl.java | 4 +- .../api/entity/MemberSearchCtrl.java | 10 ++-- .../openleg/controller/api/ApiTest.java | 11 +++- .../controller/api/entity/MemberApiTest.java | 5 ++ .../api/entity/MemberGetCtrlTest.java | 39 ++++++++----- .../api/entity/MemberSearchCtrlTest.java | 56 ++++++++++++++++++- 7 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java diff --git a/docs/api/members.rst b/docs/api/members.rst index 499e19876..e1c89ea4a 100644 --- a/docs/api/members.rst +++ b/docs/api/members.rst @@ -146,4 +146,4 @@ Note: given a sessionMemberId = #### in a session year yyyy, you can get the mem List all members who have served district code 20 :: - /api/3/members/search?term=districtCode:20 \ No newline at end of file + /api/3/members/search?term=districtCode:20 diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java index 5244adf36..dc3c4304c 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrl.java @@ -32,8 +32,8 @@ @RequestMapping(value = BASE_API_PATH + "/members", method = RequestMethod.GET, produces = APPLICATION_JSON_VALUE) public class MemberGetCtrl extends BaseCtrl { - private final MemberService memberData; - private final MemberSearchService memberSearch; + protected final MemberService memberData; + protected final MemberSearchService memberSearch; @Autowired public MemberGetCtrl(MemberService memberData, MemberSearchService memberSearch) { diff --git a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java index 2a83245e4..70ece10f6 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrl.java @@ -29,8 +29,8 @@ @RequestMapping(value = BASE_API_PATH + "/members", method = RequestMethod.GET, produces = APPLICATION_JSON_VALUE) public class MemberSearchCtrl extends BaseCtrl { - @Autowired private MemberService memberData; - @Autowired private MemberSearchService memberSearch; + @Autowired protected MemberService memberData; + @Autowired protected MemberSearchService memberSearch; /** * Member Search API @@ -76,7 +76,7 @@ public BaseResponse globalSearch(@PathVariable int sessionYear, } private BaseResponse getSearchResponse(SearchResults<Integer> results, boolean full, LimitOffset limOff) throws SearchException { - List<ViewObject> viewtypes = new ArrayList<>(); + List<ViewObject> viewTypes = new ArrayList<>(); for (SearchResult<Integer> result : results.getResults()) { FullMember member; try { @@ -84,8 +84,8 @@ private BaseResponse getSearchResponse(SearchResults<Integer> results, boolean f } catch (MemberNotFoundEx ex) { throw new SearchException("No Member found.", ex); } - viewtypes.add((full) ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().orElse(null))); + viewTypes.add((full) ? new FullMemberView(member) : new SessionMemberView(member.getLatestSessionMember().get())); } - return ListViewResponse.of(viewtypes, results.getTotalResults(), limOff); + return ListViewResponse.of(viewTypes, results.getTotalResults(), limOff); } } diff --git a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java index 28ee89f38..1e09045f9 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java @@ -1,15 +1,24 @@ package gov.nysenate.openleg.controller.api; import gov.nysenate.openleg.BaseTests; +import gov.nysenate.openleg.service.base.search.IndexedSearchService; import org.junit.Before; import org.mockito.Mockito; import org.springframework.web.context.request.WebRequest; public abstract class ApiTest extends BaseTests { protected WebRequest testRequest = Mockito.mock(WebRequest.class); + private static boolean dataLoaded = false; + + protected abstract IndexedSearchService<?> getIndex(); @Before - public void clearParams() { + public void setup() throws InterruptedException { + if (!dataLoaded) { + getIndex().rebuildIndex(); + Thread.sleep(500); + dataLoaded = true; + } testRequest = Mockito.mock(WebRequest.class); Mockito.when(testRequest.getParameter(Mockito.anyString())).thenReturn(null); } diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java new file mode 100644 index 000000000..a59b32894 --- /dev/null +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java @@ -0,0 +1,5 @@ +package gov.nysenate.openleg.controller.api.entity; + +public abstract class MemberApiTest { + +} diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java index 5b218e90e..384a0e10e 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -13,6 +13,7 @@ import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.*; import gov.nysenate.openleg.model.search.SearchException; +import gov.nysenate.openleg.service.base.search.IndexedSearchService; import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.beans.factory.annotation.Autowired; @@ -27,11 +28,16 @@ public class MemberGetCtrlTest extends ApiTest { @Autowired private MemberGetCtrl testCtrl; + @Override + protected IndexedSearchService<?> getIndex() { + return ((IndexedSearchService<?>) testCtrl.memberSearch); + } + /** - * Basically just ensures the MemberGetCtrl has been properly injected. + * Ensures the MemberGetCtrl has been properly injected. Importantly is run before other tests. */ @Test - public void getAllMembersTest() throws SearchException, MemberNotFoundEx { + public void getAllMembersTest() throws SearchException, MemberNotFoundEx, InterruptedException { testCtrl.getAllMembers("shortName:asc", true, testRequest); } @@ -40,22 +46,23 @@ public void getAllMembersTest() throws SearchException, MemberNotFoundEx { */ @Test public void getMembersByYearTest() throws SearchException, MemberNotFoundEx { - BaseResponse baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", false, testRequest); - ListViewResponse<?> listResponse = (ListViewResponse<?>) baseResponse; - assertEquals(listResponse.getTotal(), 224); + ListViewResponse<?> listResponse = (ListViewResponse<?>) testCtrl.getMembersByYear(2013, "shortName:asc", false, testRequest); + assertEquals(223, listResponse.getTotal()); // If just SessionMemberViews are returned, there should be no alternates. long numAlternates = listResponse.getResult().getItems().stream().filter(sm -> sm instanceof SessionMemberView && ((SessionMemberView) sm).isAlternate()).count(); - assertEquals(numAlternates, 0); + assertEquals(0, numAlternates); addParam("limit", "all"); - baseResponse = testCtrl.getMembersByYear(2015, "shortName:asc", true, testRequest); - listResponse = (ListViewResponse<?>) baseResponse; + listResponse = (ListViewResponse<?>) testCtrl.getMembersByYear(2013, "shortName:asc", true, testRequest);; FullMemberView testFmv = (FullMemberView) listResponse.getResult().getItems().stream().filter(fm -> fm instanceof FullMemberView && ((FullMemberView) fm).getMemberId() == 591).collect(Collectors.toList()).get(0); - assertEquals(2, testFmv.getSessionShortNameMap().get(2015).size()); + assertEquals(2, testFmv.getSessionShortNameMap().get(2013).size()); } + /** + * Tests many session members of a particular member. + */ @Test public void getMembersByYearAndIdTest() { String name = "HASSELL-THOMPSO"; @@ -103,14 +110,20 @@ public void getMembersByYearAndIdTest() { assertTrue(testFmv.exactEquals(actualFmv)); } + /** + * Tests that the correct number of members are in each chamber. + */ @Test public void getMembersByYearAndChamberTest() throws SearchException, MemberNotFoundEx { - BaseResponse baseResponse = testCtrl.getMembersByYearAndChamber(2017, "senate", "shortName:asc", false, testRequest); - assertEquals(((PaginationResponse) baseResponse).getTotal(), 67); - baseResponse = testCtrl.getMembersByYearAndChamber(2017, "assembly", "shortName:asc", false, testRequest); - assertEquals(((PaginationResponse) baseResponse).getTotal(), 163); + BaseResponse baseResponse = testCtrl.getMembersByYearAndChamber(2009, "senate", "shortName:asc", false, testRequest); + assertEquals(63, ((PaginationResponse) baseResponse).getTotal()); + baseResponse = testCtrl.getMembersByYearAndChamber(2009, "assembly", "shortName:asc", false, testRequest); + assertEquals(160, ((PaginationResponse) baseResponse).getTotal(), 160); } + /** + * Just to round out coverage. + */ @Test public void handleMemberNotFoundExTest() { ErrorResponse resp = testCtrl.handleMemberNotFoundEx(null); diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java index 3cb2c51b1..a32a36321 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java @@ -1,17 +1,67 @@ package gov.nysenate.openleg.controller.api.entity; import gov.nysenate.openleg.annotation.UnitTest; +import gov.nysenate.openleg.client.response.base.ListViewResponse; +import gov.nysenate.openleg.client.view.entity.FullMemberView; +import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.ApiTest; -import gov.nysenate.openleg.model.entity.SessionMember; +import gov.nysenate.openleg.model.base.SessionYear; +import gov.nysenate.openleg.model.entity.*; +import gov.nysenate.openleg.model.search.SearchException; +import gov.nysenate.openleg.service.base.search.IndexedSearchService; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; + +import static org.junit.Assert.*; + @Category(UnitTest.class) public class MemberSearchCtrlTest extends ApiTest { @Autowired private MemberSearchCtrl testCtrl; - private SessionMember searchBySessionMemberId() { - return null; + @Override + protected IndexedSearchService<?> getIndex() { + return ((IndexedSearchService<?>) testCtrl.memberSearch); + } + + @Test + public void aSimpleTest() throws SearchException { + Person testP = new Person(498, "Greene", "", "", "", "", "", "", false, "no_image.jpg"); + Member testM = new Member(testP, 676, Chamber.ASSEMBLY, false); + SessionMember testSm = new SessionMember(664, testM, "GREENE", new SessionYear(2009), 0, false); + + ListViewResponse<?> listResponse = (ListViewResponse<?>) testCtrl.globalSearch("memberId:676", "", false, testRequest); + assertEquals(1, listResponse.getTotal()); + + SessionMember actualSm = ((SessionMemberView) listResponse.getResult().getItems().asList().get(0)).toSessionMember(); + assertTrue(actualSm.exactEquals(testSm)); + } + + @Test + public void searchBySessionMemberId() throws SearchException { + Person testP = new Person(499, "Edward Hennessey", "Edward", "", "Hennessey", "", "", "", true, "no_image.jpg"); + Member testM = new Member(testP, 677, Chamber.ASSEMBLY, false); + SessionMember testSm = new SessionMember(666, testM, "HENNESSEY", new SessionYear(2013), 3, false); + SessionMember testSmAlt = new SessionMember(testSm); + testSmAlt.setSessionMemberId(667); + testSmAlt.setLbdcShortName("HENNESSY"); + testSmAlt.setAlternate(true); + + ListViewResponse<?> listResponse = (ListViewResponse<?>) testCtrl.globalSearch(2013, "sessionShortNameMap.2013.sessionMemberId:666", "", false, testRequest); + assertEquals(1, listResponse.getTotal()); + + SessionMember actualSm = ((SessionMemberView) listResponse.getResult().getItems().get(0)).toSessionMember(); + assertTrue(actualSm.exactEquals(testSm)); + + listResponse = (ListViewResponse<?>) testCtrl.globalSearch(2013, "sessionShortNameMap.2013.sessionMemberId:667", "", true, testRequest); + assertEquals(1, listResponse.getTotal()); + + List<SessionMemberView> actual2013Smvs = ((FullMemberView) listResponse.getResult().getItems().get(0)).getSessionShortNameMap().get(2013); + assertEquals(2, actual2013Smvs.size()); + assertTrue(actual2013Smvs.get(0).toSessionMember().exactEquals(testSm)); + assertTrue(actual2013Smvs.get(1).toSessionMember().exactEquals(testSmAlt)); } } From 4603f41f028920e93166127b5af60e770af695cd Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Tue, 24 Mar 2020 17:52:59 -0400 Subject: [PATCH 24/29] Changed views so that no fields are removed compared to the original version. Cleaned up method of filling indices with data. --- .../client/view/entity/FullMemberView.java | 1 + .../client/view/entity/MemberView.java | 26 +++++++++++++++++++ .../client/view/entity/SessionMemberView.java | 2 +- .../openleg/controller/api/ApiTest.java | 18 ++++++++----- .../api/entity/MemberGetCtrlTest.java | 12 ++++----- .../api/entity/MemberSearchCtrlTest.java | 12 ++++----- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java index 34c26f98d..c96eeaf0e 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/FullMemberView.java @@ -19,6 +19,7 @@ public FullMemberView(FullMember member) { .collect(Collectors.toMap(SessionYear::getYear, session -> member.getSessionMemberMap().get(session).stream() .map(SessionMemberView::new) + .sorted((sm1, sm2) -> Boolean.compare(sm1.alternate, sm2.alternate)) .collect(Collectors.toList()))); } diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java index 9a44b73c7..3af1d0db6 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/MemberView.java @@ -2,6 +2,7 @@ import gov.nysenate.openleg.client.view.base.ViewObject; import gov.nysenate.openleg.model.entity.Member; +import gov.nysenate.openleg.model.entity.Person; import gov.nysenate.openleg.model.entity.SessionMember; public class MemberView implements ViewObject @@ -11,6 +12,8 @@ public class MemberView implements ViewObject protected boolean incumbent; protected String fullName; protected String shortName; + protected String imgName; + private SessionMember relatedSessionMember; public MemberView(){} @@ -22,6 +25,9 @@ public MemberView(SessionMember sessionMember) { this.incumbent = member.isIncumbent(); this.fullName = member.getFullName(); this.shortName = sessionMember.getLbdcShortName(); + // This is actually associated with a person, not a member. + this.imgName = member.getImgName(); + this.relatedSessionMember = sessionMember; } } @@ -45,6 +51,26 @@ public String getShortName() { return shortName; } + public String getImgName() { + return imgName; + } + + public int getSessionMemberId() { + return relatedSessionMember.getSessionMemberId(); + } + + public int getSessionYear() { + return relatedSessionMember.getSessionYear().getYear(); + } + + public int getDistrictCode() { + return relatedSessionMember.getDistrictCode(); + } + + public boolean getAlternate() { + return relatedSessionMember.isAlternate(); + } + @Override public String getViewType() { return "member"; diff --git a/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java b/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java index 70f86ecb6..e25a8501a 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/entity/SessionMemberView.java @@ -66,6 +66,6 @@ public boolean isAlternate() { @Override public String getViewType() { - return "session-member"; + return "member-session"; } } \ No newline at end of file diff --git a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java index 1e09045f9..df26c80b0 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java @@ -1,22 +1,28 @@ package gov.nysenate.openleg.controller.api; +import com.google.common.eventbus.EventBus; import gov.nysenate.openleg.BaseTests; -import gov.nysenate.openleg.service.base.search.IndexedSearchService; +import gov.nysenate.openleg.dao.base.SearchIndex; +import gov.nysenate.openleg.model.search.RebuildIndexEvent; import org.junit.Before; import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.request.WebRequest; +import java.util.EnumSet; + public abstract class ApiTest extends BaseTests { + protected static EnumSet<SearchIndex> indicesToTest = EnumSet.noneOf(SearchIndex.class); + private static boolean dataLoaded; protected WebRequest testRequest = Mockito.mock(WebRequest.class); - private static boolean dataLoaded = false; - - protected abstract IndexedSearchService<?> getIndex(); + @Autowired + private EventBus eventBus; @Before public void setup() throws InterruptedException { if (!dataLoaded) { - getIndex().rebuildIndex(); - Thread.sleep(500); + eventBus.post(new RebuildIndexEvent(indicesToTest)); + Thread.sleep(1000); dataLoaded = true; } testRequest = Mockito.mock(WebRequest.class); diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java index 384a0e10e..4f61f012e 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -10,10 +10,10 @@ import gov.nysenate.openleg.client.view.entity.FullMemberView; import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.ApiTest; +import gov.nysenate.openleg.dao.base.SearchIndex; import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.*; import gov.nysenate.openleg.model.search.SearchException; -import gov.nysenate.openleg.service.base.search.IndexedSearchService; import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.beans.factory.annotation.Autowired; @@ -25,19 +25,17 @@ @Category(UnitTest.class) public class MemberGetCtrlTest extends ApiTest { + static { + indicesToTest.add(SearchIndex.MEMBER); + } @Autowired private MemberGetCtrl testCtrl; - @Override - protected IndexedSearchService<?> getIndex() { - return ((IndexedSearchService<?>) testCtrl.memberSearch); - } - /** * Ensures the MemberGetCtrl has been properly injected. Importantly is run before other tests. */ @Test - public void getAllMembersTest() throws SearchException, MemberNotFoundEx, InterruptedException { + public void getAllMembersTest() throws SearchException, MemberNotFoundEx { testCtrl.getAllMembers("shortName:asc", true, testRequest); } diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java index a32a36321..886d9aa92 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java @@ -5,10 +5,10 @@ import gov.nysenate.openleg.client.view.entity.FullMemberView; import gov.nysenate.openleg.client.view.entity.SessionMemberView; import gov.nysenate.openleg.controller.api.ApiTest; +import gov.nysenate.openleg.dao.base.SearchIndex; import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.entity.*; import gov.nysenate.openleg.model.search.SearchException; -import gov.nysenate.openleg.service.base.search.IndexedSearchService; import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.beans.factory.annotation.Autowired; @@ -19,14 +19,12 @@ @Category(UnitTest.class) public class MemberSearchCtrlTest extends ApiTest { + static { + indicesToTest.add(SearchIndex.MEMBER); + } @Autowired private MemberSearchCtrl testCtrl; - @Override - protected IndexedSearchService<?> getIndex() { - return ((IndexedSearchService<?>) testCtrl.memberSearch); - } - @Test public void aSimpleTest() throws SearchException { Person testP = new Person(498, "Greene", "", "", "", "", "", "", false, "no_image.jpg"); @@ -42,7 +40,7 @@ public void aSimpleTest() throws SearchException { @Test public void searchBySessionMemberId() throws SearchException { - Person testP = new Person(499, "Edward Hennessey", "Edward", "", "Hennessey", "", "", "", true, "no_image.jpg"); + Person testP = new Person(499, "Edward Hennessey", "Edward", null, "Hennessey", null, null, null, true, "no_image.jpg"); Member testM = new Member(testP, 677, Chamber.ASSEMBLY, false); SessionMember testSm = new SessionMember(666, testM, "HENNESSEY", new SessionYear(2013), 3, false); SessionMember testSmAlt = new SessionMember(testSm); From b5ed85012a1971ffbd3487307f97a1029de1e191 Mon Sep 17 00:00:00 2001 From: Jacob Keegan <jacobmkeegan@gmail.com> Date: Wed, 25 Mar 2020 12:21:14 -0400 Subject: [PATCH 25/29] Changed how indices are rebuilt to remove arbitrary waiting. --- .../openleg/controller/api/ApiTest.java | 28 ++++++++++++++----- .../controller/api/entity/MemberApiTest.java | 5 ---- .../api/entity/MemberGetCtrlTest.java | 17 ++++++----- .../api/entity/MemberSearchCtrlTest.java | 20 +++++++++++-- 4 files changed, 46 insertions(+), 24 deletions(-) delete mode 100644 src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java diff --git a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java index df26c80b0..4bba3d460 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/ApiTest.java @@ -4,6 +4,7 @@ import gov.nysenate.openleg.BaseTests; import gov.nysenate.openleg.dao.base.SearchIndex; import gov.nysenate.openleg.model.search.RebuildIndexEvent; +import gov.nysenate.openleg.model.search.SearchException; import org.junit.Before; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; @@ -12,18 +13,31 @@ import java.util.EnumSet; public abstract class ApiTest extends BaseTests { - protected static EnumSet<SearchIndex> indicesToTest = EnumSet.noneOf(SearchIndex.class); - private static boolean dataLoaded; + private static EnumSet<SearchIndex> rebuiltIndices = EnumSet.noneOf(SearchIndex.class); protected WebRequest testRequest = Mockito.mock(WebRequest.class); @Autowired private EventBus eventBus; + /** + * Forces subclasses to have an associated SearchIndex. + * @return The relevent SearchIndex. + */ + protected abstract SearchIndex getIndex(); + + protected abstract int allItemsInIndex() throws SearchException; + @Before - public void setup() throws InterruptedException { - if (!dataLoaded) { - eventBus.post(new RebuildIndexEvent(indicesToTest)); - Thread.sleep(1000); - dataLoaded = true; + public void setup() throws InterruptedException, SearchException { + if (!rebuiltIndices.contains(getIndex())) { + // Data should only be loaded once for all relevant indices. + eventBus.post(new RebuildIndexEvent(EnumSet.of(getIndex()))); + int currItemsInIndex = 0; + // While an index is rebuilding, the number of things in it will be changing. + while (currItemsInIndex == 0 || currItemsInIndex != allItemsInIndex()) { + currItemsInIndex = allItemsInIndex(); + Thread.sleep(100); + } + rebuiltIndices.add(getIndex()); } testRequest = Mockito.mock(WebRequest.class); Mockito.when(testRequest.getParameter(Mockito.anyString())).thenReturn(null); diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java deleted file mode 100644 index a59b32894..000000000 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberApiTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package gov.nysenate.openleg.controller.api.entity; - -public abstract class MemberApiTest { - -} diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java index 4f61f012e..41b5a505e 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -25,18 +25,17 @@ @Category(UnitTest.class) public class MemberGetCtrlTest extends ApiTest { - static { - indicesToTest.add(SearchIndex.MEMBER); - } @Autowired private MemberGetCtrl testCtrl; - /** - * Ensures the MemberGetCtrl has been properly injected. Importantly is run before other tests. - */ - @Test - public void getAllMembersTest() throws SearchException, MemberNotFoundEx { - testCtrl.getAllMembers("shortName:asc", true, testRequest); + @Override + protected SearchIndex getIndex() { + return SearchIndex.MEMBER; + } + + @Override + protected int allItemsInIndex() throws SearchException { + return ((ListViewResponse<?>)testCtrl.getAllMembers("shortName:asc", true, testRequest)).getTotal(); } /** diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java index 886d9aa92..1cff64ccb 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java @@ -19,12 +19,23 @@ @Category(UnitTest.class) public class MemberSearchCtrlTest extends ApiTest { - static { - indicesToTest.add(SearchIndex.MEMBER); - } + @Autowired private MemberSearchCtrl testCtrl; + @Override + protected SearchIndex getIndex() { + return SearchIndex.MEMBER; + } + + @Override + protected int allItemsInIndex() throws SearchException { + return ((ListViewResponse<?>) testCtrl.globalSearch("*", "", false, testRequest)).getTotal(); + } + + /** + * Tests that a Member is retrieved correctly. + */ @Test public void aSimpleTest() throws SearchException { Person testP = new Person(498, "Greene", "", "", "", "", "", "", false, "no_image.jpg"); @@ -38,6 +49,9 @@ public void aSimpleTest() throws SearchException { assertTrue(actualSm.exactEquals(testSm)); } + /** + * Tests that session members are retrieved correctly. + */ @Test public void searchBySessionMemberId() throws SearchException { Person testP = new Person(499, "Edward Hennessey", "Edward", null, "Hennessey", null, null, null, true, "no_image.jpg"); From 8acdee6c48b43a5e64bfc81afac61326b902d5de Mon Sep 17 00:00:00 2001 From: Kevin Caseiras <kcaseiras@gmail.com> Date: Mon, 30 Mar 2020 10:33:24 -0400 Subject: [PATCH 26/29] Convert member api test to integration tests --- .../openleg/controller/api/entity/MemberGetCtrlTest.java | 4 ++-- .../openleg/controller/api/entity/MemberSearchCtrlTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java index 41b5a505e..e7f478d50 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberGetCtrlTest.java @@ -1,6 +1,6 @@ package gov.nysenate.openleg.controller.api.entity; -import gov.nysenate.openleg.annotation.UnitTest; +import gov.nysenate.openleg.annotation.IntegrationTest; import gov.nysenate.openleg.client.response.base.BaseResponse; import gov.nysenate.openleg.client.response.base.ListViewResponse; import gov.nysenate.openleg.client.response.base.PaginationResponse; @@ -23,7 +23,7 @@ import static org.junit.Assert.*; -@Category(UnitTest.class) +@Category(IntegrationTest.class) public class MemberGetCtrlTest extends ApiTest { @Autowired private MemberGetCtrl testCtrl; diff --git a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java index 1cff64ccb..6a333829d 100644 --- a/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java +++ b/src/test/java/gov/nysenate/openleg/controller/api/entity/MemberSearchCtrlTest.java @@ -1,6 +1,6 @@ package gov.nysenate.openleg.controller.api.entity; -import gov.nysenate.openleg.annotation.UnitTest; +import gov.nysenate.openleg.annotation.IntegrationTest; import gov.nysenate.openleg.client.response.base.ListViewResponse; import gov.nysenate.openleg.client.view.entity.FullMemberView; import gov.nysenate.openleg.client.view.entity.SessionMemberView; @@ -17,7 +17,7 @@ import static org.junit.Assert.*; -@Category(UnitTest.class) +@Category(IntegrationTest.class) public class MemberSearchCtrlTest extends ApiTest { @Autowired From 8cdacd87aed726b41f0b92677dd51f0b8d3f5afe Mon Sep 17 00:00:00 2001 From: Julien <23423640+iaace-NA@users.noreply.github.com> Date: Thu, 7 May 2020 11:46:33 -0400 Subject: [PATCH 27/29] Adds the TEMPLATE bill text format and TextDiff text processing Changes the XML bill text processing to be processed into a new TextDiff format. The TextDiff format is a reusable representation of bill text which is not tied to any text format. It can be converted into plain text, html text, or template text. This allows us to store a single representation of bill text while still providing multiple formats through our API. * Add to BillTextUtils/Test: conversion to HTML5 with/without custom tags, final text, and text diff. * Fix some warnings in BillTextUtils Warnings related to performance, documentation, and conciseness. * Add Diff JSON and HTML5 formats for bill amendment full text format fullTextFormat param options: HTML5 for html5 compliant version of full text with "ol-text-" class <span>s, DIFF for JSON diff-match-patch style diff list for plaintext and html. Adds columns to bill_amendment table for full_text_html5 and full_text_diff as text fields. For DIFF.type == 0: text unchanged For DIFF.type == 1: text added For DIFF.type == -1: text removed * Add comments for new database columns master.bill_amendment.full_text_html5/full_text_diff * Update API docs to add new fullTextFormats HTML5 and DIFF * Update API docs to add new fullTextFormats HTML5 and DIFF to JSON example * Bugfix to XmlBillTextProcessor where BillTextUtils.toHTML5() was used instead of BillTextUtils.toHTML5WithTags() * Add to XmlBillTextProcessorIT to check HTML5 and DIFF fullTextFormats * BillTextUtilsTest now checks DIFF format for JSON valid syntax * Rename some methods in BillTextUtils * Adds tests for processing xml text into a BillText * Update the bill text diff processor - Move the bill text diff processor into its own class. - Add processing logic to parse our bold and header elements. - Write unit tests for new functionality. * Unit tests and functionality to convert TextDiff to plain text format * Adds support for the new HTML bill text format Also fixes some whitespace and newline issues when converting TextDiff's into the plain text format. * add bill TextDiff support for page breaks * Creates and refactors to use a TextDiffType Enum TextDiffType contains info on whether change was unchanged, added, or removed. Also contains information for creating various types of text formats. * Add support for converting TextDiff's into HTML format * Refactor DAO layer for new BillText object * Refactor views and controllers for BillText * Add an index column to the bill_amendment_text_diff table The index column is used to keep the diffs in the correct order. * Fix fields used for bill text format * Add full text check to the openleg to openleg spotcheck report * Remove html escaping of ampersands done by jsoup * Add index to bill text diff table * Bug fixes to the bill text diff processor - Remove html escaping of greater than and less than * Add BillTextDiffProcessor test cases for multiple pages * Rewrite bill text diff processor to use Jsoup's depth first search * Modify text diff plain text format to match sobi styling * Adjust bill text spotcheck reports to work with new text format * Add support for template bill text format * Fix remaining failing unit tests * Script for initializing bill_amendment_text_diff table * Ensure BillText fields are not null, add back html mismatch type * Update documentation for TextDiff implementation Co-authored-by: Kevin Caseiras <kcaseiras@gmail.com> --- docs/api/bills.rst | 79 ++-- .../client/view/bill/BillAmendmentView.java | 29 +- .../openleg/client/view/bill/BillPdfView.java | 7 +- .../openleg/client/view/bill/BillView.java | 7 +- .../client/view/bill/DetailBillView.java | 6 +- .../openleg/config/ApplicationConfig.java | 1 + .../api/admin/TextDiffInitCtrl.java | 67 +++ .../controller/api/bill/BillGetCtrl.java | 28 +- .../controller/api/bill/BillSearchCtrl.java | 2 +- .../controller/api/bill/BillUpdatesCtrl.java | 2 +- .../openleg/controller/pdf/BillPdfCtrl.java | 3 +- .../nysenate/openleg/dao/base/SqlTable.java | 1 + .../openleg/dao/bill/data/BillDao.java | 12 +- .../openleg/dao/bill/data/SqlBillDao.java | 133 +++--- .../openleg/dao/bill/data/SqlBillQuery.java | 55 ++- .../dao/bill/search/ElasticBillSearchDao.java | 34 +- .../openleg/model/bill/BillAmendment.java | 31 +- .../nysenate/openleg/model/bill/BillText.java | 145 +++++++ .../openleg/model/bill/BillTextFormat.java | 2 +- .../nysenate/openleg/model/bill/TextDiff.java | 113 +++++ .../openleg/model/bill/TextDiffType.java | 61 +++ .../spotcheck/SpotCheckMismatchType.java | 2 +- .../processor/base/AbstractDataProcessor.java | 2 +- .../processor/bill/AbstractBillProcessor.java | 4 +- .../processor/bill/BillSobiProcessor.java | 3 +- .../processor/bill/BillTextDiffProcessor.java | 136 ++++++ .../processor/bill/TextDiffSearch.java | 92 ++++ .../processor/bill/XmlBillTextProcessor.java | 27 +- .../bill/sponsor/XmlSenMemoProcessor.java | 2 +- .../service/bill/data/BillDataService.java | 13 +- .../bill/data/CachedBillDataService.java | 13 +- .../bill/search/ElasticBillSearchService.java | 7 +- .../bill/BillScrapeReferenceFactory.java | 15 +- .../bill/BillScrapeReferenceHtmlParser.java | 2 +- .../spotcheck/base/SpotcheckRunService.java | 1 + .../openleg/OpenlegBillCheckService.java | 12 +- .../openleg/OpenlegBillReportService.java | 4 +- .../scrape/BillScrapeCheckService.java | 41 +- .../scrape/BillScrapeReportService.java | 2 +- .../bill/SenateSiteBillCheckService.java | 2 +- .../nysenate/openleg/util/BillTextUtils.java | 53 +-- .../V20200325.0913__add_text_diff_table.sql | 18 + .../openleg/dao/bill/SqlBillDaoTest.java | 6 +- .../openleg/model/bill/TextDiffTest.java | 129 ++++++ .../processor/BaseXmlProcessorTest.java | 9 +- .../bill/BillTextDiffProcessorTest.java | 399 ++++++++++++++++++ .../bill/text/BillTextUtilsTest.java | 109 +++-- .../bill/text/XmlBillTextProcessorIT.java | 23 +- .../nysenate/openleg/stupid/BillTextTest.java | 198 --------- .../openleg/testNonHTMLTextParse.java | 15 - ...-01-01-00.00.00.000000_BILLTEXT_S09999.XML | 57 --- .../processor/bill/text/S9999_expected.html5 | 54 +++ .../processor/bill/text/S9999_expected.json | 1 + 53 files changed, 1613 insertions(+), 656 deletions(-) create mode 100644 src/main/java/gov/nysenate/openleg/controller/api/admin/TextDiffInitCtrl.java create mode 100644 src/main/java/gov/nysenate/openleg/model/bill/BillText.java create mode 100644 src/main/java/gov/nysenate/openleg/model/bill/TextDiff.java create mode 100644 src/main/java/gov/nysenate/openleg/model/bill/TextDiffType.java create mode 100644 src/main/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessor.java create mode 100644 src/main/java/gov/nysenate/openleg/processor/bill/TextDiffSearch.java create mode 100644 src/main/resources/sql/migrations/V20200325.0913__add_text_diff_table.sql create mode 100644 src/test/java/gov/nysenate/openleg/model/bill/TextDiffTest.java create mode 100644 src/test/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessorTest.java delete mode 100644 src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java delete mode 100644 src/test/java/gov/nysenate/openleg/testNonHTMLTextParse.java delete mode 100644 src/test/resources/processor/bill/text/2017-01-01-00.00.00.000000_BILLTEXT_S09999.XML create mode 100644 src/test/resources/processor/bill/text/S9999_expected.html5 create mode 100644 src/test/resources/processor/bill/text/S9999_expected.json diff --git a/docs/api/bills.rst b/docs/api/bills.rst index fc81d6fa3..4c93f7a38 100644 --- a/docs/api/bills.rst +++ b/docs/api/bills.rst @@ -23,7 +23,8 @@ Retrieve bill by session year and print no +----------------+----------------------------------------------------------------------------------------------+ | version | If view=only_fulltext, use the version to specify the amendment letter, e.g. version=A | +----------------+----------------------------------------------------------------------------------------------+ -| fullTextFormat | (PLAIN or HTML) Which bill text formats will be included. Multiple formats can be requested. | +| fullTextFormat | (PLAIN, HTML, TEMPLATE) Which bill text formats will be included. | +| | Multiple formats can be requested. | +----------------+----------------------------------------------------------------------------------------------+ View options @@ -129,6 +130,7 @@ Default Bill Response "": { // Map of Amendment versions "basePrintNo": "S2180", // Bill print no/session details duplicated here "session": 2013, + "basePrintNoStr": "S2180-2013", "printNo": "S2180", "version": "", // Amendment version "publishDate": "2013-01-14", // Date this amendment was published @@ -145,7 +147,10 @@ Default Bill Response "lawSection": "Penal Law", // The primary section of law this bill impacts. "lawCode": "Add §265.18, Pen L", // A code that states the actions being taken on specific portions of law. "actClause": "AN ACT to amend the..", // An Act to Clause + "fullTextFormats": [ "PLAIN" ], "fullText": "...", // Full text of the bill amendment + "fullTextHtml": null, + "fullTextTemplate": null, "coSponsors": { // List of co sponsors "items": [ { @@ -342,23 +347,23 @@ List bills within a session year **Optional Params** -+----------------+--------------------+--------------------------------------------------------+ -| Parameter | Values | Description | -+================+====================+========================================================+ -| limit | 1 - 1000 | Number of results to return | -+----------------+--------------------+--------------------------------------------------------+ -| offset | >= 1 | Result number to start from | -+----------------+--------------------+--------------------------------------------------------+ -| full | boolean | Set to true to see the full bill responses. | -+----------------+--------------------+--------------------------------------------------------+ -| idsOnly | boolean | Set to true to see only the printNo and session | -| | | for each bill. (overrides 'full' parameter) | -+----------------+--------------------+--------------------------------------------------------+ -| sort | string | Sort by any field from the response. | -+----------------+--------------------+--------------------------------------------------------+ -| fullTextFormat | (PLAIN or HTML) | Which bill text formats will be included. | -| | | Multiple formats can be requested. | -+----------------+--------------------+--------------------------------------------------------+ ++----------------+------------------------------+--------------------------------------------------------+ +| Parameter | Values | Description | ++================+==============================+========================================================+ +| limit | 1 - 1000 | Number of results to return | ++----------------+------------------------------+--------------------------------------------------------+ +| offset | >= 1 | Result number to start from | ++----------------+------------------------------+--------------------------------------------------------+ +| full | boolean | Set to true to see the full bill responses. | ++----------------+------------------------------+--------------------------------------------------------+ +| idsOnly | boolean | Set to true to see only the printNo and session | +| | | for each bill. (overrides 'full' parameter) | ++----------------+------------------------------+--------------------------------------------------------+ +| sort | string | Sort by any field from the response. | ++----------------+------------------------------+--------------------------------------------------------+ +| fullTextFormat | (PLAIN, HTML or TEMPLATE) | Which bill text formats will be included. | +| | | Multiple formats can be requested. | ++----------------+------------------------------+--------------------------------------------------------+ **Default Sort Order** @@ -489,25 +494,25 @@ List of bills updated since the given date/time **Optional Params** -+----------------+----------------------+--------------------------------------------------------+ -| Parameter | Values | Description | -+================+======================+========================================================+ -| type | (processed|published)| The type of bill update (see below for explanation) | -+----------------+----------------------+--------------------------------------------------------+ -| detail | boolean | Set to true to see `detailed update digests`_ | -+----------------+----------------------+--------------------------------------------------------+ -| filter | string | Filter by update type. See `update filters`_ | -+----------------+----------------------+--------------------------------------------------------+ -| order | string (asc|desc) | Order the results by update date/time | -+----------------+----------------------+--------------------------------------------------------+ -| summary | boolean | Include a bill info response per item | -+----------------+----------------------+--------------------------------------------------------+ -| fullBill | boolean | Include a bill info response per item | -+----------------+----------------------+--------------------------------------------------------+ -| fullTextFormat | (PLAIN or HTML) | Which bill text formats will be included | -| | | if full bills are requested. | -| | | Multiple formats can be requested. | -+----------------+----------------------+--------------------------------------------------------+ ++----------------+---------------------------+--------------------------------------------------------+ +| Parameter | Values | Description | ++================+===========================+========================================================+ +| type | (processed|published) | The type of bill update (see below for explanation) | ++----------------+---------------------------+--------------------------------------------------------+ +| detail | boolean | Set to true to see `detailed update digests`_ | ++----------------+---------------------------+--------------------------------------------------------+ +| filter | string | Filter by update type. See `update filters`_ | ++----------------+---------------------------+--------------------------------------------------------+ +| order | string (asc|desc) | Order the results by update date/time | ++----------------+---------------------------+--------------------------------------------------------+ +| summary | boolean | Include a bill info response per item | ++----------------+---------------------------+--------------------------------------------------------+ +| fullBill | boolean | Include a bill info response per item | ++----------------+---------------------------+--------------------------------------------------------+ +| fullTextFormat | (PLAIN, HTML or TEMPLATE) | Which bill text formats will be included | +| | | if full bills are requested. | +| | | Multiple formats can be requested. | ++----------------+---------------------------+--------------------------------------------------------+ .. warning:: By default the type is set to 'processed'. Ensure you have the right type in the api request so you receive the results you are looking for diff --git a/src/main/java/gov/nysenate/openleg/client/view/bill/BillAmendmentView.java b/src/main/java/gov/nysenate/openleg/client/view/bill/BillAmendmentView.java index 33e8f9f80..76317d833 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/bill/BillAmendmentView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/bill/BillAmendmentView.java @@ -11,10 +11,9 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import static gov.nysenate.openleg.model.bill.BillTextFormat.*; - @JsonIgnoreProperties(ignoreUnknown = true) public class BillAmendmentView extends BillIdView { @@ -25,16 +24,17 @@ public class BillAmendmentView extends BillIdView protected String lawCode; protected String actClause; protected List<BillTextFormat> fullTextFormats; - protected String fullText; - protected String fullTextHtml; + protected String fullText = ""; + protected String fullTextHtml = ""; + protected String fullTextTemplate = ""; protected ListView<MemberView> coSponsors; protected ListView<MemberView> multiSponsors; protected boolean uniBill; protected boolean isStricken; - protected BillAmendmentView(){} + public BillAmendmentView(){} - public BillAmendmentView(BillAmendment billAmendment, PublishStatus publishStatus) { + public BillAmendmentView(BillAmendment billAmendment, PublishStatus publishStatus, Set<BillTextFormat> fullTextFormats) { super(billAmendment != null ? billAmendment.getBillId() : null); if (billAmendment != null) { this.publishDate = publishStatus.getEffectDateTime().toLocalDate(); @@ -45,9 +45,16 @@ public BillAmendmentView(BillAmendment billAmendment, PublishStatus publishStatu this.lawSection = billAmendment.getLawSection(); this.lawCode = billAmendment.getLaw(); this.actClause = billAmendment.getActClause(); - this.fullTextFormats = new ArrayList<>(billAmendment.getFullTextFormats()); - this.fullText = BillTextUtils.formatBillText(billAmendment.isResolution(), billAmendment.getFullText(PLAIN)); - this.fullTextHtml = billAmendment.getFullText(HTML); + this.fullTextFormats = new ArrayList<>(fullTextFormats); + if (this.fullTextFormats.contains(BillTextFormat.PLAIN)) { + this.fullText = BillTextUtils.getPlainTextWithoutLineNumbers(billAmendment); + } + if (this.fullTextFormats.contains(BillTextFormat.HTML)) { + this.fullTextHtml = billAmendment.getFullText(BillTextFormat.HTML); + } + if (this.fullTextFormats.contains(BillTextFormat.TEMPLATE)) { + this.fullTextTemplate = billAmendment.getFullText(BillTextFormat.TEMPLATE); + } this.coSponsors = ListView.of(billAmendment.getCoSponsors().stream() .map(MemberView::new) .collect(Collectors.toList())); @@ -119,4 +126,8 @@ public List<BillTextFormat> getFullTextFormats() { public String getFullTextHtml() { return fullTextHtml; } + + public String getFullTextTemplate() { + return fullTextTemplate; + } } diff --git a/src/main/java/gov/nysenate/openleg/client/view/bill/BillPdfView.java b/src/main/java/gov/nysenate/openleg/client/view/bill/BillPdfView.java index a0563c58f..ad9186087 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/bill/BillPdfView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/bill/BillPdfView.java @@ -70,11 +70,8 @@ public static void writeBillPdf(Bill bill, Version version, OutputStream outputS throw new BillAmendNotFoundEx(bill.getBaseBillId().withVersion(version)); } BillAmendment ba = bill.getAmendment(version); - Set<BillTextFormat> availableFormats = ba.getFullTextFormats(); - BillTextFormat format = PLAIN; - if (availableFormats.contains(HTML) && StringUtils.isNotBlank(ba.getFullText(HTML))) { - format = HTML; - } + + BillTextFormat format = ba.getFullText(HTML).length() > 0 ? HTML : PLAIN; String fullText = ba.getFullText(format); switch (format) { case HTML: diff --git a/src/main/java/gov/nysenate/openleg/client/view/bill/BillView.java b/src/main/java/gov/nysenate/openleg/client/view/bill/BillView.java index f18a3c70e..d519bc51c 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/bill/BillView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/bill/BillView.java @@ -9,8 +9,10 @@ import gov.nysenate.openleg.client.view.committee.CommitteeVersionIdView; import gov.nysenate.openleg.client.view.entity.MemberView; import gov.nysenate.openleg.model.bill.Bill; +import gov.nysenate.openleg.model.bill.BillTextFormat; import java.util.ArrayList; +import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; @@ -34,14 +36,15 @@ public class BillView extends BillInfoView implements ViewObject protected ListView<CalendarIdView> calendars; public BillView(){} - public BillView(Bill bill) { + + public BillView(Bill bill, Set<BillTextFormat> fullTextFormats) { super(bill != null ? bill.getBillInfo() : null); if (bill != null) { // Only output amendments that are currently published TreeMap<String, BillAmendmentView> amendmentMap = new TreeMap<>(); bill.getAmendPublishStatusMap().forEach((k,v) -> { if (v.isPublished() && bill.hasAmendment(k)) { - amendmentMap.put(k.toString(), new BillAmendmentView(bill.getAmendment(k), v)); + amendmentMap.put(k.toString(), new BillAmendmentView(bill.getAmendment(k), v, fullTextFormats)); } }); diff --git a/src/main/java/gov/nysenate/openleg/client/view/bill/DetailBillView.java b/src/main/java/gov/nysenate/openleg/client/view/bill/DetailBillView.java index 2d3b9580b..8605f367b 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/bill/DetailBillView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/bill/DetailBillView.java @@ -5,6 +5,7 @@ import gov.nysenate.openleg.model.bill.BaseBillId; import gov.nysenate.openleg.model.bill.Bill; import gov.nysenate.openleg.model.bill.BillId; +import gov.nysenate.openleg.model.bill.BillTextFormat; import gov.nysenate.openleg.service.bill.data.BillDataService; import gov.nysenate.openleg.service.bill.data.BillNotFoundEx; import org.slf4j.Logger; @@ -12,6 +13,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * A BillView with some extra details. @@ -27,8 +29,8 @@ public class DetailBillView extends BillView implements ViewObject /** --- Constructors --- */ - public DetailBillView(Bill bill, BillDataService billDataService) { - super(bill); + public DetailBillView(Bill bill, BillDataService billDataService, Set<BillTextFormat> fullTextFormats) { + super(bill, fullTextFormats); Map<String, BillInfoView> refs = new HashMap<>(); // Previous version refs diff --git a/src/main/java/gov/nysenate/openleg/config/ApplicationConfig.java b/src/main/java/gov/nysenate/openleg/config/ApplicationConfig.java index 73f47e04f..90977c346 100644 --- a/src/main/java/gov/nysenate/openleg/config/ApplicationConfig.java +++ b/src/main/java/gov/nysenate/openleg/config/ApplicationConfig.java @@ -178,6 +178,7 @@ public ThreadPoolTaskExecutor getAsyncExecutor() { executor.setMaxPoolSize(12); executor.setQueueCapacity(100); executor.initialize(); + executor.shutdown(); return executor; } diff --git a/src/main/java/gov/nysenate/openleg/controller/api/admin/TextDiffInitCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/admin/TextDiffInitCtrl.java new file mode 100644 index 000000000..eaa726bec --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/controller/api/admin/TextDiffInitCtrl.java @@ -0,0 +1,67 @@ +package gov.nysenate.openleg.controller.api.admin; + +import gov.nysenate.openleg.client.response.base.BaseResponse; +import gov.nysenate.openleg.client.response.base.SimpleResponse; +import gov.nysenate.openleg.controller.api.base.BaseCtrl; +import gov.nysenate.openleg.dao.base.LimitOffset; +import gov.nysenate.openleg.dao.base.SortOrder; +import gov.nysenate.openleg.dao.bill.data.BillDao; +import gov.nysenate.openleg.model.base.SessionYear; +import gov.nysenate.openleg.model.bill.BaseBillId; +import gov.nysenate.openleg.model.bill.Bill; +import gov.nysenate.openleg.model.bill.BillAmendment; +import gov.nysenate.openleg.model.bill.BillText; +import gov.nysenate.openleg.processor.bill.BillTextDiffProcessor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.List; + +import static gov.nysenate.openleg.controller.api.base.BaseCtrl.BASE_ADMIN_API_PATH; + +/** + * A temporary api endpoint which will be used to initialize + * TextDiff data from our saved full_text_html data. + * + * Be sure to disable db triggers before running to prevent lots of entries in the bills updates api. + */ +@RestController +@RequestMapping(value = BASE_ADMIN_API_PATH + "/textdiff") +public class TextDiffInitCtrl extends BaseCtrl { + + private static final Logger logger = LoggerFactory.getLogger(TextDiffInitCtrl.class); + + @Autowired private BillDao sqlBilldao; + @Autowired private BillTextDiffProcessor textDiffProcessor; + + @RequiresPermissions("admin") + @RequestMapping(value = "", method = RequestMethod.GET) + public BaseResponse initTextDiffs() { + List<SessionYear> sessionYears = Arrays.asList(new SessionYear(2017), new SessionYear(2019)); + + logger.info("Starting to convert full_html_text into text diffs."); + + for (SessionYear sessionYear : sessionYears) { + List<BaseBillId> baseBillIds = sqlBilldao.getBillIds(sessionYear, LimitOffset.ALL, SortOrder.NONE); + for (BaseBillId id : baseBillIds) { + logger.info("Converting bill " + id.toString() + " to text diff format."); + Bill bill = sqlBilldao.getBill(id); + logger.info("Converting bill " + bill.getBaseBillId().toString() + " to text diff format."); + for (BillAmendment amend : bill.getAmendmentList()) { + String xmlFullText = sqlBilldao.getXmlFullText(amend.getBillId()); + BillText billText = textDiffProcessor.processBillText(xmlFullText); + amend.setBillText(billText); + sqlBilldao.updateBillAmendText(amend); + } + } + } + + return new SimpleResponse(true, "Done initializing textdiff's.", ""); + } +} diff --git a/src/main/java/gov/nysenate/openleg/controller/api/bill/BillGetCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/bill/BillGetCtrl.java index fa1d7a12a..2258264d1 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/bill/BillGetCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/bill/BillGetCtrl.java @@ -31,14 +31,10 @@ import org.springframework.web.context.request.WebRequest; import java.io.ByteArrayOutputStream; -import java.util.Collections; -import java.util.EnumSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; +import java.util.*; import java.util.stream.Collectors; import static gov.nysenate.openleg.controller.api.base.BaseCtrl.BASE_API_PATH; -import static gov.nysenate.openleg.model.bill.BillTextFormat.HTML; import static gov.nysenate.openleg.model.bill.BillTextFormat.PLAIN; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -109,7 +105,7 @@ public BaseResponse getBills(@PathVariable int sessionYear, return new BaseBillIdView(baseBillId); } if (full) { - return new BillView(billData.getBill(baseBillId, getFullTextFormats(webRequest))); + return new BillView(billData.getBill(baseBillId), getFullTextFormats(webRequest)); } return new BillInfoView(billData.getBillInfo(baseBillId)); }) @@ -139,13 +135,13 @@ public BaseResponse getBill(@PathVariable int sessionYear, @PathVariable String viewObject = new BillInfoView(billData.getBillInfo(baseBillId)); break; case WITH_REFS: - viewObject = new DetailBillView(billData.getBill(baseBillId, fullTextFormats), billData); + viewObject = new DetailBillView(billData.getBill(baseBillId), billData, fullTextFormats); break; case NO_FULLTEXT: - viewObject = new BillView(getFullTextStrippedBill(baseBillId)); + viewObject = new BillView(getFullTextStrippedBill(baseBillId), new HashSet<>()); break; case WITH_REFS_NO_FULLTEXT: - viewObject = new DetailBillView(getFullTextStrippedBill(baseBillId), billData); + viewObject = new DetailBillView(getFullTextStrippedBill(baseBillId), billData, new HashSet<>()); break; case ONLY_FULLTEXT: { Version amdVersion = Version.ORIGINAL; @@ -154,14 +150,14 @@ public BaseResponse getBill(@PathVariable int sessionYear, @PathVariable String } BillTextFormat firstFormat = fullTextFormats.stream().findFirst() .orElseThrow(() -> new IllegalStateException("No bill text formats available!")); - Bill bill = billData.getBill(baseBillId, Collections.singleton(firstFormat)); + Bill bill = billData.getBill(baseBillId); String fullText = bill.getAmendment(amdVersion).getFullText(firstFormat); viewObject = new BillFullTextView(bill.getBaseBillId(), amdVersion.toString(), fullText, firstFormat); break; } - default: viewObject = new BillView(billData.getBill(baseBillId, fullTextFormats)); + default: viewObject = new BillView(billData.getBill(baseBillId), fullTextFormats); } return new ViewObjectResponse<>(viewObject, "Data for bill " + baseBillId); } @@ -172,7 +168,7 @@ public BaseResponse getBill(@PathVariable int sessionYear, @PathVariable String * @return Bill */ private Bill getFullTextStrippedBill(BaseBillId baseBillId) { - return billData.getBill(baseBillId, Collections.emptySet()); + return billData.getBill(baseBillId); } /** @@ -190,7 +186,7 @@ private Bill getFullTextStrippedBill(BaseBillId baseBillId) { public ResponseEntity<byte[]> getBillPdf(@PathVariable int sessionYear, @PathVariable String printNo) throws Exception { BillId billId = getBillId(printNo, sessionYear, "printNo"); - Bill bill = billData.getBill(BaseBillId.of(billId), EnumSet.of(PLAIN, HTML)); + Bill bill = billData.getBill(BaseBillId.of(billId)); ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); BillPdfView.writeBillPdf(bill, billId.getVersion(), pdfBytes); HttpHeaders headers = new HttpHeaders(); @@ -211,11 +207,11 @@ public BaseResponse getBillDiff(@PathVariable int sessionYear, @PathVariable Str @PathVariable String version2) { StringDiffer stringDiffer = new StringDiffer(); BaseBillId baseBillId = getBaseBillId(printNo, sessionYear, "printNo"); - Bill bill = billData.getBill(baseBillId, Collections.singleton(PLAIN)); + Bill bill = billData.getBill(baseBillId); BillAmendment amend1 = bill.getAmendment(parseVersion(version1, "version1")); BillAmendment amend2 = bill.getAmendment(parseVersion(version2, "version2")); - String fullText1 = BillTextUtils.formatBillText(bill.isResolution(), amend1.getFullText(PLAIN)); - String fullText2 = BillTextUtils.formatBillText(bill.isResolution(), amend2.getFullText(PLAIN)); + String fullText1 = BillTextUtils.getPlainTextWithoutLineNumbers(amend1); + String fullText2 = BillTextUtils.getPlainTextWithoutLineNumbers(amend2); LinkedList<StringDiffer.Diff> diffs = stringDiffer.diff_main(fullText1, fullText2); stringDiffer.diff_cleanupEfficiency(diffs); stringDiffer.diff_cleanupSemantic(diffs); diff --git a/src/main/java/gov/nysenate/openleg/controller/api/bill/BillSearchCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/bill/BillSearchCtrl.java index e74d166fd..fca250cb8 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/bill/BillSearchCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/bill/BillSearchCtrl.java @@ -92,7 +92,7 @@ private BaseResponse getBillSearchResponse(SearchResults<BaseBillId> results, return ListViewResponse.of( results.getResults().stream() .map(r -> new SearchResultView((full) - ? new BillView(billData.getBill(r.getResult(), fullTextFormats)) + ? new BillView(billData.getBill(r.getResult()), fullTextFormats) : (idOnly) ? new BillIdView(r.getResult()) : new BillInfoView(billData.getBillInfo(r.getResult())), r.getRank(), r.getHighlights())) diff --git a/src/main/java/gov/nysenate/openleg/controller/api/bill/BillUpdatesCtrl.java b/src/main/java/gov/nysenate/openleg/controller/api/bill/BillUpdatesCtrl.java index adef570f9..aa9988a52 100644 --- a/src/main/java/gov/nysenate/openleg/controller/api/bill/BillUpdatesCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/api/bill/BillUpdatesCtrl.java @@ -160,7 +160,7 @@ private BaseResponse getUpdatesDuring(LocalDateTime from, LocalDateTime to, WebR if (fullBill) { Set<BillTextFormat> fullTextFormats = getFullTextFormats(request); return new UpdateTokenModelView(token, new BaseBillIdView(token.getId()), - new BillView(billData.getBill(token.getId(), fullTextFormats))); + new BillView(billData.getBill(token.getId()), fullTextFormats)); } if (summary) { return new UpdateTokenModelView(token, new BaseBillIdView(token.getId()), diff --git a/src/main/java/gov/nysenate/openleg/controller/pdf/BillPdfCtrl.java b/src/main/java/gov/nysenate/openleg/controller/pdf/BillPdfCtrl.java index ac990e779..cf0a3f777 100644 --- a/src/main/java/gov/nysenate/openleg/controller/pdf/BillPdfCtrl.java +++ b/src/main/java/gov/nysenate/openleg/controller/pdf/BillPdfCtrl.java @@ -56,8 +56,7 @@ public ResponseEntity<byte[]> getBillPdf(@PathVariable int sessionYear, @PathVar } response.sendRedirect(urlString); } else { - Set<BillTextFormat> fullTextFormats = EnumSet.of(PLAIN, HTML); - Bill bill = billData.getBill(BaseBillId.of(billId), fullTextFormats); + Bill bill = billData.getBill(BaseBillId.of(billId)); ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream(); BillPdfView.writeBillPdf(bill, billId.getVersion(), pdfBytes); HttpHeaders headers = new HttpHeaders(); diff --git a/src/main/java/gov/nysenate/openleg/dao/base/SqlTable.java b/src/main/java/gov/nysenate/openleg/dao/base/SqlTable.java index 84bf62d2a..f2c391cd9 100644 --- a/src/main/java/gov/nysenate/openleg/dao/base/SqlTable.java +++ b/src/main/java/gov/nysenate/openleg/dao/base/SqlTable.java @@ -43,6 +43,7 @@ public enum SqlTable BILL_AMENDMENT_PUBLISH_STATUS("bill_amendment_publish_status"), BILL_AMENDMENT_VOTE_INFO ("bill_amendment_vote_info"), BILL_AMENDMENT_VOTE_ROLL ("bill_amendment_vote_roll"), + BILL_AMENDMENT_TEXT_DIFF ("bill_amendment_text_diff"), BILL_APPROVAL ("bill_approval"), BILL_COMMITTEE ("bill_committee"), BILL_MULTI_SPONSOR ("bill_multi_sponsor"), diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/data/BillDao.java b/src/main/java/gov/nysenate/openleg/dao/bill/data/BillDao.java index 165676359..e64cde60d 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/data/BillDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/data/BillDao.java @@ -21,11 +21,10 @@ public interface BillDao * result was found. * * @param billId BillId - The version in the bill id is not used. - * @param textFormats {@link Set<BillTextFormat>} - specifies which text formats are loaded for the bill * @return Bill * @throws DataAccessException - If no bill was matched */ - Bill getBill(BillId billId, Set<BillTextFormat> textFormats) throws DataAccessException; + Bill getBill(BillId billId) throws DataAccessException; /** * Retrieves a BillInfo for the given BillId. The query time for a BillInfo will be less than that @@ -42,13 +41,10 @@ public interface BillDao * This can be used by caching implementations where the bill object is kept in memory but the references * to the full text and memo are dropped to save memory space. * - * Full text will be applied only in the given formats. - * * @param strippedBill Bill - The stripped Bill object. - * @param fullTextFormats {@link Set<BillTextFormat>} will apply texts for these formats. * @throws DataAccessException */ - void applyText(Bill strippedBill, Set<BillTextFormat> fullTextFormats) throws DataAccessException; + void applyTextAndMemo(Bill strippedBill) throws DataAccessException; /** * Gets a List of BaseBillIds for the given session year with options to order and limit the results. @@ -111,4 +107,8 @@ public interface BillDao * @return {@link List<BillId>} */ List<BillId> getBudgetBillIdsWithoutText(SessionYear sessionYear); + + String getXmlFullText(BillId billId); + + void updateBillAmendText(BillAmendment amend); } diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java index b49bfff3d..e1d515a31 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillDao.java @@ -1,6 +1,5 @@ package gov.nysenate.openleg.dao.bill.data; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import com.google.common.collect.Range; @@ -21,7 +20,6 @@ import gov.nysenate.openleg.service.bill.data.VetoNotFoundException; import gov.nysenate.openleg.service.entity.member.data.MemberService; import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.text.StringSubstitutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -42,8 +40,6 @@ import static gov.nysenate.openleg.dao.base.SortOrder.ASC; import static gov.nysenate.openleg.dao.bill.data.SqlBillQuery.*; -import static gov.nysenate.openleg.model.bill.BillTextFormat.HTML; -import static gov.nysenate.openleg.model.bill.BillTextFormat.PLAIN; import static gov.nysenate.openleg.util.CollectionUtils.difference; import static gov.nysenate.openleg.util.DateUtils.toDate; @@ -51,12 +47,6 @@ public class SqlBillDao extends SqlBaseDao implements BillDao { private static final Logger logger = LoggerFactory.getLogger(SqlBillDao.class); - private static final ImmutableMap<BillTextFormat, String> fullTextFields = - ImmutableMap.<BillTextFormat, String>builder() - .put(PLAIN, "full_text") - .put(HTML, "full_text_html") - .build(); - @Autowired private MemberService memberService; @Autowired private VetoDataService vetoDataService; @Autowired private ApprovalDataService approvalDataService; @@ -65,13 +55,13 @@ public class SqlBillDao extends SqlBaseDao implements BillDao { /** {@inheritDoc} */ @Override - public Bill getBill(BillId billId, Set<BillTextFormat> textFormats) { + public Bill getBill(BillId billId) { logger.trace("Fetching Bill {} from database...", billId); final ImmutableParams baseParams = getBaseParams(billId); // Retrieve base Bill object Bill bill = getBaseBill(baseParams); // Fetch the amendments - List<BillAmendment> billAmendments = getBillAmendments(baseParams, textFormats); + List<BillAmendment> billAmendments = getBillAmendments(baseParams); for (BillAmendment amendment : billAmendments) { final ImmutableParams amendParams = baseParams.add( new MapSqlParameterSource("version", amendment.getVersion().toString())); @@ -83,6 +73,8 @@ public Bill getBill(BillId billId, Set<BillTextFormat> textFormats) { amendment.setMultiSponsors(getMultiSponsors(amendParams)); // Get the votes amendment.setVotesMap(getBillVotes(amendParams)); + // Get BillText + amendment.setBillText(getBillText(amendParams)); } // Set the amendments bill.addAmendments(billAmendments); @@ -129,21 +121,23 @@ public BillInfo getBillInfo(BillId billId) throws DataAccessException { /** {@inheritDoc} */ @Override - public void applyText(Bill strippedBill, Set<BillTextFormat> fullTextFormats) throws DataAccessException { + public void applyTextAndMemo(Bill strippedBill) throws DataAccessException { if (strippedBill == null) { throw new IllegalArgumentException("Cannot apply bill text on a null bill"); } - MapSqlParameterSource billParams = new MapSqlParameterSource(); - addBillIdParams(strippedBill, billParams); - final String queryTemplate = SELECT_BILL_TEXT_TEMPLATE.getSql(schema()); - final String query = applyFullTextFields(queryTemplate, fullTextFormats); - jdbcNamed.query(query, billParams, (ResultSet rs) -> { - BillAmendment ba = strippedBill.getAmendment(Version.of(rs.getString("bill_amend_version"))); - ba.setMemo(rs.getString("sponsor_memo")); - for (BillTextFormat format : fullTextFormats) { - ba.setFullText(format, rs.getString(fullTextFields.get(format))); - } - }); + + for (Version version : strippedBill.getAmendmentMap().keySet()) { + MapSqlParameterSource params = new MapSqlParameterSource(); + addBillIdParams(strippedBill.getAmendment(version), params); + + // Set memo + jdbcNamed.query(SELECT_BILL_AMEND_MEMO.getSql(schema()), params, (ResultSet rs) -> { + strippedBill.getAmendment(version).setMemo(rs.getString("sponsor_memo")); + }); + // Set Text + BillText text = getBillText(params); + strippedBill.getAmendment(version).setBillText(text); + } } /** @@ -176,6 +170,8 @@ public void updateBill(Bill bill, LegDataFragment legDataFragment) { updateBillMultiSponsor(amendment, legDataFragment, amendParams); // Update votes updateBillVotes(amendment, legDataFragment, amendParams); + // Update bill text diffs + updateBillTextDiff(amendment, amendParams); } // Update the publish statuses of the amendments updateBillAmendPublishStatus(bill, legDataFragment, billParams); @@ -195,6 +191,11 @@ public void updateBill(Bill bill, LegDataFragment legDataFragment) { updateApprovalMessage(bill, legDataFragment); } + public void updateBillAmendText(BillAmendment amend) { + ImmutableParams params = getBillIdParams(amend.getBillId()); + updateBillTextDiff(amend, params); + } + /** {@inheritDoc} */ @Override public List<BaseBillId> getBillIds(SessionYear sessionYear, LimitOffset limOff, SortOrder billIdSort) throws DataAccessException { @@ -355,10 +356,9 @@ public LinkedList<BillStatus> getBillMilestones(ImmutableParams baseParams) { /** * Fetch the collection of bill amendment references for the base bill id in the params. */ - public List<BillAmendment> getBillAmendments(ImmutableParams baseParams, Set<BillTextFormat> fullTextFormats) { - final String queryTemplate = SELECT_BILL_AMENDMENTS_TEMPLATE.getSql(schema()); - final String fullQuery = applyFullTextFields(queryTemplate, fullTextFormats); - return jdbcNamed.query(fullQuery, baseParams, new BillAmendmentRowMapper(fullTextFormats)); + public List<BillAmendment> getBillAmendments(ImmutableParams baseParams) { + final String query = SELECT_BILL_AMENDMENTS.getSql(schema()); + return jdbcNamed.query(query, baseParams, new BillAmendmentRowMapper()); } /** @@ -422,6 +422,15 @@ public List<BillVote> getBillVotes(ImmutableParams baseParams) { return billVotes; } + private BillText getBillText(SqlParameterSource params) { + String plainText = ""; + List<TextDiff> diffs = jdbcNamed.query(SqlBillQuery.SELECT_BILL_AMEND_TEXT_DIFFS.getSql(schema()), params, new BillTextRowMapper()); + if (diffs.isEmpty()) { + plainText = jdbcNamed.queryForObject(SqlBillQuery.SELECT_BILL_AMEND_PLAIN_TEXT.getSql(schema()), params, String.class); + } + return new BillText(plainText, diffs); + } + /** * Get veto memos for the bill */ @@ -705,6 +714,32 @@ protected void updateBillVotes(BillAmendment billAmendment, LegDataFragment legD } } + private void updateBillTextDiff(BillAmendment billAmendment, ImmutableParams amendParams) { + // Delete old text + jdbcNamed.update(SqlBillQuery.DELETE_BILL_AMEND_TEXT_DIFFS.getSql(schema()), amendParams); + + // Insert new diffs + List<TextDiff> diffs = billAmendment.getBillText().getTextDiffs(); + List<ImmutableParams> params = new ArrayList<>(); + int index = 1; + for (TextDiff diff : diffs) { + params.add(amendParams.add(new MapSqlParameterSource() + .addValue("index", index) + .addValue("type", diff.getType().name()) + .addValue("text", diff.getText()))); + index++; + } + + String sql = SqlBillQuery.INSERT_BILL_AMEND_TEXT_DIFFS.getSql(schema()); + ImmutableParams[] batchParams = new ImmutableParams[params.size()]; + batchParams = params.toArray(batchParams); + jdbcNamed.batchUpdate(sql, batchParams); + + // Update the plain text + amendParams = amendParams.add(new MapSqlParameterSource("plainText", billAmendment.getBillText().getFullText(BillTextFormat.PLAIN))); + jdbcNamed.update(SqlBillQuery.UPDATE_BILL_AMEND_PLAIN_TEXT.getSql(schema()), amendParams); + } + public List<BillId> getBudgetBillIdsWithoutText(SessionYear sessionYear) { MapSqlParameterSource billParams = new MapSqlParameterSource(); billParams.addValue("sessionYear", sessionYear.getYear()); @@ -723,6 +758,12 @@ public List<BillId> getBudgetBillIdsWithoutText(SessionYear sessionYear) { )); } + @Override + public String getXmlFullText(BillId billId) { + ImmutableParams params = getBillIdParams(billId); + return jdbcNamed.queryForObject(SqlBillQuery.SELECT_XML_FULL_TEXT.getSql(schema()), params, String.class); + } + /* --- Helper Classes --- */ private List<Integer> getCoSponsorIds(SqlParameterSource params) { @@ -767,10 +808,8 @@ public Bill mapRow(ResultSet rs, int rowNum) throws SQLException { } private static class BillAmendmentRowMapper implements RowMapper<BillAmendment> { - private final Set<BillTextFormat> textFormats; - public BillAmendmentRowMapper(Set<BillTextFormat> textFormats) { - this.textFormats = textFormats; + public BillAmendmentRowMapper() { } @Override @@ -779,9 +818,6 @@ public BillAmendment mapRow(ResultSet rs, int rowNum) throws SQLException { BillAmendment amend = new BillAmendment(baseBillId, Version.of(rs.getString("bill_amend_version"))); amend.setMemo(rs.getString("sponsor_memo")); amend.setActClause(rs.getString("act_clause")); - for (BillTextFormat format : textFormats) { - amend.setFullText(format, rs.getString(fullTextFields.get(format))); - } amend.setStricken(rs.getBoolean("stricken")); amend.setUniBill(rs.getBoolean("uni_bill")); amend.setLawSection(rs.getString("law_section")); @@ -864,6 +900,16 @@ public CommitteeVersionId mapRow(ResultSet rs, int rowNum) throws SQLException { } } + private static class BillTextRowMapper implements RowMapper<TextDiff> { + + @Override + public TextDiff mapRow(ResultSet rs, int i) throws SQLException { + TextDiffType type = TextDiffType.valueOf(rs.getString("type")); + String text = rs.getString("text"); + return new TextDiff(type, text); + } + } + /** * --- Param Source Methods --- */ @@ -918,8 +964,6 @@ private static MapSqlParameterSource getBillAmendmentParams(BillAmendment amendm addBillIdParams(amendment, params); params.addValue("sponsorMemo", amendment.getMemo()) .addValue("actClause", amendment.getActClause()) - .addValue("fullText", amendment.getFullText(PLAIN)) - .addValue("fullTextHtml", amendment.getFullText(HTML)) .addValue("stricken", amendment.isStricken()) .addValue("lawSection", amendment.getLawSection()) .addValue("lawCode", amendment.getLaw()) @@ -1068,19 +1112,4 @@ private static CommitteeId getCommitteeIdFromRs(ResultSet rs) throws SQLExceptio } return null; } - - /** - * Apply the correct full text fields to a query template given a set of formats. - * - * Assumes that "fullTextFields" token is at the end of the column list WITHOUT a leading comma - * e.g. ... bill_amend_version, sponsor_memo ${fullTextFields} - */ - private String applyFullTextFields(String queryTemplate, Set<BillTextFormat> fullTextFormats) { - final String fullTextFields = fullTextFormats.stream() - .map(SqlBillDao.fullTextFields::get) - .map(field -> ", " + field) - .collect(Collectors.joining()); - final ImmutableMap<String, String> subMap = ImmutableMap.of("fullTextFields", fullTextFields); - return StringSubstitutor.replace(queryTemplate, subMap); - } -} \ No newline at end of file +} diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillQuery.java b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillQuery.java index 052538256..8e9a46684 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillQuery.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/data/SqlBillQuery.java @@ -76,12 +76,6 @@ public enum SqlBillQuery implements BasicSqlQuery /** --- Bill Text --- */ - SELECT_BILL_TEXT_TEMPLATE( - "SELECT bill_print_no, bill_session_year, bill_amend_version, sponsor_memo ${fullTextFields}\n" + - "FROM ${schema}.bill_amendment \n" + - "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear" - ), - SELECT_ALTERNATE_PDF_URL( "SELECT url_path \n" + "FROM ${schema}." + SqlTable.BILL_ALTERNATE_PDF + "\n" + @@ -89,28 +83,59 @@ public enum SqlBillQuery implements BasicSqlQuery "AND active = true" ), + SELECT_BILL_AMEND_TEXT_DIFFS( + "SELECT * FROM ${schema}." + SqlTable.BILL_AMENDMENT_TEXT_DIFF + "\n" + + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version\n" + + "ORDER BY index asc" + ), + + DELETE_BILL_AMEND_TEXT_DIFFS( + "DELETE FROM ${schema}." + SqlTable.BILL_AMENDMENT_TEXT_DIFF + "\n" + + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version" + ), + INSERT_BILL_AMEND_TEXT_DIFFS( + "INSERT INTO ${schema}." + SqlTable.BILL_AMENDMENT_TEXT_DIFF + "\n" + + "(bill_print_no, bill_session_year, bill_amend_version, index, type, text)\n" + + "VALUES (:printNo, :sessionYear, :version, :index, :type, :text)" + ), + + SELECT_BILL_AMEND_PLAIN_TEXT( + "SELECT full_text FROM ${schema}." + SqlTable.BILL_AMENDMENT + "\n" + + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version" + ), + + UPDATE_BILL_AMEND_PLAIN_TEXT( + "UPDATE ${schema}." + SqlTable.BILL_AMENDMENT + "\n" + + "SET full_text = :plainText\n" + + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version" + ), + /** --- Bill Amendment --- */ - SELECT_BILL_AMENDMENTS_TEMPLATE( + SELECT_BILL_AMEND_MEMO( + "SELECT sponsor_memo\n" + + "FROM ${schema}.bill_amendment \n" + + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version" + ), + + SELECT_BILL_AMENDMENTS( "SELECT bill_print_no, bill_session_year, bill_amend_version,\n" + " sponsor_memo, act_clause, stricken, uni_bill, law_section, law_code\n" + - " ${fullTextFields}\n" + "FROM ${schema}." + SqlTable.BILL_AMENDMENT + "\n" + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear" ), UPDATE_BILL_AMENDMENT( "UPDATE ${schema}." + SqlTable.BILL_AMENDMENT + "\n" + "SET sponsor_memo = :sponsorMemo, act_clause = :actClause,\n" + - " full_text = :fullText, full_text_html = :fullTextHtml,\n" + " stricken = :stricken, uni_bill = :uniBill, last_fragment_id = :lastFragmentId,\n" + " law_section = :lawSection, law_code = :lawCode\n" + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version" ), INSERT_BILL_AMENDMENT( "INSERT INTO ${schema}." + SqlTable.BILL_AMENDMENT + "\n" + - "(bill_print_no, bill_session_year, bill_amend_version, sponsor_memo, act_clause, full_text, full_text_html,\n" + + "(bill_print_no, bill_session_year, bill_amend_version, sponsor_memo, act_clause, \n" + " stricken, uni_bill, last_fragment_id, law_section, law_code)\n" + - "VALUES(:printNo, :sessionYear, :version, :sponsorMemo, :actClause, :fullText, :fullTextHtml,\n" + + "VALUES(:printNo, :sessionYear, :version, :sponsorMemo, :actClause, \n" + " :stricken, :uniBill, :lastFragmentId, :lawSection, :lawCode)" ), SELECT_EMPTY_TEXT_BUDGET_BILL_PRINT_NOS ( @@ -359,7 +384,13 @@ public enum SqlBillQuery implements BasicSqlQuery "FROM ${schema}." + SqlTable.CALENDAR_SUP_ENTRY + " cse\n" + "JOIN ${schema}." + SqlTable.CALENDAR_SUPPLEMENTAL + " cs ON cse.calendar_sup_id = cs.id\n" + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear" - ); + ), + + SELECT_XML_FULL_TEXT( + "SELECT full_text_html FROM ${schema}." + SqlTable.BILL_AMENDMENT + " \n" + + "WHERE bill_print_no = :printNo AND bill_session_year = :sessionYear AND bill_amend_version = :version" + ) + ; private String sql; diff --git a/src/main/java/gov/nysenate/openleg/dao/bill/search/ElasticBillSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/bill/search/ElasticBillSearchDao.java index 41e73fa43..3d80631d7 100644 --- a/src/main/java/gov/nysenate/openleg/dao/bill/search/ElasticBillSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/bill/search/ElasticBillSearchDao.java @@ -1,12 +1,14 @@ package gov.nysenate.openleg.dao.bill.search; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import gov.nysenate.openleg.client.view.bill.BillView; import gov.nysenate.openleg.dao.base.ElasticBaseDao; import gov.nysenate.openleg.dao.base.LimitOffset; import gov.nysenate.openleg.dao.base.SearchIndex; import gov.nysenate.openleg.model.bill.BaseBillId; import gov.nysenate.openleg.model.bill.Bill; +import gov.nysenate.openleg.model.bill.BillTextFormat; import gov.nysenate.openleg.model.search.SearchResults; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.common.settings.Settings; @@ -76,8 +78,7 @@ public void updateBillIndex(Bill bill) { public void updateBillIndex(Collection<Bill> bills) { BulkRequest bulkRequest = new BulkRequest(); bills.stream() - .map(this::stripNonPlainText) - .map(BillView::new) + .map(b -> new BillView(b, Sets.newHashSet(BillTextFormat.PLAIN))) .map(bv -> getJsonIndexRequest(billIndexName, toElasticId(bv.toBaseBillId()), bv)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); @@ -185,33 +186,4 @@ private String toElasticId(BaseBillId baseBillId) { return baseBillId.getSession() + "-" + baseBillId.getBasePrintNo(); } - - /** - * Ensure the passed in bill only contains plain text. - */ - private Bill stripNonPlainText(Bill bill) { - boolean allPlain = bill.getAmendmentList().stream().allMatch(a -> { - // If no plain text is present, throw exception. - if (!a.isTextFormatLoaded(PLAIN)) { - throw new IllegalArgumentException("Bills must have plain text loaded to index"); - } - return Collections.singleton(PLAIN).equals(a.getFullTextFormats()); - }); - if (allPlain) { - // Bill is good as is - return bill; - } - try { - // Create a clone with only plain text - Bill strippedBill = bill.shallowClone(); - strippedBill.getAmendmentList().forEach(amend -> { - String plainText = amend.getFullText(PLAIN); - amend.clearFullTexts(); - amend.setFullText(PLAIN, plainText); - }); - return strippedBill; - } catch (CloneNotSupportedException e) { - throw new IllegalStateException(e); - } - } } \ No newline at end of file diff --git a/src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java b/src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java index d975fb938..66cc5655a 100644 --- a/src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java +++ b/src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java @@ -1,6 +1,5 @@ package gov.nysenate.openleg.model.bill; -import com.google.common.collect.ImmutableSet; import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.base.Version; import gov.nysenate.openleg.model.entity.Chamber; @@ -42,8 +41,8 @@ public class BillAmendment implements Serializable, Cloneable /** The AN ACT TO... clause which describes the bill's intent. */ protected String actClause = ""; - /** The full bill text in various formats. Not all formats are always loaded to save space */ - protected Map<BillTextFormat, String> fullTextMap = new HashMap<>(); + /** The bill text **/ + private BillText billText = new BillText(""); /** The committee the bill is currently referred to, if any. */ protected CommitteeVersionId currentCommittee = null; @@ -84,30 +83,24 @@ public String toString() { /* --- Functional Getters --- */ - public ImmutableSet<BillTextFormat> getFullTextFormats() { - return ImmutableSet.copyOf(fullTextMap.keySet()); + public boolean isTextLoaded() { + return billText.isLoaded(); } - /** - * Checks that a {@link BillTextFormat} has been loaded for this amendment. - * - * When loading amendments from the DAO we can specify what formats we want loaded. - * This method allows us to verify a particular format has been loaded. - */ - public boolean isTextFormatLoaded(BillTextFormat format) { - return fullTextMap.containsKey(format); + public String getFullText(BillTextFormat format) { + return billText.getFullText(format); } - public String getFullText(BillTextFormat format) { - return fullTextMap.get(format); + public BillText getBillText() { + return this.billText; } - public void setFullText(BillTextFormat format, String fullText) { - fullTextMap.put(format, fullText); + public void setBillText(BillText billText) { + this.billText = billText; } public void clearFullTexts() { - fullTextMap.clear(); + this.billText = new BillText(); } /** @@ -117,7 +110,7 @@ public void clearFullTexts() { public BillAmendment shallowClone() { try { BillAmendment clone = (BillAmendment) this.clone(); - clone.fullTextMap = new HashMap<>(this.fullTextMap); + clone.billText = billText.shallowClone(); return clone; } catch (CloneNotSupportedException e) { diff --git a/src/main/java/gov/nysenate/openleg/model/bill/BillText.java b/src/main/java/gov/nysenate/openleg/model/bill/BillText.java new file mode 100644 index 000000000..7108b8e90 --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/model/bill/BillText.java @@ -0,0 +1,145 @@ +package gov.nysenate.openleg.model.bill; + +import gov.nysenate.openleg.util.BillTextUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class BillText implements Cloneable { + + private static final String HTML_STYLE = "<STYLE><!--u {color: Green}s {color: RED} i {color: DARKBLUE; background-color:yellow}\n"+ + "p.brk {page-break-before:always}--></STYLE>\n"; + + private String sobiPlainText; + private List<TextDiff> diffs; + + public BillText() { + this("", new ArrayList<>()); + } + + public BillText(String sobiPlainText) { + this(sobiPlainText, new ArrayList<>()); + } + + public BillText(List<TextDiff> diffs) { + this("", diffs); + } + + public BillText(String sobiPlainText, List<TextDiff> diffs) { + this.sobiPlainText = sobiPlainText == null ? "" : sobiPlainText; + this.diffs = diffs == null ? new ArrayList<>() : diffs; + } + + /** + * Checks if the text has been loaded into this BillText. + * @return + */ + public boolean isLoaded() { + return !(diffs.isEmpty() && sobiPlainText.isEmpty()); + } + + /** + * Get the full bill text in the specified format. + * <p> + * PLAIN format slightly alters the bill headers adding space between characters and centering. (See {@link BillTextUtils#formatHtmlExtractedBillText(String)}); + * <p> + * TextDiffs are required to create the HTML and TEMPLATE formats. TextDiffs are also used + * to create the PLAIN format when they are available. If TextDiffs are not available (i.e. SOBI data), + * the PLAIN format is taken straight from the SOBI text processor. + * + * @param format + * @return + */ + public String getFullText(BillTextFormat format) { + String text = ""; + switch (format) { + case PLAIN: + text = createPlainText(); + text = BillTextUtils.formatHtmlExtractedBillText(text); + break; + case HTML: + text = createHtmlText(); + break; + case TEMPLATE: + text = createTemplateText(); + break; + } + + return text; + } + + public List<TextDiff> getTextDiffs() { + return this.diffs; + } + + private String createPlainText() { + StringBuilder plainText = new StringBuilder(); + if (diffs.isEmpty() && !sobiPlainText.isEmpty()) { + plainText.append(sobiPlainText); + } + else { + for (TextDiff diff : this.diffs) { + plainText.append(diff.getPlainFormatText()); + } + } + return plainText.toString(); + } + + private String createHtmlText() { + StringBuilder htmlText = new StringBuilder(); + if (hasDiffs()) { + htmlText.append(HTML_STYLE); + htmlText.append("<pre>"); + for (TextDiff diff : this.diffs) { + htmlText.append(diff.getHtmlFormatText()); + } + htmlText.append("</pre>"); + } + return htmlText.toString(); + } + + private String createTemplateText() { + StringBuilder templateText = new StringBuilder(); + if (hasDiffs()) { + templateText.append("<pre class=\"ol-bill-text\">"); + for (TextDiff diff : this.diffs) { + templateText.append(diff.getTemplateFormatText()); + } + templateText.append("</pre>"); + } + return templateText.toString(); + } + + private boolean hasDiffs() { + return !diffs.isEmpty(); + } + + public BillText shallowClone() { + try { + return (BillText) this.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Failed to clone bill text!"); + } + } + + @Override + public String toString() { + return "BillText{" + + "diffs=" + diffs + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BillText billText = (BillText) o; + return Objects.equals(diffs, billText.diffs); + } + + @Override + public int hashCode() { + return Objects.hash(diffs); + } +} diff --git a/src/main/java/gov/nysenate/openleg/model/bill/BillTextFormat.java b/src/main/java/gov/nysenate/openleg/model/bill/BillTextFormat.java index c61dafa07..b95357a4f 100644 --- a/src/main/java/gov/nysenate/openleg/model/bill/BillTextFormat.java +++ b/src/main/java/gov/nysenate/openleg/model/bill/BillTextFormat.java @@ -4,8 +4,8 @@ * Enumerates the possible formats of bill text */ public enum BillTextFormat { - PLAIN, HTML, + TEMPLATE, ; } diff --git a/src/main/java/gov/nysenate/openleg/model/bill/TextDiff.java b/src/main/java/gov/nysenate/openleg/model/bill/TextDiff.java new file mode 100644 index 000000000..2715da7c7 --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/model/bill/TextDiff.java @@ -0,0 +1,113 @@ +package gov.nysenate.openleg.model.bill; + +import java.util.List; +import java.util.Objects; + +public class TextDiff { + + private TextDiffType type; + + /** + * The raw text data. + */ + private String rawText; + + + public TextDiff(TextDiffType type, String rawText) { + this.type = type; + this.rawText = rawText; + } + + /** + * Converts this text diff into the plain text format. + * + * @return + */ + protected String getPlainFormatText() { + String text = ""; + switch (this.type.getType()) { + case 0: + text = getText(); + break; + case 1: + text = getText().toUpperCase(); + break; + case -1: + text = getText(); + break; + } + return text; + } + + /** + * Converts this text diff into html format. + * @return + */ + protected String getHtmlFormatText() { + return type.getHtmlOpeningTags() + getText() + type.getHtmlClosingTags(); + } + + /** + * Converts this text diff into the template format. + * + * @return + */ + protected String getTemplateFormatText() { + if (type.getTemplateCssClass().isEmpty()) { + return getText(); + } + + StringBuilder text = new StringBuilder(); + text.append("<span class=\""); + for (int i = 0; i < type.getTemplateCssClass().size(); i++) { + if (i != 0) { + text.append(" "); + } + text.append(type.getTemplateCssClass().get(i)); + } + + text.append("\">") + .append(getText()) + .append("</span>"); + return text.toString(); + } + + public String getText() { + return rawText; + } + + public void setText(String text) { + this.rawText = text; + } + + public List<String> getCssClasses() { + return type.getTemplateCssClass(); + } + + public TextDiffType getType() { + return this.type; + } + + @Override + public String toString() { + return "TextDiff{" + + "type=" + type + + ", rawText='" + rawText + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TextDiff textDiff = (TextDiff) o; + return type == textDiff.type && + Objects.equals(rawText, textDiff.rawText); + } + + @Override + public int hashCode() { + + return Objects.hash(type, rawText); + } +} diff --git a/src/main/java/gov/nysenate/openleg/model/bill/TextDiffType.java b/src/main/java/gov/nysenate/openleg/model/bill/TextDiffType.java new file mode 100644 index 000000000..c066ca015 --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/model/bill/TextDiffType.java @@ -0,0 +1,61 @@ +package gov.nysenate.openleg.model.bill; + +import java.util.Arrays; +import java.util.List; + +public enum TextDiffType { + + UNCHANGED(0, Arrays.asList(), "", ""), + ADDED(1, Arrays.asList("ol-changed", "ol-added"), "<b><u>", "</u></b>"), + REMOVED(-1, Arrays.asList("ol-changed", "ol-removed"), "<b><s>", "</s></b>"), + HEADER(0, Arrays.asList("ol-header"), "<font size=5><b>", "</b></font>"), + BOLD(0, Arrays.asList("ol-bold"), "<b>", "</b>"), + PAGE_BREAK(0, Arrays.asList("ol-page-break"), "<p class=\"brk\">", ""); + + /** + * The type of text relative to the previous amendment's text. + * <p> + * if 0: This text is unchanged. + * if 1: This text has been added. + * if -1: This text was removed. + */ + private int type; + + /** + * List of css classes that describe how this text should be styled. + */ + private List<String> templateCssClass; + + /** + * Html tag used as a prefix for the html format. + */ + private String htmlOpeningTags; + + /** + * Html tag used as an ending for the html format. + */ + private String htmlClosingTags; + + TextDiffType(int type, List<String> templateCssClass, String htmlOpeningTags, String htmlClosingTags) { + this.type = type; + this.templateCssClass = templateCssClass; + this.htmlOpeningTags = htmlOpeningTags; + this.htmlClosingTags = htmlClosingTags; + } + + public int getType() { + return type; + } + + public List<String> getTemplateCssClass() { + return templateCssClass; + } + + public String getHtmlOpeningTags() { + return htmlOpeningTags; + } + + public String getHtmlClosingTags() { + return htmlClosingTags; + } +} diff --git a/src/main/java/gov/nysenate/openleg/model/spotcheck/SpotCheckMismatchType.java b/src/main/java/gov/nysenate/openleg/model/spotcheck/SpotCheckMismatchType.java index 5521d513b..bcd08290f 100644 --- a/src/main/java/gov/nysenate/openleg/model/spotcheck/SpotCheckMismatchType.java +++ b/src/main/java/gov/nysenate/openleg/model/spotcheck/SpotCheckMismatchType.java @@ -51,7 +51,7 @@ public enum SpotCheckMismatchType { BILL_LAST_STATUS_DATE("Last Status Date", SENATE_SITE_BILLS), BILL_SUMMARY("Summary", SENATE_SITE_BILLS, OPENLEG_BILL), BILL_LAW_CODE("Law Code", SENATE_SITE_BILLS, OPENLEG_BILL), - BILL_TEXT("Full Text", SENATE_SITE_BILLS), + BILL_TEXT("Full Text", SENATE_SITE_BILLS, OPENLEG_BILL), BILL_VOTE_INFO("Bill Vote Info", SENATE_SITE_BILLS, OPENLEG_BILL), BILL_VOTE_ROLL("Bill Vote Roll", SENATE_SITE_BILLS, OPENLEG_BILL), BILL_SCRAPE_VOTE("Bill Scrape Vote", LBDC_SCRAPED_BILL), diff --git a/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java b/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java index c19439df8..fa4829c92 100644 --- a/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/base/AbstractDataProcessor.java @@ -131,7 +131,7 @@ protected final Bill getOrCreateBaseBill(BillId billId, LegDataFragment fragment } else { try { - baseBill = billDataService.getBill(baseBillId, EnumSet.allOf(BillTextFormat.class)); + baseBill = billDataService.getBill(baseBillId); } catch (BillNotFoundEx ex) { // Create the bill since it does not exist and add it to the ingest cache. diff --git a/src/main/java/gov/nysenate/openleg/processor/bill/AbstractBillProcessor.java b/src/main/java/gov/nysenate/openleg/processor/bill/AbstractBillProcessor.java index b040af23f..743e6d758 100644 --- a/src/main/java/gov/nysenate/openleg/processor/bill/AbstractBillProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/bill/AbstractBillProcessor.java @@ -410,8 +410,6 @@ protected void addAnyMissingAmendments(Bill baseBill, List<BillAction> billActio } private void copyBillTexts(BillAmendment sourceAmend, BillAmendment destAmend) { - for (BillTextFormat format : sourceAmend.getFullTextFormats()) { - destAmend.setFullText(format, sourceAmend.getFullText(format)); - } + destAmend.setBillText(sourceAmend.getBillText()); } } \ No newline at end of file diff --git a/src/main/java/gov/nysenate/openleg/processor/bill/BillSobiProcessor.java b/src/main/java/gov/nysenate/openleg/processor/bill/BillSobiProcessor.java index 99d261f1b..416b2cd3f 100644 --- a/src/main/java/gov/nysenate/openleg/processor/bill/BillSobiProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/bill/BillSobiProcessor.java @@ -511,7 +511,8 @@ private void applyText(String data, BillAmendment billAmendment, LocalDateTime d billAmendment.setMemo(fullText); } else if (lineType == SobiLineType.RESOLUTION_TEXT || lineType == SobiLineType.TEXT) { - billAmendment.setFullText(PLAIN, fullText); + BillText billText = new BillText(fullText); + billAmendment.setBillText(billText); if (billAmendment.isUniBill()) { syncUniBillText(billAmendment, fragment); } diff --git a/src/main/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessor.java b/src/main/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessor.java new file mode 100644 index 000000000..4bf50ade5 --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessor.java @@ -0,0 +1,136 @@ +package gov.nysenate.openleg.processor.bill; + +import com.google.common.collect.Range; +import gov.nysenate.openleg.model.bill.BillText; +import gov.nysenate.openleg.model.bill.TextDiff; +import gov.nysenate.openleg.model.bill.TextDiffType; +import org.jsoup.Jsoup; +import org.jsoup.nodes.*; +import org.jsoup.safety.Whitelist; +import org.jsoup.select.Elements; +import org.jsoup.select.NodeVisitor; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +public class BillTextDiffProcessor { + + /** + * Accepts XML bill text and converts it into a BillText object. + * + * @param xmlText String + * @return ArrayList<TextDiff> + */ + public BillText processBillText(String xmlText) { + if (xmlText == null) { + return new BillText(new ArrayList<>()); + } + + String cleanHtml = cleanXmlText(xmlText); + Document doc = Jsoup.parse(cleanHtml); + BillTextNodeVisitor nodeVisitor = new BillTextNodeVisitor(); + doc.traverse(nodeVisitor); + return new BillText(nodeVisitor.getTextDiffs()); + } + + /** + * Clean the xmlText. + * - Removes closing tags with no associated opening tags. + * - Removes tags not important for parsing the bill text. + * - Converts section symbol alt code into the section symbol character. + * + * This method has to do some manual replacing of characters which are html escaped by jsoup while cleaning. + * I have been unable to find a way to prevent html escaping while still preserving whitespace. + * It seems you can only do one or the other. + * @param xmlText + * @return + */ + private String cleanXmlText(String xmlText) { + Document.OutputSettings outputSettings = new Document.OutputSettings() + .escapeMode(Entities.EscapeMode.xhtml) // Limit the characters to be html escaped as much as possible. We manually fix these later. + .prettyPrint(false); // Preserve whitespace while cleaning. + + // Whitelist of allowed tags and attributes. + Whitelist whitelist = new Whitelist(); + whitelist.addTags("pre", "b", "s", "u", "font", "p"); + whitelist.addAttributes("font", "size"); + whitelist.addAttributes("p", "class"); + + // Clean the text while preserving whitespace. + String cleanHtml = Jsoup.clean(xmlText, "", whitelist, outputSettings); + + // Remove the html escaping added by jsoup. + cleanHtml = cleanHtml.replaceAll("&", "&"); + cleanHtml = cleanHtml.replaceAll("<", "<"); + cleanHtml = cleanHtml.replaceAll(">", ">"); + return cleanHtml; + } + + /** + * Visitor class for Jsoup's depth first traversal. + * + * head method is called whenever entering into a new node. + * tail is called when leaving a node. + */ + private class BillTextNodeVisitor implements NodeVisitor { + + private List<TextDiff> diffs = new ArrayList<>(); + + public List<TextDiff> getTextDiffs() { + return diffs; + } + + @Override + public void head(Node node, int depth) { + if (node instanceof TextNode) { + Node parent = node.parent(); + String parentTagName = getParentTagName(node); + String grandparentTag = getParentTagName(parent); + + if (parentTagName.equals("u") && grandparentTag.equals("b")) { + // Text node inside <B><U> tags + diffs.add(new TextDiff(TextDiffType.ADDED, ((TextNode) node).getWholeText())); + } + else if (parentTagName.equals("s") && grandparentTag.equals("b")) { + // Text node inside <B><S> tags + diffs.add(new TextDiff(TextDiffType.REMOVED, ((TextNode) node).getWholeText())); + } + else if (parentTagName.equals("b") && grandparentTag.equals("font")) { + // Text node inside <FONT><B> tags + diffs.add(new TextDiff(TextDiffType.HEADER, ((TextNode) node).getWholeText())); + } + else if (parentTagName.equals("b")) { + // Text node inside <B> tag + diffs.add(new TextDiff(TextDiffType.BOLD, ((TextNode) node).getWholeText())); + } + else { + diffs.add(new TextDiff(TextDiffType.UNCHANGED, ((TextNode) node).getWholeText())); + } + } + else if (node instanceof Element) { + Element el = (Element) node; + if (el.tagName().equals("p") && el.className().equals("brk")) { + // <p class="brk"> tags indicate the end of a page. + diffs.add(new TextDiff(TextDiffType.PAGE_BREAK, "")); + } + } + } + + @Override + public void tail(Node node, int depth) { + + } + + private String getParentTagName(Node node) { + Node parent = node.parent(); + if (parent != null) { + if (parent instanceof Element) { + Element parentEl = (Element) parent; + return parentEl.tagName(); + } + } + return ""; + } + } +} diff --git a/src/main/java/gov/nysenate/openleg/processor/bill/TextDiffSearch.java b/src/main/java/gov/nysenate/openleg/processor/bill/TextDiffSearch.java new file mode 100644 index 000000000..56d5fa061 --- /dev/null +++ b/src/main/java/gov/nysenate/openleg/processor/bill/TextDiffSearch.java @@ -0,0 +1,92 @@ +package gov.nysenate.openleg.processor.bill; + +import gov.nysenate.openleg.model.bill.TextDiff; +import gov.nysenate.openleg.model.bill.TextDiffType; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TextDiffSearch { + + private final Pattern pattern; + private final String billText; + private final TextDiffType diffType; + private Matcher matcher; + /** + * The starting index of the matcher's current match. -1 if no match was found. + * + * When using multiple TextDiffSearch's you can determine the order of the matches + * by comparing the startingIndex. + */ + private int startingIndex; + + /** + * The ending index of the matcher's current result. -1 if no match was found. + */ + private int endingIndex; + private String matchingText; + + public TextDiffSearch(Pattern pattern, TextDiffType diffType, String billText) { + this.pattern = pattern; + this.diffType = diffType; + this.billText = billText; + this.startingIndex = -1; + this.endingIndex = -1; + } + + /** + * Searches for the next instance of pattern in billText + * and sets startingIndex, endingIndex, and matchingText. + * + * This can then be used to get a TextDiff representing this text with the createDiff method. + * + * Once the diff for a match is used, call this method again to find the next match. + */ + public void findNext() { + if (matcher == null) { + matcher = pattern.matcher(billText); + } + + if (matcher.find()) { + this.startingIndex = matcher.start(); + this.endingIndex = matcher.end(); + if (matcher.groupCount() > 0) { + this.matchingText = matcher.group(1); + } + else { + // If there is no group to match, its always an empty string. + this.matchingText = ""; + } + } + else { + this.startingIndex = -1; + this.endingIndex = -1; + this.matchingText = ""; + } + } + + /** + * Creates a TextDiff from the diffType and matchingText. + * @return + */ + public TextDiff createDiff() { + return new TextDiff(this.getDiffType(), this.getMatchingText()); + } + + public TextDiffType getDiffType() { + return diffType; + } + + public int getStartingIndex() { + return startingIndex; + } + + public int getEndingIndex() { + return endingIndex; + } + + public String getMatchingText() { + return matchingText; + } +} diff --git a/src/main/java/gov/nysenate/openleg/processor/bill/XmlBillTextProcessor.java b/src/main/java/gov/nysenate/openleg/processor/bill/XmlBillTextProcessor.java index 7bc314bc5..19459f470 100644 --- a/src/main/java/gov/nysenate/openleg/processor/bill/XmlBillTextProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/bill/XmlBillTextProcessor.java @@ -28,8 +28,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static gov.nysenate.openleg.model.bill.BillTextFormat.*; - /** * Created by Chenguang He(gaoyike@gmail.com) on 2016/12/1. */ @@ -43,11 +41,13 @@ public class XmlBillTextProcessor extends AbstractDataProcessor implements LegDa private final XmlHelper xmlHelper; private final EventBus eventBus; + private final BillTextDiffProcessor textDiffProcessor; @Autowired - public XmlBillTextProcessor(XmlHelper xmlHelper, EventBus eventBus) { + public XmlBillTextProcessor(XmlHelper xmlHelper, EventBus eventBus, BillTextDiffProcessor textDiffProcessor) { this.xmlHelper = xmlHelper; this.eventBus = eventBus; + this.textDiffProcessor = textDiffProcessor; } @Override @@ -78,31 +78,29 @@ public void process(LegDataFragment legDataFragment) { final String action = xmlHelper.getString("@action", billTextNode); // If remove action, set bill text to blank - final String billText = "remove".equals(action) + final String text = "remove".equals(action) ? "" : billTextNode.getTextContent(); - String strippedBillText = BillTextUtils.parseHTMLtext(billText); - Set<BillId> updatedBills = new HashSet<>(); + BillText billText = textDiffProcessor.processBillText(text); + Set<BillId> updatedBills = new HashSet<>(); BillId filenamePrintNo = getFilenamePrintNo(sessionYear, legDataFragment); // For Resolutions only apply to the bill from the filename if (filenamePrintNo.getBillType().isResolution()) { - applyBillText(filenamePrintNo, billText, strippedBillText, legDataFragment); + applyBillText(filenamePrintNo, billText, legDataFragment); updatedBills.add(filenamePrintNo); } else { - // Apply special formatting for bill text - strippedBillText = BillTextUtils.formatHtmlExtractedBillText(strippedBillText); // Apply to senate and/or assembly versions if referenced if (!StringUtils.isBlank(senhse)) { BillId senateId = new BillId(senhse + senno, sessionYear, senamd); - applyBillText(senateId, billText, strippedBillText, legDataFragment); + applyBillText(senateId, billText, legDataFragment); updatedBills.add(senateId); } if (!StringUtils.isBlank(asmhse)) { BillId assemblyId = new BillId(asmhse + asmno, sessionYear, asmamd); - applyBillText(assemblyId, billText, strippedBillText, legDataFragment); + applyBillText(assemblyId, billText, legDataFragment); updatedBills.add(assemblyId); } } @@ -136,13 +134,10 @@ public void postProcess() { /** * Applies bill text to a single bill using the values parsed from the billtext html tag */ - private void applyBillText(BillId billId, - String billText, String strippedBillText, - LegDataFragment fragment) { + private void applyBillText(BillId billId, BillText billText, LegDataFragment fragment) { final Bill baseBill = getOrCreateBaseBill(billId, fragment); BillAmendment amendment = baseBill.getAmendment(billId.getVersion()); - amendment.setFullText(HTML, billText); - amendment.setFullText(PLAIN, strippedBillText); + amendment.setBillText(billText); billIngestCache.set(baseBill.getBaseBillId(), baseBill, fragment); } diff --git a/src/main/java/gov/nysenate/openleg/processor/bill/sponsor/XmlSenMemoProcessor.java b/src/main/java/gov/nysenate/openleg/processor/bill/sponsor/XmlSenMemoProcessor.java index fb2596cd6..7abdbb156 100644 --- a/src/main/java/gov/nysenate/openleg/processor/bill/sponsor/XmlSenMemoProcessor.java +++ b/src/main/java/gov/nysenate/openleg/processor/bill/sponsor/XmlSenMemoProcessor.java @@ -97,7 +97,7 @@ private String getNodeText(Node node) { Node temp = childNodes.item(i); if (temp.getNodeType() == temp.CDATA_SECTION_NODE) { String htmlMemoText = temp.getTextContent(); - return BillTextUtils.parseHTMLtext(htmlMemoText); + return BillTextUtils.convertHtmlToPlainText(htmlMemoText); } } return ""; diff --git a/src/main/java/gov/nysenate/openleg/service/bill/data/BillDataService.java b/src/main/java/gov/nysenate/openleg/service/bill/data/BillDataService.java index 3a044f237..bcde030d3 100644 --- a/src/main/java/gov/nysenate/openleg/service/bill/data/BillDataService.java +++ b/src/main/java/gov/nysenate/openleg/service/bill/data/BillDataService.java @@ -19,16 +19,6 @@ */ public interface BillDataService { - /** - * Default overload of {@link #getBill(BaseBillId, Set)} that always applies plain bill text. - * - * @param billId BaseBillId - * @return Bill - * @throws BillNotFoundEx - If no Bill matching the BillId was found. - */ - default Bill getBill(BaseBillId billId) throws BillNotFoundEx { - return getBill(billId, Collections.singleton(BillTextFormat.PLAIN)); - } /** * Retrieve a Bill instance for the matching BillId. @@ -36,11 +26,10 @@ default Bill getBill(BaseBillId billId) throws BillNotFoundEx { * Will only include bill texts for the given formats. * * @param billId BaseBillId - * @param fullTextFormats {@link Set<BillTextFormat>} formats to include on bill * @return Bill * @throws BillNotFoundEx - If no Bill matching the BillId was found. */ - Bill getBill(BaseBillId billId, Set<BillTextFormat> fullTextFormats) throws BillNotFoundEx; + Bill getBill(BaseBillId billId) throws BillNotFoundEx; /** * Retrieve a BillInfo instance for the matching BillId. This contains diff --git a/src/main/java/gov/nysenate/openleg/service/bill/data/CachedBillDataService.java b/src/main/java/gov/nysenate/openleg/service/bill/data/CachedBillDataService.java index 7773a7067..209fd15cd 100644 --- a/src/main/java/gov/nysenate/openleg/service/bill/data/CachedBillDataService.java +++ b/src/main/java/gov/nysenate/openleg/service/bill/data/CachedBillDataService.java @@ -106,7 +106,7 @@ public void warmCaches() { if (sessionYear.equals(SessionYear.current())) { logger.info("Caching Bill instances for current session year: {}", sessionYear); // Don't load any text because that is not cached. - getBillIds(sessionYear, LimitOffset.ALL).forEach(id -> getBill(id, Collections.emptySet())); + getBillIds(sessionYear, LimitOffset.ALL).forEach(id -> getBill(id)); } else { logger.info("Caching Bill Info instances for session year: {}", sessionYear); @@ -156,19 +156,19 @@ public synchronized void handleCacheWarmEvent(CacheWarmEvent warmEvent) { /** {@inheritDoc} */ @Override - public Bill getBill(BaseBillId billId, Set<BillTextFormat> fullTextFormats) throws BillNotFoundEx { + public Bill getBill(BaseBillId billId) throws BillNotFoundEx { if (billId == null) { throw new IllegalArgumentException("BillId cannot be null"); } try { Bill bill; if (billCache.get(billId) != null) { - bill = constructBillFromCache(billId, fullTextFormats); + bill = constructBillFromCache(billId); logger.debug("Cache hit for bill {}", bill); } else { logger.debug("Fetching bill {}..", billId); - bill = billDao.getBill(billId, fullTextFormats); + bill = billDao.getBill(billId); putStrippedBillInCache(bill); } return bill; @@ -280,14 +280,13 @@ public Optional<String> getAlternateBillPdfUrl(BillId billId) { * method. The fulltext and memo are put back into a copy of the cached bill. * * @param billId BaseBillId - * @param billTextFormats {@link Set<BillTextFormat>} * @return Bill * @throws CloneNotSupportedException */ - private Bill constructBillFromCache(BaseBillId billId, Set<BillTextFormat> billTextFormats) throws CloneNotSupportedException { + private Bill constructBillFromCache(BaseBillId billId) throws CloneNotSupportedException { Bill cachedBill = (Bill) billCache.get(billId).getObjectValue(); cachedBill = cachedBill.shallowClone(); - billDao.applyText(cachedBill, billTextFormats); + billDao.applyTextAndMemo(cachedBill); return cachedBill; } diff --git a/src/main/java/gov/nysenate/openleg/service/bill/search/ElasticBillSearchService.java b/src/main/java/gov/nysenate/openleg/service/bill/search/ElasticBillSearchService.java index 89178dabb..c5557ec74 100644 --- a/src/main/java/gov/nysenate/openleg/service/bill/search/ElasticBillSearchService.java +++ b/src/main/java/gov/nysenate/openleg/service/bill/search/ElasticBillSearchService.java @@ -10,6 +10,7 @@ import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.bill.BaseBillId; import gov.nysenate.openleg.model.bill.Bill; +import gov.nysenate.openleg.model.bill.BillAmendment; import gov.nysenate.openleg.model.bill.BillId; import gov.nysenate.openleg.model.search.*; import gov.nysenate.openleg.service.base.search.ElasticSearchServiceUtils; @@ -277,7 +278,7 @@ public void run() { billIdQueue.drainTo(billIdBatch, billReindexBatchSize); List<Bill> bills = billIdBatch.stream() - .map((billId) -> billDataService.getBill(billId, Collections.singleton(PLAIN))) + .map((billId) -> billDataService.getBill(billId)) .collect(Collectors.toCollection(() -> new ArrayList<>(billReindexBatchSize))); updateIndex(bills); @@ -294,11 +295,11 @@ public void run() { * Returns a version of the given bill that is guaranteed to contain plaintext versions of full text for all amendments */ private Bill ensurePlainTextPresent(Bill bill) { - boolean allContainPlain = bill.getAmendmentList().stream().allMatch(a -> a.isTextFormatLoaded(PLAIN)); + boolean allContainPlain = bill.getAmendmentList().stream().allMatch(BillAmendment::isTextLoaded); if (!allContainPlain) { // If the bill doesn't have plain text for any amendments, reload bill with plain text logger.warn("Bill passed in for indexing does not contain plain text: {}", bill.getBaseBillId()); - bill = billDataService.getBill(bill.getBaseBillId(), Collections.singleton(PLAIN)); + bill = billDataService.getBill(bill.getBaseBillId()); } return bill; } diff --git a/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceFactory.java b/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceFactory.java index f2740d591..238dc276c 100644 --- a/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceFactory.java +++ b/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceFactory.java @@ -1,8 +1,11 @@ package gov.nysenate.openleg.service.scraping.bill; import gov.nysenate.openleg.model.bill.BillId; +import gov.nysenate.openleg.model.bill.BillText; +import gov.nysenate.openleg.model.bill.BillTextFormat; import gov.nysenate.openleg.model.bill.BillType; import gov.nysenate.openleg.model.spotcheck.billscrape.BillScrapeReference; +import gov.nysenate.openleg.processor.bill.BillTextDiffProcessor; import gov.nysenate.openleg.util.BillTextUtils; import org.apache.commons.io.FileUtils; import org.jsoup.Jsoup; @@ -18,21 +21,29 @@ public class BillScrapeReferenceFactory { private BillScrapeReferenceHtmlParser htmlParser; + private BillTextDiffProcessor textDiffProcessor; @Autowired - public BillScrapeReferenceFactory(BillScrapeReferenceHtmlParser htmlParser) { + public BillScrapeReferenceFactory(BillScrapeReferenceHtmlParser htmlParser, BillTextDiffProcessor textDiffProcessor) { this.htmlParser = htmlParser; + this.textDiffProcessor = textDiffProcessor; } public BillScrapeReference createFromFile(BillScrapeFile btrFile) throws IOException { Document doc = Jsoup.parse(btrFile.getFile(), "UTF-8"); + doc.outputSettings(new Document.OutputSettings().prettyPrint(false)); // Preserve whitespace. + if (htmlParser.isBillMissing(doc)) { return errorBillScrapeReference(btrFile); } BillId billId = new BillId(htmlParser.parsePrintNo(doc), btrFile.getBaseBillId().getSession()); Elements billTextElements = htmlParser.getBillTextElements(doc); String htmlText = billTextElements.outerHtml(); - String plain = formatText(BillTextUtils.parseHTMLText(billTextElements), billId.getBillType()); + + BillText billText = textDiffProcessor.processBillText(htmlText); + String plain = billText.getFullText(BillTextFormat.PLAIN); + htmlText = billText.getFullText(BillTextFormat.HTML); + // Only parse memo's for non resolutions. String memo = billId.getBillType().isResolution() ? "" : htmlParser.parseMemo(doc); BillScrapeReference reference = new BillScrapeReference(billId, btrFile.getReferenceDateTime(), plain, htmlText, memo); diff --git a/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceHtmlParser.java b/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceHtmlParser.java index a27d38361..2f8df39b1 100644 --- a/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceHtmlParser.java +++ b/src/main/java/gov/nysenate/openleg/service/scraping/bill/BillScrapeReferenceHtmlParser.java @@ -87,7 +87,7 @@ public String parseMemo(Document doc) { Element memoElement = doc.select("pre:last-of-type").first(); // you are the first and last of your kind String memoText = ""; if (memoElement != null) { - memoText = BillTextUtils.parseHTMLText(memoElement); + memoText = BillTextUtils.convertHtmlToPlainText(memoElement); } return memoText; } diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotcheckRunService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotcheckRunService.java index 83c0b206c..fef128195 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotcheckRunService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/base/SpotcheckRunService.java @@ -155,6 +155,7 @@ private void runReport(SpotCheckReportService<?> reportService, Range<LocalDateT sendMismatchEvents(report); reportDao.saveReport(report); spotCheckNotificationService.spotcheckCompleteNotification(report); + logger.info("Done saving spotcheck report."); } catch (ReferenceDataNotFoundEx ex) { logger.info("No report generated: no {} references could be found. Message: " + ex.getMessage(), reportService.getSpotcheckRefType()); } catch (Exception ex) { diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillCheckService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillCheckService.java index c6e1ae59c..d7b50df72 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillCheckService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillCheckService.java @@ -41,7 +41,7 @@ public class OpenlegBillCheckService implements SpotCheckService<BaseBillId, Bil @Autowired private SpotCheckUtils spotCheckUtils; /** - * Check the mismatch between openleg sobi-processing and xml-data-processing Bills + * Comapres bills between two openleg environments. Only checks the data on the active amendment. * * @param content ContentType - The content to check * @param reference ReferenceType - The reference content to use for comparison @@ -69,6 +69,7 @@ public SpotCheckObservation<BaseBillId> check(BillView content, BillView referen checkCalendars(content, reference, observation); checkBillCommitteeAgendas(content, reference, observation); checkBillPastCommmittee(content, reference, observation); + checkText(content, reference, observation); } else { checkActiveVersion(content, reference, observation); } @@ -287,6 +288,15 @@ protected void checkBillPastCommmittee(BillView content, BillView reference, Spo ); } + private void checkText(BillView content, BillView reference, SpotCheckObservation<BaseBillId> obs) { + spotCheckUtils.checkString( + getActiveAmendOpt(content).orElse(new BillAmendmentView()).getFullText(), + getActiveAmendOpt(reference).orElse(new BillAmendmentView()).getFullText(), + obs, + BILL_TEXT + ); + } + private Optional<BillAmendmentView> getActiveAmendOpt(BillView billView) { Optional<String> activeVersionOpt = Optional.ofNullable(billView.getActiveVersion()); if (!activeVersionOpt.isPresent()) { diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillReportService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillReportService.java index bd4ffeb28..87866b9f7 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillReportService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/openleg/OpenlegBillReportService.java @@ -1,5 +1,6 @@ package gov.nysenate.openleg.service.spotcheck.openleg; +import com.google.common.collect.Sets; import gov.nysenate.openleg.client.view.bill.BillView; import gov.nysenate.openleg.config.Environment; import gov.nysenate.openleg.dao.base.LimitOffset; @@ -8,6 +9,7 @@ import gov.nysenate.openleg.model.base.SessionYear; import gov.nysenate.openleg.model.bill.BaseBillId; import gov.nysenate.openleg.model.bill.BillInfo; +import gov.nysenate.openleg.model.bill.BillTextFormat; import gov.nysenate.openleg.model.spotcheck.SpotCheckObservation; import gov.nysenate.openleg.model.spotcheck.SpotCheckRefType; import gov.nysenate.openleg.model.spotcheck.SpotCheckReport; @@ -102,7 +104,7 @@ public SpotCheckReport<BaseBillId> generateReport(LocalDateTime start, LocalDate if (!localBillIds.contains(baseBillId)) { throw new BillNotFoundEx(baseBillId); } - BillView localBill = new BillView(billDataService.getBill(baseBillId)); + BillView localBill = new BillView(billDataService.getBill(baseBillId), Sets.newHashSet(BillTextFormat.PLAIN)); SpotCheckObservation<BaseBillId> obs = checkService.check(localBill, refBill); report.addObservation(obs); // Remove this bill from localBillIds to indicate it was present in ref. bills. diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java index 2bac43c3d..9c48f9b4e 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeCheckService.java @@ -59,10 +59,6 @@ else if (reference.isNotFound()) { if (bill.hasAmendment(reference.getActiveVersion())) { BillAmendment amendment = bill.getAmendment(reference.getActiveVersion()); checkBillText(amendment, reference, observation); - // TODO remove session check when we get xml bill text for previous years - if (bill.getSession().getYear() >= 2017) { - checkHtmlBillText(amendment, reference, observation); - } // Only check senate, non-resolution bills for sponsor memos // Todo find a better way of checking memo text // currently, memos are sent daily in batches and are not guaranteed to be present in sobi data if on lrs @@ -82,39 +78,13 @@ private void checkAmendment(Bill bill, BillScrapeReference reference, SpotCheckO } } - /** - * Cleans unnecessary elements e.g. style from observation html bill text - */ - private String cleanHtml(String rawHtml) { - if (StringUtils.isBlank(rawHtml)) { - return ""; - } - Document doc = Jsoup.parse(rawHtml); - Elements billTextElements = doc.getElementsByTag("pre"); - String preHtml = billTextElements.html(); - return preHtml - .replaceAll("\r\n", "\n") - .replaceAll(" +(?=$|\n)", ""); - } - - /** - * Check the html version of bill text. - */ - private void checkHtmlBillText(BillAmendment amend, BillScrapeReference reference, - SpotCheckObservation<BaseBillId> obs) { - ensureTextFormatExists(amend, HTML); - String contentHtmlText = cleanHtml(Optional.ofNullable(amend.getFullText(HTML)).orElse("")); - String refHtmlText = cleanHtml(reference.getHtmlText()); - spotCheckUtils.checkString(contentHtmlText, refHtmlText, obs, BILL_HTML_TEXT); - } - /** * Checks text with all whitespace removed, and generates several mismatches with different levels of text * normalization if there was a mismatch in the no-whitespace text */ private void checkBillText(BillAmendment billAmendment, BillScrapeReference reference, SpotCheckObservation<BaseBillId> obsrv){ - ensureTextFormatExists(billAmendment, PLAIN); - String dataText = Optional.ofNullable(billAmendment.getFullText(PLAIN)).orElse(""); + ensureTextLoaded(billAmendment); + String dataText = billAmendment.getFullText(PLAIN); String refText = reference.getText(); String strippedDataText = basicNormalize(dataText); String strippedRefText = basicNormalize(refText); @@ -220,10 +190,9 @@ private String ultraNormalize(String text) { return basicNormalize(stripped); } - private void ensureTextFormatExists(BillAmendment billAmendment, BillTextFormat format) { - if (!billAmendment.isTextFormatLoaded(format)) { - throw new IllegalStateException("Bill text format " + format + - " is not represented in bill reference for " + billAmendment.getBillId()); + private void ensureTextLoaded(BillAmendment billAmendment) { + if (!billAmendment.isTextLoaded()) { + throw new IllegalStateException("Bill text is missing for bill: " + billAmendment.getBillId()); } } } diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeReportService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeReportService.java index b13d6bb9f..0e6632160 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeReportService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/scrape/BillScrapeReportService.java @@ -114,7 +114,7 @@ private String createNotes(List<BillScrapeReference> references) { private SpotCheckObservation<BaseBillId> generateObservation(BillScrapeReference btr) { //Gets bill from openleg processed info try { - Bill bill = billDataService.getBill(new BaseBillId(btr.getPrintNo(), btr.getSessionYear()), EnumSet.allOf(BillTextFormat.class)); + Bill bill = billDataService.getBill(new BaseBillId(btr.getPrintNo(), btr.getSessionYear())); return billScrapeCheckService.check(bill, btr); } catch (BillNotFoundEx e) { SpotCheckObservation<BaseBillId> ob = new SpotCheckObservation<>(btr.getReferenceId(), btr.getBaseBillId()); diff --git a/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java b/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java index 7cf2a5c9a..88f6c99d4 100644 --- a/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java +++ b/src/main/java/gov/nysenate/openleg/service/spotcheck/senatesite/bill/SenateSiteBillCheckService.java @@ -58,7 +58,7 @@ public SpotCheckObservation<BillId> check(Bill content, SenateSiteBill reference SpotCheckObservation<BillId> observation = new SpotCheckObservation<>(reference.getReferenceId(), billId); - BillView contentBillView = new BillView(content); + BillView contentBillView = new BillView(content, Sets.newHashSet(BillTextFormat.PLAIN)); BillAmendmentView amendment; try { Version refVersion = reference.getBillId().getVersion(); diff --git a/src/main/java/gov/nysenate/openleg/util/BillTextUtils.java b/src/main/java/gov/nysenate/openleg/util/BillTextUtils.java index 979ea465f..14a524023 100644 --- a/src/main/java/gov/nysenate/openleg/util/BillTextUtils.java +++ b/src/main/java/gov/nysenate/openleg/util/BillTextUtils.java @@ -2,6 +2,8 @@ import com.google.common.base.Splitter; import com.google.common.base.Strings; +import gov.nysenate.openleg.model.bill.BillAmendment; +import gov.nysenate.openleg.model.bill.BillTextFormat; import gov.nysenate.openleg.model.entity.Chamber; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; @@ -99,25 +101,29 @@ public static int getPageCount(String fullText) { return 1; } - /** WIP */ - public static String formatBillText(boolean isResolution, String fullText) { - if (fullText == null) { - fullText = ""; - } - if (!isResolution && StringUtils.isNotBlank(fullText)) { - List<String> lines = Splitter.on("\n").splitToList(fullText); - StringBuilder formattedFullText = new StringBuilder(); + /** + * Return this amendment's plain text with line numbers stripped if they exist. + * + * Resolutions do not have line numbers. + * + * @param amendment + */ + public static String getPlainTextWithoutLineNumbers(BillAmendment amendment) { + String text = amendment.getFullText(BillTextFormat.PLAIN); + if (!amendment.isResolution()) { + List<String> lines = Splitter.on("\n").splitToList(text); + StringBuilder textWithoutLineNums = new StringBuilder(); lines.forEach(line -> { if (line.length() > 7) { - formattedFullText.append(line.substring(7)).append("\n"); + textWithoutLineNums.append(line.substring(7)).append("\n"); } else { - formattedFullText.append(line).append("\n"); + textWithoutLineNums.append(line).append("\n"); } }); - return formattedFullText.toString(); + text = textWithoutLineNums.toString(); } - return fullText; + return text; } /** @@ -138,21 +144,21 @@ public static boolean isFirstLineOfNextPage(String line, int lineNum) { * @see #formatHtmlExtractedBillText(String) * @see #formatHtmlExtractedResoText(String) */ - public static String parseHTMLtext(String htmlText) { + public static String convertHtmlToPlainText(String htmlText) { Document doc = Jsoup.parse(htmlText); if (doc.select("pre").size() == 0) { return htmlText; } Elements preTags = doc.select("pre"); - return parseHTMLText(preTags); + return convertHtmlToPlainText(preTags); } - public static String parseHTMLText(Element element) { - return parseHTMLText(new Elements(element)); + public static String convertHtmlToPlainText(Element element) { + return convertHtmlToPlainText(new Elements(element)); } - public static String parseHTMLText(Collection<Element> elements) { + public static String convertHtmlToPlainText(Collection<Element> elements) { StringBuilder textBuilder = new StringBuilder(); elements.forEach(element -> processTextNode(element, textBuilder)); @@ -166,7 +172,7 @@ public static String parseHTMLText(Collection<Element> elements) { private static final String inSenate = "IN SENATE"; private static final String inAssembly = "IN ASSEMBLY"; private static final String inBoth = "SENATE - ASSEMBLY"; - private static final Pattern billHeaderPattern = Pattern.compile("^(?<startingNewlines>\n*)" + + private static final Pattern billHeaderPattern = Pattern.compile("^(?<startingNewlines>\\s*)" + "[ ]{3,}STATE OF NEW YORK\n" + "(?<divider>(?:[ \\w.\\-]*\n){0,8})" + "[ ]{3,}(?<chamber>" + inSenate + "|" + inAssembly + "|" + inBoth + ")" + @@ -180,26 +186,24 @@ public static String parseHTMLText(Collection<Element> elements) { * @return String */ public static String formatHtmlExtractedBillText(String text) { - // The html has an extra space at the beginning of each line - text = text.replaceAll("(?<=\n|^) ", ""); Matcher matcher = billHeaderPattern.matcher(text); if (matcher.find()) { StringBuilder replacement = new StringBuilder() .append(matcher.group("startingNewlines")) - .append(StringUtils.repeat(' ', 27)) + .append(StringUtils.repeat(' ', 15)) .append("S T A T E O F N E W Y O R K\n") .append(matcher.group("divider")); switch (matcher.group("chamber")) { case inSenate: - replacement.append(StringUtils.repeat(' ', 35)) + replacement.append(StringUtils.repeat(' ', 36)) .append("I N S E N A T E"); break; case inAssembly: - replacement.append(StringUtils.repeat(' ', 33)) + replacement.append(StringUtils.repeat(' ', 34)) .append("I N A S S E M B L Y"); break; case inBoth: - replacement.append(StringUtils.repeat(' ', 29)) + replacement.append(StringUtils.repeat(' ', 30)) .append("S E N A T E - A S S E M B L Y"); break; default: @@ -211,7 +215,6 @@ public static String formatHtmlExtractedBillText(String text) { } text = matcher.replaceFirst(replacement.toString()); } - return text; } diff --git a/src/main/resources/sql/migrations/V20200325.0913__add_text_diff_table.sql b/src/main/resources/sql/migrations/V20200325.0913__add_text_diff_table.sql new file mode 100644 index 000000000..b9bc751c4 --- /dev/null +++ b/src/main/resources/sql/migrations/V20200325.0913__add_text_diff_table.sql @@ -0,0 +1,18 @@ +CREATE TABLE master.bill_amendment_text_diff ( + bill_amendment_text_diff_id BIGSERIAL PRIMARY KEY, + bill_print_no text NOT NULL, + bill_session_year smallint NOT NULL, + bill_amend_version character(1) NOT NULL, + index int NOT NULL, + type text NOT NULL, + text text NOT NULL +); + +ALTER TABLE master.bill_amendment_text_diff + ADD CONSTRAINT bill_amendment_text_diff_bill_amend_fkey + FOREIGN KEY (bill_print_no, bill_session_year, bill_amend_version) + REFERENCES master.bill_amendment(bill_print_no, bill_session_year, bill_amend_version); + +CREATE INDEX bill_amendment_text_diff_print_no_session_year_amend_version + ON master.bill_amendment_text_diff USING btree (bill_print_no, bill_session_year, bill_amend_version); + diff --git a/src/test/java/gov/nysenate/openleg/dao/bill/SqlBillDaoTest.java b/src/test/java/gov/nysenate/openleg/dao/bill/SqlBillDaoTest.java index 811bd0cd1..bce47fcd9 100644 --- a/src/test/java/gov/nysenate/openleg/dao/bill/SqlBillDaoTest.java +++ b/src/test/java/gov/nysenate/openleg/dao/bill/SqlBillDaoTest.java @@ -32,7 +32,7 @@ public class SqlBillDaoTest extends BaseTests @Test public void testGetBill() throws Exception { - logger.info("{}", OutputUtils.toJson(billDao.getBill(new BaseBillId("S1051", 2013), Collections.singleton(BillTextFormat.PLAIN)))); + logger.info("{}", OutputUtils.toJson(billDao.getBill(new BaseBillId("S1051", 2013)))); } @Test @@ -54,11 +54,11 @@ public void testCountAllBills() throws Exception { @Test public void testFastBill() throws Exception { - Bill bill = billDao.getBill(new BillId("S5922", 2013), EnumSet.allOf(BillTextFormat.class)); + Bill bill = billDao.getBill(new BillId("S5922", 2013)); StopWatch sw = new StopWatch(); sw.start(); - bill = billDao.getBill(new BillId("S5922", 2013), EnumSet.allOf(BillTextFormat.class)); + bill = billDao.getBill(new BillId("S5922", 2013)); sw.stop(); logger.info("Time {} ms",sw.getTime()); // logger.info("{}", OutputUtils.toJson(bill)); diff --git a/src/test/java/gov/nysenate/openleg/model/bill/TextDiffTest.java b/src/test/java/gov/nysenate/openleg/model/bill/TextDiffTest.java new file mode 100644 index 000000000..342f3a61c --- /dev/null +++ b/src/test/java/gov/nysenate/openleg/model/bill/TextDiffTest.java @@ -0,0 +1,129 @@ +package gov.nysenate.openleg.model.bill; + +import gov.nysenate.openleg.annotation.UnitTest; +import org.junit.Test; +import org.junit.experimental.categories.Category; + + +import static org.junit.Assert.assertEquals; + +@Category(UnitTest.class) +public class TextDiffTest { + + /** + * Plain text format tests + */ + + @Test + public void givenUnchangedText_plainFormatIsUnchanged() { + TextDiff diff = new TextDiff(TextDiffType.UNCHANGED, "Some unchanged basic\n text."); + String actual = diff.getPlainFormatText(); + String expected = "Some unchanged basic\n text."; + assertEquals(expected, actual); + } + + @Test + public void givenAddedText_plainFormatIsUppercase() { + TextDiff diff = new TextDiff(TextDiffType.ADDED, "This text has been\nadded."); + String actual = diff.getPlainFormatText(); + String expected = "THIS TEXT HAS BEEN\nADDED."; + assertEquals(expected, actual); + } + + @Test + public void givenRemovedText_plainFormatIsUnchanged() { + TextDiff diff = new TextDiff(TextDiffType.REMOVED, "[This text has been\nremoved.]"); + String actual = diff.getPlainFormatText(); + String expected = "[This text has been\nremoved.]"; + assertEquals(expected, actual); + } + + /** + * Html text format tests + */ + + @Test + public void givenUnchangedText_htmlFormatIsUnchanged() { + TextDiff diff = new TextDiff(TextDiffType.UNCHANGED, "Some unchanged basic\n text."); + String actual = diff.getHtmlFormatText(); + String expected = "Some unchanged basic\n text."; + assertEquals(expected, actual); + } + + @Test + public void givenAddedText_htmlTextIsInAddedElement() { + TextDiff diff = new TextDiff(TextDiffType.ADDED, "This text has been\nadded."); + String actual = diff.getHtmlFormatText(); + String expected = "<b><u>This text has been\nadded.</u></b>"; + assertEquals(expected, actual); + } + + @Test + public void givenRemovedText_htmlTextIsInRemovedElement(){ + TextDiff diff = new TextDiff(TextDiffType.REMOVED, "This text has been removed."); + String actual = diff.getHtmlFormatText(); + String expected = "<b><s>This text has been removed.</s></b>"; + assertEquals(expected, actual); + } + + @Test + public void givenHeaderText_htmlTextIsInHeaderElement() { + TextDiff diff = new TextDiff(TextDiffType.HEADER, "Some title"); + String actual = diff.getHtmlFormatText(); + String expected = "<font size=5><b>Some title</b></font>"; + assertEquals(expected, actual); + } + + @Test + public void givenBoldText_htmlTextIsInBoldElement() { + TextDiff diff = new TextDiff(TextDiffType.BOLD, "bold text"); + String actual = diff.getHtmlFormatText(); + String expected = "<b>bold text</b>"; + assertEquals(expected, actual); + } + + + /** + * Template text format tests + */ + + @Test + public void givenUnchangedText_templateFormatUnchanged() { + TextDiff diff = new TextDiff(TextDiffType.UNCHANGED, "Some unchanged basic\n text."); + String actual = diff.getTemplateFormatText(); + String expected = "Some unchanged basic\n text."; + assertEquals(expected, actual); + } + + @Test + public void givenAddedText_templateFormatStyled() { + TextDiff diff = new TextDiff(TextDiffType.ADDED, "This text has been\nadded."); + String actual = diff.getTemplateFormatText(); + String expected = "<span class=\"ol-changed ol-added\">This text has been\nadded.</span>"; + assertEquals(expected, actual); + } + + @Test + public void givenRemovedText_templateFormatStyled() { + TextDiff diff = new TextDiff(TextDiffType.REMOVED, "This text has been\nremoved."); + String actual = diff.getTemplateFormatText(); + String expected = "<span class=\"ol-changed ol-removed\">This text has been\nremoved.</span>"; + assertEquals(expected, actual); + } + + @Test + public void givenHeaderText_templateFormatHasHeaderClass() { + TextDiff diff = new TextDiff(TextDiffType.HEADER, "Some basic\n text."); + String actual = diff.getTemplateFormatText(); + String expected = "<span class=\"ol-header\">Some basic\n text.</span>"; + assertEquals(expected, actual); + } + + @Test + public void givenBoldText_templateFormatHasBoldClass() { + TextDiff diff = new TextDiff(TextDiffType.BOLD, "Some basic\n text."); + String actual = diff.getTemplateFormatText(); + String expected = "<span class=\"ol-bold\">Some basic\n text.</span>"; + assertEquals(expected, actual); + } +} diff --git a/src/test/java/gov/nysenate/openleg/processor/BaseXmlProcessorTest.java b/src/test/java/gov/nysenate/openleg/processor/BaseXmlProcessorTest.java index 150cf5507..fe776bf68 100644 --- a/src/test/java/gov/nysenate/openleg/processor/BaseXmlProcessorTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/BaseXmlProcessorTest.java @@ -138,14 +138,7 @@ protected void processXmlFile(String xmlFilePath) { * Get a bill amendment from the db */ protected BillAmendment getAmendment(BillId billId) throws BillNotFoundEx, BillAmendNotFoundEx { - return getAmendment(billId, BillTextFormat.PLAIN); - } - - /** - * Get a bill amendment from the db - */ - protected BillAmendment getAmendment(BillId billId, BillTextFormat htmlText) throws BillNotFoundEx, BillAmendNotFoundEx { - Bill bill = billDataService.getBill(BaseBillId.of(billId), Collections.singleton(htmlText)); + Bill bill = billDataService.getBill(BaseBillId.of(billId)); return bill.getAmendment(billId.getVersion()); } diff --git a/src/test/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessorTest.java b/src/test/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessorTest.java new file mode 100644 index 000000000..16c4b1757 --- /dev/null +++ b/src/test/java/gov/nysenate/openleg/processor/bill/BillTextDiffProcessorTest.java @@ -0,0 +1,399 @@ +package gov.nysenate.openleg.processor.bill; + +import gov.nysenate.openleg.annotation.UnitTest; +import gov.nysenate.openleg.model.bill.BillText; +import gov.nysenate.openleg.model.bill.BillTextFormat; +import gov.nysenate.openleg.model.bill.TextDiff; +import gov.nysenate.openleg.model.bill.TextDiffType; +import org.checkerframework.checker.units.qual.A; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@Category(UnitTest.class) +public class BillTextDiffProcessorTest { + + private BillTextDiffProcessor textProcessor; + + @Before + public void before() { + textProcessor = new BillTextDiffProcessor(); + } + + @Test + public void givenNullText_returnEmptyBillText() { + String text = null; + BillText actual = textProcessor.processBillText(text); + + BillText expected = new BillText(new ArrayList<>()); + assertEquals(expected, actual); + } + + @Test + public void givenEmptyText_returnEmptyBillText() { + String text = ""; + BillText actual = textProcessor.processBillText(text); + + BillText expected = new BillText(new ArrayList<>()); + assertEquals(expected, actual); + } + + @Test + public void givenSectionSymbolAltCode_convertsIntoSectionSymbol() { + String text = "§"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "§")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void recognizesUnchangedText() { + String text = "THE NEW YORK STATE SENATE"; + BillText actual = textProcessor.processBillText(text); + + TextDiff diff = new TextDiff(TextDiffType.UNCHANGED, text); + BillText expected = new BillText(Arrays.asList(diff)); + assertEquals(expected, actual); + } + + @Test + public void recognizesAddedText() { + String text = "Commends the <B><U>Albany fire department</U></B>"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "Commends the ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "Albany fire department")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void recognizesRemovedText() { + String text = "Commends the <B><S>Alb fire department</S></B>"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "Commends the ")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "Alb fire department")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void recognizesAddedAndRemovedTogether() { + String text = "Commends the <B><S>Alb</S></B><B><U>Albany</U></B> fire department"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "Commends the ")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "Alb")); + diffs.add(new TextDiff(TextDiffType.ADDED, "Albany")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " fire department")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void recognizesBoldedText() { + String text = "<B>Commends</B> the Albany fire department"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.BOLD, "Commends")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " the Albany fire department")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void recognizesHeaderText() { + String text = "<FONT SIZE=5><B> STATE OF NEW YORK</B></FONT>"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.HEADER, " STATE OF NEW YORK")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void recognizesPageBreak() { + String text = "page one text\n" + + "<P CLASS=\"brk\">\n" + + "page two text"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "page one text\n")); + diffs.add(new TextDiff(TextDiffType.PAGE_BREAK, "")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\npage two text")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void whitespaceIsPreserved() { + String text = "<PRE> Commends the <B><U> Albany fire department</U></B></PRE>"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " Commends the ")); + diffs.add(new TextDiff(TextDiffType.ADDED, " Albany fire department")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void newLinesPreserved() { + String text = "Commends \n" + + "the <B><U>Albany fire \n" + + "department</U></B>"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "Commends \nthe ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "Albany fire \ndepartment")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void styleAndBaseFontElementIsRemoved() { + String text = "\n<STYLE><!--U {color: Green}S {color: RED} I {color: DARKBLUE; background-color:yellow}\n" + + " P.brk {page-break-before:always}--></STYLE>\n" + + "<BASEFONT SIZE=3>\n" + + "<PRE WIDTH=\"127\">\n" + + " STATE OF NEW YORK"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\n STATE OF NEW YORK")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void stripsOtherXmlElements() { + String text = "<BASEFONT SIZE=3>\n" + + "<PRE WIDTH=\"136\">\n" + + "STATE OF NEW YORK\n" + + "Commends the city of Albany"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\nSTATE OF NEW YORK\nCommends the city of Albany")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void fullTest() { + String text = "<STYLE><!--U {color: Green}S {color: RED} I {color: DARKBLUE; background-color:yellow}\n" + + "P.brk {page-break-before:always}--></STYLE>\n" + + "<BASEFONT SIZE=3>\n" + + "<PRE WIDTH=\"127\">\n" + + "\n" + + "<FONT SIZE=5><B> STATE OF NEW YORK</B></FONT>\n" + + "\n" + + "<B>Commends</B> the city of <B><S>albany</S></B><B><U>Albany</U></B> New York\n" + + "on <B><S>there</S></B><B><U>their</U></B> work."; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\n\n")); + diffs.add(new TextDiff(TextDiffType.HEADER, " STATE OF NEW YORK")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\n\n")); + diffs.add(new TextDiff(TextDiffType.BOLD, "Commends")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " the city of ")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "albany")); + diffs.add(new TextDiff(TextDiffType.ADDED, "Albany")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " New York\non ")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "there")); + diffs.add(new TextDiff(TextDiffType.ADDED, "their")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " work.")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void onlyOneEmptyLineBetweenPages() { + String text = "<PRE>\n" + + " EXPLANATION--Matter in <B><U>italics</U></B> (underscored) is new; matter in brackets\n" + + " [<B><S> </S></B>] is old law to be omitted.\n" + + " LBD03867-09-9\n" + + "</PRE><P CLASS=\"brk\"><PRE WIDTH=\"127\">\n" + // This should be a single empty line. + "A. 1133--D 2\n" + + "\n" + + "1 provision in any section contained within a Part, including the effec-"; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\n EXPLANATION--Matter in ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "italics")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " (underscored) is new; matter in brackets\n [")); + diffs.add(new TextDiff(TextDiffType.REMOVED, " ")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "] is old law to be omitted.\n" + + " LBD03867-09-9\n")); + diffs.add(new TextDiff(TextDiffType.PAGE_BREAK, "")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\n" + + "A. 1133--D 2\n" + + "\n" + + "1 provision in any section contained within a Part, including the effec-")); + + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void formatsPageBreaksCorrectly() { + String text = + "<PRE> 54 license and record database established pursuant to section 400.02 of\n" + + " 55 this chapter.\n" + + "</PRE><P CLASS=\"brk\"><PRE WIDTH=\"99\">\n" + + " S. 2143--A 3\n" + + " \n" + + " 1 (3) Any gunsmith who fails to comply with the provisions of this\n" + + " 2 section shall be guilty of a class C felony."; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + " 54 license and record database established pursuant to section 400.02 of\n" + + " 55 this chapter.\n")); + diffs.add(new TextDiff(TextDiffType.PAGE_BREAK, "")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + "\n" + + " S. 2143--A 3\n" + + " \n" + + " 1 (3) Any gunsmith who fails to comply with the provisions of this\n" + + " 2 section shall be guilty of a class C felony.")); + + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Test + public void doesNotEscapeHtmlEntities() { + String text = "<B><U>THE & NEW < YORK > STATE \" SENATE</U></B>"; + BillText actual = textProcessor.processBillText(text); + + TextDiff diff = new TextDiff(TextDiffType.ADDED, "THE & NEW < YORK > STATE \" SENATE"); + BillText expected = new BillText(Arrays.asList(diff)); + assertEquals(expected, actual); + } + + @Test + public void correctlyFormatsChangesSpanningPageBreak() { + String text = + "<PRE> 46 New York. Upon request of the commissioner of the office of [<B><S>alco-</S></B>\n" + + "</PRE><P CLASS=\"brk\"><PRE WIDTH=\"136\">\n" + + " 449 12654-09-0\n" + + "</S></B>\n" + + " <B><S>DEPARTMENT OF MENTAL HYGIENE</S></B>\n" + + "</S></B>\n" + + " <B><S>OFFICE OF [ALCOHOLISM AND SUBSTANCE ABUSE</S></B>] <B><U>ADDICTION</U></B>\n" + + " SERVICES <B><U>AND SUPPORTS</U></B>\n" + + " \n" + + " CAPITAL PROJECTS - REAPPROPRIATIONS 2020-21\n" + + " \n" + + " 1 holism and substance abuse] <B><U>addiction</U></B> services <B><U>and supports</U></B> and"; + + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + " 46 New York. Upon request of the commissioner of the office of [")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "alco-")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "\n")); + diffs.add(new TextDiff(TextDiffType.PAGE_BREAK, "")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + "\n" + + " 449 12654-09-0\n" + + "\n ")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "DEPARTMENT OF MENTAL HYGIENE")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + "\n" + + "\n" + + " ")); + diffs.add(new TextDiff(TextDiffType.REMOVED, "OFFICE OF [ALCOHOLISM AND SUBSTANCE ABUSE")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "] ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "ADDICTION")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + "\n" + + " SERVICES ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "AND SUPPORTS")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, + "\n" + + " \n" + + " CAPITAL PROJECTS - REAPPROPRIATIONS 2020-21\n" + + " \n" + + " 1 holism and substance abuse] ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "addiction")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " services ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "and supports")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " and")); + + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + /** + * Nested added/removed element tests. + * + * Normally bill text contains brackets and removed elements around text which has been removed + * from the previous version. i.e. [<B><S>text that was removed</S></B>] + * + * However there appears to be a bug in the xml bill text we receive causing the <B><S></S></B> elements to be + * added even in situations where brackets are used in the bill text itself. + * + * This can cause issues with our parser because it makes no sense for these tags to be nested in each other. + * + * To keep our raw text accurate, we remove the <B><S></S></B> element in these instances. + * + * Examples of this: + * - 2019-01-15-18.16.27.610484_BILLTEXT_S01533.XML page 1 line 2-4 + * - 2020-03-12-11.04.18.634283_BILLTEXT_S01527C.XML page 76 line 4-8 + * - 2019-04-03-14.56.51.698683_BILLTEXT_S04984.XML page 12 line 49 + * + * // TODO figure out a solution for these edge cases. + */ + + @Ignore + @Test + public void givenRemovedElementInsideAddedElement_removedElementIgnored() { + String text = "Foo <B><U>Record & Return by [<B><S></S></B>] Mail [<B><S></S></B>] Pickup to:</U></B> bar."; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "Foo ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "Record & Return by [] Mail [] Pickup to:")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " bar.")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } + + @Ignore + @Test + public void givenAddedElementInsideRemovedElement_removeElementIgnored() { + String text = "the [<B><S>new york <B><U>state</U></B> senate</S></B>] in albany."; + BillText actual = textProcessor.processBillText(text); + + List<TextDiff> diffs = new ArrayList<>(); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, "the [new york ")); + diffs.add(new TextDiff(TextDiffType.ADDED, "state")); + diffs.add(new TextDiff(TextDiffType.UNCHANGED, " senate] in albany.")); + BillText expected = new BillText(diffs); + assertEquals(expected, actual); + } +} diff --git a/src/test/java/gov/nysenate/openleg/processor/bill/text/BillTextUtilsTest.java b/src/test/java/gov/nysenate/openleg/processor/bill/text/BillTextUtilsTest.java index baa86643a..b81838a87 100644 --- a/src/test/java/gov/nysenate/openleg/processor/bill/text/BillTextUtilsTest.java +++ b/src/test/java/gov/nysenate/openleg/processor/bill/text/BillTextUtilsTest.java @@ -9,6 +9,23 @@ @Category(UnitTest.class) public class BillTextUtilsTest { + final String bill = "\n<STYLE><!--U {color: Green}S {color: RED} I {color: DARKBLUE; background-color:yellow}\n" + + " P.brk {page-break-before:always}--></STYLE>\n" + + "<BASEFONT SIZE=3>\n" + + "<PRE WIDTH=\"136\">\n" + + "\n" + + "<FONT SIZE=5><B> STATE OF NEW YORK</B></FONT>\n" + + " ________________________________________________________________________\n" + + " 155\n" + + " 2019-2020 Regular Sessions\n" + + "\n" + + "<FONT SIZE=5><B> IN SENATE</B></FONT>\n" + + " <B><U>(Prefiled)</U></B>\n" + + "!!text has been abridged for test purposes!!\n" + + " EXPLANATION--Matter in <B><U>italics</U></B> (underscored) is new; matter in brackets\n" + + " [<B><S>removed text</S></B>] is old law to be omitted.\n" + + " LBD04958-01-9\n"; + @Test public void senateBillTextFormatTest() { @@ -31,25 +48,25 @@ public void senateBillTextFormatTest() { " printed to be committed to the Committee on Consumer Protection --\n" + " recommitted to the Committee on Consumer Protection in accordance with\n" + " Senate Rule 6, sec. 8 -- committee discharged, bill amended, ordered"; - String expectedResult = "\n" + - " S T A T E O F N E W Y O R K\n" + - " ________________________________________________________________________\n" + - "\n" + - " 100--A\n" + - "\n" + - " 2017-2018 Regular Sessions\n" + - "\n" + - " I N S E N A T E\n" + - "\n" + - " (PREFILED)\n" + - "\n" + - " January 4, 2017\n" + - " ___________\n" + - "\n" + - " Introduced by Sen. HOYLMAN -- read twice and ordered printed, and when\n" + - " printed to be committed to the Committee on Consumer Protection --\n" + - " recommitted to the Committee on Consumer Protection in accordance with\n" + - " Senate Rule 6, sec. 8 -- committee discharged, bill amended, ordered"; + String expectedResult = " \n" + + " S T A T E O F N E W Y O R K\n" + + " ________________________________________________________________________\n" + + " \n" + + " 100--A\n" + + " \n" + + " 2017-2018 Regular Sessions\n" + + " \n" + + " I N S E N A T E\n" + + " \n" + + " (PREFILED)\n" + + " \n" + + " January 4, 2017\n" + + " ___________\n" + + " \n" + + " Introduced by Sen. HOYLMAN -- read twice and ordered printed, and when\n" + + " printed to be committed to the Committee on Consumer Protection --\n" + + " recommitted to the Committee on Consumer Protection in accordance with\n" + + " Senate Rule 6, sec. 8 -- committee discharged, bill amended, ordered"; assertEquals(expectedResult, BillTextUtils.formatHtmlExtractedBillText(inputText)); } @@ -70,21 +87,21 @@ public void assemblyBillTextFormatTest() { " ___________\n" + " \n" + " Introduced by M. of A. SIMON, ARROYO, BLAKE, BARRETT, BRAUNSTEIN,"; - String expectedResult = "\n" + - " S T A T E O F N E W Y O R K\n" + - " ________________________________________________________________________\n" + - "\n" + - " 1051--A\n" + - " Cal. No. 17\n" + - "\n" + - " 2017-2018 Regular Sessions\n" + - "\n" + - " I N A S S E M B L Y\n" + - "\n" + - " January 10, 2017\n" + - " ___________\n" + - "\n" + - " Introduced by M. of A. SIMON, ARROYO, BLAKE, BARRETT, BRAUNSTEIN,"; + String expectedResult = " \n" + + " S T A T E O F N E W Y O R K\n" + + " ________________________________________________________________________\n" + + " \n" + + " 1051--A\n" + + " Cal. No. 17\n" + + " \n" + + " 2017-2018 Regular Sessions\n" + + " \n" + + " I N A S S E M B L Y\n" + + " \n" + + " January 10, 2017\n" + + " ___________\n" + + " \n" + + " Introduced by M. of A. SIMON, ARROYO, BLAKE, BARRETT, BRAUNSTEIN,"; assertEquals(expectedResult, BillTextUtils.formatHtmlExtractedBillText(inputText)); } @@ -102,18 +119,18 @@ public void uniBillTextFormatTest() { " ___________\n" + " \n" + " IN SENATE -- A BUDGET BILL, submitted by the Governor pursuant to arti-"; - String expectedResult = "\n" + - " S T A T E O F N E W Y O R K\n" + - " ________________________________________________________________________\n" + - "\n" + - " S. 2005--C A. 3005--C\n" + - "\n" + - " S E N A T E - A S S E M B L Y\n" + - "\n" + - " January 23, 2017\n" + - " ___________\n" + - "\n" + - " IN SENATE -- A BUDGET BILL, submitted by the Governor pursuant to arti-"; + String expectedResult = " \n" + + " S T A T E O F N E W Y O R K\n" + + " ________________________________________________________________________\n" + + " \n" + + " S. 2005--C A. 3005--C\n" + + " \n" + + " S E N A T E - A S S E M B L Y\n" + + " \n" + + " January 23, 2017\n" + + " ___________\n" + + " \n" + + " IN SENATE -- A BUDGET BILL, submitted by the Governor pursuant to arti-"; assertEquals(expectedResult, BillTextUtils.formatHtmlExtractedBillText(inputText)); } diff --git a/src/test/java/gov/nysenate/openleg/processor/bill/text/XmlBillTextProcessorIT.java b/src/test/java/gov/nysenate/openleg/processor/bill/text/XmlBillTextProcessorIT.java index cc629f234..73e268104 100644 --- a/src/test/java/gov/nysenate/openleg/processor/bill/text/XmlBillTextProcessorIT.java +++ b/src/test/java/gov/nysenate/openleg/processor/bill/text/XmlBillTextProcessorIT.java @@ -5,13 +5,10 @@ import gov.nysenate.openleg.processor.BaseXmlProcessorTest; import gov.nysenate.openleg.service.bill.data.BillAmendNotFoundEx; import gov.nysenate.openleg.service.bill.data.BillNotFoundEx; -import gov.nysenate.openleg.util.FileIOUtils; import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.experimental.categories.Category; -import java.io.IOException; - import static gov.nysenate.openleg.model.bill.BillTextFormat.HTML; import static gov.nysenate.openleg.model.bill.BillTextFormat.PLAIN; import static org.junit.Assert.*; @@ -99,27 +96,13 @@ public void removeUniBill(){ assertTrue("Post processed text is blank", StringUtils.isBlank(getPlainText(asmBillId))); } - @Test - public void htmlTextTest() throws IOException { - final String path = resourceDir + "/2017-01-01-00.00.00.000000_BILLTEXT_S09999.XML"; - final BillId billId = new BillId("S9999", 2017); - final String expectedHtml = FileIOUtils.getResourceFileContents(resourceDir + "/S9999_expected.html"); - final String expectedPlain = FileIOUtils.getResourceFileContents(resourceDir + "/S9999_expected.txt"); - - assertNotEquals("Preprocessed html is not set", expectedHtml, getHtmlTextSafe(billId)); - assertNotEquals("Preprocessed plaintext is not set", expectedPlain, getPlainTextSafe(billId)); - processXmlFile(path); - assertEquals("post processed html is set", expectedHtml, getHtmlText(billId)); - assertEquals("post processed plaintext is set", expectedPlain, getPlainText(billId)); - } - /* --- Internal Methods --- */ /** * Get the full text for a bill id */ private String getPlainText(BillId billId) { - return getAmendment(billId, PLAIN).getFullText(PLAIN); + return getAmendment(billId).getFullText(PLAIN); } /** @@ -137,7 +120,7 @@ private String getPlainTextSafe(BillId billId) { * Get the html text for a bill id. */ private String getHtmlText(BillId billId) { - return getAmendment(billId, HTML).getFullText(HTML); + return getAmendment(billId).getFullText(HTML); } /** @@ -145,7 +128,7 @@ private String getHtmlText(BillId billId) { */ private String getHtmlTextSafe(BillId billId) { try { - return getPlainText(billId); + return getHtmlText(billId); } catch (BillNotFoundEx | BillAmendNotFoundEx ex) { return null; } diff --git a/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java b/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java deleted file mode 100644 index c2d87dcc6..000000000 --- a/src/test/java/gov/nysenate/openleg/stupid/BillTextTest.java +++ /dev/null @@ -1,198 +0,0 @@ -package gov.nysenate.openleg.stupid; - -import gov.nysenate.openleg.BaseTests; -import gov.nysenate.openleg.annotation.SillyTest; -import gov.nysenate.openleg.dao.base.LimitOffset; -import gov.nysenate.openleg.dao.base.SortOrder; -import gov.nysenate.openleg.dao.bill.scrape.SqlFsBillScrapeReferenceDao; -import gov.nysenate.openleg.model.base.SessionYear; -import gov.nysenate.openleg.model.bill.BaseBillId; -import gov.nysenate.openleg.model.spotcheck.SpotCheckRefType; -import gov.nysenate.openleg.model.spotcheck.billscrape.BillScrapeQueueEntry; -import gov.nysenate.openleg.model.spotcheck.billscrape.ScrapeQueuePriority; -import gov.nysenate.openleg.service.spotcheck.base.SpotcheckRunService; -import gov.nysenate.openleg.service.spotcheck.scrape.BillScrapeReportService; -import gov.nysenate.openleg.service.spotcheck.scrape.BillScrapeSpotcheckProcessService; -import gov.nysenate.openleg.util.StringDiffer; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.nodes.Node; -import org.jsoup.nodes.TextNode; -import org.jsoup.select.Elements; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.stream.Collectors; - -/** - */ -@Category(SillyTest.class) -public class BillTextTest extends BaseTests { - - private static final Logger logger = LoggerFactory.getLogger(BillTextTest.class); - - @Autowired - SpotcheckRunService runService; - - @Autowired - BillScrapeReportService report; - - @Autowired - BillScrapeSpotcheckProcessService procService; - - @Autowired - SqlFsBillScrapeReferenceDao dao; - - @Test - public void processTest() throws Exception{ - procService.collate(); - procService.ingest(); - // if processing occurred, a report should be run - } - - @Test - public void queueThenProcessTest() throws Exception { - queueTest(); - processTest(); - } - - @Test - public void runTest() { - runService.runReports(SpotCheckRefType.LBDC_SCRAPED_BILL); - } - - @Test - public void getReportTest() { - LocalDateTime reportDateTime = LocalDateTime.parse("2015-04-29T11:11:13"); -// new ReportDetailView<>( -// report.getReport(new SpotCheckReportId(SpotCheckRefType.LBDC_SCRAPED_BILL, reportDateTime))); - } - - @Test - public void queueTest() { - List<BaseBillId> billIds = Collections.singletonList( - new BaseBillId("S5513", 2015)); - billIds.forEach(billId -> dao.addBillToScrapeQueue(billId, ScrapeQueuePriority.MANUAL_ENTRY.getPriority())); - logger.info("queue is now {}", dao.getScrapeQueue(LimitOffset.ALL, SortOrder.DESC).getResults().stream() - .map(BillScrapeQueueEntry::getBaseBillId) - .collect(Collectors.toList())); - } - -///////////////////////////////////////////////////////////////////////////// - - @Test - public void getBillsTest(){ -// BaseBillId id2 = new BaseBillId("S1", 2015); -// List<BillScrapeReference> list = dao.getBillScrapeReference(id2); -// -// for (BillScrapeReference ref : list){ -// System.out.println("________________________________"); -// System.out.println("print no:::: "+ ref.getPrintNo()); -// System.out.println("Amend:::: "+ ref.getActiveVersion()); -// System.out.println("Session Year:::: "+ ref.getSessionYear()); -// System.out.println("refDate :::: "+ ref.getReferenceDate()); -// } - } - - @Test - public void getBillTest(){ - BaseBillId id2 = new BaseBillId("S1", new SessionYear(2015)); - - String str = "2015-03-31 12:52:39.986"; - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); - LocalDateTime dateTime = LocalDateTime.parse(str, formatter); - -// BillTextReference ref = dao.getPKBillTextReference(id2, dateTime); -// -// System.out.println("________________turned to ref object________________"); -// System.out.println("print no:::: "+ ref.getPrintNo()); -// System.out.println("Amend:::: "+ ref.getActiveVersion()); -// System.out.println("refDate:::: "+ ref.getReferenceDate()); -// System.out.println("Session Year:::: "+ ref.getSessionYear()); - } - - ///////////////////////////////////////////////////////////////////////////// - @Test - public void diffTest() throws Exception { - String s1= "as"; - String s2 = "asd"; - StringDiffer dif = new StringDiffer(); - LinkedList<StringDiffer.Diff> diffs = dif.diff_main(s1, s2); - System.out.println(diffs); - } - - @Test - public void StringTest() throws Exception { - - - String y = "\\n texttexttext \\n"; - //String txt = y.replaceAll("\\r\\n|\\r|\\n", " "); - String txt = y.replaceAll("\\\\n", ""); - System.out.println(txt); - } - @Test - public void findDeletes() throws Exception{ - BufferedReader br = new BufferedReader(new FileReader("~.DeleteStuff.txt")); - Path path = FileSystems.getDefault().getPath("home/kyle", "DeleteStuff.log"); - - ArrayList<String> y = new ArrayList<String>(Files.readAllLines(path)) { - }; - - } - @Test - public void jsoupTest() { - String html = - "<span id='hi'>" + - "some text" + - "<u>some more text</u>" + - "<notu>texxxxt</notu>" + - "even more text" + - "</span>"; - Document doc = Jsoup.parse(html); - - Elements eles = doc.getElementById("hi").children(); - -// for (Element e : eles) { -// logger.info(e.text()); -// } - - StringBuilder stringBuilder = new StringBuilder(); - - processNode(doc.getElementById("hi"), stringBuilder); - - System.out.println(stringBuilder.toString()); - - } - - void processNode(Element ele, StringBuilder stringBuilder) { - - for (Node t : ele.childNodes()) { - if (t instanceof Element) { - Element e = (Element) t; - if (e.tag().getName().equals("u")) { - stringBuilder.append(e.text().toUpperCase()); - stringBuilder.append("\n"); - } else { - processNode(e, stringBuilder); - } - } else if (t instanceof TextNode) { - stringBuilder.append(((TextNode) t).text()); - stringBuilder.append("\n"); - } - } - } -} - - diff --git a/src/test/java/gov/nysenate/openleg/testNonHTMLTextParse.java b/src/test/java/gov/nysenate/openleg/testNonHTMLTextParse.java deleted file mode 100644 index 6d7b6cefd..000000000 --- a/src/test/java/gov/nysenate/openleg/testNonHTMLTextParse.java +++ /dev/null @@ -1,15 +0,0 @@ -package gov.nysenate.openleg; - -import gov.nysenate.openleg.util.BillTextUtils; -import org.junit.Test; - -public class testNonHTMLTextParse { - - @Test - public void testNonHTMLTextParsing() { - String sampleText = "This is the greatest bill. No one ever could have written a bill this good." - + "\n" + "This bill is revolutionary"; - - System.out.println(BillTextUtils.parseHTMLtext(sampleText)); - } -} diff --git a/src/test/resources/processor/bill/text/2017-01-01-00.00.00.000000_BILLTEXT_S09999.XML b/src/test/resources/processor/bill/text/2017-01-01-00.00.00.000000_BILLTEXT_S09999.XML deleted file mode 100644 index 739d5731a..000000000 --- a/src/test/resources/processor/bill/text/2017-01-01-00.00.00.000000_BILLTEXT_S09999.XML +++ /dev/null @@ -1,57 +0,0 @@ -<billtext_html sessyr="2017" senhse="S" senno="9999" senamd=" " asmhse="" asmno="" asmamd="" action="replace"><![CDATA[ -<STYLE><!--U {color: Green}S {color: RED} I {color: DARKBLUE; background-color:yellow} -P.brk {page-break-before:always}--></STYLE> -<BASEFONT SIZE=3> -<PRE WIDTH="94"> - -<FONT SIZE=5><B> STATE OF NEW YORK</B></FONT> - ________________________________________________________________________ - - 9999 - - 2017-2018 Regular Sessions - -<FONT SIZE=5><B> IN SENATE</B></FONT> - - <B><U>(Prefiled)</U></B> - - January 4, 2017 - ___________ - - Introduced by Sen. SQUAREPANTS -- read twice and ordered printed, and - when printed to be committed to the Committee on Nonsense - - AN ACT to amend the dairy law, in relation to giving and/or receiving - unlawful ice cream cones - - <B><U>The People of the State of New York, represented in Senate and Assem-</U></B> - <B><U>bly, do enact as follows:</U></B> - - 1 Section 1. The dairy law is amended by adding a new section 000.00 to - 2 read as follows: - 3 <B><U>§ 000.00 Giving unlawful cones.</U></B> - 4 <B><U>Anyone who otherwise than as provided by law for the proper discharge</U></B> - 5 <B><U>of coned ice cream directly or indirectly gives, offers or promises any</U></B> - 6 <B><U>cone with a value in excess of one million cookies, with the</U></B> - 7 <B><U>exception of cookie dough as defined under article umpteen of the</U></B> - 8 <B><U>baked goods law, to any public official or goat selected to be a public</U></B> - 9 <B><U>official because of that goat's official position.</U></B> - 10 <B><U>Giving unlawful ice cream is a class Z felony.</U></B> - 11 § 2. The dairy law is amended by adding a new section 000.00 to read - 12 as follows: - 13 <B><U>§ 000.00 Receiving unlawful cones.</U></B> - 14 <B><U>Anyone who, being a public official or goat selected to be a public</U></B> - 15 <B><U>official, otherwise than as provided by law for the proper discharge of</U></B> - 16 <B><U>coned ice cream, directly or indirectly seeks, receives or agrees to</U></B> - 17 <B><U>receive any ice cream, with the exception of cookie dough provided udder</U></B> - 18 <B><U>article umpteen of the baked goods law, with a value in excess of one</U></B> - 19 <B><U>million cookies because of that goat's official position.</U></B> - 20 <B><U>Receiving unlawful cones is a class Z felony.</U></B> - 21 § 3. This act shall take effect on the nine thousandth day after it - 22 shall have become a law. - - EXPLANATION--Matter in <B><U>italics</U></B> (underscored) is new; matter in brackets - [<B><S> </S></B>] is old law to be omitted. - LBD03969-02-7 -</pre> -]]></billtext_html> diff --git a/src/test/resources/processor/bill/text/S9999_expected.html5 b/src/test/resources/processor/bill/text/S9999_expected.html5 new file mode 100644 index 000000000..1e7fd9e06 --- /dev/null +++ b/src/test/resources/processor/bill/text/S9999_expected.html5 @@ -0,0 +1,54 @@ + + +<PRE> + +<SPAN class="ol-text-large"><B> STATE OF NEW YORK</B></SPAN> + ________________________________________________________________________ + + 9999 + + 2017-2018 Regular Sessions + +<SPAN class="ol-text-large"><B> IN SENATE</B></SPAN> + + <span class="ol-text-change ol-text-added">(Prefiled)</span> + + January 4, 2017 + ___________ + + Introduced by Sen. SQUAREPANTS -- read twice and ordered printed, and + when printed to be committed to the Committee on Nonsense + + AN ACT to amend the dairy law, in relation to giving and/or receiving + unlawful ice cream cones + + <span class="ol-text-change ol-text-added">The People of the State of New York, represented in Senate and Assem-</span> + <span class="ol-text-change ol-text-added">bly, do enact as follows:</span> + + 1 Section 1. The dairy law is amended by adding a new section 000.00 to + 2 read as follows: + 3 <span class="ol-text-change ol-text-added">§ 000.00 Giving unlawful cones.</span> + 4 <span class="ol-text-change ol-text-added">Anyone who otherwise than as provided by law for the proper discharge</span> + 5 <span class="ol-text-change ol-text-added">of coned ice cream directly or indirectly gives, offers or promises any</span> + 6 <span class="ol-text-change ol-text-added">cone with a value in excess of one million cookies, with the</span> + 7 <span class="ol-text-change ol-text-added">exception of cookie dough as defined under article umpteen of the</span> + 8 <span class="ol-text-change ol-text-added">baked goods law, to any public official or goat selected to be a public</span> + 9 <span class="ol-text-change ol-text-added">official because of that goat's official position.</span> + 10 <span class="ol-text-change ol-text-added">Giving unlawful ice cream is a class Z felony.</span> + 11 § 2. The dairy law is amended by adding a new section 000.00 to read + 12 as follows: + 13 <span class="ol-text-change ol-text-added">§ 000.00 Receiving unlawful cones.</span> + 14 <span class="ol-text-change ol-text-added">Anyone who, being a public official or goat selected to be a public</span> + 15 <span class="ol-text-change ol-text-added">official, otherwise than as provided by law for the proper discharge of</span> + 16 <span class="ol-text-change ol-text-added">coned ice cream, directly or indirectly seeks, receives or agrees to</span> + 17 <span class="ol-text-change ol-text-added">receive any ice cream, with the exception of cookie dough provided udder</span> + 18 <span class="ol-text-change ol-text-added">article umpteen of the baked goods law, with a value in excess of one</span> + 19 <span class="ol-text-change ol-text-added">million cookies because of that goat's official position.</span> + 20 <span class="ol-text-change ol-text-added">Receiving unlawful cones is a class Z felony.</span> + 21 § 3. This act shall take effect on the nine thousandth day after it + 22 shall have become a law. + + EXPLANATION--Matter in <span class="ol-text-change ol-text-added">italics</span> (underscored) is new; matter in brackets + <span class="ol-text-change ol-text-removed"> </span> is old law to be omitted. + LBD03969-02-7 +</pre> diff --git a/src/test/resources/processor/bill/text/S9999_expected.json b/src/test/resources/processor/bill/text/S9999_expected.json new file mode 100644 index 000000000..e4491b0a9 --- /dev/null +++ b/src/test/resources/processor/bill/text/S9999_expected.json @@ -0,0 +1 @@ +[{"type":0,"text":"\n\n\n\n STATE OF NEW YORK\n ________________________________________________________________________\n\n 9999\n\n 2017-2018 Regular Sessions\n\n IN SENATE\n\n ","html":"\n<BASEFONT SIZE=3>\n<PRE WIDTH=\"94\">\n\n<FONT SIZE=5><B> STATE OF NEW YORK</B></FONT>\n ________________________________________________________________________\n\n 9999\n\n 2017-2018 Regular Sessions\n\n<FONT SIZE=5><B> IN SENATE</B></FONT>\n\n "},{"type":1,"text":"(Prefiled)","html":"(Prefiled)"},{"type":0,"text":"\n\n January 4, 2017\n ___________\n\n Introduced by Sen. SQUAREPANTS -- read twice and ordered printed, and\n when printed to be committed to the Committee on Nonsense\n\n AN ACT to amend the dairy law, in relation to giving and/or receiving\n unlawful ice cream cones\n\n ","html":"\n\n January 4, 2017\n ___________\n\n Introduced by Sen. SQUAREPANTS -- read twice and ordered printed, and\n when printed to be committed to the Committee on Nonsense\n\n AN ACT to amend the dairy law, in relation to giving and/or receiving\n unlawful ice cream cones\n\n "},{"type":1,"text":"The People of the State of New York, represented in Senate and Assem-","html":"The People of the State of New York, represented in Senate and Assem-"},{"type":0,"text":"\n ","html":"\n "},{"type":1,"text":"bly, do enact as follows:","html":"bly, do enact as follows:"},{"type":0,"text":"\n\n 1 Section 1. The dairy law is amended by adding a new section 000.00 to\n 2 read as follows:\n 3 ","html":"\n\n 1 Section 1. The dairy law is amended by adding a new section 000.00 to\n 2 read as follows:\n 3 "},{"type":1,"text":"§ 000.00 Giving unlawful cones.","html":"§ 000.00 Giving unlawful cones."},{"type":0,"text":"\n 4 ","html":"\n 4 "},{"type":1,"text":"Anyone who otherwise than as provided by law for the proper discharge","html":"Anyone who otherwise than as provided by law for the proper discharge"},{"type":0,"text":"\n 5 ","html":"\n 5 "},{"type":1,"text":"of coned ice cream directly or indirectly gives, offers or promises any","html":"of coned ice cream directly or indirectly gives, offers or promises any"},{"type":0,"text":"\n 6 ","html":"\n 6 "},{"type":1,"text":"cone with a value in excess of one million cookies, with the","html":"cone with a value in excess of one million cookies, with the"},{"type":0,"text":"\n 7 ","html":"\n 7 "},{"type":1,"text":"exception of cookie dough as defined under article umpteen of the","html":"exception of cookie dough as defined under article umpteen of the"},{"type":0,"text":"\n 8 ","html":"\n 8 "},{"type":1,"text":"baked goods law, to any public official or goat selected to be a public","html":"baked goods law, to any public official or goat selected to be a public"},{"type":0,"text":"\n 9 ","html":"\n 9 "},{"type":1,"text":"official because of that goat's official position.","html":"official because of that goat's official position."},{"type":0,"text":"\n 10 ","html":"\n 10 "},{"type":1,"text":"Giving unlawful ice cream is a class Z felony.","html":"Giving unlawful ice cream is a class Z felony."},{"type":0,"text":"\n 11 § 2. The dairy law is amended by adding a new section 000.00 to read\n 12 as follows:\n 13 ","html":"\n 11 § 2. The dairy law is amended by adding a new section 000.00 to read\n 12 as follows:\n 13 "},{"type":1,"text":"§ 000.00 Receiving unlawful cones.","html":"§ 000.00 Receiving unlawful cones."},{"type":0,"text":"\n 14 ","html":"\n 14 "},{"type":1,"text":"Anyone who, being a public official or goat selected to be a public","html":"Anyone who, being a public official or goat selected to be a public"},{"type":0,"text":"\n 15 ","html":"\n 15 "},{"type":1,"text":"official, otherwise than as provided by law for the proper discharge of","html":"official, otherwise than as provided by law for the proper discharge of"},{"type":0,"text":"\n 16 ","html":"\n 16 "},{"type":1,"text":"coned ice cream, directly or indirectly seeks, receives or agrees to","html":"coned ice cream, directly or indirectly seeks, receives or agrees to"},{"type":0,"text":"\n 17 ","html":"\n 17 "},{"type":1,"text":"receive any ice cream, with the exception of cookie dough provided udder","html":"receive any ice cream, with the exception of cookie dough provided udder"},{"type":0,"text":"\n 18 ","html":"\n 18 "},{"type":1,"text":"article umpteen of the baked goods law, with a value in excess of one","html":"article umpteen of the baked goods law, with a value in excess of one"},{"type":0,"text":"\n 19 ","html":"\n 19 "},{"type":1,"text":"million cookies because of that goat's official position.","html":"million cookies because of that goat's official position."},{"type":0,"text":"\n 20 ","html":"\n 20 "},{"type":1,"text":"Receiving unlawful cones is a class Z felony.","html":"Receiving unlawful cones is a class Z felony."},{"type":0,"text":"\n 21 § 3. This act shall take effect on the nine thousandth day after it\n 22 shall have become a law.\n\n EXPLANATION--Matter in ","html":"\n 21 § 3. This act shall take effect on the nine thousandth day after it\n 22 shall have become a law.\n\n EXPLANATION--Matter in "},{"type":1,"text":"italics","html":"italics"},{"type":0,"text":" (underscored) is new; matter in brackets\n ","html":" (underscored) is new; matter in brackets\n "},{"type":-1,"text":" ","html":" "},{"type":0,"text":" is old law to be omitted.\n LBD03969-02-7\n\n","html":" is old law to be omitted.\n LBD03969-02-7\n</pre>\n"}] \ No newline at end of file From 34c775863138d22fe64b0adfb6ef1c0e02d187e7 Mon Sep 17 00:00:00 2001 From: Kevin Caseiras <kcaseiras@gmail.com> Date: Fri, 8 May 2020 10:31:12 -0400 Subject: [PATCH 28/29] Improvements to the bulk email functionality - Reorder the text/html parts of the message so that the html part is last. Multipart MIME messages should be in order of increased preference. Putting the html part last will properly tell email clients to prefer the html format over the plain text format. - Removed html tags for the plain text part. Previously the plain text part was the same as the html making it pretty unreadable. --- .../apiuser/ApiUserBatchEmailServiceImpl.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/java/gov/nysenate/openleg/service/mail/apiuser/ApiUserBatchEmailServiceImpl.java b/src/main/java/gov/nysenate/openleg/service/mail/apiuser/ApiUserBatchEmailServiceImpl.java index a15a42f33..8f53d12d9 100644 --- a/src/main/java/gov/nysenate/openleg/service/mail/apiuser/ApiUserBatchEmailServiceImpl.java +++ b/src/main/java/gov/nysenate/openleg/service/mail/apiuser/ApiUserBatchEmailServiceImpl.java @@ -9,6 +9,9 @@ import gov.nysenate.openleg.service.mail.MailException; import gov.nysenate.openleg.service.mail.MimeSendMailService; import gov.nysenate.openleg.util.MailUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.safety.Whitelist; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -51,23 +54,20 @@ public void sendTestMessage(String email, ApiUserMessage message) throws MailExc //rethrow a runtime exception in catch public int sendMessage(ApiUserMessage message) throws MailException { //get all the users who are subscribed to one or more of the subscriptions - Set<ApiUserSubscriptionType> subs = message.getSubscriptionTypes(); - List<ApiUser> usersList = new ArrayList<>(); - for (ApiUserSubscriptionType sub : subs) { - usersList.addAll(apiUserDao.getUsersWithSubscription(sub)); + Set<ApiUserSubscriptionType> subscriptions = message.getSubscriptionTypes(); + Set<ApiUser> users = new HashSet<>(); + for (ApiUserSubscriptionType sub : subscriptions) { + users.addAll(apiUserDao.getUsersWithSubscription(sub)); } - //use a set for the users to avoid sending duplicate emails - Set<ApiUser> users = new HashSet<>(usersList); //create a set with all the admins - List<AdminUser> adminsList = adminUserDao.getAdminUsers(); - Set<AdminUser> admins = new HashSet<>(adminsList); + Set<AdminUser> admins = new HashSet<>(adminUserDao.getAdminUsers()); Set<MimeMessage> allEmails = new HashSet<>(); MimeMessage mimeMessage; String email; - //go through set of users an admins + //go through set of users for (ApiUser user : users) { email = user.getEmail(); mimeMessage = getMimeMessage(message, email, user); @@ -139,9 +139,12 @@ protected Multipart getMultiPart(ApiUserMessage message, @Nullable ApiUser user) } try { htmlPart.setContent(content, "text/html"); - textPart.setText(text); - multipart.addBodyPart(htmlPart); + // Remove html tags from body for the plain text part. + String plainText = stripHtmlTags(text); + textPart.setText(plainText); + // Add parts in order of increasing preference. multipart.addBodyPart(textPart); + multipart.addBodyPart(htmlPart); } catch (MessagingException ex) { throw new MailException(ex.toString()); } @@ -152,14 +155,15 @@ protected Multipart getMultiPart(ApiUserMessage message, @Nullable ApiUser user) * This method takes in an ApiUserMessage, an email address (String) and an optional ApiUser. It * creates a MimeMessage, with the recipient being the email address passed in, and the subject and * content being based on the message passed in. The content is set by making a call to getMultiPart() + * + * If ApiUser is not null, there will be an unsubscribe link generated in their message. + * * @param message * @param email * @param user * @return MimeMessage * @throws MailException */ - //method that takes in a message and an email (string) and returns a mimeMessage object (without the content set) - //sets the subject and recipient protected MimeMessage getMimeMessage(ApiUserMessage message, String email, @Nullable ApiUser user) throws MailException { try { MimeMessage mimeMessage = mimeSender.createMessage(); @@ -178,4 +182,13 @@ protected MimeMessage getMimeMessage(ApiUserMessage message, String email, @Null throw new MailException(ex.toString()); } } + + /** + * Removes the html tags from the given string while preserving new lines. + * @param text + * @return + */ + private String stripHtmlTags(String text) { + return Jsoup.clean(text, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false)); + } } From 1a6adba08fde8a3d2f23199a60c7f22c1866236f Mon Sep 17 00:00:00 2001 From: Kevin Caseiras <kcaseiras@gmail.com> Date: Mon, 11 May 2020 11:25:25 -0400 Subject: [PATCH 29/29] Convert TranscriptId field name back to dateTime Naming this field dateTime is more consistant with our other date time fields and search queries used for elastic search. --- docs/api/transcripts_floor.rst | 3 +-- .../view/transcript/TranscriptIdView.java | 8 +++---- .../dao/transcript/SqlTranscriptDao.java | 2 +- .../search/ElasticTranscriptSearchDao.java | 4 ++-- .../model/transcript/TranscriptId.java | 21 ++++++++----------- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/api/transcripts_floor.rst b/docs/api/transcripts_floor.rst index 1a177fcbe..118d5612a 100644 --- a/docs/api/transcripts_floor.rst +++ b/docs/api/transcripts_floor.rst @@ -29,9 +29,8 @@ Full Transcript Response "message" : "Data for transcript 2014-09-03T09:00:00", // Response description. "responseType" : "transcript", // Response data type. "result" : { - "sessionDateTime" : "2014-09-03T09:00", // Filename of transcript. + "dateTime" : "2014-09-03T09:00", // The date and time of the session. "sessionType" : "REGULAR SESSION", // Session type - "dateTime" : "2014-09-03T09:00:00 // Date Time of senate session. "location" : "ALBANY, NEW YORK", // Location of senate session. "text" : "5100\n\n 1 NEW YORK STATE SE.." // The text of the transcript. } diff --git a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java index 5c19055e9..7af0e0276 100644 --- a/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java +++ b/src/main/java/gov/nysenate/openleg/client/view/transcript/TranscriptIdView.java @@ -5,14 +5,14 @@ public class TranscriptIdView implements ViewObject { - protected String sessionDateTime; + protected String dateTime; public TranscriptIdView(TranscriptId transcriptId) { - this.sessionDateTime = transcriptId.getSessionDateTime().toString(); + this.dateTime = transcriptId.getDateTime().toString(); } - public String getSessionDateTime() { - return sessionDateTime; + public String getDateTime() { + return dateTime; } @Override diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java index 5e099b3d8..897826f48 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/SqlTranscriptDao.java @@ -70,7 +70,7 @@ private MapSqlParameterSource getTranscriptParams(Transcript transcript, Transcr private MapSqlParameterSource getTranscriptIdParams(TranscriptId transcriptId) { MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("dateTime", DateUtils.toDate(transcriptId.getSessionDateTime())); + params.addValue("dateTime", DateUtils.toDate(transcriptId.getDateTime())); return params; } diff --git a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java index b3b023cfd..bb5dc6535 100644 --- a/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java +++ b/src/main/java/gov/nysenate/openleg/dao/transcript/search/ElasticTranscriptSearchDao.java @@ -54,7 +54,7 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { BulkRequest bulkRequest = new BulkRequest(); transcripts.stream() .map(TranscriptView::new) - .map(t -> getJsonIndexRequest(transcriptIndexName, t.getSessionDateTime(), t)) + .map(t -> getJsonIndexRequest(transcriptIndexName, t.getDateTime(), t)) .forEach(bulkRequest::add); safeBulkRequestExecute(bulkRequest); } @@ -63,7 +63,7 @@ public void updateTranscriptIndex(Collection<Transcript> transcripts) { @Override public void deleteTranscriptFromIndex(TranscriptId transcriptId) { if (transcriptId != null) { - deleteEntry(transcriptIndexName, transcriptId.getSessionDateTime().toString()); + deleteEntry(transcriptIndexName, transcriptId.getDateTime().toString()); } } diff --git a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java index 28b1bc1c8..c190f26b6 100644 --- a/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java +++ b/src/main/java/gov/nysenate/openleg/model/transcript/TranscriptId.java @@ -15,12 +15,12 @@ public class TranscriptId implements Serializable, Comparable<TranscriptId> private static final long serialVersionUID = -6509878885942142022L; /** The timestamp which corresponds to the transcript. */ - private LocalDateTime sessionDateTime; + private LocalDateTime dateTime; /** --- Constructors --- */ - public TranscriptId(LocalDateTime sessionDateTime) { - this.sessionDateTime = sessionDateTime; + public TranscriptId(LocalDateTime dateTime) { + this.dateTime = dateTime; } /** @@ -39,33 +39,30 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TranscriptId that = (TranscriptId) o; - return Objects.equals(sessionDateTime, that.sessionDateTime); + return Objects.equals(dateTime, that.dateTime); } @Override public int hashCode() { - return sessionDateTime != null ? sessionDateTime.hashCode() : 0; + return dateTime != null ? dateTime.hashCode() : 0; } @Override public int compareTo(TranscriptId o) { return ComparisonChain.start() - .compare(this.sessionDateTime, o.sessionDateTime) + .compare(this.dateTime, o.dateTime) .result(); } @Override public String toString() { - return "Transcript " + sessionDateTime; + return "Transcript " + dateTime; } /** --- Basic Getters/Setters --- */ - public LocalDateTime getSessionDateTime() { - return sessionDateTime; - } - public LocalDateTime getDateTime() { - return sessionDateTime; + return dateTime; } + }