annotation.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. // Allows a user to upload billing annotations to the billing_log_annotation table.Add
  3. // Written by Chris Martin
  4. session_start();
  5. // Connect to database
  6. $mysqli = new mysqli ( "localhost", "XXX_DBUSER_XXX", "XXX_DBPASSWORD_XXX", "XXX_DB_XXX" );
  7. if ( $mysqli -> connect_errno ) {
  8. echo "Failed to connect to MySQL: " . $mysqli -> connect_error;
  9. }
  10. // Valid HTML
  11. echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
  12. \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
  13. <html xmlns=\"http://www.w3.org/1999/xhtml\">
  14. <head>
  15. <title>Annotation Uploader</title>
  16. </head>
  17. <body>
  18. <h1>Annotation Uploader</h1>";
  19. // If no annotation file is provided, prompt for .CSV upload
  20. if(!isset($_POST['confirm']) && !isset($_FILES['annotation_file'])) {
  21. echo "Upload a .CSV file of billing annotations.<br />
  22. <ul>
  23. <li>The first row in your CSV file must be a header consisting of field/column names that match fields in the billing_log table.</li>
  24. <li>The seq_num field is required, all others are optional.</li>
  25. <li>Fields can be in any order.</li>
  26. <li>Only fields provided will be entered into the database.</li>
  27. <li>There is also an optional 'note' field, indicating a reason for the annotation.</li>
  28. <li>If using Excel to prepare your .CSV file, ensure that Excel does not auto-format your date or credential fields in the .CSV file. You can enforce this by enclosing your values in double quotes preceded with an equal sign, like =\"this\" (demonstrated in the example .CSV file).</li>
  29. </ul>
  30. <a href=\"example.csv\">Download a sample .CSV file</a> with all fields populated.
  31. <form enctype=\"multipart/form-data\" action=\"annotation.php\" method=\"POST\">
  32. <input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"4000000\" />
  33. Upload annotations file (.csv): <input name=\"annotation_file\" type=\"file\" accept=\"text/csv,application/csv\" /> <input type=\"submit\" value=\"Upload File\" />
  34. </form>
  35. <a href=\"index.php?goto=mainmenu\">Return to menu</a>";
  36. exit;
  37. }
  38. // If .CSV file is uploaded, store as two-dimensional array and perform
  39. // input validation. Present table of data to user for confirmation.
  40. if(isset($_FILES['annotation_file'])) {
  41. // Convert .CSV file to two-dimensional array
  42. $tmp_name = $_FILES['annotation_file']['tmp_name'];
  43. $csv_array = array_map('str_getcsv', file($tmp_name));
  44. // If values are encapulated like ="this", remove encapsulation.
  45. // This is a workaround for Excel mangling date fields.
  46. for($row_count = 0; $row_count < count($csv_array); $row_count++) {
  47. for($col_count = 0; $col_count < count($csv_array[$row_count]); $col_count++) {
  48. // Match on values encapsulated like ="this"
  49. $pattern = "/\A=\"(.*)\"\z/";
  50. if(preg_match($pattern,$csv_array[$row_count][$col_count],$match) == 1) {
  51. // Remove encapulation if match is found
  52. $csv_array[$row_count][$col_count] = $match[1];
  53. }
  54. }
  55. }
  56. // If seq_num is not provided, exit with error message
  57. if(!in_array("seq_num",$csv_array[0])) {
  58. echo "seq_num is a required field and is missing from your input .csv file. Please try again.";
  59. exit;
  60. }
  61. // Query database for valid fields in billing_log
  62. $field_result = $mysqli -> query("SHOW FIELDS FROM billing_log");
  63. while ($field = $field_result -> fetch_assoc()) {
  64. $valid_fields[] = $field['Field'];
  65. }
  66. $valid_fields[] = "note";
  67. // Check if each field entered is a valid field in billing_log.
  68. // If an invalid field is found, exit with error message.
  69. foreach($csv_array[0] as $input_field) {
  70. if(!in_array($input_field,$valid_fields)) {
  71. echo "Your field \"$input_field\" is not valid. Please try again. Valid fields in billing_log:<br />";
  72. foreach($valid_fields as $valid_field) {
  73. echo "$valid_field<br />";
  74. }
  75. exit;
  76. }
  77. }
  78. // Don't allow any column to appear more than once
  79. if(count(array_unique($csv_array[0]))<count($csv_array[0])) {
  80. echo "You have provided more than one column with the same name. Please try again.";
  81. exit;
  82. }
  83. // Regexes of invalid input for each field
  84. $valid_chars = array(
  85. 'seq_num' => "/\A\d*\z/",
  86. 'equip_num' => "/\A\d*\z/",
  87. 'driver' => "/\A\d*\z/",
  88. 'paddle' => "/\A\d*\z/",
  89. 'route' => "/\A\d*\z/",
  90. 'trip' => "/\A\d*\z/",
  91. 'stop' => "/\A\d*\z/",
  92. 'ride_time' => "/\A\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{2}(:\d{2})?\z/",
  93. 'latitude' => "/\A-?\d+(\.\d+)?\z/",
  94. 'longitude' => "/\A-?\d+(\.\d+)?\z/",
  95. 'action' => "/\A[A-Z]+\z/",
  96. 'rule' => "/.*/",
  97. 'ruleparam' => "/.*/",
  98. 'reason' => "/.*/",
  99. 'credential' => "/\A[0-9:]+\z/",
  100. 'logical_card_id' => "/\A\d*\z/",
  101. 'cash_value' => "/\A\d*\z/",
  102. 'stop_name' => "/.*/",
  103. 'note' => "/.*/",
  104. );
  105. // Check all input against invalid characters array.
  106. foreach($csv_array[0] as $input_field) {
  107. $col_pos = array_search($input_field, $csv_array[0]);
  108. for ($row = 1; $row <= count($csv_array)-1; $row++) {
  109. $value = $csv_array[$row][$col_pos];
  110. if(!empty($value)) {
  111. $valid_match = preg_match($valid_chars[$input_field],$value);
  112. if($valid_match==0) {
  113. $human_row = $row + 1;
  114. echo "Row $human_row of your CSV file contains invalid characters in field \"$input_field\", value \"$value\". Please <a href=\"annotation.php\">try again</a>.";
  115. exit;
  116. }
  117. }
  118. }
  119. }
  120. // Store CSV array as session variable
  121. $_SESSION['csv_array'] = $csv_array;
  122. // Display CSV table to user as HTML table and prompt for confirmation
  123. echo "<p>The following changes will be entered into the database.</p>";
  124. $table = "<table border=\"1\">";
  125. $firstrow = true;
  126. foreach($csv_array as $key => $element)
  127. {
  128. $table .= "<tr>";
  129. foreach ($element as $subkey => $subelement) {
  130. if ($firstrow == true) {
  131. $table .= "<th>$subelement</th>";
  132. } else {
  133. $table .= "<td>$subelement</td>";
  134. }
  135. }
  136. $firstrow = false;
  137. $table .= "</tr>";
  138. }
  139. $table .= "</table>";
  140. echo $table;
  141. echo "Confirm new annotations for insertion to the database.<br />
  142. <form method=\"post\" action=\"annotation.php\">
  143. <input type=\"hidden\" name=\"confirm\" value=\"true\">
  144. <button type=\"submit\">Confirm</button><br />
  145. <a href=\"annotation.php\">Cancel</a><br />
  146. </form>";
  147. }
  148. // If user has confirmed input, insert into database
  149. if(isset($_POST['confirm'])) {
  150. $csv_array = $_SESSION['csv_array'];
  151. $columns = implode(', ', array_shift($csv_array));
  152. $values = array();
  153. // Process each row of array
  154. foreach($csv_array as $row) {
  155. // Process each value in row
  156. foreach($row as $key => $value) {
  157. if(!empty($row[$key])) {
  158. // Escape each input value for SQL query
  159. $row[$key] = "'" . $mysqli->real_escape_string($row[$key]) . "'";
  160. } else {
  161. $row[$key] = 'NULL';
  162. }
  163. }
  164. $values[] = "(" . implode(', ', $row) . ")";
  165. }
  166. $insert = "INSERT INTO billing_log_annotation ($columns) VALUES " . implode(', ', $values);
  167. echo "Executing database insertion.<br />";
  168. // echo "<code>" . $insert . "</code><br />";
  169. if(!$mysqli -> query($insert)) {
  170. echo "Insert operation failed: (" . $mysqli -> errno . ") ". $mysqli -> error;
  171. } else {
  172. echo "Done! Annotation table has been updated.<br />";
  173. }
  174. }
  175. echo '<a href="index.php?goto=mainmenu">Return to Menu</a>';
  176. // Valid HTML
  177. echo "</body>
  178. </html>";
  179. ?>