コード例 #1
0
    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)
コード例 #2
0
    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)
コード例 #3
0
    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)
コード例 #4
0
    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