예제 #1
0
    def _get_property_value(property_name, contact_properties):
        property_value = contact_properties[property_name]
        if isinstance(property_value, bool):
            property_value = json_serialize(property_value)
        elif isinstance(property_value, date):
            property_value = convert_date_to_timestamp_in_milliseconds(property_value)

        property_value = unicode(property_value)
        return property_value
예제 #2
0
    def _get_property_value(property_name, contact_properties):
        property_value = contact_properties[property_name]
        if isinstance(property_value, bool):
            property_value = json_serialize(property_value)
        elif isinstance(property_value, date):
            property_value = \
                convert_date_to_timestamp_in_milliseconds(property_value)

        property_value = text_type(property_value)
        return property_value
예제 #3
0
    def _exclude_contacts_pages_after_cutoff_datetime(cls, contacts, cutoff_datetime):
        if not cutoff_datetime:
            filtered_contacts = contacts

        elif cutoff_datetime <= cls.MOST_RECENT_CONTACT_UPDATE_DATETIME:
            cutoff_timestamp = convert_date_to_timestamp_in_milliseconds(cutoff_datetime)
            cutoff_index = cls._MOST_RECENT_CONTACT_UPDATE_TIMESTAMP - cutoff_timestamp

            last_page_number_to_include = ceil((cutoff_index + 1.0) / BATCH_RETRIEVAL_SIZE_LIMIT)
            first_contact_index_to_exclude = int(last_page_number_to_include * BATCH_RETRIEVAL_SIZE_LIMIT)
            filtered_contacts = contacts[:first_contact_index_to_exclude]

        else:
            filtered_contacts = []

        return filtered_contacts
예제 #4
0
def _get_contacts_from_all_pages_by_recency(
        contact_list_id,
        connection,
        property_names=(),
        cutoff_datetime=None,
):
    contacts_data = _get_contacts_data(
        connection,
        '/lists/{}/contacts/recent'.format(contact_list_id),
        ('vid-offset', 'time-offset'),
        property_names,
    )

    if cutoff_datetime:
        cutoff_timestamp = \
            convert_date_to_timestamp_in_milliseconds(cutoff_datetime)
    else:
        cutoff_timestamp = None

    property_type_by_property_name = \
        get_property_type_by_property_name(connection)

    seen_contact_vids = set()
    for contact_data in contacts_data:
        contact = _build_contact_from_data(
            contact_data,
            property_type_by_property_name,
        )

        if contact.vid in seen_contact_vids:
            continue

        seen_contact_vids.add(contact.vid)

        if cutoff_timestamp and contact_data['addedAt'] < cutoff_timestamp:
            raise StopIteration()

        yield contact
예제 #5
0
def _get_contacts_from_all_pages_by_recency(
    contact_list_id,
    connection,
    property_names=(),
    cutoff_datetime=None,
    ):
    contacts_data = _get_contacts_data(
        connection,
        '/lists/{}/contacts/recent'.format(contact_list_id),
        ('vid-offset', 'time-offset'),
        property_names,
        )

    if cutoff_datetime:
        cutoff_timestamp = \
            convert_date_to_timestamp_in_milliseconds(cutoff_datetime)
    else:
        cutoff_timestamp = None

    property_type_by_property_name = \
        get_property_type_by_property_name(connection)

    seen_contact_vids = set()
    for contact_data in contacts_data:
        contact = _build_contact_from_data(
            contact_data,
            property_type_by_property_name,
            )

        if contact.vid in seen_contact_vids:
            continue

        seen_contact_vids.add(contact.vid)

        if cutoff_timestamp and contact_data['addedAt'] < cutoff_timestamp:
            raise StopIteration()

        yield contact
예제 #6
0
    def _exclude_contacts_pages_after_cutoff_datetime(
        cls,
        contacts,
        cutoff_datetime,
    ):
        if not cutoff_datetime:
            filtered_contacts = contacts

        elif cutoff_datetime <= cls.MOST_RECENT_CONTACT_UPDATE_DATETIME:
            cutoff_timestamp = \
                convert_date_to_timestamp_in_milliseconds(cutoff_datetime)
            cutoff_index = \
                cls._MOST_RECENT_CONTACT_UPDATE_TIMESTAMP - cutoff_timestamp

            last_page_number_to_include = \
                ceil((cutoff_index + 1.0) / BATCH_RETRIEVAL_SIZE_LIMIT)
            first_contact_index_to_exclude = \
                int(last_page_number_to_include * BATCH_RETRIEVAL_SIZE_LIMIT)
            filtered_contacts = contacts[:first_contact_index_to_exclude]

        else:
            filtered_contacts = []

        return filtered_contacts
예제 #7
0
 def _get_contact_added_at_timestamp(self, contact):
     contact_added_at_datetime = \
         self.get_contact_added_at_datetime(contact, self._contacts)
     contact_added_at_timestamp = \
         convert_date_to_timestamp_in_milliseconds(contact_added_at_datetime)
     return contact_added_at_timestamp
def _convert_date_to_datestamp_in_milliseconds(date_or_datetime):
    if isinstance(date_or_datetime, datetime):
        date_or_datetime = date_or_datetime.date()

    return convert_date_to_timestamp_in_milliseconds(date_or_datetime)
예제 #9
0
def _convert_date_to_datestamp_in_milliseconds(date_or_datetime):
    if isinstance(date_or_datetime, datetime):
        date_or_datetime = date_or_datetime.date()

    return convert_date_to_timestamp_in_milliseconds(date_or_datetime)
예제 #10
0
 def _get_contact_added_at_timestamp(self, contact):
     contact_added_at_datetime = \
         self.get_contact_added_at_datetime(contact, self._contacts)
     contact_added_at_timestamp = \
         convert_date_to_timestamp_in_milliseconds(contact_added_at_datetime)
     return contact_added_at_timestamp
예제 #11
0
class GetAllContactsByLastUpdate(GetAllContacts):
    """
    Simulator for a successful call to
    :func:`~hubspot.contacts.lists.get_all_contacts_by_last_update`.
    
    """

    _API_CALL_PATH_INFO = '/lists/recently_updated/contacts/recent'

    MOST_RECENT_CONTACT_UPDATE_DATETIME = datetime.now()

    _MOST_RECENT_CONTACT_UPDATE_TIMESTAMP = \
        convert_date_to_timestamp_in_milliseconds(
            MOST_RECENT_CONTACT_UPDATE_DATETIME,
            )

    def __init__(
            self,
            contacts,
            available_properties,
            property_names=(),
            cutoff_datetime=None,
    ):
        """
        
        :param iterable contacts: :class:`~hubspot.contacts.Contact` instances
            for all the contacts supposedly in the portal.
        :param iterable available_properties:
            :class:`~hubspot.contacts.properties.Property` instances for all
            the properties supposedly defined in the portal.
        :param iterable property_names: The names of the properties that
            :func:`~hubspot.contacts.lists.get_all_contacts` is expected to
            request.
        :param datetime.datetime cutoff_datetime: The
            :class:`~datetime.datetime` that is expected to be passed to
            :func:`~hubspot.contacts.lists.get_all_contacts_by_last_update`.
        
        When ``cutoff_datetime`` is unset, this simulator works exactly like
        :class:`GetAllContacts` -- Except that it uses a different API
        end-point. The order of the returned contacts match that of
        ``contacts``.
        
        Given that
        :func:`~hubspot.contacts.lists.get_all_contacts_by_last_update`
        relies on the last modified time of each contact to filter out those
        which were last changed before ``cutoff_datetime``, this
        simulator assigns a fake, decreasing value to each contact in the
        response.
        
        Therefore, if you need to test the effects of ``cutoff_datetime``,
        you ought to pre-compute such values with
        :meth:`get_contact_added_at_datetime`. E.g.::
        
            contacts = [
                Contact(vid=1, email_address='*****@*****.**', properties={}),
                Contact(vid=2, email_address='*****@*****.**', properties={}),
                Contact(vid=3, email_address='*****@*****.**', properties={}),
                ]
            # We expect get_all_contacts_by_last_update to exclude the last
            # contact:
            cutoff_datetime = \\
                GetAllContactsByLastUpdate.get_contact_added_at_datetime(
                    contacts[1],
                    contacts,
                    )
            simulator = GetAllContactsByLastUpdate(
                contacts,
                available_properties,
                cutoff_datetime=cutoff_datetime,
                )
        
        """

        filtered_contacts = self._exclude_contacts_pages_after_cutoff_datetime(
            contacts,
            cutoff_datetime,
        )

        super(GetAllContactsByLastUpdate, self).__init__(
            filtered_contacts,
            available_properties,
            property_names=property_names,
        )

        self._contacts = filtered_contacts

    @classmethod
    def _exclude_contacts_pages_after_cutoff_datetime(
        cls,
        contacts,
        cutoff_datetime,
    ):
        if not cutoff_datetime:
            filtered_contacts = contacts

        elif cutoff_datetime <= cls.MOST_RECENT_CONTACT_UPDATE_DATETIME:
            cutoff_timestamp = \
                convert_date_to_timestamp_in_milliseconds(cutoff_datetime)
            cutoff_index = \
                cls._MOST_RECENT_CONTACT_UPDATE_TIMESTAMP - cutoff_timestamp

            last_page_number_to_include = \
                ceil((cutoff_index + 1.0) / BATCH_RETRIEVAL_SIZE_LIMIT)
            first_contact_index_to_exclude = \
                int(last_page_number_to_include * BATCH_RETRIEVAL_SIZE_LIMIT)
            filtered_contacts = contacts[:first_contact_index_to_exclude]

        else:
            filtered_contacts = []

        return filtered_contacts

    def _get_query_string_args_for_page(self, page_number):
        super_ = super(GetAllContactsByLastUpdate, self)
        query_string_args_for_page = \
            super_._get_query_string_args_for_page(page_number)

        page_index = page_number - 1
        previous_page_contacts = self._objects_by_page[page_index - 1]
        previous_page_last_contact = previous_page_contacts[-1]
        query_string_args_for_page['timeOffset'] = \
            self._get_contact_added_at_timestamp(previous_page_last_contact)

        return query_string_args_for_page

    def _get_response_body_deserialization_for_page(self, page_contacts):
        super_ = super(GetAllContactsByLastUpdate, self)
        response_body_deserialization_for_page = \
            super_._get_response_body_deserialization_for_page(page_contacts)

        if page_contacts:
            page_last_contact = page_contacts[-1]
            time_offset = \
                self._get_contact_added_at_timestamp(page_last_contact)
        else:
            time_offset = 0
        response_body_deserialization_for_page['time-offset'] = time_offset

        return response_body_deserialization_for_page

    def _get_objects_data(self, contacts):
        contacts_data = \
            super(GetAllContactsByLastUpdate, self)._get_objects_data(contacts)

        for contact, contact_data in zip(contacts, contacts_data):
            contact_data['addedAt'] = \
                self._get_contact_added_at_timestamp(contact)

        return contacts_data

    @classmethod
    def get_contact_added_at_datetime(cls, contact, contacts):
        """
        Compute the fake last modification time that would be assigned to
        ``contact`` inside ``contacts``.
        
        :param hubspot.contacts.Contact contact: A contact in ``contacts``
        :param iterable contacts: A collection containing ``contact``
        :return: :class:`~datetime.datetime` for the last modification time
            corresponding to ``contact``
        
        This method refers to the "added at" term, which is a piece of meta-data
        that HubSpot attaches to each contact returned via the end-point for
        the recently updated contacts. In spite of its name, this datum is
        effectively the last modified date, but not exactly the same as the
        "lastmodifieddate" property.
        
        The ``cutoff_datetime`` passed to this simulator is compared against
        the "added at" value of the contact, not the "lastmodifieddate"
        property.
        
        """
        contact_index = contacts.index(contact)
        contact_added_at_timestamp = \
            cls._MOST_RECENT_CONTACT_UPDATE_TIMESTAMP - contact_index
        contact_added_at_datetime = \
            convert_timestamp_in_milliseconds_to_datetime(
                contact_added_at_timestamp,
                )
        return contact_added_at_datetime

    def _get_contact_added_at_timestamp(self, contact):
        contact_added_at_datetime = \
            self.get_contact_added_at_datetime(contact, self._contacts)
        contact_added_at_timestamp = \
            convert_date_to_timestamp_in_milliseconds(contact_added_at_datetime)
        return contact_added_at_timestamp
예제 #12
0
class GetAllContacts(_PaginatedObjectsRetriever):
    """
    Simulator for a successful call to
    :func:`~hubspot.contacts.lists.get_all_contacts`.
    
    """

    _API_CALL_PATH_INFO = '/lists/all/contacts/all'

    _OBJECT_DATA_KEY = 'contacts'

    _LAST_MODIFIED_DATE_PROPERTY = DatetimeProperty(
        'lastmodifieddate',
        'label',
        'description',
        'group_name',
        'text',
    )

    _STUB_LAST_MODIFIED_TIMESTAMP = \
        convert_date_to_timestamp_in_milliseconds(STUB_LAST_MODIFIED_DATETIME)

    def __init__(self, contacts, available_properties, property_names=()):
        """
        
        :param iterable contacts: :class:`~hubspot.contacts.Contact` instances
            for all the contacts supposedly in the portal.
        :param iterable available_properties:
            :class:`~hubspot.contacts.properties.Property` instances for all
            the properties supposedly defined in the portal.
        :param iterable property_names: The names of the properties that
            :func:`~hubspot.contacts.lists.get_all_contacts` is expected to
            request.
        
        """
        super(GetAllContacts, self).__init__(contacts)

        available_properties += [self._LAST_MODIFIED_DATE_PROPERTY]
        self._available_properties_simulator = \
            GetAllProperties(available_properties)
        self._property_names = property_names

    def __call__(self):
        api_calls = self._available_properties_simulator()
        api_calls.extend(super(GetAllContacts, self).__call__())
        return api_calls

    def _get_query_string_args(self, page_contacts):
        query_string_args = \
            super(GetAllContacts, self)._get_query_string_args(page_contacts)

        if self._property_names:
            query_string_args['property'] = self._property_names

        return query_string_args

    def _get_query_string_args_for_page(self, page_number):
        previous_page_contacts = self._objects_by_page[page_number - 2]
        previous_page_last_contact = previous_page_contacts[-1]
        query_string_args_for_page = \
            {'vidOffset': previous_page_last_contact.vid}
        return query_string_args_for_page

    def _get_response_body_deserialization_for_page(self, page_contacts):
        page_last_contact = page_contacts[-1] if page_contacts else None
        page_last_contact_vid = \
            page_last_contact.vid if page_last_contact else 0
        response_body_deserialization_for_page = {
            'vid-offset': page_last_contact_vid,
        }
        return response_body_deserialization_for_page

    def _get_objects_data(self, contacts):
        contacts_data = []
        for contact in contacts:
            contact_properties_data = \
                self._get_contact_properties_data(contact)
            contact_profiles_data = self._get_contact_profiles_data(contact)
            contact_data = {
                'vid': contact.vid,
                'canonical-vid': contact.vid,
                'properties': contact_properties_data,
                'identity-profiles': contact_profiles_data,
            }
            contacts_data.append(contact_data)

        return contacts_data

    def _get_contact_properties_data(self, contact):
        contact_properties = contact.properties

        stub_last_modified_timestamp_string = \
            str(self._STUB_LAST_MODIFIED_TIMESTAMP)
        stub_lastmodifieddate_property_value_data = \
            self._get_contact_property_value_data(
                stub_last_modified_timestamp_string,
                )
        contact_properties_data = {
            self._LAST_MODIFIED_DATE_PROPERTY.name:
            stub_lastmodifieddate_property_value_data,
        }
        for property_name in self._property_names:
            if property_name == 'email' and contact.email_address:
                if 'email' in contact_properties:
                    assert contact.email_address == contact_properties['email']
                property_value = contact.email_address
            elif property_name not in contact_properties:
                continue
            else:
                property_value = \
                    self._get_property_value(property_name, contact_properties)
            contact_properties_data[property_name] = \
                self._get_contact_property_value_data(property_value)
        return contact_properties_data

    @staticmethod
    def _get_contact_property_value_data(property_value):
        property_value_data = {'value': property_value, 'versions': []}
        return property_value_data

    @staticmethod
    def _get_property_value(property_name, contact_properties):
        property_value = contact_properties[property_name]
        if isinstance(property_value, bool):
            property_value = json_serialize(property_value)
        elif isinstance(property_value, date):
            property_value = \
                convert_date_to_timestamp_in_milliseconds(property_value)

        property_value = text_type(property_value)
        return property_value

    @staticmethod
    def _get_contact_profiles_data(contact):
        contact_profile_data = {
            'vid':
            contact.vid,
            'identities': [
                {
                    'type': 'LEAD_GUID',
                    'value': get_uuid4_str()
                },
                {
                    'type': 'EMAIL',
                    'value': contact.email_address
                },
            ],
        }
        contact_profiles_data = [contact_profile_data]

        for vid in contact.related_contact_vids:
            contact_profiles_data.append({'vid': vid, 'identities': []})

        return contact_profiles_data