def get_ability_markers(spawn_list, ship_stats): """ Parse a spawn list of lines and take the Engine, Shield, Systems and CoPilot ability activations and create markers for them to be put in the TimeLine. """ # TODO: Use ship_statistics to create availability markers categories = ["engines", "shields", "copilot", "systems"] player_id_list = Parser.get_player_id_list(spawn_list) results = {key: [] for key in categories} # Activation markers for line in spawn_list: if not isinstance(line, dict): line = Parser.line_to_dictionary(line) ability = line["ability"] if (line["source"] != line["target"] or line["source"] not in player_id_list or "AbilityActivate" not in line["effect"]): continue if ability in abilities.copilots: category = "copilot" elif ability in abilities.shields: category = "shields" elif ability in abilities.systems: category = "systems" elif ability in abilities.engines: category = "engines" else: continue start = FileHandler.datetime_to_float(line["time"]) args = ("abilities", start, start + 1/60) kwargs = {"background": FileHandler.colors[category]} results[category].append((args, kwargs)) return results
def test_get_effects_ability_eligible(self): with open(self.FILE) as fi: lindex = fi.readlines() index = lindex.index(self.EFFECT) no_effect = lindex.index(self.LINE) lines = Parser.read_file(self.FILE) player = Parser.get_player_id_list(lines) line = Parser.line_to_dictionary(lines[index], player) effect = Parser.get_effects_ability(line, lines, "2963000049645") self.assertIsInstance(effect, dict) self.assertTrue(len(effect) > 0) # Tests get_effects_eligible self.assertFalse(Parser.get_effects_ability(lines[no_effect], lines, "2963000048240"))
def insert_spawn(self, spawn, player_name, active_ids: list = None): """Insert the events of a spawn into the Treeview""" self.delete_all() if len(spawn) == 0: raise ValueError("Invalid spawn passed.") spawn = spawn if isinstance(spawn[0], dict) else [Parser.line_to_dictionary(line) for line in spawn] start_time = spawn[0]["time"] active_ids = Parser.get_player_id_list(spawn) if active_ids is None else active_ids for line in spawn: if "custom" not in line or line["custom"] is False: line_event_dict = Parser.line_to_event_dictionary(line, active_ids, spawn) else: line_event_dict = line self.insert_event(line_event_dict, player_name, active_ids, start_time)
def spawn_statistics(file_name, spawn, spawn_timing, sharing_db=None): """Build strings to show in the StatsFrame""" # Retrieve required data lines = Parser.read_file(file_name, sharing_db) player_numbers = Parser.get_player_id_list(lines) (abilities_dict, dmg_t, dmg_d, healing, dmg_s, enemies, critcount, crit_luck, hitcount, ships_list, enemy_dmg_d, enemy_dmg_t) = \ Parser.parse_spawn(spawn, player_numbers) name = Parser.get_player_name(lines) # Build the statistics string stat_string = "{name}\n{enemies} enemies\n{dmg_d}\n{dmg_t}\n{dmg_r:.1f} : 1.0\n" \ "{dmg_s}\n{healing}\n{hitcount}\n{critcount}\n{crit_luck:.2f}\n" \ "{deaths}\n{minutes}:{seconds:.0f}\n{dps:.1f}" start = spawn_timing finish = Parser.line_to_dictionary(spawn[-1])["time"] delta = finish - start minutes, seconds = divmod(delta.total_seconds(), 60) killsassists = sum(True if enemy_dmg_t[enemy] > 0 else False for enemy in enemies if enemy in enemy_dmg_t) stat_string = stat_string.format( name=name, enemies=killsassists, dmg_d=dmg_d, dmg_t=dmg_t, dmg_r=dmg_d / dmg_t if dmg_t != 0 else 0, dmg_s=dmg_s, healing=healing, hitcount=hitcount, critcount=critcount, crit_luck=critcount / hitcount if hitcount != 0 else 0, deaths="-", minutes=minutes, seconds=seconds, dps=dmg_d / delta.total_seconds() if delta.total_seconds() != 0 else 0 ) # Build the components list components = {key: "" for key in abilities.component_types} for component in [ability for ability in abilities_dict.keys() if ability in abilities.components]: for type in components.keys(): if component not in getattr(abilities, type): continue # Dual primary/secondary weapons if components[type] != "": components[type] += " / {}".format(component) break components[type] = component break components = [components[category] for category in abilities.component_types] # Return return name, spawn, abilities_dict, stat_string, ships_list, components, enemies, enemy_dmg_d, enemy_dmg_t
def update_timeline(self, file, match, spawn, match_timings, spawn_timings, file_cube): """ Update the TimeLine with the results of results the file and the screen results data """ # Get start and end times of the spawn start = FileHandler.datetime_to_float(Parser.line_to_dictionary(file_cube[match][spawn][0])["time"]) finish = FileHandler.datetime_to_float(Parser.line_to_dictionary(file_cube[match][spawn][-1])["time"])+1 self.time_line.delete_marker(tk.ALL) self.time_line.config(start=start, finish=finish) # Update the TimeLine screen_data = FileHandler.get_data_dictionary() screen_dict = FileHandler.get_spawn_dictionary( screen_data, file, match_timings[2 * match], spawn_timings[match][spawn] ) screen_dict = None if isinstance(screen_dict, str) or screen_dict is None else screen_dict spawn_list = file_cube[match][spawn] active_ids = Parser.get_player_id_list(spawn_list) markers = dict() if isinstance(screen_dict, dict): markers = FileHandler.get_markers(screen_dict, spawn_list, active_ids) markers["patterns"] = PatternParser.parse_patterns(spawn_list, screen_dict, Patterns.ALL_PATTERNS, active_ids) print("[TimeLine] Building {} markers.".format(sum(len(value) for value in markers.values()))) for category, data in markers.items(): for (args, kwargs) in data: try: self.time_line.create_marker(*args, **kwargs) except (ValueError, TypeError, tk.TclError) as e: print("[TimeLine] Marker creation failed: '{}', '{}', '{}', '{}': {}".format( args[0], args[1], args[2], kwargs["background"], repr(e)) ) if isinstance(e, ValueError): pass else: raise return
def test_line_to_dictionary(self): line_dict = Parser.line_to_dictionary(self.LINE) self.assertIsInstance(line_dict, dict) KEYS = ["time", "source", "target", "target", "amount", "ability", "effect"] for key in KEYS: self.assertTrue(key in line_dict) self.assertIsInstance(line_dict["time"], datetime) if not isinstance(line_dict["time"], datetime): raise ValueError self.assertEqual(line_dict["time"].hour, 22) self.assertEqual(line_dict["source"], "2963000048128") self.assertEqual(line_dict["target"], "2963000048240") self.assertEqual(line_dict["ability"], "Quad Laser Cannon") self.assertTrue("Damage" in line_dict["effect"]) self.assertEqual(int(line_dict["amount"]), 296)
def read_file(path, skip, include=False): """Read the file in UnicodeDecodeError safe method""" with open(path, "rb") as fi: lines = fi.readlines() read = lines[skip:] if include and len(lines) > 0 and lines[0] not in read: read.insert(0, lines[0]) dictionaries = [] for line in read: try: line = line.decode() except UnicodeDecodeError: continue line = Parser.line_to_dictionary(line) if line is None: continue dictionaries.append(line) return dictionaries
def get_markers(screen_dict: dict, spawn_list: list, active_ids: list): """ Generate a dictionary of markers to insert into the TimeLine widget of the StatsFrame. This marker dictionary is built-up as follows: {category: [(args, kwargs)]} The args and kwargs are passed onto the create_marker function for the TimeLine widget. """ if "ship" in screen_dict and screen_dict["ship"] is not None: stats = ShipStats(screen_dict["ship"], None, None) else: stats = None results = {} start_time = Parser.line_to_dictionary(spawn_list[-1])["time"] results.update(FileHandler.get_weapon_markers(screen_dict, spawn_list)) results.update(FileHandler.get_health_markers(screen_dict, start_time)) results.update(FileHandler.get_tracking_markers(screen_dict, stats)) results.update(FileHandler.get_power_mgmt_markers(screen_dict, start_time)) results.update(FileHandler.get_ability_markers(spawn_list, stats)) results.update(FileHandler.get_engine_boost_markers(screen_dict, start_time)) return results
def get_weapon_markers(dictionary, spawn): """ Parse the given screen dictionary and spawn line list to generate markers for the TimeLine for the Primary Weapon and Secondary Weapon categories. The clicks are parsed into lightly coloured markers, while ability activations (hits) are parsed into bright markers. """ # Retrieve pre-requisite data player_list = Parser.get_player_id_list(spawn) # Create lists that will hold markers results = {"primaries": [], "secondaries": []} """ File Data """ # Loop over contents of spawn for line in spawn: if isinstance(line, str): line = Parser.line_to_dictionary(line) # Retrieve the ability of the line ability = line["ability"] # If the ability was self-targeted, then it is not a weapon # If the ability was not activated by self, then it is not damage dealt if line["self"] is True or line["target"] not in player_list: continue # Determine the category of this ability if ability in abilities.primaries: category = "primaries" elif ability in abilities.secondaries: category = "secondaries" else: # Ability is not a weapon continue # Generate the arguments for the marker creation start = FileHandler.datetime_to_float(line["time"]) args = (category, start, start + 1 / 60) # Save the marker results[category].append((args, {"background": FileHandler.colors[category]})) # If screen data does not contain mouse data, then return the # markers created so far if "clicks" not in dictionary or len(dictionary["clicks"]) == 0: return results """ Screen Data """ # This dictionary will hold when each button press was started buttons = {Button.left: None, Button.right: None} # This dictionary is for backwards compatibility, see loop buttons_press = {Button.left: False, Button.right: False} # Start looping over the data found in the screen dictionary for time, data in sorted(dictionary["clicks"].items()): # Backwards compatibility if isinstance(data, tuple): # The current interface for screen results button, press = data # Also for backwards-compatibility press = "press" in press if isinstance(press, str) else press else: # An older version of the interface saved only the button as value for the dictionary # This means that determining if the button was pressed has to be determined manually button = data # Does not support any other buttons than right and left if button not in (Button.left, Button.right): continue # Update if the button was pressed buttons_press[button] = not buttons_press[button] press = buttons_press[button] # Determine the category of the button press category = "primaries" if button == Button.left else ("secondaries" if button == Button.right else None) if category is None: continue # If the button was actually pressed, then save the time for use later if press is True: buttons[button] = time # If the button was already pressed, then a new marker can be created else: results[category].append( ((category, buttons[button], time), {"background": FileHandler.click_colors[category]}) ) return results
def test_get_effect_allied(self): line = Parser.line_to_dictionary(self.LINE) effect = Parser.line_to_dictionary(self.EFFECT) self.assertTrue(Parser.get_effect_allied(effect["ability"])) self.assertFalse(Parser.get_effect_allied(line["ability"]))
def test_get_event_category(self): line = Parser.line_to_dictionary(self.LINE) self.assertEqual(Parser.get_event_category(line, "2963000048240"), "dmgt_pri") line = Parser.line_to_dictionary(self.EFFECT) self.assertEqual(Parser.get_event_category(line, "2963000049645"), "other")