<commit_before>"""
Metadata components (tags, categories) give texture to directories, allowing
for filtering and selection.
"""
import os
import itertools
import functools
from collections import defaultdict

from fuzzywuzzy import process

from .state import JSONFile
from .selectionparser import parse_selection


class Metadata(object):

    # TODO; raise meaningful exceptions when file can't be read, written, read
    # and nonexistent, missing, or treantdir missing

    def __init__(self, tree):
        self._tree = tree

    @property
    def _write(self):
        self._statefile = JSONFile(os.path.join(self._tree._treantdir,
                                                self._statefilename),
                                   init_state=self._init_state)

        return self._statefile.write()

    @property
    def _read(self):
        self._statefile = JSONFile(os.path.join(self._tree._treantdir,
                                                self._statefilename),
                                   init_state=self._init_state)

        return self._statefile.read()


@functools.total_ordering
class Tags(Metadata):
    """Interface to tags.

    """
    _statefilename = 'tags.json'

    @staticmethod
    def _init_state(jsonfile):
        """Used solely for initializing JSONFile state for storing tag
        information.

        """
        jsonfile._state = []

    def __repr__(self):
        return "<Tags({})>".format(self._list())

    def __str__(self):
        tags = self._list()
        agg = "Tags"
        majsep = "="
        seplength = len(agg)

        if not tags:
            out = "No Tags"
        else:
            out = agg + '\n'
            out = out + majsep * seplength + '\n'
            for i in range(len(tags)):
                out = out + "'{}'\n".format(tags[i])
        return out

    def __getitem__(self, value):
        # check if we might have a string to parse into a selection object
        if isinstance(value, str):
            value = parse_selection(value)
        return self._getselection(value)

    def _getselection(self, value):
        """get tags according to a selection
        """
        with self._read:
            if isinstance(value, list):
                # a list of tags gives only members with ALL the tags
                fits = all(self._getselection(item) for item in value)
            elif isinstance(value, tuple):
                # a tuple of tags gives members with ANY of the tags
                fits = any(self._getselection(item) for item in value)
            if isinstance(value, set):
                # a set of tags gives only members WITHOUT ALL the tags
                # can be used for `not`, basically
                fits = not all(self._getselection(item) for item in value)
            elif isinstance(value, str):
                fits = value in self

            return fits

    def __iter__(self):
        return self._list().__iter__()<commit_msg>Fix for #175<commit_after>"""
Metadata components (tags, categories) give texture to directories, allowing
for filtering and selection.
"""
import os
import itertools
import functools
from collections import defaultdict

from fuzzywuzzy import process

from .state import JSONFile
from .selectionparser import parse_selection


class Metadata(object):

    # TODO; raise meaningful exceptions when file can't be read, written, read
    # and nonexistent, missing, or treantdir missing

    def __init__(self, tree):
        self._tree = tree

    @property
    def _write(self):
        self._statefile = JSONFile(os.path.join(self._tree._treantdir,
                                                self._statefilename),
                                   init_state=self._init_state)

        return self._statefile.write()

    @property
    def _read(self):
        self._statefile = JSONFile(os.path.join(self._tree._treantdir,
                                                self._statefilename),
                                   init_state=self._init_state)

        return self._statefile.read()


@functools.total_ordering
class Tags(Metadata):
    """Interface to tags.

    """
    _statefilename = 'tags.json'

    @staticmethod
    def _init_state(jsonfile):
        """Used solely for initializing JSONFile state for storing tag
        information.

        """
        jsonfile._state = []

    def __repr__(self):
        return "<Tags({})>".format(self._list())

    def __str__(self):
        tags = self._list()
        agg = "Tags"
        majsep = "="
        seplength = len(agg)

        if not tags:
            out = "No Tags"
        else:
            out = agg + '\n'
            out = out + majsep * seplength + '\n'
            for i in range(len(tags)):
                out = out + "'{}'\n".format(tags[i])
        return out

    def __getitem__(self, value):
        # check if we might have a string to parse into a selection object
        if isinstance(value, str):
            value = parse_selection(value)
        return self._getselection(value)

    def _getselection(self, value):
        """get tags according to a selection
        """
        with self._read:
            if isinstance(value, list):
                # a list of tags gives only members with ALL the tags
                fits = all(self._getselection(item) for item in value)
            elif isinstance(value, tuple):
                # a tuple of tags gives members with ANY of the tags
                fits = any(self._getselection(item) for item in value)
            elif isinstance(value, set):
                # a set of tags gives only members WITHOUT ALL the tags
                # can be used for `not`, basically
                fits = not all(self._getselection(item) for item in value)
            elif isinstance(value, str):
                fits = value in self
            else:
                raise ValueError(
                        "Given value is not a valid selection. "
                        "See documentation for tags selection options")

            return fits

    def __iter__(self):
        return self._list().__iter__()
{'fraction_non_alphanumeric': 0.08556062581486311, 'fraction_numerical': 0.0004889178617992178, 'mean_word_length': 1.8216091954022988, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7024931', 'n_tokens_mistral': 1607, 'n_tokens_neox': 1527, 'n_words': 519}