feat: improve response plan display
This commit is contained in:
@@ -9,7 +9,7 @@ from .dataset import (
|
||||
to_human_readable_value,
|
||||
from_human_readable_value,
|
||||
)
|
||||
from .query import Request, ResponsePriority, Response, ResponseItem
|
||||
from .query import Request, ResponsePriority, Response, MAX_RESPONSE_CNT
|
||||
from .resolver import Resolver, LutResolver, BfsResolver
|
||||
|
||||
_TStrEnum = TypeVar("_TStrEnum", bound=enum.StrEnum)
|
||||
@@ -76,6 +76,8 @@ class App:
|
||||
print('Type "help" for more info. Type "exit" to quit.')
|
||||
self.__op_main()
|
||||
|
||||
# region: Subcommand Processors
|
||||
|
||||
class MainCmd(enum.StrEnum):
|
||||
QUERY = "query"
|
||||
HELP = "help"
|
||||
@@ -179,17 +181,8 @@ class App:
|
||||
index = current_page * (ITEMS_PER_PAGE - 1) + i
|
||||
if index >= cnt:
|
||||
continue
|
||||
# fetch item and print it
|
||||
item = response[index]
|
||||
print(
|
||||
"Plan {0}\tValue: {1}\tDiff: {2} ({3:.2%})".format(
|
||||
index + 1,
|
||||
to_human_readable_value(item.value),
|
||||
to_human_readable_value(item.difference),
|
||||
item.relative_difference,
|
||||
)
|
||||
)
|
||||
self.__illustrate_circuit(item.circuit, response.device_kind)
|
||||
# and print it
|
||||
self.__illustrate_response(response, index)
|
||||
|
||||
# print page footer
|
||||
print("")
|
||||
@@ -204,6 +197,10 @@ class App:
|
||||
case App.PageViewerCmd.QUIT:
|
||||
break
|
||||
|
||||
# endregion
|
||||
|
||||
# region: Command Utilities
|
||||
|
||||
def __accept_command(self, cmd_enum: type[_TStrEnum]) -> _TStrEnum:
|
||||
"""
|
||||
Accept a command from the user.
|
||||
@@ -223,8 +220,6 @@ class App:
|
||||
print("Unknown command, please try again.")
|
||||
|
||||
def __accept_count_value(self) -> int:
|
||||
MAX_COUNT: int = 50
|
||||
|
||||
while True:
|
||||
self.__show_prompt_arrow()
|
||||
words = input()
|
||||
@@ -237,7 +232,7 @@ class App:
|
||||
print("Wrong value, please try again.")
|
||||
continue
|
||||
|
||||
if value > MAX_COUNT or value <= 0:
|
||||
if value > MAX_RESPONSE_CNT or value <= 0:
|
||||
print("Wrong value, please try again.")
|
||||
else:
|
||||
return value
|
||||
@@ -319,28 +314,35 @@ class App:
|
||||
else:
|
||||
return None
|
||||
|
||||
def __illustrate_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None:
|
||||
match circuit.device_scale:
|
||||
case CircuitDeviceScale.ONE:
|
||||
self.__illustrate_one_device_circuit(circuit, device_kind)
|
||||
case CircuitDeviceScale.TWO:
|
||||
self.__illustrate_two_device_circuit(circuit, device_kind)
|
||||
case CircuitDeviceScale.THREE:
|
||||
self.__illustrate_three_device_circuit(circuit, device_kind)
|
||||
# endregion
|
||||
|
||||
# region: Response Display
|
||||
|
||||
def __get_device_unit(self, device_kind: DeviceKind) -> str:
|
||||
match device_kind:
|
||||
case DeviceKind.RESISTOR:
|
||||
return '\u2126'
|
||||
return "\u2126"
|
||||
case DeviceKind.CAPACITOR:
|
||||
return 'F'
|
||||
return "F"
|
||||
case DeviceKind.INDUCTOR:
|
||||
return 'H'
|
||||
return "H"
|
||||
|
||||
def __to_circult_graph_value(self, value: float, device_kind: DeviceKind) -> str:
|
||||
# Remove unit and append device unit
|
||||
# Remove sign and append device unit
|
||||
return to_human_readable_value(value)[1:] + self.__get_device_unit(device_kind)
|
||||
|
||||
def __to_plan_head_value(self, value: float, device_kind: DeviceKind) -> str:
|
||||
# Remove sign and append device unit
|
||||
return to_human_readable_value(value)[1:] + self.__get_device_unit(device_kind)
|
||||
|
||||
def __to_plan_head_diff(self, value: float, device_kind: DeviceKind) -> str:
|
||||
# Keep the sign and append device unit
|
||||
return to_human_readable_value(value) + self.__get_device_unit(device_kind)
|
||||
|
||||
def __to_plan_head_diff_pct(self, value: float) -> str:
|
||||
# Keep the sign and format it as percentage style without trailing device unit
|
||||
return "{:.2%}".format(value)
|
||||
|
||||
# YYC MARK:
|
||||
# The function showing circuit graph should be maintained carefully.
|
||||
# First, we want they are show in console properly,
|
||||
@@ -362,11 +364,46 @@ class App:
|
||||
# This value should consider the length of f-string syntax, pre-defined chars and required chars.
|
||||
# To make sure a pretty showcase both in code and display.
|
||||
|
||||
def __illustrate_one_device_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None:
|
||||
def __illustrate_response(self, response: Response, index: int) -> None:
|
||||
"""
|
||||
Illustrate response item with given response and item.
|
||||
|
||||
:param response: The response to illustrate.
|
||||
:param index: The zero-based index of the item to illustrate.
|
||||
"""
|
||||
# fetch item and device kind from response
|
||||
item = response[index]
|
||||
device_kind = response.device_kind
|
||||
# print header
|
||||
print(
|
||||
"Plan {0:<4} Value: {1:<16} Diff: {2} ({3})".format(
|
||||
index + 1,
|
||||
self.__to_plan_head_value(item.value, device_kind),
|
||||
self.__to_plan_head_diff(item.difference, device_kind),
|
||||
self.__to_plan_head_diff_pct(item.relative_difference),
|
||||
)
|
||||
)
|
||||
# print circuit graph
|
||||
self.__illustrate_circuit(item.circuit, device_kind)
|
||||
|
||||
def __illustrate_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None:
|
||||
match circuit.device_scale:
|
||||
case CircuitDeviceScale.ONE:
|
||||
self.__illustrate_one_device_circuit(circuit, device_kind)
|
||||
case CircuitDeviceScale.TWO:
|
||||
self.__illustrate_two_device_circuit(circuit, device_kind)
|
||||
case CircuitDeviceScale.THREE:
|
||||
self.__illustrate_three_device_circuit(circuit, device_kind)
|
||||
|
||||
def __illustrate_one_device_circuit(
|
||||
self, circuit: Circuit, device_kind: DeviceKind
|
||||
) -> None:
|
||||
dev1 = self.__to_circult_graph_value(circuit.first_device_value, device_kind)
|
||||
print(f"──[{dev1:^16}]──")
|
||||
|
||||
def __illustrate_two_device_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None:
|
||||
def __illustrate_two_device_circuit(
|
||||
self, circuit: Circuit, device_kind: DeviceKind
|
||||
) -> None:
|
||||
dev1 = self.__to_circult_graph_value(circuit.first_device_value, device_kind)
|
||||
j2 = circuit.second_device_joint
|
||||
dev2 = self.__to_circult_graph_value(circuit.second_device_value, device_kind)
|
||||
@@ -374,12 +411,14 @@ class App:
|
||||
case JointKind.SERIES:
|
||||
print(f"──[{dev1:^16}]──[{dev2:^16}]──")
|
||||
case JointKind.PARALLEL:
|
||||
SEP0: str = ' ' * (6 + (16 - 10))
|
||||
SEP0: str = " " * (6 + (16 - 10))
|
||||
print(f" ┌──[{dev1:^16}]──┐ ")
|
||||
print(f"──┤ {SEP0} ├──")
|
||||
print(f" └──[{dev2:^16}]──┘ ")
|
||||
|
||||
def __illustrate_three_device_circuit(self, circuit: Circuit, device_kind: DeviceKind) -> None:
|
||||
def __illustrate_three_device_circuit(
|
||||
self, circuit: Circuit, device_kind: DeviceKind
|
||||
) -> None:
|
||||
dev1 = self.__to_circult_graph_value(circuit.first_device_value, device_kind)
|
||||
j2 = circuit.second_device_joint
|
||||
dev2 = self.__to_circult_graph_value(circuit.second_device_value, device_kind)
|
||||
@@ -393,8 +432,8 @@ class App:
|
||||
print(f"──[{dev1:^16}]──[{dev2:^16}]──[{dev3:^16}]──")
|
||||
case JointKind.PARALLEL:
|
||||
# First series then parallel
|
||||
SEP0: str = '─' * (6 + ((16 - 10) // 2))
|
||||
SEP1: str = ' ' * (6 + 2 * (16 - 10))
|
||||
SEP0: str = "─" * (6 + ((16 - 10) // 2))
|
||||
SEP1: str = " " * (6 + 2 * (16 - 10))
|
||||
print(f" ┌──[{dev1:^16}]──[{dev2:^16}]──┐ ")
|
||||
print(f"──┤ {SEP1} ├──")
|
||||
print(f" └───{SEP0}[{dev3:^16}]{SEP0}───┘ ")
|
||||
@@ -402,7 +441,7 @@ class App:
|
||||
match j3:
|
||||
case JointKind.SERIES:
|
||||
# First parallel then series
|
||||
SEP0: str = ' ' * (6 + (16 - 10))
|
||||
SEP0: str = " " * (6 + (16 - 10))
|
||||
print(f" {SEP0} ┌──[{dev1:^16}]──┐ ")
|
||||
print(f"──[{dev1:^16}]──┤ {SEP0} ├──")
|
||||
print(f" {SEP0} └──[{dev2:^16}]──┘ ")
|
||||
@@ -412,6 +451,8 @@ class App:
|
||||
print(f"──┼──[{dev2:^16}]──┼──")
|
||||
print(f" └──[{dev3:^16}]──┘ ")
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
|
||||
@@ -217,6 +217,9 @@ def to_human_readable_value(v: float) -> str:
|
||||
case UnitScale.MILLI:
|
||||
return "{:+.4f} m".format(v / 1e-3)
|
||||
case UnitScale.NONE:
|
||||
# YYC MARK:
|
||||
# The space of this format string is by design
|
||||
# for keeping the same style with other format strings.
|
||||
return "{:+.4f} ".format(v)
|
||||
case UnitScale.KILO:
|
||||
return "{:+.4f} k".format(v / 1e3)
|
||||
|
||||
@@ -16,6 +16,10 @@ class ResponsePriority(enum.Enum):
|
||||
"""More accuracy is the first priority."""
|
||||
|
||||
|
||||
MAX_RESPONSE_CNT: int = 50
|
||||
"""The maximum count for the response item count passed in request."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Request:
|
||||
"""
|
||||
@@ -33,6 +37,21 @@ class Request:
|
||||
count_limit: int
|
||||
"""The limited count of results."""
|
||||
|
||||
def __post_init__(self):
|
||||
target_value = self.target_value
|
||||
if target_value <= 0:
|
||||
raise ValueError(
|
||||
f"Invalid value {target_value} for target value in request."
|
||||
)
|
||||
tolerance = self.tolerance
|
||||
if tolerance < 0:
|
||||
raise ValueError(f"Invalid value {tolerance} for tolerance in request.")
|
||||
count_limit = self.count_limit
|
||||
if count_limit <= 0 or count_limit > MAX_RESPONSE_CNT:
|
||||
raise ValueError(
|
||||
f"Too large or too less value {count_limit} for response count limit in request."
|
||||
)
|
||||
|
||||
|
||||
class ResponseItem:
|
||||
"""
|
||||
@@ -94,7 +113,9 @@ class ResponseItem:
|
||||
|
||||
:return: The unsigned difference.
|
||||
"""
|
||||
return self.__cv_trait.unsigned_difference(self.__circuit, difference=self.difference)
|
||||
return self.__cv_trait.unsigned_difference(
|
||||
self.__circuit, difference=self.difference
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def relative_difference(self) -> float:
|
||||
@@ -106,7 +127,9 @@ class ResponseItem:
|
||||
|
||||
:return: The signed relative difference.
|
||||
"""
|
||||
return self.__cv_trait.relative_difference(self.__circuit, difference=self.difference)
|
||||
return self.__cv_trait.relative_difference(
|
||||
self.__circuit, difference=self.difference
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def unsigned_relative_difference(self) -> float:
|
||||
@@ -137,9 +160,7 @@ class Response:
|
||||
def __init__(self, request: Request, candidates: Iterable[Circuit]) -> None:
|
||||
cv_trait = CircuitValueTrait(request.device_kind, request.target_value)
|
||||
self.__device_kind = request.device_kind
|
||||
self.__sorted_items = list(
|
||||
ResponseItem(item, cv_trait) for item in candidates
|
||||
)
|
||||
self.__sorted_items = list(ResponseItem(item, cv_trait) for item in candidates)
|
||||
|
||||
# Sort by different strategy
|
||||
match request.response_priority:
|
||||
|
||||
Reference in New Issue
Block a user