Ejemplo n.º 1
0
def test_config(example_subarray):
    calibrator = CameraCalibrator(subarray=example_subarray)

    # test defaults
    assert len(calibrator.image_extractors) == 1
    assert isinstance(calibrator.image_extractors["NeighborPeakWindowSum"],
                      NeighborPeakWindowSum)
    assert isinstance(calibrator.data_volume_reducer, NullDataVolumeReducer)

    # test we can configure different extractors with different options
    # per telescope.
    config = Config({
        "CameraCalibrator": {
            "image_extractor_type": [
                ("type", "*", "GlobalPeakWindowSum"),
                ("id", 1, "LocalPeakWindowSum"),
            ],
            "LocalPeakWindowSum": {
                "window_width": 15
            },
            "GlobalPeakWindowSum": {
                "window_width": [("type", "*", 10), ("id", 2, 8)]
            },
            "data_volume_reducer_type":
            "TailCutsDataVolumeReducer",
            "TailCutsDataVolumeReducer": {
                "TailcutsImageCleaner": {
                    "picture_threshold_pe": 20.0
                }
            },
        }
    })

    calibrator = CameraCalibrator(example_subarray, config=config)
    assert "GlobalPeakWindowSum" in calibrator.image_extractors
    assert "LocalPeakWindowSum" in calibrator.image_extractors
    assert isinstance(calibrator.image_extractors["LocalPeakWindowSum"],
                      LocalPeakWindowSum)
    assert isinstance(calibrator.image_extractors["GlobalPeakWindowSum"],
                      GlobalPeakWindowSum)

    extractor_1 = calibrator.image_extractors[
        calibrator.image_extractor_type.tel[1]]
    assert isinstance(extractor_1, LocalPeakWindowSum)
    assert extractor_1.window_width.tel[1] == 15

    extractor_2 = calibrator.image_extractors[
        calibrator.image_extractor_type.tel[2]]
    assert isinstance(extractor_2, GlobalPeakWindowSum)
    assert extractor_2.window_width.tel[2] == 8

    extractor_3 = calibrator.image_extractors[
        calibrator.image_extractor_type.tel[3]]
    assert isinstance(extractor_3, GlobalPeakWindowSum)
    assert extractor_3.window_width.tel[3] == 10

    assert isinstance(calibrator.data_volume_reducer,
                      TailCutsDataVolumeReducer)
    assert calibrator.data_volume_reducer.cleaner.picture_threshold_pe.tel[
        None] == 20
Ejemplo n.º 2
0
def test_config(example_subarray):
    calibrator = CameraCalibrator(subarray=example_subarray)

    # test defaults
    assert isinstance(calibrator.image_extractor, NeighborPeakWindowSum)
    assert isinstance(calibrator.data_volume_reducer, NullDataVolumeReducer)

    config = Config({
        "CameraCalibrator": {
            "image_extractor_type": "LocalPeakWindowSum",
            "LocalPeakWindowSum": {
                "window_width": 15
            },
            "data_volume_reducer_type": "TailCutsDataVolumeReducer",
            "TailCutsDataVolumeReducer": {
                "TailcutsImageCleaner": {
                    "picture_threshold_pe": 20.0
                }
            },
        }
    })

    calibrator = CameraCalibrator(example_subarray, config=config)
    assert isinstance(calibrator.image_extractor, LocalPeakWindowSum)
    assert calibrator.image_extractor.window_width.tel[None] == 15

    assert isinstance(calibrator.data_volume_reducer,
                      TailCutsDataVolumeReducer)
    assert calibrator.data_volume_reducer.cleaner.picture_threshold_pe.tel[
        None] == 20
Ejemplo n.º 3
0
 def __init__(self, **kwargs):
     try:
         cfg = PyFileConfigLoader(os.path.expanduser(
             self.config_file)).load_config()
     except ConfigFileNotFound:
         cfg = Config()
     super().__init__(config=cfg, **kwargs)
Ejemplo n.º 4
0
def test_config():
    window_shift = 3
    window_width = 9
    config = Config({
        "LocalPeakWindowSum": {
            "window_shift": window_shift,
            "window_width": window_width,
        }
    })
    calibrator = CameraCalibrator(
        image_extractor=LocalPeakWindowSum(config=config), config=config)
    assert calibrator.image_extractor.window_shift.tel[None] == window_shift
    assert calibrator.image_extractor.window_width.tel[None] == window_width
Ejemplo n.º 5
0
def test_config():
    window_shift = 3
    window_width = 9
    config = Config({"LocalPeakWindowSum": {
        "window_shift": window_shift,
        "window_width": window_width,
    }})
    calibrator = CameraCalibrator(
        extractor_name='LocalPeakWindowSum',
        config=config
    )
    assert calibrator.dl1.extractor.window_shift == window_shift
    assert calibrator.dl1.extractor.window_width == window_width
Ejemplo n.º 6
0
    def _init_all_preferences(self):

        # Get preferences from the config file
        # ---------------------------------------------------------------------

        if not self.config:
            self.config = Config()

        configfiles = []
        if self.config_file_name:
            config_file = self.config_dir / self.config_file_name
            configfiles.append(config_file)

            lis = self.config_dir.iterdir()
            for fil in lis:
                if fil.suffix == ".json":
                    jsonname = self.config_dir / fil
                    if self.reset_config or fil == "PlotPreferences.json":
                        # remove the user json file to reset to defaults
                        jsonname.unlink()
                    else:
                        configfiles.append(jsonname)

            for cfgname in configfiles:
                self.load_config_file(cfgname)
                if cfgname not in self._loaded_config_files:
                    self._loaded_config_files.append(cfgname)

        # Eventually write the default config file
        # --------------------------------------
        self._make_default_config_file()

        self.datadir = (
            DataDir()
        )  # config=self.config)  -- passing args deprecated in traitlets 4.2
        self.preferences = GeneralPreferences(config=self.config, parent=self)
        self.plot_preferences = PlotPreferences(config=self.config,
                                                parent=self)
Ejemplo n.º 7
0
def test_config(example_subarray):
    window_shift = 3
    window_width = 9
    config = Config(
        {
            "LocalPeakWindowSum": {
                "window_shift": window_shift,
                "window_width": window_width,
            }
        }
    )
    calibrator = CameraCalibrator(
        subarray=example_subarray,
        image_extractor=LocalPeakWindowSum(subarray=example_subarray, config=config),
        config=config,
    )
    assert calibrator.image_extractor.window_shift.tel[None] == window_shift
    assert calibrator.image_extractor.window_width.tel[None] == window_width
Ejemplo n.º 8
0
class SpectroChemPy(Application):
    """
    This class SpectroChemPy is the main class, containing most of the setup,
    configuration and more.
    """

    icon = Unicode('scpy.png')
    "Icon for the application"

    running = Bool(False)
    "Running status of the |scpy| application"

    name = Unicode('SpectroChemPy')
    "Running name of the application"

    description = Unicode(
        'SpectroChemPy is a framework for processing, analysing and modelling Spectroscopic data for '
        'Chemistry with Python.')
    "Short description of the |scpy| application"

    long_description = Unicode()
    "Long description of the |scpy| application"

    @default('long_description')
    def _get_long_description(self):
        desc = """
Welcome to <strong>SpectroChemPy</strong> Application<br><br>
<p><strong>SpectroChemPy</strong> is a framework for processing, analysing and modelling
 <strong>Spectro</>scopic data for <strong>Chem</strong>istry with <strong>Py</strong>thon.
 It is a cross platform software, running on Linux, Windows or OS X.</p><br><br>
<strong>Version:</strong> {version}<br>
<strong>Authors:</strong> {authors}<br>
<strong>License:</strong> {license}<br>
<div class='warning'> SpectroChemPy is still experimental and under active development. Its current design and
 functionalities are subject to major changes, reorganizations, bugs and crashes!!!. Please report any issues
to the <a url='https://redmine.spectrochempy.fr/projects/spectrochempy/issues'>Issue Tracker<a>
</div><br><br>
When using <strong>SpectroChemPy</strong> for your own work, you are kindly requested to cite it this way:
<pre>Arnaud Travert & Christian Fernandez, SpectroChemPy, a framework for processing, analysing and modelling of
Spectroscopic data for Chemistry with Python https://www.spectrochempy.fr, (version {version})
Laboratoire Catalyse and Spectrochemistry, ENSICAEN/University of Caen/CNRS, 2021
</pre></p>""".format(version=__release__,
                     authors=__author__,
                     license=__license__)

        return desc

    # ------------------------------------------------------------------------------------------------------------------
    # Configuration parameters
    # ------------------------------------------------------------------------------------------------------------------

    # Config file setting
    # ------------------------------------------------------------------------------------------------------------------
    _loaded_config_files = List()

    reset_config = Bool(
        False,
        help='Should we restore a default configuration ?').tag(config=True)
    """Flag: True if one wants to reset settings to the original config defaults"""

    config_file_name = Unicode(None,
                               help="Configuration file name").tag(config=True)
    """Configuration file name"""

    @default('config_file_name')
    def _get_config_file_name_default(self):
        return str(self.name).lower() + '_cfg'

    config_dir = Instance(
        Path, help="Set the configuration directory location").tag(config=True)
    """Configuration directory"""

    @default('config_dir')
    def _get_config_dir_default(self):
        return self.get_config_dir()

    config_manager = Instance(BaseJSONConfigManager)

    @default('config_manager')
    def _get_default_config_manager(self):
        return BaseJSONConfigManager(config_dir=str(self.config_dir))

    log_format = Unicode(
        "%(highlevel)s %(message)s",
        help="The Logging format template",
    ).tag(config=True)

    debug = Bool(True,
                 help='Set DEBUG mode, with full outputs').tag(config=True)
    """Flag to set debugging mode"""

    info = Bool(False, help='Set INFO mode, with msg outputs').tag(config=True)
    """Flag to set info mode"""

    quiet = Bool(False,
                 help='Set Quiet mode, with minimal outputs').tag(config=True)
    """Flag to set in fully quite mode (even no warnings)"""

    nodisplay = Bool(
        False,
        help='Set NO DISPLAY mode, i.e., no graphics outputs').tag(config=True)
    """Flag to set in NO DISPLAY mode """

    # last_project = Unicode('', help='Last used project').tag(config=True, type='project')
    # """Last used project"""
    #
    # @observe('last_project')
    # def _last_project_changed(self, change):
    #     if change.name in self.traits(config=True):
    #         self.config_manager.update(self.config_file_name, {self.__class__.__name__: {change.name: change.new, }})

    show_config = Bool(help="Dump configuration to stdout at startup").tag(
        config=True)

    @observe('show_config')
    def _show_config_changed(self, change):
        if change.new:
            self._save_start = self.start
            self.start = self.start_show_config

    show_config_json = Bool(help="Dump configuration to stdout (as JSON)").tag(
        config=True)

    @observe('show_config_json')
    def _show_config_json_changed(self, change):
        self.show_config = change.new

    test = Bool(False, help='test flag').tag(config=True)
    """Flag to set the application in testing mode"""

    port = Integer(7000, help='Dash server port').tag(config=True)
    """Dash server port"""

    # Command line interface
    # ------------------------------------------------------------------------------------------------------------------

    aliases = dict(
        test='SpectroChemPy.test',
        project='SpectroChemPy.last_project',
        f='SpectroChemPy.startup_filename',
        port='SpectroChemPy.port',
    )

    flags = dict(
        debug=({
            'SpectroChemPy': {
                'log_level': DEBUG
            }
        }, "Set log_level to DEBUG - most verbose mode"),
        info=({
            'SpectroChemPy': {
                'log_level': INFO
            }
        }, "Set log_level to INFO - verbose mode"),
        quiet=({
            'SpectroChemPy': {
                'log_level': ERROR
            }
        }, "Set log_level to ERROR - no verbosity at all"),
        nodisplay=({
            'SpectroChemPy': {
                'nodisplay': True
            }
        }, "Set NO DISPLAY mode to true - no graphics at all"),
        reset_config=({
            'SpectroChemPy': {
                'reset_config': True
            }
        }, "Reset config to default"),
        show_config=({
            'SpectroChemPy': {
                'show_config': True,
            }
        }, "Show the application's configuration (human-readable "
                     "format)"),
        show_config_json=({
            'SpectroChemPy': {
                'show_config_json': True,
            }
        }, "Show the application's configuration (json "
                          "format)"),
    )

    classes = List([
        GeneralPreferences,
        PlotPreferences,
        DataDir,
    ])

    # ------------------------------------------------------------------------------------------------------------------
    # Initialisation of the application
    # ------------------------------------------------------------------------------------------------------------------

    def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)

        self.logs = self.log  # we change the noame in order to avoid latter conflict with numpy.log

        self.initialize()

    def initialize(self, argv=None):
        """
        Initialisation function for the API applications

        Parameters
        ----------
        argv :  List, [optional].
            List of configuration parameters.
        """

        # parse the argv
        # --------------------------------------------------------------------

        # if we are running this under ipython and jupyter notebooks
        # deactivate potential command line arguments
        # (such that those from jupyter which cause problems here)

        IN_IPYTHON = False
        if InteractiveShell.initialized():
            IN_IPYTHON = True

        if not IN_IPYTHON:
            # remove argument not known by spectrochempy
            if 'make.py' in sys.argv[0] or 'pytest' in sys.argv[
                    0]:  # building docs
                options = []
                for item in sys.argv[:]:
                    for k in list(self.flags.keys()):
                        if item.startswith("--" +
                                           k) or k in ['--help', '--help-all']:
                            options.append(item)
                        continue
                    for k in list(self.aliases.keys()):
                        if item.startswith("-" + k) or k in [
                                'h',
                        ]:
                            options.append(item)
                self.parse_command_line(options)
            else:
                self.parse_command_line(sys.argv)

        # Get preferences from the config file and init everything
        # ---------------------------------------------------------------------

        self._init_all_preferences()

        # we catch warnings and error for a ligther display to the end-user.
        # except if we are in debugging mode

        # warning handler
        # --------------------------------------------------------------------
        def send_warnings_to_log(message, category):
            self.logs.warning(f'{category.__name__} - {message}')
            return

        warnings.showwarning = send_warnings_to_log

        # exception handler
        # --------------------------------------------------------------------

        if IN_IPYTHON:

            ip = get_ipython()

            def _custom_exc(shell, etype, evalue, tb, tb_offset=None):

                if self.log_level == logging.DEBUG:
                    shell.showtraceback((etype, evalue, tb),
                                        tb_offset=tb_offset)
                else:
                    self.logs.error(f"{etype.__name__}: {evalue}")

            ip.set_custom_exc((Exception, ), _custom_exc)

            # load our custom magic extensions
            # --------------------------------------------------------------------
            if ip is not None:
                ip.register_magics(SpectroChemPyMagics)

    def _init_all_preferences(self):

        # Get preferences from the config file
        # ---------------------------------------------------------------------

        if not self.config:
            self.config = Config()

        configfiles = []
        if self.config_file_name:
            config_file = self.config_dir / self.config_file_name
            configfiles.append(config_file)

            lis = self.config_dir.iterdir()
            for f in lis:
                if f.suffix == '.json':
                    jsonname = self.config_dir / f
                    if self.reset_config or f == 'PlotPreferences.json':
                        # remove the user json file to reset to defaults
                        jsonname.unlink()
                    else:
                        configfiles.append(jsonname)

            for cfgname in configfiles:
                self.load_config_file(cfgname)
                if cfgname not in self._loaded_config_files:
                    self._loaded_config_files.append(cfgname)

        # Eventually write the default config file
        # --------------------------------------
        self._make_default_config_file()

        self.datadir = DataDir(config=self.config)
        self.preferences = GeneralPreferences(config=self.config, parent=self)
        self.plot_preferences = PlotPreferences(config=self.config,
                                                parent=self)

    # ..................................................................................................................
    def get_config_dir(self):
        """
        Determines the SpectroChemPy configuration directory name and
        creates the directory if it doesn't exist.

        This directory is typically ``$HOME/.spectrochempy/config``,
        but if the
        SCP_CONFIG_HOME environment variable is set and the
        ``$SCP_CONFIG_HOME`` directory exists, it will be that
        directory.

        If neither exists, the former will be created.

        Returns
        -------
        config_dir : str
            The absolute path to the configuration directory.
        """

        # first look for SCP_CONFIG_HOME
        scp = environ.get('SCP_CONFIG_HOME')

        if scp is not None and Path(scp).exists():
            return Path(scp)

        config = _find_or_create_spectrochempy_dir() / 'config'
        if not config.exists():
            config.mkdir(exist_ok=True)

        return config

    def start_show_config(self, **kwargs):
        """start function used when show_config is True"""
        config = self.config.copy()
        # exclude show_config flags from displayed config
        for cls in self.__class__.mro():
            if cls.__name__ in config:
                cls_config = config[cls.__name__]
                cls_config.pop('show_config', None)
                cls_config.pop('show_config_json', None)

        if self.show_config_json:
            json.dump(config,
                      sys.stdout,
                      indent=1,
                      sort_keys=True,
                      default=repr)
            # add trailing newlines
            sys.stdout.write('\n')
            print()
            return self._start()

        if self._loaded_config_files:
            print("Loaded config files:")
            for f in self._loaded_config_files:
                print('  ' + f)
            print()

        for classname in sorted(config):
            class_config = config[classname]
            if not class_config:
                continue
            print(classname)
            pformat_kwargs = dict(indent=4)
            if sys.version_info >= (3, 4):
                # use compact pretty-print on Pythons that support it
                pformat_kwargs['compact'] = True
            for traitname in sorted(class_config):
                value = class_config[traitname]
                print('  .{} = {}'.format(
                    traitname,
                    pprint.pformat(value, **pformat_kwargs),
                ))
        print()

        # now run the actual start function
        return self._start()

    def reset_preferences(self):
        """
        Reset all preferences to default

        """
        self.reset_config = True
        self._init_all_preferences()
        self.reset_config = False

    # ------------------------------------------------------------------------------------------------------------------
    # start the application
    # ------------------------------------------------------------------------------------------------------------------

    def start(self):
        """
        Start the |scpy| API

        All configuration must have been done before calling this function
        """

        # print(f'{sys.argv}')

        return self._start()

    # ------------------------------------------------------------------------------------------------------------------
    # Private methods
    # ------------------------------------------------------------------------------------------------------------------

    def _start(self):

        if self.running:
            # API already started. Nothing done!
            return

        if self.preferences.show_info_on_loading:
            info_string = "SpectroChemPy's API - v.{}\n" \
                          "© Copyright {}".format(__version__, __copyright__)
            ip = get_ipython()
            if ip is not None and "TerminalInteractiveShell" not in str(ip):
                display_info_string(message=info_string.strip())

            else:
                if "/bin/scpy" not in sys.argv[
                        0]:  # deactivate for console scripts
                    print(info_string.strip())

        # force update of rcParams
        for rckey in mpl.rcParams.keys():
            key = rckey.replace('_', '__').replace('.',
                                                   '_').replace('-', '___')
            try:
                mpl.rcParams[rckey] = getattr(self.plot_preferences, key)
            except ValueError:
                mpl.rcParams[rckey] = getattr(self.plot_preferences,
                                              key).replace('\'', '')
            except AttributeError:
                # print(f'{e} -> you may want to add it to PlotPreferences.py')
                pass

        self.plot_preferences.set_latex_font(self.plot_preferences.font_family)

        self.running = True

        # display needs for update
        # time.sleep(1)
        fi = Path.home() / ".scpy_update"
        if fi.exists():
            try:
                msg = fi.read_text()
                self.logs.warning(msg)
            except Exception:
                pass

        return True

    # ..................................................................................................................
    def _make_default_config_file(self):
        """auto generate default config file."""

        fname = self.config_dir / self.config_file_name
        fname = fname.with_suffix('.py')

        if not fname.exists() or self.reset_config:
            s = self.generate_config_file()
            self.logs.info("Generating default config file: %r" % fname)
            with open(fname, 'w') as f:
                f.write(s)

    # ------------------------------------------------------------------------------------------------------------------
    # Events from Application
    # ------------------------------------------------------------------------------------------------------------------

    @observe('log_level')
    def _log_level_changed(self, change):

        self.log_format = '%(message)s'
        if change.new == DEBUG:
            self.log_format = '[%(filename)s-%(funcName)s %(levelname)s] %(' \
                              'message)s'
        self.logs._cache = {}
        self.logs.level = self.log_level
        for handler in self.logs.handlers:
            handler.level = self.log_level
        self.logs.info("changed default log_level to {}".format(
            logging.getLevelName(change.new)))
 def _config_default(self):
     # load application config by default
     if JupyterHub.initialized():
         return JupyterHub.instance().config
     else:
         return Config()
Ejemplo n.º 10
0
class SpectroChemPy(Application):
    """
    This class SpectroChemPy is the main class, containing most of the setup,
    configuration and more.
    """

    icon = Unicode("scpy.png")
    "Icon for the application"

    running = Bool(False)
    "Running status of the |scpy| application"

    name = Unicode("SpectroChemPy")
    "Running name of the application"

    description = Unicode(
        "SpectroChemPy is a framework for processing, analysing and modelling Spectroscopic data for "
        "Chemistry with Python.")
    "Short description of the |scpy| application"

    long_description = Unicode()
    "Long description of the |scpy| application"

    @default("long_description")
    def _get_long_description(self):
        desc = f"""
<p><strong>SpectroChemPy</strong> is a framework for processing, analysing and modelling
 <strong>Spectro</>scopic data for <strong>Chem</strong>istry with <strong>Py</strong>thon.
 It is a cross platform software, running on Linux, Windows or OS X.</p><br><br>
<strong>Version:</strong> {__release__}<br>
<strong>Authors:</strong> {__author__}<br>
<strong>License:</strong> {__license__}<br>
<div class='warning'> SpectroChemPy is still experimental and under active development. Its current design and
 functionalities are subject to major changes, reorganizations, bugs and crashes!!!. Please report any issues
to the <a url='https://github.com/spectrochempy/spectrochempy/issues'>Issue Tracker<a>
</div><br><br>
When using <strong>SpectroChemPy</strong> for your own work,
you are kindly requested to cite it this way: <pre>{__cite__}</pre></p>.
"""

        return desc

    # ------------------------------------------------------------------------
    # Configuration parameters
    # ------------------------------------------------------------------------

    # Config file setting
    # ------------------------------------------------------------------------
    _loaded_config_files = List()

    reset_config = Bool(
        False,
        help="Should we restore a default configuration ?").tag(config=True)
    """Flag: True if one wants to reset settings to the original config defaults."""

    config_file_name = Unicode(None,
                               help="Configuration file name").tag(config=True)
    """Configuration file name."""

    @default("config_file_name")
    def _get_config_file_name_default(self):
        return str(self.name).lower() + "_cfg"

    config_dir = Instance(
        Path, help="Set the configuration directory location").tag(config=True)
    """Configuration directory."""

    @default("config_dir")
    def _get_config_dir_default(self):
        return self.get_config_dir()

    config_manager = Instance(BaseJSONConfigManager)

    @default("config_manager")
    def _get_default_config_manager(self):
        return BaseJSONConfigManager(config_dir=str(self.config_dir))

    log_format = Unicode(
        "%(highlevel)s %(message)s",
        help="The Logging format template",
    ).tag(config=True)

    debug = Bool(True,
                 help="Set DEBUG mode, with full outputs").tag(config=True)
    """Flag to set debugging mode."""

    info = Bool(False, help="Set INFO mode, with msg outputs").tag(config=True)
    """Flag to set info mode."""

    quiet = Bool(False,
                 help="Set Quiet mode, with minimal outputs").tag(config=True)
    """Flag to set in fully quite mode (even no warnings)."""

    nodisplay = Bool(
        False,
        help="Set NO DISPLAY mode, i.e., no graphics outputs").tag(config=True)
    """Flag to set in NO DISPLAY mode."""

    # last_project = Unicode('', help='Last used project').tag(config=True, type='project')
    # """Last used project"""
    #
    # @observe('last_project')
    # def _last_project_changed(self, change):
    #     if change.name in self.traits(config=True):
    #         self.config_manager.update(self.config_file_name, {self.__class__.__name__: {change.name: change.new, }})

    show_config = Bool(help="Dump configuration to stdout at startup").tag(
        config=True)

    @observe("show_config")
    def _show_config_changed(self, change):
        if change.new:
            self._save_start = self.start
            self.start = self.start_show_config

    show_config_json = Bool(help="Dump configuration to stdout (as JSON)").tag(
        config=True)

    @observe("show_config_json")
    def _show_config_json_changed(self, change):
        self.show_config = change.new

    test = Bool(False, help="test flag").tag(config=True)
    """Flag to set the application in testing mode."""

    port = Integer(7000, help="Dash server port").tag(config=True)
    """Dash server port."""

    # Command line interface
    # ------------------------------------------------------------------------

    aliases = dict(
        test="SpectroChemPy.test",
        project="SpectroChemPy.last_project",
        f="SpectroChemPy.startup_filename",
        port="SpectroChemPy.port",
    )

    flags = dict(
        debug=(
            {
                "SpectroChemPy": {
                    "log_level": DEBUG
                }
            },
            "Set log_level to DEBUG - most verbose mode.",
        ),
        info=(
            {
                "SpectroChemPy": {
                    "log_level": INFO
                }
            },
            "Set log_level to INFO - verbose mode.",
        ),
        quiet=(
            {
                "SpectroChemPy": {
                    "log_level": ERROR
                }
            },
            "Set log_level to ERROR - no verbosity at all.",
        ),
        nodisplay=(
            {
                "SpectroChemPy": {
                    "nodisplay": True
                }
            },
            "Set NO DISPLAY mode to true - no graphics at all",
        ),
        reset_config=(
            {
                "SpectroChemPy": {
                    "reset_config": True
                }
            },
            "Reset config to default",
        ),
        show_config=(
            {
                "SpectroChemPy": {
                    "show_config": True,
                }
            },
            "Show the application's configuration (human-readable format).",
        ),
        show_config_json=(
            {
                "SpectroChemPy": {
                    "show_config_json": True,
                }
            },
            "Show the application's configuration (json format).",
        ),
    )

    classes = List([
        GeneralPreferences,
        PlotPreferences,
        DataDir,
    ])

    # ------------------------------------------------------------------------
    # Initialisation of the application
    # ------------------------------------------------------------------------

    def __init__(self, **kwargs):

        super().__init__(**kwargs)

        self.logs = (
            self.log
        )  # we change the no name in order to avoid latter conflict with numpy.log

        self.logs.setLevel(0)  # reset to NOTSET

        # Set a log filehandler
        logdir = self.get_config_dir().parent / "logs"
        logdir.mkdir(exist_ok=True)
        rh = RotatingFileHandler(str(logdir / "spectrochempy.log"),
                                 maxBytes=32 * 1024,
                                 backupCount=5)
        rh.setLevel(INFO)
        rh.setFormatter(logging.Formatter("%(message)s"))
        self.logs.addHandler(rh)

        self.log.info("started")

        self.initialize()

    def initialize(self, argv=None):
        """
        Initialisation function for the API applications.

        Parameters
        ----------
        argv :  List, [optional].
            List of configuration parameters.
        """

        # parse the argv
        # --------------------------------------------------------------------

        # if we are running this under ipython and jupyter notebooks
        # deactivate potential command line arguments
        # (such that those from jupyter which cause problems here)

        in_python = False
        if InteractiveShell.initialized():
            in_python = True

        if not in_python:
            # remove argument not known by spectrochempy
            if ("make.py" in sys.argv[0] or "pytest" in sys.argv[0]
                    or "validate_docstrings" in sys.argv[0]):  # building docs
                options = []
                for item in sys.argv[:]:
                    for k in list(self.flags.keys()):
                        if item.startswith("--" +
                                           k) or k in ["--help", "--help-all"]:
                            options.append(item)
                        continue
                    for k in list(self.aliases.keys()):
                        if item.startswith("-" + k) or k in [
                                "h",
                        ]:
                            options.append(item)
                self.parse_command_line(options)
            else:  # pragma: no cover
                self.parse_command_line(sys.argv)

        # Get preferences from the config file and init everything
        # ---------------------------------------------------------------------

        self._init_all_preferences()

        # we catch warnings and error for a lighter display to the end-user.
        # except if we are in debugging mode

        # warning handler
        # --------------------------------------------------------------------
        def send_warnings_to_log(message, category):
            self.logs.warning(f"{category.__name__} - {message}")

        warnings.showwarning = send_warnings_to_log

        # exception handler
        # --------------------------------------------------------------------

        if in_python:  # pragma: no cover

            ipy = get_ipython()

            def _custom_exc(shell, etype, evalue, tb, tb_offset=None):

                if self.log_level == logging.DEBUG:
                    shell.showtraceback((etype, evalue, tb),
                                        tb_offset=tb_offset)
                else:
                    self.logs.error(f"{etype.__name__}: {evalue}")

            ipy.set_custom_exc((Exception, ), _custom_exc)

            # load our custom magic extensions
            # --------------------------------------------------------------------
            if ipy is not None:
                ipy.register_magics(SpectroChemPyMagics)

    def _init_all_preferences(self):

        # Get preferences from the config file
        # ---------------------------------------------------------------------

        if not self.config:
            self.config = Config()

        configfiles = []
        if self.config_file_name:
            config_file = self.config_dir / self.config_file_name
            configfiles.append(config_file)

            lis = self.config_dir.iterdir()
            for fil in lis:
                if fil.suffix == ".json":
                    jsonname = self.config_dir / fil
                    if self.reset_config or fil == "PlotPreferences.json":
                        # remove the user json file to reset to defaults
                        jsonname.unlink()
                    else:
                        configfiles.append(jsonname)

            for cfgname in configfiles:
                self.load_config_file(cfgname)
                if cfgname not in self._loaded_config_files:
                    self._loaded_config_files.append(cfgname)

        # Eventually write the default config file
        # --------------------------------------
        self._make_default_config_file()

        self.datadir = (
            DataDir()
        )  # config=self.config)  -- passing args deprecated in traitlets 4.2
        self.preferences = GeneralPreferences(config=self.config, parent=self)
        self.plot_preferences = PlotPreferences(config=self.config,
                                                parent=self)

    # ..........................................................................
    @staticmethod
    def get_config_dir():
        """
        Determines the SpectroChemPy configuration directory name and
        creates the directory if it doesn't exist.

        This directory is typically ``$HOME/.spectrochempy/config``,
        but if the
        SCP_CONFIG_HOME environment variable is set and the
        ``$SCP_CONFIG_HOME`` directory exists, it will be that
        directory.

        If neither exists, the former will be created.

        Returns
        -------
        config_dir : str
            The absolute path to the configuration directory.
        """

        # first look for SCP_CONFIG_HOME
        scp = environ.get("SCP_CONFIG_HOME")

        if scp is not None and Path(scp).exists():
            return Path(scp)

        config = _find_or_create_spectrochempy_dir() / "config"
        if not config.exists():
            config.mkdir(exist_ok=True)

        return config

    def start_show_config(self):
        """
        Start function used when show_config is True.
        """
        config = self.config.copy()
        # exclude show_config flags from displayed config
        for cls in self.__class__.mro():
            if cls.__name__ in config:
                cls_config = config[cls.__name__]
                cls_config.pop("show_config", None)
                cls_config.pop("show_config_json", None)

        if self.show_config_json:
            json.dump(config,
                      sys.stdout,
                      indent=1,
                      sort_keys=True,
                      default=repr)
            # add trailing newlines
            sys.stdout.write("\n")
            return self._start()

        if self._loaded_config_files:
            print("Loaded config files:")
            for fil in self._loaded_config_files:
                print(f"  {fil}")

        for classname in sorted(config):
            class_config = config[classname]
            if not class_config:
                continue
            print(classname)
            pformat_kwargs = dict(indent=4)
            if sys.version_info >= (3, 4):
                # use compact pretty-print on Pythons that support it
                pformat_kwargs["compact"] = True
            for traitname in sorted(class_config):
                value = class_config[traitname]
                print(
                    f"  {traitname} = {pprint.pformat(value, **pformat_kwargs)}"
                )

        # now run the actual start function
        return self._start()

    def reset_preferences(self):
        """
        Reset all preferences to default.
        """
        self.reset_config = True
        self._init_all_preferences()
        self.reset_config = False

    # ------------------------------------------------------------------------
    # start the application
    # ------------------------------------------------------------------------

    def start(self):
        """
        Start the |scpy| API.

        All configuration must have been done before calling this function.
        """

        # print(f'{sys.argv}')

        return self._start()

    # ------------------------------------------------------------------------
    # Private methods
    # ------------------------------------------------------------------------

    def _start(self):

        if self.running:
            # API already started. Nothing done!
            return True

        if self.preferences.show_info_on_loading:
            info_string = (
                f"SpectroChemPy's API - v.{__version__}\n© Copyright {__copyright__}"
            )
            ipy = get_ipython()
            if ipy is not None and "TerminalInteractiveShell" not in str(ipy):
                display_info_string(message=info_string.strip())

            else:
                if "/bin/scpy" not in sys.argv[
                        0]:  # deactivate for console scripts
                    print(info_string.strip())

        # force update of rcParams
        for rckey in mpl.rcParams.keys():
            key = rckey.replace("_", "__").replace(".",
                                                   "_").replace("-", "___")
            try:
                mpl.rcParams[rckey] = getattr(self.plot_preferences, key)
            except ValueError:
                mpl.rcParams[rckey] = getattr(self.plot_preferences,
                                              key).replace("'", "")
            except AttributeError:
                # print(f'{e} -> you may want to add it to PlotPreferences.py')
                pass

        self.plot_preferences.set_latex_font(self.plot_preferences.font_family)

        self.running = True

        # display needs for update
        msg = _display_needs_update_message()
        if msg:
            self.log.warning(msg)

        self.logs.info("\n\nAPI loaded - application is ready")
        return True

    # ..........................................................................
    def _make_default_config_file(self):
        """auto generate default config file."""

        fname = self.config_dir / self.config_file_name
        fname = fname.with_suffix(".py")

        if not fname.exists() or self.reset_config:
            sfil = self.generate_config_file()
            self.logs.info(f"Generating default config file: {fname}")
            with open(fname, "w") as fil:
                fil.write(sfil)

    # ------------------------------------------------------------------------
    # Events from Application
    # ------------------------------------------------------------------------

    @observe("log_level")
    def _log_level_changed(self, change):

        # log file
        if change.new == DEBUG:
            self.logs.handlers[0].setLevel(INFO)  # no debug in the stdout
            self.logs.handlers[1].setLevel(DEBUG)
        else:
            self.logs.handlers[0].setLevel(change.new)
            self.logs.handlers[1].setLevel(change.new)

        # root
        self.logs.setLevel(DEBUG)  # reset to DEBUG
        self.logs.info(
            f"changed default log_level to {logging.getLevelName(change.new)}")