664 lines
28 KiB
Python
664 lines
28 KiB
Python
import re
|
|
import sys
|
|
import json
|
|
import os
|
|
import argparse
|
|
from cod_api import API, platforms
|
|
import asyncio
|
|
import datetime
|
|
|
|
# Configure asyncio for Windows
|
|
if os.name == 'nt':
|
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
|
|
# Constants
|
|
COOKIE_FILE = 'cookie.txt'
|
|
STATS_DIR = 'stats'
|
|
MATCH_DIR = 'matches'
|
|
REPLACEMENTS_FILE = 'data/replacements.json'
|
|
TIMEZONE_OPTIONS = ["GMT", "EST", "CST", "PST"]
|
|
|
|
# Initialize API
|
|
api = API()
|
|
|
|
class CodStatsManager:
|
|
"""Main class to manage COD API interactions and data processing."""
|
|
|
|
def __init__(self):
|
|
self._ensure_directories_exist()
|
|
self.replacements = self._load_replacements()
|
|
self.api_key = self._get_api_key()
|
|
api.login(self.api_key)
|
|
|
|
def _ensure_directories_exist(self):
|
|
"""Ensure necessary directories exist."""
|
|
if not os.path.exists(STATS_DIR):
|
|
os.makedirs(STATS_DIR)
|
|
match_dir_path = os.path.join(STATS_DIR, MATCH_DIR)
|
|
if not os.path.exists(match_dir_path):
|
|
os.makedirs(match_dir_path)
|
|
|
|
def _load_replacements(self):
|
|
"""Load replacements from the JSON file."""
|
|
# First, handle running as PyInstaller executable
|
|
if getattr(sys, 'frozen', False):
|
|
# If running as bundle (frozen)
|
|
bundle_dir = sys._MEIPASS
|
|
replacements_path = os.path.join(bundle_dir, 'data', 'replacements.json')
|
|
else:
|
|
# If running as a normal Python script
|
|
replacements_path = REPLACEMENTS_FILE
|
|
|
|
if not os.path.exists(replacements_path):
|
|
raise FileNotFoundError(f"{replacements_path} not found. Ensure it exists in the script's directory.")
|
|
|
|
with open(replacements_path, 'r') as file:
|
|
return json.load(file)
|
|
|
|
def _get_api_key(self):
|
|
"""Get API key from file or user input."""
|
|
if os.path.exists(COOKIE_FILE):
|
|
with open(COOKIE_FILE, 'r') as f:
|
|
return f.read().strip()
|
|
else:
|
|
api_key = input("Please enter your ACT_SSO_COOKIE: ")
|
|
with open(COOKIE_FILE, 'w') as f:
|
|
f.write(api_key)
|
|
return api_key
|
|
|
|
def save_to_file(self, data, filename):
|
|
"""Save data to a JSON file."""
|
|
file_path = os.path.join(STATS_DIR, filename)
|
|
with open(file_path, 'w') as json_file:
|
|
json.dump(data, json_file, indent=4)
|
|
print(f"Data saved to {file_path}")
|
|
|
|
def get_player_name(self):
|
|
"""Get player name from user input."""
|
|
return input("Please enter the player's username (with #1234567): ")
|
|
|
|
def fetch_data(self, player_name=None, **options):
|
|
"""Fetch data based on specified options."""
|
|
if not player_name:
|
|
player_name = self.get_player_name()
|
|
|
|
# If no specific option is selected, fetch basic stats
|
|
if not any(options.values()):
|
|
self._fetch_basic_stats(player_name)
|
|
return
|
|
|
|
# If all_stats option is selected, fetch everything
|
|
if options.get('all_stats'):
|
|
self._fetch_all_stats(player_name)
|
|
return
|
|
|
|
# Otherwise, fetch only requested data
|
|
self._fetch_specific_data(player_name, options)
|
|
|
|
def _fetch_basic_stats(self, player_name):
|
|
"""Fetch basic player stats and match history."""
|
|
player_stats = api.ModernWarfare.fullData(platforms.Activision, player_name)
|
|
match_info = api.ModernWarfare.combatHistory(platforms.Activision, player_name)
|
|
self.save_to_file(player_stats, 'stats.json')
|
|
self.save_to_file(match_info, 'match_info.json')
|
|
|
|
def _fetch_all_stats(self, player_name):
|
|
"""Fetch all available stats for a player."""
|
|
# Basic stats
|
|
player_stats = api.ModernWarfare.fullData(platforms.Activision, player_name)
|
|
match_info = api.ModernWarfare.combatHistory(platforms.Activision, player_name)
|
|
season_loot_data = api.ModernWarfare.seasonLoot(platforms.Activision, player_name)
|
|
|
|
# Player-specific data
|
|
identities_data = api.Me.loggedInIdentities()
|
|
map_list = api.ModernWarfare.mapList(platforms.Activision)
|
|
|
|
# Save basic data
|
|
self.save_to_file(player_stats, 'stats.json')
|
|
self.save_to_file(match_info, 'match_info.json')
|
|
self.save_to_file(season_loot_data, 'season_loot.json')
|
|
self.save_to_file(map_list, 'map_list.json')
|
|
self.save_to_file(identities_data, 'identities.json')
|
|
|
|
# Check if userInfo.json exists to determine if we should fetch additional data
|
|
user_info_file = os.path.join('userInfo.json')
|
|
if os.path.exists(user_info_file):
|
|
# Additional user data
|
|
info = api.Me.info()
|
|
friend_feed = api.Me.friendFeed()
|
|
event_feed = api.Me.eventFeed()
|
|
cod_points = api.Me.codPoints()
|
|
connected_accounts = api.Me.connectedAccounts()
|
|
settings = api.Me.settings()
|
|
|
|
# Save additional data
|
|
self.save_to_file(info, 'info.json')
|
|
self.save_to_file(friend_feed, 'friendFeed.json')
|
|
self.save_to_file(event_feed, 'eventFeed.json')
|
|
self.save_to_file(cod_points, 'cp.json')
|
|
self.save_to_file(connected_accounts, 'connectedAccounts.json')
|
|
|
|
def _fetch_specific_data(self, player_name, options):
|
|
"""Fetch specific data based on provided options."""
|
|
endpoints = {
|
|
'season_loot': (api.ModernWarfare.seasonLoot, [platforms.Activision, player_name], 'season_loot.json'),
|
|
'identities': (api.Me.loggedInIdentities, [], 'identities.json'),
|
|
'maps': (api.ModernWarfare.mapList, [platforms.Activision], 'map_list.json'),
|
|
'info': (api.Me.info, [], 'info.json'),
|
|
'friendFeed': (api.Me.friendFeed, [], 'friendFeed.json'),
|
|
'eventFeed': (api.Me.eventFeed, [], 'eventFeed.json'),
|
|
'cod_points': (api.Me.codPoints, [], 'cp.json'),
|
|
'connected_accounts': (api.Me.connectedAccounts, [], 'connectedAccounts.json'),
|
|
'settings': (api.Me.settings, [], 'settings.json')
|
|
}
|
|
|
|
for option, value in options.items():
|
|
if value and option in endpoints:
|
|
func, args, filename = endpoints[option]
|
|
data = func(*args)
|
|
self.save_to_file(data, filename)
|
|
|
|
def beautify_all_data(self, timezone='GMT'):
|
|
"""Beautify all data files."""
|
|
self.beautify_stats_data(timezone)
|
|
self.beautify_match_data(timezone)
|
|
self.beautify_feed_data(timezone)
|
|
self.clean_json_files('friendFeed.json', 'eventFeed.json')
|
|
print("All data beautified successfully.")
|
|
|
|
def beautify_stats_data(self, timezone='GMT'):
|
|
"""Beautify stats data."""
|
|
file_path = os.path.join(STATS_DIR, 'stats.json')
|
|
if not os.path.exists(file_path):
|
|
print(f"File {file_path} not found. Skipping beautification.")
|
|
return
|
|
|
|
with open(file_path, 'r') as file:
|
|
data = json.load(file)
|
|
|
|
# Convert times and durations
|
|
self._replace_time_and_duration_recursive(data, timezone)
|
|
|
|
# Replace keys with more readable names
|
|
data = self._recursive_key_replace(data)
|
|
|
|
# Sort data by relevant metrics
|
|
data = self._sort_data(data)
|
|
|
|
# Save modified 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(self, timezone='GMT'):
|
|
"""Beautify match data."""
|
|
file_path = os.path.join(STATS_DIR, 'match_info.json')
|
|
if not os.path.exists(file_path):
|
|
print(f"File {file_path} not found. Skipping beautification.")
|
|
return
|
|
|
|
with open(file_path, 'r') as file:
|
|
data = json.load(file)
|
|
|
|
# Convert times and durations
|
|
self._replace_time_and_duration_recursive(data, timezone)
|
|
|
|
# Replace keys with more readable names
|
|
data = self._recursive_key_replace(data)
|
|
|
|
# Save modified data
|
|
with open(file_path, 'w') as file:
|
|
json.dump(data, file, indent=4)
|
|
|
|
print(f"Keys replaced in {file_path}.")
|
|
|
|
def beautify_feed_data(self, timezone='GMT'):
|
|
"""Beautify feed data files."""
|
|
for feed_file in ['friendFeed.json', 'eventFeed.json']:
|
|
file_path = os.path.join(STATS_DIR, feed_file)
|
|
if not os.path.exists(file_path):
|
|
print(f"{feed_file} does not exist, skipping.")
|
|
continue
|
|
|
|
with open(file_path, 'r') as file:
|
|
data = json.load(file)
|
|
|
|
# Convert times and durations
|
|
self._replace_time_and_duration_recursive(data, timezone)
|
|
|
|
# Replace keys with more readable names
|
|
data = self._recursive_key_replace(data)
|
|
|
|
# Save modified data
|
|
with open(file_path, 'w') as file:
|
|
json.dump(data, file, indent=4)
|
|
|
|
print(f"Keys sorted and replaced in {feed_file}.")
|
|
|
|
def split_matches_into_files(self, timezone='GMT'):
|
|
"""Split match data into separate files."""
|
|
matches_dir = os.path.join(STATS_DIR, MATCH_DIR)
|
|
if not os.path.exists(matches_dir):
|
|
os.makedirs(matches_dir)
|
|
|
|
match_info_path = os.path.join(STATS_DIR, 'match_info.json')
|
|
if not os.path.exists(match_info_path):
|
|
print(f"Match info file not found at {match_info_path}. Skipping split.")
|
|
return
|
|
|
|
with open(match_info_path, 'r') as file:
|
|
data = json.load(file)
|
|
matches = data.get('data', {}).get('matches', [])
|
|
|
|
if not matches:
|
|
print("No matches found to split.")
|
|
return
|
|
|
|
# Check if time conversion is needed
|
|
sample_match = matches[0]
|
|
needs_time_conversion = (
|
|
isinstance(sample_match.get("utcStartSeconds"), int) or
|
|
isinstance(sample_match.get("utcEndSeconds"), int) or
|
|
isinstance(sample_match.get("duration"), int)
|
|
)
|
|
|
|
if needs_time_conversion:
|
|
print("Converting match timestamps to human-readable format...")
|
|
self._replace_time_and_duration_recursive(data, timezone)
|
|
|
|
# Update the main match file
|
|
with open(match_info_path, 'w') as file:
|
|
json.dump(data, file, indent=4)
|
|
|
|
# Process each match
|
|
for idx, match in enumerate(matches):
|
|
# Create a copy to avoid modifying the original data
|
|
match_copy = dict(match)
|
|
|
|
# Remove large loadout data to keep files smaller
|
|
if 'player' in match_copy:
|
|
match_copy['player'].pop('loadouts', None)
|
|
match_copy['player'].pop('loadout', None)
|
|
|
|
# Save to individual file
|
|
file_name = f"match_{idx + 1}.json"
|
|
file_path = os.path.join(matches_dir, file_name)
|
|
with open(file_path, 'w') as match_file:
|
|
json.dump(match_copy, match_file, indent=4)
|
|
|
|
print(f"Matches split into {len(matches)} separate files in {matches_dir}.")
|
|
|
|
def clean_json_files(self, *filenames):
|
|
"""Clean JSON files by removing HTML-like tags and entities."""
|
|
regex_pattern = r'<span class="|</span>|">|mp-stat-items|kills-value|headshots-value|username|game-mode|kdr-value|accuracy-value|defends-value'
|
|
replace = ''
|
|
|
|
for filename in filenames:
|
|
file_path = os.path.join(STATS_DIR, filename)
|
|
if not os.path.exists(file_path):
|
|
print(f"{filename} does not exist, skipping.")
|
|
continue
|
|
|
|
with open(file_path, 'r') as file:
|
|
content = file.read()
|
|
|
|
# Replace unwanted patterns
|
|
modified_content = re.sub(regex_pattern, replace, content)
|
|
|
|
# Save cleaned content
|
|
with open(file_path, 'w') as file:
|
|
file.write(modified_content)
|
|
|
|
print(f"Removed unreadable strings from {filename}.")
|
|
|
|
def _recursive_key_replace(self, obj):
|
|
"""Recursively replace keys and values with more readable versions."""
|
|
if isinstance(obj, dict):
|
|
new_obj = {}
|
|
for key, value in obj.items():
|
|
new_key = self.replacements.get(key, key)
|
|
if isinstance(value, str):
|
|
new_value = self.replacements.get(value, value)
|
|
new_obj[new_key] = self._recursive_key_replace(new_value)
|
|
else:
|
|
new_obj[new_key] = self._recursive_key_replace(value)
|
|
return new_obj
|
|
elif isinstance(obj, list):
|
|
return [self._recursive_key_replace(item) for item in obj]
|
|
else:
|
|
return self.replacements.get(obj, obj) if isinstance(obj, str) else obj
|
|
|
|
def _sort_data(self, data):
|
|
"""Sort data by meaningful metrics for better readability."""
|
|
if isinstance(data, dict):
|
|
for key, value in data.items():
|
|
if key == "mode":
|
|
# Sort game modes by time played
|
|
data[key] = dict(sorted(
|
|
value.items(),
|
|
key=lambda item: item[1]['properties']['timePlayed'],
|
|
reverse=True
|
|
))
|
|
elif key in ["Assault Rifles", "Shotguns", "Marksman Rifles", "Snipers", "LMGs", "Launchers", "Pistols", "SMGs", "Melee"]:
|
|
# Sort weapons by kills
|
|
data[key] = dict(sorted(
|
|
value.items(),
|
|
key=lambda item: item[1]['properties']['kills'],
|
|
reverse=True
|
|
))
|
|
elif key in ["Field Upgrades", "Tactical Equipment", "Lethal Equipment"]:
|
|
# Sort equipment by uses
|
|
data[key] = dict(sorted(
|
|
value.items(),
|
|
key=lambda item: item[1]['properties']['uses'],
|
|
reverse=True
|
|
))
|
|
elif key == "Scorestreaks":
|
|
# Sort scorestreaks by awarded count
|
|
for subcategory, scorestreaks in value.items():
|
|
data[key][subcategory] = dict(sorted(
|
|
scorestreaks.items(),
|
|
key=lambda item: item[1]['properties']['awardedCount'],
|
|
reverse=True
|
|
))
|
|
elif key == "Accolades":
|
|
# Sort accolades by count
|
|
if 'properties' in value:
|
|
data[key]['properties'] = dict(sorted(
|
|
value['properties'].items(),
|
|
key=lambda item: item[1],
|
|
reverse=True
|
|
))
|
|
else:
|
|
# Recursively sort nested data
|
|
data[key] = self._sort_data(value)
|
|
return data
|
|
|
|
def _replace_time_and_duration_recursive(self, data, timezone):
|
|
"""Recursively replace epoch times with human-readable formats."""
|
|
time_keys = [
|
|
"timePlayedTotal", "timePlayed", "objTime", "time", "timeProne",
|
|
"timeSpentAsPassenger", "timeSpentAsDriver", "timeOnPoint",
|
|
"timeWatchingKillcams", "timeCrouched", "timesSelectedAsSquadLeader",
|
|
"longestTimeSpentOnWeapon", "avgLifeTime", "percentTimeMoving"
|
|
]
|
|
date_keys = ["date", "updated", "originalDate", "dateAdded"]
|
|
|
|
if isinstance(data, list):
|
|
# Sort lists containing items with dateAdded
|
|
if data and isinstance(data[0], dict) and "dateAdded" in data[0]:
|
|
data.sort(key=lambda x: x.get("dateAdded", 0), reverse=True)
|
|
|
|
for item in data:
|
|
self._replace_time_and_duration_recursive(item, timezone)
|
|
elif isinstance(data, dict):
|
|
for key, value in data.items():
|
|
if key in date_keys:
|
|
if key == "dateAdded":
|
|
data[key] = self._epoch_to_human_readable(value, timezone)
|
|
else:
|
|
data[key] = self._epoch_milli_to_human_readable(value, timezone)
|
|
elif key in time_keys:
|
|
data[key] = self._convert_duration_seconds(value)
|
|
elif key == "utcStartSeconds":
|
|
data[key] = self._epoch_to_human_readable(value, timezone)
|
|
elif key == "utcEndSeconds":
|
|
data[key] = self._epoch_to_human_readable(value, timezone)
|
|
elif key == "duration":
|
|
data[key] = self._convert_duration_milliseconds(value)
|
|
else:
|
|
self._replace_time_and_duration_recursive(value, timezone)
|
|
|
|
def _epoch_milli_to_human_readable(self, epoch_millis, timezone='GMT'):
|
|
"""Convert epoch milliseconds to human-readable date string."""
|
|
if isinstance(epoch_millis, str):
|
|
return epoch_millis
|
|
|
|
dt_object = datetime.datetime.utcfromtimestamp(epoch_millis / 1000.0)
|
|
return self._format_datetime(dt_object, timezone)
|
|
|
|
def _epoch_to_human_readable(self, epoch_timestamp, timezone='GMT'):
|
|
"""Convert epoch seconds to human-readable date string."""
|
|
if isinstance(epoch_timestamp, str):
|
|
return epoch_timestamp
|
|
|
|
dt_object = datetime.datetime.utcfromtimestamp(epoch_timestamp)
|
|
return self._format_datetime(dt_object, timezone)
|
|
|
|
def _format_datetime(self, dt_object, timezone):
|
|
"""Format datetime object based on timezone."""
|
|
timezone_offsets = {
|
|
'GMT': 0,
|
|
'EST': -5, # Changed from -4 to -5 for Eastern Standard Time
|
|
'CST': -6, # Updated for consistency
|
|
'PST': -8
|
|
}
|
|
|
|
if timezone not in timezone_offsets:
|
|
raise ValueError(f"Unsupported timezone: {timezone}")
|
|
|
|
# Apply timezone offset
|
|
dt_object = dt_object + datetime.timedelta(hours=timezone_offsets[timezone])
|
|
|
|
# Format date string
|
|
return f"{timezone}: {dt_object.strftime('%A, %B %d, %Y %I:%M:%S %p')}"
|
|
|
|
def _convert_duration_milliseconds(self, milliseconds):
|
|
"""Convert milliseconds to a human-readable duration string."""
|
|
if isinstance(milliseconds, str) and "Minutes" in milliseconds:
|
|
return milliseconds # Already converted
|
|
|
|
seconds, milliseconds = divmod(milliseconds, 1000)
|
|
minutes, seconds = divmod(seconds, 60)
|
|
return f"{minutes} Minutes {seconds} Seconds {milliseconds} Milliseconds"
|
|
|
|
def _convert_duration_seconds(self, seconds):
|
|
"""Convert seconds to a human-readable duration string."""
|
|
if isinstance(seconds, str):
|
|
return seconds # Already converted
|
|
|
|
days, seconds = divmod(seconds, 86400)
|
|
hours, seconds = divmod(seconds, 3600)
|
|
minutes, seconds = divmod(seconds, 60)
|
|
|
|
days = int(days)
|
|
hours = int(hours)
|
|
minutes = int(minutes)
|
|
seconds = int(seconds)
|
|
|
|
return f"{days} Days {hours} Hours {minutes} Minutes {seconds} Seconds"
|
|
|
|
def _ensure_json_serializable(self, obj):
|
|
"""Recursively convert objects to JSON serializable types."""
|
|
if isinstance(obj, dict):
|
|
return {key: self._ensure_json_serializable(value) for key, value in obj.items()}
|
|
elif isinstance(obj, list):
|
|
return [self._ensure_json_serializable(item) for item in obj]
|
|
elif isinstance(obj, tuple):
|
|
return [self._ensure_json_serializable(item) for item in obj]
|
|
elif isinstance(obj, (int, float, str, bool, type(None))):
|
|
return obj
|
|
else:
|
|
# Convert any other type to string representation
|
|
return str(obj)
|
|
|
|
class CLI:
|
|
"""Command Line Interface manager."""
|
|
|
|
def __init__(self, stats_manager):
|
|
self.stats_manager = stats_manager
|
|
self.help_text = """
|
|
Obtaining your ACT_SSO_COOKIE
|
|
|
|
- Go to https://www.callofduty.com and login with your account
|
|
- Once logged in, press F12 for your browsers developer tools. Then go to Application --> Storage --> Cookies --> https://www.callofduty.com and find ACT_SSO_COOKIE
|
|
- Enter the value when prompted
|
|
"""
|
|
|
|
def display_menu(self):
|
|
"""Display the main menu and get user choice."""
|
|
print("\nBeautify Options:")
|
|
print("1) Beautify all data")
|
|
print("2) Split matches into separate files")
|
|
|
|
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")
|
|
|
|
print("\nOptions Not Requiring Player Name:")
|
|
print("11) Get season loot")
|
|
print("12) Get map list")
|
|
|
|
print("\n0) Exit")
|
|
|
|
try:
|
|
choice = int(input("Enter your choice: "))
|
|
return choice
|
|
except ValueError:
|
|
print("Please enter a valid number.")
|
|
return -1
|
|
|
|
def handle_menu_choice(self, choice):
|
|
"""Handle the user's menu choice."""
|
|
if choice == 0:
|
|
print("Exiting...")
|
|
return False
|
|
|
|
if choice in [3, 4, 5, 6, 7, 8, 9, 10, 11]:
|
|
player_name = input("Please enter the player's username (with #1234567): ")
|
|
|
|
options = {
|
|
3: {'all_stats': True},
|
|
4: {'season_loot': True},
|
|
5: {'identities': True},
|
|
6: {'info': True},
|
|
7: {'friendFeed': True},
|
|
8: {'eventFeed': True},
|
|
9: {'cod_points': True},
|
|
10: {'connected_accounts': True},
|
|
11: {'settings': True}
|
|
}
|
|
|
|
if choice in options:
|
|
self.stats_manager.fetch_data(player_name=player_name, **options[choice])
|
|
|
|
elif choice == 1:
|
|
self.stats_manager.beautify_all_data()
|
|
elif choice == 2:
|
|
self.stats_manager.split_matches_into_files()
|
|
elif choice == 12:
|
|
self.stats_manager.fetch_data(season_loot=True)
|
|
elif choice == 13:
|
|
self.stats_manager.fetch_data(maps=True)
|
|
else:
|
|
print("Invalid choice. Please try again.")
|
|
return True
|
|
|
|
return True
|
|
|
|
def run_interactive_mode(self):
|
|
"""Run the interactive menu mode."""
|
|
running = True
|
|
while running:
|
|
choice = self.display_menu()
|
|
running = self.handle_menu_choice(choice)
|
|
|
|
def setup_argument_parser(self):
|
|
"""Set up command line argument parser."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Detailed Modern Warfare (2019) Statistics Tool",
|
|
epilog=self.help_text,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
)
|
|
|
|
# Group arguments for better help display
|
|
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")
|
|
|
|
# Default options
|
|
group_default.add_argument(
|
|
"-tz", "--timezone",
|
|
type=str,
|
|
default="GMT",
|
|
choices=TIMEZONE_OPTIONS,
|
|
help="Specify the timezone (GMT, EST, CST, PST)"
|
|
)
|
|
|
|
# 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")
|
|
group_data.add_argument("-sl", "--season_loot", action="store_true", help="Fetch only the season loot data")
|
|
group_data.add_argument("-id", "--identities", action="store_true", help="Fetch only the logged-in identities data")
|
|
group_data.add_argument("-m", "--maps", action="store_true", help="Fetch only the map list data")
|
|
group_data.add_argument("-i", "--info", action="store_true", help="Fetch only general information")
|
|
group_data.add_argument("-f", "--friendFeed", action="store_true", help="Fetch only your friend feed")
|
|
group_data.add_argument("-e", "--eventFeed", action="store_true", help="Fetch only your event feed")
|
|
group_data.add_argument("-cp", "--cod_points", action="store_true", help="Fetch only your COD Point balance")
|
|
group_data.add_argument("-ca", "--connected_accounts", action="store_true", help="Fetch only connected accounts data")
|
|
group_data.add_argument("-s", "--settings", action="store_true", help="Fetch only your account settings")
|
|
|
|
# Data 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 matches into separate JSON files")
|
|
group_cleaning.add_argument("-csd", "--clean_stats_data", action="store_true", help="Beautify stats.json data")
|
|
group_cleaning.add_argument("-cmd", "--clean_match_data", action="store_true", help="Beautify match_info.json data")
|
|
group_cleaning.add_argument("-cff", "--clean_friend_feed", action="store_true", help="Clean friend feed data")
|
|
group_cleaning.add_argument("-cef", "--clean_event_feed", action="store_true", help="Clean event feed data")
|
|
|
|
return parser
|
|
|
|
def run_cli_mode(self, args):
|
|
"""Run the command line mode with parsed arguments."""
|
|
# Prioritize cleaning operations
|
|
if args.clean:
|
|
self.stats_manager.beautify_all_data(timezone=args.timezone)
|
|
elif args.clean_stats_data:
|
|
self.stats_manager.beautify_stats_data(timezone=args.timezone)
|
|
elif args.clean_match_data:
|
|
self.stats_manager.beautify_match_data(timezone=args.timezone)
|
|
elif args.split_matches:
|
|
self.stats_manager.split_matches_into_files(timezone=args.timezone)
|
|
elif args.clean_friend_feed:
|
|
self.stats_manager.clean_json_files('friendFeed.json')
|
|
elif args.clean_event_feed:
|
|
self.stats_manager.clean_json_files('eventFeed.json')
|
|
else:
|
|
# Data fetching operations
|
|
options = {
|
|
'all_stats': args.all_stats,
|
|
'season_loot': args.season_loot,
|
|
'identities': args.identities,
|
|
'maps': args.maps,
|
|
'info': args.info,
|
|
'friendFeed': args.friendFeed,
|
|
'eventFeed': args.eventFeed,
|
|
'cod_points': args.cod_points,
|
|
'connected_accounts': args.connected_accounts,
|
|
'settings': args.settings
|
|
}
|
|
self.stats_manager.fetch_data(args.player_name, **options)
|
|
|
|
def main():
|
|
"""Main entry point for the application."""
|
|
stats_manager = CodStatsManager()
|
|
cli = CLI(stats_manager)
|
|
|
|
# Parse command line arguments
|
|
if len(sys.argv) > 1:
|
|
parser = cli.setup_argument_parser()
|
|
args = parser.parse_args()
|
|
cli.run_cli_mode(args)
|
|
else:
|
|
# Run interactive mode
|
|
cli.run_interactive_mode()
|
|
|
|
if __name__ == "__main__":
|
|
main() |