Ejemplo n.º 1
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
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)
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
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)
Ejemplo n.º 15
0
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)
Ejemplo n.º 16
0
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)