diff --git a/killstats/api/killboard/alliance/killboard.py b/killstats/api/killboard/alliance/killboard.py index 0dc12c1..093f0fa 100644 --- a/killstats/api/killboard/alliance/killboard.py +++ b/killstats/api/killboard/alliance/killboard.py @@ -5,9 +5,6 @@ from ninja import NinjaAPI -# Django -from django.utils.translation import gettext_lazy as _ - # AA Killstats from killstats.api import schema from killstats.api.account_manager import AccountManager @@ -43,30 +40,28 @@ def get_killstats(request, month, year, alliance_id: int): alliances = [alliance_id] killmail_year = ( - Killmail.objects.select_related("victim", "victim_ship") + Killmail.objects.prefetch_related("victim", "victim_ship") .filter(killmail_date__year=year) .order_by("-killmail_date") - ) - - killmail_year = killmail_year.filter_alliances(alliances) + ).filter_entities(alliances) killmail_month = killmail_year.filter( killmail_date__year=year, killmail_date__month=month, ) - account = AccountManager(alliances=alliances) - mains, all_chars = account.get_mains_alts() - kills, totalvalue, losses, totalvalue_loss = killboard_process_kills( - killmail_month, mains, all_chars + killmail_month, alliances ) date = KillboardDate(month, year) - stats = killboard_dashboard(killmail_year, date, mains, all_chars) + account = AccountManager(alliances=alliances) + mains, _ = account.get_mains_alts() + + stats = killboard_dashboard(killmail_year, date, alliances) - shame, fame = killboard_hall(killmail_month, mains) + shame, fame = killboard_hall(killmail_month, alliances, mains) output = [] output.append( diff --git a/killstats/api/killboard/corporation/killboard.py b/killstats/api/killboard/corporation/killboard.py index 04f659b..a79821e 100644 --- a/killstats/api/killboard/corporation/killboard.py +++ b/killstats/api/killboard/corporation/killboard.py @@ -5,9 +5,6 @@ from ninja import NinjaAPI -# Django -from django.utils.translation import gettext_lazy as _ - # AA Killstats from killstats.api import schema from killstats.api.account_manager import AccountManager @@ -42,30 +39,30 @@ def get_killstats(request, month, year, corporation_id: int): corporations = [corporation_id] killmail_year = ( - Killmail.objects.select_related("victim", "victim_ship") + Killmail.objects.prefetch_related("victim", "victim_ship") .filter(killmail_date__year=year) .order_by("-killmail_date") ) - killmail_year = killmail_year.filter_corporations(corporations) + killmail_filtered = killmail_year.filter_entities(corporations) - killmail_month = killmail_year.filter( + killmail_month = killmail_filtered.filter( killmail_date__year=year, killmail_date__month=month, ) - account = AccountManager(corporations=corporations) - mains, all_chars = account.get_mains_alts() - kills, totalvalue, losses, totalvalue_loss = killboard_process_kills( - killmail_month, mains, all_chars + killmail_month, corporations ) date = KillboardDate(month, year) - stats = killboard_dashboard(killmail_year, date, mains, all_chars) + account = AccountManager(corporations=corporations) + mains, _ = account.get_mains_alts() + + stats = killboard_dashboard(killmail_filtered, date, corporations) - shame, fame = killboard_hall(killmail_month, mains) + shame, fame = killboard_hall(killmail_month, corporations, mains) output = [] output.append( diff --git a/killstats/api/killboard_manager.py b/killstats/api/killboard_manager.py index 8375ac6..8759f2b 100644 --- a/killstats/api/killboard_manager.py +++ b/killstats/api/killboard_manager.py @@ -6,6 +6,7 @@ # Alliance Auth (External Libs) from allianceauth.eveonline.evelinks import eveimageserver +from allianceauth.eveonline.models import EveCharacter # AA Killstats from killstats.hooks import get_extension_logger @@ -29,7 +30,7 @@ def __init__(self): class KillboardStatsManager: - def __init__(self, date, stats): + def __init__(self, date, stats, entities): self.worst_ship = Counter() self.top_victim = Counter() self.top_ship = Counter() @@ -37,19 +38,20 @@ def __init__(self, date, stats): self.alltime_killer = Counter() self.alltime_loss = Counter() self.count = KillboardStatsCount() + self.entities = entities self.date = date self.stats = stats - def update_for_killmail(self, killmail: Killmail, all_chars): - if killmail.is_kill(all_chars): - self._update_kill_stats(killmail, all_chars) - if killmail.is_loss(all_chars): - self._update_loss_stats(killmail) + def update_for_killmail(self, killmail: Killmail): + self._update_kill_stats(killmail) - def _update_kill_stats(self, killmail: Killmail, all_chars): + def _update_kill_stats(self, killmail: Killmail): counted_ships = set() for attacker in killmail.attackers: - if attacker["character_id"] in all_chars: + if ( + attacker["corporation_id"] in self.entities + or attacker["alliance_id"] in self.entities + ): ship_id = attacker["ship_type_id"] attacker_id = attacker["character_id"] if ship_id not in counted_ships and killmail.get_month(self.date.month): @@ -58,14 +60,16 @@ def _update_kill_stats(self, killmail: Killmail, all_chars): if killmail.get_month(self.date.month): self.top_killer[attacker_id] += 1 self.alltime_killer[attacker_id] += 1 - - def _update_loss_stats(self, killmail: Killmail): victim_id = killmail.victim.eve_id - if killmail.get_month(self.date.month): - if not killmail.is_capsule() and not killmail.is_mobile(): - self.worst_ship[killmail.victim_ship.id] += 1 - self.top_victim[victim_id] += 1 - self.alltime_loss[victim_id] += 1 + if ( + killmail.victim_corporation_id in self.entities + or killmail.victim_alliance_id in self.entities + ): + if killmail.get_month(self.date.month): + if not killmail.is_capsule() and not killmail.is_mobile(): + self.worst_ship[killmail.victim_ship.id] += 1 + self.top_victim[victim_id] += 1 + self.alltime_loss[victim_id] += 1 def _stats_count(self, title, model, count, char=False, loss=False): if model: @@ -165,97 +169,55 @@ def update_alltime_killer(self): ) -def killboard_process_kills(killmail_data: Killmail, mains, all_chars): +def killboard_process_kills(killmail_data: Killmail, entities): + killmail_data = killmail_data.prefetch_related("victim", "victim_ship") kills = [] losses = [] totalvalue = 0 totalvalue_loss = 0 - for killmail in killmail_data: - if killmail.is_kill(all_chars) and not killmail.is_loss(all_chars): - _stats_killmail(killmail, mains, kills) - totalvalue += killmail.victim_total_value - if killmail.is_loss(all_chars): - _stats_killmail(killmail, mains, losses) - totalvalue_loss += killmail.victim_total_value + for killmail in killmail_data.filter_entities_kills(entities): + _stats_killmail(killmail, kills) + totalvalue += killmail.victim_total_value + + for killmail in killmail_data.filter_entities_losses(entities): + _stats_killmail(killmail, losses) + totalvalue_loss += killmail.victim_total_value return kills, totalvalue, losses, totalvalue_loss -def _get_character_details(killmail, mains, stats_type, main_list): - if main_list: - return main_list - - main = None - main_id = None - alt = None - - # Check if Victim is Main or Alt and set main and alt accordingly - if killmail.victim.eve_id in mains: - main_data = mains[killmail.victim.eve_id] - main = main_data["main"] - main_id = killmail.victim.eve_id - else: - for main_data in mains.values(): - alts = main_data.get("alts", []) - for alt_candidate in alts: - if killmail.victim.eve_id == alt_candidate.character_id: - main = main_data["main"] - alt = alt_candidate - break - if alt: # Break outer loop if alt is found - break - - if stats_type == "attacker" and not main_list: - main, main_id, alt = killmail.attacker_main(mains, killmail.attackers) - - return main, main_id, alt - - -def _calculate_character_info(alt, main, main_id, stats_type, killmail, char_name): - if stats_type == "attacker": - if alt is None: - character_id = main_id - character_name = f"{main}" - else: - character_id = alt.character_id - character_name = f"{alt.character_name} ({main})" if alt else char_name - else: - character_id = ( - killmail.victim.eve_id if killmail.victim.category == "character" else None - ) - if main and str(char_name) != str(main): - character_name = f"{char_name} ({main})" - else: - character_name = char_name +def _evulate_portrait(killmail): + eve_id = killmail.victim.eve_id + portrait = eveimageserver.character_portrait_url(eve_id, 256) + if killmail.victim.category == "corporation": + portrait = eveimageserver.corporation_logo_url(eve_id, 256) + if killmail.victim.category == "alliance": + portrait = eveimageserver.alliance_logo_url(eve_id, 256) + return portrait + - return character_id, character_name +def _evaluate_zkb_link(killmail): + zkb = f"https://zkillboard.com/character/{killmail.victim.eve_id}/" + if killmail.victim.category == "corporation": + zkb = f"https://zkillboard.com/corporation/{killmail.victim.eve_id}/" + if killmail.victim.category == "alliance": + zkb = f"https://zkillboard.com/alliance/{killmail.victim.eve_id}/" + return zkb def _stats_killmail( killmail: Killmail, - mains: dict, stats: list, title=None, - stats_type="killmail", count=0, - main_list=None, ): - main, main_id, alt = _get_character_details(killmail, mains, stats_type, main_list) - char_name = killmail.victim.name - portrait = eveimageserver.type_icon_url(killmail.victim_ship.id, 64) - character_id, character_name = _calculate_character_info( - alt, main, main_id, stats_type, killmail, char_name - ) - stats.append( { - "title": f"{title}" if main is not None else title, + # zKB Data "killmail_id": killmail.killmail_id, - "name": char_name, - "character_id": character_id, - "character_name": character_name, - "portrait": portrait, + "character_id": killmail.victim.eve_id, + "character_name": killmail.victim.name, "corporation_id": killmail.victim_corporation_id, "alliance_id": killmail.victim_alliance_id, "ship": killmail.victim_ship.id, @@ -263,6 +225,10 @@ def _stats_killmail( "hash": killmail.hash, "totalValue": killmail.victim_total_value, "date": killmail.killmail_date, + # Additional Data + "title": f"{killmail.victim.name}" if title is None else title, + "portrait": _evulate_portrait(killmail), + "zkb_link": _evaluate_zkb_link(killmail), "count": count, } ) @@ -272,41 +238,37 @@ def _stats_killmail( def killboard_dashboard( killmail_year: Killmail, date, - sorted_mains, - all_chars, + entities, ): - stats = [] - stats_manager = KillboardStatsManager(date, stats) + killmail_year = killmail_year.prefetch_related("victim", "victim_ship") - filtered_killmails = [ - km for km in killmail_year if km.is_loss(all_chars) and km.get_month(date.month) + stats = [] + stats_manager = KillboardStatsManager(date, stats, entities=entities) + filtered_killmails_loss = [ + km for km in killmail_year if km.get_month(date.month) and km.is_loss(entities) ] filtered_killmails_kill = [ - km - for km in killmail_year - if not km.is_capsule() - and km.is_kill(all_chars) - and not km.is_loss(all_chars) - and km.get_month(date.month) + km for km in killmail_year if km.get_month(date.month) and km.is_kill(entities) ] - highest_loss = nlargest(1, filtered_killmails, key=lambda km: km.victim_total_value) + highest_loss = nlargest( + 1, filtered_killmails_loss, key=lambda km: km.victim_total_value + ) highest_kill = nlargest( 1, filtered_killmails_kill, key=lambda km: km.victim_total_value ) - for killmail in killmail_year: - stats_manager.update_for_killmail(killmail, all_chars) + stats_manager.update_for_killmail(killmail) # Update All Stats if highest_loss: - _stats_killmail(highest_loss[0], sorted_mains, stats, "Top Loss:", "ship") + _stats_killmail(highest_loss[0], stats, "Top Loss:") stats_manager.update_worst_ship() stats_manager.update_top_victim() stats_manager.update_alltime_loss() if highest_kill: - _stats_killmail(highest_kill[0], sorted_mains, stats, "Top Kill:") + _stats_killmail(highest_kill[0], stats, "Top Kill:") stats_manager.update_top_ship() stats_manager.update_top_killer() stats_manager.update_alltime_killer() @@ -314,52 +276,81 @@ def killboard_dashboard( return stats -# pylint: disable=too-many-locals -def killboard_hall(killmail_month: Killmail, mains): - all_chars = list( - set( - [main["main"].character_id for _, main in mains.items()] - + [alt.character_id for _, main in mains.items() for alt in main["alts"]] - ) - ) - - killmail_month = killmail_month.order_by("-victim_total_value") - - shame = [] - fame = [] - +def _hall_of_fame(fame: list, topkiller, mains): topkiller = ( - killmail_month.filter_kills(all_chars) - .filter_loss(all_chars, exclude=True) + topkiller.order_by("-victim_total_value") + .prefetch_related("victim", "victim_ship") .filter_top_killer(mains) ) - toplosses = killmail_month.filter_loss(all_chars, exclude=False) - for char in topkiller: - killmail_fame, alt_char = topkiller.get(char, (None, None)) + killmail, alt_char = topkiller.get(char, (None, None)) if alt_char: # Check if the attacker is an alt and get the associated main character for main_data in mains.values(): for alt in main_data["alts"]: if alt_char.character_id == alt.character_id: - main = main_data["main"] - main_id = char + # main = main_data["main"] + main_data = char alt_data = alt_char break else: if char in mains: main_data = mains[char] - main = main_data["main"] - main_id = char + # main = main_data["main"] + main_data = char alt_data = None - main_list = [main, main_id, alt_data] - - _stats_killmail( - killmail_fame, mains, fame, stats_type="attacker", main_list=main_list + char = EveCharacter.objects.get(character_id=main_data) + alt_char = EveCharacter.objects.get(character_id=alt_data) if alt_data else None + + fame.append( + { + # zKB Data + "killmail_id": killmail.killmail_id, + "character_id": killmail.victim.eve_id, + "character_name": killmail.victim.name, + "corporation_id": killmail.victim_corporation_id, + "alliance_id": killmail.victim_alliance_id, + "ship": killmail.victim_ship.id, + "ship_name": killmail.victim_ship.name, + "hash": killmail.hash, + "totalValue": killmail.victim_total_value, + "date": killmail.killmail_date, + # Additional Data + "title": f"{killmail.victim.name}", + "portrait": _evulate_portrait(killmail), + "zkb_link": _evaluate_zkb_link(killmail), + "count": None, + "attacker_id": char.character_id, + "attacker_name": ( + f"{char.character_name}" + if alt_char is None + else f"{alt_char.character_name} ({char.character_name})" + ), + "attacker_portrait": eveimageserver.character_portrait_url( + char.character_id, 256 + ), + } ) + return fame + + +# pylint: disable=too-many-locals +def killboard_hall(killmail_month: Killmail, entities, mains): + # Ensure that the killmails are sorted by total value + killmail_month = killmail_month.order_by("-victim_total_value").prefetch_related( + "victim", "victim_ship" + ) + + shame = [] + fame = [] + + topkiller = killmail_month.filter_entities_kills(entities) + toplosses = killmail_month.filter_entities_losses(entities) + + _hall_of_fame(fame, topkiller, mains) - for killmail in toplosses: - _stats_killmail(killmail, mains, shame) + for killmail in toplosses[:5]: + _stats_killmail(killmail, stats=shame) - return shame[:5], fame[:5] + return shame, fame[:5] diff --git a/killstats/managers/killboard_manager.py b/killstats/managers/killboard_manager.py index bea530e..958ada3 100644 --- a/killstats/managers/killboard_manager.py +++ b/killstats/managers/killboard_manager.py @@ -14,28 +14,55 @@ class KillmailQueryCore(models.QuerySet): - def filter_corporations(self, corporations): - """Filter Kills from Corporations List.""" + def filter_entities(self, entities): + """Filter Kills and Losses from Entities List (Corporations or Alliances).""" kms = [] for killmail in self: + if killmail.victim.eve_id in entities: + kms.append(killmail.killmail_id) + if any( - attacker["corporation_id"] in corporations + attacker["corporation_id"] in entities for attacker in killmail.attackers ): kms.append(killmail.killmail_id) - if killmail.victim_corporation_id in corporations: + + if killmail.victim_corporation_id in entities: + kms.append(killmail.killmail_id) + + if any( + attacker["alliance_id"] in entities for attacker in killmail.attackers + ): + kms.append(killmail.killmail_id) + + if killmail.victim_alliance_id in entities: kms.append(killmail.killmail_id) return self.filter(killmail_id__in=kms) - def filter_alliances(self, alliances): - """Filter Kills from Corporations List.""" + def filter_entities_kills(self, entities): + """Filter Kills from Entities List (Corporations or Alliances).""" kms = [] for killmail in self: if any( - attacker["alliance_id"] in alliances for attacker in killmail.attackers + attacker["corporation_id"] in entities + for attacker in killmail.attackers ): kms.append(killmail.killmail_id) - if killmail.victim_alliance_id in alliances: + if any( + attacker["alliance_id"] in entities for attacker in killmail.attackers + ): + kms.append(killmail.killmail_id) + return self.filter(killmail_id__in=kms) + + def filter_entities_losses(self, entities): + """Filter Losses from Entities List (Corporations or Alliances).""" + kms = [] + for killmail in self: + if killmail.victim.eve_id in entities: + kms.append(killmail.killmail_id) + if killmail.victim_corporation_id in entities: + kms.append(killmail.killmail_id) + if killmail.victim_alliance_id in entities: kms.append(killmail.killmail_id) return self.filter(killmail_id__in=kms) @@ -45,22 +72,6 @@ def filter_structure(self, exclude=False): return self.exclude(victim_ship__eve_group__eve_category_id=65) return self.filter(victim_ship__eve_group__eve_category_id=65) - def filter_loss(self, chars, exclude=False): - """Filter or Exclude Losses from Chars List.""" - if exclude: - return self.exclude(victim__eve_id__in=chars) - return self.filter(victim__eve_id__in=chars) - - def filter_kills(self, chars): - """Filter Kills from Chars List.""" - kms = [] - for killmail in self: - if any( - attacker["character_id"] in chars for attacker in killmail.attackers - ): - kms.append(killmail.killmail_id) - return self.filter(killmail_id__in=kms) - def filter_threshold(self, threshold: int): """Filter Killmails are in Threshold.""" return self.filter(victim_total_value__gt=threshold) diff --git a/killstats/models/killboard.py b/killstats/models/killboard.py index e12de28..e389fa8 100644 --- a/killstats/models/killboard.py +++ b/killstats/models/killboard.py @@ -51,13 +51,28 @@ def get_image_url(self): self.victim.category, self.victim.eve_id, 32 ) - def is_kill(self, chars: list): + def is_kill(self, eve_ids: list): """Return True if any attacker is in the list of characters.""" - return any(attacker["character_id"] in chars for attacker in self.attackers) - - def is_loss(self, chars: list): + char_ids = self.attackers_distinct_character_ids() + corp_ids = self.attackers_distinct_corporation_ids() + alliance_ids = self.attackers_distinct_alliance_ids() + if any(char_id in char_ids for char_id in eve_ids): + return True + if any(corp_id in corp_ids for corp_id in eve_ids): + return True + if any(alliance_id in alliance_ids for alliance_id in eve_ids): + return True + return False + + def is_loss(self, eve_ids: list): """Return True if the victim is in the list of characters.""" - return self.victim.eve_id in chars if self.victim else False + if self.victim.eve_id in eve_ids: + return True + if self.victim_corporation_id in eve_ids: + return True + if self.victim_alliance_id in eve_ids: + return True + return False def is_corp(self, corps: list): """Return True if the victim corporation is in the list of corporations.""" @@ -65,6 +80,12 @@ def is_corp(self, corps: list): attacker["corporation_id"] in corps for attacker in self.attackers ) + def is_alliance(self, alliances: list): + """Return True if the victim alliance is in the list of alliances.""" + return self.victim_alliance_id in alliances or any( + attacker["alliance_id"] in alliances for attacker in self.attackers + ) + def is_structure(self): """Return True if the victim is a structure.""" return self.victim_ship.eve_group.eve_category_id == 65 diff --git a/killstats/static/killstats/js/killboard.js b/killstats/static/killstats/js/killboard.js index ffe072b..16cee0a 100644 --- a/killstats/static/killstats/js/killboard.js +++ b/killstats/static/killstats/js/killboard.js @@ -4,11 +4,11 @@ var corporationPk = corporationsettings.corporation_pk; var alliancePk = corporationsettings.alliance_pk; var selectedMonth, selectedYear; var monthText; -var url; // Aktuelles Datumobjekt erstellen var currentDate = new Date(); var killsTable, lossesTable; +var url; // Aktuelles Jahr und Monat abrufen selectedYear = currentDate.getFullYear(); @@ -63,12 +63,12 @@ function updateShame(shameData) {
${kill.character_name}
- - + + @@ -104,11 +104,11 @@ function updateFame(fameData) { fameTabContent += `
  • -
    ${kill.character_name}
    +
    ${kill.attacker_name}
    - - + +