Skip to content

Commit

Permalink
fix: Support exams with arbitrary number of students
Browse files Browse the repository at this point in the history
  • Loading branch information
worldbeater committed Sep 18, 2024
1 parent bff2613 commit 2b7063b
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 57 deletions.
13 changes: 9 additions & 4 deletions tests/api/test_seeds.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from tests.utils import arrange_task
from tests.utils import arrange_task, mode

from flask.testing import FlaskClient

Expand All @@ -22,12 +22,16 @@ def test_final_seed_is_not_used(db: AppDatabase, client: FlaskClient):
assert response.json['status_name'] == "Не отправлено"


@mode("exam")
def test_final_seed_is_used(db: AppDatabase, client: FlaskClient):
(group, var, task) = arrange_task(db)
group_title = db.groups.get_by_id(group).title
task = 0
group, var, _ = arrange_task(db)
title = db.groups.get_by_id(group).title

db.tasks.create_by_ids([task])
db.seeds.begin_final_test(group)

default_template = f'/{task}/{group_title}.html#вариант-{var + 1}'
default_template = f'/{task}/{title}.html#вариант-{var + 1}'
response = client.get(f"/api/v1/group/{group}/variant/{var}/task/{task}")

assert response.is_json
Expand All @@ -54,6 +58,7 @@ def test_final_submissions_are_allowed(db: AppDatabase, client: FlaskClient):
assert response.json['status_name'] == "Отправлено"


@mode("exam")
def test_final_submissions_are_paused(db: AppDatabase, client: FlaskClient):
(group, variant, task) = arrange_task(db)
db.seeds.begin_final_test(group)
Expand Down
2 changes: 1 addition & 1 deletion tests/api/test_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def post_code(url: str, code: str):
content_type='application/json'
)

(group, variant, task) = arrange_task(db)
group, variant, task = arrange_task(db)
url = f"/api/v1/group/{group}/variant/{variant}/task/{task}"

response = post_code(url, "main = lambda x: 42")
Expand Down
1 change: 1 addition & 0 deletions tests/config.defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"HIGHLIGHT_SYNTAX": false,
"READONLY": false,
"TASK_BASE_URL": "http://sovietov.com/kispython",
"FINAL_VARIANTS": 40,
"FINAL_TASKS": null,
"ENABLE_REGISTRATION": false,
"HIDE_GROUPS": false,
Expand Down
5 changes: 4 additions & 1 deletion webapp/config.defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
"HIDE_GROUPS": false,
"IMAP_LOGIN": null,
"IMAP_PASSWORD": null,
"FINAL_TASKS": null,
"FINAL_VARIANTS": 40,
"FINAL_TASKS": {
"0": [1, 2, 3]
},
"ENABLE_LKS_OAUTH": false,
"LKS_OAUTH_CLIENT_ID": "kispython.ru",
"LKS_OAUTH_CLIENT_SECRET": "CHANGE_ME",
Expand Down
1 change: 1 addition & 0 deletions webapp/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def __init__(self, config: Config):
self.task_base_url: str = config["TASK_BASE_URL"]
self.no_background_worker: bool = config["DISABLE_BACKGROUND_WORKER"]
self.final_tasks: dict[str, list[int]] = config["FINAL_TASKS"]
self.final_variants: int = config["FINAL_VARIANTS"]
self.clearable_database: bool = config["CLEARABLE_DATABASE"]
self.readonly: bool = config["READONLY"]
self.enable_registration: bool = config["ENABLE_REGISTRATION"]
Expand Down
53 changes: 11 additions & 42 deletions webapp/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,68 +77,39 @@ def __init__(
self,
group: Group,
seed: FinalSeed | None,
tasks: TaskRepository,
groups: GroupRepository,
variants: VariantRepository,
config: AppConfig,
):
self.config = config
self.group = group
self.seed = seed
self.tasks = tasks
self.groups = groups
self.variants = variants
self.fetch_lists()
self.groups = groups.get_all() if self.config.exam else None

@property
def random_active(self) -> bool:
return self.seed and self.seed.active

def get_external_task(self, task: int, variant: int) -> ExternalTaskDto:
if self.seed is None:
not_exam_mode = not self.config.final_tasks
if not (self.config.exam and self.random_active):
return ExternalTaskDto(
group_title=self.group.title,
task=task,
variant=variant,
active=not_exam_mode
active=not self.config.exam,
)
unique = f'{task}{variant}'
task: Task = self.sample_task(str(variant), task)
group: Group = self.sample(unique, self.all_groups, self.group.id)
variant: Variant = self.sample(unique, self.all_variants, variant)
seed = f'{task}{variant}'
task: int = self.sample(seed, self.config.final_tasks[str(task)])
variant: int = self.sample(seed, list(range(1, self.config.final_variants + 1)))
group: Group = self.sample(seed, self.groups)
return ExternalTaskDto(
task=task.id,
task=task,
variant=variant,
group_title=group.external if group.external else group.title,
variant=variant.id,
active=bool(self.seed.active),
)

def sample_task(self, seed: str, task: int):
final_tasks: dict[str, list[int]] = self.config.final_tasks
if not final_tasks:
return self.sample(seed, self.all_tasks, task)
possible_options: list[int] = final_tasks[str(task)]
composite_seed = f'{self.seed.seed}{seed}'
rand = random.Random(composite_seed)
id = rand.choice(possible_options)
return Task(id=id)

def sample(self, seed: str, list: list[dict[str, int]], i: int):
composite_seed = f'{self.seed.seed}{seed}'
rand = random.Random(composite_seed)
length = len(list)
identifiers = [item.id for item in list]
index = identifiers.index(i)
random_sample = rand.sample(list, length)
return random_sample[index]

def fetch_lists(self):
if self.seed is None:
return
self.all_tasks = self.tasks.get_all()
self.all_variants = self.variants.get_all()
self.all_groups = self.groups.get_all()
def sample(self, seed: str, options: list) -> list:
return random.Random(f'{self.seed.seed}{seed}').choice(options)


class StatusManager:
Expand Down Expand Up @@ -327,9 +298,7 @@ def __get_external_task_manager(self, group: Group) -> ExternalTaskManager:
return ExternalTaskManager(
group=group,
seed=seed,
tasks=self.tasks,
groups=self.groups,
variants=self.variants,
config=self.config.config,
)

Expand Down
11 changes: 2 additions & 9 deletions webapp/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,8 @@ def process_pending_messages(config: AppConfig, db: AppDatabase):
task = db.tasks.get_by_id(message.task)
variant = db.variants.get_by_id(message.variant)
seed = db.seeds.get_final_seed(group.id)
external_manager = ExternalTaskManager(
group=group,
seed=seed,
tasks=db.tasks,
groups=db.groups,
variants=db.variants,
config=config,
)
ext = external_manager.get_external_task(task.id, variant.id)
external = ExternalTaskManager(group=group, seed=seed, groups=db.groups, config=config)
ext = external.get_external_task(task.id, variant.id)
print(f"g-{message.group}, t-{message.task}, v-{message.variant}")
print(f"external: {ext.group_title}, t-{ext.task}, v-{ext.variant}")
try:
Expand Down

0 comments on commit 2b7063b

Please sign in to comment.