def get_queryset(self): season = SeasonChooser(self.request).current qs = filter_season(Game.objects, season, should_include_live=True) query = self.request.GET.get("query", "") parts = re.split(r"[\s,]", query) for part in parts: part = part.strip() if part != "": if part[0] == "#": # Filter by hashtag in description qs = qs.filter(description__icontains=part) else: # Filter by username qs = qs.filter(players__username__icontains=part) qs = qs.distinct() if self.order.current_column == "end_datetime": # First show live games (but not dnf games), # then show all other games sorted by # end_datetime if it is not null, # otherwise use start_datetime instead qs = qs.order_by( Case( When(end_datetime__isnull=True, dnf=False, then=Value(0)), default=Value(1), output_field=IntegerField(), ), Case( When(end_datetime__isnull=True, then="start_datetime"), default="end_datetime", output_field=DateTimeField(), ).desc(), "id", ) if self.order.reverse: qs = qs.reverse() elif self.order.current_column == "duration": qs = Game.add_durations(qs) # Always show games with unknown duration last if self.order.reverse: qs = qs.order_by(F("duration").desc(nulls_last=True), "id") else: qs = qs.order_by(F("duration").asc(nulls_last=True), "id") return qs
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) season = SeasonChooser(self.request).current chooser = PlayerCountChooser(self.request) context["player_count_chooser"] = chooser player_count = chooser.current games = filter_season_and_player_count(Game.objects, season, player_count) total_sips = ( Card.objects.filter(game__id__in=games).aggregate(total_sips=Sum("value"))[ "total_sips" ] or 0 ) games_with_durations = Game.add_durations(games) total_duration = games_with_durations.aggregate(total_duration=Sum("duration"))[ "total_duration" ] or datetime.timedelta(0) context["game_stats"] = { "total_games": games.count(), "total_dnf": games.filter(dnf=True).count(), "total_sips": total_sips, "total_beers": total_sips / 14, "total_duration": str(round_timedelta(total_duration)), } games_played = Counter() for g in games.all(): if g.end_datetime: games_played[g.end_datetime.date()] += 1 context["heatmap_data"] = games_heatmap_data(games, season) stat_types = { "sips_data": ( GamePlayerStat.get_sips_distribution(season, player_count), self.sips_count_distribution, ), "chugs_data": ( GamePlayerStat.get_chugs_distribution(season, player_count), self.chug_count_distribution, ), } for name, (stats, prob_f) in stat_types.items(): xs = [] ys = [] probs = [] dist_str = None if stats.exists(): d = {s["value"]: s["value__count"] for s in stats} if prob_f: if player_count == None: dist, dist_str = self.combined_distribution(season, prob_f) else: dist, dist_str = prob_f(player_count) for x in range(stats.first()["value"], stats.last()["value"] + 1): xs.append(x) ys.append(d.get(x, 0)) if dist: probs.append(dist(x)) context[name] = { "xs": xs, "ys": ys, "total_ys": sum(ys), "probs": probs, "probs_exact": True, "dist_str": dist_str, } BUCKETS = 60 chugs = filter_season_and_player_count( Chug.objects, season, player_count, key="card__game" ) duration_data = [ { "name": "duration_data", "max_duration": 4, "max_duration_unit": "hours", "durations": lambda max_duration: ( g["duration"] for g in games_with_durations.filter( dnf=False, duration__lte=max_duration ).values("duration") ), "format": str, }, { "name": "chug_duration_data", "max_duration": 15, "max_duration_unit": "seconds", "durations": lambda max_duration: ( datetime.timedelta(milliseconds=c["duration_ms"]) for c in chugs.filter( duration_ms__lte=get_milliseconds(max_duration) ).values("duration_ms") ), "format": lambda td: f"{td.total_seconds():.2f}", }, ] for d in duration_data: max_duration = datetime.timedelta( **{d["max_duration_unit"]: d["max_duration"]} ) bucket_span = max_duration / BUCKETS occurrences = Counter() for duration in d["durations"](max_duration): x = int(duration / bucket_span) occurrences[x] += 1 context[d["name"]] = { "total_ys": sum(occurrences.values()), "bucket_span_seconds": bucket_span.total_seconds(), "max_duration": f"{d['max_duration']} {d['max_duration_unit']}", "xs": [d["format"]((i + 1) * bucket_span) for i in range(BUCKETS)], "ys": [occurrences[i] for i in range(BUCKETS)], } context["chug_table_header"] = ["Players\xa0\\\xa0Chugs", *range(6 + 1)] context["chug_table"] = [] for pcount in range(2, 6 + 1): row = [pcount] context["chug_table"].append(row) dist, _ = self.chug_count_distribution(pcount) for chugs in range(6 + 1): row.append(dist(chugs) * 100 if chugs <= pcount else None) context["location_data"] = [] for g in games.filter( location_latitude__isnull=False, location_accuracy__lte=100 * 1000 ): game_url = reverse("game_detail", args=[g.id]) context["location_data"].append( { "latitude": g.location_latitude, "longitude": g.location_longitude, "popup": f"<a href='{game_url}'>{g.date}<br>{g.players_str()}</a>", } ) return context