def test_add_to_db(self, beethoven, debussy, mockdb): """Test adding composers to the database.""" beethoven.add_to_db(mockdb) comptable = mockdb.table('composers') assert db_interface.explore_table(comptable, search=('name', 'Ludwig van Beethoven')) debussy.add_to_db(mockdb) assert db_interface.explore_table(comptable, search=('name', 'Claude Debussy'))
def load_from_db(cls, name, db): """ Loads a composer from the database. :param name: part or all of a composer's name :param db: a TinyDB instance """ table = db.table('composers') name_parts = name.split(' ') lname = name_parts.pop() comps = db_interface.explore_table(table=table, search=('name', lname)) if not isinstance(comps, list): comps = [comps] for item in comps: if name_parts[0] in item: data = db_interface.load_name_from_table(item, db=db, tablename='composers') name = data['name'] try: mutopianame = data['mutopianame'] except KeyError: mutopianame = None try: shortname = data['shortname'] except KeyError: shortname = None return cls(name=name, mutopianame=mutopianame, shortname=shortname)
def instrument_prompt(curr_instruments, db_): """ Prompt for creating instruments in the score. :param curr_instruments: list of existing instruments :param db_: database to laod to/from :return: """ prompt_help = ("Options:\n" f"{BOLD}print{END} instruments\n" f"{BOLD}create{END} a new instrument\n" f"{BOLD}delete{END} an instrument\n" f"{BOLD}reorder{END} instruments\n" f"{BOLD}help{END} to view this message\n" f"{BOLD}done{END} to save and return to main prompt") command_completer = WordCompleter( ['create', 'delete', 'reorder', 'help', 'done', 'print']) instrument_names = db_interface.explore_table(db_.table("instruments"), search=("name", "")) instruments = [ titlecase(' '.join(name.split('_'))) for name in instrument_names ] print(prompt_help) while True: # DEBUG LINE # print(curr_instruments) command = prompt("Instruments> ", completer=command_completer) if len(command) == 0: continue elif command.lower()[0] == 'p': for ins in curr_instruments: print(ins.part_name(key=True)) elif command.lower()[0] == 'c': new_ins = create_instrument(instruments, db_, instrument_names) curr_instruments.append(new_ins) elif command.lower()[0:2] == 'de': while True: instruments_with_indexes(curr_instruments) del_idx = prompt( "Enter the number of the instrument to delete or [enter] to " "finish: ") or None if del_idx is None: break elif del_idx.isdigit(): curr_instruments.pop(int(del_idx)) else: print("Invalid index") elif command.lower()[0] == 'r': curr_instruments = reorder_instruments(curr_instruments) elif command.lower()[0] == 'h': print(prompt_help) elif command.lower()[0:2] == 'do': return curr_instruments else: print(INVALID)
def composer_prompt(db): composers = db_interface.explore_table(db.table("composers"), search=("name", "")) comp = prompt("Enter Composer: ", completer=InsensitiveCompleter(composers)) matches = [] for item in composers: if comp in item: matches.append(item) if matches: load = prompt(f"Would you like to load {comp} from the database? ", default='Y', validator=YNValidator()) if answered_yes(load): if len(matches) > 1: for num, match in enumerate(matches): print(f"{num}. {match}") choice = prompt( "Please enter the number of the " "matching composer or press [enter] if none match: ", validator=IndexValidator(len(matches) - 1, allow_empty=True)) if choice: found = matches[int(choice[0])] else: found = matches[0] try: return info.Composer.load_from_db(found, db) except NameError: pass new_comp = info.Composer(comp) guess_short_name = new_comp.get_short_name() try: guess_mutopia_name = new_comp.get_mutopia_name(guess=True) except AttributeError: guess_mutopia_name = '' new_comp.shortname = prompt("Enter the abbreviated name of the composer: ", default=guess_short_name) new_comp.mutopianame = prompt( "Enter the mutopia formatted name of the composer " "or [enter] for none: ", default=guess_mutopia_name) add_to_db = prompt( "Would you like to add this composer to the database for easy usage next time? ", default='Y', validator=YNValidator()) if answered_yes(add_to_db): new_comp.add_to_db(db) return new_comp
def composer(ctx, name): """Add composer to the database.""" db_ = ctx.obj['DB'] newcomp = Composer(name) try: mutopianame = input( f"Assumed Mutopia Name is {newcomp.get_mutopia_name(guess=True)}" " Is this correct? [Y/n] ") or "Y" if mutopianame[0].lower() == 'n': newcomp.mutopianame = input("Enter correct Mutopia Name: ") except MutopiaError: newcomp.mutopianame = input("Enter correct Mutopia Name: ") shortname = input(f"Assumed short name is {newcomp.get_short_name()}" " Correct? [Y/n] ") or "Y" if shortname[0].lower() == 'n': newcomp.shortname = input("Enter correct short name: ") newcomp.add_to_db(db_) newrec = db_interface.explore_table(db_.table("composers"), search=("shortname", newcomp.shortname)) print(newrec)
def add_to_db(self, db): """ Serializes the Ensemble and adds it to the supplied database in the 'ensembles' table. This should only be called after load_from_db fails or the databse is otherwise checked so duplicates aren't added to the database. :param db: a tinydb instance to insert into. """ ens_table = db.table('ensembles') ins_table = db.table('instruments') data = dict(name=self.name) data['instruments'] = [] for instrument in self.instruments: # if the instrument isn't in the database if not explore_table(ins_table, search=('name', instrument.name)): instrument.add_to_db(db) data['instruments'].append({ 'name': instrument.name, 'number': instrument.number }) ens_table.insert(data)
def ensemble_prompt(curr_instruments, db_): """ Prompt for creating ensembles. :param curr_instruments: Current list of instruments. :param db_: database to load to/from :return: lynames.Ensemble object """ ensemble_names = db_interface.explore_table(db_.table("ensembles"), search=("name", "")) ensembles = [ titlecase(' '.join(name.split('_'))) for name in ensemble_names ] ensemble_name = prompt("Please enter a name for the ensemble: ", completer=InsensitiveCompleter(ensembles)) ensemble_name_normal = lynames.normalize_name(ensemble_name) new_ens = None if ensemble_name_normal in ensemble_names: load = prompt( f"{ensemble_name} is in the database, would you like to load it? " "[Y/n] ", default='Y', validator=YNValidator()) if answered_yes(load): return lynames.Ensemble.load_from_db(ensemble_name, db_) while True: new_ens = common.create_ensemble(ensemble_name, db_, curr_instruments) if isinstance(new_ens, lynames.Ensemble): break else: retry = prompt(f"No new ensemble was created. Try again? ", validator=YNValidator(), default='Y') if not answered_yes(retry): break return new_ens
def search(ctx, table, field, term): """Search the database""" db_ = ctx.obj['DB'] print(db_interface.explore_table(db_.table(table), search=(field, term)))
def create_ensemble(name, db, instruments_to_add=[]): """ Create an ensemble from new or old instruments :param name: The name of the ensemble :param db: the database to add the ensemble to and load instruments from. :param instruments_to_add: (Optional) existing instrument objects to add to the ensemble :return: ensemble object created by the dialog """ instrument_names = db_interface.explore_table(db.table("instruments"), search=("name", "")) instruments = [ titlecase(' '.join(name.split('_'))) for name in instrument_names ] ins_list = [] new_ens = Ensemble(name) for ins in instruments_to_add: if isinstance(ins, Instrument): ins_list.append(ins) continue ins_name = ins num = None for group in ins.split(): if group.isdigit(): num = int(group) ins_name = ins.replace(f" {group}", '') if normalize_name(ins_name) in instrument_names: ins_list.append( Instrument.load_from_db(normalize_name(ins_name), db, number=num)) else: print(f"{ins_name} not in db") if not ins_list: print( "You will need to create some instruments to add to the ensemble.") ins_list.append(create_instrument(instruments, db, instrument_names)) prompt_help = ( "You can:\n" f"{BOLD}reorder{END}, {BOLD}add{END}, {BOLD}delete{END}, {BOLD}print{END}" f"\nor {BOLD}done{END} if you are satisfied with the instruments.") print(prompt_help) instruments_with_indexes(ins_list) while True: choice = prompt( "Ensemble> ", completer=WordCompleter( ['reorder', 'add', 'delete', 'continue', 'print']), ) if len(choice) == 0: continue elif choice.lower()[0] == 'r': ins_list = reorder_instruments(ins_list) elif choice.lower()[0] == 'a': ins_list.append( create_instrument(instruments, db, instrument_names)) elif choice.lower()[0:2] == 'de': while True: instruments_with_indexes(ins_list) del_idx = prompt( "Enter the number of the instrument to delete or [enter] to " "finish: ") or None if del_idx is None: break elif del_idx.isdigit(): ins_list.pop(int(del_idx)) else: print("Invalid index") elif choice.lower()[0] == 'p': print(name + ':') instruments_with_indexes(ins_list) elif choice.lower()[0:2] == 'do': break for ins in ins_list: new_ens.add_instrument_from_obj(ins) print(new_ens) good = prompt("Save? ", validator=YNValidator(), default='Y') if not answered_yes(good): return ins_list add_to_db = prompt("Add to database for future use? ", validator=YNValidator(), default='Y') if answered_yes(add_to_db): new_ens.add_to_db(db) return new_ens
def header_prompt(curr_headers, db): prompt_help = ( "You may edit any of the following headers:\n" "title\t\tcomposer\n" "dedication\tsubtitle\n" "subsubtitle\tpoet\n" "meter\t\tarranger\n" "tagline\t\tcopyright\n" f"Enter {BOLD}print{END} to print the current headers and {BOLD}done{END} to finish" "and return to the main prompt.") print(prompt_help) titlewords = WordCompleter( db_interface.explore_table(db.table("titlewords"), search=("word", ""))) field_completer = WordCompleter([ "title", "composer", "subtitle", "subsubtitle", "poet", "meter", "arranger", "tagline", "copyright", "print", "done" ]) if curr_headers is None: composer = composer_prompt(db) title = prompt("Enter Title: ", completer=titlewords) curr_headers = info.Headers(title=title, composer=composer) while 1: # DEBUG LINE # print(curr_headers) command = prompt("Headers> ", completer=field_completer) if len(command) == 0: continue field = command.lower().strip() if field == "title": title = prompt( "Current title is \"{}\" enter a new title or press " "enter to keep the current one: ".format(curr_headers.title)) if len(title) != 0: curr_headers.title = title elif "comp" in field: change_comp = prompt( "Current composer is {}. Would you like to change " "it? ".format(curr_headers.composer.name), default='N', validator=YNValidator()) if answered_yes(change_comp): curr_headers.composer = composer_prompt(db) elif field in [ "dedication", "subtitle", "subsubtitle", "poet", "meter", "arranger", "tagline", "copyright" ]: print("{} is {}".format(field, getattr(curr_headers, field, "blank"))) new = prompt( f"Enter value for {field} or press enter to leave unchanged: ") if len(new) > 0: setattr(curr_headers, field, new) # Logistical commands elif field[0] == 'h': print(prompt_help) elif field[0] == 'p': print(curr_headers) elif field[0] == 'd' or field == "save": print("Saving headers") return curr_headers else: print(INVALID)
def test_explore_table(mocktable, livetable): """ Test exploring a database table. NOTE: some assertions use set.issubset() so that if additional matching items are added the tests will still pass. """ assert {'violin', 'violoncello', 'clarinet_in_bb'} ==\ set(db_interface.explore_table(mocktable)), ("Without search terms " "it should return all " "names.") assert {'violin', 'viola', 'violoncello'}.issubset( set(db_interface.explore_table(livetable, search=('name', 'vio'))) ), "With search terms it should return matching subset." assert {'violoncello', 'double_bass', 'contrabass'}.issubset( set(db_interface.explore_table(livetable, search=('clef', 'bass'))) ), "With search terms it should return matching subset." assert {'violin', 'viola', 'violoncello', 'double_bass', 'contrabass'}.issubset(db_interface.explore_table( livetable, search=('family', 'strings'))), ("With search " "terms it should " "return matching " "subset.") assert db_interface.explore_table(livetable, search=( 'name', 'this is not the table')) == [], ("If nothing matches " "return empty list.") assert db_interface.explore_table(livetable, search=( 'not a field', 'fail')) == [], "If nothing matches return empty list." assert db_interface.explore_table(livetable, search=('name', 'vio')),\ "A search that finds something should be implicitly true" assert not db_interface.explore_table(livetable, search=( 'name', 'not in the database')), ("A search that returns nothing " "should be implicitly false.") with pytest.raises(TypeError, match='.*tuple.*', message=( 'Expect TypeError if search is not a tuple.')): db_interface.explore_table(livetable, search='hi') with pytest.raises(TypeError, match='.*tinydb.*', message=( 'Expect TypeError if table is not a tinydb table.')): db_interface.explore_table(123, search=('name', 'test')) # test that it handles malformed data mocktable.search.return_value = [ {'name': 'hi'}, {'fake': 'table'}, {'key': 'value'}, {'name': 'test'}] assert db_interface.explore_table(mocktable, search=('name', 'test')) ==\ ['hi', 'test']