Source code for luna_python_matcher.app.handlers.matcher_raw_handler

""" Matcher Raw handler. """

from typing import Tuple, List

from sanic.response import HTTPResponse
from vlutils.descriptors.containers import sdkDescriptorDecode
from vlutils.descriptors.data import DescriptorsEnum
from vlutils.descriptors.match import match
from vlutils.descriptors.xpk_reader import readXPKFromBinary

from app.handlers.base_handler import BaseMatcherHandler
from app_common.handlers.schemas import RawMatch
from classes.enums import BinaryReferenceType
from crutches_on_wheels.errors.errors import Error
from crutches_on_wheels.errors.exception import VLException


[docs]class MatcherRawHandler(BaseMatcherHandler): """ Matcher raw handler allows to do similarity calculations for input descriptors . Resource: "/{api_version}/matcher/raw" """ descriptorTypeByVersion = {descriptorType.value.version: descriptorType.value for descriptorType in DescriptorsEnum}
[docs] async def getMatchPayload(self) -> Tuple[List[Tuple], List[Tuple]]: """ Get candidate and reference match structures Raises: VLException(Error.BadInputData) if msgpack unpack failed VLException(Error.BadSdkDescriptor) if provided sdk descriptor is corrupted VLException(Error.BadInputXpk) if provided xpk file is corrupted VLException(Error.UnknownDescriptorVersion) if provided descriptor has incorrect version VLException(Error.InvalidDescriptorLength) if descriptor length is incorrect VLException(Error.VersionNotMatchWithVersionForMatching) if descriptor version is'nt supported for matching Returns: candidate and reference match structures """ def getDescriptor(item: dict) -> Tuple[int, bytes]: """Selects appropriate decoding algorythm based on discriminator item["type"] Args: item: A dictionary with descriptor data Returns: Tuple of version and descriptor. """ if item["type"] == BinaryReferenceType.rawDescriptor.value: version, binaryDescriptor = item["data"]["version"], self.convertFromBase64ToBytesIfNeed( item["data"]["descriptor"]) elif item["type"] == BinaryReferenceType.sdkDescriptor.value: sdkDescriptor = self.convertFromBase64ToBytesIfNeed(item["data"]) try: version, binaryDescriptor = sdkDescriptorDecode(sdkDescriptor) except (ValueError, SyntaxError): raise VLException(Error.BadSdkDescriptor, 400, isCriticalError=False) elif item["type"] == BinaryReferenceType.xpkFile.value: try: xpk = readXPKFromBinary(self.convertFromBase64ToBytesIfNeed(item["data"])) except ValueError: raise VLException(Error.BadInputXpk, 400, isCriticalError=False) version, binaryDescriptor = xpk["Descriptor"]["version"], xpk["Descriptor"]["raw_descriptor"] else: raise RuntimeError(f"error binary reference type: {item['type']}") try: descriptorType = self.descriptorTypeByVersion[version] except KeyError: raise VLException(Error.UnknownDescriptorVersion.format(version), 400, isCriticalError=False) if descriptorType.length != len(binaryDescriptor): raise VLException( Error.InvalidDescriptorLength.format(len(binaryDescriptor)), 400, isCriticalError=False ) seenVersions.add(version) return version, binaryDescriptor inputJson: dict = self.request.json self.validateJson(inputJson, RawMatch.schema, useJsonSchema=False) seenVersions = set() candidates = [(item["id"], getDescriptor(item)) for item in inputJson["candidates"]] references = [(item["id"], getDescriptor(item)) for item in inputJson["references"]] if len(seenVersions) > 1: error = Error.DifferentVersionsNotAllowed.format(f"{*sorted(seenVersions),}") raise VLException(error, 400, isCriticalError=False) return candidates, references
[docs] async def post(self) -> HTTPResponse: """ Match each candidate descriptor with each reference descriptor. See `spec_matcher_raw`_. .. _spec_matcher_raw: _static/api.html#operation/matchingRaw Returns: response with matching results """ candidates, references = await self.getMatchPayload() results: List[dict] = [] for referenceId, (descriptorVersion, descriptor) in references: matches: List[Tuple[str, float]] = [ (candidateId, match(descriptor, candidateDescriptor, descriptorVersion)) for candidateId, (_, candidateDescriptor) in candidates ] matches.sort(key=lambda item: item[1], reverse=True) matchInfo = { "reference_id": referenceId, "matches": [ {"candidate_id": candidateId, "similarity": similarity} for candidateId, similarity in matches ], } results.append(matchInfo) return self.success(201, outputJson={"matches": results})