def _process_invalid_vehicle( self, request_object: BaseLookupRequest, invalid_vehicle: Vehicle) -> InvalidVehicleResponse: """Process an invalid vehicle by notifying the user which necessary lookup elements were missing or incorrect and how to correct the issue in a subsequent tweet.s """ plate_lookup_response_parts: list[Any] # Record the failed lookup. new_failed_lookup = FailedPlateLookup( message_id=request_object.external_id(), username=request_object.username()) # Insert plate lookup FailedPlateLookup.query.session.add(new_failed_lookup) FailedPlateLookup.query.session.commit() # Legacy data where state is not a valid abbreviation. if invalid_vehicle.state: LOG.debug("We have a state, but it's invalid.") plate_lookup_response_parts = [ f"The state should be two characters, but you supplied '{invalid_vehicle.state}'. " f"Please try again." ] # '<state>:<plate>' format, but no valid state could be detected. elif invalid_vehicle.original_string: LOG.debug( "We don't have a state, but we have an attempted lookup with the new format." ) plate_lookup_response_parts = [ f"Sorry, a plate and state could not be inferred from " f"{invalid_vehicle.original_string}." ] # If we have a plate, but no state. elif invalid_vehicle.plate: LOG.debug("We have a plate, but no state") plate_lookup_response_parts = [ "Sorry, the state appears to be blank." ] return InvalidVehicleResponse( response_parts=plate_lookup_response_parts)
def _process_lookup_without_detected_vehicles( self, request_object: BaseLookupRequest) -> NonVehicleResponse: """Process a lookup that had no detected vehicles, either partial or complete, by determining if the user was likely trying to submit a lookup, and if so, help them do so in a subsequent message. """ non_vehicle_response_parts: list[Any] # Record the failed lookup. new_failed_lookup = FailedPlateLookup( message_id=request_object.external_id(), username=request_object.username()) # Insert plate lookup FailedPlateLookup.query.session.add(new_failed_lookup) FailedPlateLookup.query.session.commit() LOG.debug('The data seems to be in the wrong format.') state_matches = [ regexp_constants.STATE_ABBREVIATIONS_PATTERN.search(s.upper()) != None for s in request_object.string_tokens() ] number_matches = [ regexp_constants.NUMBER_PATTERN.search(s.upper()) != None for s in list( filter( lambda part: re.sub(r'\.|@', '', part.lower()) not in set( request_object.mentioned_users), request_object.string_tokens())) ] # We have what appears to be a plate and a state abbreviation. if all([any(state_matches), any(number_matches)]): LOG.debug( 'There is both plate and state information in this message.') # Let user know plate format non_vehicle_response_parts = [ ("I’d be happy to look that up for you!\n\n" 'Just a reminder, the format is ' '<state|province|territory>:<plate>, e.g. NY:abc1234') ] # Maybe we have plate or state. Let's find out. else: LOG.debug('The tweet is missing either state or plate or both.') state_minus_words_matches = [ regexp_constants.STATE_MINUS_WORDS_PATTERN.search(s.upper()) != None for s in request_object.string_tokens() ] number_matches = [ regexp_constants.NUMBER_PATTERN.search(s.upper()) != None for s in list( filter( lambda part: re.sub(r'\.|@', '', part.lower()) not in set(request_object.mentioned_users), request_object.string_tokens())) ] # We have either plate or state. if any(state_minus_words_matches) or any(number_matches): # Let user know plate format non_vehicle_response_parts = [( "I think you're trying to look up a plate, but can't be sure.\n\n" 'Just a reminder, the format is ' '<state|province|territory>:<plate>, e.g. NY:abc1234')] # We have neither plate nor state. Do nothing. else: non_vehicle_response_parts = [] LOG.debug( 'ignoring message since no plate or state information to respond to.' ) return NonVehicleResponse(response_parts=non_vehicle_response_parts)
def _process_valid_vehicle(self, campaigns: list[Campaign], request_object: BaseLookupRequest, vehicle: Vehicle) -> ValidVehicleResponse: """Process a valid plate by: 1. searching open data 2. saving the lookup 3. returning the results """ error_on_plate_lookup: bool = False plate_lookup_response_parts: list[Any] success_on_plate_lookup: bool = False plate_lookup: Optional[OpenDataServicePlateLookup] = None plate_query: PlateQuery = self._get_plate_query( vehicle=vehicle, request_object=request_object) # do we have a previous lookup previous_lookup: Optional[ PlateLookup] = self._query_for_previous_lookup( plate_query=plate_query) LOG.debug(f'Previous lookup for this vehicle: {previous_lookup}') # Obtain a unique identifier for the lookup unique_identifier: str = self._get_unique_identifier() # Do the real work! open_data_response: OpenDataServiceResponse = self._perform_plate_lookup( campaigns=campaigns, plate_query=plate_query, unique_identifier=unique_identifier) if open_data_response.success: # Record successful lookup. success_on_plate_lookup = True plate_lookup: OpenDataServicePlateLookup = open_data_response.data # how many times have we searched for this plate from a tweet current_frequency: int = self._query_for_lookup_frequency( plate_query) if plate_lookup.violations: plate_lookup_response_parts = self._form_plate_lookup_response_parts( borough_data=plate_lookup.boroughs, camera_streak_data=plate_lookup.camera_streak_data, fine_data=plate_lookup.fines, frequency=current_frequency, lookup_source=request_object.message_source, plate=plate_lookup.plate, plate_types=plate_lookup.plate_types, previous_lookup=previous_lookup, state=plate_lookup.state, username=request_object.username(), unique_identifier=unique_identifier, violations=plate_lookup.violations, year_data=plate_lookup.years) else: # Let user know we didn't find anything. plate_types_string = (f' (types: {plate_query.plate_types})' ) if plate_lookup.plate_types else '' plate_lookup_response_parts = L10N.NO_TICKETS_FOUND_STRING.format( plate_query.state, plate_lookup.plate, plate_types_string) else: # Record lookup error. error_on_plate_lookup = True plate_lookup_response_parts = [ f"Sorry, I received an error when looking up " f"{plate_query.state}:{plate_query.plate}" f"{(' (types: ' + plate_query.plate_types + ')') if plate_query.plate_types else ''}. " f"Please try again." ] return ValidVehicleResponse(error_on_lookup=error_on_plate_lookup, plate_lookup=plate_lookup, response_parts=plate_lookup_response_parts, success_on_lookup=success_on_plate_lookup)
def build_reply_data(self, message: any, message_source: LookupSource): LOG.info( f'args for reply data:\n' f'message: {message}\n' f'message_source: {message_source}\n') lookup_request: Type[BaseLookupRequest] = None # why doesn't python have switch statements if message_source == LookupSource.STATUS: # Using old streaming service for a tweet longer than 140 # characters if hasattr(message, TwitterAPIAttribute.EXTENDED_TWEET.value): LOG.debug('We have an extended tweet') lookup_request = StreamExtendedStatus( message, TwitterMessageType.STATUS.value) # Using tweet api search endpoint elif hasattr(message, TwitterAPIAttribute.FULL_TEXT.value) and (not hasattr(message, TwitterAPIAttribute.RETWEETED_STATUS.value)): LOG.debug( 'We have a tweet from the search api endpoint') lookup_request = SearchStatus( message, TwitterMessageType.STATUS.value) # Using old streaming service for a tweet of 140 characters or # fewer elif hasattr(message, TwitterAPIAttribute.ENTITIES.value) and (not hasattr(message, TwitterAPIAttribute.RETWEETED_STATUS.value)): LOG.debug( 'We are dealing with a tweet of ' '140 characters or fewer') lookup_request = StreamingStatus( message, TwitterMessageType.STATUS.value) # Using new account api service by way of SQL table for events elif hasattr(message, TwitterAPIAttribute.EVENT_TYPE.value): LOG.debug( 'We are dealing with account activity api object') lookup_request = AccountActivityAPIStatus( message, TwitterMessageType.STATUS.value) elif message_source == LookupSource.DIRECT_MESSAGE: # Using old streaming service for a direct message if hasattr(message, TwitterAPIAttribute.DIRECT_MESSAGE.value): LOG.debug( 'We have a direct message from the streaming service') lookup_request = StreamingDirectMessage( message, TwitterMessageType.DIRECT_MESSAGE.value) # Using new direct message api endpoint elif hasattr(message, TwitterAPIAttribute.MESSAGE_CREATE.value): LOG.debug( 'We have a direct message from the direct message api') lookup_request = DirectMessageAPIDirectMessage( message, TwitterMessageType.DIRECT_MESSAGE.value, self.api) # Using account activity api endpoint elif hasattr(message, TwitterAPIAttribute.EVENT_TYPE.value): LOG.debug( 'We are dealing with an account activity api object') lookup_request = AccountActivityAPIDirectMessage( message, TwitterMessageType.DIRECT_MESSAGE.value) elif message_source == LookupSource.API: LOG.debug( 'We are dealing with a HowsMyDrivingNY API request') lookup_request = HowsMyDrivingAPIRequest(message, LookupSource.API) if not lookup_request: LOG.debug('Unrecognized request type') lookup_request = BaseLookupRequest('unknown', 'unknown') return lookup_request