Esempio n. 1
0
    def _table_convert(self, obj):
        tbl = Table([x.__dict__['_properties'] for x in obj])

        if 'subresource_uris' in tbl.columns and 'uri' in tbl.columns:
            tbl.remove_column('subresource_uris', 'uri')

        return tbl
Esempio n. 2
0
    def get_agents(self, email=None, mobile=None, phone=None, state=None):
        """
        List agents.

        `Args:`
            email: str
                Filter by email address.
            mobile: str
                Filter by mobile phone number
            phone: str
                Filter by phone number
            state: str
                Filter by state
        `Returns:`
            Parsons Table
                See :ref:`parsons-table` for output options.
        """

        params = {
            'email': email,
            'mobile': mobile,
            'phone': phone,
            'state': state
        }

        tbl = Table(self.get_request('agents', params=params))
        logger.info(f'Found {tbl.num_rows} agents.')
        tbl = self.transform_table(tbl)
        tbl = tbl.unpack_dict('contact', prepend=False)
        tbl.remove_column(
            'signature')  # Removing since raw HTML might cause issues.

        return tbl
Esempio n. 3
0
    def get_surveys(self, page=None):
        """
        Get a table of lists under the account.

        `Args:`
            page : int
                Retrieve a specific page of responses. If not given,
                then all pages are retrieved.

        `Returns:`
            Table Class
        """

        r = self._client.api.survey.list(page)
        data = r['data']

        if not page:
            while r['page'] < r['total_pages']:
                r = self._client.api.survey.list(page=(r['page']+1))
                data.extend(r['data'])

        tbl = Table(data).remove_column('links')
        tbl.unpack_dict('statistics', prepend=False)

        logger.info(f"Found {tbl.num_rows} surveys.")

        return tbl
    def test_get_url(self):

        file_name = 'delete_me.csv'
        input_tbl = Table([['a'], ['1']])
        self.cloud.upload_table(input_tbl, TEMP_BUCKET_NAME, file_name)
        url = self.cloud.get_url(TEMP_BUCKET_NAME, file_name)
        download_tbl = Table.from_csv(url)
        assert_matching_tables(input_tbl, download_tbl)
Esempio n. 5
0
    def get_records(self, fields=None, max_records=None, view=None, formula=None, sort=None):
        """
        `Args:`
            fields: str or lst
                Only return specified column or list of columns. The column name is
                case sensitive
            max_records: int
                The maximum total number of records that will be returned.
            view: str
                If set, only the records in that view will be returned. The records will be
                sorted according to the order of the view.
            formula: str
                The formula will be evaluated for each record, and if the result
                is not 0, false, "", NaN, [], or #Error! the record will be included
                in the response.

                If combined with view, only records in that view which satisfy the
                formula will be returned. For example, to only include records where
                ``COLUMN_A`` isn't empty, pass in: ``"NOT({COLUMN_A}='')"``

                For more information see
                `Airtable Docs on formulas. <https://airtable.com/api>`_

                Usage - Text Column is not empty:

                ``airtable.get_all(formula="NOT({COLUMN_A}='')")``

                Usage - Text Column contains:

                ``airtable.get_all(formula="FIND('SomeSubText', {COLUMN_STR})=1")``

            sort: str or lst
                Specifies how the records will be ordered. If you set the view parameter, the
                returned records in that view will be sorted by these fields. If sorting by
                multiple columns, column names can be passed as a list. Sorting Direction is
                ascending by default, but can be reversed by prefixing the column name with a minus
                sign -.

                Example usage:
                ``airtable.get_records(sort=['ColumnA', '-ColumnB'])``

        `Returns:`
            Parsons Table
                See :ref:`parsons-table` for output options.
        """

        # Raises an error if sort is None type. Thus, only adding if populated.
        kwargs = {'fields': fields, 'max_records': max_records, 'view': view, 'formula': formula}
        if sort:
            kwargs['sort'] = sort

        tbl = Table(self.at.get_all(**kwargs))

        # If the results are empty, then return an empty table.
        if 'fields' not in tbl.columns:
            return Table([[]])

        return tbl.unpack_dict(column='fields', prepend=False)
Esempio n. 6
0
    def table_convert(self, obj):
        # Internal method to create a Parsons table from a Twilio object.

        tbl = Table([x.__dict__['_properties'] for x in obj])

        if 'subresource_uris' in tbl.columns and 'uri' in tbl.columns:
            tbl.remove_column('subresource_uris', 'uri')

        return tbl
Esempio n. 7
0
    def test_create_leads(self, m):

        m.post(HUSTLE_URI + 'groups/cMCH0hxwGt/leads', json=expected_json.leads_tbl_01)

        tbl = Table([['phone_number', 'ln', 'first_name'],
                     ['4435705355', 'Warren', 'Elizabeth'],
                     ['5126993336', 'Obama', 'Barack']])
        ids = self.hustle.create_leads(tbl, group_id='cMCH0hxwGt')
        assert_matching_tables(ids, Table(expected_json.leads['items']))
Esempio n. 8
0
    def convert_to_table(self, data):
        # Internal method to create a Parsons table from a data element.
        table = None
        if type(data) is list:
            table = Table(data)
        else:
            table = Table([data])

        return table
Esempio n. 9
0
    def test_get_leads(self, m):

        # By Organization
        m.get(HUSTLE_URI + 'organizations/cMCH0hxwGt/leads', json=expected_json.leads)
        leads = self.hustle.get_leads(organization_id='cMCH0hxwGt')
        assert_matching_tables(leads, Table(expected_json.leads['items']))

        # By Group ID
        m.get(HUSTLE_URI + 'groups/cMCH0hxwGt/leads', json=expected_json.leads)
        leads = self.hustle.get_leads(group_id='cMCH0hxwGt')
        assert_matching_tables(leads, Table(expected_json.leads['items']))
Esempio n. 10
0
    def test_get_poll_locations(self, m):

        m.get(self.gc.uri + 'voterinfo', json=voterinfo_resp)

        expected_tbl = Table(polling_data)

        address_tbl = Table([['address'],
                             ['900 N Washtenaw, Chicago, IL 60622'],
                             ['900 N Washtenaw, Chicago, IL 60622']])

        tbl = self.gc.get_polling_locations(2000, address_tbl)

        assert_matching_tables(tbl, expected_tbl)
Esempio n. 11
0
    def get_campaign_emails(self,
                            campaign_id,
                            fields=None,
                            exclude_fields=None,
                            count=None,
                            offset=None,
                            since=None):
        """
        Get a table of individual emails from a campaign based on query
        parameters. Note that argument descriptions here are sourced from
        Mailchimp's official API documentation.

        `Args:`
            campaign_id: string
                The unique ID of the campaign to fetch emails from.
            fields: list of strings
                A comma-separated list of fields to return. Reference
                parameters of sub-objects with dot notation.
            exclude_fields: list of strings
                A comma-separated list of fields to exclude. Reference
                parameters of sub-objects with dot notation.
            count: int
                The number of records to return. Default value is 10. Maximum
                value is 1000.
            offset: int
                The number of records from a collection to skip. Iterating over
                large collections with this parameter can be slow. Default
                value is 0.
            since: string
                Restrict results to email activity events that occur after a
                specific time. We recommend ISO 8601 time format:
                2015-10-21T15:41:36+00:00.

        `Returns:`
            Table Class
        """
        params = {
            'fields': fields,
            'exclude_fields': exclude_fields,
            'count': count,
            'offset': offset,
            'since': since
        }

        response = self.client.get_request(
            f'reports/{campaign_id}/email-activity', params=params)
        tbl = Table(response['emails'])
        if tbl.num_rows > 0:
            return tbl
        else:
            return Table()
Esempio n. 12
0
    def test_geocode_address_batch(self):

        batch = [['id', 'street', 'city', 'state', 'zip'],
                 ['1', '908 N Washtenaw', 'Chicago', 'IL', '60622'],
                 ['2', '1405 Wilshire Blvd', 'Austin', 'TX', '78722'],
                 ['3', '908 N Washtenaw', 'Chicago', 'IL', '60622'],
                 ['4', '1405 Wilshire Blvd', 'Austin', 'TX', '78722'],
                 ['5', '908 N Washtenaw', 'Chicago', 'IL', '60622']]

        tbl = Table(batch)

        self.cg.cg.addressbatch = mock.MagicMock(return_value=batch_resp)
        geo = self.cg.geocode_address_batch(tbl)
        assert_matching_tables(geo, Table(petl.fromdicts(batch_resp)))
Esempio n. 13
0
    def process_custom_fields(self, json_blob):
        # Internal method to convert custom fields responses into a list of Parsons tables

        # Original table & columns
        custom_fields = Table(json_blob)

        # Available On
        available_on = custom_fields.long_table(['id'], 'available_on')

        # Options
        options = custom_fields.long_table(['id', 'name'], 'options')

        return [{'name': 'custom_fields', 'tbl': custom_fields},
                {'name': 'custom_fields_available', 'tbl': available_on},
                {'name': 'custom_fields_options', 'tbl': options}]
Esempio n. 14
0
    def test_download_file(self) -> None:
        box = Box()

        table = Table([['phone_number', 'last_name', 'first_name'],
                       ['4435705355', 'Warren', 'Elizabeth'],
                       ['5126993336', 'Obama', 'Barack']])
        uploaded_file = table.to_csv()

        path_filename = f'{self.temp_folder_name}/my_path'
        box.upload_table(table, path_filename)

        downloaded_file = box.download_file(path_filename)

        with open(uploaded_file) as uploaded, open(downloaded_file) as downloaded:
            self.assertEqual(str(uploaded.read()), str(downloaded.read()))
Esempio n. 15
0
    def get_leads(self, organization_id=None, group_id=None):
        """
        Get leads metadata. One of ``organization_id`` and ``group_id`` must be passed
        as an argument. If both are passed, an error will be raised.

        `Args:`
            organization_id: str
                The organization id.
            group_id: str
                The group id.
        `Returns:`
            Parsons Table
                See :ref:`parsons-table` for output options.
        """

        if organization_id is None and group_id is None:
            raise ValueError('Either organization_id or group_id required.')

        if organization_id is not None and group_id is not None:
            raise ValueError(
                'Only one of organization_id and group_id may be populated.')

        if organization_id:
            endpoint = f'organizations/{organization_id}/leads'
            logger.info(f'Retrieving {organization_id} organization leads.')
        if group_id:
            endpoint = f'groups/{group_id}/leads'
            logger.info(f'Retrieving {group_id} group leads.')

        tbl = Table(self._request(endpoint))
        logger.info(f'Got {tbl.num_rows} leads.')
        return tbl
Esempio n. 16
0
    def get_contacts(self,
                     email=None,
                     mobile=None,
                     phone=None,
                     company_id=None,
                     state=None,
                     updated_since=None,
                     expand_custom_fields=None):
        """
        Get contacts.

        `Args:`
            email: str
                Filter by email address.
            mobile: str
                Filter by mobile phone number.
            phone: str
                Filter by phone number.
            expand_custom_fields: boolean
                Expand nested custom fields to their own columns.
        """

        params = {
            'email': email,
            'mobile': mobile,
            'phone': phone,
            'company_id': company_id,
            'state': state,
            '_updated_since': updated_since
        }

        tbl = Table(self.get_request('contacts', params=params))
        logger.info(f'Found {tbl.num_rows} contacts.')
        return self.transform_table(tbl, expand_custom_fields)
Esempio n. 17
0
    def get_contributions(self, csv_type, date_range_start, date_range_end):
        """
        Get specified contribution data from CSV API as Parsons table.

        `Args:`
            csv_type: str
                Type of CSV you are requesting.
                Options:
                    'paid_contributions': contains paid, non-refunded contributions to the entity
                    (campaign or organization) you created the credential for, during the specified
                    date range.

                    'refunded_contributions': contributions to your entity that were refunded,
                    during the specified date range.

                    'managed_form_contributions': contributions made through any form that is
                    managed by your entity, during the specified date range - including
                    contributions to other entities via that form if it is a tandem form.
            date_range_start: str
                Start of date range to withdraw contribution data (inclusive). Ex: '2020-01-01'
            date_range_end: str
                End of date range to withdraw contribution data (exclusive). Ex: '2020-02-01'

        `Returns:`
            Contents of the generated contribution CSV as a Parsons table.
        """

        post_request_response = self.post_request(csv_type, date_range_start, date_range_end)
        csv_id = post_request_response["id"]
        download_url = self.poll_for_download_url(csv_id)
        table = Table.from_csv(download_url)
        logger.info('Completed conversion to Parsons Table.')
        return table
Esempio n. 18
0
    def test_get_leaderboard(self, m):

        m.get(self.ct.uri + '/leaderboard', json=expected_leaderboard)
        leaderboard = self.ct.get_leaderboard()
        exp_tbl = self.ct.unpack(
            Table(expected_leaderboard['result']['accountStatistics']))
        assert_matching_tables(leaderboard, exp_tbl)
Esempio n. 19
0
    def get_constituents(self,
                         page_number=1,
                         page_size=50,
                         order_by=None,
                         order_direction=None,
                         last_modified=None):
        """
        `Args:`
            page_number: int
                Number of the page to fetch
            page_size: int
                Number of records per page (maximum allowed is 50)
            order_by: str
                Sorts by ``Id``, ``CreatedDate``, or ``LastModifiedDate`` (default ``Id``).
            order_direction: str
                Sorts the order_by in ``Asc`` or ``Desc`` order.
            last_modified: str
                Filters to constituents last modified after the specified date (ISO-8601 format).
        `Returns:`
            A Table of the entries.
        """
        params = self._base_pagination_params(page_number, page_size)
        params.update(self._base_ordering_params(order_by, order_direction))

        if last_modified:
            params["lastModified"] = last_modified

        response = self._base_get('constituents', params=params)
        return Table(response['Results'])
Esempio n. 20
0
    def test_get_elections(self, m):

        m.get(self.gc.uri + 'elections', json=elections_resp)

        expected_tbl = Table(elections_resp['elections'])

        assert_matching_tables(self.gc.get_elections(), expected_tbl)
Esempio n. 21
0
    def test_upload_file(self) -> None:
        # Count on environment variables being set
        box = Box()

        table = Table([['phone_number', 'last_name', 'first_name'],
                       ['4435705355', 'Warren', 'Elizabeth'],
                       ['5126993336', 'Obama', 'Barack']])
        box_file = box.upload_table_to_folder_id(table, 'phone_numbers',
                                                 folder_id=self.temp_folder_id)

        new_table = box.get_table_by_file_id(box_file.id)

        # Check that what we saved is equal to what we got back
        self.assertEqual(str(table), str(new_table))

        # Check that things also work in JSON
        box_file = box.upload_table_to_folder_id(table, 'phone_numbers_json',
                                                 folder_id=self.temp_folder_id,
                                                 format='json')

        new_table = box.get_table_by_file_id(box_file.id, format='json')

        # Check that what we saved is equal to what we got back
        self.assertEqual(str(table), str(new_table))

        # Now check the same thing with paths instead of file_id
        path_filename = 'path_phone_numbers'
        box_file = box.upload_table(table, f'{self.temp_folder_name}/{path_filename}')
        new_table = box.get_table(path=f'{self.temp_folder_name}/{path_filename}')

        # Check that we throw an exception with bad formats
        with self.assertRaises(ValueError):
            box.upload_table_to_folder_id(table, 'phone_numbers', format='illegal_format')
        with self.assertRaises(ValueError):
            box.get_table_by_file_id(box_file.id, format='illegal_format')
Esempio n. 22
0
    def get_leaderboard(self, start_date=None, end_date=None, list_ids=None, account_ids=None):
        """
        Return advocates (person records).

        `Args:`
            start_date: str
                Filter to the earliest date at which a post could be posted.
                The time is formatted as UTC (e.g. ``yyyy-mm-ddThh:mm:ss``).
            end_date: str
                Filter to the latest date at which a post could be posted.
                The time is formatted as UTC (e.g. ``yyyy-mm-ddThh:mm:ss``).
            list_ids: list
                Filter to the ids of lists or saved searches to retrieve.
            account_ids: list
                A list of CrowdTangle accountIds to retrieve leaderboard data for.
                This and ``list_id`` are mutually exclusive; if both are sent, the
                ``account_ids`` value will be used.
        `Returns:`
            Parsons Table
                See :ref:`parsons-table` for output options.
        """

        args = {'startDate': start_date,
                'endDate': end_date,
                'listIds': self.list_to_string(list_ids),
                'accountIds': self.list_to_string(account_ids)}

        pt = Table(self.base_request('leaderboard', args=args))
        logger.info(f'Retrieved {pt.num_rows} records from the leaderbooard.')
        self.unpack(pt)
        return pt
Esempio n. 23
0
    def test_list_files_by_id(self) -> None:
        # Count on environment variables being set
        box = Box()

        subfolder = box.create_folder_by_id(
            folder_name='id_subfolder', parent_folder_id=self.temp_folder_id)

        # Create a couple of files in the temp folder
        table = Table([['phone_number', 'last_name', 'first_name'],
                       ['4435705355', 'Warren', 'Elizabeth'],
                       ['5126993336', 'Obama', 'Barack']])

        box.upload_table_to_folder_id(table, 'temp1', folder_id=subfolder)
        box.upload_table_to_folder_id(table, 'temp2', folder_id=subfolder)
        box.create_folder_by_id(folder_name='temp_folder1',
                                parent_folder_id=subfolder)
        box.create_folder_by_id(folder_name='temp_folder2',
                                parent_folder_id=subfolder)

        file_list = box.list_files_by_id(folder_id=subfolder)
        self.assertEqual(['temp1', 'temp2'], file_list['name'])

        # Check that if we delete a file, it's no longer there
        for box_file in file_list:
            if box_file['name'] == 'temp1':
                box.delete_file_by_id(box_file['id'])
                break
        file_list = box.list_files_by_id(folder_id=subfolder)
        self.assertEqual(['temp2'], file_list['name'])

        folder_list = box.list_folders_by_id(folder_id=subfolder)['name']
        self.assertEqual(['temp_folder1', 'temp_folder2'], folder_list)
Esempio n. 24
0
    def get_survey_responses(self, survey_id, page=None):
        """
        Get the responses for a given survey.

        `Args:`
            survey_id: string
                The id of survey for which to retrieve the responses.

            page : int
                Retrieve a specific page of responses. If not given,
                then all pages are retrieved.

        `Returns:`
            Table Class
        """

        r = self._client.api.surveyresponse.list(survey_id, page)
        logger.info(f"{survey_id}: {r['total_count']} responses.")
        data = r['data']

        if not page:
            while r['page'] < r['total_pages']:
                r = self._client.api.surveyresponse.list(survey_id, page=(r['page']+1))
                data.extend(r['data'])

        tbl = Table(data).add_column('survey_id', survey_id, index=1)

        logger.info(f"Found #{tbl.num_rows} responses.")

        return tbl
Esempio n. 25
0
    def get_unsubscribes(self,
                         campaign_id,
                         fields=None,
                         exclude_fields=None,
                         count=None,
                         offset=None):
        """
        Get a table of unsubscribes associated with a campaign based on query
        parameters. Note that argument descriptions here are sourced from
        Mailchimp's official API documentation.

        `Args:`
            campaign_id: string
                The unique ID of the campaign to fetch unsubscribes from.
            fields: list of strings
                A comma-separated list of fields to return. Reference
                parameters of sub-objects with dot notation.
            exclude_fields: list of strings
                A comma-separated list of fields to exclude. Reference
                parameters of sub-objects with dot notation.
            count: int
                The number of records to return. Default value is 10. Maximum
                value is 1000.
            offset: int
                The number of records from a collection to skip. Iterating over
                large collections with this parameter can be slow. Default
                value is 0.

        `Returns:`
            Table Class
        """
        params = {
            'fields': fields,
            'exclude_fields': exclude_fields,
            'count': count,
            'offset': offset
        }

        response = self.client.get_request(
            f'reports/{campaign_id}/unsubscribed', params=params)
        tbl = Table(response['unsubscribes'])
        logger.info(f'Found {tbl.num_rows} unsubscribes for {campaign_id}.')
        if tbl.num_rows > 0:
            return tbl
        else:
            return Table()
Esempio n. 26
0
    def test_get_companies(self, m):

        processed_companies = Table([
            {'id': 35015567, 'name': 'Company One', 'assignee_id': None, 'contact_type_id': 547508,
            'details': None, 'email_domain': '*****@*****.**', 'tags': [],
            'interaction_count': 1, 'date_created': 1558441519, 'date_modified': 1558441535,
            'address_city': 'CityA', 'address_country': None, 'address_postal_code': '12345',
            'address_state': 'New York', 'address_street': None},
            {'id': 35026533, 'name': 'Company Two', 'assignee_id': None, 'contact_type_id': 547508,
            'details': None, 'email_domain': '*****@*****.**', 'tags': [],
            'interaction_count': 1, 'date_created': 1558452953, 'date_modified': 1558452967,
            'address_city': 'CityB', 'address_country': None, 'address_postal_code': '23451',
            'address_state': 'New York', 'address_street': None},
            {'id': 35014973, 'name': 'Company Three', 'assignee_id': None, 'contact_type_id': 547508,
            'details': None, 'email_domain': None, 'tags': [], 'interaction_count': 1,
            'date_created': 1558434147, 'date_modified': 1558458137, 'address_city': None,
            'address_country': None, 'address_postal_code': '34512', 'address_state': 'Alabama',
            'address_street': None},
            {'id': 35029116, 'name': 'Company Four', 'assignee_id': None, 'contact_type_id': 547508,
            'details': None, 'email_domain': '*****@*****.**', 'tags': [],
            'interaction_count': 0, 'date_created': 1558461301, 'date_modified': 1558461301,
            'address_city': 'CityD ', 'address_country': None, 'address_postal_code': '45123',
            'address_state': 'California', 'address_street': None},
            {'id': 35082308, 'name': 'Company Five', 'assignee_id': None, 'contact_type_id': 547508,
            'details': None, 'email_domain': '*****@*****.**', 'tags': [],
            'interaction_count': 1, 'date_created': 1558639445, 'date_modified': 1558639459,
            'address_city': 'CityE', 'address_country': None, 'address_postal_code': '51234',
            'address_state': 'Arizona', 'address_street': None}
            ])

        processed_companies_phones = Table([
            {'id': 35082308, 'phone_numbers_category': 'work',
            'phone_numbers_number': '123-555-9876'}
            ])

        m.post(self.cp.uri + '/companies/search',
                  json=self.paginate_callback,
                  headers={"filename": "companies_search.json"})

        processed_blob = self.cp.get_companies()
        blob_companies = [f for f in processed_blob if f['name'] == "companies"][0]['tbl']
        blob_companies_phones = [f for f in processed_blob if f['name'] == "companies_phone_numbers"][0]['tbl']

        assert_matching_tables(processed_companies, blob_companies)
        assert_matching_tables(processed_companies_phones, blob_companies_phones)
Esempio n. 27
0
    def test_create_leads(self, m):

        m.post(HUSTLE_URI + 'groups/cMCH0hxwGt/leads',
               json=expected_json.leads_tbl_01)

        tbl = Table([['phone_number', 'ln', 'first_name', 'address'],
                     ['4435705355', 'Johnson', 'Lyndon', '123 Main Street'],
                     ['4435705354', 'Richards', 'Ann', '124 Main Street']])
        self.hustle.create_leads(tbl, group_id='cMCH0hxwGt')
Esempio n. 28
0
    def test_get_poll_location(self, m):

        m.get(self.gc.uri + 'voterinfo', json=voterinfo_resp)

        expected_tbl = Table(voterinfo_resp['pollingLocations'])

        tbl = self.gc.get_polling_location(2000, '900 N Washtenaw, Chicago, IL 60622')

        assert_matching_tables(tbl, expected_tbl)
Esempio n. 29
0
    def test_insert_records(self, m):

        m.post(self.base_uri, json=insert_responses)

        tbl = Table([{'Name': 'Another row!'}, {'Name': 'Another!'}])
        resp = self.at.insert_records(tbl)

        # Assert that row count is expected
        self.assertEqual(len(resp), 2)
Esempio n. 30
0
 def default_table(self):
     return Table([
         {
             'num': 1,
             'ltr': 'a'
         },
         {
             'num': 2,
             'ltr': 'b'
         },
     ])