<commit_before>import json
from datetime import datetime, timedelta
from threading import Thread

from django.core.cache import cache
from django.db import transaction

from problem.statistics import invalidate_problem
from submission.util import SubmissionStatus
from .models import Contest, ContestParticipant


def RANK_AS_DICT(x):
    return {
        "actual_rank": x.actual_rank,
        "rank": x.rank,
        "user": x.user_id,
        "penalty": x.penalty,
        "score": x.score,
        "detail": x.detail
    }


def get_submission_filter(contest: Contest, snapshot: timedelta, **kwargs):
    ret = contest.submission_set.filter(**kwargs). \
        only("id", "status", "create_time", "author_id", "problem_id", "contest_id", "contest_time", "status_percent")
    if snapshot is not None:
        ret = ret.filter(contest_time__lte=snapshot).order_by("contest_time")
    else:
        ret = ret.order_by("create_time")
    return ret


def calculate_problems(contest: Contest, problems: list, snapshot: timedelta=None):
    """

    :param contest:
    :param problems: list of ContestProblems
    :param snapshot:
    :return:
    {
        <problem_id>: {
            user_ac: int,
            user_tot: int,
            ac: int,
            tot: int,
            first_yes: None/int
        }
    }
    """
    problem_ids = list(map(lambda p: p.problem_id, problems))
    ans = {problem_id: dict(user_ac=set(), user_tot=dict(), ac=0, tot=0, first_yes_time=None, first_yes_by=None)
           for problem_id in problem_ids}
    for submission in get_submission_filter(contest, snapshot, problem_id__in=problem_ids):
        pstat = ans[submission.problem_id]
        status = submission.status
        if SubmissionStatus.is_accepted(status):
            pstat["user_ac"].add(submission.author_id)
            pstat["ac"] += 1
            if submission.contest_time is not None and pstat["first_yes_time"] is None:
                pstat["first_yes_time"] = submission.contest_time
                pstat["first_yes_by"] = submission.author_id
        if (submission.author_id in pstat["user_tot"] and pstat["user_tot"][submission.author_id] < submission.status_percent) or \
                submission.author_id not in pstat["user_tot"]:
            pstat["user_tot"][submission.author_id] = submission.status_percent
        pstat["tot"] += 1

    for p in problems:
        pstat = ans[p.problem_id]
        p.ac_user_count = len(pstat["user_ac"])
        p.total_user_count = len(pstat["user_tot"])
        p.ac_count = pstat["ac"]
        p.total_count = pstat["tot"]
        p.first_yes_time = pstat["first_yes_time"]
        p.first_yes_by = pstat["first_yes_by"]
        p.max_score = max(pstat["user_tot"].values())
        p.avg_score = sum(pstat["user_tot"].values()) / p.total_user_count if p.total_user_count > 0 else 0.0

    return ans


def calculate_participants(contest: Contest, participants: list, snapshot: timedelta=None):
    """
    :param contest
    :param participants: list of ContestParticipants
    :param snapshot: submissions in the first `snapshot` seconds will be effective
    :return
    {
        <user_id>: {
            penalty: int (seconds),
            score: int,
            is_confirmed: boolean
            detail: {
                <problem_id>: {
                    solved: boolean<commit_msg>修复 max 为空的问题<commit_after>import json
from datetime import datetime, timedelta
from threading import Thread

from django.core.cache import cache
from django.db import transaction

from problem.statistics import invalidate_problem
from submission.util import SubmissionStatus
from .models import Contest, ContestParticipant


def RANK_AS_DICT(x):
    return {
        "actual_rank": x.actual_rank,
        "rank": x.rank,
        "user": x.user_id,
        "penalty": x.penalty,
        "score": x.score,
        "detail": x.detail
    }


def get_submission_filter(contest: Contest, snapshot: timedelta, **kwargs):
    ret = contest.submission_set.filter(**kwargs). \
        only("id", "status", "create_time", "author_id", "problem_id", "contest_id", "contest_time", "status_percent")
    if snapshot is not None:
        ret = ret.filter(contest_time__lte=snapshot).order_by("contest_time")
    else:
        ret = ret.order_by("create_time")
    return ret


def calculate_problems(contest: Contest, problems: list, snapshot: timedelta=None):
    """

    :param contest:
    :param problems: list of ContestProblems
    :param snapshot:
    :return:
    {
        <problem_id>: {
            user_ac: int,
            user_tot: int,
            ac: int,
            tot: int,
            first_yes: None/int
        }
    }
    """
    problem_ids = list(map(lambda p: p.problem_id, problems))
    ans = {problem_id: dict(user_ac=set(), user_tot=dict(), ac=0, tot=0, first_yes_time=None, first_yes_by=None)
           for problem_id in problem_ids}
    for submission in get_submission_filter(contest, snapshot, problem_id__in=problem_ids):
        pstat = ans[submission.problem_id]
        status = submission.status
        if SubmissionStatus.is_accepted(status):
            pstat["user_ac"].add(submission.author_id)
            pstat["ac"] += 1
            if submission.contest_time is not None and pstat["first_yes_time"] is None:
                pstat["first_yes_time"] = submission.contest_time
                pstat["first_yes_by"] = submission.author_id
        if (submission.author_id in pstat["user_tot"] and pstat["user_tot"][submission.author_id] < submission.status_percent) or \
                submission.author_id not in pstat["user_tot"]:
            pstat["user_tot"][submission.author_id] = submission.status_percent
        pstat["tot"] += 1

    for p in problems:
        pstat = ans[p.problem_id]
        p.ac_user_count = len(pstat["user_ac"])
        p.total_user_count = len(pstat["user_tot"])
        p.ac_count = pstat["ac"]
        p.total_count = pstat["tot"]
        p.first_yes_time = pstat["first_yes_time"]
        p.first_yes_by = pstat["first_yes_by"]
        p.max_score = max(pstat["user_tot"].values()) if p.total_user_count > 0 else 0.0
        p.avg_score = sum(pstat["user_tot"].values()) / p.total_user_count if p.total_user_count > 0 else 0.0

    return ans


def calculate_participants(contest: Contest, participants: list, snapshot: timedelta=None):
    """
    :param contest
    :param participants: list of ContestParticipants
    :param snapshot: submissions in the first `snapshot` seconds will be effective
    :return
    {
        <user_id>: {
            penalty: int (seconds),
            score: int,
            is_confirmed: boolean
            detail: {
                <problem_id>: {
                    solved: boolean
{'fraction_non_alphanumeric': 0.11063388625592417, 'fraction_numerical': 0.002517772511848341, 'mean_word_length': 2.7579298831385644, 'pattern_counts': {'":': 12, '<': 11, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4784215', 'n_tokens_mistral': 2159, 'n_tokens_neox': 2077, 'n_words': 470}