95 lines
3.2 KiB
Python
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
|