コード例 #1
0
def test_add():
    os = OrderedSet()
    for value in range(100):
        os.add(value)
    assert len(os) == 100
    for value in range(100):
        assert value in os
コード例 #2
0
def test_index():
    values = list(range(100))
    random.shuffle(values)
    os = OrderedSet(values)
    assert len(os) == len(values)
    for value in values:
        assert values.index(value) == os.index(value)
コード例 #3
0
def test_add():
    os = OrderedSet()
    for value in range(100):
        os.add(value)
    assert len(os) == 100
    for value in range(100):
        assert value in os
コード例 #4
0
def test_index():
    values = list(range(100))
    random.shuffle(values)
    os = OrderedSet(values)
    assert len(os) == len(values)
    for value in values:
        assert values.index(value) == os.index(value)
コード例 #5
0
def test_contains():
    os = OrderedSet(range(100))
    assert len(os) == 100
    for value in range(100):
        assert value in os
        assert os.count(value) == 1
    assert -1 not in os
    assert 100 not in os
コード例 #6
0
def test_contains():
    os = OrderedSet(range(100))
    assert len(os) == 100
    for value in range(100):
        assert value in os
        assert os.count(value) == 1
    assert -1 not in os
    assert 100 not in os
コード例 #7
0
ファイル: nameop.py プロジェクト: mvolkert/EXIFnaming
def create_tags_csv(location: str = ""):
    """
    extract tags from the file name
    write a csv file with those tags
    :param location: optional content of directory column

    This csv can be modified to be used with :func:`write_exif_using_csv` or :func:`placeinfo.write_infos`
    If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text.
    """
    inpath = os.getcwd()
    tag_set = OrderedSet()
    tag_set_names = OrderedSet()
    out_filename = get_info_dir("tags_places.csv")
    tags_places_file, writer = fileop.create_csv_writer(
        out_filename, ["directory", "name_part"])
    filenameAccessors = [
        FilenameAccessor(filename)
        for filename in get_plain_filenames_of_type(image_types, inpath)
    ]
    for fileNameAccessor in filenameAccessors:
        for tag in fileNameAccessor.tags():
            tag_set.add(tag)
    writeToFile(get_info_dir("tags.txt"),
                location + "\n\t" + "\n\t".join(tag_set) + "\n")
    for tag in tag_set:
        tag_set_names.add((location, tag))
    writer.writerows(tag_set_names)
    tags_places_file.close()
コード例 #8
0
ファイル: nameop.py プロジェクト: mvolkert/EXIFnaming
def create_counters_csv_per_dir():
    """
    extract counter from the file name
    write a csv file with those counters for each directory

    This csv can be modified to be used with :func:`write_exif_using_csv`
    If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text.
    """
    log_function_call(create_tags_csv_per_dir.__name__)
    inpath = os.getcwd()
    tag_set_names = OrderedSet()
    out_filename = get_info_dir("tags_counters.csv")
    csvfile, writer = fileop.create_csv_writer(out_filename, [
        "directory", "name_main", "name_part", "first", "last", "tags3",
        "description"
    ])
    for (dirpath, dirnames, filenames) in os.walk(inpath):
        if not inpath == dirpath: continue
        for dirname in dirnames:
            filenameAccessors = [
                FilenameAccessor(filename)
                for filename in get_plain_filenames_of_type(
                    image_types, dirpath, dirname)
            ]
            if len(filenameAccessors) == 0: continue
            _add_counter_csv_entries(dirname, filenameAccessors, tag_set_names)
    writer.writerows(tag_set_names)
    csvfile.close()
コード例 #9
0
def test_getitem():
    values = list(range(100))
    random.shuffle(values)
    os = OrderedSet(values)
    assert len(os) == len(values)
    for index in range(len(os)):
        assert os[index] == values[index]
コード例 #10
0
ファイル: nameop.py プロジェクト: mvolkert/EXIFnaming
def _add_counter_csv_entries(dirname: str,
                             filenameAccessors: List[FilenameAccessor],
                             tag_set_names: OrderedSet):
    fileNameAccessorFirst = filenameAccessors[0]
    fileNameAccessorLast = filenameAccessors[0]
    for filenameAccessor in filenameAccessors[1:-1]:
        if not filenameAccessor.is_direct_successor_of(fileNameAccessorLast):
            tag_set_names.add((dirname, fileNameAccessorFirst.pre,
                               fileNameAccessorFirst.first_posttag(),
                               fileNameAccessorFirst.counter_main(),
                               fileNameAccessorLast.counter_main()))
            fileNameAccessorFirst = filenameAccessor
        fileNameAccessorLast = filenameAccessor
    tag_set_names.add((dirname, fileNameAccessorFirst.pre,
                       fileNameAccessorFirst.first_posttag(),
                       fileNameAccessorFirst.counter_main(),
                       fileNameAccessorLast.counter_main()))
コード例 #11
0
 def __init__(self,
              distance_measure:
              MonophonicRhythmDistanceMeasure = HammingDistanceMeasure,
              rhythm_player: tp.Union[BSRhythmPlayer,
                                      tp.Type[BSRhythmPlayer],
                                      None] = None):
     self._config = BSConfig()
     self._corpus = None  # type: MidiRhythmCorpus
     self._corpus_resolution = -1
     self._distances_to_target = np.empty(0)
     self._distances_to_target_rhythm_are_stale = False
     self._rhythm_measure = SummedMonophonicRhythmDistance(
     )  # type: SummedMonophonicRhythmDistance
     self._rhythm_selection = OrderedSet()
     self._target_rhythm = None  # type: tp.Union[MidiRhythm, None]
     self._target_rhythm_prev_update = None
     self._lock = threading.Lock()
     self._rhythm_player = None
     self._callbacks = dict((action, OrderedSet()) for action in [
         BSController.RHYTHM_SELECTION, BSController.CORPUS_LOADED,
         BSController.DISTANCES_TO_TARGET_UPDATED, BSController.
         RHYTHM_PLAYBACK_START, BSController.RHYTHM_PLAYBACK_STOP,
         BSController.TARGET_RHYTHM_SET, BSController.DISTANCE_MEASURE_SET,
         BSController.RHYTHM_LOADER_REGISTERED
     ])
     self._rhythm_loaders = OrderedDict(
     )  # rhythm loaders by loader source name
     self.set_rhythm_player(rhythm_player)
     self.set_distance_measure(distance_measure)
     # automatically register a loader for the currently selected rhythm
     self.register_rhythm_loader(BSSelectedMidiRhythmLoader(self))
     # setup config change handlers
     self._setup_config()
     # if a midi root directory is set, load the corpus
     if self.get_config().midi_root_directory.get():
         self.load_corpus()
コード例 #12
0
ファイル: nameop.py プロジェクト: mvolkert/EXIFnaming
def create_names_csv_per_dir(start_after_dir=''):
    """
    extract names from the file path
    write a csv file with those names for each directory

    This csv can be modified to be used with :func:`write_exif_using_csv`
    If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text.
    """
    log_function_call(create_names_csv_per_dir.__name__)
    inpath = os.getcwd()
    tag_set_names = OrderedSet()
    out_filename = get_info_dir("tags_names.csv")
    csvfile, writer = fileop.create_csv_writer(
        out_filename, ["directory", "name_main", "tags"])
    for (dirpath, dirnames, filenames) in os.walk(inpath):
        if is_invalid_path(dirpath): continue
        filenameAccessors = [
            FilenameAccessor(filename)
            for filename in filterFiles(filenames, image_types)
        ]
        if len(filenameAccessors) == 0: continue
        tags = []
        found = False
        for part in dirpath.split(os.sep):
            if found:
                tags += part.split(', ')
            else:
                found = part == start_after_dir
        filenameAccessorLast = filenameAccessors[0]
        tag_set_names.add(
            (", ".join(tags), filenameAccessorLast.pre,
             ', '.join(OrderedSet(tags + [filenameAccessorLast.pre]))))
        for filenameAccessor in filenameAccessors[1:]:
            if not filenameAccessor.pre == filenameAccessorLast.pre:
                tag_set_names.add(
                    (", ".join(tags), filenameAccessor.pre,
                     ', '.join(OrderedSet(tags + [filenameAccessor.pre]))))
            filenameAccessorLast = filenameAccessor
    writer.writerows(tag_set_names)
    csvfile.close()
コード例 #13
0
ファイル: nameop.py プロジェクト: mvolkert/EXIFnaming
def create_tags_csv_per_dir():
    """
    extract tags from the file name
    write a csv file with those tags and group them by toplevel directory

    This csv can be modified to be used with :func:`write_exif_using_csv` or :func:`placeinfo.write_infos`
    If you want to modify it with EXCEL or Calc take care to import all columns of the csv as text.
    """
    log_function_call(create_tags_csv_per_dir.__name__)
    inpath = os.getcwd()
    tag_set_names = OrderedSet()
    out_filename = get_info_dir("tags_per_dir.csv")
    tags_places_file, writer = fileop.create_csv_writer(
        out_filename, ["directory", "name_part"])
    for (dirpath, dirnames, filenames) in os.walk(inpath):
        if not inpath == dirpath: continue
        for dirname in dirnames:
            tag_set = OrderedSet()
            filenameAccessors = [
                FilenameAccessor(filename)
                for filename in get_plain_filenames_of_type(
                    image_types, dirpath, dirname)
            ]
            if len(filenameAccessors) == 0: continue
            for fileNameAccessor in filenameAccessors:
                for tag in fileNameAccessor.tags():
                    tag_set.add(tag)
            writeToFile(get_info_dir("tags.txt"),
                        dirname + "\n\t" + "\n\t".join(tag_set) + "\n")

            dirname_split = dirname.split("_")
            subnames = [
                subname for subname in dirname_split
                if not subname.isnumeric()
            ]
            dirname = "_".join(subnames)
            for tag in tag_set:
                tag_set_names.add((dirname, tag))
    writer.writerows(tag_set_names)
    tags_places_file.close()
コード例 #14
0
 def set_description_from_relevant(self):
     claims = self._db.get_claims()
     topic_judgments = self._read_judgments(self._judgment_path)
     posts = self._db.get_posts()
     post_dict = {p.post_id: p for p in posts}
     for claim in claims:
         topic_id = int(claim.claim_id)
         # tweets = self._twitter_api.get_tweets_by_ids(topic_judgments[topic_id][:self._num_of_relevant_tweets])
         # posts, authors = self._db.convert_tweets_to_posts_and_authors(tweets, self._domain)
         posts = list(
             map(post_dict.get,
                 topic_judgments[topic_id][:self._num_of_relevant_tweets]))
         claim_content = OrderedSet(claim.keywords.lower().split())
         for post in posts:
             list(map(claim_content.add, clean_tweet(post.content).split()))
             # if len(claim_content) > 25:
             #     break
         claim.description = clean_claim_description(
             ' '.join(claim_content), True)
     self._db.addPosts(claims)
コード例 #15
0
def test_reversed():
    os = OrderedSet(range(100))
    assert list(reversed(os)) == list(reversed(range(100)))
    names = ['eve', 'carol', 'alice', 'dave', 'bob']
    os = OrderedSet(names)
    assert list(reversed(os)) == list(reversed(names))
コード例 #16
0
def test_iter():
    os = OrderedSet(range(100))
    assert list(os) == list(range(100))
    names = ['eve', 'carol', 'alice', 'dave', 'bob']
    os = OrderedSet(names)
    assert list(os) == names
コード例 #17
0
def test_index_error():
    os = OrderedSet(range(10))
    os.index(10)
コード例 #18
0
    def _greedy_search(self, generate_next_keywords, search_type):
        claims = self._db.get_claims()
        min_tweets = self._min_tweet_count
        # word_tf_idf_dict = self._keywords_generator.get_word_tf_idf_of_claims()
        for i, claim in enumerate(claims):
            keywords_tweet_num = defaultdict(int)
            if i < self._start_from_claim:
                continue
            walked_keywords = Counter()
            print('{} Claim {}/{}'.format(search_type, i, len(claims)))
            start = timeit.default_timer()
            claim_description_words = self.get_claim_words_from_description(
                claim)
            ordered_words = OrderedSet(claim_description_words)
            base_keywords = ordered_words if search_type == 'top_down' else set(
            )
            # num_of_potential_words = len(ordered_words)
            num_of_potential_words = self._max_keywords_size
            keywords_list = []
            for size in range(1, num_of_potential_words + 1):
                same_keywords_size = Counter()
                best_word_rank_tuple = ['', 1000]
                for iter, word in enumerate(ordered_words):
                    keywords_str = ' '.join(
                        generate_next_keywords(base_keywords, word))
                    keywords_size = len(keywords_str.split(' '))
                    type_name = '{}_iter_{}_keywords_size_{}'.format(
                        search_type, iter, keywords_size)
                    evaluation = self.eval_keywords_for_claim(
                        claim, keywords_str, type_name)
                    if best_word_rank_tuple[1] > evaluation[
                            'distance'] and evaluation[
                                'tweet_num'] > min_tweets:
                        best_word_rank_tuple = [word, evaluation['distance']]
                    print('\r{} Distance: {}'.format(type_name,
                                                     evaluation['distance']),
                          end='')
                    keywords_tweet_num[keywords_str] = evaluation['tweet_num']
                    if evaluation['tweet_num'] > min_tweets:
                        walked_keywords[
                            keywords_str] = -1.0 * evaluation['distance']
                        same_keywords_size[
                            keywords_str] = -1.0 * evaluation['distance']
                    keywords_list.append(
                        [keywords_str, evaluation['distance'], type_name])
                if ordered_words:
                    if best_word_rank_tuple[0] == '':
                        best_word_rank_tuple[0] = ordered_words.pop()
                    else:
                        ordered_words.discard(best_word_rank_tuple[0])
                base_keywords = generate_next_keywords(base_keywords,
                                                       best_word_rank_tuple[0])

                curr_distance = best_word_rank_tuple[1]
                if len(same_keywords_size) > 0:
                    keywords, best_distances = same_keywords_size.most_common(
                        1)[0]
                    self._add_new_keywords(
                        claim, keywords,
                        '{}_keywords_size_{}'.format(search_type, size),
                        -1.0 * best_distances, keywords_tweet_num[keywords])
                else:
                    self._add_new_keywords(
                        claim, ' '.join(base_keywords),
                        '{}_keywords_size_{}'.format(search_type,
                                                     size), curr_distance,
                        keywords_tweet_num[' '.join(base_keywords)])

            for keywords, keywords_distance, type_name in keywords_list:
                self._add_new_keywords(claim, keywords, type_name,
                                       keywords_distance,
                                       keywords_tweet_num[keywords])

            if len(walked_keywords) > 0:
                keywords, best_distances = list(
                    zip(*(walked_keywords.most_common(
                        self._output_keywords_count))))
            else:
                sorted_by_second = sorted(keywords_list,
                                          key=lambda tup: tup[1],
                                          reverse=True)
                keywords, best_distances, type_name = list(
                    zip(*sorted_by_second[:self._output_keywords_count]))

            self._add_new_keywords(
                claim, '||'.join(keywords), '{}_final'.format(search_type),
                -1.0 * np.mean(best_distances),
                sum(list(keywords_tweet_num[k] for k in keywords)))
            with self._db.session.no_autoflush:
                self._db.addPosts(self._keywords_connections)
            self._keywords_connections = []
            end = timeit.default_timer()
            print('run time: {}'.format((end - start)))
コード例 #19
0
def test_discard():
    os = OrderedSet(range(100))
    for value in range(200):
        os.discard(value)
    assert len(os) == 0
コード例 #20
0
def find_bad_exif(do_move=True,
                  check_date_additional=False,
                  folder: str = r""):
    """
    find files with missing exif data
    """
    log_function_call(find_bad_exif.__name__, do_move)

    clock = Clock()
    inpath = os.getcwd()
    lines_no_tags = OrderedSet()
    lines_bad_date_additional = OrderedSet()
    lines_date_missing = OrderedSet()
    out_filename_no_tags = get_info_dir("no_tags.csv")
    file_no_tags, writer_no_tags = fileop.create_csv_writer(
        out_filename_no_tags, ["directory", "name_part"])
    out_filename_bad_date_additional = get_info_dir("bad_date_additional.csv")
    file_bad_date_additional, writer_bad_date_additional = fileop.create_csv_writer(
        out_filename_bad_date_additional, ["directory", "name_part"])
    out_filename_date_missing = get_info_dir("date_missing.csv")
    file_date_missing, writer_date_missing = fileop.create_csv_writer(
        out_filename_date_missing, ["directory", "name_part"])
    for (dirpath, dirnames, filenames) in os.walk(inpath):
        if is_invalid_path(dirpath, regex=folder): continue
        if fileop.count_files(filenames, settings.image_types) == 0: continue
        Tagdict = read_exiftags(dirpath, settings.image_types, ask=False)
        if len(list(Tagdict.values())) == 0: continue
        leng = len(list(Tagdict.values())[0])
        for i in range(leng):
            if (not "Keywords" in Tagdict or not Tagdict["Keywords"][i]) or \
                    (not "Subject" in Tagdict or not Tagdict["Subject"][i]) or \
                    (not "Description" in Tagdict or not Tagdict["Description"][i]) or \
                    (not "User Comment" in Tagdict or not Tagdict["User Comment"][i]):
                lines_no_tags.add((os.path.basename(dirpath),
                                   _remove_counter(Tagdict["File Name"][i])))
                if do_move and not "bad_exif" in dirpath:
                    move(
                        Tagdict["File Name"][i], dirpath,
                        dirpath.replace(
                            inpath, os.path.join(inpath, "bad_exif_keywords")))
            if not "Date/Time Original" in Tagdict or not Tagdict[
                    "Date/Time Original"][i]:
                lines_date_missing.add(
                    (os.path.basename(dirpath),
                     _remove_counter(Tagdict["File Name"][i])))
                if do_move and not "bad_exif" in dirpath:
                    move(
                        Tagdict["File Name"][i], dirpath,
                        dirpath.replace(
                            inpath,
                            os.path.join(inpath, "bad_exif_date_missing")))
            if check_date_additional and \
                    (("Date Created" in Tagdict and Tagdict["Date Created"][i]) or
                     ("Time Created" in Tagdict and Tagdict["Time Created"][i]) or
                     ("Create Date" in Tagdict and Tagdict["Create Date"][i]) or
                     ("Modify Date" in Tagdict and Tagdict["Modify Date"][i]) or
                     ("Digital Creation Date" in Tagdict and Tagdict["Digital Creation Date"][i])):
                lines_bad_date_additional.add(
                    (os.path.basename(dirpath),
                     _remove_counter(Tagdict["File Name"][i])))
                if do_move and not "bad_exif" in dirpath:
                    move(
                        Tagdict["File Name"][i], dirpath,
                        dirpath.replace(
                            inpath,
                            os.path.join(inpath, "bad_exif_date_additional")))
    writer_no_tags.writerows(lines_no_tags)
    writer_bad_date_additional.writerows(lines_bad_date_additional)
    writer_date_missing.writerows(lines_date_missing)
    file_no_tags.close()
    file_bad_date_additional.close()
    file_date_missing.close()
    clock.finish()
コード例 #21
0
def test_index_error():
    os = OrderedSet(range(10))
    with pytest.raises(ValueError):
        os.index(10)
コード例 #22
0
class BSController(object):

    # actions
    RHYTHM_SELECTION = "<Rhythm-Selection>"
    RHYTHM_PLAYBACK_START = "<RhythmPlayback-Start>"
    RHYTHM_PLAYBACK_STOP = "<RhythmPlayback-Stop>"
    CORPUS_LOADED = "<Corpus-Loaded>"
    DISTANCES_TO_TARGET_UPDATED = "<TargetDistances-Updated>"
    TARGET_RHYTHM_SET = "<TargetRhythm-Set>"
    DISTANCE_MEASURE_SET = "<DistanceMeasure-Set>"
    RHYTHM_LOADER_REGISTERED = "<RhythmLoader-Registered>"

    # description of data returned by get_rhythm_data
    RHYTHM_DATA_TYPES = OrderedDict(
        (("I", int), ("Distance to target", float), ("Name", str),
         ("BPM", float), ("Timesignature",
                          str), ("Track count", int), ("Measure count", int)))

    @staticmethod
    def get_rhythm_data_attr_names():
        structure = BSController.RHYTHM_DATA_TYPES
        return tuple(structure.keys())  # TODO Avoid re-allocation

    @staticmethod
    def get_rhythm_data_attr_types():
        structure = BSController.RHYTHM_DATA_TYPES
        return tuple(structure.values())  # TODO Avoid re-allocation

    def __init__(self,
                 distance_measure:
                 MonophonicRhythmDistanceMeasure = HammingDistanceMeasure,
                 rhythm_player: tp.Union[BSRhythmPlayer,
                                         tp.Type[BSRhythmPlayer],
                                         None] = None):
        self._config = BSConfig()
        self._corpus = None  # type: MidiRhythmCorpus
        self._corpus_resolution = -1
        self._distances_to_target = np.empty(0)
        self._distances_to_target_rhythm_are_stale = False
        self._rhythm_measure = SummedMonophonicRhythmDistance(
        )  # type: SummedMonophonicRhythmDistance
        self._rhythm_selection = OrderedSet()
        self._target_rhythm = None  # type: tp.Union[MidiRhythm, None]
        self._target_rhythm_prev_update = None
        self._lock = threading.Lock()
        self._rhythm_player = None
        self._callbacks = dict((action, OrderedSet()) for action in [
            BSController.RHYTHM_SELECTION, BSController.CORPUS_LOADED,
            BSController.DISTANCES_TO_TARGET_UPDATED, BSController.
            RHYTHM_PLAYBACK_START, BSController.RHYTHM_PLAYBACK_STOP,
            BSController.TARGET_RHYTHM_SET, BSController.DISTANCE_MEASURE_SET,
            BSController.RHYTHM_LOADER_REGISTERED
        ])
        self._rhythm_loaders = OrderedDict(
        )  # rhythm loaders by loader source name
        self.set_rhythm_player(rhythm_player)
        self.set_distance_measure(distance_measure)
        # automatically register a loader for the currently selected rhythm
        self.register_rhythm_loader(BSSelectedMidiRhythmLoader(self))
        # setup config change handlers
        self._setup_config()
        # if a midi root directory is set, load the corpus
        if self.get_config().midi_root_directory.get():
            self.load_corpus()

    def load_corpus(self):
        """Loads the rhythm corpus based on the settings in the current configuration

        Loads the rhythm corpus based on the current state of the BSConfig object. This method may be called multiple
        times throughout the lifespan of the app, e.g. after changing the rhythm resolution or the midi root directory
        in the BSConfig object.

        :return: True if load was successful; otherwise False
        """

        prev_corpus = self._corpus
        prev_corpus_id = prev_corpus.id if prev_corpus else None
        corpus = get_rhythm_corpus(self._config)

        if not corpus:
            return False

        with self._lock:
            self._corpus = corpus
            if corpus.id != prev_corpus_id:
                self._reset_distances_to_target_rhythm()

        if corpus.id != prev_corpus_id:
            self.clear_rhythm_selection()

        self._dispatch(self.CORPUS_LOADED)
        return True

    def is_corpus_loaded(self):
        """Returns whether the rhythm corpus has loaded

        :return: True if the rhythm corpus has loaded; False otherwise
        """

        return self._corpus is not None

    def get_corpus_id(self):
        """Returns the id of the current rhythm corpus or None if no corpus

        :return: id of current rhythm corpus
        """

        return self._corpus.id if self.is_corpus_loaded() else None

    def get_corpus_rootdir(self):
        """Returns the root directory of the rhythm corpus

        Returns the path to the root directory of the current rhythm corpus. Returns an empty string if no corpus has
        been set.

        :return: root directory of current rhythm corpus or empty string
        """

        return self._config.midi_root_directory.get()

    def get_corpus_rootdir_name(self):
        """Returns the name of the current rhythm corpus' root directory

        Returns the directory name of the current rhythm corpus' root directory. Returns an empty string if no corpus
        has been set.

        :return: root directory name of current rhythm corpus or empty string
        """

        rootdir = self.get_corpus_rootdir()

        try:
            return os.path.split(rootdir)[-1]
        except IndexError:
            return ""

    def get_rhythm_count(self):
        """
        Returns the number of rhythms in the current rhythm corpus.

        :return: number of rhythms in current corpus or 0 if no corpus set
        """

        try:
            return len(self._corpus)
        except TypeError:
            return 0

    def get_rhythm_by_index(self, rhythm_ix):
        """
        Returns a rhythm by its index in the current corpus.

        :param rhythm_ix: rhythm index in current corpus
        :return: the Rhythm object on the given index in the corpus
        """

        self._precondition_check_corpus_set()
        self._precondition_check_rhythm_index(rhythm_ix)
        return self._corpus[rhythm_ix]

    def set_target_rhythm(
            self, target_rhythm):  # type: (tp.Union[RhythmLoop, None]) -> None
        """
        Sets the target rhythm.

        :param target_rhythm: target rhythm or None
        """

        if target_rhythm == self._target_rhythm or target_rhythm is None:
            self._target_rhythm = target_rhythm
            return

        if not isinstance(target_rhythm, RhythmLoop):
            raise TypeError("Expected a RhythmLoop but got \"%s\"" %
                            str(target_rhythm))

        self._target_rhythm = target_rhythm
        self._distances_to_target_rhythm_are_stale = True
        self._dispatch(self.TARGET_RHYTHM_SET)

    def get_target_rhythm(self):
        """
        Returns the current target rhythm or None if there is no target rhythm set.

        :return: target rhythm
        """

        return self._target_rhythm

    def is_target_rhythm_set(self):
        """
        Returns whether or not a target rhythm has been set.

        :return: True if target rhythm has been set; False otherwise
        """

        return self._target_rhythm is not None

    def get_rhythm_data(self):
        """
        Returns an iterator yielding rhythm information on each iteration, such as the distance to the target rhythm,
        the name of the rhythm or the tempo. This data is yielded as a tuple. The type and names of the attributes in
        these tuples can be found in the ordered dictionary BSController.RHYTHM_DATA_TYPES, which gives name and type
        information for the elements in the tuple.

        Warning: This method locks this class. The lock will be released only when iteration is done or when the
        generator some time when the generator is destroyed.

        :return: iteration yielding rhythm info
        """

        if not self._corpus:
            return

        with self._lock:
            for i, rhythm in enumerate(self._corpus):
                d_to_target = self._distances_to_target[i]
                yield (i,
                       "NaN" if d_to_target == float("inf") else d_to_target,
                       rhythm.name, rhythm.bpm, str(rhythm.time_signature),
                       rhythm.get_track_count(),
                       int(rhythm.get_duration_in_measures()))

    def set_rhythm_player(
            self,
            rhythm_player):  # type: (tp.Union[BSRhythmPlayer, None]) -> None
        """
        Sets the rhythm player.

        :param rhythm_player: rhythm player or None
        :return:
        """

        if self.is_rhythm_player_set():
            self.stop_rhythm_playback()
            self._rhythm_player.on_playback_ended = no_callback

        if rhythm_player is None:
            self._rhythm_player = None
            return

        rhythm_player = type_check_and_instantiate_if_necessary(
            rhythm_player, BSRhythmPlayer, allow_none=True)
        rhythm_player.on_playback_ended = lambda *args: self._dispatch(
            self.RHYTHM_PLAYBACK_STOP)
        self._rhythm_player = rhythm_player

    def is_rhythm_player_set(self):  # type: () -> bool
        """
        Returns whether or not a rhythm player has been set.

        :return: True if a rhythm player has been set; False otherwise.
        """

        return self._rhythm_player is not None

    def is_rhythm_player_playing(self):
        """
        Returns whether or not the rhythm player is currently playing. Returns False if no rhythm player has been set.

        :return: True if rhythm player is playing; False otherwise
        """

        try:
            return self._rhythm_player.is_playing()
        except AttributeError:
            return False

    def playback_selected_rhythms(self):
        """
        Starts playing the currently selected rhythms. The rhythm player will be used for rhythm playback.

        :return: None
        """

        self._precondition_check_corpus_set()
        self._precondition_check_rhythm_player_set()
        selected_rhythm_indices = self.get_rhythm_selection()
        rhythms = [
            self.get_rhythm_by_index(i) for i in selected_rhythm_indices
        ]

        if rhythms:
            self._rhythm_player.playback_rhythms(rhythms)
            self._dispatch(BSController.RHYTHM_PLAYBACK_START)

    def stop_rhythm_playback(self):
        """
        Stops rhythm playback.

        :return: None
        """

        self._precondition_check_rhythm_player_set()
        self._rhythm_player.stop_playback()
        self._dispatch(BSController.RHYTHM_PLAYBACK_STOP)

    def set_distance_measure(self, track_distance_measure):
        """
        Sets the measure use to get the distance between individual rhythm tracks.

        :param track_distance_measure: the distance measure, one of:
            MonophonicRhythmDistanceMeasure - the track distance measure itself
            Type[MonophonicRhythmDistanceMeasure] - the track distance class, must be a subclass of
                                                    MonophonicRhythmDistanceMeasure
            str - the name of the MonophonicRhythmDistanceMeasure class (cls.__friendly_name__)
        """
        # type: (tp.Union[str, MonophonicRhythmDistanceMeasure, tp.Type[MonophonicRhythmDistanceMeasure]]) -> None

        if isinstance(track_distance_measure, str):
            try:
                track_distance_measure = MonophonicRhythmDistanceMeasure.get_measure_by_name(
                    track_distance_measure)
            except KeyError:
                raise ValueError("Unknown distance measure: \"%s\"" %
                                 str(track_distance_measure))

        self._rhythm_measure.monophonic_measure = track_distance_measure
        self._distances_to_target_rhythm_are_stale = True
        self._dispatch(self.DISTANCE_MEASURE_SET)

    def get_config(self) -> BSConfig:
        return self._config

    def is_current_distance_measure_quantizable(self):
        return isinstance(self._rhythm_measure.monophonic_measure,
                          QuantizableMixin)

    def set_measure_quantization_unit(self, unit):
        """
        Sets the quantization for the distance measures.

        :param unit: quantization unit
        :return: None
        """

        if not self.is_current_distance_measure_quantizable():
            return False

        track_distance_measure = self._rhythm_measure.monophonic_measure
        track_distance_measure.unit = unit
        track_distance_measure.quantize_enabled = True
        self._distances_to_target_rhythm_are_stale = True
        return True

    def set_tracks_to_compare(self, tracks):
        """
        Sets the tracks to compare.

        :param tracks: tracks to compare as a list or one of the wildcards ['*', 'a*', 'b*']. See
                       rhythm_pair_track_iterator for further info.
        """

        self._rhythm_measure.tracks = tracks
        self._distances_to_target_rhythm_are_stale = True

    def calculate_distances_to_target_rhythm(self):
        """
        Calculates and updates the distances between the rhythms in the corpus and the current target rhythm. This
        function call will be ignored if the distances are already up to date. The next call to get_rhythm_data will
        yield the updated distances.
        """

        self._precondition_check_corpus_set()
        self._precondition_check_target_rhythm_set()

        measure = self._rhythm_measure
        target_rhythm = self._target_rhythm

        if target_rhythm == self._target_rhythm_prev_update:
            self._distances_to_target_rhythm_are_stale = True

        # nothing to update
        if not self._distances_to_target_rhythm_are_stale:
            return

        if target_rhythm is None:
            self._reset_distances_to_target_rhythm()  # set distance to NaN
            self._target_rhythm_prev_update = None
            return

        with self._lock:
            for i, scanned_rhythm in enumerate(self._corpus):
                distance = measure.get_distance(target_rhythm, scanned_rhythm)
                self._distances_to_target[i] = distance

        self._distances_to_target_rhythm_are_stale = False
        self._target_rhythm_prev_update = target_rhythm
        self._dispatch(self.DISTANCES_TO_TARGET_UPDATED)

    def set_rhythm_selection(self, selected_rhythms):
        """
        Sets the rhythm selection.

        :param selected_rhythms: the indices in the current corpus of the rhythms to select
        """

        if self.is_rhythm_player_set() and self.is_rhythm_player_playing():
            self.stop_rhythm_playback()

        self._rhythm_selection.clear()
        for rhythm_ix in selected_rhythms:
            self._precondition_check_rhythm_index(rhythm_ix)
            self._rhythm_selection.add(rhythm_ix)
        self._dispatch(BSController.RHYTHM_SELECTION)

    def clear_rhythm_selection(self):
        """
        Clears the rhythm selection.
        """

        self.set_rhythm_selection([])

    def get_rhythm_selection(self):
        """
        Returns the corpus indices of the currently selected rhythms.

        :return: tuple containing the corpus indices of the currently selected rhythms
        """

        return tuple(self._rhythm_selection)

    def register_rhythm_loader(self, loader: BSMidiRhythmLoader) -> None:
        """
        Register a new rhythm loader.

        :param loader: rhythm loader
        :return: None
        """

        loader_class = loader.__class__
        if loader_class in self._rhythm_loaders:
            raise ValueError(
                "Already registered a rhythm loader for loader type: \"%s\"" %
                loader_class)

        config = self._config
        loader.bind_to_config(config)

        self._rhythm_loaders[loader.__class__] = loader
        self._dispatch(self.RHYTHM_LOADER_REGISTERED, loader)

    def get_rhythm_loader_source_names(self):
        """
        Returns a tuple with the rhythm source names of the currently registered rhythm loaders. See the
        BSMidiRhythmLoader.source_name property.

        :return: tuple containing the source names of the currently registered rhythm loaders
        """

        return tuple(loader.get_source_name()
                     for loader in self._rhythm_loaders.values())

    def get_rhythm_loader_count(self):
        """
        Returns the number of currently registered rhythm loaders.

        :return: number of currently registered rhythm loaders
        """

        return len(self._rhythm_loaders)

    def get_rhythm_loader_iterator(
        self
    ) -> (tp.Iterator[tp.Tuple[tp.Type[BSMidiRhythmLoader],
                               BSMidiRhythmLoader]]):
        """
        Returns an iterator over the currently registered rhythm loaders.

        :return: iterator over the currently registered rhythm loaders
        """

        return iter(self._rhythm_loaders.items())

    def get_rhythm_loader(self, loader_type: tp.Type[BSMidiRhythmLoader]
                          ):  # type: (str) -> BSMidiRhythmLoader
        """
        Returns the rhythm loader, given the loader type.

        :param loader_type: source name
        :return: loader
        """

        try:
            loader = self._rhythm_loaders[loader_type]
        except KeyError:
            raise ValueError("No rhythm loader registered of type \"%s\"" %
                             loader_type)

        return loader

    def export_single_rhythm_as_midi(self, fpath: str, rhythm_ix: int):
        """
        Exports a single rhythm as a MIDI file, given its rhythm index.

        :param fpath: path to export the MIDI file to (including the .mid extension)
        :param rhythm_ix: rhythm index
        :return: None
        """

        rhythm = self.get_rhythm_by_index(rhythm_ix)
        midi_pattern = rhythm.as_midi_pattern()
        LOGGER.info("Saving rhythm to: %s" % fpath)
        midi.write_midifile(fpath, midi_pattern)

    def export_rhythms_as_midi(self, export_directory: str,
                               rhythms: tp.Union[tp.Sequence[int], str]):
        """
        Exports rhythms as MIDI files to the given export directory. The rhythms can be given in two ways:

        * as a sequence of rhythm indices
        * as either "all" for all rhythms in the corpus or "selection" to export the currently selected rhythms

        :param export_directory: the midi files will be saved in this directory
        :param rhythms: either a sequence of rhythm indices or "all" or "selection"
        :return: None
        """

        if isinstance(rhythms, str):
            if rhythms == "all":
                n_rhythms = self.get_rhythm_count()
                rhythm_indices = range(n_rhythms)
            elif rhythms == "selection":
                rhythm_indices = self.get_rhythm_selection()
            else:
                raise ValueError(
                    "Unknown rhythms wildcard '%s', please choose between ['all', 'selection'] "
                    "or provide a sequence with rhythm indices" % rhythms)
        else:
            rhythm_indices = rhythms

        for rhythm_ix in rhythm_indices:
            rhythm = self.get_rhythm_by_index(rhythm_ix)
            fpath = os.path.join(export_directory,
                                 "%s.mid" % rhythm.get_name())
            self.export_single_rhythm_as_midi(fpath, rhythm_ix)

    def bind(self, action, callback):
        """
        Adds a callback to the given action. Callback order is preserved. When the given callback is already bound to
        the given action, the callback will be moved to the end of the callback chain.

        :param action: action
        :param callback: callable that will be called when the given action occurs
        """

        if not callable(callback):
            raise TypeError("Expected a callable")
        self._precondition_check_action(action)
        action_callbacks = self._callbacks[action]
        if callback in action_callbacks:
            action_callbacks.remove(callback)
        self._callbacks[action].add(callback)

    def unbind(self, action, callback):
        """
        Removes a callback from the given action.

        :param action: action
        :param callback: callable
        :return: True if the action was removed successfully or False if the callback was never added to
                 the given action
        """

        self._precondition_check_action(action)
        action_callbacks = self._callbacks[action]
        if callback not in action_callbacks:
            return False
        self._callbacks[action].remove(callback)
        return True

    def _reset_distances_to_target_rhythm(
            self):  # Note: the caller should acquire the lock
        n_rhythms = self.get_rhythm_count()
        if len(self._distances_to_target) == n_rhythms:
            self._distances_to_target.fill(np.inf)
        else:
            self._distances_to_target = np.full(n_rhythms, np.inf)

    def _dispatch(self, action, *args, **kwargs):
        self._precondition_check_action(action)
        for clb in self._callbacks[action]:
            clb(*args, **kwargs)

    def _on_rhythm_playback_ended(self):
        self._dispatch(BSController.RHYTHM_PLAYBACK_STOP)

    def _precondition_check_rhythm_index(self, rhythm_ix):
        n_rhythms = self.get_rhythm_count()
        if not (0 <= rhythm_ix < n_rhythms):
            raise IndexError("Expected rhythm index in range [0, %i]" %
                             (n_rhythms - 1))

    def _precondition_check_rhythm_player_set(self):
        if not self.is_rhythm_player_set():
            raise Exception("Rhythm player not set")

    def _precondition_check_action(self, action):
        if action not in self._callbacks:
            raise ValueError("Unknown action \"%s\"" % action)

    def _precondition_check_corpus_set(self):
        if not self.is_corpus_loaded():
            raise Exception("Corpus not loaded")

    def _precondition_check_target_rhythm_set(self):
        if not self.is_target_rhythm_set():
            raise Exception("Target rhythm not set")

    def _setup_config(self):
        config = self._config
        config.mapping_reducer.add_binding(self._on_mapping_reducer_changed)
        config.rhythm_resolution.add_binding(
            self._on_rhythm_resolution_changed)

    def _on_mapping_reducer_changed(self,
                                    reducer: tp.Type[MidiDrumMappingReducer]):
        target_rhythm = self._target_rhythm
        if not target_rhythm:
            return
        target_rhythm.set_midi_drum_mapping_reducer(reducer)

    def _on_rhythm_resolution_changed(self, resolution: int):
        target_rhythm = self._target_rhythm
        if not target_rhythm:
            return
        target_rhythm.set_resolution(resolution)
コード例 #23
0
def test_init():
    os = OrderedSet()
    assert len(os) == 0
コード例 #24
0
def test_index_error():
    os = OrderedSet(range(10))
    os.index(10)