working!
This commit is contained in:
BIN
__pycache__/test.cpython-313.pyc
Normal file
BIN
__pycache__/test.cpython-313.pyc
Normal file
Binary file not shown.
83
example.py
83
example.py
@ -1,54 +1,41 @@
|
||||
from enum import Enum
|
||||
from typing import Generic
|
||||
from modules.tag import Tag
|
||||
from modules.task import QTask
|
||||
from modules.task.factory import QTaskFactory
|
||||
from modules.variant_builder import VariantFactory
|
||||
from modules.variant_builder.task_pool import QTaskPool
|
||||
|
||||
from option import Some
|
||||
tasks_on_first_topic = [QTask(f"1.{i + 1}", tags=Tag("topic", "1")) for i in range(5)]
|
||||
tasks_on_second_topic = [QTask(f"2.{i + 1}", tags=Tag("topic", "2")) for i in range(5)]
|
||||
tasks_on_third_topic = [QTask(f"3.{i + 1}", tags=Tag("topic", "3")) for i in range(5)]
|
||||
tasks_on_forth_topic = [QTask(f"4.{i + 1}", tags=Tag("topic", "4")) for i in range(5)]
|
||||
tasks_on_fifth_topic = [QTask(f"5.{i + 1}", tags=Tag("topic", "5")) for i in range(5)]
|
||||
tasks_on_sixth_topic = [QTask(f"6.{i + 1}", tags=Tag("topic", "6")) for i in range(5)]
|
||||
tasks_on_seventh_topic = [QTask(f"7.{i + 1}", tags=Tag("topic", "7")) for i in range(5)]
|
||||
|
||||
from Quizard import Quizard, QuizTask, QuizTaskGeneratorMetadata, TaskGenerator
|
||||
task_pool = QTaskPool(
|
||||
tasks_on_first_topic
|
||||
+ tasks_on_second_topic
|
||||
+ tasks_on_third_topic
|
||||
+ tasks_on_forth_topic
|
||||
+ tasks_on_fifth_topic
|
||||
+ tasks_on_sixth_topic
|
||||
+ tasks_on_seventh_topic
|
||||
)
|
||||
|
||||
vf = VariantFactory(number_of_tasks=7, task_pool=task_pool)
|
||||
|
||||
class TagCategory(str, Enum):
|
||||
TOPIC = "topic"
|
||||
DIFFICULTY = "difficulty"
|
||||
_ = vf.task[0].must.include_tag("topic", "1")
|
||||
_ = vf.task[1].must.include_tag("topic", "2")
|
||||
_ = vf.task[2].must.include_tag("topic", "3")
|
||||
_ = vf.task[3].must.include_tag("topic", "4")
|
||||
_ = vf.task[4].must.include_tag("topic", "5")
|
||||
_ = vf.task[5].must.include_tag("topic", "6")
|
||||
_ = vf.task[6].must.include_tag("topic", "7")
|
||||
|
||||
variants = vf.generate_variants(number_of_variants=30)
|
||||
|
||||
class TopicTag(str, Enum):
|
||||
AVERAGE = "average"
|
||||
VARIANCE = "variance"
|
||||
|
||||
|
||||
class MyTaskGenerator(TaskGenerator[TagCategory, TopicTag]):
|
||||
def __init__(self):
|
||||
self.metadata = QuizTaskGeneratorMetadata[TagCategory, TopicTag].from_values(
|
||||
name=self.__class__.__name__,
|
||||
)
|
||||
|
||||
|
||||
class AverageTask(MyTaskGenerator):
|
||||
def generate(self):
|
||||
return QuizTask(
|
||||
"What is an average of 3, 4, 5 and 6?",
|
||||
"4.5",
|
||||
tags=[
|
||||
(TagCategory.TOPIC, TopicTag.AVERAGE),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class AverageTask1(MyTaskGenerator):
|
||||
def generate(self):
|
||||
return QuizTask(
|
||||
"What is an average of 1, 2, 3 and 4?",
|
||||
"2.5",
|
||||
tags=[
|
||||
(TagCategory.TOPIC, TopicTag.VARIANCE),
|
||||
],
|
||||
)
|
||||
|
||||
default_amount = Some(1)
|
||||
|
||||
|
||||
quizard = Quizard([AverageTask(), AverageTask1()], 3)
|
||||
|
||||
quizard.fillTaskPool()
|
||||
|
||||
print(*[task.generator_metadata.id for task in quizard.taskPool], sep="\n\n")
|
||||
i = 0
|
||||
for variant in variants:
|
||||
print(f"Variant {i + 1}:")
|
||||
print(*[task.question for task in variant.tasks])
|
||||
i += 1
|
||||
|
||||
@ -20,7 +20,6 @@ class Tag(Generic[C, V]):
|
||||
val: V
|
||||
|
||||
|
||||
@dataclass
|
||||
class Tags(Generic[C, V]):
|
||||
"""A collection of tags grouped by category
|
||||
|
||||
@ -28,9 +27,11 @@ class Tags(Generic[C, V]):
|
||||
_dict: (dict[C, set[V]]): Internal dictionary storing tags grouped by category.
|
||||
"""
|
||||
|
||||
_dict: dict[C, set[V]] = field(default_factory=dict)
|
||||
_dict: dict[C, set[V]]
|
||||
|
||||
def __init__(self, iter: Iterable[Tag[C, V]] | None = None):
|
||||
self._dict = {}
|
||||
|
||||
if iter:
|
||||
for tag in iter:
|
||||
self._dict.setdefault(tag.cat, set()).add(tag.val)
|
||||
@ -101,9 +102,7 @@ class Tags(Generic[C, V]):
|
||||
lines: list[str] = []
|
||||
for category, values in self._dict.items():
|
||||
cat_str = str(category.value if isinstance(category, Enum) else category)
|
||||
val_strs = sorted(
|
||||
[str(v.value if isinstance(v, Enum) else v for v in values)]
|
||||
)
|
||||
val_strs = [str(v.value if isinstance(v, Enum) else v) for v in values]
|
||||
lines.append(f"{cat_str}: {', '.join(val_strs)}")
|
||||
return "\n".join(lines)
|
||||
|
||||
@ -113,6 +112,7 @@ class Tags(Generic[C, V]):
|
||||
Yields:
|
||||
Tag[C, V]: Each tag in the collection.
|
||||
"""
|
||||
|
||||
for category, values in self._dict.items():
|
||||
for value in values:
|
||||
yield Tag(category, value)
|
||||
|
||||
BIN
modules/tag/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
modules/tag/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
@ -1,37 +1,42 @@
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from typing import Generic, override
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING, Generic, override
|
||||
|
||||
from option import Option
|
||||
|
||||
from modules.tag import Tag, Tags
|
||||
from modules.task.factory.metadata import QTaskFactoryMetadata
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.utils.utils import indent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from modules.task.factory.metadata import QTaskFactoryMetadata
|
||||
|
||||
|
||||
@dataclass
|
||||
class QTask(Generic[C, V, Q, A]):
|
||||
question: Q
|
||||
answer: A
|
||||
tags: Tags[C, V]
|
||||
factory_metadata: Option[QTaskFactoryMetadata[C, V]] = Option[
|
||||
QTaskFactoryMetadata[C, V]
|
||||
id: uuid.UUID
|
||||
factory_metadata: Option["QTaskFactoryMetadata[C, V]"] = Option[
|
||||
"QTaskFactoryMetadata[C, V]"
|
||||
].maybe(None)
|
||||
id: uuid.UUID = uuid.uuid4()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
question: Q,
|
||||
answer: A,
|
||||
tags: Tags[C, V] | list[Tag[C, V]] | None = None,
|
||||
answer: A = None,
|
||||
tags: Tags[C, V] | list[Tag[C, V]] | Tag[C, V] | None = None,
|
||||
):
|
||||
self.question = question
|
||||
self.answer = answer
|
||||
if isinstance(tags, Tag):
|
||||
self.tags = Tags[C, V]([tags])
|
||||
if isinstance(tags, list):
|
||||
self.tags = Tags[C, V](tags)
|
||||
elif isinstance(tags, Tags):
|
||||
self.tags = tags
|
||||
self.id = uuid.uuid4()
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
|
||||
BIN
modules/task/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
modules/task/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
@ -9,7 +9,6 @@ from modules.task.factory.metadata import QTaskFactoryMetadata
|
||||
from modules.utils.types import A, C, Q, V
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class QTaskFactory(ABC, Generic[C, V, Q, A]):
|
||||
id: uuid.UUID
|
||||
metadata: QTaskFactoryMetadata[C, V]
|
||||
|
||||
BIN
modules/task/factory/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
modules/task/factory/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
modules/task/factory/__pycache__/metadata.cpython-313.pyc
Normal file
BIN
modules/task/factory/__pycache__/metadata.cpython-313.pyc
Normal file
Binary file not shown.
@ -2,7 +2,7 @@ import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Generic
|
||||
|
||||
from option import NONE, Option
|
||||
from option import Option
|
||||
|
||||
from modules.utils.types import C, V
|
||||
|
||||
|
||||
BIN
modules/utils/__pycache__/types.cpython-313.pyc
Normal file
BIN
modules/utils/__pycache__/types.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -1,6 +1,11 @@
|
||||
from random import Random
|
||||
from typing import TypeVar
|
||||
|
||||
|
||||
def indent(text: str, spaces: int = 2) -> str:
|
||||
prefix = " " * spaces
|
||||
return "\n".join(prefix + line for line in text.splitlines())
|
||||
|
||||
|
||||
rnd = Random()
|
||||
rnd.seed(42)
|
||||
|
||||
BIN
modules/variant/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
modules/variant/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
@ -1,16 +1,96 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Generic
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant import QVariant
|
||||
from modules.variant_builder.context import DynamicCtx
|
||||
from modules.variant_builder.default_task_selector import LeastUsedTaskSelector
|
||||
from modules.variant_builder.filters import Filter
|
||||
from modules.variant_builder.task_pool import QTaskPool
|
||||
from modules.variant_builder.task_selector import QTaskSelector
|
||||
from modules.variant_builder.variant_set import QVariantSet
|
||||
from modules.variant_builder.variant_task import VariantTask
|
||||
|
||||
|
||||
@dataclass
|
||||
class VariantBuilder(Generic[C, V, Q, A]):
|
||||
class VariantFactory(Generic[C, V, Q, A]):
|
||||
task_pool: QTaskPool[C, V, Q, A]
|
||||
previos_variants: QVariantSet[C, V, Q, A]
|
||||
current_variant: QVariant[C, V, Q, A]
|
||||
previous_variants: QVariantSet[C, V, Q, A] = QVariantSet()
|
||||
task_selector: QTaskSelector[C, V, Q, A]
|
||||
task: list[VariantTask[C, V, Q, A]]
|
||||
number_of_tasks: int
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
number_of_tasks: int,
|
||||
task_pool: QTaskPool[C, V, Q, A],
|
||||
task_selector: QTaskSelector[C, V, Q, A] | None = None,
|
||||
):
|
||||
self.task = [VariantTask() for _ in range(number_of_tasks)]
|
||||
self.number_of_tasks = number_of_tasks
|
||||
self.task_pool = task_pool
|
||||
self.task_selector = (
|
||||
task_selector if task_selector is not None else LeastUsedTaskSelector()
|
||||
)
|
||||
|
||||
def generate_variants(self, number_of_variants: int) -> QVariantSet[C, V, Q, A]:
|
||||
variant_task_filters: list[Filter[C, V, Q, A]] = [
|
||||
b.must.build() for b in self.task
|
||||
]
|
||||
static_filter_matches_per_task = self._get_static_filter_matches(
|
||||
variant_task_filters
|
||||
)
|
||||
|
||||
dynamic_context = DynamicCtx(
|
||||
self.task_pool,
|
||||
)
|
||||
|
||||
for _ in range(number_of_variants):
|
||||
variant_tasks: list[QTask[C, V, Q, A]] = []
|
||||
for task_index in range(self.number_of_tasks):
|
||||
dynamic_filtered_matches = self._get_dynamic_filter_matches(
|
||||
static_filter_matches_per_task[task_index],
|
||||
variant_task_filters[task_index],
|
||||
dynamic_context,
|
||||
)
|
||||
|
||||
selected_task = self.task_selector.select(
|
||||
dynamic_filtered_matches, dynamic_context
|
||||
)
|
||||
variant_tasks.append(selected_task)
|
||||
dynamic_context.current_variant_tasks.append(selected_task)
|
||||
|
||||
variant = QVariant(variant_tasks)
|
||||
dynamic_context.previous_variants.append(variant)
|
||||
dynamic_context.current_variant_tasks.clear()
|
||||
|
||||
return dynamic_context.previous_variants
|
||||
|
||||
def _get_static_filter_matches(
|
||||
self, task_filters: list[Filter[C, V, Q, A]]
|
||||
) -> list[list[QTask[C, V, Q, A]]]:
|
||||
statical_filter_matches_per_task: list[list[QTask[C, V, Q, A]]] = [
|
||||
[] for _ in range(self.number_of_tasks)
|
||||
]
|
||||
|
||||
for task in self.task_pool:
|
||||
task_filter_index = 0
|
||||
for filter in task_filters:
|
||||
if filter.static.is_satisfied(task):
|
||||
statical_filter_matches_per_task[task_filter_index].append(task)
|
||||
task_filter_index += 1
|
||||
|
||||
return statical_filter_matches_per_task
|
||||
|
||||
def _get_dynamic_filter_matches(
|
||||
self,
|
||||
statically_filtered_pool: list[QTask[C, V, Q, A]],
|
||||
filter: Filter[C, V, Q, A],
|
||||
ctx: DynamicCtx[C, V, Q, A],
|
||||
) -> list[QTask[C, V, Q, A]]:
|
||||
dynamic_filter_matches: list[QTask[C, V, Q, A]] = []
|
||||
|
||||
for task in statically_filtered_pool:
|
||||
if filter.dynamic.check_if_satisfied(task, ctx):
|
||||
dynamic_filter_matches.append(task)
|
||||
|
||||
return dynamic_filter_matches
|
||||
|
||||
BIN
modules/variant_builder/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
modules/variant_builder/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
modules/variant_builder/__pycache__/context.cpython-313.pyc
Normal file
BIN
modules/variant_builder/__pycache__/context.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
modules/variant_builder/__pycache__/task_pool.cpython-313.pyc
Normal file
BIN
modules/variant_builder/__pycache__/task_pool.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
modules/variant_builder/__pycache__/variant_set.cpython-313.pyc
Normal file
BIN
modules/variant_builder/__pycache__/variant_set.cpython-313.pyc
Normal file
Binary file not shown.
BIN
modules/variant_builder/__pycache__/variant_task.cpython-313.pyc
Normal file
BIN
modules/variant_builder/__pycache__/variant_task.cpython-313.pyc
Normal file
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Generic
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant import QVariant
|
||||
from modules.variant_builder.task_pool import QTaskPool
|
||||
@ -8,8 +9,9 @@ from modules.variant_builder.variant_set import QVariantSet
|
||||
|
||||
|
||||
@dataclass
|
||||
class DynamicFilterCtx(Generic[C, V, Q, A]):
|
||||
class DynamicCtx(Generic[C, V, Q, A]):
|
||||
task_pool: QTaskPool[C, V, Q, A]
|
||||
previous_variants: QVariantSet[C, V, Q, A]
|
||||
current_variant: QVariant[C, V, Q, A]
|
||||
task_number: int
|
||||
previous_variants: QVariantSet[C, V, Q, A] = field(
|
||||
default_factory=QVariantSet[C, V, Q, A]
|
||||
)
|
||||
current_variant_tasks: list[QTask[C, V, Q, A]] = field(default_factory=list)
|
||||
|
||||
63
modules/variant_builder/default_task_selector.py
Normal file
63
modules/variant_builder/default_task_selector.py
Normal file
@ -0,0 +1,63 @@
|
||||
import uuid
|
||||
from math import inf
|
||||
from test import best_candidate, min_max_intersections
|
||||
from typing import override
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.utils.utils import rnd
|
||||
from modules.variant_builder.context import DynamicCtx
|
||||
from modules.variant_builder.task_pool import QTaskPool
|
||||
from modules.variant_builder.task_selector import QTaskSelector
|
||||
|
||||
|
||||
class LeastUsedTaskSelector(QTaskSelector[C, V, Q, A]):
|
||||
task_usage_count: dict[uuid.UUID, int]
|
||||
|
||||
def __init__(self):
|
||||
self.task_usage_count = {}
|
||||
|
||||
@override
|
||||
def select(
|
||||
self, filtered_tasks: list[QTask[C, V, Q, A]], ctx: DynamicCtx[C, V, Q, A]
|
||||
) -> QTask[C, V, Q, A]:
|
||||
rnd.shuffle(filtered_tasks)
|
||||
|
||||
task_scores: list[int] = []
|
||||
|
||||
min_max_intersections = inf
|
||||
for task in filtered_tasks:
|
||||
max_intersections = 0
|
||||
for variant in ctx.previous_variants:
|
||||
intersections = 0
|
||||
for t in ctx.current_variant_tasks:
|
||||
if t in variant.tasks:
|
||||
intersections += 1
|
||||
if task in variant.tasks:
|
||||
intersections += 1
|
||||
if intersections > max_intersections:
|
||||
max_intersections = intersections
|
||||
task_scores.append(max_intersections)
|
||||
if min_max_intersections > max_intersections:
|
||||
min_max_intersections = max_intersections
|
||||
|
||||
if len(ctx.current_variant_tasks) == 6:
|
||||
print(min_max_intersections)
|
||||
|
||||
best_candidates: list[QTask[C, V, Q, A]] = []
|
||||
|
||||
for task, score in zip(filtered_tasks, task_scores):
|
||||
if score == min_max_intersections:
|
||||
best_candidates.append(task)
|
||||
|
||||
least_used_score = inf
|
||||
least_used_task: QTask[C, V, Q, A] = best_candidates[0]
|
||||
|
||||
for task in best_candidates:
|
||||
if self.task_usage_count.setdefault(task.id, 0) < least_used_score:
|
||||
least_used_score = self.task_usage_count[task.id]
|
||||
least_used_task = task
|
||||
|
||||
self.task_usage_count[least_used_task.id] += 1
|
||||
|
||||
return least_used_task
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -5,11 +5,8 @@ from typing import Callable, Generic, Self, overload, override
|
||||
from modules.tag import Tag, Tags
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.filters import Filter
|
||||
from modules.variant_builder.filters.composite import CompositeFilter
|
||||
from modules.variant_builder.filters.dynamic import FilterDynamic
|
||||
from modules.variant_builder.filters.dynamic.composite import CompositeFilterDynamic
|
||||
from modules.variant_builder.filters.static import FilterStatic
|
||||
from modules.variant_builder.filters.static.composite import CompositeFilterStatic
|
||||
from modules.variant_builder.filters.static.must_be_one_of import MustBeOneOfFilter
|
||||
from modules.variant_builder.filters.static.must_include_all_tags import (
|
||||
MustIncludeAllTagsFilter,
|
||||
@ -92,6 +89,19 @@ class FilterBuilder(Generic[C, V, Q, A]):
|
||||
) -> Self:
|
||||
return self.add_static(MustIncludeAllTagsFilter(tag, **kwargs))
|
||||
|
||||
@overload
|
||||
def include_tag(self, category: C, value: V) -> Self: ...
|
||||
|
||||
@overload
|
||||
def include_tag(self, category: Tag[C, V], value: None = None) -> Self: ...
|
||||
|
||||
def include_tag(self, category: C | Tag[C, V], value: V | None = None) -> Self:
|
||||
if isinstance(category, Tag):
|
||||
return self.include_all_tags(category)
|
||||
else:
|
||||
assert value is not None
|
||||
return self.include_all_tags([Tag(category, value)])
|
||||
|
||||
def be_inverse_of(
|
||||
self,
|
||||
filter: Callable[["FilterBuilder[C, V, Q, A]"], "FilterBuilder[C, V, Q, A]"],
|
||||
|
||||
@ -5,16 +5,15 @@ from typing import Generic, Protocol, override, runtime_checkable
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant import QVariant
|
||||
from modules.variant_builder.context import DynamicFilterCtx
|
||||
from modules.variant_builder.context import DynamicCtx
|
||||
from modules.variant_builder.task_pool import QTaskPool
|
||||
from modules.variant_builder.variant_set import QVariantSet
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class FilterDynamic(ABC, Generic[C, V, Q, A]):
|
||||
@abstractmethod
|
||||
def check_if_satisfied(
|
||||
self, task: QTask[C, V, Q, A], ctx: DynamicFilterCtx[C, V, Q, A]
|
||||
self, task: QTask[C, V, Q, A], ctx: DynamicCtx[C, V, Q, A]
|
||||
) -> bool: ...
|
||||
|
||||
def __invert__(self) -> "FilterDynamic[C, V, Q, A]":
|
||||
@ -33,7 +32,7 @@ class DynamicFilterNegator(FilterDynamic[C, V, Q, A]):
|
||||
|
||||
@override
|
||||
def check_if_satisfied(
|
||||
self, task: QTask[C, V, Q, A], ctx: DynamicFilterCtx[C, V, Q, A]
|
||||
self, task: QTask[C, V, Q, A], ctx: DynamicCtx[C, V, Q, A]
|
||||
) -> bool:
|
||||
return not self.filter.check_if_satisfied(task, ctx)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -3,7 +3,7 @@ from typing import override
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.context import DynamicFilterCtx
|
||||
from modules.variant_builder.context import DynamicCtx
|
||||
from modules.variant_builder.filters.dynamic import FilterDynamic
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ class CompositeFilterDynamic(FilterDynamic[C, V, Q, A]):
|
||||
|
||||
@override
|
||||
def check_if_satisfied(
|
||||
self, task: QTask[C, V, Q, A], ctx: DynamicFilterCtx[C, V, Q, A]
|
||||
self, task: QTask[C, V, Q, A], ctx: DynamicCtx[C, V, Q, A]
|
||||
) -> bool:
|
||||
return all(filter.check_if_satisfied(task, ctx) for filter in self.filters)
|
||||
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.filters.dynamic import FilterDynamic
|
||||
|
||||
|
||||
class FilterNegatorDynamic(FilterDynamic[C, V, Q, A]):
|
||||
|
||||
@ -1 +0,0 @@
|
||||
e
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,16 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import override
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.filters import FilterBuilder
|
||||
from modules.variant_builder.filters.static import FilterStatic
|
||||
|
||||
|
||||
@dataclass
|
||||
class CompositeFilterStatic(FilterStatic[C, V, Q, A]):
|
||||
filters: list[FilterStatic[C, V, Q, A]]
|
||||
|
||||
@override
|
||||
def is_satisfied(self, task: QTask[C, V, Q, A]) -> bool:
|
||||
return all(filter.is_satisfied(task) for filter in self.filters)
|
||||
@ -1,19 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import override
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.filters.static import FilterStatic
|
||||
|
||||
|
||||
@dataclass
|
||||
class StaticFilterNegator(FilterStatic[C, V, Q, A]):
|
||||
filter: FilterStatic[C, V, Q, A]
|
||||
|
||||
@override
|
||||
def is_satisfied(self, task: QTask[C, V, Q, A]) -> bool:
|
||||
return not self.filter.is_satisfied(task)
|
||||
|
||||
@override
|
||||
def __invert__(self) -> "FilterStatic[C, V, Q, A]":
|
||||
return self.filter
|
||||
@ -1,3 +1,4 @@
|
||||
import random
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Generic
|
||||
|
||||
@ -5,6 +6,6 @@ from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class QTaskPool(Generic[C, V, Q, A]):
|
||||
pool: list[QTask[C, V, Q, A]]
|
||||
class QTaskPool(list[QTask[C, V, Q, A]], Generic[C, V, Q, A]):
|
||||
def shuffle(self):
|
||||
random.shuffle(self)
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
from typing import Generic, Protocol
|
||||
|
||||
from modules.task import QTask
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.context import DynamicCtx
|
||||
from modules.variant_builder.task_pool import QTaskPool
|
||||
|
||||
|
||||
@dataclass
|
||||
class QTaskSelector(Protocol[C, V, Q, A]):
|
||||
task_pool: QTaskPool[C, V, Q, A]
|
||||
|
||||
def select(self, filtered_task_pool_indexes: list[int]) -> QTask[C, V, Q, A]: ...
|
||||
class QTaskSelector(ABC, Generic[C, V, Q, A]):
|
||||
@abstractmethod
|
||||
def select(
|
||||
self, filtered_tasks: list[QTask[C, V, Q, A]], ctx: DynamicCtx[C, V, Q, A]
|
||||
) -> QTask[C, V, Q, A]: ...
|
||||
|
||||
@ -6,9 +6,4 @@ from modules.variant import QVariant
|
||||
|
||||
|
||||
@dataclass
|
||||
class QVariantSet(Generic[C, V, Q, A]):
|
||||
variants: list[QVariant[C, V, Q, A]] = field(default_factory=list)
|
||||
|
||||
def __iter__(self):
|
||||
for variant in self.variants:
|
||||
yield variant
|
||||
class QVariantSet(list[QVariant[C, V, Q, A]], Generic[C, V, Q, A]): ...
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Generic
|
||||
|
||||
from option import Option
|
||||
|
||||
from modules.utils.types import A, C, Q, V
|
||||
from modules.variant_builder.filters import Filter
|
||||
from modules.variant_builder.filters.builder import FilterBuilder
|
||||
|
||||
|
||||
class VariantTask(Generic[C, V, Q, A]):
|
||||
must: FilterBuilder[C, V, Q, A]
|
||||
|
||||
def __init__(self):
|
||||
self.must = FilterBuilder[C, V, Q, A]()
|
||||
|
||||
33
test.py
Normal file
33
test.py
Normal file
@ -0,0 +1,33 @@
|
||||
from math import inf
|
||||
|
||||
tasks1 = [f"1.{i + 1}" for i in range(5)]
|
||||
tasks2 = [f"2.{i + 1}" for i in range(5)]
|
||||
tasks3 = [f"3.{i + 1}" for i in range(5)]
|
||||
|
||||
all_variants = []
|
||||
|
||||
for i in tasks1:
|
||||
for j in tasks2:
|
||||
for k in tasks3:
|
||||
all_variants.append([i, j, k])
|
||||
|
||||
selected_variants = [all_variants[0]]
|
||||
|
||||
for _ in range(9):
|
||||
best_candidate = all_variants[0]
|
||||
min_max_intersections = inf
|
||||
for variant in all_variants:
|
||||
max_intersections = -inf
|
||||
for selected_variant in selected_variants:
|
||||
intersections = 0
|
||||
for j in range(3):
|
||||
if selected_variant[j] == variant[j]:
|
||||
intersections += 1
|
||||
if max_intersections < intersections:
|
||||
max_intersections = intersections
|
||||
if max_intersections < min_max_intersections:
|
||||
min_max_intersections = max_intersections
|
||||
best_candidate = variant
|
||||
selected_variants.append(best_candidate)
|
||||
|
||||
print(any([]), sep="\n")
|
||||
Reference in New Issue
Block a user