Пример #1
0
def run(command=None, config_filepath=None, verbose=False, **params):
    """High-level API entry point.
    All 'params' are passed to 'Client.safely_run()'.
    'config_filepath' specifies the path to a custom config file (optional). If
    'verbose' is set, debug level log messages are printed to the terminal.

    This function can be used for scripting. Provide 'command' and 'params'
    according to what the command line interface accepts (consult help via
    `financeager [command] --help`), e.g. {"command": "add", "name":
    "champagne", "value": "99"}.

    :return: UNIX return code (zero for success, non-zero otherwise)
    """
    if verbose:
        make_log_stream_handler_verbose()

    exit_code = FAILURE

    if config_filepath is None and os.path.exists(financeager.CONFIG_FILEPATH):
        config_filepath = financeager.CONFIG_FILEPATH
    try:
        configuration = Configuration(filepath=config_filepath)
    except InvalidConfigError as e:
        logger.error("Invalid configuration: {}".format(e))
        return FAILURE

    date_format = configuration.get_option("FRONTEND", "date_format")
    try:
        _preprocess(params, date_format)
    except PreprocessingError as e:
        logger.error(e)
        return FAILURE

    service_name = configuration.get_option("SERVICE", "name")
    if service_name == "flask":
        init_logger("urllib3")

    client = Client(configuration=configuration,
                    out=Client.Out(logger.info, logger.error))
    success, store_offline = client.safely_run(command, **params)

    if success:
        exit_code = SUCCESS

        # When regular command was successfully executed, attempt to recover
        # offline backup
        try:
            if offline.recover(client):
                logger.info("Recovered offline backup.")
        except OfflineRecoveryError:
            logger.error("Offline backup recovery failed!")
            exit_code = FAILURE

    if store_offline and offline.add(command, **params):
        logger.info("Stored '{}' request in offline backup.".format(command))

    if service_name == "none":
        client.run("stop")

    return exit_code
Пример #2
0
    def __init__(self, *, configuration, sinks):
        """Set up proxy and urllib3 logger."""
        super().__init__(configuration=configuration, sinks=sinks)
        self.proxy = httprequests.Proxy(
            http_config=configuration.get_section("SERVICE:FLASK"))

        financeager.init_logger("urllib3")
Пример #3
0
def create_app(data_dir=None, config=None):
    """Create web app with RESTful API built from resources. The function is
    named such that the flask cli detects it as app factory method.
    The log file handler is set up very first.
    If 'data_dir' or the environment variable 'FINANCEAGER_FLASK_DATA_DIR' is
    given, a directory is created to store application data.
    An instance of 'server.Server' is created, passing 'data_dir'. If 'data_dir'
    is not given, the application data is stored in memory and will be lost when
    the app terminates.
    'config' is a dict of configuration variables that flask understands.
    """
    setup_log_file_handler()

    # Propagate flask and werkzeug log messages to financeager logs
    init_logger("flask.app")
    init_logger("werkzeug")

    app = Flask(__name__)
    app.config.update(config or {})
    if app.debug:
        make_log_stream_handler_verbose()

    data_dir = data_dir or os.environ.get("FINANCEAGER_FLASK_DATA_DIR")
    if data_dir is None:
        logger.warning("'data_dir' not given. Application data is stored in "
                       "memory and is lost when the flask app terminates. Set "
                       "the environment variable FINANCEAGER_FLASK_DATA_DIR "
                       "accordingly for persistent data storage.")
    else:
        os.makedirs(data_dir, exist_ok=True)

    logger.debug("Created flask app {} - {} mode".format(
        app.name, "debug" if app.debug else "production"))

    srv = server.Server(data_dir=data_dir)
    logger.debug(
        "Started financeager server with data dir '{}'".format(data_dir))

    api = Api(app)
    api.add_resource(resources.PocketsResource,
                     POCKETS_TAIL,
                     resource_class_args=(srv, ))
    api.add_resource(resources.CopyResource,
                     COPY_TAIL,
                     resource_class_args=(srv, ))
    api.add_resource(resources.PocketResource,
                     "{}/<pocket_name>".format(POCKETS_TAIL),
                     resource_class_args=(srv, ))
    api.add_resource(
        resources.EntryResource,
        "{}/<pocket_name>/<table_name>/<eid>".format(POCKETS_TAIL),
        resource_class_args=(srv, ))

    # Assign attribute such that e.g. test_cli can access Server methods
    app._server = srv

    return app
Пример #4
0
"""Command line interface of financeager application."""
from datetime import datetime
import argparse
import os
import sys

from financeager import offline, __version__, PERIOD_DATE_FORMAT,\
    init_logger, make_log_stream_handler_verbose, setup_log_file_handler
import financeager
from .communication import Client
from .config import Configuration
from .entries import CategoryEntry
from .exceptions import OfflineRecoveryError, InvalidConfigError,\
    PreprocessingError

logger = init_logger(__name__)

# Exit codes
SUCCESS = 0
FAILURE = 1


def main():
    """Main command line entry point of the application. The config and the log
    directory are created. A FileHandler is added to the package logger.
    All command line arguments and options are parsed and passed to 'run()'.
    """
    os.makedirs(financeager.DATA_DIR, exist_ok=True)

    # Adding the FileHandler here avoids cluttering the log during tests
    setup_log_file_handler()
Пример #5
0
def run(command=None, config_filepath=None, verbose=False, **cl_kwargs):
    """High-level API entry point, useful for scripts. Run 'command' passing
    'cl_kwargs' according to what the command line interface accepts (consult
    help via `financeager [command] --help`), e.g. {"command": "add", "name":
    "champagne", "value": "99"}. All kwargs are passed to 'communication.run()'.
    'config' specifies the path to a custom config file (optional). If 'verbose'
    is set, debug level log messages are printed to the terminal.

    :return: UNIX return code (zero for success, non-zero otherwise)
    """
    if verbose:
        make_log_stream_handler_verbose()

    exit_code = FAILURE

    if config_filepath is None and os.path.exists(financeager.CONFIG_FILEPATH):
        config_filepath = financeager.CONFIG_FILEPATH
    try:
        configuration = Configuration(filepath=config_filepath)
    except InvalidConfigError as e:
        logger.error("Invalid configuration: {}".format(e))
        return FAILURE

    backend_name = configuration.get_option("SERVICE", "name")
    communication_module = communication.module(backend_name)

    proxy_kwargs = {}
    if backend_name == "flask":
        init_logger("urllib3")
        proxy_kwargs["http_config"] = configuration.get_option("SERVICE:FLASK")
    else:  # 'none' is the only other option
        proxy_kwargs["data_dir"] = financeager.DATA_DIR

    # Indicate whether to store request offline, if failed
    store_offline = False

    proxy = communication_module.proxy(**proxy_kwargs)

    try:
        logger.info(
            communication.run(proxy,
                              command,
                              default_category=configuration.get_option(
                                  "FRONTEND", "default_category"),
                              date_format=configuration.get_option(
                                  "FRONTEND", "date_format"),
                              **cl_kwargs))
        if offline.recover(proxy):
            logger.info("Recovered offline backup.")
        exit_code = SUCCESS
    except OfflineRecoveryError:
        logger.error("Offline backup recovery failed!")
    except (PreprocessingError, InvalidRequest) as e:
        # Command is erroneous and hence not stored offline
        logger.error(e)
    except CommunicationError as e:
        logger.error(e)
        store_offline = True
    except Exception:
        logger.exception("Unexpected error")
        store_offline = True

    if store_offline and offline.add(command, **cl_kwargs):
        logger.info("Stored '{}' request in offline backup.".format(command))

    if backend_name == "none":
        communication.run(proxy, "stop")

    return exit_code