From f9eb2f35a8b58f5f97ea05f6a507499ff4506f7b Mon Sep 17 00:00:00 2001 From: ton1c Date: Wed, 23 Apr 2025 01:46:29 +0300 Subject: [PATCH] working! --- __pycache__/test.cpython-313.pyc | Bin 0 -> 1368 bytes example.py | 83 +++++++--------- modules/tag/__init__.py | 10 +- .../tag/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 5901 bytes modules/task/__init__.py | 21 ++-- .../task/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 2587 bytes modules/task/factory/__init__.py | 1 - .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1505 bytes .../__pycache__/metadata.cpython-313.pyc | Bin 0 -> 1577 bytes modules/task/factory/metadata.py | 2 +- .../utils/__pycache__/types.cpython-313.pyc | Bin 0 -> 346 bytes .../utils/__pycache__/utils.cpython-313.pyc | Bin 677 -> 846 bytes modules/utils/utils.py | 5 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1031 bytes modules/variant_builder/__init__.py | 90 +++++++++++++++++- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 5000 bytes .../__pycache__/context.cpython-313.pyc | Bin 0 -> 1229 bytes .../default_task_selector.cpython-313.pyc | Bin 0 -> 2986 bytes .../__pycache__/task_pool.cpython-313.pyc | Bin 0 -> 895 bytes .../__pycache__/task_selector.cpython-313.pyc | Bin 0 -> 1217 bytes .../__pycache__/variant_set.cpython-313.pyc | Bin 0 -> 728 bytes .../__pycache__/variant_task.cpython-313.pyc | Bin 0 -> 1133 bytes modules/variant_builder/context.py | 12 ++- .../variant_builder/default_task_selector.py | 63 ++++++++++++ .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 1697 bytes .../__pycache__/builder.cpython-313.pyc | Bin 0 -> 7778 bytes .../filters/__pycache__/types.cpython-313.pyc | Bin 0 -> 755 bytes modules/variant_builder/filters/builder.py | 16 +++- .../filters/dynamic/__init__.py | 7 +- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 2967 bytes .../__pycache__/composite.cpython-313.pyc | Bin 0 -> 2230 bytes .../filters/dynamic/composite.py | 4 +- .../filters/dynamic/must_not.py | 6 -- .../filters/static/CompositeFilterStatic | 1 - .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 2401 bytes .../__pycache__/composite.cpython-313.pyc | Bin 0 -> 2478 bytes .../must_be_one_of.cpython-313.pyc | Bin 0 -> 3498 bytes .../must_include_all_tags.cpython-313.pyc | Bin 0 -> 2936 bytes .../must_include_any_tag.cpython-313.pyc | Bin 0 -> 2931 bytes .../filters/static/composite_filter.py | 16 ---- .../filters/static/must_not.py | 19 ---- modules/variant_builder/task_pool.py | 7 +- modules/variant_builder/task_selector.py | 14 +-- modules/variant_builder/variant_set.py | 7 +- modules/variant_builder/variant_task.py | 9 ++ test.py | 33 +++++++ 46 files changed, 287 insertions(+), 139 deletions(-) create mode 100644 __pycache__/test.cpython-313.pyc create mode 100644 modules/tag/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/task/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/task/factory/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/task/factory/__pycache__/metadata.cpython-313.pyc create mode 100644 modules/utils/__pycache__/types.cpython-313.pyc create mode 100644 modules/variant/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/context.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/default_task_selector.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/task_pool.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/task_selector.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/variant_set.cpython-313.pyc create mode 100644 modules/variant_builder/__pycache__/variant_task.cpython-313.pyc create mode 100644 modules/variant_builder/default_task_selector.py create mode 100644 modules/variant_builder/filters/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/variant_builder/filters/__pycache__/builder.cpython-313.pyc create mode 100644 modules/variant_builder/filters/__pycache__/types.cpython-313.pyc create mode 100644 modules/variant_builder/filters/dynamic/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/variant_builder/filters/dynamic/__pycache__/composite.cpython-313.pyc delete mode 100644 modules/variant_builder/filters/dynamic/must_not.py delete mode 100644 modules/variant_builder/filters/static/CompositeFilterStatic create mode 100644 modules/variant_builder/filters/static/__pycache__/__init__.cpython-313.pyc create mode 100644 modules/variant_builder/filters/static/__pycache__/composite.cpython-313.pyc create mode 100644 modules/variant_builder/filters/static/__pycache__/must_be_one_of.cpython-313.pyc create mode 100644 modules/variant_builder/filters/static/__pycache__/must_include_all_tags.cpython-313.pyc create mode 100644 modules/variant_builder/filters/static/__pycache__/must_include_any_tag.cpython-313.pyc delete mode 100644 modules/variant_builder/filters/static/composite_filter.py delete mode 100644 modules/variant_builder/filters/static/must_not.py create mode 100644 test.py diff --git a/__pycache__/test.cpython-313.pyc b/__pycache__/test.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3d97e76fae70b8b618415c3403e298386c375a5 GIT binary patch literal 1368 zcmbtS&2Jk;6o0e3Uhmp#dv|Ro4s!4ZX^}(8)=7GxqDV$T0xCy<1?pjhwZz-RjlFSZ zH$ZbT;$lETDH140gE+vg5`O{zflZZmJ5eR7o_w>(sW)b0y9yMw2UeQ*`TM zA|YVkZ%4tc2Ry(d-#W2- z((BEg+FrwYgJc-NCpk?fhde^)0VEz~&vnh8r8{!rC+V=*d9$;7Q2c7Iv-!=1AEmxr zXp8s%H{j2h)>auU?b?=gL?Ap8yZibWd$ literal 0 HcmV?d00001 diff --git a/example.py b/example.py index d830d5f..edfdcb3 100644 --- a/example.py +++ b/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 diff --git a/modules/tag/__init__.py b/modules/tag/__init__.py index 5a0bc6b..63e7e17 100644 --- a/modules/tag/__init__.py +++ b/modules/tag/__init__.py @@ -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) diff --git a/modules/tag/__pycache__/__init__.cpython-313.pyc b/modules/tag/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8973d3991da3bc01c4ec488e161627a5d5464c3 GIT binary patch literal 5901 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 Gfd2u>!s8SG literal 0 HcmV?d00001 diff --git a/modules/task/__init__.py b/modules/task/__init__.py index 0bc683d..4659093 100644 --- a/modules/task/__init__.py +++ b/modules/task/__init__.py @@ -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: diff --git a/modules/task/__pycache__/__init__.cpython-313.pyc b/modules/task/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51f9135ae42b5d19b54a6242668a5d2501ee9125 GIT binary patch literal 2587 zcmbtVU2GIp6u$GjKeIpmr$B)%f3gjBF%(21NbLf4Hx$dX4WvURLw8yRwzKrkECf)& z@L)+GZZyVfh^Yw=@FWjDkocmB51N?R&63buA&?j)ye(OM_uM->OF{6(JIT4{o_o$c z_kQPRYSCyI!T9Ri!0{hMgnp+Bf3O``)NTX$45>)vvIx_e&vF?a^BDmP3Az@tVn)JJ zM#gd`fCEe>W`mg!4l!QJhBFFQG7%hMYdIUu#BeMV$MH-PZpt*{W+o40TQaSc_eWC>V+XT;OTiq6SOmRu&F9k&VBxfhv6VBE9FY&6 za;<_5CkP|P6cK?qNs(~~oY3sSq)@TpOi*F;s&WL4CbdX59sjeF43Uk$*0mdNN@ z4N%%A(-55yPjI+dYk^R;_VKDhC5_;;sS(^h6-dU2#1MNNhZ9L~rf518ZXz4DbH>C( zbOCR|Te)*9UWDxUz~;o*(IL__=^?I9`;bFEHwNWtcGSigsyi#_6lr z7!}4L^SrT*_vS`Ut^v)UzdP>N2^x?1GA~qX(WsE(e6UAR9)R)2Gk%(U>HqQbA}qmmc1-+|zk2@3a2p(Hnyj9_Yj+kud<qIJ%|xzuzaSInDE%7q$C>AGcGuCDi z1R;frrR(>=KZ zUJt<>Iu{K7arKNs^PhgX9KhXRnmPDQP=)%#-J5$ql?3o38`Yt|S#^)>cdzGp87bZVgh+kL;UchVN$fU@UnkA=qL7Kiv zerPD!K~u{%rcJm|Sl7J-1U{ncGesj`--+w`q=g+fZ`r0@&~+RIdje9!whOL7JDvkl zT2IdxN*C1>M@I+Km|{+(Y2)lM6L(To+TOkDN>GkA7`%a!jX+2{1q#ld&<)qc*0G{% zf|HwwJ&9q7X{j?p@prxh@)3F%i(kqvMxvJn7sIjHnNs(xd%33~wk-xDABQf7E*)6h zHLw_5|3u(-%a3H`LvuEL>10(xQhRA~VbvSuj-55cOD)wHYG1y%C;cQ8lh!{lC@g|F4^U49_1s4r{}h_;i*Ez)yz&>nlH0@2?l1M1X6Cx*hD(`pWK9iGA;8EhPuMeUIXVSj3WQ1jJLWNu~_4sen=xx>8e34Kz~+J<`=$!Zg5$ zuvSZ&DM&?Fug#cgNJltP%a~cnnmNdsvoLGsAzvhmZhx@Nvrau=;51m*W$j)Q&54g(-vzJ1^AP&X>$_aBCO44Th>~5t)N~J=u)`=r<^sxkv?aK9n9Fr=SqOe(JZ{uqUohqfC?=ea% zN5|awuld6C^EdsULPSKuO6?0o)fZ9_C&~~vxnctRpAxBA3bg%vcz>nKVK+@REV zaDORJQ5w^MI8EvO(CLglIZ6*a2w2DSUB8EJH4L$>y1)PN4o9cs`#t7}l@BO|q^Le4 zL`Dd)$z1V##~9<*i@YpuF=$ zT{-=t-+27>K;8L6D-6~3)3v@Na(>G9Q|HUq2kQDrA^JReUaFE-JXL>8K2H8a5XM7@ z{-pw};e#tqy#ZnmqnmM^9Upmq3&duCE&}vk^q&yZksj||MeJQf17DdoRmQ7WnS2%z zsk|_W37OXkD%isfV(KEgBPJuxBWC{37B}9Dv$x;|&Lh5y;1-IPiX=(DlbH+h)&(g) kCri)Co6pGB1-UjL*Z$IzQt^kIr->B(jFDeFOBme*a literal 0 HcmV?d00001 diff --git a/modules/task/factory/__pycache__/metadata.cpython-313.pyc b/modules/task/factory/__pycache__/metadata.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87c0ee2fa84e2fd899da1201e3486e59b9e1368c GIT binary patch literal 1577 zcmb7E%}*Ow5P$n=dlv_sKyXY8j^(;BDOi>4Ql!L(LR!^UiOL2JwX4;}Uc-Vv$lG-) zI8@vkP9=w^M9RUvK`-qwYX1t$2a?xRrK+kbHzV^0bY}e_sB+0W%>LfInKv`PnYSIk zUm>u5`^mHZD^JMZxalu%4|E;^ctSLyaals~%V)U^Px*{Mg)r`gteBCg{mz+YVs zx*;wIO@?^>yt@3(1+}|@=pNU5c#=#KZAcUIvc}JNAWNvFhrF6EDQilIY=|4&fZ-8M zrGYg*?q{Q4-ZSjYPmO|8quXDYj)82y#@Sdxo{MJ5sFxikR*j0u)S_t@sMRH$W5c~u zz0`GQyj5f}HJv)G?u6cSac<#*@aoqE7`VZb0pS32z6J4w`6!9=OGiAt1ea4Bg1AUk#F!xyY=<(bVr<_x=_n{{%q|>eA1Bsp z6*J)^zNyj8#FmqIP`7prT1-@G#d_Ja6AsXu=n<5t^a!5a+GZ+DX+?i%lD>Zk~4*jL|+BQp9zmAjsuZqAks*-0yka-C!g6*)#G5i8H^v^Yz03$m4p$c zO$49P0Y`F#1un%eGq0{=J9V8Yy57wb@Q|)Qs2kk6$4A7TCF(-K0_NUkvk^V>o5!w$|McAnw6E$7uk@Fh6NyEy;h_+@oY@jyGq?~ z9BSRKJ0@N;0cSLXVjP9r3KTe-_75PwBYy?oZIlkRA2*L-3+F87azs; zuk77yiRqV9(ZkhW7LIbqGwJ3``nl7ZS!_<-+0E`>JplSz;+>bH*AAmUPajP;M^n3> zJQBb6?fF__>O=~hOiaCq#7-rTq`bHk*k9b+XbXT(#)1dQ$6vKk-Zz z;Q@7X#!TZw^g4Xt+4TQlrcH6yT{4;MY;RfBHHt&cF4s zXCTRNOHV%|KQ~psM87=0C|kdvM8B{!vnsJDML#z`r8FnCSiiI+GpAS|Xk==!UP0w8 z4jYJsT@fEpJ0lPm+W?6V%#4hTcNwG}vT%1e&k(-IBHO@t6E9nEM#xNwi!4fwj72;^ F0{{>!R0aS5 literal 0 HcmV?d00001 diff --git a/modules/utils/__pycache__/utils.cpython-313.pyc b/modules/utils/__pycache__/utils.cpython-313.pyc index 1dfdd496e36d91fcd93a07f186ac8894e05ed089..a50d8c3c4f5c16c41dd96e8d7c39d1c7bc8d1434 100644 GIT binary patch delta 424 zcmYk1ze~eF6vyx7l1o||{Dnfh7<7uDSQ6v5F& za24_2aF#&9wOfn0dzU)+hVS?Dz2m)m_pE#{{ae@bgxA}xScVI|U#8-UD(1!9Xyyij%3>eQJ3tE1X5&IUQs%=|zE z6H->KgJgwm&&p4dsyJtI42}~L|Yp#wSn{eOnft5=Q5-K&?m({DUFx}+i*8C jB+CLG4~9he%i>SwqdZadtNI1~B=knR)NZG*3)%c%NE2QC delta 234 zcmX@dwv?6kGcPX}0}xCW{h8i9kylbv4#=6x5X=_L7{d_E6vG(I9K&G37R(~Wkj0Y4 zSj3dhs>%M638-k}8fHdDwaFV8B_>~BG+|_(%*tf1$qbYzVgV7nK%yALRZws!;sesR z*fR4{Qu9iRgn=B!$+=9X8Gf2T1A#nE=3C6gB}KQGGojK&AU73>fC#Ww4x8Nkl+v73 qyCQLrW*{zB1`;2DE@1q~#Kh>tI6?RW1BjlH453#je*^Quh5!HtSub$_ diff --git a/modules/utils/utils.py b/modules/utils/utils.py index 50e3d76..16304ae 100644 --- a/modules/utils/utils.py +++ b/modules/utils/utils.py @@ -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) diff --git a/modules/variant/__pycache__/__init__.cpython-313.pyc b/modules/variant/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10a4291521151163cc11585483d53745a61c74f7 GIT binary patch literal 1031 zcmYLHOHUL*5U!rb?(73sMIK^;D~9NBU=L!9N{q&5hDd^=o+jyxyR$Gd%tB2s1Wv?@ z96ann;vT*F5BwX13+coVV?1zER}bE-o?)?*uCA)DtNN?z>J1DOf!6I^ZRr^U_@<2h zN)a-8NMaXUaM>I<44I>$R#mn(r#l83sjknNP7ZUaZp`H!3oWOB1*eEbr-Y>{cx)6V zz%|Ff&3T4b@Je3Mvt|sFvR~J;N3UlGV0kfbTSv;WHCbr*!mmeul1Q@|22sOiGXFS; z0}Sh=nhVeUWZ5<(yC>Ow$rdDgSwBvc9=T(q_lT5T@Bo<$sJRUFN$`vb@Jv^`$uQ@c zUf#2aTG1dQ8q)vT3SsH1VY4>iWN|ormfM+N0e`OG!%>*?DkqcKDt1ONXJ0e-neiH|H;~ zPcUy+RIb=x2`E>Q^XyU5%bc&Z{pc8}aNZ0t5m6Wiaf@?QDWnmFiAYOB^>3(}i)s{+ zIsiyb0ZEa59Jhoo!d9G6RX;yh8VI`@UZ@Ksv`3L7S@M;mZZ2~|ldRN~|+iMQ}| za_aNU-sIiwhdVPJv*UNqbnkW7I>GFg7@9eP>hB8P{jc-Oj zCpK5ZcoEg$$ntT!r`6x2<;cOlt1ZGPnWozmu8PVEO>0c6O?fKr*|Unfv}UvDJE&$z z%~di>;)l){JA|uWVC*}LAHvi=O#R4#R{E`(Y-DGnv(~-TUHHg4TYHs>9+0o66Z%u3 GY4(5bjr2YM literal 0 HcmV?d00001 diff --git a/modules/variant_builder/__init__.py b/modules/variant_builder/__init__.py index 4302334..1b3dce2 100644 --- a/modules/variant_builder/__init__.py +++ b/modules/variant_builder/__init__.py @@ -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 diff --git a/modules/variant_builder/__pycache__/__init__.cpython-313.pyc b/modules/variant_builder/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13eb2fcea845544638e5a360c5427b6ebea2015f GIT binary patch literal 5000 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 literal 0 HcmV?d00001 diff --git a/modules/variant_builder/__pycache__/default_task_selector.cpython-313.pyc b/modules/variant_builder/__pycache__/default_task_selector.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7867403720dc87fd4b8669c432185e89b28ec039 GIT binary patch literal 2986 zcmaJ@O>7&-6`m!R>yT z-n{wd&3kX&do#zP=tI!1eCxTtE+F(39kiRbMI8SPh({=ga$Fi=+B?!*%7Gm*N;}ie z6p#6o3%l5uPrFkd>|wMkEu_5I%V>Anm-1tODu4qi5sRq~+`;&sw3G_sAfttJDAkEO z8SPDnQxP0tv@hM2>c-tM)Qd(?&fkY}fkJm7Q0OR#1^*210{v#4wzjxug-eJp=wLp< z5vQgvCmf{Xo@$tKQPE3UNikK@wW{gzsIZq$@B|mF#fpBL|z~L2d!biH^Q5D0?8)}K#-!|h#2Ovq% z1eRPgloeGjR%*Igpd6s+u&DzpCK(lT|ahR?{{VTuQE1O0}|TBp)bPQ*=|lSJTQR6(>vTvQjIXGSdbG zF-K2U9}+=^Bh^e0VF#8c&Vods5pl&$RMo^xcZMiCjl1O(h@ok9C=eaO?YXc$O zmE&}G-m&0r&zDSAExz|-YZS2a1i$#(>kzq2&fAo)yE7SELf*Lm63@20`^4(SH~+VG9oD*aKJ#mP z?UK}9cfMt989C?mMT8Vcq9uBMtmXOkEc2^xiN+Uei{aVUNq}bt5vswcy0rKYDuY=7 zsh0PhtF`MyW@f0Rmi^1d8Lk6#z_K9HRP%9Te8;Mucnq30|I_H-@u{*asF+S=v= z*CBtHhDku5EBAaEOs7EJrHh$&TBmaUYVkY`ZaQwVGGoVv;WSvW$%~^X!Ovsr$`>?( zU(n!8h?o{C;xX>mmY2&arkOxORjfYHDm6oHC9FZZiZzT?NZob{)08!c%kJDtwW{hR z;w-B=aaA#tW8&PGb6Q${AE536^OcgklG^z~)Hb<1hngO^lstm>xKB+ck*{oL_|O zSsE^e3NHNsy%LZ&@JIh2{9E(;Lj3t~eAEAREWRrYei0ejyxWWo?*;~&{n1AM)PDce zmIoMHV)Md4yr$J*W zav)u928SBKi&pUBQ}grSPe7r8IWTl!XjkZMUY>iFwJzV@l)j3@DL&kgMlETyAtfy- zxx@d{^LNj)(4KT-_tyM@G~bM0YQ(Qv@vF}wANPFJW5w_624l^Ui+_?{dQjKskq>#r zhA?OegWHuxbjpfO?FrL6Sxa~w`sY$~SBT<1a5wIP-+UrM?+LiQRk$a7vTWZnKnG-b zt)`S)GmYQ)B25R!Nl|iCS9^@Pyt0*F-@%9tC^}H%O4_JYZ_CSRy_8j zL1!57`yFa__ifKSx%T+l)8h6mt9xRPpZG7&x#Do{?v5kJglni79eFxi57qC~*LH^M zeO7$Niq0N6>CD`lpR9d4{AqUg-RvhD*4zT)KKFKPu5Ar(Ww*J_4a?hi>;mYq6T}@; zakf5Gl|(O|gO2d4ih18oe;$-JF|Ct!GP=!&RZU;9T@h&A<&!jVm`g~w&8nGN*_Z@0 zq%m_0a~AUx_1IY;gWL6Is4b#hURp1N$zlawcIzf{F&X|b6ssRqc(q~nx*7Ywxb`~( z)7#2Ej-OGA1r|$tVa^tC3q$!uv{)Lq;dkWZIPMGNKSYr)(d&n(|34`DC7Ren6Ne~% jNI5?}M7LkLr@5i+OntbX-Qjko>l4<%>^BH_Y=iFrzW~K3 literal 0 HcmV?d00001 diff --git a/modules/variant_builder/__pycache__/task_pool.cpython-313.pyc b/modules/variant_builder/__pycache__/task_pool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b62855b7236bbbe161084bedae59f55ee0c65301 GIT binary patch literal 895 zcmY*X&1(}u6o0cH*?jjyixRBF3hMTdRj?N+LQxPlAl9{BTo|^=CSAJOq;F=Gdh*nR z7ker6*rVR!&3~hamr?0KJoF~S-ksSbl|Go?d-J~En;BIqMUZs!v$Zu(0Dh^ZsS%U2ht$iDo7n&75nYmC0r^?-sD&7P07-u;iApT!*W$ z1YWKNp5<43%P;z6zvLJ08>XzHj&EMntr$`%39DUV=TDXR)Ye3y9q^zP2aE}`6NPcx zCc=6WCLu;GDVpmq0=8`%f~*PhNRV|wUba;1l;pG-jcSq|fDcGKK+Pklds+?9@N_i0 zhHZ*Mmg-r*AD$lT5WX|gVqafjQGmYYQ zhz^%?(}C=^G1$dfnM2H-573y@P*2OM65hd&eDy>1eRZFI%U>Hw4SbU8&51mvNze-^ z6-7$NrKDV@bSDksiDib;PK1odQ4%J7O2^d}C}lk0QHutgW3-v_kV)HuOpTWn%qu8Q zL9LWo687LsBj)1RN15#zoRLhF&R;rYs1}auM~EEX9YjeN)t?dN$u=5lddw-+rKu|A zQOp|BTZXGbm{*|cqS}Kn;)wBaLl>JA`PFfbhgg@6>OI(F34e4#$akomz@48^`wiDm zVEHR7|IKM+ao^bAIJ|cl9L*iA9X>c-xOF^x`{R}2+;DBU^X0~{c5JVnf%MD9{0D9> B(!T%z literal 0 HcmV?d00001 diff --git a/modules/variant_builder/__pycache__/task_selector.cpython-313.pyc b/modules/variant_builder/__pycache__/task_selector.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02bc048cde51896245ee43a66d924d53aa400bc1 GIT binary patch literal 1217 zcmZ`&&1)M+6ra(4=v(nu+e9U7hy3HckH%TJ#uXZovxQ{oYic8E)Xx;e--(9d-Y*924BM#pk(u-!c5wQqI`ZV`&w zwmK!Z3}v?h6}Jjiw+1yGw>#I|IhfOSzBBJGz(RvuC%1@Ks1vW~Q{VP0e$B7?Ji9!6b#?=a=xXw=lz0T^h+6ctZFJA zk9jB(c!v~ZZ6}Ha2i{{sy(N`KC=R6!TnwQf8nYVsqsB|npQ47RWS2N}^Ecuc(oA{0 z0}Cn&iwaUqYBU=1yY^nP&)cH?O9D^Y2ckU~M#lm4+WSdw81uCKG=M1R3${Is;vR=K z5@4#BX4g76l6L0Er7OH!S+s(^@(P}j3!B_*OqEICa|N4DO%@naGvJKL5@T6IWmgy* z41;)fa*eT_2vQM8ecn$P1ALh@<47$+0gK@V1NH%w3W^9-gc}OvXN;vH5K+hiAt2fw z3ZAMW$N;`XxQ;M)30NY1q~JMuYb{>na#rK*@~zS8cz(P-9{k=M*C)4ECd;cA2A-@v zcr|$4eBFJs)qQn5S^G&x|0>pA3{ILS-Iw&m@uXOvS}JvF5PNBg<+85WaMM|mMz9?M z&LVqO`NKBz6mXZMEshSN{w~;OGgVkfS~Zfx&Z6~(W=*!thMYZVjh779V5@A&SmM!q4dJt}B7rJT#kF-y{( zkEPR2vIJPywub$gusX}n2`Zi-&8!_ zvNB4J$T+8I8CfOdm;o0l%aUxU!s~=^K?Um) zDg>~jTqK+jSW&EVJ&N25PKJXd5W(pX-UlNQjC1yhLN6#f@ZdwS1v$!D+DqUb^F%a9 zXHZup3!w+)`c6M1`L+#uzlDF_Uyq7^#kxwi8~Wx{0lw5K^uS2 z_BGnRL0fk=GQ6LteZF=6ae^nWCiHS`Qky+~F?;axvVTRVYtz}HcCD9 zrWe73#T;|!Md&|Mdy&E7K}32JV(-4$O`-_m9De-fy*Kkdey^9$s|3&Ecj>M7l#s9D zGB`?~^!AYK6O)*FNI zD6)#vTSC|;76ED!NSG9oa|FvvS+bQLlxHbsVjknnb^hD;u^%z(uMnjy=5!!zO!YA1D~(N#@` z9Qr`K0HHAmIUy}z6anWTG1sp#QPvc{t8Xy_eH=5P9?=t;) z+?2f;Pi=S)vCC3xmuGC-3%t;_dEt-#O9SgU%)%xR_Q>bL^uhGO+95q$>=r*3ZX6f% z*U}rMhfb_2DsENFi4Oy(;o^s?ZO2t0&)N1)+wuFJ5!Fw6oZ8x?>&n zNS!^FX3r#=T8N*C7pvMB&kpz0Wgx@`p(vNb-If>BLCD4BX0)sz6z1doS(neTwQY~6>2GQ2d|0k-{mogq&)aMoa{dey?b36Xx4qQWrSQK^( z;YX5E`h{FSA(P+8#CK9WCdCtS^MtJYlqI_GcD^&+S?kj7VyAd?e(sE*E&B8qZ5|JR literal 0 HcmV?d00001 diff --git a/modules/variant_builder/context.py b/modules/variant_builder/context.py index 3f7c925..d4ec410 100644 --- a/modules/variant_builder/context.py +++ b/modules/variant_builder/context.py @@ -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) diff --git a/modules/variant_builder/default_task_selector.py b/modules/variant_builder/default_task_selector.py new file mode 100644 index 0000000..fe1a406 --- /dev/null +++ b/modules/variant_builder/default_task_selector.py @@ -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 diff --git a/modules/variant_builder/filters/__pycache__/__init__.cpython-313.pyc b/modules/variant_builder/filters/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..deae6c459da7cac02d60cb648c3c755a86ebcfb6 GIT binary patch literal 1697 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 NpTO!5kQY^1{{Ws}tE2z` literal 0 HcmV?d00001 diff --git a/modules/variant_builder/filters/builder.py b/modules/variant_builder/filters/builder.py index afd70d7..a1737bf 100644 --- a/modules/variant_builder/filters/builder.py +++ b/modules/variant_builder/filters/builder.py @@ -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]"], diff --git a/modules/variant_builder/filters/dynamic/__init__.py b/modules/variant_builder/filters/dynamic/__init__.py index dedf8c4..276e402 100644 --- a/modules/variant_builder/filters/dynamic/__init__.py +++ b/modules/variant_builder/filters/dynamic/__init__.py @@ -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) diff --git a/modules/variant_builder/filters/dynamic/__pycache__/__init__.cpython-313.pyc b/modules/variant_builder/filters/dynamic/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5b419a427f46f30b0e93813df60aa2c39e37491 GIT binary patch literal 2967 zcmb_e%}*Ow5P!Q~e}7{WzS_hTXlg=nX(3e<(uxqZ(z-$+PNGWLYPHx4Y-4PgUDAYH z(o3tJnpQ&P$PuI-IOfY-~7D#nskoObThd1Ar!YPF0;xDi$`onk#}@d(z9S_PGLrZJET4CI0Y9!)98 zninhO*8*D5ty96Hh1xud79RH%yuIv8rCEf=eN~0Kxn(D}vTCgvP|7V;ORHtm&fRkhsL!oh<&p`xd6AQy zE4kIo>AF?199e!x4#mFU*IZX1qe&s%1RXIfrj^khVg(($8s#14Hdk*cA}iu59l zLAi>T>PBUq-D%Foaj(pSQqv%2vg!RCOJwWr5fcA|$9UfNBq65(yMsnM1(BIkMj-F-+oJ zn(5Sa+rg$Q>W1ThHMi=RHttC&i=v53hcuZaXkYslif`C6Z|YE%y{TvEo{j#E*-dG4 zY-4ygJ#^?H=g7sKl_&jAW`B`()^a|xzN zIWb&Yr<$fOE_{M?n!W^*Pm;(72Jyfb1RBg=K>nCKL<6+Y4qcr<=v7qK*)4VhpY7uY zMYN62J0@h;MQujo0;^*=E5jwU-P?WxwFYOoS4mC4&z}bNQ#giTW78hUF#(qD{^{}$ zmw!u*?WV?d2KQ2vzs4t@1}0B}i|9hU7IDk#UaLtxZn|yI7JbUkotoz{&(Q7GsOulG zgLvxuhu=MX8tDCR`M?$2uAOLHtn{6ljY|XV~ud_Y1V z>J+?kgtt+tR0)&S3PCYI;R4fGPaZIc|453z&C2wEdhZ=s8UiFDtw=|U4Otz1EjJYCE zmk0vm;pUHwY16+S;>UTgY*iM(M=|uFb4x)Kz$5j~>?nQKMRjC6!;c4drLH46F7<6(*-CCrZLRF| VZ}sl>jn^3Z@EYarmozJ`>>oS_mX-hj literal 0 HcmV?d00001 diff --git a/modules/variant_builder/filters/dynamic/__pycache__/composite.cpython-313.pyc b/modules/variant_builder/filters/dynamic/__pycache__/composite.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d420dddf3e040752321e0eb851f4b4b5c294122e GIT binary patch literal 2230 zcma)7O>7fK6rR~#|NkX+K!Mia@Kd^A?4}S9D5Of$MwQ)03Y(xxEG)}<9ky(|VRlUe z2q{tzRaL3w5-IAXIVKmBBS*xg*H|f{9SISs?SWe=b11jInOz55g4C7#=FOXVZ{GXf zH{*6H6-V&>{5W#$u87bdY|{Do3QXERfLKHdQg9VjFu@gp2o;ft1!fbfQYAz}To)wI7>QNlBwk67L?ua*+#af?Dru7DdbpaY^pKtcQgJ^zfRxByq(s#oHLfPr zgc>_3h2Z2N$cueL{(wmDOiYeHXERx(v6}AbhON6U4b?5vuF(iF=WN|DWlW>aO_LC- z#`MtSlHMQPJ?`X_%N^lQfK=E8vfi1V1)!kAt|3FAIz4A_kRz z)50`92{=J!(P?yE=-6M4x$WK#1zv}cEKC^NAz&-R^920(5hKDuRDdQ-qa6BdH$qPD z`PcUbk*CBdVJJB6CRzlFe$kIm0z7BXX|ax1@dOl{HLjV)b zun!B_1@TB92$$7aC@d_{&&$`GS+newzjny=@|;(`(X?*sq*k7FYE9d8%Qtmm=?zc2 z+O+JNNy-7j<(kj7ve9WrY3>${4bPYjbAFDD(BYl?RC+VX(y;9qy6ujXw!L#K$NeDw zd5#v)dRBR4KNx?KE4FgQm8rE8PjaW%bEhBKFL@o`>>I$cKtsHZsb3RB6L1dZWpl zM<2q<>m2%WN8Y^80>F0+UZyhw=ZuUALqQe@BMjG9F&O~eWeh3sT4>lOHqF1K9DkYh z-34|5mVah;-}9avKi4{b?pOI-D>Jel9U%u{sVjZPHJ<(fP#8ORVrAzNA3|ggY=eZ~ zgMl0d@p_7JEx3J}MhD)K=Bq6%lA8vxfHpI^AMbp3=li?s(RX=@t7rn?V0GL+uyde` zW5vRSVqvma7`-I#qEStQC((q1Aqk(*peHr$MpL(gm5ip z5cX1%1QYCaAyE(%_Uny?>#A zq3NDStgB7WWI>LxU91g0MH%M^6YN~~ClKGF%~bEbZ1$6Bf4hE)8$ zKfi3Y`il$Idwnq745hXNF*JpL>)W$5ur$d$mX5ai4sVIvT0FIO+y7#wOBjWGd>Dsm z*t<1nHD)+MG#NZFCH5e5ylAdtYI>IKmS8u;V4(Yr;5}g7p!+);|MNJN45#6l^BzZ@ z9(miY;0Y@QPnh3p4taMQXmr>QF+$G5vFzHppMuyDF~-l(U<(aCMeqED_B}&KH_*|i r=+sj*3J?j27eWutFI`$0T+!Eh*G5;)w(_Ui2y6hGkocRea@hU_yiyvM literal 0 HcmV?d00001 diff --git a/modules/variant_builder/filters/dynamic/composite.py b/modules/variant_builder/filters/dynamic/composite.py index 8c7213c..fe31b1e 100644 --- a/modules/variant_builder/filters/dynamic/composite.py +++ b/modules/variant_builder/filters/dynamic/composite.py @@ -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) diff --git a/modules/variant_builder/filters/dynamic/must_not.py b/modules/variant_builder/filters/dynamic/must_not.py deleted file mode 100644 index 4b4ef62..0000000 --- a/modules/variant_builder/filters/dynamic/must_not.py +++ /dev/null @@ -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]): - diff --git a/modules/variant_builder/filters/static/CompositeFilterStatic b/modules/variant_builder/filters/static/CompositeFilterStatic deleted file mode 100644 index feac656..0000000 --- a/modules/variant_builder/filters/static/CompositeFilterStatic +++ /dev/null @@ -1 +0,0 @@ - e diff --git a/modules/variant_builder/filters/static/__pycache__/__init__.cpython-313.pyc b/modules/variant_builder/filters/static/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1afd8f49a60ce8e2a6cce5599a34a1191ffecbb GIT binary patch literal 2401 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 DG37JI literal 0 HcmV?d00001 diff --git a/modules/variant_builder/filters/static/__pycache__/must_be_one_of.cpython-313.pyc b/modules/variant_builder/filters/static/__pycache__/must_be_one_of.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4fe0615c201c8b2dcf63f5b42b697e3c5f932a1 GIT binary patch literal 3498 zcmbtWT}&L;6}~e&yDa+$><>1$wigVz>!iCTfkt&542|XD#bE;jRTZy18g>UV_UtU& znT0q~T%EDjbMEY7?_#@E zd!;#Z=H7G9JwM;MXR5)VkHGl+WB$DfHz9w=kNRk>G1ZTOxl2SMaubB&o1fs)Jmura zyCz&|feMTlCfsQc^`u*93;T9Yc+;)4mGPblU)oRo=>QF+gEW{9(GZ(&nP^M5({{#t zCpyw$8jh2rq?d@T-9+?d+p~deC>zZBhXgQ?47(hwXk>v)_#flPWP-E3mraGrbD9FY zdtOzve1f;V#bt$RMLF+qRL!&HsTtY0kq~Td%;w&*xhb3b8SDyNQZ>*anzE_p;5)>` zGW3$1GmG?Q&NopH%W`$=KUh>^Qni$!-f3KD^a6JuhKb}o1lA-m)4l41Tt$>>UE-bupa z0QdwsXv-_}a!E6#c_#^Wr~snUoFWxqG)3un5K90L-D$AHYiQTra!9--d@ zzTpsDWQQafq_1N5*qG(69)xwc#(lrGCId=m$uvf|X-uQOrg61iHgJ)2lUc5=ZMb$; zoMif6pbaf+8nfIqqNHg&)%5~RFSm;ayZC8LOw*_r*w;lY8>qaRkRPtGq%wIoP@-aI4;yq>$o5NBFauncx|6*s5;b!o>M5|ks|zT zkMh2$=y}^!kQZ%_ytoJ-^b}iJmbH>%(37a}`2a?+8w!wB^2A31owxJ98L#;JEr0)R zpuG|}X$4MJ0s~fH;4@==WOHce^ytID*k0pO+5CLv)0M9Ssa~hb`Zovif2W11o zwK0w5-ZZcn>acxY*@y?L1^#IX&GNIZAzlQ)UiSjRSQl?p93cmwtpT9#|AHz*16;~n zp&1SK*~1`J4atB`YCtc}9|meam|BC*X2+kSe{!JHP(>K$*WsyN0y4s%0=Qu&55;@~_8al{^TGUtO5q4Bvj^ z-rSuxE0LjcdZX9s8~Qpj1oPjqgMWFVhf8=I>)Pwq6%

%ce|U0abQuNnfGzqEw`| zOV)3)3QGu1Gf08}#oW^$0;#z&I_kI*cs4QiiI;nW^>17R^4%)g3wM5U`p)T(T6SYy zmDm|8cBT?bTCwE%t)19NB{puw#rGA~xirZh$aWnsfUEk;C3Yv5inH6^hrE(;hv@VOOw)xnfbH=aG#`lGEEI z*)*v-S27jUiMNpB25Ya_CnyqZL`Ddz>{sNQfmb#!ZbiTB`b*azGuFVB)ydlz??r#x z_3N%XnH}NEo;$cJWbXaEtZz(ha+`yj^2WQ?z^HY6?17Nk%I^r7s*89+AOn*7JnxR| zMtjSHWqGY*ZLECGiYE76IC*Y-yJLH7d+EWnxZQ1?n_;tit--1rR+$R6dEeXWzRc}~ zI&a@{RIDwPuUkh3tJ=H53HWo_6bgIm{s;>hgX@L6DkZ)B1+$am)({tYMPQ` zIZDcNIhG*XkGRfhvSC0IU`h6vHy2fX!AT-!$PU#0#gbT|PQD`bsgkK`MiREbc9vL{ zY?f!txIIu8TyFf2OVSk*baOM@y+CL8+D7C zB*l6p38Nxm4v{kF0g@EjNz#>cC+p%4%vE%BQ|4K(N!E+bH()2}6@~SB(IvX$f>^hU zd@3BXLogq=@+Sjp9=TV`7OJzwcAX_uZv6_saf{xK-)zibz5QEo&g7QJsNLuna`vH5 z$hATA14?jqKZN^~Wg!6L9BYDjEgEg5$tXttiq>&8GfrRCGg8BIe-thzt1K+NXW8>9hg~lG0Za(?M-AE-(8-WldVZG7I!r-V zvJ!~H=P50z=nPv1X-m%iF)mrmD>twrNhgXb+ zVWwkQ!{P9zP`sjcsQ3GMs0|?y2Oi=Gzb=;$AU;OvrB&dL*|UT8o(yevp;24i&yWq@ zxotDD<-7fata-iUS+Tps=?z!bwQMJ&^q8H=DA^EaHRfx-b_@qre6RhkT-*QhJW7VK z5}U?waFYU-Yng?ieK{ZzkLgu)H4BZGO(_hI#X=|$5{A`fn2zbbCTn>`H|ykd8oQCT zgZ34;VH@e=Ag0L+FY#|Lr0&NTeBr7uyx6+?_Q2!7j-vDag@r&@HPH3Ab=&oDm!JKcqK!qx}?M7qr+R4j-J$VDOuA`MOVM<>*PLpTB=HHroD zR=%PDMS3O4;#-*eC21-zYxYi1k|tE98=9I`vN=g&cnYieS6kkyI#|ltYz}ETm({@% z`N(2JenWsw2t(3f_|FXUqd-P5h4EZYV=$LEE3%}Lx1u*3Iziv1V3H%X2=`Vg^$=O;b`Flc+qN;%sI5(y}3^G+Eabog-?xjT=|h zY{mi&Q8In@3miob*Jb_n))?!7DG^fU{6~j%BdenZ@ zqZarK-(!A)O~{*`la_~5tQSm>XY>;wmYtN+CDOS>_C6s8{vvHlWZx5VXr3HeBE2u% ZyXk3K{2XAN!UicPs{4=sO;E{8^)I^Mdp7_8 literal 0 HcmV?d00001 diff --git a/modules/variant_builder/filters/static/__pycache__/must_include_any_tag.cpython-313.pyc b/modules/variant_builder/filters/static/__pycache__/must_include_any_tag.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3b563babc48683dbd2fe05f02202f8c3c907994 GIT binary patch literal 2931 zcma)8|4&m_6u-B9Eu~*5ZBbCs6{yU*tQ}fZ42Vp&K#3;sz=$+^d$f=8#gEqMTe9+7=P&cmpkW60BGF_fi@%Z7CGNMKd*9hU-cj3?h&W(kMTpLDS~>di?t50iU{M?4-uaZGTK&MhOBxRqGh+&aR=ZNwJ0 z6FXaTBXw~Ham4FMJzLvGoN*U%vAKQ39rqAVyn!^ty~G=DB#lg8H{y%?i9d*1P&?us z2N7SN@FyA)jR|kU6XR@Ekua=^=Rl~`c63}+=fa$h2XuTv$D=yF3~KkMvH}iyP1NLM(g>O6^4v~6Z{zJTj;}k2 zzTmpe5X8l-+zHRNN9NVAh3f3F-C+rp+qt6O*m-B|H?=wJwEqy!nb;GVYHR&M&MxW` za&6$BJ)rK=kC6|&0G7A8SQTA6Zr~shg zO-VCiPSJ!JF{x$Ag3f7xnr;QD+li#*Naj+~Vpfa(03?XEM-{plz)7QJd}$diTXas9 zlo`?lT9w)dgZky@Z1$QI)uK1EWG;GLi_YidTOvtCuVqs?MN*?TL?VkBO_ zvzumU&v)-3wC#1uv1vEOxQ(uiJK2sVaJQMs1geR%74w~6TgE(_zPEoj@9ckhoT8lT~NH6X_@kAX5AP`E8fz|0&0w7b|L6t-MSZgm&f(R!bRzv@*G=WquK z0v$}C02&G+l)>YcG0^WGV>F>%V;hkh=$^W7djssS{l3CbkuUj51Er>ila{Y8<{;#^VZpZZxVW!4Uur9jmT~b` zrR~H9qRO&XWxp(HmDw?nS$Qsnn{;n7t0+>EVGl$S4z~xq#~-Sq%uTxyS8v$ z&ZG^{C`!7^e0?L7!5Day@O1aKTAgE1>VZvFPLmZi0-nGth1TFihBPC_&P~Hz4|>FW z&?5%;guce?1iO&e9leH!bEF?kD9fm4Kx|ks#_K4wjt)OZNB=@?>*&aHbbJ*ZUq}6~ c?T7GrTu@3~C9&))50uVUj-2`%Q6($Yzhq5#{{R30 literal 0 HcmV?d00001 diff --git a/modules/variant_builder/filters/static/composite_filter.py b/modules/variant_builder/filters/static/composite_filter.py deleted file mode 100644 index 48c5a11..0000000 --- a/modules/variant_builder/filters/static/composite_filter.py +++ /dev/null @@ -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) diff --git a/modules/variant_builder/filters/static/must_not.py b/modules/variant_builder/filters/static/must_not.py deleted file mode 100644 index 4b0c503..0000000 --- a/modules/variant_builder/filters/static/must_not.py +++ /dev/null @@ -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 diff --git a/modules/variant_builder/task_pool.py b/modules/variant_builder/task_pool.py index a72ad4f..fb616b2 100644 --- a/modules/variant_builder/task_pool.py +++ b/modules/variant_builder/task_pool.py @@ -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) diff --git a/modules/variant_builder/task_selector.py b/modules/variant_builder/task_selector.py index 0e66b2e..6b8d38f 100644 --- a/modules/variant_builder/task_selector.py +++ b/modules/variant_builder/task_selector.py @@ -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]: ... diff --git a/modules/variant_builder/variant_set.py b/modules/variant_builder/variant_set.py index 817a4c1..9e7ec1a 100644 --- a/modules/variant_builder/variant_set.py +++ b/modules/variant_builder/variant_set.py @@ -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]): ... diff --git a/modules/variant_builder/variant_task.py b/modules/variant_builder/variant_task.py index 71046f5..2a8bd97 100644 --- a/modules/variant_builder/variant_task.py +++ b/modules/variant_builder/variant_task.py @@ -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]() diff --git a/test.py b/test.py new file mode 100644 index 0000000..364809c --- /dev/null +++ b/test.py @@ -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")