def get_context_data(self, **kwargs): all_drops = get_drop_querysets(self.get_queryset()) recent_drops = { 'items': all_drops['items'].values( 'item', name=F('item__name'), icon=F('item__icon'), ).annotate( count=Sum('quantity') ).order_by('-count') if 'items' in all_drops else [], 'monsters': replace_value_with_choice( list(all_drops['monsters'].values( name=F('monster__name'), icon=F('monster__image_filename'), element=F('monster__element'), stars=F('grade'), is_awakened=F('monster__is_awakened'), can_awaken=F('monster__can_awaken'), ).annotate( count=Count('pk') ).order_by('-count')), {'element': Monster.ELEMENT_CHOICES}) if 'monsters' in all_drops else [], 'runes': replace_value_with_choice( list(all_drops['runes'].values( 'type', 'quality', 'stars', ).annotate( count=Count('pk') ).order_by('-count') if 'runes' in all_drops else []), { 'type': RuneInstance.TYPE_CHOICES, 'quality': RuneInstance.QUALITY_CHOICES, } ), } if self.get_log_count(): bin_width = 50000 damage_stats = self.get_queryset().aggregate(min=Min('damage'), max=Max('damage')) bin_start = floor_to_nearest(damage_stats['min'], bin_width) bin_end = ceil_to_nearest(damage_stats['max'], bin_width) damage_histogram = { 'type': 'histogram', 'width': bin_width, 'data': histogram(self.get_queryset(), 'damage', range(bin_start, bin_end, bin_width)), } else: damage_histogram = None context = { 'dashboard': { 'recent_drops': recent_drops, }, 'report': drop_report(self.get_queryset(), min_count=0), 'damage_histogram': damage_histogram } context.update(kwargs) return super().get_context_data(**context)
def get_context_data(self, **kwargs): all_drops = get_drop_querysets(self.get_queryset()) recent_drops = { 'items': all_drops['items'].values( 'item', name=F('item__name'), icon=F('item__icon'), ).annotate(count=Sum('quantity')).order_by('-count') if 'items' in all_drops else [], 'monsters': replace_value_with_choice( list(all_drops['monsters'].values( name=F('monster__name'), icon=F('monster__image_filename'), element=F('monster__element'), stars=F('grade'), is_awakened=F('monster__is_awakened'), can_awaken=F('monster__can_awaken'), ).annotate(count=Count('pk')).order_by('-count')), {'element': Monster.ELEMENT_CHOICES}) if 'monsters' in all_drops else [], 'runes': replace_value_with_choice( list(all_drops['runes'].values( 'type', 'quality', 'stars', ).annotate(count=Count('pk')).order_by('-count') if 'runes' in all_drops else []), { 'type': RuneInstance.TYPE_CHOICES, 'quality': RuneInstance.QUALITY_CHOICES, }), } dashboard_data = { 'energy_spent': { 'type': 'occurrences', 'total': self.get_log_count(), 'data': transform_to_dict( list(self.get_queryset( ).values('level__dungeon__name').annotate( count=Sum('level__energy_cost'), ).order_by('-count')), name_key='level__dungeon__name', ), }, 'recent_drops': recent_drops, } level_list = Level.objects.filter( pk__in=set(self.get_queryset().values_list('level', flat=True))) kwargs['dashboard'] = dashboard_data kwargs['level_list'] = level_list return super().get_context_data(**kwargs)
def get_context_data(self, **kwargs): all_drops = get_drop_querysets(self.get_queryset()) recent_drops = { 'items': all_drops['items'].values( 'item', name=F('item__name'), icon=F('item__icon'), ).annotate( count=Sum('quantity') ).order_by('-count') if 'items' in all_drops else [], 'monsters': replace_value_with_choice( list(all_drops['monsters'].values( name=F('monster__name'), icon=F('monster__image_filename'), element=F('monster__element'), stars=F('grade'), is_awakened=F('monster__is_awakened'), can_awaken=F('monster__can_awaken'), ).annotate( count=Count('pk') ).order_by('-count')), {'element': Monster.ELEMENT_CHOICES} ) if 'monsters' in all_drops else [], 'rune_crafts': replace_value_with_choice( list(all_drops['rune_crafts'].values( 'type', 'quality', 'rune', ).annotate( count=Count('pk') ).order_by('-count') if 'rune_crafts' in all_drops else []), { 'type': RuneCraft.CRAFT_CHOICES, 'quality': RuneCraft.QUALITY_CHOICES, 'rune': RuneCraft.TYPE_CHOICES, } ) } level_list = Level.objects.filter( pk__in=set(self.get_queryset().values_list('level', flat=True)) ).order_by('-floor').prefetch_related('dungeon') kwargs['dashboard'] = { 'recent_drops': recent_drops, } kwargs['level_list'] = level_list return super().get_context_data(**kwargs)
def _rune_craft_report_data(qs, total_log_count, **kwargs): if qs.count() == 0: return None min_count = kwargs.get('min_count', max(1, int(MINIMUM_THRESHOLD * total_log_count))) return { 'type': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('type').annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'type': qs.model.CRAFT_CHOICES})), }, 'rune': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('rune').annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'rune': qs.model.TYPE_CHOICES})), }, 'quality': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'quality': qs.model.QUALITY_CHOICES})), }, 'stat': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('stat').annotate(count=Count('stat')).filter( count__gt=min_count).order_by('stat')), {'stat': qs.model.STAT_CHOICES})) }, }
def get_artifact_report(qs, total_log_count, **kwargs): if qs.count() == 0: return None min_count = kwargs.get('min_count', max(1, int(MINIMUM_THRESHOLD * total_log_count))) # Secondary effect distribution # Unable to use database aggregation on an ArrayField without ORM gymnastics, so post-process data in python all_effects = qs.annotate( flat_effects=Func(F('effects'), function='unnest')).values_list( 'flat_effects', flat=True) effect_counts = Counter(all_effects) return { 'element': { 'type': 'occurrences', 'total': qs.filter(slot=Artifact.SLOT_ELEMENTAL).count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.filter(slot=Artifact.SLOT_ELEMENTAL).values( 'element').annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'element': qs.model.ELEMENT_CHOICES})), }, 'archetype': { 'type': 'occurrences', 'total': qs.filter(slot=Artifact.SLOT_ARCHETYPE).count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.filter(slot=Artifact.SLOT_ARCHETYPE).values( 'archetype').annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'archetype': qs.model.ARCHETYPE_CHOICES})), }, 'quality': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'quality': qs.model.QUALITY_CHOICES})), }, 'main_stat': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('main_stat').annotate( count=Count('main_stat')).filter( count__gt=min_count).order_by('main_stat')), {'main_stat': qs.model.STAT_CHOICES})) }, 'effects': { 'type': 'occurrences', 'total': len(all_effects), 'data': transform_to_dict( replace_value_with_choice( sorted([{ 'effect': k, 'count': v } for k, v in effect_counts.items()], key=lambda count: count['effect']), {'effect': qs.model.EFFECT_CHOICES}), ) }, 'max_efficiency': { 'type': 'histogram', 'width': 5, 'data': histogram(qs, 'max_efficiency', range(0, 100, 5), slice_on='quality'), }, }
def get_rune_report(qs, total_log_count, **kwargs): if qs.count() == 0: return None min_count = kwargs.get('min_count', max(1, int(MINIMUM_THRESHOLD * total_log_count))) # Substat distribution # Unable to use database aggregation on an ArrayField without ORM gymnastics, so post-process data in python all_substats = qs.annotate( flat_substats=Func(F('substats'), function='unnest')).values_list( 'flat_substats', flat=True) substat_counts = Counter(all_substats) # Sell value ranges min_value, max_value = qs.aggregate(Min('value'), Max('value')).values() min_value = int(floor_to_nearest(min_value, 1000)) max_value = int(ceil_to_nearest(max_value, 1000)) return { 'stars': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( list( qs.values(grade=Concat(Cast('stars', CharField( )), Value('⭐'))).annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count'))), }, 'type': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('type').annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'type': qs.model.TYPE_CHOICES})), }, 'quality': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).filter( count__gt=min_count).order_by('-count')), {'quality': qs.model.QUALITY_CHOICES})), }, 'slot': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( list( qs.values('slot').annotate(count=Count('pk')).filter( count__gt=min_count).order_by('-count'))), }, 'main_stat': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('main_stat').annotate( count=Count('main_stat')).filter( count__gt=min_count).order_by('main_stat')), {'main_stat': qs.model.STAT_CHOICES})) }, 'slot_2_main_stat': { 'type': 'occurrences', 'total': qs.filter(slot=2).count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.filter(slot=2).values('main_stat').annotate( count=Count('main_stat')).filter( count__gt=min_count).order_by('main_stat')), {'main_stat': qs.model.STAT_CHOICES})) }, 'slot_4_main_stat': { 'type': 'occurrences', 'total': qs.filter(slot=4).count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.filter(slot=4).values('main_stat').annotate( count=Count('main_stat')).filter( count__gt=min_count).order_by('main_stat')), {'main_stat': qs.model.STAT_CHOICES})) }, 'slot_6_main_stat': { 'type': 'occurrences', 'total': qs.filter(slot=6).count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.filter(slot=6).values('main_stat').annotate( count=Count('main_stat')).filter( count__gt=min_count).order_by('main_stat')), {'main_stat': qs.model.STAT_CHOICES})) }, 'innate_stat': { 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values('innate_stat').annotate( count=Count('pk')).filter( count__gt=min_count).order_by('innate_stat')), {'innate_stat': qs.model.STAT_CHOICES})) }, 'substats': { 'type': 'occurrences', 'total': len(all_substats), 'data': transform_to_dict( replace_value_with_choice( sorted([{ 'substat': k, 'count': v } for k, v in substat_counts.items()], key=lambda count: count['substat']), {'substat': qs.model.STAT_CHOICES}), ) }, 'max_efficiency': { 'type': 'histogram', 'width': 5, 'data': histogram(qs, 'max_efficiency', range(0, 100, 5), slice_on='quality'), }, 'value': { 'type': 'histogram', 'width': 500, 'data': histogram(qs, 'value', range(min_value, max_value, 500), slice_on='quality') } }
def get_monster_report(qs, total_log_count, **kwargs): if qs.count() == 0: return None min_count = kwargs.get('min_count', max(1, int(MINIMUM_THRESHOLD * total_log_count))) return { 'monsters': { # By unique monster 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( list( qs.values( 'monster', ).annotate( monster_name=Func( Concat(F('monster__element'), Value(' '), F('monster__name')), function='INITCAP', ), count=Count('pk'), ).filter(count__gt=min_count).order_by('-count') ), name_key='monster_name' ), }, 'family': { # By family 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( list( qs.values( family_id=F('monster__family_id'), name=F('monster__name'), ).annotate( count=Count('pk') ).filter( count__gt=min_count ).order_by('-count') ), name_key='name' ) }, 'nat_stars': { # By nat stars 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( list( qs.values( nat_stars=F('monster__natural_stars'), ).annotate( grade=Concat(Cast('monster__natural_stars', CharField()), Value('⭐')), count=Count('monster__natural_stars'), ).filter(count__gt=min_count).order_by('-count') ), name_key='grade' ) }, 'element': { # By element 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( replace_value_with_choice( list( qs.values( element=F('monster__element') ).annotate( element_cap=Func(F('monster__element'), function='INITCAP',), count=Count('pk') ).filter(count__gt=min_count).order_by('-count') ), {'element': Monster.ELEMENT_CHOICES} ), name_key='element_cap', ) }, 'awakened': { # By awakened/unawakened 'type': 'occurrences', 'total': qs.count(), 'data': transform_to_dict( list( qs.values( awakened=F('monster__is_awakened') ).annotate( count=Count('pk') ).filter(count__gt=min_count).order_by('-count') ), transform={ True: 'Awakened', False: 'Unawakened', } ), } }
def get_report_summary(drops, total_log_count, **kwargs): summary = { 'table': {}, 'chart': [], } min_count = kwargs.get('min_count', max(1, int(MINIMUM_THRESHOLD * total_log_count))) # Chart data: list of {'drop': <string>, 'count': <int>} # Table data: dict (by drop type) of lists of items which drop, with stats. 'count' is only required stat. for drop_type, qs in drops.items(): # Remove very low frequency occurrences from dataset qs = qs.annotate(count=Count('pk')).filter(count__gte=min_count) if drop_type == models.ItemDrop.RELATED_NAME: if kwargs.get('exclude_social_points'): qs = qs.exclude(item__category=GameItem.CATEGORY_CURRENCY, item__name='Social Point') chart_qs = qs.values(name=F('item__name')).annotate( count=Count('pk')).order_by('-count') if not kwargs.get('include_currency'): chart_qs = chart_qs.exclude( item__category=GameItem.CATEGORY_CURRENCY) chart_data = list(chart_qs) table_data = list( qs.values( name=F('item__name'), icon=F('item__icon'), ).annotate( count=Count('pk'), min=Min('quantity'), max=Max('quantity'), avg=Avg('quantity'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, qty_per_100=Cast(Sum('quantity'), FloatField()) / total_log_count * 100, ).order_by('item__category', '-count')) elif drop_type == models.MonsterDrop.RELATED_NAME: # Monster drops in chart are counted by stars chart_data = list( qs.values(name=Concat(Cast('grade', CharField()), Value('⭐ Monster'))).annotate( count=Count('pk')).filter( count__gt=0).order_by('-count')) table_data = replace_value_with_choice( list( qs.values( name=F('monster__name'), slug=F('monster__bestiary_slug'), icon=F('monster__image_filename'), element=F('monster__element'), can_awaken=F('monster__can_awaken'), is_awakened=F('monster__is_awakened'), stars=F('grade'), ).annotate( count=Count('pk'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, qty_per_100=Cast(Count('pk'), FloatField()) / total_log_count * 100, )), {'element': Monster.ELEMENT_CHOICES}) elif drop_type == models.RuneCraftDrop.RELATED_NAME: # Rune crafts are counted by type chart_data = list( replace_value_with_choice( qs.values(name=F('type'), ).annotate( count=Count('pk'), ).order_by('-count'), {'name': models.RuneCraftDrop.CRAFT_CHOICES})) table_data = { 'sets': replace_value_with_choice( list( qs.values('rune').annotate( count=Count('pk')).order_by('rune')), {'rune': Rune.TYPE_CHOICES}), 'type': replace_value_with_choice( list( qs.values('type').annotate( count=Count('pk')).order_by('type')), {'type': Rune.TYPE_CHOICES}), 'quality': replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).order_by('quality')), {'quality': Rune.QUALITY_CHOICES}), } else: # Chart is name, count only item_name = ' '.join( [s.capitalize() for s in drop_type.split('_')]).rstrip('s') count = qs.aggregate(count=Count('pk'))['count'] if count > 0: chart_data = [{ 'name': item_name, 'count': count, }] else: chart_data = [] # Table data based on item type if drop_type == models.MonsterPieceDrop.RELATED_NAME: table_data = replace_value_with_choice( list( qs.values( name=F('monster__name'), slug=F('monster__bestiary_slug'), icon=F('monster__image_filename'), element=F('monster__element'), can_awaken=F('monster__can_awaken'), is_awakened=F('monster__is_awakened'), stars=F('monster__natural_stars'), count=Count('pk'), min=Min('quantity'), max=Max('quantity'), avg=Avg('quantity'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, qty_per_100=Cast(Sum('quantity'), FloatField()) / total_log_count * 100, )), {'element': Monster.ELEMENT_CHOICES}) elif drop_type == models.RuneDrop.RELATED_NAME: table_data = { 'sets': replace_value_with_choice( list( qs.values('type').annotate( count=Count('pk')).order_by('type')), {'type': Rune.TYPE_CHOICES}), 'slots': list( qs.values('slot').annotate( count=Count('pk')).order_by('slot')), 'quality': replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).order_by('quality')), {'quality': Rune.QUALITY_CHOICES}), } elif drop_type == models.RuneCraftDrop.RELATED_NAME: table_data = replace_value_with_choice( list( qs.values('type', 'rune', 'quality').annotate( count=Count('pk'), ).order_by( 'type', 'rune', 'quality')), { 'type': Rune.TYPE_CHOICES, 'quality': Rune.QUALITY_CHOICES }) elif drop_type == models.ArtifactDrop.RELATED_NAME: table_data = { 'element': replace_value_with_choice( list( qs.filter(slot=Artifact.SLOT_ELEMENTAL).values( 'element').annotate( count=Count('pk')).order_by('element')), {'type': Artifact.ELEMENT_CHOICES}), 'archetype': replace_value_with_choice( list( qs.filter(slot=Artifact.SLOT_ARCHETYPE).values( 'archetype').annotate( count=Count('pk')).order_by('archetype')), {'type': Artifact.ARCHETYPE_CHOICES}), 'quality': replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).order_by('quality')), {'quality': Artifact.QUALITY_CHOICES}), } elif drop_type == models.DungeonSecretDungeonDrop.RELATED_NAME: table_data = replace_value_with_choice( list( qs.values( name=F( 'level__dungeon__secretdungeon__monster__name' ), slug= F('level__dungeon__secretdungeon__monster__bestiary_slug' ), icon= F('level__dungeon__secretdungeon__monster__image_filename' ), element= F('level__dungeon__secretdungeon__monster__element' ), can_awaken= F('level__dungeon__secretdungeon__monster__can_awaken' ), is_awakened= F('level__dungeon__secretdungeon__monster__is_awakened' ), stars= F('level__dungeon__secretdungeon__monster__natural_stars' ), ).annotate( count=Count('pk'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, qty_per_100=Cast(Sum('pk'), FloatField()) / total_log_count * 100, )), {'element': Monster.ELEMENT_CHOICES}) else: raise NotImplementedError( f"No summary table generation for {drop_type}") summary['chart'] += chart_data if table_data: summary['table'][drop_type] = table_data return summary
def get_context_data(self, **kwargs): all_drops = get_drop_querysets(self.get_queryset()) recent_drops = { 'items': all_drops['items'].values( 'item', name=F('item__name'), icon=F('item__icon'), ).annotate( count=Sum('quantity') ).order_by('-count') if 'items' in all_drops else [], 'monsters': replace_value_with_choice( list(all_drops['monsters'].values( name=F('monster__name'), icon=F('monster__image_filename'), element=F('monster__element'), stars=F('grade'), is_awakened=F('monster__is_awakened'), can_awaken=F('monster__can_awaken'), ).annotate( count=Count('pk') ).order_by('-count')), {'element': Monster.ELEMENT_CHOICES}) if 'monsters' in all_drops else [], # 'monster_pieces': 'insert_data_here' if 'monster_pieces' in all_drops else [], 'runes': replace_value_with_choice( list(all_drops['runes'].values( 'type', 'quality', 'stars', ).annotate( count=Count('pk') ).order_by('-count') if 'runes' in all_drops else []), { 'type': RuneInstance.TYPE_CHOICES, 'quality': RuneInstance.QUALITY_CHOICES, } ), # 'secret_dungeons': 'insert_data_here' if 'runes' in all_drops else [], } dashboard_data = { 'energy_spent': { 'type': 'occurrences', 'total': self.get_log_count(), 'data': transform_to_dict( list( self.get_queryset().values( 'level' ).annotate( dungeon_name=Concat( F('level__dungeon__name'), Value(' B'), F('level__floor'), output_field=CharField() ), count=Sum('level__energy_cost'), ).order_by('-count') ), name_key='dungeon_name', ), }, 'recent_drops': recent_drops, } level_order = self.get_queryset().values('level').annotate( energy_spent=Sum('level__energy_cost') ).order_by('-energy_spent').values_list('level', flat=True) preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(level_order)]) level_list = Level.objects.filter( pk__in=set(self.get_queryset().values_list('level', flat=True)) ).order_by(preserved_order).prefetch_related('dungeon')[:20] kwargs['dashboard'] = dashboard_data kwargs['level_list'] = level_list return super().get_context_data(**kwargs)
def get_report_summary(drops, total_log_count): summary = { 'table': {}, 'chart': [], } # Chart data: list of {'drop': <string>, 'count': <int>} # Table data: dict (by drop type) of lists of items which drop, with stats. 'count' is only required stat. for drop_type, qs in drops.items(): if drop_type == models.ItemDrop.RELATED_NAME: # Chart excludes currency chart_data = list( qs.exclude(item__category=GameItem.CATEGORY_CURRENCY, ).values( name=F('item__name'), ).annotate( count=Count('pk'), ).filter( count__gt=0).order_by('-count')) table_data = list( qs.values( name=F('item__name'), icon=F('item__icon'), ).annotate( count=Count('pk'), min=Min('quantity'), max=Max('quantity'), avg=Avg('quantity'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, avg_per_run=Cast(Sum('quantity'), FloatField()) / total_log_count, ).filter(count__gt=0).order_by('item__category', '-count')) elif drop_type == models.MonsterDrop.RELATED_NAME: # Monster drops in chart are counted by stars chart_data = list( qs.values(name=Concat(Cast('grade', CharField()), Value('⭐ Monster'))).annotate( count=Count('pk')).filter( count__gt=0).order_by('-count')) table_data = replace_value_with_choice( list( qs.values( name=F('monster__name'), slug=F('monster__bestiary_slug'), icon=F('monster__image_filename'), element=F('monster__element'), can_awaken=F('monster__can_awaken'), is_awakened=F('monster__is_awakened'), stars=F('monster__base_stars'), ).annotate( count=Count('pk'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, )), {'element': Monster.ELEMENT_CHOICES}) else: # Chart can is name, count only item_name = ' '.join( [s.capitalize() for s in drop_type.split('_')]).rstrip('s') count = qs.aggregate(count=Count('pk'))['count'] if count > 0: chart_data = [{ 'name': item_name, 'count': count, }] else: chart_data = [] # Table data based on item type if drop_type == models.MonsterPieceDrop.RELATED_NAME: table_data = replace_value_with_choice( list( qs.values( name=F('monster__name'), icon=F('monster__image_filename'), element=F('monster__element'), count=Count('pk'), min=Min('quantity'), max=Max('quantity'), avg=Avg('quantity'), )), {'element': Monster.ELEMENT_CHOICES}) elif drop_type == models.RuneDrop.RELATED_NAME: table_data = { 'sets': replace_value_with_choice( list( qs.values('type').annotate( count=Count('pk')).order_by('type')), {'type': Rune.TYPE_CHOICES}), 'slots': list( qs.values('slot').annotate( count=Count('pk')).order_by('slot')), 'quality': replace_value_with_choice( list( qs.values('quality').annotate( count=Count('pk')).order_by('quality')), {'quality': Rune.QUALITY_CHOICES}), } elif drop_type == models.RuneCraftDrop.RELATED_NAME: table_data = replace_value_with_choice( list( qs.values('type', 'rune', 'quality').annotate( count=Count('pk'), ).order_by( 'type', 'rune', 'quality')), { 'type': Rune.TYPE_CHOICES, 'quality': Rune.QUALITY_CHOICES }) elif drop_type == models.DungeonSecretDungeonDrop.RELATED_NAME: table_data = replace_value_with_choice( list( qs.values( name=F( 'level__dungeon__secretdungeon__monster__name' ), element= F('level__dungeon__secretdungeon__monster__element' ), icon= F('level__dungeon__secretdungeon__monster__image_filename' ), ).annotate(count=Count('pk'), )), {'element': Monster.ELEMENT_CHOICES}) else: raise NotImplementedError( f"No summary table generation for {drop_type}") summary['chart'] += chart_data if table_data: summary['table'][drop_type] = table_data return summary
def get_monster_report(qs, total_log_count): if qs.count() == 0: return None results = {} # By unique monster results['monsters'] = { 'type': 'occurrences', 'total': qs.count(), 'data': list( qs.values( 'monster', name=F('monster__name'), element=F('monster__element'), com2us_id=F('monster__com2us_id'), icon=F('monster__image_filename')).annotate(count=Count('pk'))) } # By family results['family'] = { 'type': 'occurrences', 'total': qs.count(), 'data': list( qs.values( family_id=F('monster__family_id'), name=F('monster__name'), ).annotate(count=Count('pk'))) } # By nat stars mons_by_nat_stars = list( qs.values(nat_stars=F('monster__base_stars'), ).annotate( count=Count('monster__base_stars'), drop_chance=Cast(Count('pk'), FloatField()) / total_log_count * 100, )) results['nat_stars'] = { 'type': 'occurrences', 'total': qs.count(), 'data': mons_by_nat_stars, } # By element results['element'] = { 'type': 'occurrences', 'total': qs.count(), 'data': replace_value_with_choice( list( qs.values(element=F('monster__element')).annotate( count=Count('pk'))), {'element': Monster.ELEMENT_CHOICES}) } # By awakened/unawakened results['awakened'] = { 'type': 'occurrences', 'total': qs.count(), 'data': list( qs.values(awakened=F('monster__is_awakened')).annotate( count=Count('pk'))), } return results