예제 #1
0
    def soft_delete_from_production_for_assignment_relation(
            self, table: str, source_system: str) -> None:
        """
        Updates production records that do not have a match in the staging table
        by setting their `deletedat` value to the current timestamp, but
        only for the related assignments in the staging table.

        Parameters
        ----------
        table: str
            Name of the table to soft delete on, not including the `stg_`.
        source_system: str
            The SourceSystem currently being processed.
        """

        assert table.strip() != "", "Argument `table` cannot be whitespace"

        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.soft_delete_from_production_for_assignment_relation(
                table, source_system)

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.soft_delete_from_production_for_assignment_relation(
                table, source_system)

        row_count = self._exec(statement)
        logger.debug(f"Soft-deleted {row_count} records in table `{table}`")
예제 #2
0
    def add_processed_file(self, path: str, resource_name: str, rows: int):
        """
        Records that a file has been processed and thus should not be processed
        a second time.

        Parameters
        ----------
        path: str
            Filesystem path for the file that was processed.
        resource_name: str
            Name of the resource covered by the file.
        rows: int
            Number of rows in the file.
        """
        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.add_processed_file(path, resource_name,
                                                      rows)

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.add_processed_file(path, resource_name,
                                                      rows)

        statement = statement.strip()

        try:
            _ = self._exec(statement)
        except ProgrammingError as pe:
            logger.exception(pe)
            raise
예제 #3
0
    def unsoft_delete_returned_submission_types(self,
                                                source_system: str) -> None:
        """
        Unmarks previously "deleted" Assignment Submission Types when they are
        present in the incoming data.

        Parameters
        ----------
        source_system: str
            The name of the source system for the current import process.
        """

        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.unsoft_delete_returned_submission_types(
                source_system)

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.unsoft_delete_returned_submission_types(
                source_system)

        row_count = self._exec(statement)
        logger.debug(
            f"Un-soft deleted {row_count} records in table `{Table.ASSIGNMENT_SUBMISSION_TYPES}`."
        )
예제 #4
0
    def insert_new_records_to_production(self, table: str,
                                         columns: List[str]) -> None:
        """
        Copies new records from the staging table to the production table.

        Parameters
        ----------
        table: str
            Name of the table to truncate, not including the `stg_` prefix
        columns: List[str]
            A list of the column names in the table
        """

        assert table.strip() != "", "Argument `table` cannot be whitespace"
        assert len(columns) > 0, "Argument `columns` cannot be empty"

        column_string = ",".join([f"\n    {c}" for c in columns])

        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.insert_new_records_to_production(
                table, column_string)

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.insert_new_records_to_production(
                table, column_string)

        row_count = self._exec(statement)
        logger.debug(f"Inserted {row_count} records into table `{table}`.")
예제 #5
0
    def truncate_staging_table(self, table: str) -> None:
        """
        Executes a truncate command on the staging version of a table.

        Parameters
        ----------
        table: str
            Name of the table to truncate, not including the `stg_` prefix
        """

        assert table.strip() != "", "Argument `table` cannot be whitespace"

        if self.engine == DbEngine.MSSQL:
            self._exec(MS_builder.truncate_stg_table(table))

        if self.engine == DbEngine.POSTGRESQL:
            self._exec(PG_builder.truncate_stg_table(table))
예제 #6
0
    def insert_new_submission_types(self) -> None:
        """
        Inserts new Assignment Submission Type records from staging table
        into the production table.
        """

        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.insert_new_submission_types()

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.insert_new_submission_types()

        row_count = self._exec(statement)
        logger.debug(
            f"Updated {row_count} records in table `{Table.ASSIGNMENT_SUBMISSION_TYPES}`."
        )
예제 #7
0
    def enable_staging_natural_key_index(self, table: str) -> None:
        """
        Re-builds the natural key index on the staging table.

        Parameters
        ----------
        table: str
            Name of the table to truncate, not including the `stg_` prefix
        """

        assert table.strip() != "", "Argument `table` cannot be whitespace"

        if self.engine == DbEngine.MSSQL:
            self._exec(MS_builder.enable_staging_natural_key_index(table))

        if self.engine == DbEngine.POSTGRESQL:
            self._exec(PG_builder.recreate_staging_natural_key_index(table))
예제 #8
0
    def copy_updates_to_production(self, table: str,
                                   columns: List[str]) -> None:
        """
        Updates modified records in production based on the staging table, based
        on the LastModifiedDate.

        Parameters
        ----------
        table: str
            Name of the table to truncate, not including the `stg_` prefix
        columns: List[str]
            A list of the column names in the table
        """

        assert table.strip() != "", "Argument `table` cannot be whitespace"
        assert len(columns) > 0, "Argument `columns` cannot be empty"

        update_columns = (
            ",".join([
                f"\n    {c} = stg.{c}" for c in columns if c not in (
                    # These are natural key columns that should never be
                    # updated.
                    "SourceSystem",
                    "SourceSystemIdentifier",
                    "LMSSectionSourceSystemIdentifier",
                    "LMSUserSourceSystemIdentifier",
                    "AssignmentSourceSystemIdentifier",
                )
            ]) + ",\n    DeletedAt = NULL")

        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.copy_updates_to_production(
                table, update_columns)

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.copy_updates_to_production(
                table, update_columns)

        row_count = self._exec(statement)
        logger.debug(f"Updated {row_count} records in table `{table}`.")
예제 #9
0
    def get_processed_files(self, resource_name: str) -> Set[str]:
        try:
            query = ""
            if self.engine == DbEngine.MSSQL:
                query = MS_builder.get_processed_files(resource_name)

            if self.engine == DbEngine.POSTGRESQL:
                query = PG_builder.get_processed_files(resource_name)

            query = query.strip()
            result = pd.read_sql_query(query, self.db_adapter.engine)

            result.columns = result.columns.str.lower()

            if "fullpath" in result:
                return set(result["fullpath"])

            return set()
        except ProgrammingError as pe:
            logger.exception(pe)
            raise
예제 #10
0
    def insert_new_records_to_production_for_assignment_and_user_relation(
            self, table: str, columns: List[str]) -> None:
        """
        Copies new records from the staging table to the production table. Specialized
        for tables that have a foreign key to both Assignment and LMSUser.

        Parameters
        ----------
        table: str
            Name of the table to truncate, not including the `stg_` prefix
        columns: List[str]
            A list of the column names in the table
        """

        assert table.strip() != "", "Argument `table` cannot be whitespace"
        assert len(columns) > 0, "Argument `columns` cannot be empty"

        insert_columns = ",".join([
            f"\n    {c}" for c in columns
            if not (c == "AssignmentSourceSystemIdentifier"
                    or c == "LMSUserSourceSystemIdentifier")
        ])
        select_columns = ",".join([
            f"\n    stg.{c}" for c in columns
            if not (c == "AssignmentSourceSystemIdentifier"
                    or c == "LMSUserSourceSystemIdentifier")
        ])

        statement = ""
        if self.engine == DbEngine.MSSQL:
            statement = MS_builder.insert_new_records_to_production_for_assignment_and_user_relation(
                table, insert_columns, select_columns)

        if self.engine == DbEngine.POSTGRESQL:
            statement = PG_builder.insert_new_records_to_production_for_assignment_and_user_relation(
                table, insert_columns, select_columns)

        row_count = self._exec(statement)
        logger.debug(f"Inserted {row_count} records into table `{table}`.")