<gh_stars>0
from abc import abstractmethod, ABC
from typing import Iterable

from models import Experiment
from utils import AbstractRegisteringType, abstract_property


class MethodResult(ABC):
    """Result should contain list of matched pathways or processes

    to be displayed in the results table. The result can include
    additional information for the end user (in `description` field).

    The names of properties of the items (pathways or processes) in
    the list which should be used for table creation ought to be
    enlisted in `columns` property.

    Additional results created by a method should be presented in
    `files` field of the result.
    """

    @abstract_property
    def columns(self) -> Iterable:
        """List with attributes of objects from `scored_list`,

        which will be used for summary table generation as columns.
        """

    def __init__(self, scored_list, files=None, description=''):
        self.scored_list = scored_list
        self.files = files or []
        self.description = description

    def generate_markdown(self, file_name, descr=""):
        with open(file_name if '.md' in file_name else file_name.split('.')[0] + '.md', 'w') as handle:
            if self.description:
                handle.write('# ' + self.description + '\n\n')
            if descr:
                handle.write('### ' + descr + '\n\n')
            endline = '|\n'
            for i in range(len(self.columns)):
                handle.write('|' + self.columns[i])
                if i == len(self.columns) - 1:
                    handle.write(endline)
            for i in range(len(self.columns)):
                handle.write('|---')
                if i == len(self.columns) - 1:
                    handle.write(endline)
            for obj in self.scored_list:
                for colname in self.columns:
                    handle.write('|' + str(getattr(obj, colname)) if hasattr(obj, colname) else '')
                handle.write(endline)


class Method(metaclass=AbstractRegisteringType):
    """Defines method of pathway analysis & its arguments.

    Simple arguments (like ``threshold``) can be simply defined as
    arguments and keyword arguments of `__init__`.

    For example::

        class MyMethod(Method)
            def __init__(self, threshold: float=0.05):
                pass

    For the simple arguments following information will be deduced:
        - type: will be retrieved from type annotations; currently only
          non-abstract types (int, str, float and so on) are supported.
          We can implement abstract types from `typing` if needed.
        - default: from keyword arguments.
        - help: will be retrieved from docstrings

    If you need more advanced options (like aggregation), or just do not
    like having a mess in your `__init__` signature, please define the
    arguments in body of your class using :class:`~command_line.parser.Argument` constructor.

    For example::

        class MyMethod(Method):

            database = Argument(
                type=argparse.FileType('r'),
                help='Path to file with the database'
            )

            def __init__(self, threshold: float=0.05, database=None):
                pass

    If help is given in both :class:`~command_line.parser.Argument` and docstring,
    then the help from `Argument()` takes precedence over the help in docstrings
    (as docstrings should cover not only CLI usage but also describe how to use
    the method as a standalone object - to enable advanced users to customize methods).
    """

    @abstract_property
    def help(self) -> str:
        """Return string providing help for this method.

        The help message shows up when `./run method_name -h`.
        Use help = __doc__
        """

    @abstract_property
    def name(self) -> str:
        """Return method name used internally and in command line interface.

        The name should not include any spaces."""

    @abstractmethod
    def run(self, experiment: Experiment) -> MethodResult:
        """Performs analysis and returns results object."""

from abc import abstractmethod, ABC
from typing import Iterable

from models import Experiment
from utils import AbstractRegisteringType, abstract_property


class MethodResult(ABC):
    """Result should contain list of matched pathways or processes

    to be displayed in the results table. The result can include
    additional information for the end user (in `description` field).

    The names of properties of the items (pathways or processes) in
    the list which should be used for table creation ought to be
    enlisted in `columns` property.

    Additional results created by a method should be presented in
    `files` field of the result.
    """

    @abstract_property
    def columns(self) -> Iterable:
        """List with attributes of objects from `scored_list`,

        which will be used for summary table generation as columns.
        """

    def __init__(self, scored_list, files=None, description=''):
        self.scored_list = scored_list
        self.files = files or []
        self.description = description

    def generate_markdown(self, file_name, descr=""):
        with open(file_name if '.md' in file_name else file_name.split('.')[0] + '.md', 'w') as handle:
            if self.description:
                handle.write('# ' + self.description + '\n\n')
            if descr:
                handle.write('### ' + descr + '\n\n')
            endline = '|\n'
            for i in range(len(self.columns)):
                handle.write('|' + self.columns[i])
                if i == len(self.columns) - 1:
                    handle.write(endline)
            for i in range(len(self.columns)):
                handle.write('|---')
                if i == len(self.columns) - 1:
                    handle.write(endline)
            for obj in self.scored_list:
                for colname in self.columns:
                    handle.write('|' + str(getattr(obj, colname)) if hasattr(obj, colname) else '')
                handle.write(endline)


class Method(metaclass=AbstractRegisteringType):
    """Defines method of pathway analysis & its arguments.

    Simple arguments (like ``threshold``) can be simply defined as
    arguments and keyword arguments of `__init__`.

    For example::

        class MyMethod(Method)
            def __init__(self, threshold: float=0.05):
                pass

    For the simple arguments following information will be deduced:
        - type: will be retrieved from type annotations; currently only
          non-abstract types (int, str, float and so on) are supported.
          We can implement abstract types from `typing` if needed.
        - default: from keyword arguments.
        - help: will be retrieved from docstrings

    If you need more advanced options (like aggregation), or just do not
    like having a mess in your `__init__` signature, please define the
    arguments in body of your class using :class:`~command_line.parser.Argument` constructor.

    For example::

        class MyMethod(Method):

            database = Argument(
                type=argparse.FileType('r'),
                help='Path to file with the database'
            )

            def __init__(self, threshold: float=0.05, database=None):
                pass

    If help is given in both :class:`~command_line.parser.Argument` and docstring,
    then the help from `Argument()` takes precedence over the help in docstrings
    (as docstrings should cover not only CLI usage but also describe how to use
    the method as a standalone object - to enable advanced users to customize methods).
    """

    @abstract_property
    def help(self) -> str:
        """Return string providing help for this method.

        The help message shows up when `./run method_name -h`.
        Use help = __doc__
        """

    @abstract_property
    def name(self) -> str:
        """Return method name used internally and in command line interface.

        The name should not include any spaces."""

    @abstractmethod
    def run(self, experiment: Experiment) -> MethodResult:
        """Performs analysis and returns results object."""
Result should contain list of matched pathways or processes      to be displayed in the results table. The result can include     additional information for the end user (in `description` field).      The names of properties of the items (pathways or processes) in     the list which should be used for table creation ought to be     enlisted in `columns` property.      Additional results created by a method should be presented in     `files` field of the result. List with attributes of objects from `scored_list`,          which will be used for summary table generation as columns. ## ' + descr + '\n\n') Defines method of pathway analysis & its arguments.      Simple arguments (like ``threshold``) can be simply defined as     arguments and keyword arguments of `__init__`.      For example::          class MyMethod(Method)             def __init__(self, threshold: float=0.05):                 pass      For the simple arguments following information will be deduced:         - type: will be retrieved from type annotations; currently only           non-abstract types (int, str, float and so on) are supported.           We can implement abstract types from `typing` if needed.         - default: from keyword arguments.         - help: will be retrieved from docstrings      If you need more advanced options (like aggregation), or just do not     like having a mess in your `__init__` signature, please define the     arguments in body of your class using :class:`~command_line.parser.Argument` constructor.      For example::          class MyMethod(Method):              database = Argument(                 type=argparse.FileType('r'),                 help='Path to file with the database'             )              def __init__(self, threshold: float=0.05, database=None):                 pass      If help is given in both :class:`~command_line.parser.Argument` and docstring,     then the help from `Argument()` takes precedence over the help in docstrings     (as docstrings should cover not only CLI usage but also describe how to use     the method as a standalone object - to enable advanced users to customize methods). Return string providing help for this method.          The help message shows up when `./run method_name -h`.         Use help = __doc__ Return method name used internally and in command line interface.          The name should not include any spaces. Performs analysis and returns results object.