Source code for turberfield.dialogue.matcher

#!/usr/bin/env python3
# encoding: UTF-8

# This file is part of turberfield.
#
# Turberfield is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Turberfield is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with turberfield.  If not, see <http://www.gnu.org/licenses/>.

import bisect
from collections.abc import Mapping
import numbers


[docs]class Matcher: @staticmethod def flatten_mapping(obj, path=[]): leaves = {k: v for k, v in obj.items() if not isinstance(v, Mapping)} children = {k: v for k, v in obj.items() if k not in leaves} for k, v in children.items(): yield from Matcher.flatten_mapping(v, path=path[:] + [k]) yield from ( (tuple(path + [k]), Matcher.simplify(getattr(v, "value", v))) for k, v in leaves.items() )
[docs] @staticmethod def mapping_key(obj): """ A keying function which allows nested objects to be sorted. """ obj = obj or {} return sorted(list(Matcher.flatten_mapping(obj)))
@staticmethod def simplify(val): if val is None: return "" elif isinstance(val, numbers.Complex): return str(abs(val)) elif isinstance(val, numbers.Number): return str(val) else: return val
[docs] def __init__(self, folders=None): """Match Turberfield Folders by their metadata. This class has methods to normalise arbitrary dictionaries. It provides a search API, so you can discover which folders are a metadata match. :param folders: A sequence of :py:class:`turberfield.dialogue.model.SceneScript.Folder` objects. """ self.folders = sorted(folders or [], key=lambda x: self.mapping_key(x.metadata)) self.keys = sorted([self.mapping_key(i.metadata) for i in self.folders])
[docs] def options(self, data): """Generate folders to best match metadata. The results will be a single, perfectly matched folder, or the two nearest neighbours of an imperfect match. :param dict data: metadata matching criteria. This method is a generator. It yields :py:class:`turberfield.dialogue.model.SceneScript.Folder` objects. """ if self.mapping_key(data) in self.keys: yield next(i for i in self.folders if i.metadata == data) else: index = bisect.bisect_left(self.keys, self.mapping_key(data)) posns = sorted(set([max(0, index - 1), index])) yield from (self.folders[i] for i in posns)