Source code for GenerIter.selector

"""
Class to catalogue and select source files.

Copyright 2020 Thomas Jackson Park & Jeremy Pavier

"""
import os
import string
import random
import json
from pathlib import Path
import GenerIter.excepts as robox
from GenerIter.source import WavSource
from GenerIter.config import Config
from GenerIter.util import jStr, debug, debug_except

[docs]class Selector(): """Represents the inventory of samples available for algorithmic processing. .. code-block:: python :caption: Example constructor usage from GenerIter.selector import Selector # Selector selector = Selector(searchpath=pathstring,loadpath=loadfile) """ def __init__(self, searchpath=None, loadpath=None): self._data = {} if searchpath is not None: self.search(spath=searchpath) if loadpath is not None: self.load(lpath=loadpath)
[docs] def subcats(self): """Get the list of top-level sub-categories in the Selector. Returns: [] (str) .. code-block:: python :caption: Example usage # Enumerate the sub-categories cats = selector.subcats() for cat in cats: # Get the sub-category category = selector[cat] """ retval = [] for key in self._data: retval.append(key) return retval
def __str__(self): return jStr(self._data) def __getitem__(self, key): return self._data[key]
[docs] def selectRandom(self, key): """Method for getting a random selection from within a sub-category of the Selector. This method will attempt to randomly choose an entry for the specified sub-category in the Selector. It will fail if there are no ``true`` enabled entries in the sub-category or if the randomised function repeatedly fails to find a ``true`` enabled entry because they are too sparse. The number of attempts is limited by the size of the sub-category array. Args: key (str) : the name of a sub-category within the Selector's structure. Raises: RDJParameterErr : if unable to select a return value. """ retval = None if key is not None: # How many entries are there in this category? try: sellen = len(self._data[key]) except KeyError: print("KeyError in Selector for key: {0}".format(key)) print(jStr(self._data)) raise KeyError # Let's not try selecting on an empty set if sellen > 0: # If I can't select after that number of tries, there's a probably an issue ctr = sellen # Set a limit while ctr > 0: src, incl = random.choice(list(self._data[key].items())) if incl is True: # We have a selection retval = src # Skip to the end. ctr = 0 else: # Marked as excluded retval = None # Need to avoid an infinite loop if ALL the entries got # accidentally excluded. ctr = ctr - 1 # Houston, we have a problem if retval is None: raise robox.RDJParameterErr("Unable to select a valid source for {0}".format(key)) # Here's a result. print("{0} : {1}".format(key, retval)) return retval
[docs] def search(self, spath=None): """Walk a directory tree to add to the Selector configuration. This method walks the specified directory tree and adds any discovered WAV files to its inventory. This method is uniquely additive in that it can be run repeatedly across the different or the same trees. Uniqueness is enforced during this process, so any repeats are silently overwritten. Any files encountered that do not match the criteria for a WAV file are ignored. Args: spath (str) : path to the root of the searchble directory tree. .. code-block:: python :caption: Example usage from GenerIter.selector import Selector # Empty Selector selector = Selector() # Search a directory tree selector.search(spath=pathstring) """ if spath is not None: # Let's use the object-oriented filesystem abstraction sdir = Path(spath) # Recursively walk the directory and subdirectories fyles = list(sdir.rglob('*.*')) for fyle in fyles: # Ensure the filepath is absolute filepath = str(fyle.resolve()) #debug(filepath) # Attempt to add this item into the current configuration try: # Is it a valid WavSource? src = WavSource(dpath=filepath, dexist=True) except Exception as inst: debug(filepath) #debug_except(inst) debug("skipping ...") src = None # The insert will skip this self.insert(source=src, include=True)
[docs] def load(self, lpath=None): """Load a previously-saved Selector configuration. This method is uniquely additive in that it can be run repeatedly and any repeats are silently overwritten. Args: lpath (str) : path to the loadable JSON file containing a saved Selector state. .. code-block:: python :caption: Example usage from GenerIter.selector import Selector # Empty Selector selector = Selector() # Load a previously-saved inventory file selector.load(lpath=pathstring) """ if lpath is not None: # Open the specified JSON file and load it with open(lpath) as fp: #debug("Open {0}".format(lpath)) tcand = json.load(fp) # Iterate down the categories for category in tcand: # Within each category, iterate down the items for item in tcand[category]: # Attempt to add this item into the current configuration try: # Is it a valid WavSource? src = WavSource(dpath=item, dexist=True) except robox.RDJValidationErr as err: debug(err) debug("WavSource exception for {0}".format(item)) src = None # The insert will skip this self.insert(source=src, include=tcand[category][item])
[docs] def insert(self, source, include=True): """Attempt to insert a source into the Selector configuration """ #debug(source) if source is not None: # Force case-insensitivity in the keys lower_key = source.dname.lower() # Capitalise the first letter key = string.capwords(lower_key) if key in self._data: # If this key already exists, that's fine pass else: # If the key doesn't exist, create the dictionary for its entries self._data[key] = {} # Insert the entry in the correct subcategory self._data[key][source.path] = include