2025-02-14 17:42:48 -07:00

95 lines
3.2 KiB
Python

from .backend import OutputAdapter
from .config import GroupConfig, ContextConfig
from typing import Tuple
def getPerfectMatch(
config: GroupConfig, outputs: list[OutputAdapter]
) -> OutputAdapter | None:
"""Returns the output adapter of a perfectly-matching output for the given config. or None if no such output exists."""
for output in outputs:
if (
output.make == config.get("make")
and output.model == config.get("model")
and output.serial == config.get("serial")
):
return output
return None
def getNamePreferences(
config: GroupConfig, outputs: list[OutputAdapter]
) -> list[OutputAdapter]:
"""
Returns the list of outputs that match the names in the config, in preference order.
"""
result = [None] * len(config.get("output_names", []))
for output in outputs:
for i, name in enumerate(config.get("output_names", [])):
if output.name == name:
result[i] = output
def computePerfectScore(
config: ContextConfig, outputs: list[OutputAdapter]
) -> Tuple[int, dict[str, OutputAdapter]]:
"""
Returns the computed score of the context for the given outputs, along with
the outputs that would be matched to each group.
Does not include context priority.
"""
score = 0
map = {}
groups = config.get("groups", [])
outputSet = set(outputs)
usedGroups = set()
# First, compute the perfect score, and remove any outputs that match
for group in groups:
match = getPerfectMatch(group, list(outputSet))
if match is None:
continue
score += 3
outputSet.remove(match)
usedGroups.add(group)
map[group.get("name")] = match
groups = [group for group in groups if group not in usedGroups]
if len(groups) == 0:
# Early return if all groups have been matched
return score, map
# Next, compute the preferences based on name
outputList = list(outputSet)
preferences = [getNamePreferences(group, outputList) for group in groups]
maxPreferences = max(len(preference) for preference in preferences)
# Iterate through the preferences, and remove any outputs that match.
# This is essentially a form of ranked-choice voting system.
for i in range(maxPreferences):
for j, preference in enumerate(preferences):
if (
i < len(preference)
and preference[i] is not None
and preference[i] in outputSet
):
score += 2
outputSet.remove(preference[i])
usedGroups.add(groups[j])
map[groups[j].get("name")] = preference[i]
groups = [group for group in groups if group not in usedGroups]
# Now, if there is more than one group left, we have failed, as only one group can match on wildcard.
# If there are no groups left, return the score.
if len(groups) > 1:
return 0, {}
elif len(groups) == 0:
return score, map
# Finally, if there is one group left, we can match on wildcard. Add one point, and give it the first
# remaining output.
score += 1
map[groups[0].get("name")] = list(outputSet)[0]
return score, map