Source code for dob.facts.add_fact

# This file exists within 'dob':
#
#   https://github.com/hotoffthehamster/dob
#
# Copyright © 2018-2020 Landon Bouma,  2015-2016 Eric Goller.  All rights reserved.
#
# 'dob' is free software: you can redistribute it and/or modify it under the terms
# of the GNU General Public License  as  published by the Free Software Foundation,
# either version 3  of the License,  or  (at your option)  any   later    version.
#
# 'dob' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY  or  FITNESS FOR A PARTICULAR
# PURPOSE.  See  the  GNU General Public License  for  more details.
#
# You can find the GNU General Public License reprinted in the file titled 'LICENSE',
# or visit <http://www.gnu.org/licenses/>.

import random

from gettext import gettext as _

from dob_bright.crud.fact_from_factoid import must_create_fact_from_factoid
from dob_bright.crud.fix_times import mend_fact_timey_wimey
from dob_bright.termio import dob_in_user_exit

from dob_prompt.prompters.triple_prompter import ask_user_for_edits

from .save_backedup import prompt_and_save_backedup

__all__ = (
    'add_fact',
)


# ***

[docs]def add_fact( controller, factoid, time_hint, use_carousel=False, edit_text=False, edit_meta=False, yes=False, dry=False, ): """ Start or add a fact. Args: factoid (str): ``factoid`` detailing Fact to be added. See elsewhere for the factoid format. time_hint (str, optional): One of: | 'verify_none': Do not expect to find any time encoded in factoid. | 'verify_both': Expect to find both start and end times. | 'verify_start': Expect to find just one time, which is the start. | 'verify_end': Expect to find just one time, which is the end. | 'verify_then': Optional time is new start; and either extend ongoing fact to new start, or back-fill interval gap. | 'verify_still': Optional time is new start; copy prev meta to new Fact; either extend ongoing fact, or back-fill interval gap. | 'verify_after': No time spec. Start new Fact at time of previous end. yes (bool, optional): If True, update other Facts changed by the new fact being added (affects other Facts' start/end/deleted attrs). If False, prompt user (i.e., using fancy interface built with python-prompt-toolkit) for each conflict. edit_meta (bool, optional): If True, prompt user for activity and/or category, if not indicated; and prompt user for tags. Shows MRU lists to try to make it easy for user to specify commonly used items. Returns: Nothing: If everything went alright. (Otherwise, will have exited.) """ def _add_fact(): new_fact = _create_fact() new_fact_or_two, conflicts = _mend_times(new_fact) edit_facts, orig_facts = _prepare_facts(new_fact_or_two) edit_fact = _add_conflicts(conflicts, edit_facts, orig_facts) _maybe_prompt_description(edit_fact) _maybe_prompt_actegory(edit_fact) saved_facts = _prompt_and_save(edit_facts, orig_facts) return saved_facts def _create_fact(): # NOTE: factoid is an ordered tuple of args; e.g., sys.argv[2:], if # sys.argv[0] is the executable name, and sys.argv[1] is command. # Because dob is totes legit, it does not insist that the user # necessary quote anything on the command line, but it does not # insist not, either, so factoid might have just one element (a # string) containing all the information; or factoid might be a # list of strings that need to be parsed and reassembled (though # not necessarily in that order). # # tl;dr: factoid is a tuple of 1+ strings that together specify the Fact. # Make a new Fact from the command line input. new_fact = must_create_fact_from_factoid( controller, factoid, time_hint, ) return new_fact def _mend_times(new_fact): # If there's an ongoing Fact, we might extend or squash it. # Also, if the new Fact overlaps existing Facts, those Facts' # times might be changed, and/or existing Facts might be deleted. try: new_fact_or_two, conflicts = mend_fact_timey_wimey( controller, new_fact, time_hint, ) except ValueError as err: # (lb): I'm very indecisive. choices = [ _("Not so fast!"), _("Cannawt!"), _("Unpossible!"), _("Insidious!"), _("Think again!"), ] msg = _('{} {}').format(random.choice(choices), err) controller.client_logger.error(msg) dob_in_user_exit(msg) return new_fact_or_two, conflicts def _prepare_facts(new_fact_or_two): edit_facts = [] orig_facts = [] new_fact_pk = -1 for new_fact in new_fact_or_two: # If ongoing was squashed, edited_fact.pk > 0, else < 0. # There might also be an extended filler gap Fact added. if new_fact.pk is None: new_fact.pk = new_fact_pk new_fact_pk -= 1 edit_facts.append(new_fact) else: # The edited fact is in conflicts, and carousel will start on it. assert new_fact.pk > 0 return edit_facts, orig_facts def _add_conflicts(conflicts, edit_facts, orig_facts): for edited, original in conflicts: edit_facts.append(edited) # (lb): This is the only place orig_facts is not [edit_fact.copy(), ...]. orig_facts.append(original) edit_fact = edit_facts[0] return edit_fact def _maybe_prompt_description(edit_fact): if not edit_text: return # User wants to edit description first. ask_user_for_edits( controller, fact=edit_fact, always_ask=False, restrict_edit='description', ) def _maybe_prompt_actegory(edit_fact): if not edit_meta: return ask_user_for_edits( controller, fact=edit_fact, ) def _prompt_and_save(edit_facts, orig_facts): saved_facts = prompt_and_save_backedup( controller, edit_facts=edit_facts, orig_facts=orig_facts, use_carousel=use_carousel, yes=yes, dry=dry, progress=None, ) return saved_facts return _add_fact()