Source code for asreview.settings

# Copyright 2019-2022 The ASReview Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import os
from configparser import ConfigParser

from asreview.config import DEFAULT_N_INSTANCES
from asreview.models.balance import get_balance_model
from asreview.models.classifiers import get_classifier
from asreview.models.feature_extraction import get_feature_model
from asreview.models.query import get_query_model
from asreview.types import type_n_queries
from asreview.utils import pretty_format

SETTINGS_TYPE_DICT = {
    "model": str,
    "query_strategy": str,
    "balance_strategy": str,
    "feature_extraction": str,
    "n_instances": int,
    "stop_if": type_n_queries,
    "n_prior_included": int,
    "n_prior_excluded": int,
    "mode": str,
    "model_param": dict,
    "query_param": dict,
    "feature_param": dict,
    "balance_param": dict,
}


def _map_settings_type(name, value):

    if value is None:
        return None

    try:
        return SETTINGS_TYPE_DICT[name](value)
    except TypeError:
        raise TypeError(
            f"Can't convert setting '{name}' to {SETTINGS_TYPE_DICT[name]}"
        )


def _convert_types(par_defaults, param):
    """Convert strings from the config file to the appropriate type."""
    for par in param:
        try:
            par_type = type(par_defaults[par])
            if par_type == bool:
                param[par] = param[par] in ["True", "true", "T", "t", True]
            else:
                try:
                    param[par] = par_type(param[par])
                except TypeError:
                    raise TypeError(
                        f"Error converting key in config file: {par}")
        except KeyError:
            logging.warning(f"Parameter {par} does not have a default.\n"
                            f"Defaults: {par_defaults}.")


[docs]class ASReviewSettings(object): """Object to store the configuration of a review session. The main difference being that it type checks (some) of its contents. """ def __init__(self, model, query_strategy, balance_strategy, feature_extraction, n_instances=DEFAULT_N_INSTANCES, stop_if=None, n_prior_included=None, n_prior_excluded=None, as_data=None, model_param={}, query_param={}, balance_param={}, feature_param={}, data_fp=None, n_queries=None, abstract_only=False, # deprecated mode=None, # deprecated n_papers=None, # deprecated data_name=None # deprecated ): self.model = model self.query_strategy = query_strategy self.balance_strategy = balance_strategy self.feature_extraction = feature_extraction self.n_instances = n_instances self.stop_if = stop_if self.n_prior_included = n_prior_included self.n_prior_excluded = n_prior_excluded self.as_data = as_data self.model_param = model_param if query_strategy == "max_random": query_param_copy = query_param.copy() try: del query_param_copy["strategy_1"] del query_param_copy["strategy_2"] except KeyError: pass self.query_param = query_param_copy else: self.query_param = query_param self.balance_param = balance_param self.feature_param = feature_param def __str__(self): return pretty_format(self.to_dict()) def __setattr__(self, name, value): try: super(ASReviewSettings, self).__setattr__( name, _map_settings_type(name, value) ) except KeyError: super(ASReviewSettings, self).__setattr__( name, value )
[docs] def to_dict(self): """Export default settings to dict.""" info_dict = {} for attrib in SETTINGS_TYPE_DICT: value = getattr(self, attrib, None) if value is not None: info_dict[attrib] = value return info_dict
[docs] def from_file(self, config_file): """Fill the contents of settings by reading a config file. Arguments --------- config_file: str Source configuration file. """ if config_file is None or not os.path.isfile(config_file): if config_file is not None: print(f"Didn't find configuration file: {config_file}") return config = ConfigParser() config.optionxform = str config.read(config_file) # Read the each of the sections. for sect in config: if sect == "global_settings": for key, value in config.items(sect): try: setattr(self, key, SETTINGS_TYPE_DICT[key](value)) except (KeyError, TypeError): print(f"Warning: value with key '{key}' is ignored " "(spelling mistake, wrong type?).") elif sect in [ "model_param", "query_param", "balance_param", "feature_param" ]: setattr(self, sect, dict(config.items(sect))) elif sect != "DEFAULT": print(f"Warning: section [{sect}] is ignored in " f"config file {config_file}") model = get_classifier(self.model) _convert_types(model.default_param, self.model_param) balance_model = get_balance_model(self.balance_strategy) _convert_types(balance_model.default_param, self.balance_param) query_model = get_query_model(self.query_strategy) _convert_types(query_model.default_param, self.query_param) feature_model = get_feature_model(self.feature_extraction) _convert_types(feature_model.default_param, self.feature_param)