Esempio n. 1
0
def add_rule(name, pattern, anchor=Rule.ANCHOR_START):
    """Add rule to current naming session. If no active rule is found, it adds
    the created one as active by default.

    Args:
        ``name`` (str): Name that best describes the rule, this will be used as a way
        to invoke the Rule object.

        ``pattern`` (str): The template pattern to use, which uses existing Tokens.
        e.g.: '{side}_{region}_{side}_{region}.png'

        ``anchor``: ([Rule.ANCHOR_START, Rule.ANCHOR_END, Rule.ANCHOR_BOTH], optional):
        For parsing, regex matching will look for a match from this Anchor. If a
        pattern is anchored to the start, it requires the start of a passed path to
        match the pattern. Defaults to ANCHOR_START.

    Returns:
        Rule: The Rule object instance created for given name and fields.
    """
    rule = Rule(name, pattern, anchor)
    _rules[name] = rule
    if get_active_rule() is None:
        set_active_rule(name)
        logger.debug(
            "No active rule found, setting this one as active: {}".format(
                name))
    return rule
Esempio n. 2
0
    def parse(self, name):
        """Build and return dictionary with keys as tokens and values as given names.

        If your rule uses the same token more than once, the returned dictionary keys
        will have the token name and an incremental digit next to them so they can be
        differentiated.

        Args:
            name (str): Name string e.g.: C_helmet_001_MSH

        Returns:
            dict: A dictionary with keys as tokens and values as given name parts.
            e.g.: {'side':'C', 'part':'helmet', 'number': 1, 'type':'MSH'}
        """
        expected_separators = self.__PATTERN_SEPARATORS_REGEX.findall(
            self._pattern)
        if len(expected_separators) <= 0:
            logger.warning(
                "No separators used for rule '{}', parsing is not possible.".
                format(self.name))
            return None
        name_separators = self.__SEPARATORS_REGEX.findall(name)
        if len(expected_separators) <= len(name_separators):
            retval = dict()
            match = self._regex.search(name)
            if match:
                name_parts = sorted(match.groupdict().items())
                logger.debug("Name parts: {}".format(", ".join([
                    "('{}': '{}')".format(k[:-3], v) for k, v in name_parts
                ])))
                repeated_fields = dict()
                for each in self.fields:
                    if each not in repeated_fields.keys():
                        if self.fields.count(each) > 1:
                            repeated_fields[each] = 1
                if repeated_fields:
                    logger.debug("Repeated tokens: {}".format(", ".join(
                        repeated_fields.keys())))

                for key, value in name_parts:
                    # Strip number that was added to make group name unique
                    token_name = key[:-3]
                    token = get_token(token_name)
                    if not token:
                        continue
                    if token_name in repeated_fields.keys():
                        counter = repeated_fields.get(token_name)
                        repeated_fields[token_name] = counter + 1
                        token_name = "{}{}".format(token_name, counter)
                    retval[token_name] = token.parse(value)
            return retval
        else:
            raise ParsingError(
                "Separators count mismatch between given name '{}':'{}' and rule's pattern '{}':'{}'."
                .format(name, len(name_separators), self._pattern,
                        len(expected_separators)))
Esempio n. 3
0
    def remove_option(self, key):
        """Remove an option on this Token.

        Args:
            key (str): Full name of the option

        Returns:
            [bool]: True if successful. False otherwise.
        """
        if key in self._options.keys():
            del self._options[key]
            return True
        logger.debug("Option '{}':'{}' doesn't exist in Token '{}'. ".format(
            key, self._options.get(key), self.name))
        return False
Esempio n. 4
0
    def update_option(self, key, value):
        """Update an option pair on this Token.

        Args:
            key (str): Full name of the option
            value (str): Abbreviation to be used when building the name.

        Returns:
            [bool]: True if successful. False otherwise.
        """
        if key in self._options.keys():
            self._options[key] = value
            return True
        logger.debug("Option '{}':'{}' doesn't exist in Token '{}'. "
                     "Use add_option() instead.".format(
                         key, self._options.get(key), self.name))
        return False
Esempio n. 5
0
def get_repo():
    """Get repository location from either global environment variable or local user,
    giving priority to environment variable.

    Environment varialble name: NAMING_REPO

    Returns:
        str: Naming repository location
    """
    env_repo = os.environ.get(NAMING_REPO_ENV)
    userPath = os.path.expanduser("~")
    module_dir = os.path.split(__file__)[0]
    config_location = os.path.join(module_dir, "cfg", "config.json")
    config = dict()
    with open(config_location) as fp:
        config = json.load(fp)
    local_repo = os.path.join(userPath, "." + config["local_repo_name"], "naming_repo")
    result = env_repo or local_repo
    logger.debug("Repo found: {}".format(result))
    return result
Esempio n. 6
0
def load_token(filepath):
    """Load token from given location and create Token or TokenNumber object in
    memory to work with it.

    Args:
        filepath (str): Path to existing .token file location

    Returns:
        bool: True if successful, False if .token wasn't found.
    """
    if not os.path.isfile(filepath):
        return False
    try:
        with open(filepath) as fp:
            data = json.load(fp)
    except Exception:
        return False
    class_name = data.get("_Serializable_classname")
    logger.debug("Loading token type: {}".format(class_name))
    token = eval("{}.from_data(data)".format(class_name))
    _tokens[token.name] = token
    return True
Esempio n. 7
0
def load_session(repo=None):
    """Load rules, tokens and config from a repository, and create
    Python objects in memory to work with them.

    Args:
        repo (str, optional): Absolute path to a repository. Defaults to None.

    Returns:
        bool: True if loading session operation was successful.
    """
    repo = repo or get_repo()
    if not os.path.exists(repo):
        logger.warning("Given repo directory does not exist: {}".format(repo))
        return False
    namingconf = os.path.join(repo, "naming.conf")
    if not os.path.exists(namingconf):
        logger.warning("Repo is not valid. naming.conf not found {}".format(namingconf))
        return False
    rules.reset_rules()
    tokens.reset_tokens()
    # tokens and rules
    for dirpath, dirnames, filenames in os.walk(repo):
        for filename in filenames:
            filepath = os.path.join(dirpath, filename)
            if filename.endswith(".token"):
                logger.debug("Loading token: {}".format(filepath))
                tokens.load_token(filepath)
            elif filename.endswith(".rule"):
                logger.debug("Loading rule: {}".format(filepath))
                rules.load_rule(filepath)
    # extra configuration
    if os.path.exists(namingconf):
        logger.debug("Loading active rule: {}".format(namingconf))
        with open(namingconf) as fp:
            config = json.load(fp)
        rules.set_active_rule(config.get('set_active_rule'))
    return True
Esempio n. 8
0
def save_session(repo=None):
    """Save rules, tokens and config files to the repository.

    Raises:
        IOError, OSError: Repository directory could not be created.

    Args:
        repo (str, optional): Absolue path to a repository. Defaults to None.

    Returns:
        bool: True if saving session operation was successful.
    """
    repo = repo or get_repo()
    if not os.path.exists(repo):
        try:
            os.mkdir(repo)
        except (IOError, OSError) as why:
            raise why
    # save tokens
    for name, token in six.iteritems(tokens.get_tokens()):
        logger.debug("Saving token: '{}' in {}".format(name, repo))
        tokens.save_token(name, repo)
    # save rules
    for name, rule in six.iteritems(rules.get_rules()):
        if not isinstance(rule, rules.Rule):
            continue
        logger.debug("Saving rule: '{}' in {}".format(name, repo))
        rules.save_rule(name, repo)
    # extra configuration
    active = rules.get_active_rule()
    config = {"set_active_rule": active.name if active else None}
    filepath = os.path.join(repo, "naming.conf")
    logger.debug("Saving active rule: {} in {}".format(active.name, filepath))
    with open(filepath, "w") as fp:
        json.dump(config, fp, indent=4)
    return True
Esempio n. 9
0
def solve(*args, **kwargs):
    """Given arguments are used to build a name following currently active rule.

    -For rules with repeated tokens:

    If your rule uses the same token more than once, pass arguments with the token
    name and add an incremental digit

    i.e.: side1='C', side2='R'

    If your rule uses the same token more than once, you can also pass a single
    instance of the argument and it'll be applied to all repetitions.

    i.e.: side='C'

    If your rule uses the same token more than once, you can ignore one of the repetitions,
    and the solver will use the default value for that token.

    i.e.: side1='C', side4='L'

    Raises:
        SolvingError: A required token was passed as None to keyword arguments.
        SolvingError: Missing argument for one field in currently active rule.

    Returns:
        str: A string with the resulting name.
    """
    rule = rules.get_active_rule()
    # * This accounts for those cases where a token is used more than once in a rule
    repeated_fields = dict()
    for each in rule.fields:
        if each not in repeated_fields.keys():
            if rule.fields.count(each) > 1:
                repeated_fields[each] = 1
    fields_with_digits = list()
    for each in rule.fields:
        if each in repeated_fields.keys():
            counter = repeated_fields.get(each)
            repeated_fields[each] = counter + 1
            fields_with_digits.append("{}{}".format(each, counter))
        else:
            fields_with_digits.append(each)
    values = dict()
    i = 0
    fields_inc = 0
    for f in fields_with_digits:
        token = tokens.get_token(rule.fields[fields_inc])
        if token:
            # Explicitly passed as keyword argument
            if kwargs.get(f) is not None:
                values[f] = token.solve(kwargs.get(f))
                fields_inc += 1
                continue
            # Explicitly passed as keyword argument without repetitive digits
            # Use passed argument for all field repetitions
            elif kwargs.get(rule.fields[fields_inc]) is not None:
                values[f] = token.solve(kwargs.get(rule.fields[fields_inc]))
                fields_inc += 1
                continue
            elif token.required and kwargs.get(f) is None and len(args) == 0:
                raise SolvingError("Token {} is required.")
            # Not required and not passed as keyword argument
            elif not token.required and kwargs.get(f) is None:
                values[f] = token.solve()
                fields_inc += 1
                continue
            # Implicitly passed as positional argument
            try:
                values[f] = token.solve(args[i])
                i += 1
                fields_inc += 1
                continue
            except IndexError as why:
                raise SolvingError("Missing argument for field '{}'\n{}".format(f, why))
    logger.debug("Solving rule '{}' with values {}".format(rule.name, values))
    return rule.solve(**values)