UNPKG

howsmydriving-nyc

Version:
1,013 lines (1,005 loc) 76.2 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spread = (this && this.__spread) || function () { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; }; var _this = this; Object.defineProperty(exports, "__esModule", { value: true }); require("./util/extend/string-ext"); var crypto = require('crypto'); var http = require('http'); var https = require('https'); var q = require('q'); var rp = require('request-promise'); var url = require('url'); var logging_1 = require("./logging"); var vehicle_lookup_response_1 = require("./models/vehicle_lookup_response"); var fine_data_1 = require("./models/fine_data"); var twitter_1 = require("./constants/twitter"); var L10N_1 = require("./constants/L10N"); var googleMapsClient = require('@google/maps').createClient({ key: process.env.GOOGLE_PLACES_API_KEY, Promise: Promise }); var MAX_USERNAME_LENGTH = 17; // 15 + @ plus space after. // humanized names for violations var opacvHumanizedNames = { '': 'No Description Given', 'ALTERING INTERCITY BUS PERMIT': 'Altered Intercity Bus Permit', 'ANGLE PARKING': 'No Angle Parking', 'ANGLE PARKING-COMM VEHICLE': 'No Angle Parking', 'BEYOND MARKED SPACE': 'No Parking Beyond Marked Space', 'BIKE LANE': 'Blocking Bike Lane', 'BLUE ZONE': 'No Parking - Blue Zone', 'BUS LANE VIOLATION': 'Bus Lane Violation', 'BUS PARKING IN LOWER MANHATTAN': 'Bus Parking in Lower Manhattan', 'COMML PLATES-UNALTERED VEHICLE': 'Commercial Plates on Unaltered Vehicle', CROSSWALK: 'Blocking Crosswalk', 'DETACHED TRAILER': 'Detached Trailer', 'DIVIDED HIGHWAY': 'No Stopping - Divided Highway', 'DOUBLE PARKING': 'Double Parking', 'DOUBLE PARKING-MIDTOWN COMML': 'Double Parking - Midtown Commercial Zone', 'ELEVATED/DIVIDED HIGHWAY/TUNNL': 'No Stopping in Tunnel or on Elevated Highway', 'EXCAVATION-VEHICLE OBSTR TRAFF': 'No Stopping - Adjacent to Street Construction', 'EXPIRED METER': 'Expired Meter', 'EXPIRED METER-COMM METER ZONE': 'Expired Meter - Commercial Meter Zone', 'EXPIRED MUNI METER': 'Expired Meter', 'EXPIRED MUNI MTR-COMM MTR ZN': 'Expired Meter - Commercial Meter Zone', 'FAIL TO DISP. MUNI METER RECPT': 'Failure to Display Meter Receipt', 'FAIL TO DSPLY MUNI METER RECPT': 'Failure to Display Meter Receipt', 'FAILURE TO DISPLAY BUS PERMIT': 'Failure to Display Bus Permit', 'FAILURE TO STOP AT RED LIGHT': 'Failure to Stop at Red Light', 'FEEDING METER': 'Feeding Meter', 'FIRE HYDRANT': 'Fire Hydrant', 'FRONT OR BACK PLATE MISSING': 'Front or Back Plate Missing', IDLING: 'Idling', 'IMPROPER REGISTRATION': 'Improper Registration', "INSP STICKER-MUTILATED/C'FEIT": 'Inspection Sticker Mutilated or Counterfeit', 'INSP. STICKER-EXPIRED/MISSING': 'Inspection Sticker Expired or Missing', INTERSECTION: 'No Stopping - Intersection', 'MARGINAL STREET/WATER FRONT': 'No Parking on Marginal Street or Waterfront', 'MIDTOWN PKG OR STD-3HR LIMIT': 'Midtown Parking or Standing - 3 Hour Limit', MISCELLANEOUS: 'Miscellaneous', 'MISSING EQUIPMENT': 'Missing Required Equipment', 'NGHT PKG ON RESID STR-COMM VEH': 'No Nighttime Parking on Residential Street - Commercial Vehicle', 'NIGHTTIME STD/ PKG IN A PARK': 'No Nighttime Standing or Parking in a Park', 'NO MATCH-PLATE/STICKER': 'Plate and Sticker Do Not Match', 'NO OPERATOR NAM/ADD/PH DISPLAY': 'Failure to Display Operator Information', 'NO PARKING-DAY/TIME LIMITS': 'No Parking - Day/Time Limits', 'NO PARKING-EXC. AUTH. VEHICLE': 'No Parking - Except Authorized Vehicles', 'NO PARKING-EXC. HNDICAP PERMIT': 'No Parking - Except Disability Permit', 'NO PARKING-EXC. HOTEL LOADING': 'No Parking - Except Hotel Loading', 'NO PARKING-STREET CLEANING': 'No Parking - Street Cleaning', 'NO PARKING-TAXI STAND': 'No Parking - Taxi Stand', 'NO STANDING EXCP D/S': 'No Standing - Except Department of State', 'NO STANDING EXCP DP': 'No Standing - Except Diplomat', 'NO STANDING-BUS LANE': 'No Standing - Bus Lane', 'NO STANDING-BUS STOP': 'No Standing - Bus Stop', 'NO STANDING-COMM METER ZONE': 'No Standing - Commercial Meter Zone', 'NO STANDING-COMMUTER VAN STOP': 'No Standing - Commuter Van Stop', 'NO STANDING-DAY/TIME LIMITS': 'No Standing - Day/Time Limits', 'NO STANDING-EXC. AUTH. VEHICLE': 'No Standing - Except Authorized Vehicle', 'NO STANDING-EXC. TRUCK LOADING': 'No Standing - Except Truck Loading', 'NO STANDING-FOR HIRE VEH STOP': 'No Standing - For Hire Vehicle Stop', 'NO STANDING-HOTEL LOADING': 'No Standing - Hotel Loading', 'NO STANDING-OFF-STREET LOT': 'No Standing - Off-Street Lot', 'NO STANDING-SNOW EMERGENCY': 'No Standing - Snow Emergency', 'NO STANDING-TAXI STAND': 'No Standing - Taxi Stand', 'NO STD(EXC TRKS/GMTDST NO-TRK)': 'No Standing - Except Trucks in Garment District', 'NO STOP/STANDNG EXCEPT PAS P/U': 'No Stopping or Standing Except for Passenger Pick-Up', 'NO STOPPING-DAY/TIME LIMITS': 'No Stopping - Day/Time Limits', 'NON-COMPLIANCE W/ POSTED SIGN': 'Non-Compliance with Posted Sign', 'OBSTRUCTING DRIVEWAY': 'Obstructing Driveway', 'OBSTRUCTING TRAFFIC/INTERSECT': 'Obstructing Traffic or Intersection', 'OT PARKING-MISSING/BROKEN METR': 'Overtime Parking at Missing or Broken Meter', OTHER: 'Other', 'OVERNIGHT TRACTOR TRAILER PKG': 'Overnight Parking of Tractor Trailer', 'OVERTIME PKG-TIME LIMIT POSTED': 'Overtime Parking - Time Limit Posted', 'OVERTIME STANDING DP': 'Overtime Standing - Diplomat', 'OVERTIME STDG D/S': 'Overtime Standing - Department of State', 'PARKED BUS-EXC. DESIG. AREA': 'Bus Parking Outside of Designated Area', 'PEDESTRIAN RAMP': 'Blocking Pedestrian Ramp', 'PHTO SCHOOL ZN SPEED VIOLATION': 'School Zone Speed Camera Violation', 'PKG IN EXC. OF LIM-COMM MTR ZN': 'Parking in Excess of Limits - Commercial Meter Zone', 'PLTFRM LFTS LWRD POS COMM VEH': 'Commercial Vehicle Platform Lifts in Lowered Position', 'RAILROAD CROSSING': 'No Stopping - Railroad Crossing', "REG STICKER-MUTILATED/C'FEIT": 'Registration Sticker Mutilated or Counterfeit', 'REG. STICKER-EXPIRED/MISSING': 'Registration Sticker Expired or Missing', 'REMOVE/REPLACE FLAT TIRE': 'Replacing Flat Tire on Major Roadway', 'SAFETY ZONE': 'No Standing - Safety Zone', 'SELLING/OFFERING MCHNDSE-METER': 'Selling or Offering Merchandise From Metered Parking', SIDEWALK: 'Parked on Sidewalk', 'STORAGE-3HR COMMERCIAL': 'Street Storage of Commercial Vehicle Over 3 Hours', 'TRAFFIC LANE': 'No Stopping - Traffic Lane', 'TUNNEL/ELEVATED/ROADWAY': 'No Stopping in Tunnel or on Elevated Highway', 'UNALTERED COMM VEH-NME/ADDRESS': 'Commercial Plates on Unaltered Vehicle', 'UNALTERED COMM VEHICLE': 'Commercial Plates on Unaltered Vehicle', 'UNAUTHORIZED BUS LAYOVER': 'Bus Layover in Unauthorized Location', 'UNAUTHORIZED PASSENGER PICK-UP': 'Unauthorized Passenger Pick-Up', 'VACANT LOT': 'No Parking - Vacant Lot', 'VEH-SALE/WSHNG/RPRNG/DRIVEWAY': 'No Parking on Street to Wash or Repair Vehicle', 'VEHICLE FOR SALE(DEALERS ONLY)': 'No Parking on Street to Display Vehicle for Sale', 'VIN OBSCURED': 'Vehicle Identification Number Obscured', 'WASH/REPAIR VEHCL-REPAIR ONLY': 'No Parking on Street to Wash or Repair Vehicle', 'WRONG WAY': 'No Parking Opposite Street Direction' }; // humanized names for violations var fyHumanizedNames = { '01': 'Failure to Display Bus Permit', '02': 'Failure to Display Operator Information', '03': 'Unauthorized Passenger Pick-Up', '04': 'Bus Parking in Lower Manhattan - Exceeded 3-Hour limit', '04A': 'Bus Parking in Lower Manhattan - Non-Bus', '04B': 'Bus Parking in Lower Manhattan - No Permit', '06': 'Overnight Parking of Tractor Trailer', '08': 'Idling', '09': 'Obstructing Traffic or Intersection', '10': 'No Stopping or Standing Except for Passenger Pick-Up', '11': 'No Parking - Except Hotel Loading', '12': 'No Standing - Snow Emergency', '13': 'No Standing - Taxi Stand', '14': 'No Standing - Day/Time Limits', '16': 'No Standing - Except Truck Loading/Unloading', '16A': 'No Standing - Except Truck Loading/Unloading', '17': 'No Parking - Except Authorized Vehicles', '18': 'No Standing - Bus Lane', '19': 'No Standing - Bus Stop', '20': 'No Parking - Day/Time Limits', '20A': 'No Parking - Day/Time Limits', '21': 'No Parking - Street Cleaning', '22': 'No Parking - Except Hotel Loading', '23': 'No Parking - Taxi Stand', '24': 'No Parking - Except Authorized Vehicles', '25': 'No Standing - Commuter Van Stop', '26': 'No Standing - For Hire Vehicle Stop', '27': 'No Parking - Except Disability Permit', '28': 'Overtime Standing - Diplomat', '29': 'Altered Intercity Bus Permit', '30': 'No Stopping/Standing', '31': 'No Standing - Commercial Meter Zone', '32': 'Overtime Parking at Missing or Broken Meter', '32A': 'Overtime Parking at Missing or Broken Meter', '33': 'Feeding Meter', '35': 'Selling or Offering Merchandise From Metered Parking', '37': 'Expired Meter', '38': 'Failure to Display Meter Receipt', '39': 'Overtime Parking - Time Limit Posted', '40': 'Fire Hydrant', '42': 'Expired Meter - Commercial Meter Zone', '43': 'Expired Meter - Commercial Meter Zone', '44': 'Overtime Parking - Commercial Meter Zone', '45': 'No Stopping - Traffic Lane', '46': 'Double Parking', '46A': 'Double Parking', '46B': 'Double Parking - Within 100 ft. of Loading Zone', '47': 'Double Parking - Midtown Commercial Zone', '47A': 'Double Parking - Angle Parking', '48': 'Blocking Bike Lane', '49': 'No Stopping - Adjacent to Street Construction', '50': 'Blocking Crosswalk', '51': 'Parked on Sidewalk', '52': 'No Stopping - Intersection', '53': 'No Standing - Safety Zone', '55': 'No Stopping in Tunnel or on Elevated Highway', '56': 'No Stopping - Divided Highway', '57': 'No Parking - Blue Zone', '58': 'No Parking on Marginal Street or Waterfront', '59': 'No Angle Parking', '60': 'No Angle Parking', '61': 'No Parking Opposite Street Direction', '62': 'No Parking Beyond Marked Space', '63': 'No Nighttime Standing or Parking in a Park', '64': 'No Standing - Consul or Diplomat', '65': 'Overtime Standing - Consul or Diplomat Over 30 Minutes', '66': 'Detached Trailer', '67': 'Blocking Pedestrian Ramp', '68': 'Non-Compliance with Posted Sign', '69': 'Failure to Display Meter Receipt', '70': 'Registration Sticker Expired or Missing', '70A': 'Registration Sticker Expired or Missing', '70B': 'Improper Display of Registration', '71': 'Inspection Sticker Expired or Missing', '71A': 'Inspection Sticker Expired or Missing', '71B': 'Improper Safety Sticker', '72': 'Inspection Sticker Mutilated or Counterfeit', '72A': 'Inspection Sticker Mutilated or Counterfeit', '73': 'Registration Sticker Mutilated or Counterfeit', '73A': 'Registration Sticker Mutilated or Counterfeit', '74': 'Front or Back Plate Missing', '74A': 'Improperly Displayed Plate', '74B': 'Covered Plate', '75': 'Plate and Sticker Do Not Match', '77': 'Bus Parking Outside of Designated Area', '78': 'Nighttime Parking on Residential Street - Commercial Vehicle', '79': 'Bus Layover in Unauthorized Location', '80': 'Missing Required Equipment', '81': 'No Standing - Except Diplomat', '82': 'Commercial Plates on Unaltered Vehicle', '83': 'Improper Registration', '84': 'Commercial Vehicle Platform Lifts in Lowered Position', '85': 'Street Storage of Commercial Vehicle Over 3 Hours', '86': 'Midtown Parking or Standing - 3 Hour Limit', '89': 'No Standing - Except Trucks in Garment District', '91': 'No Parking on Street to Display Vehicle for Sale', '92': 'No Parking on Street to Wash or Repair Vehicle', '93': 'Replacing Flat Tire on Major Roadway', '96': 'No Stopping - Railroad Crossing', '98': 'Obstructing Driveway', '01-No Intercity Pmt Displ': 'Failure to Display Bus Permit', '02-No operator N/A/PH': 'Failure to Display Operator Information', '03-Unauth passenger pick-up': 'Unauthorized Passenger Pick-Up', '04-Downtown Bus Area,3 Hr Lim': 'Bus Parking in Lower Manhattan - Exceeded 3-Hour limit', '04A-Downtown Bus Area,Non-Bus': 'Bus Parking in Lower Manhattan - Non-Bus', '04A-Downtown Bus Area, Non-Bus': 'Bus Parking in Lower Manhattan - Non-Bus', '04B-Downtown Bus Area,No Prmt': 'Bus Parking in Lower Manhattan - No Permit', '06-Nighttime PKG (Trailer)': 'Overnight Parking of Tractor Trailer', '08-Engine Idling': 'Idling', '09-Blocking the Box': 'Obstructing Traffic or Intersection', '10-No Stopping': 'No Stopping or Standing Except for Passenger Pick-Up', '11-No Stand (exc hotel load)': 'No Parking - Except Hotel Loading', '12-No Stand (snow emergency)': 'No Standing - Snow Emergency', '13-No Stand (taxi stand)': 'No Standing - Taxi Stand', '14-No Standing': 'No Standing - Day/Time Limits', '16-No Std (Com Veh) Com Plate': 'No Standing - Except Truck Loading/Unloading', '16A-No Std (Com Veh) Non-COM': 'No Standing - Except Truck Loading/Unloading', '17-No Stand (exc auth veh)': 'No Parking - Except Authorized Vehicles', '18-No Stand (bus lane)': 'No Standing - Bus Lane', '19-No Stand (bus stop)': 'No Standing - Bus Stop', '20-No Parking (Com Plate)': 'No Parking - Day/Time Limits', '20A-No Parking (Non-COM)': 'No Parking - Day/Time Limits', '21-No Parking (street clean)': 'No Parking - Street Cleaning', '22-No Parking (exc hotel load)': 'No Parking - Except Hotel Loading', '23-No Parking (taxi stand)': 'No Parking - Taxi Stand', '24-No Parking (exc auth veh)': 'No Parking - Except Authorized Vehicles', '25-No Stand (commutr van stop)': 'No Standing - Commuter Van Stop', '26-No Stnd (for-hire veh only)': 'No Standing - For Hire Vehicle Stop', '27-No Parking (exc handicap)': 'No Parking - Except Disability Permit', '28-O/T STD,PL/Con,0 Mn, Dec': 'Overtime Standing - Diplomat', '29-Altered Intercity bus pmt': 'Altered Intercity Bus Permit', '30-No stopping/standing': 'No Stopping/Standing', '31-No Stand (Com. Mtr. Zone)': 'No Standing - Commercial Meter Zone', '32-Overtime PKG-Missing Meter': 'Overtime Parking at Missing or Broken Meter', '32A Overtime PKG-Broken Meter': 'Overtime Parking at Missing or Broken Meter', '33-Feeding Meter': 'Feeding Meter', '35-Selling/Offer Merchandise': 'Selling or Offering Merchandise From Metered Parking', '37-Expired Muni Meter': 'Expired Meter', '37-Expired Parking Meter': 'Expired Meter', '38-Failure to Display Muni Rec': 'Failure to Display Meter Receipt', '38-Failure to Dsplay Meter Rec': 'Failure to Display Meter Receipt', '39-Overtime PKG-Time Limt Post': 'Overtime Parking - Time Limit Posted', '40-Fire Hydrant': 'Fire Hydrant', '42-Exp. Muni-Mtr (Com. Mtr. Z)': 'Expired Meter - Commercial Meter Zone', '42-Exp Meter (Com Zone)': 'Expired Meter - Commercial Meter Zone', '43-Exp. Mtr. (Com. Mtr. Zone)': 'Expired Meter - Commercial Meter Zone', '44-Exc Limit (Com. Mtr. Zone)': 'Overtime Parking - Commercial Meter Zone', '45-Traffic Lane': 'No Stopping - Traffic Lane', '46-Double Parking (Com Plate)': 'Double Parking', '46A-Double Parking (Non-COM)': 'Double Parking', '46B-Double Parking (Com-100Ft)': 'Double Parking - Within 100 ft. of Loading Zone', '47-Double PKG-Midtown': 'Double Parking - Midtown Commercial Zone', '47A-Angle PKG - Midtown': 'Double Parking - Angle Parking', '48-Bike Lane': 'Blocking Bike Lane', '49-Excavation (obstruct traff)': 'No Stopping - Adjacent to Street Construction', '50-Crosswalk': 'Blocking Crosswalk', '51-Sidewalk': 'Parked on Sidewalk', '52-Intersection': 'No Stopping - Intersection', '53-Safety Zone': 'No Standing - Safety Zone', '55-Tunnel/Elevated Roadway': 'No Stopping in Tunnel or on Elevated Highway', '56-Divided Highway': 'No Stopping - Divided Highway', '57-Blue Zone': 'No Parking - Blue Zone', '58-Marginal Street/Water Front': 'No Parking on Marginal Street or Waterfront', '59-Angle PKG-Commer. Vehicle': 'No Angle Parking', '60-Angle Parking': 'No Angle Parking', '61-Wrong Way': 'No Parking Opposite Street Direction', '62-Beyond Marked Space': 'No Parking Beyond Marked Space', '63-Nighttime STD/PKG in a Park': 'No Nighttime Standing or Parking in a Park', '64-No STD Ex Con/DPL,D/S Dec': 'No Standing - Consul or Diplomat', '65-O/T STD,pl/Con,0 Mn,/S': 'Overtime Standing - Consul or Diplomat Over 30 Minutes', '66-Detached Trailer': 'Detached Trailer', '67-Blocking Ped. Ramp': 'Blocking Pedestrian Ramp', '68-Not Pkg. Comp. w Psted Sign': 'Non-Compliance with Posted Sign', '69-Failure to Disp Muni Recpt': 'Failure to Display Meter Receipt', '69-Fail to Dsp Prking Mtr Rcpt': 'Failure to Display Meter Receipt', '70-Reg. Sticker Missing (NYS)': 'Registration Sticker Expired or Missing', '70A-Reg. Sticker Expired (NYS)': 'Registration Sticker Expired or Missing', '70B-Impropr Dsply of Reg (NYS)': 'Improper Display of Registration', '71-Insp. Sticker Missing (NYS': 'Inspection Sticker Expired or Missing', '71A-Insp Sticker Expired (NYS)': 'Inspection Sticker Expired or Missing', '71B-Improp Safety Stkr (NYS)': 'Improper Safety Sticker', '72-Insp Stkr Mutilated': 'Inspection Sticker Mutilated or Counterfeit', '72A-Insp Stkr Counterfeit': 'Inspection Sticker Mutilated or Counterfeit', '73-Reg Stkr Mutilated': 'Registration Sticker Mutilated or Counterfeit', '73A-Reg Stkr Counterfeit': 'Registration Sticker Mutilated or Counterfeit', '74-Missing Display Plate': 'Front or Back Plate Missing', '74A-Improperly Displayed Plate': 'Improperly Displayed Plate', '74B-Covered Plate': 'Covered Plate', '75-No Match-Plate/Reg. Sticker': 'Plate and Sticker Do Not Match', '77-Parked Bus (exc desig area)': 'Bus Parking Outside of Designated Area', '78-Nighttime PKG on Res Street': 'Nighttime Parking on Residential Street - Commercial Vehicle', '79-Bus Layover': 'Bus Layover in Unauthorized Location', '80-Missing Equipment (specify)': 'Missing Required Equipment', '81-No STD Ex C,&D Dec,30 Mn': 'No Standing - Except Diplomat', '82-Unaltered Commerc Vehicle': 'Commercial Plates on Unaltered Vehicle', '83-Improper Registration': 'Improper Registration', '84-Platform lifts in low posit': 'Commercial Vehicle Platform Lifts in Lowered Position', '85-Storage-3 hour Commercial': 'Street Storage of Commercial Vehicle Over 3 Hours', '86-Midtown PKG or STD-3 hr lim': 'Midtown Parking or Standing - 3 Hour Limit', '89-No Stand Exc Com Plate': 'No Standing - Except Trucks in Garment District', '91-Veh for Sale (Dealer Only)': 'No Parking on Street to Display Vehicle for Sale', '92-Washing/Repairing Vehicle': 'No Parking on Street to Wash or Repair Vehicle', '93-Repair Flat Tire (Maj Road)': 'Replacing Flat Tire on Major Roadway', '96-Railroad Crossing': 'No Stopping - Railroad Crossing', '98-Obstructing Driveway': 'Obstructing Driveway', 'BUS LANE VIOLATION': 'Bus Lane Violation', 'FAILURE TO STOP AT RED LIGHT': 'Failure to Stop at Red Light', 'Field Release Agreement': 'Field Release Agreement', 'PHTO SCHOOL ZN SPEED VIOLATION': 'School Zone Speed Camera Violation' }; // mapping humanized naes to violation codes var namesToCodes = { 'Failure to Display Bus Permit': '01', 'Failure to Display Operator Information': '02', 'Unauthorized Passenger Pick-Up': '03', 'Bus Parking in Lower Manhattan - Exceeded 3-Hour limit': '04', 'Bus Parking in Lower Manhattan - Non-Bus': '04A', 'Bus Parking in Lower Manhattan - No Permit': '04B', 'Bus Lane Violation': '05', 'Overnight Parking of Tractor Trailer': '06', 'Failure to Stop at Red Light': '07', Idling: '08', 'Obstructing Traffic or Intersection': '09', 'No Stopping or Standing Except for Passenger Pick-Up': '10', 'No Parking - Except Hotel Loading': '22', 'No Standing - Snow Emergency': '12', 'No Standing - Taxi Stand': '13', 'No Standing - Day/Time Limits': '14', 'No Standing - Except Truck Loading/Unloading': '16', 'No Parking - Except Authorized Vehicles': '24', 'No Standing - Bus Lane': '18', 'No Standing - Bus Stop': '19', 'No Parking - Day/Time Limits': '20', 'No Parking - Street Cleaning': '21', 'No Parking - Taxi Stand': '23', 'No Standing - Commuter Van Stop': '25', 'No Standing - For Hire Vehicle Stop': '26', 'No Parking - Except Disability Permit': '27', 'Overtime Standing - Diplomat': '28', 'Altered Intercity Bus Permit': '29', 'No Stopping/Standing': '30', 'No Standing - Commercial Meter Zone': '31', 'Overtime Parking at Missing or Broken Meter': '32', 'Feeding Meter': '33', 'Selling or Offering Merchandise From Metered Parking': '35', 'School Zone Speed Camera Violation': '36', 'Expired Meter': '37', 'Failure to Display Meter Receipt': '69', 'Overtime Parking - Time Limit Posted': '39', 'Fire Hydrant': '40', 'Expired Meter - Commercial Meter Zone': '43', 'Overtime Parking - Commercial Meter Zone': '44', 'No Stopping - Traffic Lane': '45', 'Double Parking': '46', 'Double Parking - Within 100 ft. of Loading Zone': '46B', 'Double Parking - Midtown Commercial Zone': '47', 'Double Parking - Angle Parking': '47A', 'Blocking Bike Lane': '48', 'No Stopping - Adjacent to Street Construction': '49', 'Blocking Crosswalk': '50', 'Parked on Sidewalk': '51', 'No Stopping - Intersection': '52', 'No Standing - Safety Zone': '53', 'No Stopping in Tunnel or on Elevated Highway': '55', 'No Stopping - Divided Highway': '56', 'No Parking - Blue Zone': '57', 'No Parking on Marginal Street or Waterfront': '58', 'No Angle Parking': '60', 'No Parking Opposite Street Direction': '61', 'No Parking Beyond Marked Space': '62', 'No Nighttime Standing or Parking in a Park': '63', 'No Standing - Consul or Diplomat': '64', 'Overtime Standing - Consul or Diplomat Over 30 Minutes': '65', 'Detached Trailer': '66', 'Blocking Pedestrian Ramp': '67', 'Non-Compliance with Posted Sign': '68', 'Registration Sticker Expired or Missing': '70', 'Improper Display of Registration': '70B', 'Inspection Sticker Expired or Missing': '71', 'Improper Safety Sticker': '71B', 'Inspection Sticker Mutilated or Counterfeit': '72', 'Registration Sticker Mutilated or Counterfeit': '73', 'Front or Back Plate Missing': '74', 'Improperly Displayed Plate': '74A', 'Covered Plate': '74B', 'Plate and Sticker Do Not Match': '75', 'Bus Parking Outside of Designated Area': '77', 'Nighttime Parking on Residential Street - Commercial Vehicle': '78', 'Bus Layover in Unauthorized Location': '79', 'Missing Required Equipment': '80', 'No Standing - Except Diplomat': '81', 'Commercial Plates on Unaltered Vehicle': '82', 'Improper Registration': '83', 'Commercial Vehicle Platform Lifts in Lowered Position': '84', 'Street Storage of Commercial Vehicle Over 3 Hours': '85', 'Midtown Parking or Standing - 3 Hour Limit': '86', 'No Standing - Except Trucks in Garment District': '89', 'No Parking on Street to Display Vehicle for Sale': '91', 'No Parking on Street to Wash or Repair Vehicle': '92', 'Replacing Flat Tire on Major Roadway': '93', 'No Stopping - Railroad Crossing': '96', 'Obstructing Driveway': '98' }; var stateAbbrRegex = /^(99|AB|AK|AL|AR|AZ|BC|CA|CO|CT|DC|DE|DP|FL|FM|FO|GA|GU|GV|HI|IA|ID|IL|IN|KS|KY|LA|MA|MB|MD|ME|MI|MN|MO|MP|MS|MT|MX|NB|NC|ND|NE|NF|NH|NJ|NM|NS|NT|NV|NY|OH|OK|ON|OR|PA|PE|PR|PW|QC|RI|SC|SD|SK|TN|TX|UT|VA|VI|VT|WA|WI|WV|WY|YT)$/; var registrationTypesRegex = /^(AGC|AGR|AMB|APP|ARG|ATD|ATV|AYG|BOB|BOT|CBS|CCK|CHC|CLG|CMB|CME|CMH|COM|CSP|DLR|FAR|FPW|GAC|GSM|HAC|HAM|HIR|HIS|HOU|HSM|IRP|ITP|JCA|JCL|JSC|JWV|LMA|LMB|LMC|LOC|LTR|LUA|MCD|MCL|MED|MOT|NLM|NYA|NYC|NYS|OMF|OML|OMO|OMR|OMS|OMT|OMV|ORC|ORG|ORM|PAS|PHS|PPH|PSD|RGC|RGL|SCL|SEM|SNO|SOS|SPC|SPO|SRF|SRN|STA|STG|SUP|THC|TOW|TRA|TRC|TRL|USC|USS|VAS|VPL|WUG)$/; var counties = { Bronx: 'Bronx', Brook: 'Brooklyn', BX: 'Bronx', Bx: 'Bronx', BK: 'Brooklyn', Bk: 'Brooklyn', K: 'Brooklyn', KINGS: 'Brooklyn', MAH: 'Manhattan', MANHA: 'Manhattan', MN: 'Manhattan', NEUY: 'Manhattan', NY: 'Manhattan', PBX: 'Bronx', PK: 'Brooklyn', PNY: 'Manhattan', Q: 'Queens', QN: 'Queens', QNS: 'Queens', Queen: 'Queens', R: 'Staten Island', Rich: 'Staten Island', ST: 'Staten Island' }; var issuingAgencies = { 'DEPARTMENT OF BUSINESS SERVICES': 'NYC SBS', 'DEPARTMENT OF SANITATION': 'DSNY', 'DEPARTMENT OF TRANSPORTATION': 'NYC DOT', 'Fire Department': 'FDNY', 'HOUSING AUTHORITY': 'NYCHA', 'LONG ISLAND RAILROAD': 'LIRR', 'METRO NORTH RAILROAD POLICE': 'MNRR', 'NYC OFFICE OF THE SHERIFF': 'NYC Sheriff', 'NYC TRANSIT AUTHORITY MANAGERS': 'NYCTA', 'NYS COURT OFFICERS': 'NYS Courts', 'NYS PARKS POLICE': 'NYS Parks', 'OTHER/UNKNOWN AGENCIES': 'other', 'PARKING CONTROL UNIT': 'NYC DOT', 'PARKS DEPARTMENT': 'NYC Parks', 'POLICE DEPARTMENT': 'NYPD', 'PORT AUTHORITY': 'PANYNJ', 'TAXI AND LIMOUSINE COMMISSION': 'TLC', TRAFFIC: 'NYPD Traffic', 'TRANSIT AUTHORITY': 'NYCTA', 'TRIBOROUGH BRIDGE AND TUNNEL POLICE': 'TBTA', T: 'NYPD Traffic', V: 'NYC DOT' }; /* let initializeConnection = config => { let addDisconnectHandler = connection => { connection.on('error', error => { if (error instanceof Error) { let error_code: string = undefined; let is_fatal: boolean = false; if (error.hasOwnProperty('code')) { error_code = (error as any)['code']; } if (error.hasOwnProperty('fatal')) { is_fatal = (error as any)['fatal']; } if (error_code === 'PROTOCOL_CONNECTION_LOST') { console.error(error.stack); console.log('Lost connection. Reconnecting...'); initializeConnection(connection.config); } else if (is_fatal) { throw error; } } }); }; var connection = mysql.createConnection(config); connection.promiseQuery = (sql, values) => { return new Promise((resolve, reject) => { return connection.query(sql, values, (error, results, fields) => { if (error) reject(error); if (results) resolve(results); }); }); }; // Add handlers. addDisconnectHandler(connection); connection.connect(); return connection; }; */ var detectPlateTypes = function (potentialPlateTypeString) { if (potentialPlateTypeString.indexOf(',') != -1) { var parts = potentialPlateTypeString.split(','); var bools = parts.map(function (part) { return !(part.match(registrationTypesRegex) == undefined); }); return bools.some(function (bool) { return bool === true; }); } else { return !(potentialPlateTypeString.match(registrationTypesRegex) == undefined); } }; var detectState = function (potentialStateString) { return !(potentialStateString.match(stateAbbrRegex) == undefined); }; var detectVehicles = function (potentialVehicles) { return potentialVehicles.map(function (plate) { var vehicle = { original_string: plate }; var parts = plate .toUpperCase() .trim() .split(':'); if ([2, 3].includes(parts.length)) { var stateBools = parts.map(function (part) { return detectState(part); }); var stateIndex_1 = stateBools.indexOf(true); var plateTypesBools = parts.map(function (part) { return detectPlateTypes(part); }); var plateTypesIndex_1 = plateTypesBools.indexOf(true); var haveValidPlate = (parts.length === 2 && stateIndex_1 != -1) || (parts.length === 3 && ![plateTypesIndex_1, stateIndex_1].includes(-1)); if (haveValidPlate) { var plateIndex = __spread(Array(parts.length).keys()).filter(function (part) { return ![stateIndex_1, plateTypesIndex_1].includes(part); })[0]; vehicle['state'] = parts[stateIndex_1]; vehicle['types'] = parts[plateTypesIndex_1]; vehicle['plate'] = parts[plateIndex]; vehicle['valid_plate'] = true; } else { vehicle['valid_plate'] = false; } } else { vehicle['valid_plate'] = false; } return vehicle; }); }; var findFilterFields = function (fieldsString) { if (fieldsString == undefined || fieldsString == '') return {}; var returnFields = {}; var fields = fieldsString.replace(/(\([^)]*\))/g, '').split(','); fields.forEach(function (field) { if (fieldsString[fieldsString.indexOf(field) + field.length] == '(') { var re = new RegExp(field + '\\([\\w|,]*\\)', 'g'); var subfields = fieldsString.match(re) == undefined ? null : fieldsString.match(re)[0].replace(field, ''); subfields = subfields.substring(1, subfields.length - 1); if (subfields) { returnFields[field] = findFilterFields(subfields.replace(/[\(|\)]/g, '')); } else if (subfields == '') { returnFields[field] = {}; } } else { returnFields[field] = {}; } }); return returnFields; }; var findMaxCameraViolationsStreak = function (violationTimes) { if (violationTimes.length) { var maxStreak_1 = 0; var streakEnd_1 = null; var streakStart_1 = null; violationTimes.forEach(function (date) { var yearLater = new Date(date).setFullYear(date.getFullYear() + 1); var yearLongTickets = violationTimes.filter(function (otherDate) { return otherDate >= date && otherDate <= yearLater; }); var thisStreak = yearLongTickets.length; if (thisStreak > maxStreak_1) { maxStreak_1 = thisStreak; streakEnd_1 = yearLongTickets[0]; streakStart_1 = yearLongTickets[yearLongTickets.length - 1]; } }); return { max_streak: maxStreak_1, streak_end: streakEnd_1, streak_start: streakStart_1 }; } else { return null; } }; exports.getVehicleResponse = function (vehicle, selectedFields, externalData) { return new Promise(function (resolve, reject) { var plate = vehicle.plate; var state = vehicle.state; var plateTypes = vehicle.types == undefined ? null : vehicle.types.split(',').map(function (part) { return part.trim(); }); var lookupSource = externalData.lookup_source; var fingerprintID = externalData.fingerprint_id; var mixpanelID = externalData.mixpanel_id; if (plate && state) { var apiPromises = makeOpenDataRequests(plate, state, plateTypes); q.all(apiPromises).then(function (endpointResponses) { var newPromises = []; endpointResponses.forEach(function (response) { newPromises = newPromises.concat(normalizeViolations(JSON.parse(response))); }); q.all(newPromises).then(function (violations) { violations = mergeDuplicateViolationRecords(violations); modifyViolationsForResponse(violations); var reducer = function (accumulator, currentValue) { return accumulator + currentValue; }; var totalFined = violations .map(function (obj) { return obj.fined || 0; }) .reduce(reducer, 0); var totalPaid = violations .map(function (obj) { return obj.paid || 0; }) .reduce(reducer, 0); var totalReduced = violations .map(function (obj) { return obj.reduced || 0; }) .reduce(reducer, 0); var totalOutstanding = violations .map(function (obj) { return obj.outstanding || 0; }) .reduce(reducer, 0); var fines = new fine_data_1.FineData({ total_fined: totalFined, total_paid: totalPaid, total_reduced: totalReduced, total_outstanding: totalOutstanding }); var cameraViolations = violations.filter(function (violation) { return violation.humanized_description == 'School Zone Speed Camera Violation' || violation.humanized_description == 'Failure to Stop at Red Light'; }); var streakData = findMaxCameraViolationsStreak(cameraViolations.map(function (violation) { return violation.formatted_time; })); var frequencyLookup = { plate: plate, state: state }; var searchQueryString = plateTypes == undefined ? 'select count(*) as frequency from plate_lookups where plate = ? and state = ? and count_towards_frequency = 1; select num_tickets, created_at from plate_lookups where plate = ? and state = ? and count_towards_frequency = 1 ORDER BY created_at DESC LIMIT 1' : 'select count(*) as frequency from plate_lookups where plate = ? and state = ? and plate_types = ? and count_towards_frequency = 1; select num_tickets, created_at from plate_lookups where plate = ? and state = ? and plate_types = ? and count_towards_frequency = 1 ORDER BY created_at DESC LIMIT 1'; var searchQueryArgs = plateTypes == undefined ? [plate, state, plate, state] : [ plate, state, plateTypes.join(), plate, state, plateTypes.join() ]; queryForLookups(searchQueryString, searchQueryArgs, function (error, results, fields) { if (error) throw error; var frequency = lookupSource == null ? 0 : 1; var newLookup = { plate: plate, state: state, plate_types: plateTypes == undefined ? null : plateTypes.join(), observed: null, message_id: null, lookup_source: lookupSource == undefined ? 'api' : lookupSource, created_at: new Date(), external_username: null, count_towards_frequency: lookupSource == undefined ? false : true, num_tickets: violations.length, boot_eligible: (streakData && streakData.max_streak >= 5) || 0, fingerprint_id: fingerprintID, mixpanel_id: mixpanelID, responded_to: true }; var previous_count = null; var previous_date = null; if (results && results[0] && results[0][0] && results[1] && results[1][0]) { frequency = results[0][0].frequency + (lookupSource == null ? 0 : 1); previous_count = results[1][0].num_tickets; previous_date = results[1][0].created_at; } /* insertNewLookup(newLookup, (error, results, fields) => { if (error) throw error; }); */ var returnVehicle = new vehicle_lookup_response_1.VehicleLookupData({ camera_streak_data: streakData, fines: fines, plate: plate, plate_types: plateTypes, previous_lookup_date: previous_date, previous_violation_count: previous_count, state: state, times_queried: frequency, violations: violations, violations_count: violations.length }); var returnObject = new vehicle_lookup_response_1.VehicleLookupResponse({ vehicle: returnVehicle, successful_lookup: false }); returnObject.vehicle = stripReturnData(returnObject.vehicle, selectedFields); returnObject['successful_lookup'] = true; resolve(returnObject); }); }); }); } else { resolve({ vehicle: {}, error_string: 'Sorry, a plate and state could not be inferred from ' + vehicle.original_string, successful_lookup: false }); } }); }; /* let insertNewLookup = (newLookup, callback) => { connection.query('insert into plate_lookups set ?', newLookup, callback); }; */ var makeOpenDataRequests = function (plate, state, plateTypes) { var fy_endpoints = [ 'https://data.cityofnewyork.us/resource/j7ig-zgkq.json', 'https://data.cityofnewyork.us/resource/aagd-wyjz.json', 'https://data.cityofnewyork.us/resource/avxe-2nrn.json', 'https://data.cityofnewyork.us/resource/2bnn-yakx.json', 'https://data.cityofnewyork.us/resource/9wgk-ev5c.json', 'https://data.cityofnewyork.us/resource/faiq-9dfq.json', 'https://data.cityofnewyork.us/resource/pvqr-7yc4.json' ]; var violations = []; // Checking for selected fields for violations // const fieldsForExternalRequests = 'violations' in selectedFields ? Object.keys(selectedFields['violations']) : {} // Fiscal Year Databases var promises = fy_endpoints.map(function (endpoint) { var queryString = endpoint + '?plate_id=' + encodeURIComponent(plate.toUpperCase()) + '&registration_state=' + state.toUpperCase(); if (plateTypes) { var plateTypesQuery = plateTypes .map(function (item) { return '%27' + item.toUpperCase().trim() + '%27'; }) .join(); queryString += '&$where=plate_type%20in(' + plateTypesQuery + ')'; } // if (fieldsForExternalRequests) { // queryString += '&$select=' + fieldsForExternalRequests.join(',') // } queryString += '&$limit=10000&$$app_token=q198HrEaAdCJZD4XCLDl2Uq0G'; return rp(queryString); }); // Open Parking & Camera Violations Database var opacvQueryString = 'https://data.cityofnewyork.us/resource/uvbq-3m68.json' + '?plate=' + encodeURIComponent(plate.toUpperCase()) + '&state=' + state.toUpperCase(); if (plateTypes) { var plateTypesQuery = plateTypes .map(function (item) { return '%27' + item.toUpperCase().trim() + '%27'; }) .join(); opacvQueryString += '&$where=license_type%20in(' + plateTypesQuery + ')'; } // if (fieldsForExternalRequests) { // opacvQueryString += '&$select=' + fieldsForExternalRequests.join(',') // } opacvQueryString += '&$limit=10000&$$app_token=q198HrEaAdCJZD4XCLDl2Uq0G'; promises.push(rp(opacvQueryString)); return promises; }; var mergeDuplicateViolationRecords = function (violations) { var accum = []; violations.forEach(function (object) { var existing = accum.filter(function (violation, i) { return violation.summons_number == object.summons_number; }); if (existing.length) { var existingIndex = accum.indexOf(existing[0]); var existingItem_1 = accum[existingIndex]; var mergedObject_1 = {}; Object.keys(existingItem_1).forEach(function (key) { mergedObject_1[key] = existingItem_1[key] || object[key]; }); Object.keys(object).forEach(function (key) { mergedObject_1[key] = existingItem_1[key] || object[key]; }); accum[existingIndex] = mergedObject_1; } else { accum.push(object); } }); return accum; }; var modifyViolationsForResponse = function (violations /*, selectedFields*/) { return violations.map(function (violation) { var violationDate = violation.issue_date; var violationTime = violation.violation_time; var date; var dateMatch; if (violationDate) { dateMatch = violationDate.split('T'); if (dateMatch) { date = dateMatch[0]; } } if (violationTime) { var isAM = violationTime.includes('A'); var isPM = violationTime.includes('P'); var timeMatch = violationTime.match(/\d{4}/); var hour = void 0; var minute = void 0; if (timeMatch) { hour = timeMatch[0].substring(0, 2); minute = timeMatch[0].substring(2, 4); if (isPM && parseInt(hour) < 12) { hour = (parseInt(hour) + 12).toString(); } else if (isAM && hour == 12) { hour = (parseInt(hour) - 12).toString(); } } else if ((timeMatch = violationTime.match(/\d{2}:\d{2}/))) { hour = timeMatch[0].split(':')[0]; minute = timeMatch[0].split(':')[1]; } if (hour) { if (isPM && parseInt(hour) < 12) { hour = (parseInt(hour) + 12).toString(); } else if (isAM && hour == 12) { hour = (parseInt(hour) - 12).toString(); } } // console.log(violation.summons_number) // console.log(violation.street_name) // console.log(violation.intersecting_street) // console.log(' ') if (violationDate.match(/\d{2}\/\d{2}\/\d{4}/)) { date = date.replace(/\//g, '-'); violation.formatted_time = new Date(date + ' ' + hour + ':' + minute); } else { violation.formatted_time = new Date(date + 'T' + hour + ':' + minute); } } else { date = date.replace(/\//g, '-'); violation.formatted_time = new Date(date + ' 00:00'); } var fined = null; if (violation.fine_amount) { fined = parseFloat(violation.fine_amount); } if (violation.penalty_amount) { fined += parseFloat(violation.penalty_amount); } if (violation.interest_amount) { fined += parseFloat(violation.interest_amount); } if (fined) { violation.fined = fined; } if (violation.payment_amount) { violation.paid = parseFloat(violation.payment_amount); } if (violation.reduction_amount) { violation.reduced = parseFloat(violation.reduction_amount); } if (violation.outstanding_amount) { violation.outstanding = parseFloat(violation.outstanding_amount); } }); }; var normalizeViolations = function (violations) { var that = _this; var returnViolations = []; violations.forEach(function (violation) { var promise; var newViolation = { amount_due: isNaN(parseFloat(violation.amount_due)) ? null : parseFloat(violation.amount_due), date_first_observed: violation.date_first_observed || null, feet_from_curb: violation.feet_from_curb || null, fine_amount: isNaN(parseFloat(violation.fine_amount)) ? null : parseFloat(violation.fine_amount), to_hours_in_effect: violation.from_hours_in_effect || null, house_number: violation.house_number || null, humanized_description: fyHumanizedNames[violation.violation_description || violation.violation_code] || opacvHumanizedNames[violation.violation] || null, interest_amount: isNaN(parseFloat(violation.interest_amount)) ? null : parseFloat(violation.interest_amount), intersecting_street: violation.intersecting_street || null, issue_date: violation.issue_date || null, issuer_code: violation.issuer_code || null, issuer_command: violation.issuer_command || null, issuer_precinct: isNaN(parseInt(violation.issuer_precinct)) ? null : parseInt(violation.issuer_precinct), issuing_agency: issuingAgencies[violation.issuing_agency] || null, law_section: violation.law_section || null, payment_amount: isNaN(parseFloat(violation.payment_amount)) ? null : parseFloat(violation.payment_amount), penalty_amount: isNaN(parseFloat(violation.penalty_amount)) ? null : parseFloat(violation.penalty_amount), plate_id: violation.plate_id || violation.plate || null, plate_type: violation.plate_type || violation.license_type || null, reduction_amount: isNaN(parseFloat(violation.reduction_amount)) ? null : parseFloat(violation.reduction_amount), registration_state: violation.registration_state || null, street_code1: violation.street_code1 || null, street_code2: violation.street_code2 || null, street_code3: violation.street_code3 || null, street_name: violation.street_name || null, sub_division: violation.sub_division || null, summons_image: violation.summons_image || null, summons_number: violation.summons_number || null, //to_hours_in_effect: violation.to_hours_in_effect || null, vehicle_body_type: violation.vehicle_body_type || null, vehicle_color: violation.vehicle_color || null, vehicle_expiration_date: violation.vehicle_expiration_date || null, vehicle_make: violation.vehicle_make || null, vehicle_year: violation.vehicle_year || null, violation_code: violation.violation_code || namesToCodes[fyHumanizedNames[violation.violation_description || violation.violation_code] || opacvHumanizedNames[violation.violation]] || null, violation_county: counties[violation.violation_county || violation.county] || null, violation_in_front_of_or_opposite: violation.violation_in_front_of_or_opposite || null, violation_legal_code: violation.violation_legal_code || null, violation_location: violation.violation_location || null, violation_post_code: violation.violation_post_code || null, violation_precinct: isNaN(parseInt(violation.violation_precinct || violation.precinct)) ? null : parseInt(violation.violation_precinct || violation.precinct), violation_time: violation.violation_time || null, location: violation.location || null }; var fullStreet; var houseNumber = newViolation.house_number; var street1 = newViolation.street_name; var street2 = newViolation.intersecting_street; if (street1 && street2) { if (street1.length == 20 && street1.charAt(street1.length - 1) != ' ') { fullStreet = street1 + street2; } else { fullStreet = street1 +