Sync with prod...which I really need to stop using as my dev env!
authorBrian Flowers <git-admn@bsflowers.net>
Fri, 7 Feb 2025 01:04:17 +0000 (20:04 -0500)
committerBrian Flowers <git-admn@bsflowers.net>
Fri, 7 Feb 2025 01:04:17 +0000 (20:04 -0500)
api.php
canvass.php
common.php
css/pane2.css
js/canvassing.js
js/common.js
js/voters.js
login.php
settings-api.php
settings.php
taskRunner.php

diff --git a/api.php b/api.php
index 51298bd..4a6d423 100644 (file)
--- a/api.php
+++ b/api.php
@@ -1,3 +1,5 @@
+<?php session_start(); ?>
+<?php include 'common.php'; ?>
 <?php
 /*
 * This file is part of the Cargobike Community Canvassing Program (CCCP).
@@ -15,7 +17,6 @@
 * If not, see <https://www.gnu.org/licenses/>.
 */
 
-session_start();
 @ini_set('memory_limit', '1024M');
 set_time_limit(300);
 
@@ -591,6 +592,57 @@ if( isset($_GET['get']) &&
 
   echo '{"status": "OK", "id": '.$id.'}';
 
+// URL: ?get=canvassMonitor
+// get the current status of a canvass
+// POST: id (required)
+} else if( isset($_GET['get']) && $_GET['get'] == "canvassMonitor") {
+  if( $_SESSION['permissions'] > CCCP_PERM_LEAD ) {
+    return;
+  }
+
+  // Fetch the turf level stats
+  $query = "SELECT max(c.totalContacts) total, ".
+           "sum((SELECT count(distinct voterId) FROM canvassResults r WHERE r.canvassId = c.id)) madeContacts, ".
+           "(SELECT count(distinct userId) FROM canvassResults r WHERE r.canvassId = c.id) volunteers ".
+           "FROM canvasses c WHERE turfId = CAST(? AS INTEGER)";
+/*  $query = "SELECT c.totalContacts total, ".
+           "count(distinct cr.voterId) madeContacts, ".
+           "count(distinct u.id) activeVolunteers ".
+           "FROM canvasses c, users u, canvassResults cr ".
+           "WHERE c.turfId = CAST(? AS INTEGER) ".
+           "AND u.id = cr.userId ".
+           "AND cr.canvassId = c.id ".
+           "AND cr.timestamp > TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 day));";*/
+
+//           "GROUP BY u.id";
+  if( isset($_GET['id']) ) {
+    $params = [$_GET['id']];
+  } else {
+    $params = ["10"];
+  }
+
+  $stmt = $dbh->prepare($query);
+  $stmt->execute($params);
+  $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+  // Add the volunteer level stats
+  $query = "SELECT cr.userId, max(cr.timestamp), count(*) contacts, ".
+           "(SELECT count(*) FROM canvassResults WHERE cr.priority > 0 AND userId = cr.userId) priority, ".
+           "(SELECT count(*) FROM canvassResults WHERE cr.noContact > 0 AND userId = cr.userId) noContact ".
+           "FROM canvassResults cr, canvasses c ".
+           "WHERE c.turfId = CAST(? AS INTEGER) AND c.id = cr.canvassId ".
+           "GROUP BY userId;";
+//echo $query;
+  $stmt = $dbh->prepare($query);
+  $stmt->execute($params);
+  $rows2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+// contactStatus, estSupportPct, priority, noContact
+  print_r(json_encode($rows2));
+
+  // TODO: Return total voters, reached voters, active volunteers (contacts reached, last contact time and coords, response categories)
+
+
 // URL: ?get=canvasses
 // fetch a list of available canvasses
 // POST: id (optional)
@@ -599,8 +651,15 @@ if( isset($_GET['get']) &&
            "FROM turf t";
   $params = [];
   if( isset($_GET['id']) ) {
-    $query .= " WHERE t.id=?";
+    $query .= " WHERE t.id=? AND ";
     $params = [$_GET['id']];
+  } else {
+    $query .= " WHERE ";
+  }
+  if( $_SESSION['permissions'] <= CCCP_PERM_ADMIN ) {
+    $query .= " 1=1;";
+  } else {
+    $query .= " t.id IN (SELECT turfId from canvasses where hidden = 0);";
   }
 
   $stmt = $dbh->prepare($query);
@@ -611,7 +670,10 @@ if( isset($_GET['get']) &&
     // Fetch the turf level stats
     $query = "SELECT max(c.totalContacts) total, ".
              "sum((SELECT count(distinct voterId) FROM canvassResults r WHERE r.canvassId = c.id)) madeContacts ".
-             "FROM canvasses c WHERE turfId = CAST(? AS INTEGER);";
+             "FROM canvasses c WHERE turfId = CAST(? AS INTEGER)";
+    if( $_SESSION['permissions'] > CCCP_PERM_ADMIN ) {
+      $query .= " AND c.hidden = 0;";
+    }
     $stmt  = $dbh->prepare($query);
     $stmt->execute(Array($rows[$i]["id"]));
     $irows = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -623,11 +685,22 @@ if( isset($_GET['get']) &&
     $query = "SELECT c.id, c.turfId, c.name, c.start, c.end, c.totalContacts, c.lastLoc, c.voterString, c.script, ".
              "(SELECT count(distinct voterId) FROM canvassResults r WHERE r.canvassId = c.id) as madeContacts, ".
              "(SELECT max(timestamp) FROM canvassResults r WHERE r.canvassId = c.id) as lastActive ".
-             "FROM canvasses c WHERE turfId = CAST(? AS INTEGER);";
+             "FROM canvasses c WHERE turfId = CAST(? AS INTEGER)";
+    if( $_SESSION['permissions'] > CCCP_PERM_ADMIN ) {
+      $query .= " AND c.hidden = 0;";
+    }
     $stmt = $dbh->prepare($query);
     $stmt->execute(Array($rows[$i]["id"]));
     $irows = $stmt->fetchAll(PDO::FETCH_ASSOC);
     $rows[$i]["canvasses"] = $irows;
+
+    for( $j = 0; $j < sizeof($irows); $j++) {
+      if( $_SESSION['permissions'] < CCCP_PERM_LEAD ) {
+        $rows[$i]["canvasses"][$j]["options"] = "monitor";
+      } else {
+        $rows[$i]["canvasses"][$j]["options"] = "";
+      }
+    }
   }
 
 
index a023b0c..b1d89bb 100644 (file)
@@ -151,6 +151,7 @@ EOF;
       </div>
 
       <a onclick="toggleControls();" class="category">Hide Menu</a>
+<!--      <div id="cacheMgr"></div> -->
 
       <?php licenseCategory(); ?>
       </div>
index ec8e32d..38609d0 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 define("CCCP_PERM_ADMIN", 0);
+define("CCCP_PERM_LEAD", 7);
 define("CCCP_PERM_VOLUNTEER", 10);
 
 if( basename($_SERVER['PHP_SELF']) != 'login.php') {
index 7b7c1db..20b47a3 100644 (file)
   font-size: 1.5em;
 }
 
+#cacheBug {
+  position: absolute;
+  top:  1em;
+  left: 1em;
+  width: 1em;
+  height: 1em;
+  border-radius: 0.5em;
+  opacity: 0.8;
+  background-color: red;
+  transition: width 1s;
+  z-index: 100;
+  color: white;
+  font-weight: bold;
+}
+
+#cacheBug.expanded {
+  width: auto;
+  transition: width 1s;
+}
+
+#cacheMgr {
+  display: none;
+  position: fixed;
+  width:   96%;
+  height:  96%;
+}
+
 #canvassing-list .subcategory input {
   width: 100%;
   height: 100%;
index 2b1cd35..113beaf 100644 (file)
@@ -201,6 +201,14 @@ function loadTurfs(research = false) {
 
         cell.innerHTML += "Started: " + start + "<br/>";
         cell.innerHTML += "Last Activity: " + lastActive;
+        if(turfs[i].canvasses[j].options.indexOf("monitor") != -1) {
+          cell.innerHTML += "<br/>";
+          var button = document.createElement("input");
+          button.setAttribute("type", "button");
+          button.value = "Monitor";
+          button.onclick = monitorCanvass.bind(null, turfs[i].canvasses[j].id);
+          cell.appendChild(button);
+        }
         row.appendChild(cell);
         cell = document.createElement("td");
         if(research == true) {
@@ -554,6 +562,14 @@ function enableCanvassControls() {
   var toolbar = document.createElement("div");
   toolbar.setAttribute("id", "canvassToolbar");
 
+/*  var cacheBug = document.createElement("div");
+  cacheBug.setAttribute("id", "cacheBug");
+  cacheBug.innerHTML = "0 Unsaved Results";
+  cacheBug.addClass("expanded");
+  cacheBug.onclick = showCacheMgr();
+  setTimeout(reduceCacheBug, 1000);
+  mapElem.appendChild(cacheBug);*/
+
   var gpsBtn = document.createElement("button");
   gpsBtn.onclick = function() {
 //    navigator.geolocation.getCurrentPosition(updateLocation, locationFailure);
@@ -963,4 +979,15 @@ console.log("AT PAGINATOR");
   debug("End Sync showCanvassStats");
 }
 
+function showCacheMgr() {
+  var cacheMgr = document.getElementById("cacheMgr");
+  cacheMgr.style.display = "block";
+}
+
+function reduceCacheBug() {
+  document.getElementById("cacheBug").removeClass("expanded");
+}
 
+function monitorCanvass(canvass) {
+  console.log(canvass);
+}
index 2aecf62..9958099 100644 (file)
@@ -3,6 +3,10 @@ TODO: Comment code more, especially:
         - canvass.js
         - api.php
 
+      Configurable max upload size
+
+      Cached replay of data entry while canvassing (management screen needed, convert calls to cached call, check capacity)
+
       Absolute Demographics
         - Number of voters, of each party, of age/sex, of vote history
         - Population
@@ -64,7 +68,7 @@ TODO: Comment code more, especially:
 * If not, see <https://www.gnu.org/licenses/>.
 */
 
-var _CCCP_DEBUG = true;
+var _CCCP_DEBUG = false;
 function debug(msg) {
   if(_CCCP_DEBUG) {
 //    debugger;
@@ -72,6 +76,31 @@ function debug(msg) {
   }
 }
 
+// Caching Database
+const idbCacheReq = window.indexedDB.open("cccpCache",6);
+let db;
+var transaction;
+var cacheStore;
+
+idbCacheReq.onerror = (event) => {
+        console.log("Error connecting to cache DB");
+        console.log(event);
+};
+idbCacheReq.onupgradeneeded = (event) => {
+  console.log("UPGRADING");
+
+  const db = event.target.result;
+  cacheStore = db.createObjectStore("cache", {"autoIncrement": true});
+}
+idbCacheReq.onsuccess = (event) => {
+  console.log("Connected to cache DB");
+  console.log(event);
+  db = event.target.result;
+
+  transaction= db.transaction(["cache"]);
+  cacheStore = transaction.objectStore("cache");
+}
+
 // Stores data associated to precincts, blocks, or block groups for display
 var displayAreas = [];
 
@@ -242,3 +271,53 @@ function toggleControls() {
   window.devicePixelRatio = 1;
 }
 
+// Store and allow replay of network transactions
+function cachedSend(url, options, callback) {
+  fetch(url, options)
+    .then(data => { callback(data) })
+    .catch(error => {
+/*      var list = cacheStore.get("cachedFetch"); //JSON.parse(localStorage.getItem("cachedFetch"));
+      if( list == null ) {
+        list = Array();
+      }
+      list.push({"url": url, "options": options});
+      cacheStore.add("cachedFetch", list);
+      //localStorage.setItem("cachedFetch", JSON.stringify(list));*/
+      cachedSendAdd(url, options);
+      throw error;
+  });
+}
+
+function cachedSendAdd(url, options) {
+  transaction= db.transaction(["cache"], "readwrite");
+  cacheStore = transaction.objectStore("cache");
+  const cacheReq = cacheStore.get("cachedSend");
+  cacheReq.onsuccess = (event) => {
+    var list = cacheReq.result;
+    if( list == null ) {
+      list = Array();
+    }
+    if(typeof list == "string") {
+      list = JSON.parse(list);
+    }
+    list.push({"url": url, "options": options});
+    cacheStore.put(list, "cachedSend");
+  }
+}
+
+// Retry failed network transactions
+function cachedReplay(itemNum) {
+  transaction= db.transaction(["cache"], "readwrite");
+  cacheStore = transaction.objectStore("cache");
+  const cacheReq = cacheStore.get("cachedSend");
+  cacheReq.onsuccess = (event) => {
+    var list = cacheReq.result;
+    if(itemNum == null || itemNum > list.length) {
+      itemNum = 0;
+    }
+    var item = list[itemNum];
+    list.splice(itemNum, 1);
+    cacheStore.put(list, "cachedSend");
+    cochedSend(item.url, item.options);
+  }
+}
index cf503f2..cb36693 100644 (file)
@@ -643,7 +643,8 @@ function saveVoterDetails() {
           "&corrections="+corrections+"&priority="+priority+"&dnc="+dnc+"&canvassId="+canvassId+
           newEntryBody
   };
-  fetch("api.php?get=set&set=canvassResult", options).then(data => data.json())
+//  cachedSend("api.php?get=set&set=canvassResult", options, function(data) { data.json()
+    fetch("api.php?get=set&set=canvassResult", options).then(data => data.json()
     .then(resp => {
       if(resp[0] == "00000") {
         setVoterSaveStatus("saved");
@@ -656,7 +657,7 @@ function saveVoterDetails() {
       setVoterSaveStatus("error");
       document.getElementById("voterSaveStatus").classList.add("error");
       console.log(error);
-    });
+    }));
 
   //document.getElementById("voterSave").classList.remove("unsaved");
   setLoading(-1);
index 56be889..f0e54fa 100644 (file)
--- a/login.php
+++ b/login.php
@@ -84,7 +84,7 @@ if( isset($_POST) && sizeof($_POST) > 0 ) {
         <input type="password" id="password" name="password" />
         <input type="submit" value="Login" />
       </form>
-      <?php echo $auth_error ?>
+      <?php echo isset($auth_error) ? $auth_error : "" ?>
     </div>
   </BODY>
 </HTML>
index d6bbbc7..658ad44 100644 (file)
@@ -17,7 +17,7 @@
 * If not, see <https://www.gnu.org/licenses/>.
 */
 ?>
-
+<?php //phpinfo(); die; ?>
 
 <?php
 if( $_SESSION['permissions'] > CCCP_PERM_ADMIN ) {
@@ -25,7 +25,7 @@ if( $_SESSION['permissions'] > CCCP_PERM_ADMIN ) {
   die();
 }
 
-@ini_set('memory_limit', '1024M');
+@ini_set('memory_limit', '2048M');
 set_time_limit(600);
 //ini_set('display_errors',1);
 
@@ -79,12 +79,15 @@ if(isset($_GET['get']) && $_GET['get'] == "taskStatus") {
 
   // Place the file in the tasks directory
   $targetName = floor(microtime(true)*1000).".csv";
-  if($_FILES['voterUpload']['size'] > 100000000) {
-    echo "ERROR: File too large (max size: 100MB)";
+  if($_FILES['voterUpload']['size'] > 300000000) {
+    echo "ERROR: File too large (max size: 300MB)";
     die();
   }
   session_write_close();
-  move_uploaded_file($_FILES['voterUpload']['tmp_name'], "/var/www/html/CCCP/tasks/".$targetName);
+  print_r($_FILES);
+  echo "MOVING FILE|";
+  echo move_uploaded_file($_FILES['voterUpload']['tmp_name'], "/var/www/html/CCCP/tasks/".$targetName);
+  echo "|FILE MOVED";
 
   // Insert the task in the tasks table
   $linecount = 0;
index 96280ec..e8b41e6 100644 (file)
@@ -220,10 +220,10 @@ if($STATUS['geocoder.maxDaily'] == 0) {
                     <tr><td>19</td><td>Address Line 1</td>                 <td>151 N Desplaines St</td></tr>
                     <tr><td>20</td><td>Address Line 2</td>                 <td></td></tr>
                     <tr><td>21</td><td>City</td>                           <td>Chicago</td></tr>
-                    <tr><td>21</td><td>State</td>                          <td>Illinois</td></tr>
-                    <tr><td>21</td><td>Zip</td>                            <td>60661</td></tr>
-                    <tr><td>21</td><td>Registration Date</td>              <td>1848-02-21</td></tr>
-                    <tr><td>21</td><td>Voting History</td>                 <td>1904;1908-General;1912-Primary;1920</td></tr>
+                    <tr><td>22</td><td>State</td>                          <td>Illinois</td></tr>
+                    <tr><td>23</td><td>Zip</td>                            <td>60661</td></tr>
+                    <tr><td>24</td><td>Registration Date</td>              <td>1848-02-21</td></tr>
+                    <tr><td>25</td><td>Voting History</td>                 <td>1904;1908-General;1912-Primary;1920</td></tr>
                   </table>
                   <br/>
                   The first line of this file should be a header containing the field names.
index 90b07ee..b87e105 100644 (file)
@@ -37,6 +37,69 @@ if(sizeof($rows) != 0 && $rows[0]['configValue'] != "") {
   $stmt->execute();
 }
 
+// Property mapping for voter import headers
+/*function normalizeRecord($row, $headers) {
+  $retVal = [];
+  for($i = 0; $i < sizeof($headers); $i++) {
+    if( in_array($headers[$i], ["LAST NAME"]) ) {
+      $retVal["lastName"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["FIRST NAME"]) ) {
+      $retVal["firstName"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["STREET NUMBER"]) ) {
+      $retVal["addressLine1"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["STREET NAME"]) ) {
+      $retVal["adderssLine1"] += $row[$i];
+    }
+    if( in_array($headers[$i], ["STREET NAME 2"]) ) {
+      $retVal["addressLine2"] = trim($row[$i] + $retVal["addressLine2"]);
+    }
+    if( in_array($headers[$i], ["ZIP CODE"]) ) {
+      $retVal["zip"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["CITY"]) ) {
+      $retVal["city"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["UNIT"]) ) {
+      $retVal["addressLine2"] = trim($retVal["addressLine2"] + $row[$i]);
+    }
+    if( in_array($headers[$i], ["STATE"]) ) {
+      $retVal["state"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["PARTY CODE"]) ) {
+      $retVal["party"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["GENDER"]) ) {
+      $retVal["gender"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["YEAR OF BIRTH"]) ) {
+      $retVal["yearOfBirth"] = $row[$i];
+      if( !isset($retVal["birthday"]) ) {
+        $retVal["birthday"] = $row[$i];
+      }
+    }
+    if( in_array($headers[$i], ["PHONE NUMBER"]) ) {
+      $retVal["phone"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["EMAIL"]) ) {
+      $retVal["email"] = $row[$i];
+    }
+    if( in_array($headers[$i], ["EFFECTIVE DATE"]) ) {
+      $retVal["lastUpdated"] = $row[$i];
+    }
+    if( in_array($headers[$i], [""]) ) {
+      $retVal[""] = $row[$i];
+    }
+    if( in_array($headers[$i], [""]) ) {
+      $retVal[""] = $row[$i];
+    }
+
+  }
+  return $retVal;
+}*/
+
 // Select all tasks that are not yet completed
 $query = "SELECT * FROM tasks WHERE taskComplete IS NULL;";
 $stmt  = $dbh->prepare($query);
@@ -59,21 +122,29 @@ for($i = 0; $i < sizeof($tasks); $i++) {
     $header = Array();
     $rowcount = 0;
     $pct      = 0;
-    if(($fh = fopen("tasks/".$filename, 'r')) !== FALSE ) {
+    if(($fh = fopen("/var/www/html/CCCP/tasks/".$filename, 'r')) !== FALSE ) {
       while(($data = fgetcsv($fh, 1000, ",")) !== FALSE) {
         $rowcount++;
         if(sizeOf($header) == 0) {
           $header = $data;
         } else {
+          // Sanitize the data a bit
+          for($j = 0; $j < sizeof($data); $j++) {
+            $data[$j] = trim($data[$j]);
+          }
           // Insert the voter into the database, starting with the address
           // First, check if the address already exists
           // (This check is pretty basic; geocoder will normalize and verify these a bit more)
-          $query  = "SELECT * FROM voterAddresses WHERE addressLine1=? AND addressLine2=? AND city=? AND zip=?;";
+          $query  = "SELECT * FROM voterAddresses WHERE addressLine1=? AND (addressLine2=? OR addressLine2 IS NULL) AND city=? AND zip=?;";
           $params = Array(strtoupper($data[18]), strtoupper($data[19]), strtoupper($data[20]), strtoupper($data[22]));
           $stmt   = $dbh->prepare($query);
           $stmt->execute($params);
           $addresses = $stmt->fetchAll();
           $aids = Array();
+          if(sizeof($addresses) > 50) {
+            echo "ERROR: TOO MANY (>50) matching addresses (".$data[18].",".$data[19].",".$data[20].",".$data[22].")".PHP_EOL;
+            continue;
+          }
           if(sizeof($addresses) != 0) {
             for($j = 0; $j < sizeof($addresses); $j++) {
               array_push($aids, $addresses[$j]['id']);
@@ -87,11 +158,38 @@ for($i = 0; $i < sizeof($tasks); $i++) {
                             strtoupper($data[6]), strtoupper($data[7]), strtoupper($data[8]), strtoupper($data[9]),
                             strtoupper($data[11]), strtoupper($data[12]), strtoupper($data[13]), strtoupper($data[14]));
             $stmt   = $dbh->prepare($query);
+print_r($params);
             $stmt->execute($params);
             $aids = Array($dbh->lastInsertId());
           }
+          // If the voter file has an address line 2 but the address doesn't, update it
+          // Check each address, see if any has a matching line2; if not, add one
+          $aids2 = Array();
+          for($j = 0; $j < sizeof($addresses); $j++) {
+/*            if(trim($data[20]) != "" && trim($addresses[$j]['addressLine2']) == "") {
+              $query = "INSERT INTO voterAddresses(addressLine1, addressLine2, city, state, zip,".
+                       "precinct, natConDistrict, stSenDistrict, stHouDistrict,".
+                       "wardCouncil, wardDistrict, schoolDistrict, specialDistrict, ".
+                       "latitude, longitude, parentGeocode) ".
+                       "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
+              $params = Array($addresses[0]['addressLine1'], strtoupper($data[19]), $addresses[0]['city'], $addresses[0]['state'], $addresses[0]['zip'],
+                            $addresses[0]['precinct'], $addresses[0]['natConDistrict'], $addresses[0]['stSenDistrict'], $addresses[0]['stHouDistrict'],
+                            $addresses[0]['wardCouncil'], $addresses[0]['wardDistrict'], $addresses[0]['schoolDistrict'], $addresses[0]['specialDistrict'],
+                            $addresses[0]['latitude'], $addresses[0]['longitude'], $addresses[0]['parentGeocode']);
+              $stmt  = $dbh->prepare($query);
+              $stmt->execute($params);
+              $aids = Array($dbh->lastInsertId());
+            }*/
+            if($addresses[$j]['addressLine2'] == $data[20]) {
+              array_push($aids2, $addresses[$j]['id']);
+            }
+          }
+          if(sizeof($aids2) == 1) {
+            $aids = $aids2[0];
+          }
           if($aids == Array()) {
             echo "ERROR: Could not find or insert address for line number: ".$rowcount." (".$data[18].", ".$data[20].")";
+            print_r($aids);
             continue;
           }
 
@@ -100,7 +198,7 @@ for($i = 0; $i < sizeof($tasks); $i++) {
             $query = "SELECT * FROM voters WHERE stateVoterId = ?;";
             $params = Array($data[17]);
           } else {
-            $query = "SELECT * FROM voters WHERE firstName = ? AND lastName = ? AND birthYear = ? AND ".
+            $query = "SELECT * FROM voters WHERE UPPER(firstName) = ? AND UPPER(lastName) = ? AND birthYear = ? AND ".
                      "addressId IN(".str_repeat("?,",sizeof($aids)-1)."?);";
             $params = Array(strtoupper($data[0]), strtoupper($data[2]), strtoupper($data[3]));
             for($j = 0; $j < sizeof($aids); $j++) {
@@ -120,10 +218,10 @@ for($i = 0; $i < sizeof($tasks); $i++) {
                        "WHERE id=?";
               $params = Array(strtoupper($data[0]), strtoupper($data[1]), strtoupper($data[2]), strtoupper($data[3]),
                               strtoupper($data[4]), strtoupper($data[5]), strtoupper($data[15]), strtoupper($data[16]),
-                              strtoupper($data[17]), $aids[0], $data[23], $vid);
+                              strtoupper($data[17]), $aids[0], date("Y-m-d H:i:s", strtotime($data[23])), $vid);
               $stmt   = $dbh->prepare($query);
               $stmt->execute($params);
-              $vid = $dbh->lastInsertId();
+//              $vid = $dbh->lastInsertId();
             }
           } else {
             $query = "INSERT INTO voters(firstName, middleName, lastName, birthYear, ".
@@ -132,30 +230,43 @@ for($i = 0; $i < sizeof($tasks); $i++) {
                      "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
             $params = Array(strtoupper($data[0]), strtoupper($data[1]), strtoupper($data[2]), strtoupper($data[3]),
                             strtoupper($data[4]), strtoupper($data[5]), strtoupper($data[15]), strtoupper($data[16]),
-                            strtoupper($data[17]), $aids[0], $data[23]);
+                            strtoupper($data[17]), $aids[0], date("Y-m-d H:i:s", strtotime($data[23])));
             $stmt   = $dbh->prepare($query);
+print_r($params);
             $stmt->execute($params);
             $vid = $dbh->lastInsertId();
           }
-          if($vid == null) {
-            echo "ERROR: Could not find or insert voter for line number: ".$rowcount." (".$data[3].", ".$data[1].")";
+          if($vid == null || $vid == 0) {
+            echo "ERROR: Could not find or insert voter for line number: ".$rowcount." (".$data[3].", ".$data[1].")".PHP_EOL;
+            print_r($dbh->errorInfo());
             continue;
           }
 
           // Log the voting history
           $histories = explode(";", $data[24]);
-          for($h = 0; $h < sizeof($histories); $h++) {
+          for($h = 0; $h < sizeof($histories) && $h < 10; $h++) {
+            if( trim($histories[$h]) == "" || trim($histories[$h]) == "-") {
+              continue;
+            }
             $query    = "INSERT INTO voterHistory(voterId, year, election, voted) VALUES(?, ?, ?, ?);";
             $segments = explode("-", $histories[$h]);
             $year     = 0;
             $election = "";
             if( sizeof($segments) > 0) {
               $year = $segments[0];
+              if( strlen($year) > 4) {
+                $year = date('Y', strtotime($year));
+              }
+              if($year == "" || $year == 0) {
+                echo "ERROR: Invalid year detected for voter ID $vid history ".$histories[$h].PHP_EOL;
+                continue;
+              }
             }
             if( sizeof($segments) > 1) {
               $election = $segments[1];
             }
             $params= Array($vid, $year, $election, 1);
+print_r($params);
             $stmt  = $dbh->prepare($query);
             $stmt->execute($params);
           }
@@ -163,6 +274,7 @@ for($i = 0; $i < sizeof($tasks); $i++) {
           // Update when the percent changes
           //   Saving some time by not updating every single line...this is status info not a restart journal
           if( floor(($rowcount*100) / $tasks[$i]['taskLastStep']) > $pct) {
+            echo "Updating status";
             $pct = floor(($rowcount*100) / $tasks[$i]['taskLastStep']);
             $query = "UPDATE tasks SET taskStep=?, taskPercent=?, taskUpdate=CURRENT_TIMESTAMP WHERE id=?;";
             $params = Array((int)$rowcount, $pct, $tasks[$i]['id']);
@@ -170,6 +282,7 @@ for($i = 0; $i < sizeof($tasks); $i++) {
             $stmt  = $dbh->prepare($query);
             $stmt->execute($params);
             //print_r($dbh->errorInfo());
+            echo "Updated";
           }
         }
       }
@@ -321,7 +434,7 @@ for($i = 0; $i < sizeof($tasks); $i++) {
     if( !isset($sourceId) ) {
       echo "Unable to read source ID from metadata file (".$metaFile.")".PHP_EOL;
       $query = "UPDATE tasks SET taskStep=-1, taskPercent=0, taskUpdate=CURRENT_TIMESTAMP, taskComplete = CURRENT_TIMESTAMP WHERE id=?";
-      $params = Array($idx, $tasks[$i]['id']);
+      $params = Array($tasks[$i]['id']);
       $stmt  = $dbh->prepare($query);
       $stmt->execute($params);
     }