def search_release_offline(self, id_or_title): if is_number(id_or_title): try: return self.search_release_id(id_or_title) except Exception as Exc: log.error("Not found or Database Exception: %s\n", Exc) raise Exc else: try: releases = self._select_simple( ['*'], 'release', 'discogs_title LIKE "%{}%" OR d_artist LIKE "%{}%"'.format( id_or_title, id_or_title), fetchone=False, orderby='d_artist') if releases: log.debug("First found release: {}".format(releases[0])) log.debug("All found releases: {}".format(releases)) return releases # this is a list else: return None except Exception as Exc: log.error("Not found or Database Exception: %s\n", Exc) raise Exc
def search_release_online(self, id_or_title): try: if is_number(id_or_title): releases = [self.d.release(id_or_title)] else: releases = self.d.search(id_or_title, type='release') # exceptions are only triggerd if trying to access the release obj if len(releases) > 0: # fixes list index error log.info("First found release: {}".format(releases[0])) log.debug("All found releases: {}".format(releases)) return releases except discogs_client.exceptions.HTTPError as HtErr: log.error("%s (HTTPError)", HtErr) return None except urllib3.exceptions.NewConnectionError as ConnErr: log.error("%s (NewConnectionError)", ConnErr) return None except urllib3.exceptions.MaxRetryError as RetryErr: log.error("%s (MaxRetryError)", RetryErr) return None except requests.exceptions.ConnectionError as ConnErr: log.error("%s (ConnectionError)", ConnErr) return None except gaierror as GaiErr: log.error("%s (socket.gaierror)", GaiErr) return None except TypeError as TypeErr: log.error("%s (TypeError)", TypeErr) return None except Exception as Exc: log.error("%s (Exception)", Exc) raise Exc return None
def print_and_return_first_d_release(self, discogs_results, _searchterm, _db_releases): ''' formatted output _and return of Discogs release search results''' self.first_track_on_release = '' # reset this in any case first # only show pages count if it's a Release Title Search if not is_number(_searchterm): self.cli.p("Found "+str(discogs_results.pages )+" page(s) of results!") else: self.cli.p("ID: "+discogs_results[0].id+", Title: "+discogs_results[0].title+"") for result_item in discogs_results: self.cli.p("Checking " + str(result_item.id)) for dbr in _db_releases: if result_item.id == dbr['discogs_id']: self.cli.p("Good, first matching record in your collection is:") release_details = self.collection.prepare_release_info(result_item) tracklist = self.collection.prepare_tracklist_info( result_item.id, result_item.tracklist) print('{}\n'.format(self.cli.join_links_to_str(dbr))) # we need to pass a list in list here. we use tabulate to view self.cli.tab_online_search_results([release_details]) self.cli.online_search_results_tracklist(tracklist) self.first_track_on_release = result_item.tracklist[0].position break try: if result_item.id == dbr['discogs_id']: #return release_details[0] log.info("Compiled Discogs release_details: {}".format(release_details)) return release_details except UnboundLocalError: log.error("Discogs collection was not imported to DiscoBASE. Use 'disco import' command!") #raise unb raise SystemExit(1) return None
def add_release(self, release_id): start_time = time() self.cli.exit_if_offline(self.collection.ONLINE) if not is_number(release_id): log.error('Not a number') return False else: # setup.py argparser catches non-int, this is for calls from elsewhere if self.collection.search_release_id(release_id): msg = "Release ID is already existing in DiscoBASE, " msg+= "won't add it to your Discogs collection. We don't want dups!" self.cli.p(msg) else: self.cli.p("Asking Discogs if release ID {:d} is valid.".format( release_id)) result = self.collection.get_d_release(release_id) if result: artists = self.collection.d_artists_to_str(result.artists) d_catno = self.collection.d_get_first_catno(result.labels) log.debug(dir(result)) self.cli.p("Adding \"{}\" to collection".format(result.title)) for folder in self.collection.me.collection_folders: if folder.id == 1: folder.add_release(release_id) last_row_id = self.collection.create_release(result.id, result.title, artists, d_catno, d_coll = True) if not last_row_id: self.cli.error_not_the_release() log.debug("Discogs release was maybe added to Collection") self.cli.duration_stats(start_time, 'Adding Release to Collection') return True else: log.debug("No Discogs release. Returning False") self.cli.duration_stats(start_time, 'Adding Release to Collection') return False
def create(self): if is_number(self.mix.name_or_id): log.error("Mix name can't be a number!") else: if self.mix.name_existing: log.error('Mix "{}" already existing.'.format(self.mix.name)) raise SystemExit(1) self.cli.p('Creating new mix "{}".'.format(self.mix.name)) answers = self._create_ask_details() created_id = self.mix.create(answers['played'], answers['venue']) self.cli.p('New mix created with ID {}.'.format(created_id)) self.view_mixes_list()
def __init__(self, db_conn, mix_name_or_id, db_file=False): super(Mix, self).__init__(db_conn, db_file) # figuring out names and IDs, just logs and sets instance # attributes, no exits here! self.name_or_id = mix_name_or_id self.id_existing = False self.name_existing = False self.info = [] self.name = False self.created = False self.updated = False self.played = False self.venue = False if is_number(mix_name_or_id): self.id = mix_name_or_id # if it's a mix-id, get mix-name and info try: self.info = self.get_mix_info() self.name = self.info[1] # info existing or trigger Exception? self.id_existing = True self.name_existing = True except Exception: log.info("Mix ID is not existing yet!") else: self.name = mix_name_or_id # if it's a mix-name, get the id unless it's "all" # (default value, should only show mix list) if not self.name == "all": try: mix_id_tuple = self._get_mix_id(self.name) log.debug('MODEL: mix_id_tuple type: %s', type(mix_id_tuple).__name__) self.id = mix_id_tuple[0] self.id_existing = True self.name_existing = True # load basic mix-info from DB try: self.info = self.get_mix_info() self.name = self.info[1] except Exception: log.info("Can't get mix info.") except Exception: log.info("Can't get mix-name from ID. " "Mix not existing yet?") if self.id_existing: self.created = self.info[2] self.played = self.info[4] self.venue = self.info[5] log.debug("MODEL: Mix info is {}.".format(self.info)) log.debug("MODEL: Name is {}.".format(self.name)) log.debug("MODEL: Played is {}.".format(self.played)) log.debug("MODEL: Venue is {}.".format(self.venue))
def _get_mix_id(self, mixname): # should only be called from __init__ log.info('MODEL: Getting mix_id via mix name "%s". ' 'Only returns first match', mixname) if is_number(mixname): log.info("MODEL: mix name is a number, won't try to fetch from DB") return mixname else: self.cur.execute( 'SELECT mix_id FROM mix WHERE name LIKE ?', ("%{}%".format(mixname), ) ) row = self.cur.fetchone() if row: log.info("MODEL: Found mix ID: {}".format(row["mix_id"])) return row else: log.info("MODEL: Can't fetch mix ID by name") return row
def search_release(self, _searchterm): # online or offline search is decided in this method if self.collection.ONLINE: if is_number(_searchterm): self.cli.p('Searchterm is a number, trying to add Release ID to collection...') if not self.add_release(int(_searchterm)): log.warning("Release wasn't added to Collection, continuing anyway.") db_releases = self.collection.get_all_db_releases() self.cli.p('Searching Discogs for Release ID or Title: {}'.format(_searchterm)) search_results = self.collection.search_release_online(_searchterm) # SEARCH RESULTS OUTPUT HAPPENS HERE compiled_results_list = self.print_and_return_first_d_release( search_results, _searchterm, db_releases) if compiled_results_list == None: self.cli.error_not_the_release() m = 'Try altering your search terms!' log.info(m) print(m) raise SystemExit(1) return compiled_results_list else: self.cli.p('Searching database for ID or Title: {}'.format(_searchterm)) search_results = self.collection.search_release_offline(_searchterm) if not search_results: self.cli.p('Nothing found.') return False else: #if len(search_results) == 1: # self.cli.p('Found release: {} - {}'.format(search_results[0][3], # search_results[0][1])) # return search_results #else: self.cli.p('Found releases:') for cnt,release in enumerate(search_results): self.cli.p('({}) {} - {}'.format(cnt, release[3], release[1])) #num_search_results = [[cnt,rel] for cnt,rel in enumerate(search_results)] #print(num_search_results) answ = self.cli.ask('Which release? (0) ') if answ == '': answ = 0 else: answ = int(answ) return [search_results[answ]]
def trim_table_fields(self, tuple_table, cut_pos=16, exclude=[]): """this method puts \n after a configured amount of characters into _all_ fields of a sqlite row objects tuple list""" log.info( 'VIEW: Trimming table field width to max {} chars'.format(cut_pos)) # first convert list of tuples to list of lists: table_nl = [dict(row) for row in tuple_table] # now put newlines if longer than cut_pos chars for i, row in enumerate(table_nl): for key, field in row.items(): cut_pos_space = False # reset cut_pos_space on each field cycle if (not is_number(field) and field is not None and not key in exclude): if len(field) > cut_pos: cut_pos_space = field.find(" ", cut_pos) log.debug( "cut_pos_space index (next space after cut_pos): %s", cut_pos_space) # don't edit if no space following (almost at end) if cut_pos_space == -1: edited_field = field else: edited_field = field[ 0:cut_pos_space] + "\n" + field[cut_pos_space + 1:] log.debug("string from 0 to cut_pos_space: {}".format( field[0:cut_pos_space])) log.debug( "string from cut_pos_space to end: {}".format( field[cut_pos_space:])) log.debug("the final string:") log.debug("{}".format(edited_field)) log.debug("") table_nl[i][key] = edited_field log.debug("table_nl has {} lines".format(len(table_nl))) return table_nl
def trim_table_fields(self, tuple_table, cut_pos=16, exclude=[]): """this method puts \n after a configured amount of characters into _all_ fields of a sqlite row objects tuple list""" log.info("VIEW: Trimming table field width " "to max {} chars".format(cut_pos)) # First convert list of tuples to list of dicts: table_nl = [dict(row) for row in tuple_table] # Now put newlines if longer than cut_pos chars for i, row in enumerate(table_nl): for key, field in row.items(): if ( not is_number(field) and field is not None and key not in exclude ): field_length = len(field) if field_length < cut_pos: # Exit early on short fields continue log.debug("String to be cut: {}".format(field)) possible_cuts = int(field_length / cut_pos) log.debug("possible_cuts: {}".format(possible_cuts)) edited_field = '' prev_cut_pos_space = 0 loops = range(1, possible_cuts + 1) log.debug("We will loop {} time(s)".format(len(loops))) # Run as often as cut possibilities exist for cycle in loops: log.debug("Cycle {}/{}".format(cycle, len(loops))) # In each cycle we'll put \n _roughly_around_here_. curr_cut_pos = cut_pos * cycle log.debug("cur_cut_pos: %s", curr_cut_pos) cut_pos_space = field.find(" ", curr_cut_pos) log.debug("Next space after curr_cut_pos is at %s", cut_pos_space) # If no is space following (almost at end), # don't append newline, just append as-is! if cut_pos_space == -1: log.debug("No more space following. " "Add part and break loop!") edited_field += field[prev_cut_pos_space:] break else: log.debug("Add part and continue loop " "(if a cycle left)") edited_field += field[prev_cut_pos_space:cut_pos_space] + "\n" log.debug("From previous cut pos to current: {}".format( field[prev_cut_pos_space:cut_pos_space]) ) log.debug("") # Save pos for next cycle and skip the space itself, # we don't want following lines to start with a space! prev_cut_pos_space = cut_pos_space + 1 if field_length > cut_pos_space and cut_pos_space != -1: log.debug( "Loop done, appending remaining chars: " "{} to {}".format(cut_pos_space, field_length) ) # Add 1 to pos, we don't want a leading space. edited_field += field[cut_pos_space + 1:] log.debug("FINAL with newlines:") log.debug("{}".format(edited_field)) log.debug("") table_nl[i][key] = edited_field log.debug("table_nl has {} lines".format(len(table_nl))) return table_nl
def _add_track(self, _release_id, _release_title, _track_no, _pos): if not _track_no: track_to_add = self.cli.ask_for_track() else: log.debug("_track_no was given, value is {}".format(_track_no)) track_to_add = _track_no if _pos == None: log.debug("_pos was None, setting to 0") _pos = 0 log.debug("This is _pos: {}".format(_pos)) log.debug("This is track_to_add: {}".format(track_to_add)) log.debug("This is _release_id: %s", _release_id) log.debug("This is _release_title: %s", _release_title) if self.mix.id_existing: last_track = self.mix.get_last_track() log.debug("Currently last track in mix is: %s", last_track[0]) current_id = False if _pos: # a position to squeeze in the track was given # get current tracks >= pos tracks_to_shift = self.mix.get_tracks_from_position(_pos) if self.cli.really_add_track(track_to_add, _release_title, self.mix.id, _pos): current_id = self.mix.add_track(_release_id, track_to_add, track_pos = _pos) # all good? reorder tracks if current_id: log.info("Add track to mix successful, now reordering ...") self.mix.reorder_tracks_squeeze_in(_pos, tracks_to_shift) else: print("Track not added.") return True elif is_number(last_track[0]): # no position was given, tracks already mix if self.cli.really_add_track(track_to_add, _release_title, self.mix.id, last_track[0]+1): current_id = self.mix.add_track(_release_id, track_to_add, track_pos = last_track[0] + 1) else: print("Track not added.") return True else: # no position and it's the first track ever added if self.cli.really_add_track(track_to_add, _release_title, self.mix.id, 1): current_id = self.mix.add_track(_release_id, track_to_add, track_pos = 1) else: print("Track not added.") return True # FIXME untested if this is actually a proper sanity check log.debug("Value of current_id in add_offline_track: {}".format(current_id)) if current_id: self.view() #return True else: log.error("Add track to DB failed!") #return False else: self.cli.p("Mix ID {} is not existing yet.".format(self.mix.id)) return False
def edit_ask_details(self, orig_data, edit_questions): # collect answers from user input answers = {} for db_field, question in edit_questions: # some special treatments for track_pos handling... if db_field == 'track_pos': answers['track_pos'] = self.ask( question.format(orig_data['track_pos'])) if answers['track_pos'] == '': log.info("Answer was empty, dropping item from update.") del (answers['track_pos']) elif not is_number(answers['track_pos']): while not is_number(answers['track_pos']): log.warning("Answer was not a number, asking again.") if answers['track_pos'] == '': del (answers['track_pos']) break else: answers['track_pos'] = self.ask( question.format(orig_data['track_pos'])) else: move_to = int(answers['track_pos']) if move_to < orig_data['track_pos']: mvmsg = 'Note: Tracks new position will be right _before_ ' mvmsg += 'current track {}'.format(move_to) log.debug(mvmsg) print(mvmsg) elif move_to > orig_data['track_pos']: mvmsg = 'Note: Tracks new position will be right _after_ ' mvmsg += 'current track {}'.format(move_to) log.debug(mvmsg) print(mvmsg) elif db_field == 'trans_rating': allowed = ['++', '+', '~', '-', '--'] answers['trans_rating'] = self.ask( question.format(orig_data['trans_rating'])) if answers['trans_rating'] == '': log.info("Answer was empty, dropping item from update.") del (answers['trans_rating']) else: while not answers['trans_rating'] in allowed: log.warning( "Please use one of the following: ++, +, ~, -, --") if answers['trans_rating'] == '': del (answers['trans_rating']) break else: answers['trans_rating'] = self.ask( question.format(orig_data['trans_rating'])) elif db_field == 'name': # initial user question answers['name'] = self.ask(question.format(orig_data['name'])) # sanity checking loop while answers['name'] == orig_data['name']: log.warning("Just press enter if you want to leave as-is.") answers['name'] = self.ask( question.format(orig_data['name'])) # after loop we know that existing and new are different # if answer is empty, leave as-is (no empty mixname allowed) if answers['name'] == '': log.info("Answer was empty, dropping item from update.") del (answers['name']) else: answers[db_field] = self.ask( question.format(orig_data[db_field])) if answers[db_field] == "": log.info("Answer was empty, dropping item from update.") del (answers[db_field]) log.debug("CTRL: _edit_ask_details: answers dict: {}".format(answers)) return answers