Esempio n. 1
0
    def _make_schema(
        db_name: Optional[str] = None,
    ) -> Tuple[DatabaseConnector, Dict[str, Dict[str, str]]]:
        """
        Create the schema (i.e. make all the tables) of the database.

        Parameters
        ----------
        db_name : None or str
            Name of the database

        Returns
        -------
        db_connector : DatabaseConnector
            The database connection object
        final_parameters_as_sql_types : dict
            Final parameters as sql types
        """
        db_connector = make_test_database(db_name)

        default_parameters = get_default_parameters
        final_parameters = FinalParameters(default_parameters)
        final_parameters_dict = final_parameters.get_final_parameters()
        final_parameters_as_sql_types = final_parameters.cast_to_sql_type(
            final_parameters_dict
        )

        db_creator = DatabaseCreator(db_connector)

        db_creator.create_all_schema_tables(final_parameters_as_sql_types)

        return db_connector, final_parameters_as_sql_types
def test_db_creator(
    make_test_database: Callable[[str], DatabaseConnector],
    make_test_schema: Callable[
        [str], Tuple[DatabaseConnector, Dict[str, Dict[str, str]]]
    ],
) -> None:
    """
    Test we can create the database schemas.

    Specifically this test that:
    1. The database is empty on creation
    2. The tables are created
    3. It is not possible to create the schema more than once
    4. Check that all expected tables have been created

    Parameters
    ----------
    make_test_database : function
        Function returning the database connection
    make_test_schema : function
        Function returning the database connection and the final parameters as sql types
    """
    db_connector_no_schema = make_test_database("test_creation_without_schema")
    db_reader_no_schema = DatabaseReader(db_connector_no_schema)

    # There should be no tables before creating them
    assert not db_reader_no_schema.check_tables_created()

    db_connector_schema, final_parameters_as_sql_types = make_test_schema(
        "test_creation_with_schema"
    )
    db_reader_schema = DatabaseReader(db_connector_schema)
    db_creator = DatabaseCreator(db_connector_schema)

    # The tables should now have been created
    assert db_reader_schema.check_tables_created()

    with pytest.raises(sqlite3.OperationalError):
        db_creator.create_all_schema_tables(final_parameters_as_sql_types)

    # Check that all tables has been created
    non_parameter_tables = {
        "system_info",
        "split",
        "file_modification",
        "parameters",
        "run",
    }
    parameter_tables = set(
        el.replace(":", "_") for el in final_parameters_as_sql_types.keys()
    )
    query_str = 'SELECT name FROM sqlite_master WHERE type="table"'
    table = db_reader_schema.query(query_str)

    actual = table.loc[:, "name"].values  # pylint: disable=no-member
    assert non_parameter_tables.union(parameter_tables) == set(actual)
    def __init__(
        self,
        executor: Optional[BoutRunExecutor] = None,
        db_connector: Optional[DatabaseConnector] = None,
        final_parameters: Optional[FinalParameters] = None,
    ) -> None:
        """
        Set the member data.

        This constructor will also create the schema if it does not exist.

        Parameters
        ----------
        executor : BoutRunExecutor or None
            Object executing the run
            If None, default parameters will be used
        db_connector : DatabaseConnector or None
            The connection to the database
            If None: Default database connector will be used
        final_parameters : FinalParameters or None
            The object containing the parameters which are going to be used in the run
            If None, default parameters will be used
        """
        # Set member data
        # NOTE: We are not setting the default as a keyword argument
        #       as this would mess up the paths
        logging.info("Start: Making a BoutRunSetup object")
        self.__executor = executor if executor is not None else BoutRunExecutor(
        )
        self.__final_parameters = (final_parameters if final_parameters
                                   is not None else FinalParameters())
        self.__db_connector = (
            db_connector if db_connector is not None else DatabaseConnector(
                name=self.__executor.exec_name,
                db_root_path=self.__executor.bout_paths.project_path,
            ))
        self.__db_creator = DatabaseCreator(self.db_connector)
        self.__metadata_recorder = MetadataRecorder(self.__db_connector,
                                                    self.executor.bout_paths,
                                                    self.final_parameters)

        if not self.__metadata_recorder.db_reader.check_tables_created():
            logging.info(
                "Creating schema as no tables were found in "
                "%s",
                self.__metadata_recorder.db_reader.db_connector.db_path,
            )
            self.__create_schema()
        logging.info("Done: Making a BoutRunSetup object")
def test_get_create_table_statement() -> None:
    """Test that get_create_table_statement returns expected output."""
    result = DatabaseCreator.get_create_table_statement(
        table_name="foo",
        columns=dict(bar="baz", foobar="qux"),
        primary_key="quux",
        foreign_keys=dict(quuz=("corge", "grault"), garply=("waldo", "fred")),
    )

    expected = (
        "CREATE TABLE foo \n"
        "(   quux INTEGER PRIMARY KEY,\n"
        "    bar baz NOT NULL,\n"
        "    foobar qux NOT NULL,\n"
        "    quuz INTEGER NOT NULL,\n"
        "    garply INTEGER NOT NULL,\n"
        "    FOREIGN KEY(quuz) \n"
        "        REFERENCES corge(grault)\n"
        "            ON UPDATE CASCADE\n"
        "            ON DELETE CASCADE,\n"
        "    FOREIGN KEY(garply) \n"
        "        REFERENCES waldo(fred)\n"
        "            ON UPDATE CASCADE\n"
        "            ON DELETE CASCADE)"
    )

    assert result == expected
class BoutRunSetup:
    """
    Class for setting up the BOUT++ run.

    More specifically this class will connect the executor object with the run
    parameters and a database to store the results in

    Attributes
    ----------
    __executor : BoutRunExecutor
        Getter variable for executor
    __db_connector : DatabaseConnector
        Getter variable for db_connector
    __final_parameters : FinalParameters
        Getter variable for final_parameters
    __db_creator : DatabaseCreator
        Object used to create the database
    __metadata_recorder : MetadataRecorder
        Object used to record the metadata about a run
    executor : BoutRunExecutor
        Object used to execute the run
    bout_paths : BoutPaths
        The BoutPaths obtained through the get property
    db_connector : DatabaseConnector
        Object containing the database connection
    final_parameters : FinalParameters
        Object containing the parameters to use
    metadata_recorder : MetadataRecorder
        Object containing the metadata recorder
    submitter : AbstractSubmitter
        The submitter obtained through the get property

    Methods
    -------
    __create_schema()
        Create the schema

    Examples
    --------
    >>> run_setup = BoutRunSetup(executor, db_connector, final_parameters)
    >>> run_graph = RunGraph()
    >>> run_group = RunGroup(run_graph, run_setup)
    >>> runner = BoutRunner(run_graph)
    >>> runner.run()
    """
    def __init__(
        self,
        executor: Optional[BoutRunExecutor] = None,
        db_connector: Optional[DatabaseConnector] = None,
        final_parameters: Optional[FinalParameters] = None,
    ) -> None:
        """
        Set the member data.

        This constructor will also create the schema if it does not exist.

        Parameters
        ----------
        executor : BoutRunExecutor or None
            Object executing the run
            If None, default parameters will be used
        db_connector : DatabaseConnector or None
            The connection to the database
            If None: Default database connector will be used
        final_parameters : FinalParameters or None
            The object containing the parameters which are going to be used in the run
            If None, default parameters will be used
        """
        # Set member data
        # NOTE: We are not setting the default as a keyword argument
        #       as this would mess up the paths
        logging.info("Start: Making a BoutRunSetup object")
        self.__executor = executor if executor is not None else BoutRunExecutor(
        )
        self.__final_parameters = (final_parameters if final_parameters
                                   is not None else FinalParameters())
        self.__db_connector = (
            db_connector if db_connector is not None else DatabaseConnector(
                name=self.__executor.exec_name,
                db_root_path=self.__executor.bout_paths.project_path,
            ))
        self.__db_creator = DatabaseCreator(self.db_connector)
        self.__metadata_recorder = MetadataRecorder(self.__db_connector,
                                                    self.executor.bout_paths,
                                                    self.final_parameters)

        if not self.__metadata_recorder.db_reader.check_tables_created():
            logging.info(
                "Creating schema as no tables were found in "
                "%s",
                self.__metadata_recorder.db_reader.db_connector.db_path,
            )
            self.__create_schema()
        logging.info("Done: Making a BoutRunSetup object")

    @property
    def executor(self) -> BoutRunExecutor:
        """
        Get the properties of self.executor.

        Returns
        -------
        self.__executor : BoutRunExecutor
            The executor object
        """
        return self.__executor

    @property
    def bout_paths(self) -> BoutPaths:
        """
        Return the BoutPaths.

        Returns
        -------
        BoutPaths
            The BoutPaths
        """
        return self.executor.bout_paths

    @property
    def final_parameters(self) -> FinalParameters:
        """
        Get the properties of self.final_parameters.

        Returns
        -------
        self.__final_parameters : FinalParameters
            The object containing the parameters used in the run
        """
        return self.__final_parameters

    @property
    def db_connector(self) -> DatabaseConnector:
        """
        Get the properties of self.db_connector.

        Returns
        -------
        self.__db_connector : DatabaseConnector
            The object holding the database connection
        """
        return self.__db_connector

    @property
    def metadata_recorder(self) -> MetadataRecorder:
        """
        Get the properties of self.metadata_recorder.

        Returns
        -------
        self.__metadata_recorder : MetadataRecorder
            The object holding the metadata recorder
        """
        return self.__metadata_recorder

    @property
    def submitter(self) -> AbstractSubmitter:
        """
        Return the AbstractSubmitter.

        Returns
        -------
        AbstractSubmitter
            The submitter which will be used for submitting the job
        """
        return self.executor.submitter

    def __create_schema(self) -> None:
        """Create the schema."""
        final_parameters_dict = self.final_parameters.get_final_parameters()
        final_parameters_as_sql_types = self.final_parameters.cast_to_sql_type(
            final_parameters_dict)
        self.__db_creator.create_all_schema_tables(
            final_parameters_as_sql_types)