def _do_lookup(self): """ Searches for and displays one or more log entries. Arguments: none. Returns: True if successful; False if the user aborted. ------------------------------------------------------------- """ try: # Clear the screen. wl_resource.print_header(self) # If there are no entries in the log, tell the user and then # return. if len(self.entries) == 0: io_utils.print_status( "Error", "There are no tasks in the log to search!", line_length=self.line_length) return False # end if # Main menu. option_list = [ "By Date/Time", "By Duration", "By Text Search", "By RE Pattern" ] prompt = "Please select a method by which to find entries:" search_opt = io_utils.menu(option_list, keystroke_list="#", prompt=prompt, line_length=self.line_length) # If the user quits, just return. if search_opt == 0: return False # end if # Otherwise call the appropriate lookup function. if search_opt == 1: results = wl_search.search_by_date(self) elif search_opt == 2: results = wl_search.search_by_duration(self) elif search_opt == 3: results = wl_search.search_by_text(self) else: # search_opt == 4 results = wl_search.search_by_re(self) # end if # If the search was unsuccessful, just return. if not results: return True # end if # Call the appropriate browse function, which will call other # functions if needed. if type(results[0]) == datetime.date: wl_search.select_date(self, results) return True else: wl_search.select_entry(self, results) return True # end if except Exception as err: _z_exc("worklog.py/WorkLog/_do_lookup", err)
def set_endian(wl_obj): """ Allows the user to set the preferred date format. Arguments: - wl_obj -- the work log object. Returns: nothing ----------------------------------------------------------------- """ try: # Endian examples. end_list = [ "July 15, 2010 (7/15/10)", "15 July, 2010 (15/7/10)", "2010 July 15 (10/7/15)"] # Print explanation. if wl_obj.date_format: msg = "The current date format is " if wl_obj.date_format == "M": msg += end_list[0] elif wl_obj.date_format == "L": msg += end_list[1] else: # wl_obj.date_format == "B" msg += end_list[2] # end if else: msg = "The date format has not been set." # end if io_utils.print_status( "Status", msg, go=True, line_length=wl_obj.line_length) # If a date format is already set, allow the user to leave it. if wl_obj.date_format: q = True else: q = False # end if # Ask the user to set a date format. response = io_utils.menu( end_list, keystroke_list="#", confirm=True, quit_=q, prompt="Please select your preferred date format:") if response == 0: return elif response == 1: wl_obj.date_format = "M" elif response == 2: wl_obj.date_format = "L" else: # response == 3 wl_obj.date_format = "B" # end if return except Exception as err: _z_exc("wl_datetime/set_endian", err)
def _do_settings(self, flag): """ Allows the user to change a setting. The date format, time format, or width of the screen can be changed. Arguments: none. Returns: nothing. ------------------------------------------------------------- """ try: # Loop until the user is done. while True: # Clear screen and print header. wl_resource.print_header(self) # Print instructions. self.help.print_help(self.show_help, "Settings", "_xh_settings", line_length=self.line_length) # Show menu and get a response. response = io_utils.menu( ["Set Date Format", "Set Time Format", "Set Screen Width"], keystroke_list="#", help_toggle=True, line_length=self.line_length) # If the user chose to toggle help, do that and then # loop back. if str(response).lower() == "-h": self.show_help = not self.show_help continue # end if # If the user chose to go back, just return. if response == QUIT: self.total_entries = flag return # Otherwise, to change a setting, call the appropriate # function. elif response == DATE_F: wl_datetime.set_endian(self) elif response == TIME_F: wl_datetime.set_time_format(self) else: # response == 3 wl_resource.set_screen_width(self) # end if # end while except Exception as err: _z_exc("worklog.py/WorkLog/_do_settings", err)
def set_time_format(wl_obj): """ Allows the user to set the preferred time format. Arguments: - wl_obj -- the work log object. Returns: nothing ----------------------------------------------------------------- """ try: # Build message. if not wl_obj.time_format: msg = "The time format has not been set." elif wl_obj.time_format == 12: msg = "The current time format is 12-hour (AM/PM)." else: # wl_obj.time_format == 24 msg = "The current time format is 24-hour (Military Time)." # end if # Print status. io_utils.print_status( "Status", msg, go=True, line_length=wl_obj.line_length) # Display menu and get response. response = io_utils.menu( ["12-hour Clock (AM/PM)", "24-hour Clock (Military Time)"], keystroke_list="#") # If the user chose to quit, just return without changing # anything. if response == 0: return # Else set the time_format attribute to the user's choice. elif response == 1: wl_obj.time_format = 12 else: # response == 2 wl_obj.time_format = 24 # end if return except Exception as err: _z_exc("wl_datetime/set_time_format", err)
def action_get(self): """ Gets an action from the user. If the log object is unitialized, asks the user to open or create a file. Arguments: none. Returns: False if the user chooses to exit the program, else True. ------------------------------------------------------------- """ try: print() # If the total_entries attribute is -1, the object is not # initialized. Ask the user to open or create a log file. if self.total_entries == -1: keystroke_list = ["O", "N", "X", "M"] action = io_utils.menu([ "Open Log File", "New Log File", "Change Settings", "Read User Manual" ], option_type="actions", keystroke=True, keystroke_list=keystroke_list, lines=True, top_level=True, line_length=self.line_length) # If the user chooses to quit here... if action == 0: # Set the action attribute so _action_take knows to # do nothing. self.action = None # return False to main() so it knows to exit the # program. return False else: # Set the action attribute, skip the other menu code and # immediately return. self.action = keystroke_list[action - 1] return True # end if # end if # If the object has been initialized, skip the above code, # print the header and ask the user what they want to do. wl_resource.print_header(self) keystroke_list = ["A", "F", "S", "C", "X"] action = io_utils.menu([ "Add Entries", "Find/Edit/Delete Entries", "Save Log File", "Close Log File", "Change Settings" ], option_type="actions", lines=True, keystroke=True, keystroke_list=keystroke_list, top_level=True, line_length=self.line_length) # If the user chose to quit... if action == QUIT: # Set the action attribute to None, which will be a flag # for the action_take method. self.action = None # If the log object has changed, prompt to save. if self.changed is True: save = io_utils.yes_no( "The log file has changed. Do you want to save it?", line_length=self.line_length) # If yes, just save the file now (bypass action_take # so that it will still exit). if save: self._do_save() # end if # end if # Return the flag to end the object loop. return False else: # Set the action attribute. self.action = keystroke_list[action - 1] # end if # Always return True unless the user chose to quit. return True except Exception as err: _z_exc("worklog.py/WorkLog/action_get", err)
def browse_entries(wl_obj, entry_list, ndx=0): """ Allows the user to browse entries. Arguments: - wl_obj -- the work log object. - entry_list -- the list of entries to browse. Keyword Arguments: - ndx -- the index of the entry to initially display. Returns: the entry list as modified. ----------------------------------------------------------------- """ try: # Loop. while True: # If the list is empty, automatically return. if len(entry_list) == 0: return entry_list # end if # Clear the screen. wl_resource.print_header(wl_obj) # Print status message. msg = f"Displaying task {(ndx + 1)} of {len(entry_list)}" io_utils.print_status("Status", msg, go=True, line_length=wl_obj.line_length) # Display the current entry. display_entry(wl_obj, entry_list[ndx]) # Beneath the entry, display a menu. options = ["Edit", "Delete"] key_list = ["E", "D"] if ndx > 0: options.append("Previous") key_list.append("P") # end if if ndx < (len(entry_list) - 1): options.append("Next") key_list.append("N") # end if options.append("Back") key_list.append("B") response = io_utils.menu(options, keystroke=True, keystroke_list=key_list, lines=False, quit_=False, prompt=" ", line_length=wl_obj.line_length) # Convert the integer response back into the correct option. response = key_list[response - 1] # Take the appropriate action. if response == "B": # If the user wants to go back, return the entry list # (as it may have been modified). return entry_list elif response == "P": # If the user wants to back up one entry, decrement the # index and loop. ndx -= 1 continue elif response == "N": # If the user wants to go to the next entry, increment # the index and loop. ndx += 1 continue elif response == "E": # If the user wants to edit the current entry, call the # edit function and then loop. _edit_entry(wl_obj, entry_list[ndx], ndx, len(entry_list)) continue else: # If the user wants to delete the entry, confirm, and if # confirmed, call the delete function. and then loop. if io_utils.confirm("delete this entry"): _delete_entry(wl_obj, entry_list, ndx) # Delete the entry from the entry list. # If there are no more entries, return the empty # list. if len(entry_list) == 0: return entry_list # end if # If the index is past the end of the list, reset # it. if ndx <= len(entry_list): ndx = len(entry_list) - 1 # end if # end if continue # end if # end while except Exception as err: _z_exc("wl_viewedit.py/browse_entries", err)
def parse_date_numeric(wl_obj, string): """ Validates a numeric date in the current format. Allows the date format to be changed if the numeric combination is a valid date in a different format. Arguments: - wl_obj -- the work log object. - string -- the user input. Returns: a date object if successful, or None. ----------------------------------------------------------------- """ try: # Format constants. BIG_FORMAT = "%Y %B %d" MID_FORMAT = "%B %d, %Y" LIT_FORMAT = "%d %B %Y" # First separate the elements of the date and convert them to # integers. numbers = re.findall(r"\d+", string) for x, number in enumerate(numbers): numbers[x] = int(number) # end for # If there are only two elements, the year was omitted and # defaults to the current year. Where the year element is # inserted depends on the format. if len(numbers) == 2: if wl_obj.date_format == "B": numbers.insert(0, datetime.date.today().year) else: numbers.append(datetime.date.today().year) # end if # end if # Now try to create a date object with the selected format. entry_date = _create_date(numbers, wl_obj.date_format) # If it's valid, return it. if entry_date: return entry_date # If it's not valid, try the other formats. valid_formats, valid_dates = _check_other_endians( numbers, wl_obj.date_format) # If neither of the other formats is valid, just return None. if valid_formats == []: io_utils.print_status( "Error", f"{string} could not be interpreted as a valid date.", line_length=wl_obj.line_length) return None # end if # Otherwise, inform the user of the successful format(s) and ask # if he/she wants to change the selected format to match, or # re-enter the date. msg = io_utils.print_block( f"The date {string} is not valid in the currently selected " + "format, but is valid in a different format. You can choose to " + "change the date format or re-enter the date for this task.", ret_str=True) io_utils.print_status("Warning", msg, line_length=wl_obj.line_length) option_list = [] for x, ndn in enumerate(valid_formats): if ndn == "B": option_list.append(valid_dates[x].strftime(BIG_FORMAT)) elif ndn == "M": option_list.append(valid_dates[x].strftime(MID_FORMAT)) else: option_list.append(valid_dates[x].strftime(LIT_FORMAT)) # end if # end if # end for option_list.append("Re-enter the date.") # Give the user the option to change the format or try again. response = io_utils.menu(option_list, keystroke_list="#", quit_=False) # If the user chose to try again, return None. if response == len(option_list): input("Press [ENTER] to continue.") return None # Otherwise, reset the date format and return the date object. else: wl_obj.date_format = valid_formats[response - 1] return valid_dates[response - 1] # end if except Exception as err: _z_exc("wl_datetime/parse_date_numeric", err)
def _edit_entry(wl_obj, edit_entry, ndx, total): """ Allows the user to edit a log entry. Arguments: - wl_obj -- the work log object. - edit_entry -- the entry to edit. - ndx -- the number of the entry being edited. - total -- the total number of entries being displayed. Returns: nothing. ----------------------------------------------------------------- """ try: # This function mainly piggybacks on the add functions to alter # the attributes of the log entry object. All values are # preserved via the info attribute of a working copy until # saved by the user. changed = False # Create a working copy of the entry to be edited. new_entry = _copy_entry(edit_entry) # Store the original values in the info attribute. new_entry.info["title"] = new_entry.title new_entry.info["date"] = new_entry.date new_entry.info["time"] = new_entry.time new_entry.info["duration"] = new_entry.duration new_entry.info["notes"] = new_entry.notes new_entry.info["ndx"] = f"task {ndx + 1} of {total}" resort = False # Loop. while True: # Clear the screen and display program header. wl_resource.print_header(wl_obj) # Print status message. io_utils.print_status("Status", f"Editing {new_entry.info['ndx']}…", go=True, line_length=wl_obj.line_length) # Display the entry. display_entry(wl_obj, new_entry, edit=True) # Print instructions. wl_obj.help.print_help(wl_obj.show_help, "Editing", "_eh_edit", line_length=wl_obj.line_length) options = ["Title", "Date", "Time", "Duration", "Notes"] # User selects the field to edit. response = io_utils.menu( options, keystroke_list="#", prompt="Please select a field to edit. When you are finished," + " go back to save or discard your changes:", line_length=wl_obj.line_length, help_toggle=True) # If the user chose to toggle help, do that and loop back. if str(response).lower() == "-h": wl_obj.show_help = not wl_obj.show_help continue # end if # If the user chose to quit... if response == QUIT: # If the entry has been edited, prompt to save changes. if (changed and io_utils.yes_no( "Do you want to save your changes?", line_length=wl_obj.line_length)): # Recalculate the datetime attribute, in case either # the date or time changed. new_entry.datetime = wl_add.add_datetime(new_entry) # Save the changed values to the original log entry # object. _update_entry(wl_obj, new_entry, resort) # Set the flag that the log object has changed. wl_obj.changed = True # end if return # Edit title. elif response == TITLE: ch = wl_add.add_title(wl_obj, new_entry, edit=True) # If the title was edited, turn on the resort flag. if ch: resort = True # end if # Edit date. elif response == DATE: ch = wl_add.add_date(wl_obj, new_entry, edit=True) # If the date was edited, turn on the resort flag. if ch: resort = True # end if # Edit time. elif response == TIME: ch = wl_add.add_time(wl_obj, new_entry, edit=True) # If the time was edited, turn on the resort flag. if ch: resort = True # end if # Edit duration. elif response == DURATION: ch = wl_add.add_duration(wl_obj, new_entry, edit=True) # Edit notes. else: ch = wl_add.add_note(wl_obj, new_entry, edit=True) # end if # If something was edited, turn on the changed flag. if ch: changed = True # end if # end while except Exception as err: _z_exc("wl_viewedit.py/_edit_entry", err)
def _update_entry(wl_obj, entry, resort): """ Updates a log entry object's attributes with new values. Arguments: - wl_obj -- the work log object. - entry -- the log entry object containing the values to save. - resort -- flag that the sort lists will need updating. Returns: nothing. ----------------------------------------------------------------- """ try: # If the edited entry is part of a series (either as the parent # or a child), ask whether to apply the edits to the entire # series or only the one entry--but ONLY if the date attribute # hasn't been changed. If the date attribute HAS been changed, # apply all the edits to just the one entry, even if other # attributes have also been changed. if ((entry.recurring is True or entry.rec_parent) and (entry.date == entry.info["date"])): response = io_utils.menu( ["Edit this task only", "Edit all tasks in the series"], keystroke_list="#", prompt="This task is part of a series. " + "Do you want to apply your changes to all the tasks in this " + "series?", line_length=wl_obj.line_length) # If the user chooses to back out, just return without # updating. if response == QUIT: return elif response == UPDATE_ONE: edit_series = False else: # response == UPDATE_ALL edit_series = True # end if else: edit_series = False # end if # Find the original entry (and, if applicable, any child # entries). for ndx in range(len(wl_obj.entries)): if ((wl_obj.entries[ndx].id == entry.id) or (edit_series and ((wl_obj.entries[ndx].rec_parent == entry.rec_parent) or (wl_obj.entries[ndx].id == entry.rec_parent)))): # Simpler to overwrite the values (even if unchanged). wl_obj.entries[ndx].title = entry.title wl_obj.entries[ndx].time = entry.time wl_obj.entries[ndx].duration = entry.duration wl_obj.entries[ndx].notes = entry.notes # Recalculate the datetime attribute, in case it's # changed. wl_obj.entries[ndx].datetime = (wl_add.add_datetime( wl_obj.entries[ndx])) # If title, date or time changed, need to update sort # lists. if resort: for n in range(len(wl_obj.sorts[TITLE_SORT])): # Update and re-sort the list. if wl_obj.sorts[TITLE_SORT][n][ENTRY_ID] == entry.id: wl_obj.sorts[TITLE_SORT][n] == (entry.title, entry.datetime, entry.id) wl_obj.sorts[TITLE_SORT].sort() # end if # end for for n in range(len(wl_obj.sorts[DATE_SORT])): # Update and re-sort the list. if wl_obj.sorts[DATE_SORT][n][ENTRY_ID] == entry.id: wl_obj.sorts[DATE_SORT][n] == (entry.datetime, entry.title, entry.id) wl_obj.sorts[DATE_SORT].sort() break # end if # end for # end if # end if # end for return except Exception as err: _z_exc("wl_viewedit.py/_update_entry", err)
def browse_list(wl_obj, browse_list, start=0): """ Presents a list to the user and asks him/her to select one. Arguments: - wl_obj -- the work log object. - browse_list -- the list to present. - start -- the first item to display (default 0). Returns: an integer representing an item to display, 0 if the user quits, or a string representing a navigational command. ----------------------------------------------------------------- """ try: # If the list is empty, automatically return as if the user had # quit. if len(browse_list) == 0: return 0 # end if # Clear the screen and print the matches to be listed. wl_resource.print_header(wl_obj) if len(browse_list) == 1: msg = "Showing match 1 of 1" else: msg = f"Showing matches {start + 1}-" if start + 9 <= len(browse_list): msg += f"{start + 9} of {len(browse_list)}" else: msg += f"{len(browse_list)} of {len(browse_list)}" # end if # end if io_utils.print_status("Status", msg, go=True, line_length=wl_obj.line_length) # Build the options list. options = [] # For dates, just append the date (as a string). if type(browse_list[0]) == datetime.date: for ndx in range(start, start + 9): # Stop if at the end of the list. if ndx == len(browse_list): break # end if options.append(str(browse_list[ndx])) # end for # For entries, append the title, date and time (as strings). else: for ndx in range(start, start + 9): # Stop if at the end of the list. if ndx == len(browse_list): break # end if # Gather the fields. title = browse_list[ndx].title date = wl_resource.format_string(wl_obj, browse_list[ndx].date, short=True) time = wl_resource.format_string(wl_obj, browse_list[ndx].time) # If the time had a leading zero stripped, replace it # with a space. if time[1] == ":": time = f" {time}" # end if max_title_len = wl_obj.line_length - 23 # If the title is too long to fit in the column, # truncate it. if len(title) > max_title_len: title = title[:max_title_len - 1] + "… " else: title += " " # end if rj = wl_obj.line_length - len(title) - 4 option = title + f" {date} {time}".rjust(rj, ".") # Append the assembled string. options.append(option) # end for # end if if start > 0: prev = True else: prev = False # end if if start + 9 < len(browse_list): nxt = True else: nxt = False # end if # Now display the menu and return the user's choice. return io_utils.menu(options, prompt="Select an entry to view:", keystroke_list="#", nav=True, prev=prev, nxt=nxt, line_length=wl_obj.line_length) except Exception as err: _z_exc("wl_viewedit.py/browse_list", err)
def _delete_entry(wl_obj, entry_list, ndx): """ Deletes an entry, or a series of entries. Determines whether an entry marked for deletion is part of a recurring series. If it is, asks the user whether to delete the entire series. Arguments: - wl_obj -- the work log object. - del_entry -- the entry to delete. Returns: the modified entry list. ----------------------------------------------------------------- """ try: # First determine the correct action to take. action = None # If the entry is part of a series, ask whether to delete just # it, or the entire series. if entry_list[ndx].rec_child_seq: action = io_utils.menu([ "Delete this task only.", f"Delete all {entry_list[ndx].rec_child_seq[1]} occurances of" + "this task." ], keystroke_list="#", prompt= "This task is part of a recurring series.", line_length=wl_obj.line_length) # If the user cancels, just return. if action == 0: return entry_list # end if # If the entry is the parent of a series, ask the same question. elif entry_list[ndx].recurring: response = io_utils.menu( [ "Delete this task only.", f"Delete all {entry_list[ndx].rec_total + 1} occurances of " + "this task." ], keystroke_list="#", prompt="This task is the beginning of a recurring series.", line_length=wl_obj.line_length) # If the user cancels, just return. if response == 0: return entry_list # end if # Otherwise set the action. if response == 1: action = DELETE_PARENT else: action = DELETE_ALL # end if # If the entry is not part of any series, just delete it. else: wl_obj.entries.remove(entry_list[ndx]) # Delete from the sort lists. _delete_from_sort(wl_obj, entry_list[ndx]) # Delete the entry from the entry list. del entry_list[ndx] # end if # Otherwise take the indicated action. if action == DELETE_ONE: # Delete from the series. _delete_from_series(wl_obj, entry_list[ndx]) # Delete from the sort lists. _delete_from_sort(wl_obj, entry_list[ndx]) # Delete the entry from the entry list. del entry_list[ndx] elif action == DELETE_ALL: # Set ID to match. del_id = entry_list[ndx].rec_parent # To delete the entire series, loop through all entries and # delete those in the series, including the original. for n in range(len(wl_obj.entries) - 1, -1, -1): if ((wl_obj.entries[n].rec_parent == del_id) or (wl_obj.entries[n].id == del_id)): # If the entry is also in the entry list, delete it # from the list. if wl_obj.entries[n] in entry_list: entry_list.remove(wl_obj.entries[n]) # end if # Delete the entry from the sort indexes. _delete_from_sort(wl_obj, wl_obj.entries[n]) # Delete the entry from the log. del wl_obj.entries[n] # end if # end for elif action == DELETE_PARENT: # When only the parent of a recurring series is deleted, the # first child entry becomes the new parent, and the # attributes of all the other child entries are changed to # reflect that. if entry_list[ndx].rec_total > 1: # Go through the entries, looking for child entries. for n in range(len(wl_obj.entries)): if wl_obj.entries[n].rec_parent == entry_list[ndx].id: # First child entry changes. if wl_obj.entries[n].rec_child_seq[0] == 1: wl_obj.entries[n].recurring = True wl_obj.entries[n].rec_interval = ( entry_list[ndx].rec_interval) wl_obj.entries[n].rec_total = ( entry_list[ndx].rec_total - 1) wl_obj.entries[n].rec_child_seq = None wl_obj.entries[n].rec_parent = None # Set the new parent ID. new_id = wl_obj.entries[n].id # Changes for all other child entries. else: wl_obj.entries[n].rec_child_seq = ( wl_obj.entries[n].rec_child_seq[0] - 1, wl_obj.entries[n].rec_child_seq[1] - 1) wl_obj.entries[n].rec_parent = new_id # end if # end if # end for # BUT if the first child entry is the ONLY entry in the # series remaining, then it becomes a non-recurring task. else: # Go through the entries, looking for the one child # entry. for n in range(len(wl_obj.entries)): if wl_obj.entries[n].rec_parent == entry_list[ndx].id: # Change the child entry to a regular # non-recurring task. wl_obj.entries[n].rec_child_seq = None wl_obj.entries[n].rec_parent = None # No need to keep looking. break # end if # end for # Finally, delete the parent entry. wl_obj.entries.remove(entry_list[ndx]) # Delete from the sort lists. _delete_from_sort(wl_obj, entry_list[ndx]) # Delete the entry from the entry list. del entry_list[ndx] # end if # Set the flag that the work log has changed. wl_obj.changed = True # Return the modified entry list. return entry_list except Exception as err: _z_exc("wl_viewedit.py/_delete_entry", err)
def select_entry(wl_obj, entry_list): """ Allows the user to either browse a set of entries, or choose from a list. Arguments: - wl_obj -- the work log object. - entry_list -- the list of entries. Returns: nothing. ----------------------------------------------------------------- """ try: # Clear the screen. wl_resource.print_header(wl_obj) # Print the number of dates found. if len(entry_list) == 1: msg = "Found 1 task." else: msg = f"Found {len(entry_list)} tasks." # end if io_utils.print_status("Status", msg, line_length=wl_obj.line_length) # Ask the user to browse or pick from list. response = io_utils.menu( [ "Browse all matching tasks", "Choose from a list of matching tasks" ], keystroke_list="#", quit_=True, prompt="Please select how you want to see the matching tasks:", line_length=wl_obj.line_length) # If the user chooses to go back, just return. if response == QUIT: return # end if # If the user chooses to browse the matches, call the browse # function, then return. if response == 1: entry_list = wl_viewedit.browse_entries(wl_obj, entry_list) return # If the user chooses to see a list, display it here. else: start = 0 # Run in a loop until the user is done viewing/editing, then # return. while True: # Call list browse. response = wl_viewedit.browse_list(wl_obj, entry_list, start=start) # If the response isn't an integer, it's a command to # move forward or back. Move the start position, but # only if it doesn't go beyond the bounds of the list. if type(response) == str: if (response.lower() == "p") and (start - 9 >= 0): start -= 9 elif ((response.lower() == "n") and (start + 9 < len(entry_list))): start += 9 # end if # Clear the screen before looping back. wl_resource.print_header(wl_obj) continue # end if # If the user quits, return. if response == QUIT: return # end if # If it's a non-zero integer, the user chose an entry. # Get the index number of the chosen entry in the list. ndx = start + response - 1 # Browse entries, starting with the selected one. wl_viewedit.browse_entries(wl_obj, entry_list, ndx=ndx) # Clear the screen before looping back. wl_resource.print_header(wl_obj) # end while # end if except Exception as err: _z_exc("wl_search.py/select_entry", err)
def search_by_text(wl_obj): """ Finds work log entries based on a text string. Arguments: - wl_obj -- the work log object. Returns: a list of matching entries, if any are found; else an empty list. ----------------------------------------------------------------- """ try: # Run everything inside a loop in case the user wants to start # over. while True: # Option menu. search_fields = io_utils.menu( ["Title", "Notes", "Title and Notes"], keystroke_list="#", prompt="Which field(s) would you like to search?", line_length=wl_obj.line_length) # User quits, just exit. if search_fields == QUIT: return None # end if # Print the instructions. wl_obj.help.print_help(wl_obj.show_help, "Search Terms", "_sh_text", line_length=wl_obj.line_length) # Get a text string to search for. search_string = io_utils.get_input("Enter text to search for:") # If the user chose to toggle help, do that. if re.match(r"-h", search_string, re.I): wl_obj.show_help = not (wl_obj.show_help) continue # If the user didn't enter anything, either loop back or # return. if not search_string: print("You did not enter anything.") if not io_utils.yes_no("Try again?", line_length=wl_obj.line_length): return [] else: continue # end if # end if # If the search string includes wildcard characters, ask the # user how to treat them. if re.search(r"[?*]", search_string): search_mode = io_utils.menu( [ "Literal search (? and * will only match themselves)", "Wildcard search (? and * will match any character)" ], prompt="Your search string contains one or more wildcard " + "characters (? or *). What kind of search would you " + "like to perform?", keystroke_list="#", line_length=wl_obj.line_length) # If the user chooses to go back, start over. if search_mode == QUIT: continue # end if # Set the default to literal search. else: search_mode = 1 # Return the results of the find function. return_list = _find_entries_text(wl_obj, search_string, search_fields, search_mode) # If nothing matched, tell the user. if len(return_list) == 0: io_utils.print_status("Status", "No matches found.", line_length=wl_obj.line_length) # end if return return_list # end while except Exception as err: _z_exc("wl_search.py/search_by_text", err)
def search_by_re(wl_obj): """ Finds work log entries based on a regular expression. Arguments: - wl_obj -- the work log object. Returns: a list of matching entries, if any are found; else an empty list. ----------------------------------------------------------------- """ try: # Run everything inside a loop in case the user wants to start # over. while True: wl_obj.help.print_help(wl_obj.show_help, "RE Search", "_sh_re", line_length=wl_obj.line_length) # Get a regex string. re_string = io_utils.get_input( "Enter a regular expression (without quotation marks):") # If the user chose to toggle help, do that. if re.match(r"-h", re_string, re.I): wl_obj.show_help = not (wl_obj.show_help) continue # end if # If the user didn't enter anything... if not re_string: io_utils.print_status("Error", "You did not enter anything.", go=True, line_length=wl_obj.line_length) if not io_utils.yes_no("Try again?", line_length=wl_obj.line_length): return [] else: continue # end if # end if # Option menu. search_mode = io_utils.menu( ["Title", "Notes", "Title and Notes"], keystroke_list="#", prompt="Which field(s) would you like to search?", line_length=wl_obj.line_length) # User quits, just exit. if search_mode == QUIT: return None # end if # Get the results of the find function. return_list = _find_entries_re(wl_obj, re_string, search_mode) # If nothing matched, tell the user. if len(return_list) == 0: io_utils.print_status("Status", "No matches found.", line_length=wl_obj.line_length) # end if return return_list # end if # end while except Exception as err: _z_exc("wl_search.py/search_by_re", err)
def search_by_duration(wl_obj): """ Finds work log entries based on a duration or duration range. Arguments: - wl_obj -- the work log object. Returns: a list of matching entries, if any are found; else an empty list. ----------------------------------------------------------------- """ try: # Run everything inside a loop in case the user wants to start # over. while True: # Option menu. search_mode = io_utils.menu( ["A specific duration", "A range of durations"], keystroke_list="#", prompt="What would you like to search?", line_length=wl_obj.line_length) # User quits, just exit. if search_mode == QUIT: return None # end if # Both single duration and range require one duration, so # get one now. if search_mode == DURATION: d_type = "single" else: d_type = "minimum" # end if go, min_duration = _get_duration(wl_obj, d_type) # If the user wants to go back, loop back to the beginning. if go == GO_BACK: continue # If the user aborts, return. elif go == QUIT: return [] # end if # If the user wants to search a range, get a maximum # duration. if search_mode == DURATION_RANGE: d_type = "maximum" go, max_duration = _get_duration(wl_obj, d_type, min_duration) # Again, loop back or return if the user chooses to go # back or abort. if go == GO_BACK: continue elif go == QUIT: return [] # end if # If the search is for a single duration, just set the # maximum duration to equal the minimum. else: max_duration = min_duration # end if # Now find all entries that match. r_list = _find_entries_duration(wl_obj, min_duration, max_duration) # If no matches, tell the user. if len(r_list) == 0: io_utils.print_status("Status", "No matches found.", line_length=wl_obj.line_length) # end if return r_list # end if # end while except Exception as err: _z_exc("wl_search.py/search_by_duration", err)
def search_by_date(wl_obj): """ Finds work log entries based on a date/time or date/time range. Arguments: - wl_obj -- the work log object. Returns: for searches by date/time, a list of matching entries from the date-sorted index, if any are found, an empty list if no matches are found, or None if the user aborts; for a view of all dates, a list of unique date objects. ----------------------------------------------------------------- """ try: # Run everything inside a loop in case the user wants to start # over. while True: # Option menu. search_mode = io_utils.menu([ "A single date/time", "A range of dates/times", "View all dates" ], keystroke_list="#", prompt="How would you like to search?", line_length=wl_obj.line_length) # User quits, just exit. if search_mode == QUIT: return None # end if if search_mode in [DATE, DATE_RANGE]: # Both date/time searches require at least one date. if search_mode == DATE: d_type = "single" else: d_type = "start" # end if # Get the first (and for a single date/time search, # only) date/time. go, date = _get_date(wl_obj, d_type) # If the user decided to go back here, loop back. if go == GO_BACK: continue # end if # If the user decided to abort, confirm and then return. if go == QUIT and io_utils.confirm("abort the search"): return None # end if # Now get a time, if the user wants one. go, date = _get_time(wl_obj, d_type, date) # If the user decided to go back here, loop back. if go == GO_BACK: continue # end if # If the user decided to abort, confirm and then return. if go == QUIT and io_utils.confirm("abort the search"): return None # end if # If the user declined to enter a time, set the time of # the datetime object to midnight. if type(date) == datetime.date: start_date = datetime.datetime.combine( date, datetime.time()) # Otherwise the datetime object is already set. else: start_date = date # end if # If the user wants to search a single date/time... if search_mode == DATE: # If the user wants to search a specific date AND # time, set the end of the range to equal the # start. if type(date) == datetime.datetime: end_date = start_date # If the user wants to search a particular date but # not a particular time, set the end of the range # to 11:59pm on the same date as the start. else: end_date = start_date.replace(hour=23, minute=59) # end if # If searching a range, get the end date from the user. else: go, date = _get_date(wl_obj, "end", start=start_date) # If the user decided to go back here, loop back. if go == GO_BACK: continue # end if # If the user decided to abort, confirm and then # return. if go == QUIT and io_utils.confirm("abort the search"): return None # end if # Now get a time, if the user wants one. go, date = _get_time(wl_obj, "end", date) # If the user decided to go back here, loop back. if go == GO_BACK: continue # end if # If the user decided to abort, confirm and then # return. if go == QUIT and io_utils.confirm("abort the search"): return None # end if # If the user declined to enter a time, set the time # of the datetime object to the end of the day. if type(date) == datetime.date: end_date = datetime.datetime.combine( date, datetime.time.max) # Otherwise just set the datetime object. else: end_date = date # end if # end if # Return the entries to match the search terms. return_list = _find_entries_date(wl_obj, start_date, end_date) # If nothing was found, tell the user. if len(return_list) == 0: io_utils.print_status("Status", "No matches found.", line_length=wl_obj.line_length) # end if return return_list # To view all dates, create a list of unique dates. else: return_list = [] # Iterate through the sorted list. for entry in wl_obj.sorts[DATE_SORT]: # Convert datetime to date and append all unique # dates. if entry[SORT_KEY].date() not in return_list: return_list.append(entry[SORT_KEY].date()) # end if # end for return return_list # end if # end while except Exception as err: _z_exc("wl_search.py/search_by_date", err)