Exemplo n.º 1
0
    def update_stock_prices_to_trend(self, api):
        ''' This function updates all prices in the user's stock to TREND. '''
        uploadable_json = []
        if os.path.isfile(PRICE_CHANGES_FILE):
            if PyMkmHelper.prompt_bool(
                    "Found existing changes. Upload [y] or discard [n]?"
            ) == True:
                with open(PRICE_CHANGES_FILE, 'r') as changes:
                    uploadable_json = json.load(changes)
            else:
                os.remove(PRICE_CHANGES_FILE)
                self.update_stock_prices_to_trend(api=self.api)

        else:
            uploadable_json = self.calculate_new_prices_for_stock(api=self.api)

        if len(uploadable_json) > 0:

            self.display_price_changes_table(uploadable_json)

            if PyMkmHelper.prompt_bool(
                    "Do you want to update these prices?") == True:
                # Update articles on MKM
                api.set_stock(uploadable_json)
                print('Prices updated.')
            else:
                with open(PRICE_CHANGES_FILE, 'w') as outfile:
                    json.dump(uploadable_json, outfile)
                print('Prices not updated. Changes saved.')
        else:
            print('No prices to update.')
Exemplo n.º 2
0
    def get_price_for_product(self, product_id, rarity, is_foil, language_id=1, undercut_local_market=False, api=None):
        r = api.get_product(product_id)
        rounding_limit = self.get_rounding_limit_for_rarity(rarity)

        if not is_foil:
            trend_price = r['product']['priceGuide']['TREND']
        else:
            trend_price = r['product']['priceGuide']['TRENDFOIL']

        # Set competitive price for region
        if undercut_local_market:
            table_data_local, table_data = self.get_competition(
                api, product_id, is_foil)

            if len(table_data_local) > 0:
                # Undercut if there is local competition
                lowest_in_country = PyMkmHelper.round_down_to_limit(rounding_limit,
                                                                    PyMkmHelper.calculate_lowest(table_data_local, 4))
                new_price = max(rounding_limit, min(
                    trend_price, lowest_in_country - rounding_limit))
            else:
                # No competition in our country, set price a bit higher.
                new_price = PyMkmHelper.round_up_to_limit(
                    rounding_limit, trend_price * 1.2)
        else:
            new_price = PyMkmHelper.round_up_to_limit(
                rounding_limit, trend_price)

        if new_price == None:
            raise ValueError('No price found!')
        else:
            return new_price
Exemplo n.º 3
0
    def list_competition_for_product(self, api):
        self.report("list competition for product")

        search_string = PyMkmHelper.prompt_string('Search card name')
        is_foil = PyMkmHelper.prompt_bool("Foil?")

        result = api.find_product(search_string, **{
            # 'exact ': 'true',
            'idGame': 1,
            'idLanguage': 1,
            # TODO: Add Partial Content support
            # TODO: Add language support
        })

        if (result):
            products = result['product']

            stock_list_products = [x['idProduct']
                                   for x in self.get_stock_as_array(api=self.api)]
            products = [x for x in products if x['idProduct']
                        in stock_list_products]

            if len(products) == 0:
                print('No matching cards in stock.')
            else:
                if len(products) > 1:
                    product = self.select_from_list_of_products(
                        [i for i in products if i['categoryName'] == 'Magic Single'])
                elif len(products) == 1:
                    product = products[0]

                self.show_competition_for_product(
                    product['idProduct'], product['enName'], is_foil, api=self.api)
        else:
            print('No results found.')
Exemplo n.º 4
0
    def get_price_for_product(self,
                              product_id,
                              rarity,
                              is_foil,
                              is_playset,
                              language_id=1,
                              undercut_local_market=False,
                              api=None):
        try:
            response = api.get_product(product_id)
        except Exception as err:
            print('No response from API.')
            sys.exit(0)
        else:
            rounding_limit = self.get_rounding_limit_for_rarity(rarity)

            if response:
                if not is_foil:
                    trend_price = response['product']['priceGuide']['TREND']
                else:
                    trend_price = response['product']['priceGuide'][
                        'TRENDFOIL']

                # Set competitive price for region
                if undercut_local_market and not is_playset:  # FIXME: add support for playsets?
                    table_data_local, table_data = self.get_competition(
                        api, product_id, is_foil)

                    if len(table_data_local) > 0:
                        # Undercut if there is local competition
                        lowest_in_country = PyMkmHelper.round_down_to_limit(
                            rounding_limit,
                            PyMkmHelper.calculate_lowest(table_data_local, 4))
                        new_price = max(
                            rounding_limit,
                            min(trend_price,
                                lowest_in_country - rounding_limit))
                    else:
                        # No competition in our country, set price a bit higher.
                        new_price = PyMkmHelper.round_up_to_limit(
                            rounding_limit, trend_price * 1.2)
                else:
                    new_price = PyMkmHelper.round_up_to_limit(
                        rounding_limit, trend_price)

                if new_price == None:
                    raise ValueError('No price found!')
                else:
                    if is_playset:
                        new_price = 4 * new_price
                    return new_price
            else:
                print('No results.')
Exemplo n.º 5
0
    def start(self):
        while True:
            menu_items = [
                "Update stock prices", "Update price for a card",
                "List competition for a card",
                "Show top 20 expensive items in stock", "Show account info",
                "Clear entire stock (WARNING)", "Import stock from .\list.csv"
            ]
            self.print_menu(menu_items, f"PyMKM {__version__}")

            choice = input("Action number: ")

            try:
                if choice == "1":
                    self.update_stock_prices_to_trend(api=self.api)

                elif choice == "2":
                    search_string = PyMkmHelper.prompt_string(
                        'Search card name')
                    self.update_product_to_trend(search_string, api=self.api)

                elif choice == "3":
                    is_foil = False
                    search_string = PyMkmHelper.prompt_string(
                        'Search card name')
                    if PyMkmHelper.prompt_bool("Foil?") == True:
                        is_foil = True

                    self.list_competition_for_product(search_string,
                                                      is_foil,
                                                      api=self.api)

                elif choice == "4":
                    self.show_top_expensive_articles_in_stock(20, api=self.api)

                elif choice == "5":
                    self.show_account_info(api=self.api)

                elif choice == "6":
                    self.clear_entire_stock(api=self.api)

                elif choice == "7":
                    self.import_from_csv(api=self.api)

                elif choice == "0":
                    break
                else:
                    print("Not a valid choice, try again.")
            except ConnectionError as err:
                print(err)
Exemplo n.º 6
0
 def print_product_top_list(self, title_string, table_data, sort_column, rows):
     print(70*'-')
     print('{} \n'.format(title_string))
     print(tb.tabulate(sorted(table_data, key=lambda x: x[sort_column], reverse=False)[:rows],
                       headers=['Username', 'Country',
                                'Condition', 'Count', 'Price'],
                       tablefmt="simple"))
     print(70*'-')
     print('Total average price: {}, Total median price: {}, Total # of articles: {}\n'.format(
         str(PyMkmHelper.calculate_average(table_data, 3, 4)),
         str(PyMkmHelper.calculate_median(table_data, 3, 4)),
         str(len(table_data))
     )
     )
Exemplo n.º 7
0
    def update_product_to_trend(self, api):
        ''' This function updates one product in the user's stock to TREND. '''
        self.report("update product price to trend")

        search_string = PyMkmHelper.prompt_string('Search product name')

        try:
            articles = api.find_stock_article(search_string, 1)
        except Exception as err:
            print(err)

        if len(articles) > 1:
            article = self.select_from_list_of_articles(articles)
        else:
            article = articles[0]
            found_string = f"Found: {article['product']['enName']}"
            if article['product'].get('expansion'):
                found_string += f"[{article['product'].get('expansion')}]."
            else:
                found_string += '.'
            print(found_string)

        undercut_local_market = PyMkmHelper.prompt_bool(
            'Try to undercut local market? (slower, more requests)')

        r = self.get_article_with_updated_price(article,
                                                undercut_local_market,
                                                api=self.api)

        if r:
            self.draw_price_changes_table([r])

            print('\nTotal price difference: {}.'.format(
                str(
                    round(
                        sum(item['price_diff'] * item['count']
                            for item in [r]), 2))))

            if PyMkmHelper.prompt_bool(
                    "Do you want to update these prices?") == True:
                # Update articles on MKM
                print('Updating prices...')
                api.set_stock([r])
                print('Price updated.')
            else:
                print('Prices not updated.')
        else:
            print('No prices to update.')
Exemplo n.º 8
0
    def update_product_to_trend(self, search_string, api):
        ''' This function updates one product in the user's stock to TREND. '''

        try:
            articles = api.find_stock_article(search_string, 1)
        except Exception as err:
            print(err)

        if len(articles) > 1:
            article = self.select_from_list_of_articles(articles)
        else:
            article = articles[0]
            print('Found: {} [{}].'.format(article['product']['enName'],
                                           article['product']['expansion']))
        r = self.get_price_for_single_article(article, api=self.api)

        if r:
            self.draw_price_changes_table([r])

            print('\nTotal price difference: {}.'.format(
                str(
                    round(
                        sum(item['price_diff'] * item['count']
                            for item in [r]), 2))))

            if PyMkmHelper.prompt_bool(
                    "Do you want to update these prices?") == True:
                # Update articles on MKM
                api.set_stock([r])
                print('Price updated.')
            else:
                print('Prices not updated.')
        else:
            print('No prices to update.')
Exemplo n.º 9
0
    def get_foil_price(self, api, product_id, language_id):
        # NOTE: This is a rough algorithm, designed to be safe and not to sell aggressively.
        # 1) See filter parameters below.
        # 2) Set price to lowest + (median - lowest / 4), rounded to closest quarter Euro.
        # 3) Undercut price in own country if not contradicting 2)
        # 4) Never go below 0.25 for foils

        account = api.get_account()['account']
        articles = api.get_articles(
            product_id, **{
                'isFoil': 'true',
                'isAltered': 'false',
                'isSigned': 'false',
                'minCondition': 'GD',
                'idLanguage': language_id
            })

        keys = ['idArticle', 'count', 'price', 'condition', 'seller']
        foil_articles = [{x: y
                          for x, y in art.items() if x in keys}
                         for art in articles]
        local_articles = []
        for article in foil_articles:
            if article['seller']['address']['country'] == account[
                    'country'] and article['seller']['username'] != account[
                        'username']:
                local_articles.append(article)

        local_table_data = []
        for article in local_articles:
            if article:
                local_table_data.append([
                    article['seller']['username'],
                    article['seller']['address']['country'],
                    article['condition'], article['count'], article['price']
                ])

        table_data = []
        for article in foil_articles:
            if article:
                table_data.append([
                    article['seller']['username'],
                    article['seller']['address']['country'],
                    article['condition'], article['count'], article['price']
                ])

        median_price = PyMkmHelper.calculate_median(table_data, 3, 4)
        lowest_price = PyMkmHelper.calculate_lowest(table_data, 4)
        median_guided = PyMkmHelper.round_up_to_quarter(
            lowest_price + (median_price - lowest_price) / 4)

        if len(local_table_data) > 0:
            # Undercut if there is local competition
            lowest_in_country = PyMkmHelper.round_down_to_quarter(
                PyMkmHelper.calculate_lowest(local_table_data, 4))
            return max(0.25, min(median_guided, lowest_in_country - 0.25))
        else:
            # No competition in our country, set price a bit higher.
            return PyMkmHelper.round_up_to_quarter(median_guided * 1.2)
Exemplo n.º 10
0
    def update_stock_prices_to_trend(self, api):
        ''' This function updates all prices in the user's stock to TREND. '''
        self.report("update stock price to trend")

        undercut_local_market = PyMkmHelper.prompt_bool(
            'Try to undercut local market? (slower, more requests)')

        uploadable_json = self.calculate_new_prices_for_stock(
            undercut_local_market, api=self.api)

        if len(uploadable_json) > 0:

            self.display_price_changes_table(uploadable_json)

            if PyMkmHelper.prompt_bool("Do you want to update these prices?") == True:
                # Update articles on MKM
                api.set_stock(uploadable_json)
                print('Prices updated.')
            else:
                print('Prices not updated.')
        else:
            print('No prices to update.')
Exemplo n.º 11
0
    def clear_entire_stock(self, api):
        self.report("clear entire stock")

        stock_list = self.get_stock_as_array(api=self.api)
        if PyMkmHelper.prompt_bool("Do you REALLY want to clear your entire stock ({} items)?".format(len(stock_list))) == True:

            # for article in stock_list:
                # article['count'] = 0
            delete_list = [{'count': x['count'], 'idArticle': x['idArticle']}
                           for x in stock_list]

            api.delete_stock(delete_list)
            print('Stock cleared.')
        else:
            print('Aborted.')
Exemplo n.º 12
0
 def setUp(self):
     self.helper = PyMkmHelper()
Exemplo n.º 13
0
class TestPyMkmHelperFunctions(unittest.TestCase):
    def setUp(self):
        self.helper = PyMkmHelper()

    def test_calculate_average(self):
        table = [['Yxskaft', 'SE', 'NM', 1, 1.21],
                 ['Frazze11', 'SE', 'NM', 3, 1.3],
                 ['andli826', 'SE', 'NM', 2, 1.82]]
        self.assertEqual(self.helper.calculate_average(table, 3, 4), 1.46)

    def test_calculate_median(self):
        table = [['Yxskaft', 'SE', 'NM', 1, 1.21],
                 ['Frazze11', 'SE', 'NM', 3, 1.3],
                 ['andli826', 'SE', 'NM', 2, 1.82]]
        self.assertEqual(self.helper.calculate_median(table, 3, 4), 1.3)
        self.assertEqual(self.helper.calculate_average(table, 3, 4), 1.46)

    def test_calculate_lowest(self):
        table = [['Yxskaft', 'SE', 'NM', 1, 1.21],
                 ['Frazze11', 'SE', 'NM', 3, 1.3],
                 ['andli826', 'SE', 'NM', 2, 1.82]]
        self.assertEqual(self.helper.calculate_lowest(table, 4), 1.21)

    def test_round_up_to_limit(self):
        self.assertEqual(self.helper.round_up_to_limit(0.25, 0.99), 1)
        self.assertEqual(self.helper.round_up_to_limit(0.25, 0), 0)
        self.assertEqual(self.helper.round_up_to_limit(0.25, 0.1), 0.25)

        self.assertEqual(self.helper.round_up_to_limit(0.1, 0.99), 1)
        self.assertEqual(self.helper.round_up_to_limit(0.01, 0.011), 0.02)
        self.assertEqual(self.helper.round_up_to_limit(0.01, 1), 1)
        self.assertEqual(self.helper.round_up_to_limit(1, 0.1), 1)

    def test_round_down_to_limit(self):
        self.assertEqual(self.helper.round_down_to_limit(0.25, 0.99), 0.75)
        self.assertEqual(self.helper.round_down_to_limit(0.25, 1.01), 1)
        self.assertEqual(self.helper.round_down_to_limit(0.25, 0.1), 0)

        self.assertEqual(self.helper.round_down_to_limit(0.1, 0.99), 0.9)
        self.assertEqual(self.helper.round_down_to_limit(0.01, 0.011), 0.01)
        self.assertEqual(self.helper.round_down_to_limit(0.01, 1), 1)
        self.assertEqual(self.helper.round_down_to_limit(1, 0.1), 0)

    @patch('sys.stdout', new_callable=io.StringIO)
    @patch('builtins.input', side_effect=['y', 'n', 'p', 'n'])
    def test_prompt_bool(self, mock_input, mock_stdout):
        self.assertTrue(self.helper.prompt_bool('test_y'))
        self.assertFalse(self.helper.prompt_bool('test_n'))
        self.helper.prompt_bool('test_error')
        self.assertRegex(mock_stdout.getvalue(),
                         r'\nPlease answer with y\/n\n')

    @patch('builtins.input', side_effect=['y'])
    def test_prompt_string(self, mock_input):
        self.assertEqual(self.helper.prompt_string('test'), 'y')
Exemplo n.º 14
0
    def find_deals_from_user(self, api):
        self.report("find deals from user")

        search_string = PyMkmHelper.prompt_string('Enter username')

        try:
            result = api.find_user_articles(search_string)
        except NoResultsError as err:
            print(err.mkm_msg())
        else:

            if (result):
                filtered_articles = [
                    x for x in result
                    if x.get('condition') in PyMkmApi.conditions[:3]
                ]  # EX+
                sorted_articles = sorted(result,
                                         key=lambda x: x['price'],
                                         reverse=True)
                print(
                    f"User '{search_string}' has {len(sorted_articles)} articles in stock."
                )
                num_searches = int(
                    PyMkmHelper.prompt_string(
                        f'Searching top X expensive cards (EX+) for deals, choose X (1-{len(sorted_articles)})'
                    ))
                if num_searches > 1 and num_searches <= len(sorted_articles):
                    table_data = []

                    index = 0
                    bar = progressbar.ProgressBar(max_value=num_searches)
                    for article in sorted_articles[:num_searches]:
                        p = api.get_product(article['idProduct'])
                        name = p['product']['enName']
                        expansion = p['product']['expansion']['enName']
                        condition = article.get('condition')
                        language = article['language']['languageName']
                        foil = article['isFoil']
                        price = float(article['price'])
                        if foil:
                            market_price = p['product']['priceGuide'][
                                'TRENDFOIL']
                        else:
                            market_price = p['product']['priceGuide']['TREND']
                        price_diff = price - market_price
                        if price_diff < 0:
                            table_data.append([
                                name, expansion, condition, language,
                                u'\u2713' if foil else '', price, market_price,
                                price_diff
                            ])
                        index += 1
                        bar.update(index)
                    bar.finish()

                    if table_data:
                        print('Found some interesting prices:')
                        print(
                            tb.tabulate(sorted(table_data,
                                               key=lambda x: x[5],
                                               reverse=True),
                                        headers=[
                                            'Name', 'Expansion', 'Condition',
                                            'Language', 'Foil?', 'Price',
                                            'Market price', 'Market diff'
                                        ],
                                        tablefmt="simple"))
                    else:
                        print('Found no deals. :(')
                else:
                    print("Invalid number.")
            else:
                print('No results found.')
Exemplo n.º 15
0
    def find_deals_from_user(self, api):
        self.report("find deals from user")

        search_string = PyMkmHelper.prompt_string('Enter username')

        try:
            result = api.find_user_articles(search_string)
        except NoResultsError as err:
            print(err.mkm_msg())
        else:

            if (result):
                # [x for x in result if x.get('condition') in PyMkmApi.conditions[:3]]  # EX+
                filtered_articles = result
                # price > 1
                filtered_articles = [x for x in result if x.get('price') > 1]

                sorted_articles = sorted(filtered_articles,
                                         key=lambda x: x['price'],
                                         reverse=True)
                print(
                    f"User '{search_string}' has {len(sorted_articles)} articles that meet the criteria."
                )
                num_searches = int(
                    PyMkmHelper.prompt_string(
                        f'Searching top X expensive cards for deals, choose X (1-{len(sorted_articles)})'
                    ))
                if num_searches > 1 and num_searches <= len(sorted_articles):
                    table_data = []

                    index = 0
                    bar = progressbar.ProgressBar(max_value=num_searches)
                    for article in sorted_articles[:num_searches]:
                        condition = article.get('condition')
                        language = article.get('language').get('languageName')
                        foil = article.get('isFoil')
                        playset = article.get('isPlayset')
                        price = float(article['price'])

                        p = api.get_product(article['idProduct'])
                        name = p['product']['enName']
                        expansion = p['product'].get('expansion')
                        if expansion:
                            expansion_name = expansion.get('enName')
                        else:
                            expansion_name = 'N/A'
                        if foil:
                            market_price = p['product']['priceGuide'][
                                'TRENDFOIL']
                        else:
                            market_price = p['product']['priceGuide']['TREND']
                        price_diff = price - market_price
                        percent_deal = round(-100 *
                                             (price_diff / market_price))
                        if price_diff < -1 or percent_deal >= 10:
                            table_data.append([
                                name, expansion_name, condition, language,
                                u'\u2713' if foil else '',
                                u'\u2713' if playset else '', price,
                                market_price, price_diff, percent_deal
                            ])

                        index += 1
                        bar.update(index)
                    bar.finish()

                    if table_data:
                        print('Found some interesting prices:')
                        print(
                            tb.tabulate(sorted(table_data,
                                               key=lambda x: x[9],
                                               reverse=True),
                                        headers=[
                                            'Name', 'Expansion', 'Condition',
                                            'Language', 'Foil', 'Playset',
                                            'Price', 'Market price',
                                            'Market diff', 'Deal %'
                                        ],
                                        tablefmt="simple"))
                    else:
                        print('Found no deals. :(')
                else:
                    print("Invalid number.")
            else:
                print('No results found.')