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 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
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)
def get_polling_locations(self, election_id, table, address_field='address'): """ Get polling location information for a table of addresses. `Args:` election_id: int A valid election id. Election ids can be found by running the :meth:`get_elections` method. address: str A valid US address in a single string. address_field: str The name of the column where the address is stored. `Returns:` Parsons Table See :ref:`parsons-table` for output options. """ polling_locations = [] # Iterate through the rows of the table for row in table: loc = self.get_polling_location(election_id, row[address_field]) # Insert original passed address loc[0]['passed_address'] = row[address_field] # Add to list of lists polling_locations.append(loc[0]) # Unpack values tbl = Table(polling_locations) tbl.unpack_dict('address', prepend_value='polling') tbl.unpack_list('sources', replace=True) tbl.unpack_dict('sources_0', prepend_value='source') tbl.rename_column('polling_line1', 'polling_address') # Resort columns tbl.move_column('pollingHours', len(tbl.columns)) tbl.move_column('notes', len(tbl.columns)) tbl.move_column('polling_locationName', 1) tbl.move_column('polling_address', 2) return tbl
def get_campaigns(self, state=None, zip=None, include_generic=False, include_private=False, include_content=True): """ Returns a list of campaigns `Args:` state: str Filter by US postal abbreviation for a state or territory e.g., "CA" "NY" or "DC" zip: int Filter by 5 digit zip code include_generic: boolean When filtering by state or ZIP code, include unrestricted campaigns include_private: boolean If true, will include private campaigns in results include_content: boolean If true, include campaign content fields, which may vary. This may cause sync errors. `Returns:` Parsons Table See :ref:`parsons-table` for output options. """ url = self.uri + 'campaigns' args = { 'state': state, 'zip': zip, 'includeGeneric': str(include_generic), 'includePrivate': str(include_private) } tbl = Table(self._request(url, args=args).json()) tbl.unpack_dict('updated_at') if include_content: tbl.unpack_dict('content') return tbl
def process_json(self, json_blob, obj_type, tidy=False): # Internal method for converting most types of json responses into a list of Parsons tables # Output goes here table_list = [] # Original table & columns obj_table = Table(json_blob) cols = obj_table.get_columns_type_stats() list_cols = [x['name'] for x in cols if 'list' in x['type']] dict_cols = [x['name'] for x in cols if 'dict' in x['type']] # Unpack all list columns if len(list_cols) > 0: for l in list_cols: # noqa E741 # Check for nested data list_rows = obj_table.select_rows( lambda row: isinstance(row[l], list) and any(isinstance(x, dict) for x in row[l]) ) # Add separate long table for each column with nested data if list_rows.num_rows > 0: logger.debug(l, 'is a nested column') if len([x for x in cols if x['name'] == l]) == 1: table_list.append({ 'name': f'{obj_type}_{l}', 'tbl': obj_table.long_table(['id'], l) }) else: # Ignore if column doesn't exist (or has multiples) continue else: if tidy is False: logger.debug(l, 'is a normal list column') obj_table.unpack_list(l) # Unpack all dict columns if len(dict_cols) > 0 and tidy is False: for d in dict_cols: logger.debug(d, 'is a dict column') obj_table.unpack_dict(d) if tidy is not False: packed_cols = list_cols + dict_cols for p in packed_cols: if p in obj_table.columns: logger.debug(p, 'needs to be unpacked into rows') # Determine whether or not to expand based on tidy unpacked_tidy = obj_table.unpack_nested_columns_as_rows(p, expand_original=tidy) # Check if column was removed as sign it was unpacked into separate table if p not in obj_table.columns: table_list.append({ 'name': f'{obj_type}_{p}', 'tbl': unpacked_tidy }) else: obj_table = unpacked_tidy # Original table will have had all nested columns removed if len(obj_table.columns) > 1: table_list.append({'name': obj_type, 'tbl': obj_table}) return table_list