# 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 traceback
from gettext import gettext as _
import click_hotoffthehamster as click
from inflector import English, Inflector
from dob_bright.crud.fix_times import mend_fact_timey_wimey
from dob_bright.termio import (
attr,
click_echo,
dob_in_user_exit,
fg,
)
from .echo_fact import echo_fact
__all__ = (
'mend_facts_confirm_and_save_maybe',
# Private:
# 'echo_ongoing_completed',
# 'must_confirm_fact_edits',
# 'save_facts_maybe',
)
# ***
[docs]def mend_facts_confirm_and_save_maybe(
controller, fact, time_hint, other_edits, yes, dry,
):
""""""
def _mend_facts_confirm_and_save_maybe():
new_fact_or_two, conflicts = mend_fact_timey_wimey(
controller, fact, time_hint, other_edits,
)
saved_facts = confirm_conflicts_and_save_all(new_fact_or_two, conflicts)
return saved_facts
def confirm_conflicts_and_save_all(new_fact_or_two, conflicts):
""""""
# Ask user what to do about conflicts/edits.
ignore_pks = other_edits.keys()
must_confirm_fact_edits(controller, conflicts, yes, dry)
saved_facts = save_facts_maybe(
controller, new_fact_or_two, conflicts, ignore_pks, dry,
)
return saved_facts
return _mend_facts_confirm_and_save_maybe()
# ***
def must_confirm_fact_edits(controller, conflicts, yes, dry):
""""""
def _must_confirm_fact_edits(conflicts, yes, dry):
conflicts = cull_stopped_ongoing(conflicts)
if not conflicts:
return
yes = yes or dry
if not yes:
echo_confirmation_banner(conflicts)
n_conflict = 0
n_confirms = 0
for edited_fact, original in conflicts:
n_conflict += 1
if not yes:
confirmed = echo_confirmation_edited(
n_conflict, edited_fact, original,
)
n_confirms += 1 if confirmed else 0
else:
controller.client_logger.debug(
_('Editing fact: {}').format(edited_fact)
)
if n_conflict != n_confirms:
# (lb): This function is not used by the carousel -- only by
# one-off CLI commands -- so blowing up here is perfectly fine.
# (The carousel has its own error message display mechanism;
# and more importantly the carousel should never die,
# but should only ever be asked to die by the user.)
dob_in_user_exit(_("Please try again."))
def cull_stopped_ongoing(conflicts):
return [con for con in conflicts if 'stopped' not in con[0].dirty_reasons]
def echo_confirmation_banner(conflicts):
# FIXME: (lb): Replace hardcoded styles. Assign from styles.conf. #styling
click.echo()
click.echo(_(
'Found {}{}{} {}. '
'{}Please confirm the following changes:{}'
.format(
fg('magenta'), len(conflicts), attr('reset'),
Inflector(English).conditional_plural(len(conflicts), 'conflict'),
attr('underlined'), attr('reset'),
)
))
def echo_confirmation_edited(n_conflict, edited_fact, original):
click.echo()
# MAYBE:
# from dob_viewer.ptkui.re_confirm import confirm
# confirmed = confirm()
confirmed = click.confirm(
text=_('Conflict #{}\n-----------\n{}\nReally edit fact?').format(
n_conflict,
original.friendly_diff(edited_fact),
),
default=False,
abort=False,
# prompt_suffix=': ',
# show_default=True,
# err=False,
)
return confirmed
# ***
_must_confirm_fact_edits(conflicts, yes, dry)
# ***
def save_facts_maybe(controller, new_facts, conflicts, ignore_pks, dry):
""""""
def _save_facts_maybe(controller, new_facts, conflicts, ignore_pks, dry):
new_and_edited = []
if conflicts and dry:
echo_dry_run()
for edited_fact, original in conflicts:
if not dry:
new_and_edited += save_fact(controller, edited_fact, dry)
if 'stopped' in edited_fact.dirty_reasons:
# This'll happen on one-off dob-to/dob-at/etc., but it
# will not happen on dob-import, e.g., if dob-import
# closes ongoing fact, it'll be saved normally, and it
# will not be passed herein as part of conflicts.
echo_ongoing_completed(controller, edited_fact)
else:
click.echo(original.friendly_diff(edited_fact))
for fact in new_facts:
new_and_edited += save_fact(controller, fact, dry, ignore_pks=ignore_pks)
return new_and_edited
def echo_dry_run():
click.echo()
click.echo('{}Dry run! These facts will be edited{}:\n '.format(
attr('underlined'),
attr('reset'),
))
def save_fact(controller, fact, dry, ignore_pks=[]):
# (lb): SIMILAR: edits_manager.save_edited_fact, create.save_fact.
if fact.pk and fact.pk < 0:
fact.pk = None
if fact.pk is None and fact.deleted:
controller.client_logger.debug('{}: {}'.format(_('Dead fact'), fact.short))
return []
if not dry:
controller.client_logger.debug('{}: {}'.format(_('Save fact'), fact.short))
try:
new_fact = controller.facts.save(fact, ignore_pks=ignore_pks)
except Exception as err:
traceback.print_exc()
dob_in_user_exit(str(err))
else:
new_fact = fact
echo_fact(fact)
return [new_fact, ]
# ***
return _save_facts_maybe(controller, new_facts, conflicts, ignore_pks, dry)
# ***
def echo_ongoing_completed(controller, fact, cancelled=False):
""""""
def _echo_ongoing_completed():
colorful = controller.config['term.use_color']
leader = _('Completed: ') if not cancelled else _('Cancelled: ')
cut_width = width_avail(len(leader))
completed_msg = echo_fact(leader, colorful, cut_width)
controller.client_logger.debug(completed_msg)
def width_avail(leader_len):
term_width = click.get_terminal_size()[0]
width_avail = term_width - leader_len
return width_avail
def echo_fact(leader, colorful, cut_width):
completed_msg = (
leader
+ fact.friendly_str(
shellify=False,
description_sep=': ',
# FIXME: (lb): Implement localize.
# FIXME/2018-06-10: (lb): fact being saved as UTC
localize=True,
colorful=colorful,
cut_width_complete=cut_width,
show_elapsed=True,
)
)
click_echo(completed_msg)
return completed_msg
_echo_ongoing_completed()