<commit_before># -*- coding: utf-8 -*-
"""Module for pull and push command."""
import abc
from base64 import b64decode
import six
from six.moves import configparser
from ..core.api import API
from ..core.commands import AbstractCommand
from ..core.models.terminal import clean_order
from .client.controllers import ApiController
from .client.cryptor import RNCryptor
from ..core.storage.strategies import RelatedGetStrategy, SyncSaveStrategy


@six.add_metaclass(abc.ABCMeta)
class CloudSynchronizationCommand(AbstractCommand):
    """Base class for pull and push commands."""

    def extend_parser(self, parser):
        """Add more arguments to parser."""
        parser.add_argument('-p', '--password', metavar='PASSWORD')
        return parser

    @abc.abstractmethod
    def process_sync(self, api_controller):
        """Do sync staff here."""
        pass

    def take_action(self, parsed_args):
        """Process CLI call."""
        encryption_salt = b64decode(self.config.get('User', 'salt'))
        hmac_salt = b64decode(self.config.get('User', 'hmac_salt'))
        password = parsed_args.password
        if password is None:
            password = self.prompt_password()
        self.validate_password(password)
        cryptor = RNCryptor()
        cryptor.password = password
        cryptor.encryption_salt = encryption_salt
        cryptor.hmac_salt = hmac_salt
        controller = ApiController(self.storage, self.config, cryptor)
        with self.storage:
            self.process_sync(controller)

    def validate_password(self, password):
        """Raise an error when password invalid."""
        username = self.config.get('User', 'username')
        API().login(username, password)


class PushCommand(CloudSynchronizationCommand):
    """push data to the Termius Cloud"""

    get_strategy = RelatedGetStrategy
    save_strategy = SyncSaveStrategy

    def process_sync(self, api_controller):
        """Push outdated local instances."""
        try:
            api_controller.put_setting()
            api_controller.post_bulk()
        except (configparser.NoSectionError, configparser.NoOptionError):
            self.log.error('Call pull at first.')
        else:
            self.log.info('Data delivered successfully')


class PullCommand(CloudSynchronizationCommand):
    """pull data from the Termius Cloud"""
<commit_msg>Fix push and pull for users with enabled 2fa<commit_after># -*- coding: utf-8 -*-
"""Module for pull and push command."""
import abc
from base64 import b64decode
import six
from six.moves import configparser

from ..core.exceptions import AuthyTokenIssue
from ..core.api import API
from ..core.commands import AbstractCommand
from ..core.models.terminal import clean_order
from .client.controllers import ApiController
from .client.cryptor import RNCryptor
from ..core.storage.strategies import RelatedGetStrategy, SyncSaveStrategy


@six.add_metaclass(abc.ABCMeta)
class CloudSynchronizationCommand(AbstractCommand):
    """Base class for pull and push commands."""

    def extend_parser(self, parser):
        """Add more arguments to parser."""
        parser.add_argument('-p', '--password', metavar='PASSWORD')
        return parser

    @abc.abstractmethod
    def process_sync(self, api_controller):
        """Do sync staff here."""
        pass

    # pylint: disable=no-self-use
    def prompt_authy_token(self):
        """Ask authy token prompt."""
        return six.moves.input('Authy token: ')

    def take_action(self, parsed_args):
        """Process CLI call."""
        encryption_salt = b64decode(self.config.get('User', 'salt'))
        hmac_salt = b64decode(self.config.get('User', 'hmac_salt'))
        password = parsed_args.password
        if password is None:
            password = self.prompt_password()
        self.validate_password(password)
        cryptor = RNCryptor()
        cryptor.password = password
        cryptor.encryption_salt = encryption_salt
        cryptor.hmac_salt = hmac_salt
        controller = ApiController(self.storage, self.config, cryptor)
        with self.storage:
            self.process_sync(controller)

    def validate_password(self, password):
        """Raise an error when password invalid."""
        username = self.config.get('User', 'username')
        api = API()
        try:
            api.login(username, password)
        except AuthyTokenIssue:
            authy_token = self.prompt_authy_token()
            api.login(username, password, authy_token=authy_token)


class PushCommand(CloudSynchronizationCommand):
    """push data to the Termius Cloud"""

    get_strategy = RelatedGetStrategy
    save_strategy = SyncSaveStrategy

    def process_sync(self, api_controller):
        """Push outdated local instances."""
        try:
            api_controller.put_setting()
            api_controller.post_bulk()
        except (configparser.NoSectionError, configparser.NoOptionError):
            self.log.error('Call pull at first.')
        else:
            self.log.info('Data delivered successfully')


class PullCommand(CloudSynchronizationCommand):
    """pull data from the Termius Cloud"""

{'fraction_non_alphanumeric': 0.10211609396233741, 'fraction_numerical': 0.0036886041545331006, 'mean_word_length': 3.088888888888889, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2980246', 'n_tokens_mistral': 1437, 'n_tokens_neox': 1383, 'n_words': 371}