Exemplo n.º 1
0
    def va_diff(cls, session, va_version=None, va_id=None):
        """
        Compares version identified by 'va_version' or 'va_id' with previous version.
        Provide one of va_version, va_id
        :param session: flushed session
        :param va_id: version id of log row to be compared
        :return: dict with versions, user_id's and dict with columns as keys and changes as values
        :return: dict
        """
        if va_version is not None and va_id is not None:
            log.warning(
                "both va_version and va_id provided, only va_version will be used, please exclude one of them "
                "from call")

        if va_version is None and va_id is None:
            raise LogIdentifyError(
                "Please provide at least one from va_version, va_id to identify column"
            )

        if va_version is not None:
            filter_condition = (cls.ArchiveTable.va_version == va_version, )
        else:
            filter_condition = (cls.ArchiveTable.va_id == va_id, )

        this_row = utils.result_to_dict(
            session.execute(
                sa.select({cls.ArchiveTable}).where(*filter_condition)))
        if not len(this_row):
            if va_version is not None:
                identify_str = 'va_version={}'.format(va_version)
            else:
                identify_str = 'va_id={}'.format(va_id)
            raise HistoryItemNotFound(
                "Can't find log record by {}".format(identify_str))
        this_row = this_row[0]

        va_id = this_row['va_id']
        all_history_items = {
            col_name: this_row[col_name]
            for col_name in cls.ArchiveTable._version_col_names
        }
        prev_log = [
            log for log in cls.va_list_by_pk(session, **all_history_items)
            if log['va_id'] < va_id
        ]
        if not prev_log:
            return utils.compare_rows(None, this_row)

        prev_va_id = prev_log[-1]['va_id']
        prev_row = utils.result_to_dict(
            session.execute(
                sa.select({cls.ArchiveTable
                           }).where(cls.ArchiveTable.va_id == prev_va_id)))[0]

        return utils.compare_rows(prev_row, this_row)
Exemplo n.º 2
0
def _get_historical_time_slice(va_table, session, t, conds, include_deleted, limit, offset):
    at = va_table.ArchiveTable
    vc = va_table.va_version_columns
    pk_conditions = _get_conditions_list(va_table, conds)
    and_clause = _get_conditions(
        pk_conditions,
        [at.va_updated_at <= t] +
        [] if include_deleted else [va_table.ArchiveTable.va_deleted.is_(False)],
    )
    t2 = at.__table__.alias('t2')
    return utils.result_to_dict(session.execute(
        sa.select([at])
            .select_from(at.__table__.join(
            t2,
            sa.and_(
                t2.c.va_updated_at <= t,
                at.va_version < t2.c.va_version,
                *[getattr(at, c) == getattr(t2.c, c) for c in vc]
            ),
            isouter=True,
        ))
            .where(t2.c.va_version.is_(None) & and_clause)
            .order_by(*_get_order_clause(at))
            .limit(limit)
            .offset(offset)
    ))
Exemplo n.º 3
0
 def va_get_all_by_pk(cls, session, **kwargs):
     all_history_items = utils.result_to_dict(
         session.execute(
             sa.select([
                 cls.ArchiveTable.va_id, cls.ArchiveTable.va_version,
                 cls.ArchiveTable.user_id,
                 cls.ArchiveTable.va_data.label('record')
             ]).where(cls.create_log_select_expression(kwargs))))
     return all_history_items
Exemplo n.º 4
0
 def va_list_by_pk(cls, session, **kwargs):
     """
     Returns all VA version id's of this record with there corresponding user_id.
     This can be called after a row has been inserted into the table and the session has been flushed.
     """
     return utils.result_to_dict(session.execute(
         sa.select([cls.ArchiveTable.va_id, cls.ArchiveTable.user_id, cls.ArchiveTable.va_version])
         .where(cls.create_log_select_expression(kwargs))
     ))
Exemplo n.º 5
0
    def va_diff_all_by_pk(cls, session, **kwargs):
        all_history_items = utils.result_to_dict(session.execute(
            sa.select([cls.ArchiveTable])
                .where(cls.create_log_select_expression(kwargs))
        ))
        all_changes = []
        for i in range(len(all_history_items)):
            if i is 0:
                all_changes.append(utils.compare_rows(None, all_history_items[i]))
            else:
                all_changes.append(utils.compare_rows(all_history_items[i-1], all_history_items[i]))

        return all_changes
Exemplo n.º 6
0
def _get_latest_time_slice(va_table, session, conds, include_deleted, limit,
                           offset):
    and_clause = _get_conditions(
        _get_conditions_list(va_table, conds, archive=False),
        []
        if include_deleted else [va_table.ArchiveTable.va_deleted.is_(False)],
    )
    result = session.execute(
        sa.select([va_table.ArchiveTable]).select_from(
            va_table.ArchiveTable.__table__.join(
                va_table, va_table.ArchiveTable.va_id ==
                va_table.va_id)).where(and_clause).order_by(*_get_order_clause(
                    va_table.ArchiveTable)).limit(limit).offset(offset))
    return utils.result_to_dict(result)
Exemplo n.º 7
0
def _get_historical_changes(va_table, session, conds, t1, t2, include_deleted, limit, offset):
    pk_conditions = _get_conditions_list(va_table, conds)
    and_clause = _get_conditions(
        pk_conditions,
        [va_table.ArchiveTable.va_updated_at >= t1, va_table.ArchiveTable.va_updated_at < t2] +
        [] if include_deleted else [va_table.ArchiveTable.va_deleted.is_(False)],
    )

    return utils.result_to_dict(session.execute(
        sa.select([va_table.ArchiveTable])
            .where(and_clause)
            .order_by(*_get_order_clause(va_table.ArchiveTable))
            .limit(limit)
            .offset(offset)
    ))
Exemplo n.º 8
0
    def va_get(cls, session, va_version=None, va_id=None):
        """
        Returns historic object (log record). Provide one of va_version, va_id to identify version
        This can be called after a row has been inserted into the table and the session has been flushed.
        :param session: flushed session
        :param va_version - va_version of log row (va_version field in va_version)
        :param va_id: va_id of requested record (va_id field). Can be used as alternative to va_version
        :return: a dictionary of key value pairs representing version id, id of the record in model,
         and versioned model's data
        :rtype: dict
        """
        if va_version is not None and va_id is not None:
            log.warning(
                "both va_version and va_id provided, only va_version will be used, please exclude one of them "
                "from call")

        if va_version is None and va_id is None:
            raise LogIdentifyError(
                "Please provide at least one from va_version, va_id to identify column"
            )

        if va_version is not None:
            filter_condition = (cls.ArchiveTable.va_version == va_version, )
        else:
            filter_condition = (cls.ArchiveTable.va_id == va_id, )

        result = utils.result_to_dict(
            session.execute(
                sa.select({cls.ArchiveTable.va_id, cls.ArchiveTable.va_data
                           }).where(*filter_condition)))

        if not len(result):
            if va_version is not None:
                identify_str = 'va_version={}'.format(va_version)
            else:
                identify_str = 'va_id={}'.format(va_id)
            raise HistoryItemNotFound(
                "Can't find log record by {}".format(identify_str))

        result = result[0]
        historic_object = result['va_data']
        historic_object['va_id'] = result['va_id']
        return historic_object
Exemplo n.º 9
0
def get(
    va_table,
    session,
    va_id=None,
    t1=None,
    t2=None,
    fields=None,
    conds=None,
    include_deleted=True,
    page=1,
    page_size=100,
):
    '''
    :param va_table: the model class which inherits from \
        :class:`~versionalchemy.models.user_table.VAModelMixin` and specifies the model of \
        the user table from which we are querying
    :param session: a sqlalchemy session with connections to the database
    :param va_id: if specified, the value of t1 and t2 will be ignored. If specified, this will \
        return all records after the specified va_id.
    :param t1: lower bound time for this query; if None or unspecified, \
        defaults to the unix epoch. If this is specified and t2 is not, this query \
        will simply return the time slice of data at t1. This must either be a valid \
        sql time string or a datetime.datetime object.
    :param t2: upper bound time for this query; if both t1 and t2 are none or unspecified, \
        this will return the latest data (i.e. time slice of data now). This must either be a \
        valid sql time string or a datetime.datetime object.
    :param fields: a list of strings which corresponds to columns in the table; If \
        None or unspecified, returns all fields in the table.
    :param conds: a list of dictionary of key value pairs where keys are columns in the table \
        and values are values the column should take on. If specified, this query will \
        only return rows where the columns meet all the conditions. The columns specified \
        in this dictionary must be exactly the unique columns that versioning pivots around.
    :param include_deleted: if ``True``, the response will include deleted changes. Else it will \
        only include changes where ``deleted = 0`` i.e. the data was in the user table.
    :param page: the offset of the result set (1-indexed); i.e. if page_size is 100 and page is 2, \
        the result set will contain results 100 - 199
    :param page_size: upper bound on number of results to display. Note the actual returned result \
        set may be smaller than this due to the roll up.
    '''
    limit, offset = _get_limit_and_offset(page, page_size)
    version_col_names = va_table.va_version_columns
    if fields is None:
        fields = [
            name for name in utils.get_column_names(va_table)
            if name != 'va_id'
        ]

    if va_id is not None:
        return _format_response(
            utils.result_to_dict(
                session.execute(
                    sa.select([
                        va_table.ArchiveTable
                    ]).where(va_table.ArchiveTable.va_id > va_id).order_by(
                        *_get_order_clause(va_table.ArchiveTable)).limit(
                            page_size).offset(offset))), fields,
            version_col_names)

    if t1 is None and t2 is None:
        rows = _get_latest_time_slice(va_table, session, conds,
                                      include_deleted, limit, offset)
        return _format_response(rows, fields, version_col_names)

    if t2 is None:  # return a historical time slice
        rows = _get_historical_time_slice(va_table, session, t1, conds,
                                          include_deleted, limit, offset)
        return _format_response(rows, fields, version_col_names)

    if t1 is None:
        t1 = 0

    rows = _get_historical_changes(va_table, session, conds, t1, t2,
                                   include_deleted, limit, offset)
    return _format_response(rows, fields, version_col_names)
Exemplo n.º 10
0
 def _result_to_dict(self, res):
     return utils.result_to_dict(res)