新增AI分析
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import os
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from importlib import import_module
|
||||
|
||||
@@ -485,17 +484,17 @@ class UserProblemRankAPI(APIView):
|
||||
|
||||
problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, visible=True)
|
||||
submissions = Submission.objects.filter(problem=problem, result=JudgeStatus.ACCEPTED)
|
||||
all_ac_users = submissions.values("user_id").distinct()
|
||||
all_ac_count = len(all_ac_users)
|
||||
|
||||
all_ac_count = submissions.values("user_id").distinct().count()
|
||||
|
||||
class_name = user.class_name or ""
|
||||
class_ac_count = 0
|
||||
|
||||
if class_name:
|
||||
users = User.objects.filter(class_name=user.class_name, is_disabled=False).values_list("id", flat=True)
|
||||
submissions = submissions.filter(user_id__in=list(users))
|
||||
class_ac_users = submissions.values("user_id").distinct()
|
||||
class_ac_count = len(class_ac_users)
|
||||
user_ids = list(users)
|
||||
submissions = submissions.filter(user_id__in=user_ids)
|
||||
class_ac_count = submissions.values("user_id").distinct().count()
|
||||
|
||||
my_submissions = submissions.filter(user_id=user.id)
|
||||
|
||||
|
||||
0
ai/__init__.py
Normal file
0
ai/__init__.py
Normal file
6
ai/apps.py
Normal file
6
ai/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'ai'
|
||||
0
ai/migrations/__init__.py
Normal file
0
ai/migrations/__init__.py
Normal file
3
ai/models.py
Normal file
3
ai/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
0
ai/serializers.py
Normal file
0
ai/serializers.py
Normal file
0
ai/urls/__init__.py
Normal file
0
ai/urls/__init__.py
Normal file
13
ai/urls/oj.py
Normal file
13
ai/urls/oj.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.urls import path
|
||||
|
||||
from ..views.oj import (
|
||||
AIAnalysisAPI,
|
||||
AIDetailDataAPI,
|
||||
AIWeeklyDataAPI,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("ai/detail", AIDetailDataAPI.as_view()),
|
||||
path("ai/weekly", AIWeeklyDataAPI.as_view()),
|
||||
path("ai/analysis", AIAnalysisAPI.as_view()),
|
||||
]
|
||||
0
ai/views/__init__.py
Normal file
0
ai/views/__init__.py
Normal file
412
ai/views/oj.py
Normal file
412
ai/views/oj.py
Normal file
@@ -0,0 +1,412 @@
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta, datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from utils.api import APIView
|
||||
from django.db.models import Min
|
||||
from django.utils import timezone
|
||||
from django.core.cache import cache
|
||||
import hashlib
|
||||
|
||||
from account.models import User
|
||||
from problem.models import Problem
|
||||
from submission.models import Submission, JudgeStatus
|
||||
from account.decorators import login_required
|
||||
|
||||
|
||||
# 常量定义
|
||||
CACHE_TIMEOUT = 300 # 5分钟缓存
|
||||
DIFFICULTY_MAP = {"Low": "简单", "Mid": "中等", "High": "困难"}
|
||||
DEFAULT_CLASS_SIZE = 45
|
||||
|
||||
|
||||
def get_cache_key(prefix, *args):
|
||||
"""生成缓存键"""
|
||||
key_string = f"{prefix}:{'_'.join(map(str, args))}"
|
||||
return hashlib.md5(key_string.encode()).hexdigest()
|
||||
|
||||
|
||||
def get_difficulty(difficulty):
|
||||
return DIFFICULTY_MAP.get(difficulty, "中等")
|
||||
|
||||
|
||||
def get_grade(rank, submission_count):
|
||||
"""
|
||||
根据排名和提交人数计算等级
|
||||
只有三分之一的人完成,直接给到 S
|
||||
"""
|
||||
if not rank or rank <= 0 or submission_count <= 0:
|
||||
return "C"
|
||||
|
||||
if submission_count < DEFAULT_CLASS_SIZE // 3:
|
||||
return "S"
|
||||
|
||||
top_percent = round(rank / submission_count * 100)
|
||||
if top_percent < 20:
|
||||
return "S"
|
||||
elif top_percent < 50:
|
||||
return "A"
|
||||
elif top_percent < 85:
|
||||
return "B"
|
||||
else:
|
||||
return "C"
|
||||
|
||||
|
||||
def get_class_user_ids(user):
|
||||
"""获取班级用户ID列表"""
|
||||
if not user.class_name:
|
||||
return []
|
||||
|
||||
cache_key = get_cache_key("class_users", user.class_name)
|
||||
user_ids = cache.get(cache_key)
|
||||
|
||||
if user_ids is None:
|
||||
user_ids = list(
|
||||
User.objects.filter(class_name=user.class_name).values_list("id", flat=True)
|
||||
)
|
||||
cache.set(cache_key, user_ids, CACHE_TIMEOUT)
|
||||
|
||||
return user_ids
|
||||
|
||||
|
||||
def get_user_first_ac_submissions(
|
||||
user_id, start, end, class_user_ids=None, use_class_scope=False
|
||||
):
|
||||
"""获取用户首次AC提交记录"""
|
||||
base_qs = Submission.objects.filter(
|
||||
result=JudgeStatus.ACCEPTED,
|
||||
create_time__gte=start,
|
||||
create_time__lte=end,
|
||||
)
|
||||
|
||||
if use_class_scope and class_user_ids:
|
||||
base_qs = base_qs.filter(user_id__in=class_user_ids)
|
||||
|
||||
# 获取用户首次AC
|
||||
user_first_ac = list(
|
||||
base_qs.filter(user_id=user_id)
|
||||
.values("problem_id")
|
||||
.annotate(first_ac_time=Min("create_time"))
|
||||
)
|
||||
|
||||
if not user_first_ac:
|
||||
return [], {}, []
|
||||
|
||||
# 获取相关题目的所有首次AC记录用于排名
|
||||
problem_ids = [item["problem_id"] for item in user_first_ac]
|
||||
ranked_first_ac = list(
|
||||
base_qs.filter(problem_id__in=problem_ids)
|
||||
.values("user_id", "problem_id")
|
||||
.annotate(first_ac_time=Min("create_time"))
|
||||
)
|
||||
|
||||
# 按题目分组并排序
|
||||
by_problem = defaultdict(list)
|
||||
for item in ranked_first_ac:
|
||||
by_problem[item["problem_id"]].append(item)
|
||||
|
||||
for _, arr in by_problem.items():
|
||||
arr.sort(key=lambda x: (x["first_ac_time"], x["user_id"]))
|
||||
|
||||
return user_first_ac, by_problem, problem_ids
|
||||
|
||||
|
||||
class AIDetailDataAPI(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
start = request.GET.get("start")
|
||||
end = request.GET.get("end")
|
||||
username = request.GET.get("username")
|
||||
|
||||
if username:
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
else:
|
||||
user = request.user
|
||||
|
||||
# 检查缓存
|
||||
cache_key = get_cache_key(
|
||||
"ai_detail", user.id, user.class_name or "", start, end
|
||||
)
|
||||
cached_result = cache.get(cache_key)
|
||||
if cached_result:
|
||||
return self.success(cached_result)
|
||||
|
||||
# 获取班级用户ID
|
||||
class_user_ids = get_class_user_ids(user)
|
||||
use_class_scope = bool(user.class_name) and len(class_user_ids) > 1
|
||||
|
||||
# 获取用户首次AC记录
|
||||
user_first_ac, by_problem, problem_ids = get_user_first_ac_submissions(
|
||||
user.id, start, end, class_user_ids, use_class_scope
|
||||
)
|
||||
|
||||
if not user_first_ac:
|
||||
result = {
|
||||
"user": user.username,
|
||||
"class_name": user.class_name,
|
||||
"start": start,
|
||||
"end": end,
|
||||
"solved": [],
|
||||
"grade": "",
|
||||
"tags": {},
|
||||
"difficulty": {},
|
||||
"contest_count": 0,
|
||||
}
|
||||
cache.set(cache_key, result, CACHE_TIMEOUT)
|
||||
return self.success(result)
|
||||
|
||||
# 优化的题目查询 - 一次性获取所有需要的数据
|
||||
problems = self._get_problems_with_data(problem_ids)
|
||||
|
||||
# 构建解题记录
|
||||
solved, contest_ids = self._build_solved_records(
|
||||
user_first_ac, by_problem, problems, user.id
|
||||
)
|
||||
|
||||
# 计算统计数据
|
||||
avg_grade = self._calculate_average_grade(solved)
|
||||
tags = self._calculate_top_tags(problems.values())
|
||||
difficulty = self._calculate_difficulty_distribution(problems.values())
|
||||
|
||||
result = {
|
||||
"user": user.username,
|
||||
"class_name": user.class_name,
|
||||
"start": start,
|
||||
"end": end,
|
||||
"solved": solved,
|
||||
"grade": avg_grade,
|
||||
"tags": tags,
|
||||
"difficulty": difficulty,
|
||||
"contest_count": len(set(contest_ids)),
|
||||
}
|
||||
|
||||
# 缓存结果
|
||||
cache.set(cache_key, result, CACHE_TIMEOUT)
|
||||
return self.success(result)
|
||||
|
||||
def _get_problems_with_data(self, problem_ids):
|
||||
"""优化的题目数据获取"""
|
||||
problem_qs = (
|
||||
Problem.objects.filter(id__in=problem_ids)
|
||||
.select_related("contest")
|
||||
.prefetch_related("tags")
|
||||
)
|
||||
return {p.id: p for p in problem_qs}
|
||||
|
||||
def _build_solved_records(self, user_first_ac, by_problem, problems, user_id):
|
||||
"""构建解题记录"""
|
||||
solved = []
|
||||
contest_ids = []
|
||||
|
||||
for item in user_first_ac:
|
||||
pid = item["problem_id"]
|
||||
ranking_list = by_problem.get(pid, [])
|
||||
|
||||
# 查找用户排名
|
||||
rank = None
|
||||
for idx, rec in enumerate(ranking_list):
|
||||
if rec["user_id"] == user_id:
|
||||
rank = idx + 1
|
||||
break
|
||||
|
||||
problem = problems.get(pid)
|
||||
if not problem:
|
||||
continue
|
||||
|
||||
grade = get_grade(rank, len(ranking_list))
|
||||
|
||||
if problem.contest_id:
|
||||
contest_ids.append(problem.contest_id)
|
||||
|
||||
solved.append(
|
||||
{
|
||||
"problem": {
|
||||
"display_id": problem._id,
|
||||
"title": problem.title,
|
||||
"contest_id": problem.contest_id,
|
||||
"contest_title": getattr(problem.contest, "title", ""),
|
||||
},
|
||||
"ac_time": timezone.localtime(item["first_ac_time"]).isoformat(),
|
||||
"rank": rank,
|
||||
"ac_count": len(ranking_list),
|
||||
"grade": grade,
|
||||
}
|
||||
)
|
||||
|
||||
# 按AC时间排序
|
||||
solved.sort(key=lambda x: x["ac_time"])
|
||||
return solved, contest_ids
|
||||
|
||||
def _calculate_average_grade(self, solved):
|
||||
"""计算平均等级(出现次数最多的等级)"""
|
||||
if not solved:
|
||||
return ""
|
||||
|
||||
grade_count = defaultdict(int)
|
||||
for s in solved:
|
||||
grade_count[s["grade"]] += 1
|
||||
|
||||
return max(grade_count, key=grade_count.get)
|
||||
|
||||
def _calculate_top_tags(self, problems):
|
||||
"""计算标签TOP5"""
|
||||
tags_counter = defaultdict(int)
|
||||
for problem in problems:
|
||||
for tag in problem.tags.all():
|
||||
if tag.name:
|
||||
tags_counter[tag.name] += 1
|
||||
|
||||
top_tags = sorted(tags_counter.items(), key=lambda x: x[1], reverse=True)[:5]
|
||||
return {name: count for name, count in top_tags}
|
||||
|
||||
def _calculate_difficulty_distribution(self, problems):
|
||||
"""计算难度分布"""
|
||||
diff_counter = {"Low": 0, "Mid": 0, "High": 0}
|
||||
for problem in problems:
|
||||
key = problem.difficulty if problem.difficulty in diff_counter else "Mid"
|
||||
diff_counter[key] += 1
|
||||
|
||||
diff_sorted = sorted(diff_counter.items(), key=lambda x: x[1], reverse=True)
|
||||
return {get_difficulty(k): v for k, v in diff_sorted}
|
||||
|
||||
|
||||
class AIWeeklyDataAPI(APIView):
|
||||
@login_required
|
||||
def get(self, request):
|
||||
end_iso = request.GET.get("end")
|
||||
duration = request.GET.get("duration")
|
||||
username = request.GET.get("username")
|
||||
|
||||
if username:
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
return self.error("User does not exist")
|
||||
else:
|
||||
user = request.user
|
||||
|
||||
# 检查缓存
|
||||
cache_key = get_cache_key(
|
||||
"ai_weekly", user.id, user.class_name or "", end_iso, duration
|
||||
)
|
||||
cached_result = cache.get(cache_key)
|
||||
if cached_result:
|
||||
return self.success(cached_result)
|
||||
|
||||
# 获取班级用户ID
|
||||
class_user_ids = get_class_user_ids(user)
|
||||
use_class_scope = bool(user.class_name) and len(class_user_ids) > 1
|
||||
|
||||
# 解析时间参数
|
||||
time_config = self._parse_duration(duration)
|
||||
start = datetime.fromisoformat(end_iso) - time_config["total_delta"]
|
||||
|
||||
weekly_data = []
|
||||
for i in range(time_config["show_count"]):
|
||||
start = start + time_config["delta"]
|
||||
period_end = start + time_config["delta"]
|
||||
|
||||
period_data = {
|
||||
"unit": time_config["show_unit"],
|
||||
"index": time_config["show_count"] - 1 - i,
|
||||
"start": start.isoformat(),
|
||||
"end": period_end.isoformat(),
|
||||
"problem_count": 0,
|
||||
"submission_count": 0,
|
||||
"grade": "",
|
||||
}
|
||||
|
||||
# 获取提交数量
|
||||
submission_count = Submission.objects.filter(
|
||||
user_id=user.id,
|
||||
create_time__gte=start,
|
||||
create_time__lte=period_end,
|
||||
).count()
|
||||
|
||||
period_data["submission_count"] = submission_count
|
||||
|
||||
if submission_count == 0:
|
||||
weekly_data.append(period_data)
|
||||
continue
|
||||
|
||||
# 获取AC记录和等级
|
||||
user_first_ac, by_problem, problem_ids = get_user_first_ac_submissions(
|
||||
user.id,
|
||||
start.isoformat(),
|
||||
period_end.isoformat(),
|
||||
class_user_ids,
|
||||
use_class_scope,
|
||||
)
|
||||
|
||||
if user_first_ac:
|
||||
period_data["problem_count"] = len(problem_ids)
|
||||
period_data["grade"] = self._calculate_period_grade(
|
||||
user_first_ac, by_problem, user.id
|
||||
)
|
||||
|
||||
weekly_data.append(period_data)
|
||||
|
||||
# 缓存结果
|
||||
cache.set(cache_key, weekly_data, CACHE_TIMEOUT)
|
||||
return self.success(weekly_data)
|
||||
|
||||
def _parse_duration(self, duration):
|
||||
unit, count = duration.split(":")
|
||||
count = int(count)
|
||||
|
||||
# 默认配置
|
||||
show_count = 4
|
||||
show_unit = "weeks"
|
||||
total_delta = timedelta(weeks=show_count+1)
|
||||
delta = timedelta(weeks=1)
|
||||
|
||||
if unit == "months" and count == 2:
|
||||
# 过去八周
|
||||
show_count = 8
|
||||
total_delta = timedelta(weeks=9)
|
||||
elif unit == "months" and count == 6:
|
||||
# 过去六个月
|
||||
show_count = 6
|
||||
show_unit = "months"
|
||||
total_delta = relativedelta(months=7)
|
||||
delta = relativedelta(months=1)
|
||||
elif unit == "years":
|
||||
# 过去一年
|
||||
show_count = 12
|
||||
show_unit = "months"
|
||||
total_delta = relativedelta(months=13)
|
||||
delta = relativedelta(months=1)
|
||||
|
||||
return {
|
||||
"show_count": show_count,
|
||||
"show_unit": show_unit,
|
||||
"total_delta": total_delta,
|
||||
"delta": delta,
|
||||
}
|
||||
|
||||
def _calculate_period_grade(self, user_first_ac, by_problem, user_id):
|
||||
"""计算周期内的等级"""
|
||||
grade_count = defaultdict(int)
|
||||
|
||||
for item in user_first_ac:
|
||||
pid = item["problem_id"]
|
||||
ranking_list = by_problem.get(pid, [])
|
||||
|
||||
# 查找用户排名
|
||||
rank = None
|
||||
for idx, rec in enumerate(ranking_list):
|
||||
if rec["user_id"] == user_id:
|
||||
rank = idx + 1
|
||||
break
|
||||
|
||||
grade = get_grade(rank, len(ranking_list))
|
||||
grade_count[grade] += 1
|
||||
|
||||
return max(grade_count, key=grade_count.get) if grade_count else ""
|
||||
|
||||
|
||||
class AIAnalysisAPI(APIView):
|
||||
def get(self, request):
|
||||
return self.success("AI分析")
|
||||
@@ -1,7 +1,10 @@
|
||||
annotated-types==0.7.0
|
||||
anyio==4.10.0
|
||||
asgiref==3.8.1
|
||||
certifi==2025.4.26
|
||||
charset-normalizer==3.4.2
|
||||
coverage==6.5.0
|
||||
distro==1.9.0
|
||||
django==5.2.3
|
||||
django-cas-ng==5.0.1
|
||||
django-dbconn-retry==0.1.8
|
||||
@@ -15,10 +18,15 @@ flake8==7.0.0
|
||||
flake8-coding==1.3.2
|
||||
flake8-quotes==3.3.2
|
||||
gunicorn==22.0.0
|
||||
h11==0.16.0
|
||||
httpcore==1.0.9
|
||||
httpx==0.28.1
|
||||
idna==3.10
|
||||
jiter==0.11.0
|
||||
jsonfield==3.1.0
|
||||
lxml==5.4.0
|
||||
mccabe==0.7.0
|
||||
openai==1.108.1
|
||||
otpauth==1.0.1
|
||||
packaging==25.0
|
||||
pillow==10.2.0
|
||||
@@ -26,6 +34,8 @@ prometheus-client==0.22.1
|
||||
psycopg==3.2.9
|
||||
psycopg-binary==3.2.9
|
||||
pycodestyle==2.11.1
|
||||
pydantic==2.11.9
|
||||
pydantic-core==2.33.2
|
||||
pyflakes==3.2.0
|
||||
python-cas==1.6.0
|
||||
python-dateutil==2.8.2
|
||||
@@ -34,7 +44,10 @@ raven==6.10.0
|
||||
redis==6.2.0
|
||||
requests==2.32.4
|
||||
six==1.17.0
|
||||
sniffio==1.3.1
|
||||
sqlparse==0.5.3
|
||||
tqdm==4.67.1
|
||||
typing-extensions==4.14.0
|
||||
typing-inspection==0.4.1
|
||||
urllib3==2.4.0
|
||||
xlsxwriter==3.2.0
|
||||
|
||||
@@ -7,8 +7,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"HOST": "10.13.114.114",
|
||||
"PORT": "5433",
|
||||
"HOST": "150.158.29.156",
|
||||
"PORT": "5432",
|
||||
"NAME": "onlinejudge",
|
||||
"USER": "onlinejudge",
|
||||
"PASSWORD": "onlinejudge",
|
||||
@@ -16,7 +16,7 @@ DATABASES = {
|
||||
}
|
||||
|
||||
REDIS_CONF = {
|
||||
"host": "10.13.114.114",
|
||||
"host": "150.158.29.156",
|
||||
"port": 6379,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,4 +19,5 @@ urlpatterns = [
|
||||
path("api/admin/", include("comment.urls.admin")),
|
||||
path("api/", include("tutorial.urls.tutorial")),
|
||||
path("api/admin/", include("tutorial.urls.admin")),
|
||||
path("api/", include("ai.urls.oj")),
|
||||
]
|
||||
|
||||
@@ -20,6 +20,7 @@ dependencies = [
|
||||
"flake8-quotes==3.3.2",
|
||||
"gunicorn==22.0.0",
|
||||
"jsonfield==3.1.0",
|
||||
"openai>=1.108.1",
|
||||
"otpauth==1.0.1",
|
||||
"pillow==10.2.0",
|
||||
"psycopg==3.2.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from account.decorators import super_admin_required
|
||||
from judge.tasks import judge_task
|
||||
# from judge.dispatcher import JudgeDispatcher
|
||||
|
||||
from utils.api import APIView
|
||||
from ..models import Submission, JudgeStatus
|
||||
from account.models import User, AdminType
|
||||
@@ -8,6 +8,12 @@ from problem.models import Problem
|
||||
from django.db.models import Count, Q
|
||||
|
||||
|
||||
def get_real_name(username, class_name):
|
||||
if class_name and username.startswith("ks"):
|
||||
return username[len(f"ks{class_name}"):]
|
||||
return username
|
||||
|
||||
|
||||
class SubmissionRejudgeAPI(APIView):
|
||||
@super_admin_required
|
||||
def get(self, request):
|
||||
@@ -15,7 +21,9 @@ class SubmissionRejudgeAPI(APIView):
|
||||
if not id:
|
||||
return self.error("Parameter error, id is required")
|
||||
try:
|
||||
submission = Submission.objects.select_related("problem").get(id=id, contest_id__isnull=True)
|
||||
submission = Submission.objects.select_related("problem").get(
|
||||
id=id, contest_id__isnull=True
|
||||
)
|
||||
except Submission.DoesNotExist:
|
||||
return self.error("Submission does not exists")
|
||||
submission.statistic_info = {}
|
||||
@@ -24,6 +32,7 @@ class SubmissionRejudgeAPI(APIView):
|
||||
judge_task.send(submission.id, submission.problem.id)
|
||||
return self.success()
|
||||
|
||||
|
||||
class SubmissionStatisticsAPI(APIView):
|
||||
@super_admin_required
|
||||
def get(self, request):
|
||||
@@ -33,72 +42,112 @@ class SubmissionStatisticsAPI(APIView):
|
||||
if not start or not end:
|
||||
return self.error("start and end is required")
|
||||
|
||||
submissions = Submission.objects.filter(contest_id__isnull=True,
|
||||
create_time__gte=start,
|
||||
create_time__lte=end).select_related("problem__created_by")
|
||||
submissions = Submission.objects.filter(
|
||||
contest_id__isnull=True, create_time__gte=start, create_time__lte=end
|
||||
).select_related("problem__created_by")
|
||||
|
||||
problem_id = request.GET.get("problem_id")
|
||||
|
||||
if problem_id:
|
||||
try:
|
||||
problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, visible=True)
|
||||
problem = Problem.objects.get(
|
||||
_id=problem_id, contest_id__isnull=True, visible=True
|
||||
)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem doesn't exist")
|
||||
submissions = submissions.filter(problem=problem)
|
||||
|
||||
username = request.GET.get("username")
|
||||
|
||||
# 统计人数
|
||||
person_count = 0
|
||||
all_persons = []
|
||||
all_users_dict = {}
|
||||
if username:
|
||||
submissions = submissions.filter(username__icontains=username)
|
||||
all_persons = User.objects.filter(username__icontains=username,
|
||||
all_users_dict = {
|
||||
user["username"]: user["class_name"]
|
||||
for user in User.objects.filter(
|
||||
username__icontains=username,
|
||||
is_disabled=False,
|
||||
admin_type=AdminType.REGULAR_USER).values_list("username", flat=True)
|
||||
person_count = all_persons.count()
|
||||
admin_type=AdminType.REGULAR_USER,
|
||||
).values("username", "class_name")
|
||||
}
|
||||
|
||||
submission_count = submissions.count()
|
||||
accepted_count = submissions.filter(result=JudgeStatus.ACCEPTED).count()
|
||||
# 优化:一次性获取所有统计数据
|
||||
submission_stats = submissions.aggregate(
|
||||
total_count=Count("id"),
|
||||
accepted_count=Count("id", filter=Q(result=JudgeStatus.ACCEPTED)),
|
||||
)
|
||||
submission_count = submission_stats["total_count"]
|
||||
accepted_count = submission_stats["accepted_count"]
|
||||
correct_rate = (
|
||||
round(accepted_count / submission_count * 100, 2) if submission_count else 0
|
||||
)
|
||||
|
||||
try:
|
||||
correct_rate = round(accepted_count/submission_count*100, 2)
|
||||
except ZeroDivisionError:
|
||||
correct_rate = 0
|
||||
# 优化:获取用户提交统计
|
||||
user_submissions = (
|
||||
submissions.values("username")
|
||||
.annotate(
|
||||
submission_count=Count("id"),
|
||||
accepted_count=Count("id", filter=Q(result=JudgeStatus.ACCEPTED)),
|
||||
)
|
||||
.order_by("-submission_count")
|
||||
)
|
||||
|
||||
counts = submissions.values("username").annotate(submission_count=Count("id", distinct=True),
|
||||
accepted_count=Count("id", filter=Q(result=JudgeStatus.ACCEPTED), distinct=True),
|
||||
).order_by("-submission_count")
|
||||
# 获取所有有提交记录的用户的class_name信息
|
||||
submitted_usernames = {item["username"] for item in user_submissions}
|
||||
if submitted_usernames:
|
||||
submitted_users_dict = {
|
||||
user["username"]: user["class_name"]
|
||||
for user in User.objects.filter(
|
||||
username__in=submitted_usernames
|
||||
).values("username", "class_name")
|
||||
}
|
||||
else:
|
||||
submitted_users_dict = {}
|
||||
|
||||
# 处理有提交记录的用户
|
||||
accepted = []
|
||||
for item in counts:
|
||||
|
||||
for item in user_submissions:
|
||||
username_key = item["username"]
|
||||
|
||||
if item["accepted_count"] > 0:
|
||||
rate = round(item["accepted_count"] / item["submission_count"] * 100, 2)
|
||||
item["correct_rate"] = f"{rate}%"
|
||||
accepted.append(item)
|
||||
accepted.append(
|
||||
{
|
||||
"username": username_key,
|
||||
"class_name": submitted_users_dict.get(username_key),
|
||||
"submission_count": item["submission_count"],
|
||||
"accepted_count": item["accepted_count"],
|
||||
"correct_rate": f"{rate}%",
|
||||
}
|
||||
)
|
||||
|
||||
# 处理无提交记录的用户,只返回姓名列表
|
||||
unaccepted = []
|
||||
if len(accepted) > 0:
|
||||
unaccepted = list(set(all_persons) - set([item['username'] for item in accepted]))
|
||||
if all_users_dict:
|
||||
unaccepted_usernames = set(all_users_dict.keys()) - submitted_usernames
|
||||
for username in unaccepted_usernames:
|
||||
class_name = all_users_dict[username]
|
||||
real_name = get_real_name(username, class_name)
|
||||
unaccepted.append(real_name)
|
||||
|
||||
# 统计人数完成率
|
||||
# 计算人数完成率
|
||||
person_count = len(all_users_dict) if all_users_dict else 0
|
||||
person_rate = 0
|
||||
if person_count:
|
||||
person_rate = round(len(accepted)/person_count*100, 2)
|
||||
# 下面是做一些超出 100% 的操作,比如有人已经删号了,但是提交记录还在
|
||||
if person_rate >= 100:
|
||||
person_rate = 100
|
||||
# 搜出来的人数比提交人数还多的情况
|
||||
person_rate = min(100, round(len(accepted) / person_count * 100, 2))
|
||||
# 处理已删除用户但提交记录仍存在的情况
|
||||
if person_count < len(accepted):
|
||||
person_count = len(accepted)
|
||||
|
||||
return self.success({
|
||||
return self.success(
|
||||
{
|
||||
"submission_count": submission_count,
|
||||
"accepted_count": accepted_count,
|
||||
"correct_rate": f"{correct_rate}%",
|
||||
"person_count": person_count,
|
||||
"person_rate": f"{person_rate}%",
|
||||
"data": accepted,
|
||||
"data_unaccepted": unaccepted
|
||||
})
|
||||
|
||||
"data_unaccepted": unaccepted,
|
||||
}
|
||||
)
|
||||
|
||||
230
uv.lock
generated
230
uv.lock
generated
@@ -1,7 +1,30 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.8.1"
|
||||
@@ -70,6 +93,15 @@ version = "6.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/66/38d1870cb7cf62da49add1d6803fdbcdef632b2808b5c80bcac35b7634d8/coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84", size = 775224, upload-time = "2022-09-29T20:05:58.509Z" }
|
||||
|
||||
[[package]]
|
||||
name = "distro"
|
||||
version = "1.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.2.3"
|
||||
@@ -217,6 +249,43 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/29/97/6d610ae77b5633d24b69c2ff1ac3044e0e565ecbd1ec188f02c45073054c/gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9", size = 84443, upload-time = "2024-04-16T22:58:15.233Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "certifi" },
|
||||
{ name = "httpcore" },
|
||||
{ name = "idna" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
@@ -226,6 +295,54 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiter"
|
||||
version = "0.11.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9d/c0/a3bb4cc13aced219dd18191ea66e874266bd8aa7b96744e495e1c733aa2d/jiter-0.11.0.tar.gz", hash = "sha256:1d9637eaf8c1d6a63d6562f2a6e5ab3af946c66037eb1b894e8fad75422266e4", size = 167094, upload-time = "2025-09-15T09:20:38.212Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/b5/3009b112b8f673e568ef79af9863d8309a15f0a8cdcc06ed6092051f377e/jiter-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb7b377688cc3850bbe5c192a6bd493562a0bc50cbc8b047316428fbae00ada", size = 305510, upload-time = "2025-09-15T09:19:25.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/82/15514244e03b9e71e086bbe2a6de3e4616b48f07d5f834200c873956fb8c/jiter-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1b7cbe3f25bd0d8abb468ba4302a5d45617ee61b2a7a638f63fee1dc086be99", size = 316521, upload-time = "2025-09-15T09:19:27.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/94/7a2e905f40ad2d6d660e00b68d818f9e29fb87ffe82774f06191e93cbe4a/jiter-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0a7f0ec81d5b7588c5cade1eb1925b91436ae6726dc2df2348524aeabad5de6", size = 338214, upload-time = "2025-09-15T09:19:28.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/9c/5791ed5bdc76f12110158d3316a7a3ec0b1413d018b41c5ed399549d3ad5/jiter-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07630bb46ea2a6b9c6ed986c6e17e35b26148cce2c535454b26ee3f0e8dcaba1", size = 361280, upload-time = "2025-09-15T09:19:30.013Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/7f/b7d82d77ff0d2cb06424141000176b53a9e6b16a1125525bb51ea4990c2e/jiter-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7764f27d28cd4a9cbc61704dfcd80c903ce3aad106a37902d3270cd6673d17f4", size = 487895, upload-time = "2025-09-15T09:19:31.424Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/44/10a1475d46f1fc1fd5cc2e82c58e7bca0ce5852208e0fa5df2f949353321/jiter-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4a6c4a737d486f77f842aeb22807edecb4a9417e6700c7b981e16d34ba7c72", size = 378421, upload-time = "2025-09-15T09:19:32.746Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/5f/0dc34563d8164d31d07bc09d141d3da08157a68dcd1f9b886fa4e917805b/jiter-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf408d2a0abd919b60de8c2e7bc5eeab72d4dafd18784152acc7c9adc3291591", size = 347932, upload-time = "2025-09-15T09:19:34.612Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/de/b68f32a4fcb7b4a682b37c73a0e5dae32180140cd1caf11aef6ad40ddbf2/jiter-0.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cdef53eda7d18e799625023e1e250dbc18fbc275153039b873ec74d7e8883e09", size = 386959, upload-time = "2025-09-15T09:19:35.994Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/0a/c08c92e713b6e28972a846a81ce374883dac2f78ec6f39a0dad9f2339c3a/jiter-0.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:53933a38ef7b551dd9c7f1064f9d7bb235bb3168d0fa5f14f0798d1b7ea0d9c5", size = 517187, upload-time = "2025-09-15T09:19:37.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/b5/4a283bec43b15aad54fcae18d951f06a2ec3f78db5708d3b59a48e9c3fbd/jiter-0.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11840d2324c9ab5162fc1abba23bc922124fedcff0d7b7f85fffa291e2f69206", size = 509461, upload-time = "2025-09-15T09:19:38.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/a5/f8bad793010534ea73c985caaeef8cc22dfb1fedb15220ecdf15c623c07a/jiter-0.11.0-cp312-cp312-win32.whl", hash = "sha256:4f01a744d24a5f2bb4a11657a1b27b61dc038ae2e674621a74020406e08f749b", size = 206664, upload-time = "2025-09-15T09:19:40.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/42/5823ec2b1469395a160b4bf5f14326b4a098f3b6898fbd327366789fa5d3/jiter-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:29fff31190ab3a26de026da2f187814f4b9c6695361e20a9ac2123e4d4378a4c", size = 203520, upload-time = "2025-09-15T09:19:41.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c4/d530e514d0f4f29b2b68145e7b389cbc7cac7f9c8c23df43b04d3d10fa3e/jiter-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4441a91b80a80249f9a6452c14b2c24708f139f64de959943dfeaa6cb915e8eb", size = 305021, upload-time = "2025-09-15T09:19:43.523Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/77/796a19c567c5734cbfc736a6f987affc0d5f240af8e12063c0fb93990ffa/jiter-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ff85fc6d2a431251ad82dbd1ea953affb5a60376b62e7d6809c5cd058bb39471", size = 314384, upload-time = "2025-09-15T09:19:44.849Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/9c/824334de0b037b91b6f3fa9fe5a191c83977c7ec4abe17795d3cb6d174cf/jiter-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5e86126d64706fd28dfc46f910d496923c6f95b395138c02d0e252947f452bd", size = 337389, upload-time = "2025-09-15T09:19:46.094Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/95/ed4feab69e6cf9b2176ea29d4ef9d01a01db210a3a2c8a31a44ecdc68c38/jiter-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad8bd82165961867a10f52010590ce0b7a8c53da5ddd8bbb62fef68c181b921", size = 360519, upload-time = "2025-09-15T09:19:47.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/0c/2ad00f38d3e583caba3909d95b7da1c3a7cd82c0aa81ff4317a8016fb581/jiter-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b42c2cd74273455ce439fd9528db0c6e84b5623cb74572305bdd9f2f2961d3df", size = 487198, upload-time = "2025-09-15T09:19:49.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/8b/919b64cf3499b79bdfba6036da7b0cac5d62d5c75a28fb45bad7819e22f0/jiter-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0062dab98172dd0599fcdbf90214d0dcde070b1ff38a00cc1b90e111f071982", size = 377835, upload-time = "2025-09-15T09:19:50.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/7f/8ebe15b6e0a8026b0d286c083b553779b4dd63db35b43a3f171b544de91d/jiter-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb948402821bc76d1f6ef0f9e19b816f9b09f8577844ba7140f0b6afe994bc64", size = 347655, upload-time = "2025-09-15T09:19:51.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/64/332127cef7e94ac75719dda07b9a472af6158ba819088d87f17f3226a769/jiter-0.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25a5b1110cca7329fd0daf5060faa1234be5c11e988948e4f1a1923b6a457fe1", size = 386135, upload-time = "2025-09-15T09:19:53.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/c8/557b63527442f84c14774159948262a9d4fabb0d61166f11568f22fc60d2/jiter-0.11.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bf11807e802a214daf6c485037778843fadd3e2ec29377ae17e0706ec1a25758", size = 516063, upload-time = "2025-09-15T09:19:54.447Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/13/4164c819df4a43cdc8047f9a42880f0ceef5afeb22e8b9675c0528ebdccd/jiter-0.11.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbb57da40631c267861dd0090461222060960012d70fd6e4c799b0f62d0ba166", size = 508139, upload-time = "2025-09-15T09:19:55.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/70/6e06929b401b331d41ddb4afb9f91cd1168218e3371972f0afa51c9f3c31/jiter-0.11.0-cp313-cp313-win32.whl", hash = "sha256:8e36924dad32c48d3c5e188d169e71dc6e84d6cb8dedefea089de5739d1d2f80", size = 206369, upload-time = "2025-09-15T09:19:57.048Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/0d/8185b8e15de6dce24f6afae63380e16377dd75686d56007baa4f29723ea1/jiter-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:452d13e4fd59698408087235259cebe67d9d49173b4dacb3e8d35ce4acf385d6", size = 202538, upload-time = "2025-09-15T09:19:58.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/3a/d61707803260d59520721fa326babfae25e9573a88d8b7b9cb54c5423a59/jiter-0.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:089f9df9f69532d1339e83142438668f52c97cd22ee2d1195551c2b1a9e6cf33", size = 313737, upload-time = "2025-09-15T09:19:59.638Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/cc/c9f0eec5d00f2a1da89f6bdfac12b8afdf8d5ad974184863c75060026457/jiter-0.11.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29ed1fe69a8c69bf0f2a962d8d706c7b89b50f1332cd6b9fbda014f60bd03a03", size = 346183, upload-time = "2025-09-15T09:20:01.442Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/87/fc632776344e7aabbab05a95a0075476f418c5d29ab0f2eec672b7a1f0ac/jiter-0.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a4d71d7ea6ea8786291423fe209acf6f8d398a0759d03e7f24094acb8ab686ba", size = 204225, upload-time = "2025-09-15T09:20:03.102Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/3b/e7f45be7d3969bdf2e3cd4b816a7a1d272507cd0edd2d6dc4b07514f2d9a/jiter-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9a6dff27eca70930bdbe4cbb7c1a4ba8526e13b63dc808c0670083d2d51a4a72", size = 304414, upload-time = "2025-09-15T09:20:04.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/32/13e8e0d152631fcc1907ceb4943711471be70496d14888ec6e92034e2caf/jiter-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ae2a7593a62132c7d4c2abbee80bbbb94fdc6d157e2c6cc966250c564ef774", size = 314223, upload-time = "2025-09-15T09:20:05.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/7e/abedd5b5a20ca083f778d96bba0d2366567fcecb0e6e34ff42640d5d7a18/jiter-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b13a431dba4b059e9e43019d3022346d009baf5066c24dcdea321a303cde9f0", size = 337306, upload-time = "2025-09-15T09:20:06.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/e2/30d59bdc1204c86aa975ec72c48c482fee6633120ee9c3ab755e4dfefea8/jiter-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:af62e84ca3889604ebb645df3b0a3f3bcf6b92babbff642bd214616f57abb93a", size = 360565, upload-time = "2025-09-15T09:20:08.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/88/567288e0d2ed9fa8f7a3b425fdaf2cb82b998633c24fe0d98f5417321aa8/jiter-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6f3b32bb723246e6b351aecace52aba78adb8eeb4b2391630322dc30ff6c773", size = 486465, upload-time = "2025-09-15T09:20:09.613Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/6e/7b72d09273214cadd15970e91dd5ed9634bee605176107db21e1e4205eb1/jiter-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:adcab442f4a099a358a7f562eaa54ed6456fb866e922c6545a717be51dbed7d7", size = 377581, upload-time = "2025-09-15T09:20:10.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/52/4db456319f9d14deed325f70102577492e9d7e87cf7097bda9769a1fcacb/jiter-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9967c2ab338ee2b2c0102fd379ec2693c496abf71ffd47e4d791d1f593b68e2", size = 347102, upload-time = "2025-09-15T09:20:12.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/b4/433d5703c38b26083aec7a733eb5be96f9c6085d0e270a87ca6482cbf049/jiter-0.11.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7d0bed3b187af8b47a981d9742ddfc1d9b252a7235471ad6078e7e4e5fe75c2", size = 386477, upload-time = "2025-09-15T09:20:13.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/7a/a60bfd9c55b55b07c5c441c5085f06420b6d493ce9db28d069cc5b45d9f3/jiter-0.11.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:f6fe0283e903ebc55f1a6cc569b8c1f3bf4abd026fed85e3ff8598a9e6f982f0", size = 516004, upload-time = "2025-09-15T09:20:14.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/46/f8363e5ecc179b4ed0ca6cb0a6d3bfc266078578c71ff30642ea2ce2f203/jiter-0.11.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5821e3d66606b29ae5b497230b304f1376f38137d69e35f8d2bd5f310ff73", size = 507855, upload-time = "2025-09-15T09:20:16.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/33/396083357d51d7ff0f9805852c288af47480d30dd31d8abc74909b020761/jiter-0.11.0-cp314-cp314-win32.whl", hash = "sha256:c2d13ba7567ca8799f17c76ed56b1d49be30df996eb7fa33e46b62800562a5e2", size = 205802, upload-time = "2025-09-15T09:20:17.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/ab/eb06ca556b2551d41de7d03bf2ee24285fa3d0c58c5f8d95c64c9c3281b1/jiter-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fb4790497369d134a07fc763cc88888c46f734abdd66f9fdf7865038bf3a8f40", size = 313405, upload-time = "2025-09-15T09:20:18.918Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/22/7ab7b4ec3a1c1f03aef376af11d23b05abcca3fb31fbca1e7557053b1ba2/jiter-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e2bbf24f16ba5ad4441a9845e40e4ea0cb9eed00e76ba94050664ef53ef4406", size = 347102, upload-time = "2025-09-15T09:20:20.16Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonfield"
|
||||
version = "3.1.0"
|
||||
@@ -309,6 +426,7 @@ dependencies = [
|
||||
{ name = "flake8-quotes" },
|
||||
{ name = "gunicorn" },
|
||||
{ name = "jsonfield" },
|
||||
{ name = "openai" },
|
||||
{ name = "otpauth" },
|
||||
{ name = "pillow" },
|
||||
{ name = "psycopg" },
|
||||
@@ -336,6 +454,7 @@ requires-dist = [
|
||||
{ name = "flake8-quotes", specifier = "==3.3.2" },
|
||||
{ name = "gunicorn", specifier = "==22.0.0" },
|
||||
{ name = "jsonfield", specifier = "==3.1.0" },
|
||||
{ name = "openai", specifier = ">=1.108.1" },
|
||||
{ name = "otpauth", specifier = "==1.0.1" },
|
||||
{ name = "pillow", specifier = "==10.2.0" },
|
||||
{ name = "psycopg", specifier = "==3.2.9" },
|
||||
@@ -346,6 +465,25 @@ requires-dist = [
|
||||
{ name = "xlsxwriter", specifier = "==3.2.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.108.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "distro" },
|
||||
{ name = "httpx" },
|
||||
{ name = "jiter" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/7a/3f2fbdf82a22d48405c1872f7c3176a705eee80ff2d2715d29472089171f/openai-1.108.1.tar.gz", hash = "sha256:6648468c1aec4eacfa554001e933a9fa075f57bacfc27588c2e34456cee9fef9", size = 563735, upload-time = "2025-09-19T16:52:20.399Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/38/87/6ad18ce0e7b910e3706480451df48ff9e0af3b55e5db565adafd68a0706a/openai-1.108.1-py3-none-any.whl", hash = "sha256:952fc027e300b2ac23be92b064eac136a2bc58274cec16f5d2906c361340d59b", size = 948394, upload-time = "2025-09-19T16:52:18.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "otpauth"
|
||||
version = "1.0.1"
|
||||
@@ -443,6 +581,63 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/90/a998c550d0ddd07e38605bb5c455d00fcc177a800ff9cc3dafdcb3dd7b56/pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67", size = 31132, upload-time = "2023-10-12T23:39:38.242Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.33.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.2.0"
|
||||
@@ -532,6 +727,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.3"
|
||||
@@ -541,6 +745,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.67.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.14.0"
|
||||
@@ -550,6 +766,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-inspection"
|
||||
version = "0.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
|
||||
Reference in New Issue
Block a user