def update_day(file_name, cards): attempts = 0 while attempts < 2: # Try to re-download corrupted data twice before giving up # Strip the file of tabs try: for line in fileinput.input('AH-Data/' + file_name, inplace=True): print(line.replace('\t', ''), end='') except FileExistsError: if config_handler.getter('verboseFlag'): print("File exists error: " + file_name + ". Most likely pops up because the file is bugged/empty.") attempts = 3 with open("AH-Data/" + file_name) as f: csv_f = csv.reader(f) # row[0]=card name, row[1]=rarity, row[2]=currency, row[3]=price, row[4]=date # Rarity values: 0-->Equipment, 2-->Common, 3-->Uncommon, 4-->Rare, 5-->AA, 6-->Legendary try: for row in csv_f: key = (row[0], row[1], row[2]) if key in cards: cards[key].append((row[3], row[4])) else: cards[key] = [(row[3], row[4])] attempts = 3 # No errors - we can go out of the loop except (IndexError, _csv.Error): # Erases and re-downloads corrupted files if config_handler.getter('verboseFlag'): print('Re-downloaded', file_name, 'due to an error') download_data.replace_day(file_name) attempts += 1
def update(force=False): try: last_check = datetime.datetime.strptime(config_handler.getter('cardInfoCheck'), '%Y-%m-%d') except ValueError: last_check = datetime.datetime.min # If string is malformed or empty we want to perform the download try: if last_check < datetime.datetime.today() - datetime.timedelta(days=1) or force: # Don't spam cwik's API with requests r = requests.post('http://hexdbapi2.hexsales.net/v1/objects/search') if config_handler.getter('verboseFlag'): print("Updated card info from cwik' API") with open('Card Info/card_info.json', 'w') as fp: json.dump(r.json(), fp, indent=4, sort_keys=True) config_handler.setter('cardInfoCheck', datetime.datetime.today().strftime('%Y-%m-%d')) # update date of last download store_colors() # Update files store_rarity() store_guid_to_names() store_set() with open('Card Info/guid_to_names.json') as fp: # Update guid_to_names with new values simple_guid_to_names.guid_dict = json.load(fp) if config_handler.getter('verboseFlag'): print('Updated colors, rarity, guid and set information') except Exception: if config_handler.getter('verboseFlag'): print("Could not update card info from cwik's API")
def short_term_median(): longTermMedians = long_term_median() # In case we don't have enough short term sales cards = open_ah_data() # dict with (card, rarity, currency) tuples as keys and a list of (sale, date) as value cardValues = {} for key in cards: listOfSales = cards[key] # It's still a pointer to the same list recentSales = [] for i in range(1, len(listOfSales)): # Start at 1, because list[-0] gives the first element and we want the last # if sale of the date is in the last 2 weeks if datetime.datetime.strptime(listOfSales[-i][1], '%Y-%m-%d') < datetime.datetime.today() - datetime.timedelta(days=15): break # Moves on to the next key else: try: recentSales.append(literal_eval(listOfSales[-i][0])) except IndentationError: if config_handler.getter('verboseFlag'): print('Problem with short term median literal_eval of', listOfSales[i][0]) numberOfSales = len(recentSales) if numberOfSales >= 5: median = statistics.median(recentSales) else: median = longTermMedians[key][0] cardValues[key] = (median, numberOfSales) return cardValues
def store_set(): os.makedirs('Card Info', exist_ok=True) if not os.path.isfile('Card Info/card_info.json'): update(force=True) try: with card_info_lock: with open('Card Info/card_info.json', 'rt') as fp: cardInfo = json.load(fp) except: if config_handler.getter('verboseFlag'): print('Card_info.json error') cardSet = {} translation = {'001': 'Shards of Fate', '002': 'Shattered Destiny', '003': 'Armies of Myth', '004': 'Primal Dawn', 'PVE001': 'Frost Ring Arena', 'PVE002': 'Chest Loot' } for cardDict in cardInfo: if cardDict['set_number'] not in non_cards: #Ignoring non-cards if cardDict['rarity'] not in ('Non-Collectible', ''): #Ignoring AAs, champions and non-collectible cards try: traslatedSet = translation[cardDict['set_number']] except KeyError: traslatedSet = cardDict['set_number'] cardSet[cardDict['name']] = traslatedSet with open('Card Info/simple_set.json', 'wt') as fp: json.dump(cardSet, fp, indent=4, sort_keys=True)
def store_rarity(): os.makedirs('Card Info', exist_ok=True) if not os.path.isfile('Card Info/card_info.json'): update(force=True) try: with card_info_lock: with open('Card Info/card_info.json', 'rt') as fp: cardInfo = json.load(fp) except: if config_handler.getter('verboseFlag'): print('Card_info.json error') # if not response["error"] and response["status"] == 200: # cardInfo = response["data"] # else: # if config_handler.getter('verboseFlag'): # print("Data not downloaded correctly") cardRarity = {} for cardDict in cardInfo: if cardDict['set_number'] not in non_cards: #Ignoring non-cards if cardDict['rarity'] not in ('Non-Collectible', ''): #Ignoring AAs, champions and non-collectible cards cardRarity[cardDict['name']] = cardDict['rarity'] with open('Card Info/simple_rarity.json', 'wt') as fp: json.dump(cardRarity, fp, indent=4, sort_keys=True)
def store_guid_to_names(): os.makedirs('Card Info', exist_ok=True) if not os.path.isfile('Card Info/card_info.json'): update(force=True) try: with card_info_lock: with open('Card Info/card_info.json', 'rt') as fp: cardInfo = json.load(fp) card_names = {} for cardDict in cardInfo: if cardDict['set_number'] not in non_cards: # Ignoring non-cards if cardDict['rarity'] not in ('Non-Collectible', '') or cardDict['name'] in ( 'Ruby Shard', 'Diamond Shard', 'Sapphire Shard', 'Wild Shard', 'Blood Shard'): # Ignoring champions and non-collectible cards card_names[cardDict['uuid']] = cardDict['name'] except Exception: if config_handler.getter('verboseFlag'): print('Card_info.json error') with open('Card Info/guid_to_names.json', 'wt') as fp: json.dump(card_names, fp, indent=4, sort_keys=True)
def collection_update(jsonDict): PATH = 'Collection/My_Collection.json' if jsonDict['Action'] == 'Overwrite': myCollection = {} if jsonDict['Action'] == 'Update': myCollection = collection_open(PATH) for card in jsonDict['Complete']: name = card_info_handler.simple_guid_to_names(card['Guid']['m_Guid']) number = card['Count'] myCollection[name] = number for card in jsonDict['CardsAdded']: name = card_info_handler.simple_guid_to_names(card['Guid']['m_Guid']) number = card['Count'] if name in myCollection: myCollection[name] += number else: myCollection[name] = number for card in jsonDict['CardsRemoved']: try: name = card_info_handler.simple_guid_to_names(card['Guid']['m_Guid']) if name in myCollection: myCollection[name] -= number except Exception: if config_handler.getter('verboseFlag'): print("Tried to remove a card that was not in the collection (most likely") raise with collectionLock: with open(PATH, 'wt') as fp: json.dump(myCollection, fp, indent=4, sort_keys=True)
def run_with_except_hook(*args, **kw): try: run_old(*args, **kw) except (KeyboardInterrupt, SystemExit): raise except: sys.excepthook(*sys.exc_info()) if config_handler.getter('verboseFlag'): raise
def collection_open(path): myCollection = {} try: with collectionLock: with open(path, 'rt') as fp: cards = json.load(fp) for card in cards: if card in myCollection: myCollection[card] += 1 else: myCollection[card] = cards[card] except (FileNotFoundError, ValueError): if path != 'Collection/My_Drafted_Cards.json': if config_handler.getter('verboseFlag'): print("Please update your collection by relogging to Hex") return myCollection
def simple_guid_to_names(guid): if not os.path.isfile('Card Info/guid_to_names.json'): store_guid_to_names() # This avoids reloading the dictionary on every function call if not hasattr(simple_guid_to_names, "guid_dict"): with open('Card Info/guid_to_names.json') as fp: simple_guid_to_names.guid_dict = json.load(fp) name = 'Unknown card' try: name = simple_guid_to_names.guid_dict[guid] except KeyError: if config_handler.getter('verboseFlag'): print('Card with the following Guid could not be found: ' + guid) if guid != '00000000-0000-0000-0000-000000000000': # Random guid that doesn't correspond to anything update() return name
def open_ah_data(): if not os.path.isfile('Card Info/database.json'): update() try: with open('Card Info/database.json', 'rt') as fp: cards = json.load(fp) except: if config_handler.getter('verboseFlag'): print('Problem while loading database.json') # Converting keys to tuples # Without creating a temporary dict, "for key in cards" would run for the new keys too newCards = {} for key in cards: newCards[literal_eval(key)] = cards[key] cards = newCards return cards
def update(): download_data.main() # Downloads the latest AH-Data days = date_generator() cards = {} for formatted_date in days: try: update_day(formatted_date, cards) # Only works because all instances modify the same card dictionary except FileNotFoundError: if config_handler.getter('verboseFlag'): print('Could not find ' + formatted_date) # Convert keys to strings, so that json can recognize them # Could also use 'w' instead of 'wt' since opening in text mode is the default os.makedirs('Card Info', exist_ok=True) with open('Card Info/database.json', 'wt') as fp: json.dump({str(key): value for key, value in cards.items()}, fp, indent=4, sort_keys=True)
def long_term_median(): cards = open_ah_data() # dict with (card, rarity, currency) tuples as keys and a list of (price, date) as value cardValues = {} for key in cards: try: listOfSales = cards[key] # It's still a pointer to the same list sales = [] for sale in listOfSales: sales.append(literal_eval(sale[0])) numberOfSales = len(sales) except IndentationError: if config_handler.getter('verboseFlag'): print('Problem with long term median of', key) try: median = statistics.median(sales) except statistics.StatisticsError: median = 0 cardValues[key] = (median, numberOfSales) return cardValues
def export_collection(): collection = collection_open('Collection/My_Collection.json') colors = card_info_handler.simple_colors() sets = card_info_handler.simple_set() rarities = card_info_handler.simple_rarity() prices = AH_data_handler.open_simple_median() with open("Collection/My_Collection.csv", 'wt')as fp: writer = csv.writer(fp, delimiter=';') writer.writerow(['Card', 'Price', 'Shard', 'Set', 'Rarity', 'Number Owned']) for key in collection: if key not in ('Blood Shard', 'Diamond Shard', 'Sapphire Shard', 'Ruby Shard', 'Wild Shard'): try: price = prices[key.replace(',', '')] # AH Data has no commas except: price = 0 try: writer.writerow([key, price, colors[key], sets[key], rarities[key], collection[key]]) except KeyError: if config_handler.getter('verboseFlag'): print(key, " not found")
def my_data_parsing(self, jsonDict): # Write data to log #if jsonDict['Message'] not in ('PlayerUpdated', 'CardUpdated') or self.verboseFlag: if config_handler.getter('API_logging'): with open(APIHandler.store_path, 'a') as fp: fp.write(datetime.datetime.now().strftime('%H:%M:%S') + '\t' + str(jsonDict) + '\n') myMessage = ('', '') # First DraftPack for the last 20 minutes if jsonDict['Message'] == 'DraftPack' and len(jsonDict['Cards']) == 17 and (time.time() - APIHandler.startTime) >= 1500: threading.Thread(target=self.play_sound, args=('Sounds/short_ringtone.wav',)).start() APIHandler.startTime = time.time() gui_queue.put(('Reset', 'Reset draft value')) if jsonDict['Message'] == 'GameStarted': threading.Thread(target=self.play_sound, args=('Sounds/game_start.wav',)).start() if jsonDict['Message'] == 'DraftPack': # jsonDict['Cards'] is a list of card-dictionaries. # Each card-dictionary has the Guid, Flags and Gems keys draftPack = [card_info_handler.simple_guid_to_names(x['Guid']['m_Guid']) for x in jsonDict['Cards']] myMessage = ('DraftPack', draftPack) gui_queue.put(myMessage) elif jsonDict['Message'] == 'DraftCardPicked': card_picked = card_info_handler.simple_guid_to_names(jsonDict['Card']['Guid']['m_Guid']) myMessage = ('CardPicked', card_picked ) gui_queue.put(myMessage) #threading.Thread(target=collection_handler.update_draft_value, args =(ahData[jsonDict[2][0]],)).start() # Needs updating to new format # collection_handler.draft_card_picked('Collection/My_Drafted_Cards.json', jsonDict['Card']) elif jsonDict['Message'] == 'Collection': if not os.path.isfile('Collection/My_Collection.json') and jsonDict['Action'] == 'Overwrite': gui_queue.put(('Update', 'Collection updated')) with APIHandler.collectionLock: collection_handler.collection_update(jsonDict)