import importlib
import inspect
import json
import os
import sys
import warnings
from inspect import getmembers, isfunction
from tsfel.feature_extraction.features_settings import load_json
from tsfel.utils.calculate_complexity import compute_complexity
[docs]
def add_feature_json(features_path, json_path):
    """Adds new feature to features.json.
    Parameters
    ----------
    features_path: string
        Personal Python module directory containing new features implementation.
    json_path: string
        Personal .json file directory containing existing features from TSFEL.
        New customised features will be added to file in this directory.
    """
    sys.path.append(features_path[:-len(features_path.split(os.sep)[-1]) - 1])
    exec("import " + features_path.split(os.sep)[-1][:-3])
    # Reload module containing the new features
    importlib.reload(sys.modules[features_path.split(os.sep)[-1][:-3]])
    exec("import " + features_path.split(os.sep)[-1][:-3] + " as pymodule")
    # Functions from module containing the new features
    functions_list = [o for o in getmembers(locals()['pymodule']) if isfunction(o[1])]
    function_names = [fname[0] for fname in functions_list]
    # Check if @set_domain was declared on features module
    vset_domain = False
    for fname, f in list(locals()['pymodule'].__dict__.items()):
        if getattr(f, "domain", None) is not None:
            vset_domain = True
            # Access to personal features.json
            feat_json = load_json(json_path)
            # Assign domain and tag
            domain = getattr(f, "domain", None)
            tag = getattr(f, "tag", None)
            # Feature specifications
            # Description
            if f.__doc__ is not None:
                descrip = f.__doc__.split("\n")[0]
            else:
                descrip = ""
            # Feature usage
            use = "yes"
            # Feature function arguments
            args_name = inspect.getfullargspec(f)[0]
            # Access feature parameters
            if args_name != "":
                # Retrieve default values of arguments
                spec = inspect.getfullargspec(f)
                defaults = dict(zip(spec.args[::-1], (spec.defaults or ())[::-1]))
                defaults.update(spec.kwonlydefaults or {})
                for p in args_name[1:]:
                    if p not in list(defaults.keys()):
                        if p == 'fs':
                            # Assigning a default value for fs if not given
                            defaults[p] = 100
                        else:
                            defaults[p] = None
                if len(defaults) == 0:
                    defaults = ""
            else:
                defaults = ""
            # Settings of new feature
            new_feature = {"description": descrip,
                           "parameters": defaults,
                           "function": fname,
                           "use": use
                           }
            # Check if domain exists
            try:
                feat_json[domain][fname] = new_feature
            except KeyError:
                feat_json[domain] = {fname: new_feature}
            # Insert tag if it is declared
            if tag is not None:
                feat_json[domain][fname]['tag'] = tag
            # Write new feature on json file
            with open(json_path, "w") as fout:
                json.dump(feat_json, fout, indent=" ")
            # Calculate feature complexity
            compute_complexity(fname, domain, json_path, features_path=features_path)
            print('Feature '+str(fname)+' was added.')
    if vset_domain is False:
        warnings.warn('No features were added. Please declare @set_domain.', stacklevel=2)