Skip to content

Commit

Permalink
Merge in changes from aircraft stats mod.
Browse files Browse the repository at this point in the history
  • Loading branch information
FGlazov committed May 7, 2021
1 parent 8afb0d0 commit 5cdbc3b
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 33 deletions.
3 changes: 3 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ Version 1.5.1

- Update to 2.1.6 Enhancements, 1.2.8 Global Aircraft stats.

Version 1.5.2

- Update to 1.2.9 Global Aircraft Stats
2 changes: 1 addition & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This bundle version is designed to work with version 1.2.50 of IL-2 stats.
Disconnect mod version: 1.6
Tank mod version: 3.0.1
Stats Enhancements version : 2.1.6
Global Aircraft Stats mod version : 1.2.8
Global Aircraft Stats mod version : 1.2.9

DISCLAIMER: This module is NOT (currently) retroactive, it will only split the stats of new sorties.

Expand Down
3 changes: 3 additions & 0 deletions src/mod_stats_by_aircraft/aircraft_mod_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,8 @@ class AircraftKillboard(models.Model):
# Helper fields in order to detect corrupted data.
# This field is only relevant for killboards without Player
reset_kills_turret_bug = models.BooleanField(default=False, db_index=True)
# This field is only relevant for killboards with Player.
reset_player_loses = models.BooleanField(default=False, db_index=True)

class Meta:
# The long table name is to avoid any conflicts with new tables defined in the main branch of IL2 Stats.
Expand All @@ -621,6 +623,7 @@ class SortieAugmentation(models.Model):
player_stats_processed = models.BooleanField(default=False, db_index=True)
fixed_aa_accident_stats = models.BooleanField(default=False, db_index=True)
fixed_doubled_turret_killboards = models.BooleanField(default=False, db_index=True)
added_player_kb_losses = models.BooleanField(default=False, db_index=True)

class Meta:
# The long table name is to avoid any conflicts with new tables defined in the main branch of IL2 Stats.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def log_done(self):

return "[mod_stats_by_aircraft]: WARNING: Programming error, unimplemented logs done method."

def reset_relevant_fields(self):
def reset_relevant_fields(self, tour_cutoff):
"""
Optional method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

class FixCorruptedAaAccidents(BackgroundJob):
"""
A bug in the early versions of 1.2.X the retroactive compute to double accidental/aa deaths.
A bug in the early versions of 1.2.X caused the retroactive compute to double accidental/aa deaths.
This job resets the accidental/aa deaths, and recomputes them. Note that a simple halving would not work here,
since missions processed after the bugged update did not double accidental/aa deaths.
"""

def reset_relevant_fields(self):
def reset_relevant_fields(self, tour_cutoff):
AircraftBucket.objects.filter(reset_accident_aa_stats=False).update(
deaths_to_accident=0,
deaths_to_aa=0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from .background_job import BackgroundJob
from stats.models import Sortie
from ..aircraft_mod_models import AircraftBucket, AircraftKillboard


class FixNoDeathsPlayerKB(BackgroundJob):
"""
A bug in the early versions of 1.2.X the retroactive compute to not count player deaths in aircraft overview
killboards.
This job resets the loses, and recomputes them. Not that the reset is necessary, since the normal, not retroactive
compute does not have this issue.
"""

def reset_relevant_fields(self, tour_cutoff):
AircraftKillboard.objects.filter(
aircraft_1__player__isnull=False,
reset_player_loses=False,
tour__id__gte=tour_cutoff
).update(
aircraft_2_kills=0,
aircraft_2_shotdown=0,
aircraft_2_assists=0,
aircraft_2_pk_assists=0,
aircraft_2_distinct_hits=0,
reset_player_loses=True
)

AircraftKillboard.objects.filter(
aircraft_2__player__isnull=False,
reset_player_loses=False,
tour__id__gte=tour_cutoff
).update(
aircraft_1_kills=0,
aircraft_1_shotdown=0,
aircraft_1_assists=0,
aircraft_1_pk_assists=0,
aircraft_1_distinct_hits=0,
reset_player_loses=True
)

def query_find_sorties(self, tour_cutoff):
return (Sortie.objects.filter(SortieAugmentation_MOD_STATS_BY_AIRCRAFT__added_player_kb_losses=False,
aircraft__cls_base='aircraft', tour__id__gte=tour_cutoff)
.order_by('-tour__id'))

def compute_for_sortie(self, sortie):
from ..stats_whore import process_log_entries, get_sortie_type

buckets = [(AircraftBucket.objects.get_or_create(tour=sortie.tour, aircraft=sortie.aircraft,
filter_type='NO_FILTER', player=None))[0]]
filter_type = get_sortie_type(sortie)
has_subtype = filter_type != 'NO_FILTER'
if has_subtype:
buckets.append((AircraftBucket.objects.get_or_create(tour=sortie.tour, aircraft=sortie.aircraft,
filter_type=filter_type, player=None))[0])

for bucket in buckets:
process_log_entries(bucket, sortie, has_subtype, bucket.filter_type != 'NO_FILTER',
compute_only_pure_killboard_stats=True, stop_update_primary_bucket=True)

sortie.SortieAugmentation_MOD_STATS_BY_AIRCRAFT.added_player_kb_losses = True
sortie.SortieAugmentation_MOD_STATS_BY_AIRCRAFT.save()

def log_update(self, to_compute):
return '[mod_stats_by_aircraft]: Adding loses in player aircraft killboards {} sorties left to process.' \
.format(to_compute)

def log_done(self):
return '[mod_stats_by_aircraft]: Completed adding loses in player aircraft killboards.'
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class FixTurretKillboards(BackgroundJob):
This job resets all the killoboards, Elos and lethalities,, and recomputes them.
"""

def reset_relevant_fields(self):
def reset_relevant_fields(self, tour_cutoff):
AircraftBucket.objects.filter(player=None, reset_elo=False).update(
elo=1500,
pilot_kills=0,
Expand Down Expand Up @@ -41,7 +41,8 @@ def compute_for_sortie(self, sortie):
filter_type=filter_type, player=None))[0])
for bucket in buckets:
process_log_entries(bucket, sortie, has_subtype, bucket.filter_type != 'NO_FILTER',
compute_only_pure_killboard_stats=True)
compute_only_pure_killboard_stats=True,
do_not_use_pilot_kbs=True)

sortie.SortieAugmentation_MOD_STATS_BY_AIRCRAFT.fixed_doubled_turret_killboards = True
sortie.SortieAugmentation_MOD_STATS_BY_AIRCRAFT.save()
Expand Down
27 changes: 13 additions & 14 deletions src/mod_stats_by_aircraft/background_jobs/player_retro_compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,24 @@ def compute_for_sortie(self, sortie):

process_aircraft_stats(sortie, sortie.player)

if sortie.is_lost_aircraft:
bucket = (AircraftBucket.objects.get_or_create(tour=sortie.tour, aircraft=sortie.aircraft,
filter_type='NO_FILTER', player=None))[0]
filter_type = get_sortie_type(sortie)
has_subtype = filter_type != 'NO_FILTER'
bucket = (AircraftBucket.objects.get_or_create(tour=sortie.tour, aircraft=sortie.aircraft,
filter_type='NO_FILTER', player=None))[0]
filter_type = get_sortie_type(sortie)
has_subtype = filter_type != 'NO_FILTER'

# To update killboards of buckets with Player shotdown in this sortie,
# and also AA/accident shotdowns/deaths
process_log_entries(bucket, sortie, has_subtype, False, stop_update_primary_bucket=True)
bucket.save()

if has_subtype:
bucket = (AircraftBucket.objects.get_or_create(tour=sortie.tour, aircraft=sortie.aircraft,
filter_type=filter_type, player=None))[0]
# To update killboards of buckets with Player shotdown in this sortie,
# and also AA/accident shotdowns/deaths
process_log_entries(bucket, sortie, has_subtype, False, stop_update_primary_bucket=True)
process_log_entries(bucket, sortie, True, True, stop_update_primary_bucket=True)
bucket.save()

if has_subtype:
bucket = (AircraftBucket.objects.get_or_create(tour=sortie.tour, aircraft=sortie.aircraft,
filter_type=filter_type, player=None))[0]
# To update killboards of buckets with Player shotdown in this sortie,
# and also AA/accident shotdowns/deaths
process_log_entries(bucket, sortie, True, True, stop_update_primary_bucket=True)
bucket.save()

def log_update(self, to_compute):
return '[mod_stats_by_aircraft]: Retroactively computing player aircraft stats. {} sorties left to process.' \
.format(to_compute)
Expand Down
25 changes: 19 additions & 6 deletions src/mod_stats_by_aircraft/background_jobs/run_background_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from .player_retro_compute import PlayerRetroCompute
from .fix_corrupted_aa_accident import FixCorruptedAaAccidents
from .fix_turret_killboards import FixTurretKillboards
from .fix_no_deaths_player_kb import FixNoDeathsPlayerKB
from stats.models import Tour
from stats.logger import logger
import config

# Subclasses of BackgroundJob, see background_job.py
jobs = [FullRetroCompute(), PlayerRetroCompute(), FixCorruptedAaAccidents(), FixTurretKillboards()]
jobs = [FullRetroCompute(), PlayerRetroCompute(), FixCorruptedAaAccidents(), FixTurretKillboards(),
FixNoDeathsPlayerKB()]

LOG_COUNTER = 0
LOGGING_INTERVAL = 5 # How many batches are run before an update log is produced.
Expand All @@ -29,8 +31,12 @@ def reset_corrupted_data():
Note this must be done before any new mission is processed, otherwise the new data would be overwritten
if reset later after the mission is processed.
"""
tour_cutoff = __get_tour_cutoff()
if tour_cutoff is None:
return

for job in jobs:
job.reset_relevant_fields()
job.reset_relevant_fields(tour_cutoff)


@transaction.atomic
Expand All @@ -40,11 +46,10 @@ def run_background_jobs():
@returns True if some work was done, False if there is no more work left to do.
"""
max_id = Tour.objects.aggregate(Max('id'))['id__max']
if max_id is None: # Edge case: No tour yet
return False

tour_cutoff = max_id - RETRO_COMPUTE_FOR_LAST_TOURS
tour_cutoff = __get_tour_cutoff()
if tour_cutoff is None:
return False

for job in jobs:
work_done = __run_background_job(job, tour_cutoff)
Expand Down Expand Up @@ -74,3 +79,11 @@ def __run_background_job(job, tour_cutoff):
LOG_COUNTER = 0

return True


def __get_tour_cutoff():
max_id = Tour.objects.aggregate(Max('id'))['id__max']
if max_id is None: # Edge case: No tour yet
return None

return max_id - RETRO_COMPUTE_FOR_LAST_TOURS
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2021-05-06 18:55
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('mod_stats_by_aircraft', '0004_fix_killboard_stats'),
]

operations = [
migrations.AddField(
model_name='aircraftkillboard',
name='reset_player_loses',
field=models.BooleanField(db_index=True, default=False),
),
migrations.AddField(
model_name='sortieaugmentation',
name='added_player_kb_losses',
field=models.BooleanField(db_index=True, default=False),
),
]
15 changes: 8 additions & 7 deletions src/mod_stats_by_aircraft/stats_whore.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ def stats_whore(m_report_file):


# ======================== MODDED PART BEGIN
# TODO: Refactor these functions into a new file.
# This should be run after the other objects have been saved, otherwise it will not work.
def process_aircraft_stats(sortie, player=None):
if not sortie.aircraft.cls_base == "aircraft":
Expand Down Expand Up @@ -440,11 +441,12 @@ def process_bucket(bucket, sortie, has_subtype, is_subtype):
sortie_augmentation.player_stats_processed = True
sortie_augmentation.fixed_aa_accident_stats = True
sortie_augmentation.fixed_doubled_turret_killboards = True
sortie_augmentation.added_player_kb_losses = True
sortie_augmentation.save()


def process_log_entries(bucket, sortie, has_subtype, is_subtype, stop_update_primary_bucket=False,
compute_only_pure_killboard_stats=False):
compute_only_pure_killboard_stats=False, do_not_use_pilot_kbs=False):
events = (LogEntry.objects
.select_related('act_object', 'act_sortie', 'cact_object', 'cact_sortie')
.filter(Q(act_sortie_id=sortie.id),
Expand All @@ -471,16 +473,15 @@ def process_log_entries(bucket, sortie, has_subtype, is_subtype, stop_update_pri
enemies_killed.add(enemy_plane_sortie_pair)

use_pilot_kbs = bucket.player is None
if compute_only_pure_killboard_stats:
# This is True while we're recomputing corrupted killboards which don't have players.
# So we don't want to update deaths to turret for players.
if do_not_use_pilot_kbs:
use_pilot_kbs = False
enemy_buckets, kbs = update_from_entries(bucket, enemies_damaged, enemies_killed, enemies_shotdown,
has_subtype, is_subtype, use_pilot_kbs,
update_primary_bucket=not stop_update_primary_bucket)

for killboard in kbs.values():
killboard.reset_kills_turret_bug = True
killboard.reset_player_loses = True
killboard.save()
for enemy_bucket in enemy_buckets.values():
enemy_bucket.update_derived_fields()
Expand Down Expand Up @@ -545,9 +546,7 @@ def process_log_entries(bucket, sortie, has_subtype, is_subtype, stop_update_pri
if stop_update_primary_bucket:
update_primary_bucket = False
use_pilot_kbs = bucket.player is not None
if compute_only_pure_killboard_stats:
# This is True while we're recomputing corrupted killboards which don't have players.
# So we don't want to update deaths to turret for players.
if do_not_use_pilot_kbs:
use_pilot_kbs = False

buckets, kbs = update_from_entries(turret_bucket, enemy_damaged, enemy_killed, enemy_shotdown,
Expand All @@ -564,6 +563,7 @@ def process_log_entries(bucket, sortie, has_subtype, is_subtype, stop_update_pri

for kb in kbs.values():
kb.reset_kills_turret_bug = True
kb.reset_player_loses = True
kb.save()


Expand Down Expand Up @@ -934,3 +934,4 @@ def turret_to_aircraft_bucket(turret_name, tour, player=None):
logger.info("[mod_stats_by_aircraft] WARNING: Could not find aircraft for turret " + turret_name)
return None
# ======================== MODDED PART END

0 comments on commit 5cdbc3b

Please sign in to comment.