310 lines
9.9 KiB
Python
310 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from Interfaces.OpoTestsInterface import OpoTestsInterface
|
|
from typing import Self, Any
|
|
from Utils.Patterns import RE
|
|
from Utils.Check import Check
|
|
from Utils.Utils import Utils
|
|
from re import Match as REMatch
|
|
from Interfaces.FormatModeInterface import FormatModeInterface
|
|
from Modules.Format.CapitalizeFormat import CapitalizeFormat
|
|
from Modules.Format.MixFormat import MixFormat
|
|
from Modules.Format.PlainFormat import PlainFormat
|
|
from Modules.Format.RandomFormat import RandomFormat
|
|
from Modules.Format.RangeFormat import RangeFormat
|
|
from Modules.Format.SelectFormat import SelectFormat
|
|
from Modules.Format.SerieFormat import SerieFormat
|
|
|
|
class FormatModule:
|
|
|
|
def __init__(self:Self, op:OpoTestsInterface) -> None:
|
|
self.op:OpoTestsInterface = op
|
|
self.modes:dict[str, FormatModeInterface] = {}
|
|
|
|
for keys, mode in (
|
|
(("capitalize", "cap", "capital", "Cap", "Capitalize", "Capital"), CapitalizeFormat(self)),
|
|
(("mix", "mixin"), MixFormat(self)),
|
|
(("Mix", "Mixin"), MixFormat(self, {"capitalized" : True})),
|
|
(("plain",), PlainFormat(self)),
|
|
(("rand", "random"), RandomFormat(self)),
|
|
(("range",), RangeFormat(self)),
|
|
(("select",), SelectFormat(self)),
|
|
(("Select",), SelectFormat(self, {"capitalized" : True})),
|
|
(("serie",), SerieFormat(self)),
|
|
):
|
|
for key in keys:
|
|
self.modes[key] = mode
|
|
|
|
def start(self:Self) -> None:
|
|
pass
|
|
|
|
@staticmethod
|
|
def set_fragments_level(string:str, fragments:list[str]) -> str:
|
|
return RE.FRAGMENT_PATTERN.sub(lambda matches:fragments[int(matches.group(1))], string)
|
|
|
|
@staticmethod
|
|
def __build_fragment(matches:REMatch, fragments:list[str]) -> str:
|
|
fragments += [matches.group(1)]
|
|
return "$$$" + str(len(fragments) - 1) + "$$$"
|
|
|
|
@classmethod
|
|
def build_fragments(cls:type[Self], string:str, fragments:list[str] = []) -> str:
|
|
return cls.set_fragments_level(Utils.recursive(string, lambda string:(
|
|
RE.FRAGMENT_VARIABLES.sub(lambda matches:(
|
|
cls.__build_fragment(matches, fragments)
|
|
), string)
|
|
)), fragments)
|
|
|
|
def __process_variable(self:Self,
|
|
i:int,
|
|
original:str,
|
|
key:str,
|
|
shared:dict[str, Any|None],
|
|
fragments:list[str]
|
|
) -> str:
|
|
if Check.is_key(key):
|
|
if key in shared:
|
|
return str(shared[key])
|
|
|
|
variable:list[Any|None] = self.op.questions.get_value(key, i)
|
|
method:str
|
|
data:list[Any|None]
|
|
|
|
if variable is not None:
|
|
method, *data = variable
|
|
if method in self.modes:
|
|
return self.modes[method].get(i, data, shared, fragments)
|
|
else:
|
|
|
|
matches:REMatch|None = RE.FORMAT_SPLIT.match(key)
|
|
|
|
if matches:
|
|
method, *data = matches.groups()[1:3]
|
|
if method in self.modes:
|
|
return self.modes[method].get(i, data, shared, fragments)
|
|
return original
|
|
|
|
def __execute_varibles(self:Self,
|
|
i:int,
|
|
string:str,
|
|
shared:dict[str, Any|None],
|
|
fragments:list[str]
|
|
) -> str:
|
|
try:
|
|
return RE.FRAGMENT_VARIABLES.sub(lambda matches:(
|
|
self.__process_variable(i, *matches.groups()[0:2], shared, fragments)
|
|
), string)
|
|
except Exception as exception:
|
|
print(exception)
|
|
return string
|
|
|
|
def execute(self:Self,
|
|
i:int,
|
|
string:str,
|
|
shared:dict[str, Any|None] = {},
|
|
fragments:list[str] = []
|
|
) -> str:
|
|
return Utils.recursive(self.build_fragments(string, fragments), lambda string:(
|
|
self.__execute_varibles(i, string, shared, fragments)
|
|
))
|
|
|
|
@staticmethod
|
|
def prepare_result(string:str, option:str, shared:dict[str, Any|None]) -> int:
|
|
if string.lower().startswith(option.lower()):
|
|
if "capitalized" not in shared or shared["capitalized"]:
|
|
shared["capitalized"] = False
|
|
return len(option)
|
|
return -1
|
|
|
|
def check(self:Self,
|
|
i:int,
|
|
string:str,
|
|
options:str|list[str],
|
|
shared:dict[str, Any|None] = {},
|
|
fragments:list[str] = [],
|
|
check_full:bool = True
|
|
) -> tuple[bool, int]:
|
|
|
|
length:int = 0
|
|
option:str
|
|
ok:bool = True
|
|
|
|
string = string.lower()
|
|
|
|
for option in Utils.get_array(options):
|
|
|
|
clone:str = "" + string
|
|
option_fragmented:str = self.build_fragments(option, fragments)
|
|
i:int = 0
|
|
|
|
length = 0
|
|
while True:
|
|
|
|
matches:REMatch|None = RE.FORMAT_PARTS.search(option_fragmented)
|
|
|
|
if not matches:
|
|
break
|
|
|
|
part:str = matches.group(0)
|
|
m:int = -1
|
|
key:str = matches.group(1)
|
|
method:str
|
|
data:list[Any|None]
|
|
|
|
if key:
|
|
if Check.is_key(key):
|
|
method, *data = self.op.questions.get_value(key, i)
|
|
if method in self.modes:
|
|
m = self.modes[method].check(i, clone, data, shared, fragments, clone, check_full)
|
|
|
|
# JS FormatModule lines 174-177.
|
|
|
|
else:
|
|
|
|
submatches:REMatch|None = RE.FORMAT_SPLIT.match(key)
|
|
|
|
if submatches:
|
|
|
|
method, *data = submatches.groups()[1:3]
|
|
|
|
if method in self.modes:
|
|
m = self.modes[method].check(i, clone, data, shared, fragments, clone, check_full)
|
|
|
|
if m == -1:
|
|
m = self.prepare_result(clone, part, shared)
|
|
|
|
if m != -1:
|
|
clone = clone[m:]
|
|
length += m
|
|
continue
|
|
ok = False
|
|
break
|
|
|
|
if ok:
|
|
ok = not check_full or not len(clone)
|
|
if ok:
|
|
break
|
|
|
|
return (ok, length)
|
|
|
|
def get_check_length(self:Self,
|
|
i:int,
|
|
string:str,
|
|
options:str|list[str],
|
|
shared:dict[str, Any|None] = {},
|
|
fragments:list[str] = [],
|
|
check_full:bool = True
|
|
) -> int:
|
|
|
|
has:bool
|
|
length:int
|
|
|
|
has, length = self.check(i, string, options, shared, fragments, check_full)
|
|
|
|
return length if has else -1
|
|
|
|
def check_select(self:Self,
|
|
i:int,
|
|
string:str,
|
|
options:tuple[tuple[int, int], str, list[str]],
|
|
shared:dict[str, Any|None] = {},
|
|
fragments:list[str] = []
|
|
) -> str|None:
|
|
|
|
length:int = 0
|
|
k:int = 0
|
|
check_last:bool = False
|
|
minimum:int
|
|
maximum:int
|
|
separator:str
|
|
items:list[str]
|
|
separator_length:int
|
|
|
|
(minimum, maximum), separator, items = options
|
|
separator_length = len(separator)
|
|
|
|
while k < maximum:
|
|
|
|
ok:bool = False
|
|
l:int = len(items)
|
|
j:int
|
|
item:str
|
|
|
|
if check_last or (k and k == l - 1):
|
|
if not string.startswith(" " + separator + " "):
|
|
return -1
|
|
|
|
length += separator_length + 2
|
|
string = string[0, separator_length + 2]
|
|
|
|
for j, item in enumerate(items):
|
|
|
|
has_variables:bool = RE.FRAGMENT_VARIABLES.match(
|
|
self.set_fragments_level(item, shared, fragments)
|
|
) is not None
|
|
item_length:int = (
|
|
self.get_check_length(i, string, [item], shared, fragments, False) if has_variables else
|
|
self.prepare_result(string, item, shared))
|
|
|
|
if item_length != -1:
|
|
string = string[:item_length]
|
|
items = items[:j] + items[j + 1:]
|
|
length += item_length
|
|
ok = True
|
|
break
|
|
|
|
if ok:
|
|
if "capitalized" not in shared or shared["capitalized"]:
|
|
shared["capitalized"] = False
|
|
elif check_last or not k:
|
|
return -1
|
|
else:
|
|
check_last = True
|
|
k -= 1
|
|
|
|
return -1 if k < minimum else length
|
|
|
|
def __get_list_items(self:Self, i:int, item:str) -> list[str]:
|
|
matches:REMatch|None = RE.LIST_ITEM.match(item)
|
|
|
|
if matches:
|
|
|
|
key:str = matches.group(1)
|
|
method:str
|
|
list_items:list[str]
|
|
|
|
method, *list_items = self.op.questions.get_value(key, i)
|
|
|
|
if method == "list":
|
|
return list_items
|
|
return [item]
|
|
|
|
def get_list(self:Self, i:int, items:list[str]) -> list[str]:
|
|
return [subitem for item in items for subitem in self.__get_list_items(i, item)]
|
|
|
|
@staticmethod
|
|
def check_range(string:str, inputs:str|list[str]) -> int:
|
|
|
|
number_matches:REMatch|None = RE.ABSOLUTE_INTEGER.match(string)
|
|
|
|
if number_matches:
|
|
|
|
number:int = int(string)
|
|
|
|
for range in (
|
|
[
|
|
int(value) for value in str(Utils.get_random(inputs.split("|"))).split("-")
|
|
] if Check.is_string(inputs) else
|
|
inputs if Check.is_array(inputs) else
|
|
[]):
|
|
if Check.is_number(range):
|
|
if number == range:
|
|
return len(string)
|
|
elif len(range) == 1:
|
|
if number == range[0]:
|
|
return len(string)
|
|
else:
|
|
if number >= range[0] and number <= range[1]:
|
|
return len(string)
|
|
return -1 |