Пример #1
0
def get_mint_handle():
    mint_username = keyring.get_password("mintapi", "username")
    mint_password = keyring.get_password("mintapi", "password")
    mint = mintapi.Mint(
        mint_username,  # Email used to log in to Mint
        mint_password,  # Your password used to log in to mint

        # Optional parameters
        mfa_method='sms',  # Can be 'sms' (default), 'email', or 'soft-token'.
                           # if mintapi detects an MFA request, it will trigger the requested method
                           # and prompt on the command line.
        headless=False,  # Whether the chromedriver should work without opening a
                         # visible window (useful for server-side deployments)
        mfa_input_callback=None,  # A callback accepting a single argument (the prompt)
                                  # which returns the user-inputted 2FA code. By default
                                  # the default Python `input` function is used.
        session_path=None, # Directory that the Chrome persistent session will be written/read from.
                           # To avoid the 2FA code being asked for multiple times, you can either set
                           # this parameter or log in by hand in Chrome under the same user this runs
                           # as.
        imap_account=None, # account name used to log in to your IMAP server
        imap_password=None, # account password used to log in to your IMAP server
        imap_server=None,  # IMAP server host name
        imap_folder='INBOX',  # IMAP folder that receives MFA email
        wait_for_sync=False,  # do not wait for accounts to sync
        wait_for_sync_timeout=300,  # number of seconds to wait for sync
    )
    return mint
Пример #2
0
def run(arguments):
    mint = mintapi.Mint(getCredentials("mint")["email"],
                        getCredentials("mint")["password"],
                        mfa_method='sms',
                        headless=False)

    mint.get_accounts()

    # Get extended account detail at the expense of speed - requires an
    # additional API call for each account
    print(mint.get_accounts(True))

    # Get budget information
    print(mint.get_budgets())

    # Get transactions
    print(mint.get_transactions())  # as pandas dataframe
    print(
        mint.get_transactions_csv(include_investment=False))  # as raw csv data
    print(
        mint.get_transactions_json(include_investment=False,
                                   skip_duplicates=False))

    # Get net worth
    print(mint.get_net_worth())
Пример #3
0
 def __init__(self, email, password):
     self.email = email
     # TODO: I should def hash this password b/c this is unsafe
     self.password = password
     # TODO: do i even need this _cached_spending. yeah right?
     self._cached_spending = None
     self._connection = mintapi.Mint(self.email, self.password)
Пример #4
0
def connect(credentials, scraper_args=None):
    import mintapi
    mint = mintapi.Mint()
    scraper_args = dict(scraper_args or {})

    def try_login(scraper):
        scraper = MintTokenScraper(credentials=credentials, **scraper_args)
        scraper.login()
        mint.driver = scraper.driver
        mint.token = mint.get_token()

    with scrape_lib.temp_scraper(MintTokenScraper,
                                 credentials=credentials,
                                 **scraper_args) as scraper:
        okay = False
        try:
            try_login(scraper)
            okay = True
        except (TimeoutError, selenium.common.exceptions.TimeoutException):
            if not scraper_args.get('headless') and not scraper_args.get(
                    'login_timeout'):
                raise
            traceback.print_exc()
        if okay:
            yield mint
            return
    scraper_args['headless'] = True
    scraper_args['login_timeout'] = None
    logger.info('Retrying login interactively')
    with scrape_lib.temp_scraper(MintTokenScraper,
                                 credentials=credentials,
                                 **scraper_args) as scraper:
        try_login(scraper)
        yield mint
Пример #5
0
def code(doneBefore):
    if not doneBefore:
        mint = mintapi.Mint(pw.getUser(),
                            pw.getPass())  #, ius_session, thx_guid)
        filehandler = open('accounts.pi', 'w')
        pickle.dump(mint.get_accounts(), filehandler)
    getAccounts()
Пример #6
0
 def __init__(self, user, pwd):
     """Login to Mint and refresh account data"""
     print(
         "Connecting to Mint...\nIt may take a couple minutes to login and refresh all account data..."
     )
     self.mint = mintapi.Mint(user, pwd)
     print("Connected\n")
Пример #7
0
def download_transactions(month, use_local=False):
    cur_year, cur_mon = [int(val) for val in month.split('-')]

    if not use_local:
        mint_log.trace(f'downloading transactions for {cur_mon}/{cur_year}')
        mint = mintapi.Mint(email, password, headless=True, mfa_method='sms')
        mint.initiate_account_refresh()

        df = mint.get_transactions()
        df.to_json('mint_data.json')

    else:
        mint_log.trace(
            f'using local file to get transactions for {cur_mon}/{cur_year}')
        df = pd.read_json('mint_data.json')

    df['hash'] = df.apply(hash_row, axis=1)
    df['spent'] = df.apply(lambda x: x.amount * {
        'debit': 1,
        'credit': -1
    }[x.transaction_type],
                           axis=1)
    cur_df = df[df.apply(lambda x: filter_by_month(x, cur_mon, cur_year),
                         axis=1)].copy()
    cur_df['month'] = cur_mon
    cur_df = cur_df.apply(update_categories, axis=1)
    return cur_df
Пример #8
0
def update_pickle_from_mint(pickle_name):
    with open('no_checkin.txt') as f:
        user = f.readline().strip()
        psw = f.readline().strip()
    mint = mintapi.Mint(user, psw)
    df = mint.get_transactions()
    df.to_pickle(pickle_name)
def get_transactions(mint_username, mint_password):
    """
    Grab transactions from mintapi and create transaction objects.
    BUT - don't load them into database.
    """

    # NOTE - for demo use secrets file for authentication instead of keyring

    # mint_password = keyring.get_password("system", mint_username)
    mint = mintapi.Mint(mint_username, mint_password)
    user_transactions = mint.get_transactions()

    categories = {}
    transaction_obj_list = []

    for i in range(len(user_transactions.index)):
        date = str(user_transactions["date"][i])[:10]
        date = datetime.datetime.strptime(date, "%Y-%m-%d")
        description = user_transactions["description"][i]
        category = user_transactions["category"][i]
        categories[str(category).strip()] = 0
        amount = float(user_transactions["amount"][i])
        transaction_obj = Transaction(transaction_id=i,
                                      date=date,
                                      description=description,
                                      category=category,
                                      amount=amount)
        transaction_obj_list.append(transaction_obj)

    categories_to_remove = set([
        "paycheck", "bills & utilities", "business services", "transfer",
        "federal tax", "credit card payment", "nan", "financial", "income",
        "interest income", "pharmacy", "check", "atm fee", "doctor",
        "air travel", "hotel", "gas & fuel"
    ])

    for category in categories.keys():
        if category in categories_to_remove:
            del categories[category]

    for transaction_obj in transaction_obj_list:
        category = transaction_obj.category
        if category in categories.keys():
            categories[category] += transaction_obj.amount

    categories_to_group = {
        "entertainment": ("amusement", "entertainment", "music", "sports"),
        "restaurants": ("restaurants", "food & dining", "fast food"),
        "shopping": ("clothing", "books", "electronics & software", "hair"),
        "taxi": ("taxi", "rental car & taxi")
    }

    for category in categories_to_group.keys():
        for grouped_category in categories_to_group[category]:
            if grouped_category != category:
                categories[category] += categories[grouped_category]
                del categories[grouped_category]

    return categories
Пример #10
0
 def __init__(self, username, password):
     self.mint = mintapi.Mint(username, password)
     #print("Refreshing mint accounts...")
     #self.mint.initiate_account_refresh()
     #self.refresh_accounts()
     #print("Accounts refreshed!")
     self.accounts = self.mint.get_accounts()
     mintapi.print_accounts(self.accounts)
Пример #11
0
def loadMintAPIs(filename):
    apis = {}
    creds_file = open(filename)
    for line in creds_file:
        creds = json.loads(line)
        print "Loading Mint API for: " + creds['email']
        apis[creds['email']] = mintapi.Mint(creds['email'], creds['password'])
    return apis
Пример #12
0
 def transactions(self, fromdate=None):
     time.sleep(self.__sync_delay)
     self.__sync_delay = 0
     m = mintapi.Mint(self.__username, self.__password)
     time.sleep(
         20 + 10 * random.random()
     )  # Give mint.com time to synchronise transactions with banks
     yield from self.__remap(m.get_transactions_json())
Пример #13
0
def main():
    print("Creating mint api connection")
    mint = mintapi.Mint(**mintParams())
    parse_and_write_transactions(mint.get_transactions_csv())
    mintdf, sheetsdf = tempTransactions(), sheetsTransactions()
    #pp.pprint(mintdf)
    #pp.pprint(sheetsdf)
    insertNewData(mintdf, sheetsdf, len(sheetsdf) + 2)
    update_col_format(SPREADSHEET, SHEET)
Пример #14
0
def get_mint_driver() -> mintapi.Mint:
    mint = mintapi.Mint()
    mint.driver = mintapi.signIn._create_web_driver_at_mint_com(
        HEADLESS,
        SESSION_PATH,
        USE_CHROMEDRIVER_ON_PATH,
    )
    yield mint
    mint.close()
Пример #15
0
def mintapi_instance(username, password):
    try:
        mint = mintapi.Mint(username, password)
        # mint.token = mintapi_token(username, password)
        return mint
    except Exception, e:
        if 'login' in str(e):
            raise InvalidCredentials(str(e))
        else:
            raise
Пример #16
0
def main():
    '''Main'''
    if CONFIG['mintuser'] and CONFIG['mintpassword']:
        mint = mintapi.Mint(CONFIG['mintuser'], CONFIG['mintpassword'])
        for bank in mint.get_accounts():
            print(
                "Display Name: %(fiLoginDisplayName)s\n"
                "Account Name: %(accountName)s\n"
                "Bank Name: %(fiName)s\n"
                "Id: %(id)s\n"
                "Balance: %(value)s\n") % bank
Пример #17
0
 def __init__(self):
     """ Connects to Mintapi and assigns variables for methods used
     more than once. """
     self.config = config.load()
     self.mintapi = mintapi.Mint(self.config['mint']['user'],
                                 self.config['mint']['pass'],
                                 self.config['mint']['ius_session'],
                                 self.config['mint']['thx_guid'])
     self.mintapi.initiate_account_refresh()
     self.transactionsDF = self.transactionsDF()
     self.lastMonthDF = self.last_month_transactions()
Пример #18
0
def get_transactions():
    """
    Returns a pd.DataFrame of deduplicated transactions stored in
    your Mint account
    """
    email = os.environ['EMAIL']
    password = os.environ['PASSWORD']
    mint = mintapi.Mint(email, password)
    mint.initiate_account_refresh()
    transactions = mint.get_transactions()
    transactions.drop_duplicates(['date', 'original_description', 'amount'],
                                 inplace=True)
    return transactions
Пример #19
0
def download_detailed_transactions(month, use_local=False, refresh_acct=True):
    cur_year, cur_mon = [int(val) for val in month.split('-')]

    start_date = f'{cur_mon}/01/{str(cur_year)[2:]}'
    print(start_date)

    if not use_local:
        mint_log.trace(f'downloading transactions for {cur_mon}/{cur_year}')
        mint = mintapi.Mint(email, password, headless=True, mfa_method='sms')
        if refresh_acct:
            mint_log.trace('Refreshing account')
            mint.initiate_account_refresh()

        df = mint.get_detailed_transactions(start_date=start_date,
                                            include_investment=False)

        df['transaction_type'] = df['isDebit'].map(lambda x: 'debit'
                                                   if x else 'credit')
        df = df[[
            'odate', 'mmerchant', 'omerchant', 'amount', 'transaction_type',
            'mcategory', 'account', 'labels', 'note'
        ]]
        df.columns = [
            'date', 'description', 'original_description', 'amount',
            'transaction_type', 'category', 'account_name', 'labels', 'notes'
        ]
        mint_log.trace(f'Saving data to file at {mint_data_path}')
        df.to_json(mint_data_path)

    else:
        mint_log.trace(
            f'Ising local transactions for {cur_mon}/{cur_year} from file at {mint_data_path}'
        )
        df = pd.read_json(mint_data_path)

    df['hash'] = df.apply(hash_row, axis=1)
    df['category'] = df['category'].map(lambda x: x.lower())

    df['spent'] = df['amount']
    cur_df = df[df.apply(lambda x: filter_by_month(x, cur_mon, cur_year),
                         axis=1)].copy()
    cur_df['month'] = cur_mon
    cur_df = cur_df.apply(update_categories, axis=1)
    mint_log.info(f'DataFrame created with {len(df)} records.')
    return cur_df[[
        'date', 'description', 'original_description', 'amount',
        'transaction_type', 'category', 'account_name', 'labels', 'notes',
        'hash', 'spent', 'month', 'm_category'
    ]]
Пример #20
0
def get_netWorth(intent, session):
    card_title = "Net Worth"
    ius_session = '0D729971F8014D46AFAE78535BA5EE5F'
    thx_guid = 'ECA2581CD1B3B676'
    thx_guid = 'GA1.2.869397484.1508601630'
    mint = mintapi.Mint(pw.getUser(), pw.getPass(), ius_session, thx_guid)
    net_Worth = mint.get_net_worth()

    should_end_session = True
    speech_output = "Your net worth is " + str(
        net_Worth) + " Dollars . Have a great one big boi."
    # return build_response({}, build_speechlet_response(
    #     card_title, speech_output, None, should_end_session))
    print('hello')
    print(speech_output)
Пример #21
0
def _LogIntoMint(creds: Credentials, options: ScraperOptions,
                 chromedriver_download_path: Text,
                 cookies: List[Text]) -> mintapi.Mint:
    """Logs into mint and retrieves an active connection.

    Args:
      creds: The credentials for the account to log into.
      options: Options for how to connect.
      chromedriver_download_path: Location of chromedriver.
      cookies: Cookies to attach to the session, when provided.

    Returns:
      The mint connection object.
    """
    if os.getenv('CHROME_SESSION_PATH'):
        session_path = os.getenv('CHROME_SESSION_PATH')
    else:
        session_path = os.path.join(os.getcwd(), '.mintapi', 'session')
    if os.getenv('MFA_TOKEN'):
        mfa_method = 'soft-token'
        mfa_token = os.getenv('MFA_TOKEN')
    else:
        mfa_method = 'text'
        mfa_token = None

    mint = mintapi.Mint(
        creds.email,
        creds.mintPassword,
        chromedriver_download_path=chromedriver_download_path,
        headless=not options.showBrowser,
        imap_account=creds.email,
        imap_folder='Inbox',
        imap_password=creds.emailPassword,
        imap_server=_GLOBAL_CONFIG.IMAP_SERVER,
        mfa_input_callback=None,
        mfa_method=mfa_method,
        mfa_token=mfa_token,
        session_path=session_path,
        wait_for_sync=_GLOBAL_CONFIG.WAIT_FOR_ACCOUNT_SYNC,
    )
    time.sleep(5)

    # Load cookies if provided. These are cookies only for mint.com domain.
    [mint.driver.add_cookie(cookie) for cookie in cookies]
    return mint
Пример #22
0
def do_notifications(notifications):
    '''
        Format all the notifications from get_notifications into subject
        and message
    '''
    billstring = []
    if not notifications:
        return False
    weektotal = 0
    for bill in notifications:
        bill['duedate'] = stylish_date(bill['duedate'])
        weektotal += int(bill['amount'])
        billstring.append(
            "%(name)s, %(description)s, Due: $%(amount)s on %(duedate)s" %
            bill)
    billstring = '\n'.join(billstring)
    curtime = datetime.datetime.now()
    message = ("Upcoming Bills:\n%(billstring)s\n\n"
               "Total amount of bills due: $%(weektotal)s\n") % {
                   'billstring': billstring,
                   'weektotal': weektotal,
               }
    if CONFIG['mintuser'] and CONFIG['mintpassword']:
        mint = mintapi.Mint(CONFIG['mintuser'], CONFIG['mintpassword'])
        bankinfo = mint.get_accounts()
        avaliable_balance = 0
        for bank in bankinfo:
            if int(bank['id']) == int(CONFIG['mintaccountid']):
                avaliable_balance = int(bank['value'])
        secondmessage = (
            "Expected balance at end of week: $%(remaining_balance)s\n"
            "Current Balance: $%(avaliable_balance)s") % {
                'remaining_balance': avaliable_balance - weektotal,
                'avaliable_balance': avaliable_balance,
            }
        message = message + secondmessage
    subject = 'Bills for the upcoming week of %(month)s %(day)s' % {
        'month': calendar.month_name[curtime.month],
        'day': stylish_date(curtime.day),
    }
    send_email(subject, message)
Пример #23
0
def main():
    print('Starting splitter')

    user = keyring.get_password(KEYRING_SERVICE, "user")
    password = ''

    # if credentials already saved in keyring...
    if user:
        password = keyring.get_password(KEYRING_SERVICE, "password")
    else:
        user, password = cli.save_credentials(KEYRING_SERVICE)

    print("Logging in...")
    try:
        mint = mintapi.Mint(email=user,
                            password=password,
                            mfa_method='sms',
                            headless=True,
                            session_path=path.abspath("./session"),
                            wait_for_sync=False)
    except:
        print(
            "Failed to login to mint/open new chrome session. Ensure your current chrome session is closed"
        )
        exit()

    print("Logged in!")

    splitter = mint_splitter.Splitter(mint)
    accounts = splitter.get_filtered_accounts()

    selected_accounts = cli.get_selected_accounts(accounts)

    splitter.split_transactions(selected_accounts,
                                start_date=cli.get_start_date())

    mint.close()
    sys.exit()
Пример #24
0
    def _init_mint(self) -> mintapi.Mint:
        config = {}
        with open('config/mint.json') as f:
            config = json.load(f)

        self.__mint = mintapi.Mint(
            bitarray(config['u']).tobytes().decode('utf8'),
            bitarray(config['p']).tobytes().decode('utf8'),
            # Optional parameters
            mfa_method=
            'sms',  # Can be 'sms' (default), 'email', or 'soft-token'.
            # if mintapi detects an MFA request, it will trigger the requested method
            # and prompt on the command line.
            headless=
            True,  # Whether the chromedriver should work without opening a
            # visible window (useful for server-side deployments)
            mfa_input_callback=
            None,  # A callback accepting a single argument (the prompt)
            # which returns the user-inputted 2FA code. By default
            # the default Python `input` function is used.
            session_path=
            './session',  # Directory that the Chrome persistent session will be written/read from.
            # To avoid the 2FA code being asked for multiple times, you can either set
            # this parameter or log in by hand in Chrome under the same user this runs
            # as.
            imap_account=None,  # account name used to log in to your IMAP server
            imap_password=
            None,  # account password used to log in to your IMAP server
            imap_server=None,  # IMAP server host name
            imap_folder='INBOX',  # IMAP folder that receives MFA email
            wait_for_sync=False,  # do not wait for accounts to sync
            wait_for_sync_timeout=300,  # number of seconds to wait for sync
            use_chromedriver_on_path=
            False,  # True will use a system provided chromedriver binary that
            # is on the PATH (instead of downloading the latest version)
        )
        return self.__mint
Пример #25
0
def get_mint_account():
    u = urllib.unquote(request.args.get('u'))
    p = urllib.unquote(request.args.get('p'))
    mint = mintapi.Mint(u, p)
    all_accounts = mint.get_accounts()

    # Need to sanitize python dates to be able to
    # convert to JSON

    for account in all_accounts:
        if 'closeDateInDate' in account:
            account['closeDateInDate'] = int(
                time.mktime(account['closeDateInDate'].timetuple()))
        if 'lastUpdatedInDate' in account:
            account['lastUpdatedInDate'] = int(
                time.mktime(account['lastUpdatedInDate'].timetuple()))
        if 'addAccountDateInDate' in account:
            account['addAccountDateInDate'] = int(
                time.mktime(account['addAccountDateInDate'].timetuple()))
        if 'fiLastUpdatedInDate' in account:
            account['fiLastUpdatedInDate'] = int(
                time.mktime(account['fiLastUpdatedInDate'].timetuple()))

    return simplejson.dumps(all_accounts)
Пример #26
0
 def test_get_transactions(self, mocked_get_transactions):
     mocked_get_transactions.return_value = transactions_example
     mint = mintapi.Mint()
     transactions_df = mint.get_transactions()
     assert (isinstance(transactions_df, pd.DataFrame))
Пример #27
0
import mintapi
import datetime
import pandas as pd
mint = mintapi.Mint('username', 'password')

## Get transactions
transaction=mint.get_transactions() # as pandas dataframe
transaction.describe()
transaction.head()

debit_transaction = transaction[transaction['transaction_type'] == 'debit']
debit_transaction['Date'] =pd.to_datetime(debit_transaction['date'])
debit_transaction['Month']=debit_transaction['Date'].dt.month
debit_transaction['Year'] =debit_transaction['Date'].dt.year

filter_list = ['bills & utilities','mobile phone','internet','utilities','television']
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Bills & utilities'
filter_list = ['coffee shops', 'fast food', 'food & dining','restaurants']
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Eating Out'
filter_list = ['charity','gifts & donations','kids activities' ]
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Charity'
filter_list = ['movies & dvds', 'hotel','air travel', 'travel', 'amusement', 'sports' , 'sporting goods','entertainment' ]
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Travel & Amusement'
filter_list = ['parking', 'rental car & taxi','auto & transport','public transportation','service & parts','auto insurance']
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Transport'
filter_list = ['groceries','shopping','books','clothing','gift','home improvement','newspapers & magazines','electronics & software','office supplies']
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Groceries & shopping'
filter_list = ['shipping','personal care','hair','home services','tuition']
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Business services'
filter_list = ['doctor','pharmacy']
debit_transaction.loc[debit_transaction.category.isin(filter_list),'category']='Health'
Пример #28
0
# Run this file to force account data refresh

import mintapi
import config
import time

mint = mintapi.Mint(config.mint_email, config.mint_pass)
mint.initiate_account_refresh()
Пример #29
0
            currentMonth = int(t.month)
            if currentMonth == examinedMonth:
                budget = budget - float(stringAmount)

        if (thisDate.strftime("%Y-%m-%d") == str(t)):
            if k < limit:
                listToAppendTo.append(fullString)
                if (transactions[k]['category'] == 'income'):
                    totalAmountEarnt = totalAmountEarnt + float(stringAmount)
                else:
                    totalAmountBought = totalAmountBought + float(stringAmount)
        k += 1

else:
    budget = 985
    mint = mintapi.Mint(email, password, ius_session, thx_guid)

    transactions = mint.get_transactions_json()

    for i in transactions:
        i = json.dumps(i)
        t = datetime.datetime.today()
        t = (t + datetime.timedelta(days=timeChange)).date()
        thisDate = dt.strptime(transactions[k]['date'], '%b %d').date()
        thisDate = thisDate.replace(year=datetime.datetime.today().year)
        stringDate = thisDate.strftime("%Y-%m-%d")
        stringCategory = unicodedata.normalize(
            'NFKD', transactions[k]['mmerchant']).encode('ascii', 'ignore')
        stringAmount = unicodedata.normalize('NFKD',
                                             transactions[k]['amount']).encode(
                                                 'ascii', 'ignore')
Пример #30
0
def main():
    args = parse_args()
    home = os.path.expanduser("~")
    default_session_path = os.path.join(home, '.mintapi', 'session')

    try:
        password = keyring.get_password('mintapi', args.username)
    except:
        print(
            "keyring couldn't get saved password, please manually enter the password"
        )
        password = getpass.getpass("Mint password: "******"Saving the password to keyring")
        keyring.set_password('mintapi', args.username, password)

    # init Mint
    print("Logging into Mint")
    mint = mintapi.Mint(
        args.username,
        password,
        mfa_method='sms',
        headless=False,
        session_path=
        default_session_path,  # saved session to avoid multiple MFA requests
        wait_for_sync=True,
        wait_for_sync_timeout=300,  # number of seconds to wait for sync
        use_chromedriver_on_path=
        True,  # Use chromedriver on PATH since Debian Chromium is behind the latest release
    )

    # Get Transactions
    print("Obtaining transactions")
    df = get_transactions(mint)

    # Filter by date, if applicable
    if args.start_date:
        print(f"Filtering transactions that occured before {args.start_date}")
        df = df[df["date"] >= args.start_date].copy()
    if args.end_date:
        print(f"Fitering transactions that occured after   {args.end_date}")
        df = df[df["date"] <= args.end_date].copy()

    # Capitalize first character in category
    df["category"] = df["category"].str.title()

    # Check unmapped categories
    unmapped = []
    for category in pd.unique(df.category):
        if category not in MAPPING:
            unmapped.append(category)
            print(f"{category:>25s} is unmapped")
    print(f"Found a total of {len(unmapped)} unmapped categories")
    proceed = input(
        "Proceed with logging? (y/n/print) ")  # TODO: make more user friendly

    if proceed == "print":
        for category in unmapped:
            sample_df = df.query(f"category == '{category}'")[[
                "date", "description", "original_description", "category",
                "transaction_type"
            ]]
            sample_df = sample_df.sample(n=min(5, len(sample_df)))
            print(f"Category: {category:<25}")
            print(sample_df.to_markdown())
            _ = input("Press ENTER for next category ")
            print("\n---\n")
        proceed = input("Proceed with logging? (y/n/CTRL-C) ")

    if proceed == "y":
        # Get logged transactions
        print(
            "Obtaining already logged transactions to avoid repeating transactions"
        )
        start_date = df.date.min()
        end_date = df.date.max()
        transactions = get_moneylover_log(start_date,
                                          end_date,
                                          wallet=args.wallet)
        # Log transactions in Money Lover
        print("Start logging transactions into Money Lover")
        for _, row in df.iterrows():
            log_transaction(row, args.wallet, transactions)
    else:
        print("Ending program")