This commit is contained in:
rngsurrounded
2025-03-03 20:11:13 +09:00
14 changed files with 410 additions and 15 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 30 KiB

@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import apps.achievement.models
import django.db.models.deletion
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import django.db.models.deletion
from django.db import migrations, models
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import apps.competition.models
import datetime
@@ -6,6 +6,7 @@ from django.contrib.auth.hashers import make_password
from django.core.files.base import ContentFile
from django.core.management.base import BaseCommand
from django.utils import timezone
from faker import Faker
from apps.competition.models import Competition, State
from apps.review.models import Reviewer
@@ -16,6 +17,7 @@ from apps.task.models import (
)
from apps.user.models import User, UserRole
faker = Faker("ru_RU")
class Command(BaseCommand):
help = "Generate sample data for Users, Competitions, Tasks, Submissions, and States."
@@ -44,11 +46,10 @@ class Command(BaseCommand):
def create_users(self, count):
users = []
for i in range(1, count + 1):
email = f"user{i}@example.com"
username = f"user{i}"
password = (
"password123" # In production, use proper password handling.
)
fake_profile = faker.profile()
email = fake_profile["email"]
username = fake_profile["username"]
password = faker.password()
role = random.choice(
[UserRole.STUDENT.value, UserRole.METODIST.value]
)
@@ -68,7 +69,7 @@ class Command(BaseCommand):
competitions = []
now = timezone.now()
for i in range(1, count + 1):
title = f"Competition {i}"
title = faker.sentence()
description = f"Description for competition {i}"
start_date = now - timedelta(days=random.randint(1, 10))
end_date = now + timedelta(days=random.randint(1, 10))
@@ -0,0 +1,380 @@
import random
import uuid
from datetime import timedelta, datetime
from django.contrib.auth.hashers import make_password
from django.core.files.base import ContentFile
from django.core.management.base import BaseCommand
from django.utils import timezone
from apps.competition.models import Competition, State
from apps.review.models import Reviewer
from apps.task.models import (
CompetitionTask,
CompetitionTaskCriteria,
CompetitionTaskSubmission,
)
from apps.user.models import User, UserRole
ans1 = ContentFile(
b"1984",
name=f"submission_{uuid.uuid4().hex}.txt",
)
ans2 = ContentFile(
b"3",
name=f"submission_{uuid.uuid4().hex}.txt",
)
now = timezone.now()
competitions = [
{
"obj": None, # докидывает в процессе
"title": "DANO. Финал",
"description": "Олимпиада по анализу данных от Т-Банка и ВШЭ",
"start_date": now - timedelta(days=2),
"end_date": now + timedelta(days=5),
"type": "competitive",
"participation_type": "solo",
"tasks": [
{
"obj": None,
"title": "Задача 1",
"description": """На маркетплейсе «Е-шопинг» продаются различные товары. Одна из задач аналитика —
прогнозировать, сколько товаров будет продаваться при определенной цене. В ходе
исследований и экспериментов был выявлен вид зависимости:
$Q(P) = Q_0 \times e^{E \times \frac{P_0 - P}{P_0}}$
где Q — это количество проданных единиц товара при цене P,
Q 0 — количество проданных единиц товара при цене P0 ,
E — коэффициент чувствительности количества проданных единиц товара к изменению
цены.
Найдите, сколько заработает продавец при цене по 3 000 ₽ за нож и сковороду
при условии, что себестоимость ножа — 1 000 ₽, а сковородки — 2 000 ₽.Ответ
округлите до целых.""".strip(),
"type": CompetitionTask.CompetitionTaskType.INPUT.value,
"points": 3,
"submission_reviewers_count": 2,
"max_attempts": 20,
"correct_answer_file": ans1
},
{
"obj": None,
"title": "Задача 2",
"description": """Небольшой интернет-магазин собрал данные о действиях пользователей на своем сайте
за последние несколько месяцев.
ecommerce_logs.csv — журнал действий пользователей:
• user_id — идентификатор пользователя.
• action — тип действия пользователя:
— visit — посещение сайта;
— click — клик на карточку товара;
— cart — добавление товара в корзину;
— delete — удаление товара из корзины;
— purchase — покупка товаров.
• date_time — время совершения действия.
• product_id — идентификатор товара.
• quantity — количество добавленного в корзину товара.
• delivery_price — стоимость доставки.
• sex — пол пользователя.
• region — регион пользователя.
• price — цена товара.
Вам нужно изучить воронку конверсии, которая показывает, как пользователи переходят
от одного шага к другому на сайте. В нашем случае воронка состоит из следующих шагов:
1. Посещение сайта.
2. Просмотр карточки товара.
3. Добавление товара в корзину.
4. Покупка.
1. Посещение сайта.
2. Просмотр карточки товара.
3. Добавление товара в корзину.
4. Покупка.
3 / 11
1.) Посчитайте конверсию (округлите ответ до 3 знаков после запятой):
• Из визита на сайт в клик на карточку товара.
• Из клика в добавление в корзину.
• Из добавления в корзину в покупку.
• Из визита на сайт в добавление в корзину.
• Из визита на сайт в покупку.
2. Постройте воронку конверсии с помощью столбчатой диаграммы:
• По оси X — шаги воронки.
• По оси Y — количество уникальных пользователей на каждом шаге.
3. Определите, на каком этапе конверсия из предыдущего шага ниже всего.
Сформулируйте одну гипотезу, связанную с поведением пользователей, которая
может объяснить падение конверсии именно на этом этапе. Обоснуйте механизм
работы приведенной гипотезы.
4. Постройте график динамики (по оси X — дни) для каждой из конверсий:
• Конверсия из визита в клик.
• Конверсия из визита в добавление в корзину.
• Конверсия из визита в покупку.
5. На графике найдите просадку конверсии: укажите, какая конверсия просела
и в какой примерно период это произошло (допустимая погрешность — 1–3
дня).
6. Чем вызвано снижение конверсии в этот период? Какие изменения в бизнесе
или поведении пользователей могли бы объяснить это? Ответьте на оба
вопроса, опираясь на данные.
""".strip(),
"type": CompetitionTask.CompetitionTaskType.REVIEW.value,
"points": 10,
"submission_reviewers_count": 2,
"max_attempts": 1,
"criteries": [
{
"obj": None,
"name": "Обоснованность решения",
"slug": "validity",
"description": "Аргументация",
"max_value": 5
},
{
"obj": None,
"name": "Правильность",
"slug": "correctness",
"description": "Насколько точные и верные ответы были представлены.",
"max_value": 5
}
]
},
{
"obj": None,
"title": "Задача 3",
"description": """
Напишите "hello_dano" на питоне
""".strip(),
"type": CompetitionTask.CompetitionTaskType.CHECKER.value,
"points": 25,
"submission_reviewers_count": 2,
"max_attempts": 50,
}
]
},
{
"obj": None,
"title": "PRODANO. Тур 5",
"description": "Олимпиада по олимпиаде DANO",
"start_date": now - timedelta(days=10),
"end_date": now + timedelta(days=50),
"type": "edu",
"participation_type": "solo",
"tasks": [
{
"obj": None,
"title": "Задача 1",
"description": """Сколько этапов в DANO?""".strip(),
"type": CompetitionTask.CompetitionTaskType.INPUT.value,
"points": 3,
"submission_reviewers_count": 2,
"max_attempts": 20,
"correct_answer_file": ans2
},
{
"obj": None,
"title": "Задача 2",
"description": """
Напишите отзыв про DANO(Хороший)
""".strip(),
"type": CompetitionTask.CompetitionTaskType.REVIEW.value,
"points": 15,
"submission_reviewers_count": 2,
"max_attempts": 1,
"criteries": [
{
"obj": None,
"name": "Хорошесть отзыва",
"slug": "validity",
"description": "Хорошесть",
"max_value": 10
},
{
"obj": None,
"name": "Подробность",
"slug": "detail",
"description": "Насколько подробно расписан ответ.",
"max_value": 5
}
]
},
{
"obj": None,
"title": "Задача 4",
"description": """
Напишите выведите 1+3 на питоне
""".strip(),
"type": CompetitionTask.CompetitionTaskType.CHECKER.value,
"points": 30,
"submission_reviewers_count": 2,
"max_attempts": 100,
}
]
}
]
users = [
{
"email": "germanivanov1984@gmail.com",
"username": "germanivanov",
"password": "password123!",
"role": UserRole.STUDENT.value,
},
{
"email": "dreamonovich@gmail.com",
"username": "dreamonovich",
"password": "password123!",
"role": UserRole.STUDENT.value,
}
]
reviewers = [
{
"name": "Владислав",
"surname": "Пикиневич",
"token": "pikinevich"
},
{
"name": "Александр",
"surname": "Шахов",
"token": "ashakhov"
}
]
class Command(BaseCommand):
help = "Generate sample data for Users, Competitions, Tasks, Submissions, and States."
def handle(self, *args, **options):
self.stdout.write("Starting data generation...")
users = self.create_users(5)
competitions = self.create_competitions(2, users)
self.reviewers = self.create_reviewers(2)
tasks = self.create_tasks()
self.create_incorrect_submissions(tasks, users)
self.create_states(competitions, users)
self.stdout.write("Data generation completed.")
def create_reviewers(self, count):
reviewers_objs = []
for reviewer in reviewers:
name = reviewer['name']
surname = reviewer['surname']
token = reviewer['token']
reviewer_obj = Reviewer(name=name, surname=surname, token=token)
reviewer_obj.save()
reviewers_objs.append(reviewer_obj)
return reviewers_objs
def create_users(self, count):
users_objs = []
for user in users:
user_obj, created = User.objects.get_or_create(
email=user['email'],
defaults={
"username": user['username'],
"password": make_password(user['password']),
"status": user['role'],
},
)
users_objs.append(user_obj)
self.stdout.write(f"Created user: {user['username']}")
return users_objs
def create_competitions(self, count, users):
competitions_objs = []
for i, competition in enumerate(competitions):
competition_obj = Competition.objects.create(
title=competition['title'],
description=competition['description'],
start_date=competition['start_date'],
end_date=competition['end_date'],
type=competition['type'],
participation_type=competition['participation_type'],
)
competitions[i]['obj'] = competition_obj
competition_obj.participants.add(*users)
competitions_objs.append(competition_obj)
self.stdout.write(f"Created competition: {competition['title']}")
return competitions_objs
def create_tasks(self):
tasks_objs = []
task_types = [
CompetitionTask.CompetitionTaskType.INPUT.value,
CompetitionTask.CompetitionTaskType.REVIEW.value,
CompetitionTask.CompetitionTaskType.INPUT.value,
]
for i, competition in enumerate(competitions):
for j, task in enumerate(competition['tasks']):
task_obj = CompetitionTask.objects.create(
in_competition_position=j,
competition=competition['obj'],
title=task['title'],
description=task['description'],
type=task['type'],
points=task['points'],
submission_reviewers_count=task['submission_reviewers_count'],
max_attempts=task['max_attempts'],
)
competitions[i]['tasks'][j]['obj'] = task_obj
if task['type'] == CompetitionTask.CompetitionTaskType.INPUT.value:
task_obj.correct_answer_file = task['correct_answer_file']
if (
task['type']
== CompetitionTask.CompetitionTaskType.REVIEW.value
):
for k, criteria in enumerate(task['criteries']):
criteria_obj = CompetitionTaskCriteria.objects.create(
task=task_obj,
name=criteria['name'],
slug=criteria['slug'],
description=criteria['description'],
max_value=criteria['max_value'],
)
competitions[i]['tasks'][j]['criteries'][k]['obj'] = criteria_obj
self.stdout.write(f"Created criteria: {criteria['slug']}")
tasks_objs.append(task_obj)
self.stdout.write(f"Created task: {task['title']} (type: {task['type']})")
self.add_reviewers_to_task(tasks_objs)
return tasks_objs
def add_reviewers_to_task(self, tasks):
for task in tasks:
task.reviewers.set(self.reviewers)
task.save()
def create_incorrect_submissions(self, tasks, users):
for user in users:
for task in tasks:
if task.type == CompetitionTask.CompetitionTaskType.REVIEW.value:
num_submissions = random.randint(1, 3)
for m in range(num_submissions):
dummy_content = ContentFile(
b"otvet: 112 sto proc" ,
name=f"submission_{uuid.uuid4().hex}.txt",
)
submission = CompetitionTaskSubmission.objects.create(
user=user,
task=task,
content=dummy_content,
)
submission.save()
submission.send_on_review()
self.stdout.write(
f"Created submission for task '{task.title}' by user '{user.username}'"
)
def create_states(self, competitions, users):
for comp in competitions:
for user in comp.participants.all():
state_obj, created = State.objects.get_or_create(
user=user,
competition=comp,
defaults={
"state": "started",
"changed_at": timezone.now() - timedelta(days=random.randint(1, 30)),
},
)
self.stdout.write(
f"Created state '{state_obj.state}' for user '{user.username}' in competition '{comp.title}'"
)
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import uuid
from django.db import migrations, models
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import django.db.models.deletion
from django.db import migrations, models
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import apps.task.models
import django.db.models.deletion
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import django.db.models.deletion
import uuid
+1 -1
View File
@@ -8,4 +8,4 @@ class UsersConfig(AppConfig):
verbose_name = "Пользователи (веб)"
def ready(self):
pass
import apps.user.signals
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-03 09:41
# Generated by Django 5.1.6 on 2025-03-03 09:46
import uuid
from django.db import migrations, models
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
python manage.py migrate
if [ $? -ne 0 ]; then
echo "Migration failed"
exit 1
fi
if [ "$DJANGO_CREATE_SUPERUSER" = "True" ]; then
python manage.py createsuperuser --noinput --username "$DJANGO_SUPERUSER_USERNAME" --email "$DJANGO_SUPERUSER_EMAIL" || true
fi
python manage.py init_achievments
python manage.py generate_pretty_data