1
0

feat: improve response plan display

This commit is contained in:
2026-06-28 11:19:25 +08:00
parent 5b3a2c030d
commit 5ad3e11836
3 changed files with 127 additions and 62 deletions

View File

@@ -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(

View File

@@ -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)

View File

@@ -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: