mirror of
https://github.com/Ahrimdon/detailed-cod-tracker.git
synced 2025-01-18 00:14:59 -05:00
Merge branch 'develop'
This commit is contained in:
commit
4ef07f5ead
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,6 +1,8 @@
|
||||
__pycache__
|
||||
uninstall.*
|
||||
venv.*
|
||||
/cod_pics
|
||||
/venv
|
||||
/HTML
|
||||
/stats
|
||||
/userInfo.json
|
||||
/cookie*
|
@ -27,7 +27,7 @@ replacements = {
|
||||
"mp_m_speed": "Shoot House",
|
||||
"mp_farms2_gw": "Krovnik Farmland",
|
||||
"mp_port2_gw": "Port",
|
||||
"mp_crash": "Crash",
|
||||
"mp_crash2": "Crash",
|
||||
"mp_vacant": "Vacant",
|
||||
"mp_shipment": "Shipment",
|
||||
"mp_m_cargo": "Cargo",
|
||||
@ -41,10 +41,10 @@ replacements = {
|
||||
"mp_hideout": "Khandor Hideout",
|
||||
"loading_mp_hideout": "Khandor Hideout",
|
||||
"mp_aniyah_tac": "Aniyah Incursion",
|
||||
"mp_backlot": "Talsik Backlot",
|
||||
"mp_village": "Hovec Sawmill",
|
||||
"mp_backlot2": "Talsik Backlot",
|
||||
"mp_village2": "Hovec Sawmill",
|
||||
"mp_hardhat": "Hardhat",
|
||||
"mp_m_wallco": "Aisle 9",
|
||||
"mp_m_wallco2": "Aisle 9",
|
||||
"mp_donetsk": "Verdansk",
|
||||
"mp_scrapyard": "Zhokov Scrapyard",
|
||||
"mp_m_trench": "Trench",
|
||||
@ -58,7 +58,7 @@ replacements = {
|
||||
"mp_m_stadium": "Verdansk Stadium",
|
||||
"mp_malyshev": "Mialstor Tank Factory",
|
||||
"mp_malyshev_10v10": "Mialstor Tank Factory",
|
||||
"mp_broadcast": "Broadcast",
|
||||
"mp_broadcast2": "Broadcast",
|
||||
"mp_riverside_gw": "Verdansk Riverside",
|
||||
"mp_m_train": "Station",
|
||||
"mp_kstenod": "Verdansk (Night)",
|
||||
@ -288,6 +288,56 @@ replacements = {
|
||||
"super_supply_drop": "Loadout Drop", ### Unsure if this is Loadout Drop
|
||||
"super_tac_cover": "Deployable Cover",
|
||||
"super_support_box": "Stopping Power Rounds",
|
||||
#Extra
|
||||
"mp_stat": "Statistic",
|
||||
"session_start": "Session Start",
|
||||
"uno": "PC",
|
||||
"psn": "Playstation Network",
|
||||
"xbl": "Xbox Live",
|
||||
"mw": "Modern Warfare",
|
||||
"cw": "Cold War",
|
||||
# CW Maps
|
||||
"mp_cartel": "Cartel",
|
||||
"mp_tank": "Garrison",
|
||||
"mp_miami": "Miami",
|
||||
"mp_moscow": "Moscow",
|
||||
"mp_satellite": "Satellite",
|
||||
"mp_kgb": "Checkmate",
|
||||
"wz_forest": "Ruka",
|
||||
"wz_ski_slopes": "Alpine",
|
||||
"mp_nuketown6": "Nuketown '84",
|
||||
"mp_tundra": "Crossroads",
|
||||
"mp_black_sea": "Armada",
|
||||
"mp_mall": "The Pines",
|
||||
"mp_raid_rm": "Raid",
|
||||
"mp_sm_berlin_tunnel": "U-Bahn",
|
||||
"mp_sm_finance": "KGB",
|
||||
"mp_sm_game_show": "Game Show",
|
||||
"mp_sm_central": "ICBM",
|
||||
"wz_sanatorium": "Sanatorium",
|
||||
"nuketown6_holiday": "Nuketown '84 Holiday",
|
||||
"mp_express_rm": "Express",
|
||||
"mp_apocalypse": "Apocalypse",
|
||||
"mp_sm_market": "Mansion",
|
||||
"mp_miami_strike": "Miami Strike",
|
||||
"wz_golova": "Golova",
|
||||
"mp_cliffhanger": "Yamantau",
|
||||
"mp_sm_gas_station": "Diesel",
|
||||
"wz_duga": "Duga",
|
||||
"mp_village_rm": "Standoff",
|
||||
"mp_sm_amsterdam": "Amsterdam",
|
||||
"mp_dune": "Collateral",
|
||||
"mp_hijacked_rm": "Hijacked",
|
||||
"mp_paintball_rm": "Rush",
|
||||
"mp_sm_deptstore": "Showroom",
|
||||
"mp_slums_rm": "Slums",
|
||||
"mp_echelon": "Echelon",
|
||||
"mp_drivein_rm": "Drive In",
|
||||
"mp_zoo_rm": "Zoo",
|
||||
"mp_firebase": "Deprogram",
|
||||
"mp_amerika": "Amerika",
|
||||
"mp_sm_vault": "Gluboko",
|
||||
"mp_don4_pm": "Nuketown '84 Halloween"
|
||||
# Accolades
|
||||
# "accoladeData": "Accolades",
|
||||
# "classChanges": "Most classes changed (Evolver)",
|
||||
|
172
get_cod_stats.py
172
get_cod_stats.py
@ -125,6 +125,47 @@ def get_and_save_data(player_name=None, all_stats=False, season_loot=False, iden
|
||||
settings = api.Me.settings()
|
||||
save_to_file(settings, 'settings.json')
|
||||
|
||||
def display_menu():
|
||||
print("\nBeautify Options:")
|
||||
print("1) Beautify all data")
|
||||
print("2) Split matches into separate files")
|
||||
|
||||
# Options Requiring Player Name
|
||||
print("\nOptions Requiring Player Name:")
|
||||
print("3) Get all stats")
|
||||
print("4) Get identities")
|
||||
print("5) Get general information")
|
||||
print("6) Get friend feed")
|
||||
print("7) Get event feed")
|
||||
print("8) Get COD Point balance")
|
||||
print("9) Get connected accounts")
|
||||
print("10) Get account settings")
|
||||
|
||||
# Options Not Requiring Player Name
|
||||
print("\nOptions Not Requiring Player Name:")
|
||||
print("11) Get season loot")
|
||||
print("12) Get map list")
|
||||
|
||||
# Exit Option
|
||||
print("\n0) Exit")
|
||||
|
||||
choice = input("Enter your choice: ")
|
||||
return int(choice)
|
||||
|
||||
def beautify_feed_data(timezone='GMT'):
|
||||
for feed_file in ['friendFeed.json', 'eventFeed.json']:
|
||||
file_path = os.path.join(DIR_NAME, feed_file)
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
replace_time_and_duration_recursive(data, timezone)
|
||||
data = recursive_key_replace(data)
|
||||
with open(file_path, 'w') as file:
|
||||
json.dump(data, file, indent=4)
|
||||
print(f"Keys sorted and replaced in {file_path}.")
|
||||
else:
|
||||
print(f"{feed_file} does not exist, skipping.")
|
||||
|
||||
# Save results to a JSON file inside the stats directory
|
||||
def recursive_key_replace(obj):
|
||||
if isinstance(obj, dict):
|
||||
@ -143,7 +184,7 @@ def recursive_key_replace(obj):
|
||||
return replacements.get(obj, obj) if isinstance(obj, str) else obj
|
||||
|
||||
def clean_json_files(*filenames, dir_name='stats'):
|
||||
regex_pattern = r'<span class="|</span>|">|mp-stat-items|kills-value|headshots-value|username|game-mode|kdr-value'
|
||||
regex_pattern = r'<span class="|</span>|">|mp-stat-items|kills-value|headshots-value|username|game-mode|kdr-value|accuracy-value'
|
||||
replace = ''
|
||||
|
||||
for filename in filenames:
|
||||
@ -154,7 +195,7 @@ def clean_json_files(*filenames, dir_name='stats'):
|
||||
modified_content = re.sub(regex_pattern, replace, content)
|
||||
with open(file_path, 'w') as file:
|
||||
file.write(modified_content)
|
||||
print(f"Cleaned {filename}.")
|
||||
print(f"Removed unreadable strings from {filename}.")
|
||||
else:
|
||||
print(f"{filename} does not exist, skipping.")
|
||||
|
||||
@ -180,41 +221,60 @@ def sort_data(data):
|
||||
data[key] = sort_data(value)
|
||||
return data
|
||||
|
||||
def replace_time_and_duration_recursive(data):
|
||||
def replace_time_and_duration_recursive(data, timezone):
|
||||
"""
|
||||
Recursively replace epoch times for specific keys in a nested dictionary or list.
|
||||
"""
|
||||
|
||||
time_keys = ["timePlayedTotal", "timePlayed", "objTime", "time", "timeProne",
|
||||
"timeSpentAsPassenger", "timeSpentAsDriver", "timeOnPoint",
|
||||
"timeWatchingKillcams", "timeCrouched", "timesSelectedAsSquadLeader",
|
||||
"longestTimeSpentOnWeapon", "avgLifeTime", "percentTimeMoving"]
|
||||
date_keys = ["date", "updated", "originalDate"]
|
||||
|
||||
if isinstance(data, list):
|
||||
for item in data:
|
||||
replace_time_and_duration_recursive(item)
|
||||
|
||||
replace_time_and_duration_recursive(item, timezone)
|
||||
elif isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
if key in date_keys:
|
||||
data[key] = epoch_milli_to_human_readable(value, timezone)
|
||||
if key in time_keys:
|
||||
data[key] = convert_duration_seconds(value)
|
||||
|
||||
elif key == "utcStartSeconds":
|
||||
data[key] = epoch_to_human_readable(value)
|
||||
data[key] = epoch_to_human_readable(value, timezone)
|
||||
# For EST conversion:
|
||||
# data[key] = epoch_to_human_readable(value, "EST")
|
||||
|
||||
elif key == "utcEndSeconds":
|
||||
data[key] = epoch_to_human_readable(value)
|
||||
data[key] = epoch_to_human_readable(value, timezone)
|
||||
# For EST conversion:
|
||||
# data[key] = epoch_to_human_readable(value, "EST")
|
||||
|
||||
elif key == "duration":
|
||||
data[key] = convert_duration_milliseconds(value)
|
||||
|
||||
else:
|
||||
replace_time_and_duration_recursive(value)
|
||||
replace_time_and_duration_recursive(value, timezone)
|
||||
|
||||
def epoch_milli_to_human_readable(epoch_millis, timezone='GMT'):
|
||||
"""
|
||||
Convert epoch timestamp in milliseconds to a human-readable date-time string with timezone.
|
||||
"""
|
||||
if isinstance(epoch_millis, str):
|
||||
return epoch_millis # Already converted
|
||||
|
||||
dt_object = datetime.datetime.utcfromtimestamp(epoch_millis / 1000.0)
|
||||
if timezone == 'GMT':
|
||||
date_str = dt_object.strftime("GMT: %A, %B %d, %Y %I:%M:%S %p")
|
||||
elif timezone == 'EST':
|
||||
dt_object -= datetime.timedelta(hours=4) # Adjust for EST
|
||||
date_str = dt_object.strftime("EST: %A, %B %d, %Y %I:%M:%S %p")
|
||||
elif timezone == 'CST':
|
||||
dt_object -= datetime.timedelta(hours=5) # Adjust for EST
|
||||
date_str = dt_object.strftime("CST: %A, %B %d, %Y %I:%M:%S %p")
|
||||
elif timezone == 'PST':
|
||||
dt_object -= datetime.timedelta(hours=6) # Adjust for EST
|
||||
date_str = dt_object.strftime("PST: %A, %B %d, %Y %I:%M:%S %p")
|
||||
else:
|
||||
raise ValueError("Unsupported timezone.")
|
||||
return date_str
|
||||
def epoch_to_human_readable(epoch_timestamp, timezone='GMT'):
|
||||
if isinstance(epoch_timestamp, str):
|
||||
return epoch_timestamp # Already converted
|
||||
@ -225,6 +285,12 @@ def epoch_to_human_readable(epoch_timestamp, timezone='GMT'):
|
||||
elif timezone == 'EST':
|
||||
dt_object -= datetime.timedelta(hours=4) # Using 4 hours for EST conversion instead of 5?
|
||||
date_str = dt_object.strftime("EST: %A, %B %d, %Y %I:%M:%S %p")
|
||||
elif timezone == 'CST':
|
||||
dt_object -= datetime.timedelta(hours=5) # Using 4 hours for EST conversion instead of 5?
|
||||
date_str = dt_object.strftime("CST: %A, %B %d, %Y %I:%M:%S %p")
|
||||
elif timezone == 'PST':
|
||||
dt_object -= datetime.timedelta(hours=4) # Using 4 hours for EST conversion instead of 5?
|
||||
date_str = dt_object.strftime("PST: %A, %B %d, %Y %I:%M:%S %p")
|
||||
else:
|
||||
raise ValueError("Unsupported timezone.")
|
||||
return date_str
|
||||
@ -256,22 +322,22 @@ def convert_duration_seconds(seconds):
|
||||
|
||||
return f"{days} Days {hours} Hours {minutes} Minutes {seconds} Seconds"
|
||||
|
||||
def beautify_data():
|
||||
def beautify_data(timezone='GMT'):
|
||||
file_path = (os.path.join(DIR_NAME, 'stats.json'))
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
replace_time_and_duration_recursive(data)
|
||||
replace_time_and_duration_recursive(data, timezone)
|
||||
data = recursive_key_replace(data)
|
||||
data = sort_data(data)
|
||||
with open(file_path, 'w') as file:
|
||||
json.dump(data, file, indent=4)
|
||||
print(f"Keys sorted and replaced in {file_path}.")
|
||||
|
||||
def beautify_match_data():
|
||||
def beautify_match_data(timezone='GMT'):
|
||||
file_path = (os.path.join(DIR_NAME, 'match_info.json'))
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
replace_time_and_duration_recursive(data)
|
||||
replace_time_and_duration_recursive(data, timezone)
|
||||
data = recursive_key_replace(data)
|
||||
with open(file_path, 'w') as file:
|
||||
json.dump(data, file, indent=4)
|
||||
@ -299,7 +365,7 @@ def split_matches_into_files():
|
||||
isinstance(sample_match.get("duration"), int)):
|
||||
|
||||
print("Cleaning match data...")
|
||||
replace_time_and_duration_recursive(data)
|
||||
replace_time_and_duration_recursive(data, timezone)
|
||||
|
||||
# Save the cleaned data back to match_info.json
|
||||
with open(os.path.join(DIR_NAME, 'match_info.json'), 'w') as file:
|
||||
@ -332,12 +398,72 @@ def main():
|
||||
- Enter the value when prompted
|
||||
"""
|
||||
|
||||
# Check if the script is run without any additional command-line arguments
|
||||
if len(sys.argv) == 1:
|
||||
if os.path.exists(COOKIE_FILE):
|
||||
with open(COOKIE_FILE, 'r') as f:
|
||||
api_key = f.read().strip()
|
||||
else:
|
||||
api_key = input("Please enter your ACT_SSO_COOKIE: ")
|
||||
with open(COOKIE_FILE, 'w') as f:
|
||||
f.write(api_key)
|
||||
|
||||
api.login(api_key)
|
||||
|
||||
while True:
|
||||
choice = display_menu()
|
||||
|
||||
if choice in [3, 4, 5, 6, 7, 8, 9, 10, 11]:
|
||||
player_name = input("Please enter the player's username (with #1234567): ")
|
||||
if choice == 3:
|
||||
get_and_save_data(player_name=player_name, all_stats=True)
|
||||
if choice == 4:
|
||||
get_and_save_data(player_name=player_name, season_loot=True)
|
||||
elif choice == 5:
|
||||
get_and_save_data(player_name=player_name, identities=True)
|
||||
elif choice == 6:
|
||||
get_and_save_data(player_name=player_name, info=True)
|
||||
elif choice == 7:
|
||||
get_and_save_data(player_name=player_name, friendFeed=True)
|
||||
elif choice == 8:
|
||||
get_and_save_data(player_name=player_name, eventFeed=True)
|
||||
elif choice == 9:
|
||||
get_and_save_data(player_name=player_name, cod_points=True)
|
||||
elif choice == 10:
|
||||
get_and_save_data(player_name=player_name, connected_accounts=True)
|
||||
elif choice == 11:
|
||||
get_and_save_data(player_name=player_name, settings=True)
|
||||
|
||||
elif choice == 1:
|
||||
beautify_data()
|
||||
beautify_match_data()
|
||||
beautify_feed_data()
|
||||
clean_json_files('friendFeed.json', 'eventFeed.json')
|
||||
elif choice == 2:
|
||||
split_matches_into_files()
|
||||
|
||||
elif choice == 12:
|
||||
get_and_save_data(season_loot=True)
|
||||
elif choice == 13:
|
||||
get_and_save_data(maps=True)
|
||||
elif choice == 0:
|
||||
print("Exiting...")
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please try again.")
|
||||
continue
|
||||
break
|
||||
else:
|
||||
parser = argparse.ArgumentParser(description="Detailed Modern Warfare (2019) Statistics Tool", epilog=help_text, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
# Group related arguments
|
||||
group_default = parser.add_argument_group("Default Options")
|
||||
group_data = parser.add_argument_group("Data Fetching Options")
|
||||
group_cleaning = parser.add_argument_group("Data Cleaning Options")
|
||||
|
||||
# Add an argument for timezone
|
||||
group_default.add_argument("-tz", "--timezone", type=str, default="GMT", choices=["GMT", "EST", "CST", "PST"], help="Specify the timezone (GMT, EST, CST, PST)")
|
||||
|
||||
# Add arguments for Data Fetching Options
|
||||
group_data.add_argument("-p", "--player_name", type=str, help="Player's username (with #1234567)")
|
||||
group_data.add_argument("-a", "--all_stats", action="store_true", help="Fetch all the different types of stats data")
|
||||
@ -351,6 +477,7 @@ def main():
|
||||
group_data.add_argument("-ca", "--connected_accounts", action="store_true", help="Fetch only the map list data")
|
||||
group_data.add_argument("-s", "--settings", action="store_true", help="Fetch only your account settings")
|
||||
|
||||
|
||||
# Add arguments for Cleaning Options
|
||||
group_cleaning.add_argument("-c", "--clean", action="store_true", help="Beautify all data")
|
||||
group_cleaning.add_argument("-sm", "--split_matches", action="store_true", help="Split the matches into separate JSON files within the 'matches' subfolder")
|
||||
@ -374,12 +501,13 @@ def main():
|
||||
if args.split_matches:
|
||||
split_matches_into_files()
|
||||
elif args.clean_stats_data:
|
||||
beautify_data()
|
||||
beautify_data(timezone=args.timezone)
|
||||
elif args.clean_match_data:
|
||||
beautify_match_data()
|
||||
beautify_match_data(timezone=args.timezone)
|
||||
elif args.clean:
|
||||
beautify_data()
|
||||
beautify_match_data()
|
||||
beautify_data(timezone=args.timezone)
|
||||
beautify_match_data(timezone=args.timezone)
|
||||
beautify_feed_data(timezone=args.timezone)
|
||||
clean_json_files('friendFeed.json', 'eventFeed.json')
|
||||
elif args.clean_friend_feed:
|
||||
clean_json_files('friendFeed.json')
|
||||
|
Loading…
Reference in New Issue
Block a user